@niujiajie/freespec 0.0.0-stage → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +58 -2
  3. package/bin/freespec.js +5 -0
  4. package/dist/cli/index.d.ts +25 -0
  5. package/dist/cli/index.js +359 -0
  6. package/dist/commands/shared-output.d.ts +18 -0
  7. package/dist/commands/shared-output.js +61 -0
  8. package/dist/commands/workflow/default-schema.d.ts +2 -0
  9. package/dist/commands/workflow/default-schema.js +4 -0
  10. package/dist/commands/workflow/index.d.ts +13 -0
  11. package/dist/commands/workflow/index.js +10 -0
  12. package/dist/commands/workflow/instructions.d.ts +39 -0
  13. package/dist/commands/workflow/instructions.js +565 -0
  14. package/dist/commands/workflow/new-change.d.ts +19 -0
  15. package/dist/commands/workflow/new-change.js +135 -0
  16. package/dist/commands/workflow/shared.d.ts +94 -0
  17. package/dist/commands/workflow/shared.js +169 -0
  18. package/dist/commands/workflow/status.d.ts +18 -0
  19. package/dist/commands/workflow/status.js +201 -0
  20. package/dist/core/artifact-graph/graph.d.ts +72 -0
  21. package/dist/core/artifact-graph/graph.js +165 -0
  22. package/dist/core/artifact-graph/index.d.ts +9 -0
  23. package/dist/core/artifact-graph/index.js +14 -0
  24. package/dist/core/artifact-graph/instruction-loader.d.ts +208 -0
  25. package/dist/core/artifact-graph/instruction-loader.js +268 -0
  26. package/dist/core/artifact-graph/outputs.d.ts +19 -0
  27. package/dist/core/artifact-graph/outputs.js +351 -0
  28. package/dist/core/artifact-graph/resolver.d.ts +89 -0
  29. package/dist/core/artifact-graph/resolver.js +342 -0
  30. package/dist/core/artifact-graph/schema.d.ts +27 -0
  31. package/dist/core/artifact-graph/schema.js +160 -0
  32. package/dist/core/artifact-graph/state.d.ts +12 -0
  33. package/dist/core/artifact-graph/state.js +31 -0
  34. package/dist/core/artifact-graph/types.d.ts +40 -0
  35. package/dist/core/artifact-graph/types.js +59 -0
  36. package/dist/core/available-tools.d.ts +18 -0
  37. package/dist/core/available-tools.js +49 -0
  38. package/dist/core/change-layout.d.ts +17 -0
  39. package/dist/core/change-layout.js +30 -0
  40. package/dist/core/change-metadata/index.d.ts +2 -0
  41. package/dist/core/change-metadata/index.js +2 -0
  42. package/dist/core/change-metadata/schema.d.ts +23 -0
  43. package/dist/core/change-metadata/schema.js +46 -0
  44. package/dist/core/change-status-policy.d.ts +49 -0
  45. package/dist/core/change-status-policy.js +50 -0
  46. package/dist/core/command-generation/adapters/amazon-q.d.ts +17 -0
  47. package/dist/core/command-generation/adapters/amazon-q.js +32 -0
  48. package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
  49. package/dist/core/command-generation/adapters/antigravity.js +27 -0
  50. package/dist/core/command-generation/adapters/atomcode.d.ts +24 -0
  51. package/dist/core/command-generation/adapters/atomcode.js +46 -0
  52. package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
  53. package/dist/core/command-generation/adapters/auggie.js +28 -0
  54. package/dist/core/command-generation/adapters/bob.d.ts +18 -0
  55. package/dist/core/command-generation/adapters/bob.js +33 -0
  56. package/dist/core/command-generation/adapters/claude.d.ts +13 -0
  57. package/dist/core/command-generation/adapters/claude.js +32 -0
  58. package/dist/core/command-generation/adapters/cline.d.ts +14 -0
  59. package/dist/core/command-generation/adapters/cline.js +27 -0
  60. package/dist/core/command-generation/adapters/codeassistant.d.ts +15 -0
  61. package/dist/core/command-generation/adapters/codeassistant.js +29 -0
  62. package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
  63. package/dist/core/command-generation/adapters/codebuddy.js +29 -0
  64. package/dist/core/command-generation/adapters/codestudio.d.ts +13 -0
  65. package/dist/core/command-generation/adapters/codestudio.js +27 -0
  66. package/dist/core/command-generation/adapters/command-code.d.ts +19 -0
  67. package/dist/core/command-generation/adapters/command-code.js +34 -0
  68. package/dist/core/command-generation/adapters/continue.d.ts +13 -0
  69. package/dist/core/command-generation/adapters/continue.js +33 -0
  70. package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
  71. package/dist/core/command-generation/adapters/costrict.js +28 -0
  72. package/dist/core/command-generation/adapters/crush.d.ts +13 -0
  73. package/dist/core/command-generation/adapters/crush.js +30 -0
  74. package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
  75. package/dist/core/command-generation/adapters/cursor.js +31 -0
  76. package/dist/core/command-generation/adapters/devin.d.ts +19 -0
  77. package/dist/core/command-generation/adapters/devin.js +36 -0
  78. package/dist/core/command-generation/adapters/easycode.d.ts +17 -0
  79. package/dist/core/command-generation/adapters/easycode.js +28 -0
  80. package/dist/core/command-generation/adapters/factory.d.ts +13 -0
  81. package/dist/core/command-generation/adapters/factory.js +28 -0
  82. package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
  83. package/dist/core/command-generation/adapters/gemini.js +27 -0
  84. package/dist/core/command-generation/adapters/gigacode.d.ts +17 -0
  85. package/dist/core/command-generation/adapters/gigacode.js +31 -0
  86. package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
  87. package/dist/core/command-generation/adapters/github-copilot.js +27 -0
  88. package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
  89. package/dist/core/command-generation/adapters/iflow.js +30 -0
  90. package/dist/core/command-generation/adapters/index.d.ts +40 -0
  91. package/dist/core/command-generation/adapters/index.js +40 -0
  92. package/dist/core/command-generation/adapters/junie.d.ts +13 -0
  93. package/dist/core/command-generation/adapters/junie.js +27 -0
  94. package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
  95. package/dist/core/command-generation/adapters/kilocode.js +23 -0
  96. package/dist/core/command-generation/adapters/kiro.d.ts +13 -0
  97. package/dist/core/command-generation/adapters/kiro.js +27 -0
  98. package/dist/core/command-generation/adapters/lingma.d.ts +13 -0
  99. package/dist/core/command-generation/adapters/lingma.js +30 -0
  100. package/dist/core/command-generation/adapters/oh-my-pi.d.ts +21 -0
  101. package/dist/core/command-generation/adapters/oh-my-pi.js +42 -0
  102. package/dist/core/command-generation/adapters/opencode.d.ts +14 -0
  103. package/dist/core/command-generation/adapters/opencode.js +38 -0
  104. package/dist/core/command-generation/adapters/pi.d.ts +18 -0
  105. package/dist/core/command-generation/adapters/pi.js +39 -0
  106. package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
  107. package/dist/core/command-generation/adapters/qoder.js +30 -0
  108. package/dist/core/command-generation/adapters/qwen.d.ts +17 -0
  109. package/dist/core/command-generation/adapters/qwen.js +31 -0
  110. package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
  111. package/dist/core/command-generation/adapters/roocode.js +27 -0
  112. package/dist/core/command-generation/adapters/trae.d.ts +13 -0
  113. package/dist/core/command-generation/adapters/trae.js +28 -0
  114. package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
  115. package/dist/core/command-generation/adapters/zcode.js +33 -0
  116. package/dist/core/command-generation/generator.d.ts +29 -0
  117. package/dist/core/command-generation/generator.js +43 -0
  118. package/dist/core/command-generation/index.d.ts +22 -0
  119. package/dist/core/command-generation/index.js +24 -0
  120. package/dist/core/command-generation/invocation.d.ts +73 -0
  121. package/dist/core/command-generation/invocation.js +77 -0
  122. package/dist/core/command-generation/registry.d.ts +36 -0
  123. package/dist/core/command-generation/registry.js +114 -0
  124. package/dist/core/command-generation/toml.d.ts +19 -0
  125. package/dist/core/command-generation/toml.js +39 -0
  126. package/dist/core/command-generation/types.d.ts +63 -0
  127. package/dist/core/command-generation/types.js +8 -0
  128. package/dist/core/command-generation/yaml.d.ts +36 -0
  129. package/dist/core/command-generation/yaml.js +48 -0
  130. package/dist/core/command-surface.d.ts +15 -0
  131. package/dist/core/command-surface.js +33 -0
  132. package/dist/core/config-prompts.d.ts +9 -0
  133. package/dist/core/config-prompts.js +56 -0
  134. package/dist/core/config-schema.d.ts +98 -0
  135. package/dist/core/config-schema.js +308 -0
  136. package/dist/core/config.d.ts +50 -0
  137. package/dist/core/config.js +122 -0
  138. package/dist/core/file-state.d.ts +36 -0
  139. package/dist/core/file-state.js +144 -0
  140. package/dist/core/file-view.d.ts +13 -0
  141. package/dist/core/file-view.js +14 -0
  142. package/dist/core/global-config.d.ts +101 -0
  143. package/dist/core/global-config.js +182 -0
  144. package/dist/core/id.d.ts +17 -0
  145. package/dist/core/id.js +30 -0
  146. package/dist/core/index.d.ts +6 -0
  147. package/dist/core/index.js +7 -0
  148. package/dist/core/init.d.ts +57 -0
  149. package/dist/core/init.js +836 -0
  150. package/dist/core/list.d.ts +11 -0
  151. package/dist/core/list.js +197 -0
  152. package/dist/core/migration.d.ts +121 -0
  153. package/dist/core/migration.js +540 -0
  154. package/dist/core/onboarding-commands.d.ts +30 -0
  155. package/dist/core/onboarding-commands.js +38 -0
  156. package/dist/core/openspec-root.d.ts +47 -0
  157. package/dist/core/openspec-root.js +196 -0
  158. package/dist/core/planning-home.d.ts +16 -0
  159. package/dist/core/planning-home.js +67 -0
  160. package/dist/core/profile-sync-drift.d.ts +31 -0
  161. package/dist/core/profile-sync-drift.js +201 -0
  162. package/dist/core/profiles.d.ts +19 -0
  163. package/dist/core/profiles.js +33 -0
  164. package/dist/core/project-config.d.ts +132 -0
  165. package/dist/core/project-config.js +519 -0
  166. package/dist/core/references.d.ts +10 -0
  167. package/dist/core/references.js +46 -0
  168. package/dist/core/root-selection.d.ts +59 -0
  169. package/dist/core/root-selection.js +132 -0
  170. package/dist/core/shared/allowed-tools.d.ts +12 -0
  171. package/dist/core/shared/allowed-tools.js +12 -0
  172. package/dist/core/shared/ide-restart.d.ts +26 -0
  173. package/dist/core/shared/ide-restart.js +42 -0
  174. package/dist/core/shared/index.d.ts +11 -0
  175. package/dist/core/shared/index.js +11 -0
  176. package/dist/core/shared/skill-content-equivalence.d.ts +7 -0
  177. package/dist/core/shared/skill-content-equivalence.js +45 -0
  178. package/dist/core/shared/skill-files.d.ts +7 -0
  179. package/dist/core/shared/skill-files.js +43 -0
  180. package/dist/core/shared/skill-generation.d.ts +49 -0
  181. package/dist/core/shared/skill-generation.js +125 -0
  182. package/dist/core/shared/skill-paths.d.ts +13 -0
  183. package/dist/core/shared/skill-paths.js +23 -0
  184. package/dist/core/shared/tool-detection.d.ts +99 -0
  185. package/dist/core/shared/tool-detection.js +318 -0
  186. package/dist/core/shared-skill-target.d.ts +51 -0
  187. package/dist/core/shared-skill-target.js +239 -0
  188. package/dist/core/store/errors.d.ts +20 -0
  189. package/dist/core/store/errors.js +22 -0
  190. package/dist/core/store/index.d.ts +2 -0
  191. package/dist/core/store/index.js +2 -0
  192. package/dist/core/styles/palette.d.ts +7 -0
  193. package/dist/core/styles/palette.js +8 -0
  194. package/dist/core/templates/optional-workflow.d.ts +69 -0
  195. package/dist/core/templates/optional-workflow.js +140 -0
  196. package/dist/core/templates/types.d.ts +19 -0
  197. package/dist/core/templates/types.js +5 -0
  198. package/dist/core/templates/workflows/personal.d.ts +22 -0
  199. package/dist/core/templates/workflows/personal.js +225 -0
  200. package/dist/core/update-plan.d.ts +127 -0
  201. package/dist/core/update-plan.js +390 -0
  202. package/dist/core/update.d.ts +154 -0
  203. package/dist/core/update.js +723 -0
  204. package/dist/core/version-check.d.ts +188 -0
  205. package/dist/core/version-check.js +906 -0
  206. package/dist/core/zod-issues.d.ts +4 -0
  207. package/dist/core/zod-issues.js +10 -0
  208. package/dist/index.d.ts +3 -0
  209. package/dist/index.js +3 -0
  210. package/dist/prompts/searchable-multi-select.d.ts +35 -0
  211. package/dist/prompts/searchable-multi-select.js +188 -0
  212. package/dist/telemetry/config.d.ts +37 -0
  213. package/dist/telemetry/config.js +148 -0
  214. package/dist/telemetry/index.d.ts +39 -0
  215. package/dist/telemetry/index.js +217 -0
  216. package/dist/telemetry/opt-out.d.ts +17 -0
  217. package/dist/telemetry/opt-out.js +27 -0
  218. package/dist/ui/ascii-patterns.d.ts +16 -0
  219. package/dist/ui/ascii-patterns.js +133 -0
  220. package/dist/ui/welcome-screen.d.ts +18 -0
  221. package/dist/ui/welcome-screen.js +199 -0
  222. package/dist/utils/change-metadata.d.ts +116 -0
  223. package/dist/utils/change-metadata.js +324 -0
  224. package/dist/utils/change-utils.d.ts +79 -0
  225. package/dist/utils/change-utils.js +121 -0
  226. package/dist/utils/ci.d.ts +12 -0
  227. package/dist/utils/ci.js +16 -0
  228. package/dist/utils/command-references.d.ts +104 -0
  229. package/dist/utils/command-references.js +190 -0
  230. package/dist/utils/date.d.ts +8 -0
  231. package/dist/utils/date.js +13 -0
  232. package/dist/utils/executable-path.d.ts +5 -0
  233. package/dist/utils/executable-path.js +44 -0
  234. package/dist/utils/file-system.d.ts +58 -0
  235. package/dist/utils/file-system.js +432 -0
  236. package/dist/utils/index.d.ts +6 -0
  237. package/dist/utils/index.js +9 -0
  238. package/dist/utils/interactive.d.ts +85 -0
  239. package/dist/utils/interactive.js +174 -0
  240. package/dist/utils/item-discovery.d.ts +15 -0
  241. package/dist/utils/item-discovery.js +33 -0
  242. package/dist/utils/line-endings.d.ts +34 -0
  243. package/dist/utils/line-endings.js +46 -0
  244. package/dist/utils/match.d.ts +3 -0
  245. package/dist/utils/match.js +22 -0
  246. package/dist/utils/nested-change.d.ts +35 -0
  247. package/dist/utils/nested-change.js +185 -0
  248. package/dist/utils/shell-detection.d.ts +20 -0
  249. package/dist/utils/shell-detection.js +94 -0
  250. package/dist/utils/task-progress.d.ts +67 -0
  251. package/dist/utils/task-progress.js +184 -0
  252. package/package.json +74 -4
  253. package/schemas//351/273/230/350/256/244/schema.yaml +68 -0
  254. package/schemas//351/273/230/350/256/244/templates//344/273/273/345/212/241/346/270/205/345/215/225.md +25 -0
  255. package/schemas//351/273/230/350/256/244/templates//346/265/213/350/257/225/347/224/250/344/276/213.md +34 -0
  256. package/schemas//351/273/230/350/256/244/templates//350/256/276/350/256/241/346/226/207/346/241/243.md +172 -0
  257. package/schemas//351/273/230/350/256/244/templates//351/234/200/346/261/202/346/226/207/346/241/243.md +19 -0
  258. package/skill-files//344/273/273/345/212/241/references//346/212/244/346/240/217.md +43 -0
  259. package/skill-files//344/273/273/345/212/241/references//347/262/222/345/272/246.md +23 -0
  260. package/skill-files//344/273/273/345/212/241/references//347/272/246/346/235/237.md +8 -0
  261. package/skill-files//344/273/273/345/212/241/scripts/check-tasks.mjs +83 -0
  262. package/skill-files//345/206/231Java/references/Java/350/247/204/350/214/203.md +25 -0
  263. package/skill-files//345/206/231Java/references//345/215/261/351/231/251/345/206/231/346/263/225.md +27 -0
  264. package/skill-files//345/206/231Java/references//345/256/214/346/210/220/345/257/271/347/205/247.md +15 -0
  265. package/skill-files//345/206/231Java/references//346/212/244/346/240/217.md +35 -0
  266. package/skill-files//345/206/231Java/references//347/272/246/346/235/237.md +8 -0
  267. package/skill-files//345/206/231Java/references//350/270/251/345/235/221.md +17 -0
  268. package/skill-files//345/256/241Java/references//346/212/244/346/240/217.md +19 -0
  269. package/skill-files//345/256/241Java/references//347/244/272/344/276/213.md +23 -0
  270. package/skill-files//345/256/241Java/references//347/272/246/346/235/237.md +7 -0
  271. package/skill-files//345/256/241Java/references//350/247/204/345/210/231.md +23 -0
  272. package/skill-files//345/256/241Java/references//351/242/206/345/237/237/346/243/200/351/241/271.md +58 -0
  273. package/skill-files//345/256/241/346/226/207/346/241/243/references//346/212/244/346/240/217.md +18 -0
  274. package/skill-files//345/256/241/346/226/207/346/241/243/references//347/272/246/346/235/237.md +8 -0
  275. package/skill-files//345/256/241/346/226/207/346/241/243/references//347/273/264/345/272/246.md +17 -0
  276. package/skill-files//345/256/241/346/226/207/346/241/243/references//350/270/251/345/235/221.md +11 -0
  277. package/skill-files//346/226/260/345/273/272/references//344/272/244/346/216/245.md +11 -0
  278. package/skill-files//346/226/260/345/273/272/references//346/212/244/346/240/217.md +14 -0
  279. package/skill-files//346/226/260/345/273/272/references//347/272/246/346/235/237.md +7 -0
  280. package/skill-files//346/267/261/346/200/235/config/prompts.yaml +114 -0
  281. package/skill-files//346/267/261/346/200/235/references//344/270/211/346/226/271/346/240/270/345/257/271.md +72 -0
  282. package/skill-files//346/267/261/346/200/235/references//344/276/213/345/255/220.md +39 -0
  283. package/skill-files//346/267/261/346/200/235/references//346/200/216/344/271/210/346/203/263.md +61 -0
  284. package/skill-files//346/267/261/346/200/235/references//346/212/244/346/240/217.md +26 -0
  285. package/skill-files//346/267/261/346/200/235/references//347/202/271/351/200/211/346/217/220/351/227/256.md +66 -0
  286. package/skill-files//346/267/261/346/200/235/references//347/231/273/350/256/260.md +45 -0
  287. package/skill-files//346/267/261/346/200/235/references//347/272/246/346/235/237.md +9 -0
  288. package/skill-files//347/224/250/344/276/213/references//346/200/216/344/271/210/345/206/231.md +13 -0
  289. package/skill-files//347/224/250/344/276/213/references//346/212/244/346/240/217.md +42 -0
  290. package/skill-files//347/224/250/344/276/213/references//347/272/246/346/235/237.md +5 -0
  291. package/skill-files//347/224/250/344/276/213/scripts/check-cases.mjs +70 -0
  292. package/skill-files//350/256/276/350/256/241/references//345/207/206/345/244/207.md +25 -0
  293. package/skill-files//350/256/276/350/256/241/references//346/212/244/346/240/217.md +134 -0
  294. package/skill-files//350/256/276/350/256/241/references//346/226/207/351/243/216.md +17 -0
  295. package/skill-files//350/256/276/350/256/241/references//347/225/214/351/235/242.md +18 -0
  296. package/skill-files//350/256/276/350/256/241/references//347/261/273/345/236/213.md +19 -0
  297. package/skill-files//350/256/276/350/256/241/references//347/272/246/346/235/237.md +25 -0
  298. package/skill-files//350/256/276/350/256/241/scripts/_smoke.mjs +72 -0
  299. package/skill-files//350/256/276/350/256/241/scripts/check-design.mjs +331 -0
  300. package/skill-files//351/234/200/346/261/202/references//346/212/244/346/240/217.md +26 -0
  301. package/skill-files//351/234/200/346/261/202/references//346/226/207/351/243/216.md +23 -0
  302. package/skill-files//351/234/200/346/261/202/references//346/267/261/346/200/235/346/221/204/345/205/245.md +21 -0
  303. package/skill-files//351/234/200/346/261/202/references//347/272/246/346/235/237.md +8 -0
  304. package/skill-files//351/234/200/346/261/202/references//351/234/200/346/261/202/345/276/205/345/256/232.md +13 -0
  305. package/skill-files//351/234/200/346/261/202/scripts/check-requirements.mjs +60 -0
  306. package/skill-files//351/252/214/346/224/266/references//346/212/244/346/240/217.md +27 -0
  307. package/skill-files//351/252/214/346/224/266/references//346/240/270/345/257/271.md +17 -0
  308. package/skill-files//351/252/214/346/224/266/references//347/272/246/346/235/237.md +7 -0
@@ -0,0 +1,89 @@
1
+ import * as fs from 'node:fs';
2
+ import type { SchemaYaml } from './types.js';
3
+ /**
4
+ * Error thrown when loading a schema fails.
5
+ */
6
+ export declare class SchemaLoadError extends Error {
7
+ readonly schemaPath: string;
8
+ readonly cause?: Error | undefined;
9
+ constructor(message: string, schemaPath: string, cause?: Error | undefined);
10
+ }
11
+ /**
12
+ * Points the built-in schemas at another directory. The standalone binary has
13
+ * no package directory on disk, so it passes a function that extracts its
14
+ * embedded schemas; it runs only when a command first needs them.
15
+ */
16
+ export declare function setPackageSchemasDir(resolveDir: () => string): void;
17
+ /**
18
+ * Gets the package's built-in schemas directory path.
19
+ * Uses import.meta.url to resolve relative to the current module.
20
+ */
21
+ export declare function getPackageSchemasDir(): string;
22
+ /**
23
+ * Gets the user's schema override directory path.
24
+ */
25
+ export declare function getUserSchemasDir(): string;
26
+ /**
27
+ * Gets the project-local schemas directory path.
28
+ * @param projectRoot - The project root directory
29
+ * @returns The path to the project's schemas directory
30
+ */
31
+ export declare function getProjectSchemasDir(projectRoot: string): string;
32
+ export declare function isSchemaDir(parentDir: string, entry: fs.Dirent): boolean;
33
+ /**
34
+ * Resolves a schema name to its directory path.
35
+ *
36
+ * Resolution order (when projectRoot is provided):
37
+ * 1. Project-local: <projectRoot>/freespec/schemas/<name>/schema.yaml
38
+ * 2. User override: ${XDG_DATA_HOME}/freespec/schemas/<name>/schema.yaml
39
+ * 3. Package built-in: <package>/schemas/<name>/schema.yaml
40
+ *
41
+ * When projectRoot is not provided, only user override and package built-in are checked
42
+ * (backward compatible behavior).
43
+ *
44
+ * @param name - Schema name (e.g., "spec-driven")
45
+ * @param projectRoot - Optional project root directory for project-local schema resolution
46
+ * @returns The path to the schema directory, or null if not found
47
+ */
48
+ export declare function getSchemaDir(name: string, projectRoot?: string): string | null;
49
+ /**
50
+ * Resolves a schema name to a SchemaYaml object.
51
+ *
52
+ * Resolution order (when projectRoot is provided):
53
+ * 1. Project-local: <projectRoot>/freespec/schemas/<name>/schema.yaml
54
+ * 2. User override: ${XDG_DATA_HOME}/freespec/schemas/<name>/schema.yaml
55
+ * 3. Package built-in: <package>/schemas/<name>/schema.yaml
56
+ *
57
+ * When projectRoot is not provided, only user override and package built-in are checked
58
+ * (backward compatible behavior).
59
+ *
60
+ * @param name - Schema name (e.g., "spec-driven")
61
+ * @param projectRoot - Optional project root directory for project-local schema resolution
62
+ * @returns The resolved schema object
63
+ * @throws Error if schema is not found in any location
64
+ */
65
+ export declare function resolveSchema(name: string, projectRoot?: string): SchemaYaml;
66
+ /**
67
+ * Lists all available schema names.
68
+ * Combines project-local, user override, and package built-in schemas.
69
+ *
70
+ * @param projectRoot - Optional project root directory for project-local schema resolution
71
+ */
72
+ export declare function listSchemas(projectRoot?: string): string[];
73
+ /**
74
+ * Schema info with metadata (name, description, artifacts).
75
+ */
76
+ export interface SchemaInfo {
77
+ name: string;
78
+ description: string;
79
+ artifacts: string[];
80
+ source: 'project' | 'user' | 'package';
81
+ }
82
+ /**
83
+ * Lists all available schemas with their descriptions and artifact lists.
84
+ * Useful for agent skills to present schema selection to users.
85
+ *
86
+ * @param projectRoot - Optional project root directory for project-local schema resolution
87
+ */
88
+ export declare function listSchemasWithInfo(projectRoot?: string): SchemaInfo[];
89
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1,342 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { getGlobalDataDir } from '../global-config.js';
5
+ import { FileSystemUtils } from '../../utils/file-system.js';
6
+ import { parseSchema, SchemaValidationError } from './schema.js';
7
+ /**
8
+ * Error thrown when loading a schema fails.
9
+ */
10
+ export class SchemaLoadError extends Error {
11
+ schemaPath;
12
+ cause;
13
+ constructor(message, schemaPath, cause) {
14
+ super(message);
15
+ this.schemaPath = schemaPath;
16
+ this.cause = cause;
17
+ this.name = 'SchemaLoadError';
18
+ }
19
+ }
20
+ let packageSchemasDirOverride;
21
+ /**
22
+ * Points the built-in schemas at another directory. The standalone binary has
23
+ * no package directory on disk, so it passes a function that extracts its
24
+ * embedded schemas; it runs only when a command first needs them.
25
+ */
26
+ export function setPackageSchemasDir(resolveDir) {
27
+ packageSchemasDirOverride = resolveDir;
28
+ }
29
+ /**
30
+ * Gets the package's built-in schemas directory path.
31
+ * Uses import.meta.url to resolve relative to the current module.
32
+ */
33
+ export function getPackageSchemasDir() {
34
+ if (packageSchemasDirOverride)
35
+ return packageSchemasDirOverride();
36
+ const currentFile = fileURLToPath(import.meta.url);
37
+ // Navigate from dist/core/artifact-graph/ to package root's schemas/
38
+ return path.join(path.dirname(currentFile), '..', '..', '..', 'schemas');
39
+ }
40
+ /**
41
+ * Gets the user's schema override directory path.
42
+ */
43
+ export function getUserSchemasDir() {
44
+ return path.join(getGlobalDataDir(), 'schemas');
45
+ }
46
+ /**
47
+ * Gets the project-local schemas directory path.
48
+ * @param projectRoot - The project root directory
49
+ * @returns The path to the project's schemas directory
50
+ */
51
+ export function getProjectSchemasDir(projectRoot) {
52
+ return path.join(projectRoot, 'freespec', 'schemas');
53
+ }
54
+ /**
55
+ * Determines whether a directory entry represents a schema directory candidate.
56
+ *
57
+ * Returns true for real directories and for symlinks whose target is a
58
+ * directory. `fs.Dirent.isDirectory()` reports the raw entry type, so a symlink
59
+ * (even one pointing at a directory) has `isDirectory() === false`; we
60
+ * dereference such entries via `fs.statSync` to admit symlinked schema dirs
61
+ * while still rejecting symlinks-to-files and broken/dangling symlinks.
62
+ *
63
+ * @param parentDir - The directory containing the entry
64
+ * @param entry - The directory entry from `fs.readdirSync(..., { withFileTypes: true })`
65
+ */
66
+ /**
67
+ * Directories `schema fork` and `schema init` create transiently while swapping
68
+ * a schema into place: a staging copy (`.fork-staging-<rand>` /
69
+ * `.init-staging-<rand>`, created via mkdtemp) and a backup of the previous
70
+ * destination (`<name>.fork-backup-<pid>-<ts>` /
71
+ * `<name>.init-backup-<pid>-<ts>`). Either can briefly coexist with real
72
+ * schemas in the schemas dir, and a backup outlives the run when its cleanup is
73
+ * blocked, so discovery must never surface them. Real schema names are
74
+ * kebab-case (no dots), so excluding these dot-bearing temp names can never
75
+ * hide a legitimate schema.
76
+ */
77
+ function isOwnedTransientSchemaDir(name) {
78
+ return (name.startsWith('.fork-staging-') ||
79
+ name.includes('.fork-backup-') ||
80
+ name.startsWith('.init-staging-') ||
81
+ name.includes('.init-backup-'));
82
+ }
83
+ export function isSchemaDir(parentDir, entry) {
84
+ if (isOwnedTransientSchemaDir(entry.name)) {
85
+ return false;
86
+ }
87
+ if (entry.isDirectory()) {
88
+ return true;
89
+ }
90
+ if (entry.isSymbolicLink()) {
91
+ try {
92
+ // statSync follows the link; isDirectory() reflects the target type.
93
+ return fs.statSync(path.join(parentDir, entry.name)).isDirectory();
94
+ }
95
+ catch {
96
+ // Broken symlink (dangling target) — statSync throws; treat as non-dir.
97
+ return false;
98
+ }
99
+ }
100
+ return false;
101
+ }
102
+ /**
103
+ * Returns a schema directory only when its schema file stays within that
104
+ * directory's canonical trust boundary. The directory itself may be a symlink;
105
+ * external user schema links are an intentionally supported workflow.
106
+ */
107
+ function getSchemaCandidateDir(schemasDir, name) {
108
+ const schemaDir = path.join(schemasDir, name);
109
+ const schemaPath = path.join(schemaDir, 'schema.yaml');
110
+ if (!fs.existsSync(schemaPath)) {
111
+ return null;
112
+ }
113
+ try {
114
+ FileSystemUtils.assertPathWithin(schemaDir, schemaPath);
115
+ return schemaDir;
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ /**
122
+ * Resolves a schema name to its directory path.
123
+ *
124
+ * Resolution order (when projectRoot is provided):
125
+ * 1. Project-local: <projectRoot>/freespec/schemas/<name>/schema.yaml
126
+ * 2. User override: ${XDG_DATA_HOME}/freespec/schemas/<name>/schema.yaml
127
+ * 3. Package built-in: <package>/schemas/<name>/schema.yaml
128
+ *
129
+ * When projectRoot is not provided, only user override and package built-in are checked
130
+ * (backward compatible behavior).
131
+ *
132
+ * @param name - Schema name (e.g., "spec-driven")
133
+ * @param projectRoot - Optional project root directory for project-local schema resolution
134
+ * @returns The path to the schema directory, or null if not found
135
+ */
136
+ export function getSchemaDir(name, projectRoot) {
137
+ if (name.length === 0 ||
138
+ name === '.' ||
139
+ name === '..' ||
140
+ /[\\/]/u.test(name) ||
141
+ /^[A-Za-z]:/u.test(name) ||
142
+ path.posix.isAbsolute(name) ||
143
+ path.win32.isAbsolute(name)) {
144
+ return null;
145
+ }
146
+ // 1. Check project-local directory (if projectRoot provided)
147
+ if (projectRoot) {
148
+ const projectDir = getSchemaCandidateDir(getProjectSchemasDir(projectRoot), name);
149
+ if (projectDir) {
150
+ return projectDir;
151
+ }
152
+ }
153
+ // 2. Check user override directory
154
+ const userDir = getSchemaCandidateDir(getUserSchemasDir(), name);
155
+ if (userDir) {
156
+ return userDir;
157
+ }
158
+ // 3. Check package built-in directory
159
+ const packageDir = getSchemaCandidateDir(getPackageSchemasDir(), name);
160
+ if (packageDir) {
161
+ return packageDir;
162
+ }
163
+ return null;
164
+ }
165
+ /**
166
+ * Resolves a schema name to a SchemaYaml object.
167
+ *
168
+ * Resolution order (when projectRoot is provided):
169
+ * 1. Project-local: <projectRoot>/freespec/schemas/<name>/schema.yaml
170
+ * 2. User override: ${XDG_DATA_HOME}/freespec/schemas/<name>/schema.yaml
171
+ * 3. Package built-in: <package>/schemas/<name>/schema.yaml
172
+ *
173
+ * When projectRoot is not provided, only user override and package built-in are checked
174
+ * (backward compatible behavior).
175
+ *
176
+ * @param name - Schema name (e.g., "spec-driven")
177
+ * @param projectRoot - Optional project root directory for project-local schema resolution
178
+ * @returns The resolved schema object
179
+ * @throws Error if schema is not found in any location
180
+ */
181
+ export function resolveSchema(name, projectRoot) {
182
+ // Normalize name (remove .yaml extension if provided)
183
+ const normalizedName = name.replace(/\.ya?ml$/, '');
184
+ const schemaDir = getSchemaDir(normalizedName, projectRoot);
185
+ if (!schemaDir) {
186
+ const availableSchemas = listSchemas(projectRoot);
187
+ throw new Error(`Schema '${normalizedName}' not found. Available schemas: ${availableSchemas.join(', ')}`);
188
+ }
189
+ const schemaPath = path.join(schemaDir, 'schema.yaml');
190
+ // Load and parse the schema
191
+ let content;
192
+ try {
193
+ content = fs.readFileSync(schemaPath, 'utf-8');
194
+ }
195
+ catch (err) {
196
+ const ioError = err instanceof Error ? err : new Error(String(err));
197
+ throw new SchemaLoadError(`Failed to read schema at '${schemaPath}': ${ioError.message}`, schemaPath, ioError);
198
+ }
199
+ try {
200
+ return parseSchema(content);
201
+ }
202
+ catch (err) {
203
+ if (err instanceof SchemaValidationError) {
204
+ throw new SchemaLoadError(`Invalid schema at '${schemaPath}': ${err.message}`, schemaPath, err);
205
+ }
206
+ const parseError = err instanceof Error ? err : new Error(String(err));
207
+ throw new SchemaLoadError(`Failed to parse schema at '${schemaPath}': ${parseError.message}`, schemaPath, parseError);
208
+ }
209
+ }
210
+ /**
211
+ * Lists all available schema names.
212
+ * Combines project-local, user override, and package built-in schemas.
213
+ *
214
+ * @param projectRoot - Optional project root directory for project-local schema resolution
215
+ */
216
+ export function listSchemas(projectRoot) {
217
+ const schemas = new Set();
218
+ // Add package built-in schemas
219
+ const packageDir = getPackageSchemasDir();
220
+ if (fs.existsSync(packageDir)) {
221
+ for (const entry of fs.readdirSync(packageDir, { withFileTypes: true })) {
222
+ if (isSchemaDir(packageDir, entry)) {
223
+ const schemaPath = path.join(packageDir, entry.name, 'schema.yaml');
224
+ if (fs.existsSync(schemaPath)) {
225
+ schemas.add(entry.name);
226
+ }
227
+ }
228
+ }
229
+ }
230
+ // Add user override schemas (may override package schemas)
231
+ const userDir = getUserSchemasDir();
232
+ if (fs.existsSync(userDir)) {
233
+ for (const entry of fs.readdirSync(userDir, { withFileTypes: true })) {
234
+ if (isSchemaDir(userDir, entry)) {
235
+ const schemaPath = path.join(userDir, entry.name, 'schema.yaml');
236
+ if (fs.existsSync(schemaPath)) {
237
+ schemas.add(entry.name);
238
+ }
239
+ }
240
+ }
241
+ }
242
+ // Add project-local schemas (if projectRoot provided)
243
+ if (projectRoot) {
244
+ const projectDir = getProjectSchemasDir(projectRoot);
245
+ if (fs.existsSync(projectDir)) {
246
+ for (const entry of fs.readdirSync(projectDir, { withFileTypes: true })) {
247
+ if (isSchemaDir(projectDir, entry)) {
248
+ const schemaPath = path.join(projectDir, entry.name, 'schema.yaml');
249
+ if (fs.existsSync(schemaPath)) {
250
+ schemas.add(entry.name);
251
+ }
252
+ }
253
+ }
254
+ }
255
+ }
256
+ return Array.from(schemas).sort();
257
+ }
258
+ /**
259
+ * Lists all available schemas with their descriptions and artifact lists.
260
+ * Useful for agent skills to present schema selection to users.
261
+ *
262
+ * @param projectRoot - Optional project root directory for project-local schema resolution
263
+ */
264
+ export function listSchemasWithInfo(projectRoot) {
265
+ const schemas = [];
266
+ const seenNames = new Set();
267
+ // Add project-local schemas first (highest priority, if projectRoot provided)
268
+ if (projectRoot) {
269
+ const projectDir = getProjectSchemasDir(projectRoot);
270
+ if (fs.existsSync(projectDir)) {
271
+ for (const entry of fs.readdirSync(projectDir, { withFileTypes: true })) {
272
+ if (isSchemaDir(projectDir, entry)) {
273
+ const schemaPath = path.join(projectDir, entry.name, 'schema.yaml');
274
+ if (fs.existsSync(schemaPath)) {
275
+ try {
276
+ const schema = parseSchema(fs.readFileSync(schemaPath, 'utf-8'));
277
+ schemas.push({
278
+ name: entry.name,
279
+ description: schema.description || '',
280
+ artifacts: schema.artifacts.map((a) => a.id),
281
+ source: 'project',
282
+ });
283
+ seenNames.add(entry.name);
284
+ }
285
+ catch {
286
+ // Skip invalid schemas
287
+ }
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ // Add user override schemas (if not overridden by project)
294
+ const userDir = getUserSchemasDir();
295
+ if (fs.existsSync(userDir)) {
296
+ for (const entry of fs.readdirSync(userDir, { withFileTypes: true })) {
297
+ if (isSchemaDir(userDir, entry) && !seenNames.has(entry.name)) {
298
+ const schemaPath = path.join(userDir, entry.name, 'schema.yaml');
299
+ if (fs.existsSync(schemaPath)) {
300
+ try {
301
+ const schema = parseSchema(fs.readFileSync(schemaPath, 'utf-8'));
302
+ schemas.push({
303
+ name: entry.name,
304
+ description: schema.description || '',
305
+ artifacts: schema.artifacts.map((a) => a.id),
306
+ source: 'user',
307
+ });
308
+ seenNames.add(entry.name);
309
+ }
310
+ catch {
311
+ // Skip invalid schemas
312
+ }
313
+ }
314
+ }
315
+ }
316
+ }
317
+ // Add package built-in schemas (if not overridden by project or user)
318
+ const packageDir = getPackageSchemasDir();
319
+ if (fs.existsSync(packageDir)) {
320
+ for (const entry of fs.readdirSync(packageDir, { withFileTypes: true })) {
321
+ if (isSchemaDir(packageDir, entry) && !seenNames.has(entry.name)) {
322
+ const schemaPath = path.join(packageDir, entry.name, 'schema.yaml');
323
+ if (fs.existsSync(schemaPath)) {
324
+ try {
325
+ const schema = parseSchema(fs.readFileSync(schemaPath, 'utf-8'));
326
+ schemas.push({
327
+ name: entry.name,
328
+ description: schema.description || '',
329
+ artifacts: schema.artifacts.map((a) => a.id),
330
+ source: 'package',
331
+ });
332
+ }
333
+ catch {
334
+ // Skip invalid schemas
335
+ }
336
+ }
337
+ }
338
+ }
339
+ }
340
+ return schemas.sort((a, b) => a.name.localeCompare(b.name));
341
+ }
342
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1,27 @@
1
+ import { type SchemaYaml } from './types.js';
2
+ export declare class SchemaValidationError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ /**
6
+ * Loads and validates an artifact schema from a YAML file.
7
+ */
8
+ export declare function loadSchema(filePath: string): SchemaYaml;
9
+ /**
10
+ * Parses and validates an artifact schema from YAML content.
11
+ */
12
+ export declare function parseSchema(yamlContent: string): SchemaYaml;
13
+ /**
14
+ * Describes an `apply.tracks` value that is not exactly equal to any artifact's
15
+ * `generates` value, or returns undefined when there is nothing to report.
16
+ *
17
+ * The tracked-tasks lookups select the artifact whose `generates` string equals
18
+ * `tracks`, so this is a progress-discovery problem, not a claim that nothing
19
+ * produces the file: a glob `generates: tasks/*.md` really does generate
20
+ * `tracks: tasks/main.md`, yet the strings differ, so the lookup still misses.
21
+ * Either way apply keeps working (it reads the path directly), but `freespec
22
+ * list` and `freespec status` fall back to counting the top-level `tasks.md`,
23
+ * and apply's remedy cannot name an artifact to build. A typo such as
24
+ * `task.md` is the other usual cause.
25
+ */
26
+ export declare function findApplyTracksWarning(schema: SchemaYaml): string | undefined;
27
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1,160 @@
1
+ import * as fs from 'node:fs';
2
+ import { parse as parseYaml } from 'yaml';
3
+ import { SchemaYamlSchema } from './types.js';
4
+ export class SchemaValidationError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = 'SchemaValidationError';
8
+ }
9
+ }
10
+ /**
11
+ * Loads and validates an artifact schema from a YAML file.
12
+ */
13
+ export function loadSchema(filePath) {
14
+ const content = fs.readFileSync(filePath, 'utf-8');
15
+ return parseSchema(content);
16
+ }
17
+ /**
18
+ * Parses and validates an artifact schema from YAML content.
19
+ */
20
+ export function parseSchema(yamlContent) {
21
+ const parsed = parseYaml(yamlContent);
22
+ // Validate with Zod
23
+ const result = SchemaYamlSchema.safeParse(parsed);
24
+ if (!result.success) {
25
+ const errors = result.error.issues.map(e => `${e.path.join('.')}: ${e.message}`).join(', ');
26
+ throw new SchemaValidationError(`Invalid schema: ${errors}`);
27
+ }
28
+ const schema = result.data;
29
+ // Check for duplicate artifact IDs
30
+ validateNoDuplicateIds(schema.artifacts);
31
+ // Check that all requires references are valid
32
+ validateRequiresReferences(schema.artifacts);
33
+ // Check that the apply phase names artifacts this schema declares
34
+ validateApplyReferences(schema);
35
+ // Check for cycles
36
+ validateNoCycles(schema.artifacts);
37
+ return schema;
38
+ }
39
+ /**
40
+ * Validates that there are no duplicate artifact IDs.
41
+ */
42
+ function validateNoDuplicateIds(artifacts) {
43
+ const seen = new Set();
44
+ for (const artifact of artifacts) {
45
+ if (seen.has(artifact.id)) {
46
+ throw new SchemaValidationError(`Duplicate artifact ID: ${artifact.id}`);
47
+ }
48
+ seen.add(artifact.id);
49
+ }
50
+ }
51
+ /**
52
+ * Validates that all `requires` references point to valid artifact IDs.
53
+ */
54
+ function validateRequiresReferences(artifacts) {
55
+ const validIds = new Set(artifacts.map(a => a.id));
56
+ for (const artifact of artifacts) {
57
+ for (const req of artifact.requires) {
58
+ if (!validIds.has(req)) {
59
+ throw new SchemaValidationError(`Invalid dependency reference in artifact '${artifact.id}': '${req}' does not exist`);
60
+ }
61
+ }
62
+ }
63
+ }
64
+ /**
65
+ * Validates that every `apply.requires` id is an artifact the schema declares.
66
+ *
67
+ * Apply skips an id that no artifact declares, so a typo silently dropped that
68
+ * artifact from the apply gate. An unknown artifact `requires` is already a
69
+ * load error, and this is the same kind of reference.
70
+ *
71
+ * `apply.tracks` is deliberately not checked here. It is a path, not an id:
72
+ * apply reads it as written, so a schema whose `tracks` value does not exactly
73
+ * match any `generates` value (a hand-written `TODO.md`, or `tasks/main.md`
74
+ * under a glob `generates: tasks/*.md` that really does produce it) works
75
+ * today, and failing the load would break every command on it.
76
+ * `freespec schema validate` reports that case as a warning instead
77
+ * (see `findApplyTracksWarning`).
78
+ */
79
+ function validateApplyReferences(schema) {
80
+ const apply = schema.apply;
81
+ if (!apply)
82
+ return;
83
+ const validIds = schema.artifacts.map(a => a.id);
84
+ for (const req of apply.requires) {
85
+ if (!validIds.includes(req)) {
86
+ throw new SchemaValidationError(`Invalid apply.requires reference: '${req}' does not exist (artifacts: ${validIds.join(', ')})`);
87
+ }
88
+ }
89
+ }
90
+ /**
91
+ * Describes an `apply.tracks` value that is not exactly equal to any artifact's
92
+ * `generates` value, or returns undefined when there is nothing to report.
93
+ *
94
+ * The tracked-tasks lookups select the artifact whose `generates` string equals
95
+ * `tracks`, so this is a progress-discovery problem, not a claim that nothing
96
+ * produces the file: a glob `generates: tasks/*.md` really does generate
97
+ * `tracks: tasks/main.md`, yet the strings differ, so the lookup still misses.
98
+ * Either way apply keeps working (it reads the path directly), but `freespec
99
+ * list` and `freespec status` fall back to counting the top-level `tasks.md`,
100
+ * and apply's remedy cannot name an artifact to build. A typo such as
101
+ * `task.md` is the other usual cause.
102
+ */
103
+ export function findApplyTracksWarning(schema) {
104
+ const tracks = schema.apply?.tracks;
105
+ if (tracks == null || schema.artifacts.some(a => a.generates === tracks))
106
+ return undefined;
107
+ return (`apply.tracks '${tracks}' does not exactly match any artifact's generates value ` +
108
+ `(generates: ${schema.artifacts.map(a => a.generates).join(', ')}), ` +
109
+ `so FreeSpec cannot tell which artifact's progress it tracks. ` +
110
+ `Apply still reads that file as written, but list and status count tasks.md instead. ` +
111
+ `Make apply.tracks exactly equal one of those generates values, ` +
112
+ `or confirm that file is maintained outside the artifact graph.`);
113
+ }
114
+ /**
115
+ * Validates that there are no cyclic dependencies.
116
+ * Uses DFS to detect cycles and reports the full cycle path.
117
+ */
118
+ function validateNoCycles(artifacts) {
119
+ const artifactMap = new Map(artifacts.map(a => [a.id, a]));
120
+ const visited = new Set();
121
+ const inStack = new Set();
122
+ const parent = new Map();
123
+ function dfs(id) {
124
+ visited.add(id);
125
+ inStack.add(id);
126
+ const artifact = artifactMap.get(id);
127
+ if (!artifact)
128
+ return null;
129
+ for (const dep of artifact.requires) {
130
+ if (!visited.has(dep)) {
131
+ parent.set(dep, id);
132
+ const cycle = dfs(dep);
133
+ if (cycle)
134
+ return cycle;
135
+ }
136
+ else if (inStack.has(dep)) {
137
+ // Found a cycle - reconstruct the path
138
+ const cyclePath = [dep];
139
+ let current = id;
140
+ while (current !== dep) {
141
+ cyclePath.unshift(current);
142
+ current = parent.get(current);
143
+ }
144
+ cyclePath.unshift(dep);
145
+ return cyclePath.join(' → ');
146
+ }
147
+ }
148
+ inStack.delete(id);
149
+ return null;
150
+ }
151
+ for (const artifact of artifacts) {
152
+ if (!visited.has(artifact.id)) {
153
+ const cycle = dfs(artifact.id);
154
+ if (cycle) {
155
+ throw new SchemaValidationError(`Cyclic dependency detected: ${cycle}`);
156
+ }
157
+ }
158
+ }
159
+ }
160
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1,12 @@
1
+ import type { CompletedSet } from './types.js';
2
+ import type { ArtifactGraph } from './graph.js';
3
+ /**
4
+ * Detects which artifacts are completed by checking file existence in the change directory.
5
+ * Returns a Set of completed artifact IDs.
6
+ *
7
+ * @param graph - The artifact graph to check
8
+ * @param changeDir - The change directory to scan for files
9
+ * @returns Set of artifact IDs whose generated files exist
10
+ */
11
+ export declare function detectCompleted(graph: ArtifactGraph, changeDir: string): CompletedSet;
12
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1,31 @@
1
+ import * as fs from 'node:fs';
2
+ import { artifactOutputExists } from './outputs.js';
3
+ /**
4
+ * Detects which artifacts are completed by checking file existence in the change directory.
5
+ * Returns a Set of completed artifact IDs.
6
+ *
7
+ * @param graph - The artifact graph to check
8
+ * @param changeDir - The change directory to scan for files
9
+ * @returns Set of artifact IDs whose generated files exist
10
+ */
11
+ export function detectCompleted(graph, changeDir) {
12
+ const completed = new Set();
13
+ // Handle missing change directory gracefully
14
+ if (!fs.existsSync(changeDir)) {
15
+ return completed;
16
+ }
17
+ for (const artifact of graph.getAllArtifacts()) {
18
+ if (isArtifactComplete(artifact.generates, changeDir)) {
19
+ completed.add(artifact.id);
20
+ }
21
+ }
22
+ return completed;
23
+ }
24
+ /**
25
+ * Checks if an artifact is complete by checking if its generated file(s) exist.
26
+ * Supports both simple paths and glob patterns.
27
+ */
28
+ function isArtifactComplete(generates, changeDir) {
29
+ return artifactOutputExists(changeDir, generates);
30
+ }
31
+ //# sourceMappingURL=state.js.map