create-quiver 0.9.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/BACKLOG.md +16 -17
  2. package/CHANGELOG.md +34 -0
  3. package/README.md +419 -330
  4. package/README_FOR_AI.md +93 -56
  5. package/ROADMAP.md +22 -11
  6. package/docs/AI_CONTEXT.md.template +2 -0
  7. package/docs/AI_ONBOARDING_PROMPT.md.template +36 -19
  8. package/docs/COMMANDS.md.template +73 -1
  9. package/docs/CONTEXTO.md.template +2 -0
  10. package/docs/DECISIONS.md.template +1 -0
  11. package/docs/GITFLOW_PR_GUIDE.md.template +11 -0
  12. package/docs/INDEX.md.template +20 -18
  13. package/docs/STANDARD.md.template +1 -1
  14. package/docs/STATUS.md.template +1 -0
  15. package/docs/SUPPORT_MATRIX.md.template +6 -2
  16. package/docs/TROUBLESHOOTING.md.template +79 -1
  17. package/docs/WORKFLOW.md.template +26 -18
  18. package/package.json +24 -2
  19. package/package.template.json +24 -7
  20. package/scripts/check-pr-readiness.sh +1 -1
  21. package/scripts/check-scope.sh +0 -1
  22. package/scripts/check-slice-readiness.sh +3 -4
  23. package/scripts/init-docs.sh +53 -6
  24. package/scripts/package-quiver.sh +18 -2
  25. package/specs/quiver-v20-ai-cli-orchestration/EVIDENCE_REPORT.md +23 -0
  26. package/specs/quiver-v20-ai-cli-orchestration/EXECUTION_PLAN.md +57 -0
  27. package/specs/quiver-v20-ai-cli-orchestration/SPEC.md +202 -0
  28. package/specs/quiver-v20-ai-cli-orchestration/STATUS.md +35 -0
  29. package/specs/quiver-v20-ai-cli-orchestration/pr.md +100 -0
  30. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
  31. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
  32. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-00-spec-foundation/slice.json +54 -0
  33. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-01-ai-provider-runner/CLOSURE_BRIEF.md +39 -0
  34. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-01-ai-provider-runner/EXECUTION_BRIEF.md +63 -0
  35. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-01-ai-provider-runner/slice.json +55 -0
  36. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-02-context-packs-token-budget/CLOSURE_BRIEF.md +40 -0
  37. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-02-context-packs-token-budget/EXECUTION_BRIEF.md +60 -0
  38. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-02-context-packs-token-budget/slice.json +54 -0
  39. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-03-ai-phase-gated-planner/CLOSURE_BRIEF.md +43 -0
  40. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-03-ai-phase-gated-planner/EXECUTION_BRIEF.md +62 -0
  41. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-03-ai-phase-gated-planner/slice.json +62 -0
  42. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-04-spec-slice-handoff-pr-generation/CLOSURE_BRIEF.md +36 -0
  43. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-04-spec-slice-handoff-pr-generation/EXECUTION_BRIEF.md +63 -0
  44. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-04-spec-slice-handoff-pr-generation/slice.json +59 -0
  45. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-05-execution-plan-parallel-worktrees/CLOSURE_BRIEF.md +32 -0
  46. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-05-execution-plan-parallel-worktrees/EXECUTION_BRIEF.md +61 -0
  47. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-05-execution-plan-parallel-worktrees/slice.json +59 -0
  48. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-06-ai-execute-slice-scope-enforcement/CLOSURE_BRIEF.md +36 -0
  49. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-06-ai-execute-slice-scope-enforcement/EXECUTION_BRIEF.md +64 -0
  50. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-06-ai-execute-slice-scope-enforcement/slice.json +65 -0
  51. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-07-github-pr-preflight/CLOSURE_BRIEF.md +36 -0
  52. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-07-github-pr-preflight/EXECUTION_BRIEF.md +66 -0
  53. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-07-github-pr-preflight/slice.json +63 -0
  54. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-08-docs-smokes-release-readiness/CLOSURE_BRIEF.md +35 -0
  55. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-08-docs-smokes-release-readiness/EXECUTION_BRIEF.md +64 -0
  56. package/specs/quiver-v20-ai-cli-orchestration/slices/slice-08-docs-smokes-release-readiness/slice.json +77 -0
  57. package/specs/quiver-v21-ai-first-layout/EVIDENCE_REPORT.md +31 -0
  58. package/specs/quiver-v21-ai-first-layout/EXECUTION_PLAN.md +185 -0
  59. package/specs/quiver-v21-ai-first-layout/SPEC.md +212 -0
  60. package/specs/quiver-v21-ai-first-layout/STATUS.md +37 -0
  61. package/specs/quiver-v21-ai-first-layout/pr.md +110 -0
  62. package/specs/quiver-v21-ai-first-layout/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
  63. package/specs/quiver-v21-ai-first-layout/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +63 -0
  64. package/specs/quiver-v21-ai-first-layout/slices/slice-00-spec-foundation/slice.json +45 -0
  65. package/specs/quiver-v21-ai-first-layout/slices/slice-01-init-profiles-dry-run/CLOSURE_BRIEF.md +31 -0
  66. package/specs/quiver-v21-ai-first-layout/slices/slice-01-init-profiles-dry-run/EXECUTION_BRIEF.md +59 -0
  67. package/specs/quiver-v21-ai-first-layout/slices/slice-01-init-profiles-dry-run/slice.json +57 -0
  68. package/specs/quiver-v21-ai-first-layout/slices/slice-02-internal-layout-template-resolver/CLOSURE_BRIEF.md +32 -0
  69. package/specs/quiver-v21-ai-first-layout/slices/slice-02-internal-layout-template-resolver/EXECUTION_BRIEF.md +60 -0
  70. package/specs/quiver-v21-ai-first-layout/slices/slice-02-internal-layout-template-resolver/slice.json +58 -0
  71. package/specs/quiver-v21-ai-first-layout/slices/slice-03-generation-profiles-visible-contract/CLOSURE_BRIEF.md +34 -0
  72. package/specs/quiver-v21-ai-first-layout/slices/slice-03-generation-profiles-visible-contract/EXECUTION_BRIEF.md +61 -0
  73. package/specs/quiver-v21-ai-first-layout/slices/slice-03-generation-profiles-visible-contract/slice.json +64 -0
  74. package/specs/quiver-v21-ai-first-layout/slices/slice-04-analyze-scan-relocation/CLOSURE_BRIEF.md +32 -0
  75. package/specs/quiver-v21-ai-first-layout/slices/slice-04-analyze-scan-relocation/EXECUTION_BRIEF.md +58 -0
  76. package/specs/quiver-v21-ai-first-layout/slices/slice-04-analyze-scan-relocation/slice.json +64 -0
  77. package/specs/quiver-v21-ai-first-layout/slices/slice-05-empty-specs-layout-doctor/CLOSURE_BRIEF.md +32 -0
  78. package/specs/quiver-v21-ai-first-layout/slices/slice-05-empty-specs-layout-doctor/EXECUTION_BRIEF.md +60 -0
  79. package/specs/quiver-v21-ai-first-layout/slices/slice-05-empty-specs-layout-doctor/slice.json +65 -0
  80. package/specs/quiver-v21-ai-first-layout/slices/slice-06-legacy-migration-optional-assets/CLOSURE_BRIEF.md +31 -0
  81. package/specs/quiver-v21-ai-first-layout/slices/slice-06-legacy-migration-optional-assets/EXECUTION_BRIEF.md +62 -0
  82. package/specs/quiver-v21-ai-first-layout/slices/slice-06-legacy-migration-optional-assets/slice.json +66 -0
  83. package/specs/quiver-v21-ai-first-layout/slices/slice-07-docs-guidance-alignment/CLOSURE_BRIEF.md +33 -0
  84. package/specs/quiver-v21-ai-first-layout/slices/slice-07-docs-guidance-alignment/EXECUTION_BRIEF.md +61 -0
  85. package/specs/quiver-v21-ai-first-layout/slices/slice-07-docs-guidance-alignment/slice.json +67 -0
  86. package/specs/quiver-v21-ai-first-layout/slices/slice-08-smokes-release-readiness/CLOSURE_BRIEF.md +35 -0
  87. package/specs/quiver-v21-ai-first-layout/slices/slice-08-smokes-release-readiness/EXECUTION_BRIEF.md +66 -0
  88. package/specs/quiver-v21-ai-first-layout/slices/slice-08-smokes-release-readiness/slice.json +62 -0
  89. package/specs/quiver-v22-guided-ai-workflow/EVIDENCE_REPORT.md +58 -0
  90. package/specs/quiver-v22-guided-ai-workflow/EXECUTION_PLAN.md +88 -0
  91. package/specs/quiver-v22-guided-ai-workflow/SPEC.md +228 -0
  92. package/specs/quiver-v22-guided-ai-workflow/STATUS.md +42 -0
  93. package/specs/quiver-v22-guided-ai-workflow/pr.md +104 -0
  94. package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +35 -0
  95. package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
  96. package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/slice.json +51 -0
  97. package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/CLOSURE_BRIEF.md +31 -0
  98. package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/EXECUTION_BRIEF.md +58 -0
  99. package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/slice.json +55 -0
  100. package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/CLOSURE_BRIEF.md +30 -0
  101. package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/EXECUTION_BRIEF.md +57 -0
  102. package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/slice.json +57 -0
  103. package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/CLOSURE_BRIEF.md +32 -0
  104. package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/EXECUTION_BRIEF.md +56 -0
  105. package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/slice.json +56 -0
  106. package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/CLOSURE_BRIEF.md +33 -0
  107. package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/EXECUTION_BRIEF.md +56 -0
  108. package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/slice.json +58 -0
  109. package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/CLOSURE_BRIEF.md +32 -0
  110. package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/EXECUTION_BRIEF.md +56 -0
  111. package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/slice.json +54 -0
  112. package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/CLOSURE_BRIEF.md +32 -0
  113. package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/EXECUTION_BRIEF.md +58 -0
  114. package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/slice.json +57 -0
  115. package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/CLOSURE_BRIEF.md +32 -0
  116. package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/EXECUTION_BRIEF.md +58 -0
  117. package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/slice.json +55 -0
  118. package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/CLOSURE_BRIEF.md +32 -0
  119. package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/EXECUTION_BRIEF.md +58 -0
  120. package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/slice.json +53 -0
  121. package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/CLOSURE_BRIEF.md +33 -0
  122. package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/EXECUTION_BRIEF.md +59 -0
  123. package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/slice.json +59 -0
  124. package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +34 -0
  125. package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +58 -0
  126. package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/slice.json +60 -0
  127. package/specs/quiver-v23-guided-flow-productization/EVIDENCE_REPORT.md +80 -0
  128. package/specs/quiver-v23-guided-flow-productization/EXECUTION_PLAN.md +80 -0
  129. package/specs/quiver-v23-guided-flow-productization/SPEC.md +203 -0
  130. package/specs/quiver-v23-guided-flow-productization/STATUS.md +39 -0
  131. package/specs/quiver-v23-guided-flow-productization/pr.md +119 -0
  132. package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
  133. package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
  134. package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/slice.json +51 -0
  135. package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/CLOSURE_BRIEF.md +33 -0
  136. package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/EXECUTION_BRIEF.md +35 -0
  137. package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/slice.json +56 -0
  138. package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/CLOSURE_BRIEF.md +31 -0
  139. package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/EXECUTION_BRIEF.md +29 -0
  140. package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/slice.json +55 -0
  141. package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/CLOSURE_BRIEF.md +33 -0
  142. package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/EXECUTION_BRIEF.md +29 -0
  143. package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/slice.json +54 -0
  144. package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/CLOSURE_BRIEF.md +32 -0
  145. package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/EXECUTION_BRIEF.md +30 -0
  146. package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/slice.json +59 -0
  147. package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/CLOSURE_BRIEF.md +31 -0
  148. package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/EXECUTION_BRIEF.md +29 -0
  149. package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/slice.json +53 -0
  150. package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/CLOSURE_BRIEF.md +33 -0
  151. package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/EXECUTION_BRIEF.md +30 -0
  152. package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/slice.json +54 -0
  153. package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/CLOSURE_BRIEF.md +33 -0
  154. package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/EXECUTION_BRIEF.md +30 -0
  155. package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/slice.json +55 -0
  156. package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/CLOSURE_BRIEF.md +32 -0
  157. package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/EXECUTION_BRIEF.md +30 -0
  158. package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/slice.json +55 -0
  159. package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/CLOSURE_BRIEF.md +33 -0
  160. package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/EXECUTION_BRIEF.md +34 -0
  161. package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/slice.json +57 -0
  162. package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +33 -0
  163. package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +32 -0
  164. package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/slice.json +63 -0
  165. package/specs/quiver-v24-dx-onboarding-hardening/EVIDENCE_REPORT.md +55 -0
  166. package/specs/quiver-v24-dx-onboarding-hardening/EXECUTION_PLAN.md +43 -0
  167. package/specs/quiver-v24-dx-onboarding-hardening/SPEC.md +149 -0
  168. package/specs/quiver-v24-dx-onboarding-hardening/STATUS.md +31 -0
  169. package/specs/quiver-v24-dx-onboarding-hardening/pr.md +76 -0
  170. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +31 -0
  171. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
  172. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/slice.json +51 -0
  173. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/CLOSURE_BRIEF.md +38 -0
  174. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/EXECUTION_BRIEF.md +53 -0
  175. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/slice.json +55 -0
  176. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/CLOSURE_BRIEF.md +33 -0
  177. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/EXECUTION_BRIEF.md +50 -0
  178. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/slice.json +52 -0
  179. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/CLOSURE_BRIEF.md +33 -0
  180. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/EXECUTION_BRIEF.md +50 -0
  181. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/slice.json +53 -0
  182. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/CLOSURE_BRIEF.md +33 -0
  183. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/EXECUTION_BRIEF.md +50 -0
  184. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/slice.json +70 -0
  185. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/CLOSURE_BRIEF.md +36 -0
  186. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/EXECUTION_BRIEF.md +49 -0
  187. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/slice.json +52 -0
  188. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/CLOSURE_BRIEF.md +43 -0
  189. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/EXECUTION_BRIEF.md +53 -0
  190. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/slice.json +60 -0
  191. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/CLOSURE_BRIEF.md +32 -0
  192. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/EXECUTION_BRIEF.md +50 -0
  193. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/slice.json +51 -0
  194. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/CLOSURE_BRIEF.md +34 -0
  195. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/EXECUTION_BRIEF.md +52 -0
  196. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/slice.json +54 -0
  197. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/CLOSURE_BRIEF.md +34 -0
  198. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/EXECUTION_BRIEF.md +51 -0
  199. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/slice.json +59 -0
  200. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +33 -0
  201. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +54 -0
  202. package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/slice.json +76 -0
  203. package/src/create-quiver/commands/ai.js +915 -0
  204. package/src/create-quiver/commands/demo.js +22 -0
  205. package/src/create-quiver/commands/evidence.js +37 -0
  206. package/src/create-quiver/commands/flow.js +561 -0
  207. package/src/create-quiver/commands/graph.js +14 -1
  208. package/src/create-quiver/commands/next.js +28 -0
  209. package/src/create-quiver/commands/plan.js +6 -3
  210. package/src/create-quiver/commands/prepare.js +236 -0
  211. package/src/create-quiver/commands/spec.js +133 -0
  212. package/src/create-quiver/index.js +1096 -96
  213. package/src/create-quiver/lib/agent-profiles.js +148 -0
  214. package/src/create-quiver/lib/ai/context-packs.js +170 -0
  215. package/src/create-quiver/lib/ai/execution-plan.js +614 -0
  216. package/src/create-quiver/lib/ai/executor.js +682 -0
  217. package/src/create-quiver/lib/ai/github.js +525 -0
  218. package/src/create-quiver/lib/ai/onboarding-template.js +365 -0
  219. package/src/create-quiver/lib/ai/phase-gates.js +72 -0
  220. package/src/create-quiver/lib/ai/plan-review.js +283 -0
  221. package/src/create-quiver/lib/ai/preflight.js +58 -0
  222. package/src/create-quiver/lib/ai/prompt-transport.js +81 -0
  223. package/src/create-quiver/lib/ai/prompts.js +39 -0
  224. package/src/create-quiver/lib/ai/providers.js +315 -0
  225. package/src/create-quiver/lib/ai/safety.js +156 -0
  226. package/src/create-quiver/lib/ai/spec-generator.js +314 -0
  227. package/src/create-quiver/lib/ai/spec-templates.js +715 -0
  228. package/src/create-quiver/lib/approvals.js +350 -0
  229. package/src/create-quiver/lib/demo.js +657 -0
  230. package/src/create-quiver/lib/doctor.js +348 -0
  231. package/src/create-quiver/lib/evidence.js +115 -0
  232. package/src/create-quiver/lib/git.js +21 -0
  233. package/src/create-quiver/lib/init-docs.js +545 -23
  234. package/src/create-quiver/lib/init-layout.js +451 -0
  235. package/src/create-quiver/lib/lifecycle.js +8 -2
  236. package/src/create-quiver/lib/package-safety.js +117 -0
  237. package/src/create-quiver/lib/paths.js +63 -2
  238. package/src/create-quiver/lib/project-scan.js +66 -0
  239. package/src/create-quiver/lib/readiness.js +87 -18
  240. package/src/create-quiver/lib/scope.js +125 -0
  241. package/src/create-quiver/lib/slice-graph.js +7 -0
  242. package/src/create-quiver/lib/slice.js +59 -16
  243. package/src/create-quiver/lib/spec-worktrees.js +349 -0
  244. package/src/create-quiver/lib/state.js +18 -1
  245. package/src/create-quiver/lib/template-resolver.js +74 -0
@@ -1,6 +1,42 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const { readAllSlices } = require('./slice-graph');
4
+ const { hasGeneratedProjectSpec, hasInitializedStateMetadata, readState } = require('./state');
3
5
  const { worktreeList } = require('./git');
6
+ const {
7
+ buildQuiverConfig,
8
+ buildQuiverInternalGitignore,
9
+ resolveInitPackageScripts,
10
+ } = require('./init-layout');
11
+
12
+ const NEW_LAYOUT_REQUIRED_PATHS = [
13
+ 'README.md',
14
+ 'AGENTS.md',
15
+ 'package.json',
16
+ 'docs/AI_CONTEXT.md',
17
+ 'docs/AI_ONBOARDING_PROMPT.md',
18
+ 'docs/COMMANDS.md',
19
+ 'docs/WORKFLOW.md',
20
+ '.quiver/state.json',
21
+ '.quiver/config.json',
22
+ '.quiver/.gitignore',
23
+ ];
24
+
25
+ const LEGACY_LAYOUT_PROBES = [
26
+ 'docs-template/',
27
+ 'tools/scripts/start-slice.sh',
28
+ 'tools/scripts/check-slice-readiness.sh',
29
+ 'tools/scripts/check-pr-readiness.sh',
30
+ '.github/pull_request_template.md',
31
+ 'docs/PROJECT_SCAN.json',
32
+ ];
33
+
34
+ const ROOT_GITIGNORE_DEFAULTS = [
35
+ 'node_modules/',
36
+ '.DS_Store',
37
+ 'dist/',
38
+ 'coverage/',
39
+ ];
4
40
 
5
41
  function readTextIfExists(filePath) {
6
42
  if (!fs.existsSync(filePath)) {
@@ -10,6 +46,34 @@ function readTextIfExists(filePath) {
10
46
  return fs.readFileSync(filePath, 'utf8');
11
47
  }
12
48
 
49
+ function normalizeIgnorePattern(line) {
50
+ const trimmed = line.trim();
51
+ if (!trimmed || trimmed.startsWith('#')) {
52
+ return trimmed;
53
+ }
54
+
55
+ return trimmed.replace(/\/+$/g, '');
56
+ }
57
+
58
+ function missingLineDefaults(existingText, defaults) {
59
+ const seen = new Set(
60
+ String(existingText || '')
61
+ .split(/\r?\n/)
62
+ .map(normalizeIgnorePattern)
63
+ .filter(Boolean),
64
+ );
65
+
66
+ return defaults.filter((line) => !seen.has(normalizeIgnorePattern(line)));
67
+ }
68
+
69
+ function appendMissingLines(filePath, lines) {
70
+ const existingText = readTextIfExists(filePath) || '';
71
+ const trimmed = existingText.replace(/\s+$/g, '');
72
+ const prefix = trimmed.length > 0 ? `${trimmed}\n` : '';
73
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
74
+ fs.writeFileSync(filePath, `${prefix}${lines.join('\n')}\n`);
75
+ }
76
+
13
77
  function countNonEmptyLines(text) {
14
78
  return String(text || '')
15
79
  .split(/\r?\n/)
@@ -30,6 +94,18 @@ function normalizeRelativePath(root, absolutePath) {
30
94
  return path.relative(root, absolutePath).split(path.sep).join('/');
31
95
  }
32
96
 
97
+ function hasPath(projectRoot, relativePath) {
98
+ return fs.existsSync(path.join(projectRoot, relativePath));
99
+ }
100
+
101
+ function collectPresentPaths(projectRoot, relativePaths) {
102
+ return relativePaths.filter((relativePath) => hasPath(projectRoot, relativePath));
103
+ }
104
+
105
+ function collectMissingPaths(projectRoot, relativePaths) {
106
+ return relativePaths.filter((relativePath) => !hasPath(projectRoot, relativePath));
107
+ }
108
+
33
109
  function collectAiMarkdownFiles(projectRoot) {
34
110
  const aiDir = path.join(projectRoot, 'docs', 'ai');
35
111
  if (!fs.existsSync(aiDir)) {
@@ -174,6 +250,268 @@ function countStackInfoLeaks(projectRoot) {
174
250
  return leaks;
175
251
  }
176
252
 
253
+ function collectGeneratedMarkdownFiles(projectRoot) {
254
+ const files = [];
255
+ const rootFiles = ['README.md', 'AGENTS.md'];
256
+
257
+ for (const file of rootFiles) {
258
+ const absolutePath = path.join(projectRoot, file);
259
+ if (fs.existsSync(absolutePath)) {
260
+ files.push(absolutePath);
261
+ }
262
+ }
263
+
264
+ const docsDir = path.join(projectRoot, 'docs');
265
+ if (!fs.existsSync(docsDir)) {
266
+ return files;
267
+ }
268
+
269
+ const walk = (dirPath) => {
270
+ for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
271
+ const fullPath = path.join(dirPath, entry.name);
272
+ if (entry.isDirectory()) {
273
+ walk(fullPath);
274
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
275
+ files.push(fullPath);
276
+ }
277
+ }
278
+ };
279
+
280
+ walk(docsDir);
281
+
282
+ return files;
283
+ }
284
+
285
+ function isExternalLink(target) {
286
+ return /^(?:[a-z][a-z0-9+.-]*:|#)/i.test(target);
287
+ }
288
+
289
+ function normalizeMarkdownLinkTarget(target) {
290
+ return target
291
+ .trim()
292
+ .replace(/^<|>$/g, '')
293
+ .split('#')[0]
294
+ .trim();
295
+ }
296
+
297
+ function collectMissingMarkdownLinks(projectRoot) {
298
+ const missing = [];
299
+ const linkPattern = /!?\[[^\]]*]\(([^)]+)\)/g;
300
+
301
+ for (const filePath of collectGeneratedMarkdownFiles(projectRoot)) {
302
+ const text = readTextIfExists(filePath);
303
+ if (!text) {
304
+ continue;
305
+ }
306
+
307
+ let match;
308
+ while ((match = linkPattern.exec(text)) !== null) {
309
+ const target = normalizeMarkdownLinkTarget(match[1]);
310
+ if (!target || isExternalLink(target)) {
311
+ continue;
312
+ }
313
+
314
+ const resolved = path.resolve(path.dirname(filePath), target);
315
+ const relativeToRoot = path.relative(projectRoot, resolved);
316
+ if (relativeToRoot.startsWith('..') || path.isAbsolute(relativeToRoot)) {
317
+ continue;
318
+ }
319
+
320
+ if (!fs.existsSync(resolved)) {
321
+ missing.push(`${normalizeRelativePath(projectRoot, filePath)} -> ${target}`);
322
+ }
323
+ }
324
+ }
325
+
326
+ return missing;
327
+ }
328
+
329
+ function collectLayoutReport(projectRoot) {
330
+ const hasStateMetadata = hasInitializedStateMetadata(readState(projectRoot));
331
+ const realSlices = readAllSlices(projectRoot);
332
+ const specSlugs = Array.from(new Set(realSlices.map((slice) => slice.specSlug))).sort((left, right) => left.localeCompare(right));
333
+ const newLayoutFiles = collectPresentPaths(projectRoot, NEW_LAYOUT_REQUIRED_PATHS);
334
+ const missingNewLayoutFiles = collectMissingPaths(projectRoot, NEW_LAYOUT_REQUIRED_PATHS);
335
+ const legacySignals = collectPresentPaths(projectRoot, LEGACY_LAYOUT_PROBES);
336
+ const hasLegacyProjectSpec = hasGeneratedProjectSpec(projectRoot);
337
+
338
+ if (hasLegacyProjectSpec) {
339
+ legacySignals.push('specs/<project-slug>/SPEC.md');
340
+ }
341
+
342
+ const hasNewLayout = missingNewLayoutFiles.length === 0;
343
+ const hasLegacyLayout = legacySignals.length > 0;
344
+
345
+ let layout = 'incomplete';
346
+ if (hasNewLayout && hasLegacyLayout) {
347
+ layout = 'hybrid';
348
+ } else if (hasNewLayout) {
349
+ layout = 'new';
350
+ } else if (hasLegacyLayout) {
351
+ layout = 'legacy';
352
+ }
353
+
354
+ const recommendations = [];
355
+
356
+ if (layout === 'new') {
357
+ if (specSlugs.length === 0) {
358
+ recommendations.push('No specs yet. That is valid after the AI-first init flow.');
359
+ } else {
360
+ recommendations.push(`Specs found: ${specSlugs.join(', ')}.`);
361
+ }
362
+
363
+ if (!hasPath(projectRoot, 'docs/PROJECT_MAP.md')) {
364
+ recommendations.push('Run `npx create-quiver analyze` to generate docs/PROJECT_MAP.md when you want the visible project map.');
365
+ }
366
+ } else if (layout === 'legacy') {
367
+ recommendations.push('Legacy layout detected. Run `npx create-quiver migrate` to add the modern .quiver/ contract and AI-first docs.');
368
+ } else if (layout === 'hybrid') {
369
+ recommendations.push('Hybrid layout detected. Keep the new .quiver/ contract as the source of truth and plan cleanup of legacy roots.');
370
+ recommendations.push('Review any remaining docs-template/, tools/scripts/, or docs/PROJECT_SCAN.json paths and migrate them only if they are still needed.');
371
+ } else {
372
+ recommendations.push('Incomplete layout detected. Restore the missing AI-first contract files before relying on this project for onboarding.');
373
+ if (missingNewLayoutFiles.length > 0) {
374
+ recommendations.push(`Missing files: ${missingNewLayoutFiles.join(', ')}.`);
375
+ }
376
+ if (!hasStateMetadata && !hasLegacyLayout) {
377
+ recommendations.push('Run `npx create-quiver --name "Project Name"` or `npx create-quiver init` to create the Quiver contract first.');
378
+ }
379
+ }
380
+
381
+ return {
382
+ hasLegacyLayout,
383
+ hasNewLayout,
384
+ hasStateMetadata,
385
+ layout,
386
+ legacySignals,
387
+ missingNewLayoutFiles,
388
+ newLayoutFiles,
389
+ recommendations,
390
+ realSlices,
391
+ specSlugs,
392
+ };
393
+ }
394
+
395
+ function buildDoctorFixPlan(projectRoot) {
396
+ const fixes = [];
397
+ const rootGitignorePath = path.join(projectRoot, '.gitignore');
398
+ const rootGitignoreText = readTextIfExists(rootGitignorePath) || '';
399
+ const missingRootGitignoreLines = missingLineDefaults(rootGitignoreText, ROOT_GITIGNORE_DEFAULTS);
400
+ if (!fs.existsSync(rootGitignorePath)) {
401
+ fixes.push({
402
+ type: 'append-lines',
403
+ path: '.gitignore',
404
+ description: 'Create root .gitignore with safe Quiver defaults.',
405
+ lines: ROOT_GITIGNORE_DEFAULTS,
406
+ });
407
+ } else if (missingRootGitignoreLines.length > 0) {
408
+ fixes.push({
409
+ type: 'append-lines',
410
+ path: '.gitignore',
411
+ description: `Merge missing root .gitignore defaults: ${missingRootGitignoreLines.join(', ')}.`,
412
+ lines: missingRootGitignoreLines,
413
+ });
414
+ }
415
+
416
+ const quiverGitignorePath = path.join(projectRoot, '.quiver', '.gitignore');
417
+ const quiverGitignoreText = readTextIfExists(quiverGitignorePath) || '';
418
+ const quiverDefaults = buildQuiverInternalGitignore().split(/\r?\n/).filter(Boolean);
419
+ const missingQuiverLines = missingLineDefaults(quiverGitignoreText, quiverDefaults);
420
+ if (!fs.existsSync(quiverGitignorePath)) {
421
+ fixes.push({
422
+ type: 'write-json-or-text',
423
+ path: '.quiver/.gitignore',
424
+ description: 'Create internal .quiver/.gitignore for local AI state.',
425
+ content: buildQuiverInternalGitignore(),
426
+ });
427
+ } else if (missingQuiverLines.length > 0) {
428
+ fixes.push({
429
+ type: 'append-lines',
430
+ path: '.quiver/.gitignore',
431
+ description: `Merge missing .quiver/.gitignore defaults: ${missingQuiverLines.join(', ')}.`,
432
+ lines: missingQuiverLines,
433
+ });
434
+ }
435
+
436
+ const configPath = path.join(projectRoot, '.quiver', 'config.json');
437
+ if (!fs.existsSync(configPath)) {
438
+ fixes.push({
439
+ type: 'write-json-or-text',
440
+ path: '.quiver/config.json',
441
+ description: 'Create missing Quiver config metadata.',
442
+ content: `${JSON.stringify(buildQuiverConfig(), null, 2)}\n`,
443
+ });
444
+ }
445
+
446
+ const packageJsonPath = path.join(projectRoot, 'package.json');
447
+ if (fs.existsSync(packageJsonPath)) {
448
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
449
+ const scripts = packageJson.scripts && typeof packageJson.scripts === 'object' ? packageJson.scripts : {};
450
+ const expectedScripts = resolveInitPackageScripts('default');
451
+ const missingScripts = Object.entries(expectedScripts)
452
+ .filter(([name]) => (name.startsWith('quiver:') || name === 'check-handoff') && typeof scripts[name] !== 'string');
453
+
454
+ if (missingScripts.length > 0) {
455
+ fixes.push({
456
+ type: 'merge-package-scripts',
457
+ path: 'package.json',
458
+ description: `Add missing package scripts: ${missingScripts.map(([name]) => name).join(', ')}.`,
459
+ scripts: Object.fromEntries(missingScripts),
460
+ });
461
+ }
462
+ }
463
+
464
+ return fixes;
465
+ }
466
+
467
+ function applyDoctorFixPlan(projectRoot, fixes) {
468
+ for (const fix of fixes) {
469
+ const targetPath = path.join(projectRoot, fix.path);
470
+ if (fix.type === 'append-lines') {
471
+ appendMissingLines(targetPath, fix.lines);
472
+ continue;
473
+ }
474
+
475
+ if (fix.type === 'write-json-or-text') {
476
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
477
+ fs.writeFileSync(targetPath, fix.content);
478
+ continue;
479
+ }
480
+
481
+ if (fix.type === 'merge-package-scripts') {
482
+ const packageJson = JSON.parse(fs.readFileSync(targetPath, 'utf8'));
483
+ packageJson.scripts = {
484
+ ...(packageJson.scripts || {}),
485
+ ...fix.scripts,
486
+ };
487
+ fs.writeFileSync(targetPath, `${JSON.stringify(packageJson, null, 2)}\n`);
488
+ }
489
+ }
490
+ }
491
+
492
+ function formatDoctorFixPlan(fixes, { dryRun = false } = {}) {
493
+ const lines = [dryRun ? 'Quiver doctor fix dry-run' : 'Quiver doctor fix'];
494
+ if (fixes.length === 0) {
495
+ lines.push('- No safe fixes to apply.');
496
+ } else {
497
+ for (const fix of fixes) {
498
+ lines.push(`- ${dryRun ? 'Would update' : 'Updated'} ${fix.path}: ${fix.description}`);
499
+ }
500
+ }
501
+ lines.push('');
502
+ return lines.join('\n');
503
+ }
504
+
505
+ function collectDoctorReport(projectRoot) {
506
+ const layout = collectLayoutReport(projectRoot);
507
+ const warnings = collectDoctorWarnings(projectRoot);
508
+
509
+ return {
510
+ ...layout,
511
+ warnings,
512
+ };
513
+ }
514
+
177
515
  function collectDoctorWarnings(projectRoot) {
178
516
  const warnings = [];
179
517
 
@@ -204,9 +542,19 @@ function collectDoctorWarnings(projectRoot) {
204
542
  warnings.push(`stack information appears outside docs/PROJECT_MAP.md: ${leakIssues.join(', ')}`);
205
543
  }
206
544
 
545
+ const missingLinks = collectMissingMarkdownLinks(projectRoot);
546
+ for (const issue of missingLinks) {
547
+ warnings.push(`missing local docs link: ${issue}`);
548
+ }
549
+
207
550
  return warnings;
208
551
  }
209
552
 
210
553
  module.exports = {
554
+ applyDoctorFixPlan,
555
+ buildDoctorFixPlan,
556
+ collectDoctorReport,
211
557
  collectDoctorWarnings,
558
+ collectLayoutReport,
559
+ formatDoctorFixPlan,
212
560
  };
@@ -0,0 +1,115 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { spawnSync } = require('node:child_process');
4
+
5
+ const DEFAULT_OUTPUT_LIMIT = 4000;
6
+
7
+ function redactSecrets(text) {
8
+ return String(text || '')
9
+ .replace(/(authorization:\s*bearer\s+)[^\s`'"]+/gi, '$1[REDACTED]')
10
+ .replace(/\b((?:api[_-]?key|token|secret|password|passwd|pwd)[A-Z0-9_-]*\s*[:=]\s*)[^\s`'"]+/gi, '$1[REDACTED]')
11
+ .replace(/\b(npm_[A-Za-z0-9]{20,})\b/g, '[REDACTED_NPM_TOKEN]');
12
+ }
13
+
14
+ function truncateText(text, maxLength = DEFAULT_OUTPUT_LIMIT) {
15
+ const value = String(text || '');
16
+ if (value.length <= maxLength) {
17
+ return {
18
+ text: value,
19
+ truncated: false,
20
+ };
21
+ }
22
+
23
+ return {
24
+ text: `${value.slice(0, maxLength)}\n[... truncated ${value.length - maxLength} chars ...]`,
25
+ truncated: true,
26
+ };
27
+ }
28
+
29
+ function quoteCommandPart(value) {
30
+ const part = String(value || '');
31
+ return /\s/.test(part) ? JSON.stringify(part) : part;
32
+ }
33
+
34
+ function formatCommand(commandArgs) {
35
+ return commandArgs.map(quoteCommandPart).join(' ');
36
+ }
37
+
38
+ function defaultEvidencePath(repoRoot, startedAt = new Date()) {
39
+ const stamp = startedAt.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z');
40
+ return path.join(repoRoot, '.quiver', 'evidence', `evidence-${stamp}.md`);
41
+ }
42
+
43
+ function renderEvidenceMarkdown(record) {
44
+ return `# Quiver Evidence
45
+
46
+ - Command: \`${record.command}\`
47
+ - Exit code: ${record.exit_code}
48
+ - Duration ms: ${record.duration_ms}
49
+ - Started at: ${record.started_at}
50
+ - Finished at: ${record.finished_at}
51
+ - Output truncated: ${record.output_truncated ? 'yes' : 'no'}
52
+
53
+ ## Stdout
54
+
55
+ \`\`\`\`text
56
+ ${record.stdout || ''}
57
+ \`\`\`\`
58
+
59
+ ## Stderr
60
+
61
+ \`\`\`\`text
62
+ ${record.stderr || ''}
63
+ \`\`\`\`
64
+ `;
65
+ }
66
+
67
+ function runEvidenceCommand(repoRoot, commandArgs, options = {}) {
68
+ if (!Array.isArray(commandArgs) || commandArgs.length === 0) {
69
+ throw new Error('create-quiver: evidence run requires a command after --');
70
+ }
71
+
72
+ const startedAtDate = new Date();
73
+ const started = Date.now();
74
+ const result = (options.spawnSync || spawnSync)(commandArgs[0], commandArgs.slice(1), {
75
+ cwd: repoRoot,
76
+ encoding: 'utf8',
77
+ shell: false,
78
+ });
79
+ const finishedAtDate = new Date();
80
+ const duration = Date.now() - started;
81
+ const exitCode = typeof result.status === 'number' ? result.status : 1;
82
+ const stdout = truncateText(redactSecrets(result.stdout || ''), options.maxOutput || DEFAULT_OUTPUT_LIMIT);
83
+ const stderr = truncateText(redactSecrets(result.stderr || result.error?.message || ''), options.maxOutput || DEFAULT_OUTPUT_LIMIT);
84
+ const record = {
85
+ command: redactSecrets(formatCommand(commandArgs)),
86
+ duration_ms: duration,
87
+ exit_code: exitCode,
88
+ finished_at: finishedAtDate.toISOString(),
89
+ output_truncated: stdout.truncated || stderr.truncated,
90
+ stderr: stderr.text,
91
+ stdout: stdout.text,
92
+ started_at: startedAtDate.toISOString(),
93
+ };
94
+ const outputPath = options.outputPath
95
+ ? path.resolve(repoRoot, options.outputPath)
96
+ : defaultEvidencePath(repoRoot, startedAtDate);
97
+
98
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
99
+ fs.writeFileSync(outputPath, renderEvidenceMarkdown(record));
100
+
101
+ return {
102
+ exitCode,
103
+ outputPath,
104
+ record,
105
+ };
106
+ }
107
+
108
+ module.exports = {
109
+ DEFAULT_OUTPUT_LIMIT,
110
+ defaultEvidencePath,
111
+ redactSecrets,
112
+ renderEvidenceMarkdown,
113
+ runEvidenceCommand,
114
+ truncateText,
115
+ };
@@ -101,6 +101,23 @@ function statusPorcelain(repoRoot) {
101
101
  return tryGit(['status', '--porcelain'], repoRoot);
102
102
  }
103
103
 
104
+ function remoteList(repoRoot) {
105
+ const output = tryGit(['remote'], repoRoot);
106
+ return output ? output.split('\n').map((line) => line.trim()).filter(Boolean) : [];
107
+ }
108
+
109
+ function hasRemote(repoRoot, remoteName = 'origin') {
110
+ return remoteList(repoRoot).includes(remoteName);
111
+ }
112
+
113
+ function isCleanWorktree(repoRoot) {
114
+ return statusPorcelain(repoRoot) === '';
115
+ }
116
+
117
+ function isDetachedHead(repoRoot) {
118
+ return currentBranch(repoRoot) === '';
119
+ }
120
+
104
121
  function revListCount(repoRoot, range) {
105
122
  const output = tryGit(['rev-list', '--count', range], repoRoot);
106
123
  return Number(output || '0');
@@ -143,7 +160,11 @@ module.exports = {
143
160
  hasRemoteBranch,
144
161
  lsRemoteHeads,
145
162
  mergeBaseIsAncestor,
163
+ hasRemote,
164
+ isCleanWorktree,
165
+ isDetachedHead,
146
166
  revListCount,
167
+ remoteList,
147
168
  runGit,
148
169
  statusPorcelain,
149
170
  tryGit,