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
|
@@ -249,6 +249,157 @@ function ensureIdentityFile(repoRoot, identityFile) {
|
|
|
249
249
|
return resolved;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
function findPrBodyCandidates(repoRoot) {
|
|
253
|
+
const candidates = [];
|
|
254
|
+
const rootPr = path.join(repoRoot, 'pr.md');
|
|
255
|
+
if (fs.existsSync(rootPr)) {
|
|
256
|
+
candidates.push(rootPr);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const specsDir = path.join(repoRoot, 'specs');
|
|
260
|
+
if (fs.existsSync(specsDir)) {
|
|
261
|
+
for (const entry of fs.readdirSync(specsDir, { withFileTypes: true })) {
|
|
262
|
+
if (!entry.isDirectory()) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const candidate = path.join(specsDir, entry.name, 'pr.md');
|
|
266
|
+
if (fs.existsSync(candidate)) {
|
|
267
|
+
candidates.push(candidate);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return candidates.sort((left, right) => left.localeCompare(right));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function resolvePrBodyPath(repoRoot, prBodyPath) {
|
|
276
|
+
const configured = String(prBodyPath || '').trim();
|
|
277
|
+
if (configured) {
|
|
278
|
+
const resolved = resolveConfiguredPath(repoRoot, configured);
|
|
279
|
+
if (!fs.existsSync(resolved)) {
|
|
280
|
+
throw createError('MISSING_PR_BODY', formatError(`missing PR body file at ${resolved}. Pass --input specs/<spec-slug>/pr.md or generate pr.md first.`), {
|
|
281
|
+
prBodyPath: configured,
|
|
282
|
+
resolvedPrBodyPath: resolved,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
return resolved;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const candidates = findPrBodyCandidates(repoRoot);
|
|
289
|
+
if (candidates.length === 1) {
|
|
290
|
+
return candidates[0];
|
|
291
|
+
}
|
|
292
|
+
if (candidates.length === 0) {
|
|
293
|
+
throw createError('MISSING_PR_BODY', formatError('missing PR body file. Pass --input specs/<spec-slug>/pr.md or generate pr.md first.'), {
|
|
294
|
+
candidates,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
throw createError('AMBIGUOUS_PR_BODY', formatError(`multiple pr.md files found: ${candidates.map((item) => path.relative(repoRoot, item).split(path.sep).join('/')).join(', ')}. Pass --input with the intended PR body.`), {
|
|
299
|
+
candidates,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function readPrBody(repoRoot, prBodyPath) {
|
|
304
|
+
const resolved = resolvePrBodyPath(repoRoot, prBodyPath);
|
|
305
|
+
const body = fs.readFileSync(resolved, 'utf8');
|
|
306
|
+
if (!body.trim()) {
|
|
307
|
+
throw createError('EMPTY_PR_BODY', formatError(`PR body file is empty: ${path.relative(repoRoot, resolved).split(path.sep).join('/')}`), {
|
|
308
|
+
prBodyPath: resolved,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
body,
|
|
313
|
+
path: resolved,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function extractPrTitle(prBody, fallbackTitle) {
|
|
318
|
+
const lines = String(prBody || '').split(/\r?\n/);
|
|
319
|
+
const titleIndex = lines.findIndex((line) => /^##\s+Title\s*$/i.test(line.trim()));
|
|
320
|
+
if (titleIndex !== -1) {
|
|
321
|
+
for (const line of lines.slice(titleIndex + 1)) {
|
|
322
|
+
const value = line.trim().replace(/^#+\s*/, '');
|
|
323
|
+
if (value) {
|
|
324
|
+
return value;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const firstHeading = lines.find((line) => /^#\s+\S/.test(line.trim()));
|
|
330
|
+
if (firstHeading) {
|
|
331
|
+
return firstHeading.trim().replace(/^#\s+/, '');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return fallbackTitle || 'Quiver PR';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function buildPrCreateArgs(plan) {
|
|
338
|
+
const args = [
|
|
339
|
+
'pr',
|
|
340
|
+
'create',
|
|
341
|
+
'--base',
|
|
342
|
+
plan.baseBranch,
|
|
343
|
+
'--head',
|
|
344
|
+
plan.branchName,
|
|
345
|
+
'--title',
|
|
346
|
+
plan.title,
|
|
347
|
+
'--body-file',
|
|
348
|
+
plan.prBodyPath,
|
|
349
|
+
];
|
|
350
|
+
return args;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function buildPrCreatePlan(repoRoot, preflightReport, options = {}) {
|
|
354
|
+
const prBody = readPrBody(repoRoot, options.prBodyPath || options.input);
|
|
355
|
+
const baseBranch = String(options.baseBranch || 'main').trim() || 'main';
|
|
356
|
+
const title = String(options.title || '').trim() || extractPrTitle(prBody.body, preflightReport.branchName);
|
|
357
|
+
const plan = {
|
|
358
|
+
baseBranch,
|
|
359
|
+
branchName: preflightReport.branchName,
|
|
360
|
+
ghCommand: options.ghCommand || DEFAULT_GH_COMMAND,
|
|
361
|
+
prBodyPath: prBody.path,
|
|
362
|
+
prBodyRelativePath: path.relative(repoRoot, prBody.path).split(path.sep).join('/'),
|
|
363
|
+
remote: preflightReport.remote,
|
|
364
|
+
repoRoot,
|
|
365
|
+
title,
|
|
366
|
+
};
|
|
367
|
+
plan.args = buildPrCreateArgs(plan);
|
|
368
|
+
return plan;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function runGhPrCreate(plan, options = {}) {
|
|
372
|
+
const result = runCommand(plan.ghCommand, plan.args, {
|
|
373
|
+
cwd: plan.repoRoot,
|
|
374
|
+
runner: options.runner || options.ghCreateRunner || spawnSync,
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
if (result && result.error) {
|
|
378
|
+
throw createError('GH_PR_CREATE_FAILED', formatError(`gh pr create could not be executed. ${result.error.message}`), {
|
|
379
|
+
args: plan.args,
|
|
380
|
+
errorCode: result.error.code,
|
|
381
|
+
errorMessage: result.error.message,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (!result || typeof result.status !== 'number' || result.status !== 0) {
|
|
386
|
+
const stderr = result && typeof result.stderr === 'string' ? result.stderr.trim() : '';
|
|
387
|
+
const stdout = result && typeof result.stdout === 'string' ? result.stdout.trim() : '';
|
|
388
|
+
throw createError('GH_PR_CREATE_FAILED', `${formatError('gh pr create failed.')}${[stderr, stdout].filter(Boolean).length > 0 ? `\n${[stderr, stdout].filter(Boolean).join('\n')}` : ''}`, {
|
|
389
|
+
args: plan.args,
|
|
390
|
+
status: result && result.status,
|
|
391
|
+
stderr,
|
|
392
|
+
stdout,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return {
|
|
397
|
+
status: result.status,
|
|
398
|
+
stdout: typeof result.stdout === 'string' ? result.stdout : '',
|
|
399
|
+
stderr: typeof result.stderr === 'string' ? result.stderr : '',
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
252
403
|
function buildPreflightReport(repoRoot, options = {}, checks = {}) {
|
|
253
404
|
return {
|
|
254
405
|
ok: true,
|
|
@@ -310,20 +461,65 @@ function formatPreflightReport(report, options = {}) {
|
|
|
310
461
|
return `${lines.join('\n')}\n`;
|
|
311
462
|
}
|
|
312
463
|
|
|
464
|
+
function quoteCommandArg(arg) {
|
|
465
|
+
const value = String(arg);
|
|
466
|
+
return /^[A-Za-z0-9_./:=@-]+$/.test(value) ? value : JSON.stringify(value);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function formatPrCreateReport({ preflight, plan, result }, options = {}) {
|
|
470
|
+
const dryRun = options.dryRun === true;
|
|
471
|
+
const create = options.create === true;
|
|
472
|
+
const lines = [
|
|
473
|
+
`GitHub pr ${dryRun ? 'dry-run' : create ? 'created' : 'preflight'}`,
|
|
474
|
+
`Remote: ${preflight.remote}`,
|
|
475
|
+
`Branch: ${preflight.branchName}`,
|
|
476
|
+
`Base: ${plan.baseBranch}`,
|
|
477
|
+
`PR body: ${plan.prBodyRelativePath}`,
|
|
478
|
+
`Title: ${plan.title}`,
|
|
479
|
+
`Command: ${plan.ghCommand} ${plan.args.map(quoteCommandArg).join(' ')}`,
|
|
480
|
+
];
|
|
481
|
+
|
|
482
|
+
if (preflight.sshHostAlias) {
|
|
483
|
+
lines.push(`SSH host alias: ${preflight.sshHostAlias}`);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (preflight.identityFile) {
|
|
487
|
+
lines.push(`Identity file: ${preflight.identityFile}`);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (dryRun) {
|
|
491
|
+
lines.push('No PR will be created in dry-run mode.');
|
|
492
|
+
} else if (!create) {
|
|
493
|
+
lines.push('No PR was created. Re-run with --create after reviewing the plan.');
|
|
494
|
+
} else if (result && result.stdout) {
|
|
495
|
+
lines.push(result.stdout.trimEnd());
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return `${lines.join('\n')}\n`;
|
|
499
|
+
}
|
|
500
|
+
|
|
313
501
|
module.exports = {
|
|
314
502
|
DEFAULT_GH_COMMAND,
|
|
315
503
|
DEFAULT_GITFLOW_GUIDE_PATH,
|
|
316
504
|
DEFAULT_REMOTE,
|
|
317
505
|
GitHubPreflightError,
|
|
506
|
+
buildPrCreateArgs,
|
|
507
|
+
buildPrCreatePlan,
|
|
318
508
|
buildPreflightReport,
|
|
509
|
+
extractPrTitle,
|
|
319
510
|
ensureGhAuthenticated,
|
|
320
511
|
ensureGhInstalled,
|
|
321
512
|
ensureGitFlowGuide,
|
|
322
513
|
ensureIdentityFile,
|
|
323
514
|
ensureRemote,
|
|
324
515
|
ensureWorktreeReady,
|
|
516
|
+
findPrBodyCandidates,
|
|
325
517
|
formatGhInstallGuidance,
|
|
326
518
|
formatPreflightReport,
|
|
519
|
+
formatPrCreateReport,
|
|
327
520
|
preflightGitHubPr,
|
|
521
|
+
readPrBody,
|
|
328
522
|
resolveConfiguredPath,
|
|
523
|
+
resolvePrBodyPath,
|
|
524
|
+
runGhPrCreate,
|
|
329
525
|
};
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
|
|
4
|
+
const { getPreparedContextDocPaths } = require('./context-packs');
|
|
5
|
+
|
|
6
|
+
const PROMPT_SOURCE = 'packaged planner onboarding template';
|
|
7
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '../../../..');
|
|
8
|
+
|
|
9
|
+
const ONBOARDING_DOCS = Object.freeze([
|
|
10
|
+
['README.md', 'project entrypoint and human summary'],
|
|
11
|
+
['AGENTS.md', 'agent router and reading rules'],
|
|
12
|
+
['docs/INDEX.md', 'index-first navigation map'],
|
|
13
|
+
['docs/PROJECT_MAP.md', 'stack, commands, and structure facts'],
|
|
14
|
+
['docs/AI_CONTEXT.md', 'AI working contract'],
|
|
15
|
+
['docs/AI_ONBOARDING_PROMPT.md', 'project-specific onboarding contract'],
|
|
16
|
+
['docs/CONTEXTO.md', 'human-readable project context'],
|
|
17
|
+
['docs/WORKFLOW.md', 'WDD/SDD workflow rules'],
|
|
18
|
+
['docs/STATUS.md', 'current state and open risks'],
|
|
19
|
+
['docs/DECISIONS.md', 'durable decisions'],
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
const OMITTED_BY_DEFAULT = Object.freeze([
|
|
23
|
+
'Do not read all docs/ recursively by default.',
|
|
24
|
+
'Do not read source trees until the selected docs identify relevant files.',
|
|
25
|
+
'Do not read dependency folders, generated outputs, caches, secrets, or local AI state.',
|
|
26
|
+
'Use .quiver/scans/PROJECT_SCAN.json only when docs/PROJECT_MAP.md is not enough.',
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const CONTEXT_PREP_SOURCE_DOCS = Object.freeze([
|
|
30
|
+
['README.md', 'project entrypoint and human summary'],
|
|
31
|
+
['AGENTS.md', 'agent router and reading rules'],
|
|
32
|
+
['README_FOR_AI.md', 'framework guidance source; not project debt when absent'],
|
|
33
|
+
['docs/INDEX.md', 'index-first navigation map'],
|
|
34
|
+
['docs/PROJECT_MAP.md', 'stack, commands, and structure facts'],
|
|
35
|
+
['docs/WORKFLOW.md', 'workflow rules and execution contract'],
|
|
36
|
+
['docs/AI_CONTEXT.md', 'agent-facing project context pack'],
|
|
37
|
+
['docs/AI_ONBOARDING_PROMPT.md', 'project-specific onboarding contract'],
|
|
38
|
+
['docs/CONTEXTO.md', 'human-readable project context'],
|
|
39
|
+
['docs/STATUS.md', 'current state and open risks'],
|
|
40
|
+
['docs/DECISIONS.md', 'durable decisions log'],
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
function hasPath(projectRoot, relativePath) {
|
|
44
|
+
return fs.existsSync(path.join(projectRoot, relativePath));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function toProjectSlug(projectName) {
|
|
48
|
+
return String(projectName || '')
|
|
49
|
+
.normalize('NFKD')
|
|
50
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
51
|
+
.toLowerCase()
|
|
52
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
53
|
+
.replace(/^-+|-+$/g, '') || 'quiver-project';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function readProjectName(projectRoot) {
|
|
57
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
58
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
59
|
+
try {
|
|
60
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
61
|
+
if (typeof packageJson.name === 'string' && packageJson.name.trim()) {
|
|
62
|
+
return packageJson.name.trim();
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
// Best effort only. Fall back to the directory name when package.json is unreadable.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return path.basename(projectRoot) || 'Quiver Project';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function renderTemplate(text, replacements) {
|
|
73
|
+
return text
|
|
74
|
+
.replace(/{{PROJECT_NAME}}/g, replacements.projectName)
|
|
75
|
+
.replace(/{{PROJECT_SLUG}}/g, replacements.projectSlug)
|
|
76
|
+
.replace(/\[project\]/g, replacements.projectSlug)
|
|
77
|
+
.replace(/\[project-name\]/g, replacements.projectSlug)
|
|
78
|
+
.replace(/\[project-slug\]/g, replacements.projectSlug)
|
|
79
|
+
.replace(/{{FECHA}}/g, replacements.currentDate)
|
|
80
|
+
.replace(/{{FECHA_PROXIMA}}/g, replacements.datePlus7 || replacements.currentDate)
|
|
81
|
+
.replace(/{{FECHA_PROXIMA_MES}}/g, replacements.datePlus30 || replacements.currentDate)
|
|
82
|
+
.replace(/{{FECHA_LAUNCH}}/g, replacements.datePlus35 || replacements.currentDate)
|
|
83
|
+
.replace(/{{ESTADO}}/g, replacements.estado || 'En preparación')
|
|
84
|
+
.replace(/{{FASE}}/g, replacements.fase || 'Fase 0')
|
|
85
|
+
.replace(/{{X}}%/g, `${typeof replacements.progress === 'number' ? replacements.progress : 0}%`)
|
|
86
|
+
.replace(/{{PACKAGE_MANAGER}}/g, replacements.packageManager || 'npm')
|
|
87
|
+
.replace(/{{STACK_SUMMARY}}/g, replacements.stackSummary || 'unknown until analyze')
|
|
88
|
+
.replace(/{{PRIMARY_INSTALL}}/g, replacements.primaryInstall || 'npm install')
|
|
89
|
+
.replace(/{{PRIMARY_DEV}}/g, replacements.primaryDev || 'not defined')
|
|
90
|
+
.replace(/{{PRIMARY_TEST}}/g, replacements.primaryTest || 'not defined')
|
|
91
|
+
.replace(/{{ANALYZE_COMMAND}}/g, replacements.analyzeCommand || 'npx create-quiver analyze')
|
|
92
|
+
.replace(/{{PLAN_COMMAND}}/g, replacements.planCommand || 'npx create-quiver plan')
|
|
93
|
+
.replace(/{{GRAPH_COMMAND}}/g, replacements.graphCommand || 'npx create-quiver graph')
|
|
94
|
+
.replace(/{{NEXT_COMMAND}}/g, replacements.nextCommand || 'npx create-quiver next')
|
|
95
|
+
.replace(/{{DOCTOR_COMMAND}}/g, replacements.doctorCommand || 'npx create-quiver doctor')
|
|
96
|
+
.replace(/{{START_SLICE_COMMAND}}/g, replacements.startSliceCommand || 'npx create-quiver start-slice <slice.json>')
|
|
97
|
+
.replace(/{{CHECK_SLICE_COMMAND}}/g, replacements.checkSliceCommand || 'npx create-quiver check-slice <slice.json>')
|
|
98
|
+
.replace(/{{CHECK_PR_COMMAND}}/g, replacements.checkPrCommand || 'npx create-quiver check-pr <slice.json>')
|
|
99
|
+
.replace(/{{CLEANUP_SLICE_COMMAND}}/g, replacements.cleanupSliceCommand || 'npx create-quiver cleanup-slice <slice.json>')
|
|
100
|
+
.replace(/{{CHECK_SCOPE_COMMAND}}/g, replacements.checkScopeCommand || 'npx create-quiver check-scope <slice.json>')
|
|
101
|
+
.replace(/{{REFRESH_ACTIVE_SLICES_COMMAND}}/g, replacements.refreshActiveSlicesCommand || 'npx create-quiver refresh-active-slices');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function uniq(values) {
|
|
105
|
+
return Array.from(new Set((Array.isArray(values) ? values : []).filter(Boolean)));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function markPendingConfirmation(value) {
|
|
109
|
+
const text = String(value || '').trim();
|
|
110
|
+
if (!text || /^(TODO|Assumption|Pending confirmation):/i.test(text)) {
|
|
111
|
+
return text;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return `Pending confirmation: ${text}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function formatDocStatusLines(items) {
|
|
118
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
119
|
+
return ['- none'];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return items.map((item) => {
|
|
123
|
+
const status = item.present ? 'present' : 'absent';
|
|
124
|
+
const note = item.reason ? ` (${item.reason})` : '';
|
|
125
|
+
return `- ${item.path}: ${status}${note}`;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function formatSimpleBullets(items, emptyLabel = 'none') {
|
|
130
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
131
|
+
return [`- ${emptyLabel}`];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return items.map((item) => `- ${item}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function resolveProjectIdentity(projectRoot) {
|
|
138
|
+
const projectName = readProjectName(projectRoot);
|
|
139
|
+
return {
|
|
140
|
+
projectName,
|
|
141
|
+
projectSlug: toProjectSlug(projectName),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function collectOnboardingContextPlan(projectRoot) {
|
|
146
|
+
const docs = ONBOARDING_DOCS.map(([relativePath, reason]) => ({
|
|
147
|
+
path: relativePath,
|
|
148
|
+
reason,
|
|
149
|
+
present: hasPath(projectRoot, relativePath),
|
|
150
|
+
}));
|
|
151
|
+
const selectedDocs = docs.filter((item) => item.present);
|
|
152
|
+
const missingDocs = docs.filter((item) => !item.present);
|
|
153
|
+
const assumptions = [];
|
|
154
|
+
const risks = [];
|
|
155
|
+
|
|
156
|
+
if (!hasPath(projectRoot, 'docs/INDEX.md')) {
|
|
157
|
+
risks.push('docs/INDEX.md is missing, so the planner cannot use index-first navigation.');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!hasPath(projectRoot, 'docs/PROJECT_MAP.md')) {
|
|
161
|
+
risks.push('docs/PROJECT_MAP.md is missing; run analyze before broad onboarding.');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!hasPath(projectRoot, 'docs/AI_ONBOARDING_PROMPT.md')) {
|
|
165
|
+
risks.push('docs/AI_ONBOARDING_PROMPT.md is missing; project-specific onboarding may be incomplete.');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (selectedDocs.length === 0) {
|
|
169
|
+
assumptions.push('No onboarding docs were detected; use README.md or initialize Quiver first.');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
promptSource: PROMPT_SOURCE,
|
|
174
|
+
selectedDocs,
|
|
175
|
+
missingDocs,
|
|
176
|
+
omittedByDefault: OMITTED_BY_DEFAULT.slice(),
|
|
177
|
+
assumptions,
|
|
178
|
+
risks,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function collectContextPreparationPlan(projectRoot) {
|
|
183
|
+
const onboardingPlan = collectOnboardingContextPlan(projectRoot);
|
|
184
|
+
const identity = resolveProjectIdentity(projectRoot);
|
|
185
|
+
const filesConsidered = CONTEXT_PREP_SOURCE_DOCS.map(([relativePath, reason]) => ({
|
|
186
|
+
path: relativePath,
|
|
187
|
+
reason,
|
|
188
|
+
present: hasPath(projectRoot, relativePath),
|
|
189
|
+
}));
|
|
190
|
+
const assumptions = uniq([
|
|
191
|
+
...onboardingPlan.assumptions,
|
|
192
|
+
!hasPath(projectRoot, 'README_FOR_AI.md')
|
|
193
|
+
? 'Assumption: README_FOR_AI.md is framework guidance only and is not counted as generated-project debt when absent.'
|
|
194
|
+
: null,
|
|
195
|
+
]);
|
|
196
|
+
const risks = uniq([
|
|
197
|
+
...onboardingPlan.risks.map(markPendingConfirmation),
|
|
198
|
+
!hasPath(projectRoot, 'docs/PROJECT_MAP.md')
|
|
199
|
+
? 'Pending confirmation: docs/PROJECT_MAP.md is missing, so the draft must stay on documented facts only.'
|
|
200
|
+
: null,
|
|
201
|
+
!hasPath(projectRoot, 'docs/INDEX.md')
|
|
202
|
+
? 'Pending confirmation: docs/INDEX.md is missing, so navigation should stay conservative and index-first.'
|
|
203
|
+
: null,
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
...onboardingPlan,
|
|
208
|
+
...identity,
|
|
209
|
+
approvedDocPaths: getPreparedContextDocPaths(),
|
|
210
|
+
filesConsidered,
|
|
211
|
+
omittedPaths: onboardingPlan.omittedByDefault.slice(),
|
|
212
|
+
assumptions,
|
|
213
|
+
risks,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function buildContextPreparationNotes(plan) {
|
|
218
|
+
const lines = [
|
|
219
|
+
'## Context Preparation Notes',
|
|
220
|
+
'- TODO: confirm any repo fact that is not supported by README.md, AGENTS.md, docs/INDEX.md, or docs/PROJECT_MAP.md.',
|
|
221
|
+
'- Assumption: missing README_FOR_AI.md in a generated project is guidance-only and not project debt.',
|
|
222
|
+
'- Pending confirmation: keep writes limited to docs/context files and never product code.',
|
|
223
|
+
'',
|
|
224
|
+
'### Files Considered',
|
|
225
|
+
...formatDocStatusLines(plan.filesConsidered),
|
|
226
|
+
'',
|
|
227
|
+
'### Assumptions',
|
|
228
|
+
...formatSimpleBullets(plan.assumptions, 'none'),
|
|
229
|
+
'',
|
|
230
|
+
'### Risks',
|
|
231
|
+
...formatSimpleBullets(plan.risks, 'none'),
|
|
232
|
+
'',
|
|
233
|
+
'### Omitted Paths',
|
|
234
|
+
...formatSimpleBullets(plan.omittedPaths, 'none'),
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
return `${lines.join('\n')}\n`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function appendNotes(body, notes) {
|
|
241
|
+
const trimmedBody = String(body || '').replace(/\s+$/g, '');
|
|
242
|
+
const trimmedNotes = String(notes || '').trimEnd();
|
|
243
|
+
return `${trimmedBody}\n\n${trimmedNotes}\n`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function readTemplate(relativePath) {
|
|
247
|
+
return fs.readFileSync(path.join(PACKAGE_ROOT, relativePath), 'utf8');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function buildContextPreparationDrafts(projectRoot) {
|
|
251
|
+
const plan = collectContextPreparationPlan(projectRoot);
|
|
252
|
+
const currentDate = new Date().toISOString().slice(0, 10);
|
|
253
|
+
const replacements = {
|
|
254
|
+
currentDate,
|
|
255
|
+
projectName: plan.projectName,
|
|
256
|
+
projectSlug: plan.projectSlug,
|
|
257
|
+
estado: 'En preparación',
|
|
258
|
+
fase: 'Fase 0',
|
|
259
|
+
progress: 0,
|
|
260
|
+
};
|
|
261
|
+
const notes = buildContextPreparationNotes(plan);
|
|
262
|
+
const decisionSection = [
|
|
263
|
+
'## Context Preparation Decisions',
|
|
264
|
+
'| Date | Decision | Reason | Alternatives | Impact |',
|
|
265
|
+
`| ${currentDate} | README_FOR_AI.md absence in generated projects is guidance-only, not project debt | Keeps prepare output aligned with generated repos. | Report it as debt | Dry-runs stay accurate and less noisy |`,
|
|
266
|
+
`| ${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
|
+
].join('\n');
|
|
268
|
+
const docs = [
|
|
269
|
+
{
|
|
270
|
+
path: 'docs/AI_CONTEXT.md',
|
|
271
|
+
content: appendNotes(renderTemplate(readTemplate('docs/AI_CONTEXT.md.template'), replacements), notes),
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
path: 'docs/AI_ONBOARDING_PROMPT.md',
|
|
275
|
+
content: appendNotes(renderTemplate(readTemplate('docs/AI_ONBOARDING_PROMPT.md.template'), replacements), notes),
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
path: 'docs/CONTEXTO.md',
|
|
279
|
+
content: appendNotes(renderTemplate(readTemplate('docs/CONTEXTO.md.template'), replacements), notes),
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
path: 'docs/STATUS.md',
|
|
283
|
+
content: appendNotes(renderTemplate(readTemplate('docs/STATUS.md.template'), replacements), notes),
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
path: 'docs/DECISIONS.md',
|
|
287
|
+
content: appendNotes(renderTemplate(readTemplate('docs/DECISIONS.md.template'), replacements), decisionSection),
|
|
288
|
+
},
|
|
289
|
+
];
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
docs,
|
|
293
|
+
plan,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function formatDocBullets(items) {
|
|
298
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
299
|
+
return ['- none'];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return items.map((item) => `- ${item.path}: ${item.reason}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function buildPlannerOnboardingPrompt({ pack, inputText, inputPath, repoRoot }) {
|
|
306
|
+
const plan = collectOnboardingContextPlan(repoRoot);
|
|
307
|
+
const sections = [
|
|
308
|
+
pack.prompt,
|
|
309
|
+
'Task: planner onboarding for this repository.',
|
|
310
|
+
'Goal: understand the project context and prepare safe WDD/SDD work with the smallest useful context.',
|
|
311
|
+
'Rules:',
|
|
312
|
+
'- Start with README.md and AGENTS.md when present.',
|
|
313
|
+
'- Then read docs/INDEX.md as the navigation map when present.',
|
|
314
|
+
'- Use docs/INDEX.md to choose only the docs needed for the current task.',
|
|
315
|
+
'- Do not read all docs/ recursively by default.',
|
|
316
|
+
'- Do not modify product code.',
|
|
317
|
+
'- Do not modify product code during onboarding.',
|
|
318
|
+
'- Do not invent architecture, workflow, roles, domain rules, or conventions.',
|
|
319
|
+
'- Report assumptions, risks, missing docs, and files read.',
|
|
320
|
+
'Selected docs to inspect first:',
|
|
321
|
+
...formatDocBullets(plan.selectedDocs),
|
|
322
|
+
'Documentation debt to report if relevant:',
|
|
323
|
+
...formatDocBullets(plan.missingDocs),
|
|
324
|
+
'Omit by default:',
|
|
325
|
+
...plan.omittedByDefault.map((item) => `- ${item}`),
|
|
326
|
+
];
|
|
327
|
+
|
|
328
|
+
if (inputPath) {
|
|
329
|
+
sections.push(`Input file: ${inputPath}`);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (inputText) {
|
|
333
|
+
sections.push('Input:', inputText.trimEnd());
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
sections.push(
|
|
337
|
+
'Expected output:',
|
|
338
|
+
'- Files read.',
|
|
339
|
+
'- Files created or modified.',
|
|
340
|
+
'- Product code status.',
|
|
341
|
+
'- Project context prepared.',
|
|
342
|
+
'- WDD/SDD understanding based only on repo docs.',
|
|
343
|
+
'- Assumptions.',
|
|
344
|
+
'- Risks and blockers.',
|
|
345
|
+
'- Next safe steps.',
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
plan,
|
|
350
|
+
prompt: sections.join('\n\n'),
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
module.exports = {
|
|
355
|
+
CONTEXT_PREP_SOURCE_DOCS,
|
|
356
|
+
OMITTED_BY_DEFAULT,
|
|
357
|
+
ONBOARDING_DOCS,
|
|
358
|
+
PROMPT_SOURCE,
|
|
359
|
+
buildContextPreparationDrafts,
|
|
360
|
+
buildContextPreparationPlan: collectContextPreparationPlan,
|
|
361
|
+
buildPlannerOnboardingPrompt,
|
|
362
|
+
collectOnboardingContextPlan,
|
|
363
|
+
resolveProjectIdentity,
|
|
364
|
+
renderTemplate,
|
|
365
|
+
};
|