create-quiver 0.15.4 → 0.17.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 (199) hide show
  1. package/.github/pull_request_template.md +17 -0
  2. package/.github/workflows/ci.yml +76 -0
  3. package/.markdown-link-check.json +15 -0
  4. package/.markdownlint.json +11 -0
  5. package/ARCHITECTURE.md +133 -0
  6. package/CHANGELOG.md +16 -0
  7. package/CONTRIBUTING.md +174 -9
  8. package/README.md +17 -2
  9. package/README_FOR_AI.md +4 -1
  10. package/ROADMAP.md +4 -0
  11. package/SECURITY.md +9 -2
  12. package/docs/AI_CONTEXT.md.es.template +3 -1
  13. package/docs/AI_CONTEXT.md.template +3 -1
  14. package/docs/CLI_UX_GUIDE.md +20 -1
  15. package/docs/COMMANDS.md.template +20 -6
  16. package/docs/GITFLOW_PR_GUIDE.md +73 -11
  17. package/docs/GITFLOW_PR_GUIDE.md.es.template +61 -2
  18. package/docs/GITFLOW_PR_GUIDE.md.template +72 -10
  19. package/docs/INDEX.md +6 -1
  20. package/docs/QUICK.md.template +2 -1
  21. package/docs/STANDARD.md.template +2 -1
  22. package/docs/WORKFLOW.md.es.template +3 -1
  23. package/docs/WORKFLOW.md.template +6 -4
  24. package/docs/getting-started/installation.md +7 -0
  25. package/docs/reference/commands.md +138 -6
  26. package/docs/reference/slice-schema.md +37 -0
  27. package/docs/schema/slice.schema.json +221 -0
  28. package/docs/specs/01-spec/01-slice/01-slice.json +36 -0
  29. package/docs/specs/01-spec/01-slice/01-slice.md +106 -0
  30. package/docs/specs/01-spec/01-slice/CLOSURE_BRIEF.md +80 -0
  31. package/docs/specs/01-spec/01-slice/EXECUTION_BRIEF.md +39 -0
  32. package/docs/specs/01-spec/02-slice/02-slice.json +36 -0
  33. package/docs/specs/01-spec/02-slice/02-slice.md +108 -0
  34. package/docs/specs/01-spec/02-slice/CLOSURE_BRIEF.md +88 -0
  35. package/docs/specs/01-spec/02-slice/EXECUTION_BRIEF.md +40 -0
  36. package/docs/specs/01-spec/03-slice/03-slice.json +36 -0
  37. package/docs/specs/01-spec/03-slice/03-slice.md +103 -0
  38. package/docs/specs/01-spec/03-slice/CLOSURE_BRIEF.md +54 -0
  39. package/docs/specs/01-spec/03-slice/EXECUTION_BRIEF.md +39 -0
  40. package/docs/specs/01-spec/04-slice/04-slice.json +36 -0
  41. package/docs/specs/01-spec/04-slice/04-slice.md +107 -0
  42. package/docs/specs/01-spec/04-slice/CLOSURE_BRIEF.md +46 -0
  43. package/docs/specs/01-spec/04-slice/EXECUTION_BRIEF.md +39 -0
  44. package/docs/specs/01-spec/05-slice/05-slice.json +36 -0
  45. package/docs/specs/01-spec/05-slice/05-slice.md +106 -0
  46. package/docs/specs/01-spec/05-slice/CLOSURE_BRIEF.md +84 -0
  47. package/docs/specs/01-spec/05-slice/EXECUTION_BRIEF.md +109 -0
  48. package/docs/specs/01-spec/06-slice/06-slice.json +36 -0
  49. package/docs/specs/01-spec/06-slice/06-slice.md +107 -0
  50. package/docs/specs/01-spec/06-slice/CLOSURE_BRIEF.md +67 -0
  51. package/docs/specs/01-spec/06-slice/EXECUTION_BRIEF.md +39 -0
  52. package/docs/specs/01-spec/07-slice/07-slice.json +36 -0
  53. package/docs/specs/01-spec/07-slice/07-slice.md +105 -0
  54. package/docs/specs/01-spec/07-slice/CLOSURE_BRIEF.md +71 -0
  55. package/docs/specs/01-spec/07-slice/EXECUTION_BRIEF.md +39 -0
  56. package/docs/specs/01-spec/08-slice/08-slice.json +36 -0
  57. package/docs/specs/01-spec/08-slice/08-slice.md +105 -0
  58. package/docs/specs/01-spec/08-slice/CLOSURE_BRIEF.md +114 -0
  59. package/docs/specs/01-spec/08-slice/EXECUTION_BRIEF.md +39 -0
  60. package/docs/specs/01-spec/CLOSURE_BRIEF.md +87 -0
  61. package/docs/specs/01-spec/spec.md +197 -0
  62. package/docs/workflows/full-ai-spec-to-pr.md +46 -3
  63. package/i18n/es/docs/GITFLOW_PR_GUIDE.md.template +38 -1
  64. package/package.json +27 -1
  65. package/scripts/package-quiver.sh +82 -2
  66. package/scripts/release-quiver.sh +14 -3
  67. package/specs/[project-name]/slices/pr.md.template +19 -0
  68. package/specs/quiver-v43-cli-i18n-audit-release-readiness/command-language-mode-matrix.json +122 -1
  69. package/specs/quiver-v46-deep-project-analysis/EVIDENCE_REPORT.md +94 -0
  70. package/specs/quiver-v46-deep-project-analysis/EXECUTION_PLAN.md +26 -0
  71. package/specs/quiver-v46-deep-project-analysis/SPEC.md +157 -0
  72. package/specs/quiver-v46-deep-project-analysis/STATUS.md +26 -0
  73. package/specs/quiver-v46-deep-project-analysis/pr.md +131 -0
  74. package/specs/quiver-v46-deep-project-analysis/slices/slice-00-analysis-contract-foundation/CLOSURE_BRIEF.md +14 -0
  75. package/specs/quiver-v46-deep-project-analysis/slices/slice-00-analysis-contract-foundation/EXECUTION_BRIEF.md +41 -0
  76. package/specs/quiver-v46-deep-project-analysis/slices/slice-00-analysis-contract-foundation/slice.json +61 -0
  77. package/specs/quiver-v46-deep-project-analysis/slices/slice-01-stack-agnostic-discovery-sampling/CLOSURE_BRIEF.md +22 -0
  78. package/specs/quiver-v46-deep-project-analysis/slices/slice-01-stack-agnostic-discovery-sampling/EXECUTION_BRIEF.md +33 -0
  79. package/specs/quiver-v46-deep-project-analysis/slices/slice-01-stack-agnostic-discovery-sampling/slice.json +80 -0
  80. package/specs/quiver-v46-deep-project-analysis/slices/slice-02-provider-analysis-json-contract/CLOSURE_BRIEF.md +21 -0
  81. package/specs/quiver-v46-deep-project-analysis/slices/slice-02-provider-analysis-json-contract/EXECUTION_BRIEF.md +34 -0
  82. package/specs/quiver-v46-deep-project-analysis/slices/slice-02-provider-analysis-json-contract/slice.json +79 -0
  83. package/specs/quiver-v46-deep-project-analysis/slices/slice-03-doc-proposal-review-safe-writes/CLOSURE_BRIEF.md +19 -0
  84. package/specs/quiver-v46-deep-project-analysis/slices/slice-03-doc-proposal-review-safe-writes/EXECUTION_BRIEF.md +33 -0
  85. package/specs/quiver-v46-deep-project-analysis/slices/slice-03-doc-proposal-review-safe-writes/slice.json +79 -0
  86. package/specs/quiver-v46-deep-project-analysis/slices/slice-04-validation-docs-release-readiness/CLOSURE_BRIEF.md +20 -0
  87. package/specs/quiver-v46-deep-project-analysis/slices/slice-04-validation-docs-release-readiness/EXECUTION_BRIEF.md +32 -0
  88. package/specs/quiver-v46-deep-project-analysis/slices/slice-04-validation-docs-release-readiness/slice.json +85 -0
  89. package/specs/quiver-v50-audit-trust-foundation/EVIDENCE_REPORT.md +151 -0
  90. package/specs/quiver-v50-audit-trust-foundation/EXECUTION_PLAN.md +34 -0
  91. package/specs/quiver-v50-audit-trust-foundation/SPEC.md +113 -0
  92. package/specs/quiver-v50-audit-trust-foundation/STATUS.md +26 -0
  93. package/specs/quiver-v50-audit-trust-foundation/pr.md +120 -0
  94. package/specs/quiver-v50-audit-trust-foundation/slices/slice-00-audit-baseline-and-resolved-findings/CLOSURE_BRIEF.md +20 -0
  95. package/specs/quiver-v50-audit-trust-foundation/slices/slice-00-audit-baseline-and-resolved-findings/EXECUTION_BRIEF.md +58 -0
  96. package/specs/quiver-v50-audit-trust-foundation/slices/slice-00-audit-baseline-and-resolved-findings/slice.json +57 -0
  97. package/specs/quiver-v50-audit-trust-foundation/slices/slice-01-runtime-minimum-and-package-metadata/CLOSURE_BRIEF.md +24 -0
  98. package/specs/quiver-v50-audit-trust-foundation/slices/slice-01-runtime-minimum-and-package-metadata/EXECUTION_BRIEF.md +67 -0
  99. package/specs/quiver-v50-audit-trust-foundation/slices/slice-01-runtime-minimum-and-package-metadata/slice.json +71 -0
  100. package/specs/quiver-v50-audit-trust-foundation/slices/slice-02-migrate-write-safety-contract/CLOSURE_BRIEF.md +24 -0
  101. package/specs/quiver-v50-audit-trust-foundation/slices/slice-02-migrate-write-safety-contract/EXECUTION_BRIEF.md +71 -0
  102. package/specs/quiver-v50-audit-trust-foundation/slices/slice-02-migrate-write-safety-contract/slice.json +81 -0
  103. package/specs/quiver-v50-audit-trust-foundation/slices/slice-03-security-reporting-channel/CLOSURE_BRIEF.md +22 -0
  104. package/specs/quiver-v50-audit-trust-foundation/slices/slice-03-security-reporting-channel/EXECUTION_BRIEF.md +57 -0
  105. package/specs/quiver-v50-audit-trust-foundation/slices/slice-03-security-reporting-channel/slice.json +56 -0
  106. package/specs/quiver-v50-audit-trust-foundation/slices/slice-04-user-facing-i18n-error-coverage/CLOSURE_BRIEF.md +23 -0
  107. package/specs/quiver-v50-audit-trust-foundation/slices/slice-04-user-facing-i18n-error-coverage/EXECUTION_BRIEF.md +68 -0
  108. package/specs/quiver-v50-audit-trust-foundation/slices/slice-04-user-facing-i18n-error-coverage/slice.json +84 -0
  109. package/specs/quiver-v50-audit-trust-foundation/slices/slice-05-init-analyze-progress-and-summaries/CLOSURE_BRIEF.md +25 -0
  110. package/specs/quiver-v50-audit-trust-foundation/slices/slice-05-init-analyze-progress-and-summaries/EXECUTION_BRIEF.md +73 -0
  111. package/specs/quiver-v50-audit-trust-foundation/slices/slice-05-init-analyze-progress-and-summaries/slice.json +72 -0
  112. package/specs/quiver-v50-audit-trust-foundation/slices/slice-06-contributor-and-architecture-docs/CLOSURE_BRIEF.md +22 -0
  113. package/specs/quiver-v50-audit-trust-foundation/slices/slice-06-contributor-and-architecture-docs/EXECUTION_BRIEF.md +66 -0
  114. package/specs/quiver-v50-audit-trust-foundation/slices/slice-06-contributor-and-architecture-docs/slice.json +74 -0
  115. package/specs/quiver-v50-audit-trust-foundation/slices/slice-07-ci-and-documentation-lint-baseline/CLOSURE_BRIEF.md +29 -0
  116. package/specs/quiver-v50-audit-trust-foundation/slices/slice-07-ci-and-documentation-lint-baseline/EXECUTION_BRIEF.md +74 -0
  117. package/specs/quiver-v50-audit-trust-foundation/slices/slice-07-ci-and-documentation-lint-baseline/slice.json +81 -0
  118. package/specs/quiver-v51-cli-ergonomics-automation-contracts/EVIDENCE_REPORT.md +145 -0
  119. package/specs/quiver-v51-cli-ergonomics-automation-contracts/EXECUTION_PLAN.md +32 -0
  120. package/specs/quiver-v51-cli-ergonomics-automation-contracts/SPEC.md +109 -0
  121. package/specs/quiver-v51-cli-ergonomics-automation-contracts/STATUS.md +25 -0
  122. package/specs/quiver-v51-cli-ergonomics-automation-contracts/pr.md +69 -0
  123. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-00-cli-contract-baseline/CLOSURE_BRIEF.md +20 -0
  124. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-00-cli-contract-baseline/EXECUTION_BRIEF.md +58 -0
  125. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-00-cli-contract-baseline/slice.json +56 -0
  126. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-01-flow-json-compatibility/CLOSURE_BRIEF.md +21 -0
  127. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-01-flow-json-compatibility/EXECUTION_BRIEF.md +61 -0
  128. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-01-flow-json-compatibility/slice.json +68 -0
  129. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-02-dashboard-section-validation-i18n/CLOSURE_BRIEF.md +25 -0
  130. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-02-dashboard-section-validation-i18n/EXECUTION_BRIEF.md +65 -0
  131. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-02-dashboard-section-validation-i18n/pr.md +134 -0
  132. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-02-dashboard-section-validation-i18n/slice.json +80 -0
  133. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-03-base-branch-resolution-policy/CLOSURE_BRIEF.md +31 -0
  134. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-03-base-branch-resolution-policy/EXECUTION_BRIEF.md +80 -0
  135. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-03-base-branch-resolution-policy/pr.md +159 -0
  136. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-03-base-branch-resolution-policy/slice.json +102 -0
  137. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-04-next-plan-graph-ux-edge-cases/CLOSURE_BRIEF.md +25 -0
  138. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-04-next-plan-graph-ux-edge-cases/EXECUTION_BRIEF.md +74 -0
  139. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-04-next-plan-graph-ux-edge-cases/pr.md +147 -0
  140. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-04-next-plan-graph-ux-edge-cases/slice.json +91 -0
  141. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-05-evidence-robustness-path-safety/CLOSURE_BRIEF.md +28 -0
  142. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-05-evidence-robustness-path-safety/EXECUTION_BRIEF.md +73 -0
  143. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-05-evidence-robustness-path-safety/pr.md +146 -0
  144. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-05-evidence-robustness-path-safety/slice.json +100 -0
  145. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-06-namespace-compatibility-windows-scripts/CLOSURE_BRIEF.md +36 -0
  146. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-06-namespace-compatibility-windows-scripts/EXECUTION_BRIEF.md +76 -0
  147. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-06-namespace-compatibility-windows-scripts/pr.md +155 -0
  148. package/specs/quiver-v51-cli-ergonomics-automation-contracts/slices/slice-06-namespace-compatibility-windows-scripts/slice.json +111 -0
  149. package/specs/quiver-v52-schema-docs-release-hygiene/EVIDENCE_REPORT.md +95 -0
  150. package/specs/quiver-v52-schema-docs-release-hygiene/EXECUTION_PLAN.md +31 -0
  151. package/specs/quiver-v52-schema-docs-release-hygiene/SPEC.md +107 -0
  152. package/specs/quiver-v52-schema-docs-release-hygiene/STATUS.md +22 -0
  153. package/specs/quiver-v52-schema-docs-release-hygiene/pr.md +69 -0
  154. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-00-schema-docs-release-baseline/CLOSURE_BRIEF.md +21 -0
  155. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-00-schema-docs-release-baseline/EXECUTION_BRIEF.md +60 -0
  156. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-00-schema-docs-release-baseline/slice.json +62 -0
  157. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-01-json-schema-for-slice-json/CLOSURE_BRIEF.md +34 -0
  158. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-01-json-schema-for-slice-json/EXECUTION_BRIEF.md +71 -0
  159. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-01-json-schema-for-slice-json/pr.md +146 -0
  160. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-01-json-schema-for-slice-json/slice.json +106 -0
  161. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-02-generated-cli-reference/CLOSURE_BRIEF.md +33 -0
  162. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-02-generated-cli-reference/EXECUTION_BRIEF.md +67 -0
  163. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-02-generated-cli-reference/pr.md +144 -0
  164. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-02-generated-cli-reference/slice.json +86 -0
  165. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-03-changelog-package-release-smoke-hygiene/CLOSURE_BRIEF.md +38 -0
  166. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-03-changelog-package-release-smoke-hygiene/EXECUTION_BRIEF.md +75 -0
  167. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-03-changelog-package-release-smoke-hygiene/pr.md +159 -0
  168. package/specs/quiver-v52-schema-docs-release-hygiene/slices/slice-03-changelog-package-release-smoke-hygiene/slice.json +105 -0
  169. package/src/create-quiver/commands/ai.js +521 -0
  170. package/src/create-quiver/commands/changelog.js +93 -0
  171. package/src/create-quiver/commands/config.js +16 -5
  172. package/src/create-quiver/commands/evidence.js +33 -2
  173. package/src/create-quiver/commands/flow.js +1 -0
  174. package/src/create-quiver/commands/graph.js +8 -2
  175. package/src/create-quiver/commands/plan.js +6 -0
  176. package/src/create-quiver/commands/spec.js +28 -13
  177. package/src/create-quiver/index.js +408 -98
  178. package/src/create-quiver/lib/ai/analyze-project-discovery.js +387 -0
  179. package/src/create-quiver/lib/ai/analyze-project-docs.js +364 -0
  180. package/src/create-quiver/lib/ai/analyze-project-parser.js +277 -0
  181. package/src/create-quiver/lib/ai/analyze-project-prompts.js +214 -0
  182. package/src/create-quiver/lib/ai/analyze-project-review.js +99 -0
  183. package/src/create-quiver/lib/ai/analyze-project-sampling.js +402 -0
  184. package/src/create-quiver/lib/ai/analyze-project-schema.js +92 -0
  185. package/src/create-quiver/lib/ai/analyze-project-validation.js +309 -0
  186. package/src/create-quiver/lib/ai/artifacts.js +4 -1
  187. package/src/create-quiver/lib/ai/github.js +5 -2
  188. package/src/create-quiver/lib/ai/spec-templates.js +19 -0
  189. package/src/create-quiver/lib/cli/command-registry.js +72 -0
  190. package/src/create-quiver/lib/cli/parser.js +14 -0
  191. package/src/create-quiver/lib/evidence.js +161 -6
  192. package/src/create-quiver/lib/git.js +99 -0
  193. package/src/create-quiver/lib/i18n/messages/en.js +52 -7
  194. package/src/create-quiver/lib/i18n/messages/es.js +54 -9
  195. package/src/create-quiver/lib/init-docs.js +1 -1
  196. package/src/create-quiver/lib/init-layout.js +9 -8
  197. package/src/create-quiver/lib/lifecycle.js +24 -26
  198. package/src/create-quiver/lib/readiness.js +67 -56
  199. package/src/create-quiver/lib/spec-worktrees.js +16 -34
@@ -6,12 +6,34 @@ const { redactSecrets } = require('../lib/evidence');
6
6
  const { formatActionableError } = require('../lib/actionable-error');
7
7
  const {
8
8
  assertProviderPromptWithinLimit,
9
+ byteLength,
9
10
  compactRevisionInput,
10
11
  extractCleanProviderOutput,
12
+ redactSensitiveLocalValues,
11
13
  writeRawProviderArtifact,
12
14
  } = require('../lib/ai/artifacts');
13
15
  const { buildContextPackMetadata, normalizeRole } = require('../lib/ai/context-packs');
14
16
  const { parseContextProposalOutput } = require('../lib/ai/context-proposal');
17
+ const { discoverProjectFiles } = require('../lib/ai/analyze-project-discovery');
18
+ const {
19
+ buildAnalyzeProjectDocProposal,
20
+ buildAnalyzeProjectWritePlan,
21
+ createAnalyzeProjectSnapshot,
22
+ formatAnalyzeProjectDiffPreview,
23
+ writeAnalyzeProjectDocs,
24
+ } = require('../lib/ai/analyze-project-docs');
25
+ const { parseAnalyzeProjectOutput } = require('../lib/ai/analyze-project-parser');
26
+ const { buildAnalyzeProjectPrompt } = require('../lib/ai/analyze-project-prompts');
27
+ const {
28
+ confirmAnalyzeProjectWrites,
29
+ reviewAnalyzeProjectDocProposal,
30
+ } = require('../lib/ai/analyze-project-review');
31
+ const {
32
+ DEFAULT_MAX_BYTES: DEFAULT_ANALYZE_MAX_BYTES,
33
+ DEFAULT_MAX_FILES: DEFAULT_ANALYZE_MAX_FILES,
34
+ sampleProjectFiles,
35
+ } = require('../lib/ai/analyze-project-sampling');
36
+ const { validateAnalyzeProjectPostWrite } = require('../lib/ai/analyze-project-validation');
15
37
  const { openEditor } = require('../lib/cli/editor');
16
38
  const { selectOption, promptText } = require('../lib/cli/selectors');
17
39
  const { createUx } = require('../lib/cli/ux');
@@ -103,11 +125,316 @@ const DEFAULT_PLAN_CONTEXT = 'planning';
103
125
  const DEFAULT_PLAN_PHASE = 'acceptance';
104
126
  const CONTEXT_PREP_START = '<!-- quiver:context-prep:start -->';
105
127
  const CONTEXT_PREP_END = '<!-- quiver:context-prep:end -->';
128
+ const ANALYZE_PROJECT_KIND = 'quiver-project-analysis-plan';
106
129
 
107
130
  function formatError(message) {
108
131
  return `create-quiver: ${message}`;
109
132
  }
110
133
 
134
+ function normalizeAnalyzeBudget(value, fallback, flagName) {
135
+ if (typeof value === 'undefined' || value === null || value === '') {
136
+ return fallback;
137
+ }
138
+ const parsed = Number.parseInt(value, 10);
139
+ if (!Number.isInteger(parsed) || parsed <= 0) {
140
+ throw new Error(formatError(`invalid value for ${flagName}`));
141
+ }
142
+ return parsed;
143
+ }
144
+
145
+ function mergeReasonSummaries(...summaries) {
146
+ const merged = {};
147
+ for (const summary of summaries) {
148
+ for (const [reason, count] of Object.entries(summary || {})) {
149
+ merged[reason] = (merged[reason] || 0) + count;
150
+ }
151
+ }
152
+ return Object.keys(merged)
153
+ .sort()
154
+ .reduce((acc, reason) => {
155
+ acc[reason] = merged[reason];
156
+ return acc;
157
+ }, {});
158
+ }
159
+
160
+ function limitList(items, maxItems = 30) {
161
+ const list = Array.isArray(items) ? items : [];
162
+ return {
163
+ items: list.slice(0, maxItems),
164
+ hidden: Math.max(0, list.length - maxItems),
165
+ };
166
+ }
167
+
168
+ function formatAnalyzeProjectFileLine(file) {
169
+ const details = [];
170
+ if (Array.isArray(file.signals) && file.signals.length > 0) {
171
+ details.push(file.signals.join(', '));
172
+ }
173
+ if (typeof file.bytes === 'number') {
174
+ details.push(`${file.bytes} bytes`);
175
+ }
176
+ if (file.reason) {
177
+ details.push(file.reason);
178
+ }
179
+ return `- ${file.path}${details.length > 0 ? ` (${details.join('; ')})` : ''}`;
180
+ }
181
+
182
+ function formatAnalyzeProjectReport(report) {
183
+ const selected = limitList(report.selected_files, 80);
184
+ const omitted = limitList(report.omitted_files, 30);
185
+ const safety = limitList(report.safety_exclusions, 30);
186
+ const workspaces = limitList(report.roots.workspaces, 20);
187
+ const lines = [
188
+ 'AI analyze-project read-only analysis',
189
+ `Mode: ${report.mode}`,
190
+ `Dry-run: ${report.dry_run ? 'yes' : 'no'} (dry-run never writes)`,
191
+ `Provider execution: ${report.provider_execution}`,
192
+ `Writes: ${report.writes.length === 0 ? 'none' : report.writes.join(', ')}`,
193
+ `Project: ${report.project.name}`,
194
+ `Scope: ${report.options.scope}`,
195
+ `Budgets: ${report.budgets.selected_files}/${report.budgets.max_files} files, ${report.budgets.selected_bytes}/${report.budgets.max_bytes} bytes`,
196
+ `Selected files: ${report.selected_files.length}`,
197
+ `Omitted files: ${report.omitted_files.length}`,
198
+ `Safety exclusions: ${report.safety_exclusions.length}`,
199
+ '',
200
+ 'Workspace roots:',
201
+ ];
202
+
203
+ for (const workspace of workspaces.items) {
204
+ lines.push(`- ${workspace.path} (${workspace.name}; ${workspace.source})`);
205
+ }
206
+ if (workspaces.hidden > 0) {
207
+ lines.push(`- ... ${workspaces.hidden} more`);
208
+ }
209
+
210
+ lines.push('', `Detected stack: ${report.detected.stack.length > 0 ? report.detected.stack.join(', ') : 'unknown'}`);
211
+ lines.push(`Source roots: ${report.detected.source_roots.length > 0 ? report.detected.source_roots.join(', ') : 'none'}`);
212
+ lines.push(`Entrypoints: ${report.detected.entrypoints.length > 0 ? report.detected.entrypoints.join(', ') : 'none'}`);
213
+ lines.push(`Configs: ${report.detected.configs.length > 0 ? report.detected.configs.join(', ') : 'none'}`);
214
+
215
+ lines.push('', 'Selected files:');
216
+ for (const file of selected.items) {
217
+ lines.push(formatAnalyzeProjectFileLine(file));
218
+ }
219
+ if (selected.hidden > 0) {
220
+ lines.push(`- ... ${selected.hidden} more`);
221
+ }
222
+ if (selected.items.length === 0) {
223
+ lines.push('- none');
224
+ }
225
+
226
+ lines.push('', 'Omitted files:');
227
+ for (const file of omitted.items) {
228
+ lines.push(formatAnalyzeProjectFileLine(file));
229
+ }
230
+ if (omitted.hidden > 0) {
231
+ lines.push(`- ... ${omitted.hidden} more`);
232
+ }
233
+ if (omitted.items.length === 0) {
234
+ lines.push('- none');
235
+ }
236
+
237
+ lines.push('', 'Safety exclusions:');
238
+ for (const file of safety.items) {
239
+ lines.push(`- ${file.path} (${file.reason})`);
240
+ }
241
+ if (safety.hidden > 0) {
242
+ lines.push(`- ... ${safety.hidden} more`);
243
+ }
244
+ if (safety.items.length === 0) {
245
+ lines.push('- none');
246
+ }
247
+
248
+ lines.push('', 'Next commands:');
249
+ for (const command of report.next_commands) {
250
+ lines.push(`- ${command}`);
251
+ }
252
+
253
+ return `${lines.join('\n')}\n`;
254
+ }
255
+
256
+ function buildAnalyzeProjectReport(repoRoot, options = {}) {
257
+ const deep = options.deep === true;
258
+ const includeSource = options.includeSource === true || deep;
259
+ const includeDb = options.includeDb === true || deep;
260
+ const includeTests = options.includeTests === true;
261
+ const maxFiles = normalizeAnalyzeBudget(options.maxFiles, DEFAULT_ANALYZE_MAX_FILES, '--max-files');
262
+ const maxBytes = normalizeAnalyzeBudget(options.maxBytes, DEFAULT_ANALYZE_MAX_BYTES, '--max-bytes');
263
+ const discovery = discoverProjectFiles(repoRoot, { scope: options.scope || '' });
264
+ const sample = sampleProjectFiles(discovery.files, {
265
+ includeDb,
266
+ includeSource,
267
+ includeTests,
268
+ maxBytes,
269
+ maxFiles,
270
+ });
271
+ const omittedFiles = [
272
+ ...sample.omittedFiles,
273
+ ...discovery.skippedFiles.map((file) => ({
274
+ path: file.path,
275
+ reason: file.reason,
276
+ })),
277
+ ].sort((a, b) => a.path.localeCompare(b.path));
278
+ const omittedSummary = mergeReasonSummaries(
279
+ sample.omittedSummary,
280
+ discovery.skippedSummary,
281
+ );
282
+
283
+ return {
284
+ schema_version: 1,
285
+ kind: ANALYZE_PROJECT_KIND,
286
+ command: 'ai analyze-project',
287
+ mode: 'read-only',
288
+ dry_run: options.dryRun === true,
289
+ read_only: true,
290
+ provider_execution: 'skipped',
291
+ writes: [],
292
+ project: discovery.project,
293
+ options: {
294
+ deep,
295
+ scope: discovery.roots.analysis_root,
296
+ max_files: maxFiles,
297
+ max_bytes: maxBytes,
298
+ include_source: includeSource,
299
+ include_tests: includeTests,
300
+ include_db: includeDb,
301
+ },
302
+ roots: discovery.roots,
303
+ detected: discovery.detected,
304
+ budgets: sample.budgets,
305
+ selected_files: sample.selectedFiles,
306
+ omitted_files: omittedFiles,
307
+ omitted_summary: omittedSummary,
308
+ safety_exclusions: discovery.safetyExclusions,
309
+ safety_summary: discovery.safetySummary,
310
+ next_commands: [
311
+ 'npx create-quiver ai analyze-project --deep --dry-run --json',
312
+ 'npx create-quiver ai analyze-project --deep --review',
313
+ ],
314
+ };
315
+ }
316
+
317
+ function limitProviderArtifactText(text, maxBytes = 12_000) {
318
+ let value = String(text || '');
319
+ const redacted = value;
320
+ if (byteLength(redacted) <= maxBytes) {
321
+ return {
322
+ text: redacted,
323
+ truncated: false,
324
+ bytes: byteLength(redacted),
325
+ };
326
+ }
327
+
328
+ value = redacted;
329
+ while (byteLength(value) > maxBytes && value.length > 0) {
330
+ value = value.slice(0, Math.max(0, value.length - Math.ceil((byteLength(value) - maxBytes) / 2) - 16));
331
+ }
332
+
333
+ return {
334
+ text: `${value.trimEnd()}\n[TRUNCATED BY QUIVER]\n`,
335
+ truncated: true,
336
+ bytes: byteLength(redacted),
337
+ };
338
+ }
339
+
340
+ function buildAnalyzeProjectProviderArtifact(result, clean, repoRoot, options = {}) {
341
+ const rawOutput = clean?.cleanOutput || result?.stdout || result?.stderr || '';
342
+ const redactedOutput = redactSensitiveLocalValues(rawOutput, { projectRoot: repoRoot });
343
+ const limited = limitProviderArtifactText(redactedOutput, options.maxBytes || 12_000);
344
+ return {
345
+ schema_version: 1,
346
+ kind: 'quiver-analyze-project-provider-artifact',
347
+ persisted: false,
348
+ redacted: true,
349
+ size_limited: true,
350
+ provider: result?.provider || null,
351
+ command: result?.command || null,
352
+ exit_code: typeof result?.exitCode === 'number' ? result.exitCode : null,
353
+ output_source: clean?.source || 'unknown',
354
+ output_bytes: limited.bytes,
355
+ output_truncated: limited.truncated,
356
+ output: limited.text,
357
+ };
358
+ }
359
+
360
+ function formatAnalyzeProjectLiveReport(report) {
361
+ const warningCount = report.analysis_validation?.warnings?.length || 0;
362
+ const docUpdatePaths = report.analysis_validation?.doc_update_paths || [];
363
+ const lines = [
364
+ 'AI analyze-project provider analysis',
365
+ `Mode: ${report.mode}`,
366
+ `Provider: ${report.provider}`,
367
+ `Provider execution: ${report.provider_execution}`,
368
+ `Writes: ${report.writes.length === 0 ? 'none' : report.writes.join(', ')}`,
369
+ `Privacy preflight: ${report.privacy_preflight.ok ? 'passed' : 'failed'}`,
370
+ `Prompt bytes: ${report.prompt.bytes}/${report.prompt.max_provider_prompt_bytes}`,
371
+ `Selected files: ${report.selected_files.length}`,
372
+ `Omitted files: ${report.omitted_files.length}`,
373
+ `Safety exclusions: ${report.safety_exclusions.length}`,
374
+ `Analysis validation: passed (${warningCount} warning${warningCount === 1 ? '' : 's'})`,
375
+ `Doc update proposals: ${docUpdatePaths.length > 0 ? docUpdatePaths.join(', ') : 'none'}`,
376
+ ];
377
+
378
+ if (warningCount > 0) {
379
+ lines.push('', 'Warnings:');
380
+ for (const warning of report.analysis_validation.warnings.slice(0, 20)) {
381
+ lines.push(`- ${warning.path}: ${warning.issue}`);
382
+ }
383
+ }
384
+
385
+ lines.push('', 'Next commands:');
386
+ lines.push('- npx create-quiver ai analyze-project --deep --review');
387
+ lines.push('- npx create-quiver ai analyze-project --deep --json');
388
+
389
+ return `${lines.join('\n')}\n`;
390
+ }
391
+
392
+ function formatAnalyzeProjectPostWriteValidation(validation) {
393
+ if (!validation) {
394
+ return [];
395
+ }
396
+ const warningCount = validation.warnings?.length || 0;
397
+ const errorCount = validation.errors?.length || 0;
398
+ const lines = [
399
+ `Post-write validation: ${validation.ok ? 'passed' : 'failed'} (${errorCount} error${errorCount === 1 ? '' : 's'}, ${warningCount} warning${warningCount === 1 ? '' : 's'})`,
400
+ ];
401
+ for (const issue of [...(validation.errors || []), ...(validation.warnings || [])].slice(0, 20)) {
402
+ lines.push(`- ${issue.path || 'analysis'}: ${issue.issue} - ${issue.message}`);
403
+ }
404
+ return lines;
405
+ }
406
+
407
+ function formatAnalyzeProjectReviewPlan({ writePlan, reviewPath, snapshot, writtenDocs, completed = false, validation = null } = {}) {
408
+ const changed = (writePlan || []).filter((item) => item.action !== 'skip');
409
+ const dirty = changed.filter((item) => item.dirty);
410
+ const lines = [
411
+ completed ? 'AI analyze-project docs written' : 'AI analyze-project review write plan',
412
+ `Review artifact: ${reviewPath || 'none'}`,
413
+ `Writes: ${changed.length > 0 ? changed.map((item) => item.path).join(', ') : 'none'}`,
414
+ `Dirty target docs: ${dirty.length > 0 ? dirty.map((item) => item.path).join(', ') : 'none'}`,
415
+ ];
416
+
417
+ if (snapshot) {
418
+ lines.push(`Snapshot: ${snapshot.root}`);
419
+ lines.push(`Manifest: ${snapshot.manifestPath}`);
420
+ }
421
+ if (completed) {
422
+ lines.push(`Written docs: ${writtenDocs && writtenDocs.length > 0 ? writtenDocs.join(', ') : 'none'}`);
423
+ lines.push(...formatAnalyzeProjectPostWriteValidation(validation));
424
+ return `${lines.join('\n')}\n`;
425
+ }
426
+
427
+ lines.push('', 'Proposed changes:');
428
+ for (const item of writePlan || []) {
429
+ lines.push(`- ${item.path}: ${item.action}${item.reason ? ` (${item.reason})` : ''}`);
430
+ }
431
+ lines.push('', 'Final diff:');
432
+ lines.push(...formatAnalyzeProjectDiffPreview(writePlan || []));
433
+ lines.push('', 'Confirmation required before writing.');
434
+
435
+ return `${lines.join('\n')}\n`;
436
+ }
437
+
111
438
  function formatLocalizedActionableError({ failure, impact, fix, nextCommand } = {}, options = {}) {
112
439
  const translator = createTranslator(options.language);
113
440
  const lines = [`create-quiver: ${String(failure || 'operation failed').trim()}`];
@@ -1541,6 +1868,199 @@ async function runPrepareContext(repoRoot, options = {}) {
1541
1868
  };
1542
1869
  }
1543
1870
 
1871
+ async function runAnalyzeProject(repoRoot, options = {}) {
1872
+ const report = buildAnalyzeProjectReport(repoRoot, options);
1873
+ if (options.dryRun === true) {
1874
+ if (options.json === true) {
1875
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
1876
+ return report;
1877
+ }
1878
+ process.stdout.write(formatAnalyzeProjectReport(report));
1879
+ return report;
1880
+ }
1881
+
1882
+ const role = normalizeRole(options.role || DEFAULT_PLAN_ROLE);
1883
+ const runtimeProfile = resolveRuntimeAgentProfile(repoRoot, role, options, DEFAULT_PLAN_PROVIDER);
1884
+ const provider = runtimeProfile.provider;
1885
+ const timeoutMs = normalizeTimeout(options.timeout);
1886
+ const promptPackage = buildAnalyzeProjectPrompt({
1887
+ analysisPlan: report,
1888
+ repoRoot,
1889
+ maxFileBytes: options.maxPromptFileBytes,
1890
+ maxTotalFileBytes: options.maxPromptTotalBytes,
1891
+ });
1892
+ const prompt = promptPackage.prompt;
1893
+ const promptLimit = assertProviderPromptWithinLimit(prompt, options.promptLimitOptions || {});
1894
+ const privacyPreflight = promptPackage.privacyPreflight;
1895
+
1896
+ if (!privacyPreflight.ok) {
1897
+ const error = new Error(formatError('ai analyze-project privacy preflight failed; provider execution was blocked before sending repository content.'));
1898
+ error.code = 'AI_ANALYZE_PROJECT_PRIVACY_PREFLIGHT_FAILED';
1899
+ error.details = privacyPreflight;
1900
+ throw error;
1901
+ }
1902
+
1903
+ let invocation;
1904
+ try {
1905
+ invocation = buildProviderInvocation(provider, {
1906
+ prompt,
1907
+ cwd: repoRoot,
1908
+ timeoutMs,
1909
+ ...runtimeModelExecutionOptions(runtimeProfile, options),
1910
+ enforceModelSelection: false,
1911
+ });
1912
+ } catch (error) {
1913
+ throw annotateProviderError(error, 'analyze-project');
1914
+ }
1915
+
1916
+ if (options.printPrompt) {
1917
+ process.stdout.write(prompt.endsWith('\n') ? prompt : `${prompt}\n`);
1918
+ return {
1919
+ ...report,
1920
+ provider,
1921
+ role,
1922
+ invocation,
1923
+ privacy_preflight: privacyPreflight,
1924
+ prompt: {
1925
+ bytes: promptLimit.bytes,
1926
+ max_provider_prompt_bytes: promptLimit.maxProviderPromptBytes,
1927
+ files: promptPackage.files,
1928
+ },
1929
+ };
1930
+ }
1931
+
1932
+ let result;
1933
+ try {
1934
+ result = await (options.runProviderFn || runProvider)(provider, {
1935
+ prompt,
1936
+ cwd: repoRoot,
1937
+ timeoutMs,
1938
+ dryRun: false,
1939
+ probe: options.probe,
1940
+ spawn: options.spawn,
1941
+ tempRoot: options.tempRoot,
1942
+ tempFileName: options.tempFileName,
1943
+ tempFilePrefix: options.tempFilePrefix,
1944
+ ...runtimeModelExecutionOptions(runtimeProfile, options),
1945
+ enforceModelSelection: false,
1946
+ });
1947
+ } catch (error) {
1948
+ throw annotateProviderError(error, 'analyze-project');
1949
+ }
1950
+
1951
+ if (!result.ok) {
1952
+ throw annotateProviderError(result.error || new Error('provider run failed'), 'analyze-project');
1953
+ }
1954
+
1955
+ const clean = extractCleanProviderOutput(result, { prompt, projectRoot: repoRoot });
1956
+ const parsed = parseAnalyzeProjectOutput(clean.cleanOutput, {
1957
+ selectedFiles: report.selected_files,
1958
+ promptFiles: promptPackage.files,
1959
+ });
1960
+ const completedReport = {
1961
+ ...report,
1962
+ provider,
1963
+ role,
1964
+ provider_execution: 'completed',
1965
+ invocation,
1966
+ privacy_preflight: privacyPreflight,
1967
+ prompt: {
1968
+ bytes: promptLimit.bytes,
1969
+ max_provider_prompt_bytes: promptLimit.maxProviderPromptBytes,
1970
+ files: promptPackage.files,
1971
+ },
1972
+ analysis: parsed.analysis,
1973
+ analysis_validation: {
1974
+ parse_source: parsed.parseSource,
1975
+ warnings: parsed.warnings,
1976
+ doc_update_paths: parsed.docUpdatePaths,
1977
+ },
1978
+ provider_artifact: buildAnalyzeProjectProviderArtifact(result, clean, repoRoot),
1979
+ };
1980
+
1981
+ if (options.review === true) {
1982
+ const initialProposal = buildAnalyzeProjectDocProposal(parsed.analysis);
1983
+ const reviewed = await reviewAnalyzeProjectDocProposal(repoRoot, initialProposal, options);
1984
+ const writePlan = buildAnalyzeProjectWritePlan(repoRoot, reviewed.proposal);
1985
+ process.stdout.write(formatAnalyzeProjectReviewPlan({
1986
+ writePlan,
1987
+ reviewPath: reviewed.reviewPath,
1988
+ }));
1989
+ await confirmAnalyzeProjectWrites(writePlan, options);
1990
+ const lifecycleRun = ensureAiRun(repoRoot, {
1991
+ command: 'ai analyze-project',
1992
+ input: reviewed.reviewPath,
1993
+ runId: options.runId,
1994
+ phase: 'created',
1995
+ });
1996
+ const snapshot = createAnalyzeProjectSnapshot(repoRoot, lifecycleRun, writePlan, {
1997
+ providerArtifact: completedReport.provider_artifact,
1998
+ proposal: reviewed.proposal,
1999
+ now: options.now || new Date(),
2000
+ });
2001
+ const writtenDocs = writeAnalyzeProjectDocs(writePlan);
2002
+ let writeReport = {
2003
+ ...completedReport,
2004
+ review: true,
2005
+ review_path: reviewed.reviewPath,
2006
+ doc_proposal: reviewed.proposal,
2007
+ write_plan: writePlan.map((item) => ({
2008
+ path: item.path,
2009
+ action: item.action,
2010
+ dirty: item.dirty,
2011
+ before_sha256: item.before_sha256,
2012
+ after_sha256: item.after_sha256,
2013
+ reason: item.reason,
2014
+ })),
2015
+ snapshot,
2016
+ written_docs: writtenDocs,
2017
+ run_id: lifecycleRun.run_id,
2018
+ };
2019
+ const postWriteValidation = validateAnalyzeProjectPostWrite(repoRoot, writeReport, {
2020
+ strict: options.strict === true,
2021
+ });
2022
+ writeReport = {
2023
+ ...writeReport,
2024
+ post_write_validation: postWriteValidation,
2025
+ };
2026
+
2027
+ if (options.json === true) {
2028
+ process.stdout.write(`${JSON.stringify(writeReport, null, 2)}\n`);
2029
+ if (!postWriteValidation.ok) {
2030
+ const error = new Error(formatError('ai analyze-project post-write validation failed.'));
2031
+ error.code = 'AI_ANALYZE_PROJECT_POST_WRITE_VALIDATION_FAILED';
2032
+ error.validation = postWriteValidation;
2033
+ throw error;
2034
+ }
2035
+ return writeReport;
2036
+ }
2037
+
2038
+ process.stdout.write(formatAnalyzeProjectReviewPlan({
2039
+ writePlan,
2040
+ reviewPath: reviewed.reviewPath,
2041
+ snapshot,
2042
+ writtenDocs,
2043
+ validation: postWriteValidation,
2044
+ completed: true,
2045
+ }));
2046
+ if (!postWriteValidation.ok) {
2047
+ const error = new Error(formatError('ai analyze-project post-write validation failed.'));
2048
+ error.code = 'AI_ANALYZE_PROJECT_POST_WRITE_VALIDATION_FAILED';
2049
+ error.validation = postWriteValidation;
2050
+ throw error;
2051
+ }
2052
+ return writeReport;
2053
+ }
2054
+
2055
+ if (options.json === true) {
2056
+ process.stdout.write(`${JSON.stringify(completedReport, null, 2)}\n`);
2057
+ return completedReport;
2058
+ }
2059
+
2060
+ process.stdout.write(formatAnalyzeProjectLiveReport(completedReport));
2061
+ return completedReport;
2062
+ }
2063
+
1544
2064
  async function runPrepareContextWithPlanner(repoRoot, options = {}) {
1545
2065
  const role = normalizeRole(options.role || DEFAULT_PLAN_ROLE);
1546
2066
  const runtimeProfile = resolveRuntimeAgentProfile(repoRoot, role, options, DEFAULT_PLAN_PROVIDER);
@@ -3412,6 +3932,7 @@ module.exports = {
3412
3932
  resolveInteractiveAgentSetOptions,
3413
3933
  runAgent,
3414
3934
  runActiveSlice,
3935
+ runAnalyzeProject,
3415
3936
  runDoctor,
3416
3937
  runExecutePlan,
3417
3938
  runExecuteSlice,
@@ -0,0 +1,93 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function parseChangelog(content) {
5
+ const lines = String(content || '').split(/\r?\n/);
6
+ const entries = [];
7
+ let current = null;
8
+
9
+ for (const line of lines) {
10
+ const heading = line.match(/^##\s+\[?([^\]\n]+)\]?(?:\s+-\s+(.+))?$/);
11
+ if (heading) {
12
+ if (current) {
13
+ current.body = current.body.trim();
14
+ entries.push(current);
15
+ }
16
+ current = {
17
+ version: heading[1].trim(),
18
+ date: heading[2]?.trim() || null,
19
+ body: '',
20
+ };
21
+ continue;
22
+ }
23
+
24
+ if (current) {
25
+ current.body += `${line}\n`;
26
+ }
27
+ }
28
+
29
+ if (current) {
30
+ current.body = current.body.trim();
31
+ entries.push(current);
32
+ }
33
+
34
+ return entries;
35
+ }
36
+
37
+ function readChangelog(packageRoot) {
38
+ const changelogPath = path.join(packageRoot, 'CHANGELOG.md');
39
+ if (!fs.existsSync(changelogPath)) {
40
+ return { path: changelogPath, entries: [], missing: true };
41
+ }
42
+
43
+ const content = fs.readFileSync(changelogPath, 'utf8');
44
+ return { path: changelogPath, entries: parseChangelog(content), missing: false };
45
+ }
46
+
47
+ function formatHumanChangelog(report) {
48
+ if (report.missing) {
49
+ return 'Quiver changelog\n\nNo local CHANGELOG.md was found in the installed package.\n';
50
+ }
51
+
52
+ const entries = report.entries.slice(0, 3);
53
+ if (entries.length === 0) {
54
+ return 'Quiver changelog\n\nLocal CHANGELOG.md did not contain parseable release entries.\n';
55
+ }
56
+
57
+ const lines = ['Quiver changelog', ''];
58
+ for (const entry of entries) {
59
+ lines.push(`## ${entry.version}${entry.date ? ` - ${entry.date}` : ''}`);
60
+ if (entry.body) {
61
+ lines.push(entry.body);
62
+ }
63
+ lines.push('');
64
+ }
65
+
66
+ return `${lines.join('\n').trimEnd()}\n`;
67
+ }
68
+
69
+ function runChangelog(options = {}) {
70
+ const packageRoot = options.packageRoot || path.resolve(__dirname, '../..', '..');
71
+ const report = readChangelog(packageRoot);
72
+ const payload = {
73
+ schema_version: 1,
74
+ source: report.path,
75
+ missing: report.missing,
76
+ entries: report.entries.slice(0, 10),
77
+ };
78
+
79
+ if (options.json) {
80
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
81
+ return payload;
82
+ }
83
+
84
+ process.stdout.write(formatHumanChangelog(report));
85
+ return payload;
86
+ }
87
+
88
+ module.exports = {
89
+ formatHumanChangelog,
90
+ parseChangelog,
91
+ readChangelog,
92
+ runChangelog,
93
+ };