@devtrack-solution/codesdd 1.2.2

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 (433) hide show
  1. package/.sdd/skills/curated/api-clean-flask-langgraph/SKILL.md +2751 -0
  2. package/.sdd/skills/curated/devtrack-api/SKILL.md +137 -0
  3. package/.sdd/skills/curated/devtrack-api/agents/openai.yaml +4 -0
  4. package/.sdd/skills/curated/devtrack-api/references/application-presentation.md +381 -0
  5. package/.sdd/skills/curated/devtrack-api/references/architecture-governance.md +219 -0
  6. package/.sdd/skills/curated/devtrack-api/references/domain-modeling.md +359 -0
  7. package/.sdd/skills/curated/devtrack-api/references/implementation-checklist.md +127 -0
  8. package/.sdd/skills/curated/devtrack-api/references/imports-lint.md +207 -0
  9. package/.sdd/skills/curated/devtrack-api/references/testing-validation.md +167 -0
  10. package/.sdd/skills/curated/devtrack-api/references/typeorm-infrastructure.md +334 -0
  11. package/LICENSE +21 -0
  12. package/README.md +842 -0
  13. package/bin/codesdd.js +10 -0
  14. package/dist/cli/index.d.ts +3 -0
  15. package/dist/cli/index.js +560 -0
  16. package/dist/commands/change.d.ts +35 -0
  17. package/dist/commands/change.js +296 -0
  18. package/dist/commands/completion.d.ts +72 -0
  19. package/dist/commands/completion.js +258 -0
  20. package/dist/commands/config.d.ts +36 -0
  21. package/dist/commands/config.js +552 -0
  22. package/dist/commands/feedback.d.ts +9 -0
  23. package/dist/commands/feedback.js +184 -0
  24. package/dist/commands/schema.d.ts +6 -0
  25. package/dist/commands/schema.js +870 -0
  26. package/dist/commands/sdd/execution.d.ts +3 -0
  27. package/dist/commands/sdd/execution.js +409 -0
  28. package/dist/commands/sdd/shared.d.ts +9 -0
  29. package/dist/commands/sdd/shared.js +84 -0
  30. package/dist/commands/sdd/skills.d.ts +3 -0
  31. package/dist/commands/sdd/skills.js +154 -0
  32. package/dist/commands/sdd.d.ts +3 -0
  33. package/dist/commands/sdd.js +769 -0
  34. package/dist/commands/show.d.ts +14 -0
  35. package/dist/commands/show.js +133 -0
  36. package/dist/commands/spec.d.ts +15 -0
  37. package/dist/commands/spec.js +228 -0
  38. package/dist/commands/validate.d.ts +24 -0
  39. package/dist/commands/validate.js +295 -0
  40. package/dist/commands/workflow/index.d.ts +17 -0
  41. package/dist/commands/workflow/index.js +12 -0
  42. package/dist/commands/workflow/instructions.d.ts +29 -0
  43. package/dist/commands/workflow/instructions.js +383 -0
  44. package/dist/commands/workflow/new-change.d.ts +11 -0
  45. package/dist/commands/workflow/new-change.js +45 -0
  46. package/dist/commands/workflow/schemas.d.ts +10 -0
  47. package/dist/commands/workflow/schemas.js +34 -0
  48. package/dist/commands/workflow/shared.d.ts +57 -0
  49. package/dist/commands/workflow/shared.js +117 -0
  50. package/dist/commands/workflow/status.d.ts +14 -0
  51. package/dist/commands/workflow/status.js +76 -0
  52. package/dist/commands/workflow/templates.d.ts +16 -0
  53. package/dist/commands/workflow/templates.js +68 -0
  54. package/dist/core/archive.d.ts +16 -0
  55. package/dist/core/archive.js +487 -0
  56. package/dist/core/artifact-graph/graph.d.ts +56 -0
  57. package/dist/core/artifact-graph/graph.js +141 -0
  58. package/dist/core/artifact-graph/index.d.ts +7 -0
  59. package/dist/core/artifact-graph/index.js +13 -0
  60. package/dist/core/artifact-graph/instruction-loader.d.ts +143 -0
  61. package/dist/core/artifact-graph/instruction-loader.js +215 -0
  62. package/dist/core/artifact-graph/resolver.d.ts +81 -0
  63. package/dist/core/artifact-graph/resolver.js +258 -0
  64. package/dist/core/artifact-graph/schema.d.ts +13 -0
  65. package/dist/core/artifact-graph/schema.js +108 -0
  66. package/dist/core/artifact-graph/state.d.ts +12 -0
  67. package/dist/core/artifact-graph/state.js +54 -0
  68. package/dist/core/artifact-graph/types.d.ts +45 -0
  69. package/dist/core/artifact-graph/types.js +43 -0
  70. package/dist/core/available-tools.d.ts +16 -0
  71. package/dist/core/available-tools.js +30 -0
  72. package/dist/core/branding.d.ts +8 -0
  73. package/dist/core/branding.js +12 -0
  74. package/dist/core/cli/command-matrix.d.ts +23 -0
  75. package/dist/core/cli/command-matrix.js +123 -0
  76. package/dist/core/command-generation/adapters/amazon-q.d.ts +13 -0
  77. package/dist/core/command-generation/adapters/amazon-q.js +26 -0
  78. package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
  79. package/dist/core/command-generation/adapters/antigravity.js +26 -0
  80. package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
  81. package/dist/core/command-generation/adapters/auggie.js +27 -0
  82. package/dist/core/command-generation/adapters/claude.d.ts +13 -0
  83. package/dist/core/command-generation/adapters/claude.js +50 -0
  84. package/dist/core/command-generation/adapters/cline.d.ts +14 -0
  85. package/dist/core/command-generation/adapters/cline.js +27 -0
  86. package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
  87. package/dist/core/command-generation/adapters/codebuddy.js +28 -0
  88. package/dist/core/command-generation/adapters/codex.d.ts +16 -0
  89. package/dist/core/command-generation/adapters/codex.js +39 -0
  90. package/dist/core/command-generation/adapters/continue.d.ts +13 -0
  91. package/dist/core/command-generation/adapters/continue.js +28 -0
  92. package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
  93. package/dist/core/command-generation/adapters/costrict.js +27 -0
  94. package/dist/core/command-generation/adapters/crush.d.ts +13 -0
  95. package/dist/core/command-generation/adapters/crush.js +30 -0
  96. package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
  97. package/dist/core/command-generation/adapters/cursor.js +44 -0
  98. package/dist/core/command-generation/adapters/factory.d.ts +13 -0
  99. package/dist/core/command-generation/adapters/factory.js +27 -0
  100. package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
  101. package/dist/core/command-generation/adapters/gemini.js +26 -0
  102. package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
  103. package/dist/core/command-generation/adapters/github-copilot.js +26 -0
  104. package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
  105. package/dist/core/command-generation/adapters/iflow.js +29 -0
  106. package/dist/core/command-generation/adapters/index.d.ts +29 -0
  107. package/dist/core/command-generation/adapters/index.js +29 -0
  108. package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
  109. package/dist/core/command-generation/adapters/kilocode.js +23 -0
  110. package/dist/core/command-generation/adapters/kiro.d.ts +13 -0
  111. package/dist/core/command-generation/adapters/kiro.js +26 -0
  112. package/dist/core/command-generation/adapters/opencode.d.ts +13 -0
  113. package/dist/core/command-generation/adapters/opencode.js +29 -0
  114. package/dist/core/command-generation/adapters/pi.d.ts +14 -0
  115. package/dist/core/command-generation/adapters/pi.js +41 -0
  116. package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
  117. package/dist/core/command-generation/adapters/qoder.js +30 -0
  118. package/dist/core/command-generation/adapters/qwen.d.ts +13 -0
  119. package/dist/core/command-generation/adapters/qwen.js +26 -0
  120. package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
  121. package/dist/core/command-generation/adapters/roocode.js +27 -0
  122. package/dist/core/command-generation/adapters/windsurf.d.ts +14 -0
  123. package/dist/core/command-generation/adapters/windsurf.js +51 -0
  124. package/dist/core/command-generation/generator.d.ts +21 -0
  125. package/dist/core/command-generation/generator.js +27 -0
  126. package/dist/core/command-generation/index.d.ts +22 -0
  127. package/dist/core/command-generation/index.js +24 -0
  128. package/dist/core/command-generation/registry.d.ts +36 -0
  129. package/dist/core/command-generation/registry.js +92 -0
  130. package/dist/core/command-generation/types.d.ts +56 -0
  131. package/dist/core/command-generation/types.js +8 -0
  132. package/dist/core/completions/command-registry.d.ts +7 -0
  133. package/dist/core/completions/command-registry.js +461 -0
  134. package/dist/core/completions/completion-provider.d.ts +60 -0
  135. package/dist/core/completions/completion-provider.js +102 -0
  136. package/dist/core/completions/factory.d.ts +64 -0
  137. package/dist/core/completions/factory.js +75 -0
  138. package/dist/core/completions/generators/bash-generator.d.ts +32 -0
  139. package/dist/core/completions/generators/bash-generator.js +174 -0
  140. package/dist/core/completions/generators/fish-generator.d.ts +32 -0
  141. package/dist/core/completions/generators/fish-generator.js +157 -0
  142. package/dist/core/completions/generators/powershell-generator.d.ts +33 -0
  143. package/dist/core/completions/generators/powershell-generator.js +207 -0
  144. package/dist/core/completions/generators/zsh-generator.d.ts +44 -0
  145. package/dist/core/completions/generators/zsh-generator.js +250 -0
  146. package/dist/core/completions/installers/bash-installer.d.ts +87 -0
  147. package/dist/core/completions/installers/bash-installer.js +318 -0
  148. package/dist/core/completions/installers/fish-installer.d.ts +43 -0
  149. package/dist/core/completions/installers/fish-installer.js +143 -0
  150. package/dist/core/completions/installers/powershell-installer.d.ts +88 -0
  151. package/dist/core/completions/installers/powershell-installer.js +327 -0
  152. package/dist/core/completions/installers/zsh-installer.d.ts +125 -0
  153. package/dist/core/completions/installers/zsh-installer.js +452 -0
  154. package/dist/core/completions/templates/bash-templates.d.ts +6 -0
  155. package/dist/core/completions/templates/bash-templates.js +24 -0
  156. package/dist/core/completions/templates/fish-templates.d.ts +7 -0
  157. package/dist/core/completions/templates/fish-templates.js +39 -0
  158. package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
  159. package/dist/core/completions/templates/powershell-templates.js +25 -0
  160. package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
  161. package/dist/core/completions/templates/zsh-templates.js +36 -0
  162. package/dist/core/completions/types.d.ts +79 -0
  163. package/dist/core/completions/types.js +2 -0
  164. package/dist/core/config-prompts.d.ts +9 -0
  165. package/dist/core/config-prompts.js +34 -0
  166. package/dist/core/config-schema.d.ts +86 -0
  167. package/dist/core/config-schema.js +213 -0
  168. package/dist/core/config.d.ts +17 -0
  169. package/dist/core/config.js +33 -0
  170. package/dist/core/converters/json-converter.d.ts +6 -0
  171. package/dist/core/converters/json-converter.js +51 -0
  172. package/dist/core/global-config.d.ts +44 -0
  173. package/dist/core/global-config.js +125 -0
  174. package/dist/core/index.d.ts +2 -0
  175. package/dist/core/index.js +3 -0
  176. package/dist/core/init.d.ts +36 -0
  177. package/dist/core/init.js +576 -0
  178. package/dist/core/legacy-cleanup.d.ts +162 -0
  179. package/dist/core/legacy-cleanup.js +512 -0
  180. package/dist/core/list.d.ts +9 -0
  181. package/dist/core/list.js +173 -0
  182. package/dist/core/migration.d.ts +23 -0
  183. package/dist/core/migration.js +108 -0
  184. package/dist/core/parsers/change-parser.d.ts +13 -0
  185. package/dist/core/parsers/change-parser.js +193 -0
  186. package/dist/core/parsers/markdown-parser.d.ts +22 -0
  187. package/dist/core/parsers/markdown-parser.js +187 -0
  188. package/dist/core/parsers/requirement-blocks.d.ts +37 -0
  189. package/dist/core/parsers/requirement-blocks.js +201 -0
  190. package/dist/core/profile-sync-drift.d.ts +38 -0
  191. package/dist/core/profile-sync-drift.js +201 -0
  192. package/dist/core/profiles.d.ts +26 -0
  193. package/dist/core/profiles.js +41 -0
  194. package/dist/core/project-config.d.ts +64 -0
  195. package/dist/core/project-config.js +223 -0
  196. package/dist/core/schemas/base.schema.d.ts +13 -0
  197. package/dist/core/schemas/base.schema.js +13 -0
  198. package/dist/core/schemas/change.schema.d.ts +73 -0
  199. package/dist/core/schemas/change.schema.js +31 -0
  200. package/dist/core/schemas/index.d.ts +4 -0
  201. package/dist/core/schemas/index.js +4 -0
  202. package/dist/core/schemas/spec.schema.d.ts +18 -0
  203. package/dist/core/schemas/spec.schema.js +15 -0
  204. package/dist/core/sdd/adr-policy.d.ts +7 -0
  205. package/dist/core/sdd/adr-policy.js +47 -0
  206. package/dist/core/sdd/adr.d.ts +4 -0
  207. package/dist/core/sdd/adr.js +27 -0
  208. package/dist/core/sdd/bootstrap.d.ts +28 -0
  209. package/dist/core/sdd/bootstrap.js +353 -0
  210. package/dist/core/sdd/check.d.ts +51 -0
  211. package/dist/core/sdd/check.js +831 -0
  212. package/dist/core/sdd/coordination/coordination-adapters.d.ts +73 -0
  213. package/dist/core/sdd/coordination/coordination-adapters.js +87 -0
  214. package/dist/core/sdd/coordination/index.d.ts +2 -0
  215. package/dist/core/sdd/coordination/index.js +2 -0
  216. package/dist/core/sdd/dedup.d.ts +23 -0
  217. package/dist/core/sdd/dedup.js +62 -0
  218. package/dist/core/sdd/default-bootstrap-files.d.ts +23 -0
  219. package/dist/core/sdd/default-bootstrap-files.js +385 -0
  220. package/dist/core/sdd/default-skills.d.ts +16 -0
  221. package/dist/core/sdd/default-skills.js +427 -0
  222. package/dist/core/sdd/diagnose.d.ts +25 -0
  223. package/dist/core/sdd/diagnose.js +1312 -0
  224. package/dist/core/sdd/docs-sync.d.ts +21 -0
  225. package/dist/core/sdd/docs-sync.js +231 -0
  226. package/dist/core/sdd/domain/helpers.d.ts +6 -0
  227. package/dist/core/sdd/domain/helpers.js +37 -0
  228. package/dist/core/sdd/domain/lifecycle-guardrails.d.ts +22 -0
  229. package/dist/core/sdd/domain/lifecycle-guardrails.js +31 -0
  230. package/dist/core/sdd/domain/lifecycle-hooks.d.ts +16 -0
  231. package/dist/core/sdd/domain/lifecycle-hooks.js +27 -0
  232. package/dist/core/sdd/domain/post-active-validation.d.ts +15 -0
  233. package/dist/core/sdd/domain/post-active-validation.js +71 -0
  234. package/dist/core/sdd/domain/traceability.d.ts +8 -0
  235. package/dist/core/sdd/domain/traceability.js +83 -0
  236. package/dist/core/sdd/domain/transition-engine.d.ts +49 -0
  237. package/dist/core/sdd/domain/transition-engine.js +120 -0
  238. package/dist/core/sdd/fingerprint.d.ts +23 -0
  239. package/dist/core/sdd/fingerprint.js +146 -0
  240. package/dist/core/sdd/import-openspec.d.ts +31 -0
  241. package/dist/core/sdd/import-openspec.js +232 -0
  242. package/dist/core/sdd/init.d.ts +36 -0
  243. package/dist/core/sdd/init.js +65 -0
  244. package/dist/core/sdd/json-schema.d.ts +6 -0
  245. package/dist/core/sdd/json-schema.js +59 -0
  246. package/dist/core/sdd/legacy-operations.d.ts +286 -0
  247. package/dist/core/sdd/legacy-operations.js +2175 -0
  248. package/dist/core/sdd/lenses.d.ts +14 -0
  249. package/dist/core/sdd/lenses.js +97 -0
  250. package/dist/core/sdd/merge-catalog.d.ts +9 -0
  251. package/dist/core/sdd/merge-catalog.js +70 -0
  252. package/dist/core/sdd/migrate-workspace.d.ts +36 -0
  253. package/dist/core/sdd/migrate-workspace.js +344 -0
  254. package/dist/core/sdd/migrate.d.ts +24 -0
  255. package/dist/core/sdd/migrate.js +385 -0
  256. package/dist/core/sdd/resolve-project-root.d.ts +15 -0
  257. package/dist/core/sdd/resolve-project-root.js +46 -0
  258. package/dist/core/sdd/root-resolver.d.ts +16 -0
  259. package/dist/core/sdd/root-resolver.js +62 -0
  260. package/dist/core/sdd/sanitize.d.ts +35 -0
  261. package/dist/core/sdd/sanitize.js +750 -0
  262. package/dist/core/sdd/services/approve.service.d.ts +20 -0
  263. package/dist/core/sdd/services/approve.service.js +82 -0
  264. package/dist/core/sdd/services/audit.service.d.ts +53 -0
  265. package/dist/core/sdd/services/audit.service.js +136 -0
  266. package/dist/core/sdd/services/breakdown.service.d.ts +35 -0
  267. package/dist/core/sdd/services/breakdown.service.js +185 -0
  268. package/dist/core/sdd/services/context.service.d.ts +346 -0
  269. package/dist/core/sdd/services/context.service.js +278 -0
  270. package/dist/core/sdd/services/debate.service.d.ts +16 -0
  271. package/dist/core/sdd/services/debate.service.js +73 -0
  272. package/dist/core/sdd/services/decide.service.d.ts +23 -0
  273. package/dist/core/sdd/services/decide.service.js +81 -0
  274. package/dist/core/sdd/services/dedup-apply.service.d.ts +39 -0
  275. package/dist/core/sdd/services/dedup-apply.service.js +259 -0
  276. package/dist/core/sdd/services/feature-lint.service.d.ts +29 -0
  277. package/dist/core/sdd/services/feature-lint.service.js +146 -0
  278. package/dist/core/sdd/services/finalize.service.d.ts +33 -0
  279. package/dist/core/sdd/services/finalize.service.js +707 -0
  280. package/dist/core/sdd/services/frontend-gap.service.d.ts +23 -0
  281. package/dist/core/sdd/services/frontend-gap.service.js +117 -0
  282. package/dist/core/sdd/services/frontend-impact.service.d.ts +19 -0
  283. package/dist/core/sdd/services/frontend-impact.service.js +46 -0
  284. package/dist/core/sdd/services/ingest-deposito.service.d.ts +32 -0
  285. package/dist/core/sdd/services/ingest-deposito.service.js +231 -0
  286. package/dist/core/sdd/services/insight.service.d.ts +21 -0
  287. package/dist/core/sdd/services/insight.service.js +81 -0
  288. package/dist/core/sdd/services/legacy-capability.service.d.ts +24 -0
  289. package/dist/core/sdd/services/legacy-capability.service.js +59 -0
  290. package/dist/core/sdd/services/mcp-runtime.service.d.ts +42 -0
  291. package/dist/core/sdd/services/mcp-runtime.service.js +144 -0
  292. package/dist/core/sdd/services/metrics.service.d.ts +49 -0
  293. package/dist/core/sdd/services/metrics.service.js +181 -0
  294. package/dist/core/sdd/services/next.service.d.ts +35 -0
  295. package/dist/core/sdd/services/next.service.js +54 -0
  296. package/dist/core/sdd/services/onboard.service.d.ts +9 -0
  297. package/dist/core/sdd/services/onboard.service.js +165 -0
  298. package/dist/core/sdd/services/rebuild.service.d.ts +31 -0
  299. package/dist/core/sdd/services/rebuild.service.js +482 -0
  300. package/dist/core/sdd/services/scan-naming.service.d.ts +43 -0
  301. package/dist/core/sdd/services/scan-naming.service.js +246 -0
  302. package/dist/core/sdd/services/skills-invoke.service.d.ts +24 -0
  303. package/dist/core/sdd/services/skills-invoke.service.js +63 -0
  304. package/dist/core/sdd/services/skills-sync.service.d.ts +15 -0
  305. package/dist/core/sdd/services/skills-sync.service.js +117 -0
  306. package/dist/core/sdd/services/start.service.d.ts +26 -0
  307. package/dist/core/sdd/services/start.service.js +237 -0
  308. package/dist/core/sdd/skills.d.ts +15 -0
  309. package/dist/core/sdd/skills.js +46 -0
  310. package/dist/core/sdd/state-lock.d.ts +19 -0
  311. package/dist/core/sdd/state-lock.js +144 -0
  312. package/dist/core/sdd/state.d.ts +155 -0
  313. package/dist/core/sdd/state.js +1000 -0
  314. package/dist/core/sdd/store/in-memory-adapter.d.ts +12 -0
  315. package/dist/core/sdd/store/in-memory-adapter.js +27 -0
  316. package/dist/core/sdd/store/index.d.ts +5 -0
  317. package/dist/core/sdd/store/index.js +5 -0
  318. package/dist/core/sdd/store/sdd-stores.d.ts +25 -0
  319. package/dist/core/sdd/store/sdd-stores.js +59 -0
  320. package/dist/core/sdd/store/state-store.d.ts +32 -0
  321. package/dist/core/sdd/store/state-store.js +2 -0
  322. package/dist/core/sdd/store/yaml-file-adapter.d.ts +12 -0
  323. package/dist/core/sdd/store/yaml-file-adapter.js +43 -0
  324. package/dist/core/sdd/structural-health.d.ts +557 -0
  325. package/dist/core/sdd/structural-health.js +187 -0
  326. package/dist/core/sdd/transaction.d.ts +14 -0
  327. package/dist/core/sdd/transaction.js +100 -0
  328. package/dist/core/sdd/types.d.ts +1570 -0
  329. package/dist/core/sdd/types.js +617 -0
  330. package/dist/core/sdd/views.d.ts +3 -0
  331. package/dist/core/sdd/views.js +560 -0
  332. package/dist/core/sdd/workspace-schemas.d.ts +620 -0
  333. package/dist/core/sdd/workspace-schemas.js +254 -0
  334. package/dist/core/sdd/write-manifest.d.ts +25 -0
  335. package/dist/core/sdd/write-manifest.js +353 -0
  336. package/dist/core/shared/index.d.ts +8 -0
  337. package/dist/core/shared/index.js +8 -0
  338. package/dist/core/shared/skill-generation.d.ts +49 -0
  339. package/dist/core/shared/skill-generation.js +106 -0
  340. package/dist/core/shared/tool-detection.d.ts +71 -0
  341. package/dist/core/shared/tool-detection.js +158 -0
  342. package/dist/core/specs-apply.d.ts +73 -0
  343. package/dist/core/specs-apply.js +385 -0
  344. package/dist/core/styles/palette.d.ts +7 -0
  345. package/dist/core/styles/palette.js +8 -0
  346. package/dist/core/templates/index.d.ts +8 -0
  347. package/dist/core/templates/index.js +9 -0
  348. package/dist/core/templates/skill-templates.d.ts +20 -0
  349. package/dist/core/templates/skill-templates.js +19 -0
  350. package/dist/core/templates/types.d.ts +19 -0
  351. package/dist/core/templates/types.js +5 -0
  352. package/dist/core/templates/workflows/apply-change.d.ts +10 -0
  353. package/dist/core/templates/workflows/apply-change.js +308 -0
  354. package/dist/core/templates/workflows/archive-change.d.ts +10 -0
  355. package/dist/core/templates/workflows/archive-change.js +277 -0
  356. package/dist/core/templates/workflows/bulk-archive-change.d.ts +10 -0
  357. package/dist/core/templates/workflows/bulk-archive-change.js +502 -0
  358. package/dist/core/templates/workflows/continue-change.d.ts +10 -0
  359. package/dist/core/templates/workflows/continue-change.js +232 -0
  360. package/dist/core/templates/workflows/explore.d.ts +10 -0
  361. package/dist/core/templates/workflows/explore.js +475 -0
  362. package/dist/core/templates/workflows/feedback.d.ts +9 -0
  363. package/dist/core/templates/workflows/feedback.js +108 -0
  364. package/dist/core/templates/workflows/ff-change.d.ts +10 -0
  365. package/dist/core/templates/workflows/ff-change.js +206 -0
  366. package/dist/core/templates/workflows/new-change.d.ts +10 -0
  367. package/dist/core/templates/workflows/new-change.js +151 -0
  368. package/dist/core/templates/workflows/onboard.d.ts +10 -0
  369. package/dist/core/templates/workflows/onboard.js +573 -0
  370. package/dist/core/templates/workflows/propose.d.ts +10 -0
  371. package/dist/core/templates/workflows/propose.js +224 -0
  372. package/dist/core/templates/workflows/sdd.d.ts +10 -0
  373. package/dist/core/templates/workflows/sdd.js +107 -0
  374. package/dist/core/templates/workflows/sync-specs.d.ts +10 -0
  375. package/dist/core/templates/workflows/sync-specs.js +286 -0
  376. package/dist/core/templates/workflows/verify-change.d.ts +10 -0
  377. package/dist/core/templates/workflows/verify-change.js +346 -0
  378. package/dist/core/update.d.ts +77 -0
  379. package/dist/core/update.js +538 -0
  380. package/dist/core/validation/constants.d.ts +34 -0
  381. package/dist/core/validation/constants.js +40 -0
  382. package/dist/core/validation/types.d.ts +18 -0
  383. package/dist/core/validation/types.js +2 -0
  384. package/dist/core/validation/validator.d.ts +33 -0
  385. package/dist/core/validation/validator.js +409 -0
  386. package/dist/core/view.d.ts +8 -0
  387. package/dist/core/view.js +170 -0
  388. package/dist/index.d.ts +3 -0
  389. package/dist/index.js +3 -0
  390. package/dist/prompts/searchable-multi-select.d.ts +28 -0
  391. package/dist/prompts/searchable-multi-select.js +159 -0
  392. package/dist/telemetry/config.d.ts +32 -0
  393. package/dist/telemetry/config.js +68 -0
  394. package/dist/telemetry/index.d.ts +44 -0
  395. package/dist/telemetry/index.js +207 -0
  396. package/dist/ui/ascii-patterns.d.ts +16 -0
  397. package/dist/ui/ascii-patterns.js +133 -0
  398. package/dist/ui/welcome-screen.d.ts +10 -0
  399. package/dist/ui/welcome-screen.js +146 -0
  400. package/dist/utils/change-metadata.d.ts +51 -0
  401. package/dist/utils/change-metadata.js +147 -0
  402. package/dist/utils/change-utils.d.ts +62 -0
  403. package/dist/utils/change-utils.js +121 -0
  404. package/dist/utils/command-references.d.ts +18 -0
  405. package/dist/utils/command-references.js +20 -0
  406. package/dist/utils/file-system.d.ts +36 -0
  407. package/dist/utils/file-system.js +281 -0
  408. package/dist/utils/index.d.ts +6 -0
  409. package/dist/utils/index.js +9 -0
  410. package/dist/utils/interactive.d.ts +18 -0
  411. package/dist/utils/interactive.js +21 -0
  412. package/dist/utils/item-discovery.d.ts +4 -0
  413. package/dist/utils/item-discovery.js +73 -0
  414. package/dist/utils/match.d.ts +3 -0
  415. package/dist/utils/match.js +22 -0
  416. package/dist/utils/openspec-compat.d.ts +2 -0
  417. package/dist/utils/openspec-compat.js +2 -0
  418. package/dist/utils/shell-detection.d.ts +20 -0
  419. package/dist/utils/shell-detection.js +41 -0
  420. package/dist/utils/task-progress.d.ts +8 -0
  421. package/dist/utils/task-progress.js +36 -0
  422. package/package.json +111 -0
  423. package/schemas/sdd/1-spec.schema.json +221 -0
  424. package/schemas/sdd/2-plan.schema.json +199 -0
  425. package/schemas/sdd/3-tasks.schema.json +102 -0
  426. package/schemas/sdd/4-changelog.schema.json +55 -0
  427. package/schemas/sdd/5-quality.schema.json +427 -0
  428. package/schemas/sdd/workspace-catalog.schema.json +1012 -0
  429. package/schemas/spec-driven/schema.yaml +153 -0
  430. package/schemas/spec-driven/templates/design.md +19 -0
  431. package/schemas/spec-driven/templates/proposal.md +23 -0
  432. package/schemas/spec-driven/templates/spec.md +8 -0
  433. package/schemas/spec-driven/templates/tasks.md +9 -0
@@ -0,0 +1,750 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { lstat, mkdir, readFile, readdir, rename, rm, rmdir, unlink, writeFile, cp } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { stringify as stringifyYaml } from 'yaml';
5
+ import { StructuralDiagnosticReportSchema, StructuralSanitizerActionSchema, StructuralSanitizerReportSchema, StructuralSanitizerRollbackOperationSchema, StructuralSanitizerTransactionManifestSchema, StructuralSanitizerTransactionOperationSchema, } from './structural-health.js';
6
+ import { diagnoseSddRoot } from './diagnose.js';
7
+ import { loadProjectSddConfig, loadStateSnapshot, resolveSddPaths } from './state.js';
8
+ import { renderViews } from './views.js';
9
+ import { RootResolver, SddBoundaryError } from './root-resolver.js';
10
+ export class SddSanitizeOptionsError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = 'SddSanitizeOptionsError';
14
+ }
15
+ }
16
+ export class SddSanitizeApplyError extends Error {
17
+ constructor(message) {
18
+ super(message);
19
+ this.name = 'SddSanitizeApplyError';
20
+ }
21
+ }
22
+ function actionToken(input) {
23
+ const normalized = input.toUpperCase().replace(/[^A-Z0-9]+/g, '-').replace(/^-+|-+$/g, '');
24
+ return normalized.length > 0 ? normalized : 'GENERIC';
25
+ }
26
+ function createSourceReportId(report) {
27
+ const digest = createHash('sha1')
28
+ .update(JSON.stringify({
29
+ root_path: report.root_path,
30
+ generated_at: report.generated_at,
31
+ strict: report.strict,
32
+ findings: report.findings.map((finding) => ({
33
+ finding_id: finding.finding_id,
34
+ rule_id: finding.rule_id,
35
+ category: finding.category,
36
+ severity: finding.severity,
37
+ location: finding.location ?? '',
38
+ disposition: finding.sanitizer.disposition,
39
+ })),
40
+ }))
41
+ .digest('hex')
42
+ .slice(0, 12)
43
+ .toUpperCase();
44
+ return `SH-REPORT-${digest}`;
45
+ }
46
+ function isInsidePath(rootPath, candidatePath) {
47
+ const relative = path.relative(rootPath, candidatePath);
48
+ return relative === '' || (!!relative && !relative.startsWith('..') && !path.isAbsolute(relative));
49
+ }
50
+ function toAuditTimestampToken(value = new Date()) {
51
+ return value.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z');
52
+ }
53
+ function createTransactionId(sourceReportId, value = new Date()) {
54
+ const timestamp = toAuditTimestampToken(value);
55
+ const suffix = createHash('sha1').update(`${sourceReportId}:${value.toISOString()}`).digest('hex').slice(0, 8).toUpperCase();
56
+ return `SAN-${timestamp}-${suffix}`;
57
+ }
58
+ async function pathExists(targetPath) {
59
+ try {
60
+ await lstat(targetPath);
61
+ return true;
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ async function hashPath(targetPath) {
68
+ let stat;
69
+ try {
70
+ stat = await lstat(targetPath);
71
+ }
72
+ catch {
73
+ return undefined;
74
+ }
75
+ if (stat.isFile()) {
76
+ const content = await readFile(targetPath);
77
+ return `file:${createHash('sha1').update(content).digest('hex')}`;
78
+ }
79
+ if (stat.isDirectory()) {
80
+ const children = await readdir(targetPath, { withFileTypes: true });
81
+ const digest = createHash('sha1');
82
+ for (const child of children.sort((a, b) => a.name.localeCompare(b.name))) {
83
+ digest.update(child.name);
84
+ digest.update(child.isDirectory() ? ':dir' : ':file');
85
+ digest.update('\n');
86
+ }
87
+ return `dir:${digest.digest('hex')}`;
88
+ }
89
+ return `other:${stat.mode}`;
90
+ }
91
+ async function resolveInSddRoot(projectRoot, memoryRoot, candidatePath, label) {
92
+ try {
93
+ return await RootResolver.verifyRealpathContainment(projectRoot, memoryRoot, candidatePath, label);
94
+ }
95
+ catch (error) {
96
+ if (error instanceof SddBoundaryError) {
97
+ throw new SddSanitizeApplyError(error.message);
98
+ }
99
+ throw error;
100
+ }
101
+ }
102
+ function transactionDirs(projectRoot, transactionId) {
103
+ const sanitizeRoot = path.resolve(projectRoot, '.sdd', 'reports', 'sanitize');
104
+ const backupRoot = path.join(sanitizeRoot, transactionId, 'backups');
105
+ return {
106
+ sanitizeRoot,
107
+ backupRoot,
108
+ manifestPath: path.join(sanitizeRoot, `${transactionId}-manifest.json`),
109
+ auditLogPath: path.join(sanitizeRoot, `${transactionId}-audit.jsonl`),
110
+ };
111
+ }
112
+ async function backupPath(targetPath, backupRoot) {
113
+ if (!(await pathExists(targetPath)))
114
+ return undefined;
115
+ const suffix = createHash('sha1').update(targetPath).digest('hex').slice(0, 10);
116
+ const backupPathResolved = path.join(backupRoot, `${path.basename(targetPath)}-${suffix}.bak`);
117
+ await mkdir(path.dirname(backupPathResolved), { recursive: true });
118
+ await cp(targetPath, backupPathResolved, { recursive: true });
119
+ return backupPathResolved;
120
+ }
121
+ async function removePathSafely(targetPath) {
122
+ const stat = await lstat(targetPath).catch(() => null);
123
+ if (!stat)
124
+ return;
125
+ if (stat.isDirectory()) {
126
+ await rmdir(targetPath);
127
+ return;
128
+ }
129
+ await unlink(targetPath);
130
+ }
131
+ function toLifecycleTargetPath(finding) {
132
+ const evidence = finding.evidence;
133
+ const expectedRoot = typeof evidence.expected_root === 'string' ? evidence.expected_root : undefined;
134
+ const itemId = typeof evidence.id === 'string' ? evidence.id : undefined;
135
+ if (expectedRoot && itemId) {
136
+ return path.posix.join('.sdd', expectedRoot, itemId);
137
+ }
138
+ if (typeof finding.location === 'string' && finding.location.startsWith('.sdd/active/')) {
139
+ const tail = finding.location.slice('.sdd/active/'.length);
140
+ if (tail.length > 0) {
141
+ return path.posix.join('.sdd', 'archived', tail);
142
+ }
143
+ }
144
+ return undefined;
145
+ }
146
+ function toQuarantinePath(location) {
147
+ if (!location)
148
+ return undefined;
149
+ const baseName = path.posix.basename(location);
150
+ return path.posix.join('.sdd', 'quarantine', baseName);
151
+ }
152
+ function toWorkspaceBackupPath(location) {
153
+ if (!location)
154
+ return undefined;
155
+ const normalized = location.replace(/\\/g, '/').replace(/^\.sdd\//, '');
156
+ return path.posix.join('.sdd', 'backup', normalized);
157
+ }
158
+ function inferOperation(finding) {
159
+ switch (finding.rule_id) {
160
+ case 'SH-RULE-MISSING-DIRECTORY':
161
+ return 'mkdir';
162
+ case 'SH-RULE-GENERATED-VIEW':
163
+ return 'render';
164
+ case 'SH-RULE-LIFECYCLE-PLACEMENT':
165
+ case 'SH-RULE-ORPHAN-WORKSPACE':
166
+ return 'move';
167
+ case 'SH-RULE-WORKSPACE-FILE-NOT-ALLOWLISTED':
168
+ return 'move';
169
+ case 'SH-RULE-MISSING-STATE-FILE':
170
+ return 'register';
171
+ case 'SH-RULE-WORKSPACE-SCHEMA-INVALID':
172
+ case 'SH-RULE-LIFECYCLE-VIOLATION':
173
+ case 'SH-RULE-QUALITY-EVIDENCE-INCOMPLETE':
174
+ return 'manual_review';
175
+ default:
176
+ if (finding.sanitizer.disposition === 'blocked' || finding.sanitizer.disposition === 'noop') {
177
+ return 'skip';
178
+ }
179
+ return 'manual_review';
180
+ }
181
+ }
182
+ function makeActionFromFinding(finding, index) {
183
+ const operation = inferOperation(finding);
184
+ const actionId = `SH-ACTION-${actionToken(finding.finding_id)}-${actionToken(operation)}-${index + 1}`;
185
+ const candidate = {
186
+ action_id: actionId,
187
+ finding_id: finding.finding_id,
188
+ disposition: finding.sanitizer.disposition,
189
+ operation,
190
+ notes: `${finding.summary} ${finding.sanitizer.reason}`,
191
+ };
192
+ if (operation === 'mkdir' || operation === 'render' || operation === 'register') {
193
+ candidate.to_path = finding.location;
194
+ }
195
+ if (operation === 'move') {
196
+ candidate.from_path = finding.location;
197
+ candidate.to_path = finding.rule_id === 'SH-RULE-WORKSPACE-FILE-NOT-ALLOWLISTED'
198
+ ? toWorkspaceBackupPath(finding.location)
199
+ : finding.rule_id === 'SH-RULE-ORPHAN-WORKSPACE'
200
+ ? toQuarantinePath(finding.location)
201
+ : toLifecycleTargetPath(finding);
202
+ if (finding.rule_id === 'SH-RULE-ORPHAN-WORKSPACE') {
203
+ candidate.notes = `${candidate.notes} Quarantine candidate generated for manual review.`;
204
+ }
205
+ if (finding.rule_id === 'SH-RULE-WORKSPACE-FILE-NOT-ALLOWLISTED') {
206
+ candidate.notes = `${candidate.notes} Backup candidate generated for confirmation-driven cleanup; remove only after review.`;
207
+ }
208
+ }
209
+ if (finding.rule_id === 'SH-RULE-ROOT-BOUNDARY' && finding.location) {
210
+ candidate.from_path = finding.location;
211
+ candidate.to_path = toQuarantinePath(finding.location);
212
+ candidate.notes = `${candidate.notes} Candidate path suggested for in-root quarantine.`;
213
+ }
214
+ if (finding.rule_id === 'SH-RULE-WORKSPACE-SCHEMA-INVALID') {
215
+ const evidence = finding.evidence;
216
+ const id = typeof evidence.id === 'string' ? evidence.id : undefined;
217
+ const hasLegacyMarkdown = evidence.has_legacy_markdown === true;
218
+ candidate.notes = hasLegacyMarkdown && id
219
+ ? `${candidate.notes} Suggested migration: opensdd sdd migrate-workspace --feat ${id}.`
220
+ : `${candidate.notes} Suggested repair: manually edit the YAML to satisfy the workspace schema.`;
221
+ }
222
+ if (finding.rule_id === 'SH-RULE-LIFECYCLE-VIOLATION') {
223
+ const evidence = finding.evidence;
224
+ const id = typeof evidence.id === 'string' ? evidence.id : undefined;
225
+ const expectedStatus = typeof evidence.expected_status === 'string' ? evidence.expected_status : undefined;
226
+ candidate.to_path = '.sdd/state/backlog.yaml';
227
+ candidate.notes = id && expectedStatus
228
+ ? `${candidate.notes} Suggested repair: set ${id}.status to ${expectedStatus} in backlog.yaml based on its lifecycle directory.`
229
+ : `${candidate.notes} Suggested repair: align backlog.yaml status with lifecycle directory.`;
230
+ }
231
+ if (finding.rule_id === 'SH-RULE-QUALITY-EVIDENCE-INCOMPLETE') {
232
+ candidate.notes = `${candidate.notes} Suggested repair: add validation commands/results to evidence_log[] and reference coverage targets.`;
233
+ }
234
+ return StructuralSanitizerActionSchema.parse(candidate);
235
+ }
236
+ function buildSanitizerDryRunReport(report) {
237
+ const actions = [];
238
+ const blockedActions = [];
239
+ report.findings.forEach((finding, index) => {
240
+ const action = makeActionFromFinding(finding, index);
241
+ if (action.disposition === 'blocked') {
242
+ blockedActions.push(action);
243
+ return;
244
+ }
245
+ actions.push(action);
246
+ });
247
+ return StructuralSanitizerReportSchema.parse({
248
+ report_version: 1,
249
+ generated_at: new Date().toISOString(),
250
+ source_report_id: createSourceReportId(report),
251
+ mode: 'dry_run',
252
+ actions,
253
+ blocked_actions: blockedActions,
254
+ });
255
+ }
256
+ function manifestSummary(operations) {
257
+ return {
258
+ applied: operations.filter((item) => item.status === 'applied').length,
259
+ skipped: operations.filter((item) => item.status === 'skipped').length,
260
+ failed: operations.filter((item) => item.status === 'failed').length,
261
+ rolled_back: operations.filter((item) => item.status === 'rolled_back').length,
262
+ };
263
+ }
264
+ async function writeAuditLog(auditLogPath, lines) {
265
+ await mkdir(path.dirname(auditLogPath), { recursive: true });
266
+ if (lines.length === 0) {
267
+ await writeFile(auditLogPath, '', 'utf-8');
268
+ return;
269
+ }
270
+ const payload = `${lines.map((line) => JSON.stringify(line)).join('\n')}\n`;
271
+ await writeFile(auditLogPath, payload, 'utf-8');
272
+ }
273
+ async function rollbackOperation(projectRoot, memoryRoot, rollback) {
274
+ const operation = StructuralSanitizerRollbackOperationSchema.parse(rollback);
275
+ if (operation.operation === 'noop')
276
+ return;
277
+ if (operation.operation === 'delete_path') {
278
+ if (!operation.path)
279
+ return;
280
+ const target = await resolveInSddRoot(projectRoot, memoryRoot, operation.path, 'Rollback delete target');
281
+ await removePathSafely(target);
282
+ return;
283
+ }
284
+ if (operation.operation === 'move') {
285
+ if (!operation.from_path || !operation.to_path)
286
+ return;
287
+ const fromPath = await resolveInSddRoot(projectRoot, memoryRoot, operation.from_path, 'Rollback move source');
288
+ const toPath = await resolveInSddRoot(projectRoot, memoryRoot, operation.to_path, 'Rollback move target');
289
+ if (!(await pathExists(fromPath)))
290
+ return;
291
+ await mkdir(path.dirname(toPath), { recursive: true });
292
+ await rename(fromPath, toPath);
293
+ return;
294
+ }
295
+ if (operation.operation === 'restore_backup') {
296
+ if (!operation.path || !operation.backup_path)
297
+ return;
298
+ const target = await resolveInSddRoot(projectRoot, memoryRoot, operation.path, 'Rollback restore target');
299
+ const backup = await resolveInSddRoot(projectRoot, memoryRoot, operation.backup_path, 'Rollback backup path');
300
+ await rm(target, { recursive: true, force: true });
301
+ await mkdir(path.dirname(target), { recursive: true });
302
+ await cp(backup, target, { recursive: true });
303
+ }
304
+ }
305
+ async function applySanitizerPlan(projectRoot, source, diagnosticReport, dryRunReport) {
306
+ const config = await loadProjectSddConfig(projectRoot);
307
+ const paths = resolveSddPaths(path.resolve(projectRoot), config);
308
+ const transactionId = createTransactionId(dryRunReport.source_report_id);
309
+ const txDirs = transactionDirs(projectRoot, transactionId);
310
+ await mkdir(paths.memoryRoot, { recursive: true });
311
+ await mkdir(txDirs.sanitizeRoot, { recursive: true });
312
+ await mkdir(txDirs.backupRoot, { recursive: true });
313
+ const operations = [];
314
+ const auditLines = [
315
+ {
316
+ timestamp: new Date().toISOString(),
317
+ event: 'sanitize.apply.started',
318
+ transaction_id: transactionId,
319
+ source_report_id: dryRunReport.source_report_id,
320
+ planned_actions: dryRunReport.actions.length,
321
+ planned_blocked_actions: dryRunReport.blocked_actions.length,
322
+ },
323
+ ];
324
+ let viewsRendered = false;
325
+ for (let index = 0; index < dryRunReport.actions.length; index += 1) {
326
+ const action = dryRunReport.actions[index];
327
+ const txActionId = `SAN-OP-${actionToken(action.action_id)}-${index + 1}`;
328
+ if (action.disposition !== 'safe') {
329
+ operations.push(StructuralSanitizerTransactionOperationSchema.parse({
330
+ transaction_action_id: txActionId,
331
+ action_id: action.action_id,
332
+ finding_id: action.finding_id,
333
+ disposition: action.disposition,
334
+ operation: action.operation,
335
+ status: 'skipped',
336
+ from_path: action.from_path,
337
+ to_path: action.to_path,
338
+ rollback: { operation: 'noop' },
339
+ }));
340
+ continue;
341
+ }
342
+ try {
343
+ if (action.operation === 'skip' || action.operation === 'manual_review') {
344
+ operations.push(StructuralSanitizerTransactionOperationSchema.parse({
345
+ transaction_action_id: txActionId,
346
+ action_id: action.action_id,
347
+ finding_id: action.finding_id,
348
+ disposition: action.disposition,
349
+ operation: action.operation,
350
+ status: 'skipped',
351
+ from_path: action.from_path,
352
+ to_path: action.to_path,
353
+ rollback: { operation: 'noop' },
354
+ }));
355
+ continue;
356
+ }
357
+ if (action.operation === 'mkdir') {
358
+ if (!action.to_path) {
359
+ throw new SddSanitizeApplyError(`Action ${action.action_id} missing to_path for mkdir.`);
360
+ }
361
+ const targetPath = await resolveInSddRoot(projectRoot, paths.memoryRoot, action.to_path, 'mkdir target');
362
+ const beforeHash = await hashPath(targetPath);
363
+ await mkdir(targetPath, { recursive: true });
364
+ const afterHash = await hashPath(targetPath);
365
+ operations.push(StructuralSanitizerTransactionOperationSchema.parse({
366
+ transaction_action_id: txActionId,
367
+ action_id: action.action_id,
368
+ finding_id: action.finding_id,
369
+ disposition: action.disposition,
370
+ operation: action.operation,
371
+ status: 'applied',
372
+ to_path: action.to_path,
373
+ before_hash: beforeHash,
374
+ after_hash: afterHash,
375
+ rollback: { operation: 'delete_path', path: action.to_path },
376
+ }));
377
+ continue;
378
+ }
379
+ if (action.operation === 'move') {
380
+ if (!action.from_path || !action.to_path) {
381
+ throw new SddSanitizeApplyError(`Action ${action.action_id} requires from_path and to_path for move.`);
382
+ }
383
+ const fromPath = await resolveInSddRoot(projectRoot, paths.memoryRoot, action.from_path, 'move source');
384
+ const toPath = await resolveInSddRoot(projectRoot, paths.memoryRoot, action.to_path, 'move target');
385
+ if (!(await pathExists(fromPath))) {
386
+ throw new SddSanitizeApplyError(`Action ${action.action_id} cannot move missing source: ${action.from_path}`);
387
+ }
388
+ if (await pathExists(toPath)) {
389
+ throw new SddSanitizeApplyError(`Action ${action.action_id} refuses to overwrite existing target: ${action.to_path}`);
390
+ }
391
+ const beforeHash = await hashPath(fromPath);
392
+ await mkdir(path.dirname(toPath), { recursive: true });
393
+ await rename(fromPath, toPath);
394
+ const afterHash = await hashPath(toPath);
395
+ operations.push(StructuralSanitizerTransactionOperationSchema.parse({
396
+ transaction_action_id: txActionId,
397
+ action_id: action.action_id,
398
+ finding_id: action.finding_id,
399
+ disposition: action.disposition,
400
+ operation: action.operation,
401
+ status: 'applied',
402
+ from_path: action.from_path,
403
+ to_path: action.to_path,
404
+ before_hash: beforeHash,
405
+ after_hash: afterHash,
406
+ rollback: {
407
+ operation: 'move',
408
+ from_path: action.to_path,
409
+ to_path: action.from_path,
410
+ },
411
+ }));
412
+ continue;
413
+ }
414
+ if (action.operation === 'render') {
415
+ if (!action.to_path) {
416
+ throw new SddSanitizeApplyError(`Action ${action.action_id} missing to_path for render.`);
417
+ }
418
+ const renderPath = await resolveInSddRoot(projectRoot, paths.memoryRoot, action.to_path, 'render target');
419
+ const backup = await backupPath(renderPath, txDirs.backupRoot);
420
+ const beforeHash = await hashPath(renderPath);
421
+ if (!viewsRendered) {
422
+ const state = await loadStateSnapshot(paths, config);
423
+ await renderViews(paths, config, state);
424
+ viewsRendered = true;
425
+ }
426
+ const afterHash = await hashPath(renderPath);
427
+ operations.push(StructuralSanitizerTransactionOperationSchema.parse({
428
+ transaction_action_id: txActionId,
429
+ action_id: action.action_id,
430
+ finding_id: action.finding_id,
431
+ disposition: action.disposition,
432
+ operation: action.operation,
433
+ status: 'applied',
434
+ to_path: action.to_path,
435
+ before_hash: beforeHash,
436
+ after_hash: afterHash,
437
+ rollback: backup
438
+ ? {
439
+ operation: 'restore_backup',
440
+ path: action.to_path,
441
+ backup_path: path.relative(projectRoot, backup),
442
+ }
443
+ : {
444
+ operation: 'delete_path',
445
+ path: action.to_path,
446
+ },
447
+ }));
448
+ continue;
449
+ }
450
+ if (action.operation === 'register') {
451
+ if (!action.to_path) {
452
+ throw new SddSanitizeApplyError(`Action ${action.action_id} missing to_path for register.`);
453
+ }
454
+ const targetPath = await resolveInSddRoot(projectRoot, paths.memoryRoot, action.to_path, 'register target');
455
+ if (await pathExists(targetPath)) {
456
+ throw new SddSanitizeApplyError(`Action ${action.action_id} refuses to overwrite existing file: ${action.to_path}`);
457
+ }
458
+ await mkdir(path.dirname(targetPath), { recursive: true });
459
+ await writeFile(targetPath, stringifyYaml({ version: 1 }), 'utf-8');
460
+ const afterHash = await hashPath(targetPath);
461
+ operations.push(StructuralSanitizerTransactionOperationSchema.parse({
462
+ transaction_action_id: txActionId,
463
+ action_id: action.action_id,
464
+ finding_id: action.finding_id,
465
+ disposition: action.disposition,
466
+ operation: action.operation,
467
+ status: 'applied',
468
+ to_path: action.to_path,
469
+ after_hash: afterHash,
470
+ rollback: { operation: 'delete_path', path: action.to_path },
471
+ }));
472
+ continue;
473
+ }
474
+ operations.push(StructuralSanitizerTransactionOperationSchema.parse({
475
+ transaction_action_id: txActionId,
476
+ action_id: action.action_id,
477
+ finding_id: action.finding_id,
478
+ disposition: action.disposition,
479
+ operation: action.operation,
480
+ status: 'skipped',
481
+ from_path: action.from_path,
482
+ to_path: action.to_path,
483
+ rollback: { operation: 'noop' },
484
+ }));
485
+ }
486
+ catch (error) {
487
+ const failure = StructuralSanitizerTransactionOperationSchema.parse({
488
+ transaction_action_id: txActionId,
489
+ action_id: action.action_id,
490
+ finding_id: action.finding_id,
491
+ disposition: action.disposition,
492
+ operation: action.operation,
493
+ status: 'failed',
494
+ from_path: action.from_path,
495
+ to_path: action.to_path,
496
+ error: error.message,
497
+ rollback: { operation: 'noop' },
498
+ });
499
+ operations.push(failure);
500
+ for (const operation of [...operations].reverse()) {
501
+ if (operation.status !== 'applied')
502
+ continue;
503
+ try {
504
+ await rollbackOperation(projectRoot, paths.memoryRoot, operation.rollback);
505
+ operation.status = 'rolled_back';
506
+ }
507
+ catch {
508
+ // Keep status as applied if rollback failed for this operation.
509
+ }
510
+ }
511
+ auditLines.push({
512
+ timestamp: new Date().toISOString(),
513
+ event: 'sanitize.apply.failed',
514
+ transaction_id: transactionId,
515
+ action_id: action.action_id,
516
+ error: error.message,
517
+ });
518
+ const summary = manifestSummary(operations);
519
+ const manifest = StructuralSanitizerTransactionManifestSchema.parse({
520
+ report_version: 1,
521
+ transaction_id: transactionId,
522
+ generated_at: new Date().toISOString(),
523
+ source_report_id: dryRunReport.source_report_id,
524
+ root_path: paths.memoryRoot,
525
+ mode: 'apply',
526
+ operations,
527
+ summary,
528
+ audit_log_path: path.relative(projectRoot, txDirs.auditLogPath),
529
+ });
530
+ await writeAuditLog(txDirs.auditLogPath, auditLines);
531
+ await writeFile(txDirs.manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf-8');
532
+ throw new SddSanitizeApplyError(`Sanitizer apply failed and rolled back transaction ${transactionId}: ${error.message}`);
533
+ }
534
+ }
535
+ for (const action of dryRunReport.blocked_actions) {
536
+ operations.push(StructuralSanitizerTransactionOperationSchema.parse({
537
+ transaction_action_id: `SAN-OP-${actionToken(action.action_id)}-BLOCKED`,
538
+ action_id: action.action_id,
539
+ finding_id: action.finding_id,
540
+ disposition: action.disposition,
541
+ operation: action.operation,
542
+ status: 'skipped',
543
+ from_path: action.from_path,
544
+ to_path: action.to_path,
545
+ rollback: { operation: 'noop' },
546
+ }));
547
+ }
548
+ const summary = manifestSummary(operations);
549
+ const manifest = StructuralSanitizerTransactionManifestSchema.parse({
550
+ report_version: 1,
551
+ transaction_id: transactionId,
552
+ generated_at: new Date().toISOString(),
553
+ source_report_id: dryRunReport.source_report_id,
554
+ root_path: paths.memoryRoot,
555
+ mode: 'apply',
556
+ operations,
557
+ summary,
558
+ audit_log_path: path.relative(projectRoot, txDirs.auditLogPath),
559
+ });
560
+ auditLines.push({
561
+ timestamp: new Date().toISOString(),
562
+ event: 'sanitize.apply.completed',
563
+ transaction_id: transactionId,
564
+ summary,
565
+ });
566
+ await writeAuditLog(txDirs.auditLogPath, auditLines);
567
+ await writeFile(txDirs.manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf-8');
568
+ const report = StructuralSanitizerReportSchema.parse({
569
+ ...dryRunReport,
570
+ generated_at: new Date().toISOString(),
571
+ mode: 'apply',
572
+ transaction_id: transactionId,
573
+ manifest_path: path.relative(projectRoot, txDirs.manifestPath),
574
+ audit_log_path: path.relative(projectRoot, txDirs.auditLogPath),
575
+ summary,
576
+ });
577
+ return {
578
+ source,
579
+ diagnosticReport,
580
+ report,
581
+ transaction: manifest,
582
+ };
583
+ }
584
+ async function loadDiagnosticReportFromFile(projectRoot, reportPath) {
585
+ const absolutePath = path.resolve(projectRoot, reportPath);
586
+ let raw;
587
+ try {
588
+ raw = await readFile(absolutePath, 'utf-8');
589
+ }
590
+ catch (error) {
591
+ throw new SddSanitizeOptionsError(`Could not read report file: ${path.relative(projectRoot, absolutePath)} (${error.message})`);
592
+ }
593
+ let parsed;
594
+ try {
595
+ parsed = JSON.parse(raw);
596
+ }
597
+ catch (error) {
598
+ throw new SddSanitizeOptionsError(`Invalid JSON report in ${path.relative(projectRoot, absolutePath)}: ${error.message}`);
599
+ }
600
+ const result = StructuralDiagnosticReportSchema.safeParse(parsed);
601
+ if (!result.success) {
602
+ throw new SddSanitizeOptionsError(`Invalid diagnostic report schema in ${path.relative(projectRoot, absolutePath)}.`);
603
+ }
604
+ return result.data;
605
+ }
606
+ export function formatSanitizerText(report) {
607
+ const lines = [
608
+ `SDD sanitizer plan: ${report.mode}`,
609
+ `Source report: ${report.source_report_id}`,
610
+ `Actions: ${report.actions.length}, blocked: ${report.blocked_actions.length}`,
611
+ ];
612
+ if (report.transaction_id) {
613
+ lines.push(`Transaction: ${report.transaction_id}`);
614
+ }
615
+ if (report.manifest_path) {
616
+ lines.push(`Manifest: ${report.manifest_path}`);
617
+ }
618
+ if (report.audit_log_path) {
619
+ lines.push(`Audit log: ${report.audit_log_path}`);
620
+ }
621
+ if (report.summary) {
622
+ lines.push(`Apply summary: applied=${report.summary.applied}, skipped=${report.summary.skipped}, failed=${report.summary.failed}, rolled_back=${report.summary.rolled_back}`);
623
+ }
624
+ if (report.actions.length > 0 || report.blocked_actions.length > 0) {
625
+ lines.push('');
626
+ }
627
+ for (const action of report.actions) {
628
+ lines.push(`${action.disposition.toUpperCase()} ${action.operation} ${action.finding_id}`);
629
+ if (action.from_path)
630
+ lines.push(` from: ${action.from_path}`);
631
+ if (action.to_path)
632
+ lines.push(` to: ${action.to_path}`);
633
+ if (action.notes)
634
+ lines.push(` notes: ${action.notes}`);
635
+ }
636
+ for (const action of report.blocked_actions) {
637
+ lines.push(`BLOCKED ${action.operation} ${action.finding_id}`);
638
+ if (action.from_path)
639
+ lines.push(` from: ${action.from_path}`);
640
+ if (action.to_path)
641
+ lines.push(` to: ${action.to_path}`);
642
+ if (action.notes)
643
+ lines.push(` notes: ${action.notes}`);
644
+ }
645
+ return lines.join('\n');
646
+ }
647
+ export class SddSanitizeCommand {
648
+ async execute(projectRoot, options = {}) {
649
+ const wantsApply = options.apply === true;
650
+ const wantsDryRun = options.dryRun !== false && !wantsApply;
651
+ if (wantsApply && options.yes !== true) {
652
+ throw new SddSanitizeOptionsError('Applying sanitizer actions requires explicit confirmation with --yes.');
653
+ }
654
+ if (!wantsApply && options.dryRun === false) {
655
+ throw new SddSanitizeOptionsError('Use --apply together with --yes to run a non-dry execution.');
656
+ }
657
+ const diagnosticReport = options.fromReport
658
+ ? await loadDiagnosticReportFromFile(projectRoot, options.fromReport)
659
+ : await diagnoseSddRoot(projectRoot, { strict: false });
660
+ const dryRunReport = buildSanitizerDryRunReport(diagnosticReport);
661
+ if (wantsApply) {
662
+ return applySanitizerPlan(projectRoot, options.fromReport ? 'report' : 'diagnose', diagnosticReport, dryRunReport);
663
+ }
664
+ const report = StructuralSanitizerReportSchema.parse({
665
+ ...dryRunReport,
666
+ mode: wantsDryRun ? 'dry_run' : dryRunReport.mode,
667
+ });
668
+ return {
669
+ source: options.fromReport ? 'report' : 'diagnose',
670
+ diagnosticReport,
671
+ report,
672
+ };
673
+ }
674
+ async rollback(projectRoot, transactionId, options) {
675
+ if (!options.reason || options.reason.trim().length === 0) {
676
+ throw new SddSanitizeOptionsError('Rollback requires a non-empty --reason.');
677
+ }
678
+ if (!/^SAN-[0-9]{8}T[0-9]{6}Z-[A-F0-9]{8}$/.test(transactionId)) {
679
+ throw new SddSanitizeOptionsError(`Invalid transaction id: ${transactionId}`);
680
+ }
681
+ const config = await loadProjectSddConfig(projectRoot);
682
+ const paths = resolveSddPaths(path.resolve(projectRoot), config);
683
+ const txDirs = transactionDirs(projectRoot, transactionId);
684
+ let rawManifest;
685
+ try {
686
+ rawManifest = await readFile(txDirs.manifestPath, 'utf-8');
687
+ }
688
+ catch (error) {
689
+ throw new SddSanitizeOptionsError(`Could not read manifest for ${transactionId}: ${error.message}`);
690
+ }
691
+ let parsedManifest;
692
+ try {
693
+ parsedManifest = JSON.parse(rawManifest);
694
+ }
695
+ catch (error) {
696
+ throw new SddSanitizeOptionsError(`Invalid JSON manifest for ${transactionId}: ${error.message}`);
697
+ }
698
+ const manifestResult = StructuralSanitizerTransactionManifestSchema.safeParse(parsedManifest);
699
+ if (!manifestResult.success) {
700
+ throw new SddSanitizeOptionsError(`Invalid sanitizer manifest schema for ${transactionId}.`);
701
+ }
702
+ const manifest = manifestResult.data;
703
+ const auditLines = [
704
+ {
705
+ timestamp: new Date().toISOString(),
706
+ event: 'sanitize.rollback.started',
707
+ transaction_id: transactionId,
708
+ reason: options.reason,
709
+ },
710
+ ];
711
+ let rolledBack = 0;
712
+ let skipped = 0;
713
+ for (const operation of [...manifest.operations].reverse()) {
714
+ if (operation.status !== 'applied') {
715
+ skipped += 1;
716
+ continue;
717
+ }
718
+ const target = operation.to_path ?? operation.from_path;
719
+ if (target && operation.after_hash) {
720
+ const targetPath = await resolveInSddRoot(projectRoot, paths.memoryRoot, target, 'Rollback validation target');
721
+ const currentHash = await hashPath(targetPath);
722
+ if (currentHash !== operation.after_hash) {
723
+ throw new SddSanitizeApplyError(`Rollback refused for ${operation.action_id} because ${target} changed after transaction.`);
724
+ }
725
+ }
726
+ await rollbackOperation(projectRoot, paths.memoryRoot, operation.rollback);
727
+ rolledBack += 1;
728
+ }
729
+ auditLines.push({
730
+ timestamp: new Date().toISOString(),
731
+ event: 'sanitize.rollback.completed',
732
+ transaction_id: transactionId,
733
+ rolled_back: rolledBack,
734
+ skipped,
735
+ reason: options.reason,
736
+ });
737
+ await mkdir(path.dirname(txDirs.auditLogPath), { recursive: true });
738
+ const existingAudit = await readFile(txDirs.auditLogPath, 'utf-8').catch(() => '');
739
+ const extraAudit = `${auditLines.map((line) => JSON.stringify(line)).join('\n')}\n`;
740
+ await writeFile(txDirs.auditLogPath, `${existingAudit}${extraAudit}`, 'utf-8');
741
+ return {
742
+ transactionId,
743
+ rolledBack,
744
+ skipped,
745
+ manifestPath: path.relative(projectRoot, txDirs.manifestPath),
746
+ auditLogPath: path.relative(projectRoot, txDirs.auditLogPath),
747
+ };
748
+ }
749
+ }
750
+ //# sourceMappingURL=sanitize.js.map