@fission-ai/openspec 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli/index.js +12 -2
- package/dist/commands/change.d.ts +24 -0
- package/dist/commands/change.js +229 -5
- package/dist/commands/schema.d.ts +5 -0
- package/dist/commands/schema.js +201 -35
- package/dist/commands/show.js +1 -1
- package/dist/commands/validate.d.ts +57 -0
- package/dist/commands/validate.js +99 -13
- package/dist/commands/workflow/index.d.ts +1 -1
- package/dist/commands/workflow/index.js +1 -1
- package/dist/commands/workflow/status.d.ts +2 -0
- package/dist/commands/workflow/status.js +76 -12
- package/dist/core/artifact-graph/resolver.js +15 -9
- package/dist/core/available-tools.js +16 -1
- package/dist/core/command-generation/adapters/antigravity.d.ts +1 -1
- package/dist/core/command-generation/adapters/antigravity.js +2 -2
- package/dist/core/command-generation/adapters/codeassistant.d.ts +15 -0
- package/dist/core/command-generation/adapters/codeassistant.js +29 -0
- package/dist/core/command-generation/adapters/index.d.ts +1 -0
- package/dist/core/command-generation/adapters/index.js +1 -0
- package/dist/core/command-generation/registry.js +2 -0
- package/dist/core/completions/command-registry.js +21 -0
- package/dist/core/completions/generators/fish-generator.d.ts +12 -0
- package/dist/core/completions/generators/fish-generator.js +79 -47
- package/dist/core/completions/templates/fish-templates.d.ts +1 -1
- package/dist/core/completions/templates/fish-templates.js +107 -10
- package/dist/core/completions/types.d.ts +4 -0
- package/dist/core/config.js +7 -1
- package/dist/core/init.d.ts +1 -0
- package/dist/core/init.js +71 -65
- package/dist/core/legacy-cleanup.js +6 -0
- package/dist/core/migration.js +42 -12
- package/dist/core/openspec-root.d.ts +1 -0
- package/dist/core/openspec-root.js +10 -2
- package/dist/core/parsers/requirement-blocks.d.ts +1 -0
- package/dist/core/parsers/requirement-blocks.js +2 -0
- package/dist/core/shared/ide-restart.d.ts +26 -0
- package/dist/core/shared/ide-restart.js +42 -0
- package/dist/core/shared/index.d.ts +1 -0
- package/dist/core/shared/index.js +1 -0
- package/dist/core/shared/tool-detection.js +9 -3
- package/dist/core/shared-skill-target.d.ts +13 -0
- package/dist/core/shared-skill-target.js +43 -1
- package/dist/core/specs-apply.js +22 -9
- package/dist/core/templates/workflows/explore.js +108 -73
- package/dist/core/templates/workflows/ff-change.js +8 -0
- package/dist/core/templates/workflows/propose.js +8 -0
- package/dist/core/update.js +28 -31
- package/dist/core/validation/constants.d.ts +3 -0
- package/dist/core/validation/constants.js +12 -0
- package/dist/core/validation/purpose-placeholder.d.ts +45 -0
- package/dist/core/validation/purpose-placeholder.js +117 -0
- package/dist/core/validation/validator.d.ts +12 -3
- package/dist/core/validation/validator.js +90 -4
- package/dist/utils/command-references.js +7 -8
- package/dist/utils/requirement-diff.d.ts +42 -0
- package/dist/utils/requirement-diff.js +61 -0
- package/package.json +4 -3
|
@@ -1,11 +1,32 @@
|
|
|
1
1
|
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
2
|
+
const PLANNING_GUIDANCE = `## Planning a Change
|
|
3
|
+
|
|
4
|
+
When the user is planning a change, guide them toward shared understanding with focused discovery questions. For open-ended discussion, follow the conversation without imposing an interview or a required output.
|
|
5
|
+
|
|
6
|
+
Before asking a factual question, follow the context discovery below and inspect relevant OpenSpec artifacts, source, tests, docs, and configuration. Do not ask the user to repeat facts you can verify. Summarize relevant findings without reproducing private context or rules. If evidence is missing, conflicting, or inaccessible, state that limitation and ask only for the clarification needed to proceed.
|
|
7
|
+
|
|
8
|
+
- **Follow dependencies** - Resolve the next blocking decision before its dependent details. For example, clarify the user's outcome and scope before choosing an API or data model. Revisit downstream assumptions when an earlier answer changes. Skip branches that do not matter to this goal.
|
|
9
|
+
- **Keep questions focused** - Ask one focused question at a time, and briefly explain why it matters and which decision it unlocks. Batch questions only if the user asks for a batch; keep them small and group related decisions.
|
|
10
|
+
- **Offer grounded recommendations** - When evidence supports a recommendation, state your preferred option and why it fits the user's goals, with alternatives and their tradeoffs when useful. Do not invent intent, priorities, or external constraints: ask the user when only they can answer. Avoid a fixed question format.
|
|
11
|
+
- **Keep a conversational record** - Track decisions in the conversation, not in files. Separate confirmed decisions from proposed defaults and unresolved questions. Silence is not acceptance. Accepting an answer or a batch of recommendations is not permission to write. Keep file-write confirmation separate from discovery questions and follow the guardrails below.
|
|
12
|
+
|
|
13
|
+
Stop asking when the user has enough clarity. Let them pause, pivot, or defer a decision; do not exhaust every branch or force a proposal.
|
|
14
|
+
|
|
15
|
+
For example, after inspecting the relevant code:
|
|
16
|
+
|
|
17
|
+
\`\`\`text
|
|
18
|
+
The CLI already uses SQLite and has no remote service. Is sharing state
|
|
19
|
+
across devices in scope? That determines whether local storage is enough.
|
|
20
|
+
If this stays a single-device tool, I recommend keeping SQLite to avoid
|
|
21
|
+
adding a service to operate; shared state would need a separate sync design.
|
|
22
|
+
\`\`\``;
|
|
2
23
|
export function getExploreSkillTemplate() {
|
|
3
24
|
return {
|
|
4
25
|
name: 'openspec-explore',
|
|
5
26
|
description: 'Enter explore mode - a thinking partner for exploring ideas, investigating problems, and clarifying requirements. Use when the user wants to think through something before or during a change.',
|
|
6
27
|
instructions: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
|
7
28
|
|
|
8
|
-
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code,
|
|
29
|
+
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. For a new change, scaffold it first as described below.
|
|
9
30
|
|
|
10
31
|
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
|
11
32
|
|
|
@@ -24,6 +45,10 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
24
45
|
|
|
25
46
|
---
|
|
26
47
|
|
|
48
|
+
${PLANNING_GUIDANCE}
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
27
52
|
## What You Might Do
|
|
28
53
|
|
|
29
54
|
Depending on what the user brings, you might:
|
|
@@ -48,22 +73,25 @@ Depending on what the user brings, you might:
|
|
|
48
73
|
|
|
49
74
|
**Visualize**
|
|
50
75
|
\`\`\`
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
+------------------------------------------+
|
|
77
|
+
| Use ASCII diagrams liberally |
|
|
78
|
+
+------------------------------------------+
|
|
79
|
+
| |
|
|
80
|
+
| [State A] -------> [State B] |
|
|
81
|
+
| | |
|
|
82
|
+
| v |
|
|
83
|
+
| [State C] |
|
|
84
|
+
| |
|
|
85
|
+
| System diagrams, state machines, |
|
|
86
|
+
| data flows, architecture sketches, |
|
|
87
|
+
| dependency graphs, comparison tables |
|
|
88
|
+
| |
|
|
89
|
+
+------------------------------------------+
|
|
65
90
|
\`\`\`
|
|
66
91
|
|
|
92
|
+
**Draw with plain ASCII only** — borders \`+\` \`-\` \`|\`, arrows \`-->\` \`<--\` \`^\` \`v\`, markers \`*\` \`x\`.
|
|
93
|
+
Unicode diagram glyphs can render at different widths across terminals, fonts, and locales, so padded boxes and aligned tables can drift. Keep every diagram character ASCII.
|
|
94
|
+
|
|
67
95
|
**Surface risks and unknowns**
|
|
68
96
|
- Identify what could go wrong
|
|
69
97
|
- Find gaps in understanding
|
|
@@ -164,17 +192,17 @@ User: I'm thinking about adding real-time collaboration
|
|
|
164
192
|
You: Real-time collab is a big space. Let me think about this...
|
|
165
193
|
|
|
166
194
|
COLLABORATION SPECTRUM
|
|
167
|
-
|
|
195
|
+
============================================
|
|
168
196
|
|
|
169
197
|
Awareness Coordination Sync
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
198
|
+
| | |
|
|
199
|
+
v v v
|
|
200
|
+
+--------+ +--------+ +--------+
|
|
201
|
+
|Presence| |Cursors | | CRDT |
|
|
202
|
+
| "3 | | Multi | |Conflict|
|
|
203
|
+
|online" | | select | | free |
|
|
204
|
+
+--------+ +--------+ +--------+
|
|
205
|
+
| | |
|
|
178
206
|
trivial moderate complex
|
|
179
207
|
|
|
180
208
|
Where's your head at?
|
|
@@ -186,27 +214,27 @@ User: The auth system is a mess
|
|
|
186
214
|
|
|
187
215
|
You: [reads codebase]
|
|
188
216
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
217
|
+
+---------------------------------------------+
|
|
218
|
+
| CURRENT AUTH FLOW |
|
|
219
|
+
+---------------------------------------------+
|
|
220
|
+
|
|
|
221
|
+
+-------------+-------------+
|
|
222
|
+
v v v
|
|
223
|
+
+---------+ +---------+ +---------+
|
|
224
|
+
| Google | | GitHub | | Email |
|
|
225
|
+
| OAuth | | OAuth | | Magic |
|
|
226
|
+
+----+----+ +----+----+ +----+----+
|
|
227
|
+
| | |
|
|
228
|
+
+-------------+-------------+
|
|
229
|
+
v
|
|
230
|
+
+-----------+
|
|
231
|
+
| Session |
|
|
232
|
+
+-----+-----+
|
|
233
|
+
|
|
|
234
|
+
v
|
|
235
|
+
+-----------+
|
|
236
|
+
| Perms |
|
|
237
|
+
+-----------+
|
|
210
238
|
|
|
211
239
|
I see three tangles. Which one's burning?
|
|
212
240
|
\`\`\`
|
|
@@ -238,19 +266,19 @@ User: A CLI tool that tracks local dev environments
|
|
|
238
266
|
|
|
239
267
|
You: That changes everything.
|
|
240
268
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
269
|
+
+-------------------------------------------------+
|
|
270
|
+
| CLI TOOL DATA STORAGE |
|
|
271
|
+
+-------------------------------------------------+
|
|
244
272
|
|
|
245
273
|
Key constraints:
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
274
|
+
- No daemon running
|
|
275
|
+
- Must work offline
|
|
276
|
+
- Single user
|
|
249
277
|
|
|
250
278
|
SQLite Postgres
|
|
251
|
-
Deployment embedded
|
|
252
|
-
Offline yes
|
|
253
|
-
Single file yes
|
|
279
|
+
Deployment embedded needs server
|
|
280
|
+
Offline yes no
|
|
281
|
+
Single file yes no
|
|
254
282
|
|
|
255
283
|
SQLite. Not even close.
|
|
256
284
|
|
|
@@ -290,11 +318,11 @@ But this summary is optional. Sometimes the thinking IS the value.
|
|
|
290
318
|
|
|
291
319
|
## Guardrails
|
|
292
320
|
|
|
293
|
-
- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing
|
|
321
|
+
- **Don't implement** - Never write code or implement features. Workflow configuration counts too: creating or editing schemas, templates, or \`openspec/config.yaml\` is a change, not thinking. Creating or updating OpenSpec change artifacts within the confirmed scope is fine, writing anything else is not.
|
|
294
322
|
- **Don't fake understanding** - If something is unclear, dig deeper
|
|
295
323
|
- **Don't rush** - Discovery is thinking time, not task time
|
|
296
324
|
- **Don't force structure** - Let patterns emerge naturally
|
|
297
|
-
- **Don't auto-capture** - Offer to save insights, don't just do it
|
|
325
|
+
- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including \`openspec new change\` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write.
|
|
298
326
|
- **Don't manually scaffold changes** - Never create a new change directory under \`openspec/changes/\` by hand. Always use \`openspec new change "<name>"\` (with \`--store <id>\` when applicable) so required metadata such as \`.openspec.yaml\` is created before writing artifacts.
|
|
299
327
|
- **Do visualize** - A good diagram is worth many paragraphs
|
|
300
328
|
- **Do explore the codebase** - Ground discussions in reality
|
|
@@ -312,7 +340,7 @@ export function getOpsxExploreCommandTemplate() {
|
|
|
312
340
|
tags: ['workflow', 'explore', 'experimental', 'thinking'],
|
|
313
341
|
content: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
|
314
342
|
|
|
315
|
-
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code,
|
|
343
|
+
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, investigate the codebase, and run read-only commands or tools without confirmation, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create or update OpenSpec change artifacts (proposals, designs, specs) within a confirmed scope—that's capturing thinking, not implementing. Answering design or clarifying questions is never consent to write. Before the first write-capable action, name the artifacts or files you would change and what you would do, ask a direct yes/no question, and wait for the user's confirmation in a separate message. Confirmation covers only the scope you described; ask again before expanding it. For a new change, scaffold it first as described below.
|
|
316
344
|
|
|
317
345
|
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
|
318
346
|
|
|
@@ -338,6 +366,10 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
338
366
|
|
|
339
367
|
---
|
|
340
368
|
|
|
369
|
+
${PLANNING_GUIDANCE}
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
341
373
|
## What You Might Do
|
|
342
374
|
|
|
343
375
|
Depending on what the user brings, you might:
|
|
@@ -362,22 +394,25 @@ Depending on what the user brings, you might:
|
|
|
362
394
|
|
|
363
395
|
**Visualize**
|
|
364
396
|
\`\`\`
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
397
|
+
+------------------------------------------+
|
|
398
|
+
| Use ASCII diagrams liberally |
|
|
399
|
+
+------------------------------------------+
|
|
400
|
+
| |
|
|
401
|
+
| [State A] -------> [State B] |
|
|
402
|
+
| | |
|
|
403
|
+
| v |
|
|
404
|
+
| [State C] |
|
|
405
|
+
| |
|
|
406
|
+
| System diagrams, state machines, |
|
|
407
|
+
| data flows, architecture sketches, |
|
|
408
|
+
| dependency graphs, comparison tables |
|
|
409
|
+
| |
|
|
410
|
+
+------------------------------------------+
|
|
379
411
|
\`\`\`
|
|
380
412
|
|
|
413
|
+
**Draw with plain ASCII only** — borders \`+\` \`-\` \`|\`, arrows \`-->\` \`<--\` \`^\` \`v\`, markers \`*\` \`x\`.
|
|
414
|
+
Unicode diagram glyphs can render at different widths across terminals, fonts, and locales, so padded boxes and aligned tables can drift. Keep every diagram character ASCII.
|
|
415
|
+
|
|
381
416
|
**Surface risks and unknowns**
|
|
382
417
|
- Identify what could go wrong
|
|
383
418
|
- Find gaps in understanding
|
|
@@ -486,11 +521,11 @@ When things crystallize, you might offer a summary - but it's optional. Sometime
|
|
|
486
521
|
|
|
487
522
|
## Guardrails
|
|
488
523
|
|
|
489
|
-
- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing
|
|
524
|
+
- **Don't implement** - Never write code or implement features. Workflow configuration counts too: creating or editing schemas, templates, or \`openspec/config.yaml\` is a change, not thinking. Creating or updating OpenSpec change artifacts within the confirmed scope is fine, writing anything else is not.
|
|
490
525
|
- **Don't fake understanding** - If something is unclear, dig deeper
|
|
491
526
|
- **Don't rush** - Discovery is thinking time, not task time
|
|
492
527
|
- **Don't force structure** - Let patterns emerge naturally
|
|
493
|
-
- **Don't auto-capture** - Offer to save insights, don't just do it
|
|
528
|
+
- **Don't auto-capture** - Offer to save insights, don't just do it. Read-only commands and tools need no confirmation. Before the first write-capable action—including \`openspec new change\` or another command that writes files—name the artifacts or files and proposed changes, ask a direct yes/no question, and wait for explicit confirmation in a separate user message. That confirmation covers only the described scope; ask again before expanding it. Answers to design or clarifying questions are never consent to write.
|
|
494
529
|
- **Don't manually scaffold changes** - Never create a new change directory under \`openspec/changes/\` by hand. Always use \`openspec new change "<name>"\` (with \`--store <id>\` when applicable) so required metadata such as \`.openspec.yaml\` is created before writing artifacts.
|
|
495
530
|
- **Do visualize** - A good diagram is worth many paragraphs
|
|
496
531
|
- **Do explore the codebase** - Ground discussions in reality
|
|
@@ -55,6 +55,10 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
55
55
|
- \`resolvedOutputPath\`: Resolved path or pattern to write the artifact
|
|
56
56
|
- \`dependencies\`: Completed artifacts to read for context
|
|
57
57
|
- Read any completed dependency files for context - always re-read them from disk, even if you saw them earlier in the conversation (the user may have edited them)
|
|
58
|
+
- **Inspect the relevant project before drafting**: Read \`context\` and \`rules\` first, then inspect relevant implementation, nearby tests, configuration, and documentation outside \`openspec/\`. Keep inspection read-only and proportional to the change; reuse findings for later artifacts and inspect more only as needed.
|
|
59
|
+
- Identify the target project from the request and project context; the planning home may be separate from the code. If the target is unclear, ask. For greenfield or non-code changes, inspect the available structure and relevant documents. If source is unavailable, state the limitation and ask when it materially affects the plan.
|
|
60
|
+
- Ground scope, approach, and tasks in what you find. Distinguish observed behavior from assumptions and proposed additions; surface conflicts with existing specs instead of silently deciding which is correct.
|
|
61
|
+
- Do this discovery now, rather than leaving generic "explore the codebase" or "make a plan" tasks for implementation. Keep any necessary follow-up investigation specific to an unresolved question.
|
|
58
62
|
- If the \`instruction\` field delegates creation to a specific skill or command, invoke it to produce the artifact instead of writing the file yourself, then verify the artifact file exists at \`resolvedOutputPath\`
|
|
59
63
|
- Otherwise create the artifact file using \`template\` as the structure and write it to \`resolvedOutputPath\`. If \`resolvedOutputPath\` is a glob, follow \`instruction\` to choose the concrete file path
|
|
60
64
|
- Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file
|
|
@@ -167,6 +171,10 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
167
171
|
- \`resolvedOutputPath\`: Resolved path or pattern to write the artifact
|
|
168
172
|
- \`dependencies\`: Completed artifacts to read for context
|
|
169
173
|
- Read any completed dependency files for context - always re-read them from disk, even if you saw them earlier in the conversation (the user may have edited them)
|
|
174
|
+
- **Inspect the relevant project before drafting**: Read \`context\` and \`rules\` first, then inspect relevant implementation, nearby tests, configuration, and documentation outside \`openspec/\`. Keep inspection read-only and proportional to the change; reuse findings for later artifacts and inspect more only as needed.
|
|
175
|
+
- Identify the target project from the request and project context; the planning home may be separate from the code. If the target is unclear, ask. For greenfield or non-code changes, inspect the available structure and relevant documents. If source is unavailable, state the limitation and ask when it materially affects the plan.
|
|
176
|
+
- Ground scope, approach, and tasks in what you find. Distinguish observed behavior from assumptions and proposed additions; surface conflicts with existing specs instead of silently deciding which is correct.
|
|
177
|
+
- Do this discovery now, rather than leaving generic "explore the codebase" or "make a plan" tasks for implementation. Keep any necessary follow-up investigation specific to an unresolved question.
|
|
170
178
|
- If the \`instruction\` field delegates creation to a specific skill or command, invoke it to produce the artifact instead of writing the file yourself, then verify the artifact file exists at \`resolvedOutputPath\`
|
|
171
179
|
- Otherwise create the artifact file using \`template\` as the structure and write it to \`resolvedOutputPath\`. If \`resolvedOutputPath\` is a glob, follow \`instruction\` to choose the concrete file path
|
|
172
180
|
- Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file
|
|
@@ -90,6 +90,10 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
90
90
|
- \`resolvedOutputPath\`: Resolved path or pattern to write the artifact
|
|
91
91
|
- \`dependencies\`: Completed artifacts to read for context
|
|
92
92
|
- Read any completed dependency files for context - always re-read them from disk, even if you saw them earlier in the conversation (the user may have edited them)
|
|
93
|
+
- **Inspect the relevant project before drafting**: Read \`context\` and \`rules\` first, then inspect relevant implementation, nearby tests, configuration, and documentation outside \`openspec/\`. Keep inspection read-only and proportional to the change; reuse findings for later artifacts and inspect more only as needed.
|
|
94
|
+
- Identify the target project from the request and project context; the planning home may be separate from the code. If the target is unclear, ask. For greenfield or non-code changes, inspect the available structure and relevant documents. If source is unavailable, state the limitation and ask when it materially affects the plan.
|
|
95
|
+
- Ground scope, approach, and tasks in what you find. Distinguish observed behavior from assumptions and proposed additions; surface conflicts with existing specs instead of silently deciding which is correct.
|
|
96
|
+
- Do this discovery now, rather than leaving generic "explore the codebase" or "make a plan" tasks for implementation. Keep any necessary follow-up investigation specific to an unresolved question.
|
|
93
97
|
- If the \`instruction\` field delegates creation to a specific skill or command, invoke it to produce the artifact instead of writing the file yourself, then verify the artifact file exists at \`resolvedOutputPath\`
|
|
94
98
|
- Otherwise create the artifact file using \`template\` as the structure and write it to \`resolvedOutputPath\`. If \`resolvedOutputPath\` is a glob, follow \`instruction\` to choose the concrete file path
|
|
95
99
|
- Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file
|
|
@@ -238,6 +242,10 @@ ${STORE_SELECTION_GUIDANCE}
|
|
|
238
242
|
- \`resolvedOutputPath\`: Resolved path or pattern to write the artifact
|
|
239
243
|
- \`dependencies\`: Completed artifacts to read for context
|
|
240
244
|
- Read any completed dependency files for context - always re-read them from disk, even if you saw them earlier in the conversation (the user may have edited them)
|
|
245
|
+
- **Inspect the relevant project before drafting**: Read \`context\` and \`rules\` first, then inspect relevant implementation, nearby tests, configuration, and documentation outside \`openspec/\`. Keep inspection read-only and proportional to the change; reuse findings for later artifacts and inspect more only as needed.
|
|
246
|
+
- Identify the target project from the request and project context; the planning home may be separate from the code. If the target is unclear, ask. For greenfield or non-code changes, inspect the available structure and relevant documents. If source is unavailable, state the limitation and ask when it materially affects the plan.
|
|
247
|
+
- Ground scope, approach, and tasks in what you find. Distinguish observed behavior from assumptions and proposed additions; surface conflicts with existing specs instead of silently deciding which is correct.
|
|
248
|
+
- Do this discovery now, rather than leaving generic "explore the codebase" or "make a plan" tasks for implementation. Keep any necessary follow-up investigation specific to an unresolved question.
|
|
241
249
|
- If the \`instruction\` field delegates creation to a specific skill or command, invoke it to produce the artifact instead of writing the file yourself, then verify the artifact file exists at \`resolvedOutputPath\`
|
|
242
250
|
- Otherwise create the artifact file using \`template\` as the structure and write it to \`resolvedOutputPath\`. If \`resolvedOutputPath\` is a glob, follow \`instruction\` to choose the concrete file path
|
|
243
251
|
- Apply \`context\` and \`rules\` as constraints - but do NOT copy them into the file
|
package/dist/core/update.js
CHANGED
|
@@ -13,7 +13,7 @@ import { FileSystemUtils } from '../utils/file-system.js';
|
|
|
13
13
|
import { getSkillReferenceTransformer, getTransformerForTool, transformToSkillReferences } from '../utils/command-references.js';
|
|
14
14
|
import { AI_TOOLS, OPENSPEC_DIR_NAME } from './config.js';
|
|
15
15
|
import { generateCommands, CommandAdapterRegistry, } from './command-generation/index.js';
|
|
16
|
-
import { getToolVersionStatus, getSkillTemplates, getCommandContents, generateSkillContent, getToolsWithSkillsDir, hasGlobalSkillTarget, resolveToolSkillsDir, toolSupportsSkills, } from './shared/index.js';
|
|
16
|
+
import { getToolVersionStatus, getSkillTemplates, getCommandContents, generateSkillContent, getToolsWithSkillsDir, hasGlobalSkillTarget, resolveToolSkillsDir, toolSupportsSkills, formatIdeRestart, } from './shared/index.js';
|
|
17
17
|
import { detectLegacyArtifacts, cleanupLegacyArtifacts, formatDeferredGlobalPromptSummary, formatCleanupSummary, formatDetectionSummary, getLegacyGlobalPromptMatches, getLegacyWorkflowIdsForTool, getToolsFromLegacyArtifacts, omitGlobalLegacyPromptFiles, omitToolLegacyArtifacts, pickGlobalLegacyPromptFiles, } from './legacy-cleanup.js';
|
|
18
18
|
import { isInteractive } from '../utils/interactive.js';
|
|
19
19
|
import { getGlobalConfig } from './global-config.js';
|
|
@@ -23,7 +23,7 @@ import { getAvailableTools } from './available-tools.js';
|
|
|
23
23
|
import { WORKFLOW_TO_SKILL_DIR, getConfiguredToolsForProfileSync, getToolsNeedingProfileSync, } from './profile-sync-drift.js';
|
|
24
24
|
import { scanInstalledWorkflows as scanInstalledWorkflowsShared, migrateIfNeeded as migrateIfNeededShared, findLegacyToolMigrations, migrateLegacyToolDirs, describeLegacyMigration, legacyMigrationNotice, keptInPlaceNotice, hasMovableContent, } from './migration.js';
|
|
25
25
|
import { resolveCommandSurfaceCapability, resolveCommandInvocation, shouldGenerateCommandsForTool, shouldGenerateSkillsForTool, shouldReconcileCommandFilesForTool, shouldRemoveSkillsForTool, } from './command-surface.js';
|
|
26
|
-
import { writeSharedSkillTarget,
|
|
26
|
+
import { resolveSharedSkillWriters, writeSharedSkillTarget, } from './shared-skill-target.js';
|
|
27
27
|
import { includesGitHubCopilot, writeCopilotCloudFiles, removeCopilotCloudFiles, isCopilotCloudEnabled, readCopilotCloudOptIn, findUnmanagedCloudFiles } from './github-copilot/cloud-agent.js';
|
|
28
28
|
const require = createRequire(import.meta.url);
|
|
29
29
|
const { version: OPENSPEC_VERSION } = require('../../package.json');
|
|
@@ -154,6 +154,9 @@ export class UpdateCommand {
|
|
|
154
154
|
const deliveryIncludesCommands = delivery !== 'skills';
|
|
155
155
|
// 10. Update tools (all if force, otherwise only those needing update)
|
|
156
156
|
const toolsToUpdate = this.force ? configuredTools : [...toolsToUpdateSet];
|
|
157
|
+
const sharedSkillWriters = resolveSharedSkillWriters(resolvedProjectPath, configuredAndNewTools
|
|
158
|
+
.map((toolId) => AI_TOOLS.find((tool) => tool.value === toolId))
|
|
159
|
+
.filter((tool) => tool !== undefined));
|
|
157
160
|
const updatedTools = [];
|
|
158
161
|
const updatedToolIds = [];
|
|
159
162
|
const failedTools = [];
|
|
@@ -173,11 +176,12 @@ export class UpdateCommand {
|
|
|
173
176
|
const skillsRoot = hasGlobalSkillTarget(tool) ? skillsDir : resolvedProjectPath;
|
|
174
177
|
const shouldGenerateSkills = shouldGenerateSkillsForTool(tool.value, delivery);
|
|
175
178
|
const shouldGenerateCommands = shouldGenerateCommandsForTool(tool.value, delivery);
|
|
179
|
+
const writesSkills = !tool.skillsDir || sharedSkillWriters.has(tool.value);
|
|
176
180
|
const toolWorkflows = legacyWorkflowOverrides[tool.value] ?? desiredWorkflows;
|
|
177
181
|
const skillTemplates = getSkillTemplates(toolWorkflows);
|
|
178
182
|
const commandContents = getCommandContents(toolWorkflows);
|
|
179
183
|
// Generate skill files if delivery includes skills
|
|
180
|
-
if (shouldGenerateSkills) {
|
|
184
|
+
if (shouldGenerateSkills && writesSkills) {
|
|
181
185
|
for (const { template, dirName } of skillTemplates) {
|
|
182
186
|
const skillDir = path.join(skillsDir, dirName);
|
|
183
187
|
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
@@ -190,7 +194,9 @@ export class UpdateCommand {
|
|
|
190
194
|
removedDeselectedSkillCount += await this.removeUnselectedSkillDirs(skillsRoot, skillsDir, toolWorkflows);
|
|
191
195
|
}
|
|
192
196
|
// Delete skill directories if delivery is commands-only
|
|
193
|
-
if (shouldRemoveSkillsForTool(tool.value, delivery) &&
|
|
197
|
+
if (shouldRemoveSkillsForTool(tool.value, delivery) &&
|
|
198
|
+
writesSkills &&
|
|
199
|
+
!hasGlobalSkillTarget(tool)) {
|
|
194
200
|
removedSkillCount += await this.removeSkillDirs(skillsRoot, skillsDir);
|
|
195
201
|
// Persist the selected owner even when commands-only delivery leaves
|
|
196
202
|
// this target with no generated skills.
|
|
@@ -321,14 +327,9 @@ export class UpdateCommand {
|
|
|
321
327
|
}
|
|
322
328
|
console.log();
|
|
323
329
|
const affectedToolIds = [...new Set([...newlyConfiguredTools, ...updatedToolIds])];
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
(shouldGenerateCommandsForTool(toolId, delivery) ||
|
|
328
|
-
shouldGenerateSkillsForTool(toolId, delivery)));
|
|
329
|
-
});
|
|
330
|
-
if (shouldRestartIde) {
|
|
331
|
-
console.log(chalk.dim('Restart your IDE for changes to take effect.'));
|
|
330
|
+
const restartHint = formatIdeRestart(affectedToolIds, delivery);
|
|
331
|
+
if (restartHint) {
|
|
332
|
+
console.log(chalk.dim(restartHint));
|
|
332
333
|
}
|
|
333
334
|
if (failedTools.length > 0) {
|
|
334
335
|
throw new Error(`OpenSpec update failed for: ${failedTools.map((tool) => tool.name).join(', ')}`);
|
|
@@ -803,6 +804,10 @@ export class UpdateCommand {
|
|
|
803
804
|
const newlyConfigured = [];
|
|
804
805
|
const skippedSharedSkillTools = [];
|
|
805
806
|
const workflowOverrides = {};
|
|
807
|
+
const arbitrationTools = [...new Set([...configuredTools, ...selectedTools])]
|
|
808
|
+
.map((toolId) => AI_TOOLS.find((tool) => tool.value === toolId))
|
|
809
|
+
.filter((tool) => tool !== undefined);
|
|
810
|
+
const sharedSkillWriters = resolveSharedSkillWriters(projectPath, arbitrationTools);
|
|
806
811
|
for (const toolId of selectedTools) {
|
|
807
812
|
const tool = AI_TOOLS.find((t) => t.value === toolId);
|
|
808
813
|
if (!tool || !toolSupportsSkills(tool))
|
|
@@ -813,6 +818,7 @@ export class UpdateCommand {
|
|
|
813
818
|
const skillsRoot = hasGlobalSkillTarget(tool) ? skillsDir : projectPath;
|
|
814
819
|
const shouldGenerateSkills = shouldGenerateSkillsForTool(tool.value, delivery);
|
|
815
820
|
const shouldGenerateCommands = shouldGenerateCommandsForTool(tool.value, delivery);
|
|
821
|
+
const writesSkills = !tool.skillsDir || sharedSkillWriters.has(tool.value);
|
|
816
822
|
const toolWorkflows = (tool.value === 'codex' && inferredCodexWorkflows.length > 0
|
|
817
823
|
? inferredCodexWorkflows
|
|
818
824
|
: desiredWorkflows);
|
|
@@ -821,32 +827,23 @@ export class UpdateCommand {
|
|
|
821
827
|
}
|
|
822
828
|
const skillTemplates = getSkillTemplates(toolWorkflows);
|
|
823
829
|
const commandContents = getCommandContents(toolWorkflows);
|
|
824
|
-
//
|
|
825
|
-
//
|
|
826
|
-
//
|
|
827
|
-
//
|
|
828
|
-
//
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
// Skipping here means the tool is never recorded as configured, so a
|
|
833
|
-
// persistent legacy signal re-offers it on later runs. Because no
|
|
834
|
-
// replacement is written, this tool is also exempted from immediate
|
|
835
|
-
// legacy cleanup (see skippedSharedSkillTools) — otherwise a repo-local
|
|
836
|
-
// `.codex/prompts` would be deleted with nothing put in its place. That
|
|
837
|
-
// repeat is idempotent and harmless — the alternative is the silent
|
|
838
|
-
// hijack this prevents.
|
|
839
|
-
const sharedOwner = shouldGenerateSkills
|
|
840
|
-
? sharedSkillRootOwner(projectPath, tool.value)
|
|
830
|
+
// Never overwrite a shared skills root owned by another tool. A tool
|
|
831
|
+
// with its own command surface can still install those commands: this
|
|
832
|
+
// is how a legacy Antigravity install gains `.agents/workflows` beside
|
|
833
|
+
// Codex-owned `.agents/skills`. A skills-only tool has no safe artifact
|
|
834
|
+
// to install, so preserve its legacy files and re-offer it later.
|
|
835
|
+
const sharedOwner = shouldGenerateSkills && !writesSkills
|
|
836
|
+
? arbitrationTools.find((candidate) => candidate.skillsDir === tool.skillsDir &&
|
|
837
|
+
sharedSkillWriters.has(candidate.value))?.value
|
|
841
838
|
: undefined;
|
|
842
|
-
if (sharedOwner) {
|
|
839
|
+
if (sharedOwner && !shouldGenerateCommands) {
|
|
843
840
|
const ownerName = AI_TOOLS.find((candidate) => candidate.value === sharedOwner)?.name ?? sharedOwner;
|
|
844
841
|
spinner.info(`Skipped ${tool.name}: ${tool.skillsDir}/skills is already managed by another tool (${ownerName}).`);
|
|
845
842
|
skippedSharedSkillTools.push(tool.value);
|
|
846
843
|
continue;
|
|
847
844
|
}
|
|
848
845
|
// Create skill files when delivery includes skills
|
|
849
|
-
if (shouldGenerateSkills) {
|
|
846
|
+
if (shouldGenerateSkills && writesSkills) {
|
|
850
847
|
for (const { template, dirName } of skillTemplates) {
|
|
851
848
|
const skillDir = path.join(skillsDir, dirName);
|
|
852
849
|
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
@@ -6,6 +6,8 @@ export declare const MIN_PURPOSE_LENGTH = 50;
|
|
|
6
6
|
export declare const MAX_WHY_SECTION_LENGTH = 1000;
|
|
7
7
|
export declare const MAX_REQUIREMENT_TEXT_LENGTH = 500;
|
|
8
8
|
export declare const MAX_DELTAS_PER_CHANGE = 10;
|
|
9
|
+
export declare const PURPOSE_PLACEHOLDER_PREFIX = "TBD - created by archiving change ";
|
|
10
|
+
export declare const PURPOSE_PLACEHOLDER_SUFFIX = ". Update Purpose after archive.";
|
|
9
11
|
export declare const VALIDATION_MESSAGES: {
|
|
10
12
|
readonly SCENARIO_EMPTY: "Scenario text cannot be empty";
|
|
11
13
|
readonly REQUIREMENT_EMPTY: "Requirement text cannot be empty";
|
|
@@ -26,6 +28,7 @@ export declare const VALIDATION_MESSAGES: {
|
|
|
26
28
|
readonly DELTA_SPEC_EMPTY: "Spec name cannot be empty";
|
|
27
29
|
readonly DELTA_DESCRIPTION_EMPTY: "Delta description cannot be empty";
|
|
28
30
|
readonly PURPOSE_TOO_BRIEF: "Purpose section is too brief (less than 50 characters)";
|
|
31
|
+
readonly PURPOSE_IS_PLACEHOLDER: string;
|
|
29
32
|
readonly REQUIREMENT_TOO_LONG: "Requirement text is very long (>500 characters). Consider breaking it down.";
|
|
30
33
|
readonly DELTA_DESCRIPTION_TOO_BRIEF: "Delta description is too brief";
|
|
31
34
|
readonly DELTA_MISSING_REQUIREMENTS: "Delta should include requirements";
|
|
@@ -8,6 +8,14 @@ export const MIN_PURPOSE_LENGTH = 50;
|
|
|
8
8
|
export const MAX_WHY_SECTION_LENGTH = 1000;
|
|
9
9
|
export const MAX_REQUIREMENT_TEXT_LENGTH = 500;
|
|
10
10
|
export const MAX_DELTAS_PER_CHANGE = 10;
|
|
11
|
+
// The Purpose `openspec archive` writes into a main spec it creates when the
|
|
12
|
+
// delta introduced the capability without a usable `## Purpose`. Named here, and
|
|
13
|
+
// composed from these two halves at the write site, so validation recognises the
|
|
14
|
+
// placeholder through the same definition that produces it: a second, hand-copied
|
|
15
|
+
// spelling would stop matching the day the wording changed, and a check that
|
|
16
|
+
// matches nothing looks exactly like a check that found nothing.
|
|
17
|
+
export const PURPOSE_PLACEHOLDER_PREFIX = 'TBD - created by archiving change ';
|
|
18
|
+
export const PURPOSE_PLACEHOLDER_SUFFIX = '. Update Purpose after archive.';
|
|
11
19
|
// Validation messages
|
|
12
20
|
export const VALIDATION_MESSAGES = {
|
|
13
21
|
// Required content
|
|
@@ -31,6 +39,10 @@ export const VALIDATION_MESSAGES = {
|
|
|
31
39
|
DELTA_DESCRIPTION_EMPTY: 'Delta description cannot be empty',
|
|
32
40
|
// Warnings
|
|
33
41
|
PURPOSE_TOO_BRIEF: `Purpose section is too brief (less than ${MIN_PURPOSE_LENGTH} characters)`,
|
|
42
|
+
PURPOSE_IS_PLACEHOLDER: 'Purpose section is still a placeholder rather than a Purpose anyone wrote (the sentence `openspec archive` ' +
|
|
43
|
+
'writes for a new capability, or a `TBD`/`TODO` marker left in its place). Replace it with what this ' +
|
|
44
|
+
'capability is for, editing the main spec directly: a `## Purpose` in a delta is read only when the ' +
|
|
45
|
+
'capability is created, so it cannot replace this one.',
|
|
34
46
|
REQUIREMENT_TOO_LONG: `Requirement text is very long (>${MAX_REQUIREMENT_TEXT_LENGTH} characters). Consider breaking it down.`,
|
|
35
47
|
DELTA_DESCRIPTION_TOO_BRIEF: 'Delta description is too brief',
|
|
36
48
|
DELTA_MISSING_REQUIREMENTS: 'Delta should include requirements',
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects a `## Purpose` that is still a placeholder, rather than one somebody
|
|
3
|
+
* wrote.
|
|
4
|
+
*
|
|
5
|
+
* When a delta introduces a capability with no usable `## Purpose`, archive
|
|
6
|
+
* stamps the placeholder into the new main spec. That text is over
|
|
7
|
+
* `MIN_PURPOSE_LENGTH`, so the brevity check cannot reach it: the one rule that
|
|
8
|
+
* exists to catch a Purpose nobody wrote is satisfied by the exact string
|
|
9
|
+
* meaning nobody wrote one. Nothing else reads it afterwards, so the capability
|
|
10
|
+
* keeps a to-do in it while every command reports success.
|
|
11
|
+
*
|
|
12
|
+
* Two things count, and deliberately nothing else:
|
|
13
|
+
*
|
|
14
|
+
* - the placeholder this tool generates, recognised through the same constants
|
|
15
|
+
* the writer composes it from, wherever it sits in the Purpose - nobody types
|
|
16
|
+
* that sentence by accident;
|
|
17
|
+
* - a `TBD` or `TODO` **opening** the Purpose, which is the marker left behind
|
|
18
|
+
* when someone is told to leave "a brief TBD placeholder" and never comes
|
|
19
|
+
* back. Which of the two words got typed says nothing about whether the
|
|
20
|
+
* Purpose was written, so both are read the same way.
|
|
21
|
+
*
|
|
22
|
+
* A marker inside a sentence is left alone. "The retry budget is TBD pending
|
|
23
|
+
* benchmarks" is a real Purpose with an open question in it, and reporting it
|
|
24
|
+
* would teach people to ignore the warning - which costs more than the findings
|
|
25
|
+
* it would add.
|
|
26
|
+
*
|
|
27
|
+
* Fenced code inside the Purpose is quoted material rather than the Purpose
|
|
28
|
+
* speaking, so it is read out first. Without that, a Purpose documenting the
|
|
29
|
+
* sentence archive writes is reported as being that sentence: a document about
|
|
30
|
+
* the placeholder, failing for carrying one.
|
|
31
|
+
*/
|
|
32
|
+
export interface PurposePlaceholderIssue {
|
|
33
|
+
/** 1-based line of the placeholder text, when it can be located. */
|
|
34
|
+
line?: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reports the Purpose of a main spec as an unwritten placeholder, or null when
|
|
38
|
+
* it reads as authored content.
|
|
39
|
+
*
|
|
40
|
+
* An empty Purpose is not reported here - `SPEC_PURPOSE_EMPTY` already covers
|
|
41
|
+
* it, and reporting both would put two findings on one line. That falls out of
|
|
42
|
+
* the two rules rather than needing a case of its own.
|
|
43
|
+
*/
|
|
44
|
+
export declare function findPurposePlaceholderIssue(overview: string, content?: string): PurposePlaceholderIssue | null;
|
|
45
|
+
//# sourceMappingURL=purpose-placeholder.d.ts.map
|