@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
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## [4.7.3] - 2026-06-19
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
- feat: add reversible context compaction
|
|
5
|
+
|
|
6
|
+
### Chores
|
|
7
|
+
- chore(deps): update patch and minor dependencies
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## [4.7.2] - 2026-06-16
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
- feat(SPEC-1088): add policy-driven minimal implementation gate
|
|
14
|
+
|
|
15
|
+
### Chores
|
|
16
|
+
- chore(deps): update patch/minor dependencies
|
|
17
|
+
|
|
18
|
+
|
|
1
19
|
## [4.7.1] - 2026-06-12
|
|
2
20
|
|
|
3
21
|
### Chores
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"enabled": true,
|
|
4
|
+
"report": {
|
|
5
|
+
"maxFindings": 12,
|
|
6
|
+
"maxMarkdownLines": 12,
|
|
7
|
+
"maxEvidenceChars": 180
|
|
8
|
+
},
|
|
9
|
+
"tags": {
|
|
10
|
+
"delete": { "description": "Remove avoidable code or files." },
|
|
11
|
+
"stdlib": { "description": "Use a standard library capability before adding custom code." },
|
|
12
|
+
"native": { "description": "Use an existing platform or Planu-native capability." },
|
|
13
|
+
"installed-dependency": { "description": "Use an already-installed dependency instead of adding another." },
|
|
14
|
+
"yagni": { "description": "Avoid speculative code that is not required by the approved spec." },
|
|
15
|
+
"shrink": { "description": "Reduce an oversized abstraction or workflow." },
|
|
16
|
+
"dependency": { "description": "Avoid an unnecessary new package or dependency path." },
|
|
17
|
+
"file-scope": { "description": "Keep implementation inside the approved file scope." },
|
|
18
|
+
"debt-marker": { "description": "Record accepted shortcuts as structured debt evidence." }
|
|
19
|
+
},
|
|
20
|
+
"rules": [
|
|
21
|
+
{
|
|
22
|
+
"id": "minimality.delete.dead-compat",
|
|
23
|
+
"tag": "delete",
|
|
24
|
+
"severity": "warning",
|
|
25
|
+
"confidence": "medium",
|
|
26
|
+
"patterns": ["legacy compatibility", "deprecated fallback", "unused fallback"],
|
|
27
|
+
"replacementGuidance": "Remove the compatibility path unless the approved spec explicitly requires backward compatibility."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "minimality.stdlib.custom-parser",
|
|
31
|
+
"tag": "stdlib",
|
|
32
|
+
"severity": "warning",
|
|
33
|
+
"confidence": "medium",
|
|
34
|
+
"patterns": ["custom parser", "manual parser", "hand rolled parser"],
|
|
35
|
+
"replacementGuidance": "Use an existing parser from the runtime or current dependency set before writing parsing code."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "minimality.native.existing-planu-surface",
|
|
39
|
+
"tag": "native",
|
|
40
|
+
"severity": "warning",
|
|
41
|
+
"confidence": "high",
|
|
42
|
+
"patterns": ["new public mcp tool", "new command", "new cli command"],
|
|
43
|
+
"replacementGuidance": "Prefer existing Planu SDD surfaces unless the spec proves a new public surface is required."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "minimality.installed-dependency.add-package",
|
|
47
|
+
"tag": "installed-dependency",
|
|
48
|
+
"severity": "warning",
|
|
49
|
+
"confidence": "medium",
|
|
50
|
+
"patterns": ["add dependency", "new dependency", "install package"],
|
|
51
|
+
"replacementGuidance": "Check package.json first and reuse an already-installed dependency when possible."
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "minimality.yagni.future-proofing",
|
|
55
|
+
"tag": "yagni",
|
|
56
|
+
"severity": "blocker",
|
|
57
|
+
"confidence": "high",
|
|
58
|
+
"patterns": ["future-proof", "just in case", "might need later", "eventually support"],
|
|
59
|
+
"replacementGuidance": "Remove speculative scope unless it is directly required by an acceptance criterion.",
|
|
60
|
+
"blockWhenRiskAtLeast": "medium"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "minimality.shrink.over-architecture",
|
|
64
|
+
"tag": "shrink",
|
|
65
|
+
"severity": "warning",
|
|
66
|
+
"confidence": "medium",
|
|
67
|
+
"patterns": ["framework", "orchestration layer", "plugin architecture", "abstract factory"],
|
|
68
|
+
"replacementGuidance": "Shrink the design to the smallest correct module/function that satisfies the spec."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "minimality.dependency.new-runtime",
|
|
72
|
+
"tag": "dependency",
|
|
73
|
+
"severity": "blocker",
|
|
74
|
+
"confidence": "high",
|
|
75
|
+
"patterns": ["npm install", "pnpm add", "yarn add"],
|
|
76
|
+
"replacementGuidance": "Avoid adding runtime dependencies unless the approved spec explicitly requires one.",
|
|
77
|
+
"blockWhenRiskAtLeast": "low"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "minimality.file-scope.unapproved-area",
|
|
81
|
+
"tag": "file-scope",
|
|
82
|
+
"severity": "warning",
|
|
83
|
+
"confidence": "medium",
|
|
84
|
+
"patterns": ["touch unrelated", "broad refactor", "repo-wide refactor"],
|
|
85
|
+
"replacementGuidance": "Keep edits inside the approved technical references and tests."
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"id": "minimality.debt-marker.comment-only",
|
|
89
|
+
"tag": "debt-marker",
|
|
90
|
+
"severity": "warning",
|
|
91
|
+
"confidence": "medium",
|
|
92
|
+
"patterns": ["todo debt", "temporary hack", "shortcut accepted"],
|
|
93
|
+
"replacementGuidance": "Record accepted shortcuts as structured debt evidence with ceiling and upgrade trigger."
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"safetyExclusions": [
|
|
97
|
+
"security",
|
|
98
|
+
"accessibility",
|
|
99
|
+
"data-loss",
|
|
100
|
+
"compliance",
|
|
101
|
+
"money",
|
|
102
|
+
"access-control",
|
|
103
|
+
"explicitly requested",
|
|
104
|
+
"trust boundary"
|
|
105
|
+
],
|
|
106
|
+
"excludedPathPatterns": ["dist/**", "coverage/**", "node_modules/**", "pnpm-lock.yaml", "*.snap"],
|
|
107
|
+
"debtEvidence": {
|
|
108
|
+
"requiredFields": ["specId", "files", "ceiling", "upgradeTrigger", "reviewRationale"]
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -17,6 +17,22 @@
|
|
|
17
17
|
"generic": { "maxLines": 60 }
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
+
"contextArtifacts": {
|
|
21
|
+
"enabled": true,
|
|
22
|
+
"ttlMs": 86400000,
|
|
23
|
+
"minTokens": 200
|
|
24
|
+
},
|
|
25
|
+
"contentCompaction": {
|
|
26
|
+
"strategies": {
|
|
27
|
+
"json": { "maxLines": 60, "maxSnippetChars": 240 },
|
|
28
|
+
"test-log": { "maxLines": 80, "keepFailures": true, "maxSnippetChars": 240 },
|
|
29
|
+
"runtime-log": { "maxLines": 60, "keepFailures": true, "uniqueOnly": true, "maxSnippetChars": 240 },
|
|
30
|
+
"search-results": { "maxLines": 80, "uniqueOnly": true, "maxSnippetChars": 180 },
|
|
31
|
+
"code": { "maxLines": 120, "maxSnippetChars": 240 },
|
|
32
|
+
"spec-or-handoff": { "maxLines": 120, "maxSnippetChars": 240 },
|
|
33
|
+
"generic-text": { "maxLines": 60, "maxSnippetChars": 240 }
|
|
34
|
+
}
|
|
35
|
+
},
|
|
20
36
|
"tools": {
|
|
21
37
|
"groups": {
|
|
22
38
|
"spec": ["create_spec", "check_readiness", "challenge_spec", "update_status"],
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ToolResult } from '../../types/index.js';
|
|
2
|
-
import type { CompactDecision } from '../../types/compact/compact-mode.js';
|
|
2
|
+
import type { CompactDecision, CompactModeOptions } from '../../types/compact/compact-mode.js';
|
|
3
3
|
/**
|
|
4
4
|
* Apply compact mode formatting to a ToolResult.
|
|
5
5
|
* - Truncates text content blocks to tokenBudget
|
|
6
6
|
* - Preserves structuredContent (essential data: status, scores, blockers)
|
|
7
7
|
* - Adds _meta.compactMode and _meta.contextUsed
|
|
8
8
|
*/
|
|
9
|
-
export declare function applyCompactMode(result: ToolResult, decision: CompactDecision): ToolResult;
|
|
9
|
+
export declare function applyCompactMode(result: ToolResult, decision: CompactDecision, options?: CompactModeOptions): ToolResult;
|
|
10
10
|
//# sourceMappingURL=compact-middleware.d.ts.map
|
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
// engine/compact/compact-middleware.ts — SPEC-922: Apply compact mode to tool responses
|
|
2
|
+
import { compactContentAware } from '../token-optimizer/content-aware-compactor.js';
|
|
2
3
|
/** Approximate chars per token for English text. */
|
|
3
4
|
const CHARS_PER_TOKEN = 4;
|
|
5
|
+
function defaultCompactPolicy(tokenBudget) {
|
|
6
|
+
return {
|
|
7
|
+
contextArtifacts: {
|
|
8
|
+
enabled: true,
|
|
9
|
+
ttlMs: 24 * 60 * 60 * 1000,
|
|
10
|
+
minTokens: tokenBudget,
|
|
11
|
+
},
|
|
12
|
+
contentCompaction: {
|
|
13
|
+
strategies: {
|
|
14
|
+
json: { maxLines: tokenBudget },
|
|
15
|
+
'test-log': { maxLines: tokenBudget },
|
|
16
|
+
'runtime-log': { maxLines: tokenBudget },
|
|
17
|
+
'search-results': { maxLines: tokenBudget },
|
|
18
|
+
code: { maxLines: tokenBudget },
|
|
19
|
+
'spec-or-handoff': { maxLines: tokenBudget },
|
|
20
|
+
'generic-text': { maxLines: tokenBudget },
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
redaction: {
|
|
24
|
+
maxSnippetChars: 240,
|
|
25
|
+
redactPatterns: ['secret', 'token', 'password', 'api_key'],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function withCompactionMeta(result, compaction) {
|
|
30
|
+
if (compaction === undefined) {
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
...result,
|
|
35
|
+
structuredContent: {
|
|
36
|
+
...(result.structuredContent ?? {}),
|
|
37
|
+
compaction,
|
|
38
|
+
},
|
|
39
|
+
_meta: {
|
|
40
|
+
...(typeof result._meta === 'object' && result._meta !== null ? result._meta : {}),
|
|
41
|
+
compaction,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
4
45
|
/** Truncate a single text block to a token budget. */
|
|
5
46
|
function truncateBlock(text, tokenBudget) {
|
|
6
47
|
const maxChars = tokenBudget * CHARS_PER_TOKEN;
|
|
@@ -17,7 +58,7 @@ function truncateBlock(text, tokenBudget) {
|
|
|
17
58
|
* - Preserves structuredContent (essential data: status, scores, blockers)
|
|
18
59
|
* - Adds _meta.compactMode and _meta.contextUsed
|
|
19
60
|
*/
|
|
20
|
-
export function applyCompactMode(result, decision) {
|
|
61
|
+
export function applyCompactMode(result, decision, options = {}) {
|
|
21
62
|
if (decision.mode !== 'compact') {
|
|
22
63
|
// Still add _meta even in verbose mode if contextUsed was provided
|
|
23
64
|
if (decision.contextUsed !== undefined) {
|
|
@@ -32,11 +73,31 @@ export function applyCompactMode(result, decision) {
|
|
|
32
73
|
}
|
|
33
74
|
return result;
|
|
34
75
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
76
|
+
let firstCompaction;
|
|
77
|
+
const truncatedContent = result.content.map((block) => {
|
|
78
|
+
if (options.projectPath !== undefined) {
|
|
79
|
+
const compacted = compactContentAware({
|
|
80
|
+
text: block.text,
|
|
81
|
+
policy: defaultCompactPolicy(decision.tokenBudget),
|
|
82
|
+
projectPath: options.projectPath,
|
|
83
|
+
sourcePath: options.sourcePath,
|
|
84
|
+
flow: options.flow,
|
|
85
|
+
kind: options.flow,
|
|
86
|
+
});
|
|
87
|
+
firstCompaction ??= compacted.artifact;
|
|
88
|
+
return {
|
|
89
|
+
...block,
|
|
90
|
+
text: compacted.text.length < block.text.length
|
|
91
|
+
? `${compacted.text}\n\nArtifact: ${compacted.artifact?.artifactRef ?? 'not stored'}`
|
|
92
|
+
: truncateBlock(block.text, decision.tokenBudget),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
...block,
|
|
97
|
+
text: truncateBlock(block.text, decision.tokenBudget),
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
return withCompactionMeta({
|
|
40
101
|
...result,
|
|
41
102
|
content: truncatedContent,
|
|
42
103
|
_meta: {
|
|
@@ -45,6 +106,6 @@ export function applyCompactMode(result, decision) {
|
|
|
45
106
|
contextUsed: decision.contextUsed,
|
|
46
107
|
tokenBudget: decision.tokenBudget,
|
|
47
108
|
},
|
|
48
|
-
};
|
|
109
|
+
}, firstCompaction);
|
|
49
110
|
}
|
|
50
111
|
//# sourceMappingURL=compact-middleware.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ContextArtifactStats, RetrieveContextArtifactResult, StoreContextArtifactInput, StoreContextArtifactResult } from '../../types/context-artifacts.js';
|
|
2
|
+
export declare function storeContextArtifact(input: StoreContextArtifactInput): StoreContextArtifactResult;
|
|
3
|
+
export declare function retrieveContextArtifact(projectPath: string, ref: string): RetrieveContextArtifactResult;
|
|
4
|
+
export declare function getContextArtifactStats(projectPath: string): ContextArtifactStats;
|
|
5
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { getSensitivePathRefusal, shouldBypassLeanMode, } from '../context-intelligence/compression-guards.js';
|
|
5
|
+
import { hashProjectPath, projectDataDir } from '../../storage/base-store.js';
|
|
6
|
+
const REF_PREFIX = 'ctx_';
|
|
7
|
+
const REF_PATTERN = /^ctx_[a-f0-9]{32}$/;
|
|
8
|
+
const FORBIDDEN_METADATA_KEY = /(?:secret|token|password|credential|api[_-]?key|private[_-]?key)/i;
|
|
9
|
+
function artifactDir(projectPath) {
|
|
10
|
+
return join(projectDataDir(hashProjectPath(projectPath)), 'context-artifacts');
|
|
11
|
+
}
|
|
12
|
+
function artifactPath(projectPath, ref) {
|
|
13
|
+
if (!REF_PATTERN.test(ref)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return join(artifactDir(projectPath), `${ref}.json`);
|
|
17
|
+
}
|
|
18
|
+
function sha256(value) {
|
|
19
|
+
return createHash('sha256').update(value, 'utf8').digest('hex');
|
|
20
|
+
}
|
|
21
|
+
function safeMetadata(metadata) {
|
|
22
|
+
const safe = {};
|
|
23
|
+
for (const [key, value] of Object.entries(metadata ?? {})) {
|
|
24
|
+
if (FORBIDDEN_METADATA_KEY.test(key)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
28
|
+
safe[key] =
|
|
29
|
+
typeof value === 'string' && FORBIDDEN_METADATA_KEY.test(value) ? '[redacted]' : value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return safe;
|
|
33
|
+
}
|
|
34
|
+
function compactionMetadata(artifact) {
|
|
35
|
+
return {
|
|
36
|
+
artifactRef: artifact.ref,
|
|
37
|
+
originalTokens: artifact.originalTokens,
|
|
38
|
+
compactTokens: artifact.compactTokens,
|
|
39
|
+
tokensSaved: artifact.tokensSaved,
|
|
40
|
+
strategy: artifact.strategy,
|
|
41
|
+
contentType: artifact.contentType,
|
|
42
|
+
expiresAt: artifact.expiresAt,
|
|
43
|
+
retrievalHint: `Use artifactRef ${artifact.ref} to retrieve the full local output before ${artifact.expiresAt}.`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function readArtifactFile(path) {
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function writeArtifactFile(path, artifact) {
|
|
55
|
+
writeFileSync(path, JSON.stringify(artifact, null, 2), 'utf-8');
|
|
56
|
+
}
|
|
57
|
+
export function storeContextArtifact(input) {
|
|
58
|
+
if (input.sourcePath) {
|
|
59
|
+
const refusal = getSensitivePathRefusal(input.sourcePath);
|
|
60
|
+
if (refusal !== null) {
|
|
61
|
+
return { stored: false, refusedReason: refusal };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (input.flow && shouldBypassLeanMode(input.flow)) {
|
|
65
|
+
return {
|
|
66
|
+
stored: false,
|
|
67
|
+
refusedReason: `Refusing recoverable artifact for sensitive flow ${input.flow}`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const contentHash = sha256(input.originalContent);
|
|
71
|
+
const ref = `${REF_PREFIX}${contentHash.slice(0, 32)}`;
|
|
72
|
+
const path = artifactPath(input.projectPath, ref);
|
|
73
|
+
if (path === null) {
|
|
74
|
+
return { stored: false, refusedReason: 'Invalid generated artifact ref' };
|
|
75
|
+
}
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
const createdAt = new Date(now).toISOString();
|
|
78
|
+
const expiresAt = new Date(now + input.ttlMs).toISOString();
|
|
79
|
+
const tokensSaved = Math.max(0, input.originalTokens - input.compactTokens);
|
|
80
|
+
const previous = existsSync(path) ? readArtifactFile(path) : null;
|
|
81
|
+
const artifact = {
|
|
82
|
+
ref,
|
|
83
|
+
contentHash,
|
|
84
|
+
createdAt: previous?.createdAt ?? createdAt,
|
|
85
|
+
expiresAt,
|
|
86
|
+
ttlMs: input.ttlMs,
|
|
87
|
+
projectId: hashProjectPath(input.projectPath),
|
|
88
|
+
contentType: input.contentType,
|
|
89
|
+
strategy: input.strategy,
|
|
90
|
+
originalTokens: input.originalTokens,
|
|
91
|
+
compactTokens: input.compactTokens,
|
|
92
|
+
tokensSaved,
|
|
93
|
+
retrievalCount: previous?.retrievalCount ?? 0,
|
|
94
|
+
metadata: safeMetadata(input.metadata),
|
|
95
|
+
originalContent: input.originalContent,
|
|
96
|
+
compactContent: input.compactContent,
|
|
97
|
+
};
|
|
98
|
+
mkdirSync(artifactDir(input.projectPath), { recursive: true });
|
|
99
|
+
writeArtifactFile(path, artifact);
|
|
100
|
+
return { stored: true, artifact, metadata: compactionMetadata(artifact) };
|
|
101
|
+
}
|
|
102
|
+
export function retrieveContextArtifact(projectPath, ref) {
|
|
103
|
+
const path = artifactPath(projectPath, ref);
|
|
104
|
+
if (path === null) {
|
|
105
|
+
return {
|
|
106
|
+
found: false,
|
|
107
|
+
reason: 'invalid-ref',
|
|
108
|
+
hint: 'Artifact refs are opaque ctx_<hash> identifiers.',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (!existsSync(path)) {
|
|
112
|
+
return {
|
|
113
|
+
found: false,
|
|
114
|
+
reason: 'missing',
|
|
115
|
+
hint: 'The artifact is missing or belongs to a different project.',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const artifact = readArtifactFile(path);
|
|
119
|
+
if (artifact === null) {
|
|
120
|
+
return {
|
|
121
|
+
found: false,
|
|
122
|
+
reason: 'corrupt',
|
|
123
|
+
hint: 'The artifact payload is corrupt and cannot be recovered.',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (artifact.projectId !== hashProjectPath(projectPath)) {
|
|
127
|
+
return {
|
|
128
|
+
found: false,
|
|
129
|
+
reason: 'unauthorized',
|
|
130
|
+
hint: 'The artifact does not belong to this project scope.',
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (Date.parse(artifact.expiresAt) <= Date.now()) {
|
|
134
|
+
return {
|
|
135
|
+
found: false,
|
|
136
|
+
reason: 'expired',
|
|
137
|
+
hint: 'The artifact expired; rerun the source operation if exact output is needed.',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const updated = { ...artifact, retrievalCount: artifact.retrievalCount + 1 };
|
|
141
|
+
writeArtifactFile(path, updated);
|
|
142
|
+
return {
|
|
143
|
+
found: true,
|
|
144
|
+
artifact: updated,
|
|
145
|
+
hint: 'Full artifact recovered from local Planu storage.',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export function getContextArtifactStats(projectPath) {
|
|
149
|
+
const dir = artifactDir(projectPath);
|
|
150
|
+
const stats = {
|
|
151
|
+
artifactCount: 0,
|
|
152
|
+
totalOriginalTokens: 0,
|
|
153
|
+
totalCompactTokens: 0,
|
|
154
|
+
totalTokensSaved: 0,
|
|
155
|
+
retrievalCount: 0,
|
|
156
|
+
};
|
|
157
|
+
if (!existsSync(dir)) {
|
|
158
|
+
return stats;
|
|
159
|
+
}
|
|
160
|
+
for (const entry of readdirSync(dir)) {
|
|
161
|
+
if (!entry.endsWith('.json')) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const artifact = readArtifactFile(join(dir, entry));
|
|
165
|
+
if (artifact === null || Date.parse(artifact.expiresAt) <= Date.now()) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
stats.artifactCount += 1;
|
|
169
|
+
stats.totalOriginalTokens += artifact.originalTokens;
|
|
170
|
+
stats.totalCompactTokens += artifact.compactTokens;
|
|
171
|
+
stats.totalTokensSaved += artifact.tokensSaved;
|
|
172
|
+
stats.retrievalCount += artifact.retrievalCount;
|
|
173
|
+
}
|
|
174
|
+
return stats;
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -96,6 +96,62 @@ export declare const ValidationReportV1Schema: z.ZodObject<{
|
|
|
96
96
|
evidence: z.ZodArray<z.ZodString>;
|
|
97
97
|
}, z.core.$strip>>;
|
|
98
98
|
}, z.core.$strip>>;
|
|
99
|
+
minimalityReport: z.ZodOptional<z.ZodObject<{
|
|
100
|
+
enabled: z.ZodBoolean;
|
|
101
|
+
blocked: z.ZodBoolean;
|
|
102
|
+
findings: z.ZodArray<z.ZodObject<{
|
|
103
|
+
ruleId: z.ZodString;
|
|
104
|
+
tag: z.ZodString;
|
|
105
|
+
severity: z.ZodEnum<{
|
|
106
|
+
blocker: "blocker";
|
|
107
|
+
warning: "warning";
|
|
108
|
+
info: "info";
|
|
109
|
+
}>;
|
|
110
|
+
confidence: z.ZodEnum<{
|
|
111
|
+
low: "low";
|
|
112
|
+
medium: "medium";
|
|
113
|
+
high: "high";
|
|
114
|
+
}>;
|
|
115
|
+
target: z.ZodString;
|
|
116
|
+
line: z.ZodOptional<z.ZodNumber>;
|
|
117
|
+
evidence: z.ZodString;
|
|
118
|
+
replacementGuidance: z.ZodString;
|
|
119
|
+
blocksDone: z.ZodBoolean;
|
|
120
|
+
evidenceSource: z.ZodArray<z.ZodObject<{
|
|
121
|
+
source: z.ZodEnum<{
|
|
122
|
+
spec: "spec";
|
|
123
|
+
runtime: "runtime";
|
|
124
|
+
policy: "policy";
|
|
125
|
+
"project-config": "project-config";
|
|
126
|
+
}>;
|
|
127
|
+
key: z.ZodString;
|
|
128
|
+
value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
129
|
+
}, z.core.$strip>>;
|
|
130
|
+
}, z.core.$strip>>;
|
|
131
|
+
safetyExceptions: z.ZodArray<z.ZodObject<{
|
|
132
|
+
target: z.ZodString;
|
|
133
|
+
reason: z.ZodString;
|
|
134
|
+
evidence: z.ZodString;
|
|
135
|
+
}, z.core.$strip>>;
|
|
136
|
+
debtEvidence: z.ZodArray<z.ZodObject<{
|
|
137
|
+
specId: z.ZodString;
|
|
138
|
+
files: z.ZodArray<z.ZodString>;
|
|
139
|
+
ceiling: z.ZodString;
|
|
140
|
+
upgradeTrigger: z.ZodString;
|
|
141
|
+
reviewRationale: z.ZodString;
|
|
142
|
+
}, z.core.$strip>>;
|
|
143
|
+
evidence: z.ZodArray<z.ZodObject<{
|
|
144
|
+
source: z.ZodEnum<{
|
|
145
|
+
spec: "spec";
|
|
146
|
+
runtime: "runtime";
|
|
147
|
+
policy: "policy";
|
|
148
|
+
"project-config": "project-config";
|
|
149
|
+
}>;
|
|
150
|
+
key: z.ZodString;
|
|
151
|
+
value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
152
|
+
}, z.core.$strip>>;
|
|
153
|
+
markdown: z.ZodString;
|
|
154
|
+
}, z.core.$strip>>;
|
|
99
155
|
score: z.ZodOptional<z.ZodNumber>;
|
|
100
156
|
completedAt: z.ZodISODateTime;
|
|
101
157
|
}, z.core.$strip>;
|
|
@@ -194,6 +250,62 @@ export declare const ARTIFACT_SCHEMAS: {
|
|
|
194
250
|
evidence: z.ZodArray<z.ZodString>;
|
|
195
251
|
}, z.core.$strip>>;
|
|
196
252
|
}, z.core.$strip>>;
|
|
253
|
+
minimalityReport: z.ZodOptional<z.ZodObject<{
|
|
254
|
+
enabled: z.ZodBoolean;
|
|
255
|
+
blocked: z.ZodBoolean;
|
|
256
|
+
findings: z.ZodArray<z.ZodObject<{
|
|
257
|
+
ruleId: z.ZodString;
|
|
258
|
+
tag: z.ZodString;
|
|
259
|
+
severity: z.ZodEnum<{
|
|
260
|
+
blocker: "blocker";
|
|
261
|
+
warning: "warning";
|
|
262
|
+
info: "info";
|
|
263
|
+
}>;
|
|
264
|
+
confidence: z.ZodEnum<{
|
|
265
|
+
low: "low";
|
|
266
|
+
medium: "medium";
|
|
267
|
+
high: "high";
|
|
268
|
+
}>;
|
|
269
|
+
target: z.ZodString;
|
|
270
|
+
line: z.ZodOptional<z.ZodNumber>;
|
|
271
|
+
evidence: z.ZodString;
|
|
272
|
+
replacementGuidance: z.ZodString;
|
|
273
|
+
blocksDone: z.ZodBoolean;
|
|
274
|
+
evidenceSource: z.ZodArray<z.ZodObject<{
|
|
275
|
+
source: z.ZodEnum<{
|
|
276
|
+
spec: "spec";
|
|
277
|
+
runtime: "runtime";
|
|
278
|
+
policy: "policy";
|
|
279
|
+
"project-config": "project-config";
|
|
280
|
+
}>;
|
|
281
|
+
key: z.ZodString;
|
|
282
|
+
value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
283
|
+
}, z.core.$strip>>;
|
|
284
|
+
}, z.core.$strip>>;
|
|
285
|
+
safetyExceptions: z.ZodArray<z.ZodObject<{
|
|
286
|
+
target: z.ZodString;
|
|
287
|
+
reason: z.ZodString;
|
|
288
|
+
evidence: z.ZodString;
|
|
289
|
+
}, z.core.$strip>>;
|
|
290
|
+
debtEvidence: z.ZodArray<z.ZodObject<{
|
|
291
|
+
specId: z.ZodString;
|
|
292
|
+
files: z.ZodArray<z.ZodString>;
|
|
293
|
+
ceiling: z.ZodString;
|
|
294
|
+
upgradeTrigger: z.ZodString;
|
|
295
|
+
reviewRationale: z.ZodString;
|
|
296
|
+
}, z.core.$strip>>;
|
|
297
|
+
evidence: z.ZodArray<z.ZodObject<{
|
|
298
|
+
source: z.ZodEnum<{
|
|
299
|
+
spec: "spec";
|
|
300
|
+
runtime: "runtime";
|
|
301
|
+
policy: "policy";
|
|
302
|
+
"project-config": "project-config";
|
|
303
|
+
}>;
|
|
304
|
+
key: z.ZodString;
|
|
305
|
+
value: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
306
|
+
}, z.core.$strip>>;
|
|
307
|
+
markdown: z.ZodString;
|
|
308
|
+
}, z.core.$strip>>;
|
|
197
309
|
score: z.ZodOptional<z.ZodNumber>;
|
|
198
310
|
completedAt: z.ZodISODateTime;
|
|
199
311
|
}, z.core.$strip>;
|
|
@@ -73,6 +73,46 @@ export const ValidationReportV1Schema = z.object({
|
|
|
73
73
|
})),
|
|
74
74
|
})
|
|
75
75
|
.optional(),
|
|
76
|
+
minimalityReport: z
|
|
77
|
+
.object({
|
|
78
|
+
enabled: z.boolean(),
|
|
79
|
+
blocked: z.boolean(),
|
|
80
|
+
findings: z.array(z.object({
|
|
81
|
+
ruleId: z.string(),
|
|
82
|
+
tag: z.string(),
|
|
83
|
+
severity: z.enum(['info', 'warning', 'blocker']),
|
|
84
|
+
confidence: z.enum(['low', 'medium', 'high']),
|
|
85
|
+
target: z.string(),
|
|
86
|
+
line: z.number().optional(),
|
|
87
|
+
evidence: z.string(),
|
|
88
|
+
replacementGuidance: z.string(),
|
|
89
|
+
blocksDone: z.boolean(),
|
|
90
|
+
evidenceSource: z.array(z.object({
|
|
91
|
+
source: z.enum(['policy', 'project-config', 'runtime', 'spec']),
|
|
92
|
+
key: z.string(),
|
|
93
|
+
value: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
94
|
+
})),
|
|
95
|
+
})),
|
|
96
|
+
safetyExceptions: z.array(z.object({
|
|
97
|
+
target: z.string(),
|
|
98
|
+
reason: z.string(),
|
|
99
|
+
evidence: z.string(),
|
|
100
|
+
})),
|
|
101
|
+
debtEvidence: z.array(z.object({
|
|
102
|
+
specId: z.string(),
|
|
103
|
+
files: z.array(z.string()),
|
|
104
|
+
ceiling: z.string(),
|
|
105
|
+
upgradeTrigger: z.string(),
|
|
106
|
+
reviewRationale: z.string(),
|
|
107
|
+
})),
|
|
108
|
+
evidence: z.array(z.object({
|
|
109
|
+
source: z.enum(['policy', 'project-config', 'runtime', 'spec']),
|
|
110
|
+
key: z.string(),
|
|
111
|
+
value: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
112
|
+
})),
|
|
113
|
+
markdown: z.string(),
|
|
114
|
+
})
|
|
115
|
+
.optional(),
|
|
76
116
|
score: z.number().optional(),
|
|
77
117
|
completedAt: z.iso.datetime(),
|
|
78
118
|
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MinimalImplementationInput, MinimalImplementationReport } from '../../types/minimal-implementation-gate.js';
|
|
2
|
+
export declare function analyzeMinimalImplementation(input: MinimalImplementationInput): MinimalImplementationReport;
|
|
3
|
+
//# sourceMappingURL=analyzer.d.ts.map
|