create-quiver 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BACKLOG.md +16 -17
- package/CHANGELOG.md +34 -0
- package/README.md +174 -39
- package/README_FOR_AI.md +48 -24
- package/ROADMAP.md +22 -11
- package/docs/AI_CONTEXT.md.template +2 -0
- package/docs/AI_ONBOARDING_PROMPT.md.template +25 -18
- package/docs/COMMANDS.md.template +59 -11
- package/docs/CONTEXTO.md.template +2 -0
- package/docs/DECISIONS.md.template +1 -0
- package/docs/INDEX.md.template +20 -18
- package/docs/STATUS.md.template +1 -0
- package/docs/SUPPORT_MATRIX.md.template +2 -2
- package/docs/TROUBLESHOOTING.md.template +50 -0
- package/docs/WORKFLOW.md.template +25 -17
- package/package.json +19 -2
- package/package.template.json +13 -1
- package/scripts/init-docs.sh +11 -4
- package/scripts/package-quiver.sh +18 -2
- package/specs/quiver-v22-guided-ai-workflow/EVIDENCE_REPORT.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/EXECUTION_PLAN.md +88 -0
- package/specs/quiver-v22-guided-ai-workflow/SPEC.md +228 -0
- package/specs/quiver-v22-guided-ai-workflow/STATUS.md +42 -0
- package/specs/quiver-v22-guided-ai-workflow/pr.md +104 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/slice.json +51 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/slice.json +55 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/slice.json +57 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/slice.json +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/slice.json +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/slice.json +54 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/slice.json +57 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/slice.json +55 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/slice.json +53 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/EXECUTION_BRIEF.md +59 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/slice.json +59 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/slice.json +60 -0
- package/specs/quiver-v23-guided-flow-productization/EVIDENCE_REPORT.md +80 -0
- package/specs/quiver-v23-guided-flow-productization/EXECUTION_PLAN.md +80 -0
- package/specs/quiver-v23-guided-flow-productization/SPEC.md +203 -0
- package/specs/quiver-v23-guided-flow-productization/STATUS.md +39 -0
- package/specs/quiver-v23-guided-flow-productization/pr.md +119 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/slice.json +51 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/EXECUTION_BRIEF.md +35 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/slice.json +56 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/EXECUTION_BRIEF.md +29 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/slice.json +55 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/EXECUTION_BRIEF.md +29 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/slice.json +54 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/slice.json +59 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/EXECUTION_BRIEF.md +29 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/slice.json +53 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/slice.json +54 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/slice.json +55 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/slice.json +55 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/EXECUTION_BRIEF.md +34 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/slice.json +57 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +32 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/slice.json +63 -0
- package/specs/quiver-v24-dx-onboarding-hardening/EVIDENCE_REPORT.md +55 -0
- package/specs/quiver-v24-dx-onboarding-hardening/EXECUTION_PLAN.md +43 -0
- package/specs/quiver-v24-dx-onboarding-hardening/SPEC.md +149 -0
- package/specs/quiver-v24-dx-onboarding-hardening/STATUS.md +31 -0
- package/specs/quiver-v24-dx-onboarding-hardening/pr.md +76 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/slice.json +51 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/slice.json +55 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/slice.json +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/slice.json +53 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/slice.json +70 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/EXECUTION_BRIEF.md +49 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/slice.json +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/CLOSURE_BRIEF.md +43 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/slice.json +60 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/slice.json +51 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/slice.json +54 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/EXECUTION_BRIEF.md +51 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/slice.json +59 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/slice.json +76 -0
- package/src/create-quiver/commands/ai.js +508 -35
- package/src/create-quiver/commands/demo.js +22 -0
- package/src/create-quiver/commands/evidence.js +37 -0
- package/src/create-quiver/commands/flow.js +561 -0
- package/src/create-quiver/commands/graph.js +14 -1
- package/src/create-quiver/commands/next.js +28 -0
- package/src/create-quiver/commands/plan.js +6 -3
- package/src/create-quiver/commands/prepare.js +236 -0
- package/src/create-quiver/commands/spec.js +133 -0
- package/src/create-quiver/index.js +688 -25
- package/src/create-quiver/lib/agent-profiles.js +148 -0
- package/src/create-quiver/lib/ai/context-packs.js +12 -0
- package/src/create-quiver/lib/ai/execution-plan.js +370 -10
- package/src/create-quiver/lib/ai/executor.js +376 -17
- package/src/create-quiver/lib/ai/github.js +196 -0
- package/src/create-quiver/lib/ai/onboarding-template.js +365 -0
- package/src/create-quiver/lib/ai/plan-review.js +283 -0
- package/src/create-quiver/lib/ai/providers.js +1 -0
- package/src/create-quiver/lib/ai/safety.js +5 -0
- package/src/create-quiver/lib/ai/spec-templates.js +2 -2
- package/src/create-quiver/lib/approvals.js +350 -0
- package/src/create-quiver/lib/demo.js +657 -0
- package/src/create-quiver/lib/doctor.js +234 -0
- package/src/create-quiver/lib/evidence.js +115 -0
- package/src/create-quiver/lib/init-docs.js +284 -17
- package/src/create-quiver/lib/init-layout.js +26 -1
- package/src/create-quiver/lib/lifecycle.js +6 -0
- package/src/create-quiver/lib/package-safety.js +117 -0
- package/src/create-quiver/lib/readiness.js +85 -18
- package/src/create-quiver/lib/slice-graph.js +1 -0
- package/src/create-quiver/lib/slice.js +8 -8
- package/src/create-quiver/lib/spec-worktrees.js +349 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
const fs = require('node:fs');
|
|
2
2
|
const path = require('node:path');
|
|
3
|
+
const cp = require('node:child_process');
|
|
3
4
|
|
|
4
5
|
const { buildContextPackMetadata, normalizeRole } = require('./context-packs');
|
|
5
6
|
const { buildProviderInvocation, runProvider } = require('./providers');
|
|
7
|
+
const { resolveProfileProvider } = require('../agent-profiles');
|
|
8
|
+
const { runGit } = require('../git');
|
|
6
9
|
const { captureWorktreeSnapshot, validateScopeSnapshot } = require('../scope');
|
|
7
10
|
const { resolveSliceContext } = require('../slice');
|
|
8
11
|
|
|
@@ -74,6 +77,199 @@ function formatList(items) {
|
|
|
74
77
|
return items.map((item) => `- ${item}`);
|
|
75
78
|
}
|
|
76
79
|
|
|
80
|
+
function extractMarkdownHeading(text) {
|
|
81
|
+
const match = String(text || '').match(/^#\s+(.+)$/m);
|
|
82
|
+
return match ? match[1].trim() : '';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function extractMarkdownSection(text, headings) {
|
|
86
|
+
const lines = String(text || '').split(/\r?\n/);
|
|
87
|
+
const normalized = new Set(headings.map((heading) => String(heading).trim().toLowerCase()));
|
|
88
|
+
const section = [];
|
|
89
|
+
let capture = false;
|
|
90
|
+
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
const heading = line.match(/^##\s+(.+)$/);
|
|
93
|
+
if (heading) {
|
|
94
|
+
const key = heading[1].trim().toLowerCase();
|
|
95
|
+
if (normalized.has(key)) {
|
|
96
|
+
capture = true;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (capture) {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (capture) {
|
|
105
|
+
section.push(line);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return section.join('\n').trim();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function buildSpecExcerpt(repoRoot, slice) {
|
|
113
|
+
const specPath = path.join(slice.specDirAbs, 'SPEC.md');
|
|
114
|
+
if (!fs.existsSync(specPath)) {
|
|
115
|
+
return {
|
|
116
|
+
path: toRelativePath(repoRoot, specPath),
|
|
117
|
+
lines: ['- n/a'],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const text = fs.readFileSync(specPath, 'utf8');
|
|
122
|
+
const title = extractMarkdownHeading(text);
|
|
123
|
+
const objective = extractMarkdownSection(text, ['Objective', 'Objetivo']);
|
|
124
|
+
const lines = [];
|
|
125
|
+
|
|
126
|
+
if (title) {
|
|
127
|
+
lines.push(`- Title: ${title}`);
|
|
128
|
+
}
|
|
129
|
+
if (objective) {
|
|
130
|
+
lines.push(`- Objective: ${objective.replace(/\s+/g, ' ').slice(0, 500)}`);
|
|
131
|
+
}
|
|
132
|
+
if (lines.length === 0) {
|
|
133
|
+
lines.push('- SPEC.md exists, but no short title/objective excerpt was found.');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
path: toRelativePath(repoRoot, specPath),
|
|
138
|
+
lines,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function buildManualExecutorPrompt({ repoRoot, slicePath, role, context, tokenLimit } = {}) {
|
|
143
|
+
const executorContext = buildExecuteSliceContext({
|
|
144
|
+
repoRoot,
|
|
145
|
+
slicePath,
|
|
146
|
+
role: role || DEFAULT_EXECUTE_ROLE,
|
|
147
|
+
context: context || DEFAULT_EXECUTE_CONTEXT,
|
|
148
|
+
});
|
|
149
|
+
const canonicalRepoRoot = canonicalizeRepoRoot(repoRoot);
|
|
150
|
+
const closurePath = path.join(path.dirname(executorContext.slice.sliceAbs), 'CLOSURE_BRIEF.md');
|
|
151
|
+
const closureText = readTextFile(closurePath, canonicalRepoRoot);
|
|
152
|
+
const relativeClosurePath = toRelativePath(canonicalRepoRoot, closurePath);
|
|
153
|
+
const specExcerpt = buildSpecExcerpt(canonicalRepoRoot, executorContext.slice);
|
|
154
|
+
const slice = executorContext.slice;
|
|
155
|
+
const objective = String(slice.json.objective || slice.json.description || slice.sliceId).trim();
|
|
156
|
+
const restrictions = [
|
|
157
|
+
'Do not read the whole repo.',
|
|
158
|
+
'Do not modify files outside the allowed files.',
|
|
159
|
+
'Before editing, list the files you will read and the files you expect to modify.',
|
|
160
|
+
'Do not add unrequested features.',
|
|
161
|
+
'Do not refactor architecture unless the slice explicitly requires it.',
|
|
162
|
+
'If another file is needed, justify why before reading it.',
|
|
163
|
+
'If blocked, stop and report the blocker before improvising.',
|
|
164
|
+
];
|
|
165
|
+
const outputFormat = [
|
|
166
|
+
'## Cambios realizados',
|
|
167
|
+
'## Archivos modificados',
|
|
168
|
+
'## Comandos ejecutados',
|
|
169
|
+
'## Validaciones',
|
|
170
|
+
'## Riesgos pendientes',
|
|
171
|
+
'## Proximo paso recomendado',
|
|
172
|
+
];
|
|
173
|
+
const promptLines = [
|
|
174
|
+
'Act as a WDD + SDD executor agent.',
|
|
175
|
+
'',
|
|
176
|
+
'MODE: controlled SLICE execution.',
|
|
177
|
+
'',
|
|
178
|
+
'Slice objective:',
|
|
179
|
+
objective || 'n/a',
|
|
180
|
+
'',
|
|
181
|
+
'Minimal context:',
|
|
182
|
+
`- Spec: ${slice.specSlug}`,
|
|
183
|
+
`- Slice: ${slice.sliceId}`,
|
|
184
|
+
`- Slice file: ${slice.sliceRel}`,
|
|
185
|
+
`- Execution brief: ${executorContext.briefPath}`,
|
|
186
|
+
`- Closure brief: ${relativeClosurePath}`,
|
|
187
|
+
'',
|
|
188
|
+
'Relevant SPEC excerpts:',
|
|
189
|
+
`- Source: ${specExcerpt.path}`,
|
|
190
|
+
...specExcerpt.lines,
|
|
191
|
+
'',
|
|
192
|
+
'Allowed files:',
|
|
193
|
+
...formatList(executorContext.allowedFiles),
|
|
194
|
+
'',
|
|
195
|
+
'Restrictions:',
|
|
196
|
+
...formatList(restrictions),
|
|
197
|
+
'',
|
|
198
|
+
'Acceptance criteria:',
|
|
199
|
+
...formatList(slice.acceptance),
|
|
200
|
+
'',
|
|
201
|
+
'Validation commands:',
|
|
202
|
+
...formatList(executorContext.validationCommands),
|
|
203
|
+
'',
|
|
204
|
+
'Exact deliverable expected:',
|
|
205
|
+
'- Implement only this slice.',
|
|
206
|
+
'- Keep the change inside the allowed files.',
|
|
207
|
+
'- Leave evidence in the final report.',
|
|
208
|
+
'',
|
|
209
|
+
'Required final report format:',
|
|
210
|
+
...outputFormat.map((line) => `- ${line}`),
|
|
211
|
+
'',
|
|
212
|
+
`Suggested token limit: ${Number(tokenLimit) > 0 ? Number(tokenLimit) : 3000}`,
|
|
213
|
+
'',
|
|
214
|
+
'Execution brief content:',
|
|
215
|
+
executorContext.briefText.trimEnd(),
|
|
216
|
+
'',
|
|
217
|
+
'Closure brief content:',
|
|
218
|
+
closureText.trimEnd(),
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
allowedFiles: executorContext.allowedFiles,
|
|
223
|
+
closurePath: relativeClosurePath,
|
|
224
|
+
prompt: `${promptLines.join('\n')}\n`,
|
|
225
|
+
slice,
|
|
226
|
+
specExcerpt,
|
|
227
|
+
validationCommands: executorContext.validationCommands,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function runPromptSlice(repoRoot, options = {}) {
|
|
232
|
+
if (!options.slice) {
|
|
233
|
+
throw new Error(formatError('missing required --slice path for ai prompt-slice'));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const built = buildManualExecutorPrompt({
|
|
237
|
+
repoRoot,
|
|
238
|
+
slicePath: options.slice,
|
|
239
|
+
tokenLimit: options.tokenLimit,
|
|
240
|
+
});
|
|
241
|
+
process.stdout.write(built.prompt);
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
task: 'prompt-slice',
|
|
245
|
+
slice: built.slice.sliceId,
|
|
246
|
+
specSlug: built.slice.specSlug,
|
|
247
|
+
prompt: built.prompt,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function buildRecoveryGuidance(slice) {
|
|
252
|
+
const sliceRef = slice && slice.sliceRel ? slice.sliceRel : '<slice.json>';
|
|
253
|
+
return [
|
|
254
|
+
'Recovery:',
|
|
255
|
+
`- Retry: npx create-quiver ai execute-slice --slice ${sliceRef}`,
|
|
256
|
+
'- Abort: inspect the local changes, then manually revert or stash anything you do not want to keep.',
|
|
257
|
+
'- Commit: rerun with --commit only after provider, scope, and validation pass.',
|
|
258
|
+
].join('\n');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function appendRecovery(error, slice) {
|
|
262
|
+
if (!error || !error.message || error.message.includes('Recovery:')) {
|
|
263
|
+
return error;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const wrapped = new Error(`${error.message}\n\n${buildRecoveryGuidance(slice)}`);
|
|
267
|
+
wrapped.cause = error;
|
|
268
|
+
wrapped.code = error.code;
|
|
269
|
+
wrapped.details = error.details;
|
|
270
|
+
return wrapped;
|
|
271
|
+
}
|
|
272
|
+
|
|
77
273
|
function buildExecuteSliceContext({ repoRoot, slicePath, role, context }) {
|
|
78
274
|
const canonicalRepoRoot = canonicalizeRepoRoot(repoRoot);
|
|
79
275
|
const resolvedRole = normalizeRole(role || DEFAULT_EXECUTE_ROLE);
|
|
@@ -123,7 +319,7 @@ function buildExecuteSliceContext({ repoRoot, slicePath, role, context }) {
|
|
|
123
319
|
|
|
124
320
|
sections.push(
|
|
125
321
|
'Constraints:',
|
|
126
|
-
'- Do not commit
|
|
322
|
+
'- Do not commit manually. Quiver can create the slice commit after scope and validation pass when the user enables --commit.',
|
|
127
323
|
'- Do not fix scope violations automatically.',
|
|
128
324
|
'- Do not run multiple executors concurrently.',
|
|
129
325
|
'- Stay inside the allowed files declared by slice.json.',
|
|
@@ -142,7 +338,7 @@ function buildExecuteSliceContext({ repoRoot, slicePath, role, context }) {
|
|
|
142
338
|
};
|
|
143
339
|
}
|
|
144
340
|
|
|
145
|
-
function formatExecuteSliceDryRunReport({ provider, role, contextPack, slice, briefPath, invocation, validationCommands, allowedFiles }) {
|
|
341
|
+
function formatExecuteSliceDryRunReport({ provider, role, contextPack, slice, briefPath, invocation, validationCommands, allowedFiles, commitEnabled }) {
|
|
146
342
|
const lines = [
|
|
147
343
|
'AI execute-slice dry-run',
|
|
148
344
|
`Provider: ${provider}`,
|
|
@@ -155,6 +351,7 @@ function formatExecuteSliceDryRunReport({ provider, role, contextPack, slice, br
|
|
|
155
351
|
`Timeout: ${invocation.timeoutMs}ms`,
|
|
156
352
|
`Prompt transport: ${invocation.promptTransport.mode}`,
|
|
157
353
|
`Prompt length: ${invocation.promptLength} bytes`,
|
|
354
|
+
`Commit after validation: ${commitEnabled ? 'enabled' : 'disabled'}`,
|
|
158
355
|
'Allowed files:',
|
|
159
356
|
...formatList(allowedFiles),
|
|
160
357
|
'Validation commands:',
|
|
@@ -164,7 +361,7 @@ function formatExecuteSliceDryRunReport({ provider, role, contextPack, slice, br
|
|
|
164
361
|
return `${lines.join('\n')}\n`;
|
|
165
362
|
}
|
|
166
363
|
|
|
167
|
-
function formatExecuteSliceResult({ slice, changedFiles, scopeResult }) {
|
|
364
|
+
function formatExecuteSliceResult({ slice, changedFiles, scopeResult, validationResults, commitResult, commitEnabled }) {
|
|
168
365
|
const lines = [
|
|
169
366
|
'AI execute-slice completed',
|
|
170
367
|
`Slice: ${slice.sliceId}`,
|
|
@@ -177,6 +374,17 @@ function formatExecuteSliceResult({ slice, changedFiles, scopeResult }) {
|
|
|
177
374
|
}
|
|
178
375
|
|
|
179
376
|
lines.push(`Scope validation: ${scopeResult.ok ? 'passed' : 'failed'}`);
|
|
377
|
+
if (!Array.isArray(validationResults) || validationResults.length === 0) {
|
|
378
|
+
lines.push('Validation commands: skipped (none declared)');
|
|
379
|
+
} else {
|
|
380
|
+
lines.push(`Validation commands: passed (${validationResults.length})`);
|
|
381
|
+
}
|
|
382
|
+
if (commitResult) {
|
|
383
|
+
lines.push(`Commit: created ${commitResult.hash}`);
|
|
384
|
+
lines.push(`Commit message: ${commitResult.message}`);
|
|
385
|
+
} else {
|
|
386
|
+
lines.push(`Commit: ${commitEnabled ? 'not created' : 'skipped'}`);
|
|
387
|
+
}
|
|
180
388
|
|
|
181
389
|
return `${lines.join('\n')}\n`;
|
|
182
390
|
}
|
|
@@ -190,9 +398,105 @@ function annotateProviderError(error, scope) {
|
|
|
190
398
|
return wrapped;
|
|
191
399
|
}
|
|
192
400
|
|
|
401
|
+
function runValidationCommand(command, repoRoot) {
|
|
402
|
+
try {
|
|
403
|
+
const stdout = cp.execSync(command, {
|
|
404
|
+
cwd: repoRoot,
|
|
405
|
+
encoding: 'utf8',
|
|
406
|
+
shell: true,
|
|
407
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
408
|
+
});
|
|
409
|
+
return {
|
|
410
|
+
command,
|
|
411
|
+
ok: true,
|
|
412
|
+
stdout,
|
|
413
|
+
stderr: '',
|
|
414
|
+
exitCode: 0,
|
|
415
|
+
};
|
|
416
|
+
} catch (error) {
|
|
417
|
+
return {
|
|
418
|
+
command,
|
|
419
|
+
ok: false,
|
|
420
|
+
stdout: error.stdout ? String(error.stdout) : '',
|
|
421
|
+
stderr: error.stderr ? String(error.stderr) : '',
|
|
422
|
+
exitCode: Number.isInteger(error.status) ? error.status : 1,
|
|
423
|
+
error,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function runValidationCommands(repoRoot, commands, runner = runValidationCommand) {
|
|
429
|
+
const results = [];
|
|
430
|
+
for (const command of commands) {
|
|
431
|
+
const result = runner(command, repoRoot);
|
|
432
|
+
results.push(result);
|
|
433
|
+
if (!result.ok) {
|
|
434
|
+
const details = [
|
|
435
|
+
formatError(`validation command failed: ${command}`),
|
|
436
|
+
`Exit code: ${result.exitCode}`,
|
|
437
|
+
];
|
|
438
|
+
if (result.stderr) {
|
|
439
|
+
details.push(`stderr:\n${result.stderr.trimEnd()}`);
|
|
440
|
+
}
|
|
441
|
+
if (result.stdout) {
|
|
442
|
+
details.push(`stdout:\n${result.stdout.trimEnd()}`);
|
|
443
|
+
}
|
|
444
|
+
const error = new Error(details.join('\n'));
|
|
445
|
+
error.code = 'VALIDATION_FAILED';
|
|
446
|
+
error.details = { command, result, results };
|
|
447
|
+
throw error;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return results;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function commitTypeForSlice(slice) {
|
|
454
|
+
const type = String(slice.json.type || slice.json.git?.branch_type || '').trim().toLowerCase();
|
|
455
|
+
if (type === 'bugfix' || type === 'hotfix' || type === 'fix') {
|
|
456
|
+
return 'fix';
|
|
457
|
+
}
|
|
458
|
+
if (type === 'docs' || type === 'documentation') {
|
|
459
|
+
return 'docs';
|
|
460
|
+
}
|
|
461
|
+
if (type === 'test' || type === 'tests') {
|
|
462
|
+
return 'test';
|
|
463
|
+
}
|
|
464
|
+
if (type === 'chore') {
|
|
465
|
+
return 'chore';
|
|
466
|
+
}
|
|
467
|
+
return 'feat';
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function buildSliceCommitMessage(slice) {
|
|
471
|
+
const title = String(slice.json.title || slice.sliceId || 'slice').trim();
|
|
472
|
+
const ticket = String(slice.ticket || '').trim();
|
|
473
|
+
const subject = ticket ? `${ticket} ${title}` : title;
|
|
474
|
+
return `${commitTypeForSlice(slice)}: ${subject}`;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function commitSliceChanges(repoRoot, slice, changedFiles, options = {}) {
|
|
478
|
+
if (!Array.isArray(changedFiles) || changedFiles.length === 0) {
|
|
479
|
+
const error = new Error(formatError('commit requested but provider produced no changed files.'));
|
|
480
|
+
error.code = 'NO_CHANGES_TO_COMMIT';
|
|
481
|
+
throw error;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const message = options.message || buildSliceCommitMessage(slice);
|
|
485
|
+
runGit(['add', '--', ...changedFiles], repoRoot);
|
|
486
|
+
runGit(['commit', '-m', message], repoRoot);
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
files: changedFiles,
|
|
490
|
+
hash: runGit(['rev-parse', '--short', 'HEAD'], repoRoot),
|
|
491
|
+
message,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
193
495
|
async function runExecuteSlice(repoRoot, options = {}) {
|
|
194
|
-
const provider = String(options.provider || DEFAULT_EXECUTE_PROVIDER).trim().toLowerCase();
|
|
195
496
|
const role = normalizeRole(options.role || DEFAULT_EXECUTE_ROLE);
|
|
497
|
+
const provider = options.providerExplicit === true || (options.provider && options.providerExplicit !== false)
|
|
498
|
+
? String(options.provider || DEFAULT_EXECUTE_PROVIDER).trim().toLowerCase()
|
|
499
|
+
: resolveProfileProvider(repoRoot, role, DEFAULT_EXECUTE_PROVIDER);
|
|
196
500
|
const context = options.context || DEFAULT_EXECUTE_CONTEXT;
|
|
197
501
|
const timeoutMs = normalizeTimeout(options.timeout);
|
|
198
502
|
|
|
@@ -231,6 +535,7 @@ async function runExecuteSlice(repoRoot, options = {}) {
|
|
|
231
535
|
briefPath: executorContext.briefPath,
|
|
232
536
|
allowedFiles: executorContext.allowedFiles,
|
|
233
537
|
validationCommands: executorContext.validationCommands,
|
|
538
|
+
commitEnabled: options.commit === true,
|
|
234
539
|
};
|
|
235
540
|
process.stdout.write(formatExecuteSliceDryRunReport({
|
|
236
541
|
provider,
|
|
@@ -241,13 +546,14 @@ async function runExecuteSlice(repoRoot, options = {}) {
|
|
|
241
546
|
invocation,
|
|
242
547
|
validationCommands: executorContext.validationCommands,
|
|
243
548
|
allowedFiles: executorContext.allowedFiles,
|
|
549
|
+
commitEnabled: options.commit === true,
|
|
244
550
|
}));
|
|
245
551
|
return report;
|
|
246
552
|
}
|
|
247
553
|
|
|
248
554
|
const beforeSnapshot = captureWorktreeSnapshot(repoRoot);
|
|
249
555
|
if (beforeSnapshot.files.length > 0 && options.allowDirty !== true) {
|
|
250
|
-
throw new Error(formatError(`ai execute-slice requires a clean worktree before running. Commit or stash first: ${beforeSnapshot.files.join(', ')}`));
|
|
556
|
+
throw appendRecovery(new Error(formatError(`ai execute-slice requires a clean worktree before running. Commit or stash first: ${beforeSnapshot.files.join(', ')}`)), executorContext.slice);
|
|
251
557
|
}
|
|
252
558
|
|
|
253
559
|
let result;
|
|
@@ -264,7 +570,7 @@ async function runExecuteSlice(repoRoot, options = {}) {
|
|
|
264
570
|
tempFilePrefix: options.tempFilePrefix,
|
|
265
571
|
});
|
|
266
572
|
} catch (error) {
|
|
267
|
-
throw annotateProviderError(error, 'execute-slice');
|
|
573
|
+
throw appendRecovery(annotateProviderError(error, 'execute-slice'), executorContext.slice);
|
|
268
574
|
}
|
|
269
575
|
|
|
270
576
|
if (result.stdout) {
|
|
@@ -275,21 +581,64 @@ async function runExecuteSlice(repoRoot, options = {}) {
|
|
|
275
581
|
}
|
|
276
582
|
|
|
277
583
|
if (!result.ok) {
|
|
278
|
-
throw annotateProviderError(result.error || new Error('provider run failed'), 'execute-slice');
|
|
584
|
+
throw appendRecovery(annotateProviderError(result.error || new Error('provider run failed'), 'execute-slice'), executorContext.slice);
|
|
279
585
|
}
|
|
280
586
|
|
|
281
587
|
const afterSnapshot = captureWorktreeSnapshot(repoRoot);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
588
|
+
let scopeResult;
|
|
589
|
+
try {
|
|
590
|
+
scopeResult = validateScopeSnapshot({
|
|
591
|
+
allowedFiles: executorContext.allowedFiles,
|
|
592
|
+
beforeSnapshot,
|
|
593
|
+
afterSnapshot,
|
|
594
|
+
strict: true,
|
|
595
|
+
});
|
|
596
|
+
} catch (error) {
|
|
597
|
+
throw appendRecovery(error, executorContext.slice);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
let validationResults = [];
|
|
601
|
+
try {
|
|
602
|
+
validationResults = runValidationCommands(
|
|
603
|
+
repoRoot,
|
|
604
|
+
executorContext.validationCommands,
|
|
605
|
+
options.runValidationCommandFn,
|
|
606
|
+
);
|
|
607
|
+
} catch (error) {
|
|
608
|
+
throw appendRecovery(error, executorContext.slice);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const finalSnapshot = captureWorktreeSnapshot(repoRoot);
|
|
612
|
+
let finalScopeResult;
|
|
613
|
+
try {
|
|
614
|
+
finalScopeResult = validateScopeSnapshot({
|
|
615
|
+
allowedFiles: executorContext.allowedFiles,
|
|
616
|
+
beforeSnapshot,
|
|
617
|
+
afterSnapshot: finalSnapshot,
|
|
618
|
+
strict: true,
|
|
619
|
+
});
|
|
620
|
+
} catch (error) {
|
|
621
|
+
throw appendRecovery(error, executorContext.slice);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
let commitResult = null;
|
|
625
|
+
if (options.commit === true) {
|
|
626
|
+
try {
|
|
627
|
+
commitResult = commitSliceChanges(repoRoot, executorContext.slice, finalScopeResult.changedFiles, {
|
|
628
|
+
message: options.commitMessage,
|
|
629
|
+
});
|
|
630
|
+
} catch (error) {
|
|
631
|
+
throw appendRecovery(error, executorContext.slice);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
288
634
|
|
|
289
635
|
process.stdout.write(formatExecuteSliceResult({
|
|
290
636
|
slice: executorContext.slice,
|
|
291
|
-
changedFiles:
|
|
292
|
-
scopeResult,
|
|
637
|
+
changedFiles: finalScopeResult.changedFiles,
|
|
638
|
+
scopeResult: finalScopeResult,
|
|
639
|
+
validationResults,
|
|
640
|
+
commitResult,
|
|
641
|
+
commitEnabled: options.commit === true,
|
|
293
642
|
}));
|
|
294
643
|
|
|
295
644
|
return {
|
|
@@ -302,8 +651,10 @@ async function runExecuteSlice(repoRoot, options = {}) {
|
|
|
302
651
|
invocation,
|
|
303
652
|
result,
|
|
304
653
|
beforeSnapshot,
|
|
305
|
-
afterSnapshot,
|
|
306
|
-
scopeResult,
|
|
654
|
+
afterSnapshot: finalSnapshot,
|
|
655
|
+
scopeResult: finalScopeResult,
|
|
656
|
+
validationResults,
|
|
657
|
+
commitResult,
|
|
307
658
|
};
|
|
308
659
|
}
|
|
309
660
|
|
|
@@ -312,12 +663,20 @@ module.exports = {
|
|
|
312
663
|
DEFAULT_EXECUTE_PROVIDER,
|
|
313
664
|
DEFAULT_EXECUTE_ROLE,
|
|
314
665
|
annotateProviderError,
|
|
666
|
+
appendRecovery,
|
|
315
667
|
buildExecuteSliceContext,
|
|
668
|
+
buildManualExecutorPrompt,
|
|
669
|
+
buildRecoveryGuidance,
|
|
670
|
+
buildSliceCommitMessage,
|
|
316
671
|
canonicalizeRepoRoot,
|
|
672
|
+
commitSliceChanges,
|
|
317
673
|
formatExecuteSliceDryRunReport,
|
|
318
674
|
formatExecuteSliceResult,
|
|
675
|
+
runValidationCommand,
|
|
676
|
+
runValidationCommands,
|
|
319
677
|
normalizeTimeout,
|
|
320
678
|
readTextFile,
|
|
321
679
|
resolveSliceJsonPath,
|
|
322
680
|
runExecuteSlice,
|
|
681
|
+
runPromptSlice,
|
|
323
682
|
};
|