create-quiver 0.17.1 → 0.17.3
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.
- package/ACTIVE_SLICES.md +43 -0
- package/CHANGELOG.md +8 -0
- package/auditoria-ux-ui-performance-documentacion.md +705 -0
- package/copys-landing-page.md +244 -0
- package/docs/CLI_UX_GUIDE.md +13 -3
- package/docs/TROUBLESHOOTING.md +94 -0
- package/docs/reference/commands.md +68 -4
- package/docs/workflows/existing-project-ai-quiver-setup.md +552 -0
- package/docs/workflows/existing-project.md +36 -0
- package/package.json +1 -1
- package/specs/quiver-v43-cli-i18n-audit-release-readiness/command-language-mode-matrix.json +5 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/EVIDENCE_REPORT.md +151 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/EXECUTION_PLAN.md +50 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/SPEC.md +207 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/STATUS.md +32 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/pr.md +73 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-00-analysis-run-contract/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-00-analysis-run-contract/EXECUTION_BRIEF.md +63 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-00-analysis-run-contract/slice.json +70 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-01-provider-fixture-harness/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-01-provider-fixture-harness/EXECUTION_BRIEF.md +60 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-01-provider-fixture-harness/slice.json +74 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-02-safe-context-boundary/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-02-safe-context-boundary/EXECUTION_BRIEF.md +68 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-02-safe-context-boundary/slice.json +85 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-03-schema-error-grouping/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-03-schema-error-grouping/EXECUTION_BRIEF.md +65 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-03-schema-error-grouping/slice.json +80 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-04-safe-repair-layer/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-04-safe-repair-layer/EXECUTION_BRIEF.md +63 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-04-safe-repair-layer/slice.json +80 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-05-controlled-retry-layer/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-05-controlled-retry-layer/EXECUTION_BRIEF.md +65 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-05-controlled-retry-layer/slice.json +81 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-06-audit-review-transaction/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-06-audit-review-transaction/EXECUTION_BRIEF.md +71 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-06-audit-review-transaction/slice.json +88 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-07-semantic-validation-docs/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-07-semantic-validation-docs/EXECUTION_BRIEF.md +66 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-07-semantic-validation-docs/slice.json +86 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-08-structural-map-hardening/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-08-structural-map-hardening/EXECUTION_BRIEF.md +67 -0
- package/specs/quiver-v53-reliable-deep-project-analysis/slices/slice-08-structural-map-hardening/slice.json +81 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/EVIDENCE_REPORT.md +53 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/EXECUTION_PLAN.md +55 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/SPEC.md +184 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/STATUS.md +34 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-01-contract-regression-harness/CLOSURE_BRIEF.md +10 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-01-contract-regression-harness/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-01-contract-regression-harness/slice.json +72 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-02-safe-discovery-sampling/CLOSURE_BRIEF.md +11 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-02-safe-discovery-sampling/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-02-safe-discovery-sampling/slice.json +69 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-03-run-lifecycle-cli-io/CLOSURE_BRIEF.md +10 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-03-run-lifecycle-cli-io/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-03-run-lifecycle-cli-io/slice.json +69 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-04-artifact-store-redaction/CLOSURE_BRIEF.md +10 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-04-artifact-store-redaction/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-04-artifact-store-redaction/slice.json +73 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-05-path-aware-repair-engine/CLOSURE_BRIEF.md +10 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-05-path-aware-repair-engine/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-05-path-aware-repair-engine/slice.json +72 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-06-retry-controller/CLOSURE_BRIEF.md +9 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-06-retry-controller/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-06-retry-controller/slice.json +70 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-07-final-validation-review-write-gate/CLOSURE_BRIEF.md +10 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-07-final-validation-review-write-gate/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-07-final-validation-review-write-gate/slice.json +75 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-08-doctor-agents-guidance/CLOSURE_BRIEF.md +9 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-08-doctor-agents-guidance/EXECUTION_BRIEF.md +51 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-08-doctor-agents-guidance/slice.json +66 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-09-release-smoke-rollback-docs/CLOSURE_BRIEF.md +10 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-09-release-smoke-rollback-docs/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v54-deep-project-analysis-hardening/slices/slice-09-release-smoke-rollback-docs/slice.json +69 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/EVIDENCE_REPORT.md +209 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/EXECUTION_PLAN.md +49 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/SPEC.md +276 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/STATUS.md +29 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-01-cli-proposal-contract/CLOSURE_BRIEF.md +47 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-01-cli-proposal-contract/EXECUTION_BRIEF.md +67 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-01-cli-proposal-contract/pr.md +129 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-01-cli-proposal-contract/slice.json +82 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-02-save-proposal-flow/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-02-save-proposal-flow/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-02-save-proposal-flow/pr.md +116 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-02-save-proposal-flow/slice.json +69 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-03-noninteractive-apply-engine/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-03-noninteractive-apply-engine/EXECUTION_BRIEF.md +62 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-03-noninteractive-apply-engine/pr.md +132 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-03-noninteractive-apply-engine/slice.json +76 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-04-interactive-apply-ux/CLOSURE_BRIEF.md +47 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-04-interactive-apply-ux/EXECUTION_BRIEF.md +70 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-04-interactive-apply-ux/pr.md +141 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-04-interactive-apply-ux/slice.json +72 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-05-apply-saved-proposal/CLOSURE_BRIEF.md +42 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-05-apply-saved-proposal/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-05-apply-saved-proposal/pr.md +129 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-05-apply-saved-proposal/slice.json +67 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-06-i18n-docs-release-smoke/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-06-i18n-docs-release-smoke/EXECUTION_BRIEF.md +64 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-06-i18n-docs-release-smoke/pr.md +123 -0
- package/specs/quiver-v55-analyze-project-doc-apply-ux/slices/slice-06-i18n-docs-release-smoke/slice.json +76 -0
- package/src/create-quiver/commands/ai.js +989 -136
- package/src/create-quiver/index.js +83 -1
- package/src/create-quiver/lib/ai/analyze-project-apply.js +269 -0
- package/src/create-quiver/lib/ai/analyze-project-discovery.js +213 -2
- package/src/create-quiver/lib/ai/analyze-project-docs.js +3 -1
- package/src/create-quiver/lib/ai/analyze-project-interactive.js +241 -0
- package/src/create-quiver/lib/ai/analyze-project-parser.js +1 -0
- package/src/create-quiver/lib/ai/analyze-project-prompts.js +130 -0
- package/src/create-quiver/lib/ai/analyze-project-proposal.js +582 -0
- package/src/create-quiver/lib/ai/analyze-project-repair.js +402 -0
- package/src/create-quiver/lib/ai/analyze-project-sampling.js +31 -4
- package/src/create-quiver/lib/ai/analyze-project-validation.js +178 -8
- package/src/create-quiver/lib/ai/artifacts.js +83 -2
- package/src/create-quiver/lib/cli/ux-flags.js +6 -0
- package/src/create-quiver/lib/doctor.js +107 -12
- package/src/create-quiver/lib/i18n/messages/en.js +33 -0
- package/src/create-quiver/lib/i18n/messages/es.js +34 -0
|
@@ -22,18 +22,43 @@ const {
|
|
|
22
22
|
formatAnalyzeProjectDiffPreview,
|
|
23
23
|
writeAnalyzeProjectDocs,
|
|
24
24
|
} = require('../lib/ai/analyze-project-docs');
|
|
25
|
-
const {
|
|
26
|
-
|
|
25
|
+
const {
|
|
26
|
+
buildAnalyzeProjectPrompt,
|
|
27
|
+
buildAnalyzeProjectRetryPrompt,
|
|
28
|
+
buildSelectedContextManifest,
|
|
29
|
+
writeSelectedContextManifest,
|
|
30
|
+
} = require('../lib/ai/analyze-project-prompts');
|
|
31
|
+
const {
|
|
32
|
+
parseAnalyzeProjectOutputWithRepair,
|
|
33
|
+
writeAnalyzeProjectRepairManifest,
|
|
34
|
+
} = require('../lib/ai/analyze-project-repair');
|
|
27
35
|
const {
|
|
28
36
|
confirmAnalyzeProjectWrites,
|
|
29
37
|
reviewAnalyzeProjectDocProposal,
|
|
30
38
|
} = require('../lib/ai/analyze-project-review');
|
|
39
|
+
const {
|
|
40
|
+
findLatestAnalyzeProjectSavedProposalRun,
|
|
41
|
+
readAnalyzeProjectSavedProposal,
|
|
42
|
+
writeAnalyzeProjectProposalArtifacts,
|
|
43
|
+
} = require('../lib/ai/analyze-project-proposal');
|
|
44
|
+
const {
|
|
45
|
+
applyAnalyzeProjectDocProposal,
|
|
46
|
+
} = require('../lib/ai/analyze-project-apply');
|
|
47
|
+
const {
|
|
48
|
+
canUseAnalyzeProjectInteractiveSelector,
|
|
49
|
+
runAnalyzeProjectInteractiveApplySelector,
|
|
50
|
+
} = require('../lib/ai/analyze-project-interactive');
|
|
31
51
|
const {
|
|
32
52
|
DEFAULT_MAX_BYTES: DEFAULT_ANALYZE_MAX_BYTES,
|
|
33
53
|
DEFAULT_MAX_FILES: DEFAULT_ANALYZE_MAX_FILES,
|
|
34
54
|
sampleProjectFiles,
|
|
35
55
|
} = require('../lib/ai/analyze-project-sampling');
|
|
36
|
-
const {
|
|
56
|
+
const {
|
|
57
|
+
groupAnalyzeProjectIssues,
|
|
58
|
+
validateAnalyzeProjectPostWrite,
|
|
59
|
+
writeAnalyzeProjectRetryManifest,
|
|
60
|
+
writeAnalyzeProjectValidationManifest,
|
|
61
|
+
} = require('../lib/ai/analyze-project-validation');
|
|
37
62
|
const { openEditor } = require('../lib/cli/editor');
|
|
38
63
|
const { selectOption, promptText } = require('../lib/cli/selectors');
|
|
39
64
|
const { createUx } = require('../lib/cli/ux');
|
|
@@ -166,22 +191,23 @@ function limitList(items, maxItems = 30) {
|
|
|
166
191
|
}
|
|
167
192
|
|
|
168
193
|
function formatAnalyzeProjectIssues(issues = [], maxIssues = 8) {
|
|
169
|
-
const
|
|
170
|
-
const visible =
|
|
171
|
-
const lines = visible.map((
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
194
|
+
const groups = groupAnalyzeProjectIssues(issues, { maxExamplesPerGroup: 1 });
|
|
195
|
+
const visible = groups.slice(0, maxIssues);
|
|
196
|
+
const lines = visible.map((group) => {
|
|
197
|
+
const example = group.examples[0];
|
|
198
|
+
const exampleText = example
|
|
199
|
+
? ` Example: ${example.path || 'analysis'} - ${example.message}`
|
|
200
|
+
: '';
|
|
201
|
+
return `- ${group.path_family}: ${group.type} (${group.count} issue${group.count === 1 ? '' : 's'}). Cause: ${group.cause_hint}${exampleText}`;
|
|
176
202
|
});
|
|
177
|
-
const hidden =
|
|
203
|
+
const hidden = groups.length - visible.length;
|
|
178
204
|
if (hidden > 0) {
|
|
179
|
-
lines.push(`- ... ${hidden} more issue${hidden === 1 ? '' : 's'}`);
|
|
205
|
+
lines.push(`- ... ${hidden} more issue group${hidden === 1 ? '' : 's'}`);
|
|
180
206
|
}
|
|
181
207
|
return lines;
|
|
182
208
|
}
|
|
183
209
|
|
|
184
|
-
function enhanceAnalyzeProjectAnalysisError(error) {
|
|
210
|
+
function enhanceAnalyzeProjectAnalysisError(error, options = {}) {
|
|
185
211
|
if (!error || error.code !== 'AI_ANALYZE_PROJECT_INVALID') {
|
|
186
212
|
return error;
|
|
187
213
|
}
|
|
@@ -195,13 +221,19 @@ function enhanceAnalyzeProjectAnalysisError(error) {
|
|
|
195
221
|
error.message,
|
|
196
222
|
'Issues:',
|
|
197
223
|
...issueLines,
|
|
224
|
+
options.repairManifest?.path ? `Repair manifest: ${options.repairManifest.path}` : '',
|
|
225
|
+
options.retryManifest?.path ? `Retry manifest: ${options.retryManifest.path}` : '',
|
|
226
|
+
options.validationManifest?.path ? `Validation manifest: ${options.validationManifest.path}` : '',
|
|
198
227
|
'Next safe step: inspect the selected evidence with `npx create-quiver ai analyze-project --deep --dry-run --json`, then rerun live. If provider drift repeats, reduce --max-files or --max-bytes.',
|
|
199
|
-
].join('\n'));
|
|
228
|
+
].filter(Boolean).join('\n'));
|
|
200
229
|
wrapped.name = error.name;
|
|
201
230
|
wrapped.code = error.code;
|
|
202
231
|
wrapped.cause = error;
|
|
203
232
|
wrapped.issues = error.issues;
|
|
204
233
|
wrapped.details = error.issues;
|
|
234
|
+
wrapped.repair_manifest = options.repairManifest || null;
|
|
235
|
+
wrapped.retry_manifest = options.retryManifest || null;
|
|
236
|
+
wrapped.validation_manifest = options.validationManifest || null;
|
|
205
237
|
return wrapped;
|
|
206
238
|
}
|
|
207
239
|
|
|
@@ -251,6 +283,9 @@ function formatAnalyzeProjectReport(report) {
|
|
|
251
283
|
lines.push(`Source roots: ${report.detected.source_roots.length > 0 ? report.detected.source_roots.join(', ') : 'none'}`);
|
|
252
284
|
lines.push(`Entrypoints: ${report.detected.entrypoints.length > 0 ? report.detected.entrypoints.join(', ') : 'none'}`);
|
|
253
285
|
lines.push(`Configs: ${report.detected.configs.length > 0 ? report.detected.configs.join(', ') : 'none'}`);
|
|
286
|
+
lines.push(`Lockfiles: ${(report.detected.lockfiles || []).length > 0
|
|
287
|
+
? report.detected.lockfiles.map((file) => `${file.path} (${file.package_manager}; metadata only)`).join(', ')
|
|
288
|
+
: 'none'}`);
|
|
254
289
|
|
|
255
290
|
lines.push('', 'Selected files:');
|
|
256
291
|
for (const file of selected.items) {
|
|
@@ -397,6 +432,60 @@ function buildAnalyzeProjectProviderArtifact(result, clean, repoRoot, options =
|
|
|
397
432
|
};
|
|
398
433
|
}
|
|
399
434
|
|
|
435
|
+
function createAnalyzeProjectAuditRunId(now = new Date()) {
|
|
436
|
+
const date = now instanceof Date ? now : new Date(now || Date.now());
|
|
437
|
+
return `run-${date.toISOString()
|
|
438
|
+
.replace(/\.\d{3}Z$/, 'z')
|
|
439
|
+
.replace(/[^0-9a-z]+/gi, '-')
|
|
440
|
+
.toLowerCase()
|
|
441
|
+
.replace(/^-+|-+$/g, '')}`;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function writeAnalyzeProjectRunStatus(repoRoot, runId, status, details = {}) {
|
|
445
|
+
const statusPath = path.join(repoRoot, '.quiver', 'runs', runId, 'status.json');
|
|
446
|
+
fs.mkdirSync(path.dirname(statusPath), { recursive: true });
|
|
447
|
+
fs.writeFileSync(statusPath, `${JSON.stringify({
|
|
448
|
+
schema_version: 1,
|
|
449
|
+
kind: 'quiver-analyze-project-run-status',
|
|
450
|
+
run_id: runId,
|
|
451
|
+
status,
|
|
452
|
+
updated_at: (details.now instanceof Date ? details.now : new Date(details.now || Date.now())).toISOString(),
|
|
453
|
+
provider: details.provider || null,
|
|
454
|
+
attempts: details.attempts || [],
|
|
455
|
+
artifacts: details.artifacts || {},
|
|
456
|
+
}, null, 2)}\n`);
|
|
457
|
+
return path.relative(repoRoot, statusPath).split(path.sep).join('/');
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function normalizeAnalyzeProjectMaxRetries(value) {
|
|
461
|
+
if (value === undefined || value === null || value === '') {
|
|
462
|
+
return 1;
|
|
463
|
+
}
|
|
464
|
+
const parsed = Number(value);
|
|
465
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
466
|
+
return 1;
|
|
467
|
+
}
|
|
468
|
+
return Math.min(2, Math.floor(parsed));
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function isAnalyzeProjectRetryableError(error) {
|
|
472
|
+
if (!error || error.code !== 'AI_ANALYZE_PROJECT_INVALID') {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
const retryableIssues = new Set([
|
|
476
|
+
'empty-output',
|
|
477
|
+
'malformed-json',
|
|
478
|
+
'invalid_type',
|
|
479
|
+
'invalid_value',
|
|
480
|
+
'unrecognized_keys',
|
|
481
|
+
'missing-evidence',
|
|
482
|
+
'evidence-not-selected',
|
|
483
|
+
'unapproved-doc-update-path',
|
|
484
|
+
]);
|
|
485
|
+
const issues = Array.isArray(error.issues) ? error.issues : [];
|
|
486
|
+
return issues.length > 0 && issues.every((issue) => retryableIssues.has(issue.issue || ''));
|
|
487
|
+
}
|
|
488
|
+
|
|
400
489
|
function formatAnalyzeProjectLiveReport(report) {
|
|
401
490
|
const warningCount = report.analysis_validation?.warnings?.length || 0;
|
|
402
491
|
const docUpdatePaths = report.analysis_validation?.doc_update_paths || [];
|
|
@@ -405,6 +494,7 @@ function formatAnalyzeProjectLiveReport(report) {
|
|
|
405
494
|
`Mode: ${report.mode}`,
|
|
406
495
|
`Provider: ${report.provider}`,
|
|
407
496
|
`Provider execution: ${report.provider_execution}`,
|
|
497
|
+
`Provider attempts: ${report.provider_attempts?.length || 1}`,
|
|
408
498
|
`Writes: ${report.writes.length === 0 ? 'none' : report.writes.join(', ')}`,
|
|
409
499
|
`Privacy preflight: ${report.privacy_preflight.ok ? 'passed' : 'failed'}`,
|
|
410
500
|
`Prompt bytes: ${report.prompt.bytes}/${report.prompt.max_provider_prompt_bytes}`,
|
|
@@ -423,12 +513,46 @@ function formatAnalyzeProjectLiveReport(report) {
|
|
|
423
513
|
}
|
|
424
514
|
|
|
425
515
|
lines.push('', 'Next commands:');
|
|
516
|
+
lines.push('- npx create-quiver ai analyze-project --deep --save-proposal --provider <provider> --model <model>');
|
|
426
517
|
lines.push('- npx create-quiver ai analyze-project --deep --review');
|
|
427
518
|
lines.push('- npx create-quiver ai analyze-project --deep --json');
|
|
428
519
|
|
|
429
520
|
return `${lines.join('\n')}\n`;
|
|
430
521
|
}
|
|
431
522
|
|
|
523
|
+
function summarizeAnalyzeProjectWritePlan(writePlan = []) {
|
|
524
|
+
return writePlan.map((item) => ({
|
|
525
|
+
path: item.path,
|
|
526
|
+
action: item.action,
|
|
527
|
+
dirty: item.dirty,
|
|
528
|
+
before_sha256: item.before_sha256,
|
|
529
|
+
after_sha256: item.after_sha256,
|
|
530
|
+
reason: item.reason,
|
|
531
|
+
}));
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function formatAnalyzeProjectSavedProposalReport(report = {}) {
|
|
535
|
+
const artifacts = report.proposal_artifacts || {};
|
|
536
|
+
const changed = report.write_plan?.filter((item) => item.action !== 'skip') || [];
|
|
537
|
+
const lines = [
|
|
538
|
+
'AI analyze-project proposal saved',
|
|
539
|
+
`Run: ${report.run_id || 'unknown'}`,
|
|
540
|
+
`Provider: ${report.provider || 'unknown'}`,
|
|
541
|
+
`Writes: none (final docs were not modified)`,
|
|
542
|
+
`Proposed docs: ${report.doc_paths?.length || 0}`,
|
|
543
|
+
`Docs with changes: ${changed.length}`,
|
|
544
|
+
`Proposal JSON: ${artifacts.proposal_json || 'none'}`,
|
|
545
|
+
`Proposal summary: ${artifacts.proposal_markdown || 'none'}`,
|
|
546
|
+
`Proposal diff: ${artifacts.proposal_diff || 'none'}`,
|
|
547
|
+
`Manifest: ${artifacts.manifest || 'none'}`,
|
|
548
|
+
'',
|
|
549
|
+
'Next commands:',
|
|
550
|
+
`- npx create-quiver ai analyze-project apply --run ${report.run_id || '<run-id>'}`,
|
|
551
|
+
'- npx create-quiver ai analyze-project --deep --apply-docs --provider <provider> --model <model>',
|
|
552
|
+
];
|
|
553
|
+
return `${lines.join('\n')}\n`;
|
|
554
|
+
}
|
|
555
|
+
|
|
432
556
|
function formatAnalyzeProjectPostWriteValidation(validation) {
|
|
433
557
|
if (!validation) {
|
|
434
558
|
return [];
|
|
@@ -475,6 +599,430 @@ function formatAnalyzeProjectReviewPlan({ writePlan, reviewPath, snapshot, writt
|
|
|
475
599
|
return `${lines.join('\n')}\n`;
|
|
476
600
|
}
|
|
477
601
|
|
|
602
|
+
function formatAnalyzeProjectApplyReport(report = {}) {
|
|
603
|
+
const artifacts = report.proposal_artifacts || {};
|
|
604
|
+
const writeManifest = report.write_manifest || {};
|
|
605
|
+
const changed = report.write_plan?.filter((item) => item.action !== 'skip') || [];
|
|
606
|
+
const dirty = changed.filter((item) => item.dirty);
|
|
607
|
+
const lines = [
|
|
608
|
+
'AI analyze-project docs applied',
|
|
609
|
+
`Run: ${report.run_id || 'unknown'}`,
|
|
610
|
+
`Provider: ${report.provider || 'unknown'}`,
|
|
611
|
+
report.provider_execution ? `Provider execution: ${report.provider_execution}` : '',
|
|
612
|
+
report.apply_run ? `Saved proposal edited: ${report.saved_proposal?.proposal_edited ? 'yes' : 'no'}` : '',
|
|
613
|
+
`Writes: ${changed.length > 0 ? changed.map((item) => item.path).join(', ') : 'none'}`,
|
|
614
|
+
`Dirty target docs: ${dirty.length > 0 ? dirty.map((item) => item.path).join(', ') : 'none'}`,
|
|
615
|
+
`Written docs: ${report.written_docs?.length > 0 ? report.written_docs.join(', ') : 'none'}`,
|
|
616
|
+
`Snapshot: ${report.snapshot?.root || 'none'}`,
|
|
617
|
+
`Proposal manifest: ${artifacts.manifest || 'none'}`,
|
|
618
|
+
`Write manifest: ${writeManifest.path || 'none'}`,
|
|
619
|
+
].filter(Boolean);
|
|
620
|
+
lines.push(...formatAnalyzeProjectPostWriteValidation(report.post_write_validation));
|
|
621
|
+
return `${lines.join('\n')}\n`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function formatAnalyzeProjectApplyPreflightError(error) {
|
|
625
|
+
if (!error || error.code !== 'AI_ANALYZE_PROJECT_APPLY_BLOCKED') {
|
|
626
|
+
return error;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const issueLines = (error.issues || []).slice(0, 12).map((issue) => {
|
|
630
|
+
const hashes = issue.expected_sha256 || issue.current_sha256
|
|
631
|
+
? ` expected=${issue.expected_sha256 || 'none'} current=${issue.current_sha256 || 'none'}`
|
|
632
|
+
: '';
|
|
633
|
+
return `- ${issue.path || 'docs'}: ${issue.issue}${hashes} - ${issue.message}`;
|
|
634
|
+
});
|
|
635
|
+
const wrapped = new Error([
|
|
636
|
+
error.message,
|
|
637
|
+
'Issues:',
|
|
638
|
+
...issueLines,
|
|
639
|
+
error.apply_run
|
|
640
|
+
? 'Next safe step: inspect `.quiver/runs/<run-id>/proposal/analyze-project-doc-proposal.diff`, then rerun with an explicit run id. Add `--allow-dirty-docs` only if overwriting existing managed docs is intended.'
|
|
641
|
+
: 'Next safe step: rerun with `--review`, or inspect with `--save-proposal`, or add `--allow-dirty-docs` if overwriting existing managed docs is intended.',
|
|
642
|
+
].join('\n'));
|
|
643
|
+
wrapped.name = error.name;
|
|
644
|
+
wrapped.code = error.code;
|
|
645
|
+
wrapped.cause = error;
|
|
646
|
+
wrapped.issues = error.issues;
|
|
647
|
+
return wrapped;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function resolveAnalyzeProjectSavedProposalRunId(repoRoot, options = {}) {
|
|
651
|
+
const requestedRunId = String(options.runId || '').trim();
|
|
652
|
+
if (requestedRunId !== 'latest') {
|
|
653
|
+
return { runId: requestedRunId, requestedRunId };
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if (!canUseInteractiveTerminal(options)) {
|
|
657
|
+
throw analyzeProjectContractError(
|
|
658
|
+
'ai analyze-project apply --run latest requires an interactive terminal.',
|
|
659
|
+
'Use an explicit run id for automation: `npx create-quiver ai analyze-project apply --run <run-id>`.',
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const latestRunId = findLatestAnalyzeProjectSavedProposalRun(repoRoot);
|
|
664
|
+
if (!latestRunId) {
|
|
665
|
+
throw analyzeProjectContractError(
|
|
666
|
+
'ai analyze-project apply --run latest could not find a saved proposal.',
|
|
667
|
+
'Create one first: `npx create-quiver ai analyze-project --deep --save-proposal --provider <provider> --model <model>`.',
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (options.json !== true) {
|
|
672
|
+
process.stdout.write(`Resolved latest analyze-project proposal: ${latestRunId}\n`);
|
|
673
|
+
}
|
|
674
|
+
return { runId: latestRunId, requestedRunId };
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function analyzeProjectRawProviderArtifactPaths(rawProviderArtifacts = []) {
|
|
678
|
+
return (Array.isArray(rawProviderArtifacts) ? rawProviderArtifacts : [])
|
|
679
|
+
.map((artifact) => (typeof artifact === 'string' ? artifact : artifact?.path))
|
|
680
|
+
.filter(Boolean);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function summarizeAnalyzeProjectProposalArtifacts(proposalArtifacts = {}) {
|
|
684
|
+
return {
|
|
685
|
+
root: proposalArtifacts.root,
|
|
686
|
+
proposal_json: proposalArtifacts.proposal_json,
|
|
687
|
+
proposal_markdown: proposalArtifacts.proposal_markdown,
|
|
688
|
+
proposal_diff: proposalArtifacts.proposal_diff,
|
|
689
|
+
manifest: proposalArtifacts.manifest,
|
|
690
|
+
proposal_sha256: proposalArtifacts.proposal_sha256,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function listAnalyzeProjectProposalArtifactFiles(proposalArtifacts = {}) {
|
|
695
|
+
if (Array.isArray(proposalArtifacts.files)) {
|
|
696
|
+
return proposalArtifacts.files.filter(Boolean);
|
|
697
|
+
}
|
|
698
|
+
return [
|
|
699
|
+
proposalArtifacts.proposal_json,
|
|
700
|
+
proposalArtifacts.proposal_markdown,
|
|
701
|
+
proposalArtifacts.proposal_diff,
|
|
702
|
+
proposalArtifacts.manifest,
|
|
703
|
+
].filter(Boolean);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function saveAnalyzeProjectDocProposalReport(repoRoot, options = {}) {
|
|
707
|
+
const proposalArtifacts = writeAnalyzeProjectProposalArtifacts(repoRoot, {
|
|
708
|
+
runId: options.auditRunId,
|
|
709
|
+
now: options.commandOptions?.now || new Date(),
|
|
710
|
+
provider: options.provider,
|
|
711
|
+
language: options.commandOptions?.language,
|
|
712
|
+
proposal: options.proposal,
|
|
713
|
+
writePlan: options.writePlan,
|
|
714
|
+
selectedContextManifest: options.selectedContextManifest,
|
|
715
|
+
repairManifest: options.repairManifest,
|
|
716
|
+
events: options.interactiveAction ? [{
|
|
717
|
+
type: 'interactive-action',
|
|
718
|
+
action: options.interactiveAction,
|
|
719
|
+
at: (options.commandOptions?.now instanceof Date
|
|
720
|
+
? options.commandOptions.now
|
|
721
|
+
: new Date(options.commandOptions?.now || Date.now())).toISOString(),
|
|
722
|
+
}] : [],
|
|
723
|
+
});
|
|
724
|
+
const saveReport = {
|
|
725
|
+
...options.completedReport,
|
|
726
|
+
save_proposal: true,
|
|
727
|
+
interactive_action: options.interactiveAction || undefined,
|
|
728
|
+
doc_proposal: options.proposal,
|
|
729
|
+
doc_paths: proposalArtifacts.doc_paths,
|
|
730
|
+
proposal_artifacts: summarizeAnalyzeProjectProposalArtifacts(proposalArtifacts),
|
|
731
|
+
write_plan: summarizeAnalyzeProjectWritePlan(options.writePlan),
|
|
732
|
+
};
|
|
733
|
+
writeAnalyzeProjectRunStatus(repoRoot, options.auditRunId, 'proposal-saved', {
|
|
734
|
+
now: options.commandOptions?.now,
|
|
735
|
+
provider: options.provider,
|
|
736
|
+
attempts: options.providerAttempts,
|
|
737
|
+
artifacts: {
|
|
738
|
+
selected_context: options.selectedContextManifest,
|
|
739
|
+
raw_provider: analyzeProjectRawProviderArtifactPaths(options.rawProviderArtifacts),
|
|
740
|
+
repair: options.repairManifest,
|
|
741
|
+
retry: options.retryManifest,
|
|
742
|
+
proposal_manifest: proposalArtifacts.manifest,
|
|
743
|
+
proposal_files: proposalArtifacts.files,
|
|
744
|
+
},
|
|
745
|
+
});
|
|
746
|
+
return saveReport;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function applyAnalyzeProjectDocProposalReport(repoRoot, options = {}) {
|
|
750
|
+
let applyReport;
|
|
751
|
+
try {
|
|
752
|
+
applyReport = applyAnalyzeProjectDocProposal(repoRoot, {
|
|
753
|
+
report: options.completedReport,
|
|
754
|
+
runId: options.auditRunId,
|
|
755
|
+
now: options.commandOptions?.now || new Date(),
|
|
756
|
+
provider: options.provider,
|
|
757
|
+
language: options.commandOptions?.language,
|
|
758
|
+
proposal: options.proposal,
|
|
759
|
+
providerArtifact: options.completedReport.provider_artifact,
|
|
760
|
+
selectedContextManifest: options.selectedContextManifest,
|
|
761
|
+
repairManifest: options.repairManifest,
|
|
762
|
+
strict: options.commandOptions?.strict === true,
|
|
763
|
+
allowDirtyDocs: options.allowDirtyDocs === true,
|
|
764
|
+
summarizeWritePlan: summarizeAnalyzeProjectWritePlan,
|
|
765
|
+
});
|
|
766
|
+
} catch (error) {
|
|
767
|
+
const proposalArtifacts = error.proposal_artifacts || {};
|
|
768
|
+
writeAnalyzeProjectRunStatus(repoRoot, options.auditRunId, error.code === 'AI_ANALYZE_PROJECT_APPLY_BLOCKED' ? 'apply-blocked' : 'apply-failed', {
|
|
769
|
+
now: options.commandOptions?.now,
|
|
770
|
+
provider: options.provider,
|
|
771
|
+
attempts: options.providerAttempts,
|
|
772
|
+
artifacts: {
|
|
773
|
+
selected_context: options.selectedContextManifest,
|
|
774
|
+
raw_provider: analyzeProjectRawProviderArtifactPaths(options.rawProviderArtifacts),
|
|
775
|
+
repair: options.repairManifest,
|
|
776
|
+
retry: options.retryManifest,
|
|
777
|
+
proposal_manifest: proposalArtifacts.manifest || null,
|
|
778
|
+
proposal_files: listAnalyzeProjectProposalArtifactFiles(proposalArtifacts),
|
|
779
|
+
write_manifest: error.write_manifest?.path || null,
|
|
780
|
+
snapshot_manifest: error.snapshot?.manifestPath || null,
|
|
781
|
+
},
|
|
782
|
+
});
|
|
783
|
+
throw formatAnalyzeProjectApplyPreflightError(error);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
applyReport = {
|
|
787
|
+
...applyReport,
|
|
788
|
+
interactive_action: options.interactiveAction || applyReport.interactive_action,
|
|
789
|
+
};
|
|
790
|
+
writeAnalyzeProjectRunStatus(repoRoot, options.auditRunId, applyReport.post_write_validation?.ok ? 'docs-applied' : 'apply-validation-failed', {
|
|
791
|
+
now: options.commandOptions?.now,
|
|
792
|
+
provider: options.provider,
|
|
793
|
+
attempts: options.providerAttempts,
|
|
794
|
+
artifacts: {
|
|
795
|
+
selected_context: options.selectedContextManifest,
|
|
796
|
+
raw_provider: analyzeProjectRawProviderArtifactPaths(options.rawProviderArtifacts),
|
|
797
|
+
repair: options.repairManifest,
|
|
798
|
+
retry: options.retryManifest,
|
|
799
|
+
proposal_manifest: applyReport.proposal_artifacts?.manifest,
|
|
800
|
+
proposal_files: listAnalyzeProjectProposalArtifactFiles(applyReport.proposal_artifacts),
|
|
801
|
+
write_manifest: applyReport.write_manifest?.path,
|
|
802
|
+
snapshot_manifest: applyReport.snapshot?.manifestPath,
|
|
803
|
+
},
|
|
804
|
+
});
|
|
805
|
+
return applyReport;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function applyAnalyzeProjectSavedProposalRun(repoRoot, options = {}) {
|
|
809
|
+
const { runId, requestedRunId } = resolveAnalyzeProjectSavedProposalRunId(repoRoot, options);
|
|
810
|
+
const now = options.now instanceof Date ? options.now : new Date(options.now || Date.now());
|
|
811
|
+
const saved = readAnalyzeProjectSavedProposal(repoRoot, runId);
|
|
812
|
+
const provider = saved.manifest.provider || null;
|
|
813
|
+
const runStatusPath = path.join('.quiver', 'runs', runId, 'status.json').split(path.sep).join('/');
|
|
814
|
+
const baseReport = {
|
|
815
|
+
schema_version: 1,
|
|
816
|
+
kind: 'quiver-project-analysis-apply-saved-proposal',
|
|
817
|
+
command: 'ai analyze-project apply',
|
|
818
|
+
mode: 'apply-saved-proposal',
|
|
819
|
+
apply_run: true,
|
|
820
|
+
requested_run_id: requestedRunId,
|
|
821
|
+
run_id: runId,
|
|
822
|
+
provider,
|
|
823
|
+
provider_execution: 'skipped',
|
|
824
|
+
writes: [],
|
|
825
|
+
doc_proposal: saved.proposal,
|
|
826
|
+
saved_proposal: {
|
|
827
|
+
requested_run_id: requestedRunId,
|
|
828
|
+
run_id: runId,
|
|
829
|
+
manifest: saved.artifacts.manifest,
|
|
830
|
+
proposal_json: saved.artifacts.proposal_json,
|
|
831
|
+
proposal_sha256: saved.proposal_sha256,
|
|
832
|
+
proposal_edited: saved.proposal_edited,
|
|
833
|
+
},
|
|
834
|
+
run_status_path: runStatusPath,
|
|
835
|
+
};
|
|
836
|
+
const events = [
|
|
837
|
+
{
|
|
838
|
+
type: 'saved-proposal-apply',
|
|
839
|
+
at: now.toISOString(),
|
|
840
|
+
requested_run_id: requestedRunId,
|
|
841
|
+
run_id: runId,
|
|
842
|
+
},
|
|
843
|
+
];
|
|
844
|
+
if (saved.proposal_edited) {
|
|
845
|
+
events.push({
|
|
846
|
+
type: 'saved-proposal-edited',
|
|
847
|
+
at: now.toISOString(),
|
|
848
|
+
original_sha256: saved.manifest.proposal_sha256,
|
|
849
|
+
current_sha256: saved.proposal_sha256,
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
let applyReport;
|
|
854
|
+
try {
|
|
855
|
+
applyReport = applyAnalyzeProjectDocProposal(repoRoot, {
|
|
856
|
+
report: baseReport,
|
|
857
|
+
runId,
|
|
858
|
+
now,
|
|
859
|
+
provider,
|
|
860
|
+
language: saved.manifest.language,
|
|
861
|
+
proposal: saved.proposal,
|
|
862
|
+
proposalArtifacts: saved.artifacts,
|
|
863
|
+
proposalManifest: saved.manifest,
|
|
864
|
+
proposalEdited: saved.proposal_edited,
|
|
865
|
+
strict: options.strict === true,
|
|
866
|
+
allowDirtyDocs: options.allowDirtyDocs === true,
|
|
867
|
+
summarizeWritePlan: summarizeAnalyzeProjectWritePlan,
|
|
868
|
+
events,
|
|
869
|
+
});
|
|
870
|
+
} catch (error) {
|
|
871
|
+
error.apply_run = true;
|
|
872
|
+
const proposalArtifacts = error.proposal_artifacts || saved.artifacts || {};
|
|
873
|
+
writeAnalyzeProjectRunStatus(repoRoot, runId, error.code === 'AI_ANALYZE_PROJECT_APPLY_BLOCKED' ? 'apply-blocked' : 'apply-failed', {
|
|
874
|
+
now,
|
|
875
|
+
provider,
|
|
876
|
+
attempts: [],
|
|
877
|
+
artifacts: {
|
|
878
|
+
proposal_manifest: proposalArtifacts.manifest || saved.artifacts.manifest,
|
|
879
|
+
proposal_files: listAnalyzeProjectProposalArtifactFiles(proposalArtifacts),
|
|
880
|
+
write_manifest: error.write_manifest?.path || null,
|
|
881
|
+
snapshot_manifest: error.snapshot?.manifestPath || null,
|
|
882
|
+
},
|
|
883
|
+
});
|
|
884
|
+
throw formatAnalyzeProjectApplyPreflightError(error);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
applyReport = {
|
|
888
|
+
...applyReport,
|
|
889
|
+
apply_run: true,
|
|
890
|
+
requested_run_id: requestedRunId,
|
|
891
|
+
saved_proposal: baseReport.saved_proposal,
|
|
892
|
+
run_status_path: runStatusPath,
|
|
893
|
+
};
|
|
894
|
+
writeAnalyzeProjectRunStatus(repoRoot, runId, applyReport.post_write_validation?.ok ? 'docs-applied' : 'apply-validation-failed', {
|
|
895
|
+
now,
|
|
896
|
+
provider,
|
|
897
|
+
attempts: [],
|
|
898
|
+
artifacts: {
|
|
899
|
+
proposal_manifest: applyReport.proposal_artifacts?.manifest,
|
|
900
|
+
proposal_files: listAnalyzeProjectProposalArtifactFiles(applyReport.proposal_artifacts),
|
|
901
|
+
write_manifest: applyReport.write_manifest?.path,
|
|
902
|
+
snapshot_manifest: applyReport.snapshot?.manifestPath,
|
|
903
|
+
},
|
|
904
|
+
});
|
|
905
|
+
return emitAnalyzeProjectApplyReport(applyReport, options);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
function emitAnalyzeProjectApplyReport(applyReport, options = {}) {
|
|
909
|
+
if (options.json === true) {
|
|
910
|
+
process.stdout.write(`${JSON.stringify(applyReport, null, 2)}\n`);
|
|
911
|
+
} else {
|
|
912
|
+
process.stdout.write(formatAnalyzeProjectApplyReport(applyReport));
|
|
913
|
+
}
|
|
914
|
+
if (!applyReport.post_write_validation?.ok) {
|
|
915
|
+
const error = new Error(formatError('ai analyze-project post-write validation failed.'));
|
|
916
|
+
error.code = 'AI_ANALYZE_PROJECT_POST_WRITE_VALIDATION_FAILED';
|
|
917
|
+
error.validation = applyReport.post_write_validation;
|
|
918
|
+
throw error;
|
|
919
|
+
}
|
|
920
|
+
return applyReport;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
async function reviewAndWriteAnalyzeProjectDocs(repoRoot, options = {}) {
|
|
924
|
+
const commandOptions = options.commandOptions || {};
|
|
925
|
+
const reviewed = await reviewAnalyzeProjectDocProposal(repoRoot, options.initialProposal, commandOptions);
|
|
926
|
+
const writePlan = buildAnalyzeProjectWritePlan(repoRoot, reviewed.proposal);
|
|
927
|
+
process.stdout.write(formatAnalyzeProjectReviewPlan({
|
|
928
|
+
writePlan,
|
|
929
|
+
reviewPath: reviewed.reviewPath,
|
|
930
|
+
}));
|
|
931
|
+
await confirmAnalyzeProjectWrites(writePlan, commandOptions);
|
|
932
|
+
const lifecycleRun = ensureAiRun(repoRoot, {
|
|
933
|
+
command: 'ai analyze-project',
|
|
934
|
+
input: reviewed.reviewPath,
|
|
935
|
+
runId: commandOptions.runId,
|
|
936
|
+
phase: 'created',
|
|
937
|
+
});
|
|
938
|
+
const snapshot = createAnalyzeProjectSnapshot(repoRoot, lifecycleRun, writePlan, {
|
|
939
|
+
providerArtifact: options.completedReport.provider_artifact,
|
|
940
|
+
proposal: reviewed.proposal,
|
|
941
|
+
now: commandOptions.now || new Date(),
|
|
942
|
+
});
|
|
943
|
+
const writtenDocs = writeAnalyzeProjectDocs(writePlan);
|
|
944
|
+
let writeReport = {
|
|
945
|
+
...options.completedReport,
|
|
946
|
+
review: true,
|
|
947
|
+
interactive_action: options.interactiveAction || undefined,
|
|
948
|
+
review_path: reviewed.reviewPath,
|
|
949
|
+
doc_proposal: reviewed.proposal,
|
|
950
|
+
write_plan: summarizeAnalyzeProjectWritePlan(writePlan),
|
|
951
|
+
snapshot,
|
|
952
|
+
written_docs: writtenDocs,
|
|
953
|
+
run_id: lifecycleRun.run_id,
|
|
954
|
+
};
|
|
955
|
+
const postWriteValidation = validateAnalyzeProjectPostWrite(repoRoot, writeReport, {
|
|
956
|
+
strict: commandOptions.strict === true,
|
|
957
|
+
});
|
|
958
|
+
writeReport = {
|
|
959
|
+
...writeReport,
|
|
960
|
+
post_write_validation: postWriteValidation,
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
if (commandOptions.json === true) {
|
|
964
|
+
process.stdout.write(`${JSON.stringify(writeReport, null, 2)}\n`);
|
|
965
|
+
if (!postWriteValidation.ok) {
|
|
966
|
+
const error = new Error(formatError('ai analyze-project post-write validation failed.'));
|
|
967
|
+
error.code = 'AI_ANALYZE_PROJECT_POST_WRITE_VALIDATION_FAILED';
|
|
968
|
+
error.validation = postWriteValidation;
|
|
969
|
+
throw error;
|
|
970
|
+
}
|
|
971
|
+
return writeReport;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
process.stdout.write(formatAnalyzeProjectReviewPlan({
|
|
975
|
+
writePlan,
|
|
976
|
+
reviewPath: reviewed.reviewPath,
|
|
977
|
+
snapshot,
|
|
978
|
+
writtenDocs,
|
|
979
|
+
validation: postWriteValidation,
|
|
980
|
+
completed: true,
|
|
981
|
+
}));
|
|
982
|
+
if (!postWriteValidation.ok) {
|
|
983
|
+
const error = new Error(formatError('ai analyze-project post-write validation failed.'));
|
|
984
|
+
error.code = 'AI_ANALYZE_PROJECT_POST_WRITE_VALIDATION_FAILED';
|
|
985
|
+
error.validation = postWriteValidation;
|
|
986
|
+
throw error;
|
|
987
|
+
}
|
|
988
|
+
return writeReport;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
function cancelAnalyzeProjectInteractiveApplyReport(repoRoot, options = {}) {
|
|
992
|
+
const translator = createTranslator(options.commandOptions?.language);
|
|
993
|
+
const savedArtifacts = options.savedReport?.proposal_artifacts || null;
|
|
994
|
+
const cancelReport = {
|
|
995
|
+
...options.completedReport,
|
|
996
|
+
apply_docs: true,
|
|
997
|
+
interactive_action: options.interactiveAction || 'cancel',
|
|
998
|
+
cancelled: true,
|
|
999
|
+
writes: [],
|
|
1000
|
+
written_docs: [],
|
|
1001
|
+
doc_proposal: options.proposal,
|
|
1002
|
+
proposal_artifacts: savedArtifacts || undefined,
|
|
1003
|
+
write_plan: summarizeAnalyzeProjectWritePlan(options.writePlan),
|
|
1004
|
+
};
|
|
1005
|
+
writeAnalyzeProjectRunStatus(repoRoot, options.auditRunId, 'apply-canceled', {
|
|
1006
|
+
now: options.commandOptions?.now,
|
|
1007
|
+
provider: options.provider,
|
|
1008
|
+
attempts: options.providerAttempts,
|
|
1009
|
+
artifacts: {
|
|
1010
|
+
selected_context: options.selectedContextManifest,
|
|
1011
|
+
raw_provider: analyzeProjectRawProviderArtifactPaths(options.rawProviderArtifacts),
|
|
1012
|
+
repair: options.repairManifest,
|
|
1013
|
+
retry: options.retryManifest,
|
|
1014
|
+
proposal_manifest: savedArtifacts?.manifest || null,
|
|
1015
|
+
proposal_files: savedArtifacts ? listAnalyzeProjectProposalArtifactFiles(savedArtifacts) : [],
|
|
1016
|
+
},
|
|
1017
|
+
});
|
|
1018
|
+
process.stdout.write([
|
|
1019
|
+
translator.t('ai.analyze_project.apply.cancelled'),
|
|
1020
|
+
translator.t('ai.analyze_project.apply.cancelled_writes'),
|
|
1021
|
+
'',
|
|
1022
|
+
].join('\n'));
|
|
1023
|
+
return cancelReport;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
478
1026
|
function formatLocalizedActionableError({ failure, impact, fix, nextCommand } = {}, options = {}) {
|
|
479
1027
|
const translator = createTranslator(options.language);
|
|
480
1028
|
const lines = [`create-quiver: ${String(failure || 'operation failed').trim()}`];
|
|
@@ -492,6 +1040,84 @@ function formatLocalizedActionableError({ failure, impact, fix, nextCommand } =
|
|
|
492
1040
|
return lines.join('\n');
|
|
493
1041
|
}
|
|
494
1042
|
|
|
1043
|
+
function analyzeProjectContractError(message, nextCommand) {
|
|
1044
|
+
const error = new Error(formatError([
|
|
1045
|
+
message,
|
|
1046
|
+
nextCommand ? `Next safe step: ${nextCommand}` : '',
|
|
1047
|
+
].filter(Boolean).join('\n')));
|
|
1048
|
+
error.code = 'AI_ANALYZE_PROJECT_CONTRACT_UNAVAILABLE';
|
|
1049
|
+
return error;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
function canUseInteractiveTerminal(options = {}) {
|
|
1053
|
+
return options.stdinIsTTY === true || (options.stdinIsTTY !== false && Boolean(process.stdin.isTTY));
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
function assertAnalyzeProjectCommandContract(options = {}) {
|
|
1057
|
+
if (options.review === true && options.applyDocs === true) {
|
|
1058
|
+
throw analyzeProjectContractError(
|
|
1059
|
+
'ai analyze-project --apply-docs cannot be combined with --review.',
|
|
1060
|
+
'Use either `npx create-quiver ai analyze-project --deep --review` or `npx create-quiver ai analyze-project --deep --apply-docs`.',
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (options.dryRun === true && options.applyDocs === true) {
|
|
1065
|
+
throw analyzeProjectContractError(
|
|
1066
|
+
'ai analyze-project --dry-run cannot be combined with --apply-docs.',
|
|
1067
|
+
'Preview with `npx create-quiver ai analyze-project --deep --dry-run`, then rerun without --dry-run.',
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (options.dryRun === true && options.saveProposal === true) {
|
|
1072
|
+
throw analyzeProjectContractError(
|
|
1073
|
+
'ai analyze-project --dry-run cannot be combined with --save-proposal.',
|
|
1074
|
+
'Preview with `npx create-quiver ai analyze-project --deep --dry-run`, then rerun with --save-proposal.',
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
if (options.applyRun === true && options.dryRun === true) {
|
|
1079
|
+
throw analyzeProjectContractError(
|
|
1080
|
+
'ai analyze-project apply --run cannot be combined with --dry-run.',
|
|
1081
|
+
'Inspect the saved proposal under `.quiver/runs/<run-id>/proposal/`, then rerun without --dry-run.',
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
if (options.applyRun === true && (options.applyDocs === true || options.saveProposal === true || options.review === true)) {
|
|
1086
|
+
throw analyzeProjectContractError(
|
|
1087
|
+
'ai analyze-project apply --run cannot be combined with --apply-docs, --save-proposal, or --review.',
|
|
1088
|
+
'Use `npx create-quiver ai analyze-project apply --run <run-id>` to apply a saved proposal, or rerun `ai analyze-project --deep` for a fresh proposal.',
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
if (options.json === true && options.applyDocs === true && options.force !== true) {
|
|
1093
|
+
throw analyzeProjectContractError(
|
|
1094
|
+
'ai analyze-project --json with --apply-docs requires --yes because JSON output cannot include an interactive selector.',
|
|
1095
|
+
'Use `npx create-quiver ai analyze-project --deep --apply-docs --yes --json` for automation.',
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
if (options.applyRun === true && !String(options.runId || '').trim()) {
|
|
1100
|
+
throw analyzeProjectContractError(
|
|
1101
|
+
'ai analyze-project apply requires --run <run-id>.',
|
|
1102
|
+
'Use `npx create-quiver ai analyze-project apply --run <run-id>`.',
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
if (options.applyRun === true && String(options.runId || '').trim() === 'latest' && options.force === true) {
|
|
1107
|
+
throw analyzeProjectContractError(
|
|
1108
|
+
'ai analyze-project apply --run latest cannot be combined with --yes.',
|
|
1109
|
+
'Use an explicit run id for automation: `npx create-quiver ai analyze-project apply --run <run-id> --yes`.',
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
if (options.applyRun === true && String(options.runId || '').trim() === 'latest' && options.json === true) {
|
|
1114
|
+
throw analyzeProjectContractError(
|
|
1115
|
+
'ai analyze-project apply --run latest cannot be combined with --json.',
|
|
1116
|
+
'Use an explicit run id for JSON automation: `npx create-quiver ai analyze-project apply --run <run-id> --json`.',
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
495
1121
|
function readTextFile(filePath, repoRoot) {
|
|
496
1122
|
if (!filePath) {
|
|
497
1123
|
return '';
|
|
@@ -609,7 +1235,8 @@ function shouldShowHumanProgress(ux, options = {}) {
|
|
|
609
1235
|
return options.progress !== false
|
|
610
1236
|
&& options.dryRun !== true
|
|
611
1237
|
&& options.printPrompt !== true
|
|
612
|
-
&& ux?.mode?.
|
|
1238
|
+
&& ux?.mode?.json !== true
|
|
1239
|
+
&& (ux?.mode?.decoration === true || options.linearProgress === true);
|
|
613
1240
|
}
|
|
614
1241
|
|
|
615
1242
|
function plannerProgressTitle(action, runtimeProfile, options = {}) {
|
|
@@ -630,10 +1257,32 @@ function writeProgressChecks(ux, enabled, title, checks = []) {
|
|
|
630
1257
|
}
|
|
631
1258
|
}
|
|
632
1259
|
|
|
633
|
-
|
|
1260
|
+
function writeProgressCheck(ux, enabled, check) {
|
|
1261
|
+
if (!enabled || !check) {
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
ux.check(check);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
function writeProgressInfo(ux, enabled, message) {
|
|
1268
|
+
if (!enabled || !message) {
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
ux.info(message);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
async function runProviderWithProgress({
|
|
1275
|
+
ux,
|
|
1276
|
+
enabled,
|
|
1277
|
+
message = 'Running agent...',
|
|
1278
|
+
successMessage = 'Agent finished',
|
|
1279
|
+
failureMessage = 'Agent failed',
|
|
1280
|
+
failOnProviderResult = true,
|
|
1281
|
+
run,
|
|
1282
|
+
}) {
|
|
634
1283
|
async function runAndFailOnProviderResult() {
|
|
635
1284
|
const result = await run();
|
|
636
|
-
if (result && result.ok === false) {
|
|
1285
|
+
if (failOnProviderResult && result && result.ok === false) {
|
|
637
1286
|
const error = new Error(result.error?.message || 'provider run failed');
|
|
638
1287
|
error.code = result.error?.code || 'AI_PROVIDER_RUN_FAILED';
|
|
639
1288
|
error.providerResult = result;
|
|
@@ -1909,6 +2558,33 @@ async function runPrepareContext(repoRoot, options = {}) {
|
|
|
1909
2558
|
}
|
|
1910
2559
|
|
|
1911
2560
|
async function runAnalyzeProject(repoRoot, options = {}) {
|
|
2561
|
+
assertAnalyzeProjectCommandContract(options);
|
|
2562
|
+
if (options.applyRun === true) {
|
|
2563
|
+
return applyAnalyzeProjectSavedProposalRun(repoRoot, options);
|
|
2564
|
+
}
|
|
2565
|
+
if (options.applyDocs === true && options.force !== true && !canUseAnalyzeProjectInteractiveSelector(options)) {
|
|
2566
|
+
throw analyzeProjectContractError(
|
|
2567
|
+
'ai analyze-project --apply-docs requires an interactive TTY unless --yes is passed. No provider was run and no files were written.',
|
|
2568
|
+
'Use `npx create-quiver ai analyze-project --deep --apply-docs --yes --provider <provider> --model <model>` for automation, or rerun from an interactive terminal.',
|
|
2569
|
+
);
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
const role = normalizeRole(options.role || DEFAULT_PLAN_ROLE);
|
|
2573
|
+
const runtimeProfile = resolveRuntimeAgentProfile(repoRoot, role, options, DEFAULT_PLAN_PROVIDER);
|
|
2574
|
+
const provider = runtimeProfile.provider;
|
|
2575
|
+
const ux = createCommandUx(options);
|
|
2576
|
+
const showProgress = shouldShowHumanProgress(ux, { ...options, linearProgress: true });
|
|
2577
|
+
const progressTranslator = createTranslator(options.language);
|
|
2578
|
+
const progressTitle = plannerProgressTitle(
|
|
2579
|
+
progressTranslator.t('ai.planner.progress.analyze_project'),
|
|
2580
|
+
runtimeProfile,
|
|
2581
|
+
options,
|
|
2582
|
+
);
|
|
2583
|
+
|
|
2584
|
+
if (showProgress) {
|
|
2585
|
+
ux.heading(progressTitle);
|
|
2586
|
+
}
|
|
2587
|
+
|
|
1912
2588
|
const report = buildAnalyzeProjectReport(repoRoot, options);
|
|
1913
2589
|
if (options.dryRun === true) {
|
|
1914
2590
|
if (options.json === true) {
|
|
@@ -1919,13 +2595,10 @@ async function runAnalyzeProject(repoRoot, options = {}) {
|
|
|
1919
2595
|
return report;
|
|
1920
2596
|
}
|
|
1921
2597
|
|
|
1922
|
-
const role = normalizeRole(options.role || DEFAULT_PLAN_ROLE);
|
|
1923
|
-
const runtimeProfile = resolveRuntimeAgentProfile(repoRoot, role, options, DEFAULT_PLAN_PROVIDER);
|
|
1924
|
-
const provider = runtimeProfile.provider;
|
|
1925
2598
|
const timeoutMs = normalizeTimeout(options.timeout);
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
2599
|
+
writeProgressCheck(ux, showProgress, progressTranslator.t('ai.planner.progress.reading_base_docs'));
|
|
2600
|
+
writeProgressCheck(ux, showProgress, progressTranslator.t('ai.planner.progress.detecting_structure'));
|
|
2601
|
+
writeProgressCheck(ux, showProgress, progressTranslator.t('ai.planner.progress.selecting_sample'));
|
|
1929
2602
|
const promptPackage = buildAnalyzeProjectPrompt({
|
|
1930
2603
|
analysisPlan: report,
|
|
1931
2604
|
repoRoot,
|
|
@@ -1935,17 +2608,8 @@ async function runAnalyzeProject(repoRoot, options = {}) {
|
|
|
1935
2608
|
const prompt = promptPackage.prompt;
|
|
1936
2609
|
const promptLimit = assertProviderPromptWithinLimit(prompt, options.promptLimitOptions || {});
|
|
1937
2610
|
const privacyPreflight = promptPackage.privacyPreflight;
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
ux,
|
|
1941
|
-
showProgress,
|
|
1942
|
-
plannerProgressTitle(progressTranslator.t('ai.planner.progress.analyze_project'), runtimeProfile, options),
|
|
1943
|
-
[
|
|
1944
|
-
progressTranslator.t('ai.planner.progress.reading_base_docs'),
|
|
1945
|
-
progressTranslator.t('ai.planner.progress.detecting_structure'),
|
|
1946
|
-
progressTranslator.t('ai.planner.progress.preparing_prompt'),
|
|
1947
|
-
],
|
|
1948
|
-
);
|
|
2611
|
+
const auditRunId = options.runId || createAnalyzeProjectAuditRunId(options.now || new Date());
|
|
2612
|
+
writeProgressCheck(ux, showProgress, progressTranslator.t('ai.planner.progress.preparing_prompt'));
|
|
1949
2613
|
|
|
1950
2614
|
if (!privacyPreflight.ok) {
|
|
1951
2615
|
const error = new Error(formatError('ai analyze-project privacy preflight failed; provider execution was blocked before sending repository content.'));
|
|
@@ -1983,45 +2647,215 @@ async function runAnalyzeProject(repoRoot, options = {}) {
|
|
|
1983
2647
|
};
|
|
1984
2648
|
}
|
|
1985
2649
|
|
|
2650
|
+
const selectedContextManifest = writeSelectedContextManifest(
|
|
2651
|
+
repoRoot,
|
|
2652
|
+
buildSelectedContextManifest({
|
|
2653
|
+
analysisPlan: report,
|
|
2654
|
+
promptPackage,
|
|
2655
|
+
promptLimit,
|
|
2656
|
+
provider,
|
|
2657
|
+
runId: auditRunId,
|
|
2658
|
+
now: options.now,
|
|
2659
|
+
}),
|
|
2660
|
+
{
|
|
2661
|
+
runId: auditRunId,
|
|
2662
|
+
now: options.now,
|
|
2663
|
+
},
|
|
2664
|
+
);
|
|
2665
|
+
|
|
2666
|
+
const maxRetries = normalizeAnalyzeProjectMaxRetries(options.maxAnalyzeProjectRetries);
|
|
2667
|
+
const providerAttempts = [];
|
|
1986
2668
|
let result;
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
failureMessage: progressTranslator.t('ai.planner.progress.agent_failed'),
|
|
1994
|
-
run: () => (options.runProviderFn || runProvider)(provider, {
|
|
1995
|
-
prompt,
|
|
1996
|
-
cwd: repoRoot,
|
|
1997
|
-
timeoutMs,
|
|
1998
|
-
dryRun: false,
|
|
1999
|
-
probe: options.probe,
|
|
2000
|
-
spawn: options.spawn,
|
|
2001
|
-
tempRoot: options.tempRoot,
|
|
2002
|
-
tempFileName: options.tempFileName,
|
|
2003
|
-
tempFilePrefix: options.tempFilePrefix,
|
|
2004
|
-
...runtimeModelExecutionOptions(runtimeProfile, options),
|
|
2005
|
-
enforceModelSelection: false,
|
|
2006
|
-
}),
|
|
2007
|
-
});
|
|
2008
|
-
} catch (error) {
|
|
2009
|
-
throw annotateProviderError(error, 'analyze-project');
|
|
2010
|
-
}
|
|
2669
|
+
let clean;
|
|
2670
|
+
let parsed;
|
|
2671
|
+
let repairManifest = null;
|
|
2672
|
+
let retryManifest = null;
|
|
2673
|
+
let currentPrompt = prompt;
|
|
2674
|
+
const rawProviderArtifacts = [];
|
|
2011
2675
|
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2676
|
+
for (let attemptIndex = 0; attemptIndex <= maxRetries; attemptIndex += 1) {
|
|
2677
|
+
try {
|
|
2678
|
+
result = await runProviderWithProgress({
|
|
2679
|
+
ux,
|
|
2680
|
+
enabled: showProgress,
|
|
2681
|
+
message: attemptIndex === 0
|
|
2682
|
+
? progressTranslator.t('ai.planner.progress.running_agent')
|
|
2683
|
+
: `Retrying agent (${attemptIndex}/${maxRetries})...`,
|
|
2684
|
+
successMessage: progressTranslator.t('ai.planner.progress.agent_finished'),
|
|
2685
|
+
failureMessage: progressTranslator.t('ai.planner.progress.agent_failed'),
|
|
2686
|
+
failOnProviderResult: false,
|
|
2687
|
+
run: () => (options.runProviderFn || runProvider)(provider, {
|
|
2688
|
+
prompt: currentPrompt,
|
|
2689
|
+
cwd: repoRoot,
|
|
2690
|
+
timeoutMs,
|
|
2691
|
+
dryRun: false,
|
|
2692
|
+
probe: options.probe,
|
|
2693
|
+
spawn: options.spawn,
|
|
2694
|
+
tempRoot: options.tempRoot,
|
|
2695
|
+
tempFileName: options.tempFileName,
|
|
2696
|
+
tempFilePrefix: options.tempFilePrefix,
|
|
2697
|
+
...runtimeModelExecutionOptions(runtimeProfile, options),
|
|
2698
|
+
enforceModelSelection: false,
|
|
2699
|
+
}),
|
|
2700
|
+
});
|
|
2701
|
+
} catch (error) {
|
|
2702
|
+
throw annotateProviderError(error, 'analyze-project');
|
|
2703
|
+
}
|
|
2015
2704
|
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
});
|
|
2023
|
-
|
|
2024
|
-
|
|
2705
|
+
rawProviderArtifacts.push(writeRawProviderArtifact(repoRoot, auditRunId, `analyze-project-provider-attempt-${attemptIndex + 1}`, result, {
|
|
2706
|
+
now: options.now,
|
|
2707
|
+
metadata: {
|
|
2708
|
+
attempt: attemptIndex + 1,
|
|
2709
|
+
retry: attemptIndex > 0,
|
|
2710
|
+
},
|
|
2711
|
+
}));
|
|
2712
|
+
writeProgressCheck(ux, showProgress, progressTranslator.t('ai.planner.progress.writing_artifacts'));
|
|
2713
|
+
if (!result.ok) {
|
|
2714
|
+
writeProgressInfo(ux, showProgress, progressTranslator.t('ai.planner.progress.agent_failed'));
|
|
2715
|
+
writeAnalyzeProjectRunStatus(repoRoot, auditRunId, 'failed', {
|
|
2716
|
+
now: options.now,
|
|
2717
|
+
provider,
|
|
2718
|
+
attempts: [{
|
|
2719
|
+
attempt: attemptIndex + 1,
|
|
2720
|
+
status: 'provider-failed',
|
|
2721
|
+
retry: attemptIndex > 0,
|
|
2722
|
+
}],
|
|
2723
|
+
artifacts: {
|
|
2724
|
+
selected_context: selectedContextManifest,
|
|
2725
|
+
raw_provider: rawProviderArtifacts.map((artifact) => artifact.path),
|
|
2726
|
+
},
|
|
2727
|
+
});
|
|
2728
|
+
throw annotateProviderError(result.error || new Error('provider run failed'), 'analyze-project');
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
clean = extractCleanProviderOutput(result, { prompt: currentPrompt, projectRoot: repoRoot });
|
|
2732
|
+
try {
|
|
2733
|
+
parsed = parseAnalyzeProjectOutputWithRepair(clean.cleanOutput, {
|
|
2734
|
+
selectedFiles: report.selected_files,
|
|
2735
|
+
promptFiles: promptPackage.files,
|
|
2736
|
+
});
|
|
2737
|
+
writeProgressCheck(ux, showProgress, progressTranslator.t('ai.planner.progress.validating_schema'));
|
|
2738
|
+
if (parsed.repairManifest) {
|
|
2739
|
+
repairManifest = writeAnalyzeProjectRepairManifest(repoRoot, parsed.repairManifest, {
|
|
2740
|
+
runId: auditRunId,
|
|
2741
|
+
now: options.now,
|
|
2742
|
+
});
|
|
2743
|
+
writeProgressCheck(ux, showProgress, progressTranslator.t('ai.planner.progress.repairing_schema'));
|
|
2744
|
+
}
|
|
2745
|
+
providerAttempts.push({
|
|
2746
|
+
attempt: attemptIndex + 1,
|
|
2747
|
+
status: 'valid',
|
|
2748
|
+
retry: attemptIndex > 0,
|
|
2749
|
+
parse_source: parsed.parseSource,
|
|
2750
|
+
repaired: parsed.repaired === true,
|
|
2751
|
+
repair_manifest: repairManifest,
|
|
2752
|
+
});
|
|
2753
|
+
if (providerAttempts.length > 1) {
|
|
2754
|
+
retryManifest = writeAnalyzeProjectRetryManifest(repoRoot, {
|
|
2755
|
+
provider,
|
|
2756
|
+
command: 'ai analyze-project',
|
|
2757
|
+
runId: auditRunId,
|
|
2758
|
+
now: options.now,
|
|
2759
|
+
maxRetries,
|
|
2760
|
+
finalStatus: 'valid',
|
|
2761
|
+
attempts: providerAttempts,
|
|
2762
|
+
});
|
|
2763
|
+
}
|
|
2764
|
+
writeAnalyzeProjectRunStatus(repoRoot, auditRunId, 'completed', {
|
|
2765
|
+
now: options.now,
|
|
2766
|
+
provider,
|
|
2767
|
+
attempts: providerAttempts,
|
|
2768
|
+
artifacts: {
|
|
2769
|
+
selected_context: selectedContextManifest,
|
|
2770
|
+
raw_provider: rawProviderArtifacts.map((artifact) => artifact.path),
|
|
2771
|
+
repair: repairManifest,
|
|
2772
|
+
retry: retryManifest,
|
|
2773
|
+
},
|
|
2774
|
+
});
|
|
2775
|
+
break;
|
|
2776
|
+
} catch (error) {
|
|
2777
|
+
let attemptRepairManifest = null;
|
|
2778
|
+
if (error.repair_manifest) {
|
|
2779
|
+
try {
|
|
2780
|
+
attemptRepairManifest = writeAnalyzeProjectRepairManifest(repoRoot, error.repair_manifest, {
|
|
2781
|
+
runId: auditRunId,
|
|
2782
|
+
now: options.now,
|
|
2783
|
+
});
|
|
2784
|
+
} catch {
|
|
2785
|
+
attemptRepairManifest = null;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
const retryable = isAnalyzeProjectRetryableError(error);
|
|
2789
|
+
providerAttempts.push({
|
|
2790
|
+
attempt: attemptIndex + 1,
|
|
2791
|
+
status: 'invalid',
|
|
2792
|
+
retry: attemptIndex > 0,
|
|
2793
|
+
retryable,
|
|
2794
|
+
issue_count: Array.isArray(error.issues) ? error.issues.length : 0,
|
|
2795
|
+
groups: groupAnalyzeProjectIssues(error.issues, { maxExamplesPerGroup: 2 }),
|
|
2796
|
+
repair_manifest: attemptRepairManifest,
|
|
2797
|
+
});
|
|
2798
|
+
|
|
2799
|
+
if (retryable && attemptIndex < maxRetries) {
|
|
2800
|
+
writeProgressInfo(ux, showProgress, progressTranslator.t('ai.planner.progress.retrying_agent'));
|
|
2801
|
+
currentPrompt = buildAnalyzeProjectRetryPrompt({
|
|
2802
|
+
previousOutput: clean.cleanOutput,
|
|
2803
|
+
issueLines: formatAnalyzeProjectIssues(error.issues, 6),
|
|
2804
|
+
attempt: attemptIndex + 1,
|
|
2805
|
+
maxRetries,
|
|
2806
|
+
});
|
|
2807
|
+
continue;
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
if (providerAttempts.length > 1) {
|
|
2811
|
+
retryManifest = writeAnalyzeProjectRetryManifest(repoRoot, {
|
|
2812
|
+
provider,
|
|
2813
|
+
command: 'ai analyze-project',
|
|
2814
|
+
runId: auditRunId,
|
|
2815
|
+
now: options.now,
|
|
2816
|
+
maxRetries,
|
|
2817
|
+
finalStatus: 'invalid',
|
|
2818
|
+
attempts: providerAttempts,
|
|
2819
|
+
});
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
let validationManifest = null;
|
|
2823
|
+
try {
|
|
2824
|
+
validationManifest = writeAnalyzeProjectValidationManifest(repoRoot, {
|
|
2825
|
+
error,
|
|
2826
|
+
provider,
|
|
2827
|
+
command: 'ai analyze-project',
|
|
2828
|
+
runId: auditRunId,
|
|
2829
|
+
now: options.now,
|
|
2830
|
+
retry: {
|
|
2831
|
+
attempts: providerAttempts.length,
|
|
2832
|
+
max_retries: maxRetries,
|
|
2833
|
+
retryable,
|
|
2834
|
+
exhausted: retryable && attemptIndex >= maxRetries,
|
|
2835
|
+
manifest: retryManifest,
|
|
2836
|
+
},
|
|
2837
|
+
});
|
|
2838
|
+
} catch {
|
|
2839
|
+
validationManifest = null;
|
|
2840
|
+
}
|
|
2841
|
+
writeAnalyzeProjectRunStatus(repoRoot, auditRunId, 'failed', {
|
|
2842
|
+
now: options.now,
|
|
2843
|
+
provider,
|
|
2844
|
+
attempts: providerAttempts,
|
|
2845
|
+
artifacts: {
|
|
2846
|
+
selected_context: selectedContextManifest,
|
|
2847
|
+
raw_provider: rawProviderArtifacts.map((artifact) => artifact.path),
|
|
2848
|
+
repair: attemptRepairManifest,
|
|
2849
|
+
retry: retryManifest,
|
|
2850
|
+
validation: validationManifest,
|
|
2851
|
+
},
|
|
2852
|
+
});
|
|
2853
|
+
throw enhanceAnalyzeProjectAnalysisError(error, {
|
|
2854
|
+
validationManifest,
|
|
2855
|
+
repairManifest: attemptRepairManifest,
|
|
2856
|
+
retryManifest,
|
|
2857
|
+
});
|
|
2858
|
+
}
|
|
2025
2859
|
}
|
|
2026
2860
|
const completedReport = {
|
|
2027
2861
|
...report,
|
|
@@ -2040,82 +2874,101 @@ async function runAnalyzeProject(repoRoot, options = {}) {
|
|
|
2040
2874
|
parse_source: parsed.parseSource,
|
|
2041
2875
|
warnings: parsed.warnings,
|
|
2042
2876
|
doc_update_paths: parsed.docUpdatePaths,
|
|
2877
|
+
repaired: parsed.repaired === true,
|
|
2878
|
+
repair_manifest: repairManifest,
|
|
2879
|
+
retry_count: Math.max(0, providerAttempts.length - 1),
|
|
2880
|
+
retry_manifest: retryManifest,
|
|
2043
2881
|
},
|
|
2044
2882
|
provider_artifact: buildAnalyzeProjectProviderArtifact(result, clean, repoRoot),
|
|
2883
|
+
raw_provider_artifacts: rawProviderArtifacts.map((artifact) => artifact.path),
|
|
2884
|
+
provider_attempts: providerAttempts,
|
|
2885
|
+
run_id: auditRunId,
|
|
2886
|
+
run_status_path: path.join('.quiver', 'runs', auditRunId, 'status.json').split(path.sep).join('/'),
|
|
2887
|
+
selected_context_manifest: selectedContextManifest,
|
|
2888
|
+
};
|
|
2889
|
+
const needsDocProposal = options.applyDocs === true || options.saveProposal === true || options.review === true;
|
|
2890
|
+
const docProposal = needsDocProposal ? buildAnalyzeProjectDocProposal(parsed.analysis) : null;
|
|
2891
|
+
const docWritePlan = docProposal ? buildAnalyzeProjectWritePlan(repoRoot, docProposal) : [];
|
|
2892
|
+
const docActionContext = {
|
|
2893
|
+
auditRunId,
|
|
2894
|
+
commandOptions: options,
|
|
2895
|
+
completedReport,
|
|
2896
|
+
proposal: docProposal,
|
|
2897
|
+
writePlan: docWritePlan,
|
|
2898
|
+
provider,
|
|
2899
|
+
providerAttempts,
|
|
2900
|
+
rawProviderArtifacts,
|
|
2901
|
+
selectedContextManifest,
|
|
2902
|
+
repairManifest,
|
|
2903
|
+
retryManifest,
|
|
2045
2904
|
};
|
|
2046
2905
|
|
|
2047
|
-
if (options.
|
|
2048
|
-
const
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
process.stdout.write(formatAnalyzeProjectReviewPlan({
|
|
2052
|
-
writePlan,
|
|
2053
|
-
reviewPath: reviewed.reviewPath,
|
|
2054
|
-
}));
|
|
2055
|
-
await confirmAnalyzeProjectWrites(writePlan, options);
|
|
2056
|
-
const lifecycleRun = ensureAiRun(repoRoot, {
|
|
2057
|
-
command: 'ai analyze-project',
|
|
2058
|
-
input: reviewed.reviewPath,
|
|
2059
|
-
runId: options.runId,
|
|
2060
|
-
phase: 'created',
|
|
2061
|
-
});
|
|
2062
|
-
const snapshot = createAnalyzeProjectSnapshot(repoRoot, lifecycleRun, writePlan, {
|
|
2063
|
-
providerArtifact: completedReport.provider_artifact,
|
|
2064
|
-
proposal: reviewed.proposal,
|
|
2065
|
-
now: options.now || new Date(),
|
|
2906
|
+
if (options.applyDocs === true && options.force === true) {
|
|
2907
|
+
const applyReport = applyAnalyzeProjectDocProposalReport(repoRoot, {
|
|
2908
|
+
...docActionContext,
|
|
2909
|
+
allowDirtyDocs: options.allowDirtyDocs === true,
|
|
2066
2910
|
});
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2911
|
+
return emitAnalyzeProjectApplyReport(applyReport, options);
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
if (options.applyDocs === true) {
|
|
2915
|
+
return runAnalyzeProjectInteractiveApplySelector({
|
|
2916
|
+
report: completedReport,
|
|
2917
|
+
proposal: docProposal,
|
|
2918
|
+
writePlan: docWritePlan,
|
|
2919
|
+
options,
|
|
2920
|
+
actions: {
|
|
2921
|
+
apply: ({ allowDirtyDocs, interactiveAction }) => {
|
|
2922
|
+
const applyReport = applyAnalyzeProjectDocProposalReport(repoRoot, {
|
|
2923
|
+
...docActionContext,
|
|
2924
|
+
allowDirtyDocs,
|
|
2925
|
+
interactiveAction,
|
|
2926
|
+
});
|
|
2927
|
+
return emitAnalyzeProjectApplyReport(applyReport, options);
|
|
2928
|
+
},
|
|
2929
|
+
save: ({ interactiveAction, silent, savedReport } = {}) => {
|
|
2930
|
+
const saveReport = savedReport || saveAnalyzeProjectDocProposalReport(repoRoot, {
|
|
2931
|
+
...docActionContext,
|
|
2932
|
+
interactiveAction,
|
|
2933
|
+
});
|
|
2934
|
+
if (silent !== true) {
|
|
2935
|
+
process.stdout.write(formatAnalyzeProjectSavedProposalReport(saveReport));
|
|
2936
|
+
}
|
|
2937
|
+
return saveReport;
|
|
2938
|
+
},
|
|
2939
|
+
edit: ({ interactiveAction } = {}) => reviewAndWriteAnalyzeProjectDocs(repoRoot, {
|
|
2940
|
+
commandOptions: options,
|
|
2941
|
+
completedReport,
|
|
2942
|
+
initialProposal: docProposal,
|
|
2943
|
+
interactiveAction,
|
|
2944
|
+
}),
|
|
2945
|
+
cancel: ({ interactiveAction, savedReport } = {}) => cancelAnalyzeProjectInteractiveApplyReport(repoRoot, {
|
|
2946
|
+
...docActionContext,
|
|
2947
|
+
interactiveAction,
|
|
2948
|
+
savedReport,
|
|
2949
|
+
}),
|
|
2950
|
+
},
|
|
2087
2951
|
});
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
if (options.saveProposal === true) {
|
|
2955
|
+
const saveReport = saveAnalyzeProjectDocProposalReport(repoRoot, docActionContext);
|
|
2092
2956
|
|
|
2093
2957
|
if (options.json === true) {
|
|
2094
|
-
process.stdout.write(`${JSON.stringify(
|
|
2095
|
-
|
|
2096
|
-
const error = new Error(formatError('ai analyze-project post-write validation failed.'));
|
|
2097
|
-
error.code = 'AI_ANALYZE_PROJECT_POST_WRITE_VALIDATION_FAILED';
|
|
2098
|
-
error.validation = postWriteValidation;
|
|
2099
|
-
throw error;
|
|
2100
|
-
}
|
|
2101
|
-
return writeReport;
|
|
2958
|
+
process.stdout.write(`${JSON.stringify(saveReport, null, 2)}\n`);
|
|
2959
|
+
return saveReport;
|
|
2102
2960
|
}
|
|
2103
2961
|
|
|
2104
|
-
process.stdout.write(
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
error.code = 'AI_ANALYZE_PROJECT_POST_WRITE_VALIDATION_FAILED';
|
|
2115
|
-
error.validation = postWriteValidation;
|
|
2116
|
-
throw error;
|
|
2117
|
-
}
|
|
2118
|
-
return writeReport;
|
|
2962
|
+
process.stdout.write(formatAnalyzeProjectSavedProposalReport(saveReport));
|
|
2963
|
+
return saveReport;
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
if (options.review === true) {
|
|
2967
|
+
return reviewAndWriteAnalyzeProjectDocs(repoRoot, {
|
|
2968
|
+
commandOptions: options,
|
|
2969
|
+
completedReport,
|
|
2970
|
+
initialProposal: docProposal,
|
|
2971
|
+
});
|
|
2119
2972
|
}
|
|
2120
2973
|
|
|
2121
2974
|
if (options.json === true) {
|