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
|
@@ -3,6 +3,11 @@ const path = require('path');
|
|
|
3
3
|
const { readAllSlices } = require('./slice-graph');
|
|
4
4
|
const { hasGeneratedProjectSpec, hasInitializedStateMetadata, readState } = require('./state');
|
|
5
5
|
const { worktreeList } = require('./git');
|
|
6
|
+
const {
|
|
7
|
+
buildQuiverConfig,
|
|
8
|
+
buildQuiverInternalGitignore,
|
|
9
|
+
resolveInitPackageScripts,
|
|
10
|
+
} = require('./init-layout');
|
|
6
11
|
|
|
7
12
|
const NEW_LAYOUT_REQUIRED_PATHS = [
|
|
8
13
|
'README.md',
|
|
@@ -26,6 +31,13 @@ const LEGACY_LAYOUT_PROBES = [
|
|
|
26
31
|
'docs/PROJECT_SCAN.json',
|
|
27
32
|
];
|
|
28
33
|
|
|
34
|
+
const ROOT_GITIGNORE_DEFAULTS = [
|
|
35
|
+
'node_modules/',
|
|
36
|
+
'.DS_Store',
|
|
37
|
+
'dist/',
|
|
38
|
+
'coverage/',
|
|
39
|
+
];
|
|
40
|
+
|
|
29
41
|
function readTextIfExists(filePath) {
|
|
30
42
|
if (!fs.existsSync(filePath)) {
|
|
31
43
|
return null;
|
|
@@ -34,6 +46,34 @@ function readTextIfExists(filePath) {
|
|
|
34
46
|
return fs.readFileSync(filePath, 'utf8');
|
|
35
47
|
}
|
|
36
48
|
|
|
49
|
+
function normalizeIgnorePattern(line) {
|
|
50
|
+
const trimmed = line.trim();
|
|
51
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
52
|
+
return trimmed;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return trimmed.replace(/\/+$/g, '');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function missingLineDefaults(existingText, defaults) {
|
|
59
|
+
const seen = new Set(
|
|
60
|
+
String(existingText || '')
|
|
61
|
+
.split(/\r?\n/)
|
|
62
|
+
.map(normalizeIgnorePattern)
|
|
63
|
+
.filter(Boolean),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return defaults.filter((line) => !seen.has(normalizeIgnorePattern(line)));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function appendMissingLines(filePath, lines) {
|
|
70
|
+
const existingText = readTextIfExists(filePath) || '';
|
|
71
|
+
const trimmed = existingText.replace(/\s+$/g, '');
|
|
72
|
+
const prefix = trimmed.length > 0 ? `${trimmed}\n` : '';
|
|
73
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
74
|
+
fs.writeFileSync(filePath, `${prefix}${lines.join('\n')}\n`);
|
|
75
|
+
}
|
|
76
|
+
|
|
37
77
|
function countNonEmptyLines(text) {
|
|
38
78
|
return String(text || '')
|
|
39
79
|
.split(/\r?\n/)
|
|
@@ -210,6 +250,82 @@ function countStackInfoLeaks(projectRoot) {
|
|
|
210
250
|
return leaks;
|
|
211
251
|
}
|
|
212
252
|
|
|
253
|
+
function collectGeneratedMarkdownFiles(projectRoot) {
|
|
254
|
+
const files = [];
|
|
255
|
+
const rootFiles = ['README.md', 'AGENTS.md'];
|
|
256
|
+
|
|
257
|
+
for (const file of rootFiles) {
|
|
258
|
+
const absolutePath = path.join(projectRoot, file);
|
|
259
|
+
if (fs.existsSync(absolutePath)) {
|
|
260
|
+
files.push(absolutePath);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const docsDir = path.join(projectRoot, 'docs');
|
|
265
|
+
if (!fs.existsSync(docsDir)) {
|
|
266
|
+
return files;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const walk = (dirPath) => {
|
|
270
|
+
for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
|
|
271
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
272
|
+
if (entry.isDirectory()) {
|
|
273
|
+
walk(fullPath);
|
|
274
|
+
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
275
|
+
files.push(fullPath);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
walk(docsDir);
|
|
281
|
+
|
|
282
|
+
return files;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function isExternalLink(target) {
|
|
286
|
+
return /^(?:[a-z][a-z0-9+.-]*:|#)/i.test(target);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function normalizeMarkdownLinkTarget(target) {
|
|
290
|
+
return target
|
|
291
|
+
.trim()
|
|
292
|
+
.replace(/^<|>$/g, '')
|
|
293
|
+
.split('#')[0]
|
|
294
|
+
.trim();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function collectMissingMarkdownLinks(projectRoot) {
|
|
298
|
+
const missing = [];
|
|
299
|
+
const linkPattern = /!?\[[^\]]*]\(([^)]+)\)/g;
|
|
300
|
+
|
|
301
|
+
for (const filePath of collectGeneratedMarkdownFiles(projectRoot)) {
|
|
302
|
+
const text = readTextIfExists(filePath);
|
|
303
|
+
if (!text) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let match;
|
|
308
|
+
while ((match = linkPattern.exec(text)) !== null) {
|
|
309
|
+
const target = normalizeMarkdownLinkTarget(match[1]);
|
|
310
|
+
if (!target || isExternalLink(target)) {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const resolved = path.resolve(path.dirname(filePath), target);
|
|
315
|
+
const relativeToRoot = path.relative(projectRoot, resolved);
|
|
316
|
+
if (relativeToRoot.startsWith('..') || path.isAbsolute(relativeToRoot)) {
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (!fs.existsSync(resolved)) {
|
|
321
|
+
missing.push(`${normalizeRelativePath(projectRoot, filePath)} -> ${target}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return missing;
|
|
327
|
+
}
|
|
328
|
+
|
|
213
329
|
function collectLayoutReport(projectRoot) {
|
|
214
330
|
const hasStateMetadata = hasInitializedStateMetadata(readState(projectRoot));
|
|
215
331
|
const realSlices = readAllSlices(projectRoot);
|
|
@@ -276,6 +392,116 @@ function collectLayoutReport(projectRoot) {
|
|
|
276
392
|
};
|
|
277
393
|
}
|
|
278
394
|
|
|
395
|
+
function buildDoctorFixPlan(projectRoot) {
|
|
396
|
+
const fixes = [];
|
|
397
|
+
const rootGitignorePath = path.join(projectRoot, '.gitignore');
|
|
398
|
+
const rootGitignoreText = readTextIfExists(rootGitignorePath) || '';
|
|
399
|
+
const missingRootGitignoreLines = missingLineDefaults(rootGitignoreText, ROOT_GITIGNORE_DEFAULTS);
|
|
400
|
+
if (!fs.existsSync(rootGitignorePath)) {
|
|
401
|
+
fixes.push({
|
|
402
|
+
type: 'append-lines',
|
|
403
|
+
path: '.gitignore',
|
|
404
|
+
description: 'Create root .gitignore with safe Quiver defaults.',
|
|
405
|
+
lines: ROOT_GITIGNORE_DEFAULTS,
|
|
406
|
+
});
|
|
407
|
+
} else if (missingRootGitignoreLines.length > 0) {
|
|
408
|
+
fixes.push({
|
|
409
|
+
type: 'append-lines',
|
|
410
|
+
path: '.gitignore',
|
|
411
|
+
description: `Merge missing root .gitignore defaults: ${missingRootGitignoreLines.join(', ')}.`,
|
|
412
|
+
lines: missingRootGitignoreLines,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const quiverGitignorePath = path.join(projectRoot, '.quiver', '.gitignore');
|
|
417
|
+
const quiverGitignoreText = readTextIfExists(quiverGitignorePath) || '';
|
|
418
|
+
const quiverDefaults = buildQuiverInternalGitignore().split(/\r?\n/).filter(Boolean);
|
|
419
|
+
const missingQuiverLines = missingLineDefaults(quiverGitignoreText, quiverDefaults);
|
|
420
|
+
if (!fs.existsSync(quiverGitignorePath)) {
|
|
421
|
+
fixes.push({
|
|
422
|
+
type: 'write-json-or-text',
|
|
423
|
+
path: '.quiver/.gitignore',
|
|
424
|
+
description: 'Create internal .quiver/.gitignore for local AI state.',
|
|
425
|
+
content: buildQuiverInternalGitignore(),
|
|
426
|
+
});
|
|
427
|
+
} else if (missingQuiverLines.length > 0) {
|
|
428
|
+
fixes.push({
|
|
429
|
+
type: 'append-lines',
|
|
430
|
+
path: '.quiver/.gitignore',
|
|
431
|
+
description: `Merge missing .quiver/.gitignore defaults: ${missingQuiverLines.join(', ')}.`,
|
|
432
|
+
lines: missingQuiverLines,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const configPath = path.join(projectRoot, '.quiver', 'config.json');
|
|
437
|
+
if (!fs.existsSync(configPath)) {
|
|
438
|
+
fixes.push({
|
|
439
|
+
type: 'write-json-or-text',
|
|
440
|
+
path: '.quiver/config.json',
|
|
441
|
+
description: 'Create missing Quiver config metadata.',
|
|
442
|
+
content: `${JSON.stringify(buildQuiverConfig(), null, 2)}\n`,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
447
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
448
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
449
|
+
const scripts = packageJson.scripts && typeof packageJson.scripts === 'object' ? packageJson.scripts : {};
|
|
450
|
+
const expectedScripts = resolveInitPackageScripts('default');
|
|
451
|
+
const missingScripts = Object.entries(expectedScripts)
|
|
452
|
+
.filter(([name]) => (name.startsWith('quiver:') || name === 'check-handoff') && typeof scripts[name] !== 'string');
|
|
453
|
+
|
|
454
|
+
if (missingScripts.length > 0) {
|
|
455
|
+
fixes.push({
|
|
456
|
+
type: 'merge-package-scripts',
|
|
457
|
+
path: 'package.json',
|
|
458
|
+
description: `Add missing package scripts: ${missingScripts.map(([name]) => name).join(', ')}.`,
|
|
459
|
+
scripts: Object.fromEntries(missingScripts),
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return fixes;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function applyDoctorFixPlan(projectRoot, fixes) {
|
|
468
|
+
for (const fix of fixes) {
|
|
469
|
+
const targetPath = path.join(projectRoot, fix.path);
|
|
470
|
+
if (fix.type === 'append-lines') {
|
|
471
|
+
appendMissingLines(targetPath, fix.lines);
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (fix.type === 'write-json-or-text') {
|
|
476
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
477
|
+
fs.writeFileSync(targetPath, fix.content);
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (fix.type === 'merge-package-scripts') {
|
|
482
|
+
const packageJson = JSON.parse(fs.readFileSync(targetPath, 'utf8'));
|
|
483
|
+
packageJson.scripts = {
|
|
484
|
+
...(packageJson.scripts || {}),
|
|
485
|
+
...fix.scripts,
|
|
486
|
+
};
|
|
487
|
+
fs.writeFileSync(targetPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function formatDoctorFixPlan(fixes, { dryRun = false } = {}) {
|
|
493
|
+
const lines = [dryRun ? 'Quiver doctor fix dry-run' : 'Quiver doctor fix'];
|
|
494
|
+
if (fixes.length === 0) {
|
|
495
|
+
lines.push('- No safe fixes to apply.');
|
|
496
|
+
} else {
|
|
497
|
+
for (const fix of fixes) {
|
|
498
|
+
lines.push(`- ${dryRun ? 'Would update' : 'Updated'} ${fix.path}: ${fix.description}`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
lines.push('');
|
|
502
|
+
return lines.join('\n');
|
|
503
|
+
}
|
|
504
|
+
|
|
279
505
|
function collectDoctorReport(projectRoot) {
|
|
280
506
|
const layout = collectLayoutReport(projectRoot);
|
|
281
507
|
const warnings = collectDoctorWarnings(projectRoot);
|
|
@@ -316,11 +542,19 @@ function collectDoctorWarnings(projectRoot) {
|
|
|
316
542
|
warnings.push(`stack information appears outside docs/PROJECT_MAP.md: ${leakIssues.join(', ')}`);
|
|
317
543
|
}
|
|
318
544
|
|
|
545
|
+
const missingLinks = collectMissingMarkdownLinks(projectRoot);
|
|
546
|
+
for (const issue of missingLinks) {
|
|
547
|
+
warnings.push(`missing local docs link: ${issue}`);
|
|
548
|
+
}
|
|
549
|
+
|
|
319
550
|
return warnings;
|
|
320
551
|
}
|
|
321
552
|
|
|
322
553
|
module.exports = {
|
|
554
|
+
applyDoctorFixPlan,
|
|
555
|
+
buildDoctorFixPlan,
|
|
323
556
|
collectDoctorReport,
|
|
324
557
|
collectDoctorWarnings,
|
|
325
558
|
collectLayoutReport,
|
|
559
|
+
formatDoctorFixPlan,
|
|
326
560
|
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { spawnSync } = require('node:child_process');
|
|
4
|
+
|
|
5
|
+
const DEFAULT_OUTPUT_LIMIT = 4000;
|
|
6
|
+
|
|
7
|
+
function redactSecrets(text) {
|
|
8
|
+
return String(text || '')
|
|
9
|
+
.replace(/(authorization:\s*bearer\s+)[^\s`'"]+/gi, '$1[REDACTED]')
|
|
10
|
+
.replace(/\b((?:api[_-]?key|token|secret|password|passwd|pwd)[A-Z0-9_-]*\s*[:=]\s*)[^\s`'"]+/gi, '$1[REDACTED]')
|
|
11
|
+
.replace(/\b(npm_[A-Za-z0-9]{20,})\b/g, '[REDACTED_NPM_TOKEN]');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function truncateText(text, maxLength = DEFAULT_OUTPUT_LIMIT) {
|
|
15
|
+
const value = String(text || '');
|
|
16
|
+
if (value.length <= maxLength) {
|
|
17
|
+
return {
|
|
18
|
+
text: value,
|
|
19
|
+
truncated: false,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
text: `${value.slice(0, maxLength)}\n[... truncated ${value.length - maxLength} chars ...]`,
|
|
25
|
+
truncated: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function quoteCommandPart(value) {
|
|
30
|
+
const part = String(value || '');
|
|
31
|
+
return /\s/.test(part) ? JSON.stringify(part) : part;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function formatCommand(commandArgs) {
|
|
35
|
+
return commandArgs.map(quoteCommandPart).join(' ');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function defaultEvidencePath(repoRoot, startedAt = new Date()) {
|
|
39
|
+
const stamp = startedAt.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z');
|
|
40
|
+
return path.join(repoRoot, '.quiver', 'evidence', `evidence-${stamp}.md`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function renderEvidenceMarkdown(record) {
|
|
44
|
+
return `# Quiver Evidence
|
|
45
|
+
|
|
46
|
+
- Command: \`${record.command}\`
|
|
47
|
+
- Exit code: ${record.exit_code}
|
|
48
|
+
- Duration ms: ${record.duration_ms}
|
|
49
|
+
- Started at: ${record.started_at}
|
|
50
|
+
- Finished at: ${record.finished_at}
|
|
51
|
+
- Output truncated: ${record.output_truncated ? 'yes' : 'no'}
|
|
52
|
+
|
|
53
|
+
## Stdout
|
|
54
|
+
|
|
55
|
+
\`\`\`\`text
|
|
56
|
+
${record.stdout || ''}
|
|
57
|
+
\`\`\`\`
|
|
58
|
+
|
|
59
|
+
## Stderr
|
|
60
|
+
|
|
61
|
+
\`\`\`\`text
|
|
62
|
+
${record.stderr || ''}
|
|
63
|
+
\`\`\`\`
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function runEvidenceCommand(repoRoot, commandArgs, options = {}) {
|
|
68
|
+
if (!Array.isArray(commandArgs) || commandArgs.length === 0) {
|
|
69
|
+
throw new Error('create-quiver: evidence run requires a command after --');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const startedAtDate = new Date();
|
|
73
|
+
const started = Date.now();
|
|
74
|
+
const result = (options.spawnSync || spawnSync)(commandArgs[0], commandArgs.slice(1), {
|
|
75
|
+
cwd: repoRoot,
|
|
76
|
+
encoding: 'utf8',
|
|
77
|
+
shell: false,
|
|
78
|
+
});
|
|
79
|
+
const finishedAtDate = new Date();
|
|
80
|
+
const duration = Date.now() - started;
|
|
81
|
+
const exitCode = typeof result.status === 'number' ? result.status : 1;
|
|
82
|
+
const stdout = truncateText(redactSecrets(result.stdout || ''), options.maxOutput || DEFAULT_OUTPUT_LIMIT);
|
|
83
|
+
const stderr = truncateText(redactSecrets(result.stderr || result.error?.message || ''), options.maxOutput || DEFAULT_OUTPUT_LIMIT);
|
|
84
|
+
const record = {
|
|
85
|
+
command: redactSecrets(formatCommand(commandArgs)),
|
|
86
|
+
duration_ms: duration,
|
|
87
|
+
exit_code: exitCode,
|
|
88
|
+
finished_at: finishedAtDate.toISOString(),
|
|
89
|
+
output_truncated: stdout.truncated || stderr.truncated,
|
|
90
|
+
stderr: stderr.text,
|
|
91
|
+
stdout: stdout.text,
|
|
92
|
+
started_at: startedAtDate.toISOString(),
|
|
93
|
+
};
|
|
94
|
+
const outputPath = options.outputPath
|
|
95
|
+
? path.resolve(repoRoot, options.outputPath)
|
|
96
|
+
: defaultEvidencePath(repoRoot, startedAtDate);
|
|
97
|
+
|
|
98
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
99
|
+
fs.writeFileSync(outputPath, renderEvidenceMarkdown(record));
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
exitCode,
|
|
103
|
+
outputPath,
|
|
104
|
+
record,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
DEFAULT_OUTPUT_LIMIT,
|
|
110
|
+
defaultEvidencePath,
|
|
111
|
+
redactSecrets,
|
|
112
|
+
renderEvidenceMarkdown,
|
|
113
|
+
runEvidenceCommand,
|
|
114
|
+
truncateText,
|
|
115
|
+
};
|