pan-wizard 3.5.1 → 3.7.10
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 +10 -10
- 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/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/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 +501 -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/optimize.cjs +474 -1
- package/pan-wizard-core/bin/lib/runner.cjs +472 -0
- package/pan-wizard-core/bin/pan-tools.cjs +222 -2
- 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/release-check.js +184 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* doc-lint.cjs — markdown frontmatter linter for PAN's own files.
|
|
4
|
+
*
|
|
5
|
+
* Vendored from whooo (https://github.com/oharms/PanWizard experiments/whooo).
|
|
6
|
+
* Wraps doc-lint/{frontmatter,schema,validate,walk,reporter}.js with PAN's
|
|
7
|
+
* core.cjs output() pattern.
|
|
8
|
+
*
|
|
9
|
+
* Spec: docs/specs/self_improvement_loop_featureai.md (whooo experiment outputs)
|
|
10
|
+
* Pattern source: P-201 + P-202 + P-301 (promoted from whooo run, v3.7.0)
|
|
11
|
+
*
|
|
12
|
+
* Usage (CLI):
|
|
13
|
+
* pan-tools doc-lint <dir> [--schema <path>] [--format json|human] [--strict]
|
|
14
|
+
*
|
|
15
|
+
* Default schema: pan-wizard-core/references/schemas/pan-command.schema.yml
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { output, error } = require('./core.cjs');
|
|
21
|
+
|
|
22
|
+
const { parseFrontmatter } = require('./doc-lint/frontmatter.js');
|
|
23
|
+
const { parseSchema } = require('./doc-lint/schema.js');
|
|
24
|
+
const { validateAgainstSchema } = require('./doc-lint/validate.js');
|
|
25
|
+
const { walkMarkdownFiles } = require('./doc-lint/walk.js');
|
|
26
|
+
const { formatHuman, formatJson, summaryLine } = require('./doc-lint/reporter.js');
|
|
27
|
+
|
|
28
|
+
const DEFAULT_SCHEMA_PATH = path.join(
|
|
29
|
+
__dirname,
|
|
30
|
+
'..',
|
|
31
|
+
'..',
|
|
32
|
+
'references',
|
|
33
|
+
'schemas',
|
|
34
|
+
'pan-command.schema.yml'
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Lint a directory of markdown files against a schema.
|
|
39
|
+
* @param {string} cwd - working directory (used to resolve relative paths)
|
|
40
|
+
* @param {string} dir - directory to scan (relative to cwd or absolute)
|
|
41
|
+
* @param {object} opts - { schema: string, format: 'json'|'human', strict: bool, exclude: string[], raw: bool }
|
|
42
|
+
* @returns {void} — writes to stdout via output(); exit code via process.exit
|
|
43
|
+
*/
|
|
44
|
+
function cmdDocLint(cwd, dir, opts = {}) {
|
|
45
|
+
const targetDir = path.isAbsolute(dir) ? dir : path.join(cwd, dir);
|
|
46
|
+
if (!fs.existsSync(targetDir)) {
|
|
47
|
+
error(`directory not found: ${targetDir}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const schemaPath = opts.schema
|
|
51
|
+
? (path.isAbsolute(opts.schema) ? opts.schema : path.join(cwd, opts.schema))
|
|
52
|
+
: DEFAULT_SCHEMA_PATH;
|
|
53
|
+
if (!fs.existsSync(schemaPath)) {
|
|
54
|
+
error(`schema not found: ${schemaPath}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const schemaText = fs.readFileSync(schemaPath, 'utf-8');
|
|
58
|
+
const { schema, errors: schemaErrors } = parseSchema(schemaText);
|
|
59
|
+
if (schemaErrors.length > 0) {
|
|
60
|
+
if (opts.raw) {
|
|
61
|
+
process.stderr.write(`schema has ${schemaErrors.length} error(s):\n`);
|
|
62
|
+
for (const e of schemaErrors) {
|
|
63
|
+
process.stderr.write(` ${schemaPath}:${e.line} — ${e.message}\n`);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
output({ schema_errors: schemaErrors, schema: schemaPath }, false);
|
|
67
|
+
}
|
|
68
|
+
process.exit(2);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const exclude = opts.exclude || [];
|
|
72
|
+
const files = walkMarkdownFiles(targetDir, { exclude });
|
|
73
|
+
const violations = [];
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
if (file.readError) {
|
|
76
|
+
violations.push({
|
|
77
|
+
file: file.relativePath,
|
|
78
|
+
line: 1, field: null,
|
|
79
|
+
code: 'file-read-error',
|
|
80
|
+
message: file.readError,
|
|
81
|
+
severity: 'error',
|
|
82
|
+
});
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const fm = parseFrontmatter(file.content);
|
|
86
|
+
const v = validateAgainstSchema(fm, schema, file.relativePath, { strict: !!opts.strict });
|
|
87
|
+
violations.push(...v);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const fileCount = files.length;
|
|
91
|
+
const errorCount = violations.filter(v => v.severity === 'error').length;
|
|
92
|
+
const warningCount = violations.filter(v => v.severity === 'warning').length;
|
|
93
|
+
|
|
94
|
+
const format = opts.format || 'human';
|
|
95
|
+
if (opts.raw) {
|
|
96
|
+
if (format === 'json') {
|
|
97
|
+
const txt = formatJson(violations);
|
|
98
|
+
if (txt) process.stdout.write(txt + '\n');
|
|
99
|
+
} else {
|
|
100
|
+
const txt = formatHuman(violations);
|
|
101
|
+
if (txt) process.stdout.write(txt + '\n');
|
|
102
|
+
process.stdout.write(summaryLine(violations, fileCount) + '\n');
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
output({
|
|
106
|
+
directory: dir,
|
|
107
|
+
schema: schemaPath,
|
|
108
|
+
file_count: fileCount,
|
|
109
|
+
error_count: errorCount,
|
|
110
|
+
warning_count: warningCount,
|
|
111
|
+
violations,
|
|
112
|
+
}, false);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
process.exit(errorCount > 0 ? 1 : 0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Validate that a schema file is well-formed.
|
|
120
|
+
*/
|
|
121
|
+
function cmdDocLintSchemaCheck(cwd, schemaPath, opts = {}) {
|
|
122
|
+
const resolved = path.isAbsolute(schemaPath) ? schemaPath : path.join(cwd, schemaPath);
|
|
123
|
+
if (!fs.existsSync(resolved)) {
|
|
124
|
+
error(`schema not found: ${resolved}`);
|
|
125
|
+
}
|
|
126
|
+
const text = fs.readFileSync(resolved, 'utf-8');
|
|
127
|
+
const { errors } = parseSchema(text);
|
|
128
|
+
const result = {
|
|
129
|
+
schema: resolved,
|
|
130
|
+
ok: errors.length === 0,
|
|
131
|
+
error_count: errors.length,
|
|
132
|
+
errors,
|
|
133
|
+
};
|
|
134
|
+
output(result, opts.raw);
|
|
135
|
+
process.exit(result.ok ? 0 : 1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ─── Count-drift lint (IMPROVEMENT-TODO P1, v3.7.10) ────────────────────────
|
|
139
|
+
//
|
|
140
|
+
// Counts (tests, commands, agents, modules, etc.) are supposed to live ONLY
|
|
141
|
+
// in CLAUDE.md. Other docs MUST NOT embed them — they drift instantly. This
|
|
142
|
+
// linter scans markdown files and flags any drift-prone numeric count it
|
|
143
|
+
// finds outside the allowed paths.
|
|
144
|
+
|
|
145
|
+
// Negative lookbehind `(?<!\.)` excludes version-number captures like
|
|
146
|
+
// "v3.5 module" or "v4.7 Commands" — these are version refs, not counts.
|
|
147
|
+
const COUNT_PATTERNS = [
|
|
148
|
+
// "52 commands", "21 agents", "30 modules", "2667 tests", etc.
|
|
149
|
+
// Word boundaries + allowed plurals; case-insensitive matching.
|
|
150
|
+
{ re: /(?<!\.)\b(\d+)\s+(commands?|agents?|modules?|workflows?|templates?|references?|specs?|adrs?|test\s+files?|test\s+suites?)\b/gi,
|
|
151
|
+
label: 'noun-phrase count' },
|
|
152
|
+
// "27th module", "21st agent", "52nd command" — drift-prone ordinals
|
|
153
|
+
{ re: /(?<!\.)\b(\d+)(th|st|nd|rd)\s+(module|reference|agent|command|template|hook|workflow|spec|adr)\b/gi,
|
|
154
|
+
label: 'ordinal' },
|
|
155
|
+
// "(9 files)", "(58 tests)" — parenthetical counts
|
|
156
|
+
{ re: /(?<!\.)\((\d+)\s+(files?|tests?)\)/gi, label: 'parenthetical count' },
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
// Files where counts ARE allowed:
|
|
160
|
+
// - CLAUDE.md (the SSoT)
|
|
161
|
+
// - CHANGELOG.md (frozen historical record)
|
|
162
|
+
// - MEMORY.md (user memory file, not shipped)
|
|
163
|
+
// - SKILLS-FULL-TEXT.md / SKILLS-REFERENCE.md (auto-generated; embed command
|
|
164
|
+
// prompt text that itself may legitimately reference numbers)
|
|
165
|
+
// - EXAMPLES.md (illustrative tool-output scenarios, not authoritative claims)
|
|
166
|
+
const COUNT_ALLOWED_RE = /(^|[\\/])(CLAUDE\.md|CHANGELOG\.md|MEMORY\.md|SKILLS-FULL-TEXT\.md|SKILLS-REFERENCE\.md|EXAMPLES\.md)$/i;
|
|
167
|
+
|
|
168
|
+
// Path SEGMENTS that mark a directory as count-allowed (frozen historical
|
|
169
|
+
// content). Matched as path segments so they catch both project-root-relative
|
|
170
|
+
// paths (e.g. "docs/decisions/X.md") and scan-root-relative paths (e.g.
|
|
171
|
+
// "decisions/X.md" when the scan rooted at docs/).
|
|
172
|
+
const COUNT_ALLOWED_DIR_SEGMENTS = [
|
|
173
|
+
'decisions', // ADRs — frozen
|
|
174
|
+
'specs', // feature specs — frozen
|
|
175
|
+
'experiments', // harvested experiment artifacts
|
|
176
|
+
'learnings', // AI-derived patterns; evidence quotes reference numbers
|
|
177
|
+
'archive', // archived old docs
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
function isCountAllowed(relativePath) {
|
|
181
|
+
if (COUNT_ALLOWED_RE.test(relativePath)) return true;
|
|
182
|
+
const norm = relativePath.replace(/\\/g, '/');
|
|
183
|
+
const segments = norm.split('/');
|
|
184
|
+
return segments.some(seg => COUNT_ALLOWED_DIR_SEGMENTS.includes(seg));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Things that LOOK like counts but are stable identities (allowed everywhere):
|
|
188
|
+
const STABLE_IDENTITIES = [
|
|
189
|
+
/\b5\s+(target\s+)?runtimes\b/i, // 5 target runtimes
|
|
190
|
+
/\b5\s+hooks\b/i, // 5 hooks (named individually)
|
|
191
|
+
/\bLAYER\s+\d+\b/, // architecture layer labels
|
|
192
|
+
/\b5\s+(parallel\s+)?(researchers?|research\s+)/i, // 5 parallel researchers
|
|
193
|
+
/\b6\s+(parallel\s+)?agents\b/i, // 6 parallel agents (codebase mapper)
|
|
194
|
+
/\b6\s+focus\s+areas\b/i, // 6 focus areas (mapper)
|
|
195
|
+
/\b4\s+parallel\s+research/i, // 4 parallel research
|
|
196
|
+
/\bthree\s+phases\b|\bfour\s+phases\b/i, // generic phase counts in narrative
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
function isStableIdentity(matchText, surrounding) {
|
|
200
|
+
for (const re of STABLE_IDENTITIES) {
|
|
201
|
+
if (re.test(matchText) || re.test(surrounding)) return true;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Scan a directory tree for drift-prone count violations.
|
|
208
|
+
* @param {string} cwd - working directory
|
|
209
|
+
* @param {string} dir - dir to scan (relative or absolute)
|
|
210
|
+
* @param {object} opts - { format, raw, exclude }
|
|
211
|
+
*/
|
|
212
|
+
function cmdDocLintCounts(cwd, dir, opts = {}) {
|
|
213
|
+
const targetDir = path.isAbsolute(dir) ? dir : path.join(cwd, dir);
|
|
214
|
+
if (!fs.existsSync(targetDir)) {
|
|
215
|
+
error(`directory not found: ${targetDir}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const exclude = opts.exclude || [];
|
|
219
|
+
const files = walkMarkdownFiles(targetDir, { exclude });
|
|
220
|
+
const violations = [];
|
|
221
|
+
|
|
222
|
+
for (const file of files) {
|
|
223
|
+
if (isCountAllowed(file.relativePath)) continue;
|
|
224
|
+
if (file.readError) continue;
|
|
225
|
+
|
|
226
|
+
const lines = file.content.split(/\r?\n/);
|
|
227
|
+
let inFence = false; // track ```fenced``` code blocks; skip their content
|
|
228
|
+
for (let i = 0; i < lines.length; i++) {
|
|
229
|
+
const line = lines[i];
|
|
230
|
+
// Toggle fence on lines starting with ```
|
|
231
|
+
if (/^\s{0,3}```/.test(line)) {
|
|
232
|
+
inFence = !inFence;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (inFence) continue;
|
|
236
|
+
|
|
237
|
+
for (const { re, label } of COUNT_PATTERNS) {
|
|
238
|
+
re.lastIndex = 0; // reset for /g
|
|
239
|
+
let m;
|
|
240
|
+
while ((m = re.exec(line)) !== null) {
|
|
241
|
+
// Skip if this whole line is in a stable-identity surrounding
|
|
242
|
+
if (isStableIdentity(m[0], line)) continue;
|
|
243
|
+
violations.push({
|
|
244
|
+
file: file.relativePath,
|
|
245
|
+
line: i + 1,
|
|
246
|
+
column: m.index + 1,
|
|
247
|
+
match: m[0],
|
|
248
|
+
label,
|
|
249
|
+
severity: 'warning',
|
|
250
|
+
message: `Drift-prone count "${m[0]}" outside CLAUDE.md. Counts live only in CLAUDE.md; replace with qualitative phrasing or remove.`,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const fileCount = files.length;
|
|
258
|
+
const result = {
|
|
259
|
+
directory: dir,
|
|
260
|
+
file_count: fileCount,
|
|
261
|
+
violation_count: violations.length,
|
|
262
|
+
violations,
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
if (opts.raw) {
|
|
266
|
+
if (violations.length === 0) {
|
|
267
|
+
process.stdout.write(`OK — ${fileCount} files scanned, no count violations\n`);
|
|
268
|
+
} else {
|
|
269
|
+
for (const v of violations) {
|
|
270
|
+
process.stdout.write(`${v.file}:${v.line}:${v.column} — ${v.match} (${v.label})\n`);
|
|
271
|
+
}
|
|
272
|
+
process.stdout.write(`\n${violations.length} violation(s) across ${fileCount} files\n`);
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
output(result, false);
|
|
276
|
+
}
|
|
277
|
+
process.exit(violations.length > 0 ? 1 : 0);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = {
|
|
281
|
+
cmdDocLint,
|
|
282
|
+
cmdDocLintSchemaCheck,
|
|
283
|
+
cmdDocLintCounts,
|
|
284
|
+
isCountAllowed,
|
|
285
|
+
COUNT_PATTERNS,
|
|
286
|
+
DEFAULT_SCHEMA_PATH,
|
|
287
|
+
};
|