@codewalla_india/openspec 1.3.1 → 1.3.3

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 (333) hide show
  1. package/LICENSE +22 -22
  2. package/README.md +214 -133
  3. package/bin/openspec.js +5 -5
  4. package/dist/cli/index.js +99 -62
  5. package/dist/commands/change.d.ts +0 -2
  6. package/dist/commands/change.js +107 -86
  7. package/dist/commands/config.js +14 -16
  8. package/dist/commands/doctor.js +9 -4
  9. package/dist/commands/feedback.js +79 -30
  10. package/dist/commands/modify.d.ts +26 -0
  11. package/dist/commands/modify.js +147 -0
  12. package/dist/commands/schema.js +170 -91
  13. package/dist/commands/show.js +5 -2
  14. package/dist/commands/spec.js +49 -29
  15. package/dist/commands/store.js +15 -21
  16. package/dist/commands/validate.d.ts +8 -0
  17. package/dist/commands/validate.js +43 -20
  18. package/dist/commands/workflow/index.d.ts +2 -2
  19. package/dist/commands/workflow/index.js +1 -1
  20. package/dist/commands/workflow/instructions.d.ts +6 -26
  21. package/dist/commands/workflow/instructions.js +183 -376
  22. package/dist/commands/workflow/new-change.d.ts +0 -4
  23. package/dist/commands/workflow/new-change.js +10 -28
  24. package/dist/commands/workflow/shared.d.ts +34 -19
  25. package/dist/commands/workflow/shared.js +34 -4
  26. package/dist/commands/workflow/status.js +10 -32
  27. package/dist/commands/workflow/templates.js +15 -5
  28. package/dist/commands/workset.d.ts +2 -2
  29. package/dist/commands/workset.js +19 -22
  30. package/dist/comprehension-quiz/fingerprint.d.ts +39 -0
  31. package/dist/comprehension-quiz/fingerprint.js +84 -0
  32. package/dist/comprehension-quiz/index.d.ts +42 -0
  33. package/dist/comprehension-quiz/index.js +47 -0
  34. package/dist/comprehension-quiz/pass-record.d.ts +44 -0
  35. package/dist/comprehension-quiz/pass-record.js +98 -0
  36. package/dist/comprehension-quiz/providers/anthropic.d.ts +35 -0
  37. package/dist/comprehension-quiz/providers/anthropic.js +69 -0
  38. package/dist/comprehension-quiz/providers/factory.d.ts +44 -0
  39. package/dist/comprehension-quiz/providers/factory.js +174 -0
  40. package/dist/comprehension-quiz/providers/interface.d.ts +98 -0
  41. package/dist/comprehension-quiz/providers/interface.js +150 -0
  42. package/dist/comprehension-quiz/providers/local.d.ts +35 -0
  43. package/dist/comprehension-quiz/providers/local.js +69 -0
  44. package/dist/comprehension-quiz/providers/ollama.d.ts +35 -0
  45. package/dist/comprehension-quiz/providers/ollama.js +69 -0
  46. package/dist/comprehension-quiz/providers/openai.d.ts +35 -0
  47. package/dist/comprehension-quiz/providers/openai.js +72 -0
  48. package/dist/comprehension-quiz/question-generator.d.ts +117 -0
  49. package/dist/comprehension-quiz/question-generator.js +203 -0
  50. package/dist/comprehension-quiz/quiz-executor.d.ts +75 -0
  51. package/dist/comprehension-quiz/quiz-executor.js +181 -0
  52. package/dist/comprehension-quiz/types.d.ts +137 -0
  53. package/dist/comprehension-quiz/types.js +11 -0
  54. package/dist/core/archive.d.ts +14 -1
  55. package/dist/core/archive.js +1350 -190
  56. package/dist/core/artifact-graph/graph.d.ts +28 -9
  57. package/dist/core/artifact-graph/graph.js +94 -43
  58. package/dist/core/artifact-graph/index.d.ts +1 -1
  59. package/dist/core/artifact-graph/index.js +1 -1
  60. package/dist/core/artifact-graph/instruction-loader.d.ts +30 -3
  61. package/dist/core/artifact-graph/instruction-loader.js +70 -12
  62. package/dist/core/artifact-graph/outputs.d.ts +1 -0
  63. package/dist/core/artifact-graph/outputs.js +67 -5
  64. package/dist/core/artifact-graph/resolver.d.ts +14 -0
  65. package/dist/core/artifact-graph/resolver.js +69 -15
  66. package/dist/core/artifact-graph/types.js +21 -3
  67. package/dist/core/available-tools.d.ts +2 -2
  68. package/dist/core/available-tools.js +14 -3
  69. package/dist/core/change-metadata/schema.d.ts +2 -0
  70. package/dist/core/change-metadata/schema.js +13 -0
  71. package/dist/core/change-status-policy.d.ts +10 -1
  72. package/dist/core/change-status-policy.js +30 -2
  73. package/dist/core/command-generation/adapters/amazon-q.d.ts +4 -0
  74. package/dist/core/command-generation/adapters/amazon-q.js +11 -5
  75. package/dist/core/command-generation/adapters/antigravity.js +6 -5
  76. package/dist/core/command-generation/adapters/auggie.js +7 -6
  77. package/dist/core/command-generation/adapters/bob.d.ts +5 -1
  78. package/dist/core/command-generation/adapters/bob.js +11 -10
  79. package/dist/core/command-generation/adapters/claude.d.ts +1 -1
  80. package/dist/core/command-generation/adapters/claude.js +12 -17
  81. package/dist/core/command-generation/adapters/cline.js +5 -5
  82. package/dist/core/command-generation/adapters/codebuddy.js +8 -7
  83. package/dist/core/command-generation/adapters/continue.js +8 -7
  84. package/dist/core/command-generation/adapters/costrict.js +7 -6
  85. package/dist/core/command-generation/adapters/crush.js +9 -9
  86. package/dist/core/command-generation/adapters/cursor.js +8 -8
  87. package/dist/core/command-generation/adapters/devin.d.ts +19 -0
  88. package/dist/core/command-generation/adapters/devin.js +36 -0
  89. package/dist/core/command-generation/adapters/factory.js +7 -6
  90. package/dist/core/command-generation/adapters/gemini.js +40 -5
  91. package/dist/core/command-generation/adapters/github-copilot.js +6 -5
  92. package/dist/core/command-generation/adapters/iflow.js +9 -8
  93. package/dist/core/command-generation/adapters/index.d.ts +4 -2
  94. package/dist/core/command-generation/adapters/index.js +4 -2
  95. package/dist/core/command-generation/adapters/junie.js +6 -5
  96. package/dist/core/command-generation/adapters/kilocode.js +1 -1
  97. package/dist/core/command-generation/adapters/kiro.js +6 -5
  98. package/dist/core/command-generation/adapters/lingma.js +9 -9
  99. package/dist/core/command-generation/adapters/oh-my-pi.d.ts +21 -0
  100. package/dist/core/command-generation/adapters/oh-my-pi.js +42 -0
  101. package/dist/core/command-generation/adapters/opencode.js +6 -8
  102. package/dist/core/command-generation/adapters/pi.d.ts +2 -2
  103. package/dist/core/command-generation/adapters/pi.js +7 -10
  104. package/dist/core/command-generation/adapters/qoder.js +9 -9
  105. package/dist/core/command-generation/adapters/qwen.d.ts +7 -3
  106. package/dist/core/command-generation/adapters/qwen.js +14 -9
  107. package/dist/core/command-generation/adapters/roocode.d.ts +4 -4
  108. package/dist/core/command-generation/adapters/roocode.js +9 -9
  109. package/dist/core/command-generation/adapters/trae.d.ts +13 -0
  110. package/dist/core/command-generation/adapters/trae.js +28 -0
  111. package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
  112. package/dist/core/command-generation/adapters/zcode.js +33 -0
  113. package/dist/core/command-generation/generator.d.ts +8 -0
  114. package/dist/core/command-generation/generator.js +15 -1
  115. package/dist/core/command-generation/index.d.ts +1 -1
  116. package/dist/core/command-generation/index.js +1 -1
  117. package/dist/core/command-generation/invocation.d.ts +73 -0
  118. package/dist/core/command-generation/invocation.js +77 -0
  119. package/dist/core/command-generation/registry.js +8 -4
  120. package/dist/core/command-generation/types.d.ts +9 -2
  121. package/dist/core/command-generation/yaml.d.ts +20 -6
  122. package/dist/core/command-generation/yaml.js +31 -21
  123. package/dist/core/command-surface.d.ts +15 -0
  124. package/dist/core/command-surface.js +33 -0
  125. package/dist/core/completions/command-registry.js +16 -61
  126. package/dist/core/completions/generators/bash-generator.js +41 -41
  127. package/dist/core/completions/generators/fish-generator.js +7 -7
  128. package/dist/core/completions/generators/powershell-generator.js +44 -37
  129. package/dist/core/completions/generators/zsh-generator.js +33 -33
  130. package/dist/core/completions/installers/zsh-installer.d.ts +9 -0
  131. package/dist/core/completions/installers/zsh-installer.js +20 -4
  132. package/dist/core/completions/templates/bash-templates.js +24 -24
  133. package/dist/core/completions/templates/fish-templates.js +38 -38
  134. package/dist/core/completions/templates/powershell-templates.js +28 -28
  135. package/dist/core/completions/templates/zsh-templates.js +39 -39
  136. package/dist/core/config-prompts.d.ts +1 -1
  137. package/dist/core/config-prompts.js +14 -1
  138. package/dist/core/config-schema.d.ts +10 -1
  139. package/dist/core/config-schema.js +67 -1
  140. package/dist/core/config.d.ts +15 -0
  141. package/dist/core/config.js +46 -5
  142. package/dist/core/file-state.js +56 -24
  143. package/dist/core/github-copilot/cloud-agent.d.ts +95 -0
  144. package/dist/core/github-copilot/cloud-agent.js +497 -0
  145. package/dist/core/global-config.d.ts +16 -0
  146. package/dist/core/init.d.ts +58 -0
  147. package/dist/core/init.js +474 -73
  148. package/dist/core/legacy-cleanup.d.ts +59 -0
  149. package/dist/core/legacy-cleanup.js +253 -24
  150. package/dist/core/list.js +23 -16
  151. package/dist/core/migration.d.ts +73 -1
  152. package/dist/core/migration.js +360 -11
  153. package/dist/core/modify/conflict-detector.d.ts +26 -0
  154. package/dist/core/modify/conflict-detector.js +62 -0
  155. package/dist/core/modify/dependency-propagator.d.ts +38 -0
  156. package/dist/core/modify/dependency-propagator.js +103 -0
  157. package/dist/core/modify/history-tracker.d.ts +34 -0
  158. package/dist/core/modify/history-tracker.js +114 -0
  159. package/dist/core/modify/index.d.ts +7 -0
  160. package/dist/core/modify/index.js +7 -0
  161. package/dist/core/onboarding-commands.d.ts +30 -0
  162. package/dist/core/onboarding-commands.js +38 -0
  163. package/dist/core/openspec-root.js +18 -15
  164. package/dist/core/parsers/change-parser.d.ts +17 -2
  165. package/dist/core/parsers/change-parser.js +35 -21
  166. package/dist/core/parsers/code-fence.d.ts +15 -0
  167. package/dist/core/parsers/code-fence.js +49 -0
  168. package/dist/core/parsers/markdown-parser.d.ts +0 -3
  169. package/dist/core/parsers/markdown-parser.js +7 -60
  170. package/dist/core/parsers/requirement-blocks.d.ts +29 -0
  171. package/dist/core/parsers/requirement-blocks.js +150 -31
  172. package/dist/core/parsers/requirement-text.d.ts +48 -0
  173. package/dist/core/parsers/requirement-text.js +98 -0
  174. package/dist/core/parsers/spec-structure.d.ts +1 -1
  175. package/dist/core/parsers/spec-structure.js +20 -30
  176. package/dist/core/profile-sync-drift.d.ts +0 -8
  177. package/dist/core/profile-sync-drift.js +49 -50
  178. package/dist/core/profiles.d.ts +2 -2
  179. package/dist/core/profiles.js +2 -2
  180. package/dist/core/project-config.d.ts +27 -18
  181. package/dist/core/project-config.js +101 -34
  182. package/dist/core/references.js +42 -4
  183. package/dist/core/relationship-health.d.ts +8 -0
  184. package/dist/core/relationship-health.js +13 -0
  185. package/dist/core/root-selection.d.ts +9 -6
  186. package/dist/core/root-selection.js +42 -5
  187. package/dist/core/schemas/base.schema.js +7 -2
  188. package/dist/core/shared/allowed-tools.d.ts +12 -0
  189. package/dist/core/shared/allowed-tools.js +12 -0
  190. package/dist/core/shared/index.d.ts +1 -0
  191. package/dist/core/shared/index.js +1 -0
  192. package/dist/core/shared/skill-content-equivalence.d.ts +7 -0
  193. package/dist/core/shared/skill-content-equivalence.js +41 -0
  194. package/dist/core/shared/skill-generation.js +17 -13
  195. package/dist/core/shared/skill-paths.d.ts +13 -0
  196. package/dist/core/shared/skill-paths.js +23 -0
  197. package/dist/core/shared/tool-detection.d.ts +27 -7
  198. package/dist/core/shared/tool-detection.js +167 -37
  199. package/dist/core/shared-skill-target.d.ts +16 -0
  200. package/dist/core/shared-skill-target.js +156 -0
  201. package/dist/core/specs-apply.d.ts +80 -33
  202. package/dist/core/specs-apply.js +667 -145
  203. package/dist/core/store/git.d.ts +13 -0
  204. package/dist/core/store/git.js +24 -0
  205. package/dist/core/store/operations.js +20 -0
  206. package/dist/core/templates/skill-templates.d.ts +3 -2
  207. package/dist/core/templates/skill-templates.js +3 -2
  208. package/dist/core/templates/workflows/apply-change.d.ts +10 -0
  209. package/dist/core/templates/workflows/apply-change.js +186 -224
  210. package/dist/core/templates/workflows/archive-change.js +387 -260
  211. package/dist/core/templates/workflows/bulk-archive-change.js +655 -475
  212. package/dist/core/templates/workflows/continue-change.js +210 -219
  213. package/dist/core/templates/workflows/explore.js +479 -443
  214. package/dist/core/templates/workflows/feedback.js +97 -97
  215. package/dist/core/templates/workflows/ff-change.js +200 -191
  216. package/dist/core/templates/workflows/modify-change.d.ts +4 -1
  217. package/dist/core/templates/workflows/modify-change.js +193 -71
  218. package/dist/core/templates/workflows/new-change.js +127 -134
  219. package/dist/core/templates/workflows/onboard.js +548 -542
  220. package/dist/core/templates/workflows/propose.js +273 -217
  221. package/dist/core/templates/workflows/store-selection.d.ts +1 -1
  222. package/dist/core/templates/workflows/store-selection.js +1 -1
  223. package/dist/core/templates/workflows/sync-specs.js +500 -273
  224. package/dist/core/templates/workflows/update-change.d.ts +10 -0
  225. package/dist/core/templates/workflows/update-change.js +177 -0
  226. package/dist/core/templates/workflows/verify-change.js +324 -326
  227. package/dist/core/update.d.ts +40 -5
  228. package/dist/core/update.js +411 -96
  229. package/dist/core/validation/constants.d.ts +4 -1
  230. package/dist/core/validation/constants.js +4 -1
  231. package/dist/core/validation/plan-validator.d.ts +8 -0
  232. package/dist/core/validation/plan-validator.js +45 -0
  233. package/dist/core/validation/task-numbering.d.ts +16 -0
  234. package/dist/core/validation/task-numbering.js +57 -0
  235. package/dist/core/validation/validator.d.ts +27 -3
  236. package/dist/core/validation/validator.js +337 -48
  237. package/dist/core/version-check.d.ts +152 -0
  238. package/dist/core/version-check.js +731 -0
  239. package/dist/core/view.js +13 -18
  240. package/dist/prompts/searchable-multi-select.js +1 -1
  241. package/dist/telemetry/caller-detection.d.ts +27 -0
  242. package/dist/telemetry/caller-detection.js +85 -0
  243. package/dist/telemetry/caller-detection.test.d.ts +5 -0
  244. package/dist/telemetry/config.d.ts +48 -2
  245. package/dist/telemetry/config.js +113 -11
  246. package/dist/telemetry/config.test.d.ts +5 -0
  247. package/dist/telemetry/deduplication.d.ts +35 -0
  248. package/dist/telemetry/deduplication.js +82 -0
  249. package/dist/telemetry/deduplication.test.d.ts +5 -0
  250. package/dist/telemetry/identity.d.ts +62 -22
  251. package/dist/telemetry/identity.js +147 -102
  252. package/dist/telemetry/index.d.ts +126 -15
  253. package/dist/telemetry/index.js +569 -37
  254. package/dist/telemetry/sanitization.d.ts +40 -0
  255. package/dist/telemetry/sanitization.js +114 -0
  256. package/dist/telemetry/sanitization.test.d.ts +5 -0
  257. package/dist/telemetry/throttling.d.ts +20 -0
  258. package/dist/telemetry/throttling.js +70 -0
  259. package/dist/ui/welcome-screen.d.ts +9 -1
  260. package/dist/ui/welcome-screen.js +91 -38
  261. package/dist/utils/change-metadata.d.ts +43 -0
  262. package/dist/utils/change-metadata.js +123 -7
  263. package/dist/utils/change-utils.d.ts +10 -4
  264. package/dist/utils/change-utils.js +20 -13
  265. package/dist/utils/ci.d.ts +12 -0
  266. package/dist/utils/ci.js +16 -0
  267. package/dist/utils/command-references.d.ts +92 -6
  268. package/dist/utils/command-references.js +180 -7
  269. package/dist/utils/date.d.ts +8 -0
  270. package/dist/utils/date.js +13 -0
  271. package/dist/utils/file-system.d.ts +10 -0
  272. package/dist/utils/file-system.js +70 -0
  273. package/dist/utils/index.d.ts +1 -1
  274. package/dist/utils/index.js +1 -1
  275. package/dist/utils/interactive.d.ts +23 -0
  276. package/dist/utils/interactive.js +32 -0
  277. package/dist/utils/item-discovery.d.ts +19 -0
  278. package/dist/utils/item-discovery.js +30 -48
  279. package/dist/utils/shell-detection.d.ts +1 -1
  280. package/dist/utils/shell-detection.js +64 -11
  281. package/dist/utils/spec-discovery.d.ts +40 -0
  282. package/dist/utils/spec-discovery.js +115 -0
  283. package/dist/utils/task-progress.d.ts +29 -1
  284. package/dist/utils/task-progress.js +109 -15
  285. package/package.json +96 -84
  286. package/schemas/spec-driven/schema.yaml +252 -184
  287. package/schemas/spec-driven/templates/design.md +19 -19
  288. package/schemas/spec-driven/templates/plan-template.md +91 -0
  289. package/schemas/spec-driven/templates/proposal.md +29 -24
  290. package/schemas/spec-driven/templates/spec.md +11 -8
  291. package/schemas/spec-driven/templates/tasks.md +9 -9
  292. package/scripts/postinstall.js +83 -83
  293. package/dist/core/command-generation/adapters/codex.d.ts +0 -16
  294. package/dist/core/command-generation/adapters/codex.js +0 -39
  295. package/dist/core/command-generation/adapters/windsurf.d.ts +0 -14
  296. package/dist/core/command-generation/adapters/windsurf.js +0 -38
  297. package/dist/core/comprehension/config.d.ts +0 -20
  298. package/dist/core/comprehension/config.js +0 -23
  299. package/dist/core/comprehension/fingerprint.d.ts +0 -14
  300. package/dist/core/comprehension/fingerprint.js +0 -33
  301. package/dist/core/comprehension/index.d.ts +0 -57
  302. package/dist/core/comprehension/index.js +0 -87
  303. package/dist/core/comprehension/pass-record.d.ts +0 -29
  304. package/dist/core/comprehension/pass-record.js +0 -64
  305. package/dist/core/comprehension/stats.d.ts +0 -35
  306. package/dist/core/comprehension/stats.js +0 -133
  307. package/dist/core/templates/workflows/comprehension-guidance.d.ts +0 -9
  308. package/dist/core/templates/workflows/comprehension-guidance.js +0 -73
  309. package/dist/core/templates/workflows/mcp-guidance.d.ts +0 -13
  310. package/dist/core/templates/workflows/mcp-guidance.js +0 -131
  311. package/dist/core/templates/workflows/user-prompt-guidance.d.ts +0 -16
  312. package/dist/core/templates/workflows/user-prompt-guidance.js +0 -48
  313. package/dist/telemetry/caller.d.ts +0 -5
  314. package/dist/telemetry/caller.js +0 -29
  315. package/dist/telemetry/client.d.ts +0 -27
  316. package/dist/telemetry/client.js +0 -127
  317. package/dist/telemetry/command-context.d.ts +0 -13
  318. package/dist/telemetry/command-context.js +0 -59
  319. package/dist/telemetry/comprehension.d.ts +0 -44
  320. package/dist/telemetry/comprehension.js +0 -105
  321. package/dist/telemetry/content.d.ts +0 -10
  322. package/dist/telemetry/content.js +0 -56
  323. package/dist/telemetry/git-stats.d.ts +0 -12
  324. package/dist/telemetry/git-stats.js +0 -69
  325. package/dist/telemetry/identify-cache.d.ts +0 -7
  326. package/dist/telemetry/identify-cache.js +0 -47
  327. package/dist/telemetry/input.d.ts +0 -17
  328. package/dist/telemetry/input.js +0 -68
  329. package/dist/telemetry/marker.d.ts +0 -37
  330. package/dist/telemetry/marker.js +0 -67
  331. package/dist/telemetry/workflow.d.ts +0 -85
  332. package/dist/telemetry/workflow.js +0 -318
  333. package/schemas/spec-driven/templates/plan.md +0 -19
@@ -1,32 +1,43 @@
1
1
  import path from 'path';
2
2
  import { FileSystemUtils } from './file-system.js';
3
3
  import { writeChangeMetadata, validateSchemaName } from './change-metadata.js';
4
+ import { formatLocalDate } from './date.js';
4
5
  import { readProjectConfig } from '../core/project-config.js';
6
+ import { isKebabId } from '../core/id.js';
5
7
  const DEFAULT_SCHEMA = 'spec-driven';
6
8
  /**
7
9
  * Validates that a change name follows kebab-case conventions.
8
10
  *
9
- * Valid names:
10
- * - Start with a lowercase letter
11
+ * Uses OpenSpec's shared kebab-id grammar (the same one store ids and change
12
+ * metadata ids use), so a change name may:
13
+ * - Start with a lowercase letter or a digit
11
14
  * - Contain only lowercase letters, numbers, and hyphens
12
- * - Do not start or end with a hyphen
13
- * - Do not contain consecutive hyphens
15
+ * - Not start or end with a hyphen
16
+ * - Not contain consecutive hyphens
17
+ *
18
+ * A leading digit is allowed so ordering conventions like `100-add-feature` or
19
+ * `00001-add-auth` work; archive already treats such prefixes as a supported
20
+ * convention (see ARCHIVE_DATE_PREFIX_PATTERN).
14
21
  *
15
22
  * @param name - The change name to validate
16
23
  * @returns Validation result with `valid: true` or `valid: false` with an error message
17
24
  *
18
25
  * @example
19
26
  * validateChangeName('add-auth') // { valid: true }
27
+ * validateChangeName('100-add-feature') // { valid: true }
20
28
  * validateChangeName('Add-Auth') // { valid: false, error: '...' }
21
29
  */
22
30
  export function validateChangeName(name) {
23
- // Pattern: starts with lowercase letter, followed by lowercase letters/numbers,
24
- // optionally followed by hyphen + lowercase letters/numbers (repeatable)
25
- const kebabCasePattern = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
26
31
  if (!name) {
27
32
  return { valid: false, error: 'Change name cannot be empty' };
28
33
  }
29
- if (!kebabCasePattern.test(name)) {
34
+ // Filesystem directory components cap at 255 bytes and archive prepends a
35
+ // date prefix; bounding here turns the failure into a validation message
36
+ // instead of a raw ENAMETOOLONG from mkdir.
37
+ if (name.length > 200) {
38
+ return { valid: false, error: 'Change name is too long (200 characters max)' };
39
+ }
40
+ if (!isKebabId(name)) {
30
41
  // Provide specific error messages for common mistakes
31
42
  if (/[A-Z]/.test(name)) {
32
43
  return { valid: false, error: 'Change name must be lowercase (use kebab-case)' };
@@ -49,9 +60,6 @@ export function validateChangeName(name) {
49
60
  if (/[^a-z0-9-]/.test(name)) {
50
61
  return { valid: false, error: 'Change name can only contain lowercase letters, numbers, and hyphens' };
51
62
  }
52
- if (/^[0-9]/.test(name)) {
53
- return { valid: false, error: 'Change name must start with a letter' };
54
- }
55
63
  return { valid: false, error: 'Change name must follow kebab-case convention (e.g., add-auth, refactor-db)' };
56
64
  }
57
65
  return { valid: true };
@@ -127,10 +135,9 @@ export async function createChange(projectRoot, name, options = {}) {
127
135
  await FileSystemUtils.writeFile(configPath, `schema: ${defaultSchema}\n`);
128
136
  }
129
137
  // Write metadata file with schema and creation date
130
- const today = new Date().toISOString().split('T')[0];
131
138
  writeChangeMetadata(changeDir, {
132
139
  schema: schemaName,
133
- created: today,
140
+ created: formatLocalDate(),
134
141
  ...options.metadata,
135
142
  }, projectRoot);
136
143
  return { schema: schemaName, changeDir };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * CI environment detection shared by telemetry and the version check.
3
+ *
4
+ * Providers set CI to "true", "1", "yes", etc. Only an explicit off-value
5
+ * counts as "not CI", so an unknown value still suppresses outbound requests
6
+ * rather than surprising a build.
7
+ */
8
+ /**
9
+ * True when `CI` is set to anything other than an explicit off-value.
10
+ */
11
+ export declare function isCiEnvironment(env?: NodeJS.ProcessEnv): boolean;
12
+ //# sourceMappingURL=ci.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * CI environment detection shared by telemetry and the version check.
3
+ *
4
+ * Providers set CI to "true", "1", "yes", etc. Only an explicit off-value
5
+ * counts as "not CI", so an unknown value still suppresses outbound requests
6
+ * rather than surprising a build.
7
+ */
8
+ const CI_DISABLED_VALUES = new Set(['', 'false', '0', 'no', 'off']);
9
+ /**
10
+ * True when `CI` is set to anything other than an explicit off-value.
11
+ */
12
+ export function isCiEnvironment(env = process.env) {
13
+ const value = env.CI;
14
+ return value !== undefined && !CI_DISABLED_VALUES.has(value.trim().toLowerCase());
15
+ }
16
+ //# sourceMappingURL=ci.js.map
@@ -3,16 +3,102 @@
3
3
  *
4
4
  * Utilities for transforming command references to tool-specific formats.
5
5
  */
6
+ import type { CommandSurfaceCapability } from '../core/command-surface.js';
7
+ import type { CommandInvocation } from '../core/command-generation/invocation.js';
6
8
  /**
7
- * Transforms colon-based command references to hyphen-based format.
8
- * Converts `/opsx:` patterns to `/opsx-` for tools that use hyphen syntax.
9
+ * Rewrites the canonical `/opsx:<command>` references that command bodies and
10
+ * skill templates are authored with into the form one tool actually registers
11
+ * — `/opsx-<command>` for tools that name the command by filename,
12
+ * `@opsx-<command>` for Amazon Q's prompt library.
13
+ *
14
+ * Only known command ids are rewritten, matching how
15
+ * `transformToSkillReferences` leaves unrecognized references alone, so a
16
+ * mistyped or invented `/opsx:<something>` is left as written rather than
17
+ * silently reshaped into a command that does not exist either.
9
18
  *
10
19
  * @param text - The text containing command references
11
- * @returns Text with command references transformed to hyphen format
20
+ * @param invocation - The tool's invocation, from resolveCommandInvocation()
21
+ * @returns Text with command references spelled the tool's way
12
22
  *
13
23
  * @example
14
- * transformToHyphenCommands('/opsx:new') // returns '/opsx-new'
15
- * transformToHyphenCommands('Use /opsx:apply to implement') // returns 'Use /opsx-apply to implement'
24
+ * transformCommandInvocations('/opsx:new', { style: 'flat', prefix: '/' }) // '/opsx-new'
25
+ * transformCommandInvocations('/opsx:new', { style: 'flat', prefix: '@' }) // '@opsx-new'
26
+ */
27
+ export declare function transformCommandInvocations(text: string, invocation: CommandInvocation): string;
28
+ /**
29
+ * Whether a tool references skills by natural language rather than a slash
30
+ * command (see NATURAL_LANGUAGE_SKILL_TOOLS).
31
+ */
32
+ export declare function usesNaturalLanguageSkillReferences(toolId: string): boolean;
33
+ /**
34
+ * Keeps Codex's `$<name>` spelling first while making its canonical shared
35
+ * `.agents` tree usable by agents that invoke the same skills with `/<name>`.
36
+ */
37
+ export declare function transformToCodexCompatibleSkillReferences(text: string): string;
38
+ /**
39
+ * Transforms command references to skill references using the default `/`
40
+ * invocation prefix. Converts `/opsx:<command>` patterns to
41
+ * `/openspec-<skill>` so that generated skills do not reference commands
42
+ * that were never generated. Used for channels that are not tied to one
43
+ * tool (e.g. the skills.sh distribution); tool-targeted generation should
44
+ * go through getSkillReferenceTransformer instead.
45
+ *
46
+ * Unknown command references are left unchanged.
47
+ *
48
+ * @param text - The text containing command references
49
+ * @returns Text with command references transformed to skill references
50
+ *
51
+ * @example
52
+ * transformToSkillReferences('/opsx:apply') // returns '/openspec-apply-change'
53
+ * transformToSkillReferences('Use /opsx:archive next') // returns 'Use /openspec-archive-change next'
54
+ */
55
+ export declare function transformToSkillReferences(text: string): string;
56
+ /**
57
+ * Returns the skill-reference transformer for a specific tool, honoring the
58
+ * tool's documented skill invocation syntax (e.g. Kimi Code's
59
+ * `/skill:openspec-propose`). Tools with no slash surface (e.g. Rovo Dev) get
60
+ * natural-language references ("the openspec-propose skill"); everything else
61
+ * falls back to the default `/openspec-*` form.
62
+ *
63
+ * @param toolId - The AI tool identifier (e.g. 'kimi', 'vibe', 'rovodev')
64
+ * @returns A transformer converting `/opsx:*` references to skill invocations
65
+ */
66
+ export declare function getSkillReferenceTransformer(toolId: string): (text: string) => string;
67
+ /**
68
+ * Selects the command-reference transformer for a skill generation target.
69
+ *
70
+ * Skill references are used whenever the tool ends up without `/opsx:*`
71
+ * commands — because delivery is skills-only, because the tool has no command
72
+ * surface at all (capability 'none', e.g. Kimi Code or Mistral Vibe), or
73
+ * because the tool invokes skills directly and OpenSpec generates no command
74
+ * files for it (capability 'skills-invocable', i.e. Codex) — so those skills
75
+ * never point at commands that were not generated.
76
+ *
77
+ * When commands are generated, the spelling follows the tool's invocation: a
78
+ * `flat` adapter names the command by filename (`.cursor/commands/opsx-apply.md`
79
+ * → `/opsx-apply`), a `namespaced` adapter puts it in an `opsx/` directory
80
+ * (`.claude/commands/opsx/apply.md` → `/opsx:apply`), and a non-slash prefix
81
+ * wraps it further (`.amazonq/prompts/opsx-apply.md` → `@opsx-apply`). Passing
82
+ * the invocation in keeps this module free of a hand-maintained tool list —
83
+ * the list drifted and left 16 tools advertising commands their palettes never
84
+ * registered (#727, #1307).
85
+ *
86
+ * Devin is the one tool that takes skill references even though its commands
87
+ * are generated: only Devin Desktop reads `.devin/workflows/`, so a workflow
88
+ * reference is dead text for anyone on Devin Local, while the `/openspec-*`
89
+ * skills work on both agents. Under commands-only delivery there are no Devin
90
+ * skills to point at, so it falls through to the invocation rewrite below and
91
+ * gets the `/opsx-<id>` form its workflow filenames register.
92
+ *
93
+ * @param toolId - The AI tool identifier (e.g. 'claude', 'opencode', 'pi')
94
+ * @param delivery - The configured delivery mode
95
+ * @param capability - The tool's command surface capability
96
+ * @param invocation - How the tool's generated commands are invoked, from
97
+ * resolveCommandInvocation(); undefined for tools with no command
98
+ * adapter. Required rather than optional so a caller that forgets it
99
+ * fails to compile instead of silently getting the canonical form.
100
+ * @returns The transformer to pass to generateSkillContent, or undefined when
101
+ * the tool already answers to the canonical `/opsx:<id>`
16
102
  */
17
- export declare function transformToHyphenCommands(text: string): string;
103
+ export declare function getTransformerForTool(toolId: string, delivery: 'both' | 'skills' | 'commands', capability: CommandSurfaceCapability, invocation: CommandInvocation | undefined): ((text: string) => string) | undefined;
18
104
  //# sourceMappingURL=command-references.d.ts.map
@@ -3,18 +3,191 @@
3
3
  *
4
4
  * Utilities for transforming command references to tool-specific formats.
5
5
  */
6
+ // Value import of a pure, dependency-free helper: invocation.ts imports only
7
+ // `path` and a type, so this does not close the cycle the note above guards.
8
+ import { formatCommandInvocation, needsInvocationRewrite, } from '../core/command-generation/invocation.js';
6
9
  /**
7
- * Transforms colon-based command references to hyphen-based format.
8
- * Converts `/opsx:` patterns to `/opsx-` for tools that use hyphen syntax.
10
+ * Rewrites the canonical `/opsx:<command>` references that command bodies and
11
+ * skill templates are authored with into the form one tool actually registers
12
+ * — `/opsx-<command>` for tools that name the command by filename,
13
+ * `@opsx-<command>` for Amazon Q's prompt library.
14
+ *
15
+ * Only known command ids are rewritten, matching how
16
+ * `transformToSkillReferences` leaves unrecognized references alone, so a
17
+ * mistyped or invented `/opsx:<something>` is left as written rather than
18
+ * silently reshaped into a command that does not exist either.
19
+ *
20
+ * @param text - The text containing command references
21
+ * @param invocation - The tool's invocation, from resolveCommandInvocation()
22
+ * @returns Text with command references spelled the tool's way
23
+ *
24
+ * @example
25
+ * transformCommandInvocations('/opsx:new', { style: 'flat', prefix: '/' }) // '/opsx-new'
26
+ * transformCommandInvocations('/opsx:new', { style: 'flat', prefix: '@' }) // '@opsx-new'
27
+ */
28
+ export function transformCommandInvocations(text, invocation) {
29
+ return text.replace(/\/opsx:([a-z-]+)/g, (match, commandId) => commandId in COMMAND_TO_SKILL_NAME
30
+ ? formatCommandInvocation(invocation, commandId)
31
+ : match);
32
+ }
33
+ /**
34
+ * Maps command short names to their skill names.
35
+ * Keep in sync with WORKFLOW_TO_SKILL_DIR, which exists in both
36
+ * src/core/profile-sync-drift.ts (exported) and src/core/init.ts (local copy).
37
+ */
38
+ const COMMAND_TO_SKILL_NAME = {
39
+ 'explore': 'openspec-explore',
40
+ 'new': 'openspec-new-change',
41
+ 'continue': 'openspec-continue-change',
42
+ 'apply': 'openspec-apply-change',
43
+ 'update': 'openspec-update-change',
44
+ 'ff': 'openspec-ff-change',
45
+ 'sync': 'openspec-sync-specs',
46
+ 'archive': 'openspec-archive-change',
47
+ 'bulk-archive': 'openspec-bulk-archive-change',
48
+ 'verify': 'openspec-verify-change',
49
+ 'onboard': 'openspec-onboard',
50
+ 'propose': 'openspec-propose',
51
+ };
52
+ /**
53
+ * Tools whose skill invocation uses a non-default prefix. The default is `/`
54
+ * (e.g. `/openspec-propose`); Kimi Code invokes skills as `/skill:<name>` and
55
+ * Codex CLI as `$<name>` — a `/<name>` form Codex does not recognize
56
+ * (see docs/supported-tools.md).
57
+ */
58
+ const SKILL_INVOCATION_PREFIX = {
59
+ kimi: '/skill:',
60
+ codex: '$',
61
+ };
62
+ /**
63
+ * Tools that have no slash-command surface at all: skills are matched
64
+ * automatically or invoked by natural-language prompts, never by typing a
65
+ * `/<name>` command. Rovo Dev CLI is such a tool — `/skills` only manages
66
+ * skills, and any `/openspec-*` form would be a dead command (see
67
+ * docs/supported-tools.md). References for these tools are spelled as prose
68
+ * ("the openspec-propose skill") so generated content never tells the user to
69
+ * type a command their CLI does not register.
70
+ */
71
+ const NATURAL_LANGUAGE_SKILL_TOOLS = new Set(['rovodev']);
72
+ /**
73
+ * Whether a tool references skills by natural language rather than a slash
74
+ * command (see NATURAL_LANGUAGE_SKILL_TOOLS).
75
+ */
76
+ export function usesNaturalLanguageSkillReferences(toolId) {
77
+ return NATURAL_LANGUAGE_SKILL_TOOLS.has(toolId);
78
+ }
79
+ function replaceCommandsWithNaturalLanguageSkillReferences(text) {
80
+ return text.replace(/\/opsx:([a-z-]+)/g, (match, commandId) => {
81
+ const skillName = COMMAND_TO_SKILL_NAME[commandId];
82
+ return skillName === undefined ? match : `the ${skillName} skill`;
83
+ });
84
+ }
85
+ function replaceCommandsWithSkillReferences(text, prefix) {
86
+ return text.replace(/\/opsx:([a-z-]+)/g, (match, commandId) => {
87
+ const skillName = COMMAND_TO_SKILL_NAME[commandId];
88
+ return skillName === undefined ? match : `${prefix}${skillName}`;
89
+ });
90
+ }
91
+ /**
92
+ * Keeps Codex's `$<name>` spelling first while making its canonical shared
93
+ * `.agents` tree usable by agents that invoke the same skills with `/<name>`.
94
+ */
95
+ export function transformToCodexCompatibleSkillReferences(text) {
96
+ return text.replace(/\/opsx:([a-z-]+)/g, (match, commandId) => {
97
+ const skillName = COMMAND_TO_SKILL_NAME[commandId];
98
+ return skillName === undefined
99
+ ? match
100
+ : `$${skillName} (Codex) or /${skillName} (other agents)`;
101
+ });
102
+ }
103
+ /**
104
+ * Transforms command references to skill references using the default `/`
105
+ * invocation prefix. Converts `/opsx:<command>` patterns to
106
+ * `/openspec-<skill>` so that generated skills do not reference commands
107
+ * that were never generated. Used for channels that are not tied to one
108
+ * tool (e.g. the skills.sh distribution); tool-targeted generation should
109
+ * go through getSkillReferenceTransformer instead.
110
+ *
111
+ * Unknown command references are left unchanged.
9
112
  *
10
113
  * @param text - The text containing command references
11
- * @returns Text with command references transformed to hyphen format
114
+ * @returns Text with command references transformed to skill references
12
115
  *
13
116
  * @example
14
- * transformToHyphenCommands('/opsx:new') // returns '/opsx-new'
15
- * transformToHyphenCommands('Use /opsx:apply to implement') // returns 'Use /opsx-apply to implement'
117
+ * transformToSkillReferences('/opsx:apply') // returns '/openspec-apply-change'
118
+ * transformToSkillReferences('Use /opsx:archive next') // returns 'Use /openspec-archive-change next'
119
+ */
120
+ export function transformToSkillReferences(text) {
121
+ return replaceCommandsWithSkillReferences(text, '/');
122
+ }
123
+ /**
124
+ * Returns the skill-reference transformer for a specific tool, honoring the
125
+ * tool's documented skill invocation syntax (e.g. Kimi Code's
126
+ * `/skill:openspec-propose`). Tools with no slash surface (e.g. Rovo Dev) get
127
+ * natural-language references ("the openspec-propose skill"); everything else
128
+ * falls back to the default `/openspec-*` form.
129
+ *
130
+ * @param toolId - The AI tool identifier (e.g. 'kimi', 'vibe', 'rovodev')
131
+ * @returns A transformer converting `/opsx:*` references to skill invocations
132
+ */
133
+ export function getSkillReferenceTransformer(toolId) {
134
+ if (usesNaturalLanguageSkillReferences(toolId)) {
135
+ return replaceCommandsWithNaturalLanguageSkillReferences;
136
+ }
137
+ const prefix = SKILL_INVOCATION_PREFIX[toolId];
138
+ if (prefix === undefined) {
139
+ return transformToSkillReferences;
140
+ }
141
+ return (text) => replaceCommandsWithSkillReferences(text, prefix);
142
+ }
143
+ /**
144
+ * Selects the command-reference transformer for a skill generation target.
145
+ *
146
+ * Skill references are used whenever the tool ends up without `/opsx:*`
147
+ * commands — because delivery is skills-only, because the tool has no command
148
+ * surface at all (capability 'none', e.g. Kimi Code or Mistral Vibe), or
149
+ * because the tool invokes skills directly and OpenSpec generates no command
150
+ * files for it (capability 'skills-invocable', i.e. Codex) — so those skills
151
+ * never point at commands that were not generated.
152
+ *
153
+ * When commands are generated, the spelling follows the tool's invocation: a
154
+ * `flat` adapter names the command by filename (`.cursor/commands/opsx-apply.md`
155
+ * → `/opsx-apply`), a `namespaced` adapter puts it in an `opsx/` directory
156
+ * (`.claude/commands/opsx/apply.md` → `/opsx:apply`), and a non-slash prefix
157
+ * wraps it further (`.amazonq/prompts/opsx-apply.md` → `@opsx-apply`). Passing
158
+ * the invocation in keeps this module free of a hand-maintained tool list —
159
+ * the list drifted and left 16 tools advertising commands their palettes never
160
+ * registered (#727, #1307).
161
+ *
162
+ * Devin is the one tool that takes skill references even though its commands
163
+ * are generated: only Devin Desktop reads `.devin/workflows/`, so a workflow
164
+ * reference is dead text for anyone on Devin Local, while the `/openspec-*`
165
+ * skills work on both agents. Under commands-only delivery there are no Devin
166
+ * skills to point at, so it falls through to the invocation rewrite below and
167
+ * gets the `/opsx-<id>` form its workflow filenames register.
168
+ *
169
+ * @param toolId - The AI tool identifier (e.g. 'claude', 'opencode', 'pi')
170
+ * @param delivery - The configured delivery mode
171
+ * @param capability - The tool's command surface capability
172
+ * @param invocation - How the tool's generated commands are invoked, from
173
+ * resolveCommandInvocation(); undefined for tools with no command
174
+ * adapter. Required rather than optional so a caller that forgets it
175
+ * fails to compile instead of silently getting the canonical form.
176
+ * @returns The transformer to pass to generateSkillContent, or undefined when
177
+ * the tool already answers to the canonical `/opsx:<id>`
16
178
  */
17
- export function transformToHyphenCommands(text) {
18
- return text.replace(/\/opsx:/g, '/opsx-');
179
+ export function getTransformerForTool(toolId, delivery, capability, invocation) {
180
+ if (delivery === 'skills' || capability !== 'adapter-backed') {
181
+ return toolId === 'codex'
182
+ ? transformToCodexCompatibleSkillReferences
183
+ : getSkillReferenceTransformer(toolId);
184
+ }
185
+ if (toolId === 'devin' && delivery === 'both') {
186
+ return getSkillReferenceTransformer(toolId);
187
+ }
188
+ if (invocation !== undefined && needsInvocationRewrite(invocation)) {
189
+ return (text) => transformCommandInvocations(text, invocation);
190
+ }
191
+ return undefined;
19
192
  }
20
193
  //# sourceMappingURL=command-references.js.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Formats a date using the effective local time zone of the Node.js process.
3
+ *
4
+ * The result is locale-independent and suitable for date-only metadata and
5
+ * path prefixes.
6
+ */
7
+ export declare function formatLocalDate(date?: Date): string;
8
+ //# sourceMappingURL=date.d.ts.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Formats a date using the effective local time zone of the Node.js process.
3
+ *
4
+ * The result is locale-independent and suitable for date-only metadata and
5
+ * path prefixes.
6
+ */
7
+ export function formatLocalDate(date = new Date()) {
8
+ const year = date.getFullYear();
9
+ const month = String(date.getMonth() + 1).padStart(2, '0');
10
+ const day = String(date.getDate()).padStart(2, '0');
11
+ return `${year}-${month}-${day}`;
12
+ }
13
+ //# sourceMappingURL=date.js.map
@@ -9,6 +9,16 @@ export declare class FileSystemUtils {
9
9
  * Falls back to path.resolve() so callers can still produce a stable absolute path.
10
10
  */
11
11
  static canonicalizeExistingPath(targetPath: string): string;
12
+ /**
13
+ * Refuses a target that leaves an allowed directory, including through an
14
+ * existing symlink in either the target or one of its parent directories.
15
+ * Missing suffixes are resolved from their nearest existing ancestor.
16
+ */
17
+ static assertPathWithin(allowedDirectory: string, targetPath: string): void;
18
+ static resolveProjectArtifactPath(projectPath: string, artifactPath: string): string;
19
+ static assertProjectArtifactPath(projectPath: string, targetPath: string): void;
20
+ private static isPathWithin;
21
+ private static canonicalizePotentialPath;
12
22
  private static isWindowsBasePath;
13
23
  private static normalizeSegments;
14
24
  static joinPath(basePath: string, ...segments: string[]): string;
@@ -87,6 +87,76 @@ export class FileSystemUtils {
87
87
  }
88
88
  }
89
89
  }
90
+ /**
91
+ * Refuses a target that leaves an allowed directory, including through an
92
+ * existing symlink in either the target or one of its parent directories.
93
+ * Missing suffixes are resolved from their nearest existing ancestor.
94
+ */
95
+ static assertPathWithin(allowedDirectory, targetPath) {
96
+ const resolvedDirectory = path.resolve(allowedDirectory);
97
+ const resolvedTarget = path.resolve(targetPath);
98
+ if (!this.isPathWithin(resolvedDirectory, resolvedTarget)) {
99
+ throw new Error(`Path is outside the allowed directory: ${targetPath}`);
100
+ }
101
+ const canonicalDirectory = this.canonicalizePotentialPath(resolvedDirectory);
102
+ const canonicalTarget = this.canonicalizePotentialPath(resolvedTarget);
103
+ if (!this.isPathWithin(canonicalDirectory, canonicalTarget)) {
104
+ throw new Error(`Path is outside the allowed directory: ${targetPath}`);
105
+ }
106
+ }
107
+ static resolveProjectArtifactPath(projectPath, artifactPath) {
108
+ if (path.isAbsolute(artifactPath)) {
109
+ throw new Error(`Refusing to manage an artifact outside the project: ${artifactPath}`);
110
+ }
111
+ const targetPath = path.join(projectPath, artifactPath);
112
+ this.assertPathWithin(projectPath, targetPath);
113
+ return targetPath;
114
+ }
115
+ static assertProjectArtifactPath(projectPath, targetPath) {
116
+ this.assertPathWithin(projectPath, targetPath);
117
+ }
118
+ static isPathWithin(allowedDirectory, targetPath) {
119
+ const relative = path.relative(allowedDirectory, targetPath);
120
+ return (relative === '' ||
121
+ (relative !== '..' &&
122
+ !relative.startsWith(`..${path.sep}`) &&
123
+ !path.isAbsolute(relative)));
124
+ }
125
+ static canonicalizePotentialPath(targetPath) {
126
+ let existingPath = targetPath;
127
+ const missingSegments = [];
128
+ while (true) {
129
+ try {
130
+ // lstat distinguishes a missing path from a dangling symlink. A
131
+ // dangling link cannot be proven confined, so realpath must fail it.
132
+ nodeFs.lstatSync(existingPath);
133
+ const canonicalExisting = nodeFs.realpathSync.native(existingPath);
134
+ return path.resolve(canonicalExisting, ...missingSegments);
135
+ }
136
+ catch (error) {
137
+ const code = error.code;
138
+ if (code !== 'ENOENT') {
139
+ throw error;
140
+ }
141
+ try {
142
+ if (nodeFs.lstatSync(existingPath).isSymbolicLink()) {
143
+ throw new Error(`Cannot verify dangling symbolic link: ${existingPath}`);
144
+ }
145
+ }
146
+ catch (lstatError) {
147
+ if (lstatError.code !== 'ENOENT') {
148
+ throw lstatError;
149
+ }
150
+ }
151
+ const parent = path.dirname(existingPath);
152
+ if (parent === existingPath) {
153
+ throw new Error(`Cannot resolve an existing parent for ${targetPath}`);
154
+ }
155
+ missingSegments.unshift(path.basename(existingPath));
156
+ existingPath = parent;
157
+ }
158
+ }
159
+ }
90
160
  static isWindowsBasePath(basePath) {
91
161
  return /^[A-Za-z]:[\\/]/.test(basePath) || basePath.startsWith('\\');
92
162
  }
@@ -2,5 +2,5 @@ export { validateChangeName, createChange } from './change-utils.js';
2
2
  export type { ValidationResult, CreateChangeOptions } from './change-utils.js';
3
3
  export { readChangeMetadata, writeChangeMetadata, resolveSchemaForChange, validateSchemaName, ChangeMetadataError, } from './change-metadata.js';
4
4
  export { FileSystemUtils, removeMarkerBlock } from './file-system.js';
5
- export { transformToHyphenCommands } from './command-references.js';
5
+ export { transformCommandInvocations, transformToSkillReferences, getSkillReferenceTransformer, getTransformerForTool, } from './command-references.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -5,5 +5,5 @@ export { readChangeMetadata, writeChangeMetadata, resolveSchemaForChange, valida
5
5
  // File system utilities
6
6
  export { FileSystemUtils, removeMarkerBlock } from './file-system.js';
7
7
  // Command reference utilities
8
- export { transformToHyphenCommands } from './command-references.js';
8
+ export { transformCommandInvocations, transformToSkillReferences, getSkillReferenceTransformer, getTransformerForTool, } from './command-references.js';
9
9
  //# sourceMappingURL=index.js.map
@@ -15,4 +15,27 @@ export type InteractiveOptions = {
15
15
  */
16
16
  export declare function resolveNoInteractive(value?: boolean | InteractiveOptions): boolean;
17
17
  export declare function isInteractive(value?: boolean | InteractiveOptions): boolean;
18
+ /**
19
+ * True when a prompt failed because no answer could be read — an agent or a
20
+ * script that ran the command with stdin closed, a CI job, or a shell whose
21
+ * stdin is not a terminal. @inquirer rejects those with `User force closed
22
+ * the prompt with 0 null`, which is accurate and useless: it names no flag
23
+ * and no next step (#1479).
24
+ *
25
+ * Two things it deliberately is not:
26
+ *
27
+ * - It is not a substitute for `isInteractive()`. This classifies a prompt
28
+ * that has *already failed*, so piped answers are unaffected: an answer
29
+ * that arrives resolves the prompt and never reaches this check. Refusing
30
+ * to prompt up front would break `printf 'y\n' | openspec archive ...`,
31
+ * which works today.
32
+ * - It is not a cancellation check. Ctrl-C raises the same error class, and
33
+ * it reaches a process whose stdin is a pipe just as easily as one at a
34
+ * terminal, so the SIGINT signal - not the terminal - is what proves
35
+ * somebody was there and chose to quit.
36
+ *
37
+ * Beyond that it defers to `isInteractive()`, so `CI`, `OPEN_SPEC_INTERACTIVE=0`
38
+ * and `--no-interactive` count even when a runner allocated a pty.
39
+ */
40
+ export declare function isNonInteractivePromptError(error: unknown, value?: boolean | InteractiveOptions): boolean;
18
41
  //# sourceMappingURL=interactive.d.ts.map
@@ -18,4 +18,36 @@ export function isInteractive(value) {
18
18
  return false;
19
19
  return !!process.stdin.isTTY;
20
20
  }
21
+ /**
22
+ * True when a prompt failed because no answer could be read — an agent or a
23
+ * script that ran the command with stdin closed, a CI job, or a shell whose
24
+ * stdin is not a terminal. @inquirer rejects those with `User force closed
25
+ * the prompt with 0 null`, which is accurate and useless: it names no flag
26
+ * and no next step (#1479).
27
+ *
28
+ * Two things it deliberately is not:
29
+ *
30
+ * - It is not a substitute for `isInteractive()`. This classifies a prompt
31
+ * that has *already failed*, so piped answers are unaffected: an answer
32
+ * that arrives resolves the prompt and never reaches this check. Refusing
33
+ * to prompt up front would break `printf 'y\n' | openspec archive ...`,
34
+ * which works today.
35
+ * - It is not a cancellation check. Ctrl-C raises the same error class, and
36
+ * it reaches a process whose stdin is a pipe just as easily as one at a
37
+ * terminal, so the SIGINT signal - not the terminal - is what proves
38
+ * somebody was there and chose to quit.
39
+ *
40
+ * Beyond that it defers to `isInteractive()`, so `CI`, `OPEN_SPEC_INTERACTIVE=0`
41
+ * and `--no-interactive` count even when a runner allocated a pty.
42
+ */
43
+ export function isNonInteractivePromptError(error, value) {
44
+ if (!(error instanceof Error))
45
+ return false;
46
+ const failedPrompt = error.name === 'ExitPromptError' || error.message.includes('force closed the prompt');
47
+ if (!failedPrompt)
48
+ return false;
49
+ if (error.message.includes('SIGINT'))
50
+ return false;
51
+ return !isInteractive(value);
52
+ }
21
53
  //# sourceMappingURL=interactive.js.map
@@ -1,4 +1,23 @@
1
+ /**
2
+ * Returns the ids of active changes: every directory under openspec/changes/
3
+ * except the archive and hidden directories.
4
+ *
5
+ * A change is resolved by its directory alone - the same rule `list`,
6
+ * `status`, `instructions` and `validate` use (`getAvailableChanges`).
7
+ * Requiring proposal.md here made `openspec show` and shell completion miss
8
+ * changes those commands resolve: `openspec new change <name>` scaffolds only
9
+ * `.openspec.yaml`, and a custom schema need not define a proposal artifact at
10
+ * all (#1161).
11
+ */
1
12
  export declare function getActiveChangeIds(root?: string): Promise<string[]>;
2
13
  export declare function getSpecIds(root?: string): Promise<string[]>;
14
+ /**
15
+ * Returns the ids of archived changes: every directory under
16
+ * openspec/changes/archive/ except hidden directories.
17
+ *
18
+ * Resolved by directory for the same reason as `getActiveChangeIds`: a change
19
+ * archived from a schema without a proposal artifact has no proposal.md, and
20
+ * gating on it hid those entries from shell completion.
21
+ */
3
22
  export declare function getArchivedChangeIds(root?: string): Promise<string[]>;
4
23
  //# sourceMappingURL=item-discovery.d.ts.map