create-quiver 0.12.0 → 0.12.1
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 +44 -0
- package/README.md +49 -17
- package/README_FOR_AI.md +31 -29
- package/ROADMAP.md +15 -3
- package/docs/AI_ONBOARDING_PROMPT.md.template +7 -1
- package/docs/COMMANDS.md.template +44 -18
- package/docs/STATUS.md.template +5 -1
- package/docs/WORKFLOW.md.template +13 -11
- package/package.json +9 -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/src/create-quiver/commands/ai.js +577 -27
- package/src/create-quiver/commands/flow.js +6 -5
- package/src/create-quiver/commands/graph.js +6 -4
- package/src/create-quiver/commands/plan.js +3 -3
- package/src/create-quiver/index.js +328 -12
- package/src/create-quiver/lib/actionable-error.js +27 -0
- package/src/create-quiver/lib/agent-profiles.js +1 -1
- package/src/create-quiver/lib/ai/context-packs.js +4 -0
- package/src/create-quiver/lib/ai/execution-plan.js +7 -1
- package/src/create-quiver/lib/ai/executor.js +270 -20
- package/src/create-quiver/lib/ai/export-state.js +534 -0
- package/src/create-quiver/lib/ai/github.js +83 -0
- package/src/create-quiver/lib/ai/onboarding-template.js +215 -2
- package/src/create-quiver/lib/ai/plan-review.js +5 -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 +12 -0
- package/src/create-quiver/lib/ai/spec-templates.js +78 -9
- package/src/create-quiver/lib/approvals.js +22 -3
- package/src/create-quiver/lib/demo.js +189 -14
- package/src/create-quiver/lib/doctor.js +75 -0
- package/src/create-quiver/lib/handoff.js +81 -12
- package/src/create-quiver/lib/init-docs.js +24 -6
- package/src/create-quiver/lib/init-layout.js +8 -0
- package/src/create-quiver/lib/json.js +53 -3
- package/src/create-quiver/lib/readiness.js +18 -3
- package/src/create-quiver/lib/scope.js +50 -7
- package/src/create-quiver/lib/slice-graph.js +138 -38
- package/src/create-quiver/lib/slice.js +6 -1
- package/src/create-quiver/lib/spec-worktrees.js +16 -2
|
@@ -114,6 +114,119 @@ function markPendingConfirmation(value) {
|
|
|
114
114
|
return `Pending confirmation: ${text}`;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
function readJsonIfExists(filePath) {
|
|
118
|
+
if (!fs.existsSync(filePath)) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function detectPackageManager(projectRoot) {
|
|
130
|
+
if (hasPath(projectRoot, 'bun.lockb') || hasPath(projectRoot, 'bun.lock')) return 'bun';
|
|
131
|
+
if (hasPath(projectRoot, 'pnpm-lock.yaml')) return 'pnpm';
|
|
132
|
+
if (hasPath(projectRoot, 'yarn.lock')) return 'yarn';
|
|
133
|
+
return 'npm';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function detectSourceDirectories(projectRoot) {
|
|
137
|
+
const names = ['src', 'app', 'apps', 'packages', 'lib', 'server', 'client', 'web'];
|
|
138
|
+
return names.filter((name) => {
|
|
139
|
+
try {
|
|
140
|
+
return fs.statSync(path.join(projectRoot, name)).isDirectory();
|
|
141
|
+
} catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function collectRootNames(projectRoot) {
|
|
148
|
+
try {
|
|
149
|
+
return fs.readdirSync(projectRoot, { withFileTypes: true })
|
|
150
|
+
.filter((entry) => !entry.name.startsWith('.') && entry.name !== 'node_modules')
|
|
151
|
+
.map((entry) => `${entry.name}${entry.isDirectory() ? '/' : ''}`)
|
|
152
|
+
.slice(0, 20);
|
|
153
|
+
} catch {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function detectStackSummary(packageJson, projectRoot) {
|
|
159
|
+
const dependencies = {
|
|
160
|
+
...(packageJson?.dependencies || {}),
|
|
161
|
+
...(packageJson?.devDependencies || {}),
|
|
162
|
+
};
|
|
163
|
+
const signals = [];
|
|
164
|
+
|
|
165
|
+
if (dependencies.next || hasPath(projectRoot, 'next.config.js') || hasPath(projectRoot, 'next.config.mjs')) signals.push('Next.js');
|
|
166
|
+
if (dependencies.vite || hasPath(projectRoot, 'vite.config.js') || hasPath(projectRoot, 'vite.config.ts')) signals.push('Vite');
|
|
167
|
+
if (dependencies.react) signals.push('React');
|
|
168
|
+
if (dependencies.vue) signals.push('Vue');
|
|
169
|
+
if (dependencies.angular || dependencies['@angular/core'] || hasPath(projectRoot, 'angular.json')) signals.push('Angular');
|
|
170
|
+
if (dependencies.svelte || hasPath(projectRoot, 'svelte.config.js')) signals.push('Svelte');
|
|
171
|
+
if (dependencies.express) signals.push('Express');
|
|
172
|
+
if (hasPath(projectRoot, 'pyproject.toml') || hasPath(projectRoot, 'requirements.txt')) signals.push('Python');
|
|
173
|
+
if (hasPath(projectRoot, 'go.mod')) signals.push('Go');
|
|
174
|
+
|
|
175
|
+
return signals.length > 0 ? signals.join(', ') : 'Pending confirmation: no primary stack could be inferred from root signals.';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function collectProjectFacts(projectRoot) {
|
|
179
|
+
const packageJson = readJsonIfExists(path.join(projectRoot, 'package.json'));
|
|
180
|
+
const scripts = packageJson?.scripts && typeof packageJson.scripts === 'object' ? packageJson.scripts : {};
|
|
181
|
+
const packageManager = packageJson?.packageManager
|
|
182
|
+
? String(packageJson.packageManager).split('@')[0]
|
|
183
|
+
: detectPackageManager(projectRoot);
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
packageJsonPresent: Boolean(packageJson),
|
|
187
|
+
packageManager,
|
|
188
|
+
stackSummary: detectStackSummary(packageJson, projectRoot),
|
|
189
|
+
scripts,
|
|
190
|
+
rootNames: collectRootNames(projectRoot),
|
|
191
|
+
sourceDirectories: detectSourceDirectories(projectRoot),
|
|
192
|
+
commands: {
|
|
193
|
+
install: packageManager === 'pnpm' ? 'pnpm install' : packageManager === 'yarn' ? 'yarn install' : packageManager === 'bun' ? 'bun install' : 'npm install',
|
|
194
|
+
dev: scripts.dev || scripts.start || 'Pending confirmation: no dev/start script detected.',
|
|
195
|
+
build: scripts.build || 'Pending confirmation: no build script detected.',
|
|
196
|
+
test: scripts.test || 'Pending confirmation: no test script detected.',
|
|
197
|
+
lint: scripts.lint || 'Pending confirmation: no lint script detected.',
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function readProjectMapField(projectRoot, label) {
|
|
203
|
+
const filePath = path.join(projectRoot, 'docs', 'PROJECT_MAP.md');
|
|
204
|
+
if (!fs.existsSync(filePath)) {
|
|
205
|
+
return '';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const text = fs.readFileSync(filePath, 'utf8');
|
|
209
|
+
const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
210
|
+
const match = text.match(new RegExp(`^- ${escaped}:\\s*(.+)$`, 'mi'));
|
|
211
|
+
return match ? match[1].trim() : '';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function collectContextContradictions(projectRoot, plan, facts) {
|
|
215
|
+
const contradictions = [];
|
|
216
|
+
const mappedName = readProjectMapField(projectRoot, 'Name');
|
|
217
|
+
const mappedPackageManager = readProjectMapField(projectRoot, 'Package manager');
|
|
218
|
+
|
|
219
|
+
if (mappedName && mappedName !== plan.projectName) {
|
|
220
|
+
contradictions.push(`docs/PROJECT_MAP.md reports project name '${mappedName}', but package/root identity resolves to '${plan.projectName}'.`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (mappedPackageManager && mappedPackageManager !== facts.packageManager) {
|
|
224
|
+
contradictions.push(`docs/PROJECT_MAP.md reports package manager '${mappedPackageManager}', but current root signals resolve to '${facts.packageManager}'.`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return contradictions.map(markPendingConfirmation);
|
|
228
|
+
}
|
|
229
|
+
|
|
117
230
|
function formatDocStatusLines(items) {
|
|
118
231
|
if (!Array.isArray(items) || items.length === 0) {
|
|
119
232
|
return ['- none'];
|
|
@@ -182,6 +295,7 @@ function collectOnboardingContextPlan(projectRoot) {
|
|
|
182
295
|
function collectContextPreparationPlan(projectRoot) {
|
|
183
296
|
const onboardingPlan = collectOnboardingContextPlan(projectRoot);
|
|
184
297
|
const identity = resolveProjectIdentity(projectRoot);
|
|
298
|
+
const facts = collectProjectFacts(projectRoot);
|
|
185
299
|
const filesConsidered = CONTEXT_PREP_SOURCE_DOCS.map(([relativePath, reason]) => ({
|
|
186
300
|
path: relativePath,
|
|
187
301
|
reason,
|
|
@@ -202,8 +316,7 @@ function collectContextPreparationPlan(projectRoot) {
|
|
|
202
316
|
? 'Pending confirmation: docs/INDEX.md is missing, so navigation should stay conservative and index-first.'
|
|
203
317
|
: null,
|
|
204
318
|
]);
|
|
205
|
-
|
|
206
|
-
return {
|
|
319
|
+
const plan = {
|
|
207
320
|
...onboardingPlan,
|
|
208
321
|
...identity,
|
|
209
322
|
approvedDocPaths: getPreparedContextDocPaths(),
|
|
@@ -211,6 +324,12 @@ function collectContextPreparationPlan(projectRoot) {
|
|
|
211
324
|
omittedPaths: onboardingPlan.omittedByDefault.slice(),
|
|
212
325
|
assumptions,
|
|
213
326
|
risks,
|
|
327
|
+
facts,
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
...plan,
|
|
332
|
+
contradictions: collectContextContradictions(projectRoot, plan, facts),
|
|
214
333
|
};
|
|
215
334
|
}
|
|
216
335
|
|
|
@@ -230,6 +349,9 @@ function buildContextPreparationNotes(plan) {
|
|
|
230
349
|
'### Risks',
|
|
231
350
|
...formatSimpleBullets(plan.risks, 'none'),
|
|
232
351
|
'',
|
|
352
|
+
'### Contradictions',
|
|
353
|
+
...formatSimpleBullets(plan.contradictions, 'none'),
|
|
354
|
+
'',
|
|
233
355
|
'### Omitted Paths',
|
|
234
356
|
...formatSimpleBullets(plan.omittedPaths, 'none'),
|
|
235
357
|
];
|
|
@@ -247,6 +369,76 @@ function readTemplate(relativePath) {
|
|
|
247
369
|
return fs.readFileSync(path.join(PACKAGE_ROOT, relativePath), 'utf8');
|
|
248
370
|
}
|
|
249
371
|
|
|
372
|
+
function renderProjectMapDraft(plan) {
|
|
373
|
+
const facts = plan.facts;
|
|
374
|
+
const lines = [
|
|
375
|
+
'# Project Map',
|
|
376
|
+
'',
|
|
377
|
+
'This file was prepared by `npx create-quiver ai prepare-context`.',
|
|
378
|
+
'Run `npx create-quiver analyze` to refresh it with a deeper repository scan.',
|
|
379
|
+
'',
|
|
380
|
+
'## Project',
|
|
381
|
+
`- Name: ${plan.projectName}`,
|
|
382
|
+
`- Slug: ${plan.projectSlug}`,
|
|
383
|
+
`- Package manager: ${facts.packageManager}`,
|
|
384
|
+
`- package.json present: ${facts.packageJsonPresent ? 'yes' : 'no'}`,
|
|
385
|
+
`- Stack summary: ${facts.stackSummary}`,
|
|
386
|
+
'',
|
|
387
|
+
'## Commands',
|
|
388
|
+
`- Install: ${facts.commands.install}`,
|
|
389
|
+
`- Dev: ${facts.commands.dev}`,
|
|
390
|
+
`- Build: ${facts.commands.build}`,
|
|
391
|
+
`- Test: ${facts.commands.test}`,
|
|
392
|
+
`- Lint: ${facts.commands.lint}`,
|
|
393
|
+
'',
|
|
394
|
+
'## Structure',
|
|
395
|
+
`- Source directories: ${facts.sourceDirectories.length > 0 ? facts.sourceDirectories.join(', ') : 'Pending confirmation: no common source directory detected.'}`,
|
|
396
|
+
`- Root entries: ${facts.rootNames.length > 0 ? facts.rootNames.join(', ') : 'Pending confirmation: root entries could not be listed.'}`,
|
|
397
|
+
'',
|
|
398
|
+
'## Assumptions',
|
|
399
|
+
...formatSimpleBullets(plan.assumptions, 'none'),
|
|
400
|
+
'',
|
|
401
|
+
'## Risks',
|
|
402
|
+
...formatSimpleBullets(plan.risks, 'none'),
|
|
403
|
+
'',
|
|
404
|
+
'## Contradictions',
|
|
405
|
+
...formatSimpleBullets(plan.contradictions, 'none'),
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
return `${lines.join('\n')}\n`;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function renderArchitectureDraft(plan) {
|
|
412
|
+
const facts = plan.facts;
|
|
413
|
+
const lines = [
|
|
414
|
+
`# ${plan.projectName} Architecture`,
|
|
415
|
+
'',
|
|
416
|
+
'This document captures only what Quiver can infer safely from repository structure and docs.',
|
|
417
|
+
'',
|
|
418
|
+
'## Current Understanding',
|
|
419
|
+
`- Stack: ${facts.stackSummary}`,
|
|
420
|
+
`- Source directories: ${facts.sourceDirectories.length > 0 ? facts.sourceDirectories.join(', ') : 'Pending confirmation: no common source directory detected.'}`,
|
|
421
|
+
`- Package manager: ${facts.packageManager}`,
|
|
422
|
+
'',
|
|
423
|
+
'## Boundaries',
|
|
424
|
+
'- TODO: confirm application boundaries with the team.',
|
|
425
|
+
'- Pending confirmation: no architecture decision should be treated as approved unless it appears in `docs/DECISIONS.md` or an approved spec.',
|
|
426
|
+
'',
|
|
427
|
+
'## Commands That Shape Architecture',
|
|
428
|
+
`- Build: ${facts.commands.build}`,
|
|
429
|
+
`- Test: ${facts.commands.test}`,
|
|
430
|
+
`- Lint: ${facts.commands.lint}`,
|
|
431
|
+
'',
|
|
432
|
+
'## Risks',
|
|
433
|
+
...formatSimpleBullets(plan.risks, 'none'),
|
|
434
|
+
'',
|
|
435
|
+
'## Contradictions',
|
|
436
|
+
...formatSimpleBullets(plan.contradictions, 'none'),
|
|
437
|
+
];
|
|
438
|
+
|
|
439
|
+
return `${lines.join('\n')}\n`;
|
|
440
|
+
}
|
|
441
|
+
|
|
250
442
|
function buildContextPreparationDrafts(projectRoot) {
|
|
251
443
|
const plan = collectContextPreparationPlan(projectRoot);
|
|
252
444
|
const currentDate = new Date().toISOString().slice(0, 10);
|
|
@@ -257,6 +449,11 @@ function buildContextPreparationDrafts(projectRoot) {
|
|
|
257
449
|
estado: 'En preparación',
|
|
258
450
|
fase: 'Fase 0',
|
|
259
451
|
progress: 0,
|
|
452
|
+
packageManager: plan.facts.packageManager,
|
|
453
|
+
stackSummary: plan.facts.stackSummary,
|
|
454
|
+
primaryInstall: plan.facts.commands.install,
|
|
455
|
+
primaryDev: plan.facts.commands.dev,
|
|
456
|
+
primaryTest: plan.facts.commands.test,
|
|
260
457
|
};
|
|
261
458
|
const notes = buildContextPreparationNotes(plan);
|
|
262
459
|
const decisionSection = [
|
|
@@ -266,6 +463,14 @@ function buildContextPreparationDrafts(projectRoot) {
|
|
|
266
463
|
`| ${currentDate} | ai prepare-context must remain docs-only | Keeps context prep from touching product code. | Broader write targets | Draft generation stays safe and reviewable |`,
|
|
267
464
|
].join('\n');
|
|
268
465
|
const docs = [
|
|
466
|
+
{
|
|
467
|
+
path: 'docs/INDEX.md',
|
|
468
|
+
content: appendNotes(renderTemplate(readTemplate('docs/INDEX.md.template'), replacements), notes),
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
path: 'docs/PROJECT_MAP.md',
|
|
472
|
+
content: appendNotes(renderProjectMapDraft(plan), notes),
|
|
473
|
+
},
|
|
269
474
|
{
|
|
270
475
|
path: 'docs/AI_CONTEXT.md',
|
|
271
476
|
content: appendNotes(renderTemplate(readTemplate('docs/AI_CONTEXT.md.template'), replacements), notes),
|
|
@@ -278,6 +483,14 @@ function buildContextPreparationDrafts(projectRoot) {
|
|
|
278
483
|
path: 'docs/CONTEXTO.md',
|
|
279
484
|
content: appendNotes(renderTemplate(readTemplate('docs/CONTEXTO.md.template'), replacements), notes),
|
|
280
485
|
},
|
|
486
|
+
{
|
|
487
|
+
path: 'docs/WORKFLOW.md',
|
|
488
|
+
content: appendNotes(renderTemplate(readTemplate('docs/WORKFLOW.md.template'), replacements), notes),
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
path: 'docs/ARCHITECTURE.md',
|
|
492
|
+
content: appendNotes(renderArchitectureDraft(plan), notes),
|
|
493
|
+
},
|
|
281
494
|
{
|
|
282
495
|
path: 'docs/STATUS.md',
|
|
283
496
|
content: appendNotes(renderTemplate(readTemplate('docs/STATUS.md.template'), replacements), notes),
|
|
@@ -208,8 +208,11 @@ function assertPlanReviewed(projectRoot) {
|
|
|
208
208
|
if (review.status !== 'reviewed') {
|
|
209
209
|
const nextCommand = review.status === 'unapproved'
|
|
210
210
|
? 'npx create-quiver ai approve --phase technical-plan --version <n>'
|
|
211
|
-
: 'npx create-quiver ai review-plan';
|
|
212
|
-
|
|
211
|
+
: 'npx create-quiver ai review-plan --dry-run';
|
|
212
|
+
const followUp = review.status === 'unapproved'
|
|
213
|
+
? ''
|
|
214
|
+
: ' Preview the review first, then run `npx create-quiver ai review-plan` to persist it.';
|
|
215
|
+
throw new Error(formatError(`ai plan phase 'spec' requires a reviewed and approved technical-plan input; current review status: ${review.status}. Run \`${nextCommand}\`.${followUp}`));
|
|
213
216
|
}
|
|
214
217
|
return review;
|
|
215
218
|
}
|
|
@@ -2,6 +2,7 @@ const fs = require('node:fs');
|
|
|
2
2
|
const { spawn } = require('node:child_process');
|
|
3
3
|
|
|
4
4
|
const { finalizePromptTransport, preparePromptTransport, describePromptTransport } = require('./prompt-transport');
|
|
5
|
+
const { redactSecrets } = require('../evidence');
|
|
5
6
|
|
|
6
7
|
const SUPPORTED_PROVIDERS = ['codex', 'claude', 'gemini'];
|
|
7
8
|
|
|
@@ -107,7 +108,7 @@ function serializeError(error, provider, invocation) {
|
|
|
107
108
|
|
|
108
109
|
return {
|
|
109
110
|
code: error.code || 'PROVIDER_ERROR',
|
|
110
|
-
message: error.message || String(error),
|
|
111
|
+
message: redactSecrets(error.message || String(error)),
|
|
111
112
|
provider,
|
|
112
113
|
command: invocation.command,
|
|
113
114
|
args: invocation.args.slice(),
|
|
@@ -178,8 +179,8 @@ function runSpawn(command, args, options = {}) {
|
|
|
178
179
|
ok: payload.exitCode === 0,
|
|
179
180
|
exitCode: payload.exitCode,
|
|
180
181
|
signal: payload.signal || null,
|
|
181
|
-
stdout,
|
|
182
|
-
stderr,
|
|
182
|
+
stdout: redactSecrets(stdout),
|
|
183
|
+
stderr: redactSecrets(stderr),
|
|
183
184
|
error: payload.error ? serializeError(payload.error, options.provider, options.invocation) : null,
|
|
184
185
|
});
|
|
185
186
|
};
|