@fission-ai/openspec 1.6.0 → 1.8.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 (208) hide show
  1. package/README.md +31 -2
  2. package/dist/cli/index.js +79 -8
  3. package/dist/commands/change.d.ts +0 -1
  4. package/dist/commands/change.js +105 -51
  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 +110 -31
  9. package/dist/commands/show.js +5 -2
  10. package/dist/commands/spec.js +49 -29
  11. package/dist/commands/validate.js +20 -5
  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 +121 -33
  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 +9 -4
  19. package/dist/commands/workflow/templates.js +15 -5
  20. package/dist/core/archive.d.ts +14 -1
  21. package/dist/core/archive.js +1332 -155
  22. package/dist/core/artifact-graph/graph.d.ts +16 -0
  23. package/dist/core/artifact-graph/graph.js +30 -6
  24. package/dist/core/artifact-graph/index.d.ts +1 -1
  25. package/dist/core/artifact-graph/index.js +1 -1
  26. package/dist/core/artifact-graph/instruction-loader.d.ts +30 -3
  27. package/dist/core/artifact-graph/instruction-loader.js +70 -12
  28. package/dist/core/artifact-graph/outputs.d.ts +1 -0
  29. package/dist/core/artifact-graph/outputs.js +67 -5
  30. package/dist/core/artifact-graph/resolver.d.ts +14 -0
  31. package/dist/core/artifact-graph/resolver.js +69 -15
  32. package/dist/core/artifact-graph/types.js +21 -3
  33. package/dist/core/available-tools.d.ts +2 -2
  34. package/dist/core/available-tools.js +14 -3
  35. package/dist/core/change-metadata/schema.d.ts +2 -0
  36. package/dist/core/change-metadata/schema.js +13 -0
  37. package/dist/core/change-status-policy.d.ts +1 -1
  38. package/dist/core/change-status-policy.js +2 -2
  39. package/dist/core/command-generation/adapters/amazon-q.d.ts +4 -0
  40. package/dist/core/command-generation/adapters/amazon-q.js +7 -1
  41. package/dist/core/command-generation/adapters/antigravity.js +2 -1
  42. package/dist/core/command-generation/adapters/auggie.js +2 -1
  43. package/dist/core/command-generation/adapters/bob.d.ts +5 -1
  44. package/dist/core/command-generation/adapters/bob.js +6 -5
  45. package/dist/core/command-generation/adapters/claude.js +1 -8
  46. package/dist/core/command-generation/adapters/codebuddy.js +3 -2
  47. package/dist/core/command-generation/adapters/continue.js +3 -2
  48. package/dist/core/command-generation/adapters/costrict.js +2 -1
  49. package/dist/core/command-generation/adapters/crush.js +5 -5
  50. package/dist/core/command-generation/adapters/cursor.js +2 -2
  51. package/dist/core/command-generation/adapters/devin.d.ts +19 -0
  52. package/dist/core/command-generation/adapters/devin.js +36 -0
  53. package/dist/core/command-generation/adapters/factory.js +2 -1
  54. package/dist/core/command-generation/adapters/gemini.js +37 -2
  55. package/dist/core/command-generation/adapters/github-copilot.js +2 -1
  56. package/dist/core/command-generation/adapters/iflow.js +5 -4
  57. package/dist/core/command-generation/adapters/index.d.ts +2 -2
  58. package/dist/core/command-generation/adapters/index.js +2 -2
  59. package/dist/core/command-generation/adapters/junie.js +2 -1
  60. package/dist/core/command-generation/adapters/kiro.js +2 -1
  61. package/dist/core/command-generation/adapters/lingma.js +5 -5
  62. package/dist/core/command-generation/adapters/oh-my-pi.d.ts +2 -2
  63. package/dist/core/command-generation/adapters/oh-my-pi.js +3 -5
  64. package/dist/core/command-generation/adapters/opencode.js +3 -5
  65. package/dist/core/command-generation/adapters/pi.d.ts +2 -2
  66. package/dist/core/command-generation/adapters/pi.js +3 -6
  67. package/dist/core/command-generation/adapters/qoder.js +5 -5
  68. package/dist/core/command-generation/adapters/qwen.d.ts +7 -3
  69. package/dist/core/command-generation/adapters/qwen.js +12 -7
  70. package/dist/core/command-generation/adapters/roocode.d.ts +4 -4
  71. package/dist/core/command-generation/adapters/roocode.js +4 -4
  72. package/dist/core/command-generation/adapters/trae.js +1 -21
  73. package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
  74. package/dist/core/command-generation/adapters/zcode.js +33 -0
  75. package/dist/core/command-generation/generator.d.ts +8 -0
  76. package/dist/core/command-generation/generator.js +15 -1
  77. package/dist/core/command-generation/index.d.ts +1 -1
  78. package/dist/core/command-generation/index.js +1 -1
  79. package/dist/core/command-generation/invocation.d.ts +73 -0
  80. package/dist/core/command-generation/invocation.js +77 -0
  81. package/dist/core/command-generation/registry.js +4 -4
  82. package/dist/core/command-generation/types.d.ts +9 -2
  83. package/dist/core/command-generation/yaml.d.ts +20 -6
  84. package/dist/core/command-generation/yaml.js +31 -21
  85. package/dist/core/command-surface.d.ts +15 -0
  86. package/dist/core/command-surface.js +33 -0
  87. package/dist/core/completions/command-registry.js +16 -2
  88. package/dist/core/completions/generators/powershell-generator.js +15 -8
  89. package/dist/core/completions/installers/zsh-installer.d.ts +9 -0
  90. package/dist/core/completions/installers/zsh-installer.js +20 -4
  91. package/dist/core/config-prompts.d.ts +1 -1
  92. package/dist/core/config-prompts.js +14 -1
  93. package/dist/core/config-schema.d.ts +10 -1
  94. package/dist/core/config-schema.js +67 -1
  95. package/dist/core/config.d.ts +15 -0
  96. package/dist/core/config.js +45 -5
  97. package/dist/core/file-state.js +56 -24
  98. package/dist/core/github-copilot/cloud-agent.d.ts +95 -0
  99. package/dist/core/github-copilot/cloud-agent.js +497 -0
  100. package/dist/core/global-config.d.ts +16 -0
  101. package/dist/core/init.d.ts +58 -0
  102. package/dist/core/init.js +450 -69
  103. package/dist/core/legacy-cleanup.d.ts +59 -0
  104. package/dist/core/legacy-cleanup.js +253 -24
  105. package/dist/core/list.js +5 -7
  106. package/dist/core/migration.d.ts +73 -1
  107. package/dist/core/migration.js +360 -11
  108. package/dist/core/onboarding-commands.d.ts +30 -0
  109. package/dist/core/onboarding-commands.js +38 -0
  110. package/dist/core/parsers/change-parser.d.ts +17 -2
  111. package/dist/core/parsers/change-parser.js +33 -20
  112. package/dist/core/parsers/code-fence.d.ts +15 -0
  113. package/dist/core/parsers/code-fence.js +49 -0
  114. package/dist/core/parsers/markdown-parser.js +2 -1
  115. package/dist/core/parsers/requirement-blocks.d.ts +18 -0
  116. package/dist/core/parsers/requirement-blocks.js +120 -35
  117. package/dist/core/parsers/requirement-text.d.ts +1 -8
  118. package/dist/core/parsers/requirement-text.js +4 -42
  119. package/dist/core/parsers/spec-structure.d.ts +1 -1
  120. package/dist/core/parsers/spec-structure.js +20 -30
  121. package/dist/core/profile-sync-drift.d.ts +0 -8
  122. package/dist/core/profile-sync-drift.js +48 -49
  123. package/dist/core/project-config.d.ts +28 -6
  124. package/dist/core/project-config.js +108 -8
  125. package/dist/core/references.js +42 -4
  126. package/dist/core/relationship-health.d.ts +8 -0
  127. package/dist/core/relationship-health.js +13 -0
  128. package/dist/core/root-selection.d.ts +9 -6
  129. package/dist/core/root-selection.js +42 -5
  130. package/dist/core/shared/index.d.ts +1 -0
  131. package/dist/core/shared/index.js +1 -0
  132. package/dist/core/shared/skill-content-equivalence.d.ts +7 -0
  133. package/dist/core/shared/skill-content-equivalence.js +41 -0
  134. package/dist/core/shared/skill-paths.d.ts +13 -0
  135. package/dist/core/shared/skill-paths.js +23 -0
  136. package/dist/core/shared/tool-detection.d.ts +25 -5
  137. package/dist/core/shared/tool-detection.js +166 -36
  138. package/dist/core/shared-skill-target.d.ts +16 -0
  139. package/dist/core/shared-skill-target.js +156 -0
  140. package/dist/core/specs-apply.d.ts +80 -33
  141. package/dist/core/specs-apply.js +659 -171
  142. package/dist/core/store/git.d.ts +13 -0
  143. package/dist/core/store/git.js +24 -0
  144. package/dist/core/templates/skill-templates.d.ts +1 -1
  145. package/dist/core/templates/skill-templates.js +1 -1
  146. package/dist/core/templates/workflows/apply-change.d.ts +10 -0
  147. package/dist/core/templates/workflows/apply-change.js +48 -158
  148. package/dist/core/templates/workflows/archive-change.js +170 -42
  149. package/dist/core/templates/workflows/bulk-archive-change.js +256 -75
  150. package/dist/core/templates/workflows/continue-change.js +38 -46
  151. package/dist/core/templates/workflows/explore.js +54 -18
  152. package/dist/core/templates/workflows/ff-change.js +46 -30
  153. package/dist/core/templates/workflows/new-change.js +2 -2
  154. package/dist/core/templates/workflows/onboard.js +13 -7
  155. package/dist/core/templates/workflows/propose.js +116 -46
  156. package/dist/core/templates/workflows/store-selection.d.ts +1 -1
  157. package/dist/core/templates/workflows/store-selection.js +1 -1
  158. package/dist/core/templates/workflows/sync-specs.js +260 -32
  159. package/dist/core/templates/workflows/update-change.js +26 -16
  160. package/dist/core/templates/workflows/verify-change.js +16 -10
  161. package/dist/core/update.d.ts +40 -5
  162. package/dist/core/update.js +389 -93
  163. package/dist/core/validation/constants.d.ts +4 -1
  164. package/dist/core/validation/constants.js +4 -1
  165. package/dist/core/validation/validator.d.ts +22 -8
  166. package/dist/core/validation/validator.js +222 -50
  167. package/dist/core/version-check.d.ts +152 -0
  168. package/dist/core/version-check.js +731 -0
  169. package/dist/core/view.js +12 -17
  170. package/dist/prompts/searchable-multi-select.js +1 -1
  171. package/dist/telemetry/config.d.ts +3 -4
  172. package/dist/telemetry/index.d.ts +10 -5
  173. package/dist/telemetry/index.js +75 -41
  174. package/dist/ui/welcome-screen.d.ts +9 -1
  175. package/dist/ui/welcome-screen.js +91 -38
  176. package/dist/utils/change-metadata.d.ts +43 -0
  177. package/dist/utils/change-metadata.js +123 -7
  178. package/dist/utils/change-utils.d.ts +10 -4
  179. package/dist/utils/change-utils.js +20 -13
  180. package/dist/utils/ci.d.ts +12 -0
  181. package/dist/utils/ci.js +16 -0
  182. package/dist/utils/command-references.d.ts +92 -6
  183. package/dist/utils/command-references.js +180 -7
  184. package/dist/utils/date.d.ts +8 -0
  185. package/dist/utils/date.js +13 -0
  186. package/dist/utils/file-system.d.ts +10 -0
  187. package/dist/utils/file-system.js +70 -0
  188. package/dist/utils/index.d.ts +1 -1
  189. package/dist/utils/index.js +1 -1
  190. package/dist/utils/interactive.d.ts +23 -0
  191. package/dist/utils/interactive.js +32 -0
  192. package/dist/utils/item-discovery.d.ts +19 -0
  193. package/dist/utils/item-discovery.js +30 -48
  194. package/dist/utils/shell-detection.d.ts +1 -1
  195. package/dist/utils/shell-detection.js +64 -11
  196. package/dist/utils/spec-discovery.d.ts +40 -0
  197. package/dist/utils/spec-discovery.js +115 -0
  198. package/dist/utils/task-progress.d.ts +17 -0
  199. package/dist/utils/task-progress.js +41 -13
  200. package/package.json +14 -12
  201. package/schemas/spec-driven/schema.yaml +67 -10
  202. package/schemas/spec-driven/templates/design.md +2 -2
  203. package/schemas/spec-driven/templates/proposal.md +10 -4
  204. package/schemas/spec-driven/templates/spec.md +3 -0
  205. package/dist/core/command-generation/adapters/codex.d.ts +0 -16
  206. package/dist/core/command-generation/adapters/codex.js +0 -39
  207. package/dist/core/command-generation/adapters/windsurf.d.ts +0 -14
  208. package/dist/core/command-generation/adapters/windsurf.js +0 -38
@@ -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
@@ -5,7 +5,6 @@
5
5
  * Pi prompt templates live in .pi/prompts/*.md with description frontmatter.
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
  const PI_INPUT_HEADING = /^\*\*Input\*\*:[^\n]*$/m;
11
10
  function injectPiArgs(body) {
@@ -20,8 +19,8 @@ function injectPiArgs(body) {
20
19
  * Frontmatter: description
21
20
  *
22
21
  * Pi uses the filename (minus .md) as the slash command name, so
23
- * opsx-propose.md → /opsx-propose. Command references in the body
24
- * are transformed from /opsx: to /opsx- for consistency.
22
+ * opsx-propose.md → /opsx-propose. generateCommand rewrites the body's
23
+ * command references to that form before this adapter formats it.
25
24
  */
26
25
  export const piAdapter = {
27
26
  toolId: 'pi',
@@ -29,13 +28,11 @@ export const piAdapter = {
29
28
  return path.join('.pi', 'prompts', `opsx-${commandId}.md`);
30
29
  },
31
30
  formatFile(content) {
32
- // Transform /opsx: references to /opsx- and inject $@ for template args
33
- const transformedBody = transformToHyphenCommands(content.body);
34
31
  return `---
35
32
  description: ${escapeYamlValue(content.description)}
36
33
  ---
37
34
 
38
- ${injectPiArgs(transformedBody)}
35
+ ${injectPiArgs(content.body)}
39
36
  `;
40
37
  },
41
38
  };
@@ -4,6 +4,7 @@
4
4
  * Formats commands for Qoder following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
+ import { escapeYamlValue, formatTagsArray } from '../yaml.js';
7
8
  /**
8
9
  * Qoder adapter for command generation.
9
10
  * File path: .qoder/commands/opsx/<id>.md
@@ -15,12 +16,11 @@ export const qoderAdapter = {
15
16
  return path.join('.qoder', '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}
@@ -1,13 +1,17 @@
1
1
  /**
2
2
  * Qwen Code Command Adapter
3
3
  *
4
- * Formats commands for Qwen Code following its TOML specification.
4
+ * Formats commands for Qwen Code following its Markdown custom command
5
+ * specification. Qwen Code has deprecated TOML commands in favor of
6
+ * Markdown files with YAML frontmatter.
7
+ *
8
+ * @see https://qwenlm.github.io/qwen-code-docs/en/users/features/commands/#markdown-file-format-specification-recommended
5
9
  */
6
10
  import type { ToolCommandAdapter } from '../types.js';
7
11
  /**
8
12
  * Qwen adapter for command generation.
9
- * File path: .qwen/commands/opsx-<id>.toml
10
- * Format: TOML with description and prompt fields
13
+ * File path: .qwen/commands/opsx-<id>.md
14
+ * Format: Markdown with description frontmatter
11
15
  */
12
16
  export declare const qwenAdapter: ToolCommandAdapter;
13
17
  //# sourceMappingURL=qwen.d.ts.map
@@ -1,25 +1,30 @@
1
1
  /**
2
2
  * Qwen Code Command Adapter
3
3
  *
4
- * Formats commands for Qwen Code following its TOML specification.
4
+ * Formats commands for Qwen Code following its Markdown custom command
5
+ * specification. Qwen Code has deprecated TOML commands in favor of
6
+ * Markdown files with YAML frontmatter.
7
+ *
8
+ * @see https://qwenlm.github.io/qwen-code-docs/en/users/features/commands/#markdown-file-format-specification-recommended
5
9
  */
6
10
  import path from 'path';
11
+ import { escapeYamlValue } from '../yaml.js';
7
12
  /**
8
13
  * Qwen adapter for command generation.
9
- * File path: .qwen/commands/opsx-<id>.toml
10
- * Format: TOML with description and prompt fields
14
+ * File path: .qwen/commands/opsx-<id>.md
15
+ * Format: Markdown with description frontmatter
11
16
  */
12
17
  export const qwenAdapter = {
13
18
  toolId: 'qwen',
14
19
  getFilePath(commandId) {
15
- return path.join('.qwen', 'commands', `opsx-${commandId}.toml`);
20
+ return path.join('.qwen', 'commands', `opsx-${commandId}.md`);
16
21
  },
17
22
  formatFile(content) {
18
- return `description = "${content.description}"
23
+ return `---
24
+ description: ${escapeYamlValue(content.description)}
25
+ ---
19
26
 
20
- prompt = """
21
27
  ${content.body}
22
- """
23
28
  `;
24
29
  },
25
30
  };
@@ -1,12 +1,12 @@
1
1
  /**
2
- * RooCode Command Adapter
2
+ * Zoo Code Command Adapter
3
3
  *
4
- * Formats commands for RooCode following its workflow specification.
5
- * RooCode uses markdown headers instead of YAML frontmatter.
4
+ * Formats commands for Zoo Code following its workflow specification.
5
+ * Zoo Code uses markdown headers instead of YAML frontmatter.
6
6
  */
7
7
  import type { ToolCommandAdapter } from '../types.js';
8
8
  /**
9
- * RooCode adapter for command generation.
9
+ * Zoo Code adapter for command generation.
10
10
  * File path: .roo/commands/opsx-<id>.md
11
11
  * Format: Markdown header with description
12
12
  */
@@ -1,12 +1,12 @@
1
1
  /**
2
- * RooCode Command Adapter
2
+ * Zoo Code Command Adapter
3
3
  *
4
- * Formats commands for RooCode following its workflow specification.
5
- * RooCode uses markdown headers instead of YAML frontmatter.
4
+ * Formats commands for Zoo Code following its workflow specification.
5
+ * Zoo Code uses markdown headers instead of YAML frontmatter.
6
6
  */
7
7
  import path from 'path';
8
8
  /**
9
- * RooCode adapter for command generation.
9
+ * Zoo Code adapter for command generation.
10
10
  * File path: .roo/commands/opsx-<id>.md
11
11
  * Format: Markdown header with description
12
12
  */
@@ -4,27 +4,7 @@
4
4
  * Formats commands for Trae IDE following its command specification.
5
5
  */
6
6
  import path from 'path';
7
- /**
8
- * Escapes a string value for safe YAML output.
9
- * Quotes the string if it contains special YAML characters.
10
- */
11
- function escapeYamlValue(value) {
12
- if (value === '') {
13
- return '""';
14
- }
15
- // Check if value needs quoting (contains special YAML characters or starts/ends with whitespace)
16
- const needsQuoting = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);
17
- if (needsQuoting) {
18
- // Use double quotes and escape internal double quotes, backslashes, and newlines
19
- const escaped = value
20
- .replace(/\\/g, '\\\\')
21
- .replace(/"/g, '\\"')
22
- .replace(/\n/g, '\\n')
23
- .replace(/\r/g, '\\r');
24
- return `"${escaped}"`;
25
- }
26
- return value;
27
- }
7
+ import { escapeYamlValue } from '../yaml.js';
28
8
  /**
29
9
  * Trae adapter for command generation.
30
10
  * File path: .trae/commands/opsx-<id>.md
@@ -0,0 +1,16 @@
1
+ /**
2
+ * ZCode Command Adapter
3
+ *
4
+ * Formats commands for ZCode following its frontmatter specification.
5
+ * ZCode shares Claude Code's command format conventions.
6
+ * File path: .zcode/commands/opsx/<id>.md
7
+ * Frontmatter: name, description, category, tags
8
+ */
9
+ import type { ToolCommandAdapter } from '../types.js';
10
+ /**
11
+ * ZCode adapter for command generation.
12
+ * File path: .zcode/commands/opsx/<id>.md
13
+ * Frontmatter: name, description, category, tags
14
+ */
15
+ export declare const zcodeAdapter: ToolCommandAdapter;
16
+ //# sourceMappingURL=zcode.d.ts.map
@@ -0,0 +1,33 @@
1
+ /**
2
+ * ZCode Command Adapter
3
+ *
4
+ * Formats commands for ZCode following its frontmatter specification.
5
+ * ZCode shares Claude Code's command format conventions.
6
+ * File path: .zcode/commands/opsx/<id>.md
7
+ * Frontmatter: name, description, category, tags
8
+ */
9
+ import path from 'path';
10
+ import { escapeYamlValue, formatTagsArray } from '../yaml.js';
11
+ /**
12
+ * ZCode adapter for command generation.
13
+ * File path: .zcode/commands/opsx/<id>.md
14
+ * Frontmatter: name, description, category, tags
15
+ */
16
+ export const zcodeAdapter = {
17
+ toolId: 'zcode',
18
+ getFilePath(commandId) {
19
+ return path.join('.zcode', 'commands', 'opsx', `${commandId}.md`);
20
+ },
21
+ formatFile(content) {
22
+ return `---
23
+ name: ${escapeYamlValue(content.name)}
24
+ description: ${escapeYamlValue(content.description)}
25
+ category: ${escapeYamlValue(content.category)}
26
+ tags: ${formatTagsArray(content.tags)}
27
+ ---
28
+
29
+ ${content.body}
30
+ `;
31
+ },
32
+ };
33
+ //# sourceMappingURL=zcode.js.map
@@ -6,6 +6,14 @@
6
6
  import type { CommandContent, ToolCommandAdapter, GeneratedCommand } from './types.js';
7
7
  /**
8
8
  * Generate a single command file using the provided adapter.
9
+ *
10
+ * Command bodies are authored with `/opsx:<id>` references. Tools whose command
11
+ * files are invoked by filename register `/opsx-<id>` instead, and Amazon Q
12
+ * surfaces them in its prompt library as `@opsx-<id>`, so the body is rewritten
13
+ * to the form that tool answers to before the adapter formats it. Doing it here
14
+ * rather than per adapter keeps every tool in step (#727, #1307); adapters stay
15
+ * pure formatters.
16
+ *
9
17
  * @param content - The tool-agnostic command content
10
18
  * @param adapter - The tool-specific adapter
11
19
  * @returns Generated command with path and file content
@@ -3,16 +3,30 @@
3
3
  *
4
4
  * Functions for generating command files using tool adapters.
5
5
  */
6
+ import { getInvocationForAdapter, needsInvocationRewrite } from './invocation.js';
7
+ import { transformCommandInvocations } from '../../utils/command-references.js';
6
8
  /**
7
9
  * Generate a single command file using the provided adapter.
10
+ *
11
+ * Command bodies are authored with `/opsx:<id>` references. Tools whose command
12
+ * files are invoked by filename register `/opsx-<id>` instead, and Amazon Q
13
+ * surfaces them in its prompt library as `@opsx-<id>`, so the body is rewritten
14
+ * to the form that tool answers to before the adapter formats it. Doing it here
15
+ * rather than per adapter keeps every tool in step (#727, #1307); adapters stay
16
+ * pure formatters.
17
+ *
8
18
  * @param content - The tool-agnostic command content
9
19
  * @param adapter - The tool-specific adapter
10
20
  * @returns Generated command with path and file content
11
21
  */
12
22
  export function generateCommand(content, adapter) {
23
+ const invocation = getInvocationForAdapter(adapter);
24
+ const formatted = needsInvocationRewrite(invocation)
25
+ ? { ...content, body: transformCommandInvocations(content.body, invocation) }
26
+ : content;
13
27
  return {
14
28
  path: adapter.getFilePath(content.id),
15
- fileContent: adapter.formatFile(content),
29
+ fileContent: adapter.formatFile(formatted),
16
30
  };
17
31
  }
18
32
  /**
@@ -18,5 +18,5 @@
18
18
  export type { CommandContent, ToolCommandAdapter, GeneratedCommand, } from './types.js';
19
19
  export { CommandAdapterRegistry } from './registry.js';
20
20
  export { generateCommand, generateCommands } from './generator.js';
21
- export { claudeAdapter, cursorAdapter, windsurfAdapter } from './adapters/index.js';
21
+ export { claudeAdapter, cursorAdapter, devinAdapter } from './adapters/index.js';
22
22
  //# sourceMappingURL=index.d.ts.map
@@ -20,5 +20,5 @@ export { CommandAdapterRegistry } from './registry.js';
20
20
  // Generator functions
21
21
  export { generateCommand, generateCommands } from './generator.js';
22
22
  // Adapters (for direct access if needed)
23
- export { claudeAdapter, cursorAdapter, windsurfAdapter } from './adapters/index.js';
23
+ export { claudeAdapter, cursorAdapter, devinAdapter } from './adapters/index.js';
24
24
  //# sourceMappingURL=index.js.map