@codewalla_india/openspec 1.0.1

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 (356) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +225 -0
  3. package/bin/openspec.js +5 -0
  4. package/dist/cli/index.d.ts +10 -0
  5. package/dist/cli/index.js +548 -0
  6. package/dist/commands/change.d.ts +39 -0
  7. package/dist/commands/change.js +279 -0
  8. package/dist/commands/completion.d.ts +72 -0
  9. package/dist/commands/completion.js +264 -0
  10. package/dist/commands/config.d.ts +36 -0
  11. package/dist/commands/config.js +552 -0
  12. package/dist/commands/context.d.ts +3 -0
  13. package/dist/commands/context.js +155 -0
  14. package/dist/commands/doctor.d.ts +8 -0
  15. package/dist/commands/doctor.js +163 -0
  16. package/dist/commands/feedback.d.ts +9 -0
  17. package/dist/commands/feedback.js +183 -0
  18. package/dist/commands/schema.d.ts +6 -0
  19. package/dist/commands/schema.js +869 -0
  20. package/dist/commands/shared-gather.d.ts +14 -0
  21. package/dist/commands/shared-gather.js +31 -0
  22. package/dist/commands/shared-output.d.ts +18 -0
  23. package/dist/commands/shared-output.js +61 -0
  24. package/dist/commands/show.d.ts +19 -0
  25. package/dist/commands/show.js +177 -0
  26. package/dist/commands/spec.d.ts +19 -0
  27. package/dist/commands/spec.js +236 -0
  28. package/dist/commands/store.d.ts +3 -0
  29. package/dist/commands/store.js +547 -0
  30. package/dist/commands/validate.d.ts +26 -0
  31. package/dist/commands/validate.js +330 -0
  32. package/dist/commands/workflow/index.d.ts +17 -0
  33. package/dist/commands/workflow/index.js +12 -0
  34. package/dist/commands/workflow/instructions.d.ts +45 -0
  35. package/dist/commands/workflow/instructions.js +500 -0
  36. package/dist/commands/workflow/new-change.d.ts +20 -0
  37. package/dist/commands/workflow/new-change.js +106 -0
  38. package/dist/commands/workflow/schemas.d.ts +10 -0
  39. package/dist/commands/workflow/schemas.js +34 -0
  40. package/dist/commands/workflow/shared.d.ts +84 -0
  41. package/dist/commands/workflow/shared.js +133 -0
  42. package/dist/commands/workflow/status.d.ts +16 -0
  43. package/dist/commands/workflow/status.js +92 -0
  44. package/dist/commands/workflow/templates.d.ts +16 -0
  45. package/dist/commands/workflow/templates.js +69 -0
  46. package/dist/commands/workset-input.d.ts +19 -0
  47. package/dist/commands/workset-input.js +112 -0
  48. package/dist/commands/workset-prompts.d.ts +12 -0
  49. package/dist/commands/workset-prompts.js +143 -0
  50. package/dist/commands/workset.d.ts +25 -0
  51. package/dist/commands/workset.js +446 -0
  52. package/dist/core/archive.d.ts +22 -0
  53. package/dist/core/archive.js +471 -0
  54. package/dist/core/artifact-graph/graph.d.ts +56 -0
  55. package/dist/core/artifact-graph/graph.js +141 -0
  56. package/dist/core/artifact-graph/index.d.ts +9 -0
  57. package/dist/core/artifact-graph/index.js +14 -0
  58. package/dist/core/artifact-graph/instruction-loader.d.ts +188 -0
  59. package/dist/core/artifact-graph/instruction-loader.js +233 -0
  60. package/dist/core/artifact-graph/outputs.d.ts +14 -0
  61. package/dist/core/artifact-graph/outputs.js +39 -0
  62. package/dist/core/artifact-graph/resolver.d.ts +81 -0
  63. package/dist/core/artifact-graph/resolver.js +257 -0
  64. package/dist/core/artifact-graph/schema.d.ts +13 -0
  65. package/dist/core/artifact-graph/schema.js +108 -0
  66. package/dist/core/artifact-graph/state.d.ts +12 -0
  67. package/dist/core/artifact-graph/state.js +31 -0
  68. package/dist/core/artifact-graph/types.d.ts +40 -0
  69. package/dist/core/artifact-graph/types.js +29 -0
  70. package/dist/core/available-tools.d.ts +17 -0
  71. package/dist/core/available-tools.js +43 -0
  72. package/dist/core/change-metadata/index.d.ts +2 -0
  73. package/dist/core/change-metadata/index.js +2 -0
  74. package/dist/core/change-metadata/schema.d.ts +19 -0
  75. package/dist/core/change-metadata/schema.js +30 -0
  76. package/dist/core/change-status-policy.d.ts +37 -0
  77. package/dist/core/change-status-policy.js +35 -0
  78. package/dist/core/command-generation/adapters/amazon-q.d.ts +13 -0
  79. package/dist/core/command-generation/adapters/amazon-q.js +26 -0
  80. package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
  81. package/dist/core/command-generation/adapters/antigravity.js +26 -0
  82. package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
  83. package/dist/core/command-generation/adapters/auggie.js +27 -0
  84. package/dist/core/command-generation/adapters/bob.d.ts +14 -0
  85. package/dist/core/command-generation/adapters/bob.js +32 -0
  86. package/dist/core/command-generation/adapters/claude.d.ts +13 -0
  87. package/dist/core/command-generation/adapters/claude.js +37 -0
  88. package/dist/core/command-generation/adapters/cline.d.ts +14 -0
  89. package/dist/core/command-generation/adapters/cline.js +27 -0
  90. package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
  91. package/dist/core/command-generation/adapters/codebuddy.js +28 -0
  92. package/dist/core/command-generation/adapters/codex.d.ts +16 -0
  93. package/dist/core/command-generation/adapters/codex.js +39 -0
  94. package/dist/core/command-generation/adapters/continue.d.ts +13 -0
  95. package/dist/core/command-generation/adapters/continue.js +28 -0
  96. package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
  97. package/dist/core/command-generation/adapters/costrict.js +27 -0
  98. package/dist/core/command-generation/adapters/crush.d.ts +13 -0
  99. package/dist/core/command-generation/adapters/crush.js +30 -0
  100. package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
  101. package/dist/core/command-generation/adapters/cursor.js +31 -0
  102. package/dist/core/command-generation/adapters/factory.d.ts +13 -0
  103. package/dist/core/command-generation/adapters/factory.js +27 -0
  104. package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
  105. package/dist/core/command-generation/adapters/gemini.js +26 -0
  106. package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
  107. package/dist/core/command-generation/adapters/github-copilot.js +26 -0
  108. package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
  109. package/dist/core/command-generation/adapters/iflow.js +29 -0
  110. package/dist/core/command-generation/adapters/index.d.ts +32 -0
  111. package/dist/core/command-generation/adapters/index.js +32 -0
  112. package/dist/core/command-generation/adapters/junie.d.ts +13 -0
  113. package/dist/core/command-generation/adapters/junie.js +26 -0
  114. package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
  115. package/dist/core/command-generation/adapters/kilocode.js +23 -0
  116. package/dist/core/command-generation/adapters/kiro.d.ts +13 -0
  117. package/dist/core/command-generation/adapters/kiro.js +26 -0
  118. package/dist/core/command-generation/adapters/lingma.d.ts +13 -0
  119. package/dist/core/command-generation/adapters/lingma.js +30 -0
  120. package/dist/core/command-generation/adapters/opencode.d.ts +13 -0
  121. package/dist/core/command-generation/adapters/opencode.js +29 -0
  122. package/dist/core/command-generation/adapters/pi.d.ts +18 -0
  123. package/dist/core/command-generation/adapters/pi.js +42 -0
  124. package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
  125. package/dist/core/command-generation/adapters/qoder.js +30 -0
  126. package/dist/core/command-generation/adapters/qwen.d.ts +13 -0
  127. package/dist/core/command-generation/adapters/qwen.js +26 -0
  128. package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
  129. package/dist/core/command-generation/adapters/roocode.js +27 -0
  130. package/dist/core/command-generation/adapters/windsurf.d.ts +14 -0
  131. package/dist/core/command-generation/adapters/windsurf.js +38 -0
  132. package/dist/core/command-generation/generator.d.ts +21 -0
  133. package/dist/core/command-generation/generator.js +27 -0
  134. package/dist/core/command-generation/index.d.ts +22 -0
  135. package/dist/core/command-generation/index.js +24 -0
  136. package/dist/core/command-generation/registry.d.ts +36 -0
  137. package/dist/core/command-generation/registry.js +98 -0
  138. package/dist/core/command-generation/types.d.ts +56 -0
  139. package/dist/core/command-generation/types.js +8 -0
  140. package/dist/core/command-generation/yaml.d.ts +22 -0
  141. package/dist/core/command-generation/yaml.js +38 -0
  142. package/dist/core/completions/command-registry.d.ts +3 -0
  143. package/dist/core/completions/command-registry.js +778 -0
  144. package/dist/core/completions/completion-provider.d.ts +71 -0
  145. package/dist/core/completions/completion-provider.js +129 -0
  146. package/dist/core/completions/factory.d.ts +64 -0
  147. package/dist/core/completions/factory.js +75 -0
  148. package/dist/core/completions/generators/bash-generator.d.ts +35 -0
  149. package/dist/core/completions/generators/bash-generator.js +230 -0
  150. package/dist/core/completions/generators/fish-generator.d.ts +32 -0
  151. package/dist/core/completions/generators/fish-generator.js +160 -0
  152. package/dist/core/completions/generators/powershell-generator.d.ts +36 -0
  153. package/dist/core/completions/generators/powershell-generator.js +266 -0
  154. package/dist/core/completions/generators/zsh-generator.d.ts +47 -0
  155. package/dist/core/completions/generators/zsh-generator.js +276 -0
  156. package/dist/core/completions/installers/bash-installer.d.ts +87 -0
  157. package/dist/core/completions/installers/bash-installer.js +321 -0
  158. package/dist/core/completions/installers/fish-installer.d.ts +43 -0
  159. package/dist/core/completions/installers/fish-installer.js +151 -0
  160. package/dist/core/completions/installers/powershell-installer.d.ts +102 -0
  161. package/dist/core/completions/installers/powershell-installer.js +415 -0
  162. package/dist/core/completions/installers/zsh-installer.d.ts +117 -0
  163. package/dist/core/completions/installers/zsh-installer.js +424 -0
  164. package/dist/core/completions/shared-flags.d.ts +13 -0
  165. package/dist/core/completions/shared-flags.js +33 -0
  166. package/dist/core/completions/templates/bash-templates.d.ts +6 -0
  167. package/dist/core/completions/templates/bash-templates.js +30 -0
  168. package/dist/core/completions/templates/fish-templates.d.ts +7 -0
  169. package/dist/core/completions/templates/fish-templates.js +45 -0
  170. package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
  171. package/dist/core/completions/templates/powershell-templates.js +34 -0
  172. package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
  173. package/dist/core/completions/templates/zsh-templates.js +45 -0
  174. package/dist/core/completions/types.d.ts +101 -0
  175. package/dist/core/completions/types.js +2 -0
  176. package/dist/core/comprehension/config.d.ts +20 -0
  177. package/dist/core/comprehension/config.js +23 -0
  178. package/dist/core/comprehension/fingerprint.d.ts +5 -0
  179. package/dist/core/comprehension/fingerprint.js +25 -0
  180. package/dist/core/comprehension/index.d.ts +49 -0
  181. package/dist/core/comprehension/index.js +78 -0
  182. package/dist/core/comprehension/pass-record.d.ts +29 -0
  183. package/dist/core/comprehension/pass-record.js +64 -0
  184. package/dist/core/comprehension/stats.d.ts +18 -0
  185. package/dist/core/comprehension/stats.js +41 -0
  186. package/dist/core/config-prompts.d.ts +9 -0
  187. package/dist/core/config-prompts.js +34 -0
  188. package/dist/core/config-schema.d.ts +87 -0
  189. package/dist/core/config-schema.js +239 -0
  190. package/dist/core/config.d.ts +18 -0
  191. package/dist/core/config.js +39 -0
  192. package/dist/core/converters/json-converter.d.ts +6 -0
  193. package/dist/core/converters/json-converter.js +51 -0
  194. package/dist/core/file-state.d.ts +36 -0
  195. package/dist/core/file-state.js +112 -0
  196. package/dist/core/global-config.d.ts +51 -0
  197. package/dist/core/global-config.js +124 -0
  198. package/dist/core/id.d.ts +17 -0
  199. package/dist/core/id.js +30 -0
  200. package/dist/core/index.d.ts +6 -0
  201. package/dist/core/index.js +7 -0
  202. package/dist/core/init.d.ts +37 -0
  203. package/dist/core/init.js +613 -0
  204. package/dist/core/legacy-cleanup.d.ts +162 -0
  205. package/dist/core/legacy-cleanup.js +514 -0
  206. package/dist/core/list.d.ts +11 -0
  207. package/dist/core/list.js +185 -0
  208. package/dist/core/migration.d.ts +23 -0
  209. package/dist/core/migration.js +108 -0
  210. package/dist/core/openers.d.ts +77 -0
  211. package/dist/core/openers.js +251 -0
  212. package/dist/core/openspec-root.d.ts +45 -0
  213. package/dist/core/openspec-root.js +192 -0
  214. package/dist/core/parsers/change-parser.d.ts +13 -0
  215. package/dist/core/parsers/change-parser.js +197 -0
  216. package/dist/core/parsers/markdown-parser.d.ts +26 -0
  217. package/dist/core/parsers/markdown-parser.js +227 -0
  218. package/dist/core/parsers/requirement-blocks.d.ts +37 -0
  219. package/dist/core/parsers/requirement-blocks.js +201 -0
  220. package/dist/core/parsers/spec-structure.d.ts +9 -0
  221. package/dist/core/parsers/spec-structure.js +88 -0
  222. package/dist/core/planning-home.d.ts +16 -0
  223. package/dist/core/planning-home.js +67 -0
  224. package/dist/core/profile-sync-drift.d.ts +38 -0
  225. package/dist/core/profile-sync-drift.js +200 -0
  226. package/dist/core/profiles.d.ts +26 -0
  227. package/dist/core/profiles.js +40 -0
  228. package/dist/core/project-config.d.ts +120 -0
  229. package/dist/core/project-config.js +406 -0
  230. package/dist/core/references.d.ts +63 -0
  231. package/dist/core/references.js +310 -0
  232. package/dist/core/relationship-health.d.ts +65 -0
  233. package/dist/core/relationship-health.js +64 -0
  234. package/dist/core/root-selection.d.ts +122 -0
  235. package/dist/core/root-selection.js +337 -0
  236. package/dist/core/schemas/base.schema.d.ts +13 -0
  237. package/dist/core/schemas/base.schema.js +13 -0
  238. package/dist/core/schemas/change.schema.d.ts +73 -0
  239. package/dist/core/schemas/change.schema.js +31 -0
  240. package/dist/core/schemas/index.d.ts +4 -0
  241. package/dist/core/schemas/index.js +4 -0
  242. package/dist/core/schemas/spec.schema.d.ts +18 -0
  243. package/dist/core/schemas/spec.schema.js +15 -0
  244. package/dist/core/shared/index.d.ts +8 -0
  245. package/dist/core/shared/index.js +8 -0
  246. package/dist/core/shared/skill-generation.d.ts +49 -0
  247. package/dist/core/shared/skill-generation.js +96 -0
  248. package/dist/core/shared/tool-detection.d.ts +71 -0
  249. package/dist/core/shared/tool-detection.js +158 -0
  250. package/dist/core/specs-apply.d.ts +78 -0
  251. package/dist/core/specs-apply.js +394 -0
  252. package/dist/core/store/errors.d.ts +20 -0
  253. package/dist/core/store/errors.js +22 -0
  254. package/dist/core/store/foundation.d.ts +56 -0
  255. package/dist/core/store/foundation.js +251 -0
  256. package/dist/core/store/git.d.ts +23 -0
  257. package/dist/core/store/git.js +137 -0
  258. package/dist/core/store/index.d.ts +5 -0
  259. package/dist/core/store/index.js +5 -0
  260. package/dist/core/store/operations.d.ts +114 -0
  261. package/dist/core/store/operations.js +783 -0
  262. package/dist/core/store/registry.d.ts +58 -0
  263. package/dist/core/store/registry.js +275 -0
  264. package/dist/core/styles/palette.d.ts +7 -0
  265. package/dist/core/styles/palette.js +8 -0
  266. package/dist/core/templates/index.d.ts +8 -0
  267. package/dist/core/templates/index.js +9 -0
  268. package/dist/core/templates/skill-templates.d.ts +19 -0
  269. package/dist/core/templates/skill-templates.js +18 -0
  270. package/dist/core/templates/types.d.ts +19 -0
  271. package/dist/core/templates/types.js +5 -0
  272. package/dist/core/templates/workflows/apply-change.d.ts +10 -0
  273. package/dist/core/templates/workflows/apply-change.js +337 -0
  274. package/dist/core/templates/workflows/archive-change.d.ts +10 -0
  275. package/dist/core/templates/workflows/archive-change.js +278 -0
  276. package/dist/core/templates/workflows/bulk-archive-change.d.ts +10 -0
  277. package/dist/core/templates/workflows/bulk-archive-change.js +493 -0
  278. package/dist/core/templates/workflows/comprehension-guidance.d.ts +9 -0
  279. package/dist/core/templates/workflows/comprehension-guidance.js +58 -0
  280. package/dist/core/templates/workflows/continue-change.d.ts +10 -0
  281. package/dist/core/templates/workflows/continue-change.js +239 -0
  282. package/dist/core/templates/workflows/explore.d.ts +10 -0
  283. package/dist/core/templates/workflows/explore.js +464 -0
  284. package/dist/core/templates/workflows/feedback.d.ts +9 -0
  285. package/dist/core/templates/workflows/feedback.js +108 -0
  286. package/dist/core/templates/workflows/ff-change.d.ts +10 -0
  287. package/dist/core/templates/workflows/ff-change.js +205 -0
  288. package/dist/core/templates/workflows/mcp-guidance.d.ts +13 -0
  289. package/dist/core/templates/workflows/mcp-guidance.js +116 -0
  290. package/dist/core/templates/workflows/new-change.d.ts +10 -0
  291. package/dist/core/templates/workflows/new-change.js +148 -0
  292. package/dist/core/templates/workflows/onboard.d.ts +10 -0
  293. package/dist/core/templates/workflows/onboard.js +566 -0
  294. package/dist/core/templates/workflows/propose.d.ts +10 -0
  295. package/dist/core/templates/workflows/propose.js +228 -0
  296. package/dist/core/templates/workflows/store-selection.d.ts +8 -0
  297. package/dist/core/templates/workflows/store-selection.js +8 -0
  298. package/dist/core/templates/workflows/sync-specs.d.ts +10 -0
  299. package/dist/core/templates/workflows/sync-specs.js +291 -0
  300. package/dist/core/templates/workflows/verify-change.d.ts +10 -0
  301. package/dist/core/templates/workflows/verify-change.js +346 -0
  302. package/dist/core/update.d.ts +82 -0
  303. package/dist/core/update.js +557 -0
  304. package/dist/core/validation/constants.d.ts +34 -0
  305. package/dist/core/validation/constants.js +40 -0
  306. package/dist/core/validation/types.d.ts +18 -0
  307. package/dist/core/validation/types.js +2 -0
  308. package/dist/core/validation/validator.d.ts +44 -0
  309. package/dist/core/validation/validator.js +435 -0
  310. package/dist/core/view.d.ts +8 -0
  311. package/dist/core/view.js +168 -0
  312. package/dist/core/working-set.d.ts +47 -0
  313. package/dist/core/working-set.js +43 -0
  314. package/dist/core/worksets.d.ts +75 -0
  315. package/dist/core/worksets.js +245 -0
  316. package/dist/core/zod-issues.d.ts +4 -0
  317. package/dist/core/zod-issues.js +10 -0
  318. package/dist/index.d.ts +3 -0
  319. package/dist/index.js +3 -0
  320. package/dist/prompts/searchable-multi-select.d.ts +28 -0
  321. package/dist/prompts/searchable-multi-select.js +159 -0
  322. package/dist/telemetry/config.d.ts +38 -0
  323. package/dist/telemetry/config.js +136 -0
  324. package/dist/telemetry/index.d.ts +31 -0
  325. package/dist/telemetry/index.js +164 -0
  326. package/dist/ui/ascii-patterns.d.ts +16 -0
  327. package/dist/ui/ascii-patterns.js +133 -0
  328. package/dist/ui/welcome-screen.d.ts +10 -0
  329. package/dist/ui/welcome-screen.js +146 -0
  330. package/dist/utils/change-metadata.d.ts +55 -0
  331. package/dist/utils/change-metadata.js +141 -0
  332. package/dist/utils/change-utils.d.ts +71 -0
  333. package/dist/utils/change-utils.js +138 -0
  334. package/dist/utils/command-references.d.ts +18 -0
  335. package/dist/utils/command-references.js +20 -0
  336. package/dist/utils/file-system.d.ts +41 -0
  337. package/dist/utils/file-system.js +320 -0
  338. package/dist/utils/index.d.ts +6 -0
  339. package/dist/utils/index.js +9 -0
  340. package/dist/utils/interactive.d.ts +18 -0
  341. package/dist/utils/interactive.js +21 -0
  342. package/dist/utils/item-discovery.d.ts +4 -0
  343. package/dist/utils/item-discovery.js +72 -0
  344. package/dist/utils/match.d.ts +3 -0
  345. package/dist/utils/match.js +22 -0
  346. package/dist/utils/shell-detection.d.ts +20 -0
  347. package/dist/utils/shell-detection.js +41 -0
  348. package/dist/utils/task-progress.d.ts +8 -0
  349. package/dist/utils/task-progress.js +36 -0
  350. package/package.json +84 -0
  351. package/schemas/spec-driven/schema.yaml +153 -0
  352. package/schemas/spec-driven/templates/design.md +19 -0
  353. package/schemas/spec-driven/templates/proposal.md +23 -0
  354. package/schemas/spec-driven/templates/spec.md +8 -0
  355. package/schemas/spec-driven/templates/tasks.md +9 -0
  356. package/scripts/postinstall.js +83 -0
@@ -0,0 +1,192 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import { FileSystemUtils } from '../utils/file-system.js';
4
+ import { serializeConfig } from './config-prompts.js';
5
+ import { makeStoreDiagnostic, } from './store/errors.js';
6
+ export const OPENSPEC_ROOT_DIR = 'openspec';
7
+ export const OPENSPEC_CONFIG_YAML = 'openspec/config.yaml';
8
+ export const OPENSPEC_CONFIG_YML = 'openspec/config.yml';
9
+ export const OPENSPEC_SPECS_DIR = 'openspec/specs';
10
+ export const OPENSPEC_CHANGES_DIR = 'openspec/changes';
11
+ export const OPENSPEC_ARCHIVE_DIR = 'openspec/changes/archive';
12
+ export const DEFAULT_OPENSPEC_SCHEMA = 'spec-driven';
13
+ export const DIRECTORY_ANCHOR_FILE_NAME = '.gitkeep';
14
+ // Git cannot track empty directories, so clones of a fresh store would lose
15
+ // these and fail root-health checks. Anchored at setup time.
16
+ export const ANCHORED_OPENSPEC_DIRS = [OPENSPEC_SPECS_DIR, OPENSPEC_ARCHIVE_DIR];
17
+ async function pathKind(targetPath) {
18
+ try {
19
+ const stat = await fs.stat(targetPath);
20
+ if (stat.isDirectory())
21
+ return 'directory';
22
+ if (stat.isFile())
23
+ return 'file';
24
+ return 'other';
25
+ }
26
+ catch (error) {
27
+ if (typeof error === 'object' &&
28
+ error !== null &&
29
+ 'code' in error &&
30
+ error.code === 'ENOENT') {
31
+ return 'missing';
32
+ }
33
+ throw error;
34
+ }
35
+ }
36
+ function relativeArtifact(relativePath, kind) {
37
+ const normalized = FileSystemUtils.toPosixPath(relativePath);
38
+ return kind === 'directory' ? `${normalized}/` : normalized;
39
+ }
40
+ function unresolvedInspection() {
41
+ return {
42
+ present: null,
43
+ config: { present: null },
44
+ specs: { present: null },
45
+ changes: { present: null },
46
+ archive: { present: null },
47
+ healthy: false,
48
+ diagnostics: [],
49
+ };
50
+ }
51
+ function missingDirectoryDiagnostic(code, message, target) {
52
+ return makeStoreDiagnostic('error', code, message, { target });
53
+ }
54
+ export async function inspectOpenSpecRoot(storeRoot) {
55
+ const rootKind = await pathKind(storeRoot);
56
+ const inspection = unresolvedInspection();
57
+ if (rootKind === 'missing') {
58
+ inspection.diagnostics.push(missingDirectoryDiagnostic('openspec_store_root_missing', 'Store root does not exist.', 'store.root'));
59
+ return inspection;
60
+ }
61
+ if (rootKind !== 'directory') {
62
+ inspection.diagnostics.push(missingDirectoryDiagnostic('openspec_store_root_not_directory', 'Store root is not a directory.', 'store.root'));
63
+ return inspection;
64
+ }
65
+ const openspecPath = path.join(storeRoot, OPENSPEC_ROOT_DIR);
66
+ const openspecKind = await pathKind(openspecPath);
67
+ inspection.present = openspecKind === 'directory';
68
+ if (openspecKind === 'missing') {
69
+ inspection.diagnostics.push(missingDirectoryDiagnostic('openspec_root_missing', 'Missing openspec/ directory.', 'openspec.root'));
70
+ return inspection;
71
+ }
72
+ if (openspecKind !== 'directory') {
73
+ inspection.diagnostics.push(missingDirectoryDiagnostic('openspec_root_not_directory', 'openspec/ exists but is not a directory.', 'openspec.root'));
74
+ return inspection;
75
+ }
76
+ const configYamlKind = await pathKind(path.join(storeRoot, OPENSPEC_CONFIG_YAML));
77
+ const configYmlKind = await pathKind(path.join(storeRoot, OPENSPEC_CONFIG_YML));
78
+ if (configYamlKind === 'file') {
79
+ inspection.config = { present: true, path: OPENSPEC_CONFIG_YAML };
80
+ }
81
+ else if (configYmlKind === 'file') {
82
+ inspection.config = { present: true, path: OPENSPEC_CONFIG_YML };
83
+ }
84
+ else {
85
+ inspection.config = { present: false };
86
+ if (configYamlKind !== 'missing' || configYmlKind !== 'missing') {
87
+ inspection.diagnostics.push(missingDirectoryDiagnostic('openspec_config_not_file', 'OpenSpec config path exists but is not a file.', 'openspec.config'));
88
+ }
89
+ else {
90
+ inspection.diagnostics.push(missingDirectoryDiagnostic('openspec_config_missing', 'Missing openspec/config.yaml or openspec/config.yml.', 'openspec.config'));
91
+ }
92
+ }
93
+ for (const [key, relativePath, code, message, target] of [
94
+ ['specs', OPENSPEC_SPECS_DIR, 'openspec_specs_missing', 'Missing openspec/specs/.', 'openspec.specs'],
95
+ ['changes', OPENSPEC_CHANGES_DIR, 'openspec_changes_missing', 'Missing openspec/changes/.', 'openspec.changes'],
96
+ ['archive', OPENSPEC_ARCHIVE_DIR, 'openspec_archive_missing', 'Missing openspec/changes/archive/.', 'openspec.archive'],
97
+ ]) {
98
+ const kind = await pathKind(path.join(storeRoot, relativePath));
99
+ inspection[key] = { present: kind === 'directory' };
100
+ if (kind === 'directory')
101
+ continue;
102
+ inspection.diagnostics.push(missingDirectoryDiagnostic(kind === 'missing' ? code : code.replace('_missing', '_not_directory'), kind === 'missing' ? message : `${relativePath}/ exists but is not a directory.`, target));
103
+ }
104
+ inspection.healthy =
105
+ inspection.present === true &&
106
+ inspection.config.present === true &&
107
+ inspection.specs.present === true &&
108
+ inspection.changes.present === true &&
109
+ inspection.archive.present === true;
110
+ return inspection;
111
+ }
112
+ async function ensureDirectory(storeRoot, relativePath, ledger) {
113
+ const absolutePath = path.join(storeRoot, relativePath);
114
+ const kind = await pathKind(absolutePath);
115
+ if (kind === 'directory')
116
+ return;
117
+ if (kind !== 'missing') {
118
+ throw new Error(`${relativePath}/ exists but is not a directory.`);
119
+ }
120
+ await fs.mkdir(absolutePath, { recursive: true });
121
+ ledger.push({
122
+ relativePath: relativeArtifact(relativePath, 'directory'),
123
+ absolutePath,
124
+ kind: 'directory',
125
+ });
126
+ }
127
+ async function ensureDefaultConfig(storeRoot, ledger) {
128
+ const configYamlPath = path.join(storeRoot, OPENSPEC_CONFIG_YAML);
129
+ const configYmlPath = path.join(storeRoot, OPENSPEC_CONFIG_YML);
130
+ const yamlKind = await pathKind(configYamlPath);
131
+ const ymlKind = await pathKind(configYmlPath);
132
+ if (yamlKind === 'file' || ymlKind === 'file')
133
+ return;
134
+ if (yamlKind !== 'missing' || ymlKind !== 'missing') {
135
+ throw new Error('OpenSpec config path exists but is not a file.');
136
+ }
137
+ await FileSystemUtils.writeFile(configYamlPath, serializeConfig({ schema: DEFAULT_OPENSPEC_SCHEMA }));
138
+ ledger.push({
139
+ relativePath: relativeArtifact(OPENSPEC_CONFIG_YAML, 'file'),
140
+ absolutePath: configYamlPath,
141
+ kind: 'file',
142
+ });
143
+ }
144
+ async function ensureDirectoryAnchor(storeRoot, relativeDir, ledger) {
145
+ const directory = path.join(storeRoot, relativeDir);
146
+ if ((await fs.readdir(directory)).length > 0)
147
+ return;
148
+ const relativePath = `${relativeDir}/${DIRECTORY_ANCHOR_FILE_NAME}`;
149
+ const absolutePath = path.join(directory, DIRECTORY_ANCHOR_FILE_NAME);
150
+ await fs.writeFile(absolutePath, '', 'utf-8');
151
+ ledger.push({
152
+ relativePath: relativeArtifact(relativePath, 'file'),
153
+ absolutePath,
154
+ kind: 'file',
155
+ });
156
+ }
157
+ export async function ensureOpenSpecRoot(storeRoot, options = {}) {
158
+ const ledger = [];
159
+ const rootKind = await pathKind(storeRoot);
160
+ if (rootKind === 'missing') {
161
+ await fs.mkdir(storeRoot, { recursive: true });
162
+ }
163
+ else if (rootKind !== 'directory') {
164
+ throw new Error('Store root is not a directory.');
165
+ }
166
+ await ensureDirectory(storeRoot, OPENSPEC_ROOT_DIR, ledger);
167
+ await ensureDirectory(storeRoot, OPENSPEC_SPECS_DIR, ledger);
168
+ await ensureDirectory(storeRoot, OPENSPEC_CHANGES_DIR, ledger);
169
+ await ensureDirectory(storeRoot, OPENSPEC_ARCHIVE_DIR, ledger);
170
+ await ensureDefaultConfig(storeRoot, ledger);
171
+ if (options.anchorEmptyDirectories) {
172
+ for (const relativeDir of ANCHORED_OPENSPEC_DIRS) {
173
+ await ensureDirectoryAnchor(storeRoot, relativeDir, ledger);
174
+ }
175
+ }
176
+ return {
177
+ inspection: await inspectOpenSpecRoot(storeRoot),
178
+ createdArtifacts: ledger.map((entry) => entry.relativePath),
179
+ createdPaths: ledger,
180
+ };
181
+ }
182
+ export async function rollbackCreatedPaths(entries) {
183
+ for (const entry of [...entries].reverse()) {
184
+ if (entry.kind === 'file') {
185
+ await fs.rm(entry.absolutePath, { force: true }).catch(() => undefined);
186
+ }
187
+ else {
188
+ await fs.rmdir(entry.absolutePath).catch(() => undefined);
189
+ }
190
+ }
191
+ }
192
+ //# sourceMappingURL=openspec-root.js.map
@@ -0,0 +1,13 @@
1
+ import { MarkdownParser } from './markdown-parser.js';
2
+ import { Change } from '../schemas/index.js';
3
+ export declare class ChangeParser extends MarkdownParser {
4
+ private changeDir;
5
+ constructor(content: string, changeDir: string);
6
+ parseChangeWithDeltas(name: string): Promise<Change>;
7
+ private parseDeltaSpecs;
8
+ private parseSpecDeltas;
9
+ private parseRenames;
10
+ private parseSectionsFromContent;
11
+ private getContentUntilNextHeaderFromLines;
12
+ }
13
+ //# sourceMappingURL=change-parser.d.ts.map
@@ -0,0 +1,197 @@
1
+ import { MarkdownParser } from './markdown-parser.js';
2
+ import path from 'path';
3
+ import { promises as fs } from 'fs';
4
+ export class ChangeParser extends MarkdownParser {
5
+ changeDir;
6
+ constructor(content, changeDir) {
7
+ super(content);
8
+ this.changeDir = changeDir;
9
+ }
10
+ async parseChangeWithDeltas(name) {
11
+ const sections = this.parseSections();
12
+ const why = this.findSection(sections, 'Why')?.content || '';
13
+ const whatChanges = this.findSection(sections, 'What Changes')?.content || '';
14
+ if (!why) {
15
+ throw new Error('Change must have a Why section');
16
+ }
17
+ if (!whatChanges) {
18
+ throw new Error('Change must have a What Changes section');
19
+ }
20
+ // Parse deltas from the What Changes section (simple format)
21
+ const simpleDeltas = this.parseDeltas(whatChanges);
22
+ // Check if there are spec files with delta format
23
+ const specsDir = path.join(this.changeDir, 'specs');
24
+ const deltaDeltas = await this.parseDeltaSpecs(specsDir);
25
+ // Combine both types of deltas, preferring delta format if available
26
+ const deltas = deltaDeltas.length > 0 ? deltaDeltas : simpleDeltas;
27
+ return {
28
+ name,
29
+ why: why.trim(),
30
+ whatChanges: whatChanges.trim(),
31
+ deltas,
32
+ metadata: {
33
+ version: '1.0.0',
34
+ format: 'openspec-change',
35
+ },
36
+ };
37
+ }
38
+ async parseDeltaSpecs(specsDir) {
39
+ const deltas = [];
40
+ try {
41
+ const specDirs = await fs.readdir(specsDir, { withFileTypes: true });
42
+ for (const dir of specDirs) {
43
+ if (!dir.isDirectory())
44
+ continue;
45
+ const specName = dir.name;
46
+ const specFile = path.join(specsDir, specName, 'spec.md');
47
+ try {
48
+ const content = await fs.readFile(specFile, 'utf-8');
49
+ const specDeltas = this.parseSpecDeltas(specName, content);
50
+ deltas.push(...specDeltas);
51
+ }
52
+ catch (error) {
53
+ // Spec file might not exist, which is okay
54
+ continue;
55
+ }
56
+ }
57
+ }
58
+ catch (error) {
59
+ // Specs directory might not exist, which is okay
60
+ return [];
61
+ }
62
+ return deltas;
63
+ }
64
+ parseSpecDeltas(specName, content) {
65
+ const deltas = [];
66
+ const sections = this.parseSectionsFromContent(content);
67
+ // Parse ADDED requirements
68
+ const addedSection = this.findSection(sections, 'ADDED Requirements');
69
+ if (addedSection) {
70
+ const requirements = this.parseRequirements(addedSection);
71
+ requirements.forEach(req => {
72
+ deltas.push({
73
+ spec: specName,
74
+ operation: 'ADDED',
75
+ description: `Add requirement: ${req.text}`,
76
+ // Provide both single and plural forms for compatibility
77
+ requirement: req,
78
+ requirements: [req],
79
+ });
80
+ });
81
+ }
82
+ // Parse MODIFIED requirements
83
+ const modifiedSection = this.findSection(sections, 'MODIFIED Requirements');
84
+ if (modifiedSection) {
85
+ const requirements = this.parseRequirements(modifiedSection);
86
+ requirements.forEach(req => {
87
+ deltas.push({
88
+ spec: specName,
89
+ operation: 'MODIFIED',
90
+ description: `Modify requirement: ${req.text}`,
91
+ requirement: req,
92
+ requirements: [req],
93
+ });
94
+ });
95
+ }
96
+ // Parse REMOVED requirements
97
+ const removedSection = this.findSection(sections, 'REMOVED Requirements');
98
+ if (removedSection) {
99
+ const requirements = this.parseRequirements(removedSection);
100
+ requirements.forEach(req => {
101
+ deltas.push({
102
+ spec: specName,
103
+ operation: 'REMOVED',
104
+ description: `Remove requirement: ${req.text}`,
105
+ requirement: req,
106
+ requirements: [req],
107
+ });
108
+ });
109
+ }
110
+ // Parse RENAMED requirements
111
+ const renamedSection = this.findSection(sections, 'RENAMED Requirements');
112
+ if (renamedSection) {
113
+ const renames = this.parseRenames(renamedSection.content);
114
+ renames.forEach(rename => {
115
+ deltas.push({
116
+ spec: specName,
117
+ operation: 'RENAMED',
118
+ description: `Rename requirement from "${rename.from}" to "${rename.to}"`,
119
+ rename,
120
+ });
121
+ });
122
+ }
123
+ return deltas;
124
+ }
125
+ parseRenames(content) {
126
+ const renames = [];
127
+ const lines = ChangeParser.normalizeContent(content).split('\n');
128
+ let currentRename = {};
129
+ for (const line of lines) {
130
+ const fromMatch = line.match(/^\s*-?\s*FROM:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
131
+ const toMatch = line.match(/^\s*-?\s*TO:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
132
+ if (fromMatch) {
133
+ currentRename.from = fromMatch[1].trim();
134
+ }
135
+ else if (toMatch) {
136
+ currentRename.to = toMatch[1].trim();
137
+ if (currentRename.from && currentRename.to) {
138
+ renames.push({
139
+ from: currentRename.from,
140
+ to: currentRename.to,
141
+ });
142
+ currentRename = {};
143
+ }
144
+ }
145
+ }
146
+ return renames;
147
+ }
148
+ parseSectionsFromContent(content) {
149
+ const normalizedContent = ChangeParser.normalizeContent(content);
150
+ const lines = normalizedContent.split('\n');
151
+ const codeFenceLineMask = ChangeParser.buildCodeFenceMask(lines);
152
+ const sections = [];
153
+ const stack = [];
154
+ for (let i = 0; i < lines.length; i++) {
155
+ const line = lines[i];
156
+ if (codeFenceLineMask[i]) {
157
+ continue;
158
+ }
159
+ const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
160
+ if (headerMatch) {
161
+ const level = headerMatch[1].length;
162
+ const title = headerMatch[2].trim();
163
+ const contentLines = this.getContentUntilNextHeaderFromLines(lines, codeFenceLineMask, i + 1, level);
164
+ const section = {
165
+ level,
166
+ title,
167
+ content: contentLines.join('\n').trim(),
168
+ children: [],
169
+ };
170
+ while (stack.length > 0 && stack[stack.length - 1].level >= level) {
171
+ stack.pop();
172
+ }
173
+ if (stack.length === 0) {
174
+ sections.push(section);
175
+ }
176
+ else {
177
+ stack[stack.length - 1].children.push(section);
178
+ }
179
+ stack.push(section);
180
+ }
181
+ }
182
+ return sections;
183
+ }
184
+ getContentUntilNextHeaderFromLines(lines, codeFenceLineMask, startLine, currentLevel) {
185
+ const contentLines = [];
186
+ for (let i = startLine; i < lines.length; i++) {
187
+ const line = lines[i];
188
+ const headerMatch = codeFenceLineMask[i] ? null : line.match(/^(#{1,6})\s+/);
189
+ if (headerMatch && headerMatch[1].length <= currentLevel) {
190
+ break;
191
+ }
192
+ contentLines.push(line);
193
+ }
194
+ return contentLines;
195
+ }
196
+ }
197
+ //# sourceMappingURL=change-parser.js.map
@@ -0,0 +1,26 @@
1
+ import { Spec, Change, Requirement, Scenario, Delta } from '../schemas/index.js';
2
+ export interface Section {
3
+ level: number;
4
+ title: string;
5
+ content: string;
6
+ children: Section[];
7
+ }
8
+ export declare class MarkdownParser {
9
+ private lines;
10
+ private codeFenceLineMask;
11
+ private currentLine;
12
+ constructor(content: string);
13
+ protected static normalizeContent(content: string): string;
14
+ protected static buildCodeFenceMask(lines: string[]): boolean[];
15
+ private static getFenceMarker;
16
+ private static isClosingFence;
17
+ parseSpec(name: string): Spec;
18
+ parseChange(name: string): Change;
19
+ protected parseSections(): Section[];
20
+ protected getContentUntilNextHeader(startLine: number, currentLevel: number): string;
21
+ protected findSection(sections: Section[], title: string): Section | undefined;
22
+ protected parseRequirements(section: Section): Requirement[];
23
+ protected parseScenarios(requirementSection: Section): Scenario[];
24
+ protected parseDeltas(content: string): Delta[];
25
+ }
26
+ //# sourceMappingURL=markdown-parser.d.ts.map
@@ -0,0 +1,227 @@
1
+ export class MarkdownParser {
2
+ lines;
3
+ codeFenceLineMask;
4
+ currentLine;
5
+ constructor(content) {
6
+ const normalized = MarkdownParser.normalizeContent(content);
7
+ this.lines = normalized.split('\n');
8
+ this.codeFenceLineMask = MarkdownParser.buildCodeFenceMask(this.lines);
9
+ this.currentLine = 0;
10
+ }
11
+ static normalizeContent(content) {
12
+ return content.replace(/\r\n?/g, '\n');
13
+ }
14
+ static buildCodeFenceMask(lines) {
15
+ const mask = new Array(lines.length).fill(false);
16
+ let activeFence = null;
17
+ for (let i = 0; i < lines.length; i++) {
18
+ const fence = MarkdownParser.getFenceMarker(lines[i]);
19
+ if (!activeFence) {
20
+ if (fence) {
21
+ activeFence = fence;
22
+ mask[i] = true;
23
+ }
24
+ continue;
25
+ }
26
+ mask[i] = true;
27
+ if (MarkdownParser.isClosingFence(lines[i], activeFence)) {
28
+ activeFence = null;
29
+ }
30
+ }
31
+ return mask;
32
+ }
33
+ static getFenceMarker(line) {
34
+ const fenceMatch = line.match(/^\s*(`{3,}|~{3,})/);
35
+ if (!fenceMatch) {
36
+ return null;
37
+ }
38
+ return {
39
+ marker: fenceMatch[1][0],
40
+ length: fenceMatch[1].length,
41
+ };
42
+ }
43
+ static isClosingFence(line, activeFence) {
44
+ const fenceMatch = line.match(/^\s*(`{3,}|~{3,})\s*$/);
45
+ return Boolean(fenceMatch &&
46
+ fenceMatch[1][0] === activeFence.marker &&
47
+ fenceMatch[1].length >= activeFence.length);
48
+ }
49
+ parseSpec(name) {
50
+ const sections = this.parseSections();
51
+ const purpose = this.findSection(sections, 'Purpose')?.content || '';
52
+ const requirementsSection = this.findSection(sections, 'Requirements');
53
+ if (!purpose) {
54
+ throw new Error('Spec must have a Purpose section');
55
+ }
56
+ if (!requirementsSection) {
57
+ throw new Error('Spec must have a Requirements section');
58
+ }
59
+ const requirements = this.parseRequirements(requirementsSection);
60
+ return {
61
+ name,
62
+ overview: purpose.trim(),
63
+ requirements,
64
+ metadata: {
65
+ version: '1.0.0',
66
+ format: 'openspec',
67
+ },
68
+ };
69
+ }
70
+ parseChange(name) {
71
+ const sections = this.parseSections();
72
+ const why = this.findSection(sections, 'Why')?.content || '';
73
+ const whatChanges = this.findSection(sections, 'What Changes')?.content || '';
74
+ if (!why) {
75
+ throw new Error('Change must have a Why section');
76
+ }
77
+ if (!whatChanges) {
78
+ throw new Error('Change must have a What Changes section');
79
+ }
80
+ const deltas = this.parseDeltas(whatChanges);
81
+ return {
82
+ name,
83
+ why: why.trim(),
84
+ whatChanges: whatChanges.trim(),
85
+ deltas,
86
+ metadata: {
87
+ version: '1.0.0',
88
+ format: 'openspec-change',
89
+ },
90
+ };
91
+ }
92
+ parseSections() {
93
+ const sections = [];
94
+ const stack = [];
95
+ for (let i = 0; i < this.lines.length; i++) {
96
+ const line = this.lines[i];
97
+ if (this.codeFenceLineMask[i]) {
98
+ continue;
99
+ }
100
+ const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
101
+ if (headerMatch) {
102
+ const level = headerMatch[1].length;
103
+ const title = headerMatch[2].trim();
104
+ const content = this.getContentUntilNextHeader(i + 1, level);
105
+ const section = {
106
+ level,
107
+ title,
108
+ content,
109
+ children: [],
110
+ };
111
+ while (stack.length > 0 && stack[stack.length - 1].level >= level) {
112
+ stack.pop();
113
+ }
114
+ if (stack.length === 0) {
115
+ sections.push(section);
116
+ }
117
+ else {
118
+ stack[stack.length - 1].children.push(section);
119
+ }
120
+ stack.push(section);
121
+ }
122
+ }
123
+ return sections;
124
+ }
125
+ getContentUntilNextHeader(startLine, currentLevel) {
126
+ const contentLines = [];
127
+ for (let i = startLine; i < this.lines.length; i++) {
128
+ const line = this.lines[i];
129
+ const headerMatch = this.codeFenceLineMask[i] ? null : line.match(/^(#{1,6})\s+/);
130
+ if (headerMatch && headerMatch[1].length <= currentLevel) {
131
+ break;
132
+ }
133
+ contentLines.push(line);
134
+ }
135
+ return contentLines.join('\n').trim();
136
+ }
137
+ findSection(sections, title) {
138
+ for (const section of sections) {
139
+ if (section.title.toLowerCase() === title.toLowerCase()) {
140
+ return section;
141
+ }
142
+ const child = this.findSection(section.children, title);
143
+ if (child) {
144
+ return child;
145
+ }
146
+ }
147
+ return undefined;
148
+ }
149
+ parseRequirements(section) {
150
+ const requirements = [];
151
+ for (const child of section.children) {
152
+ // Extract requirement text from first non-empty content line, fall back to heading
153
+ let text = child.title;
154
+ // Get content before any child sections (scenarios)
155
+ if (child.content.trim()) {
156
+ // Split content into lines and find content before any child headers
157
+ const lines = child.content.split('\n');
158
+ const contentBeforeChildren = [];
159
+ for (const line of lines) {
160
+ // Stop at child headers (scenarios start with ####)
161
+ if (line.trim().startsWith('#')) {
162
+ break;
163
+ }
164
+ contentBeforeChildren.push(line);
165
+ }
166
+ // Find first non-empty line
167
+ const directContent = contentBeforeChildren.join('\n').trim();
168
+ if (directContent) {
169
+ const firstLine = directContent.split('\n').find(l => l.trim());
170
+ if (firstLine) {
171
+ text = firstLine.trim();
172
+ }
173
+ }
174
+ }
175
+ const scenarios = this.parseScenarios(child);
176
+ requirements.push({
177
+ text,
178
+ scenarios,
179
+ });
180
+ }
181
+ return requirements;
182
+ }
183
+ parseScenarios(requirementSection) {
184
+ const scenarios = [];
185
+ for (const scenarioSection of requirementSection.children) {
186
+ // Store the raw text content of the scenario section
187
+ if (scenarioSection.content.trim()) {
188
+ scenarios.push({
189
+ rawText: scenarioSection.content
190
+ });
191
+ }
192
+ }
193
+ return scenarios;
194
+ }
195
+ parseDeltas(content) {
196
+ const deltas = [];
197
+ const lines = content.split('\n');
198
+ for (const line of lines) {
199
+ // Match both formats: **spec:** and **spec**:
200
+ const deltaMatch = line.match(/^\s*-\s*\*\*([^*:]+)(?::\*\*|\*\*:)\s*(.+)$/);
201
+ if (deltaMatch) {
202
+ const specName = deltaMatch[1].trim();
203
+ const description = deltaMatch[2].trim();
204
+ let operation = 'MODIFIED';
205
+ const lowerDesc = description.toLowerCase();
206
+ // Use word boundaries to avoid false matches (e.g., "address" matching "add")
207
+ // Check RENAMED first since it's more specific than patterns containing "new"
208
+ if (/\brename(s|d|ing)?\b/.test(lowerDesc) || /\brenamed\s+(to|from)\b/.test(lowerDesc)) {
209
+ operation = 'RENAMED';
210
+ }
211
+ else if (/\badd(s|ed|ing)?\b/.test(lowerDesc) || /\bcreate(s|d|ing)?\b/.test(lowerDesc) || /\bnew\b/.test(lowerDesc)) {
212
+ operation = 'ADDED';
213
+ }
214
+ else if (/\bremove(s|d|ing)?\b/.test(lowerDesc) || /\bdelete(s|d|ing)?\b/.test(lowerDesc)) {
215
+ operation = 'REMOVED';
216
+ }
217
+ deltas.push({
218
+ spec: specName,
219
+ operation,
220
+ description,
221
+ });
222
+ }
223
+ }
224
+ return deltas;
225
+ }
226
+ }
227
+ //# sourceMappingURL=markdown-parser.js.map