@planu/cli 4.7.1 → 4.7.3
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 +18 -0
- package/dist/config/minimal-implementation-gate.json +110 -0
- package/dist/config/token-waste-autopilot.json +16 -0
- package/dist/engine/compact/compact-middleware.d.ts +2 -2
- package/dist/engine/compact/compact-middleware.js +68 -7
- package/dist/engine/context-artifacts/index.d.ts +2 -0
- package/dist/engine/context-artifacts/index.js +2 -0
- package/dist/engine/context-artifacts/store.d.ts +5 -0
- package/dist/engine/context-artifacts/store.js +176 -0
- package/dist/engine/handoff-artifacts/schemas.d.ts +112 -0
- package/dist/engine/handoff-artifacts/schemas.js +40 -0
- package/dist/engine/minimality/analyzer.d.ts +3 -0
- package/dist/engine/minimality/analyzer.js +140 -0
- package/dist/engine/minimality/formatter.d.ts +3 -0
- package/dist/engine/minimality/formatter.js +25 -0
- package/dist/engine/minimality/index.d.ts +4 -0
- package/dist/engine/minimality/index.js +4 -0
- package/dist/engine/minimality/policy-loader.d.ts +3 -0
- package/dist/engine/minimality/policy-loader.js +133 -0
- package/dist/engine/token-optimizer/content-aware-compactor.d.ts +4 -0
- package/dist/engine/token-optimizer/content-aware-compactor.js +230 -0
- package/dist/engine/token-optimizer/index.d.ts +1 -0
- package/dist/engine/token-optimizer/index.js +1 -0
- package/dist/engine/token-optimizer/output-filter.js +18 -2
- package/dist/engine/token-optimizer/policy-loader.js +12 -0
- package/dist/engine/token-optimizer/reporter.d.ts +4 -0
- package/dist/engine/token-optimizer/reporter.js +14 -1
- package/dist/engine/validator/validation-report-writer.d.ts +2 -0
- package/dist/engine/validator/validation-report-writer.js +19 -0
- package/dist/engine/web-fetcher/docs-fetcher.js +5 -1
- package/dist/tools/challenge-spec.js +25 -0
- package/dist/tools/package-handoff.js +23 -1
- package/dist/tools/safe-handler.js +4 -1
- package/dist/tools/token-usage-handler.js +5 -3
- package/dist/tools/update-status/dod-gates.js +9 -0
- package/dist/tools/validate.js +34 -0
- package/dist/types/compact/compact-mode.d.ts +5 -0
- package/dist/types/context-artifacts.d.ts +96 -0
- package/dist/types/context-artifacts.js +2 -0
- package/dist/types/handoff-artifacts.d.ts +2 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/minimal-implementation-gate.d.ts +92 -0
- package/dist/types/minimal-implementation-gate.js +2 -0
- package/dist/types/token-optimization.d.ts +2 -0
- package/dist/types/token-waste-autopilot.d.ts +15 -0
- package/package.json +17 -17
- package/planu-native.json +1 -1
- package/planu-plugin.json +1 -1
|
@@ -28,6 +28,16 @@ export class OptimizationReporter {
|
|
|
28
28
|
record.tokensSaved += tokensSaved;
|
|
29
29
|
this.totalSaved += tokensSaved;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Record measured savings from reversible compaction.
|
|
33
|
+
*/
|
|
34
|
+
recordCompaction(toolName, tokensSaved, retrievals = 0) {
|
|
35
|
+
const record = this.getOrCreateRecord(toolName);
|
|
36
|
+
record.compactionTokensSaved = (record.compactionTokensSaved ?? 0) + tokensSaved;
|
|
37
|
+
record.artifactRetrievals = (record.artifactRetrievals ?? 0) + retrievals;
|
|
38
|
+
record.tokensSaved += tokensSaved;
|
|
39
|
+
this.totalSaved += tokensSaved;
|
|
40
|
+
}
|
|
31
41
|
/**
|
|
32
42
|
* Record a cache miss for a tool.
|
|
33
43
|
*/
|
|
@@ -106,7 +116,8 @@ export class OptimizationReporter {
|
|
|
106
116
|
for (const record of toolRecords) {
|
|
107
117
|
lines.push(`- ${record.toolName}: ${String(record.totalTokens)} tokens, ` +
|
|
108
118
|
`${String(record.callCount)} calls, ` +
|
|
109
|
-
`${String(record.tokensSaved)} saved`
|
|
119
|
+
`${String(record.tokensSaved)} saved ` +
|
|
120
|
+
`(compaction ${String(record.compactionTokensSaved ?? 0)}, retrievals ${String(record.artifactRetrievals ?? 0)})`);
|
|
110
121
|
}
|
|
111
122
|
}
|
|
112
123
|
return lines.join('\n');
|
|
@@ -134,6 +145,8 @@ export class OptimizationReporter {
|
|
|
134
145
|
cacheHits: 0,
|
|
135
146
|
cacheMisses: 0,
|
|
136
147
|
tokensSaved: 0,
|
|
148
|
+
compactionTokensSaved: 0,
|
|
149
|
+
artifactRetrievals: 0,
|
|
137
150
|
};
|
|
138
151
|
this.toolRecords.set(toolName, record);
|
|
139
152
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Spec } from '../../types/index.js';
|
|
2
2
|
import type { ValidationReportV1 } from '../../types/handoff-artifacts.js';
|
|
3
|
+
import type { MinimalImplementationReport } from '../../types/minimal-implementation-gate.js';
|
|
3
4
|
export declare function writeImplementationReviewReport(input: {
|
|
4
5
|
projectId: string;
|
|
5
6
|
specId: string;
|
|
@@ -8,6 +9,7 @@ export declare function writeImplementationReviewReport(input: {
|
|
|
8
9
|
score: number | null;
|
|
9
10
|
lintPassed?: boolean;
|
|
10
11
|
conventionRegression?: boolean;
|
|
12
|
+
minimalityReport?: MinimalImplementationReport;
|
|
11
13
|
}): Promise<{
|
|
12
14
|
written: boolean;
|
|
13
15
|
path?: string;
|
|
@@ -7,6 +7,7 @@ export async function writeImplementationReviewReport(input) {
|
|
|
7
7
|
lintPassed: input.lintPassed ?? true,
|
|
8
8
|
conventionRegression: input.conventionRegression ?? false,
|
|
9
9
|
specCompliance,
|
|
10
|
+
minimalityReport: input.minimalityReport,
|
|
10
11
|
});
|
|
11
12
|
const passed = gates.every((gate) => gate.passed);
|
|
12
13
|
const reviewer = {
|
|
@@ -22,6 +23,7 @@ export async function writeImplementationReviewReport(input) {
|
|
|
22
23
|
gates,
|
|
23
24
|
reviewer,
|
|
24
25
|
specCompliance: reportCompliance,
|
|
26
|
+
minimalityReport: input.minimalityReport,
|
|
25
27
|
score: input.score ?? undefined,
|
|
26
28
|
completedAt: new Date().toISOString(),
|
|
27
29
|
};
|
|
@@ -73,8 +75,25 @@ function buildGates(args) {
|
|
|
73
75
|
reason: args.conventionRegression ? 'Convention baseline regression detected.' : undefined,
|
|
74
76
|
},
|
|
75
77
|
buildSpecComplianceGate(args.specCompliance),
|
|
78
|
+
buildMinimalityGate(args.minimalityReport),
|
|
76
79
|
];
|
|
77
80
|
}
|
|
81
|
+
function buildMinimalityGate(report) {
|
|
82
|
+
if (report?.enabled !== true) {
|
|
83
|
+
return {
|
|
84
|
+
name: 'minimal-implementation',
|
|
85
|
+
passed: true,
|
|
86
|
+
reason: 'Minimal implementation report unavailable or disabled.',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
name: 'minimal-implementation',
|
|
91
|
+
passed: !report.blocked,
|
|
92
|
+
reason: report.blocked
|
|
93
|
+
? `Blocking minimality findings: ${String(report.findings.filter((finding) => finding.blocksDone).length)}.`
|
|
94
|
+
: undefined,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
78
97
|
function buildSpecComplianceGate(result) {
|
|
79
98
|
if (result.perScenario.length === 0) {
|
|
80
99
|
return {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Planu — Web Fetcher: consultDocs orchestration
|
|
2
2
|
import { searchAndRegisterFramework, saveRegistry } from '../registry-updater.js';
|
|
3
3
|
import { loadDocsRegistry, findDocsEntry, setDocsRegistryCache, getStoredRegistryPath, } from './registry-loader.js';
|
|
4
|
+
import { DEFAULT_CONFIG } from './cache.js';
|
|
4
5
|
import { fetchUrl } from './http-client.js';
|
|
5
6
|
import { extractTextContent, extractCodeBlocks, extractBestPractices, buildSummaryFromContent, buildPlaceholderSummary, buildPlaceholderExamples, buildPlaceholderBestPractices, } from './content-extractor.js';
|
|
6
7
|
export async function consultDocsImpl(topic, framework) {
|
|
@@ -25,7 +26,10 @@ export async function consultDocsImpl(topic, framework) {
|
|
|
25
26
|
: `https://www.google.com/search?q=${encodeURIComponent(`${resolvedFramework} ${topic} documentation`)}`;
|
|
26
27
|
const baseUrl = docsEntry?.base ?? '';
|
|
27
28
|
if (baseUrl) {
|
|
28
|
-
const fetchResult = await fetchUrl(docsUrl
|
|
29
|
+
const fetchResult = await fetchUrl(docsUrl, {
|
|
30
|
+
...DEFAULT_CONFIG,
|
|
31
|
+
timeoutMs: 2_500,
|
|
32
|
+
});
|
|
29
33
|
if (fetchResult) {
|
|
30
34
|
const textContent = extractTextContent(fetchResult.content);
|
|
31
35
|
const codeBlocks = extractCodeBlocks(fetchResult.content);
|
|
@@ -24,6 +24,7 @@ import { generateResilienceChallengeScenarios } from './challenge-spec/resilienc
|
|
|
24
24
|
import { detectContradictions as detectDecisionContradictions, searchPriorDecisions, } from '../engine/prior-decisions/index.js';
|
|
25
25
|
import { getDecisions } from '../storage/decision-store.js';
|
|
26
26
|
import { calculateTokenBudget, injectBudgetIntoPrompt } from '../engine/token-budget/index.js';
|
|
27
|
+
import { analyzeMinimalImplementation, loadMinimalImplementationPolicy, } from '../engine/minimality/index.js';
|
|
27
28
|
const ALL_FOCUS_AREAS = [
|
|
28
29
|
'failures',
|
|
29
30
|
'concurrency',
|
|
@@ -127,6 +128,30 @@ export async function handleChallengeSpec(args, server) {
|
|
|
127
128
|
const focusAreas = focus && focus.length > 0 ? focus : ALL_FOCUS_AREAS;
|
|
128
129
|
// 5. Generate failure scenarios
|
|
129
130
|
const failureScenarios = [];
|
|
131
|
+
try {
|
|
132
|
+
const { policy, evidence } = await loadMinimalImplementationPolicy(knowledge.projectPath);
|
|
133
|
+
const minimality = analyzeMinimalImplementation({
|
|
134
|
+
policy,
|
|
135
|
+
specId,
|
|
136
|
+
specRisk: spec.risk,
|
|
137
|
+
specText: specContent,
|
|
138
|
+
evidence,
|
|
139
|
+
});
|
|
140
|
+
for (const finding of minimality.findings.slice(0, 5)) {
|
|
141
|
+
failureScenarios.push({
|
|
142
|
+
scenario: `Minimal implementation concern: ${finding.tag}`,
|
|
143
|
+
probability: finding.confidence === 'high' ? 'high' : 'medium',
|
|
144
|
+
impact: finding.blocksDone ? 'high' : 'low',
|
|
145
|
+
currentHandling: `${finding.target}${finding.line ? `:${String(finding.line)}` : ''} — ${finding.evidence}`,
|
|
146
|
+
requiredHandling: finding.replacementGuidance,
|
|
147
|
+
dataConsistency: 'Scope control risk',
|
|
148
|
+
userExperience: 'Avoidable implementation complexity can increase token use and maintenance cost',
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Minimality challenge is best-effort and must not hide resilience findings.
|
|
154
|
+
}
|
|
130
155
|
if (focusAreas.includes('failures')) {
|
|
131
156
|
failureScenarios.push(...generateFailureScenarios(spec, specContent, knowledge));
|
|
132
157
|
}
|
|
@@ -6,6 +6,7 @@ import { detectParadigms } from '../engine/paradigm-detector.js';
|
|
|
6
6
|
import { analyzeContextPreflight, buildTokenWasteReport, formatTokenWasteReport, loadTokenWastePolicy, recommendRelevantTools, toolsFromPolicyGroups, } from '../engine/token-optimizer/index.js';
|
|
7
7
|
import { appendTransitionEvent } from '../storage/transition-log.js';
|
|
8
8
|
import { formatProjectGraphContext, queryProjectGraphSlice, } from '../engine/project-graph/index.js';
|
|
9
|
+
import { analyzeMinimalImplementation, loadMinimalImplementationPolicy, } from '../engine/minimality/index.js';
|
|
9
10
|
// ── Formatting helpers ───────────────────────────────────────────────────────
|
|
10
11
|
function formatHandoff(pkg) {
|
|
11
12
|
const lines = [];
|
|
@@ -196,6 +197,22 @@ async function buildGraphContextSection(args) {
|
|
|
196
197
|
return null;
|
|
197
198
|
}
|
|
198
199
|
}
|
|
200
|
+
async function buildMinimalImplementationReport(pkg, knowledge, spec, handoffText) {
|
|
201
|
+
try {
|
|
202
|
+
const { policy, evidence } = await loadMinimalImplementationPolicy(knowledge.projectPath);
|
|
203
|
+
return analyzeMinimalImplementation({
|
|
204
|
+
policy,
|
|
205
|
+
specId: spec.id,
|
|
206
|
+
specRisk: spec.risk,
|
|
207
|
+
handoffText,
|
|
208
|
+
files: [...pkg.filesToModify, ...pkg.filesToCreate].map((path) => ({ path, content: path })),
|
|
209
|
+
evidence,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
199
216
|
// ── Handler ──────────────────────────────────────────────────────────────────
|
|
200
217
|
export async function handlePackageHandoff(args) {
|
|
201
218
|
const { projectId, specId } = args;
|
|
@@ -256,9 +273,13 @@ export async function handlePackageHandoff(args) {
|
|
|
256
273
|
projectPath: knowledge.projectPath,
|
|
257
274
|
specId,
|
|
258
275
|
});
|
|
259
|
-
const
|
|
276
|
+
const baseHandoffText = graphContext !== null
|
|
260
277
|
? `${formatHandoff(pkgWithScore)}\n${graphContext.text}`
|
|
261
278
|
: formatHandoff(pkgWithScore);
|
|
279
|
+
const minimalityReport = await buildMinimalImplementationReport(pkgWithScore, knowledge, spec, baseHandoffText);
|
|
280
|
+
const handoffText = minimalityReport !== null && minimalityReport.markdown.length > 0
|
|
281
|
+
? `${baseHandoffText}\n${minimalityReport.markdown}`
|
|
282
|
+
: baseHandoffText;
|
|
262
283
|
const formatted = tokenWasteReport !== null
|
|
263
284
|
? `${handoffText}\n${formatTokenWasteReport(tokenWasteReport)}`
|
|
264
285
|
: handoffText;
|
|
@@ -282,6 +303,7 @@ export async function handlePackageHandoff(args) {
|
|
|
282
303
|
blockers: pkgWithScore.blockers,
|
|
283
304
|
handoffPath: pkgWithScore.handoffPath,
|
|
284
305
|
contextHash: pkgWithScore.contextHash,
|
|
306
|
+
...(minimalityReport !== null ? { minimality: minimalityReport } : {}),
|
|
285
307
|
...(tokenWasteReport !== null ? { tokenWaste: tokenWasteReport } : {}),
|
|
286
308
|
...(graphContext !== null ? { graphContext: graphContext.structured } : {}),
|
|
287
309
|
},
|
|
@@ -268,7 +268,10 @@ function safeWithTelemetry(toolName, handler) {
|
|
|
268
268
|
// SPEC-455: Compress verbose JSON outputs to save LLM tokens
|
|
269
269
|
const compressed = compressToolOutput(result);
|
|
270
270
|
// SPEC-922: Apply compact mode middleware
|
|
271
|
-
const compacted = applyCompactMode(compressed, decision
|
|
271
|
+
const compacted = applyCompactMode(compressed, decision, {
|
|
272
|
+
projectPath,
|
|
273
|
+
flow: toolName,
|
|
274
|
+
});
|
|
272
275
|
// Inject pending drift banner (non-blocking, informational only)
|
|
273
276
|
const driftBanner = projectPath !== undefined ? await checkPendingDriftBanner(projectPath) : null;
|
|
274
277
|
const withDrift = injectDriftBanner(compacted, driftBanner);
|
|
@@ -27,10 +27,12 @@ export function handleTokenUsage(args) {
|
|
|
27
27
|
let details;
|
|
28
28
|
if (groupBy === 'tool' && toolRecords.length > 0) {
|
|
29
29
|
const lines = toolRecords.map((r) => `| ${r.toolName} | ${String(r.totalTokens)} | ${String(r.callCount)} | ` +
|
|
30
|
-
`${String(r.cacheHits)}/${String(r.cacheHits + r.cacheMisses)} |
|
|
30
|
+
`${String(r.cacheHits)}/${String(r.cacheHits + r.cacheMisses)} | ` +
|
|
31
|
+
`${String(r.tokensSaved)} | ${String(r.compactionTokensSaved ?? 0)} | ` +
|
|
32
|
+
`${String(r.artifactRetrievals ?? 0)} |`);
|
|
31
33
|
details = [
|
|
32
|
-
'| Tool | Tokens | Calls | Cache H/T | Saved |',
|
|
33
|
-
'
|
|
34
|
+
'| Tool | Tokens | Calls | Cache H/T | Saved | Compaction Saved | Retrievals |',
|
|
35
|
+
'|------|--------|-------|-----------|-------|------------------|------------|',
|
|
34
36
|
...lines,
|
|
35
37
|
].join('\n');
|
|
36
38
|
}
|
|
@@ -299,6 +299,15 @@ export async function checkValidationReportGate(specId, projectId, force) {
|
|
|
299
299
|
fixHint: 'Fix failing gates and re-run validate before marking done.',
|
|
300
300
|
});
|
|
301
301
|
}
|
|
302
|
+
if (result.payload.minimalityReport?.blocked) {
|
|
303
|
+
return validationReportGateError({
|
|
304
|
+
specId,
|
|
305
|
+
error: 'minimality_findings_block_done',
|
|
306
|
+
message: 'Validation report contains blocking minimal implementation findings. Remove avoidable complexity or provide an audited forceStatusReason.',
|
|
307
|
+
gates: result.payload.gates,
|
|
308
|
+
fixHint: 'Fix blocking minimality findings and re-run validate before marking done. Use forceStatus only with an audited reason for accepted complexity.',
|
|
309
|
+
});
|
|
310
|
+
}
|
|
302
311
|
if (result.payload.reviewer.verdict !== 'approved' ||
|
|
303
312
|
result.payload.reviewer.agent.trim().length === 0) {
|
|
304
313
|
return validationReportGateError({
|
package/dist/tools/validate.js
CHANGED
|
@@ -18,6 +18,7 @@ import { compareWithBaseline } from '../storage/convention-baseline.js';
|
|
|
18
18
|
import { writeImplementationReviewReport } from '../engine/validator/validation-report-writer.js';
|
|
19
19
|
import { evaluateNewCodeGate } from '../engine/ai-assurance/index.js';
|
|
20
20
|
import { queryProjectGraphSlice } from '../engine/project-graph/index.js';
|
|
21
|
+
import { analyzeMinimalImplementation, loadMinimalImplementationPolicy, } from '../engine/minimality/index.js';
|
|
21
22
|
// Re-export for external use (SPEC-018)
|
|
22
23
|
export { validateContractCompliance };
|
|
23
24
|
function graphCoverageGaps(slice) {
|
|
@@ -47,6 +48,25 @@ function formatGraphCoverageText(report) {
|
|
|
47
48
|
? `\nGRAPH ${String(report.gaps.length)} graph-backed coverage gap(s)`
|
|
48
49
|
: '';
|
|
49
50
|
}
|
|
51
|
+
async function buildMinimalityReport(args) {
|
|
52
|
+
try {
|
|
53
|
+
const { policy, evidence } = await loadMinimalImplementationPolicy(args.projectPath);
|
|
54
|
+
return analyzeMinimalImplementation({
|
|
55
|
+
policy,
|
|
56
|
+
specId: args.specId,
|
|
57
|
+
specRisk: args.risk,
|
|
58
|
+
specText: [args.title ?? '', ...args.missing, ...args.extra].join('\n'),
|
|
59
|
+
files: args.qualityIssues.map((issue) => ({
|
|
60
|
+
path: issue.file,
|
|
61
|
+
content: [issue.rule, issue.message, issue.suggestion ?? ''].join('\n'),
|
|
62
|
+
})),
|
|
63
|
+
evidence,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
50
70
|
async function validateStrictLayoutOrError(args) {
|
|
51
71
|
try {
|
|
52
72
|
const { validateStrictPlanuLayout } = await import('../engine/spec-migrator/index.js');
|
|
@@ -176,6 +196,15 @@ export async function handleValidate(args, server) {
|
|
|
176
196
|
// 7. Lint check (best-effort — non-blocking)
|
|
177
197
|
const lintCheck = runLintCheck(projectPath, knowledge.lintCommand ?? null);
|
|
178
198
|
const assuranceGates = runAssuranceGates(projectPath);
|
|
199
|
+
const minimalityReport = await buildMinimalityReport({
|
|
200
|
+
projectPath,
|
|
201
|
+
specId,
|
|
202
|
+
risk: spec.risk,
|
|
203
|
+
title: spec.title,
|
|
204
|
+
missing: result.missing,
|
|
205
|
+
extra: result.extra,
|
|
206
|
+
qualityIssues: result.qualityIssues,
|
|
207
|
+
});
|
|
179
208
|
// SPEC-1050: Generate a mandatory implementation-review artifact.
|
|
180
209
|
const validationReport = await writeImplementationReviewReport({
|
|
181
210
|
projectId,
|
|
@@ -185,6 +214,7 @@ export async function handleValidate(args, server) {
|
|
|
185
214
|
score: result.score,
|
|
186
215
|
lintPassed: lintCheck.passed,
|
|
187
216
|
conventionRegression: regressionDetected,
|
|
217
|
+
minimalityReport: minimalityReport ?? undefined,
|
|
188
218
|
});
|
|
189
219
|
// 8. Build output
|
|
190
220
|
const output = {
|
|
@@ -262,6 +292,7 @@ export async function handleValidate(args, server) {
|
|
|
262
292
|
lintCheck,
|
|
263
293
|
assuranceGates,
|
|
264
294
|
validationReport,
|
|
295
|
+
minimalityReport,
|
|
265
296
|
graphCoverage,
|
|
266
297
|
};
|
|
267
298
|
// SPEC-612: Scope boundary validation — warn if impl files match outOfScope items
|
|
@@ -312,6 +343,9 @@ export async function handleValidate(args, server) {
|
|
|
312
343
|
if (!assuranceGates.newCode.passed) {
|
|
313
344
|
suggestions.push(`Assurance gate failed: ${String(assuranceGates.newCode.findings.length)} new-code domain inference issue(s) found.`);
|
|
314
345
|
}
|
|
346
|
+
if (minimalityReport?.blocked) {
|
|
347
|
+
suggestions.push(`${minimalityReport.findings.filter((finding) => finding.blocksDone).length} blocking minimality finding(s) detected. Remove avoidable complexity or record accepted debt before marking done.`);
|
|
348
|
+
}
|
|
315
349
|
const outputWithSuggestions = compactObj({
|
|
316
350
|
...output,
|
|
317
351
|
suggestions: suggestions.length > 0 ? suggestions : undefined,
|
|
@@ -20,6 +20,11 @@ export interface CompactDecision {
|
|
|
20
20
|
contextUsed?: number;
|
|
21
21
|
reason: 'header_above_enter' | 'header_below_exit' | 'hysteresis_keep' | 'no_header';
|
|
22
22
|
}
|
|
23
|
+
export interface CompactModeOptions {
|
|
24
|
+
projectPath?: string;
|
|
25
|
+
sourcePath?: string;
|
|
26
|
+
flow?: string;
|
|
27
|
+
}
|
|
23
28
|
export interface CompactMetrics {
|
|
24
29
|
compactDecisions: number;
|
|
25
30
|
verboseDecisions: number;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export type ContextArtifactContentType = 'json' | 'test-log' | 'runtime-log' | 'search-results' | 'code' | 'spec-or-handoff' | 'generic-text';
|
|
2
|
+
export interface ContextArtifactCompactionMetadata {
|
|
3
|
+
artifactRef: string;
|
|
4
|
+
originalTokens: number;
|
|
5
|
+
compactTokens: number;
|
|
6
|
+
tokensSaved: number;
|
|
7
|
+
strategy: string;
|
|
8
|
+
contentType: ContextArtifactContentType;
|
|
9
|
+
expiresAt: string;
|
|
10
|
+
retrievalHint: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ContextArtifact {
|
|
13
|
+
ref: string;
|
|
14
|
+
contentHash: string;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
expiresAt: string;
|
|
17
|
+
ttlMs: number;
|
|
18
|
+
projectId: string;
|
|
19
|
+
contentType: ContextArtifactContentType;
|
|
20
|
+
strategy: string;
|
|
21
|
+
originalTokens: number;
|
|
22
|
+
compactTokens: number;
|
|
23
|
+
tokensSaved: number;
|
|
24
|
+
retrievalCount: number;
|
|
25
|
+
metadata: Record<string, string | number | boolean>;
|
|
26
|
+
originalContent: string;
|
|
27
|
+
compactContent: string;
|
|
28
|
+
}
|
|
29
|
+
export interface StoreContextArtifactInput {
|
|
30
|
+
projectPath: string;
|
|
31
|
+
originalContent: string;
|
|
32
|
+
compactContent: string;
|
|
33
|
+
contentType: ContextArtifactContentType;
|
|
34
|
+
strategy: string;
|
|
35
|
+
originalTokens: number;
|
|
36
|
+
compactTokens: number;
|
|
37
|
+
ttlMs: number;
|
|
38
|
+
sourcePath?: string;
|
|
39
|
+
flow?: string;
|
|
40
|
+
metadata?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
export interface StoreContextArtifactResult {
|
|
43
|
+
stored: boolean;
|
|
44
|
+
artifact?: ContextArtifact;
|
|
45
|
+
metadata?: ContextArtifactCompactionMetadata;
|
|
46
|
+
refusedReason?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface RetrieveContextArtifactResult {
|
|
49
|
+
found: boolean;
|
|
50
|
+
artifact?: ContextArtifact;
|
|
51
|
+
reason?: 'missing' | 'expired' | 'corrupt' | 'invalid-ref' | 'unauthorized';
|
|
52
|
+
hint: string;
|
|
53
|
+
}
|
|
54
|
+
export interface ContextArtifactStats {
|
|
55
|
+
artifactCount: number;
|
|
56
|
+
totalOriginalTokens: number;
|
|
57
|
+
totalCompactTokens: number;
|
|
58
|
+
totalTokensSaved: number;
|
|
59
|
+
retrievalCount: number;
|
|
60
|
+
}
|
|
61
|
+
export interface ContentAwareCompactionInput {
|
|
62
|
+
text: string;
|
|
63
|
+
policy: {
|
|
64
|
+
contextArtifacts?: {
|
|
65
|
+
enabled?: boolean;
|
|
66
|
+
ttlMs?: number;
|
|
67
|
+
minTokens?: number;
|
|
68
|
+
};
|
|
69
|
+
contentCompaction?: {
|
|
70
|
+
strategies?: Partial<Record<ContextArtifactContentType, {
|
|
71
|
+
maxLines?: number;
|
|
72
|
+
maxSnippetChars?: number;
|
|
73
|
+
}>>;
|
|
74
|
+
};
|
|
75
|
+
redaction: {
|
|
76
|
+
maxSnippetChars: number;
|
|
77
|
+
redactPatterns: string[];
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
projectPath?: string;
|
|
81
|
+
kind?: string;
|
|
82
|
+
sourcePath?: string;
|
|
83
|
+
flow?: string;
|
|
84
|
+
metadata?: Record<string, unknown>;
|
|
85
|
+
}
|
|
86
|
+
export interface ContentAwareCompactionResult {
|
|
87
|
+
text: string;
|
|
88
|
+
originalTokens: number;
|
|
89
|
+
compactTokens: number;
|
|
90
|
+
tokensSaved: number;
|
|
91
|
+
strategy: string;
|
|
92
|
+
contentType: ContextArtifactContentType;
|
|
93
|
+
artifact?: ContextArtifactCompactionMetadata;
|
|
94
|
+
refusedReason?: string;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=context-artifacts.d.ts.map
|
|
@@ -72,6 +72,7 @@ export interface ValidationReportV1 {
|
|
|
72
72
|
evidence: string[];
|
|
73
73
|
}[];
|
|
74
74
|
};
|
|
75
|
+
minimalityReport?: MinimalImplementationReport;
|
|
75
76
|
score?: number;
|
|
76
77
|
completedAt: string;
|
|
77
78
|
}
|
|
@@ -83,4 +84,5 @@ export interface ArtifactPayloadMap {
|
|
|
83
84
|
'validation-report': ValidationReportV1;
|
|
84
85
|
}
|
|
85
86
|
export type ArtifactPayload<K extends ArtifactKind> = ArtifactPayloadMap[K];
|
|
87
|
+
import type { MinimalImplementationReport } from './minimal-implementation-gate.js';
|
|
86
88
|
//# sourceMappingURL=handoff-artifacts.d.ts.map
|
package/dist/types/index.d.ts
CHANGED
|
@@ -80,6 +80,8 @@ export * from './workers.js';
|
|
|
80
80
|
export * from './orchestration-runtime.js';
|
|
81
81
|
export * from './token-optimization.js';
|
|
82
82
|
export * from './token-waste-autopilot.js';
|
|
83
|
+
export * from './context-artifacts.js';
|
|
84
|
+
export * from './minimal-implementation-gate.js';
|
|
83
85
|
export * from './llm-providers.js';
|
|
84
86
|
export * from './plugins.js';
|
|
85
87
|
export * from './github.js';
|
package/dist/types/index.js
CHANGED
|
@@ -81,6 +81,8 @@ export * from './workers.js';
|
|
|
81
81
|
export * from './orchestration-runtime.js';
|
|
82
82
|
export * from './token-optimization.js';
|
|
83
83
|
export * from './token-waste-autopilot.js';
|
|
84
|
+
export * from './context-artifacts.js';
|
|
85
|
+
export * from './minimal-implementation-gate.js';
|
|
84
86
|
export * from './llm-providers.js';
|
|
85
87
|
export * from './plugins.js';
|
|
86
88
|
export * from './github.js';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export type MinimalImplementationTag = string;
|
|
2
|
+
export type MinimalImplementationSeverity = 'info' | 'warning' | 'blocker';
|
|
3
|
+
export type MinimalImplementationConfidence = 'low' | 'medium' | 'high';
|
|
4
|
+
export type MinimalImplementationSource = 'policy' | 'project-config' | 'runtime' | 'spec';
|
|
5
|
+
export interface MinimalImplementationPolicyLoadOptions {
|
|
6
|
+
defaultPolicyPath?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface MinimalImplementationEvidence {
|
|
9
|
+
source: MinimalImplementationSource;
|
|
10
|
+
key: string;
|
|
11
|
+
value?: string | number | boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface MinimalImplementationRule {
|
|
14
|
+
id: string;
|
|
15
|
+
tag: MinimalImplementationTag;
|
|
16
|
+
severity: MinimalImplementationSeverity;
|
|
17
|
+
confidence: MinimalImplementationConfidence;
|
|
18
|
+
patterns: string[];
|
|
19
|
+
replacementGuidance: string;
|
|
20
|
+
blockWhenRiskAtLeast?: 'low' | 'medium' | 'high' | 'max';
|
|
21
|
+
}
|
|
22
|
+
export interface MinimalImplementationPolicy {
|
|
23
|
+
version: 1;
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
report: {
|
|
26
|
+
maxFindings: number;
|
|
27
|
+
maxMarkdownLines: number;
|
|
28
|
+
maxEvidenceChars: number;
|
|
29
|
+
};
|
|
30
|
+
tags: Record<string, {
|
|
31
|
+
description: string;
|
|
32
|
+
}>;
|
|
33
|
+
rules: MinimalImplementationRule[];
|
|
34
|
+
safetyExclusions: string[];
|
|
35
|
+
excludedPathPatterns: string[];
|
|
36
|
+
debtEvidence: {
|
|
37
|
+
requiredFields: string[];
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface LoadedMinimalImplementationPolicy {
|
|
41
|
+
policy: MinimalImplementationPolicy;
|
|
42
|
+
evidence: MinimalImplementationEvidence[];
|
|
43
|
+
}
|
|
44
|
+
export interface MinimalImplementationFileSignal {
|
|
45
|
+
path: string;
|
|
46
|
+
content?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface MinimalImplementationInput {
|
|
49
|
+
policy: MinimalImplementationPolicy;
|
|
50
|
+
specId?: string;
|
|
51
|
+
specRisk?: string;
|
|
52
|
+
specText?: string;
|
|
53
|
+
handoffText?: string;
|
|
54
|
+
files?: MinimalImplementationFileSignal[];
|
|
55
|
+
packageManifestText?: string;
|
|
56
|
+
acceptedDebt?: MinimalImplementationDebtEvidence[];
|
|
57
|
+
evidence?: MinimalImplementationEvidence[];
|
|
58
|
+
}
|
|
59
|
+
export interface MinimalImplementationFinding {
|
|
60
|
+
ruleId: string;
|
|
61
|
+
tag: MinimalImplementationTag;
|
|
62
|
+
severity: MinimalImplementationSeverity;
|
|
63
|
+
confidence: MinimalImplementationConfidence;
|
|
64
|
+
target: string;
|
|
65
|
+
line?: number;
|
|
66
|
+
evidence: string;
|
|
67
|
+
replacementGuidance: string;
|
|
68
|
+
blocksDone: boolean;
|
|
69
|
+
evidenceSource: MinimalImplementationEvidence[];
|
|
70
|
+
}
|
|
71
|
+
export interface MinimalImplementationSafetyException {
|
|
72
|
+
target: string;
|
|
73
|
+
reason: string;
|
|
74
|
+
evidence: string;
|
|
75
|
+
}
|
|
76
|
+
export interface MinimalImplementationDebtEvidence {
|
|
77
|
+
specId: string;
|
|
78
|
+
files: string[];
|
|
79
|
+
ceiling: string;
|
|
80
|
+
upgradeTrigger: string;
|
|
81
|
+
reviewRationale: string;
|
|
82
|
+
}
|
|
83
|
+
export interface MinimalImplementationReport {
|
|
84
|
+
enabled: boolean;
|
|
85
|
+
blocked: boolean;
|
|
86
|
+
findings: MinimalImplementationFinding[];
|
|
87
|
+
safetyExceptions: MinimalImplementationSafetyException[];
|
|
88
|
+
debtEvidence: MinimalImplementationDebtEvidence[];
|
|
89
|
+
evidence: MinimalImplementationEvidence[];
|
|
90
|
+
markdown: string;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=minimal-implementation-gate.d.ts.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ContextArtifactCompactionMetadata } from './context-artifacts.js';
|
|
1
2
|
export type TokenWasteDecisionKind = 'include' | 'summarize' | 'exclude' | 'recommend' | 'avoid' | 'warn' | 'override';
|
|
2
3
|
export type TokenWasteConfidence = 'high' | 'medium' | 'low';
|
|
3
4
|
export type TokenWasteAction = string;
|
|
@@ -21,6 +22,7 @@ export interface TokenWasteOutputStrategy {
|
|
|
21
22
|
keepFailures?: boolean;
|
|
22
23
|
uniqueOnly?: boolean;
|
|
23
24
|
summarizeKeys?: boolean;
|
|
25
|
+
maxSnippetChars?: number;
|
|
24
26
|
}
|
|
25
27
|
export interface TokenWastePolicy {
|
|
26
28
|
version: 1;
|
|
@@ -35,6 +37,14 @@ export interface TokenWastePolicy {
|
|
|
35
37
|
outputs: {
|
|
36
38
|
strategies: Record<string, TokenWasteOutputStrategy>;
|
|
37
39
|
};
|
|
40
|
+
contextArtifacts?: {
|
|
41
|
+
enabled: boolean;
|
|
42
|
+
ttlMs: number;
|
|
43
|
+
minTokens: number;
|
|
44
|
+
};
|
|
45
|
+
contentCompaction?: {
|
|
46
|
+
strategies: Record<string, TokenWasteOutputStrategy>;
|
|
47
|
+
};
|
|
38
48
|
tools: {
|
|
39
49
|
groups: Record<string, string[]>;
|
|
40
50
|
maxRecommended: number;
|
|
@@ -83,12 +93,17 @@ export interface VerboseOutputInput {
|
|
|
83
93
|
text: string;
|
|
84
94
|
policy: TokenWastePolicy;
|
|
85
95
|
fullOutputRef?: string;
|
|
96
|
+
projectPath?: string;
|
|
97
|
+
sourcePath?: string;
|
|
98
|
+
flow?: string;
|
|
99
|
+
metadata?: Record<string, unknown>;
|
|
86
100
|
}
|
|
87
101
|
export interface VerboseOutputResult {
|
|
88
102
|
text: string;
|
|
89
103
|
originalLines: number;
|
|
90
104
|
returnedLines: number;
|
|
91
105
|
fullOutputRef?: string;
|
|
106
|
+
compaction?: ContextArtifactCompactionMetadata;
|
|
92
107
|
decisions: TokenWasteDecision[];
|
|
93
108
|
}
|
|
94
109
|
export interface ToolRelevanceInput {
|