Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions skills/openspec-update-change/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit

**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.

`/openspec-continue-change` is an expanded-profile workflow and may not be installed. Before suggesting it anywhere below, verify that it is available. If it is unavailable, `openspec status --change "<name>" --json` shows the next artifact and `openspec instructions "<artifact-id>" --change "<name>" --json` explains how to create it.
`/openspec-continue-change` is an optional workflow and may not be installed. Before suggesting it anywhere below, verify that it is available. If it is unavailable, `openspec status --change "<name>" --json` shows the next artifact and `openspec instructions "<artifact-id>" --change "<name>" --json` explains how to create it.

**Steps**

Expand Down Expand Up @@ -87,4 +87,4 @@ After each invocation, show:
- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/openspec-continue-change`'s job.
- Confirm every edit with the user before writing.
- If the request changes the change's *intent* rather than refining it, first verify whether the expanded-profile `/openspec-new-change` workflow is available. If it is, recommend starting fresh with `/openspec-new-change` (the "Update vs. Start Fresh" heuristic). If it is unavailable, ask for a distinct unused change name and recommend `openspec new change "<new-change-name>"` instead.
- If the request changes the change's *intent* rather than refining it, first verify whether the optional `/openspec-new-change` workflow is available. If it is, recommend starting fresh with `/openspec-new-change` (the "Update vs. Start Fresh" heuristic). If it is unavailable, ask for a distinct unused change name and recommend `openspec new change "<new-change-name>"` instead.
6 changes: 5 additions & 1 deletion src/commands/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ interface WorkflowPromptMeta {
description: string;
}

const WORKFLOW_PROMPT_META: Record<string, WorkflowPromptMeta> = {
export const WORKFLOW_PROMPT_META: Record<string, WorkflowPromptMeta> = {
propose: {
name: 'Propose change',
description: 'Create proposal, design, and tasks from a request',
Expand All @@ -65,6 +65,10 @@ const WORKFLOW_PROMPT_META: Record<string, WorkflowPromptMeta> = {
name: 'Apply tasks',
description: 'Implement tasks from the current change',
},
update: {
name: 'Update change',
description: 'Revise the planning artifacts of an existing change',
},
ff: {
name: 'Fast-forward',
description: 'Run a faster implementation workflow',
Expand Down
8 changes: 4 additions & 4 deletions src/core/templates/workflows/update-change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ ${STORE_SELECTION_GUIDANCE}

**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.

\`/opsx:continue\` is an expanded-profile workflow and may not be installed. Before suggesting it anywhere below, verify that it is available. If it is unavailable, \`openspec status --change "<name>" --json\` shows the next artifact and \`openspec instructions "<artifact-id>" --change "<name>" --json\` explains how to create it.
\`/opsx:continue\` is an optional workflow and may not be installed. Before suggesting it anywhere below, verify that it is available. If it is unavailable, \`openspec status --change "<name>" --json\` shows the next artifact and \`openspec instructions "<artifact-id>" --change "<name>" --json\` explains how to create it.

**Steps**

Expand Down Expand Up @@ -89,7 +89,7 @@ After each invocation, show:
- Edit only the concrete files in \`existingOutputPaths\`; never write to a glob \`resolvedOutputPath\`.
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is \`/opsx:continue\`'s job.
- Confirm every edit with the user before writing.
- If the request changes the change's *intent* rather than refining it, first verify whether the expanded-profile \`/opsx:new\` workflow is available. If it is, recommend starting fresh with \`/opsx:new\` (the "Update vs. Start Fresh" heuristic). If it is unavailable, ask for a distinct unused change name and recommend \`openspec new change "<new-change-name>"\` instead.`,
- If the request changes the change's *intent* rather than refining it, first verify whether the optional \`/opsx:new\` workflow is available. If it is, recommend starting fresh with \`/opsx:new\` (the "Update vs. Start Fresh" heuristic). If it is unavailable, ask for a distinct unused change name and recommend \`openspec new change "<new-change-name>"\` instead.`,
license: 'MIT',
compatibility: 'Requires openspec CLI.',
metadata: { author: 'openspec', version: '1.0' },
Expand All @@ -108,7 +108,7 @@ ${STORE_SELECTION_GUIDANCE}

**Input**: Optionally specify a change name after \`/opsx:update\` (e.g., \`/opsx:update add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.

\`/opsx:continue\` is an expanded-profile workflow and may not be installed. Before suggesting it anywhere below, verify that it is available. If it is unavailable, \`openspec status --change "<name>" --json\` shows the next artifact and \`openspec instructions "<artifact-id>" --change "<name>" --json\` explains how to create it.
\`/opsx:continue\` is an optional workflow and may not be installed. Before suggesting it anywhere below, verify that it is available. If it is unavailable, \`openspec status --change "<name>" --json\` shows the next artifact and \`openspec instructions "<artifact-id>" --change "<name>" --json\` explains how to create it.

**Steps**

Expand Down Expand Up @@ -180,6 +180,6 @@ After each invocation, show:
- Edit only the concrete files in \`existingOutputPaths\`; never write to a glob \`resolvedOutputPath\`.
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is \`/opsx:continue\`'s job.
- Confirm every edit with the user before writing.
- If the request changes the change's *intent* rather than refining it, first verify whether the expanded-profile \`/opsx:new\` workflow is available. If it is, recommend starting fresh with \`/opsx:new\` (the "Update vs. Start Fresh" heuristic). If it is unavailable, ask for a distinct unused change name and recommend \`openspec new change "<new-change-name>"\` instead.`
- If the request changes the change's *intent* rather than refining it, first verify whether the optional \`/opsx:new\` workflow is available. If it is, recommend starting fresh with \`/opsx:new\` (the "Update vs. Start Fresh" heuristic). If it is unavailable, ask for a distinct unused change name and recommend \`openspec new change "<new-change-name>"\` instead.`
};
}
21 changes: 21 additions & 0 deletions test/commands/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,3 +395,24 @@ describe('config profile command', () => {
expect(result.success).toBe(false);
});
});

describe('workflow picker labels', () => {
it('gives every workflow a friendly label instead of the raw-id fallback', async () => {
const { WORKFLOW_PROMPT_META } = await import('../../src/commands/config.js');
const { ALL_WORKFLOWS } = await import('../../src/core/profiles.js');

for (const workflow of ALL_WORKFLOWS) {
const meta = WORKFLOW_PROMPT_META[workflow];
// A missing entry is exactly what made `update` render as its raw id
// with a `Workflow: update` placeholder in the config picker (#1627).
expect(meta, `missing picker metadata for "${workflow}"`).toBeDefined();
expect(meta!.name, `label for "${workflow}" must not be the raw id`).not.toBe(workflow);
expect(meta!.name.length, `label for "${workflow}" must be non-empty`).toBeGreaterThan(0);
expect(
meta!.description.startsWith('Workflow:'),
`description for "${workflow}" must not be the placeholder`
).toBe(false);
expect(meta!.description.length, `description for "${workflow}" must be non-empty`).toBeGreaterThan(0);
}
});
});
2 changes: 1 addition & 1 deletion test/core/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ describe('InitCommand', () => {

for (const [content, continueReference] of updateVariants) {
const availabilityGuidance = content.indexOf(
`${continueReference} is an expanded-profile workflow and may not be installed`
`${continueReference} is an optional workflow and may not be installed`
);
const nextReference = content.indexOf(
continueReference,
Expand Down
6 changes: 3 additions & 3 deletions test/core/templates/skill-templates-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ const EXPECTED_FUNCTION_HASHES: Record<string, string> = {
getOpsxProposeSkillTemplate: '24623c066f97e34b957d448d1f9a9e8b8a13da3dfce45d45671f6226a2534848',
getOpsxProposeCommandTemplate: 'e67ba591efb0fecacb2229d06dfa84af18b825fab8a7b01377279e4f09a06ce4',
getFeedbackSkillTemplate: 'd7d83c5f7fc2b92fe8f4588a5bf2d9cb315e4c73ec19bcd5ef28270906319a0d',
getUpdateChangeSkillTemplate: '7aa7351aca25fe2d8d29df4339ca2c768e32c6a59596316bb451831447fa15fc',
getOpsxUpdateCommandTemplate: 'fdc8ba0502910f74e85044e450906f189e939ffd70d5e85b7307a6ec33805148',
getUpdateChangeSkillTemplate: '7dc8abc6f64c58bf34d7581ed4ab095a3b7a53cb372349bee2d840db58622819',
getOpsxUpdateCommandTemplate: 'e2388521b22f92f74561df9a0c2f98e1fa4d265af93b5ba26f42fb47a6c5bfed',
};

const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record<string, string> = {
Expand All @@ -77,7 +77,7 @@ const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record<string, string> = {
'openspec-verify-change': 'af9be013dcbe8c6d8f6d9ab10c893fbd03f4c62933c384d82f63894dd0ceb84f',
'openspec-onboard': 'd53403b4910ab64307862ccf97e70bd8f7174ee44508088fb239c880f0939331',
'openspec-propose': '25d08ed4f031770cea219604167d76bca9f3e89fe0c2f545263674482c6f13f0',
'openspec-update-change': '5ee000a8bf5507a553fbb9ed666625d11986f9c0bf14edb495648554a4d1c53c',
'openspec-update-change': '586547406aca94422dfeb3ffedce6c01049429b743f57ce829baa79ebc714d51',
};

// Intentionally excludes getFeedbackSkillTemplate: this list only models templates
Expand Down
4 changes: 2 additions & 2 deletions test/core/templates/update-change.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe('update-change templates', () => {
it('explains the optional continue workflow before suggesting it', () => {
for (const [label, body] of bodies) {
const availabilityGuidance = body.indexOf(
'`/opsx:continue` is an expanded-profile workflow and may not be installed'
'`/opsx:continue` is an optional workflow and may not be installed'
);
const firstSuggestion = body.indexOf(
'`/opsx:continue`',
Expand Down Expand Up @@ -110,7 +110,7 @@ describe('update-change templates', () => {
expect(body, label).not.toContain('openspec new change "<name>"');

const newAvailabilityCheck = body.indexOf(
'first verify whether the expanded-profile `/opsx:new` workflow is available'
'first verify whether the optional `/opsx:new` workflow is available'
);
const newRecommendation = body.indexOf('recommend starting fresh with `/opsx:new`');
expect(newAvailabilityCheck, label).toBeGreaterThanOrEqual(0);
Expand Down
Loading