@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,135 @@
1
+ /**
2
+ * New Change Command
3
+ *
4
+ * Creates a new change directory with optional description and schema in the
5
+ * resolved FreeSpec root. Initiative linking and workspace affected areas
6
+ * are no longer part of this command.
7
+ */
8
+ import chalk from 'chalk';
9
+ import path from 'path';
10
+ import { createChange, validateChangeName } from '../../utils/change-utils.js';
11
+ import { normalizeChangeName } from '../../core/change-layout.js';
12
+ import { formatChangeLocation } from '../../core/planning-home.js';
13
+ import { resolveRootForCommand, RootSelectionError, toPlanningHome, toRootOutput, } from '../../core/root-selection.js';
14
+ import { readProjectConfig } from '../../core/project-config.js';
15
+ import { printJson, statusFromError, validateSchemaExists } from './shared.js';
16
+ // -----------------------------------------------------------------------------
17
+ // Command Implementation
18
+ // -----------------------------------------------------------------------------
19
+ function assertRemovedOptionsAbsent(options) {
20
+ if (options.initiative !== undefined) {
21
+ throw new RootSelectionError('--initiative is no longer supported. Normal changes no longer attach to initiatives; --store <id> selects the FreeSpec root.', 'initiative_option_removed', { target: 'change.options' });
22
+ }
23
+ if (options.areas !== undefined) {
24
+ throw new RootSelectionError('--areas is no longer supported. Workspace affected areas are not part of the normal FreeSpec root path.', 'areas_option_removed', { target: 'change.options' });
25
+ }
26
+ }
27
+ function printCreatedChangeHuman(payload, root) {
28
+ // A relative path is only honest when the root is where the user
29
+ // stands; a distant ancestor root gets the absolute path.
30
+ const location = root.path === process.cwd()
31
+ ? formatChangeLocation(toPlanningHome(root), payload.change.id)
32
+ : payload.change.path;
33
+ console.log(`Created change '${payload.change.id}' at ${location}/`);
34
+ console.log(`Schema: ${payload.change.schema}`);
35
+ console.log(`Next: freespec status --change ${payload.change.id}`);
36
+ }
37
+ /**
38
+ * An implicit root is the fallback taken when no `freespec/` directory was
39
+ * found: creating a change there materializes FreeSpec in whatever directory
40
+ * the caller happened to be in, which is how an agent ends up adopting a
41
+ * project that never ran `freespec init` (#1645). The creation itself stays
42
+ * zero-config; this only makes it visible.
43
+ */
44
+ function printImplicitRootNotice(root) {
45
+ if (root.source !== 'implicit') {
46
+ return;
47
+ }
48
+ const freespecDir = path.dirname(root.changesDir);
49
+ const relative = path.relative(process.cwd(), freespecDir);
50
+ const location = relative && !relative.startsWith('..') ? relative : freespecDir;
51
+ console.log();
52
+ console.log(chalk.dim(`Note: no FreeSpec root was found here, so one was created at ${location}/.`));
53
+ console.log(chalk.dim('Run `freespec init` to finish setting this project up, or delete that directory if you meant a different project.'));
54
+ }
55
+ export async function newChangeCommand(name, options) {
56
+ let spinner;
57
+ if (!options.json) {
58
+ const { default: ora } = await import('ora');
59
+ spinner = ora();
60
+ }
61
+ try {
62
+ if (!name) {
63
+ throw new Error('Missing required argument <name>');
64
+ }
65
+ const folderName = normalizeChangeName(name);
66
+ const validation = validateChangeName(folderName);
67
+ if (!validation.valid) {
68
+ throw new Error(validation.error);
69
+ }
70
+ assertRemovedOptionsAbsent(options);
71
+ const root = await resolveRootForCommand(options, {
72
+ json: options.json,
73
+ failurePayload: { change: null },
74
+ });
75
+ if (!root) {
76
+ return;
77
+ }
78
+ const projectRoot = root.path;
79
+ // Validate schema if provided
80
+ if (options.schema) {
81
+ validateSchemaExists(options.schema, projectRoot);
82
+ }
83
+ // Resolve the schema once (--schema, then config, then the root default)
84
+ // and hand it to createChange, so the progress line names the schema the
85
+ // change gets and config warnings print only once. An empty --schema is
86
+ // absent, matching createChange's `if (options.schema)`.
87
+ const resolvedSchema = options.schema || (readProjectConfig(projectRoot)?.schema ?? root.defaultSchema);
88
+ if (spinner) {
89
+ spinner.start(`正在创建「${folderName}」,schema「${resolvedSchema}」...`);
90
+ }
91
+ const result = await createChange(projectRoot, folderName, {
92
+ schema: resolvedSchema,
93
+ defaultSchema: root.defaultSchema,
94
+ changesDir: root.changesDir,
95
+ metadata: {
96
+ ...(options.goal ? { goal: options.goal } : {}),
97
+ },
98
+ });
99
+ // If description provided, create README.md with description
100
+ if (options.description) {
101
+ const { promises: fs } = await import('fs');
102
+ const readmePath = path.join(result.changeDir, 'README.md');
103
+ await fs.writeFile(readmePath, `# ${name}\n\n${options.description}\n`, 'utf-8');
104
+ }
105
+ const payload = {
106
+ change: {
107
+ id: result.changeName,
108
+ path: result.changeDir,
109
+ metadataPath: path.join(result.changeDir, '.freespec.yaml'),
110
+ schema: result.schema,
111
+ },
112
+ root: toRootOutput(root),
113
+ };
114
+ if (options.json) {
115
+ printJson(payload);
116
+ return;
117
+ }
118
+ spinner?.stop();
119
+ printCreatedChangeHuman(payload, root);
120
+ printImplicitRootNotice(root);
121
+ }
122
+ catch (error) {
123
+ spinner?.stop();
124
+ if (options.json) {
125
+ printJson({
126
+ change: null,
127
+ status: [statusFromError(error)],
128
+ });
129
+ process.exitCode = 1;
130
+ return;
131
+ }
132
+ throw error;
133
+ }
134
+ }
135
+ //# sourceMappingURL=new-change.js.map
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Shared Types and Utilities for Artifact Workflow Commands
3
+ *
4
+ * This module contains types, constants, and validation helpers used across
5
+ * multiple artifact workflow commands.
6
+ */
7
+ export interface ChangeCommandStatus {
8
+ severity: 'error' | 'warning';
9
+ code: string;
10
+ message: string;
11
+ target?: string;
12
+ fix?: string;
13
+ }
14
+ export interface TaskItem {
15
+ id: string;
16
+ description: string;
17
+ done: boolean;
18
+ sourcePath: string;
19
+ line: number;
20
+ }
21
+ export interface ApplyInstructions {
22
+ changeName: string;
23
+ changeDir: string;
24
+ schemaName: string;
25
+ contextFiles: Record<string, string[]>;
26
+ progress: {
27
+ total: number;
28
+ complete: number;
29
+ remaining: number;
30
+ };
31
+ tasks: TaskItem[];
32
+ taskTrackingConfigured: boolean;
33
+ unavailableTrackingFiles?: Array<{
34
+ path: string;
35
+ reason: string;
36
+ }>;
37
+ state: 'blocked' | 'all_done' | 'ready';
38
+ missingArtifacts?: string[];
39
+ /**
40
+ * Everything still to build before apply can run, in build order - the
41
+ * transitive closure of the schema's `apply.requires`, so it can be longer
42
+ * than `missingArtifacts`, which stops at the first hop apply blocks on.
43
+ */
44
+ missingPrerequisites?: string[];
45
+ /** Non-blocking problems with the change, reported alongside the instruction. */
46
+ warnings?: string[];
47
+ instruction: string;
48
+ /** Current project background from the selected root. */
49
+ context?: string;
50
+ /** Current advisory guidance for apply. */
51
+ operationGuidance?: string[];
52
+ }
53
+ export interface ArchiveInstructions {
54
+ changeName: string;
55
+ /** Current project background from the selected root. */
56
+ context?: string;
57
+ /** Current advisory guidance for archive. */
58
+ operationGuidance?: string[];
59
+ }
60
+ export { DEFAULT_SCHEMA } from './default-schema.js';
61
+ export declare function printJson(payload: unknown): void;
62
+ export declare function statusFromError(error: unknown): ChangeCommandStatus;
63
+ /**
64
+ * Checks if color output is disabled via NO_COLOR env or --no-color flag.
65
+ */
66
+ export declare function isColorDisabled(): boolean;
67
+ /**
68
+ * Gets the color function based on status.
69
+ */
70
+ export declare function getStatusColor(status: 'done' | 'skipped' | 'ready' | 'blocked'): (text: string) => string;
71
+ /**
72
+ * Gets the status indicator for an artifact.
73
+ */
74
+ export declare function getStatusIndicator(status: 'done' | 'skipped' | 'ready' | 'blocked'): string;
75
+ /**
76
+ * Returns change directory names directly under freespec/.
77
+ * A change folder is named `<名称>变更`. Hidden directories are excluded.
78
+ */
79
+ export declare function getAvailableChanges(projectRoot: string, changesDir?: string): Promise<string[]>;
80
+ /**
81
+ * Validates that a change exists and returns available changes if not.
82
+ * Checks directory existence directly to support scaffolded changes (without proposal.md).
83
+ */
84
+ export declare function validateChangeExists(changeName: string | undefined, projectRoot: string, changesDir?: string, hints?: {
85
+ newChangeHint?: string;
86
+ }): Promise<string>;
87
+ /**
88
+ * Validates that a schema exists and returns available schemas if not.
89
+ *
90
+ * @param schemaName - The schema name to validate
91
+ * @param projectRoot - Optional project root for project-local schema resolution
92
+ */
93
+ export declare function validateSchemaExists(schemaName: string, projectRoot?: string): string;
94
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Shared Types and Utilities for Artifact Workflow Commands
3
+ *
4
+ * This module contains types, constants, and validation helpers used across
5
+ * multiple artifact workflow commands.
6
+ */
7
+ import chalk from 'chalk';
8
+ import path from 'path';
9
+ import { describeNestedChange, findNestedChangesIn, } from '../../utils/nested-change.js';
10
+ import * as fs from 'fs';
11
+ import { getSchemaDir, listSchemas } from '../../core/artifact-graph/index.js';
12
+ import { isRootSelectionError } from '../../core/root-selection.js';
13
+ import { changeParentDir, isChangeDirectoryName } from '../../core/change-layout.js';
14
+ // -----------------------------------------------------------------------------
15
+ // Constants
16
+ // -----------------------------------------------------------------------------
17
+ export { DEFAULT_SCHEMA } from './default-schema.js';
18
+ // -----------------------------------------------------------------------------
19
+ // Utility Functions
20
+ // -----------------------------------------------------------------------------
21
+ export function printJson(payload) {
22
+ console.log(JSON.stringify(payload, null, 2));
23
+ }
24
+ export function statusFromError(error) {
25
+ if (isRootSelectionError(error)) {
26
+ return { ...error.diagnostic };
27
+ }
28
+ return {
29
+ severity: 'error',
30
+ code: 'change_error',
31
+ message: error instanceof Error ? error.message : String(error),
32
+ };
33
+ }
34
+ /**
35
+ * Checks if color output is disabled via NO_COLOR env or --no-color flag.
36
+ */
37
+ export function isColorDisabled() {
38
+ return process.env.NO_COLOR === '1' || process.env.NO_COLOR === 'true';
39
+ }
40
+ /**
41
+ * Gets the color function based on status.
42
+ */
43
+ export function getStatusColor(status) {
44
+ if (isColorDisabled()) {
45
+ return (text) => text;
46
+ }
47
+ switch (status) {
48
+ case 'done':
49
+ return chalk.green;
50
+ case 'skipped':
51
+ return chalk.gray;
52
+ case 'ready':
53
+ return chalk.yellow;
54
+ case 'blocked':
55
+ return chalk.red;
56
+ }
57
+ }
58
+ /**
59
+ * Gets the status indicator for an artifact.
60
+ */
61
+ export function getStatusIndicator(status) {
62
+ const color = getStatusColor(status);
63
+ switch (status) {
64
+ case 'done':
65
+ return color('[x]');
66
+ case 'skipped':
67
+ return color('[~]');
68
+ case 'ready':
69
+ return color('[ ]');
70
+ case 'blocked':
71
+ return color('[-]');
72
+ }
73
+ }
74
+ /**
75
+ * Returns change directory names directly under freespec/.
76
+ * A change folder is named `<名称>变更`. Hidden directories are excluded.
77
+ */
78
+ export async function getAvailableChanges(projectRoot, changesDir = changeParentDir(projectRoot)) {
79
+ const changesPath = changesDir;
80
+ try {
81
+ const entries = await fs.promises.readdir(changesPath, { withFileTypes: true });
82
+ return entries
83
+ .filter((e) => e.isDirectory() && isChangeDirectoryName(e.name))
84
+ .map((e) => e.name);
85
+ }
86
+ catch (error) {
87
+ if (error.code === 'ENOENT')
88
+ return [];
89
+ throw error;
90
+ }
91
+ }
92
+ /**
93
+ * Validates a change name used to look up an existing change directory.
94
+ * Lookup accepts any directory name that `getAvailableChanges` could return
95
+ * (the kebab-case convention in `validateChangeName` applies at creation
96
+ * time only); it only rejects names that would escape the changes directory
97
+ * or address entries `getAvailableChanges` excludes (hidden dirs, archive).
98
+ *
99
+ * @returns An error message, or undefined if the name is safe to look up
100
+ */
101
+ function validateChangeLookupName(changeName) {
102
+ if (changeName === '.' || changeName === '..') {
103
+ return 'Change name cannot be a relative path segment';
104
+ }
105
+ if (changeName.includes('/') || changeName.includes('\\')) {
106
+ return 'Change name cannot contain path separators';
107
+ }
108
+ if (changeName.includes('\0')) {
109
+ return 'Change name cannot contain null characters';
110
+ }
111
+ if (changeName.startsWith('.')) {
112
+ return 'Change name cannot start with a dot';
113
+ }
114
+ return undefined;
115
+ }
116
+ /**
117
+ * Validates that a change exists and returns available changes if not.
118
+ * Checks directory existence directly to support scaffolded changes (without proposal.md).
119
+ */
120
+ export async function validateChangeExists(changeName, projectRoot, changesDir = changeParentDir(projectRoot), hints = {}) {
121
+ // Hints must stay pasteable: callers with a selected store pass a
122
+ // store-carrying hint so following it lands in the same root.
123
+ const newChangeHint = hints.newChangeHint ?? 'freespec new change <name>';
124
+ if (!changeName) {
125
+ const available = await getAvailableChanges(projectRoot, changesDir);
126
+ if (available.length === 0) {
127
+ throw new Error(`No changes found. Create one with: ${newChangeHint}`);
128
+ }
129
+ throw new Error(`Missing required option --change. Available changes:\n ${available.join('\n ')}`);
130
+ }
131
+ // Validate change name format to prevent path traversal
132
+ const lookupError = validateChangeLookupName(changeName);
133
+ if (lookupError) {
134
+ throw new Error(`Invalid change name '${changeName}': ${lookupError}`);
135
+ }
136
+ // Check directory existence directly
137
+ const changePath = path.join(changesDir, changeName);
138
+ const exists = fs.existsSync(changePath) && fs.statSync(changePath).isDirectory();
139
+ if (!exists) {
140
+ const available = await getAvailableChanges(projectRoot, changesDir);
141
+ if (available.length === 0) {
142
+ throw new Error(`Change '${changeName}' not found. No changes exist. Create one with: ${newChangeHint}`);
143
+ }
144
+ throw new Error(`Change '${changeName}' not found. Available changes:\n ${available.join('\n ')}`);
145
+ }
146
+ // The directory exists but is a namespace folder wrapping nested change
147
+ // directories. Every artifact lookup below it would report "not started" for
148
+ // work that is in fact there, so say what is actually wrong instead (#1846).
149
+ const nested = await findNestedChangesIn(changesDir, changeName);
150
+ if (nested) {
151
+ throw new Error(describeNestedChange(nested));
152
+ }
153
+ return changeName;
154
+ }
155
+ /**
156
+ * Validates that a schema exists and returns available schemas if not.
157
+ *
158
+ * @param schemaName - The schema name to validate
159
+ * @param projectRoot - Optional project root for project-local schema resolution
160
+ */
161
+ export function validateSchemaExists(schemaName, projectRoot) {
162
+ const schemaDir = getSchemaDir(schemaName, projectRoot);
163
+ if (!schemaDir) {
164
+ const availableSchemas = listSchemas(projectRoot);
165
+ throw new Error(`Schema '${schemaName}' not found. Available schemas:\n ${availableSchemas.join('\n ')}`);
166
+ }
167
+ return schemaName;
168
+ }
169
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Status Command
3
+ *
4
+ * Displays artifact completion status for a change.
5
+ */
6
+ import { type ChangeStatus } from '../../core/artifact-graph/index.js';
7
+ export interface StatusOptions {
8
+ change?: string;
9
+ all?: boolean;
10
+ schema?: string;
11
+ store?: string;
12
+ storePath?: string;
13
+ json?: boolean;
14
+ }
15
+ export declare const BATCH_STATUS_FAILURE_PAYLOAD: Record<string, unknown>;
16
+ export declare function statusCommand(options: StatusOptions): Promise<void>;
17
+ export declare function printStatusText(status: ChangeStatus): void;
18
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Status Command
3
+ *
4
+ * Displays artifact completion status for a change.
5
+ */
6
+ import chalk from 'chalk';
7
+ import { getChangeDir } from '../../core/planning-home.js';
8
+ import { resolveRootForCommand, toPlanningHome, toRootOutput, } from '../../core/root-selection.js';
9
+ import { loadChangeContext, formatChangeStatus, } from '../../core/artifact-graph/index.js';
10
+ import { resolveNextStep } from '../../core/change-status-policy.js';
11
+ import { asStatus } from '../shared-output.js';
12
+ import { describeNestedChange, findNestedChanges, } from '../../utils/nested-change.js';
13
+ import { validateChangeExists, validateSchemaExists, getAvailableChanges, getStatusIndicator, getStatusColor, } from './shared.js';
14
+ // The --all --json failure null-shape. Root-selection failures (handled in
15
+ // resolveRootForCommand) and thrown errors (caught by the CLI wrapper) must
16
+ // emit the same shape, so both call sites reference this one constant.
17
+ export const BATCH_STATUS_FAILURE_PAYLOAD = {
18
+ changes: [],
19
+ root: null,
20
+ };
21
+ export async function statusCommand(options) {
22
+ if (options.all && options.change) {
23
+ throw new Error('The --all and --change options are mutually exclusive.');
24
+ }
25
+ // The root resolves (and the store banner prints) before the spinner starts
26
+ // so the two do not fight over stderr. The batch null-shape rides along so
27
+ // a root-selection failure under --all --json still carries `changes: []`.
28
+ const root = await resolveRootForCommand(options, {
29
+ json: options.json,
30
+ failurePayload: options.all ? BATCH_STATUS_FAILURE_PAYLOAD : undefined,
31
+ });
32
+ if (!root) {
33
+ return;
34
+ }
35
+ let spinner;
36
+ if (!options.json) {
37
+ const { default: ora } = await import('ora');
38
+ spinner = ora('Loading change status...').start();
39
+ }
40
+ try {
41
+ const planningHome = toPlanningHome(root);
42
+ const projectRoot = root.path;
43
+ const rootOutput = toRootOutput(root);
44
+ const newChangeHint = 'freespec new change <name>';
45
+ // Single definition of "load one change's status" so the batch and
46
+ // single-change payloads can never drift apart.
47
+ const loadStatus = (changeName) => formatChangeStatus(loadChangeContext(projectRoot, changeName, options.schema, {
48
+ changeDir: getChangeDir(planningHome, changeName),
49
+ planningHome,
50
+ }));
51
+ // Handle no-changes case gracefully — status is informational,
52
+ // so "no changes" is a valid state, not an error.
53
+ if (!options.change) {
54
+ // Validate before the no-changes early return so a bogus --schema
55
+ // fails the same way whether or not any change exists yet.
56
+ if (options.all && options.schema) {
57
+ validateSchemaExists(options.schema, projectRoot);
58
+ }
59
+ const available = await getAvailableChanges(projectRoot, root.changesDir);
60
+ if (available.length === 0) {
61
+ spinner?.stop();
62
+ if (options.json) {
63
+ console.log(JSON.stringify({ changes: [], message: 'No active changes.', root: rootOutput }, null, 2));
64
+ return;
65
+ }
66
+ console.log(`No active changes. Create one with: ${newChangeHint}`);
67
+ return;
68
+ }
69
+ if (options.all) {
70
+ // readdir order is platform-dependent; sort for deterministic output,
71
+ // with the same comparator validate --all uses so the two batch
72
+ // commands order a given change set identically.
73
+ const entries = [];
74
+ // The sweep reads each directory straight through `loadStatus`, so a
75
+ // namespace folder wrapping nested changes would report a whole
76
+ // artifact plan for work that is not there (#1846). It carries the same
77
+ // per-change diagnostic a malformed change does.
78
+ const nestedByName = new Map((await findNestedChanges(root.changesDir, available)).map((finding) => [
79
+ finding.name,
80
+ finding,
81
+ ]));
82
+ for (const changeName of available.sort((a, b) => a.localeCompare(b))) {
83
+ const nested = nestedByName.get(changeName);
84
+ if (nested) {
85
+ entries.push({
86
+ changeName,
87
+ status: [asStatus(new Error(describeNestedChange(nested)), 'change_error')],
88
+ });
89
+ continue;
90
+ }
91
+ try {
92
+ entries.push(loadStatus(changeName));
93
+ }
94
+ catch (error) {
95
+ // One malformed change must not blank the sweep; carry its
96
+ // diagnostic in place and keep going.
97
+ entries.push({ changeName, status: [asStatus(error, 'change_error')] });
98
+ }
99
+ }
100
+ spinner?.stop();
101
+ const failed = entries.some((entry) => !('artifacts' in entry));
102
+ if (options.json) {
103
+ console.log(JSON.stringify({ changes: entries, root: rootOutput }, null, 2));
104
+ if (failed) {
105
+ process.exitCode = 1;
106
+ }
107
+ return;
108
+ }
109
+ entries.forEach((entry, index) => {
110
+ if (index > 0) {
111
+ console.log();
112
+ }
113
+ if ('artifacts' in entry) {
114
+ printStatusText(entry);
115
+ }
116
+ else {
117
+ console.log(chalk.red(`✗ ${entry.changeName}: ${entry.status[0]?.message}`));
118
+ }
119
+ });
120
+ // A partial load is still a failed command in both output modes;
121
+ // JSON callers can parse the complete envelope independently of
122
+ // the process exit code.
123
+ if (failed) {
124
+ process.exitCode = 1;
125
+ }
126
+ return;
127
+ }
128
+ // Changes exist but neither --change nor --all provided. Name --all
129
+ // here too: it is the other way to answer this prompt, and a caller
130
+ // who wants every change should not have to find it in --help.
131
+ spinner?.stop();
132
+ throw new Error(`Missing required option --change (or --all for every active change). Available changes:\n ${available.join('\n ')}`);
133
+ }
134
+ const changeName = await validateChangeExists(options.change, projectRoot, root.changesDir, { newChangeHint });
135
+ // Validate schema if explicitly provided
136
+ if (options.schema) {
137
+ validateSchemaExists(options.schema, projectRoot);
138
+ }
139
+ // loadChangeContext will auto-detect schema from metadata if not provided
140
+ const status = loadStatus(changeName);
141
+ spinner?.stop();
142
+ if (options.json) {
143
+ console.log(JSON.stringify({ ...status, root: rootOutput }, null, 2));
144
+ return;
145
+ }
146
+ printStatusText(status);
147
+ }
148
+ catch (error) {
149
+ spinner?.stop();
150
+ throw error;
151
+ }
152
+ }
153
+ export function printStatusText(status) {
154
+ const doneCount = status.artifacts.filter((a) => a.status === 'done').length;
155
+ const skippedCount = status.artifacts.filter((a) => a.status === 'skipped').length;
156
+ const total = status.artifacts.length - skippedCount;
157
+ console.log(`Change: ${status.changeName}`);
158
+ console.log(`Schema: ${status.schemaName}`);
159
+ if (status.warnings) {
160
+ for (const warning of status.warnings) {
161
+ console.log(chalk.yellow(`Warning: ${warning}`));
162
+ }
163
+ }
164
+ if (status.changeRoot) {
165
+ console.log(`Change root: ${status.changeRoot}`);
166
+ }
167
+ const skippedSuffix = skippedCount > 0 ? ` (${skippedCount} skipped)` : '';
168
+ console.log(`Progress: ${doneCount}/${total} artifacts complete${skippedSuffix}`);
169
+ console.log();
170
+ for (const artifact of status.artifacts) {
171
+ const indicator = getStatusIndicator(artifact.status);
172
+ const color = getStatusColor(artifact.status);
173
+ let line = `${indicator} ${artifact.id}`;
174
+ if (artifact.status === 'skipped') {
175
+ line += color(' (skipped: change declares skip_specs)');
176
+ }
177
+ if (artifact.status === 'blocked' && artifact.missingDeps && artifact.missingDeps.length > 0) {
178
+ line += color(` (blocked by: ${artifact.missingDeps.join(', ')})`);
179
+ }
180
+ console.log(line);
181
+ }
182
+ // Derived from the same inputs as the JSON `nextSteps` sentence, so the two
183
+ // surfaces always name the same command. Without this line the text surface
184
+ // reports state and no verb, which leaves someone resuming a change - after a
185
+ // lost session, or on a change they did not start - with nowhere to go.
186
+ const nextStep = resolveNextStep({
187
+ changeName: status.changeName,
188
+ artifactStatuses: status.artifacts,
189
+ allArtifactsComplete: status.isPlanningComplete,
190
+ });
191
+ if (status.isPlanningComplete || nextStep) {
192
+ console.log();
193
+ }
194
+ if (status.isPlanningComplete) {
195
+ console.log(chalk.green('All planning artifacts complete!'));
196
+ }
197
+ if (nextStep) {
198
+ console.log(`Next: ${nextStep.command}`);
199
+ }
200
+ }
201
+ //# sourceMappingURL=status.js.map