@fission-ai/openspec 1.6.0-beta.1 → 1.7.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 (175) hide show
  1. package/README.md +28 -1
  2. package/dist/cli/index.js +73 -7
  3. package/dist/commands/change.d.ts +0 -1
  4. package/dist/commands/change.js +90 -50
  5. package/dist/commands/config.js +14 -8
  6. package/dist/commands/doctor.js +9 -4
  7. package/dist/commands/feedback.js +74 -23
  8. package/dist/commands/schema.js +21 -11
  9. package/dist/commands/show.js +5 -2
  10. package/dist/commands/spec.js +20 -24
  11. package/dist/commands/validate.js +18 -3
  12. package/dist/commands/workflow/index.d.ts +1 -1
  13. package/dist/commands/workflow/index.js +1 -1
  14. package/dist/commands/workflow/instructions.d.ts +7 -1
  15. package/dist/commands/workflow/instructions.js +89 -6
  16. package/dist/commands/workflow/shared.d.ts +13 -2
  17. package/dist/commands/workflow/shared.js +34 -4
  18. package/dist/commands/workflow/status.js +7 -2
  19. package/dist/core/archive.d.ts +0 -1
  20. package/dist/core/archive.js +96 -33
  21. package/dist/core/artifact-graph/graph.d.ts +16 -0
  22. package/dist/core/artifact-graph/graph.js +30 -6
  23. package/dist/core/artifact-graph/instruction-loader.d.ts +27 -2
  24. package/dist/core/artifact-graph/instruction-loader.js +55 -7
  25. package/dist/core/artifact-graph/resolver.d.ts +14 -0
  26. package/dist/core/artifact-graph/resolver.js +34 -6
  27. package/dist/core/change-metadata/schema.d.ts +1 -0
  28. package/dist/core/change-metadata/schema.js +6 -0
  29. package/dist/core/change-status-policy.d.ts +1 -1
  30. package/dist/core/command-generation/adapters/amazon-q.d.ts +4 -0
  31. package/dist/core/command-generation/adapters/amazon-q.js +7 -1
  32. package/dist/core/command-generation/adapters/antigravity.js +2 -1
  33. package/dist/core/command-generation/adapters/auggie.js +2 -1
  34. package/dist/core/command-generation/adapters/bob.d.ts +5 -1
  35. package/dist/core/command-generation/adapters/bob.js +6 -5
  36. package/dist/core/command-generation/adapters/claude.js +1 -8
  37. package/dist/core/command-generation/adapters/codebuddy.js +3 -2
  38. package/dist/core/command-generation/adapters/continue.js +3 -2
  39. package/dist/core/command-generation/adapters/costrict.js +2 -1
  40. package/dist/core/command-generation/adapters/crush.js +5 -5
  41. package/dist/core/command-generation/adapters/cursor.js +2 -2
  42. package/dist/core/command-generation/adapters/devin.d.ts +19 -0
  43. package/dist/core/command-generation/adapters/devin.js +36 -0
  44. package/dist/core/command-generation/adapters/factory.js +2 -1
  45. package/dist/core/command-generation/adapters/gemini.js +37 -2
  46. package/dist/core/command-generation/adapters/github-copilot.js +2 -1
  47. package/dist/core/command-generation/adapters/iflow.js +5 -4
  48. package/dist/core/command-generation/adapters/index.d.ts +2 -2
  49. package/dist/core/command-generation/adapters/index.js +2 -2
  50. package/dist/core/command-generation/adapters/junie.js +2 -1
  51. package/dist/core/command-generation/adapters/kiro.js +2 -1
  52. package/dist/core/command-generation/adapters/lingma.js +5 -5
  53. package/dist/core/command-generation/adapters/oh-my-pi.d.ts +2 -2
  54. package/dist/core/command-generation/adapters/oh-my-pi.js +3 -5
  55. package/dist/core/command-generation/adapters/opencode.js +3 -5
  56. package/dist/core/command-generation/adapters/pi.d.ts +2 -2
  57. package/dist/core/command-generation/adapters/pi.js +3 -6
  58. package/dist/core/command-generation/adapters/qoder.js +5 -5
  59. package/dist/core/command-generation/adapters/qwen.d.ts +7 -3
  60. package/dist/core/command-generation/adapters/qwen.js +12 -7
  61. package/dist/core/command-generation/adapters/roocode.d.ts +4 -4
  62. package/dist/core/command-generation/adapters/roocode.js +4 -4
  63. package/dist/core/command-generation/adapters/trae.js +1 -21
  64. package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
  65. package/dist/core/command-generation/adapters/zcode.js +33 -0
  66. package/dist/core/command-generation/generator.d.ts +8 -0
  67. package/dist/core/command-generation/generator.js +15 -1
  68. package/dist/core/command-generation/index.d.ts +1 -1
  69. package/dist/core/command-generation/index.js +1 -1
  70. package/dist/core/command-generation/invocation.d.ts +73 -0
  71. package/dist/core/command-generation/invocation.js +77 -0
  72. package/dist/core/command-generation/registry.js +4 -4
  73. package/dist/core/command-generation/types.d.ts +9 -2
  74. package/dist/core/command-generation/yaml.d.ts +20 -6
  75. package/dist/core/command-generation/yaml.js +31 -21
  76. package/dist/core/command-surface.d.ts +15 -0
  77. package/dist/core/command-surface.js +33 -0
  78. package/dist/core/completions/command-registry.js +8 -2
  79. package/dist/core/completions/generators/powershell-generator.js +15 -8
  80. package/dist/core/completions/installers/zsh-installer.d.ts +9 -0
  81. package/dist/core/completions/installers/zsh-installer.js +20 -4
  82. package/dist/core/config-prompts.d.ts +1 -1
  83. package/dist/core/config-prompts.js +14 -1
  84. package/dist/core/config-schema.d.ts +7 -1
  85. package/dist/core/config-schema.js +40 -1
  86. package/dist/core/config.d.ts +12 -0
  87. package/dist/core/config.js +21 -3
  88. package/dist/core/global-config.d.ts +5 -0
  89. package/dist/core/init.d.ts +35 -0
  90. package/dist/core/init.js +225 -39
  91. package/dist/core/legacy-cleanup.d.ts +59 -0
  92. package/dist/core/legacy-cleanup.js +249 -22
  93. package/dist/core/list.js +5 -7
  94. package/dist/core/migration.d.ts +71 -1
  95. package/dist/core/migration.js +294 -1
  96. package/dist/core/onboarding-commands.d.ts +30 -0
  97. package/dist/core/onboarding-commands.js +38 -0
  98. package/dist/core/parsers/change-parser.d.ts +17 -2
  99. package/dist/core/parsers/change-parser.js +33 -20
  100. package/dist/core/parsers/code-fence.d.ts +15 -0
  101. package/dist/core/parsers/code-fence.js +49 -0
  102. package/dist/core/parsers/markdown-parser.js +2 -1
  103. package/dist/core/parsers/requirement-blocks.d.ts +8 -0
  104. package/dist/core/parsers/requirement-blocks.js +60 -35
  105. package/dist/core/parsers/requirement-text.d.ts +1 -8
  106. package/dist/core/parsers/requirement-text.js +4 -42
  107. package/dist/core/parsers/spec-structure.js +3 -29
  108. package/dist/core/profile-sync-drift.d.ts +0 -8
  109. package/dist/core/profile-sync-drift.js +9 -45
  110. package/dist/core/project-config.d.ts +25 -6
  111. package/dist/core/project-config.js +77 -7
  112. package/dist/core/references.js +42 -4
  113. package/dist/core/relationship-health.d.ts +8 -0
  114. package/dist/core/relationship-health.js +13 -0
  115. package/dist/core/root-selection.d.ts +9 -6
  116. package/dist/core/root-selection.js +42 -5
  117. package/dist/core/shared/tool-detection.d.ts +25 -5
  118. package/dist/core/shared/tool-detection.js +105 -6
  119. package/dist/core/specs-apply.d.ts +8 -33
  120. package/dist/core/specs-apply.js +252 -157
  121. package/dist/core/store/git.d.ts +13 -0
  122. package/dist/core/store/git.js +24 -0
  123. package/dist/core/templates/workflows/apply-change.js +54 -4
  124. package/dist/core/templates/workflows/archive-change.js +166 -42
  125. package/dist/core/templates/workflows/bulk-archive-change.js +240 -63
  126. package/dist/core/templates/workflows/continue-change.js +32 -40
  127. package/dist/core/templates/workflows/explore.js +12 -0
  128. package/dist/core/templates/workflows/ff-change.js +46 -30
  129. package/dist/core/templates/workflows/new-change.js +2 -2
  130. package/dist/core/templates/workflows/onboard.js +4 -4
  131. package/dist/core/templates/workflows/propose.js +50 -32
  132. package/dist/core/templates/workflows/store-selection.d.ts +1 -1
  133. package/dist/core/templates/workflows/store-selection.js +1 -1
  134. package/dist/core/templates/workflows/sync-specs.js +172 -22
  135. package/dist/core/templates/workflows/update-change.js +20 -12
  136. package/dist/core/templates/workflows/verify-change.js +16 -10
  137. package/dist/core/update.d.ts +39 -5
  138. package/dist/core/update.js +288 -71
  139. package/dist/core/validation/constants.d.ts +4 -1
  140. package/dist/core/validation/constants.js +4 -1
  141. package/dist/core/validation/validator.d.ts +1 -7
  142. package/dist/core/validation/validator.js +92 -36
  143. package/dist/core/version-check.d.ts +152 -0
  144. package/dist/core/version-check.js +736 -0
  145. package/dist/core/view.js +12 -17
  146. package/dist/prompts/searchable-multi-select.js +1 -1
  147. package/dist/telemetry/index.d.ts +1 -1
  148. package/dist/telemetry/index.js +55 -33
  149. package/dist/ui/welcome-screen.d.ts +9 -1
  150. package/dist/ui/welcome-screen.js +91 -38
  151. package/dist/utils/change-metadata.d.ts +30 -0
  152. package/dist/utils/change-metadata.js +101 -7
  153. package/dist/utils/change-utils.d.ts +10 -4
  154. package/dist/utils/change-utils.js +20 -13
  155. package/dist/utils/command-references.d.ts +80 -6
  156. package/dist/utils/command-references.js +138 -7
  157. package/dist/utils/date.d.ts +8 -0
  158. package/dist/utils/date.js +13 -0
  159. package/dist/utils/index.d.ts +1 -1
  160. package/dist/utils/index.js +1 -1
  161. package/dist/utils/item-discovery.d.ts +19 -0
  162. package/dist/utils/item-discovery.js +30 -48
  163. package/dist/utils/shell-detection.d.ts +1 -1
  164. package/dist/utils/shell-detection.js +64 -11
  165. package/dist/utils/spec-discovery.d.ts +40 -0
  166. package/dist/utils/spec-discovery.js +102 -0
  167. package/package.json +29 -30
  168. package/schemas/spec-driven/schema.yaml +60 -5
  169. package/schemas/spec-driven/templates/design.md +2 -2
  170. package/schemas/spec-driven/templates/proposal.md +5 -1
  171. package/schemas/spec-driven/templates/spec.md +3 -0
  172. package/dist/core/command-generation/adapters/codex.d.ts +0 -16
  173. package/dist/core/command-generation/adapters/codex.js +0 -39
  174. package/dist/core/command-generation/adapters/windsurf.d.ts +0 -14
  175. package/dist/core/command-generation/adapters/windsurf.js +0 -38
@@ -14,6 +14,7 @@ export declare const ChangeMetadataSchema: z.ZodObject<{
14
14
  store: z.ZodString;
15
15
  id: z.ZodString;
16
16
  }, z.core.$strict>>;
17
+ skip_specs: z.ZodOptional<z.ZodBoolean>;
17
18
  }, z.core.$strip>;
18
19
  export type ChangeMetadata = z.infer<typeof ChangeMetadataSchema>;
19
20
  //# sourceMappingURL=schema.d.ts.map
@@ -26,5 +26,11 @@ export const ChangeMetadataSchema = z.object({
26
26
  goal: z.string().min(1).optional(),
27
27
  affected_areas: z.array(z.string().min(1)).optional(),
28
28
  initiative: InitiativeLinkSchema.optional(),
29
+ // Declares that this change intentionally has no spec deltas (pure refactor,
30
+ // tooling, or docs work). Validation accepts zero deltas, and the artifact
31
+ // graph counts artifacts whose `generates` path lives under specs/ as
32
+ // complete - that path prefix, not the artifact id, is the contract custom
33
+ // schemas inherit.
34
+ skip_specs: z.boolean().optional(),
29
35
  });
30
36
  //# sourceMappingURL=schema.js.map
@@ -18,7 +18,7 @@ export interface ActionContext {
18
18
  }
19
19
  export interface ChangeStatusPolicyArtifact {
20
20
  id: string;
21
- status: 'done' | 'ready' | 'blocked';
21
+ status: 'done' | 'skipped' | 'ready' | 'blocked';
22
22
  }
23
23
  export interface ChangeNextStepsInput {
24
24
  changeName: string;
@@ -8,6 +8,10 @@ import type { ToolCommandAdapter } from '../types.js';
8
8
  * Amazon Q adapter for command generation.
9
9
  * File path: .amazonq/prompts/opsx-<id>.md
10
10
  * Frontmatter: description
11
+ *
12
+ * Amazon Q surfaces these files as its prompt library rather than as slash
13
+ * commands: the user types `@opsx-propose`, not `/opsx-propose`.
14
+ * https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-prompts.html
11
15
  */
12
16
  export declare const amazonQAdapter: ToolCommandAdapter;
13
17
  //# sourceMappingURL=amazon-q.d.ts.map
@@ -4,19 +4,25 @@
4
4
  * Formats commands for Amazon Q Developer following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * Amazon Q adapter for command generation.
9
10
  * File path: .amazonq/prompts/opsx-<id>.md
10
11
  * Frontmatter: description
12
+ *
13
+ * Amazon Q surfaces these files as its prompt library rather than as slash
14
+ * commands: the user types `@opsx-propose`, not `/opsx-propose`.
15
+ * https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-prompts.html
11
16
  */
12
17
  export const amazonQAdapter = {
13
18
  toolId: 'amazon-q',
14
19
  getFilePath(commandId) {
15
20
  return path.join('.amazonq', 'prompts', `opsx-${commandId}.md`);
16
21
  },
22
+ invocationPrefix: '@',
17
23
  formatFile(content) {
18
24
  return `---
19
- description: ${content.description}
25
+ description: ${escapeYamlValue(content.description)}
20
26
  ---
21
27
 
22
28
  ${content.body}
@@ -4,6 +4,7 @@
4
4
  * Formats commands for Antigravity following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * Antigravity adapter for command generation.
9
10
  * File path: .agent/workflows/opsx-<id>.md
@@ -16,7 +17,7 @@ export const antigravityAdapter = {
16
17
  },
17
18
  formatFile(content) {
18
19
  return `---
19
- description: ${content.description}
20
+ description: ${escapeYamlValue(content.description)}
20
21
  ---
21
22
 
22
23
  ${content.body}
@@ -4,6 +4,7 @@
4
4
  * Formats commands for Auggie following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * Auggie adapter for command generation.
9
10
  * File path: .augment/commands/opsx-<id>.md
@@ -16,7 +17,7 @@ export const auggieAdapter = {
16
17
  },
17
18
  formatFile(content) {
18
19
  return `---
19
- description: ${content.description}
20
+ description: ${escapeYamlValue(content.description)}
20
21
  argument-hint: command arguments
21
22
  ---
22
23
 
@@ -8,7 +8,11 @@ import type { ToolCommandAdapter } from '../types.js';
8
8
  /**
9
9
  * Bob Shell adapter for command generation.
10
10
  * File path: .bob/commands/opsx-<id>.md
11
- * Frontmatter: description, argument-hint
11
+ * Frontmatter: description
12
+ *
13
+ * Bob uses the filename (minus .md) as the slash command name, so
14
+ * opsx-propose.md → /opsx-propose. generateCommand rewrites the body's
15
+ * command references to that form before this adapter formats it.
12
16
  */
13
17
  export declare const bobAdapter: ToolCommandAdapter;
14
18
  //# sourceMappingURL=bob.d.ts.map
@@ -5,12 +5,15 @@
5
5
  * Commands are stored in .bob/commands/ directory.
6
6
  */
7
7
  import path from 'path';
8
- import { transformToHyphenCommands } from '../../../utils/command-references.js';
9
8
  import { escapeYamlValue } from '../yaml.js';
10
9
  /**
11
10
  * Bob Shell adapter for command generation.
12
11
  * File path: .bob/commands/opsx-<id>.md
13
- * Frontmatter: description, argument-hint
12
+ * Frontmatter: description
13
+ *
14
+ * Bob uses the filename (minus .md) as the slash command name, so
15
+ * opsx-propose.md → /opsx-propose. generateCommand rewrites the body's
16
+ * command references to that form before this adapter formats it.
14
17
  */
15
18
  export const bobAdapter = {
16
19
  toolId: 'bob',
@@ -18,14 +21,12 @@ export const bobAdapter = {
18
21
  return path.join('.bob', 'commands', `opsx-${commandId}.md`);
19
22
  },
20
23
  formatFile(content) {
21
- // Transform command references from colon to hyphen format for Bob
22
- const transformedBody = transformToHyphenCommands(content.body);
23
24
  return `---
24
25
  description: ${escapeYamlValue(content.description)}
25
26
  argument-hint: command arguments
26
27
  ---
27
28
 
28
- ${transformedBody}
29
+ ${content.body}
29
30
  `;
30
31
  },
31
32
  };
@@ -4,15 +4,8 @@
4
4
  * Formats commands for Claude Code following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
- import { escapeYamlValue } from '../yaml.js';
7
+ import { escapeYamlValue, formatTagsArray } from '../yaml.js';
8
8
  import { OPENSPEC_CLI_ALLOWED_TOOLS } from '../../shared/allowed-tools.js';
9
- /**
10
- * Formats a tags array as a YAML array with proper escaping.
11
- */
12
- function formatTagsArray(tags) {
13
- const escapedTags = tags.map((tag) => escapeYamlValue(tag));
14
- return `[${escapedTags.join(', ')}]`;
15
- }
16
9
  /**
17
10
  * Claude Code adapter for command generation.
18
11
  * File path: .claude/commands/opsx/<id>.md
@@ -4,6 +4,7 @@
4
4
  * Formats commands for CodeBuddy following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * CodeBuddy adapter for command generation.
9
10
  * File path: .codebuddy/commands/opsx/<id>.md
@@ -16,8 +17,8 @@ export const codebuddyAdapter = {
16
17
  },
17
18
  formatFile(content) {
18
19
  return `---
19
- name: ${content.name}
20
- description: "${content.description}"
20
+ name: ${escapeYamlValue(content.name)}
21
+ description: ${escapeYamlValue(content.description)}
21
22
  argument-hint: "[command arguments]"
22
23
  ---
23
24
 
@@ -4,6 +4,7 @@
4
4
  * Formats commands for Continue following its .prompt specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * Continue adapter for command generation.
9
10
  * File path: .continue/prompts/opsx-<id>.prompt
@@ -16,8 +17,8 @@ export const continueAdapter = {
16
17
  },
17
18
  formatFile(content) {
18
19
  return `---
19
- name: opsx-${content.id}
20
- description: ${content.description}
20
+ name: ${escapeYamlValue(`opsx-${content.id}`)}
21
+ description: ${escapeYamlValue(content.description)}
21
22
  invokable: true
22
23
  ---
23
24
 
@@ -4,6 +4,7 @@
4
4
  * Formats commands for CoStrict following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * CoStrict adapter for command generation.
9
10
  * File path: .cospec/openspec/commands/opsx-<id>.md
@@ -16,7 +17,7 @@ export const costrictAdapter = {
16
17
  },
17
18
  formatFile(content) {
18
19
  return `---
19
- description: "${content.description}"
20
+ description: ${escapeYamlValue(content.description)}
20
21
  argument-hint: command arguments
21
22
  ---
22
23
 
@@ -4,6 +4,7 @@
4
4
  * Formats commands for Crush following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue, formatTagsArray } from '../yaml.js';
7
8
  /**
8
9
  * Crush adapter for command generation.
9
10
  * File path: .crush/commands/opsx/<id>.md
@@ -15,12 +16,11 @@ export const crushAdapter = {
15
16
  return path.join('.crush', 'commands', 'opsx', `${commandId}.md`);
16
17
  },
17
18
  formatFile(content) {
18
- const tagsStr = content.tags.join(', ');
19
19
  return `---
20
- name: ${content.name}
21
- description: ${content.description}
22
- category: ${content.category}
23
- tags: [${tagsStr}]
20
+ name: ${escapeYamlValue(content.name)}
21
+ description: ${escapeYamlValue(content.description)}
22
+ category: ${escapeYamlValue(content.category)}
23
+ tags: ${formatTagsArray(content.tags)}
24
24
  ---
25
25
 
26
26
  ${content.body}
@@ -18,8 +18,8 @@ export const cursorAdapter = {
18
18
  },
19
19
  formatFile(content) {
20
20
  return `---
21
- name: /opsx-${content.id}
22
- id: opsx-${content.id}
21
+ name: ${escapeYamlValue(`/opsx-${content.id}`)}
22
+ id: ${escapeYamlValue(`opsx-${content.id}`)}
23
23
  category: ${escapeYamlValue(content.category)}
24
24
  description: ${escapeYamlValue(content.description)}
25
25
  ---
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Devin Desktop Command Adapter
3
+ *
4
+ * Formats commands for Devin Desktop following its frontmatter specification.
5
+ * Devin Desktop reads Cascade-style workflows from `.devin/workflows/`, the
6
+ * same shape Windsurf uses.
7
+ */
8
+ import type { ToolCommandAdapter } from '../types.js';
9
+ /**
10
+ * Devin Desktop adapter for command generation.
11
+ * File path: .devin/workflows/opsx-<id>.md
12
+ * Frontmatter: name, description, category, tags
13
+ *
14
+ * The `opsx-` filename prefix makes this a flat invocation, so the generator
15
+ * rewrites the body's `/opsx:*` references to the `/opsx-*` form Devin
16
+ * registers — see invocation.ts.
17
+ */
18
+ export declare const devinAdapter: ToolCommandAdapter;
19
+ //# sourceMappingURL=devin.d.ts.map
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Devin Desktop Command Adapter
3
+ *
4
+ * Formats commands for Devin Desktop following its frontmatter specification.
5
+ * Devin Desktop reads Cascade-style workflows from `.devin/workflows/`, the
6
+ * same shape Windsurf uses.
7
+ */
8
+ import path from 'path';
9
+ import { escapeYamlValue, formatTagsArray } from '../yaml.js';
10
+ /**
11
+ * Devin Desktop adapter for command generation.
12
+ * File path: .devin/workflows/opsx-<id>.md
13
+ * Frontmatter: name, description, category, tags
14
+ *
15
+ * The `opsx-` filename prefix makes this a flat invocation, so the generator
16
+ * rewrites the body's `/opsx:*` references to the `/opsx-*` form Devin
17
+ * registers — see invocation.ts.
18
+ */
19
+ export const devinAdapter = {
20
+ toolId: 'devin',
21
+ getFilePath(commandId) {
22
+ return path.join('.devin', 'workflows', `opsx-${commandId}.md`);
23
+ },
24
+ formatFile(content) {
25
+ return `---
26
+ name: ${escapeYamlValue(content.name)}
27
+ description: ${escapeYamlValue(content.description)}
28
+ category: ${escapeYamlValue(content.category)}
29
+ tags: ${formatTagsArray(content.tags)}
30
+ ---
31
+
32
+ ${content.body}
33
+ `;
34
+ },
35
+ };
36
+ //# sourceMappingURL=devin.js.map
@@ -4,6 +4,7 @@
4
4
  * Formats commands for Factory Droid following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * Factory adapter for command generation.
9
10
  * File path: .factory/commands/opsx-<id>.md
@@ -16,7 +17,7 @@ export const factoryAdapter = {
16
17
  },
17
18
  formatFile(content) {
18
19
  return `---
19
- description: ${content.description}
20
+ description: ${escapeYamlValue(content.description)}
20
21
  argument-hint: command arguments
21
22
  ---
22
23
 
@@ -4,6 +4,41 @@
4
4
  * Formats commands for Gemini CLI following its TOML specification.
5
5
  */
6
6
  import path from 'path';
7
+ /**
8
+ * Control characters (C0 except tab/newline/carriage return, plus DEL) are
9
+ * invalid inside TOML strings and must be written as escapes.
10
+ */
11
+ const TOML_CONTROL_CHARS = new RegExp('[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]', 'g');
12
+ /**
13
+ * TOML basic strings are escape-active: a backslash or double quote in the
14
+ * value breaks the file if written raw. Newlines cannot appear in a
15
+ * single-line basic string at all, so they are escaped too.
16
+ */
17
+ function escapeTomlBasicString(value) {
18
+ return value
19
+ .replace(/\\/g, '\\\\')
20
+ .replace(/"/g, '\\"')
21
+ .replace(/\n/g, '\\n')
22
+ .replace(/\r/g, '\\r')
23
+ .replace(/\t/g, '\\t')
24
+ .replace(TOML_CONTROL_CHARS, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
25
+ }
26
+ /**
27
+ * Multiline basic strings keep raw newlines and tabs, but backslashes are
28
+ * still escape-active, any run of three quotes would end the string, and the
29
+ * same control characters are invalid as in single-line basic strings — a
30
+ * lone carriage return included (only LF and CRLF may appear raw; CRLF is
31
+ * normalized away so the emitted file is single-convention). Escapes are
32
+ * introduced after backslash-doubling so they are not re-doubled.
33
+ */
34
+ function escapeTomlMultilineBasicString(value) {
35
+ return value
36
+ .replace(/\r\n/g, '\n')
37
+ .replace(/\\/g, '\\\\')
38
+ .replace(/"""/g, '""\\"')
39
+ .replace(/\r/g, '\\r')
40
+ .replace(TOML_CONTROL_CHARS, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
41
+ }
7
42
  /**
8
43
  * Gemini adapter for command generation.
9
44
  * File path: .gemini/commands/opsx/<id>.toml
@@ -15,10 +50,10 @@ export const geminiAdapter = {
15
50
  return path.join('.gemini', 'commands', 'opsx', `${commandId}.toml`);
16
51
  },
17
52
  formatFile(content) {
18
- return `description = "${content.description}"
53
+ return `description = "${escapeTomlBasicString(content.description)}"
19
54
 
20
55
  prompt = """
21
- ${content.body}
56
+ ${escapeTomlMultilineBasicString(content.body)}
22
57
  """
23
58
  `;
24
59
  },
@@ -4,6 +4,7 @@
4
4
  * Formats commands for GitHub Copilot following its .prompt.md specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * GitHub Copilot adapter for command generation.
9
10
  * File path: .github/prompts/opsx-<id>.prompt.md
@@ -16,7 +17,7 @@ export const githubCopilotAdapter = {
16
17
  },
17
18
  formatFile(content) {
18
19
  return `---
19
- description: ${content.description}
20
+ description: ${escapeYamlValue(content.description)}
20
21
  ---
21
22
 
22
23
  ${content.body}
@@ -4,6 +4,7 @@
4
4
  * Formats commands for iFlow following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * iFlow adapter for command generation.
9
10
  * File path: .iflow/commands/opsx-<id>.md
@@ -16,10 +17,10 @@ export const iflowAdapter = {
16
17
  },
17
18
  formatFile(content) {
18
19
  return `---
19
- name: /opsx-${content.id}
20
- id: opsx-${content.id}
21
- category: ${content.category}
22
- description: ${content.description}
20
+ name: ${escapeYamlValue(`/opsx-${content.id}`)}
21
+ id: ${escapeYamlValue(`opsx-${content.id}`)}
22
+ category: ${escapeYamlValue(content.category)}
23
+ description: ${escapeYamlValue(content.description)}
23
24
  ---
24
25
 
25
26
  ${content.body}
@@ -9,12 +9,12 @@ export { auggieAdapter } from './auggie.js';
9
9
  export { bobAdapter } from './bob.js';
10
10
  export { claudeAdapter } from './claude.js';
11
11
  export { clineAdapter } from './cline.js';
12
- export { codexAdapter } from './codex.js';
13
12
  export { codebuddyAdapter } from './codebuddy.js';
14
13
  export { continueAdapter } from './continue.js';
15
14
  export { costrictAdapter } from './costrict.js';
16
15
  export { crushAdapter } from './crush.js';
17
16
  export { cursorAdapter } from './cursor.js';
17
+ export { devinAdapter } from './devin.js';
18
18
  export { factoryAdapter } from './factory.js';
19
19
  export { geminiAdapter } from './gemini.js';
20
20
  export { githubCopilotAdapter } from './github-copilot.js';
@@ -30,5 +30,5 @@ export { lingmaAdapter } from './lingma.js';
30
30
  export { qwenAdapter } from './qwen.js';
31
31
  export { roocodeAdapter } from './roocode.js';
32
32
  export { traeAdapter } from './trae.js';
33
- export { windsurfAdapter } from './windsurf.js';
33
+ export { zcodeAdapter } from './zcode.js';
34
34
  //# sourceMappingURL=index.d.ts.map
@@ -9,12 +9,12 @@ export { auggieAdapter } from './auggie.js';
9
9
  export { bobAdapter } from './bob.js';
10
10
  export { claudeAdapter } from './claude.js';
11
11
  export { clineAdapter } from './cline.js';
12
- export { codexAdapter } from './codex.js';
13
12
  export { codebuddyAdapter } from './codebuddy.js';
14
13
  export { continueAdapter } from './continue.js';
15
14
  export { costrictAdapter } from './costrict.js';
16
15
  export { crushAdapter } from './crush.js';
17
16
  export { cursorAdapter } from './cursor.js';
17
+ export { devinAdapter } from './devin.js';
18
18
  export { factoryAdapter } from './factory.js';
19
19
  export { geminiAdapter } from './gemini.js';
20
20
  export { githubCopilotAdapter } from './github-copilot.js';
@@ -30,5 +30,5 @@ export { lingmaAdapter } from './lingma.js';
30
30
  export { qwenAdapter } from './qwen.js';
31
31
  export { roocodeAdapter } from './roocode.js';
32
32
  export { traeAdapter } from './trae.js';
33
- export { windsurfAdapter } from './windsurf.js';
33
+ export { zcodeAdapter } from './zcode.js';
34
34
  //# sourceMappingURL=index.js.map
@@ -4,6 +4,7 @@
4
4
  * Formats commands for Junie following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * Junie adapter for command generation.
9
10
  * File path: .junie/commands/opsx-<id>.md
@@ -16,7 +17,7 @@ export const junieAdapter = {
16
17
  },
17
18
  formatFile(content) {
18
19
  return `---
19
- description: ${content.description}
20
+ description: ${escapeYamlValue(content.description)}
20
21
  ---
21
22
 
22
23
  ${content.body}
@@ -4,6 +4,7 @@
4
4
  * Formats commands for Kiro following its .prompt.md specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue } from '../yaml.js';
7
8
  /**
8
9
  * Kiro adapter for command generation.
9
10
  * File path: .kiro/prompts/opsx-<id>.prompt.md
@@ -16,7 +17,7 @@ export const kiroAdapter = {
16
17
  },
17
18
  formatFile(content) {
18
19
  return `---
19
- description: ${content.description}
20
+ description: ${escapeYamlValue(content.description)}
20
21
  ---
21
22
 
22
23
  ${content.body}
@@ -4,6 +4,7 @@
4
4
  * Formats commands for Lingma following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue, formatTagsArray } from '../yaml.js';
7
8
  /**
8
9
  * Lingma adapter for command generation.
9
10
  * File path: .lingma/commands/opsx/<id>.md
@@ -15,12 +16,11 @@ export const lingmaAdapter = {
15
16
  return path.join('.lingma', 'commands', 'opsx', `${commandId}.md`);
16
17
  },
17
18
  formatFile(content) {
18
- const tagsStr = content.tags.join(', ');
19
19
  return `---
20
- name: ${content.name}
21
- description: ${content.description}
22
- category: ${content.category}
23
- tags: [${tagsStr}]
20
+ name: ${escapeYamlValue(content.name)}
21
+ description: ${escapeYamlValue(content.description)}
22
+ category: ${escapeYamlValue(content.category)}
23
+ tags: ${formatTagsArray(content.tags)}
24
24
  ---
25
25
 
26
26
  ${content.body}
@@ -12,8 +12,8 @@ import type { ToolCommandAdapter } from '../types.js';
12
12
  * Frontmatter: description
13
13
  *
14
14
  * OMP uses the filename (minus .md) as the slash command name, so
15
- * opsx-propose.md → /opsx-propose. Command references in the body
16
- * are transformed from /opsx: to /opsx- for consistency, and
15
+ * opsx-propose.md → /opsx-propose. generateCommand rewrites the body's
16
+ * command references to that form before this adapter formats it, and
17
17
  * $@ is injected after **Input**: headings so user-supplied arguments
18
18
  * (e.g. /opsx-propose my-feature) are visible to the agent.
19
19
  */
@@ -6,7 +6,6 @@
6
6
  * The filename (minus .md) becomes the slash command name.
7
7
  */
8
8
  import path from 'path';
9
- import { transformToHyphenCommands } from '../../../utils/command-references.js';
10
9
  import { escapeYamlValue } from '../yaml.js';
11
10
  const OMP_INPUT_HEADING = /^\*\*Input\*\*:[^\n]*$/m;
12
11
  function injectOmpArgs(body) {
@@ -21,8 +20,8 @@ function injectOmpArgs(body) {
21
20
  * Frontmatter: description
22
21
  *
23
22
  * OMP uses the filename (minus .md) as the slash command name, so
24
- * opsx-propose.md → /opsx-propose. Command references in the body
25
- * are transformed from /opsx: to /opsx- for consistency, and
23
+ * opsx-propose.md → /opsx-propose. generateCommand rewrites the body's
24
+ * command references to that form before this adapter formats it, and
26
25
  * $@ is injected after **Input**: headings so user-supplied arguments
27
26
  * (e.g. /opsx-propose my-feature) are visible to the agent.
28
27
  */
@@ -32,12 +31,11 @@ export const ohMyPiAdapter = {
32
31
  return path.join('.omp', 'commands', `opsx-${commandId}.md`);
33
32
  },
34
33
  formatFile(content) {
35
- const transformedBody = transformToHyphenCommands(content.body);
36
34
  return `---
37
35
  description: ${escapeYamlValue(content.description)}
38
36
  ---
39
37
 
40
- ${injectOmpArgs(transformedBody)}
38
+ ${injectOmpArgs(content.body)}
41
39
  `;
42
40
  },
43
41
  };
@@ -4,7 +4,7 @@
4
4
  * Formats commands for OpenCode following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
- import { transformToHyphenCommands } from '../../../utils/command-references.js';
7
+ import { escapeYamlValue } from '../yaml.js';
8
8
  /**
9
9
  * OpenCode adapter for command generation.
10
10
  * File path: .opencode/commands/opsx-<id>.md
@@ -16,13 +16,11 @@ export const opencodeAdapter = {
16
16
  return path.join('.opencode', 'commands', `opsx-${commandId}.md`);
17
17
  },
18
18
  formatFile(content) {
19
- // Transform command references from colon to hyphen format for OpenCode
20
- const transformedBody = transformToHyphenCommands(content.body);
21
19
  return `---
22
- description: ${content.description}
20
+ description: ${escapeYamlValue(content.description)}
23
21
  ---
24
22
 
25
- ${transformedBody}
23
+ ${content.body}
26
24
  `;
27
25
  },
28
26
  };
@@ -11,8 +11,8 @@ import type { ToolCommandAdapter } from '../types.js';
11
11
  * Frontmatter: description
12
12
  *
13
13
  * Pi uses the filename (minus .md) as the slash command name, so
14
- * opsx-propose.md → /opsx-propose. Command references in the body
15
- * are transformed from /opsx: to /opsx- for consistency.
14
+ * opsx-propose.md → /opsx-propose. generateCommand rewrites the body's
15
+ * command references to that form before this adapter formats it.
16
16
  */
17
17
  export declare const piAdapter: ToolCommandAdapter;
18
18
  //# sourceMappingURL=pi.d.ts.map