create-quiver 0.9.0 → 0.10.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/README.md +312 -124
- package/README_FOR_AI.md +59 -45
- package/ROADMAP.md +12 -11
- package/docs/AI_ONBOARDING_PROMPT.md.template +120 -52
- package/docs/COMMANDS.md.template +41 -6
- package/docs/GITFLOW_PR_GUIDE.md.template +11 -0
- package/docs/STANDARD.md.template +1 -1
- package/docs/SUPPORT_MATRIX.md.template +4 -0
- package/docs/TROUBLESHOOTING.md.template +29 -1
- package/docs/WORKFLOW.md.template +1 -1
- package/package.json +6 -1
- package/package.template.json +11 -6
- package/scripts/check-pr-readiness.sh +1 -1
- package/scripts/check-scope.sh +0 -1
- package/scripts/check-slice-readiness.sh +3 -4
- package/scripts/init-docs.sh +55 -9
- package/specs/quiver-v19-self-install-dev-dep/EVIDENCE_REPORT.md +2 -2
- package/specs/quiver-v19-self-install-dev-dep/STATUS.md +4 -4
- package/specs/quiver-v19-self-install-dev-dep/slices/slice-01-auto-install-dev-dep/slice.json +4 -4
- package/specs/quiver-v20-ai-cli-orchestration/EVIDENCE_REPORT.md +23 -0
- package/specs/quiver-v20-ai-cli-orchestration/EXECUTION_PLAN.md +57 -0
- package/specs/quiver-v20-ai-cli-orchestration/SPEC.md +202 -0
- package/specs/quiver-v20-ai-cli-orchestration/STATUS.md +35 -0
- package/specs/quiver-v20-ai-cli-orchestration/pr.md +100 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-00-spec-foundation/slice.json +54 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-01-ai-provider-runner/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-01-ai-provider-runner/EXECUTION_BRIEF.md +63 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-01-ai-provider-runner/slice.json +55 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-02-context-packs-token-budget/CLOSURE_BRIEF.md +40 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-02-context-packs-token-budget/EXECUTION_BRIEF.md +60 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-02-context-packs-token-budget/slice.json +54 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-03-ai-phase-gated-planner/CLOSURE_BRIEF.md +43 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-03-ai-phase-gated-planner/EXECUTION_BRIEF.md +62 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-03-ai-phase-gated-planner/slice.json +62 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-04-spec-slice-handoff-pr-generation/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-04-spec-slice-handoff-pr-generation/EXECUTION_BRIEF.md +63 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-04-spec-slice-handoff-pr-generation/slice.json +59 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-05-execution-plan-parallel-worktrees/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-05-execution-plan-parallel-worktrees/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-05-execution-plan-parallel-worktrees/slice.json +59 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-06-ai-execute-slice-scope-enforcement/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-06-ai-execute-slice-scope-enforcement/EXECUTION_BRIEF.md +64 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-06-ai-execute-slice-scope-enforcement/slice.json +65 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-07-github-pr-preflight/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-07-github-pr-preflight/EXECUTION_BRIEF.md +66 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-07-github-pr-preflight/slice.json +63 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-08-docs-smokes-release-readiness/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-08-docs-smokes-release-readiness/EXECUTION_BRIEF.md +64 -0
- package/specs/quiver-v20-ai-cli-orchestration/slices/slice-08-docs-smokes-release-readiness/slice.json +77 -0
- package/specs/quiver-v21-ai-first-layout/EVIDENCE_REPORT.md +31 -0
- package/specs/quiver-v21-ai-first-layout/EXECUTION_PLAN.md +185 -0
- package/specs/quiver-v21-ai-first-layout/SPEC.md +212 -0
- package/specs/quiver-v21-ai-first-layout/STATUS.md +37 -0
- package/specs/quiver-v21-ai-first-layout/pr.md +110 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +63 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-00-spec-foundation/slice.json +45 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-01-init-profiles-dry-run/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-01-init-profiles-dry-run/EXECUTION_BRIEF.md +59 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-01-init-profiles-dry-run/slice.json +57 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-02-internal-layout-template-resolver/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-02-internal-layout-template-resolver/EXECUTION_BRIEF.md +60 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-02-internal-layout-template-resolver/slice.json +58 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-03-generation-profiles-visible-contract/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-03-generation-profiles-visible-contract/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-03-generation-profiles-visible-contract/slice.json +64 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-04-analyze-scan-relocation/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-04-analyze-scan-relocation/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-04-analyze-scan-relocation/slice.json +64 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-05-empty-specs-layout-doctor/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-05-empty-specs-layout-doctor/EXECUTION_BRIEF.md +60 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-05-empty-specs-layout-doctor/slice.json +65 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-06-legacy-migration-optional-assets/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-06-legacy-migration-optional-assets/EXECUTION_BRIEF.md +62 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-06-legacy-migration-optional-assets/slice.json +66 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-07-docs-guidance-alignment/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-07-docs-guidance-alignment/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-07-docs-guidance-alignment/slice.json +67 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-08-smokes-release-readiness/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-08-smokes-release-readiness/EXECUTION_BRIEF.md +66 -0
- package/specs/quiver-v21-ai-first-layout/slices/slice-08-smokes-release-readiness/slice.json +62 -0
- package/src/create-quiver/commands/ai.js +442 -0
- package/src/create-quiver/index.js +421 -84
- package/src/create-quiver/lib/ai/context-packs.js +158 -0
- package/src/create-quiver/lib/ai/execution-plan.js +254 -0
- package/src/create-quiver/lib/ai/executor.js +323 -0
- package/src/create-quiver/lib/ai/github.js +329 -0
- package/src/create-quiver/lib/ai/phase-gates.js +72 -0
- package/src/create-quiver/lib/ai/preflight.js +58 -0
- package/src/create-quiver/lib/ai/prompt-transport.js +81 -0
- package/src/create-quiver/lib/ai/prompts.js +39 -0
- package/src/create-quiver/lib/ai/providers.js +314 -0
- package/src/create-quiver/lib/ai/safety.js +151 -0
- package/src/create-quiver/lib/ai/spec-generator.js +314 -0
- package/src/create-quiver/lib/ai/spec-templates.js +715 -0
- package/src/create-quiver/lib/doctor.js +114 -0
- package/src/create-quiver/lib/git.js +21 -0
- package/src/create-quiver/lib/init-docs.js +286 -25
- package/src/create-quiver/lib/init-layout.js +426 -0
- package/src/create-quiver/lib/lifecycle.js +2 -2
- package/src/create-quiver/lib/paths.js +63 -2
- package/src/create-quiver/lib/project-scan.js +66 -0
- package/src/create-quiver/lib/readiness.js +4 -2
- package/src/create-quiver/lib/scope.js +125 -0
- package/src/create-quiver/lib/slice-graph.js +6 -0
- package/src/create-quiver/lib/slice.js +51 -8
- package/src/create-quiver/lib/state.js +18 -1
- package/src/create-quiver/lib/template-resolver.js +74 -0
- package/.claude/settings.local.json +0 -52
|
@@ -1,5 +1,130 @@
|
|
|
1
|
+
const { statusPorcelain } = require('./git');
|
|
2
|
+
const { normalizeContextPath } = require('./ai/safety');
|
|
1
3
|
const { checkScope } = require('./readiness');
|
|
2
4
|
|
|
5
|
+
class ScopeValidationError extends Error {
|
|
6
|
+
constructor(code, message, details = {}) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'ScopeValidationError';
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.details = details;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function formatError(message) {
|
|
15
|
+
return `create-quiver: ${message}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeScopePath(filePath) {
|
|
19
|
+
return normalizeContextPath(filePath);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseStatusPorcelain(text) {
|
|
23
|
+
if (!text) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return String(text)
|
|
28
|
+
.split('\n')
|
|
29
|
+
.map((line) => line.trimEnd())
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.map((line) => {
|
|
32
|
+
if (line.startsWith('?? ')) {
|
|
33
|
+
return normalizeScopePath(line.slice(3));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const entry = (line[2] === ' ' ? line.slice(3) : line[1] === ' ' ? line.slice(2) : line.slice(3)).trim();
|
|
37
|
+
if (!entry) {
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const renamedTarget = entry.includes(' -> ') ? entry.split(' -> ').pop() : entry;
|
|
42
|
+
return normalizeScopePath(renamedTarget);
|
|
43
|
+
})
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function captureWorktreeSnapshot(repoRoot, options = {}) {
|
|
48
|
+
const raw = typeof options.rawStatus === 'string' ? options.rawStatus : statusPorcelain(repoRoot);
|
|
49
|
+
const files = parseStatusPorcelain(raw);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
repoRoot,
|
|
53
|
+
raw,
|
|
54
|
+
files,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function diffWorktreeSnapshots(beforeSnapshot, afterSnapshot) {
|
|
59
|
+
const beforeFiles = new Set((beforeSnapshot && Array.isArray(beforeSnapshot.files) ? beforeSnapshot.files : []).map(normalizeScopePath));
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
const changedFiles = [];
|
|
62
|
+
|
|
63
|
+
for (const file of afterSnapshot && Array.isArray(afterSnapshot.files) ? afterSnapshot.files : []) {
|
|
64
|
+
const normalized = normalizeScopePath(file);
|
|
65
|
+
if (!normalized || beforeFiles.has(normalized) || seen.has(normalized)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
seen.add(normalized);
|
|
70
|
+
changedFiles.push(normalized);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return changedFiles;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function validateScopeSnapshot({ allowedFiles = [], beforeSnapshot, afterSnapshot, strict = true } = {}) {
|
|
77
|
+
const normalizedAllowedFiles = new Set(
|
|
78
|
+
Array.isArray(allowedFiles)
|
|
79
|
+
? allowedFiles.map(normalizeScopePath).filter(Boolean)
|
|
80
|
+
: [],
|
|
81
|
+
);
|
|
82
|
+
const changedFiles = diffWorktreeSnapshots(beforeSnapshot, afterSnapshot);
|
|
83
|
+
const outOfScopeFiles = changedFiles.filter((file) => !normalizedAllowedFiles.has(file));
|
|
84
|
+
|
|
85
|
+
if (outOfScopeFiles.length === 0) {
|
|
86
|
+
return {
|
|
87
|
+
ok: true,
|
|
88
|
+
changedFiles,
|
|
89
|
+
outOfScopeFiles,
|
|
90
|
+
allowedFiles: Array.from(normalizedAllowedFiles),
|
|
91
|
+
beforeSnapshot,
|
|
92
|
+
afterSnapshot,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const message = formatError(
|
|
97
|
+
`scope violation detected: changed files outside slice.json.files: ${outOfScopeFiles.join(', ')}`,
|
|
98
|
+
);
|
|
99
|
+
const error = new ScopeValidationError('SCOPE_VIOLATION', message, {
|
|
100
|
+
allowedFiles: Array.from(normalizedAllowedFiles),
|
|
101
|
+
beforeSnapshot,
|
|
102
|
+
afterSnapshot,
|
|
103
|
+
changedFiles,
|
|
104
|
+
outOfScopeFiles,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (strict) {
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
changedFiles,
|
|
114
|
+
outOfScopeFiles,
|
|
115
|
+
allowedFiles: Array.from(normalizedAllowedFiles),
|
|
116
|
+
beforeSnapshot,
|
|
117
|
+
afterSnapshot,
|
|
118
|
+
error,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
3
122
|
module.exports = {
|
|
123
|
+
ScopeValidationError,
|
|
124
|
+
captureWorktreeSnapshot,
|
|
125
|
+
diffWorktreeSnapshots,
|
|
4
126
|
checkScope,
|
|
127
|
+
normalizeScopePath,
|
|
128
|
+
parseStatusPorcelain,
|
|
129
|
+
validateScopeSnapshot,
|
|
5
130
|
};
|
|
@@ -34,6 +34,10 @@ function naturalNumberFromSliceId(sliceId) {
|
|
|
34
34
|
return match ? Number.parseInt(match[1], 10) : Number.POSITIVE_INFINITY;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function isFoundationSliceId(sliceId) {
|
|
38
|
+
return naturalNumberFromSliceId(sliceId) === 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
function compareSliceRefs(a, b) {
|
|
38
42
|
const left = String(a || '');
|
|
39
43
|
const right = String(b || '');
|
|
@@ -448,6 +452,8 @@ module.exports = {
|
|
|
448
452
|
computeLevels,
|
|
449
453
|
detectFileConflicts,
|
|
450
454
|
inferDependencies,
|
|
455
|
+
isFoundationSliceId,
|
|
451
456
|
readAllSlices,
|
|
457
|
+
naturalNumberFromSliceId,
|
|
452
458
|
topoSort,
|
|
453
459
|
};
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { parseJsonWithComments } = require('./json');
|
|
4
|
-
const { resolveTargetRoot, toPosixPath } = require('./paths');
|
|
4
|
+
const { normalizeGitBashDrivePath, relativePosixPath, resolveTargetRoot, specRelativePathFromPath, toPosixPath } = require('./paths');
|
|
5
5
|
|
|
6
6
|
function readJson(filePath) {
|
|
7
7
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
function canonicalizePath(dirPath) {
|
|
11
|
+
const normalizedPath = normalizeGitBashDrivePath(dirPath);
|
|
11
12
|
try {
|
|
12
|
-
return fs.realpathSync(
|
|
13
|
+
return normalizeGitBashDrivePath(fs.realpathSync(normalizedPath));
|
|
13
14
|
} catch {
|
|
14
|
-
return path.resolve(
|
|
15
|
+
return path.resolve(normalizedPath);
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -94,19 +95,61 @@ function validateSliceMetaForStart(slice) {
|
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
function isSpecRelativePath(parts) {
|
|
99
|
+
return parts[0] === 'specs' || parts[0] === 'specs-fix';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function resolveRepoSlicePath(repoRoot, relSlicePath) {
|
|
103
|
+
const candidate = path.join(repoRoot, relSlicePath);
|
|
104
|
+
if (!fs.existsSync(candidate)) {
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return canonicalizePath(candidate);
|
|
109
|
+
}
|
|
110
|
+
|
|
97
111
|
function resolveSliceContext(repoRoot, slicePath) {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
const canonicalRepoRoot = canonicalizePath(repoRoot);
|
|
113
|
+
let absSlicePath = resolveSlicePath(slicePath);
|
|
114
|
+
let relSlicePath = relativePosixPath(canonicalRepoRoot, absSlicePath);
|
|
115
|
+
let parts = relSlicePath.split('/');
|
|
116
|
+
|
|
117
|
+
if (!isSpecRelativePath(parts)) {
|
|
118
|
+
const cwdRelSlicePath = relativePosixPath(canonicalizePath(process.cwd()), absSlicePath);
|
|
119
|
+
const cwdParts = cwdRelSlicePath.split('/');
|
|
120
|
+
if (isSpecRelativePath(cwdParts)) {
|
|
121
|
+
relSlicePath = cwdRelSlicePath;
|
|
122
|
+
parts = cwdParts;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!isSpecRelativePath(parts) && !path.isAbsolute(slicePath)) {
|
|
127
|
+
const inputRelSlicePath = toPosixPath(slicePath).replace(/^\.\/+/, '');
|
|
128
|
+
const inputParts = inputRelSlicePath.split('/');
|
|
129
|
+
if (isSpecRelativePath(inputParts)) {
|
|
130
|
+
relSlicePath = inputRelSlicePath;
|
|
131
|
+
parts = inputParts;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!isSpecRelativePath(parts)) {
|
|
136
|
+
const candidateRelSlicePath = specRelativePathFromPath(absSlicePath) || specRelativePathFromPath(slicePath);
|
|
137
|
+
const candidateAbsSlicePath = candidateRelSlicePath ? resolveRepoSlicePath(canonicalRepoRoot, candidateRelSlicePath) : '';
|
|
138
|
+
if (candidateAbsSlicePath) {
|
|
139
|
+
relSlicePath = candidateRelSlicePath;
|
|
140
|
+
parts = relSlicePath.split('/');
|
|
141
|
+
absSlicePath = candidateAbsSlicePath;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
101
144
|
|
|
102
|
-
if (parts
|
|
145
|
+
if (!isSpecRelativePath(parts)) {
|
|
103
146
|
throw new Error('create-quiver: el slice debe vivir dentro de specs/ o specs-fix/.');
|
|
104
147
|
}
|
|
105
148
|
|
|
106
149
|
const specFamily = parts[0];
|
|
107
150
|
const specSlug = parts[1];
|
|
108
151
|
const specDirRel = `${specFamily}/${specSlug}`;
|
|
109
|
-
const specDirAbs = path.join(
|
|
152
|
+
const specDirAbs = path.join(canonicalRepoRoot, specDirRel);
|
|
110
153
|
const slice = readSliceMeta(absSlicePath);
|
|
111
154
|
slice.specFamily = specFamily;
|
|
112
155
|
slice.specSlug = specSlug;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const { quiverInternalPaths } = require('./init-layout');
|
|
3
4
|
|
|
4
5
|
function statePath(projectRoot) {
|
|
5
|
-
return
|
|
6
|
+
return quiverInternalPaths(projectRoot).statePath;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
function ensureDir(dirPath) {
|
|
@@ -60,6 +61,21 @@ function hasLegacyQuiverInitializationEvidence(projectRoot) {
|
|
|
60
61
|
&& hasGeneratedProjectSpec(projectRoot);
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
function inspectLegacyMigrationLayout(projectRoot) {
|
|
65
|
+
const candidates = [
|
|
66
|
+
'docs-template/',
|
|
67
|
+
'tools/scripts/',
|
|
68
|
+
'docs/PROJECT_SCAN.json',
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
const detected = candidates.filter((relativePath) => fs.existsSync(path.join(projectRoot, relativePath)));
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
hasLegacyLayout: detected.length > 0,
|
|
75
|
+
legacyPaths: detected,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
63
79
|
function hasQuiverInitializationEvidence(projectRoot) {
|
|
64
80
|
const state = readState(projectRoot);
|
|
65
81
|
return hasInitializedStateMetadata(state) || hasLegacyQuiverInitializationEvidence(projectRoot);
|
|
@@ -129,6 +145,7 @@ module.exports = {
|
|
|
129
145
|
hasGeneratedProjectSpec,
|
|
130
146
|
hasInitializedStateMetadata,
|
|
131
147
|
hasLegacyQuiverInitializationEvidence,
|
|
148
|
+
inspectLegacyMigrationLayout,
|
|
132
149
|
hasQuiverInitializationEvidence,
|
|
133
150
|
readState,
|
|
134
151
|
statePath,
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
function resolvePackagedTemplateRoot(packageRoot = path.resolve(__dirname, '../../..')) {
|
|
5
|
+
return packageRoot;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function resolveExportedTemplateRoot(projectRoot) {
|
|
9
|
+
return path.join(projectRoot, '.quiver', 'templates');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function resolveLegacyTemplateRoot(projectRoot) {
|
|
13
|
+
return path.join(projectRoot, 'docs-template');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function templateRootExists(templateRoot) {
|
|
17
|
+
return fs.existsSync(path.join(templateRoot, 'docs'))
|
|
18
|
+
&& fs.existsSync(path.join(templateRoot, 'specs'))
|
|
19
|
+
&& fs.existsSync(path.join(templateRoot, 'package.template.json'));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resolveTemplateRoot(projectRoot, options = {}) {
|
|
23
|
+
const packageRoot = options.packageRoot || resolvePackagedTemplateRoot();
|
|
24
|
+
const candidates = [];
|
|
25
|
+
|
|
26
|
+
if (options.preferExported === true) {
|
|
27
|
+
candidates.push({
|
|
28
|
+
kind: 'exported',
|
|
29
|
+
path: resolveExportedTemplateRoot(projectRoot),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
candidates.push({
|
|
34
|
+
kind: 'packaged',
|
|
35
|
+
path: resolvePackagedTemplateRoot(packageRoot),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
candidates.push({
|
|
39
|
+
kind: 'legacy',
|
|
40
|
+
path: resolveLegacyTemplateRoot(projectRoot),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
for (const candidate of candidates) {
|
|
44
|
+
if (templateRootExists(candidate.path)) {
|
|
45
|
+
return candidate;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const searched = candidates.map((candidate) => `${candidate.kind}: ${candidate.path}`).join(', ');
|
|
50
|
+
throw new Error(`create-quiver: missing Quiver templates. Searched ${searched}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveTemplatePath(projectRoot, relativePath, options = {}) {
|
|
54
|
+
const root = resolveTemplateRoot(projectRoot, options);
|
|
55
|
+
const templatePath = path.join(root.path, relativePath);
|
|
56
|
+
|
|
57
|
+
if (!fs.existsSync(templatePath)) {
|
|
58
|
+
throw new Error(`create-quiver: missing template ${relativePath} in ${root.path}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
...root,
|
|
63
|
+
templatePath,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
resolveExportedTemplateRoot,
|
|
69
|
+
resolveLegacyTemplateRoot,
|
|
70
|
+
resolvePackagedTemplateRoot,
|
|
71
|
+
resolveTemplatePath,
|
|
72
|
+
resolveTemplateRoot,
|
|
73
|
+
templateRootExists,
|
|
74
|
+
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(git fetch *)",
|
|
5
|
-
"Bash(git checkout *)",
|
|
6
|
-
"Bash(git add *)",
|
|
7
|
-
"Bash(git commit -m ' *)",
|
|
8
|
-
"Bash(git cherry-pick *)",
|
|
9
|
-
"Bash(git reset *)",
|
|
10
|
-
"Bash(gh pr create --title 'docs: register v17-v22 orchestration plan and park web console idea' --base main --head docs/register-orchestration-plan-park-web-console --body ' *)",
|
|
11
|
-
"Bash(gh auth *)",
|
|
12
|
-
"Bash(gh pr *)",
|
|
13
|
-
"Bash(node -e \"const g=require\\('./src/create-quiver/lib/slice-graph'\\); const slices=g.readAllSlices\\('.'\\); console.log\\('Total slices:', slices.length\\); const graph=g.buildGraph\\(slices\\); const levels=g.computeLevels\\(graph\\); console.log\\('Levels:', levels.length\\);\")",
|
|
14
|
-
"Bash(npx . plan)",
|
|
15
|
-
"Bash(git pull *)",
|
|
16
|
-
"Read(//Users/fabrijk/Documents/Work/Proyectos Personales/nika/frameworks/.worktrees/quiver/bugfix-QUIVER-01-fix-legacy-dependency-resolution/**)",
|
|
17
|
-
"Bash(node --test tests/)",
|
|
18
|
-
"Bash(node --test tests/**/*.test.js)",
|
|
19
|
-
"Bash(node bin/create-quiver.js plan)",
|
|
20
|
-
"Bash(echo \"EXIT_$?\")",
|
|
21
|
-
"Bash(node bin/create-quiver.js plan --json)",
|
|
22
|
-
"Bash(node -e \"const d=JSON.parse\\(require\\('fs'\\).readFileSync\\('/dev/stdin','utf8'\\)\\); console.log\\('plan items:', Array.isArray\\(d.plan\\) ? d.plan.length : 'NOT ARRAY'\\); console.log\\('critical_path:', Array.isArray\\(d.critical_path\\)\\); console.log\\('total_hours:', typeof d.total_hours\\); process.exit\\(Array.isArray\\(d.plan\\) && Array.isArray\\(d.critical_path\\) && typeof d.total_hours === 'number' ? 0 : 1\\)\")",
|
|
23
|
-
"Bash(grep -v \"^\\\\-\\\\-$\")",
|
|
24
|
-
"Bash(echo \"EXIT:$?\")",
|
|
25
|
-
"Bash(node -e \"const d=JSON.parse\\(require\\('fs'\\).readFileSync\\('/tmp/plan_json.txt','utf8'\\)\\); console.log\\('plan:', Array.isArray\\(d.plan\\) ? d.plan.length + ' items' : 'INVALID'\\); console.log\\('critical_path:', Array.isArray\\(d.critical_path\\)\\); console.log\\('total_hours:', typeof d.total_hours\\)\")",
|
|
26
|
-
"Bash(xargs git branch -d)",
|
|
27
|
-
"Bash(git branch *)",
|
|
28
|
-
"Read(//private/tmp/**)",
|
|
29
|
-
"Read(//Users/fabrijk/Documents/Work/Proyectos Personales/nika/frameworks/quiver-v18-slice02/**)",
|
|
30
|
-
"Bash(git worktree *)",
|
|
31
|
-
"Bash(git ls-remote *)",
|
|
32
|
-
"Bash(node *)",
|
|
33
|
-
"Bash(npm view *)",
|
|
34
|
-
"Bash(bash scripts/release-quiver.sh)",
|
|
35
|
-
"Bash(git stash *)",
|
|
36
|
-
"Bash(npm whoami *)",
|
|
37
|
-
"Bash(bash scripts/release-quiver.sh --publish-current)",
|
|
38
|
-
"Bash(npm pkg *)",
|
|
39
|
-
"Bash(NPM_TOKEN=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 npm publish --access public)",
|
|
40
|
-
"Bash(NPM_TOKEN=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 npm publish --access public)",
|
|
41
|
-
"Bash(npm info *)",
|
|
42
|
-
"Bash(npm publish *)",
|
|
43
|
-
"Bash(rm -rf /tmp/quiver-skip-test)",
|
|
44
|
-
"Bash(mkdir -p /tmp/quiver-skip-test)",
|
|
45
|
-
"Bash(echo '{\"name\":\"test\",\"scripts\":{}}')",
|
|
46
|
-
"Bash(ls /tmp/quiver-skip-test/node_modules/create-quiver)",
|
|
47
|
-
"Bash(rm -rf /tmp/quiver-install-test)",
|
|
48
|
-
"Bash(mkdir -p /tmp/quiver-install-test)",
|
|
49
|
-
"Bash(npm config *)"
|
|
50
|
-
]
|
|
51
|
-
}
|
|
52
|
-
}
|