create-quiver 0.17.4 → 0.17.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/docs/CLI_UX_GUIDE.md +7 -0
- package/docs/TROUBLESHOOTING.md +62 -0
- package/docs/reference/commands.md +16 -0
- package/docs/workflows/existing-project-ai-quiver-setup.md +0 -1
- package/docs/workflows/existing-project.md +1 -0
- package/package.json +1 -1
- package/specs/quiver-v43-cli-i18n-audit-release-readiness/command-language-mode-matrix.json +1 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/EVIDENCE_REPORT.md +11 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/EXECUTION_PLAN.md +28 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/SPEC.md +96 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/STATUS.md +26 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/pr.md +56 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-01-document-classification-merge-engine/CLOSURE_BRIEF.md +61 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-01-document-classification-merge-engine/EXECUTION_BRIEF.md +59 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-01-document-classification-merge-engine/pr.md +61 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-01-document-classification-merge-engine/slice.json +71 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-02-apply-integration-validation-contract/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-02-apply-integration-validation-contract/EXECUTION_BRIEF.md +39 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-02-apply-integration-validation-contract/pr.md +62 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-02-apply-integration-validation-contract/slice.json +80 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-03-cli-docs-real-fixture-smoke/CLOSURE_BRIEF.md +29 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-03-cli-docs-real-fixture-smoke/EXECUTION_BRIEF.md +32 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-03-cli-docs-real-fixture-smoke/pr.md +61 -0
- package/specs/quiver-v56-analyze-project-usable-doc-merge/slices/slice-03-cli-docs-real-fixture-smoke/slice.json +79 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/EVIDENCE_REPORT.md +27 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/EXECUTION_PLAN.md +41 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/SPEC.md +139 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/STATUS.md +20 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/pr.md +124 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-01-recovery-contract-security-classifier/CLOSURE_BRIEF.md +22 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-01-recovery-contract-security-classifier/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-01-recovery-contract-security-classifier/pr.md +12 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-01-recovery-contract-security-classifier/slice.json +60 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-02-budget-command-recommendation/CLOSURE_BRIEF.md +9 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-02-budget-command-recommendation/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-02-budget-command-recommendation/pr.md +12 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-02-budget-command-recommendation/slice.json +60 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-03-cli-json-i18n-output/CLOSURE_BRIEF.md +10 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-03-cli-json-i18n-output/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-03-cli-json-i18n-output/pr.md +13 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-03-cli-json-i18n-output/slice.json +68 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-04-integration-fixtures-docs-release-smoke/CLOSURE_BRIEF.md +14 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-04-integration-fixtures-docs-release-smoke/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-04-integration-fixtures-docs-release-smoke/pr.md +14 -0
- package/specs/quiver-v57-evidence-budget-recovery-ux/slices/slice-04-integration-fixtures-docs-release-smoke/slice.json +65 -0
- package/src/create-quiver/commands/ai.js +132 -2
- package/src/create-quiver/lib/ai/analyze-project-docs.js +284 -4
- package/src/create-quiver/lib/ai/analyze-project-proposal.js +11 -0
- package/src/create-quiver/lib/ai/analyze-project-recovery.js +736 -0
- package/src/create-quiver/lib/ai/analyze-project-validation.js +27 -0
- package/src/create-quiver/lib/i18n/messages/en.js +6 -0
- package/src/create-quiver/lib/i18n/messages/es.js +6 -0
|
@@ -15,6 +15,43 @@ const ANALYZE_DOC_PROPOSAL_SCHEMA_VERSION = 1;
|
|
|
15
15
|
const ANALYZE_DOC_PROPOSAL_KIND = 'quiver-analyze-project-doc-proposal';
|
|
16
16
|
const ANALYZE_MANAGED_START = '<!-- quiver:analyze-project:start -->';
|
|
17
17
|
const ANALYZE_MANAGED_END = '<!-- quiver:analyze-project:end -->';
|
|
18
|
+
const CONTEXT_PREP_MANAGED_START = '<!-- quiver:context-prep:start -->';
|
|
19
|
+
const CONTEXT_PREP_MANAGED_END = '<!-- quiver:context-prep:end -->';
|
|
20
|
+
|
|
21
|
+
const CRITICAL_SCAFFOLD_PLACEHOLDER_PATTERNS = [
|
|
22
|
+
/\[Uno o dos parrafos que expliquen el proyecto\.\]/i,
|
|
23
|
+
/\[Frase principal del proyecto\]/i,
|
|
24
|
+
/\[Describe el usuario principal\.\]/i,
|
|
25
|
+
/\[Objetivo actual \d+\]/i,
|
|
26
|
+
/\[Datos sensibles que nunca se guardan\]/i,
|
|
27
|
+
/\[Datos que si se guardan\]/i,
|
|
28
|
+
/\[Medida de seguridad importante\]/i,
|
|
29
|
+
/\[One or two paragraphs? that explain the project\.\]/i,
|
|
30
|
+
/\[Project tagline\]/i,
|
|
31
|
+
/\[Describe the primary user\.\]/i,
|
|
32
|
+
/\[Current goal \d+\]/i,
|
|
33
|
+
/\[Sensitive data that is never stored\]/i,
|
|
34
|
+
/\[Data that is stored\]/i,
|
|
35
|
+
/\[Important security measure\]/i,
|
|
36
|
+
/\[Short project summary\.\]/i,
|
|
37
|
+
/\[Description\]/i,
|
|
38
|
+
/\[TODO: confirm [^\]\n]+\]/i,
|
|
39
|
+
/\[TODO: confirmar [^\]\n]+\]/i,
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const SCAFFOLD_BOILERPLATE_PATTERNS = [
|
|
43
|
+
/^purpose:\s*"Human-readable project overview"$/i,
|
|
44
|
+
/^applies_when:\s*"onboarding, review"$/i,
|
|
45
|
+
/^supersedes:\s*null$/i,
|
|
46
|
+
/^El stack, package manager y comandos se generan en `docs\/PROJECT_MAP\.md` despues de ejecutar `analyze`\.$/i,
|
|
47
|
+
/^The stack, package manager, and command surface are generated in `docs\/PROJECT_MAP\.md` after analysis\.$/i,
|
|
48
|
+
/^If sos agente IA, lee `AI_CONTEXT\.md`\./i,
|
|
49
|
+
/^Read `AI_CONTEXT\.md` first if you are an AI agent\./i,
|
|
50
|
+
/^Use `npx create-quiver ai prepare-context --dry-run`/i,
|
|
51
|
+
/^TODO: confirm any repo fact/i,
|
|
52
|
+
/^Assumption: missing README_FOR_AI\.md/i,
|
|
53
|
+
/^Pending confirmation:/i,
|
|
54
|
+
];
|
|
18
55
|
|
|
19
56
|
const docProposalEntrySchema = z.object({
|
|
20
57
|
path: z.string().trim().min(1),
|
|
@@ -67,6 +104,10 @@ function normalizeDocContent(content) {
|
|
|
67
104
|
return `${String(content || '').replace(/\s+$/g, '')}\n`;
|
|
68
105
|
}
|
|
69
106
|
|
|
107
|
+
function normalizeNewlines(content) {
|
|
108
|
+
return String(content || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
109
|
+
}
|
|
110
|
+
|
|
70
111
|
function managedBlock(content) {
|
|
71
112
|
return [
|
|
72
113
|
ANALYZE_MANAGED_START,
|
|
@@ -76,8 +117,240 @@ function managedBlock(content) {
|
|
|
76
117
|
].join('\n');
|
|
77
118
|
}
|
|
78
119
|
|
|
120
|
+
function findManagedBlockRange(content, startMarker, endMarker) {
|
|
121
|
+
const text = normalizeNewlines(content);
|
|
122
|
+
const start = text.indexOf(startMarker);
|
|
123
|
+
const end = text.indexOf(endMarker);
|
|
124
|
+
if (start < 0 || end <= start) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
start,
|
|
129
|
+
end: end + endMarker.length,
|
|
130
|
+
content: text.slice(start + startMarker.length, end),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function removeManagedBlock(content, startMarker, endMarker) {
|
|
135
|
+
const text = normalizeNewlines(content);
|
|
136
|
+
const range = findManagedBlockRange(text, startMarker, endMarker);
|
|
137
|
+
if (!range) {
|
|
138
|
+
return {
|
|
139
|
+
content: text,
|
|
140
|
+
removed: false,
|
|
141
|
+
block: '',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
content: `${text.slice(0, range.start)}${text.slice(range.end)}`.replace(/\n{3,}/g, '\n\n').trimEnd(),
|
|
146
|
+
removed: true,
|
|
147
|
+
block: range.content,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function splitFrontmatter(content) {
|
|
152
|
+
const text = normalizeNewlines(content);
|
|
153
|
+
if (!text.startsWith('---\n')) {
|
|
154
|
+
return {
|
|
155
|
+
frontmatter: '',
|
|
156
|
+
body: text,
|
|
157
|
+
valid: false,
|
|
158
|
+
present: false,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const match = text.slice(4).match(/\n---\n/);
|
|
162
|
+
if (!match || match.index === undefined) {
|
|
163
|
+
return {
|
|
164
|
+
frontmatter: '',
|
|
165
|
+
body: text,
|
|
166
|
+
valid: false,
|
|
167
|
+
present: true,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const endIndex = 4 + match.index + '\n---\n'.length;
|
|
171
|
+
return {
|
|
172
|
+
frontmatter: text.slice(0, endIndex),
|
|
173
|
+
body: text.slice(endIndex),
|
|
174
|
+
valid: true,
|
|
175
|
+
present: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function collectCriticalPlaceholders(content) {
|
|
180
|
+
const findings = [];
|
|
181
|
+
const text = normalizeNewlines(content);
|
|
182
|
+
for (const pattern of CRITICAL_SCAFFOLD_PLACEHOLDER_PATTERNS) {
|
|
183
|
+
const match = text.match(pattern);
|
|
184
|
+
if (match) {
|
|
185
|
+
findings.push(match[0]);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return [...new Set(findings)].sort();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function isCriticalPlaceholderLine(line) {
|
|
192
|
+
const trimmed = String(line || '').trim();
|
|
193
|
+
return CRITICAL_SCAFFOLD_PLACEHOLDER_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function isScaffoldBoilerplateLine(line) {
|
|
197
|
+
const trimmed = String(line || '')
|
|
198
|
+
.replace(/^[-*]\s+/, '')
|
|
199
|
+
.replace(/^>\s*/, '')
|
|
200
|
+
.trim();
|
|
201
|
+
if (!trimmed) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
if (/^#+\s+/.test(trimmed)) {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
if (/^last_updated:\s*"/i.test(trimmed) || /^token_cost:\s*\d+/i.test(trimmed)) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
if (/^Files Considered$|^Assumptions$|^Risks$|^Contradictions$|^Omitted Paths$/i.test(trimmed.replace(/^#+\s*/, ''))) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
return SCAFFOLD_BOILERPLATE_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function significantHumanLines(content) {
|
|
217
|
+
return normalizeNewlines(content)
|
|
218
|
+
.split('\n')
|
|
219
|
+
.map((line) => line.trim())
|
|
220
|
+
.filter((line) => line.length >= 12)
|
|
221
|
+
.filter((line) => !isCriticalPlaceholderLine(line))
|
|
222
|
+
.filter((line) => !isScaffoldBoilerplateLine(line));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function classifyAnalyzeProjectDoc(currentContent) {
|
|
226
|
+
const current = normalizeNewlines(currentContent);
|
|
227
|
+
const frontmatter = splitFrontmatter(current);
|
|
228
|
+
const withoutAnalyze = removeManagedBlock(frontmatter.body, ANALYZE_MANAGED_START, ANALYZE_MANAGED_END);
|
|
229
|
+
const withoutContextPrep = removeManagedBlock(withoutAnalyze.content, CONTEXT_PREP_MANAGED_START, CONTEXT_PREP_MANAGED_END);
|
|
230
|
+
const visibleBody = withoutContextPrep.content.trim();
|
|
231
|
+
const visiblePlaceholders = collectCriticalPlaceholders(visibleBody);
|
|
232
|
+
const contextPrepPlaceholders = collectCriticalPlaceholders(withoutContextPrep.block);
|
|
233
|
+
const humanLines = significantHumanLines(visibleBody);
|
|
234
|
+
const contextPrepHumanLines = significantHumanLines(withoutContextPrep.block);
|
|
235
|
+
const hasManagedOnly = !visibleBody && (withoutAnalyze.removed || withoutContextPrep.removed);
|
|
236
|
+
|
|
237
|
+
let classification = 'unknown';
|
|
238
|
+
if (!current.trim()) {
|
|
239
|
+
classification = 'managed_only';
|
|
240
|
+
} else if (hasManagedOnly) {
|
|
241
|
+
classification = 'managed_only';
|
|
242
|
+
} else if (visiblePlaceholders.length > 0 && humanLines.length === 0) {
|
|
243
|
+
classification = 'scaffold';
|
|
244
|
+
} else if (visiblePlaceholders.length > 0 && humanLines.length > 0) {
|
|
245
|
+
classification = 'partial_scaffold';
|
|
246
|
+
} else if (withoutAnalyze.removed || withoutContextPrep.removed) {
|
|
247
|
+
classification = humanLines.length > 0 ? 'mixed' : 'managed_only';
|
|
248
|
+
} else if (humanLines.length > 0) {
|
|
249
|
+
classification = 'human_content';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
classification,
|
|
254
|
+
frontmatter,
|
|
255
|
+
visible_body: visibleBody,
|
|
256
|
+
has_analyze_project_block: withoutAnalyze.removed,
|
|
257
|
+
has_context_prep_block: withoutContextPrep.removed,
|
|
258
|
+
context_prep_is_scaffold: withoutContextPrep.removed && contextPrepHumanLines.length === 0 && contextPrepPlaceholders.length > 0,
|
|
259
|
+
critical_placeholders: visiblePlaceholders,
|
|
260
|
+
context_prep_placeholders: contextPrepPlaceholders,
|
|
261
|
+
human_line_count: humanLines.length,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function composeWithFrontmatter(frontmatter, body) {
|
|
266
|
+
const prefix = frontmatter?.valid ? `${frontmatter.frontmatter.trimEnd()}\n\n` : '';
|
|
267
|
+
return `${prefix}${normalizeDocContent(body)}`;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function visibleAnalyzeProjectContent(proposedContent) {
|
|
271
|
+
return normalizeDocContent(proposedContent).trimEnd();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function buildPreservedContentSection(content) {
|
|
275
|
+
const body = normalizeNewlines(content).trim();
|
|
276
|
+
if (!body) {
|
|
277
|
+
return '';
|
|
278
|
+
}
|
|
279
|
+
return `## Existing Content Preserved\n\n${body}`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function normalizedMarkdownEquivalent(left, right) {
|
|
283
|
+
return normalizeNewlines(left).trim() === normalizeNewlines(right).trim();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function mergeAnalyzeProjectDoc(currentContent, proposedContent) {
|
|
287
|
+
const current = normalizeNewlines(currentContent);
|
|
288
|
+
const proposedVisible = visibleAnalyzeProjectContent(proposedContent);
|
|
289
|
+
const classification = classifyAnalyzeProjectDoc(current);
|
|
290
|
+
const currentWithoutAnalyze = removeManagedBlock(classification.frontmatter.body, ANALYZE_MANAGED_START, ANALYZE_MANAGED_END);
|
|
291
|
+
const currentWithoutContextPrep = removeManagedBlock(currentWithoutAnalyze.content, CONTEXT_PREP_MANAGED_START, CONTEXT_PREP_MANAGED_END);
|
|
292
|
+
const bodyWithoutManaged = currentWithoutContextPrep.content.trim();
|
|
293
|
+
const report = {
|
|
294
|
+
classification: classification.classification,
|
|
295
|
+
strategy: 'preserve-and-update-managed-block',
|
|
296
|
+
scaffold_replaced: false,
|
|
297
|
+
human_content_preserved: false,
|
|
298
|
+
analyze_project_block_replaced: classification.has_analyze_project_block,
|
|
299
|
+
context_prep_removed: false,
|
|
300
|
+
critical_placeholders: classification.critical_placeholders,
|
|
301
|
+
warnings: [],
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
let visibleBody;
|
|
305
|
+
if (['scaffold', 'managed_only'].includes(classification.classification)) {
|
|
306
|
+
visibleBody = proposedVisible;
|
|
307
|
+
report.strategy = classification.classification === 'scaffold'
|
|
308
|
+
? 'replace-scaffold-primary-content'
|
|
309
|
+
: 'replace-managed-only-content';
|
|
310
|
+
report.scaffold_replaced = classification.classification === 'scaffold';
|
|
311
|
+
} else if (['partial_scaffold', 'mixed'].includes(classification.classification)) {
|
|
312
|
+
const cleaned = bodyWithoutManaged
|
|
313
|
+
.split('\n')
|
|
314
|
+
.filter((line) => !isCriticalPlaceholderLine(line))
|
|
315
|
+
.join('\n')
|
|
316
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
317
|
+
.trim();
|
|
318
|
+
const preserved = normalizedMarkdownEquivalent(cleaned, proposedVisible)
|
|
319
|
+
? ''
|
|
320
|
+
: buildPreservedContentSection(cleaned);
|
|
321
|
+
visibleBody = preserved ? `${proposedVisible}\n\n${preserved}` : proposedVisible;
|
|
322
|
+
report.strategy = 'replace-placeholders-preserve-human-content';
|
|
323
|
+
report.scaffold_replaced = classification.critical_placeholders.length > 0;
|
|
324
|
+
report.human_content_preserved = Boolean(preserved);
|
|
325
|
+
} else {
|
|
326
|
+
visibleBody = bodyWithoutManaged
|
|
327
|
+
? `${bodyWithoutManaged}\n\n${managedBlock(proposedContent).trimEnd()}`
|
|
328
|
+
: managedBlock(proposedContent).trimEnd();
|
|
329
|
+
report.human_content_preserved = bodyWithoutManaged.trim().length > 0;
|
|
330
|
+
if (classification.classification === 'unknown') {
|
|
331
|
+
report.warnings.push('document classification is unknown; preserved existing content and updated managed block only');
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
content: composeWithFrontmatter(classification.frontmatter, visibleBody),
|
|
335
|
+
report,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (classification.has_context_prep_block && (classification.context_prep_is_scaffold || classification.context_prep_placeholders.length > 0)) {
|
|
340
|
+
report.context_prep_removed = true;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
content: composeWithFrontmatter(
|
|
345
|
+
classification.frontmatter,
|
|
346
|
+
`${visibleBody}\n\n${managedBlock(proposedContent).trimEnd()}`,
|
|
347
|
+
),
|
|
348
|
+
report,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
79
352
|
function mergeManagedBlock(currentContent, proposedContent) {
|
|
80
|
-
const current =
|
|
353
|
+
const current = normalizeNewlines(currentContent);
|
|
81
354
|
const block = managedBlock(proposedContent);
|
|
82
355
|
const startIndex = current.indexOf(ANALYZE_MANAGED_START);
|
|
83
356
|
const endIndex = current.indexOf(ANALYZE_MANAGED_END);
|
|
@@ -253,9 +526,10 @@ function buildAnalyzeProjectWritePlan(repoRoot, proposal) {
|
|
|
253
526
|
const destinationPath = path.join(repoRoot, doc.path);
|
|
254
527
|
const exists = fs.existsSync(destinationPath);
|
|
255
528
|
const currentContent = exists ? fs.readFileSync(destinationPath, 'utf8') : '';
|
|
256
|
-
const
|
|
257
|
-
? currentContent
|
|
258
|
-
:
|
|
529
|
+
const merged = doc.action === 'skip'
|
|
530
|
+
? { content: currentContent, report: { classification: 'skipped', strategy: 'skip' } }
|
|
531
|
+
: mergeAnalyzeProjectDoc(currentContent, doc.content);
|
|
532
|
+
const proposedContent = merged.content;
|
|
259
533
|
const changed = currentContent.replace(/\r\n/g, '\n') !== proposedContent.replace(/\r\n/g, '\n');
|
|
260
534
|
const action = doc.action === 'skip' || !changed ? 'skip' : exists ? 'update' : 'create';
|
|
261
535
|
|
|
@@ -271,6 +545,7 @@ function buildAnalyzeProjectWritePlan(repoRoot, proposal) {
|
|
|
271
545
|
before_sha256: exists ? sha256(currentContent) : null,
|
|
272
546
|
after_sha256: action === 'skip' ? (exists ? sha256(currentContent) : null) : sha256(proposedContent),
|
|
273
547
|
diff: buildDiffSnippet(doc.path, currentContent, proposedContent),
|
|
548
|
+
merge_report: merged.report,
|
|
274
549
|
};
|
|
275
550
|
});
|
|
276
551
|
}
|
|
@@ -368,12 +643,17 @@ module.exports = {
|
|
|
368
643
|
ANALYZE_DOC_PROPOSAL_SCHEMA_VERSION,
|
|
369
644
|
ANALYZE_MANAGED_END,
|
|
370
645
|
ANALYZE_MANAGED_START,
|
|
646
|
+
CONTEXT_PREP_MANAGED_END,
|
|
647
|
+
CONTEXT_PREP_MANAGED_START,
|
|
371
648
|
AnalyzeProjectDocsError,
|
|
372
649
|
analyzeProjectDocProposalSchema,
|
|
373
650
|
buildAnalyzeProjectDocProposal,
|
|
374
651
|
buildAnalyzeProjectWritePlan,
|
|
652
|
+
classifyAnalyzeProjectDoc,
|
|
653
|
+
collectCriticalPlaceholders,
|
|
375
654
|
createAnalyzeProjectSnapshot,
|
|
376
655
|
formatAnalyzeProjectDiffPreview,
|
|
656
|
+
mergeAnalyzeProjectDoc,
|
|
377
657
|
mergeManagedBlock,
|
|
378
658
|
normalizeAnalyzeProjectDocProposal,
|
|
379
659
|
parseAnalyzeProjectDocProposal,
|
|
@@ -127,6 +127,7 @@ const proposalManifestSchema = z.object({
|
|
|
127
127
|
repair_manifest: relativePathSchema('repair_manifest').nullable(),
|
|
128
128
|
doc_paths: z.array(relativePathSchema('doc_paths')).default([]),
|
|
129
129
|
doc_before_hashes: z.record(z.string().trim().min(1), z.string().trim().min(1).nullable()).default({}),
|
|
130
|
+
merge_plan: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
130
131
|
proposal_sha256: z.string().trim().min(1),
|
|
131
132
|
events: z.array(z.record(z.string(), z.unknown())).default([]),
|
|
132
133
|
}).strict();
|
|
@@ -139,6 +140,7 @@ const writeManifestActionSchema = z.object({
|
|
|
139
140
|
snapshot_path: relativePathSchema('actions.snapshot_path').nullable(),
|
|
140
141
|
dirty: z.boolean().default(false),
|
|
141
142
|
status: z.enum(['planned', 'written', 'skipped', 'failed']).default('planned'),
|
|
143
|
+
merge_report: z.record(z.string(), z.unknown()).optional(),
|
|
142
144
|
}).strict();
|
|
143
145
|
|
|
144
146
|
const writeManifestSchema = z.object({
|
|
@@ -419,9 +421,16 @@ function writeAnalyzeProjectProposalArtifacts(repoRoot, options = {}) {
|
|
|
419
421
|
const proposalJson = `${JSON.stringify(proposal, null, 2)}\n`;
|
|
420
422
|
const proposalHash = sha256(proposalJson);
|
|
421
423
|
const docBeforeHashes = {};
|
|
424
|
+
const mergePlan = [];
|
|
422
425
|
|
|
423
426
|
for (const item of writePlan) {
|
|
424
427
|
docBeforeHashes[item.path] = item.before_sha256 || null;
|
|
428
|
+
mergePlan.push({
|
|
429
|
+
path: item.path,
|
|
430
|
+
action: item.action,
|
|
431
|
+
dirty: item.dirty === true,
|
|
432
|
+
merge_report: item.merge_report || null,
|
|
433
|
+
});
|
|
425
434
|
}
|
|
426
435
|
|
|
427
436
|
const selectedContextPath = artifactPathFromRef(options.selectedContextManifest)
|
|
@@ -441,6 +450,7 @@ function writeAnalyzeProjectProposalArtifacts(repoRoot, options = {}) {
|
|
|
441
450
|
repair_manifest: repairPath,
|
|
442
451
|
doc_paths: writePlan.map((item) => item.path),
|
|
443
452
|
doc_before_hashes: docBeforeHashes,
|
|
453
|
+
merge_plan: mergePlan,
|
|
444
454
|
proposal_sha256: proposalHash,
|
|
445
455
|
events: [
|
|
446
456
|
{
|
|
@@ -524,6 +534,7 @@ function writeAnalyzeProjectWriteManifest(repoRoot, options = {}) {
|
|
|
524
534
|
snapshot_path: snapshotEntry?.snapshot_path || null,
|
|
525
535
|
dirty: item.dirty === true,
|
|
526
536
|
status,
|
|
537
|
+
merge_report: item.merge_report || undefined,
|
|
527
538
|
};
|
|
528
539
|
});
|
|
529
540
|
const validation = options.validation || {
|