create-quiver 0.12.0 → 0.13.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.
- package/CHANGELOG.md +52 -0
- package/README.md +65 -25
- package/README_FOR_AI.md +36 -29
- package/ROADMAP.md +22 -3
- package/docs/AI_ONBOARDING_PROMPT.md.template +7 -1
- package/docs/COMMANDS.md.template +53 -20
- package/docs/STATUS.md.template +5 -1
- package/docs/WORKFLOW.md.template +13 -11
- package/package.json +10 -3
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EVIDENCE_REPORT.md +293 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EXECUTION_PLAN.md +58 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/SPEC.md +242 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/STATUS.md +35 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/pr.md +77 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/slice.json +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/CLOSURE_BRIEF.md +43 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/slice.json +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/slice.json +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/slice.json +55 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/slice.json +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/slice.json +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/slice.json +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/EVIDENCE_REPORT.md +208 -0
- package/specs/quiver-v26-0121-smoke-hardening/EXECUTION_PLAN.md +57 -0
- package/specs/quiver-v26-0121-smoke-hardening/SPEC.md +137 -0
- package/specs/quiver-v26-0121-smoke-hardening/STATUS.md +32 -0
- package/specs/quiver-v26-0121-smoke-hardening/pr.md +96 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/slice.json +73 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/EXECUTION_BRIEF.md +51 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/slice.json +76 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/slice.json +75 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/slice.json +77 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/slice.json +77 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/slice.json +84 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/slice.json +82 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/slice.json +92 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/AUDIT_V24_V25_V26.md +67 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/COMMAND_CONTRACTS.md +125 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/COVERAGE_MATRIX.md +74 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/EVIDENCE_REPORT.md +179 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/EXECUTION_PLAN.md +71 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/SPEC.md +176 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/STATUS.md +37 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/pr.md +132 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-00-docs-audit-coverage-and-contracts/slice.json +75 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-01-core-state-resolver-and-canonical-statuses/slice.json +79 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-02-json-export-contract-and-machine-output/slice.json +75 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-03-approved-plan-to-spec-create/slice.json +78 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-04-ai-artifact-storage-redaction-and-token-compaction/slice.json +77 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-05-worktree-lifecycle-locks-and-recovery/slice.json +84 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-06-validation-gates-and-scope-safety/slice.json +99 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-07-context-analysis-and-doctor-flow/slice.json +88 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-08-cross-platform-help-auth-and-dx/slice.json +85 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v27-reliability-ai-workflow-hardening/slices/slice-09-fixtures-smoke-docs-and-release-readiness/slice.json +91 -0
- package/src/create-quiver/commands/ai.js +652 -27
- package/src/create-quiver/commands/flow.js +58 -9
- package/src/create-quiver/commands/graph.js +11 -9
- package/src/create-quiver/commands/plan.js +7 -16
- package/src/create-quiver/commands/spec.js +282 -0
- package/src/create-quiver/index.js +409 -31
- package/src/create-quiver/lib/actionable-error.js +27 -0
- package/src/create-quiver/lib/agent-profiles.js +16 -4
- package/src/create-quiver/lib/ai/artifacts.js +318 -0
- package/src/create-quiver/lib/ai/context-packs.js +4 -0
- package/src/create-quiver/lib/ai/execution-plan.js +16 -1
- package/src/create-quiver/lib/ai/executor.js +272 -21
- package/src/create-quiver/lib/ai/export-state.js +679 -0
- package/src/create-quiver/lib/ai/github.js +162 -2
- package/src/create-quiver/lib/ai/onboarding-template.js +215 -2
- package/src/create-quiver/lib/ai/plan-review.js +7 -2
- package/src/create-quiver/lib/ai/providers.js +4 -3
- package/src/create-quiver/lib/ai/run-state.js +414 -0
- package/src/create-quiver/lib/ai/spec-generator.js +84 -13
- package/src/create-quiver/lib/ai/spec-templates.js +150 -21
- package/src/create-quiver/lib/analyze.js +2 -2
- package/src/create-quiver/lib/approvals.js +36 -5
- package/src/create-quiver/lib/demo.js +189 -14
- package/src/create-quiver/lib/doctor.js +154 -0
- package/src/create-quiver/lib/git.js +40 -1
- package/src/create-quiver/lib/handoff.js +123 -12
- package/src/create-quiver/lib/init-docs.js +35 -13
- package/src/create-quiver/lib/init-layout.js +9 -0
- package/src/create-quiver/lib/json.js +53 -3
- package/src/create-quiver/lib/lifecycle.js +52 -3
- package/src/create-quiver/lib/locks.js +134 -0
- package/src/create-quiver/lib/package-safety.js +7 -0
- package/src/create-quiver/lib/paths.js +74 -0
- package/src/create-quiver/lib/project-scan.js +74 -0
- package/src/create-quiver/lib/project-state-resolver.js +236 -0
- package/src/create-quiver/lib/readiness.js +66 -10
- package/src/create-quiver/lib/scope.js +52 -8
- package/src/create-quiver/lib/slice-graph.js +138 -38
- package/src/create-quiver/lib/slice.js +14 -5
- package/src/create-quiver/lib/spec-worktrees.js +129 -32
- package/src/create-quiver/lib/statuses.js +115 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
3
4
|
const { readAllSlices } = require('./slice-graph');
|
|
4
5
|
const { hasGeneratedProjectSpec, hasInitializedStateMetadata, readState } = require('./state');
|
|
5
6
|
const { worktreeList } = require('./git');
|
|
@@ -330,6 +331,7 @@ function collectLayoutReport(projectRoot) {
|
|
|
330
331
|
const hasStateMetadata = hasInitializedStateMetadata(readState(projectRoot));
|
|
331
332
|
const realSlices = readAllSlices(projectRoot);
|
|
332
333
|
const specSlugs = Array.from(new Set(realSlices.map((slice) => slice.specSlug))).sort((left, right) => left.localeCompare(right));
|
|
334
|
+
const exampleTarget = selectDoctorExampleTarget(realSlices, specSlugs);
|
|
333
335
|
const newLayoutFiles = collectPresentPaths(projectRoot, NEW_LAYOUT_REQUIRED_PATHS);
|
|
334
336
|
const missingNewLayoutFiles = collectMissingPaths(projectRoot, NEW_LAYOUT_REQUIRED_PATHS);
|
|
335
337
|
const legacySignals = collectPresentPaths(projectRoot, LEGACY_LAYOUT_PROBES);
|
|
@@ -378,7 +380,14 @@ function collectLayoutReport(projectRoot) {
|
|
|
378
380
|
}
|
|
379
381
|
}
|
|
380
382
|
|
|
383
|
+
if (exampleTarget.source === 'generic-multiple-specs') {
|
|
384
|
+
recommendations.push('Multiple specs were found and no active slice is obvious. Doctor examples use placeholders so they do not point to the wrong spec.');
|
|
385
|
+
} else if (exampleTarget.source === 'active-slice') {
|
|
386
|
+
recommendations.push(`Doctor examples target the active slice candidate ${exampleTarget.specSlug}/${exampleTarget.sliceId} (${exampleTarget.status}).`);
|
|
387
|
+
}
|
|
388
|
+
|
|
381
389
|
return {
|
|
390
|
+
exampleTarget,
|
|
382
391
|
hasLegacyLayout,
|
|
383
392
|
hasNewLayout,
|
|
384
393
|
hasStateMetadata,
|
|
@@ -392,6 +401,76 @@ function collectLayoutReport(projectRoot) {
|
|
|
392
401
|
};
|
|
393
402
|
}
|
|
394
403
|
|
|
404
|
+
function normalizeStatus(value) {
|
|
405
|
+
return String(value || '').trim().toLowerCase().replace(/_/g, '-');
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function statusRank(status) {
|
|
409
|
+
const normalized = normalizeStatus(status);
|
|
410
|
+
const ranks = new Map([
|
|
411
|
+
['in-progress', 0],
|
|
412
|
+
['review', 1],
|
|
413
|
+
['ready', 2],
|
|
414
|
+
['planned', 3],
|
|
415
|
+
['approved', 4],
|
|
416
|
+
['blocked', 5],
|
|
417
|
+
['draft', 6],
|
|
418
|
+
['completed', 99],
|
|
419
|
+
['done', 99],
|
|
420
|
+
]);
|
|
421
|
+
|
|
422
|
+
return ranks.has(normalized) ? ranks.get(normalized) : 20;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function selectDoctorExampleTarget(realSlices, specSlugs) {
|
|
426
|
+
if (!Array.isArray(specSlugs) || specSlugs.length === 0) {
|
|
427
|
+
return {
|
|
428
|
+
sliceId: '<slice-id>',
|
|
429
|
+
source: 'no-specs',
|
|
430
|
+
specSlug: '<spec-slug>',
|
|
431
|
+
status: '',
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const rankedSlices = (Array.isArray(realSlices) ? realSlices : [])
|
|
436
|
+
.filter((slice) => specSlugs.includes(slice.specSlug))
|
|
437
|
+
.slice()
|
|
438
|
+
.sort((left, right) => {
|
|
439
|
+
const rankDelta = statusRank(left.status) - statusRank(right.status);
|
|
440
|
+
if (rankDelta !== 0) {
|
|
441
|
+
return rankDelta;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return left.ref.localeCompare(right.ref);
|
|
445
|
+
});
|
|
446
|
+
const activeSlice = rankedSlices.find((slice) => statusRank(slice.status) < 99);
|
|
447
|
+
|
|
448
|
+
if (activeSlice) {
|
|
449
|
+
return {
|
|
450
|
+
sliceId: activeSlice.sliceId,
|
|
451
|
+
source: 'active-slice',
|
|
452
|
+
specSlug: activeSlice.specSlug,
|
|
453
|
+
status: activeSlice.status || '',
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (specSlugs.length === 1) {
|
|
458
|
+
return {
|
|
459
|
+
sliceId: '<slice-id>',
|
|
460
|
+
source: 'single-spec',
|
|
461
|
+
specSlug: specSlugs[0],
|
|
462
|
+
status: '',
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return {
|
|
467
|
+
sliceId: '<slice-id>',
|
|
468
|
+
source: 'generic-multiple-specs',
|
|
469
|
+
specSlug: '<spec-slug>',
|
|
470
|
+
status: '',
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
395
474
|
function buildDoctorFixPlan(projectRoot) {
|
|
396
475
|
const fixes = [];
|
|
397
476
|
const rootGitignorePath = path.join(projectRoot, '.gitignore');
|
|
@@ -512,6 +591,75 @@ function collectDoctorReport(projectRoot) {
|
|
|
512
591
|
};
|
|
513
592
|
}
|
|
514
593
|
|
|
594
|
+
function runEnvironmentProbe(command, args = [], options = {}) {
|
|
595
|
+
const runner = options.runner || spawnSync;
|
|
596
|
+
return runner(command, args, {
|
|
597
|
+
cwd: options.cwd,
|
|
598
|
+
encoding: 'utf8',
|
|
599
|
+
shell: false,
|
|
600
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
601
|
+
timeout: options.timeout || 3000,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function probeOk(command, args, options = {}) {
|
|
606
|
+
const result = runEnvironmentProbe(command, args, options);
|
|
607
|
+
if (result && result.error && result.error.code === 'ENOENT') {
|
|
608
|
+
return {
|
|
609
|
+
ok: false,
|
|
610
|
+
reason: 'missing',
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
ok: Boolean(result && result.status === 0),
|
|
615
|
+
reason: result && result.error ? result.error.message : result && result.stderr ? String(result.stderr).trim() : '',
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function collectEnvironmentWarnings(projectRoot, options = {}) {
|
|
620
|
+
const warnings = [];
|
|
621
|
+
const cwd = projectRoot;
|
|
622
|
+
|
|
623
|
+
const checks = [
|
|
624
|
+
['node', ['--version'], 'Node.js is required to run create-quiver. Install Node 20+ and retry.'],
|
|
625
|
+
['npm', ['--version'], 'npm is required for generated npm scripts and package smokes. Install npm or use a Node distribution that includes it.'],
|
|
626
|
+
['git', ['--version'], 'git is required for specs, slices, worktrees, commits, and PR flow. Install git and retry.'],
|
|
627
|
+
];
|
|
628
|
+
|
|
629
|
+
for (const [command, args, message] of checks) {
|
|
630
|
+
const check = probeOk(command, args, { ...options, cwd });
|
|
631
|
+
if (!check.ok) {
|
|
632
|
+
warnings.push(`${command} check failed: ${message}`);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const ghCheck = probeOk('gh', ['--version'], { ...options, cwd });
|
|
637
|
+
if (!ghCheck.ok) {
|
|
638
|
+
warnings.push('gh check failed: GitHub CLI is required for `ai pr` and `ai doctor`. macOS: brew install gh. Linux: use your distro package manager. Windows: winget install GitHub.cli.');
|
|
639
|
+
} else {
|
|
640
|
+
const authCheck = probeOk('gh', ['auth', 'status'], { ...options, cwd });
|
|
641
|
+
if (!authCheck.ok) {
|
|
642
|
+
warnings.push('gh auth check failed: run `gh auth login` before using `ai pr --create`.');
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (!process.env.SHELL && !process.env.ComSpec) {
|
|
647
|
+
warnings.push('shell check failed: no SHELL or ComSpec environment variable was detected.');
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (projectRoot.includes(' ')) {
|
|
651
|
+
warnings.push('path contains spaces: use quoted paths when copying manual commands.');
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
try {
|
|
655
|
+
fs.accessSync(projectRoot, fs.constants.W_OK);
|
|
656
|
+
} catch {
|
|
657
|
+
warnings.push('permission check failed: current user cannot write to the project root.');
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return warnings;
|
|
661
|
+
}
|
|
662
|
+
|
|
515
663
|
function collectDoctorWarnings(projectRoot) {
|
|
516
664
|
const warnings = [];
|
|
517
665
|
|
|
@@ -547,6 +695,10 @@ function collectDoctorWarnings(projectRoot) {
|
|
|
547
695
|
warnings.push(`missing local docs link: ${issue}`);
|
|
548
696
|
}
|
|
549
697
|
|
|
698
|
+
for (const issue of collectEnvironmentWarnings(projectRoot)) {
|
|
699
|
+
warnings.push(issue);
|
|
700
|
+
}
|
|
701
|
+
|
|
550
702
|
return warnings;
|
|
551
703
|
}
|
|
552
704
|
|
|
@@ -554,7 +706,9 @@ module.exports = {
|
|
|
554
706
|
applyDoctorFixPlan,
|
|
555
707
|
buildDoctorFixPlan,
|
|
556
708
|
collectDoctorReport,
|
|
709
|
+
collectEnvironmentWarnings,
|
|
557
710
|
collectDoctorWarnings,
|
|
558
711
|
collectLayoutReport,
|
|
559
712
|
formatDoctorFixPlan,
|
|
713
|
+
selectDoctorExampleTarget,
|
|
560
714
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const cp = require('child_process');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
2
4
|
|
|
3
5
|
function runGit(args, cwd, options = {}) {
|
|
4
6
|
return cp.execFileSync('git', args, {
|
|
@@ -98,6 +100,9 @@ function currentBranch(repoRoot) {
|
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
function statusPorcelain(repoRoot) {
|
|
103
|
+
if (!repoRoot || !fs.existsSync(repoRoot)) {
|
|
104
|
+
return '__MISSING_WORKTREE__';
|
|
105
|
+
}
|
|
101
106
|
return tryGit(['status', '--porcelain'], repoRoot);
|
|
102
107
|
}
|
|
103
108
|
|
|
@@ -111,7 +116,37 @@ function hasRemote(repoRoot, remoteName = 'origin') {
|
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
function isCleanWorktree(repoRoot) {
|
|
114
|
-
return statusPorcelain(repoRoot) === '';
|
|
119
|
+
return Boolean(repoRoot && fs.existsSync(repoRoot) && isGitWorktree(repoRoot) && statusPorcelain(repoRoot) === '');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function isGitWorktree(repoRoot) {
|
|
123
|
+
return tryGit(['rev-parse', '--is-inside-work-tree'], repoRoot) === 'true';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function absoluteGitDir(repoRoot) {
|
|
127
|
+
return tryGit(['rev-parse', '--absolute-git-dir'], repoRoot);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function gitCommonDir(repoRoot) {
|
|
131
|
+
const value = tryGit(['rev-parse', '--git-common-dir'], repoRoot);
|
|
132
|
+
if (!value) {
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
return path.isAbsolute(value) ? path.resolve(value) : path.resolve(repoRoot, value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function realpathOrResolve(value) {
|
|
139
|
+
try {
|
|
140
|
+
return fs.realpathSync(value);
|
|
141
|
+
} catch {
|
|
142
|
+
return path.resolve(value);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function isLinkedWorktree(repoRoot) {
|
|
147
|
+
const gitDir = absoluteGitDir(repoRoot);
|
|
148
|
+
const commonDir = gitCommonDir(repoRoot);
|
|
149
|
+
return Boolean(gitDir && commonDir && realpathOrResolve(gitDir) !== realpathOrResolve(commonDir));
|
|
115
150
|
}
|
|
116
151
|
|
|
117
152
|
function isDetachedHead(repoRoot) {
|
|
@@ -161,8 +196,12 @@ module.exports = {
|
|
|
161
196
|
lsRemoteHeads,
|
|
162
197
|
mergeBaseIsAncestor,
|
|
163
198
|
hasRemote,
|
|
199
|
+
absoluteGitDir,
|
|
200
|
+
gitCommonDir,
|
|
164
201
|
isCleanWorktree,
|
|
165
202
|
isDetachedHead,
|
|
203
|
+
isGitWorktree,
|
|
204
|
+
isLinkedWorktree,
|
|
166
205
|
revListCount,
|
|
167
206
|
remoteList,
|
|
168
207
|
runGit,
|
|
@@ -10,6 +10,36 @@ const REQUIRED_HEADINGS = [
|
|
|
10
10
|
'## Constraints',
|
|
11
11
|
];
|
|
12
12
|
|
|
13
|
+
const EXECUTION_BRIEF_REQUIRED_HEADINGS = [
|
|
14
|
+
{
|
|
15
|
+
label: 'context',
|
|
16
|
+
alternatives: ['## Context', '## Contexto'],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: 'objective',
|
|
20
|
+
alternatives: ['## Objective', '## Objetivo'],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: 'acceptance criteria',
|
|
24
|
+
alternatives: ['## Acceptance Criteria', '## Criterios de aceptacion', '## Criterios de aceptación'],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: 'completion checklist',
|
|
28
|
+
alternatives: ['## Completion Checklist', '## Checklist de finalizacion', '## Checklist de finalización'],
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const CLOSURE_BRIEF_REQUIRED_HEADINGS = [
|
|
33
|
+
{
|
|
34
|
+
label: 'summary',
|
|
35
|
+
alternatives: ['## Summary', '## Summary of Work', '## Resumen de lo realizado'],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
label: 'validation',
|
|
39
|
+
alternatives: ['## Validation', '## Validation Against Acceptance Criteria', '## Validacion contra criterios de aceptacion', '## Validación contra criterios de aceptación'],
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
13
43
|
const HANDOFF_TEMPLATE_PATH = path.resolve(__dirname, '..', '..', '..', 'specs', '[project-name]', 'HANDOFF.md.template');
|
|
14
44
|
|
|
15
45
|
function normalizePosixPath(filePath, pathLib = path) {
|
|
@@ -21,19 +51,36 @@ function resolveHandoffPath(repoRoot, handoffInput, pathLib = path) {
|
|
|
21
51
|
const relativePath = normalizePosixPath(pathLib.relative(repoRoot, absolutePath), pathLib);
|
|
22
52
|
|
|
23
53
|
if (relativePath.startsWith('..') || pathLib.isAbsolute(relativePath)) {
|
|
24
|
-
throw new Error(`create-quiver: handoff must live
|
|
54
|
+
throw new Error(`create-quiver: handoff or brief must live under specs/<spec-slug>/ (got ${normalizePosixPath(handoffInput, pathLib)})`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const handoffMatch = relativePath.match(/^(specs|specs-fix)\/([^/]+)\/HANDOFF\.md$/);
|
|
58
|
+
if (handoffMatch) {
|
|
59
|
+
return {
|
|
60
|
+
absolutePath,
|
|
61
|
+
relativePath,
|
|
62
|
+
specFamily: handoffMatch[1],
|
|
63
|
+
specSlug: handoffMatch[2],
|
|
64
|
+
kind: 'handoff',
|
|
65
|
+
label: 'Handoff',
|
|
66
|
+
};
|
|
25
67
|
}
|
|
26
68
|
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
-
|
|
69
|
+
const briefMatch = relativePath.match(/^(specs|specs-fix)\/([^/]+)\/slices\/([^/]+)\/(EXECUTION_BRIEF|CLOSURE_BRIEF)\.md$/);
|
|
70
|
+
if (briefMatch) {
|
|
71
|
+
const briefName = briefMatch[4];
|
|
72
|
+
return {
|
|
73
|
+
absolutePath,
|
|
74
|
+
relativePath,
|
|
75
|
+
specFamily: briefMatch[1],
|
|
76
|
+
specSlug: briefMatch[2],
|
|
77
|
+
sliceId: briefMatch[3],
|
|
78
|
+
kind: briefName === 'EXECUTION_BRIEF' ? 'execution-brief' : 'closure-brief',
|
|
79
|
+
label: briefName === 'EXECUTION_BRIEF' ? 'Execution brief' : 'Closure brief',
|
|
80
|
+
};
|
|
30
81
|
}
|
|
31
82
|
|
|
32
|
-
|
|
33
|
-
absolutePath,
|
|
34
|
-
relativePath,
|
|
35
|
-
specSlug: match[1],
|
|
36
|
-
};
|
|
83
|
+
throw new Error(`create-quiver: handoff or brief must live at specs/<spec-slug>/HANDOFF.md or specs/<spec-slug>/slices/<slice-id>/EXECUTION_BRIEF.md|CLOSURE_BRIEF.md (got ${relativePath})`);
|
|
37
84
|
}
|
|
38
85
|
|
|
39
86
|
function readHandoffSections(text) {
|
|
@@ -49,17 +96,76 @@ function validateHandoffSections(text) {
|
|
|
49
96
|
return REQUIRED_HEADINGS.filter((heading) => !sections.has(heading));
|
|
50
97
|
}
|
|
51
98
|
|
|
99
|
+
function normalizeHeading(heading) {
|
|
100
|
+
return String(heading || '')
|
|
101
|
+
.normalize('NFD')
|
|
102
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
103
|
+
.trim()
|
|
104
|
+
.toLowerCase();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function validateBriefSections(text, kind) {
|
|
108
|
+
const sections = new Set(readHandoffSections(text).map(normalizeHeading));
|
|
109
|
+
const requiredGroups = kind === 'closure-brief' ? CLOSURE_BRIEF_REQUIRED_HEADINGS : EXECUTION_BRIEF_REQUIRED_HEADINGS;
|
|
110
|
+
|
|
111
|
+
return requiredGroups
|
|
112
|
+
.filter((group) => !group.alternatives.some((heading) => sections.has(normalizeHeading(heading))))
|
|
113
|
+
.map((group) => group.label);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function headingGroupsForKind(kind) {
|
|
117
|
+
if (kind === 'handoff') {
|
|
118
|
+
return REQUIRED_HEADINGS.map((heading) => ({
|
|
119
|
+
label: heading.replace(/^##\s+/, '').toLowerCase(),
|
|
120
|
+
alternatives: [heading],
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
return kind === 'closure-brief' ? CLOSURE_BRIEF_REQUIRED_HEADINGS : EXECUTION_BRIEF_REQUIRED_HEADINGS;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function formatAliasGuidance(kind) {
|
|
127
|
+
return headingGroupsForKind(kind)
|
|
128
|
+
.map((group) => `- ${group.label}: ${group.alternatives.join(' | ')}`)
|
|
129
|
+
.join('\n');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function canonicalHeadingForGroup(group) {
|
|
133
|
+
return group.alternatives[0] || `## ${group.label}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function formatMinimalTemplate(kind) {
|
|
137
|
+
const lines = [];
|
|
138
|
+
for (const group of headingGroupsForKind(kind)) {
|
|
139
|
+
lines.push(canonicalHeadingForGroup(group), '', 'TODO', '');
|
|
140
|
+
}
|
|
141
|
+
return lines.join('\n').trimEnd();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function formatMissingSectionsError(resolved, missingSections) {
|
|
145
|
+
return [
|
|
146
|
+
`create-quiver: ${resolved.label.toLowerCase()} is missing required sections: ${missingSections.join(', ')}`,
|
|
147
|
+
'',
|
|
148
|
+
'Accepted headings/aliases:',
|
|
149
|
+
formatAliasGuidance(resolved.kind),
|
|
150
|
+
'',
|
|
151
|
+
'Minimal template:',
|
|
152
|
+
formatMinimalTemplate(resolved.kind),
|
|
153
|
+
].join('\n');
|
|
154
|
+
}
|
|
155
|
+
|
|
52
156
|
function checkHandoff(handoffInput, repoRoot = process.cwd()) {
|
|
53
157
|
const resolved = resolveHandoffPath(repoRoot, handoffInput);
|
|
54
158
|
|
|
55
159
|
if (!fs.existsSync(resolved.absolutePath)) {
|
|
56
|
-
throw new Error(`create-quiver: missing
|
|
160
|
+
throw new Error(`create-quiver: missing ${resolved.label.toLowerCase()} file: ${resolved.relativePath}`);
|
|
57
161
|
}
|
|
58
162
|
|
|
59
163
|
const text = fs.readFileSync(resolved.absolutePath, 'utf8');
|
|
60
|
-
const missingSections =
|
|
164
|
+
const missingSections = resolved.kind === 'handoff'
|
|
165
|
+
? validateHandoffSections(text)
|
|
166
|
+
: validateBriefSections(text, resolved.kind);
|
|
61
167
|
if (missingSections.length > 0) {
|
|
62
|
-
throw new Error(
|
|
168
|
+
throw new Error(formatMissingSectionsError(resolved, missingSections));
|
|
63
169
|
}
|
|
64
170
|
|
|
65
171
|
return resolved;
|
|
@@ -95,10 +201,15 @@ function scaffoldHandoff(specSlug, repoRoot = process.cwd()) {
|
|
|
95
201
|
}
|
|
96
202
|
|
|
97
203
|
module.exports = {
|
|
204
|
+
CLOSURE_BRIEF_REQUIRED_HEADINGS,
|
|
205
|
+
EXECUTION_BRIEF_REQUIRED_HEADINGS,
|
|
98
206
|
REQUIRED_HEADINGS,
|
|
99
207
|
checkHandoff,
|
|
208
|
+
formatAliasGuidance,
|
|
209
|
+
formatMinimalTemplate,
|
|
100
210
|
readHandoffSections,
|
|
101
211
|
scaffoldHandoff,
|
|
102
212
|
resolveHandoffPath,
|
|
213
|
+
validateBriefSections,
|
|
103
214
|
validateHandoffSections,
|
|
104
215
|
};
|
|
@@ -316,8 +316,14 @@ Use \`AGENTS.md\` first, then \`docs/AI_CONTEXT.md\` and \`docs/AI_ONBOARDING_PR
|
|
|
316
316
|
\`\`\`bash
|
|
317
317
|
npm run quiver:prepare -- --dry-run
|
|
318
318
|
npm run quiver:ai:onboard -- --dry-run
|
|
319
|
+
npm run quiver:ai:inspect
|
|
320
|
+
npm run quiver:ai:export -- --format json
|
|
321
|
+
npm run quiver:ai:specs
|
|
322
|
+
npm run quiver:ai:slices
|
|
323
|
+
npm run quiver:ai:trace
|
|
319
324
|
npm run quiver:ai:plan -- --phase acceptance --input requirements.md --dry-run
|
|
320
|
-
npm run quiver:ai:
|
|
325
|
+
npm run quiver:ai:revise -- --phase acceptance --input feedback.md --dry-run
|
|
326
|
+
npm run quiver:ai:approve -- --phase acceptance --version <n>
|
|
321
327
|
npm run quiver:ai:plan -- --phase technical-plan --dry-run
|
|
322
328
|
npm run quiver:ai:review-plan -- --dry-run
|
|
323
329
|
npm run quiver:ai:approve -- --phase technical-plan --version <n>
|
|
@@ -371,7 +377,8 @@ Use dry-runs before spending model tokens:
|
|
|
371
377
|
npm run quiver:prepare -- --dry-run
|
|
372
378
|
npm run quiver:ai:onboard -- --dry-run
|
|
373
379
|
npm run quiver:ai:plan -- --phase acceptance --input requirements.md --dry-run
|
|
374
|
-
npm run quiver:ai:
|
|
380
|
+
npm run quiver:ai:revise -- --phase acceptance --input feedback.md --dry-run
|
|
381
|
+
npm run quiver:ai:approve -- --phase acceptance --version <n>
|
|
375
382
|
npm run quiver:ai:plan -- --phase technical-plan --dry-run
|
|
376
383
|
npm run quiver:ai:review-plan -- --dry-run
|
|
377
384
|
npm run quiver:ai:approve -- --phase technical-plan --version <n>
|
|
@@ -397,9 +404,16 @@ npm run quiver:plan
|
|
|
397
404
|
npm run quiver:graph
|
|
398
405
|
npm run quiver:next
|
|
399
406
|
npm run quiver:doctor
|
|
407
|
+
npm run quiver:ai:inspect
|
|
408
|
+
npm run quiver:ai:export -- --format json
|
|
409
|
+
npm run quiver:ai:export -- --format markdown
|
|
410
|
+
npm run quiver:ai:specs
|
|
411
|
+
npm run quiver:ai:slices
|
|
412
|
+
npm run quiver:ai:trace
|
|
400
413
|
npm run quiver:ai:onboard -- --dry-run
|
|
401
414
|
npm run quiver:ai:plan -- --phase acceptance --input requirements.md --dry-run
|
|
402
|
-
npm run quiver:ai:
|
|
415
|
+
npm run quiver:ai:revise -- --phase acceptance --input feedback.md --dry-run
|
|
416
|
+
npm run quiver:ai:approve -- --phase acceptance --version <n>
|
|
403
417
|
npm run quiver:ai:plan -- --phase technical-plan --dry-run
|
|
404
418
|
npm run quiver:ai:review-plan -- --dry-run
|
|
405
419
|
npm run quiver:ai:approve -- --phase technical-plan --version <n>
|
|
@@ -470,7 +484,8 @@ Start with dry-runs so you can inspect the provider, role, context pack, and inv
|
|
|
470
484
|
npm run quiver:prepare -- --dry-run
|
|
471
485
|
npm run quiver:ai:onboard -- --dry-run
|
|
472
486
|
npm run quiver:ai:plan -- --phase acceptance --input requirements.md --dry-run
|
|
473
|
-
npm run quiver:ai:
|
|
487
|
+
npm run quiver:ai:revise -- --phase acceptance --input feedback.md --dry-run
|
|
488
|
+
npm run quiver:ai:approve -- --phase acceptance --version <n>
|
|
474
489
|
npm run quiver:ai:plan -- --phase technical-plan --dry-run
|
|
475
490
|
npm run quiver:ai:review-plan -- --dry-run
|
|
476
491
|
npm run quiver:ai:approve -- --phase technical-plan --version <n>
|
|
@@ -497,7 +512,8 @@ npm run quiver:next
|
|
|
497
512
|
npm run quiver:doctor
|
|
498
513
|
npm run quiver:ai:onboard -- --dry-run
|
|
499
514
|
npm run quiver:ai:plan -- --phase acceptance --input requirements.md --dry-run
|
|
500
|
-
npm run quiver:ai:
|
|
515
|
+
npm run quiver:ai:revise -- --phase acceptance --input feedback.md --dry-run
|
|
516
|
+
npm run quiver:ai:approve -- --phase acceptance --version <n>
|
|
501
517
|
npm run quiver:ai:plan -- --phase technical-plan --dry-run
|
|
502
518
|
npm run quiver:ai:review-plan -- --dry-run
|
|
503
519
|
npm run quiver:ai:approve -- --phase technical-plan --version <n>
|
|
@@ -515,6 +531,7 @@ npm run quiver:start-slice -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
|
515
531
|
npm run quiver:check-slice -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
516
532
|
npm run quiver:check-pr -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
517
533
|
npm run quiver:check-handoff -- specs/${projectSlug}/HANDOFF.md
|
|
534
|
+
npm run quiver:check-handoff -- specs/${projectSlug}/slices/slice-01/EXECUTION_BRIEF.md
|
|
518
535
|
npm run quiver:cleanup-slice -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
519
536
|
npm run quiver:check-scope -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
520
537
|
npm run quiver:refresh-active-slices
|
|
@@ -522,13 +539,14 @@ npm run quiver:refresh-active-slices
|
|
|
522
539
|
|
|
523
540
|
The \`quiver:graph\` script prints the tree view by default; use \`npx create-quiver graph --format mermaid\` for PR-ready Markdown and \`--format dot\` when you want Graphviz source.
|
|
524
541
|
The \`quiver:next\` script points to the next ready slice and can auto-start it behind a confirmation prompt.
|
|
525
|
-
The \`quiver:ai:*\` scripts standardize planner/executor AI flows. Use dry-run first: onboarding and planning dry-runs do not require provider auth, \`quiver:ai:execute-plan -- --dry-run --commit --mode manual\` prints manual prompts, \`--mode delegated\` prints safe waves, and \`quiver:ai:pr -- --dry-run\` validates \`gh\`, GitFlow docs, branch/worktree state, SSH inputs, and \`pr.md\` without creating a PR. Add \`--create\` only after reviewing the plan.
|
|
542
|
+
The \`quiver:ai:*\` scripts standardize planner/executor AI flows. Use dry-run first: onboarding and planning dry-runs do not require provider auth, \`quiver:ai:execute-plan -- --dry-run --commit --mode manual\` prints manual prompts, \`--mode delegated\` prints safe waves, \`quiver:ai:inspect\` shows lifecycle state, \`quiver:ai:export -- --format json|markdown\` emits dashboard/agent-friendly state, and \`quiver:ai:pr -- --dry-run\` validates \`gh\`, GitFlow docs, branch/worktree state, SSH inputs, and \`pr.md\` without creating a PR. Add \`--create\` only after reviewing the plan.
|
|
526
543
|
Use \`quiver:spec:create\`, \`quiver:spec:start\`, \`quiver:spec:status\`, and \`quiver:spec:close\` for one spec generation and worktree per spec.
|
|
527
544
|
Use \`npx create-quiver next --all-ready\` when you want the full ready level instead of a single suggestion.
|
|
528
545
|
The legacy Bash wrappers remain in \`tools/scripts/\` for compatibility, but new project-level automation should prefer the \`quiver:*\` scripts and the direct \`npx create-quiver ...\` commands below.
|
|
529
546
|
\`npm run quiver:migrate\` is only for projects that were already initialized by Quiver.
|
|
530
547
|
\`npm run check-handoff -- specs/${projectSlug}/HANDOFF.md\` is available as a legacy-friendly alias for the handoff validator.
|
|
531
548
|
If a new bounded transfer is needed, scaffold \`specs/${projectSlug}/HANDOFF.md\` with \`npx create-quiver new-handoff ${projectSlug}\` and validate it with \`npx create-quiver check-handoff specs/${projectSlug}/HANDOFF.md\`.
|
|
549
|
+
Use \`npx create-quiver check-handoff specs/${projectSlug}/slices/slice-01/EXECUTION_BRIEF.md\` or \`CLOSURE_BRIEF.md\` to validate the current per-slice brief contract.
|
|
532
550
|
For exceptional context transfers between agents or phases, a dedicated \`HANDOFF.md\` can live alongside the usual spec and docs files.
|
|
533
551
|
|
|
534
552
|
## Cross-Platform Support
|
|
@@ -1074,6 +1092,15 @@ function installSelfAsDevDep(projectRoot, version) {
|
|
|
1074
1092
|
return 'skipped-already-present';
|
|
1075
1093
|
}
|
|
1076
1094
|
|
|
1095
|
+
try {
|
|
1096
|
+
execSync(formatInstallSelfCommand(projectRoot, version), { cwd: projectRoot, stdio: 'inherit' });
|
|
1097
|
+
return 'installed';
|
|
1098
|
+
} catch {
|
|
1099
|
+
return 'failed';
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
function formatInstallSelfCommand(projectRoot, version) {
|
|
1077
1104
|
const pm = detectPackageManager(projectRoot);
|
|
1078
1105
|
const commands = {
|
|
1079
1106
|
npm: `npm install -D create-quiver@${version}`,
|
|
@@ -1081,13 +1108,7 @@ function installSelfAsDevDep(projectRoot, version) {
|
|
|
1081
1108
|
pnpm: `pnpm add -D create-quiver@${version}`,
|
|
1082
1109
|
bun: `bun add -d create-quiver@${version}`,
|
|
1083
1110
|
};
|
|
1084
|
-
|
|
1085
|
-
try {
|
|
1086
|
-
execSync(commands[pm], { cwd: projectRoot, stdio: 'inherit' });
|
|
1087
|
-
return 'installed';
|
|
1088
|
-
} catch {
|
|
1089
|
-
return 'failed';
|
|
1090
|
-
}
|
|
1111
|
+
return commands[pm] || commands.npm;
|
|
1091
1112
|
}
|
|
1092
1113
|
|
|
1093
1114
|
function normalizeSkippedReason(reason) {
|
|
@@ -1235,5 +1256,6 @@ module.exports = {
|
|
|
1235
1256
|
writeFrontMatter,
|
|
1236
1257
|
toProjectSlug,
|
|
1237
1258
|
detectPackageManager,
|
|
1259
|
+
formatInstallSelfCommand,
|
|
1238
1260
|
installSelfAsDevDep,
|
|
1239
1261
|
};
|
|
@@ -82,6 +82,7 @@ function quiverInternalPaths(projectRoot) {
|
|
|
82
82
|
cacheDir: path.join(root, 'cache'),
|
|
83
83
|
configPath: path.join(root, 'config.json'),
|
|
84
84
|
gitignorePath: path.join(root, '.gitignore'),
|
|
85
|
+
locksDir: path.join(root, 'locks'),
|
|
85
86
|
runsDir: path.join(root, 'runs'),
|
|
86
87
|
scansDir: path.join(root, 'scans'),
|
|
87
88
|
statePath: path.join(root, 'state.json'),
|
|
@@ -94,6 +95,7 @@ function buildQuiverInternalGitignore() {
|
|
|
94
95
|
return [
|
|
95
96
|
'cache/',
|
|
96
97
|
'evidence/',
|
|
98
|
+
'locks/',
|
|
97
99
|
'runs/',
|
|
98
100
|
'worktrees/',
|
|
99
101
|
'',
|
|
@@ -188,9 +190,15 @@ function resolveInitPackageScripts(profile, options = {}) {
|
|
|
188
190
|
'quiver:doctor': 'npx create-quiver doctor',
|
|
189
191
|
'quiver:evidence': 'npx create-quiver evidence',
|
|
190
192
|
'quiver:ai:agent': 'npx create-quiver ai agent',
|
|
193
|
+
'quiver:ai:inspect': 'npx create-quiver ai inspect',
|
|
194
|
+
'quiver:ai:export': 'npx create-quiver ai export',
|
|
195
|
+
'quiver:ai:specs': 'npx create-quiver ai specs list',
|
|
196
|
+
'quiver:ai:slices': 'npx create-quiver ai slices list',
|
|
197
|
+
'quiver:ai:trace': 'npx create-quiver ai trace report',
|
|
191
198
|
'quiver:ai:onboard': 'npx create-quiver ai onboard',
|
|
192
199
|
'quiver:ai:prepare-context': 'npx create-quiver ai prepare-context',
|
|
193
200
|
'quiver:ai:plan': 'npx create-quiver ai plan',
|
|
201
|
+
'quiver:ai:revise': 'npx create-quiver ai revise',
|
|
194
202
|
'quiver:ai:review-plan': 'npx create-quiver ai review-plan',
|
|
195
203
|
'quiver:ai:approve': 'npx create-quiver ai approve',
|
|
196
204
|
'quiver:ai:prompt-slice': 'npx create-quiver ai prompt-slice',
|
|
@@ -201,6 +209,7 @@ function resolveInitPackageScripts(profile, options = {}) {
|
|
|
201
209
|
'quiver:spec:create': 'npx create-quiver spec create',
|
|
202
210
|
'quiver:spec:start': 'npx create-quiver spec start',
|
|
203
211
|
'quiver:spec:status': 'npx create-quiver spec status',
|
|
212
|
+
'quiver:spec:validate': 'npx create-quiver spec validate',
|
|
204
213
|
'quiver:spec:close': 'npx create-quiver spec close',
|
|
205
214
|
'quiver:start-slice': 'npx create-quiver start-slice',
|
|
206
215
|
'quiver:check-slice': 'npx create-quiver check-slice',
|
|
@@ -1,7 +1,57 @@
|
|
|
1
1
|
function stripJsonComments(text) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
const input = String(text || '');
|
|
3
|
+
let output = '';
|
|
4
|
+
let inString = false;
|
|
5
|
+
let escaped = false;
|
|
6
|
+
|
|
7
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
8
|
+
const char = input[index];
|
|
9
|
+
const next = input[index + 1];
|
|
10
|
+
|
|
11
|
+
if (inString) {
|
|
12
|
+
output += char;
|
|
13
|
+
if (escaped) {
|
|
14
|
+
escaped = false;
|
|
15
|
+
} else if (char === '\\') {
|
|
16
|
+
escaped = true;
|
|
17
|
+
} else if (char === '"') {
|
|
18
|
+
inString = false;
|
|
19
|
+
}
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (char === '"') {
|
|
24
|
+
inString = true;
|
|
25
|
+
output += char;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (char === '/' && next === '/') {
|
|
30
|
+
while (index < input.length && input[index] !== '\n') {
|
|
31
|
+
index += 1;
|
|
32
|
+
}
|
|
33
|
+
if (index < input.length) {
|
|
34
|
+
output += '\n';
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (char === '/' && next === '*') {
|
|
40
|
+
index += 2;
|
|
41
|
+
while (index < input.length && !(input[index] === '*' && input[index + 1] === '/')) {
|
|
42
|
+
if (input[index] === '\n') {
|
|
43
|
+
output += '\n';
|
|
44
|
+
}
|
|
45
|
+
index += 1;
|
|
46
|
+
}
|
|
47
|
+
index += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
output += char;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return output;
|
|
5
55
|
}
|
|
6
56
|
|
|
7
57
|
function parseJsonWithComments(text) {
|