pan-wizard 3.5.2 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -9
- package/agents/pan-executor.md +18 -0
- package/agents/pan-experiment-runner.md +126 -0
- package/agents/pan-phase-researcher.md +16 -0
- package/agents/pan-plan-checker.md +80 -0
- package/agents/pan-planner.md +19 -0
- package/agents/pan-reviewer.md +2 -0
- package/agents/pan-verifier.md +41 -0
- package/bin/install-lib.cjs +55 -0
- package/bin/install.js +71 -22
- package/commands/pan/debug.md +1 -1
- package/commands/pan/experiment.md +219 -0
- package/commands/pan/health.md +1 -1
- package/commands/pan/learn.md +15 -1
- package/commands/pan/links.md +102 -0
- package/commands/pan/optimize.md +13 -0
- package/commands/pan/patches.md +10 -1
- package/commands/pan/phase-tests.md +1 -4
- package/commands/pan/todo-add.md +1 -1
- package/commands/pan/todo-check.md +1 -1
- package/hooks/dist/pan-cost-logger.js +54 -4
- package/hooks/dist/pan-trace-logger.js +72 -3
- package/package.json +67 -66
- package/pan-wizard-core/bin/lib/codebase.cjs +2 -0
- package/pan-wizard-core/bin/lib/commands.cjs +8 -0
- package/pan-wizard-core/bin/lib/config.cjs +13 -2
- package/pan-wizard-core/bin/lib/context-budget.cjs +73 -0
- package/pan-wizard-core/bin/lib/core.cjs +13 -0
- package/pan-wizard-core/bin/lib/doc-lint/frontmatter.js +270 -0
- package/pan-wizard-core/bin/lib/doc-lint/reporter.js +45 -0
- package/pan-wizard-core/bin/lib/doc-lint/schema.js +202 -0
- package/pan-wizard-core/bin/lib/doc-lint/validate.js +190 -0
- package/pan-wizard-core/bin/lib/doc-lint/walk.js +135 -0
- package/pan-wizard-core/bin/lib/doc-lint.cjs +287 -0
- package/pan-wizard-core/bin/lib/experiment.cjs +502 -0
- package/pan-wizard-core/bin/lib/learn-index.cjs +235 -0
- package/pan-wizard-core/bin/lib/learn-lint.cjs +292 -0
- package/pan-wizard-core/bin/lib/links.cjs +549 -0
- package/pan-wizard-core/bin/lib/optimize.cjs +474 -1
- package/pan-wizard-core/bin/lib/runner.cjs +473 -0
- package/pan-wizard-core/bin/lib/verify.cjs +23 -0
- package/pan-wizard-core/bin/pan-tools.cjs +247 -3
- package/pan-wizard-core/learnings/README.md +70 -0
- package/pan-wizard-core/learnings/index.json +540 -0
- package/pan-wizard-core/learnings/internal/.gitkeep +2 -0
- package/pan-wizard-core/learnings/internal/experiment-runner.md +81 -0
- package/pan-wizard-core/learnings/internal/external-research.md +93 -0
- package/pan-wizard-core/learnings/internal/loop-design.md +33 -0
- package/pan-wizard-core/learnings/internal/pan-dev-bugs.md +181 -0
- package/pan-wizard-core/learnings/universal/.gitkeep +2 -0
- package/pan-wizard-core/learnings/universal/atomic-state.md +21 -0
- package/pan-wizard-core/learnings/universal/binary-io.md +21 -0
- package/pan-wizard-core/learnings/universal/comment-syntax.md +21 -0
- package/pan-wizard-core/learnings/universal/composition.md +33 -0
- package/pan-wizard-core/learnings/universal/concurrency.md +33 -0
- package/pan-wizard-core/learnings/universal/dag-scheduler.md +33 -0
- package/pan-wizard-core/learnings/universal/data-driven-design.md +21 -0
- package/pan-wizard-core/learnings/universal/design-process.md +21 -0
- package/pan-wizard-core/learnings/universal/empirical-spike.md +21 -0
- package/pan-wizard-core/learnings/universal/error-handling.md +23 -0
- package/pan-wizard-core/learnings/universal/error-paths.md +21 -0
- package/pan-wizard-core/learnings/universal/glob-semantics.md +21 -0
- package/pan-wizard-core/learnings/universal/idempotency.md +21 -0
- package/pan-wizard-core/learnings/universal/invariants.md +21 -0
- package/pan-wizard-core/learnings/universal/io-patterns.md +21 -0
- package/pan-wizard-core/learnings/universal/numeric-edge-cases.md +21 -0
- package/pan-wizard-core/learnings/universal/output-conventions.md +21 -0
- package/pan-wizard-core/learnings/universal/parser-design.md +21 -0
- package/pan-wizard-core/learnings/universal/phase-locking.md +21 -0
- package/pan-wizard-core/learnings/universal/pipe-friendly-cli.md +21 -0
- package/pan-wizard-core/learnings/universal/schema-design.md +21 -0
- package/pan-wizard-core/learnings/universal/secret-handling.md +21 -0
- package/pan-wizard-core/learnings/universal/streaming-io.md +21 -0
- package/pan-wizard-core/learnings/universal/test-patterns.md +57 -0
- package/pan-wizard-core/learnings/universal/test-strategy.md +33 -0
- package/pan-wizard-core/learnings/universal/unicode.md +21 -0
- package/pan-wizard-core/learnings/universal/vendor-pattern.md +21 -0
- package/pan-wizard-core/references/guardrails.md +58 -0
- package/pan-wizard-core/references/handoff-decisions.md +156 -0
- package/pan-wizard-core/references/schemas/pan-command.schema.yml +39 -0
- package/pan-wizard-core/references/verification-patterns.md +31 -0
- package/pan-wizard-core/templates/config.json +2 -1
- package/pan-wizard-core/templates/idea.md +52 -0
- package/pan-wizard-core/templates/summary-complex.md +14 -5
- package/pan-wizard-core/templates/summary-minimal.md +6 -0
- package/pan-wizard-core/templates/summary-standard.md +14 -3
- package/pan-wizard-core/workflows/discuss-phase.md +108 -1
- package/pan-wizard-core/workflows/exec-phase.md +37 -1
- package/pan-wizard-core/workflows/execute-plan.md +14 -0
- package/pan-wizard-core/workflows/health.md +23 -0
- package/pan-wizard-core/workflows/new-project.md +65 -81
- package/pan-wizard-core/workflows/plan-phase.md +58 -0
- package/pan-wizard-core/workflows/transition.md +102 -7
- package/pan-wizard-core/workflows/verify-phase.md +14 -0
- package/scripts/build-hooks.js +7 -1
- package/scripts/generate-skills-docs.py +10 -8
- package/scripts/git-hooks/pre-commit +40 -0
- package/scripts/release-check.js +184 -0
|
@@ -80,6 +80,65 @@ function extractNumber(obj, key) {
|
|
|
80
80
|
return typeof v === 'number' ? v : 0;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* P-1805 (v3.7.8): extract usage totals by reading the SubagentStop transcript.
|
|
85
|
+
* The hook payload from Claude Code in headless mode does NOT include
|
|
86
|
+
* `data.usage` — only `transcript_path`. This function reads the transcript
|
|
87
|
+
* JSONL file and sums the `usage` fields across all assistant messages
|
|
88
|
+
* belonging to the just-completed subagent (its session_id is in `data.session_id`).
|
|
89
|
+
*
|
|
90
|
+
* Returns the SUM of input/output/cache tokens for the subagent's whole
|
|
91
|
+
* conversation. Agents that finish in one Claude API call will have one
|
|
92
|
+
* usage record; agents with multi-turn tool use will have several.
|
|
93
|
+
*
|
|
94
|
+
* Returns `{input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens}`
|
|
95
|
+
* with all zeros if the transcript is unreadable / missing — same fallback
|
|
96
|
+
* shape as the original behavior, so callers don't break.
|
|
97
|
+
*
|
|
98
|
+
* @param {string} transcriptPath - Absolute path to the transcript JSONL file
|
|
99
|
+
* @param {string} [sessionId] - Optional subagent session_id to filter on
|
|
100
|
+
* @returns {Object} usage totals object
|
|
101
|
+
*/
|
|
102
|
+
function readUsageFromTranscript(transcriptPath, sessionId) {
|
|
103
|
+
const totals = {
|
|
104
|
+
input_tokens: 0,
|
|
105
|
+
output_tokens: 0,
|
|
106
|
+
cache_read_input_tokens: 0,
|
|
107
|
+
cache_creation_input_tokens: 0,
|
|
108
|
+
};
|
|
109
|
+
if (!transcriptPath || typeof transcriptPath !== 'string') return totals;
|
|
110
|
+
let raw;
|
|
111
|
+
try {
|
|
112
|
+
raw = fs.readFileSync(transcriptPath, 'utf-8');
|
|
113
|
+
} catch {
|
|
114
|
+
return totals;
|
|
115
|
+
}
|
|
116
|
+
for (const line of raw.split('\n')) {
|
|
117
|
+
if (!line) continue;
|
|
118
|
+
let entry;
|
|
119
|
+
try {
|
|
120
|
+
entry = JSON.parse(line);
|
|
121
|
+
} catch {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
// Filter to entries from this subagent if a session_id is provided.
|
|
125
|
+
// The transcript may include parent + child traffic; session_id discriminates.
|
|
126
|
+
if (sessionId && entry.session_id && entry.session_id !== sessionId) continue;
|
|
127
|
+
// Usage typically lives on assistant message records.
|
|
128
|
+
const usage = entry.usage
|
|
129
|
+
|| entry.message?.usage
|
|
130
|
+
|| entry.response?.usage
|
|
131
|
+
|| (entry.type === 'assistant' && entry.message?.usage)
|
|
132
|
+
|| null;
|
|
133
|
+
if (!usage || typeof usage !== 'object') continue;
|
|
134
|
+
totals.input_tokens += extractNumber(usage, 'input_tokens');
|
|
135
|
+
totals.output_tokens += extractNumber(usage, 'output_tokens');
|
|
136
|
+
totals.cache_read_input_tokens += extractNumber(usage, 'cache_read_input_tokens');
|
|
137
|
+
totals.cache_creation_input_tokens += extractNumber(usage, 'cache_creation_input_tokens');
|
|
138
|
+
}
|
|
139
|
+
return totals;
|
|
140
|
+
}
|
|
141
|
+
|
|
83
142
|
/**
|
|
84
143
|
* Build trace event(s) from a SubagentStop payload.
|
|
85
144
|
* Pure function — no side effects.
|
|
@@ -93,9 +152,19 @@ function buildTraceEvents(data, sessionId) {
|
|
|
93
152
|
|
|
94
153
|
const ts = new Date().toISOString();
|
|
95
154
|
const agent = data.agent_type || data.subagent_type || 'unknown';
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
155
|
+
|
|
156
|
+
// P-1805: prefer usage from data.usage when present (interactive Claude Code path).
|
|
157
|
+
// Fall back to reading the transcript file (headless `claude -p` path — usage
|
|
158
|
+
// not in payload but discoverable via transcript_path).
|
|
159
|
+
let inputTokens = extractNumber(data.usage, 'input_tokens');
|
|
160
|
+
let outputTokens = extractNumber(data.usage, 'output_tokens');
|
|
161
|
+
let cacheRead = extractNumber(data.usage, 'cache_read_input_tokens');
|
|
162
|
+
if ((inputTokens + outputTokens + cacheRead) === 0 && data.transcript_path) {
|
|
163
|
+
const fromTranscript = readUsageFromTranscript(data.transcript_path, data.session_id);
|
|
164
|
+
inputTokens = fromTranscript.input_tokens;
|
|
165
|
+
outputTokens = fromTranscript.output_tokens;
|
|
166
|
+
cacheRead = fromTranscript.cache_read_input_tokens;
|
|
167
|
+
}
|
|
99
168
|
const totalTokens = inputTokens + outputTokens;
|
|
100
169
|
|
|
101
170
|
const events = [];
|
package/package.json
CHANGED
|
@@ -1,66 +1,67 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pan-wizard",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "A lightweight workflow automation and context engineering system for Claude Code, OpenCode, Gemini CLI, Codex, and Copilot CLI.",
|
|
5
|
-
"bin": {
|
|
6
|
-
"pan-wizard": "bin/install.js"
|
|
7
|
-
},
|
|
8
|
-
"files": [
|
|
9
|
-
"bin",
|
|
10
|
-
"commands",
|
|
11
|
-
"pan-wizard-core",
|
|
12
|
-
"agents",
|
|
13
|
-
"hooks/dist",
|
|
14
|
-
"scripts",
|
|
15
|
-
"assets"
|
|
16
|
-
],
|
|
17
|
-
"keywords": [
|
|
18
|
-
"claude",
|
|
19
|
-
"claude-code",
|
|
20
|
-
"ai",
|
|
21
|
-
"workflow-automation",
|
|
22
|
-
"context-engineering",
|
|
23
|
-
"project-automation",
|
|
24
|
-
"gemini",
|
|
25
|
-
"gemini-cli",
|
|
26
|
-
"codex",
|
|
27
|
-
"codex-cli",
|
|
28
|
-
"copilot",
|
|
29
|
-
"copilot-cli",
|
|
30
|
-
"github-copilot"
|
|
31
|
-
],
|
|
32
|
-
"author": "PAN Wizard Contributors",
|
|
33
|
-
"contributors": [
|
|
34
|
-
{
|
|
35
|
-
"name": "oharms",
|
|
36
|
-
"url": "https://github.com/oharms"
|
|
37
|
-
}
|
|
38
|
-
],
|
|
39
|
-
"license": "MIT",
|
|
40
|
-
"repository": {
|
|
41
|
-
"type": "git",
|
|
42
|
-
"url": "git+https://github.com/oharms/PanWizard.git"
|
|
43
|
-
},
|
|
44
|
-
"homepage": "https://github.com/oharms/PanWizard#readme",
|
|
45
|
-
"bugs": {
|
|
46
|
-
"url": "https://github.com/oharms/PanWizard/issues"
|
|
47
|
-
},
|
|
48
|
-
"engines": {
|
|
49
|
-
"node": ">=16.7.0"
|
|
50
|
-
},
|
|
51
|
-
"devDependencies": {
|
|
52
|
-
"@playwright/test": "^1.58.2",
|
|
53
|
-
"@vscode/test-electron": "^2.5.2",
|
|
54
|
-
"esbuild": "^0.
|
|
55
|
-
},
|
|
56
|
-
"scripts": {
|
|
57
|
-
"build:hooks": "node scripts/build-hooks.js",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"test
|
|
61
|
-
"test:
|
|
62
|
-
"test:
|
|
63
|
-
"test:
|
|
64
|
-
"test:
|
|
65
|
-
|
|
66
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "pan-wizard",
|
|
3
|
+
"version": "3.8.0",
|
|
4
|
+
"description": "A lightweight workflow automation and context engineering system for Claude Code, OpenCode, Gemini CLI, Codex, and Copilot CLI.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"pan-wizard": "bin/install.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin",
|
|
10
|
+
"commands",
|
|
11
|
+
"pan-wizard-core",
|
|
12
|
+
"agents",
|
|
13
|
+
"hooks/dist",
|
|
14
|
+
"scripts",
|
|
15
|
+
"assets"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"claude",
|
|
19
|
+
"claude-code",
|
|
20
|
+
"ai",
|
|
21
|
+
"workflow-automation",
|
|
22
|
+
"context-engineering",
|
|
23
|
+
"project-automation",
|
|
24
|
+
"gemini",
|
|
25
|
+
"gemini-cli",
|
|
26
|
+
"codex",
|
|
27
|
+
"codex-cli",
|
|
28
|
+
"copilot",
|
|
29
|
+
"copilot-cli",
|
|
30
|
+
"github-copilot"
|
|
31
|
+
],
|
|
32
|
+
"author": "PAN Wizard Contributors",
|
|
33
|
+
"contributors": [
|
|
34
|
+
{
|
|
35
|
+
"name": "oharms",
|
|
36
|
+
"url": "https://github.com/oharms"
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/oharms/PanWizard.git"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/oharms/PanWizard#readme",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/oharms/PanWizard/issues"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=16.7.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@playwright/test": "^1.58.2",
|
|
53
|
+
"@vscode/test-electron": "^2.5.2",
|
|
54
|
+
"esbuild": "^0.28.0"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build:hooks": "node scripts/build-hooks.js",
|
|
58
|
+
"release:check": "node scripts/release-check.js",
|
|
59
|
+
"prepublishOnly": "node scripts/release-check.js",
|
|
60
|
+
"test": "node --test tests/*.test.cjs",
|
|
61
|
+
"test:scenarios": "node --test tests/scenarios/*.test.cjs",
|
|
62
|
+
"test:all": "node --test tests/*.test.cjs tests/scenarios/*.test.cjs",
|
|
63
|
+
"test:e2e": "node --test tests/scenarios/*.test.cjs",
|
|
64
|
+
"test:vscode": "npx playwright test --config tests/e2e/playwright.config.mjs",
|
|
65
|
+
"test:watch": "node --test --watch tests/*.test.cjs"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -379,6 +379,11 @@ function runCommitSafetyChecks(cwd, config, force) {
|
|
|
379
379
|
function cmdCommit(cwd, message, files, raw, amend, opts) {
|
|
380
380
|
const commitType = opts && opts.type;
|
|
381
381
|
const force = opts && opts.force;
|
|
382
|
+
// P-EXP-001 follow-up (v3.7.10): when failOnError is true, commit_failed
|
|
383
|
+
// exits non-zero so callers (especially autonomous loops) detect the silent-
|
|
384
|
+
// failure case where git refused (e.g. missing identity) and the loop would
|
|
385
|
+
// otherwise keep going thinking the artifact landed.
|
|
386
|
+
const failOnError = opts && opts.failOnError;
|
|
382
387
|
|
|
383
388
|
if (!isGitRepo(cwd)) {
|
|
384
389
|
output({ committed: false, hash: null, reason: 'not_a_git_repo', hint: 'Run git init to initialize a repository' }, raw, 'not a git repo');
|
|
@@ -425,6 +430,9 @@ function cmdCommit(cwd, message, files, raw, amend, opts) {
|
|
|
425
430
|
output({ committed: false, hash: null, reason: 'nothing_to_commit' }, raw, 'nothing');
|
|
426
431
|
return;
|
|
427
432
|
}
|
|
433
|
+
if (failOnError) {
|
|
434
|
+
error('commit_failed: ' + (commitResult.stderr || 'unknown git error').trim());
|
|
435
|
+
}
|
|
428
436
|
output({ committed: false, hash: null, reason: 'commit_failed', error: commitResult.stderr }, raw, 'failed');
|
|
429
437
|
return;
|
|
430
438
|
}
|
|
@@ -52,6 +52,7 @@ function buildConfigDefaults(hasBraveSearch, userDefaults) {
|
|
|
52
52
|
plan_check: true,
|
|
53
53
|
verifier: true,
|
|
54
54
|
nyquist_validation: false,
|
|
55
|
+
phase_record_compact: false,
|
|
55
56
|
},
|
|
56
57
|
parallelization: true,
|
|
57
58
|
brave_search: hasBraveSearch,
|
|
@@ -422,17 +423,27 @@ function cmdStandardsRecommend(cwd, raw) {
|
|
|
422
423
|
if (/\b(enterprise|togaf|architecture\s*governance|compliance)\b/.test(lower)) detectedTypes.push('enterprise');
|
|
423
424
|
if (/\b(cli|command.line|terminal|shell|argv)\b/.test(lower)) detectedTypes.push('cli');
|
|
424
425
|
|
|
425
|
-
|
|
426
|
+
const explicitlyDetected = detectedTypes.length > 0;
|
|
427
|
+
if (!explicitlyDetected) detectedTypes.push('general');
|
|
426
428
|
|
|
427
429
|
const seen = new Set();
|
|
428
430
|
const recommendations = [];
|
|
429
431
|
for (const type of detectedTypes) {
|
|
430
432
|
const recs = STANDARDS_RECOMMENDATIONS[type] || [];
|
|
433
|
+
let perTypeIndex = 0;
|
|
431
434
|
for (const id of recs) {
|
|
432
435
|
if (seen.has(id)) continue;
|
|
433
436
|
seen.add(id);
|
|
434
437
|
const s = STANDARDS_CATALOG[id];
|
|
435
|
-
|
|
438
|
+
const isHigh = explicitlyDetected && type !== 'general' && perTypeIndex === 0;
|
|
439
|
+
recommendations.push({
|
|
440
|
+
id,
|
|
441
|
+
name: s.name,
|
|
442
|
+
reason: type + ' project detected',
|
|
443
|
+
priority: isHigh ? 'high' : 'medium',
|
|
444
|
+
source_type: type,
|
|
445
|
+
});
|
|
446
|
+
perTypeIndex += 1;
|
|
436
447
|
}
|
|
437
448
|
}
|
|
438
449
|
|
|
@@ -21,6 +21,51 @@ function estimateTokens(text) {
|
|
|
21
21
|
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Estimate a relevance signal for per-phase markdown content (P-RES-002).
|
|
26
|
+
*
|
|
27
|
+
* Per Chroma's "Context Rot" research (Hong & Huber, July 2025): a single
|
|
28
|
+
* semantically-similar-but-irrelevant distractor degrades performance even
|
|
29
|
+
* at modest context sizes. Distractor density matters more than token count.
|
|
30
|
+
*
|
|
31
|
+
* Computing TRUE topic-relevance requires embeddings or keyword analysis
|
|
32
|
+
* we can't do cheaply at zero deps. This v0 heuristic reports a simpler
|
|
33
|
+
* signal: structure-vs-content ratio. Markdown files heavy on headers,
|
|
34
|
+
* separators, empty bullet lists, and placeholder text are LESS dense in
|
|
35
|
+
* actual signal than files of equal length with concrete prose. The ratio
|
|
36
|
+
* isn't true distractor density but is correlated with it for the
|
|
37
|
+
* "thin/template-only context" failure mode.
|
|
38
|
+
*
|
|
39
|
+
* Returns ratio in [0, 1] where 1 = all content lines, 0 = all structure.
|
|
40
|
+
* Returns null if not enough lines to compute meaningfully.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} text
|
|
43
|
+
* @returns {number|null}
|
|
44
|
+
*/
|
|
45
|
+
function estimateRelevanceRatio(text) {
|
|
46
|
+
if (!text) return null;
|
|
47
|
+
const lines = text.split(/\r?\n/);
|
|
48
|
+
if (lines.length < 5) return null;
|
|
49
|
+
let contentLines = 0;
|
|
50
|
+
let totalLines = 0;
|
|
51
|
+
for (const raw of lines) {
|
|
52
|
+
const line = raw.trim();
|
|
53
|
+
if (!line) continue; // skip blank
|
|
54
|
+
totalLines++;
|
|
55
|
+
if (/^#{1,6}\s/.test(line)) continue; // skip header
|
|
56
|
+
if (/^[-*_]{3,}$/.test(line)) continue; // skip separator
|
|
57
|
+
if (/^[-*]\s*$/.test(line)) continue; // skip empty bullet
|
|
58
|
+
if (/^[-*]\s*\[\s*[\]x_]\s*\]\s*$/.test(line)) continue; // empty checkbox
|
|
59
|
+
if (/^\|\s*-+\s*\|/.test(line)) continue; // table separator row
|
|
60
|
+
if (/^>\s*$/.test(line)) continue; // empty blockquote
|
|
61
|
+
if (line.length < 10) continue; // very short — likely scaffolding
|
|
62
|
+
if (/^(TODO|TBD|FIXME|placeholder|todo|tbd|fixme|placeholder|coming soon)\b/i.test(line)) continue;
|
|
63
|
+
contentLines++;
|
|
64
|
+
}
|
|
65
|
+
if (totalLines === 0) return null;
|
|
66
|
+
return Math.round((contentLines / totalLines) * 1000) / 1000;
|
|
67
|
+
}
|
|
68
|
+
|
|
24
69
|
/**
|
|
25
70
|
* Compute context budget for the current project state.
|
|
26
71
|
* @param {string} cwd - Project root directory
|
|
@@ -101,6 +146,32 @@ function cmdContextBudget(cwd, raw) {
|
|
|
101
146
|
recommendation = `Within budget. ~${additionalPlans} more plans could fit before degradation.`;
|
|
102
147
|
}
|
|
103
148
|
|
|
149
|
+
// P-RES-002 signal: structure-vs-content ratio for per-phase markdown.
|
|
150
|
+
// High structure (lots of empty bullets, headers, placeholders) suggests
|
|
151
|
+
// the per-phase context is thin/templatey rather than substantive — a
|
|
152
|
+
// proxy for "context that's wasting tokens on filler."
|
|
153
|
+
let relevanceSignal = null;
|
|
154
|
+
if (phaseDir) {
|
|
155
|
+
const fullPhasePath = path.join(cwd, phaseDir);
|
|
156
|
+
const samples = [];
|
|
157
|
+
for (const fname of ['research.md', 'context.md']) {
|
|
158
|
+
const candidate = path.join(fullPhasePath, fname);
|
|
159
|
+
const content = safeReadFile(candidate);
|
|
160
|
+
if (content) {
|
|
161
|
+
const ratio = estimateRelevanceRatio(content);
|
|
162
|
+
if (ratio !== null) samples.push({ file: toPosix(path.join(phaseDir, fname)), ratio });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (samples.length > 0) {
|
|
166
|
+
const avg = samples.reduce((a, s) => a + s.ratio, 0) / samples.length;
|
|
167
|
+
relevanceSignal = {
|
|
168
|
+
avg_ratio: Math.round(avg * 1000) / 1000,
|
|
169
|
+
samples,
|
|
170
|
+
note: 'P-RES-002 v0 heuristic — structure/content ratio. <0.4 suggests thin per-phase context (heavy on headers + empty buckets + placeholders).',
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
104
175
|
// E-8: cache metrics — surface how much of the total context would be
|
|
105
176
|
// served from prompt cache when Opus 4.7 cache_control is active.
|
|
106
177
|
const { buildCachedContext } = require('./core.cjs');
|
|
@@ -141,6 +212,7 @@ function cmdContextBudget(cwd, raw) {
|
|
|
141
212
|
contextWindow: CONTEXT_WINDOW,
|
|
142
213
|
budgetUtilization: Math.round(utilization * 1000) / 1000,
|
|
143
214
|
cache,
|
|
215
|
+
relevanceSignal,
|
|
144
216
|
recommendation,
|
|
145
217
|
};
|
|
146
218
|
|
|
@@ -174,4 +246,5 @@ function cmdContextBudget(cwd, raw) {
|
|
|
174
246
|
module.exports = {
|
|
175
247
|
cmdContextBudget,
|
|
176
248
|
estimateTokens,
|
|
249
|
+
estimateRelevanceRatio,
|
|
177
250
|
};
|
|
@@ -48,6 +48,7 @@ const COST_MULTIPLIERS = { reasoning: 15, mid: 3, fast: 1 };
|
|
|
48
48
|
// ─── Model Profile Table ─────────────────────────────────────────────────────
|
|
49
49
|
|
|
50
50
|
const MODEL_PROFILES = {
|
|
51
|
+
// Original planning/execution agents (pre-v3.0)
|
|
51
52
|
'pan-planner': { quality: 'reasoning', balanced: 'reasoning', budget: 'mid' },
|
|
52
53
|
'pan-roadmapper': { quality: 'reasoning', balanced: 'mid', budget: 'mid' },
|
|
53
54
|
'pan-executor': { quality: 'reasoning', balanced: 'mid', budget: 'mid' },
|
|
@@ -60,6 +61,18 @@ const MODEL_PROFILES = {
|
|
|
60
61
|
'pan-plan-checker': { quality: 'reasoning', balanced: 'mid', budget: 'fast' },
|
|
61
62
|
'pan-integration-checker': { quality: 'reasoning', balanced: 'mid', budget: 'fast' },
|
|
62
63
|
'pan-reviewer': { quality: 'reasoning', balanced: 'fast', budget: 'fast' },
|
|
64
|
+
// Spec B v2 agents (v3.0–v3.4) — added v3.7.5 to close MODEL_PROFILES drift
|
|
65
|
+
'pan-conductor': { quality: 'reasoning', balanced: 'reasoning', budget: 'mid' },
|
|
66
|
+
'pan-counterfactual': { quality: 'reasoning', balanced: 'mid', budget: 'mid' },
|
|
67
|
+
'pan-hardener': { quality: 'reasoning', balanced: 'mid', budget: 'fast' },
|
|
68
|
+
'pan-meta-reviewer': { quality: 'reasoning', balanced: 'mid', budget: 'fast' },
|
|
69
|
+
'pan-knowledge': { quality: 'reasoning', balanced: 'mid', budget: 'fast' },
|
|
70
|
+
'pan-previewer': { quality: 'reasoning', balanced: 'fast', budget: 'fast' },
|
|
71
|
+
// v3.5 agents
|
|
72
|
+
'pan-optimizer': { quality: 'reasoning', balanced: 'mid', budget: 'fast' },
|
|
73
|
+
'pan-distiller': { quality: 'reasoning', balanced: 'fast', budget: 'fast' },
|
|
74
|
+
// v3.7.0 self-improvement loop — observation-only watchdog
|
|
75
|
+
'pan-experiment-runner': { quality: 'reasoning', balanced: 'fast', budget: 'fast' },
|
|
63
76
|
};
|
|
64
77
|
|
|
65
78
|
// ─── Output helpers ───────────────────────────────────────────────────────────
|