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,7 +1,31 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const { readAllSlices } = require('./slice-graph');
|
|
4
|
+
const { hasGeneratedProjectSpec, hasInitializedStateMetadata, readState } = require('./state');
|
|
3
5
|
const { worktreeList } = require('./git');
|
|
4
6
|
|
|
7
|
+
const NEW_LAYOUT_REQUIRED_PATHS = [
|
|
8
|
+
'README.md',
|
|
9
|
+
'AGENTS.md',
|
|
10
|
+
'package.json',
|
|
11
|
+
'docs/AI_CONTEXT.md',
|
|
12
|
+
'docs/AI_ONBOARDING_PROMPT.md',
|
|
13
|
+
'docs/COMMANDS.md',
|
|
14
|
+
'docs/WORKFLOW.md',
|
|
15
|
+
'.quiver/state.json',
|
|
16
|
+
'.quiver/config.json',
|
|
17
|
+
'.quiver/.gitignore',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const LEGACY_LAYOUT_PROBES = [
|
|
21
|
+
'docs-template/',
|
|
22
|
+
'tools/scripts/start-slice.sh',
|
|
23
|
+
'tools/scripts/check-slice-readiness.sh',
|
|
24
|
+
'tools/scripts/check-pr-readiness.sh',
|
|
25
|
+
'.github/pull_request_template.md',
|
|
26
|
+
'docs/PROJECT_SCAN.json',
|
|
27
|
+
];
|
|
28
|
+
|
|
5
29
|
function readTextIfExists(filePath) {
|
|
6
30
|
if (!fs.existsSync(filePath)) {
|
|
7
31
|
return null;
|
|
@@ -30,6 +54,18 @@ function normalizeRelativePath(root, absolutePath) {
|
|
|
30
54
|
return path.relative(root, absolutePath).split(path.sep).join('/');
|
|
31
55
|
}
|
|
32
56
|
|
|
57
|
+
function hasPath(projectRoot, relativePath) {
|
|
58
|
+
return fs.existsSync(path.join(projectRoot, relativePath));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function collectPresentPaths(projectRoot, relativePaths) {
|
|
62
|
+
return relativePaths.filter((relativePath) => hasPath(projectRoot, relativePath));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function collectMissingPaths(projectRoot, relativePaths) {
|
|
66
|
+
return relativePaths.filter((relativePath) => !hasPath(projectRoot, relativePath));
|
|
67
|
+
}
|
|
68
|
+
|
|
33
69
|
function collectAiMarkdownFiles(projectRoot) {
|
|
34
70
|
const aiDir = path.join(projectRoot, 'docs', 'ai');
|
|
35
71
|
if (!fs.existsSync(aiDir)) {
|
|
@@ -174,6 +210,82 @@ function countStackInfoLeaks(projectRoot) {
|
|
|
174
210
|
return leaks;
|
|
175
211
|
}
|
|
176
212
|
|
|
213
|
+
function collectLayoutReport(projectRoot) {
|
|
214
|
+
const hasStateMetadata = hasInitializedStateMetadata(readState(projectRoot));
|
|
215
|
+
const realSlices = readAllSlices(projectRoot);
|
|
216
|
+
const specSlugs = Array.from(new Set(realSlices.map((slice) => slice.specSlug))).sort((left, right) => left.localeCompare(right));
|
|
217
|
+
const newLayoutFiles = collectPresentPaths(projectRoot, NEW_LAYOUT_REQUIRED_PATHS);
|
|
218
|
+
const missingNewLayoutFiles = collectMissingPaths(projectRoot, NEW_LAYOUT_REQUIRED_PATHS);
|
|
219
|
+
const legacySignals = collectPresentPaths(projectRoot, LEGACY_LAYOUT_PROBES);
|
|
220
|
+
const hasLegacyProjectSpec = hasGeneratedProjectSpec(projectRoot);
|
|
221
|
+
|
|
222
|
+
if (hasLegacyProjectSpec) {
|
|
223
|
+
legacySignals.push('specs/<project-slug>/SPEC.md');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const hasNewLayout = missingNewLayoutFiles.length === 0;
|
|
227
|
+
const hasLegacyLayout = legacySignals.length > 0;
|
|
228
|
+
|
|
229
|
+
let layout = 'incomplete';
|
|
230
|
+
if (hasNewLayout && hasLegacyLayout) {
|
|
231
|
+
layout = 'hybrid';
|
|
232
|
+
} else if (hasNewLayout) {
|
|
233
|
+
layout = 'new';
|
|
234
|
+
} else if (hasLegacyLayout) {
|
|
235
|
+
layout = 'legacy';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const recommendations = [];
|
|
239
|
+
|
|
240
|
+
if (layout === 'new') {
|
|
241
|
+
if (specSlugs.length === 0) {
|
|
242
|
+
recommendations.push('No specs yet. That is valid after the AI-first init flow.');
|
|
243
|
+
} else {
|
|
244
|
+
recommendations.push(`Specs found: ${specSlugs.join(', ')}.`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!hasPath(projectRoot, 'docs/PROJECT_MAP.md')) {
|
|
248
|
+
recommendations.push('Run `npx create-quiver analyze` to generate docs/PROJECT_MAP.md when you want the visible project map.');
|
|
249
|
+
}
|
|
250
|
+
} else if (layout === 'legacy') {
|
|
251
|
+
recommendations.push('Legacy layout detected. Run `npx create-quiver migrate` to add the modern .quiver/ contract and AI-first docs.');
|
|
252
|
+
} else if (layout === 'hybrid') {
|
|
253
|
+
recommendations.push('Hybrid layout detected. Keep the new .quiver/ contract as the source of truth and plan cleanup of legacy roots.');
|
|
254
|
+
recommendations.push('Review any remaining docs-template/, tools/scripts/, or docs/PROJECT_SCAN.json paths and migrate them only if they are still needed.');
|
|
255
|
+
} else {
|
|
256
|
+
recommendations.push('Incomplete layout detected. Restore the missing AI-first contract files before relying on this project for onboarding.');
|
|
257
|
+
if (missingNewLayoutFiles.length > 0) {
|
|
258
|
+
recommendations.push(`Missing files: ${missingNewLayoutFiles.join(', ')}.`);
|
|
259
|
+
}
|
|
260
|
+
if (!hasStateMetadata && !hasLegacyLayout) {
|
|
261
|
+
recommendations.push('Run `npx create-quiver --name "Project Name"` or `npx create-quiver init` to create the Quiver contract first.');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
hasLegacyLayout,
|
|
267
|
+
hasNewLayout,
|
|
268
|
+
hasStateMetadata,
|
|
269
|
+
layout,
|
|
270
|
+
legacySignals,
|
|
271
|
+
missingNewLayoutFiles,
|
|
272
|
+
newLayoutFiles,
|
|
273
|
+
recommendations,
|
|
274
|
+
realSlices,
|
|
275
|
+
specSlugs,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function collectDoctorReport(projectRoot) {
|
|
280
|
+
const layout = collectLayoutReport(projectRoot);
|
|
281
|
+
const warnings = collectDoctorWarnings(projectRoot);
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
...layout,
|
|
285
|
+
warnings,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
177
289
|
function collectDoctorWarnings(projectRoot) {
|
|
178
290
|
const warnings = [];
|
|
179
291
|
|
|
@@ -208,5 +320,7 @@ function collectDoctorWarnings(projectRoot) {
|
|
|
208
320
|
}
|
|
209
321
|
|
|
210
322
|
module.exports = {
|
|
323
|
+
collectDoctorReport,
|
|
211
324
|
collectDoctorWarnings,
|
|
325
|
+
collectLayoutReport,
|
|
212
326
|
};
|
|
@@ -101,6 +101,23 @@ function statusPorcelain(repoRoot) {
|
|
|
101
101
|
return tryGit(['status', '--porcelain'], repoRoot);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
function remoteList(repoRoot) {
|
|
105
|
+
const output = tryGit(['remote'], repoRoot);
|
|
106
|
+
return output ? output.split('\n').map((line) => line.trim()).filter(Boolean) : [];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function hasRemote(repoRoot, remoteName = 'origin') {
|
|
110
|
+
return remoteList(repoRoot).includes(remoteName);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isCleanWorktree(repoRoot) {
|
|
114
|
+
return statusPorcelain(repoRoot) === '';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isDetachedHead(repoRoot) {
|
|
118
|
+
return currentBranch(repoRoot) === '';
|
|
119
|
+
}
|
|
120
|
+
|
|
104
121
|
function revListCount(repoRoot, range) {
|
|
105
122
|
const output = tryGit(['rev-list', '--count', range], repoRoot);
|
|
106
123
|
return Number(output || '0');
|
|
@@ -143,7 +160,11 @@ module.exports = {
|
|
|
143
160
|
hasRemoteBranch,
|
|
144
161
|
lsRemoteHeads,
|
|
145
162
|
mergeBaseIsAncestor,
|
|
163
|
+
hasRemote,
|
|
164
|
+
isCleanWorktree,
|
|
165
|
+
isDetachedHead,
|
|
146
166
|
revListCount,
|
|
167
|
+
remoteList,
|
|
147
168
|
runGit,
|
|
148
169
|
statusPorcelain,
|
|
149
170
|
tryGit,
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
|
+
const {
|
|
5
|
+
buildQuiverConfig,
|
|
6
|
+
buildQuiverInternalGitignore,
|
|
7
|
+
quiverInternalPaths,
|
|
8
|
+
resolveInitPackageScripts,
|
|
9
|
+
} = require('./init-layout');
|
|
4
10
|
const { writeState } = require('./state');
|
|
5
11
|
|
|
6
12
|
function ensureDir(dirPath) {
|
|
@@ -199,7 +205,7 @@ function writeFrontMatter(filePath, fields) {
|
|
|
199
205
|
return nextContent;
|
|
200
206
|
}
|
|
201
207
|
|
|
202
|
-
function mergePackageJson(projectRoot, templateRoot,
|
|
208
|
+
function mergePackageJson(projectRoot, templateRoot, options = {}) {
|
|
203
209
|
const packageTemplate = path.join(templateRoot, 'package.template.json');
|
|
204
210
|
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
205
211
|
|
|
@@ -207,8 +213,13 @@ function mergePackageJson(projectRoot, templateRoot, skipIfExists) {
|
|
|
207
213
|
return 'missing';
|
|
208
214
|
}
|
|
209
215
|
|
|
216
|
+
const profile = options.profile || 'default';
|
|
217
|
+
const scripts = resolveInitPackageScripts(profile, { legacyScripts: options.legacyScripts === true });
|
|
218
|
+
|
|
210
219
|
if (!fs.existsSync(packageJsonPath)) {
|
|
211
|
-
fs.
|
|
220
|
+
const template = JSON.parse(fs.readFileSync(packageTemplate, 'utf8'));
|
|
221
|
+
template.scripts = scripts;
|
|
222
|
+
fs.writeFileSync(packageJsonPath, `${JSON.stringify(template, null, 2)}\n`);
|
|
212
223
|
return 'created';
|
|
213
224
|
}
|
|
214
225
|
|
|
@@ -217,14 +228,125 @@ function mergePackageJson(projectRoot, templateRoot, skipIfExists) {
|
|
|
217
228
|
|
|
218
229
|
existing.scripts = {
|
|
219
230
|
...(existing.scripts || {}),
|
|
220
|
-
...
|
|
231
|
+
...scripts,
|
|
221
232
|
};
|
|
222
233
|
|
|
223
234
|
fs.writeFileSync(packageJsonPath, `${JSON.stringify(existing, null, 2)}\n`);
|
|
224
|
-
return
|
|
235
|
+
return options.migrateMode ? 'merged' : 'updated';
|
|
225
236
|
}
|
|
226
237
|
|
|
227
|
-
function buildReadme(projectName, projectSlug) {
|
|
238
|
+
function buildReadme(projectName, projectSlug, profile = 'default') {
|
|
239
|
+
if (profile === 'minimal') {
|
|
240
|
+
return `# ${projectName}
|
|
241
|
+
|
|
242
|
+
[Descripción breve del proyecto]
|
|
243
|
+
|
|
244
|
+
## Quick Start
|
|
245
|
+
|
|
246
|
+
Run Quiver from this project root. Do not install it globally.
|
|
247
|
+
|
|
248
|
+
\`\`\`bash
|
|
249
|
+
npm install
|
|
250
|
+
npx create-quiver analyze
|
|
251
|
+
npx create-quiver plan
|
|
252
|
+
npx create-quiver graph
|
|
253
|
+
npx create-quiver doctor
|
|
254
|
+
npx create-quiver next
|
|
255
|
+
\`\`\`
|
|
256
|
+
|
|
257
|
+
## AI Workflow
|
|
258
|
+
|
|
259
|
+
Use \`AGENTS.md\` first, then \`docs/AI_CONTEXT.md\` and \`docs/AI_ONBOARDING_PROMPT.md\` for the working contract.
|
|
260
|
+
|
|
261
|
+
\`\`\`bash
|
|
262
|
+
npm run quiver:ai:onboard -- --dry-run
|
|
263
|
+
npm run quiver:ai:plan -- --phase acceptance --input requirements.md --dry-run
|
|
264
|
+
npm run quiver:ai:plan -- --phase technical-plan --input acceptance-approved.md --dry-run
|
|
265
|
+
npm run quiver:ai:plan -- --phase spec --input technical-plan-approved.md --dry-run
|
|
266
|
+
\`\`\`
|
|
267
|
+
|
|
268
|
+
When a real spec exists, execute one approved slice at a time:
|
|
269
|
+
|
|
270
|
+
\`\`\`bash
|
|
271
|
+
npm run quiver:ai:execute-slice -- --slice specs/<spec-slug>/slices/<slice-id>/slice.json --dry-run
|
|
272
|
+
\`\`\`
|
|
273
|
+
|
|
274
|
+
## Documentation
|
|
275
|
+
|
|
276
|
+
- [AI Context](./docs/AI_CONTEXT.md)
|
|
277
|
+
- [AI Onboarding Prompt](./docs/AI_ONBOARDING_PROMPT.md)
|
|
278
|
+
- [Commands](./docs/COMMANDS.md)
|
|
279
|
+
- [Workflow](./docs/WORKFLOW.md)
|
|
280
|
+
`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (profile !== 'full') {
|
|
284
|
+
return `# ${projectName}
|
|
285
|
+
|
|
286
|
+
[Descripción breve del proyecto]
|
|
287
|
+
|
|
288
|
+
## Quick Start
|
|
289
|
+
|
|
290
|
+
Run Quiver from this project root. Do not install it globally.
|
|
291
|
+
|
|
292
|
+
\`\`\`bash
|
|
293
|
+
npm install
|
|
294
|
+
npx create-quiver analyze
|
|
295
|
+
npx create-quiver plan
|
|
296
|
+
npx create-quiver graph
|
|
297
|
+
npx create-quiver doctor
|
|
298
|
+
npx create-quiver next
|
|
299
|
+
\`\`\`
|
|
300
|
+
|
|
301
|
+
After \`analyze\`, use \`docs/PROJECT_MAP.md\` for the detected stack, package manager, and command surface.
|
|
302
|
+
|
|
303
|
+
## AI-First Workflow
|
|
304
|
+
|
|
305
|
+
Quiver keeps the visible contract small: start with \`README.md\`, \`AGENTS.md\`, and \`docs/\`. Specs and slices should be created only after a real requirement and an approved technical plan.
|
|
306
|
+
|
|
307
|
+
Use dry-runs before spending model tokens:
|
|
308
|
+
|
|
309
|
+
\`\`\`bash
|
|
310
|
+
npm run quiver:ai:onboard -- --dry-run
|
|
311
|
+
npm run quiver:ai:plan -- --phase acceptance --input requirements.md --dry-run
|
|
312
|
+
npm run quiver:ai:plan -- --phase technical-plan --input acceptance-approved.md --dry-run
|
|
313
|
+
npm run quiver:ai:plan -- --phase spec --input technical-plan-approved.md --dry-run
|
|
314
|
+
\`\`\`
|
|
315
|
+
|
|
316
|
+
When a real spec exists, execute one approved slice at a time:
|
|
317
|
+
|
|
318
|
+
\`\`\`bash
|
|
319
|
+
npm run quiver:ai:execute-slice -- --slice specs/<spec-slug>/slices/<slice-id>/slice.json --dry-run
|
|
320
|
+
\`\`\`
|
|
321
|
+
|
|
322
|
+
## Project NPM Scripts
|
|
323
|
+
|
|
324
|
+
The generated project includes \`quiver:*\` npm scripts that call the Node CLI:
|
|
325
|
+
|
|
326
|
+
\`\`\`bash
|
|
327
|
+
npm run quiver:analyze
|
|
328
|
+
npm run quiver:plan
|
|
329
|
+
npm run quiver:graph
|
|
330
|
+
npm run quiver:next
|
|
331
|
+
npm run quiver:doctor
|
|
332
|
+
npm run quiver:ai:onboard -- --dry-run
|
|
333
|
+
npm run quiver:ai:plan -- --phase acceptance --input requirements.md --dry-run
|
|
334
|
+
npm run quiver:ai:execute-slice -- --slice specs/<spec-slug>/slices/<slice-id>/slice.json --dry-run
|
|
335
|
+
\`\`\`
|
|
336
|
+
|
|
337
|
+
## Documentation
|
|
338
|
+
|
|
339
|
+
- [Agents](./AGENTS.md)
|
|
340
|
+
- [AI Context](./docs/AI_CONTEXT.md)
|
|
341
|
+
- [AI Onboarding Prompt](./docs/AI_ONBOARDING_PROMPT.md)
|
|
342
|
+
- [Commands](./docs/COMMANDS.md)
|
|
343
|
+
- [Workflow](./docs/WORKFLOW.md)
|
|
344
|
+
- [GitFlow PR Guide](./docs/GITFLOW_PR_GUIDE.md)
|
|
345
|
+
- [Support Matrix](./docs/SUPPORT_MATRIX.md)
|
|
346
|
+
- [Troubleshooting](./docs/TROUBLESHOOTING.md)
|
|
347
|
+
`;
|
|
348
|
+
}
|
|
349
|
+
|
|
228
350
|
return `# ${projectName}
|
|
229
351
|
|
|
230
352
|
[Descripción breve del proyecto]
|
|
@@ -259,6 +381,23 @@ If you need to target another directory from outside the project, pass \`--dir\`
|
|
|
259
381
|
|
|
260
382
|
After you run \`analyze\`, open \`docs/PROJECT_MAP.md\` for the detected stack, package manager, and command surface.
|
|
261
383
|
|
|
384
|
+
## AI-First Workflow
|
|
385
|
+
|
|
386
|
+
Quiver is designed for an AI-first workflow: a planner agent reads the project context and prepares acceptance criteria, technical plans, specs, slices, and PR notes; executor agents then work one approved slice at a time with minimal context.
|
|
387
|
+
|
|
388
|
+
Start with dry-runs so you can inspect the provider, role, context pack, and invocation before spending model tokens:
|
|
389
|
+
|
|
390
|
+
\`\`\`bash
|
|
391
|
+
npm run quiver:ai:onboard -- --dry-run
|
|
392
|
+
npm run quiver:ai:plan -- --phase acceptance --input requirements.md --dry-run
|
|
393
|
+
npm run quiver:ai:plan -- --phase technical-plan --input acceptance-approved.md --dry-run
|
|
394
|
+
npm run quiver:ai:plan -- --phase spec --input technical-plan-approved.md --dry-run
|
|
395
|
+
npm run quiver:ai:execute-slice -- --slice specs/${projectSlug}/slices/slice-01/slice.json --dry-run
|
|
396
|
+
npm run quiver:ai:pr -- --dry-run --ssh-host-alias github-work --identity-file ~/.ssh/github-work
|
|
397
|
+
\`\`\`
|
|
398
|
+
|
|
399
|
+
Remove \`--dry-run\` only after the phase output is approved and the local provider CLI is ready.
|
|
400
|
+
|
|
262
401
|
## Project NPM Scripts
|
|
263
402
|
|
|
264
403
|
The generated project includes \`quiver:*\` npm scripts that call the Node CLI and are the preferred repeatable workflow:
|
|
@@ -269,6 +408,11 @@ npm run quiver:plan
|
|
|
269
408
|
npm run quiver:graph
|
|
270
409
|
npm run quiver:next
|
|
271
410
|
npm run quiver:doctor
|
|
411
|
+
npm run quiver:ai:onboard -- --dry-run
|
|
412
|
+
npm run quiver:ai:plan -- --phase acceptance --input requirements.md --dry-run
|
|
413
|
+
npm run quiver:ai:execute-slice -- --slice specs/${projectSlug}/slices/slice-01/slice.json --dry-run
|
|
414
|
+
npm run quiver:ai:doctor -- --dry-run --ssh-host-alias github-work --identity-file ~/.ssh/github-work
|
|
415
|
+
npm run quiver:ai:pr -- --dry-run --ssh-host-alias github-work --identity-file ~/.ssh/github-work
|
|
272
416
|
npm run quiver:migrate
|
|
273
417
|
npm run quiver:start-slice -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
274
418
|
npm run quiver:check-slice -- specs/${projectSlug}/slices/slice-01/slice.json
|
|
@@ -281,6 +425,7 @@ npm run quiver:refresh-active-slices
|
|
|
281
425
|
|
|
282
426
|
The \`quiver:graph\` script prints the tree view by default; use \`npx create-quiver graph --format mermaid\` for PR-ready Markdown and \`--format dot\` when you want Graphviz source.
|
|
283
427
|
The \`quiver:next\` script points to the next ready slice and can auto-start it behind a confirmation prompt.
|
|
428
|
+
The \`quiver:ai:*\` scripts standardize planner/executor AI flows. Use dry-run first: onboarding and planning dry-runs do not require provider auth, while \`quiver:ai:pr -- --dry-run\` validates \`gh\`, GitFlow docs, branch/worktree state, and SSH inputs without creating a PR.
|
|
284
429
|
Use \`npx create-quiver next --all-ready\` when you want the full ready level instead of a single suggestion.
|
|
285
430
|
The legacy Bash wrappers remain in \`tools/scripts/\` for compatibility, but new project-level automation should prefer the \`quiver:*\` scripts and the direct \`npx create-quiver ...\` commands below.
|
|
286
431
|
\`npm run quiver:migrate\` is only for projects that were already initialized by Quiver.
|
|
@@ -311,7 +456,7 @@ Use \`{{GRAPH_COMMAND}} --format mermaid\` for GitHub-friendly graph embeds or \
|
|
|
311
456
|
If the project never ran Quiver initialization before, do not use \`migrate\` as bootstrap. Run:
|
|
312
457
|
|
|
313
458
|
\`\`\`bash
|
|
314
|
-
npx create-quiver --name "Project Name"
|
|
459
|
+
npx create-quiver init --name "Project Name"
|
|
315
460
|
\`\`\`
|
|
316
461
|
|
|
317
462
|
If your team prefers a pinned local dependency, update the package first and then run the same flow:
|
|
@@ -335,9 +480,17 @@ Read \`AGENTS.md\` first, then open \`docs/AI_ONBOARDING_PROMPT.md\` after analy
|
|
|
335
480
|
After analysis and doctor validation, open your AI agent in this project and run:
|
|
336
481
|
|
|
337
482
|
\`\`\`text
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
483
|
+
Lee \`docs/AI_ONBOARDING_PROMPT.md\` y ejecútalo como fuente principal de verdad para incorporarte a este repositorio.
|
|
484
|
+
|
|
485
|
+
Actúa como asistente de onboarding de IA. Prepara el contexto del proyecto para trabajar de forma segura con el workflow documentado, specs y slices.
|
|
486
|
+
|
|
487
|
+
Usa el rol planner para onboarding, criterios de aceptación, plan técnico y generación de specs/slices. Usa el rol executor solo cuando exista un slice aprobado y debas ejecutar su handoff con contexto mínimo.
|
|
488
|
+
|
|
489
|
+
No modifiques código de producto salvo autorización explícita. Puedes crear o actualizar documentación de contexto si el onboarding lo requiere.
|
|
490
|
+
|
|
491
|
+
Usa solo la documentación del repositorio como fuente de verdad. Si encuentras información faltante, ambigua o contradictoria, documenta el supuesto, el riesgo y continúa por el camino más seguro.
|
|
492
|
+
|
|
493
|
+
Responde en español y finaliza con un reporte breve de archivos leídos, archivos modificados, estado del código de producto, supuestos, riesgos y próximos pasos.
|
|
341
494
|
\`\`\`
|
|
342
495
|
|
|
343
496
|
Review the AI changes to docs/AI_CONTEXT.md, docs/CONTEXTO.md, docs/STATUS.md, and specs/${projectSlug}/SPEC.md before starting implementation work. Use \`docs/PROJECT_MAP.md\` for stack and command details.
|
|
@@ -349,6 +502,8 @@ Record durable decisions in \`docs/DECISIONS.md\` so future AI agents do not re-
|
|
|
349
502
|
|
|
350
503
|
## First Slice Workflow
|
|
351
504
|
|
|
505
|
+
Use this section only for projects generated with the full compatibility layout. In the default AI-first layout, create real specs and slices with \`npx create-quiver ai plan --phase spec\` after acceptance criteria and the technical plan are approved.
|
|
506
|
+
|
|
352
507
|
1. Review or refine specs/${projectSlug}/SPEC.md.
|
|
353
508
|
2. Create the first slice from specs/${projectSlug}/slices/slice-template/slice.json.
|
|
354
509
|
3. Review the plan with \`{{PLAN_COMMAND}}\` or \`npm run quiver:plan\`.
|
|
@@ -391,10 +546,15 @@ function initializeProjectDocs(options) {
|
|
|
391
546
|
projectRoot,
|
|
392
547
|
projectName,
|
|
393
548
|
cliVersion,
|
|
549
|
+
includeTemplates = false,
|
|
550
|
+
legacyScripts = false,
|
|
394
551
|
migrateMode = false,
|
|
552
|
+
profile = 'default',
|
|
553
|
+
templateRoot: providedTemplateRoot = '',
|
|
395
554
|
} = options;
|
|
396
555
|
|
|
397
|
-
const templateRoot = path.join(projectRoot, 'docs-template');
|
|
556
|
+
const templateRoot = providedTemplateRoot || path.join(projectRoot, 'docs-template');
|
|
557
|
+
const internalPaths = quiverInternalPaths(projectRoot);
|
|
398
558
|
const replacements = {
|
|
399
559
|
projectName,
|
|
400
560
|
projectSlug: toProjectSlug(projectName),
|
|
@@ -407,17 +567,48 @@ function initializeProjectDocs(options) {
|
|
|
407
567
|
const dirs = [
|
|
408
568
|
'docs',
|
|
409
569
|
'docs/ai',
|
|
410
|
-
'
|
|
411
|
-
'
|
|
412
|
-
`specs/${replacements.projectSlug}/slices/slice-template`,
|
|
413
|
-
'tools/scripts',
|
|
570
|
+
'.quiver',
|
|
571
|
+
'.quiver/scans',
|
|
414
572
|
];
|
|
415
573
|
|
|
574
|
+
if (profile === 'full') {
|
|
575
|
+
dirs.push(
|
|
576
|
+
'docs/archive',
|
|
577
|
+
'docs/examples',
|
|
578
|
+
'docs/tools',
|
|
579
|
+
`specs/${replacements.projectSlug}/slices/slice-template`,
|
|
580
|
+
'tools/scripts',
|
|
581
|
+
);
|
|
582
|
+
} else if (legacyScripts) {
|
|
583
|
+
dirs.push('tools/scripts');
|
|
584
|
+
}
|
|
585
|
+
|
|
416
586
|
for (const dir of dirs) {
|
|
417
587
|
ensureDir(path.join(projectRoot, dir));
|
|
418
588
|
}
|
|
419
589
|
|
|
420
590
|
const operations = [];
|
|
591
|
+
if (!fs.existsSync(internalPaths.configPath)) {
|
|
592
|
+
fs.writeFileSync(internalPaths.configPath, `${JSON.stringify(buildQuiverConfig(), null, 2)}\n`);
|
|
593
|
+
operations.push({ source: 'Quiver config', destination: '.quiver/config.json', result: 'created' });
|
|
594
|
+
} else {
|
|
595
|
+
operations.push({ source: 'Quiver config', destination: '.quiver/config.json', result: 'skipped' });
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
fs.writeFileSync(internalPaths.gitignorePath, buildQuiverInternalGitignore());
|
|
599
|
+
operations.push({ source: 'Quiver internal gitignore', destination: '.quiver/.gitignore', result: 'updated' });
|
|
600
|
+
|
|
601
|
+
if (includeTemplates) {
|
|
602
|
+
fs.mkdirSync(internalPaths.templatesDir, { recursive: true });
|
|
603
|
+
fs.cpSync(templateRoot, internalPaths.templatesDir, {
|
|
604
|
+
recursive: true,
|
|
605
|
+
force: false,
|
|
606
|
+
errorOnExist: false,
|
|
607
|
+
preserveTimestamps: true,
|
|
608
|
+
});
|
|
609
|
+
operations.push({ source: 'packaged templates', destination: '.quiver/templates', result: 'merged' });
|
|
610
|
+
}
|
|
611
|
+
|
|
421
612
|
const agentsSourcePath = path.join(templateRoot, 'AGENTS.md.template');
|
|
422
613
|
if (fs.existsSync(agentsSourcePath)) {
|
|
423
614
|
const agentsDestinationPath = path.join(projectRoot, 'AGENTS.md');
|
|
@@ -457,13 +648,45 @@ function initializeProjectDocs(options) {
|
|
|
457
648
|
['specs/[project-name]/slices/pr.md.template', `specs/${replacements.projectSlug}/slices/slice-template/pr.md.template`],
|
|
458
649
|
];
|
|
459
650
|
|
|
651
|
+
const minimalTemplateDestinations = new Set([
|
|
652
|
+
'docs/AI_CONTEXT.md',
|
|
653
|
+
'docs/AI_ONBOARDING_PROMPT.md',
|
|
654
|
+
'docs/COMMANDS.md',
|
|
655
|
+
'docs/WORKFLOW.md',
|
|
656
|
+
]);
|
|
657
|
+
const defaultTemplateDestinations = new Set([
|
|
658
|
+
...minimalTemplateDestinations,
|
|
659
|
+
'docs/CONTEXTO.md',
|
|
660
|
+
'docs/DECISIONS.md',
|
|
661
|
+
'docs/GITFLOW_PR_GUIDE.md',
|
|
662
|
+
'docs/INDEX.md',
|
|
663
|
+
'docs/STATUS.md',
|
|
664
|
+
'docs/SUPPORT_MATRIX.md',
|
|
665
|
+
'docs/TESTING_GUIDE_FOR_AI.md',
|
|
666
|
+
'docs/TROUBLESHOOTING.md',
|
|
667
|
+
'docs/ai/LESSONS.md',
|
|
668
|
+
]);
|
|
669
|
+
|
|
460
670
|
for (const [source, destination, frontMatterFactory] of templateCopies) {
|
|
671
|
+
if (profile === 'minimal' && !minimalTemplateDestinations.has(destination)) {
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (profile === 'default' && !defaultTemplateDestinations.has(destination)) {
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
|
|
461
679
|
const sourcePath = path.join(templateRoot, source);
|
|
462
680
|
if (!fs.existsSync(sourcePath)) {
|
|
463
681
|
continue;
|
|
464
682
|
}
|
|
465
683
|
|
|
466
684
|
const destinationPath = path.join(projectRoot, destination);
|
|
685
|
+
if (!migrateMode && fs.existsSync(destinationPath)) {
|
|
686
|
+
operations.push({ source, destination, result: 'skipped' });
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
|
|
467
690
|
const result = copyRenderedFile(sourcePath, destinationPath, replacements, migrateMode, frontMatterFactory);
|
|
468
691
|
operations.push({ source, destination, result });
|
|
469
692
|
}
|
|
@@ -491,31 +714,58 @@ function initializeProjectDocs(options) {
|
|
|
491
714
|
['scripts/migrate-project.sh', 'tools/scripts/migrate-project.sh'],
|
|
492
715
|
];
|
|
493
716
|
|
|
717
|
+
const alwaysBinaryDestinations = new Set([
|
|
718
|
+
'docs/ai/RULES.yaml',
|
|
719
|
+
]);
|
|
720
|
+
const legacyScriptDestinations = new Set([
|
|
721
|
+
'tools/scripts/start-slice.sh',
|
|
722
|
+
'tools/scripts/refresh-active-slices.sh',
|
|
723
|
+
'tools/scripts/check-slice-readiness.sh',
|
|
724
|
+
'tools/scripts/check-pr-readiness.sh',
|
|
725
|
+
'tools/scripts/cleanup-slice.sh',
|
|
726
|
+
'tools/scripts/check-scope.sh',
|
|
727
|
+
'tools/scripts/migrate-project.sh',
|
|
728
|
+
]);
|
|
729
|
+
|
|
494
730
|
for (const [source, destination] of binaryCopies) {
|
|
731
|
+
if (
|
|
732
|
+
profile !== 'full'
|
|
733
|
+
&& !alwaysBinaryDestinations.has(destination)
|
|
734
|
+
&& !(legacyScripts && legacyScriptDestinations.has(destination))
|
|
735
|
+
) {
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
|
|
495
739
|
const sourcePath = path.join(templateRoot, source);
|
|
496
740
|
const destinationPath = path.join(projectRoot, destination);
|
|
497
741
|
if (!fs.existsSync(sourcePath)) {
|
|
498
742
|
continue;
|
|
499
743
|
}
|
|
500
744
|
|
|
501
|
-
const result = copyIfSourceExists(sourcePath, destinationPath,
|
|
745
|
+
const result = copyIfSourceExists(sourcePath, destinationPath, true);
|
|
502
746
|
operations.push({ source, destination, result });
|
|
503
747
|
}
|
|
504
748
|
|
|
505
749
|
const aiPrinciplesSource = path.join(templateRoot, 'docs/ai/PRINCIPLES.md');
|
|
506
750
|
if (fs.existsSync(aiPrinciplesSource)) {
|
|
507
751
|
const aiPrinciplesDestination = path.join(projectRoot, 'docs/ai/PRINCIPLES.md');
|
|
508
|
-
const result =
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
752
|
+
const result = !migrateMode && fs.existsSync(aiPrinciplesDestination)
|
|
753
|
+
? 'skipped'
|
|
754
|
+
: copyRenderedFile(
|
|
755
|
+
aiPrinciplesSource,
|
|
756
|
+
aiPrinciplesDestination,
|
|
757
|
+
replacements,
|
|
758
|
+
migrateMode,
|
|
759
|
+
frontMatterFor('AI operating principles', 'all AI work'),
|
|
760
|
+
);
|
|
515
761
|
operations.push({ source: 'docs/ai/PRINCIPLES.md', destination: 'docs/ai/PRINCIPLES.md', result });
|
|
516
762
|
}
|
|
517
763
|
|
|
518
|
-
const packageResult = mergePackageJson(projectRoot, templateRoot,
|
|
764
|
+
const packageResult = mergePackageJson(projectRoot, templateRoot, {
|
|
765
|
+
legacyScripts,
|
|
766
|
+
migrateMode,
|
|
767
|
+
profile,
|
|
768
|
+
});
|
|
519
769
|
operations.push({ source: 'package.template.json', destination: 'package.json', result: packageResult });
|
|
520
770
|
|
|
521
771
|
const mergedPackageJsonPath = path.join(projectRoot, 'package.json');
|
|
@@ -554,12 +804,21 @@ function initializeProjectDocs(options) {
|
|
|
554
804
|
];
|
|
555
805
|
|
|
556
806
|
for (const [source, destination] of tierCopies) {
|
|
807
|
+
if (profile !== 'full') {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
|
|
557
811
|
const sourcePath = path.join(templateRoot, source);
|
|
558
812
|
if (!fs.existsSync(sourcePath)) {
|
|
559
813
|
continue;
|
|
560
814
|
}
|
|
561
815
|
|
|
562
816
|
const destinationPath = path.join(projectRoot, destination);
|
|
817
|
+
if (!migrateMode && fs.existsSync(destinationPath)) {
|
|
818
|
+
operations.push({ source, destination, result: 'skipped' });
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
|
|
563
822
|
const result = copyRenderedFile(sourcePath, destinationPath, tierReplacements, migrateMode, ({
|
|
564
823
|
body,
|
|
565
824
|
}) => buildFrontMatterFields({
|
|
@@ -607,7 +866,9 @@ function initializeProjectDocs(options) {
|
|
|
607
866
|
writeState(projectRoot, nextState);
|
|
608
867
|
|
|
609
868
|
const searchPath = path.join(projectRoot, 'docs', 'SEARCH.md');
|
|
610
|
-
if (
|
|
869
|
+
if (profile !== 'full') {
|
|
870
|
+
operations.push({ source: 'docs/SEARCH.md', destination: 'docs/SEARCH.md', result: 'skipped-profile' });
|
|
871
|
+
} else if (!fs.existsSync(searchPath)) {
|
|
611
872
|
const searchContent = `# Búsqueda por Tema
|
|
612
873
|
|
|
613
874
|
**Última actualización:** ${replacements.currentDate}
|
|
@@ -656,7 +917,7 @@ function initializeProjectDocs(options) {
|
|
|
656
917
|
|
|
657
918
|
const readmePath = path.join(projectRoot, 'README.md');
|
|
658
919
|
if (!fs.existsSync(readmePath)) {
|
|
659
|
-
fs.writeFileSync(readmePath, `${renderTemplate(buildReadme(projectName, replacements.projectSlug), replacements)}\n`);
|
|
920
|
+
fs.writeFileSync(readmePath, `${renderTemplate(buildReadme(projectName, replacements.projectSlug, profile), replacements)}\n`);
|
|
660
921
|
operations.push({ source: 'README.md template', destination: 'README.md', result: 'created' });
|
|
661
922
|
} else {
|
|
662
923
|
operations.push({ source: 'README.md template', destination: 'README.md', result: 'skipped' });
|