@fission-ai/openspec 1.9.0 → 1.11.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.
Files changed (72) hide show
  1. package/dist/cli/index.d.ts +17 -0
  2. package/dist/cli/index.js +60 -9
  3. package/dist/commands/change.d.ts +24 -0
  4. package/dist/commands/change.js +223 -0
  5. package/dist/commands/config.js +14 -3
  6. package/dist/commands/feedback.js +28 -7
  7. package/dist/commands/schema.d.ts +5 -0
  8. package/dist/commands/schema.js +201 -35
  9. package/dist/commands/show.js +1 -1
  10. package/dist/commands/workflow/index.d.ts +1 -1
  11. package/dist/commands/workflow/index.js +1 -1
  12. package/dist/commands/workflow/status.d.ts +2 -0
  13. package/dist/commands/workflow/status.js +76 -12
  14. package/dist/core/archive.js +64 -7
  15. package/dist/core/artifact-graph/instruction-loader.js +2 -7
  16. package/dist/core/artifact-graph/outputs.d.ts +4 -0
  17. package/dist/core/artifact-graph/outputs.js +7 -0
  18. package/dist/core/artifact-graph/resolver.js +15 -9
  19. package/dist/core/available-tools.js +16 -1
  20. package/dist/core/command-generation/adapters/antigravity.d.ts +1 -1
  21. package/dist/core/command-generation/adapters/antigravity.js +2 -2
  22. package/dist/core/command-generation/adapters/opencode.d.ts +2 -1
  23. package/dist/core/command-generation/adapters/opencode.js +13 -2
  24. package/dist/core/completion-tip.d.ts +15 -0
  25. package/dist/core/completion-tip.js +133 -0
  26. package/dist/core/completions/command-registry.js +20 -0
  27. package/dist/core/completions/factory.d.ts +11 -0
  28. package/dist/core/completions/generators/fish-generator.d.ts +12 -0
  29. package/dist/core/completions/generators/fish-generator.js +79 -47
  30. package/dist/core/completions/installers/bash-installer.d.ts +7 -0
  31. package/dist/core/completions/installers/bash-installer.js +15 -0
  32. package/dist/core/completions/installers/fish-installer.d.ts +7 -0
  33. package/dist/core/completions/installers/fish-installer.js +15 -0
  34. package/dist/core/completions/installers/powershell-installer.d.ts +7 -0
  35. package/dist/core/completions/installers/powershell-installer.js +15 -0
  36. package/dist/core/completions/templates/fish-templates.d.ts +1 -1
  37. package/dist/core/completions/templates/fish-templates.js +107 -10
  38. package/dist/core/completions/types.d.ts +4 -0
  39. package/dist/core/config-prompts.js +19 -10
  40. package/dist/core/config-schema.d.ts +1 -0
  41. package/dist/core/config-schema.js +2 -0
  42. package/dist/core/config.js +7 -1
  43. package/dist/core/global-config.d.ts +2 -0
  44. package/dist/core/init.d.ts +5 -0
  45. package/dist/core/init.js +117 -14
  46. package/dist/core/legacy-cleanup.js +6 -0
  47. package/dist/core/migration.js +42 -12
  48. package/dist/core/parsers/requirement-blocks.d.ts +1 -0
  49. package/dist/core/parsers/requirement-blocks.js +2 -0
  50. package/dist/core/profiles.d.ts +1 -1
  51. package/dist/core/profiles.js +11 -2
  52. package/dist/core/shared/tool-detection.js +9 -3
  53. package/dist/core/shared-skill-target.d.ts +13 -0
  54. package/dist/core/shared-skill-target.js +43 -1
  55. package/dist/core/specs-apply.js +16 -8
  56. package/dist/core/templates/workflows/explore.js +79 -73
  57. package/dist/core/templates/workflows/feedback.js +1 -0
  58. package/dist/core/templates/workflows/onboard.js +4 -4
  59. package/dist/core/update.js +39 -26
  60. package/dist/core/validation/constants.d.ts +3 -0
  61. package/dist/core/validation/constants.js +12 -0
  62. package/dist/core/validation/purpose-placeholder.d.ts +45 -0
  63. package/dist/core/validation/purpose-placeholder.js +117 -0
  64. package/dist/core/validation/validator.js +16 -1
  65. package/dist/telemetry/index.js +3 -2
  66. package/dist/utils/change-metadata.js +17 -13
  67. package/dist/utils/change-utils.js +5 -0
  68. package/dist/utils/requirement-diff.d.ts +42 -0
  69. package/dist/utils/requirement-diff.js +61 -0
  70. package/package.json +4 -6
  71. package/schemas/spec-driven/schema.yaml +17 -7
  72. package/scripts/postinstall.js +0 -83
@@ -115,12 +115,14 @@ export function parseDeltaSpec(content) {
115
115
  sink: skippedHeaders,
116
116
  });
117
117
  const removedNames = parseRemovedNames(removedLookup.body);
118
+ const removedBlocks = parseRequirementBlocksFromSection(removedLookup.body);
118
119
  const renamedPairs = parseRenamedPairs(renamedLookup.body);
119
120
  skippedHeaders.sort((a, b) => a.line - b.line);
120
121
  return {
121
122
  added,
122
123
  modified,
123
124
  removed: removedNames,
125
+ removedBlocks,
124
126
  renamed: renamedPairs,
125
127
  skippedHeaders,
126
128
  sectionPresence: {
@@ -20,7 +20,7 @@ export type CoreWorkflowId = (typeof CORE_WORKFLOWS)[number];
20
20
  * Resolves which workflows should be active for a given profile configuration.
21
21
  *
22
22
  * - 'core' profile always returns CORE_WORKFLOWS
23
- * - 'custom' profile returns the provided customWorkflows, or empty array if not provided
23
+ * - 'custom' profile returns the provided customWorkflows and required dependencies
24
24
  */
25
25
  export declare function getProfileWorkflows(profile: Profile, customWorkflows?: string[]): readonly string[];
26
26
  //# sourceMappingURL=profiles.d.ts.map
@@ -30,11 +30,20 @@ export const ALL_WORKFLOWS = [
30
30
  * Resolves which workflows should be active for a given profile configuration.
31
31
  *
32
32
  * - 'core' profile always returns CORE_WORKFLOWS
33
- * - 'custom' profile returns the provided customWorkflows, or empty array if not provided
33
+ * - 'custom' profile returns the provided customWorkflows and required dependencies
34
34
  */
35
35
  export function getProfileWorkflows(profile, customWorkflows) {
36
36
  if (profile === 'custom') {
37
- return customWorkflows ?? [];
37
+ const workflows = customWorkflows ?? [];
38
+ const syncDependentIndex = workflows.findIndex((workflow) => workflow === 'archive' || workflow === 'bulk-archive');
39
+ if (syncDependentIndex !== -1 && !workflows.includes('sync')) {
40
+ return [
41
+ ...workflows.slice(0, syncDependentIndex),
42
+ 'sync',
43
+ ...workflows.slice(syncDependentIndex),
44
+ ];
45
+ }
46
+ return workflows;
38
47
  }
39
48
  return CORE_WORKFLOWS;
40
49
  }
@@ -10,8 +10,8 @@ import { CommandAdapterRegistry, generateCommands } from '../command-generation/
10
10
  import { getCommandContents } from './skill-generation.js';
11
11
  import { getGlobalConfig } from '../global-config.js';
12
12
  import { getProfileWorkflows, ALL_WORKFLOWS } from '../profiles.js';
13
- import { isSharedSkillTargetActive, readSharedSkillTarget, reconcileSharedSkillTargets, } from '../shared-skill-target.js';
14
- import { shouldGenerateCommandsForTool, shouldGenerateSkillsForTool, } from '../command-surface.js';
13
+ import { isSharedSkillTargetActive, hasLegacySkills, readSharedSkillTarget, reconcileSharedSkillTargets, } from '../shared-skill-target.js';
14
+ import { shouldGenerateCommandsForTool, shouldGenerateSkillsForTool, resolveCommandSurfaceCapability, } from '../command-surface.js';
15
15
  import { getSkillCapableTools, resolveToolSkillsDir, toolSupportsSkills, } from './skill-paths.js';
16
16
  /**
17
17
  * Names of skill directories created by openspec init.
@@ -272,12 +272,18 @@ export function getConfiguredTools(projectRoot) {
272
272
  return false;
273
273
  return (getToolSkillStatus(projectRoot, t.value).configured ||
274
274
  toolHasAnyConfiguredCommand(projectRoot, t.value) ||
275
+ (resolveCommandSurfaceCapability(t.value) === 'adapter-backed' &&
276
+ hasLegacySkills(projectRoot, t)) ||
275
277
  (Boolean(t.skillsDir) &&
276
278
  readSharedSkillTarget(projectRoot, t.skillsDir) === t.value));
277
279
  });
278
280
  const activeProjectTools = new Set(reconcileSharedSkillTargets(projectRoot, configured.filter((tool) => tool.skillsDir)).map((tool) => tool.value));
279
281
  return configured
280
- .filter((tool) => tool.globalSkillsDir || activeProjectTools.has(tool.value))
282
+ .filter((tool) => tool.globalSkillsDir ||
283
+ toolHasAnyConfiguredCommand(projectRoot, tool.value) ||
284
+ (resolveCommandSurfaceCapability(tool.value) === 'adapter-backed' &&
285
+ hasLegacySkills(projectRoot, tool)) ||
286
+ activeProjectTools.has(tool.value))
281
287
  .map((tool) => tool.value);
282
288
  }
283
289
  /**
@@ -1,6 +1,19 @@
1
1
  import { type AIToolOption } from './config.js';
2
2
  /** Reads a valid-looking marker value without letting linked roots escape. */
3
3
  export declare function readSharedSkillTarget(projectPath: string, skillsDir: string): string | undefined;
4
+ /** Whether a tool still has an allowlisted managed skill under an old root. */
5
+ export declare function hasLegacySkills(projectPath: string, tool: AIToolOption): boolean;
6
+ /**
7
+ * Chooses the one selected tool that may render each physical skills root.
8
+ * Command files are not part of this arbitration: tools that share a skills
9
+ * root can still write their own command surface elsewhere under that root.
10
+ *
11
+ * Existing compatible owners win. On a new root, prefer a skills-native
12
+ * renderer over an adapter-backed renderer because its references remain
13
+ * usable by every consumer of the shared tree. Codex is the best fresh owner
14
+ * because its renderer includes both Codex and generic skill invocation forms.
15
+ */
16
+ export declare function resolveSharedSkillWriters(projectPath: string, tools: AIToolOption[]): Set<string>;
4
17
  /**
5
18
  * A shared skill root can only hold one rendered variant of each skill.
6
19
  * Keep the writer recorded so later updates do not infer every tool that
@@ -2,6 +2,7 @@ import path from 'path';
2
2
  import * as fs from 'fs';
3
3
  import { AI_TOOLS, OPENSPEC_SKILL_NAMES } from './config.js';
4
4
  import { FileSystemUtils } from '../utils/file-system.js';
5
+ import { resolveCommandSurfaceCapability } from './command-surface.js';
5
6
  const TARGET_MARKER = '.openspec-target';
6
7
  /** Returns the ownership-marker path for one shared skills root. */
7
8
  function markerPath(projectPath, skillsDir) {
@@ -19,7 +20,7 @@ export function readSharedSkillTarget(projectPath, skillsDir) {
19
20
  }
20
21
  }
21
22
  /** Whether a tool still has an allowlisted managed skill under an old root. */
22
- function hasLegacySkills(projectPath, tool) {
23
+ export function hasLegacySkills(projectPath, tool) {
23
24
  return (tool.legacySkillsDirs ?? []).some((root) => {
24
25
  const skillsDir = path.join(projectPath, root, 'skills');
25
26
  return OPENSPEC_SKILL_NAMES.some((skillName) => {
@@ -69,6 +70,47 @@ function hasCurrentSkills(projectPath, skillsDir) {
69
70
  }
70
71
  });
71
72
  }
73
+ /**
74
+ * Chooses the one selected tool that may render each physical skills root.
75
+ * Command files are not part of this arbitration: tools that share a skills
76
+ * root can still write their own command surface elsewhere under that root.
77
+ *
78
+ * Existing compatible owners win. On a new root, prefer a skills-native
79
+ * renderer over an adapter-backed renderer because its references remain
80
+ * usable by every consumer of the shared tree. Codex is the best fresh owner
81
+ * because its renderer includes both Codex and generic skill invocation forms.
82
+ */
83
+ export function resolveSharedSkillWriters(projectPath, tools) {
84
+ const byRoot = new Map();
85
+ const writers = new Set();
86
+ for (const tool of tools) {
87
+ if (!tool.skillsDir)
88
+ continue;
89
+ const group = byRoot.get(tool.skillsDir) ?? [];
90
+ group.push(tool);
91
+ byRoot.set(tool.skillsDir, group);
92
+ }
93
+ for (const [root, unsortedGroup] of byRoot) {
94
+ const group = [...unsortedGroup].sort((left, right) => AI_TOOLS.indexOf(left) - AI_TOOLS.indexOf(right));
95
+ if (group.length === 1) {
96
+ writers.add(group[0].value);
97
+ continue;
98
+ }
99
+ const skillsNative = group.filter((tool) => resolveCommandSurfaceCapability(tool.value) !== 'adapter-backed');
100
+ const preferredPool = skillsNative.length > 0 ? skillsNative : group;
101
+ const marked = readSharedSkillTarget(projectPath, root);
102
+ const inferred = inferSharedSkillTarget(projectPath, root);
103
+ const owner = preferredPool.find((tool) => tool.value === marked) ??
104
+ preferredPool.find((tool) => tool.value === inferred) ??
105
+ (hasCurrentSkills(projectPath, root)
106
+ ? preferredPool.find((tool) => tool.value === 'agents')
107
+ : undefined) ??
108
+ preferredPool.find((tool) => tool.value === 'codex') ??
109
+ preferredPool[0];
110
+ writers.add(owner.value);
111
+ }
112
+ return writers;
113
+ }
72
114
  /**
73
115
  * A shared skill root can only hold one rendered variant of each skill.
74
116
  * Keep the writer recorded so later updates do not infer every tool that
@@ -12,7 +12,7 @@ import { extractRequirementsSection, findMissingCurrentScenarios, foldRequiremen
12
12
  import { findMainSpecStructureIssues } from './parsers/spec-structure.js';
13
13
  import { buildCodeFenceMask } from './parsers/code-fence.js';
14
14
  import { MarkdownParser } from './parsers/markdown-parser.js';
15
- import { MIN_PURPOSE_LENGTH } from './validation/constants.js';
15
+ import { MIN_PURPOSE_LENGTH, PURPOSE_PLACEHOLDER_PREFIX, PURPOSE_PLACEHOLDER_SUFFIX, } from './validation/constants.js';
16
16
  import { discoverSpecFiles } from '../utils/spec-discovery.js';
17
17
  import { FileSystemUtils } from '../utils/file-system.js';
18
18
  function isLexicallyWithin(allowedDirectory, targetPath) {
@@ -254,10 +254,12 @@ export async function buildUpdatedSpec(update, changeName, options = {}) {
254
254
  for (const block of parts.bodyBlocks) {
255
255
  nameToBlock.set(normalizeRequirementName(block.name), block);
256
256
  }
257
+ // Keep source blocks immutable for loss attribution. This parallel key list
258
+ // carries only positional identity as renames change lookup keys.
259
+ const orderedKeys = parts.bodyBlocks.map((block) => normalizeRequirementName(block.name));
257
260
  // Apply operations in order: RENAMED → REMOVED → MODIFIED → ADDED
258
261
  // RENAMED
259
262
  let renamedApplied = 0;
260
- const renamedTargets = new Map();
261
263
  for (const r of plan.renamed) {
262
264
  const from = normalizeRequirementName(r.from);
263
265
  const to = normalizeRequirementName(r.to);
@@ -291,7 +293,12 @@ export async function buildUpdatedSpec(update, changeName, options = {}) {
291
293
  };
292
294
  nameToBlock.delete(from);
293
295
  nameToBlock.set(to, renamedBlock);
294
- renamedTargets.set(from, to);
296
+ // A Map delete+set moves the renamed block to insertion-order tail. Carry
297
+ // its new key in the source slot instead; chained renames update it again.
298
+ const orderIndex = orderedKeys.indexOf(from);
299
+ if (orderIndex >= 0) {
300
+ orderedKeys[orderIndex] = to;
301
+ }
295
302
  renamedApplied++;
296
303
  }
297
304
  // REMOVED
@@ -364,8 +371,9 @@ export async function buildUpdatedSpec(update, changeName, options = {}) {
364
371
  // Recompose requirements section preserving original ordering where possible
365
372
  const keptOrder = [];
366
373
  const seen = new Set();
367
- for (const block of parts.bodyBlocks) {
368
- const key = normalizeRequirementName(block.name);
374
+ for (let index = 0; index < parts.bodyBlocks.length; index++) {
375
+ const block = parts.bodyBlocks[index];
376
+ const key = orderedKeys[index];
369
377
  const replacement = nameToBlock.get(key);
370
378
  if (replacement) {
371
379
  keptOrder.push(replacement);
@@ -377,8 +385,7 @@ export async function buildUpdatedSpec(update, changeName, options = {}) {
377
385
  // full absorbed suffix. RENAMED carries the original raw content under a
378
386
  // new map key, and MODIFIED may repeat the suffix deliberately; neither is
379
387
  // data loss.
380
- const renamedTarget = renamedTargets.get(key);
381
- const replacementFromOriginal = replacement ?? (renamedTarget ? nameToBlock.get(renamedTarget) : undefined);
388
+ const replacementFromOriginal = replacement;
382
389
  if (replacementFromOriginal !== block) {
383
390
  const foreign = firstForeignTail(block.raw);
384
391
  const replacementRaw = replacementFromOriginal?.raw;
@@ -917,7 +924,8 @@ function readableOverview(skeleton, specName) {
917
924
  */
918
925
  export function buildSpecSkeleton(specFolderName, changeName, purpose) {
919
926
  const titleBase = specFolderName;
920
- const purposeBody = purpose?.trim() || `TBD - created by archiving change ${changeName}. Update Purpose after archive.`;
927
+ const purposeBody = purpose?.trim() ||
928
+ `${PURPOSE_PLACEHOLDER_PREFIX}${changeName}${PURPOSE_PLACEHOLDER_SUFFIX}`;
921
929
  return `# ${titleBase} Specification\n\n## Purpose\n${purposeBody}\n\n## Requirements\n`;
922
930
  }
923
931
  //# sourceMappingURL=specs-apply.js.map
@@ -5,7 +5,7 @@ export function getExploreSkillTemplate() {
5
5
  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
6
  instructions: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
7
7
 
8
- **IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, 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 OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing. For a new change, scaffold it first as described below.
8
+ **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
9
 
10
10
  **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
11
 
@@ -48,22 +48,25 @@ Depending on what the user brings, you might:
48
48
 
49
49
  **Visualize**
50
50
  \`\`\`
51
- ┌─────────────────────────────────────────┐
52
- │ Use ASCII diagrams liberally │
53
- ├─────────────────────────────────────────┤
54
- │ │
55
- │ ┌────────┐ ┌────────┐ │
56
- │ │ State │────────▶│ State │ │
57
- │ │ A │ │ B │ │
58
- │ └────────┘ └────────┘ │
59
- │ │
60
- │ System diagrams, state machines, │
61
- │ data flows, architecture sketches, │
62
- │ dependency graphs, comparison tables │
63
- │ │
64
- └─────────────────────────────────────────┘
51
+ +------------------------------------------+
52
+ | Use ASCII diagrams liberally |
53
+ +------------------------------------------+
54
+ | |
55
+ | [State A] -------> [State B] |
56
+ | | |
57
+ | v |
58
+ | [State C] |
59
+ | |
60
+ | System diagrams, state machines, |
61
+ | data flows, architecture sketches, |
62
+ | dependency graphs, comparison tables |
63
+ | |
64
+ +------------------------------------------+
65
65
  \`\`\`
66
66
 
67
+ **Draw with plain ASCII only** — borders \`+\` \`-\` \`|\`, arrows \`-->\` \`<--\` \`^\` \`v\`, markers \`*\` \`x\`.
68
+ 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.
69
+
67
70
  **Surface risks and unknowns**
68
71
  - Identify what could go wrong
69
72
  - Find gaps in understanding
@@ -164,17 +167,17 @@ User: I'm thinking about adding real-time collaboration
164
167
  You: Real-time collab is a big space. Let me think about this...
165
168
 
166
169
  COLLABORATION SPECTRUM
167
- ════════════════════════════════════════════
170
+ ============================================
168
171
 
169
172
  Awareness Coordination Sync
170
- │ │ │
171
- ▼ ▼ ▼
172
- ┌────────┐ ┌────────┐ ┌────────┐
173
- │Presence│ │Cursors │ │ CRDT │
174
- │ "3 │ │ Multi │ │Conflict│
175
- │online" │ │ select │ │ free │
176
- └────────┘ └────────┘ └────────┘
177
- │ │ │
173
+ | | |
174
+ v v v
175
+ +--------+ +--------+ +--------+
176
+ |Presence| |Cursors | | CRDT |
177
+ | "3 | | Multi | |Conflict|
178
+ |online" | | select | | free |
179
+ +--------+ +--------+ +--------+
180
+ | | |
178
181
  trivial moderate complex
179
182
 
180
183
  Where's your head at?
@@ -186,27 +189,27 @@ User: The auth system is a mess
186
189
 
187
190
  You: [reads codebase]
188
191
 
189
- ┌─────────────────────────────────────────────┐
190
- │ CURRENT AUTH FLOW │
191
- └─────────────────────────────────────────────┘
192
- │
193
- ┌─────────────┼─────────────┐
194
- ▼ ▼ ▼
195
- ┌─────────┐ ┌─────────┐ ┌─────────┐
196
- │ Google │ │ GitHub │ │ Email │
197
- │ OAuth │ │ OAuth │ │ Magic │
198
- └────┬────┘ └────┬────┘ └────┬────┘
199
- │ │ │
200
- └─────────────┼─────────────┘
201
- ▼
202
- ┌───────────┐
203
- │ Session │
204
- └─────┬─────┘
205
- │
206
- ▼
207
- ┌───────────┐
208
- │ Perms │
209
- └───────────┘
192
+ +---------------------------------------------+
193
+ | CURRENT AUTH FLOW |
194
+ +---------------------------------------------+
195
+ |
196
+ +-------------+-------------+
197
+ v v v
198
+ +---------+ +---------+ +---------+
199
+ | Google | | GitHub | | Email |
200
+ | OAuth | | OAuth | | Magic |
201
+ +----+----+ +----+----+ +----+----+
202
+ | | |
203
+ +-------------+-------------+
204
+ v
205
+ +-----------+
206
+ | Session |
207
+ +-----+-----+
208
+ |
209
+ v
210
+ +-----------+
211
+ | Perms |
212
+ +-----------+
210
213
 
211
214
  I see three tangles. Which one's burning?
212
215
  \`\`\`
@@ -238,19 +241,19 @@ User: A CLI tool that tracks local dev environments
238
241
 
239
242
  You: That changes everything.
240
243
 
241
- ┌─────────────────────────────────────────────────┐
242
- │ CLI TOOL DATA STORAGE │
243
- └─────────────────────────────────────────────────┘
244
+ +-------------------------------------------------+
245
+ | CLI TOOL DATA STORAGE |
246
+ +-------------------------------------------------+
244
247
 
245
248
  Key constraints:
246
- • No daemon running
247
- • Must work offline
248
- • Single user
249
+ - No daemon running
250
+ - Must work offline
251
+ - Single user
249
252
 
250
253
  SQLite Postgres
251
- Deployment embedded ✓ needs server ✗
252
- Offline yes ✓ no ✗
253
- Single file yes ✓ no ✗
254
+ Deployment embedded needs server
255
+ Offline yes no
256
+ Single file yes no
254
257
 
255
258
  SQLite. Not even close.
256
259
 
@@ -290,11 +293,11 @@ But this summary is optional. Sometimes the thinking IS the value.
290
293
 
291
294
  ## Guardrails
292
295
 
293
- - **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.
296
+ - **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
297
  - **Don't fake understanding** - If something is unclear, dig deeper
295
298
  - **Don't rush** - Discovery is thinking time, not task time
296
299
  - **Don't force structure** - Let patterns emerge naturally
297
- - **Don't auto-capture** - Offer to save insights, don't just do it
300
+ - **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
301
  - **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
302
  - **Do visualize** - A good diagram is worth many paragraphs
300
303
  - **Do explore the codebase** - Ground discussions in reality
@@ -312,7 +315,7 @@ export function getOpsxExploreCommandTemplate() {
312
315
  tags: ['workflow', 'explore', 'experimental', 'thinking'],
313
316
  content: `Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
314
317
 
315
- **IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, 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 OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing. For a new change, scaffold it first as described below.
318
+ **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
319
 
317
320
  **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
321
 
@@ -362,22 +365,25 @@ Depending on what the user brings, you might:
362
365
 
363
366
  **Visualize**
364
367
  \`\`\`
365
- ┌─────────────────────────────────────────┐
366
- │ Use ASCII diagrams liberally │
367
- ├─────────────────────────────────────────┤
368
- │ │
369
- │ ┌────────┐ ┌────────┐ │
370
- │ │ State │────────▶│ State │ │
371
- │ │ A │ │ B │ │
372
- │ └────────┘ └────────┘ │
373
- │ │
374
- │ System diagrams, state machines, │
375
- │ data flows, architecture sketches, │
376
- │ dependency graphs, comparison tables │
377
- │ │
378
- └─────────────────────────────────────────┘
368
+ +------------------------------------------+
369
+ | Use ASCII diagrams liberally |
370
+ +------------------------------------------+
371
+ | |
372
+ | [State A] -------> [State B] |
373
+ | | |
374
+ | v |
375
+ | [State C] |
376
+ | |
377
+ | System diagrams, state machines, |
378
+ | data flows, architecture sketches, |
379
+ | dependency graphs, comparison tables |
380
+ | |
381
+ +------------------------------------------+
379
382
  \`\`\`
380
383
 
384
+ **Draw with plain ASCII only** — borders \`+\` \`-\` \`|\`, arrows \`-->\` \`<--\` \`^\` \`v\`, markers \`*\` \`x\`.
385
+ 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.
386
+
381
387
  **Surface risks and unknowns**
382
388
  - Identify what could go wrong
383
389
  - Find gaps in understanding
@@ -486,11 +492,11 @@ When things crystallize, you might offer a summary - but it's optional. Sometime
486
492
 
487
493
  ## Guardrails
488
494
 
489
- - **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.
495
+ - **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
496
  - **Don't fake understanding** - If something is unclear, dig deeper
491
497
  - **Don't rush** - Discovery is thinking time, not task time
492
498
  - **Don't force structure** - Let patterns emerge naturally
493
- - **Don't auto-capture** - Offer to save insights, don't just do it
499
+ - **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
500
  - **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
501
  - **Do visualize** - A good diagram is worth many paragraphs
496
502
  - **Do explore the codebase** - Ground discussions in reality
@@ -39,6 +39,7 @@ export function getFeedbackSkillTemplate() {
39
39
  5. **Submit on confirmation**
40
40
  - Use the \`openspec feedback\` command to submit
41
41
  - Format: \`openspec feedback "title" --body "body content"\`
42
+ - The command preserves the title text in the issue body and shortens long GitHub issue titles
42
43
  - The command will automatically add metadata (version, platform, timestamp)
43
44
 
44
45
  **Example Draft**
@@ -374,12 +374,12 @@ Here are the implementation tasks:
374
374
 
375
375
  ## 1. [Category or file]
376
376
 
377
- - [ ] 1.1 [Specific task]
378
- - [ ] 1.2 [Specific task]
377
+ - [ ] 1.1 [Specific task] — verify: [test, command, observable behavior, or delivered artifact]
378
+ - [ ] 1.2 [Specific task] — verify: [test, command, observable behavior, or delivered artifact]
379
379
 
380
- ## 2. Verify
380
+ ## 2. Integration Verification
381
381
 
382
- - [ ] 2.1 [Verification step]
382
+ - [ ] 2.1 Verify [broader integration or system behavior] with [end-to-end test or observable result]
383
383
 
384
384
  ---
385
385