pan-wizard 3.14.0 → 3.15.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/LICENSE +21 -21
- package/README.md +1 -1
- package/commands/pan/audit-deployment.md +384 -384
- package/commands/pan/focus-auto.md +683 -683
- package/commands/pan/focus-doc-audit.md +530 -530
- package/commands/pan/focus-drift-walking.md +525 -525
- package/commands/pan/git.md +1 -1
- package/commands/pan/hud.md +3 -2
- package/commands/pan/report.md +70 -0
- package/hooks/dist/pan-check-update.js +62 -62
- package/hooks/dist/pan-context-monitor.js +134 -134
- package/package.json +1 -1
- package/pan-wizard-core/bin/lib/frontmatter.cjs +442 -442
- package/pan-wizard-core/bin/lib/hud.cjs +183 -14
- package/pan-wizard-core/bin/lib/phase-report.cjs +723 -0
- package/pan-wizard-core/bin/lib/utils.cjs +171 -171
- package/pan-wizard-core/bin/pan-tools.cjs +1499 -1473
- package/pan-wizard-core/references/checkpoints.md +776 -776
- package/pan-wizard-core/references/continuation-format.md +249 -249
- package/pan-wizard-core/references/questioning.md +145 -145
- package/pan-wizard-core/references/tdd.md +263 -263
- package/pan-wizard-core/references/ui-brand.md +160 -160
- package/pan-wizard-core/templates/config.json +38 -38
- package/scripts/build-hooks.js +51 -51
- package/scripts/git-hooks/pre-commit +0 -0
- package/scripts/release-check.js +27 -36
|
@@ -1,171 +1,171 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utils — Shared utility functions used across multiple modules
|
|
3
|
-
*
|
|
4
|
-
* Functions here were previously duplicated in core.cjs, commands.cjs,
|
|
5
|
-
* state.cjs, and phase.cjs. Now centralized for single-source-of-truth.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const os = require('os');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const {
|
|
12
|
-
PLANNING_DIR,
|
|
13
|
-
PHASES_DIR,
|
|
14
|
-
MILESTONES_DIR,
|
|
15
|
-
isPlanFile,
|
|
16
|
-
isSummaryFile,
|
|
17
|
-
PHASE_DIR_RE,
|
|
18
|
-
} = require('./constants.cjs');
|
|
19
|
-
|
|
20
|
-
// ─── File utilities ──────────────────────────────────────────────────────────
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Read and parse a JSON file, returning null on any failure.
|
|
24
|
-
* @param {string} filePath - Absolute path to the JSON file
|
|
25
|
-
* @returns {Object|null} Parsed JSON object, or null if unreadable/unparseable
|
|
26
|
-
*/
|
|
27
|
-
function readJsonFile(filePath) {
|
|
28
|
-
try {
|
|
29
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
30
|
-
} catch {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Remove surrounding quotes (single or double) from a string.
|
|
37
|
-
* @param {string} str - Input string possibly wrapped in quotes
|
|
38
|
-
* @returns {string} String with leading/trailing quotes removed
|
|
39
|
-
*/
|
|
40
|
-
function removeQuotes(str) {
|
|
41
|
-
return str.replace(/^["']|["']$/g, '');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ─── Phase directory utilities ───────────────────────────────────────────────
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Build the absolute path to the .planning directory.
|
|
48
|
-
* @param {string} cwd - Project root directory
|
|
49
|
-
* @returns {string} Absolute path to .planning/
|
|
50
|
-
*/
|
|
51
|
-
function planningPath(cwd) {
|
|
52
|
-
return path.join(cwd, PLANNING_DIR);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Build the absolute path to the phases directory.
|
|
57
|
-
* @param {string} cwd - Project root directory
|
|
58
|
-
* @returns {string} Absolute path to .planning/phases/
|
|
59
|
-
*/
|
|
60
|
-
function phasesPath(cwd) {
|
|
61
|
-
return path.join(cwd, PLANNING_DIR, PHASES_DIR);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Build the absolute path to the milestones directory.
|
|
66
|
-
* @param {string} cwd - Project root directory
|
|
67
|
-
* @returns {string} Absolute path to .planning/milestones/
|
|
68
|
-
*/
|
|
69
|
-
function milestonesPath(cwd) {
|
|
70
|
-
return path.join(cwd, PLANNING_DIR, MILESTONES_DIR);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Read phase directories from the phases folder, sorted by phase number.
|
|
75
|
-
* @param {string} cwd - Project root directory
|
|
76
|
-
* @returns {string[]} Sorted array of directory names, or empty array on failure
|
|
77
|
-
*/
|
|
78
|
-
function listPhaseDirs(cwd) {
|
|
79
|
-
const { comparePhaseNum } = require('./core.cjs');
|
|
80
|
-
try {
|
|
81
|
-
const entries = fs.readdirSync(phasesPath(cwd), { withFileTypes: true });
|
|
82
|
-
return entries
|
|
83
|
-
.filter(e => e.isDirectory())
|
|
84
|
-
.map(e => e.name)
|
|
85
|
-
.sort((a, b) => comparePhaseNum(a, b));
|
|
86
|
-
} catch {
|
|
87
|
-
return [];
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Filter an array of filenames to only plan files, sorted.
|
|
93
|
-
* @param {string[]} files - Array of filenames
|
|
94
|
-
* @returns {string[]} Sorted plan filenames
|
|
95
|
-
*/
|
|
96
|
-
function filterPlanFiles(files) {
|
|
97
|
-
return files.filter(isPlanFile).sort();
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Filter an array of filenames to only summary files, sorted.
|
|
102
|
-
* @param {string[]} files - Array of filenames
|
|
103
|
-
* @returns {string[]} Sorted summary filenames
|
|
104
|
-
*/
|
|
105
|
-
function filterSummaryFiles(files) {
|
|
106
|
-
return files.filter(isSummaryFile).sort();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Extract the phase number and name from a phase directory name.
|
|
111
|
-
* e.g. "01-setup-auth" → { number: "01", name: "setup-auth" }
|
|
112
|
-
* @param {string} dirName - Phase directory name
|
|
113
|
-
* @returns {{ number: string, name: string|null }} Parsed phase info
|
|
114
|
-
*/
|
|
115
|
-
function parsePhaseDir(dirName) {
|
|
116
|
-
const match = dirName.match(PHASE_DIR_RE);
|
|
117
|
-
if (!match) return { number: dirName, name: null };
|
|
118
|
-
return {
|
|
119
|
-
number: match[1],
|
|
120
|
-
name: match[2] || null,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Classify phase status from file counts. Returns a granular status string.
|
|
126
|
-
* @param {number} planCount - Number of plan files
|
|
127
|
-
* @param {number} summaryCount - Number of summary files
|
|
128
|
-
* @param {{hasContext?: boolean, hasResearch?: boolean}} [flags] - Extra file flags
|
|
129
|
-
* @returns {string} One of: 'complete', 'partial', 'planned', 'researched', 'discussed', 'empty'
|
|
130
|
-
*/
|
|
131
|
-
function classifyPhaseStatus(planCount, summaryCount, flags = {}) {
|
|
132
|
-
if (summaryCount >= planCount && planCount > 0) return 'complete';
|
|
133
|
-
if (summaryCount > 0) return 'partial';
|
|
134
|
-
if (planCount > 0) return 'planned';
|
|
135
|
-
if (flags.hasResearch) return 'researched';
|
|
136
|
-
if (flags.hasContext) return 'discussed';
|
|
137
|
-
return 'empty';
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Check if a file is accessible (exists and is readable).
|
|
142
|
-
* @param {string} filePath - Absolute path to check
|
|
143
|
-
* @returns {boolean}
|
|
144
|
-
*/
|
|
145
|
-
function fileAccessible(filePath) {
|
|
146
|
-
try { fs.accessSync(filePath, fs.constants.R_OK); return true; } catch { return false; }
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Detect whether Brave Search API key is available (env var or key file).
|
|
151
|
-
* @returns {boolean}
|
|
152
|
-
*/
|
|
153
|
-
function hasBraveSearchKey() {
|
|
154
|
-
if (process.env.BRAVE_API_KEY) return true;
|
|
155
|
-
return fileAccessible(path.join(os.homedir(), '.pan-wizard', 'brave_api_key'));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
module.exports = {
|
|
159
|
-
readJsonFile,
|
|
160
|
-
removeQuotes,
|
|
161
|
-
planningPath,
|
|
162
|
-
phasesPath,
|
|
163
|
-
milestonesPath,
|
|
164
|
-
listPhaseDirs,
|
|
165
|
-
filterPlanFiles,
|
|
166
|
-
filterSummaryFiles,
|
|
167
|
-
parsePhaseDir,
|
|
168
|
-
classifyPhaseStatus,
|
|
169
|
-
fileAccessible,
|
|
170
|
-
hasBraveSearchKey,
|
|
171
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Utils — Shared utility functions used across multiple modules
|
|
3
|
+
*
|
|
4
|
+
* Functions here were previously duplicated in core.cjs, commands.cjs,
|
|
5
|
+
* state.cjs, and phase.cjs. Now centralized for single-source-of-truth.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const {
|
|
12
|
+
PLANNING_DIR,
|
|
13
|
+
PHASES_DIR,
|
|
14
|
+
MILESTONES_DIR,
|
|
15
|
+
isPlanFile,
|
|
16
|
+
isSummaryFile,
|
|
17
|
+
PHASE_DIR_RE,
|
|
18
|
+
} = require('./constants.cjs');
|
|
19
|
+
|
|
20
|
+
// ─── File utilities ──────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Read and parse a JSON file, returning null on any failure.
|
|
24
|
+
* @param {string} filePath - Absolute path to the JSON file
|
|
25
|
+
* @returns {Object|null} Parsed JSON object, or null if unreadable/unparseable
|
|
26
|
+
*/
|
|
27
|
+
function readJsonFile(filePath) {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Remove surrounding quotes (single or double) from a string.
|
|
37
|
+
* @param {string} str - Input string possibly wrapped in quotes
|
|
38
|
+
* @returns {string} String with leading/trailing quotes removed
|
|
39
|
+
*/
|
|
40
|
+
function removeQuotes(str) {
|
|
41
|
+
return str.replace(/^["']|["']$/g, '');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ─── Phase directory utilities ───────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Build the absolute path to the .planning directory.
|
|
48
|
+
* @param {string} cwd - Project root directory
|
|
49
|
+
* @returns {string} Absolute path to .planning/
|
|
50
|
+
*/
|
|
51
|
+
function planningPath(cwd) {
|
|
52
|
+
return path.join(cwd, PLANNING_DIR);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Build the absolute path to the phases directory.
|
|
57
|
+
* @param {string} cwd - Project root directory
|
|
58
|
+
* @returns {string} Absolute path to .planning/phases/
|
|
59
|
+
*/
|
|
60
|
+
function phasesPath(cwd) {
|
|
61
|
+
return path.join(cwd, PLANNING_DIR, PHASES_DIR);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build the absolute path to the milestones directory.
|
|
66
|
+
* @param {string} cwd - Project root directory
|
|
67
|
+
* @returns {string} Absolute path to .planning/milestones/
|
|
68
|
+
*/
|
|
69
|
+
function milestonesPath(cwd) {
|
|
70
|
+
return path.join(cwd, PLANNING_DIR, MILESTONES_DIR);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Read phase directories from the phases folder, sorted by phase number.
|
|
75
|
+
* @param {string} cwd - Project root directory
|
|
76
|
+
* @returns {string[]} Sorted array of directory names, or empty array on failure
|
|
77
|
+
*/
|
|
78
|
+
function listPhaseDirs(cwd) {
|
|
79
|
+
const { comparePhaseNum } = require('./core.cjs');
|
|
80
|
+
try {
|
|
81
|
+
const entries = fs.readdirSync(phasesPath(cwd), { withFileTypes: true });
|
|
82
|
+
return entries
|
|
83
|
+
.filter(e => e.isDirectory())
|
|
84
|
+
.map(e => e.name)
|
|
85
|
+
.sort((a, b) => comparePhaseNum(a, b));
|
|
86
|
+
} catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Filter an array of filenames to only plan files, sorted.
|
|
93
|
+
* @param {string[]} files - Array of filenames
|
|
94
|
+
* @returns {string[]} Sorted plan filenames
|
|
95
|
+
*/
|
|
96
|
+
function filterPlanFiles(files) {
|
|
97
|
+
return files.filter(isPlanFile).sort();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Filter an array of filenames to only summary files, sorted.
|
|
102
|
+
* @param {string[]} files - Array of filenames
|
|
103
|
+
* @returns {string[]} Sorted summary filenames
|
|
104
|
+
*/
|
|
105
|
+
function filterSummaryFiles(files) {
|
|
106
|
+
return files.filter(isSummaryFile).sort();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Extract the phase number and name from a phase directory name.
|
|
111
|
+
* e.g. "01-setup-auth" → { number: "01", name: "setup-auth" }
|
|
112
|
+
* @param {string} dirName - Phase directory name
|
|
113
|
+
* @returns {{ number: string, name: string|null }} Parsed phase info
|
|
114
|
+
*/
|
|
115
|
+
function parsePhaseDir(dirName) {
|
|
116
|
+
const match = dirName.match(PHASE_DIR_RE);
|
|
117
|
+
if (!match) return { number: dirName, name: null };
|
|
118
|
+
return {
|
|
119
|
+
number: match[1],
|
|
120
|
+
name: match[2] || null,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Classify phase status from file counts. Returns a granular status string.
|
|
126
|
+
* @param {number} planCount - Number of plan files
|
|
127
|
+
* @param {number} summaryCount - Number of summary files
|
|
128
|
+
* @param {{hasContext?: boolean, hasResearch?: boolean}} [flags] - Extra file flags
|
|
129
|
+
* @returns {string} One of: 'complete', 'partial', 'planned', 'researched', 'discussed', 'empty'
|
|
130
|
+
*/
|
|
131
|
+
function classifyPhaseStatus(planCount, summaryCount, flags = {}) {
|
|
132
|
+
if (summaryCount >= planCount && planCount > 0) return 'complete';
|
|
133
|
+
if (summaryCount > 0) return 'partial';
|
|
134
|
+
if (planCount > 0) return 'planned';
|
|
135
|
+
if (flags.hasResearch) return 'researched';
|
|
136
|
+
if (flags.hasContext) return 'discussed';
|
|
137
|
+
return 'empty';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Check if a file is accessible (exists and is readable).
|
|
142
|
+
* @param {string} filePath - Absolute path to check
|
|
143
|
+
* @returns {boolean}
|
|
144
|
+
*/
|
|
145
|
+
function fileAccessible(filePath) {
|
|
146
|
+
try { fs.accessSync(filePath, fs.constants.R_OK); return true; } catch { return false; }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Detect whether Brave Search API key is available (env var or key file).
|
|
151
|
+
* @returns {boolean}
|
|
152
|
+
*/
|
|
153
|
+
function hasBraveSearchKey() {
|
|
154
|
+
if (process.env.BRAVE_API_KEY) return true;
|
|
155
|
+
return fileAccessible(path.join(os.homedir(), '.pan-wizard', 'brave_api_key'));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
module.exports = {
|
|
159
|
+
readJsonFile,
|
|
160
|
+
removeQuotes,
|
|
161
|
+
planningPath,
|
|
162
|
+
phasesPath,
|
|
163
|
+
milestonesPath,
|
|
164
|
+
listPhaseDirs,
|
|
165
|
+
filterPlanFiles,
|
|
166
|
+
filterSummaryFiles,
|
|
167
|
+
parsePhaseDir,
|
|
168
|
+
classifyPhaseStatus,
|
|
169
|
+
fileAccessible,
|
|
170
|
+
hasBraveSearchKey,
|
|
171
|
+
};
|