@planu/cli 5.7.5 → 5.7.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 +75 -0
- package/README.md +3 -3
- package/dist/.planu-build.json +1 -1
- package/dist/cli/commands/package-handoff.js +30 -5
- package/dist/config/compliance-profiles.json +36 -3
- package/dist/config/environment-schema.json +21 -0
- package/dist/config/hook-templates/planu-session-safeguard.sh +22 -15
- package/dist/config/official-sdd-tools.d.ts +1 -1
- package/dist/config/official-sdd-tools.js +11 -24
- package/dist/config/registries/hosts/codex.json +14 -30
- package/dist/config/registries/hosts/opencode.json +1 -6
- package/dist/config/server-instructions.js +0 -21
- package/dist/config/skill-templates/planu-context-assets.md +3 -6
- package/dist/config/skill-templates/planu-implement.md +2 -2
- package/dist/config/skill-templates/planu-multi-teammate-review.md +1 -1
- package/dist/config/skill-templates/planu-release.md +2 -7
- package/dist/config/skill-templates/planu-validate.md +3 -3
- package/dist/config/spec-templates/crud-rest-api/template.json +5 -1
- package/dist/config/spec-templates/file-upload-s3/template.json +5 -1
- package/dist/config/spec-templates/stripe-payments/template.json +5 -1
- package/dist/config/spec-templates/webhook-system/template.json +5 -1
- package/dist/config/subagent-templates/planu-challenger.md +4 -9
- package/dist/config/subagent-templates/planu-spec-implementer.md +2 -4
- package/dist/config/subagent-templates/planu-validator.md +5 -8
- package/dist/config/tool-groups.json +6 -144
- package/dist/engine/ambiguity-scorer.js +40 -1
- package/dist/engine/autopilot/bootstrap.js +1 -1
- package/dist/engine/code-impact-analyzer.js +2 -6
- package/dist/engine/context-orchestrator/index.js +2 -2
- package/dist/engine/convention-scanner/codebase-scanner.js +2 -2
- package/dist/engine/dogfooding/runtime-gap-detector.js +2 -2
- package/dist/engine/evidence-gates/artifact-reader.d.ts +1 -0
- package/dist/engine/evidence-gates/artifact-reader.js +1 -1
- package/dist/engine/evidence-gates/evidence-autofill.d.ts +13 -5
- package/dist/engine/evidence-gates/evidence-autofill.js +106 -43
- package/dist/engine/evidence-gates/lifecycle-gate.js +7 -4
- package/dist/engine/evidence-index/index-builder.js +21 -4
- package/dist/engine/execution/job-runtime.js +1 -1
- package/dist/engine/execution/package-handoff-job-executor.d.ts +22 -0
- package/dist/engine/execution/package-handoff-job-executor.js +490 -0
- package/dist/engine/execution/validate-job-executor.js +20 -8
- package/dist/engine/git/exec-git.d.ts +4 -0
- package/dist/engine/git/exec-git.js +14 -0
- package/dist/engine/handoff-artifacts/implementation-review-reader.js +15 -2
- package/dist/engine/handoff-artifacts/schemas.d.ts +2 -0
- package/dist/engine/handoff-artifacts/schemas.js +1 -0
- package/dist/engine/handoff-format.d.ts +36 -0
- package/dist/engine/handoff-format.js +314 -0
- package/dist/engine/handoff-packager.d.ts +5 -3
- package/dist/engine/handoff-packager.js +89 -32
- package/dist/engine/host-tool-filter.js +20 -49
- package/dist/engine/human-summary.js +14 -3
- package/dist/engine/implementation-contract/common.d.ts +8 -3
- package/dist/engine/implementation-contract/common.js +9 -6
- package/dist/engine/implementation-contract/evaluator.js +4 -6
- package/dist/engine/implementation-contract/renderer.js +18 -23
- package/dist/engine/project-health-checker.js +2 -2
- package/dist/engine/readiness-checker.js +27 -0
- package/dist/engine/reconcile/apply-changes.js +4 -0
- package/dist/engine/reconcile/propagate-mirrors.d.ts +3 -0
- package/dist/engine/reconcile/propagate-mirrors.js +311 -0
- package/dist/engine/self-healing/healer.js +12 -5
- package/dist/engine/self-healing/strategies/lint-fix.d.ts +1 -1
- package/dist/engine/self-healing/strategies/lint-fix.js +4 -1
- package/dist/engine/self-healing/strategies/test-fix.d.ts +4 -3
- package/dist/engine/self-healing/strategies/test-fix.js +32 -14
- package/dist/engine/self-healing/strategies/typescript-fix.d.ts +1 -1
- package/dist/engine/self-healing/strategies/typescript-fix.js +28 -10
- package/dist/engine/session/checkpoint-writer.js +8 -4
- package/dist/engine/session/session-tracker.d.ts +5 -16
- package/dist/engine/session/session-tracker.js +29 -26
- package/dist/engine/session-state/writer.js +3 -11
- package/dist/engine/skill-generator/workflow-skill-generator.d.ts +4 -2
- package/dist/engine/skill-generator/workflow-skill-generator.js +113 -38
- package/dist/engine/spec-format/bdd-parser.d.ts +1 -11
- package/dist/engine/spec-format/bdd-parser.js +40 -11
- package/dist/engine/spec-format/lean-spec-generator.js +30 -10
- package/dist/engine/spec-format/retired-scaffold.d.ts +3 -0
- package/dist/engine/spec-format/retired-scaffold.js +5 -0
- package/dist/engine/spec-format/technical-md-populator.d.ts +1 -0
- package/dist/engine/spec-format/technical-md-populator.js +27 -0
- package/dist/engine/spec-format/unified-spec-builder.d.ts +1 -0
- package/dist/engine/spec-format/unified-spec-builder.js +30 -20
- package/dist/engine/spec-grounding/contract.js +115 -1
- package/dist/engine/spec-quality/generic-output-gate.js +37 -3
- package/dist/engine/technical-enricher/index.js +4 -1
- package/dist/engine/type-safety-gate.js +3 -11
- package/dist/engine/universal-rules/catalog.js +0 -2
- package/dist/engine/universal-rules/rules/planu-workflow.js +1 -1
- package/dist/engine/validation/durable-validation.d.ts +3 -2
- package/dist/engine/validation/durable-validation.js +106 -40
- package/dist/engine/validation/validation-freshness.d.ts +3 -2
- package/dist/engine/validation/validation-freshness.js +22 -16
- package/dist/engine/validation/validation-worktree.js +34 -0
- package/dist/hosts/claude-code/ux/mcp-prompts.js +8 -8
- package/dist/index.js +3 -17
- package/dist/resources/process.js +38 -65
- package/dist/storage/session-state-store.js +2 -0
- package/dist/tools/challenge-spec/scenarios-utils.js +2 -2
- package/dist/tools/configure-checkpoint-policy.js +3 -3
- package/dist/tools/create-spec-helpers.js +2 -5
- package/dist/tools/create-spec.js +21 -4
- package/dist/tools/facilitate.js +3 -6
- package/dist/tools/git/branch-ops.js +16 -4
- package/dist/tools/github-release-handler.js +1 -20
- package/dist/tools/init-project/agents-md-writer.js +6 -6
- package/dist/tools/init-project/claude-md-generator.d.ts +0 -1
- package/dist/tools/init-project/claude-md-generator.js +0 -35
- package/dist/tools/init-project/handler.js +7 -5
- package/dist/tools/init-project/per-client-files-writer.js +5 -5
- package/dist/tools/init-project/planu-workflow-generator.js +2 -13
- package/dist/tools/init-project/portable-index-reconciler.d.ts +2 -1
- package/dist/tools/init-project/portable-index-reconciler.js +7 -2
- package/dist/tools/init-project/rules-generator.d.ts +0 -11
- package/dist/tools/init-project/rules-generator.js +4 -85
- package/dist/tools/init-project/scaffold-writer.d.ts +0 -1
- package/dist/tools/init-project/scaffold-writer.js +1 -12
- package/dist/tools/jobs/handlers.d.ts +0 -6
- package/dist/tools/jobs/handlers.js +13 -40
- package/dist/tools/list-specs.js +6 -21
- package/dist/tools/package-handoff.d.ts +3 -2
- package/dist/tools/package-handoff.js +153 -427
- package/dist/tools/reconcile-session-safeguard-hook.js +22 -15
- package/dist/tools/register-sdd-tools.js +0 -23
- package/dist/tools/register-spec-tools/core-spec-tools.js +18 -27
- package/dist/tools/schemas/index.d.ts +1 -1
- package/dist/tools/schemas/index.js +1 -1
- package/dist/tools/schemas/output-schemas.d.ts +1 -18
- package/dist/tools/schemas/output-schemas.js +1 -11
- package/dist/tools/schemas/package-handoff-output-schema.d.ts +40 -0
- package/dist/tools/schemas/package-handoff-output-schema.js +68 -0
- package/dist/tools/schemas/validate-output-schema.d.ts +1 -2
- package/dist/tools/schemas/validate-output-schema.js +3 -2
- package/dist/tools/session-checkpoint.js +2 -2
- package/dist/tools/skill-registry/index.d.ts +0 -1
- package/dist/tools/skill-registry/index.js +0 -1
- package/dist/tools/sync-spec-state-handler.d.ts +2 -2
- package/dist/tools/sync-spec-state-handler.js +23 -3
- package/dist/tools/tool-registry/core-tools.js +1 -140
- package/dist/tools/tool-registry/group-infra.js +2 -39
- package/dist/tools/tool-registry/group-quality-compliance.js +9 -1
- package/dist/tools/update-status/batch.js +17 -0
- package/dist/tools/update-status/dod-gates.js +14 -1
- package/dist/tools/update-status/done-receipt-verifier.d.ts +22 -0
- package/dist/tools/update-status/done-receipt-verifier.js +56 -26
- package/dist/tools/update-status/evidence-gate.js +10 -21
- package/dist/tools/update-status/file-sync.d.ts +1 -0
- package/dist/tools/update-status/file-sync.js +6 -0
- package/dist/tools/update-status/index.d.ts +6 -0
- package/dist/tools/update-status/index.js +39 -44
- package/dist/tools/update-status-actions.js +2 -7
- package/dist/tools/validate.js +34 -28
- package/dist/transports/oauth-validator.js +12 -1
- package/dist/transports/transport-factory.d.ts +2 -1
- package/dist/transports/transport-factory.js +19 -0
- package/dist/types/common/primitives.d.ts +2 -0
- package/dist/types/durable-job.d.ts +45 -0
- package/dist/types/durable-validation.d.ts +1 -1
- package/dist/types/evidence-autofill.d.ts +13 -1
- package/dist/types/handoff-artifacts.d.ts +2 -1
- package/dist/types/readiness.d.ts +8 -1
- package/dist/types/reconcile.d.ts +6 -0
- package/dist/types/skill-registry.d.ts +26 -1
- package/dist/types/spec/core.d.ts +5 -0
- package/dist/types/spec/inputs.d.ts +2 -2
- package/dist/types/spec-format.d.ts +11 -1
- package/dist/types/spec-grounding.d.ts +12 -0
- package/dist/types/transport.d.ts +1 -0
- package/dist/types/validation-receipt.d.ts +27 -0
- package/package.json +3 -2
- package/planu-plugin.json +13 -26
- package/src/i18n/messages/en.json +1 -1
- package/src/i18n/messages/es.json +1 -1
- package/src/i18n/messages/pt.json +1 -1
- package/dist/engine/skill-generator/conventions-hasher.d.ts +0 -7
- package/dist/engine/skill-generator/conventions-hasher.js +0 -24
- package/dist/engine/universal-rules/rules/agent-teams.d.ts +0 -3
- package/dist/engine/universal-rules/rules/agent-teams.js +0 -63
- package/dist/tools/clarify-requirements/multiple-choice.d.ts +0 -30
- package/dist/tools/clarify-requirements/multiple-choice.js +0 -306
- package/dist/tools/clarify-requirements/questions-context.d.ts +0 -9
- package/dist/tools/clarify-requirements/questions-context.js +0 -74
- package/dist/tools/clarify-requirements/questions.d.ts +0 -16
- package/dist/tools/clarify-requirements/questions.js +0 -96
- package/dist/tools/clarify-requirements.d.ts +0 -4
- package/dist/tools/clarify-requirements.js +0 -314
- package/dist/tools/code-graph-handler.d.ts +0 -6
- package/dist/tools/code-graph-handler.js +0 -72
- package/dist/tools/create-rule.d.ts +0 -4
- package/dist/tools/create-rule.js +0 -107
- package/dist/tools/feedback-handler.d.ts +0 -7
- package/dist/tools/feedback-handler.js +0 -150
- package/dist/tools/semantic-search-handler.d.ts +0 -7
- package/dist/tools/semantic-search-handler.js +0 -71
- package/dist/tools/skill-registry/search.d.ts +0 -11
- package/dist/tools/skill-registry/search.js +0 -159
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { escapeRegex } from '../../core/shared/strings.js';
|
|
2
|
+
import { normalizeCriterionText } from '../criterion-identity.js';
|
|
3
|
+
import { parseBddScenarios, renderBddScenariosYaml } from '../spec-format/bdd-parser.js';
|
|
4
|
+
import { extractSectionCriteria } from '../spec-format/acceptance-criteria.js';
|
|
5
|
+
import { extractListItems, findMarkdownSectionRange } from '../spec-format/markdown-sections.js';
|
|
6
|
+
import { parseCriterionGroundingRecords, parseTechnicalReferenceGroundingRecords, renderGroundingFrontmatter, renderTechnicalReferenceGroundingFrontmatter, } from '../spec-grounding/contract.js';
|
|
7
|
+
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n/;
|
|
8
|
+
const ACCEPTANCE_CRITERIA_HEADING = 'Acceptance Criteria';
|
|
9
|
+
const FILES_HEADING = 'Files';
|
|
10
|
+
const TECHNICAL_HEADING = 'Technical';
|
|
11
|
+
const FILE_WORK_PLAN_HEADING = 'File-Level Work Plan';
|
|
12
|
+
const ACCEPTANCE_MAP_HEADING = 'Acceptance-To-Verification Map';
|
|
13
|
+
const VERIFICATION_COMMANDS_HEADING = 'Verification Commands';
|
|
14
|
+
const VERIFICATION_STARTS_WITH_RE = /Verification starts with:\s*`([^`]+)`/i;
|
|
15
|
+
export function propagateMirrors(content, appliedSections) {
|
|
16
|
+
let result = content;
|
|
17
|
+
if (appliedSections.has(ACCEPTANCE_CRITERIA_HEADING)) {
|
|
18
|
+
result = propagateAcceptanceCriteria(result);
|
|
19
|
+
}
|
|
20
|
+
if (appliedSections.has(FILES_HEADING)) {
|
|
21
|
+
result = propagateFiles(result);
|
|
22
|
+
}
|
|
23
|
+
if (appliedSections.has(TECHNICAL_HEADING)) {
|
|
24
|
+
result = propagateTechnicalVerificationCommand(result);
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
function propagateAcceptanceCriteria(content) {
|
|
29
|
+
const section = findMarkdownSectionRange(content, ACCEPTANCE_CRITERIA_HEADING);
|
|
30
|
+
if (!section) {
|
|
31
|
+
return content;
|
|
32
|
+
}
|
|
33
|
+
const sectionBody = content.slice(section.contentStart, section.end);
|
|
34
|
+
const criteriaTexts = extractSectionCriteria(sectionBody);
|
|
35
|
+
const scenarios = criteriaTexts.map(criterionToScenario);
|
|
36
|
+
const oldRecords = mapWithFrontmatter(content, (fm) => indexCriterionRecords(parseCriterionGroundingRecords(fm)));
|
|
37
|
+
const oldTestPaths = mapWithFrontmatter(content, (fm) => parseTechnicalReferenceGroundingRecords(fm)
|
|
38
|
+
.filter((record) => record.section === 'test')
|
|
39
|
+
.map((record) => record.path));
|
|
40
|
+
const criteriaRecords = criteriaTexts.map((text) => oldRecords.get(normalizeCriterionText(text)) ?? {
|
|
41
|
+
text,
|
|
42
|
+
source: 'user_input',
|
|
43
|
+
evidence: ['reconcile_spec.changes'],
|
|
44
|
+
confidence: 'high',
|
|
45
|
+
});
|
|
46
|
+
let result = transformFrontmatter(content, (fm) => {
|
|
47
|
+
let updated = replaceFrontmatterKeyBlock(fm, 'scenarios', renderBddScenariosYaml(scenarios));
|
|
48
|
+
updated = replaceFrontmatterKeyBlock(updated, 'grounding', groundingBlockLines(criteriaRecords, technicalReferencesFromFrontmatter(fm)));
|
|
49
|
+
return updated;
|
|
50
|
+
});
|
|
51
|
+
result = withSectionBody(result, ACCEPTANCE_MAP_HEADING, (body) => syncAcceptanceMapForCriteria(body, criteriaTexts, oldTestPaths));
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
function propagateFiles(content) {
|
|
55
|
+
const section = findMarkdownSectionRange(content, FILES_HEADING);
|
|
56
|
+
if (!section) {
|
|
57
|
+
return content;
|
|
58
|
+
}
|
|
59
|
+
const sectionBody = content.slice(section.contentStart, section.end);
|
|
60
|
+
const files = parseFilesSection(sectionBody);
|
|
61
|
+
const oldTestPaths = mapWithFrontmatter(content, (fm) => parseTechnicalReferenceGroundingRecords(fm)
|
|
62
|
+
.filter((record) => record.section === 'test')
|
|
63
|
+
.map((record) => record.path));
|
|
64
|
+
const oldTechnicalReferences = mapWithFrontmatter(content, (fm) => indexTechnicalReferenceRecords(parseTechnicalReferenceGroundingRecords(fm)));
|
|
65
|
+
const technicalReferences = buildTechnicalReferenceRecords(files, oldTechnicalReferences);
|
|
66
|
+
const newTestPaths = files.test;
|
|
67
|
+
let result = transformFrontmatter(content, (fm) => replaceFrontmatterKeyBlock(fm, 'grounding', groundingBlockLines(criteriaRecordsFromFrontmatter(fm), technicalReferences)));
|
|
68
|
+
result = replaceNamedSection(result, FILE_WORK_PLAN_HEADING, renderFileWorkPlan(files));
|
|
69
|
+
result = withSectionBody(result, ACCEPTANCE_MAP_HEADING, (body) => addVerifiedTestPathEntries(body, oldTestPaths, newTestPaths));
|
|
70
|
+
result = withSectionBody(result, VERIFICATION_COMMANDS_HEADING, (body) => syncVerificationCommandLines(body, oldTestPaths, newTestPaths));
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
function propagateTechnicalVerificationCommand(content) {
|
|
74
|
+
const technicalSection = findMarkdownSectionRange(content, TECHNICAL_HEADING);
|
|
75
|
+
if (!technicalSection) {
|
|
76
|
+
return content;
|
|
77
|
+
}
|
|
78
|
+
const technicalBody = content.slice(technicalSection.contentStart, technicalSection.end);
|
|
79
|
+
const newCommand = VERIFICATION_STARTS_WITH_RE.exec(technicalBody)?.[1]?.trim();
|
|
80
|
+
if (!newCommand) {
|
|
81
|
+
return content;
|
|
82
|
+
}
|
|
83
|
+
const commandsSection = findMarkdownSectionRange(content, VERIFICATION_COMMANDS_HEADING);
|
|
84
|
+
if (!commandsSection) {
|
|
85
|
+
return content;
|
|
86
|
+
}
|
|
87
|
+
const currentBody = content.slice(commandsSection.contentStart, commandsSection.end);
|
|
88
|
+
if (currentBody.includes(newCommand)) {
|
|
89
|
+
return content;
|
|
90
|
+
}
|
|
91
|
+
return replaceNamedSection(content, VERIFICATION_COMMANDS_HEADING, [`- \`${newCommand}\``]);
|
|
92
|
+
}
|
|
93
|
+
function criterionToScenario(text) {
|
|
94
|
+
const gwtLines = text
|
|
95
|
+
.split(/\b(GIVEN|WHEN|THEN|AND)\b/)
|
|
96
|
+
.reduce((lines, part, index, parts) => {
|
|
97
|
+
if (index % 2 === 0) {
|
|
98
|
+
return lines;
|
|
99
|
+
}
|
|
100
|
+
const keyword = part;
|
|
101
|
+
const rest = (parts[index + 1] ?? '').trim();
|
|
102
|
+
lines.push(`${keyword} ${rest}`);
|
|
103
|
+
return lines;
|
|
104
|
+
}, []);
|
|
105
|
+
const [scenario] = parseBddScenarios(gwtLines.join('\n'));
|
|
106
|
+
return scenario ?? { title: text, steps: [{ keyword: 'then', text }], done: false };
|
|
107
|
+
}
|
|
108
|
+
function indexCriterionRecords(records) {
|
|
109
|
+
const index = new Map();
|
|
110
|
+
for (const record of records) {
|
|
111
|
+
index.set(normalizeCriterionText(record.text), record);
|
|
112
|
+
}
|
|
113
|
+
return index;
|
|
114
|
+
}
|
|
115
|
+
function indexTechnicalReferenceRecords(records) {
|
|
116
|
+
const index = new Map();
|
|
117
|
+
for (const record of records) {
|
|
118
|
+
index.set(record.path, record);
|
|
119
|
+
}
|
|
120
|
+
return index;
|
|
121
|
+
}
|
|
122
|
+
function parseFilesSection(sectionBody) {
|
|
123
|
+
return {
|
|
124
|
+
create: parseFilesSubsection(sectionBody, 'Create'),
|
|
125
|
+
modify: parseFilesSubsection(sectionBody, 'Modify'),
|
|
126
|
+
test: parseFilesSubsection(sectionBody, 'Test'),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function parseFilesSubsection(sectionBody, heading) {
|
|
130
|
+
const range = findMarkdownSectionRange(sectionBody, heading);
|
|
131
|
+
if (!range) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
const body = sectionBody.slice(range.contentStart, range.end);
|
|
135
|
+
return extractListItems(body).map((item) => item.replace(/`/g, '').trim());
|
|
136
|
+
}
|
|
137
|
+
function buildTechnicalReferenceRecords(files, oldRecords) {
|
|
138
|
+
const groups = [
|
|
139
|
+
['create', files.create],
|
|
140
|
+
['modify', files.modify],
|
|
141
|
+
['test', files.test],
|
|
142
|
+
];
|
|
143
|
+
const records = [];
|
|
144
|
+
for (const [section, paths] of groups) {
|
|
145
|
+
for (const path of paths) {
|
|
146
|
+
const existing = oldRecords.get(path);
|
|
147
|
+
records.push(existing
|
|
148
|
+
? { ...existing, section }
|
|
149
|
+
: {
|
|
150
|
+
path,
|
|
151
|
+
section,
|
|
152
|
+
source: 'user_input',
|
|
153
|
+
evidence: ['reconcile_spec.changes'],
|
|
154
|
+
confidence: 'high',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return records;
|
|
159
|
+
}
|
|
160
|
+
function renderFileWorkPlan(files) {
|
|
161
|
+
const groups = [
|
|
162
|
+
['Create', files.create],
|
|
163
|
+
['Modify', files.modify],
|
|
164
|
+
['Test', files.test],
|
|
165
|
+
];
|
|
166
|
+
return groups.flatMap(([label, paths]) => paths.map((path) => `- ${label}: \`${path}\` (pending)`));
|
|
167
|
+
}
|
|
168
|
+
function isVerifiedByLine(line, path) {
|
|
169
|
+
return /^-\s*Verified by:/i.test(line.trim()) && line.includes(path);
|
|
170
|
+
}
|
|
171
|
+
function addVerifiedTestPathEntries(body, oldPaths, newPaths) {
|
|
172
|
+
const removed = oldPaths.filter((path) => !newPaths.includes(path));
|
|
173
|
+
const added = newPaths.filter((path) => !oldPaths.includes(path));
|
|
174
|
+
if (removed.length === 0 && added.length === 0) {
|
|
175
|
+
return body;
|
|
176
|
+
}
|
|
177
|
+
let updated = removed.length === 0
|
|
178
|
+
? body
|
|
179
|
+
: body
|
|
180
|
+
.split('\n')
|
|
181
|
+
.filter((line) => !removed.some((path) => isVerifiedByLine(line, path)))
|
|
182
|
+
.join('\n');
|
|
183
|
+
if (added.length > 0) {
|
|
184
|
+
const bulletLines = added.map((path) => `- Verified by: \`${path}\``).join('\n');
|
|
185
|
+
updated = `${updated.trimEnd()}\n${bulletLines}\n`;
|
|
186
|
+
}
|
|
187
|
+
return updated;
|
|
188
|
+
}
|
|
189
|
+
function isTestRunnerCommandLine(line) {
|
|
190
|
+
return /`[^`]*\b(vitest|jest|mocha|pnpm test|npm test)\b[^`]*`/i.test(line);
|
|
191
|
+
}
|
|
192
|
+
function syncVerificationCommandLines(body, oldPaths, newPaths) {
|
|
193
|
+
const removed = oldPaths.filter((path) => !newPaths.includes(path));
|
|
194
|
+
const added = newPaths.filter((path) => !oldPaths.includes(path));
|
|
195
|
+
if (removed.length === 0 && added.length === 0) {
|
|
196
|
+
return body;
|
|
197
|
+
}
|
|
198
|
+
const lines = body.split('\n').map((line) => {
|
|
199
|
+
if (!oldPaths.some((path) => line.includes(path))) {
|
|
200
|
+
return line;
|
|
201
|
+
}
|
|
202
|
+
return substituteTestPathsInLine(line, removed, added);
|
|
203
|
+
});
|
|
204
|
+
const stillMissing = added.filter((path) => !lines.some((line) => line.includes(path)));
|
|
205
|
+
if (stillMissing.length === 0) {
|
|
206
|
+
return lines.join('\n');
|
|
207
|
+
}
|
|
208
|
+
const commandLineIndex = lines.findIndex(isTestRunnerCommandLine);
|
|
209
|
+
if (commandLineIndex !== -1) {
|
|
210
|
+
lines[commandLineIndex] = substituteTestPathsInLine(lines[commandLineIndex] ?? '', [], stillMissing);
|
|
211
|
+
return lines.join('\n');
|
|
212
|
+
}
|
|
213
|
+
while (lines.length > 0 && lines[lines.length - 1]?.trim() === '') {
|
|
214
|
+
lines.pop();
|
|
215
|
+
}
|
|
216
|
+
return `${[...lines, ...stillMissing.map((path) => `- \`${path}\``)].join('\n')}\n`;
|
|
217
|
+
}
|
|
218
|
+
function syncAcceptanceMapForCriteria(body, criteriaTexts, testPaths) {
|
|
219
|
+
const lines = body.split('\n').filter((line) => line.trim().length > 0);
|
|
220
|
+
const kept = lines.filter((line) => criteriaTexts.some((text) => line.includes(text)));
|
|
221
|
+
const covered = new Set(kept.flatMap((line) => criteriaTexts.filter((text) => line.includes(text))));
|
|
222
|
+
const newEntries = criteriaTexts
|
|
223
|
+
.filter((text) => !covered.has(text))
|
|
224
|
+
.map((text) => renderAcceptanceMapEntry(text, testPaths));
|
|
225
|
+
const resultLines = [...kept, ...newEntries];
|
|
226
|
+
if (resultLines.length === 0) {
|
|
227
|
+
return body;
|
|
228
|
+
}
|
|
229
|
+
return `${resultLines.join('\n')}\n`;
|
|
230
|
+
}
|
|
231
|
+
function renderAcceptanceMapEntry(criterionText, testPaths) {
|
|
232
|
+
return testPaths.length > 0
|
|
233
|
+
? `- ${criterionText}: verified by ${testPaths.map((path) => `\`${path}\``).join(', ')}`
|
|
234
|
+
: `- ${criterionText}: verification pending — no test file listed yet`;
|
|
235
|
+
}
|
|
236
|
+
function substituteTestPathsInLine(line, removed, added) {
|
|
237
|
+
let updated = line;
|
|
238
|
+
for (const path of removed) {
|
|
239
|
+
updated = updated.replace(new RegExp(`\\s*${escapeRegex(path)}`), '');
|
|
240
|
+
}
|
|
241
|
+
if (added.length === 0) {
|
|
242
|
+
return updated;
|
|
243
|
+
}
|
|
244
|
+
const insertion = ` ${added.join(' ')}`;
|
|
245
|
+
if (updated.endsWith('`.')) {
|
|
246
|
+
return `${updated.slice(0, -2)}${insertion}\`.`;
|
|
247
|
+
}
|
|
248
|
+
if (updated.endsWith('`')) {
|
|
249
|
+
return `${updated.slice(0, -1)}${insertion}\``;
|
|
250
|
+
}
|
|
251
|
+
return `${updated}${insertion}`;
|
|
252
|
+
}
|
|
253
|
+
function replaceNamedSection(content, headingName, bodyLines) {
|
|
254
|
+
const range = findMarkdownSectionRange(content, headingName);
|
|
255
|
+
if (!range) {
|
|
256
|
+
return content;
|
|
257
|
+
}
|
|
258
|
+
const isLast = range.end >= content.length;
|
|
259
|
+
const body = bodyLines.join('\n');
|
|
260
|
+
const tail = isLast ? `${body}\n` : `${body}\n\n`;
|
|
261
|
+
return content.slice(0, range.contentStart) + tail + content.slice(range.end);
|
|
262
|
+
}
|
|
263
|
+
function withSectionBody(content, headingName, transform) {
|
|
264
|
+
const range = findMarkdownSectionRange(content, headingName);
|
|
265
|
+
if (!range) {
|
|
266
|
+
return content;
|
|
267
|
+
}
|
|
268
|
+
const body = content.slice(range.contentStart, range.end);
|
|
269
|
+
const updated = transform(body);
|
|
270
|
+
if (updated === body) {
|
|
271
|
+
return content;
|
|
272
|
+
}
|
|
273
|
+
return content.slice(0, range.contentStart) + updated + content.slice(range.end);
|
|
274
|
+
}
|
|
275
|
+
function transformFrontmatter(content, transform) {
|
|
276
|
+
const match = FRONTMATTER_RE.exec(content);
|
|
277
|
+
if (!match?.[1]) {
|
|
278
|
+
return content;
|
|
279
|
+
}
|
|
280
|
+
const updated = transform(match[1]);
|
|
281
|
+
return `---\n${updated}\n---\n${content.slice(match[0].length)}`;
|
|
282
|
+
}
|
|
283
|
+
function mapWithFrontmatter(content, read) {
|
|
284
|
+
const match = FRONTMATTER_RE.exec(content);
|
|
285
|
+
return read(match?.[1] ?? '');
|
|
286
|
+
}
|
|
287
|
+
function technicalReferencesFromFrontmatter(frontmatter) {
|
|
288
|
+
return parseTechnicalReferenceGroundingRecords(frontmatter);
|
|
289
|
+
}
|
|
290
|
+
function criteriaRecordsFromFrontmatter(frontmatter) {
|
|
291
|
+
return parseCriterionGroundingRecords(frontmatter);
|
|
292
|
+
}
|
|
293
|
+
function groundingBlockLines(criteria, technicalReferences) {
|
|
294
|
+
const criteriaLines = renderGroundingFrontmatter(criteria).slice(2);
|
|
295
|
+
const technicalReferenceLines = technicalReferences.length > 0
|
|
296
|
+
? renderTechnicalReferenceGroundingFrontmatter(technicalReferences)
|
|
297
|
+
: [];
|
|
298
|
+
return ['grounding:', ...criteriaLines, ...technicalReferenceLines];
|
|
299
|
+
}
|
|
300
|
+
function replaceFrontmatterKeyBlock(frontmatter, key, blockLines) {
|
|
301
|
+
const re = new RegExp(`^${escapeRegex(key)}:.*(?:\\n[ \\t]+.*)*`, 'm');
|
|
302
|
+
const match = re.exec(frontmatter);
|
|
303
|
+
const replacement = blockLines.join('\n');
|
|
304
|
+
if (!match) {
|
|
305
|
+
return `${frontmatter.trimEnd()}\n${replacement}`;
|
|
306
|
+
}
|
|
307
|
+
return (frontmatter.slice(0, match.index) +
|
|
308
|
+
replacement +
|
|
309
|
+
frontmatter.slice(match.index + match[0].length));
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=propagate-mirrors.js.map
|
|
@@ -3,6 +3,7 @@ import { applyTypescriptFix } from './strategies/typescript-fix.js';
|
|
|
3
3
|
import { applyLintFix } from './strategies/lint-fix.js';
|
|
4
4
|
import { applyTestFix } from './strategies/test-fix.js';
|
|
5
5
|
import { applyCoverageFix } from './strategies/coverage-fix.js';
|
|
6
|
+
import { qaCheckTimeoutMs } from '../qa-gate.js';
|
|
6
7
|
const MAX_ATTEMPTS = 3;
|
|
7
8
|
/**
|
|
8
9
|
* Maps a failure type to the appropriate fix strategy name.
|
|
@@ -24,14 +25,14 @@ export function identifyFixStrategy(failureType) {
|
|
|
24
25
|
/**
|
|
25
26
|
* Dispatches to the appropriate strategy based on strategy name.
|
|
26
27
|
*/
|
|
27
|
-
function runStrategy(strategyName, projectPath, details) {
|
|
28
|
+
function runStrategy(strategyName, projectPath, details, timeoutMs) {
|
|
28
29
|
switch (strategyName) {
|
|
29
30
|
case technologyValue('technology-typescript-969545'):
|
|
30
|
-
return applyTypescriptFix(projectPath, details);
|
|
31
|
+
return applyTypescriptFix(projectPath, details, timeoutMs);
|
|
31
32
|
case 'lint':
|
|
32
|
-
return applyLintFix(projectPath, details);
|
|
33
|
+
return applyLintFix(projectPath, details, timeoutMs);
|
|
33
34
|
case 'tests':
|
|
34
|
-
return applyTestFix(projectPath, details);
|
|
35
|
+
return applyTestFix(projectPath, details, timeoutMs);
|
|
35
36
|
case 'coverage':
|
|
36
37
|
return applyCoverageFix(projectPath, details);
|
|
37
38
|
}
|
|
@@ -47,8 +48,14 @@ export async function runHealingLoop(specId, projectPath, failureContext) {
|
|
|
47
48
|
const fixedIssues = [];
|
|
48
49
|
const remainingIssues = [];
|
|
49
50
|
let healed = false;
|
|
51
|
+
const deadline = Date.now() + qaCheckTimeoutMs();
|
|
50
52
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
51
|
-
const
|
|
53
|
+
const remainingMs = deadline - Date.now();
|
|
54
|
+
if (remainingMs <= 0) {
|
|
55
|
+
remainingIssues.push(`Attempt ${attempt}: skipped — healing wall-clock budget exhausted`);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
const result = await runStrategy(strategy, projectPath, failureContext.details, remainingMs);
|
|
52
59
|
if (result.fixed) {
|
|
53
60
|
attempts.push({
|
|
54
61
|
strategy,
|
|
@@ -2,5 +2,5 @@ import type { StrategyResult } from '../../../types/index.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Runs eslint --fix on the project and reports what was fixed.
|
|
4
4
|
*/
|
|
5
|
-
export declare function applyLintFix(projectPath: string, _details: string): Promise<StrategyResult>;
|
|
5
|
+
export declare function applyLintFix(projectPath: string, _details: string, timeoutMs?: number): Promise<StrategyResult>;
|
|
6
6
|
//# sourceMappingURL=lint-fix.d.ts.map
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
// engine/self-healing/strategies/lint-fix.ts — SPEC-474: ESLint auto-fix strategy
|
|
2
2
|
import { execFile } from 'node:child_process';
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
|
+
import { qaCheckTimeoutMs } from '../../qa-gate.js';
|
|
4
5
|
const execFileAsync = promisify(execFile);
|
|
5
6
|
/**
|
|
6
7
|
* Runs eslint --fix on the project and reports what was fixed.
|
|
7
8
|
*/
|
|
8
|
-
export async function applyLintFix(projectPath, _details) {
|
|
9
|
+
export async function applyLintFix(projectPath, _details, timeoutMs = qaCheckTimeoutMs()) {
|
|
9
10
|
try {
|
|
10
11
|
await execFileAsync('pnpm', ['eslint', '--fix', 'src/', 'tests/', '--ext', '.ts'], {
|
|
11
12
|
cwd: projectPath,
|
|
13
|
+
timeout: timeoutMs,
|
|
14
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
12
15
|
});
|
|
13
16
|
return {
|
|
14
17
|
fixed: true,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { StrategyResult } from '../../../types/index.js';
|
|
2
2
|
/**
|
|
3
|
-
* Identifies failing tests from
|
|
4
|
-
* Does NOT modify test files — test logic changes
|
|
3
|
+
* Identifies failing tests from the project's test command output and reports
|
|
4
|
+
* what needs manual review. Does NOT modify test files — test logic changes
|
|
5
|
+
* require human judgment.
|
|
5
6
|
*/
|
|
6
|
-
export declare function applyTestFix(projectPath: string, _details: string): Promise<StrategyResult>;
|
|
7
|
+
export declare function applyTestFix(projectPath: string, _details: string, timeoutMs?: number): Promise<StrategyResult>;
|
|
7
8
|
//# sourceMappingURL=test-fix.d.ts.map
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// engine/self-healing/strategies/test-fix.ts — SPEC-474: Test fix strategy
|
|
2
|
-
import {
|
|
2
|
+
import { detectPackageManager } from '../../project-dna/stack-detector.js';
|
|
3
|
+
import { runAbortableProcess } from '../../abortable-process-runner.js';
|
|
4
|
+
import { getRuntimePolicy } from '../../runtime-policy.js';
|
|
5
|
+
import { qaCheckTimeoutMs } from '../../qa-gate.js';
|
|
3
6
|
/**
|
|
4
7
|
* Parses failing test entries from vitest output.
|
|
5
8
|
* Looks for:
|
|
@@ -42,25 +45,40 @@ function parseFailingTests(output) {
|
|
|
42
45
|
}
|
|
43
46
|
return results;
|
|
44
47
|
}
|
|
48
|
+
function testCommandFor(packageManager) {
|
|
49
|
+
switch (packageManager) {
|
|
50
|
+
case 'yarn':
|
|
51
|
+
return { executable: 'yarn', args: ['test'] };
|
|
52
|
+
case 'npm':
|
|
53
|
+
return { executable: 'npm', args: ['test'] };
|
|
54
|
+
case 'bun':
|
|
55
|
+
return { executable: 'bun', args: ['run', 'test'] };
|
|
56
|
+
default:
|
|
57
|
+
return { executable: 'pnpm', args: ['test'] };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
45
60
|
/**
|
|
46
|
-
* Identifies failing tests from
|
|
47
|
-
* Does NOT modify test files — test logic changes
|
|
61
|
+
* Identifies failing tests from the project's test command output and reports
|
|
62
|
+
* what needs manual review. Does NOT modify test files — test logic changes
|
|
63
|
+
* require human judgment.
|
|
48
64
|
*/
|
|
49
|
-
export function applyTestFix(projectPath, _details) {
|
|
50
|
-
return
|
|
65
|
+
export async function applyTestFix(projectPath, _details, timeoutMs = qaCheckTimeoutMs()) {
|
|
66
|
+
return runTestDiagnostic(await captureTestOutput(projectPath, timeoutMs));
|
|
51
67
|
}
|
|
52
|
-
function captureTestOutput(projectPath) {
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
async function captureTestOutput(projectPath, timeoutMs) {
|
|
69
|
+
const packageManager = await detectPackageManager(projectPath);
|
|
70
|
+
const { executable, args } = testCommandFor(packageManager);
|
|
71
|
+
const result = await runAbortableProcess(executable, args, {
|
|
72
|
+
cwd: projectPath,
|
|
73
|
+
timeoutMs,
|
|
74
|
+
maxBufferBytes: getRuntimePolicy().qualityAssurance.maximumOutputBufferBytes,
|
|
75
|
+
});
|
|
76
|
+
if (result.status === 0) {
|
|
55
77
|
return null;
|
|
56
78
|
}
|
|
57
|
-
|
|
58
|
-
const errObj = err;
|
|
59
|
-
return errObj.stdout ?? errObj.stderr ?? errObj.message ?? '';
|
|
60
|
-
}
|
|
79
|
+
return [result.stdout, result.stderr].join('\n');
|
|
61
80
|
}
|
|
62
|
-
function runTestDiagnostic(
|
|
63
|
-
const output = captureTestOutput(projectPath);
|
|
81
|
+
function runTestDiagnostic(output) {
|
|
64
82
|
if (output === null) {
|
|
65
83
|
return { fixed: true, reason: 'All tests pass — no failures detected', fixedIssues: [] };
|
|
66
84
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { StrategyResult } from '../../../types/index.js';
|
|
2
|
-
export declare function applyTypescriptFix(projectPath: string, _details: string): Promise<StrategyResult>;
|
|
2
|
+
export declare function applyTypescriptFix(projectPath: string, _details: string, timeoutMs?: number): Promise<StrategyResult>;
|
|
3
3
|
//# sourceMappingURL=typescript-fix.d.ts.map
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// engine/self-healing/strategies/typescript-fix.ts — SPEC-474: TypeScript fix strategy
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
3
2
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
4
3
|
import { join } from 'node:path';
|
|
4
|
+
import { detectPackageManager } from '../../project-dna/stack-detector.js';
|
|
5
|
+
import { runAbortableProcess } from '../../abortable-process-runner.js';
|
|
6
|
+
import { getRuntimePolicy } from '../../runtime-policy.js';
|
|
7
|
+
import { qaCheckTimeoutMs } from '../../qa-gate.js';
|
|
5
8
|
/**
|
|
6
9
|
* Parses TS6133 (unused import) errors from tsc output.
|
|
7
10
|
* Format: path/file.ts(10,5): error TS6133: 'foo' is declared but its value is never read.
|
|
@@ -94,22 +97,37 @@ async function fixFileUnusedImports(projectPath, filePath, entries) {
|
|
|
94
97
|
}
|
|
95
98
|
return { fixed: fixedDescriptions, skipped };
|
|
96
99
|
}
|
|
100
|
+
function typecheckCommandFor(packageManager) {
|
|
101
|
+
switch (packageManager) {
|
|
102
|
+
case 'yarn':
|
|
103
|
+
return { executable: 'yarn', args: ['typecheck'] };
|
|
104
|
+
case 'npm':
|
|
105
|
+
return { executable: 'npm', args: ['run', 'typecheck'] };
|
|
106
|
+
case 'bun':
|
|
107
|
+
return { executable: 'bun', args: ['run', 'typecheck'] };
|
|
108
|
+
default:
|
|
109
|
+
return { executable: 'pnpm', args: ['typecheck'] };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
97
112
|
/**
|
|
98
113
|
* Attempts to fix TypeScript errors by parsing tsc output and applying safe fixes:
|
|
99
114
|
* - TS6133: removes unused imports when the pattern is unambiguous
|
|
100
115
|
*/
|
|
101
|
-
function captureTypecheckOutput(projectPath) {
|
|
102
|
-
|
|
103
|
-
|
|
116
|
+
async function captureTypecheckOutput(projectPath, timeoutMs) {
|
|
117
|
+
const packageManager = await detectPackageManager(projectPath);
|
|
118
|
+
const { executable, args } = typecheckCommandFor(packageManager);
|
|
119
|
+
const result = await runAbortableProcess(executable, args, {
|
|
120
|
+
cwd: projectPath,
|
|
121
|
+
timeoutMs,
|
|
122
|
+
maxBufferBytes: getRuntimePolicy().qualityAssurance.maximumOutputBufferBytes,
|
|
123
|
+
});
|
|
124
|
+
if (result.status === 0) {
|
|
104
125
|
return null;
|
|
105
126
|
}
|
|
106
|
-
|
|
107
|
-
const errObj = err;
|
|
108
|
-
return errObj.stdout ?? errObj.stderr ?? errObj.message ?? '';
|
|
109
|
-
}
|
|
127
|
+
return [result.stdout, result.stderr].join('\n');
|
|
110
128
|
}
|
|
111
|
-
export async function applyTypescriptFix(projectPath, _details) {
|
|
112
|
-
const output = captureTypecheckOutput(projectPath);
|
|
129
|
+
export async function applyTypescriptFix(projectPath, _details, timeoutMs = qaCheckTimeoutMs()) {
|
|
130
|
+
const output = await captureTypecheckOutput(projectPath, timeoutMs);
|
|
113
131
|
if (output === null) {
|
|
114
132
|
return { fixed: true, reason: 'No TypeScript errors found', fixedIssues: [] };
|
|
115
133
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Fire-and-forget safe — never throws to caller.
|
|
4
4
|
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
|
-
import {
|
|
6
|
+
import { getSessionTracker } from './session-tracker.js';
|
|
7
7
|
const MAX_DISPLAYED_CALLS = 5;
|
|
8
8
|
const MAX_DISPLAYED_ERRORS = 3;
|
|
9
9
|
const MAX_DISPLAYED_FILES = 10;
|
|
@@ -83,9 +83,13 @@ export async function writeCheckpoint(projectPath, snapshot) {
|
|
|
83
83
|
* Call this from safe-handler.ts after each tool response.
|
|
84
84
|
*/
|
|
85
85
|
export function maybeWriteCheckpoint(toolName, success, projectPath) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
if (projectPath === undefined) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const tracker = getSessionTracker(projectPath);
|
|
90
|
+
tracker.recordCall(toolName, success);
|
|
91
|
+
if (tracker.shouldCheckpoint()) {
|
|
92
|
+
void writeCheckpoint(projectPath, tracker.getSnapshot()).catch(() => {
|
|
89
93
|
/* resilient — never affects tool response */
|
|
90
94
|
});
|
|
91
95
|
}
|
|
@@ -1,31 +1,20 @@
|
|
|
1
1
|
import type { AutoCheckpointPolicy, AutoSessionSnapshot } from '../../types/index.js';
|
|
2
|
-
declare class SessionTracker {
|
|
2
|
+
export declare class SessionTracker {
|
|
3
3
|
private callCount;
|
|
4
4
|
private readonly lastCalls;
|
|
5
5
|
private readonly modifiedFiles;
|
|
6
6
|
private readonly errors;
|
|
7
|
-
private policy;
|
|
8
|
-
/** Record a tool call. Appends to lastCalls (capped at MAX_LAST_CALLS). */
|
|
9
7
|
recordCall(tool: string, success: boolean): void;
|
|
10
|
-
/** Record a file path as modified during this session. */
|
|
11
8
|
recordFile(path: string): void;
|
|
12
|
-
/** Record an error message (capped at MAX_ERRORS). */
|
|
13
9
|
recordError(msg: string): void;
|
|
14
|
-
/**
|
|
15
|
-
* Returns true when a checkpoint should be written:
|
|
16
|
-
* policy is enabled, callCount > 0, and callCount is a multiple of threshold.
|
|
17
|
-
*/
|
|
18
10
|
shouldCheckpoint(): boolean;
|
|
19
|
-
/** Return a compact snapshot of the current session state. */
|
|
20
11
|
getSnapshot(): AutoSessionSnapshot;
|
|
21
|
-
/** Update the checkpoint policy (partial update — unspecified fields keep current values). */
|
|
22
12
|
setPolicy(p: Partial<AutoCheckpointPolicy>): void;
|
|
23
|
-
/** Return the current checkpoint policy. */
|
|
24
13
|
getPolicy(): AutoCheckpointPolicy;
|
|
25
|
-
/** Reset the tracker state (useful for testing). */
|
|
26
14
|
reset(): void;
|
|
27
15
|
}
|
|
28
|
-
|
|
29
|
-
export declare
|
|
30
|
-
export
|
|
16
|
+
export declare function getSessionTracker(projectPath: string): SessionTracker;
|
|
17
|
+
export declare function setCheckpointPolicy(p: Partial<AutoCheckpointPolicy>): void;
|
|
18
|
+
export declare function getCheckpointPolicy(): AutoCheckpointPolicy;
|
|
19
|
+
export declare function resetSessionTrackers(): void;
|
|
31
20
|
//# sourceMappingURL=session-tracker.d.ts.map
|