pan-wizard 3.26.0 → 3.28.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 +48 -48
- package/agents/pan-previewer.md +1 -1
- package/bin/install-lib.cjs +580 -18
- package/bin/install.js +25 -44
- package/commands/pan/army.md +1 -1
- package/commands/pan/hygiene.md +14 -8
- package/commands/pan/milestone-audit.md +10 -4
- package/commands/pan/preview.md +2 -2
- package/hooks/dist/pan-cost-logger.js +69 -5
- package/hooks/dist/pan-stop-guard.js +32 -1
- package/hooks/dist/pan-trace-logger.js +35 -2
- package/package.json +5 -2
- package/pan-wizard-core/bin/lib/bridge.cjs +0 -1
- package/pan-wizard-core/bin/lib/bus.cjs +0 -1
- package/pan-wizard-core/bin/lib/campaign.cjs +3 -2
- package/pan-wizard-core/bin/lib/commands-learnings.cjs +8 -8
- package/pan-wizard-core/bin/lib/commands.cjs +15 -14
- package/pan-wizard-core/bin/lib/config.cjs +5 -5
- package/pan-wizard-core/bin/lib/constants.cjs +49 -0
- package/pan-wizard-core/bin/lib/context-budget.cjs +98 -0
- package/pan-wizard-core/bin/lib/core.cjs +190 -26
- package/pan-wizard-core/bin/lib/cost.cjs +113 -11
- package/pan-wizard-core/bin/lib/distill.cjs +3 -3
- package/pan-wizard-core/bin/lib/focus.cjs +16 -16
- package/pan-wizard-core/bin/lib/foreign-planning.cjs +56 -0
- package/pan-wizard-core/bin/lib/hud.cjs +1 -1
- package/pan-wizard-core/bin/lib/hygiene.cjs +428 -37
- package/pan-wizard-core/bin/lib/init.cjs +98 -13
- package/pan-wizard-core/bin/lib/knowledge.cjs +0 -1
- package/pan-wizard-core/bin/lib/memory.cjs +1 -1
- package/pan-wizard-core/bin/lib/milestone.cjs +3 -3
- package/pan-wizard-core/bin/lib/optimize.cjs +3 -3
- package/pan-wizard-core/bin/lib/phase.cjs +4 -4
- package/pan-wizard-core/bin/lib/planning-root.cjs +327 -0
- package/pan-wizard-core/bin/lib/preview.cjs +0 -1
- package/pan-wizard-core/bin/lib/review-deep.cjs +0 -1
- package/pan-wizard-core/bin/lib/roadmap.cjs +1 -1
- package/pan-wizard-core/bin/lib/state-compact.cjs +339 -0
- package/pan-wizard-core/bin/lib/state.cjs +0 -1
- package/pan-wizard-core/bin/lib/template.cjs +1 -1
- package/pan-wizard-core/bin/lib/utils.cjs +39 -11
- package/pan-wizard-core/bin/lib/verify.cjs +26 -5
- package/pan-wizard-core/bin/lib/whatif.cjs +0 -1
- package/pan-wizard-core/bin/pan-tools.cjs +58 -4
- package/pan-wizard-core/mcp/server.cjs +92 -8
- package/pan-wizard-core/mcp/tool-registry.cjs +50 -3
- package/pan-wizard-core/references/model-profiles.md +2 -2
- package/pan-wizard-core/workflows/health.md +1 -0
- package/pan-wizard-core/workflows/milestone-audit.md +35 -6
- package/pan-zcode/README.md +1 -1
- package/scripts/build-agent-plugin.js +220 -0
- package/scripts/build-plugin.js +48 -3
- package/scripts/generate-skills-docs.py +1 -1
- package/scripts/release-check.js +58 -12
|
@@ -10,7 +10,7 @@ const fs = require('fs');
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const { output, EXIT_OK, error, safeReadFile, loadConfig, scanPendingTodos, scanSourceTodos, toPosix, isGitRepo, execGit, escapeRegex, normalizePhaseName } = require('./core.cjs');
|
|
12
12
|
const {
|
|
13
|
-
|
|
13
|
+
PHASES_DIR, ROADMAP_FILE, PATTERNS_FILE, EFFORT_POINTS, PRIORITY_LEVELS, EFFORT_SIZES,
|
|
14
14
|
FOCUS_MODES, FOCUS_TIERS, FOCUS_DIR,
|
|
15
15
|
BUDGET_LIMIT_BUGFIX, BUDGET_LIMIT_FULL, STABILITY_RATIO, FEATURE_RATIO,
|
|
16
16
|
DIMINISHING_RETURNS_THRESHOLD,
|
|
@@ -22,7 +22,7 @@ const {
|
|
|
22
22
|
const { extractFrontmatter, extractPriorityEffort } = require('./frontmatter.cjs');
|
|
23
23
|
const { enumerateRoadmapPhases } = require('./roadmap.cjs');
|
|
24
24
|
const { readErrorPatterns } = require('./commands.cjs');
|
|
25
|
-
const { planningPath, listPhaseDirs, classifyPhaseStatus, filterPlanFiles, filterSummaryFiles } = require('./utils.cjs');
|
|
25
|
+
const { planningPath, listPhaseDirs, classifyPhaseStatus, filterPlanFiles, filterSummaryFiles, planningRel } = require('./utils.cjs');
|
|
26
26
|
|
|
27
27
|
// ─── Scan helpers ───────────────────────────────────────────────────────────
|
|
28
28
|
|
|
@@ -37,11 +37,11 @@ function collectWorkItems(cwd) {
|
|
|
37
37
|
const sources = { phases: 0, todos: 0, patterns: 0 };
|
|
38
38
|
|
|
39
39
|
// 1. Phase-based items from ROADMAP + plan.md frontmatter
|
|
40
|
-
const roadmapPath =
|
|
40
|
+
const roadmapPath = planningPath(cwd, ROADMAP_FILE);
|
|
41
41
|
const roadmapContent = safeReadFile(roadmapPath);
|
|
42
42
|
if (roadmapContent) {
|
|
43
43
|
const phases = enumerateRoadmapPhases(roadmapContent);
|
|
44
|
-
const phasesDir =
|
|
44
|
+
const phasesDir = planningPath(cwd, PHASES_DIR);
|
|
45
45
|
let dirs;
|
|
46
46
|
try { dirs = fs.readdirSync(phasesDir); } catch { dirs = []; }
|
|
47
47
|
|
|
@@ -89,7 +89,7 @@ function collectWorkItems(cwd) {
|
|
|
89
89
|
effort,
|
|
90
90
|
points: EFFORT_POINTS[effort] || 4,
|
|
91
91
|
status,
|
|
92
|
-
file:
|
|
92
|
+
file: planningRel(PHASES_DIR, dirName),
|
|
93
93
|
});
|
|
94
94
|
sources.phases++;
|
|
95
95
|
}
|
|
@@ -107,7 +107,7 @@ function collectWorkItems(cwd) {
|
|
|
107
107
|
effort: 'S',
|
|
108
108
|
points: EFFORT_POINTS.S,
|
|
109
109
|
status: 'pending',
|
|
110
|
-
file:
|
|
110
|
+
file: planningRel('todos', 'pending', todo.file),
|
|
111
111
|
});
|
|
112
112
|
sources.todos++;
|
|
113
113
|
}
|
|
@@ -124,7 +124,7 @@ function collectWorkItems(cwd) {
|
|
|
124
124
|
effort: 'S',
|
|
125
125
|
points: EFFORT_POINTS.S,
|
|
126
126
|
status: 'active',
|
|
127
|
-
file:
|
|
127
|
+
file: planningRel(PATTERNS_FILE),
|
|
128
128
|
});
|
|
129
129
|
sources.patterns++;
|
|
130
130
|
}
|
|
@@ -384,7 +384,7 @@ function cmdFocusPlan(cwd, raw, ...args) {
|
|
|
384
384
|
const { batch, allocated, remaining } = allocateBudget(sorted, budget, mode);
|
|
385
385
|
|
|
386
386
|
// Write batch file
|
|
387
|
-
const focusDir =
|
|
387
|
+
const focusDir = planningPath(cwd, FOCUS_DIR);
|
|
388
388
|
try { fs.mkdirSync(focusDir, { recursive: true }); } catch { /* exists */ }
|
|
389
389
|
|
|
390
390
|
const date = new Date().toISOString().split('T')[0];
|
|
@@ -574,7 +574,7 @@ function cmdFocusSync(cwd, raw, ...args) {
|
|
|
574
574
|
* @returns {Object|null} Parsed batch data or null
|
|
575
575
|
*/
|
|
576
576
|
function readLatestBatch(cwd) {
|
|
577
|
-
const focusDir =
|
|
577
|
+
const focusDir = planningPath(cwd, FOCUS_DIR);
|
|
578
578
|
let files;
|
|
579
579
|
try {
|
|
580
580
|
files = fs.readdirSync(focusDir).filter(f => f.startsWith('batch-') && f.endsWith('.json'));
|
|
@@ -642,7 +642,7 @@ function cmdFocusExec(cwd, raw, ...args) {
|
|
|
642
642
|
full: full.length,
|
|
643
643
|
},
|
|
644
644
|
items: batch.batch,
|
|
645
|
-
batch_file:
|
|
645
|
+
batch_file: planningRel(FOCUS_DIR, `batch-${batch.date}.json`),
|
|
646
646
|
};
|
|
647
647
|
|
|
648
648
|
output(result, raw);
|
|
@@ -672,7 +672,7 @@ function categoryFilter(items, category) {
|
|
|
672
672
|
* @returns {object|null} Parsed auto-run state or null
|
|
673
673
|
*/
|
|
674
674
|
function readAutoRun(cwd) {
|
|
675
|
-
const filePath =
|
|
675
|
+
const filePath = planningPath(cwd, FOCUS_DIR, AUTO_RUN_FILE);
|
|
676
676
|
const content = safeReadFile(filePath);
|
|
677
677
|
if (!content) return null;
|
|
678
678
|
try {
|
|
@@ -689,7 +689,7 @@ function readAutoRun(cwd) {
|
|
|
689
689
|
* @returns {boolean} true on success
|
|
690
690
|
*/
|
|
691
691
|
function writeAutoRun(cwd, data) {
|
|
692
|
-
const dirPath =
|
|
692
|
+
const dirPath = planningPath(cwd, FOCUS_DIR);
|
|
693
693
|
try {
|
|
694
694
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
695
695
|
fs.writeFileSync(path.join(dirPath, AUTO_RUN_FILE), JSON.stringify(data, null, 2));
|
|
@@ -929,9 +929,9 @@ function focusAutoCheckpointCommit(cwd, cycle, run) {
|
|
|
929
929
|
// Enabled projects: refresh the HTML reports before staging so the committed
|
|
930
930
|
// .planning/ snapshot reflects this cycle.
|
|
931
931
|
maybeRenderPhaseReports(cwd);
|
|
932
|
-
const status = execGit(cwd, ['status', '--porcelain',
|
|
932
|
+
const status = execGit(cwd, ['status', '--porcelain', planningRel() + '/']);
|
|
933
933
|
if (status.exitCode !== 0 || !status.stdout) return null;
|
|
934
|
-
execGit(cwd, ['add',
|
|
934
|
+
execGit(cwd, ['add', planningRel() + '/']);
|
|
935
935
|
const msg = `docs: focus-auto cycle ${cycle.cycle} — ${cycle.items_completed} items completed`;
|
|
936
936
|
const commitResult = execGit(cwd, ['commit', '-m', msg]);
|
|
937
937
|
if (commitResult.exitCode !== 0) return null;
|
|
@@ -1057,11 +1057,11 @@ function focusAutoInit(cwd, raw, getVal, hasFlag) {
|
|
|
1057
1057
|
};
|
|
1058
1058
|
|
|
1059
1059
|
if (hasFlag('--dry-run')) {
|
|
1060
|
-
return output({ dry_run: true, ...runData, run_file:
|
|
1060
|
+
return output({ dry_run: true, ...runData, run_file: planningRel(FOCUS_DIR, AUTO_RUN_FILE) }, raw);
|
|
1061
1061
|
}
|
|
1062
1062
|
|
|
1063
1063
|
writeAutoRun(cwd, runData);
|
|
1064
|
-
output({ ...runData, run_file:
|
|
1064
|
+
output({ ...runData, run_file: planningRel(FOCUS_DIR, AUTO_RUN_FILE) }, raw);
|
|
1065
1065
|
}
|
|
1066
1066
|
|
|
1067
1067
|
function cmdFocusAuto(cwd, raw, ...args) {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Foreign planning-tree detection (reality check RC17 / plan item R15, 2026-09-10).
|
|
4
|
+
*
|
|
5
|
+
* gsd-core (open-gsd/gsd-core, the continuation of Get Shit Done) writes a `.planning/`
|
|
6
|
+
* directory with the same core files PAN's pre-v2.2 layout used — STATE.md, ROADMAP.md,
|
|
7
|
+
* PROJECT.md, REQUIREMENTS.md, MILESTONES.md in uppercase. To PAN's hygiene those read
|
|
8
|
+
* as LEGACY PAN files, and `hygiene clean --apply` would rename another tool's state.
|
|
9
|
+
* `validate health` would call the tree broken; `init new-project` would scaffold PAN
|
|
10
|
+
* files into it. This module answers one question — does this tree belong to another
|
|
11
|
+
* tool? — from POSITIVE markers PAN never writes (FOREIGN_PLANNING_MARKERS in
|
|
12
|
+
* constants.cjs, sourced from gsd-core's docs/USER-GUIDE.md, read 2026-09-10).
|
|
13
|
+
*
|
|
14
|
+
* Rule: foreign when any marker FILE exists, or at least two marker DIRECTORIES exist,
|
|
15
|
+
* or config.json carries any of the tool's flat dotted keys (PAN nests `workflow: {}`;
|
|
16
|
+
* gsd-core writes `"workflow.discuss_mode"`). An unreadable config.json is not evidence.
|
|
17
|
+
* Returns { tool, key, evidence[] } or null. Never throws.
|
|
18
|
+
*/
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const { FOREIGN_PLANNING_MARKERS } = require('./constants.cjs');
|
|
22
|
+
const { planningPath } = require('./utils.cjs');
|
|
23
|
+
|
|
24
|
+
function detectForeignPlanningTree(planningDir) {
|
|
25
|
+
let entries;
|
|
26
|
+
try { entries = fs.readdirSync(planningDir, { withFileTypes: true }); } catch { return null; }
|
|
27
|
+
const names = new Set(entries.map(e => e.name));
|
|
28
|
+
const dirs = new Set(entries.filter(e => e.isDirectory()).map(e => e.name));
|
|
29
|
+
let cfg = null;
|
|
30
|
+
if (names.has('config.json')) {
|
|
31
|
+
try {
|
|
32
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(planningDir, 'config.json'), 'utf8'));
|
|
33
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) cfg = parsed;
|
|
34
|
+
} catch { cfg = null; }
|
|
35
|
+
}
|
|
36
|
+
for (const [key, m] of Object.entries(FOREIGN_PLANNING_MARKERS)) {
|
|
37
|
+
const fileHits = m.files.filter(f => names.has(f));
|
|
38
|
+
const dirHits = m.dirs.filter(d => dirs.has(d));
|
|
39
|
+
const configHits = cfg ? m.configKeys.filter(k => Object.prototype.hasOwnProperty.call(cfg, k)) : [];
|
|
40
|
+
if (fileHits.length > 0 || dirHits.length >= 2 || configHits.length > 0) {
|
|
41
|
+
return {
|
|
42
|
+
tool: m.tool,
|
|
43
|
+
key,
|
|
44
|
+
evidence: [...fileHits, ...dirHits.map(d => d + '/'), ...configHits.map(k => 'config.json:' + k)],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Same check, addressed by project cwd (honours --planning-dir / --track). */
|
|
52
|
+
function detectForeignPlanningTreeAt(cwd) {
|
|
53
|
+
return detectForeignPlanningTree(planningPath(cwd));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { detectForeignPlanningTree, detectForeignPlanningTreeAt };
|
|
@@ -30,7 +30,7 @@ const {
|
|
|
30
30
|
execGit, isGitRepo, escapeRegex, toPosix,
|
|
31
31
|
} = require('./core.cjs');
|
|
32
32
|
const {
|
|
33
|
-
|
|
33
|
+
STATE_FILE, PROJECT_FILE, REQUIREMENTS_FILE, PAUSE_FILE,
|
|
34
34
|
} = require('./constants.cjs');
|
|
35
35
|
const {
|
|
36
36
|
planningPath, phasesPath, listPhaseDirs, parsePhaseDir,
|