mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-09-14 20:16:53 +08:00
b3b05e1abe
Fixes #1409. The `openspec init` welcome screen and the `openspec update` legacy-upgrade menu hardcoded /opsx:new and /opsx:continue. The default core profile is propose/explore/apply/update/sync/archive, so it never generates them and users were told to run commands that did not exist. getOnboardingCommands() holds the hints in lifecycle order and returns only those whose workflow is installed; both surfaces print its result. In `update` the set is what the newly configured tools actually received, since a legacy upgrade installs an inferred subset for Codex. Stacked on #1404, which decides how each hint is spelled per tool. This commit decides which hints appear; #1404's referenceFor/printStartHints decide the reference form, so Kimi still gets /skill:openspec-*. The welcome screen's quick-start block is width-constrained: it renders beside a 24-column art column and only animates at MIN_WIDTH (60) or wider, and the animation moves the cursor up a fixed count of logical lines. A wrapped line desyncs it, so descriptions are capped at DESCRIPTION_BUDGET and a test asserts no rendered line exceeds 59. Also validates --profile before the welcome screen rather than casting it, so an invalid value fails before the user presses Enter.
44 lines
1.7 KiB
TypeScript
44 lines
1.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
DESCRIPTION_BUDGET,
|
|
getOnboardingCommands,
|
|
} from '../../src/core/onboarding-commands.js';
|
|
import { ALL_WORKFLOWS, CORE_WORKFLOWS } from '../../src/core/profiles.js';
|
|
|
|
describe('getOnboardingCommands', () => {
|
|
it('omits commands the profile does not install', () => {
|
|
const commands = getOnboardingCommands(CORE_WORKFLOWS).map((c) => c.command);
|
|
|
|
expect(commands).toEqual(['/opsx:propose', '/opsx:apply']);
|
|
expect(commands).not.toContain('/opsx:new');
|
|
expect(commands).not.toContain('/opsx:continue');
|
|
});
|
|
|
|
it('includes expanded commands when a custom profile installs them', () => {
|
|
const commands = getOnboardingCommands(['new', 'continue', 'apply']).map((c) => c.command);
|
|
|
|
expect(commands).toEqual(['/opsx:new', '/opsx:continue', '/opsx:apply']);
|
|
});
|
|
|
|
it('returns lifecycle order regardless of the order workflows are given', () => {
|
|
const commands = getOnboardingCommands(['apply', 'continue', 'propose']).map((c) => c.command);
|
|
|
|
expect(commands).toEqual(['/opsx:propose', '/opsx:continue', '/opsx:apply']);
|
|
});
|
|
|
|
it('returns nothing when no onboarding workflow is installed', () => {
|
|
expect(getOnboardingCommands(['archive', 'sync'])).toEqual([]);
|
|
expect(getOnboardingCommands([])).toEqual([]);
|
|
});
|
|
|
|
it('keeps descriptions within the welcome screen width budget', () => {
|
|
// A longer description wraps the welcome screen at 60 columns, which desyncs
|
|
// its animation. See the width test in test/ui/welcome-screen.test.ts.
|
|
for (const { command, description } of getOnboardingCommands(ALL_WORKFLOWS)) {
|
|
expect(description.length, `${command} description is too long`).toBeLessThanOrEqual(
|
|
DESCRIPTION_BUDGET
|
|
);
|
|
}
|
|
});
|
|
});
|