@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,251 @@
1
+ import * as nodeFs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
4
+ import { z } from 'zod';
5
+ import { folderStyleNameProblem, isKebabId, KEBAB_ID_DESCRIPTION, KEBAB_ID_FIX, } from '../id.js';
6
+ import { getGlobalDataDir } from '../global-config.js';
7
+ import { FileSystemUtils } from '../../utils/file-system.js';
8
+ import { acquireFileLock, isNodeErrorCode, makeLockErrorFactory, pathIsDirectory, pathIsFile, releaseFileLock, writeFileAtomically, } from '../file-state.js';
9
+ import { formatZodIssues } from '../zod-issues.js';
10
+ import { StoreError } from './errors.js';
11
+ const fs = nodeFs.promises;
12
+ export const STORE_METADATA_DIR_NAME = '.openspec-store';
13
+ export const STORE_METADATA_FILE_NAME = 'store.yaml';
14
+ export const STORES_DIR_NAME = 'stores';
15
+ export const STORE_REGISTRY_FILE_NAME = 'registry.yaml';
16
+ function joinStorePath(basePath, ...segments) {
17
+ return FileSystemUtils.joinPath(basePath, ...segments);
18
+ }
19
+ export function getStoresDir(options = {}) {
20
+ return joinStorePath(options.globalDataDir ?? getGlobalDataDir(), STORES_DIR_NAME);
21
+ }
22
+ export function getStoreRegistryPath(options = {}) {
23
+ return joinStorePath(getStoresDir(options), STORE_REGISTRY_FILE_NAME);
24
+ }
25
+ export function getStoreMetadataDir(storeRoot) {
26
+ return joinStorePath(storeRoot, STORE_METADATA_DIR_NAME);
27
+ }
28
+ export function getStoreMetadataPath(storeRoot) {
29
+ return joinStorePath(getStoreMetadataDir(storeRoot), STORE_METADATA_FILE_NAME);
30
+ }
31
+ export function validateStoreId(id) {
32
+ const folderProblem = folderStyleNameProblem(id, 'Store id');
33
+ if (folderProblem !== null) {
34
+ throw new StoreError(folderProblem, 'invalid_store_id', {
35
+ target: 'store.id',
36
+ fix: KEBAB_ID_FIX,
37
+ });
38
+ }
39
+ if (!isKebabId(id)) {
40
+ throw new StoreError(`Store id ${KEBAB_ID_DESCRIPTION}`, 'invalid_store_id', {
41
+ target: 'store.id',
42
+ fix: KEBAB_ID_FIX,
43
+ });
44
+ }
45
+ return id;
46
+ }
47
+ export function isValidStoreId(id) {
48
+ try {
49
+ validateStoreId(id);
50
+ return true;
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ function isFileNotFoundError(error) {
57
+ return isNodeErrorCode(error, 'ENOENT');
58
+ }
59
+ function normalizeExistingPathForStorage(existingPath) {
60
+ return FileSystemUtils.canonicalizeExistingPath(existingPath);
61
+ }
62
+ function nonEmptyOptionalString() {
63
+ return z.string().min(1).optional();
64
+ }
65
+ const GitBackendConfigSchema = z.object({
66
+ type: z.literal('git'),
67
+ local_path: z.string().min(1),
68
+ remote: nonEmptyOptionalString(),
69
+ branch: nonEmptyOptionalString(),
70
+ }).strict();
71
+ const RegistryEntrySchema = z.object({
72
+ backend: GitBackendConfigSchema,
73
+ }).strict();
74
+ const RegistryStateSchema = z.object({
75
+ version: z.literal(1),
76
+ stores: z.record(z.string(), RegistryEntrySchema),
77
+ // Legacy code-checkout map data is tolerated on read and dropped on
78
+ // the next write.
79
+ repos: z.unknown().optional(),
80
+ }).strict();
81
+ const MetadataStateSchema = z.object({
82
+ version: z.literal(1),
83
+ id: z.string(),
84
+ remote: nonEmptyOptionalString(),
85
+ }).strict();
86
+ function storeStateDiagnostic(label) {
87
+ if (label.includes('metadata')) {
88
+ return {
89
+ code: 'invalid_store_metadata',
90
+ target: 'store.metadata',
91
+ fix: 'Repair .openspec-store/store.yaml.',
92
+ };
93
+ }
94
+ return {
95
+ code: 'invalid_store_registry',
96
+ target: 'store.registry',
97
+ fix: `Repair or remove ${getStoreRegistryPath({})}.`,
98
+ };
99
+ }
100
+ function invalidStoreStateError(label, message) {
101
+ const diagnostic = storeStateDiagnostic(label);
102
+ return new StoreError(`Invalid ${label}: ${message}`, diagnostic.code, {
103
+ target: diagnostic.target,
104
+ fix: diagnostic.fix,
105
+ });
106
+ }
107
+ function parseYamlObject(content, label) {
108
+ try {
109
+ return parseYaml(content);
110
+ }
111
+ catch (error) {
112
+ const message = error instanceof Error ? error.message : String(error);
113
+ throw invalidStoreStateError(label, message);
114
+ }
115
+ }
116
+ function assertValidStoreIds(ids, label) {
117
+ for (const id of ids) {
118
+ if (!isKebabId(id)) {
119
+ throw invalidStoreStateError(label, `'${id}': ${KEBAB_ID_DESCRIPTION}`);
120
+ }
121
+ }
122
+ }
123
+ export function parseStoreRegistryState(content) {
124
+ const raw = parseYamlObject(content, 'store registry state');
125
+ const result = RegistryStateSchema.safeParse(raw);
126
+ if (!result.success) {
127
+ throw invalidStoreStateError('store registry state', formatZodIssues(result.error));
128
+ }
129
+ assertValidStoreIds(Object.keys(result.data.stores), 'store id');
130
+ return {
131
+ version: 1,
132
+ stores: result.data.stores,
133
+ };
134
+ }
135
+ export function parseStoreMetadataState(content) {
136
+ const raw = parseYamlObject(content, 'store metadata state');
137
+ const result = MetadataStateSchema.safeParse(raw);
138
+ if (!result.success) {
139
+ throw invalidStoreStateError('store metadata state', formatZodIssues(result.error));
140
+ }
141
+ validateStoreId(result.data.id);
142
+ return {
143
+ version: 1,
144
+ id: result.data.id,
145
+ ...(result.data.remote !== undefined ? { remote: result.data.remote } : {}),
146
+ };
147
+ }
148
+ export function serializeStoreRegistryState(state) {
149
+ const result = RegistryStateSchema.safeParse(state);
150
+ if (!result.success) {
151
+ throw invalidStoreStateError('store registry state', formatZodIssues(result.error));
152
+ }
153
+ assertValidStoreIds(Object.keys(result.data.stores), 'store id');
154
+ return stringifyYaml({
155
+ version: 1,
156
+ stores: result.data.stores,
157
+ });
158
+ }
159
+ export function serializeStoreMetadataState(state) {
160
+ const result = MetadataStateSchema.safeParse(state);
161
+ if (!result.success) {
162
+ throw invalidStoreStateError('store metadata state', formatZodIssues(result.error));
163
+ }
164
+ validateStoreId(result.data.id);
165
+ return stringifyYaml({
166
+ version: 1,
167
+ id: result.data.id,
168
+ ...(result.data.remote !== undefined ? { remote: result.data.remote } : {}),
169
+ });
170
+ }
171
+ export function listStoreRegistryEntries(registry) {
172
+ return Object.entries(registry.stores)
173
+ .map(([id, store]) => ({ id, backend: store.backend }))
174
+ .sort((a, b) => a.id.localeCompare(b.id));
175
+ }
176
+ export async function isStoreRoot(candidateRoot) {
177
+ return pathIsFile(getStoreMetadataPath(candidateRoot));
178
+ }
179
+ export async function readStoreRegistryState(options = {}) {
180
+ const registryPath = getStoreRegistryPath(options);
181
+ if (!(await pathIsFile(registryPath))) {
182
+ return null;
183
+ }
184
+ return parseStoreRegistryState(await fs.readFile(registryPath, 'utf-8'));
185
+ }
186
+ export async function writeStoreRegistryState(state, options = {}) {
187
+ await writeFileAtomically(getStoreRegistryPath(options), serializeStoreRegistryState(state));
188
+ }
189
+ const storeRegistryLockError = makeLockErrorFactory({
190
+ createSubject: 'the registry lock file',
191
+ busyMessage: 'Store registry is busy.',
192
+ code: 'store_registry_busy',
193
+ target: 'store.registry',
194
+ });
195
+ export async function updateStoreRegistryState(updater, options = {}) {
196
+ const registryPath = getStoreRegistryPath(options);
197
+ const lockPath = `${registryPath}.lock`;
198
+ const lock = await acquireFileLock({
199
+ lockPath,
200
+ errorFor: storeRegistryLockError,
201
+ });
202
+ try {
203
+ const next = await updater(await readStoreRegistryState(options));
204
+ await writeStoreRegistryState(next, options);
205
+ return next;
206
+ }
207
+ finally {
208
+ await releaseFileLock(lock, lockPath);
209
+ }
210
+ }
211
+ export async function readStoreMetadataState(storeRoot) {
212
+ return parseStoreMetadataState(await fs.readFile(getStoreMetadataPath(storeRoot), 'utf-8'));
213
+ }
214
+ export async function readOptionalStoreMetadataState(storeRoot) {
215
+ try {
216
+ return await readStoreMetadataState(storeRoot);
217
+ }
218
+ catch (error) {
219
+ if (isFileNotFoundError(error)) {
220
+ return null;
221
+ }
222
+ throw error;
223
+ }
224
+ }
225
+ export async function writeStoreMetadataState(storeRoot, state) {
226
+ await FileSystemUtils.writeFile(getStoreMetadataPath(storeRoot), serializeStoreMetadataState(state));
227
+ }
228
+ export async function resolveGitStoreBackendConfig(input, cwd = process.cwd()) {
229
+ if (input.localPath.length === 0) {
230
+ throw new Error('Store local path must not be empty.');
231
+ }
232
+ const resolvedPath = path.isAbsolute(input.localPath)
233
+ ? path.resolve(input.localPath)
234
+ : path.resolve(cwd, input.localPath);
235
+ if (!(await pathIsDirectory(resolvedPath))) {
236
+ throw new Error(`Store local path does not exist: ${input.localPath}`);
237
+ }
238
+ if (input.remote !== undefined && input.remote.length === 0) {
239
+ throw new Error('Store backend remote must not be empty when provided.');
240
+ }
241
+ if (input.branch !== undefined && input.branch.length === 0) {
242
+ throw new Error('Store branch must not be empty when provided.');
243
+ }
244
+ return {
245
+ type: 'git',
246
+ local_path: normalizeExistingPathForStorage(resolvedPath),
247
+ ...(input.remote ? { remote: input.remote } : {}),
248
+ ...(input.branch ? { branch: input.branch } : {}),
249
+ };
250
+ }
251
+ //# sourceMappingURL=foundation.js.map
@@ -0,0 +1,23 @@
1
+ export declare function isGitRepositoryAtRoot(storeRoot: string): Promise<boolean>;
2
+ export declare function initGitRepository(storeRoot: string): Promise<boolean>;
3
+ /**
4
+ * `git var` resolves identity exactly as `git commit` would (config, env vars,
5
+ * auto-detection), so this fails precisely when the initial commit would.
6
+ */
7
+ export declare function assertGitCommitIdentity(probeCwd: string): Promise<void>;
8
+ /**
9
+ * Index-preserving initial commit: the pathspec on `git commit` keeps files
10
+ * the user had already staged out of setup's commit and leaves them staged.
11
+ * Pathspecs may be files or directories.
12
+ */
13
+ export declare function commitStoreFiles(storeRoot: string, id: string, pathspecs: string[]): Promise<boolean>;
14
+ export declare function gitHasCommits(storeRoot: string): Promise<boolean | null>;
15
+ export declare function gitHasUncommittedChanges(storeRoot: string): Promise<boolean | null>;
16
+ export declare function gitHasRemote(storeRoot: string): Promise<boolean | null>;
17
+ /**
18
+ * The configured origin URL, read from local Git config only — never a
19
+ * network touch. Null when there is no repository or no origin.
20
+ */
21
+ export declare function gitOriginUrl(storeRoot: string): Promise<string | null>;
22
+ export declare function gitDirectoryHasTrackedFiles(storeRoot: string, relativeDir: string): Promise<boolean | null>;
23
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1,137 @@
1
+ import { execFile } from 'node:child_process';
2
+ import * as nodeFs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import { promisify } from 'node:util';
5
+ import { StoreError } from './errors.js';
6
+ const fs = nodeFs.promises;
7
+ const execFileAsync = promisify(execFile);
8
+ /**
9
+ * Git mechanics for stores: repository detection, setup-time init and
10
+ * commit, and the read-only facts doctor reports. Nothing here clones, pulls,
11
+ * pushes, or syncs — setup-time `git init` plus one initial commit is the
12
+ * entire write surface.
13
+ */
14
+ function isSpawnNotFoundError(error) {
15
+ return (typeof error === 'object' &&
16
+ error !== null &&
17
+ 'code' in error &&
18
+ error.code === 'ENOENT');
19
+ }
20
+ export async function isGitRepositoryAtRoot(storeRoot) {
21
+ try {
22
+ const stat = await fs.stat(path.join(storeRoot, '.git'));
23
+ return stat.isDirectory() || stat.isFile();
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ export async function initGitRepository(storeRoot) {
30
+ if (await isGitRepositoryAtRoot(storeRoot)) {
31
+ return false;
32
+ }
33
+ try {
34
+ await execFileAsync('git', ['init'], { cwd: storeRoot });
35
+ }
36
+ catch (error) {
37
+ throw new StoreError(`Failed to initialize Git repository: ${error instanceof Error ? error.message : String(error)}`, 'store_git_init_failed', {
38
+ target: 'store.git',
39
+ fix: 'Install Git or rerun setup with --no-init-git.',
40
+ });
41
+ }
42
+ return true;
43
+ }
44
+ /**
45
+ * `git var` resolves identity exactly as `git commit` would (config, env vars,
46
+ * auto-detection), so this fails precisely when the initial commit would.
47
+ */
48
+ export async function assertGitCommitIdentity(probeCwd) {
49
+ for (const identVar of ['GIT_COMMITTER_IDENT', 'GIT_AUTHOR_IDENT']) {
50
+ try {
51
+ await execFileAsync('git', ['var', identVar], { cwd: probeCwd });
52
+ }
53
+ catch (error) {
54
+ if (isSpawnNotFoundError(error)) {
55
+ throw new StoreError('Git is not available, so setup cannot create the initial store commit.', 'store_git_init_failed', {
56
+ target: 'store.git',
57
+ fix: 'Install Git or rerun setup with --no-init-git.',
58
+ });
59
+ }
60
+ throw new StoreError('No usable Git commit identity is configured, so setup cannot create the initial store commit.', 'store_git_identity_missing', {
61
+ target: 'store.git',
62
+ fix: 'Run git config --global user.name "Your Name" and git config --global user.email "you@example.com", or rerun setup with --no-init-git.',
63
+ });
64
+ }
65
+ }
66
+ }
67
+ /**
68
+ * Index-preserving initial commit: the pathspec on `git commit` keeps files
69
+ * the user had already staged out of setup's commit and leaves them staged.
70
+ * Pathspecs may be files or directories.
71
+ */
72
+ export async function commitStoreFiles(storeRoot, id, pathspecs) {
73
+ if (pathspecs.length === 0) {
74
+ return false;
75
+ }
76
+ try {
77
+ await execFileAsync('git', ['add', '--', ...pathspecs], { cwd: storeRoot });
78
+ await execFileAsync('git', ['commit', '-m', `Initialize OpenSpec store ${id}`, '--', ...pathspecs], { cwd: storeRoot });
79
+ }
80
+ catch (error) {
81
+ // Best-effort unstage so a failed commit (gpg signing, hooks) does not
82
+ // leave setup's files in the user's index after rollback deletes them.
83
+ await execFileAsync('git', ['rm', '--cached', '-r', '-f', '-q', '--', ...pathspecs], {
84
+ cwd: storeRoot,
85
+ }).catch(() => undefined);
86
+ throw new StoreError(`Failed to create the initial store commit: ${error instanceof Error ? error.message : String(error)}`, 'store_git_commit_failed', {
87
+ target: 'store.git',
88
+ fix: 'Commit the created files manually, or rerun setup with --no-init-git.',
89
+ });
90
+ }
91
+ return true;
92
+ }
93
+ async function gitProbe(storeRoot, args) {
94
+ try {
95
+ const { stdout } = await execFileAsync('git', ['-C', storeRoot, ...args]);
96
+ return stdout;
97
+ }
98
+ catch {
99
+ return null;
100
+ }
101
+ }
102
+ export async function gitHasCommits(storeRoot) {
103
+ try {
104
+ await execFileAsync('git', ['-C', storeRoot, 'rev-parse', '--verify', '--quiet', 'HEAD']);
105
+ return true;
106
+ }
107
+ catch (error) {
108
+ if (isSpawnNotFoundError(error))
109
+ return null;
110
+ // Exit 1 = repo exists but HEAD has no commits. Anything else (exit 128:
111
+ // corrupt or fake .git) is unknown, not "commitless".
112
+ const exitCode = error.code;
113
+ return exitCode === 1 ? false : null;
114
+ }
115
+ }
116
+ export async function gitHasUncommittedChanges(storeRoot) {
117
+ const stdout = await gitProbe(storeRoot, ['status', '--porcelain']);
118
+ return stdout === null ? null : stdout.trim().length > 0;
119
+ }
120
+ export async function gitHasRemote(storeRoot) {
121
+ const stdout = await gitProbe(storeRoot, ['remote']);
122
+ return stdout === null ? null : stdout.trim().length > 0;
123
+ }
124
+ /**
125
+ * The configured origin URL, read from local Git config only — never a
126
+ * network touch. Null when there is no repository or no origin.
127
+ */
128
+ export async function gitOriginUrl(storeRoot) {
129
+ const stdout = await gitProbe(storeRoot, ['remote', 'get-url', 'origin']);
130
+ const url = stdout?.trim();
131
+ return url ? url : null;
132
+ }
133
+ export async function gitDirectoryHasTrackedFiles(storeRoot, relativeDir) {
134
+ const stdout = await gitProbe(storeRoot, ['ls-files', '--', relativeDir]);
135
+ return stdout === null ? null : stdout.trim().length > 0;
136
+ }
137
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1,5 @@
1
+ export * from './foundation.js';
2
+ export * from './errors.js';
3
+ export * from './registry.js';
4
+ export * from './operations.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,5 @@
1
+ export * from './foundation.js';
2
+ export * from './errors.js';
3
+ export * from './registry.js';
4
+ export * from './operations.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,114 @@
1
+ import { type OpenSpecRootInspection } from '../openspec-root.js';
2
+ import { type StoreGitBackendConfig, type StorePathOptions, type StoreRegistryState } from './foundation.js';
3
+ import { type StoreDiagnostic } from './errors.js';
4
+ type PathKind = 'missing' | 'directory' | 'file' | 'other';
5
+ export interface StoreInfo {
6
+ id: string;
7
+ root: string;
8
+ metadataPath?: string;
9
+ }
10
+ export interface StoreMutationResult {
11
+ store: StoreInfo;
12
+ /** Clone-source knowledge for human sharing guidance; never in JSON. */
13
+ remotes?: {
14
+ canonical?: string;
15
+ observed?: string;
16
+ };
17
+ registryCommit: {
18
+ path: string;
19
+ registered: boolean;
20
+ alreadyRegistered: boolean;
21
+ };
22
+ git: {
23
+ isRepository: boolean;
24
+ initialized: boolean;
25
+ committed: boolean;
26
+ };
27
+ createdArtifacts: string[];
28
+ diagnostics: StoreDiagnostic[];
29
+ }
30
+ export interface StoreCleanupResult {
31
+ store: StoreInfo;
32
+ registryCommit: {
33
+ path: string;
34
+ removed: boolean;
35
+ };
36
+ files: {
37
+ deleted: boolean;
38
+ deletedPath?: string;
39
+ leftOnDisk?: string;
40
+ };
41
+ diagnostics: StoreDiagnostic[];
42
+ }
43
+ export interface StoreListResult {
44
+ stores: StoreInfo[];
45
+ }
46
+ export interface StoreDoctorResult {
47
+ stores: StoreInspection[];
48
+ diagnostics: StoreDiagnostic[];
49
+ }
50
+ export interface StoreInspection extends StoreInfo {
51
+ openspecRoot: OpenSpecRootInspection;
52
+ metadata: {
53
+ present: boolean | null;
54
+ valid: boolean | null;
55
+ id?: string;
56
+ /** Canonical clone source from store.yaml; null when absent. */
57
+ remote: string | null;
58
+ };
59
+ git: {
60
+ isRepository: boolean | null;
61
+ hasCommits: boolean | null;
62
+ hasUncommittedChanges: boolean | null;
63
+ hasRemote: boolean | null;
64
+ /** Observed origin URL, live-probed; null when none. */
65
+ originUrl: string | null;
66
+ };
67
+ diagnostics: StoreDiagnostic[];
68
+ }
69
+ export interface SetupStoreInput {
70
+ id?: string;
71
+ path?: string;
72
+ initGit?: boolean;
73
+ allowInsideGitRepository?: boolean;
74
+ /** Canonical clone source written into store.yaml (slice 3.3). */
75
+ remote?: string;
76
+ }
77
+ export interface RegisterExistingStoreInput {
78
+ path?: string;
79
+ id?: string;
80
+ allowCreateIdentity?: boolean;
81
+ }
82
+ export interface CleanupStoreInput extends StorePathOptions {
83
+ id: string;
84
+ }
85
+ export interface PreparedStoreCleanup extends StoreInfo, StorePathOptions {
86
+ backend: StoreGitBackendConfig;
87
+ }
88
+ export interface PreparedStoreSetup {
89
+ id: string;
90
+ root: string;
91
+ rootKind: Extract<PathKind, 'missing' | 'directory'>;
92
+ backend?: StoreGitBackendConfig;
93
+ registry: StoreRegistryState | null;
94
+ remote?: string;
95
+ }
96
+ export declare function expandUserPath(inputPath: string): string;
97
+ /**
98
+ * Resolves the effective Git mode for a prepared setup: on by default for new
99
+ * stores, off for reruns of an already-registered store (which must stay
100
+ * no-ops), and always honoring an explicit --init-git/--no-init-git.
101
+ */
102
+ export declare function resolveSetupGitEnabled(prepared: PreparedStoreSetup, initGit?: boolean): boolean;
103
+ export declare function prepareStoreSetup(input: Pick<SetupStoreInput, 'id' | 'path' | 'allowInsideGitRepository' | 'remote'>): Promise<PreparedStoreSetup>;
104
+ export declare function setupPreparedStore(prepared: PreparedStoreSetup, input?: Pick<SetupStoreInput, 'initGit'>): Promise<StoreMutationResult>;
105
+ export declare function setupStore(input: SetupStoreInput): Promise<StoreMutationResult>;
106
+ export declare function registerExistingStore(input: RegisterExistingStoreInput): Promise<StoreMutationResult>;
107
+ export declare function prepareStoreCleanup(input: CleanupStoreInput): Promise<PreparedStoreCleanup>;
108
+ export declare function unregisterStore(input: CleanupStoreInput): Promise<StoreCleanupResult>;
109
+ export declare function removeStore(target: PreparedStoreCleanup): Promise<StoreCleanupResult>;
110
+ export declare function listStores(): Promise<StoreListResult>;
111
+ export declare function doctorStores(id?: string): Promise<StoreDoctorResult>;
112
+ export declare function normalizeStorePathForComparison(targetPath: string): string;
113
+ export {};
114
+ //# sourceMappingURL=operations.d.ts.map