prizmkit 1.1.99 → 1.1.101

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 (321) hide show
  1. package/bin/create-prizmkit.js +4 -4
  2. package/bundled/VERSION.json +3 -3
  3. package/bundled/dev-pipeline/.env.example +3 -3
  4. package/bundled/dev-pipeline/README.md +110 -90
  5. package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +1 -1
  6. package/bundled/dev-pipeline/cli.py +27 -0
  7. package/bundled/dev-pipeline/prizmkit_runtime/__init__.py +13 -0
  8. package/bundled/dev-pipeline/prizmkit_runtime/__main__.py +9 -0
  9. package/bundled/dev-pipeline/prizmkit_runtime/cli.py +193 -0
  10. package/bundled/dev-pipeline/prizmkit_runtime/commands.py +313 -0
  11. package/bundled/dev-pipeline/prizmkit_runtime/compat.py +32 -0
  12. package/bundled/dev-pipeline/prizmkit_runtime/config.py +410 -0
  13. package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +478 -0
  14. package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +734 -0
  15. package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +256 -0
  16. package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +109 -0
  17. package/bundled/dev-pipeline/prizmkit_runtime/paths.py +110 -0
  18. package/bundled/dev-pipeline/prizmkit_runtime/processes.py +373 -0
  19. package/bundled/dev-pipeline/prizmkit_runtime/reset.py +389 -0
  20. package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +51 -0
  21. package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +179 -0
  22. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +345 -0
  23. package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +118 -0
  24. package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +187 -0
  25. package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +147 -0
  26. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +422 -0
  27. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +552 -0
  28. package/bundled/dev-pipeline/prizmkit_runtime/status.py +71 -0
  29. package/bundled/dev-pipeline/scripts/check-session-status.py +74 -1
  30. package/bundled/dev-pipeline/scripts/continuation.py +374 -0
  31. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +64 -52
  32. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +35 -25
  33. package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +2 -2
  34. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +15 -20
  35. package/bundled/dev-pipeline/scripts/parse-stream-progress.py +185 -16
  36. package/bundled/dev-pipeline/scripts/update-bug-status.py +214 -6
  37. package/bundled/dev-pipeline/scripts/update-feature-status.py +237 -6
  38. package/bundled/dev-pipeline/scripts/update-refactor-status.py +214 -6
  39. package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +8 -2
  40. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +4 -0
  41. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +14 -5
  42. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +16 -6
  43. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +16 -6
  44. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +1 -1
  45. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +3 -3
  46. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +2 -0
  47. package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +13 -69
  48. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +15 -7
  49. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +42 -29
  50. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +41 -39
  51. package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +4 -0
  52. package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +4 -0
  53. package/bundled/dev-pipeline/templates/session-status-schema.json +1 -1
  54. package/bundled/dev-pipeline/tests/conftest.py +1 -0
  55. package/bundled/dev-pipeline/tests/test_auto_skip.py +932 -0
  56. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +207 -0
  57. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +100 -0
  58. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +43 -0
  59. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +1138 -0
  60. package/bundled/dev-pipeline/tests/test_unified_cli.py +1045 -0
  61. package/bundled/skills/_metadata.json +1 -1
  62. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +29 -34
  63. package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +6 -5
  64. package/bundled/skills/feature-pipeline-launcher/SKILL.md +29 -35
  65. package/bundled/skills/feature-pipeline-launcher/references/configuration.md +7 -6
  66. package/bundled/skills/feature-workflow/SKILL.md +3 -6
  67. package/bundled/skills/recovery-workflow/SKILL.md +23 -32
  68. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +29 -35
  69. package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +7 -6
  70. package/bundled/skills/refactor-workflow/SKILL.md +3 -6
  71. package/bundled/templates/hooks/commit-intent.json +2 -2
  72. package/bundled/templates/hooks/diff-prizm-docs.py +226 -0
  73. package/bundled/templates/hooks/prizm-pre-commit.py +45 -0
  74. package/bundled/templates/hooks/validate-prizm-docs.py +240 -0
  75. package/package.json +1 -1
  76. package/src/config.js +33 -12
  77. package/src/index.js +3 -6
  78. package/src/manifest.js +3 -2
  79. package/src/metadata.js +7 -18
  80. package/src/prompts.js +0 -12
  81. package/src/runtimes.js +18 -11
  82. package/src/scaffold.js +133 -116
  83. package/src/upgrade.js +1 -1
  84. package/bundled/dev-pipeline/launch-bugfix-daemon.sh +0 -500
  85. package/bundled/dev-pipeline/launch-feature-daemon.sh +0 -657
  86. package/bundled/dev-pipeline/launch-refactor-daemon.sh +0 -502
  87. package/bundled/dev-pipeline/lib/branch.sh +0 -326
  88. package/bundled/dev-pipeline/lib/common.sh +0 -1257
  89. package/bundled/dev-pipeline/lib/heartbeat.sh +0 -469
  90. package/bundled/dev-pipeline/reset-bug.sh +0 -395
  91. package/bundled/dev-pipeline/reset-feature.sh +0 -406
  92. package/bundled/dev-pipeline/reset-refactor.sh +0 -393
  93. package/bundled/dev-pipeline/run-bugfix.sh +0 -1315
  94. package/bundled/dev-pipeline/run-feature.sh +0 -1602
  95. package/bundled/dev-pipeline/run-recovery.sh +0 -648
  96. package/bundled/dev-pipeline/run-refactor.sh +0 -1348
  97. package/bundled/dev-pipeline/scripts/monitor-log.sh +0 -104
  98. package/bundled/dev-pipeline/tests/test-path-resolution.sh +0 -100
  99. package/bundled/dev-pipeline-windows/.env.example +0 -36
  100. package/bundled/dev-pipeline-windows/README.md +0 -30
  101. package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +0 -533
  102. package/bundled/dev-pipeline-windows/assets/feature-list-example.json +0 -146
  103. package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +0 -137
  104. package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +0 -9
  105. package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +0 -9
  106. package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +0 -9
  107. package/bundled/dev-pipeline-windows/lib/branch.ps1 +0 -235
  108. package/bundled/dev-pipeline-windows/lib/common.ps1 +0 -829
  109. package/bundled/dev-pipeline-windows/lib/daemon.ps1 +0 -140
  110. package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +0 -967
  111. package/bundled/dev-pipeline-windows/lib/reset.ps1 +0 -87
  112. package/bundled/dev-pipeline-windows/reset-bug.ps1 +0 -9
  113. package/bundled/dev-pipeline-windows/reset-feature.ps1 +0 -9
  114. package/bundled/dev-pipeline-windows/reset-refactor.ps1 +0 -9
  115. package/bundled/dev-pipeline-windows/run-bugfix.ps1 +0 -9
  116. package/bundled/dev-pipeline-windows/run-feature.ps1 +0 -9
  117. package/bundled/dev-pipeline-windows/run-recovery.ps1 +0 -179
  118. package/bundled/dev-pipeline-windows/run-refactor.ps1 +0 -9
  119. package/bundled/dev-pipeline-windows/scripts/check-session-status.py +0 -228
  120. package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +0 -530
  121. package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +0 -2282
  122. package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +0 -767
  123. package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +0 -805
  124. package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +0 -845
  125. package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +0 -316
  126. package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +0 -134
  127. package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +0 -382
  128. package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +0 -399
  129. package/bundled/dev-pipeline-windows/scripts/monitor-log.ps1 +0 -102
  130. package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +0 -1232
  131. package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +0 -191
  132. package/bundled/dev-pipeline-windows/scripts/prizmkit-test-gate.py +0 -446
  133. package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +0 -913
  134. package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +0 -173
  135. package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +0 -1558
  136. package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +0 -1119
  137. package/bundled/dev-pipeline-windows/scripts/utils.py +0 -542
  138. package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +0 -7
  139. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +0 -7
  140. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +0 -65
  141. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +0 -5
  142. package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +0 -7
  143. package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +0 -73
  144. package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +0 -589
  145. package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +0 -710
  146. package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +0 -791
  147. package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +0 -263
  148. package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +0 -326
  149. package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +0 -237
  150. package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +0 -340
  151. package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +0 -270
  152. package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +0 -7
  153. package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +0 -91
  154. package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +0 -34
  155. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +0 -10
  156. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +0 -12
  157. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +0 -7
  158. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +0 -8
  159. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +0 -9
  160. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +0 -6
  161. package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +0 -21
  162. package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +0 -21
  163. package/bundled/dev-pipeline-windows/templates/sections/log-size-awareness.md +0 -78
  164. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +0 -283
  165. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +0 -112
  166. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +0 -168
  167. package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +0 -82
  168. package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +0 -75
  169. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +0 -23
  170. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +0 -14
  171. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +0 -19
  172. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +0 -63
  173. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +0 -42
  174. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +0 -44
  175. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +0 -59
  176. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +0 -39
  177. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +0 -27
  178. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +0 -27
  179. package/bundled/dev-pipeline-windows/templates/sections/phase-prizmkit-test.md +0 -39
  180. package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +0 -35
  181. package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +0 -35
  182. package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +0 -73
  183. package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +0 -13
  184. package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +0 -21
  185. package/bundled/dev-pipeline-windows/templates/sections/session-context.md +0 -5
  186. package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +0 -6
  187. package/bundled/dev-pipeline-windows/templates/sections/task-contract.md +0 -34
  188. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +0 -48
  189. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +0 -48
  190. package/bundled/dev-pipeline-windows/templates/session-status-schema.json +0 -83
  191. package/bundled/skills-windows/app-planner/SKILL.md +0 -296
  192. package/bundled/skills-windows/app-planner/assets/app-design-guide.md +0 -101
  193. package/bundled/skills-windows/app-planner/references/architecture-decisions.md +0 -52
  194. package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +0 -101
  195. package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +0 -71
  196. package/bundled/skills-windows/app-planner/references/infrastructure-convention-discovery.md +0 -108
  197. package/bundled/skills-windows/app-planner/references/project-brief-guide.md +0 -82
  198. package/bundled/skills-windows/app-planner/references/project-conventions-discovery.md +0 -59
  199. package/bundled/skills-windows/app-planner/references/project-state-detection.md +0 -90
  200. package/bundled/skills-windows/app-planner/references/red-team-checklist.md +0 -40
  201. package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +0 -619
  202. package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +0 -285
  203. package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +0 -249
  204. package/bundled/skills-windows/app-planner/references/rules/backend/question-manifest.json +0 -46
  205. package/bundled/skills-windows/app-planner/references/rules/backend/template.md +0 -173
  206. package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +0 -382
  207. package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +0 -211
  208. package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +0 -184
  209. package/bundled/skills-windows/app-planner/references/rules/database/question-manifest.json +0 -39
  210. package/bundled/skills-windows/app-planner/references/rules/database/template.md +0 -158
  211. package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +0 -820
  212. package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +0 -188
  213. package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +0 -319
  214. package/bundled/skills-windows/app-planner/references/rules/frontend/question-manifest.json +0 -51
  215. package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +0 -339
  216. package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +0 -649
  217. package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +0 -290
  218. package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +0 -232
  219. package/bundled/skills-windows/app-planner/references/rules/mobile/question-manifest.json +0 -47
  220. package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +0 -175
  221. package/bundled/skills-windows/app-planner/references/rules-configuration.md +0 -46
  222. package/bundled/skills-windows/bug-fix-workflow/SKILL.md +0 -379
  223. package/bundled/skills-windows/bug-fix-workflow/references/bug-diagnosis.md +0 -41
  224. package/bundled/skills-windows/bug-planner/SKILL.md +0 -402
  225. package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +0 -43
  226. package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +0 -44
  227. package/bundled/skills-windows/bug-planner/references/error-recovery.md +0 -73
  228. package/bundled/skills-windows/bug-planner/references/input-formats.md +0 -53
  229. package/bundled/skills-windows/bug-planner/references/schema-validation.md +0 -25
  230. package/bundled/skills-windows/bug-planner/references/severity-rules.md +0 -16
  231. package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +0 -322
  232. package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +0 -304
  233. package/bundled/skills-windows/bugfix-pipeline-launcher/references/configuration.md +0 -84
  234. package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +0 -381
  235. package/bundled/skills-windows/feature-pipeline-launcher/references/configuration.md +0 -67
  236. package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
  237. package/bundled/skills-windows/feature-planner/SKILL.md +0 -397
  238. package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +0 -64
  239. package/bundled/skills-windows/feature-planner/assets/planning-guide.md +0 -214
  240. package/bundled/skills-windows/feature-planner/references/browser-interaction.md +0 -59
  241. package/bundled/skills-windows/feature-planner/references/completeness-review.md +0 -57
  242. package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +0 -75
  243. package/bundled/skills-windows/feature-planner/references/error-recovery.md +0 -90
  244. package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +0 -112
  245. package/bundled/skills-windows/feature-planner/references/new-project-planning.md +0 -85
  246. package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +0 -1020
  247. package/bundled/skills-windows/feature-workflow/SKILL.md +0 -374
  248. package/bundled/skills-windows/feature-workflow/references/brainstorm-guide.md +0 -137
  249. package/bundled/skills-windows/prizm-kit/SKILL.md +0 -93
  250. package/bundled/skills-windows/prizmkit-code-review/SKILL.md +0 -161
  251. package/bundled/skills-windows/prizmkit-code-review/references/dev-agent-prompt.md +0 -30
  252. package/bundled/skills-windows/prizmkit-code-review/references/review-report-template.md +0 -31
  253. package/bundled/skills-windows/prizmkit-code-review/references/reviewer-agent-prompt.md +0 -66
  254. package/bundled/skills-windows/prizmkit-code-review/scripts/check_loop.py +0 -186
  255. package/bundled/skills-windows/prizmkit-committer/SKILL.md +0 -89
  256. package/bundled/skills-windows/prizmkit-deploy/SKILL.md +0 -444
  257. package/bundled/skills-windows/prizmkit-deploy/references/ci-cd-workflows.md +0 -115
  258. package/bundled/skills-windows/prizmkit-deploy/references/cloud-platform-deploy.md +0 -93
  259. package/bundled/skills-windows/prizmkit-deploy/references/data-safety-examples.md +0 -120
  260. package/bundled/skills-windows/prizmkit-deploy/references/database-setup.md +0 -46
  261. package/bundled/skills-windows/prizmkit-deploy/references/deploy-config-schema.md +0 -148
  262. package/bundled/skills-windows/prizmkit-deploy/references/deploy-history-schema.md +0 -62
  263. package/bundled/skills-windows/prizmkit-deploy/references/deployment-modes.md +0 -50
  264. package/bundled/skills-windows/prizmkit-deploy/references/direct-upload.md +0 -26
  265. package/bundled/skills-windows/prizmkit-deploy/references/dns-setup.md +0 -42
  266. package/bundled/skills-windows/prizmkit-deploy/references/docker-deploy.md +0 -31
  267. package/bundled/skills-windows/prizmkit-deploy/references/firewall-setup.md +0 -37
  268. package/bundled/skills-windows/prizmkit-deploy/references/live-validation-notes.md +0 -21
  269. package/bundled/skills-windows/prizmkit-deploy/references/nginx-blue-green.md +0 -59
  270. package/bundled/skills-windows/prizmkit-deploy/references/ssh-bootstrap-flow.md +0 -49
  271. package/bundled/skills-windows/prizmkit-deploy/references/ssh-execution-flow.md +0 -41
  272. package/bundled/skills-windows/prizmkit-deploy/references/ssh-takeover.md +0 -20
  273. package/bundled/skills-windows/prizmkit-deploy/references/ssl-setup.md +0 -56
  274. package/bundled/skills-windows/prizmkit-implement/SKILL.md +0 -71
  275. package/bundled/skills-windows/prizmkit-init/SKILL.md +0 -356
  276. package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +0 -82
  277. package/bundled/skills-windows/prizmkit-init/references/config-schema.md +0 -70
  278. package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +0 -41
  279. package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +0 -13
  280. package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +0 -9
  281. package/bundled/skills-windows/prizmkit-plan/SKILL.md +0 -102
  282. package/bundled/skills-windows/prizmkit-plan/assets/plan-template.md +0 -115
  283. package/bundled/skills-windows/prizmkit-plan/assets/spec-template.md +0 -73
  284. package/bundled/skills-windows/prizmkit-plan/references/clarify-guide.md +0 -67
  285. package/bundled/skills-windows/prizmkit-plan/references/examples.md +0 -85
  286. package/bundled/skills-windows/prizmkit-plan/references/verification-checklist.md +0 -60
  287. package/bundled/skills-windows/prizmkit-prizm-docs/SKILL.md +0 -129
  288. package/bundled/skills-windows/prizmkit-prizm-docs/assets/prizm-docs-format.md +0 -613
  289. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-init.md +0 -45
  290. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-rebuild.md +0 -15
  291. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-status.md +0 -14
  292. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-update.md +0 -19
  293. package/bundled/skills-windows/prizmkit-prizm-docs/references/op-validate.md +0 -17
  294. package/bundled/skills-windows/prizmkit-retrospective/SKILL.md +0 -87
  295. package/bundled/skills-windows/prizmkit-retrospective/references/knowledge-injection-steps.md +0 -50
  296. package/bundled/skills-windows/prizmkit-retrospective/references/structural-sync-steps.md +0 -43
  297. package/bundled/skills-windows/prizmkit-test/SKILL.md +0 -227
  298. package/bundled/skills-windows/prizmkit-test/references/boundary-coverage-protocol.md +0 -220
  299. package/bundled/skills-windows/prizmkit-test/references/examples.md +0 -143
  300. package/bundled/skills-windows/prizmkit-test/references/service-boundary-test-catalog.md +0 -225
  301. package/bundled/skills-windows/prizmkit-test/references/test-generation-steps.md +0 -109
  302. package/bundled/skills-windows/prizmkit-test/references/test-report-template.md +0 -111
  303. package/bundled/skills-windows/prizmkit-test/scripts/validate_boundary_report.py +0 -347
  304. package/bundled/skills-windows/recovery-workflow/SKILL.md +0 -355
  305. package/bundled/skills-windows/recovery-workflow/evals/evals.json +0 -46
  306. package/bundled/skills-windows/recovery-workflow/references/detection.md +0 -58
  307. package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +0 -544
  308. package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +0 -346
  309. package/bundled/skills-windows/refactor-pipeline-launcher/references/configuration.md +0 -72
  310. package/bundled/skills-windows/refactor-planner/SKILL.md +0 -398
  311. package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +0 -292
  312. package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +0 -301
  313. package/bundled/skills-windows/refactor-planner/references/fast-path.md +0 -59
  314. package/bundled/skills-windows/refactor-planner/references/planning-phases.md +0 -135
  315. package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +0 -221
  316. package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +0 -858
  317. package/bundled/skills-windows/refactor-workflow/SKILL.md +0 -343
  318. package/bundled/skills-windows/refactor-workflow/references/brainstorm-guide.md +0 -116
  319. package/bundled/templates/hooks/diff-prizm-docs.sh +0 -158
  320. package/bundled/templates/hooks/prizm-pre-commit.sh +0 -108
  321. package/bundled/templates/hooks/validate-prizm-docs.sh +0 -139
@@ -1,1020 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- validate-and-generate.py - Validate and generate feature-list.json files
4
- for the dev-pipeline system.
5
-
6
- Commands:
7
- validate Validate an existing .prizmkit/plans/feature-list.json
8
- template Generate a blank template .prizmkit/plans/feature-list.json
9
- generate Validate a draft JSON and generate final feature-list.json with defaults
10
- summary Print a summary table of features from a .prizmkit/plans/feature-list.json
11
- grade Generate grading results from eval runs (for npm run skill:review)
12
-
13
- Usage:
14
- python3 validate-and-generate.py validate --input .prizmkit/plans/feature-list.json [--output validated.json]
15
- python3 validate-and-generate.py template --output .prizmkit/plans/feature-list.json
16
- python3 validate-and-generate.py generate --input draft.json --output .prizmkit/plans/feature-list.json
17
- python3 validate-and-generate.py summary --input .prizmkit/plans/feature-list.json [--format markdown|json]
18
- python3 validate-and-generate.py grade --workspace /.codebuddy/skill-evals/feature-planner-workspace --iteration iteration-1
19
-
20
- Mode is determined automatically by --output path:
21
- - Output file does NOT exist -> new plan (all features must be "pending")
22
- - Output file already exists -> incremental append (existing features keep their status)
23
-
24
- Python 3.6+ required. No external dependencies.
25
- """
26
-
27
- import argparse
28
- import collections
29
- import json
30
- import os
31
- import re
32
- import sys
33
- from datetime import datetime, timezone
34
-
35
- # ---------------------------------------------------------------------------
36
- # Constants
37
- # ---------------------------------------------------------------------------
38
-
39
- SCHEMA_VERSION = "dev-pipeline-feature-list-v1"
40
-
41
- VALID_STATUSES = {"pending", "in_progress", "completed", "failed", "skipped", "split", "auto_skipped"}
42
- VALID_COMPLEXITIES = {"low", "medium", "high", "critical"}
43
- VALID_PRIORITIES = {"critical", "high", "medium", "low"}
44
- VALID_GRANULARITIES = {"feature", "sub_feature", "auto"}
45
- FEATURE_ID_RE = re.compile(r"^F-\d{3}(-[A-Z])?$")
46
- SUB_FEATURE_ID_RE = re.compile(r"^F-\d{3}-[A-Z]$")
47
-
48
- # Keywords in acceptance criteria that indicate UI/browser interaction behavior.
49
- UI_KEYWORDS_RE = re.compile(
50
- r"\b(click|button|modal|page|form|display|navigate|tab|input|opens|shows|"
51
- r"renders|visible|redirect|download|upload|preview|select|toggle|dropdown|"
52
- r"popup|toast|menu)\b",
53
- re.IGNORECASE,
54
- )
55
-
56
- # ---------------------------------------------------------------------------
57
- # Helpers
58
- # ---------------------------------------------------------------------------
59
-
60
-
61
- def _err(msg):
62
- """Print an error message to stderr."""
63
- print("ERROR: {}".format(msg), file=sys.stderr)
64
-
65
-
66
- def _warn(msg):
67
- """Print a warning message to stderr."""
68
- print("WARNING: {}".format(msg), file=sys.stderr)
69
-
70
-
71
- def _info(msg):
72
- """Print an informational message to stderr."""
73
- print("INFO: {}".format(msg), file=sys.stderr)
74
-
75
-
76
- def _load_json(path):
77
- """Load and return parsed JSON from *path*.
78
-
79
- Returns (data, error_message). On success error_message is None.
80
- """
81
- if not os.path.isfile(path):
82
- return None, "File not found: {}".format(path)
83
- try:
84
- with open(path, "r", encoding="utf-8") as fh:
85
- data = json.load(fh)
86
- return data, None
87
- except json.JSONDecodeError as exc:
88
- return None, "JSON parse error in {}: {}".format(path, exc)
89
- except Exception as exc:
90
- return None, "Failed to read {}: {}".format(path, exc)
91
-
92
-
93
- def _write_json(path, data):
94
- """Write *data* as pretty-printed JSON to *path*."""
95
- parent = os.path.dirname(path)
96
- if parent and not os.path.isdir(parent):
97
- os.makedirs(parent, exist_ok=True)
98
- with open(path, "w", encoding="utf-8") as fh:
99
- json.dump(data, fh, indent=2, ensure_ascii=False)
100
- fh.write("\n")
101
-
102
-
103
- # ---------------------------------------------------------------------------
104
- # Cycle detection (Kahn's algorithm)
105
- # ---------------------------------------------------------------------------
106
-
107
-
108
-
109
- def _detect_cycles(features):
110
- """Return (has_cycles: bool, max_depth: int) using Kahn's topological sort.
111
-
112
- *features* is the list of feature dicts. We build a graph from the
113
- ``dependencies`` field and run Kahn's algorithm.
114
-
115
- Returns a tuple ``(has_cycles, max_depth)`` where *max_depth* is the
116
- longest path in the DAG (0 if there are cycles or a single node).
117
- """
118
- id_set = {f["id"] for f in features}
119
- # Build adjacency list and in-degree map.
120
- adj = {fid: [] for fid in id_set} # dependency -> [dependent]
121
- in_degree = {fid: 0 for fid in id_set}
122
-
123
- for feat in features:
124
- fid = feat["id"]
125
- for dep in feat.get("dependencies", []):
126
- if dep in id_set:
127
- adj[dep].append(fid)
128
- in_degree[fid] += 1
129
-
130
- # Kahn's algorithm
131
- queue = collections.deque()
132
- for fid, deg in in_degree.items():
133
- if deg == 0:
134
- queue.append(fid)
135
-
136
- sorted_order = []
137
- # Track depth for each node to compute max dependency depth.
138
- depth = {fid: 0 for fid in id_set}
139
-
140
- while queue:
141
- node = queue.popleft()
142
- sorted_order.append(node)
143
- for neighbour in adj[node]:
144
- in_degree[neighbour] -= 1
145
- new_depth = depth[node] + 1
146
- if new_depth > depth[neighbour]:
147
- depth[neighbour] = new_depth
148
- if in_degree[neighbour] == 0:
149
- queue.append(neighbour)
150
-
151
- has_cycles = len(sorted_order) != len(id_set)
152
- max_depth = max(depth.values()) if depth else 0
153
- return has_cycles, max_depth
154
-
155
-
156
- # ---------------------------------------------------------------------------
157
- # Validation
158
- # ---------------------------------------------------------------------------
159
-
160
-
161
- def validate_feature_list(data, is_new_plan=True):
162
- """Validate a parsed feature-list data structure.
163
-
164
- Returns a dict with keys ``valid``, ``errors``, ``warnings``, ``stats``.
165
- """
166
-
167
- errors = []
168
- warnings = []
169
-
170
- # ------------------------------------------------------------------
171
- # 1. Top-level schema validation
172
- # ------------------------------------------------------------------
173
- schema = data.get("$schema")
174
- if schema != SCHEMA_VERSION:
175
- errors.append(
176
- "$schema must be '{}', got '{}'".format(SCHEMA_VERSION, schema)
177
- )
178
-
179
- # Support both project_name (canonical) and app_name (legacy)
180
- app_name = data.get("project_name", data.get("app_name"))
181
- if not isinstance(app_name, str) or not app_name.strip():
182
- errors.append("project_name must be a non-empty string")
183
-
184
- features = data.get("features")
185
- if not isinstance(features, list) or len(features) == 0:
186
- errors.append("features must be a non-empty array")
187
- # Early-out: nothing else to validate if features are missing.
188
- return {
189
- "valid": False,
190
- "errors": errors,
191
- "warnings": warnings,
192
- "stats": {
193
- "total_features": 0,
194
- "total_sub_features": 0,
195
- "complexity_distribution": {},
196
- "max_dependency_depth": 0,
197
- "has_cycles": False,
198
- },
199
- }
200
-
201
- # ------------------------------------------------------------------
202
- # 2. Per-feature validation
203
- # ------------------------------------------------------------------
204
- required_keys = {
205
- "id", "title", "description", "priority",
206
- "dependencies", "acceptance_criteria", "status",
207
- }
208
-
209
- seen_ids = set()
210
- priorities = []
211
- complexity_dist = {"low": 0, "medium": 0, "high": 0, "critical": 0}
212
- total_sub_features = 0
213
-
214
- for idx, feat in enumerate(features):
215
- label = "features[{}]".format(idx)
216
-
217
- # -- Required keys --
218
- if not isinstance(feat, dict):
219
- errors.append("{} is not an object".format(label))
220
- continue
221
-
222
- missing = required_keys - set(feat.keys())
223
- if missing:
224
- errors.append("{} missing required keys: {}".format(
225
- label, ", ".join(sorted(missing))
226
- ))
227
-
228
- # -- ID format & uniqueness --
229
- fid = feat.get("id", "")
230
- if not FEATURE_ID_RE.match(str(fid)):
231
- errors.append(
232
- "{}: id '{}' does not match pattern F-NNN or F-NNN-X".format(label, fid)
233
- )
234
- if fid in seen_ids:
235
- errors.append("{}: duplicate id '{}'".format(label, fid))
236
- seen_ids.add(fid)
237
-
238
- # -- Title / description --
239
- for key in ("title", "description"):
240
- val = feat.get(key)
241
- if not isinstance(val, str) or not val.strip():
242
- errors.append("{}: {} must be a non-empty string".format(label, key))
243
-
244
- # -- Description depth check --
245
- desc = feat.get("description", "")
246
- if isinstance(desc, str) and desc.strip():
247
- word_count = len(desc.split())
248
- complexity = feat.get("estimated_complexity", "medium")
249
- min_words = {
250
- "low": 30, "medium": 50, "high": 80, "critical": 100,
251
- }.get(complexity, 50)
252
- if word_count < 15:
253
- errors.append(
254
- "{}: description too short ({} words, minimum 15). "
255
- "Include: what to build, key behaviors, integration points, "
256
- "and data model overview.".format(label, word_count)
257
- )
258
- elif word_count < min_words:
259
- warnings.append(
260
- "{}: description only {} words (recommend {}+ for {} complexity). "
261
- "Richer descriptions produce better pipeline results.".format(
262
- label, word_count, min_words, complexity
263
- )
264
- )
265
-
266
- # -- Priority --
267
- priority = feat.get("priority")
268
- if isinstance(priority, str) and priority in VALID_PRIORITIES:
269
- priorities.append(priority)
270
- else:
271
- errors.append("{}: priority must be one of 'high', 'medium', 'low', got {}".format(
272
- label, repr(priority)
273
- ))
274
-
275
- # -- Dependencies (list of strings) --
276
- deps = feat.get("dependencies")
277
- if not isinstance(deps, list):
278
- errors.append("{}: dependencies must be an array".format(label))
279
-
280
- # -- Acceptance criteria --
281
- criteria = feat.get("acceptance_criteria")
282
- if isinstance(criteria, list):
283
- if len(criteria) < 1:
284
- errors.append("{}: must have at least 1 acceptance criterion".format(label))
285
- elif len(criteria) < 3:
286
- warnings.append(
287
- "{}: only {} acceptance criteria (recommend at least 3)".format(
288
- label, len(criteria)
289
- )
290
- )
291
- else:
292
- errors.append("{}: acceptance_criteria must be an array".format(label))
293
-
294
- # -- Status --
295
- status = feat.get("status")
296
- if status not in VALID_STATUSES:
297
- errors.append(
298
- "{}: status '{}' invalid, must be one of: {}".format(
299
- label, status, ", ".join(sorted(VALID_STATUSES))
300
- )
301
- )
302
- if is_new_plan and status and status != "pending":
303
- warnings.append(
304
- "{}: status is '{}' (expected 'pending' for new plans)".format(label, status)
305
- )
306
-
307
- # -- Complexity (optional but validated if present) --
308
- complexity = feat.get("estimated_complexity")
309
- if complexity is not None:
310
- if complexity not in VALID_COMPLEXITIES:
311
- errors.append(
312
- "{}: estimated_complexity '{}' invalid, must be one of: {}".format(
313
- label, complexity, ", ".join(sorted(VALID_COMPLEXITIES))
314
- )
315
- )
316
- else:
317
- complexity_dist[complexity] += 1
318
-
319
- # -- Granularity (optional but validated if present) --
320
- granularity = feat.get("session_granularity")
321
- if granularity is not None:
322
- if granularity not in VALID_GRANULARITIES:
323
- errors.append(
324
- "{}: session_granularity '{}' invalid, must be one of: {}".format(
325
- label, granularity, ", ".join(sorted(VALID_GRANULARITIES))
326
- )
327
- )
328
- if granularity == "auto":
329
- subs = feat.get("sub_features")
330
- if not isinstance(subs, list) or len(subs) == 0:
331
- warnings.append(
332
- "{}: granularity is 'auto' but no sub_features defined".format(label)
333
- )
334
-
335
- # -- Sub-features --
336
- subs = feat.get("sub_features")
337
-
338
- # -- Critic fields (optional but validated if present) --
339
- critic = feat.get("critic")
340
- if critic is not None and not isinstance(critic, bool):
341
- errors.append(
342
- "{}: 'critic' must be a boolean, got {}".format(label, type(critic).__name__)
343
- )
344
- critic_count = feat.get("critic_count")
345
- if critic_count is not None and critic_count not in (1, 3):
346
- errors.append(
347
- "{}: 'critic_count' must be 1 or 3, got {}".format(label, critic_count)
348
- )
349
-
350
- # -- Browser interaction check (warning for frontend features) --
351
- has_frontend = bool(
352
- data.get("global_context", {}).get("frontend_framework")
353
- )
354
- if has_frontend and feat.get("status") != "completed":
355
- criteria = feat.get("acceptance_criteria", [])
356
- criteria_text = " ".join(
357
- c for c in criteria if isinstance(c, str)
358
- )
359
- if UI_KEYWORDS_RE.search(criteria_text):
360
- if feat.get("browser_interaction") is None:
361
- warnings.append(
362
- "{}: has UI acceptance criteria but no browser_interaction "
363
- "field. Consider adding browser verification.".format(label)
364
- )
365
-
366
- if isinstance(subs, list):
367
- for sidx, sub in enumerate(subs):
368
- sub_label = "{}->sub_features[{}]".format(label, sidx)
369
- if not isinstance(sub, dict):
370
- errors.append("{} is not an object".format(sub_label))
371
- continue
372
-
373
- sub_missing = {"id", "title", "description"} - set(sub.keys())
374
- if sub_missing:
375
- errors.append("{} missing required keys: {}".format(
376
- sub_label, ", ".join(sorted(sub_missing))
377
- ))
378
-
379
- sub_id = sub.get("id", "")
380
- if not SUB_FEATURE_ID_RE.match(str(sub_id)):
381
- errors.append(
382
- "{}: id '{}' must be F-NNN-X format".format(sub_label, sub_id)
383
- )
384
-
385
- # Sub-feature ID should share parent prefix
386
- parent_prefix = str(fid).rstrip("ABCDEFGHIJKLMNOPQRSTUVWXYZ").rstrip("-")
387
- sub_prefix = str(sub_id)[:5] # e.g. "F-001"
388
- if parent_prefix and sub_prefix != parent_prefix:
389
- warnings.append(
390
- "{}: sub-feature '{}' does not share parent prefix '{}'".format(
391
- sub_label, sub_id, parent_prefix
392
- )
393
- )
394
-
395
- if sub_id in seen_ids:
396
- errors.append("{}: duplicate id '{}'".format(sub_label, sub_id))
397
- seen_ids.add(sub_id)
398
- total_sub_features += 1
399
-
400
- # ------------------------------------------------------------------
401
- # 3. Dependency validation
402
- # ------------------------------------------------------------------
403
- all_ids = {f.get("id") for f in features}
404
- for idx, feat in enumerate(features):
405
- label = "features[{}]".format(idx)
406
- deps = feat.get("dependencies", [])
407
- if isinstance(deps, list):
408
- for dep in deps:
409
- if dep not in all_ids:
410
- errors.append(
411
- "{}: dependency '{}' does not exist in feature list".format(label, dep)
412
- )
413
-
414
- # -- Cycle detection --
415
- has_cycles, max_depth = _detect_cycles(features)
416
- if has_cycles:
417
- errors.append("Dependency graph contains cycles (not a valid DAG)")
418
-
419
- # ------------------------------------------------------------------
420
- # 4. Build result
421
- # ------------------------------------------------------------------
422
- is_valid = len(errors) == 0
423
-
424
- return {
425
- "valid": is_valid,
426
- "errors": errors,
427
- "warnings": warnings,
428
- "stats": {
429
- "total_features": len(features),
430
- "total_sub_features": total_sub_features,
431
- "complexity_distribution": complexity_dist,
432
- "max_dependency_depth": max_depth,
433
- "has_cycles": has_cycles,
434
- },
435
- }
436
-
437
-
438
- # ---------------------------------------------------------------------------
439
- # Template generation
440
- # ---------------------------------------------------------------------------
441
-
442
-
443
- def generate_template():
444
- """Return a template feature-list dict with placeholder values."""
445
- now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
446
-
447
- return {
448
- "$schema": SCHEMA_VERSION,
449
- "project_name": "YOUR_PROJECT_NAME",
450
- "project_description": "YOUR_PROJECT_DESCRIPTION",
451
- "created_at": now,
452
- "created_by": "feature-planner",
453
- "features": [
454
- {
455
- "id": "F-001",
456
- "title": "Project Infrastructure Setup",
457
- "description": (
458
- "Initialize project structure, configure build tools, "
459
- "set up development environment."
460
- ),
461
- "priority": "high",
462
- "estimated_complexity": "medium",
463
- "dependencies": [],
464
- "acceptance_criteria": [
465
- "Project builds successfully",
466
- "Development server starts",
467
- "Linting and formatting configured",
468
- ],
469
- "status": "pending",
470
- "session_granularity": "feature",
471
- "sub_features": [],
472
- }
473
- ],
474
- "global_context": {
475
- "tech_stack": "",
476
- "design_system": "",
477
- "testing_strategy": "",
478
- },
479
- }
480
-
481
-
482
- # ---------------------------------------------------------------------------
483
- # Summary
484
- # ---------------------------------------------------------------------------
485
-
486
-
487
- def _build_dependency_graph_text(features):
488
- """Build a human-readable text representation of the dependency graph.
489
-
490
- Produces an arrow-chain format that shows all dependency paths,
491
- including convergent edges (where multiple paths lead to the same node).
492
-
493
- Returns a list of lines.
494
- """
495
- all_ids = [f["id"] for f in features]
496
-
497
- # Build adjacency: dependency -> list of dependents (forward edges)
498
- dependents = {fid: [] for fid in all_ids}
499
- has_parent = set()
500
- for feat in features:
501
- for dep in feat.get("dependencies", []):
502
- if dep in dependents:
503
- dependents[dep].append(feat["id"])
504
- has_parent.add(feat["id"])
505
-
506
- # Sort children for deterministic output
507
- for fid in dependents:
508
- dependents[fid] = sorted(set(dependents[fid]))
509
-
510
- # Roots: features with no incoming dependencies
511
- roots = [fid for fid in all_ids if fid not in has_parent]
512
- if not roots:
513
- return ["(cycle detected - no root nodes)"]
514
- if not any(dependents[r] for r in all_ids):
515
- # No dependencies at all
516
- return ["(no dependencies)"]
517
-
518
- result_lines = []
519
-
520
- def _render(node, prefix, is_continuation):
521
- """Render a node and its dependents recursively.
522
-
523
- *prefix*: whitespace to print before " -> node" on branch lines.
524
- *is_continuation*: True if this node is appended on the same line
525
- as its parent (first child).
526
- """
527
- children = dependents.get(node, [])
528
- if not children:
529
- return
530
-
531
- for i, child in enumerate(children):
532
- if i == 0:
533
- # First child: continue on the same line
534
- result_lines[-1] += " -> {}".format(child)
535
- _render(child, prefix + " " * (len(node) + 4), True)
536
- else:
537
- # Subsequent children: new line, indented under the arrow
538
- line = "{}-> {}".format(prefix, child)
539
- result_lines.append(line)
540
- child_prefix = prefix + " " * (len(child) + 4)
541
- _render(child, child_prefix, True)
542
-
543
- for root in sorted(roots):
544
- result_lines.append(root)
545
- _render(root, " " * len(root), False)
546
-
547
- return result_lines
548
-
549
-
550
- def generate_summary_markdown(data):
551
- """Generate a markdown summary of the feature list."""
552
- app_name = data.get("project_name", data.get("app_name", "Unknown"))
553
- features = data.get("features", [])
554
-
555
- lines = []
556
- lines.append("# Feature Summary: {}".format(app_name))
557
- lines.append("")
558
-
559
- # Table header
560
- lines.append("| ID | Title | Complexity | Priority | Dependencies | Criteria | Granularity |")
561
- lines.append("|----|-------|------------|----------|--------------|----------|-------------|")
562
-
563
- for feat in features:
564
- fid = feat.get("id", "?")
565
- title = feat.get("title", "?")
566
- complexity = feat.get("estimated_complexity", "-")
567
- priority = feat.get("priority", "?")
568
- deps = feat.get("dependencies", [])
569
- deps_str = ", ".join(deps) if deps else "-"
570
- criteria_count = len(feat.get("acceptance_criteria", []))
571
- granularity = feat.get("session_granularity", "-")
572
-
573
- lines.append("| {} | {} | {} | {} | {} | {} | {} |".format(
574
- fid, title, complexity, priority, deps_str, criteria_count, granularity
575
- ))
576
-
577
- lines.append("")
578
-
579
- # Dependency graph
580
- lines.append("## Dependency Graph")
581
- graph_lines = _build_dependency_graph_text(features)
582
- for gl in graph_lines:
583
- lines.append(gl)
584
- lines.append("")
585
-
586
- # Statistics
587
- complexity_dist = {"low": 0, "medium": 0, "high": 0, "critical": 0}
588
- total_sub = 0
589
- for feat in features:
590
- c = feat.get("estimated_complexity")
591
- if c in complexity_dist:
592
- complexity_dist[c] += 1
593
- subs = feat.get("sub_features")
594
- if isinstance(subs, list):
595
- total_sub += len(subs)
596
-
597
- _, max_depth = _detect_cycles(features)
598
-
599
- lines.append("## Statistics")
600
- lines.append("- Total features: {}".format(len(features)))
601
- if total_sub > 0:
602
- lines.append("- Total sub-features: {}".format(total_sub))
603
- lines.append("- Complexity: {} low, {} medium, {} high, {} critical".format(
604
- complexity_dist["low"], complexity_dist["medium"],
605
- complexity_dist["high"], complexity_dist["critical"]
606
- ))
607
- lines.append("- Max dependency depth: {}".format(max_depth))
608
-
609
- return "\n".join(lines)
610
-
611
-
612
- def generate_summary_json(data):
613
- """Generate a JSON summary of the feature list."""
614
- features = data.get("features", [])
615
-
616
- complexity_dist = {"low": 0, "medium": 0, "high": 0, "critical": 0}
617
- total_sub = 0
618
- for feat in features:
619
- c = feat.get("estimated_complexity")
620
- if c in complexity_dist:
621
- complexity_dist[c] += 1
622
- subs = feat.get("sub_features")
623
- if isinstance(subs, list):
624
- total_sub += len(subs)
625
-
626
- has_cycles, max_depth = _detect_cycles(features)
627
-
628
- feature_summaries = []
629
- for feat in features:
630
- feature_summaries.append({
631
- "id": feat.get("id"),
632
- "title": feat.get("title"),
633
- "priority": feat.get("priority"),
634
- "estimated_complexity": feat.get("estimated_complexity"),
635
- "dependencies": feat.get("dependencies", []),
636
- "acceptance_criteria_count": len(feat.get("acceptance_criteria", [])),
637
- "session_granularity": feat.get("session_granularity"),
638
- "status": feat.get("status"),
639
- })
640
-
641
- return {
642
- "project_name": data.get("project_name", data.get("app_name", "")),
643
- "features": feature_summaries,
644
- "stats": {
645
- "total_features": len(features),
646
- "total_sub_features": total_sub,
647
- "complexity_distribution": complexity_dist,
648
- "max_dependency_depth": max_depth,
649
- "has_cycles": has_cycles,
650
- },
651
- }
652
-
653
-
654
- # ---------------------------------------------------------------------------
655
- # CLI
656
- # ---------------------------------------------------------------------------
657
-
658
-
659
- def cmd_validate(args):
660
- """Handle the 'validate' command."""
661
- if not args.input:
662
- _err("--input is required for the validate command")
663
- return 2
664
-
665
- data, load_err = _load_json(args.input)
666
- if load_err:
667
- _err(load_err)
668
- result = {
669
- "valid": False,
670
- "errors": [load_err],
671
- "warnings": [],
672
- "stats": {
673
- "total_features": 0,
674
- "total_sub_features": 0,
675
- "complexity_distribution": {},
676
- "max_dependency_depth": 0,
677
- "has_cycles": False,
678
- },
679
- }
680
- print(json.dumps(result, indent=2, ensure_ascii=False))
681
- return 2
682
-
683
- # Validate input file exists -> incremental mode (existing features keep their status)
684
- is_new_plan = False
685
- result = validate_feature_list(data, is_new_plan=is_new_plan)
686
-
687
- # Print results to stdout
688
- print(json.dumps(result, indent=2, ensure_ascii=False))
689
-
690
- # Log to stderr for humans
691
- if result["valid"]:
692
- _info("Validation passed with {} warning(s)".format(len(result["warnings"])))
693
- else:
694
- _err("Validation failed with {} error(s) and {} warning(s)".format(
695
- len(result["errors"]), len(result["warnings"])
696
- ))
697
-
698
- for e in result["errors"]:
699
- _err(" " + e)
700
- for w in result["warnings"]:
701
- _warn(" " + w)
702
-
703
- # Optionally write validated/cleaned output
704
- if args.output and result["valid"]:
705
- _write_json(args.output, data)
706
- _info("Validated output written to {}".format(args.output))
707
-
708
- return 0 if result["valid"] else 1
709
-
710
-
711
- def cmd_template(args):
712
- """Handle the 'template' command."""
713
- if not args.output:
714
- _err("--output is required for the template command")
715
- return 2
716
-
717
- template = generate_template()
718
- _write_json(args.output, template)
719
- _info("Template written to {}".format(args.output))
720
- return 0
721
-
722
-
723
- def cmd_generate(args):
724
- """Handle the 'generate' command.
725
-
726
- Loads a draft JSON (produced by AI), fills in defaults, validates,
727
- and writes the final feature-list.json.
728
- """
729
- if not args.input:
730
- _err("--input is required for the generate command")
731
- return 2
732
- if not args.output:
733
- _err("--output is required for the generate command")
734
- return 2
735
-
736
- # Load draft (supports stdin via '-')
737
- if args.input == "-":
738
- try:
739
- data = json.load(sys.stdin)
740
- except json.JSONDecodeError as exc:
741
- _err("Invalid JSON from stdin: {}".format(exc))
742
- return 2
743
- else:
744
- data, load_err = _load_json(args.input)
745
- if load_err:
746
- _err(load_err)
747
- return 2
748
-
749
- # Fill in defaults
750
- now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
751
- data.setdefault("$schema", SCHEMA_VERSION)
752
- data.setdefault("created_at", now)
753
- data.setdefault("created_by", "feature-planner")
754
-
755
- # Ensure project_name from app_name if needed
756
- if "project_name" not in data and "app_name" in data:
757
- data["project_name"] = data["app_name"]
758
-
759
- # Set default status for features without one
760
- for feature in data.get("features", []):
761
- feature.setdefault("status", "pending")
762
-
763
- # Validate. Mode is determined by whether --output file already exists:
764
- # - does NOT exist -> new plan (all features must be "pending")
765
- # - already exists -> incremental append (existing features keep their status)
766
- is_new_plan = not os.path.exists(args.output)
767
- result = validate_feature_list(data, is_new_plan=is_new_plan)
768
-
769
- # Output validation result
770
- print(json.dumps(result, indent=2, ensure_ascii=False))
771
-
772
- if result["valid"]:
773
- _write_json(args.output, data)
774
- _info("Generated feature-list written to {}".format(args.output))
775
- return 0
776
- else:
777
- _err("Validation failed with {} error(s)".format(len(result["errors"])))
778
- for e in result["errors"]:
779
- _err(" " + e)
780
- for w in result.get("warnings", []):
781
- _warn(" " + w)
782
- return 1
783
-
784
-
785
- def cmd_summary(args):
786
- """Handle the 'summary' command."""
787
- if not args.input:
788
- _err("--input is required for the summary command")
789
- return 2
790
-
791
- data, load_err = _load_json(args.input)
792
- if load_err:
793
- _err(load_err)
794
- return 2
795
-
796
- output_format = getattr(args, "format", "markdown") or "markdown"
797
-
798
- if output_format == "json":
799
- summary = generate_summary_json(data)
800
- print(json.dumps(summary, indent=2, ensure_ascii=False))
801
- else:
802
- summary = generate_summary_markdown(data)
803
- print(summary)
804
-
805
- return 0
806
-
807
-
808
- def cmd_grade(args):
809
- """Handle the 'grade' command for evaluation framework integration.
810
-
811
- Collects validation results from eval runs and generates grading data.
812
- Used by npm run skill:review for automated evaluation of feature-planner.
813
- """
814
- workspace = getattr(args, 'workspace', None)
815
- iteration = getattr(args, 'iteration', None)
816
-
817
- if not workspace or not iteration:
818
- _err("--workspace and --iteration are required for the grade command")
819
- return 2
820
-
821
- workspace_path = os.path.expanduser(workspace)
822
-
823
- if not os.path.isdir(workspace_path):
824
- _err("Workspace directory not found: {}".format(workspace_path))
825
- return 2
826
-
827
- # Collect run outputs from iteration subdirectory
828
- iteration_dir = os.path.join(workspace_path, iteration)
829
- if not os.path.isdir(iteration_dir):
830
- _err("Iteration directory not found: {}".format(iteration_dir))
831
- return 2
832
-
833
- # Find all eval-* subdirectories
834
- eval_dirs = []
835
- try:
836
- for item in os.listdir(iteration_dir):
837
- item_path = os.path.join(iteration_dir, item)
838
- if os.path.isdir(item_path) and item.startswith('eval-'):
839
- eval_dirs.append((item, item_path))
840
- except Exception as exc:
841
- _err("Failed to list iteration directory: {}".format(exc))
842
- return 2
843
-
844
- if not eval_dirs:
845
- _warn("No eval-* directories found in {}".format(iteration_dir))
846
-
847
- grades = []
848
-
849
- for eval_name, eval_path in sorted(eval_dirs):
850
- output_json = os.path.join(eval_path, "outputs", "feature-list.json")
851
-
852
- if not os.path.isfile(output_json):
853
- _info("Skipping {}: no output/feature-list.json found".format(eval_name))
854
- continue
855
-
856
- # Load and validate the output
857
- data, load_err = _load_json(output_json)
858
- if load_err:
859
- _warn("Failed to load {}: {}".format(output_json, load_err))
860
- continue
861
-
862
- # Run validation
863
- result = validate_feature_list(data, is_new_plan=True)
864
-
865
- # Create grading entry
866
- grade_entry = {
867
- "test_name": eval_name,
868
- "passed": result["valid"],
869
- "assertions": [
870
- {
871
- "name": "Feature list valid schema",
872
- "passed": result["valid"],
873
- "evidence": "valid={}".format(result["valid"])
874
- },
875
- {
876
- "name": "No cycles in DAG",
877
- "passed": not result["stats"].get("has_cycles", False),
878
- "evidence": "cycles={}".format(result["stats"].get("has_cycles", False))
879
- },
880
- {
881
- "name": "Features generated",
882
- "passed": result["stats"].get("total_features", 0) > 0,
883
- "evidence": "count={}".format(result["stats"].get("total_features", 0))
884
- },
885
- {
886
- "name": "No validation errors",
887
- "passed": len(result.get("errors", [])) == 0,
888
- "evidence": "error_count={}".format(len(result.get("errors", [])))
889
- }
890
- ]
891
- }
892
-
893
- grades.append(grade_entry)
894
-
895
- # Write grading.json to eval run directory
896
- grading_file = os.path.join(eval_path, "grading.json")
897
- _write_json(grading_file, grade_entry)
898
- _info("Wrote grading to {}".format(grading_file))
899
-
900
- # Write aggregated results
901
- aggregated = {
902
- "iteration": iteration,
903
- "total_runs": len(grades),
904
- "passed_runs": sum(1 for g in grades if g["passed"]),
905
- "pass_rate": len([g for g in grades if g["passed"]]) / len(grades) if grades else 0,
906
- "grades": grades
907
- }
908
-
909
- benchmark_file = os.path.join(iteration_dir, "benchmark.json")
910
- _write_json(benchmark_file, aggregated)
911
- _info("Wrote aggregated benchmark to {}".format(benchmark_file))
912
-
913
- return 0
914
-
915
-
916
- def main():
917
- parser = argparse.ArgumentParser(
918
- description="Validate and generate .prizmkit/plans/feature-list.json files for the dev-pipeline system.",
919
- formatter_class=argparse.RawDescriptionHelpFormatter,
920
- epilog=(
921
- "Examples:\n"
922
- " %(prog)s validate --input .prizmkit/plans/feature-list.json\n"
923
- " %(prog)s validate --input .prizmkit/plans/feature-list.json --output validated.json\n"
924
- " %(prog)s template --output .prizmkit/plans/feature-list.json\n"
925
- " %(prog)s generate --input draft.json --output .prizmkit/plans/feature-list.json\n"
926
- " %(prog)s summary --input .prizmkit/plans/feature-list.json\n"
927
- " %(prog)s summary --input .prizmkit/plans/feature-list.json --format json\n"
928
- ),
929
- )
930
-
931
- subparsers = parser.add_subparsers(dest="command", help="Command to execute")
932
-
933
- # -- validate --
934
- p_validate = subparsers.add_parser(
935
- "validate",
936
- help="Validate an existing .prizmkit/plans/feature-list.json",
937
- )
938
- p_validate.add_argument(
939
- "--input", required=True, help="Path to input .prizmkit/plans/feature-list.json"
940
- )
941
- p_validate.add_argument(
942
- "--output", help="Path to write validated output (optional)"
943
- )
944
-
945
- # -- template --
946
- p_template = subparsers.add_parser(
947
- "template",
948
- help="Generate a blank template .prizmkit/plans/feature-list.json",
949
- )
950
- p_template.add_argument(
951
- "--output", required=True, help="Path to write template file"
952
- )
953
-
954
- # -- generate --
955
- p_generate = subparsers.add_parser(
956
- "generate",
957
- help="Validate a draft and generate final feature-list.json with defaults filled in",
958
- )
959
- p_generate.add_argument(
960
- "--input", required=True, help="Path to draft JSON (or '-' for stdin)"
961
- )
962
- p_generate.add_argument(
963
- "--output", required=True, help="Path to write final feature-list.json"
964
- )
965
-
966
- # -- summary --
967
- p_summary = subparsers.add_parser(
968
- "summary",
969
- help="Print a summary table of features from a .prizmkit/plans/feature-list.json",
970
- )
971
- p_summary.add_argument(
972
- "--input", required=True, help="Path to input .prizmkit/plans/feature-list.json"
973
- )
974
- p_summary.add_argument(
975
- "--format",
976
- choices=["json", "markdown"],
977
- default="markdown",
978
- help="Output format (default: markdown)",
979
- )
980
-
981
- # -- grade --
982
- p_grade = subparsers.add_parser(
983
- "grade",
984
- help="Generate grading results from eval runs (for npm run skill:review)",
985
- )
986
- p_grade.add_argument(
987
- "--workspace",
988
- required=True,
989
- help="Path to eval workspace (e.g., /.codebuddy/skill-evals/feature-planner-workspace)",
990
- )
991
- p_grade.add_argument(
992
- "--iteration",
993
- required=True,
994
- help="Iteration ID (e.g., iteration-1)",
995
- )
996
-
997
- args = parser.parse_args()
998
-
999
- if not args.command:
1000
- parser.print_help(sys.stderr)
1001
- return 2
1002
-
1003
- dispatch = {
1004
- "validate": cmd_validate,
1005
- "template": cmd_template,
1006
- "generate": cmd_generate,
1007
- "summary": cmd_summary,
1008
- "grade": cmd_grade,
1009
- }
1010
-
1011
- handler = dispatch.get(args.command)
1012
- if handler is None:
1013
- _err("Unknown command: {}".format(args.command))
1014
- return 2
1015
-
1016
- return handler(args)
1017
-
1018
-
1019
- if __name__ == "__main__":
1020
- sys.exit(main())