pan-wizard 3.15.0 → 3.17.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 +2 -2
- package/commands/pan/army.md +4 -0
- package/commands/pan/focus-doc-audit.md +1 -1
- package/commands/pan/report.md +4 -3
- package/package.json +1 -1
- package/pan-wizard-core/bin/lib/config.cjs +2 -0
- package/pan-wizard-core/bin/lib/focus.cjs +37 -0
- package/pan-wizard-core/bin/lib/phase-report.cjs +84 -22
- package/pan-wizard-core/bin/pan-tools.cjs +1 -0
- package/pan-wizard-core/templates/config.json +2 -1
- package/pan-wizard-core/workflows/exec-phase.md +17 -1
package/README.md
CHANGED
|
@@ -553,7 +553,7 @@ You're never locked in. The system adapts.
|
|
|
553
553
|
|
|
554
554
|
PAN is not a replacement for your IDE or AI agent — it's the orchestration layer that handles everything *around* them: breaking work into context-safe chunks, researching before planning, verifying after execution, and maintaining state across sessions.
|
|
555
555
|
|
|
556
|
-
> For the full comparison across
|
|
556
|
+
> For the full comparison across the major AI coding tools and many dimensions, see [COMPARISON.md](docs/COMPARISON.md).
|
|
557
557
|
|
|
558
558
|
---
|
|
559
559
|
|
|
@@ -834,7 +834,7 @@ This removes all PAN commands, agents, hooks, and settings while preserving your
|
|
|
834
834
|
## Documentation
|
|
835
835
|
|
|
836
836
|
<div align="center">
|
|
837
|
-
<img src="https://cdn.jsdelivr.net/npm/pan-wizard@latest/assets/pan-
|
|
837
|
+
<img src="https://cdn.jsdelivr.net/npm/pan-wizard@latest/assets/pan-developer.png" alt="A developer reading the PAN Wizard documentation" width="600" />
|
|
838
838
|
</div>
|
|
839
839
|
|
|
840
840
|
| Document | Audience | What it covers |
|
package/commands/pan/army.md
CHANGED
|
@@ -125,9 +125,13 @@ Spawn the Quality squad on the built tree (parallel, read-only). Merge findings
|
|
|
125
125
|
### Phase 5 — Integrate (Release, human-gated)
|
|
126
126
|
Spawn `pan-release`. It prepares the squash-merge, runs the configured `verification`, and surfaces an **always-ask** approval request. A human approves the merge to the protected branch; release then tags and records the rollback target. `--push` pushes the approved result.
|
|
127
127
|
|
|
128
|
+
**Phase report (opt-in build deliverable):** when `workflow.phase_reports.enabled` is `true`, generate the mission's self-contained per-phase HTML report **in the built tree, before staging the squash-merge** — `pan-tools report phase <N>` — so the report rides along in the merge as a phase deliverable. **Never run `report index` inside a squad worktree:** the timeline index is a single shared file that aggregates *all* phases, so a worktree would see only its own phase and concurrent squads would race on it. The index is a single-writer, post-merge concern (Phase 6). Never opens a browser.
|
|
129
|
+
|
|
128
130
|
### Phase 6 — Learn (Dreaming)
|
|
129
131
|
Squad summaries return to Mission Control. Run `/pan:retro --write-memory` (and `/pan:learn` if traces exist) so recurring patterns persist into agent memory for the next mission. Strike the landed item; update loop-state. For a scheduled campaign, also `pan-tools campaign record-run --items <n> --points <p>` so the next-due time and the day's spend advance.
|
|
130
132
|
|
|
133
|
+
**Rebuild the timeline index (single writer).** When `workflow.phase_reports.enabled` and `workflow.phase_reports.index` are `true`, Mission Control — and *only* Mission Control, on the integration branch after the merge has landed — rebuilds the project index once against the now-merged set of phases: `pan-tools report index`, then commit it (the commit honors `commit_docs`). Doing this post-merge from the single conductor is what keeps `report-index.html` consistent while builds run in parallel worktrees.
|
|
134
|
+
|
|
131
135
|
---
|
|
132
136
|
|
|
133
137
|
## Scheduled, self-resuming campaigns (ADR-0034)
|
|
@@ -156,7 +156,7 @@ For each target document, extract every verifiable claim.
|
|
|
156
156
|
| Category | Pattern | Example |
|
|
157
157
|
|----------|---------|---------|
|
|
158
158
|
| **Version claim** | `v1.2.3`, `version 1.2`, `Version: X` | "Currently at v3.1.0" |
|
|
159
|
-
| **Count claim** | Number + noun | "42
|
|
159
|
+
| **Count claim** | Number + noun | "42 database tables", "15 API endpoints" |
|
|
160
160
|
| **File path claim** | Path-like strings | "`src/core/handler.ts`" |
|
|
161
161
|
| **Command claim** | Code block with shell prefix or instruction | "`npm run build`" |
|
|
162
162
|
| **Feature claim** | "supports X", "includes Y", "provides Z" | "Supports hot reloading" |
|
package/commands/pan/report.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: pan:report
|
|
3
3
|
group: Observability
|
|
4
4
|
description: Generate a self-contained HTML report for one phase, or a project-level timeline index linking every phase report
|
|
5
|
-
argument-hint: "phase <N> | index | all [--out <file>] [--open] [--stdout]"
|
|
5
|
+
argument-hint: "phase <N> | index [--bundle] | all [--out <file>] [--open] [--stdout]"
|
|
6
6
|
allowed-tools:
|
|
7
7
|
- Read
|
|
8
8
|
- Bash
|
|
@@ -24,7 +24,7 @@ Reports are honest by construction: the `verify reconcile` verdict is shown besi
|
|
|
24
24
|
|
|
25
25
|
```
|
|
26
26
|
pan-tools report phase <N> [--out <file>] [--open] [--stdout]
|
|
27
|
-
pan-tools report index [--out <file>] [--open] [--stdout]
|
|
27
|
+
pan-tools report index [--out <file>] [--open] [--stdout] [--bundle]
|
|
28
28
|
pan-tools report all [--open]
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -37,6 +37,7 @@ pan-tools report all [--open]
|
|
|
37
37
|
- `--out <file>` — write to a custom path instead of the default (relative paths resolve against the project root).
|
|
38
38
|
- `--open` — best-effort: launch the written file in the default browser (cross-platform; silently no-ops if no opener is available, and never opens when nothing was written).
|
|
39
39
|
- `--stdout` — print the HTML to stdout instead of writing a file (for `phase`/`index`).
|
|
40
|
+
- `--bundle` (on `index`) — emit one self-contained `.planning/report-bundle.html`: the timeline plus every phase report inlined under in-page anchors, with no links to sibling files. This is the form to email or attach — a single file that stays whole when moved off-disk.
|
|
40
41
|
|
|
41
42
|
**Behaviour worth knowing:**
|
|
42
43
|
- **Deterministic writes.** Re-running with unchanged phase data rewrites nothing (the only volatile value, the generated-at timestamp, is ignored when comparing) — so reports produce no git churn.
|
|
@@ -61,7 +62,7 @@ pan-tools report all [--open]
|
|
|
61
62
|
|
|
62
63
|
**Review a phase:** run `pan-tools report phase <N> --open` to see one phase's objective, what changed, and its verification verdict at a glance.
|
|
63
64
|
|
|
64
|
-
**Share the project:** `pan-tools report index` builds the timeline entry point — send `.planning/report-index.html` and the linked phase files, or open the index and click through.
|
|
65
|
+
**Share the project:** `pan-tools report index` builds the timeline entry point — send `.planning/report-index.html` and the linked phase files, or open the index and click through. To hand someone a single file instead, `pan-tools report index --bundle` inlines every phase into one `.planning/report-bundle.html` that survives being emailed or moved.
|
|
65
66
|
|
|
66
67
|
**Refresh everything:** `pan-tools report all` after a milestone regenerates every report; unchanged ones are skipped, so only what actually moved is rewritten.
|
|
67
68
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pan-wizard",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.17.0",
|
|
4
4
|
"description": "Command a bot army for your codebase: an Opus Mission Control delegates whole-project goals to specialist squads and ships behind a human merge gate. Five AI CLIs, zero context rot.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"pan-wizard": "bin/install.js"
|
|
@@ -53,6 +53,8 @@ function buildConfigDefaults(hasBraveSearch, userDefaults) {
|
|
|
53
53
|
verifier: true,
|
|
54
54
|
nyquist_validation: false,
|
|
55
55
|
phase_record_compact: false,
|
|
56
|
+
// M2: opt-in per-phase HTML reports at the verify→complete gate.
|
|
57
|
+
phase_reports: { enabled: false, open: false, theme: 'auto', index: true },
|
|
56
58
|
},
|
|
57
59
|
parallelization: true,
|
|
58
60
|
brave_search: hasBraveSearch,
|
|
@@ -809,11 +809,48 @@ function focusAutoUpdate(cwd, raw, getVal) {
|
|
|
809
809
|
}, raw);
|
|
810
810
|
}
|
|
811
811
|
|
|
812
|
+
/**
|
|
813
|
+
* Whether opt-in HTML phase reports are enabled for this project.
|
|
814
|
+
* Reads the raw config.json (loadConfig doesn't surface the workflow block);
|
|
815
|
+
* absent / malformed config → disabled.
|
|
816
|
+
*/
|
|
817
|
+
function phaseReportsEnabled(cwd) {
|
|
818
|
+
try {
|
|
819
|
+
const raw = JSON.parse(fs.readFileSync(path.join(planningPath(cwd), 'config.json'), 'utf-8'));
|
|
820
|
+
return !!(raw.workflow && raw.workflow.phase_reports && raw.workflow.phase_reports.enabled);
|
|
821
|
+
} catch {
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Regenerate every phase report + index as part of a focus-auto checkpoint,
|
|
828
|
+
* when the feature is enabled. Best-effort and side-effect-light: never opens a
|
|
829
|
+
* browser, and silently no-ops for phase-less / focus-auto-only projects. The
|
|
830
|
+
* fresh HTML lands in .planning/ so the checkpoint's `git add` sweeps it in.
|
|
831
|
+
*/
|
|
832
|
+
function maybeRenderPhaseReports(cwd) {
|
|
833
|
+
if (!phaseReportsEnabled(cwd)) return;
|
|
834
|
+
try {
|
|
835
|
+
const { renderAllToDisk } = require('./phase-report.cjs');
|
|
836
|
+
renderAllToDisk(cwd, {});
|
|
837
|
+
} catch {
|
|
838
|
+
// Report generation must never block the checkpoint commit.
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
812
842
|
function focusAutoCheckpointCommit(cwd, cycle, run) {
|
|
813
843
|
if (!isGitRepo(cwd)) return null;
|
|
814
844
|
const config = loadConfig(cwd);
|
|
815
845
|
const autoCommit = config.focus && config.focus.auto_commit !== undefined ? config.focus.auto_commit : true;
|
|
816
846
|
if (!autoCommit) return null;
|
|
847
|
+
// commit_docs is the global switch for committing planning artifacts; focus-auto
|
|
848
|
+
// is a planning-doc committer, so honor it. commit_docs=false → hands the .planning
|
|
849
|
+
// commit (and any report regeneration) back to the user.
|
|
850
|
+
if (config.commit_docs === false) return null;
|
|
851
|
+
// Enabled projects: refresh the HTML reports before staging so the committed
|
|
852
|
+
// .planning/ snapshot reflects this cycle.
|
|
853
|
+
maybeRenderPhaseReports(cwd);
|
|
817
854
|
const status = execGit(cwd, ['status', '--porcelain', PLANNING_DIR + '/']);
|
|
818
855
|
if (status.exitCode !== 0 || !status.stdout) return null;
|
|
819
856
|
execGit(cwd, ['add', PLANNING_DIR + '/']);
|
|
@@ -44,6 +44,7 @@ const cost = require('./cost.cjs');
|
|
|
44
44
|
|
|
45
45
|
const REPORT_SUFFIX = '-report.html';
|
|
46
46
|
const INDEX_FILE = 'report-index.html';
|
|
47
|
+
const BUNDLE_FILE = 'report-bundle.html';
|
|
47
48
|
|
|
48
49
|
// ─── small helpers ─────────────────────────────────────────────────────────────
|
|
49
50
|
|
|
@@ -548,7 +549,8 @@ function indexHero(d) {
|
|
|
548
549
|
</section>`;
|
|
549
550
|
}
|
|
550
551
|
|
|
551
|
-
function indexTimeline(d) {
|
|
552
|
+
function indexTimeline(d, hrefFn) {
|
|
553
|
+
const href = hrefFn || ((p) => p.href);
|
|
552
554
|
const rows = d.phases.map((p, i) => {
|
|
553
555
|
const color = STATUS_DOT[p.status] || '#C9C0AE';
|
|
554
556
|
const isLast = i === d.phases.length - 1;
|
|
@@ -560,7 +562,7 @@ function indexTimeline(d) {
|
|
|
560
562
|
return `
|
|
561
563
|
<div class="trow">
|
|
562
564
|
<div class="rail"><span class="rd" style="background:${color}"></span>${isLast ? '' : `<span class="rl" style="background:${railColor}"></span>`}</div>
|
|
563
|
-
<a class="tcard" href="${esc(p
|
|
565
|
+
<a class="tcard" href="${esc(href(p))}">
|
|
564
566
|
<div>
|
|
565
567
|
<div class="tt">${esc(p.number)}${p.name ? ' · ' + esc(p.name) : ''}</div>
|
|
566
568
|
<div class="tsub"><span class="mini">${mini}</span><span>${p.plansTotal ? p.plansDone + ' / ' + p.plansTotal + ' plans' : 'no plans'}</span>${p.has_report ? '' : '<span>· report pending</span>'}</div>
|
|
@@ -577,6 +579,33 @@ function renderIndexHtml(d) {
|
|
|
577
579
|
return docShell(`PanWizard · ${d.project} — timeline`, body, d.generated_at);
|
|
578
580
|
}
|
|
579
581
|
|
|
582
|
+
/**
|
|
583
|
+
* Bundle: the timeline index plus EVERY phase report inlined into one
|
|
584
|
+
* self-contained document, cross-linked by in-page #phase-NN anchors. Because
|
|
585
|
+
* navigation is anchors (not sibling files), the links never dangle when the
|
|
586
|
+
* single file is emailed or copied on its own. (indexData, phaseDatas) are pure.
|
|
587
|
+
*/
|
|
588
|
+
function renderBundleHtml(indexData, phaseDatas) {
|
|
589
|
+
const anchorHref = (p) => '#phase-' + encodeURIComponent(String(p.number));
|
|
590
|
+
const sections = (phaseDatas || []).filter(Boolean).map(d => `
|
|
591
|
+
<section id="phase-${esc(d.phase.number)}" style="margin-top:26px;border-top:1px solid var(--border);padding-top:8px">
|
|
592
|
+
<div class="crumbs"><a href="#top">↑ back to timeline</a></div>
|
|
593
|
+
${reportHero(d)}
|
|
594
|
+
${reportMetrics(d)}
|
|
595
|
+
<div class="grid"><div class="gcol">${reportObjective(d)}</div><div class="gcol">${reportVerification(d)}</div></div>
|
|
596
|
+
${reportChanges(d)}
|
|
597
|
+
${reportGaps(d)}
|
|
598
|
+
</section>`).join('\n');
|
|
599
|
+
const body = [
|
|
600
|
+
'<span id="top"></span>',
|
|
601
|
+
indexTopBar(indexData),
|
|
602
|
+
indexHero(indexData),
|
|
603
|
+
indexTimeline(indexData, anchorHref),
|
|
604
|
+
sections,
|
|
605
|
+
].filter(Boolean).join('\n');
|
|
606
|
+
return docShell(`PanWizard · ${indexData.project} — full report`, body, indexData.generated_at);
|
|
607
|
+
}
|
|
608
|
+
|
|
580
609
|
// ─── side effects (the only impure layer) ────────────────────────────────────────
|
|
581
610
|
|
|
582
611
|
/**
|
|
@@ -667,6 +696,19 @@ function cmdReport(cwd, opts = {}, raw) {
|
|
|
667
696
|
if (action === 'index') {
|
|
668
697
|
const data = collectIndexData(cwd, { now });
|
|
669
698
|
if (!data) return error('No phases found — nothing to index (phase-less / focus-auto project). Use `pan-tools hud` instead.');
|
|
699
|
+
if (opts.bundle) {
|
|
700
|
+
const phaseDatas = data.phases.map(p => collectPhaseData(cwd, p.number, { now })).filter(Boolean);
|
|
701
|
+
const html = renderBundleHtml(data, phaseDatas);
|
|
702
|
+
if (opts.stdout) { process.stdout.write(html); return; }
|
|
703
|
+
const outPath = opts.out ? path.resolve(cwd, opts.out) : path.join(planningPath(cwd), BUNDLE_FILE);
|
|
704
|
+
const res = writeIfChanged(outPath, html);
|
|
705
|
+
const opened = opts.open && res.written ? openInBrowser(outPath) : false;
|
|
706
|
+
return output(
|
|
707
|
+
{ action: 'bundle', phases: phaseDatas.length, path: toPosix(outPath), bytes: Buffer.byteLength(html), written: res.written, opened },
|
|
708
|
+
raw,
|
|
709
|
+
`bundle ${res.written ? 'written' : 'unchanged'}: ${toPosix(outPath)}`,
|
|
710
|
+
);
|
|
711
|
+
}
|
|
670
712
|
const html = renderIndexHtml(data);
|
|
671
713
|
if (opts.stdout) { process.stdout.write(html); return; }
|
|
672
714
|
const outPath = opts.out ? path.resolve(cwd, opts.out) : path.join(planningPath(cwd), INDEX_FILE);
|
|
@@ -680,42 +722,62 @@ function cmdReport(cwd, opts = {}, raw) {
|
|
|
680
722
|
}
|
|
681
723
|
|
|
682
724
|
if (action === 'all') {
|
|
683
|
-
const
|
|
684
|
-
if (!
|
|
685
|
-
const reports = [];
|
|
686
|
-
for (const dirName of dirs) {
|
|
687
|
-
const { number } = parsePhaseDir(dirName);
|
|
688
|
-
const data = collectPhaseData(cwd, number, { now });
|
|
689
|
-
if (!data) continue;
|
|
690
|
-
const outPath = path.join(data.phase.dir, `${data.phase.number}${REPORT_SUFFIX}`);
|
|
691
|
-
const res = writeIfChanged(outPath, renderPhaseHtml(data));
|
|
692
|
-
reports.push({ phase: data.phase.number, path: toPosix(outPath), written: res.written });
|
|
693
|
-
}
|
|
694
|
-
const idx = collectIndexData(cwd, { now });
|
|
695
|
-
let index = { path: null, written: false };
|
|
696
|
-
if (idx) {
|
|
697
|
-
const outPath = path.join(planningPath(cwd), INDEX_FILE);
|
|
698
|
-
const res = writeIfChanged(outPath, renderIndexHtml(idx));
|
|
699
|
-
index = { path: toPosix(outPath), written: res.written };
|
|
700
|
-
}
|
|
725
|
+
const res = renderAllToDisk(cwd, { now });
|
|
726
|
+
if (!res) return error('No phases found — nothing to report.');
|
|
701
727
|
return output(
|
|
702
|
-
{ action, reports, index },
|
|
728
|
+
{ action, reports: res.reports, index: res.index },
|
|
703
729
|
raw,
|
|
704
|
-
`generated ${reports.length} phase report(s)${index.path ? ' + index' : ''}`,
|
|
730
|
+
`generated ${res.reports.length} phase report(s)${res.index.path ? ' + index' : ''}`,
|
|
705
731
|
);
|
|
706
732
|
}
|
|
707
733
|
|
|
708
734
|
return error('Unknown report action. Available: phase <N>, index, all');
|
|
709
735
|
}
|
|
710
736
|
|
|
737
|
+
/**
|
|
738
|
+
* Regenerate every phase report + the timeline index straight to disk.
|
|
739
|
+
* Programmatic sibling of `cmdReport('all')` with NO stdout side effects and
|
|
740
|
+
* no browser opening — used by focus-auto / army in-tree checkpoints, which
|
|
741
|
+
* emit their own JSON and must not have report output interleaved into it.
|
|
742
|
+
* @param {string} cwd
|
|
743
|
+
* @param {{now?:Date}} opts
|
|
744
|
+
* @returns {{reports:Array<{phase:string,path:string,written:boolean}>, index:{path:string|null,written:boolean}}|null}
|
|
745
|
+
* null when the project has no phases (phase-less / focus-auto-only).
|
|
746
|
+
*/
|
|
747
|
+
function renderAllToDisk(cwd, opts = {}) {
|
|
748
|
+
const now = opts.now;
|
|
749
|
+
const dirs = listPhaseDirs(cwd);
|
|
750
|
+
if (!dirs.length) return null;
|
|
751
|
+
const reports = [];
|
|
752
|
+
for (const dirName of dirs) {
|
|
753
|
+
const { number } = parsePhaseDir(dirName);
|
|
754
|
+
const data = collectPhaseData(cwd, number, { now });
|
|
755
|
+
if (!data) continue;
|
|
756
|
+
const outPath = path.join(data.phase.dir, `${data.phase.number}${REPORT_SUFFIX}`);
|
|
757
|
+
const res = writeIfChanged(outPath, renderPhaseHtml(data));
|
|
758
|
+
reports.push({ phase: data.phase.number, path: toPosix(outPath), written: res.written });
|
|
759
|
+
}
|
|
760
|
+
const idx = collectIndexData(cwd, { now });
|
|
761
|
+
let index = { path: null, written: false };
|
|
762
|
+
if (idx) {
|
|
763
|
+
const outPath = path.join(planningPath(cwd), INDEX_FILE);
|
|
764
|
+
const res = writeIfChanged(outPath, renderIndexHtml(idx));
|
|
765
|
+
index = { path: toPosix(outPath), written: res.written };
|
|
766
|
+
}
|
|
767
|
+
return { reports, index };
|
|
768
|
+
}
|
|
769
|
+
|
|
711
770
|
module.exports = {
|
|
712
771
|
REPORT_SUFFIX,
|
|
713
772
|
INDEX_FILE,
|
|
773
|
+
BUNDLE_FILE,
|
|
714
774
|
collectPhaseData,
|
|
715
775
|
collectIndexData,
|
|
716
776
|
renderPhaseHtml,
|
|
717
777
|
renderIndexHtml,
|
|
778
|
+
renderBundleHtml,
|
|
718
779
|
cmdReport,
|
|
780
|
+
renderAllToDisk,
|
|
719
781
|
// exported for focused unit tests
|
|
720
782
|
resolvePhase,
|
|
721
783
|
extractPhaseGoal,
|
|
@@ -890,6 +890,7 @@ async function main() {
|
|
|
890
890
|
out: getArgValue(args, '--out'),
|
|
891
891
|
open: args.includes('--open'),
|
|
892
892
|
stdout: args.includes('--stdout'),
|
|
893
|
+
bundle: args.includes('--bundle'),
|
|
893
894
|
}, raw);
|
|
894
895
|
} else if (subcommand === 'all') {
|
|
895
896
|
phaseReport.cmdReport(cwd, { action: 'all', open: args.includes('--open') }, raw);
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
"verifier": true,
|
|
8
8
|
"auto_advance": false,
|
|
9
9
|
"nyquist_validation": false,
|
|
10
|
-
"phase_record_compact": false
|
|
10
|
+
"phase_record_compact": false,
|
|
11
|
+
"phase_reports": { "enabled": false, "open": false, "theme": "auto", "index": true }
|
|
11
12
|
},
|
|
12
13
|
"planning": {
|
|
13
14
|
"commit_docs": true,
|
|
@@ -602,8 +602,24 @@ The CLI handles:
|
|
|
602
602
|
|
|
603
603
|
Extract from result: `next_phase`, `next_phase_name`, `is_last_phase`.
|
|
604
604
|
|
|
605
|
+
**Phase reports (opt-in build deliverable):** when `workflow.phase_reports.enabled` is `true`, generate the self-contained per-phase HTML report — and, when `workflow.phase_reports.index` is `true`, the project timeline index — at this verify→complete gate so they ship with the phase and ride the commit below. Disabled by default; phase-less projects are skipped automatically by `report`. Never opens a browser here (that's reserved for a manual `pan-tools report --open`).
|
|
606
|
+
|
|
607
|
+
```bash
|
|
608
|
+
REPORT_FILES=""
|
|
609
|
+
if [ "$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs config-get workflow.phase_reports.enabled 2>/dev/null || echo false)" = "true" ]; then
|
|
610
|
+
if node ~/.claude/pan-wizard-core/bin/pan-tools.cjs report phase "${PHASE_NUMBER}" >/dev/null 2>&1; then
|
|
611
|
+
REPORT_FILES="{phase_dir}/*-report.html"
|
|
612
|
+
if [ "$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs config-get workflow.phase_reports.index 2>/dev/null || echo true)" = "true" ]; then
|
|
613
|
+
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs report index >/dev/null 2>&1 && REPORT_FILES="$REPORT_FILES .planning/report-index.html"
|
|
614
|
+
fi
|
|
615
|
+
fi
|
|
616
|
+
fi
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
The report files are appended to the completion commit's `--files` list (`$REPORT_FILES`, empty when disabled), so `cmdCommit` still honors `commit_docs` + gitignore + safety checks with no new commit logic:
|
|
620
|
+
|
|
605
621
|
```bash
|
|
606
|
-
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs commit "docs(phase-{X}): complete phase execution" --files .planning/roadmap.md .planning/state.md .planning/requirements.md {phase_dir}/*-verification.md
|
|
622
|
+
node ~/.claude/pan-wizard-core/bin/pan-tools.cjs commit "docs(phase-{X}): complete phase execution" --files .planning/roadmap.md .planning/state.md .planning/requirements.md {phase_dir}/*-verification.md $REPORT_FILES
|
|
607
623
|
```
|
|
608
624
|
|
|
609
625
|
**Circular optimization — finalize trace session:**
|