pi-goal-x 0.25.1 → 0.25.2

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 CHANGED
@@ -249,17 +249,15 @@ Verify that the generated report contains every required section.
249
249
  Always visible above the editor while a goal is focused:
250
250
 
251
251
  ```text
252
- ╭─ pi-goal-x ─ Add CSV export to reports ─────── 12m47s · 18.2K tok ─╮
253
- │ ● In progress · Focused: yes · Other goals: 2 │
254
- │ Tasks [██████░░░░] 3/5 · 60% │
255
- ├─ Tasks ────────────────────────────────────────────────────────────┤
252
+ ╭─ pi-goal-x ─ Add CSV export to reports ────────────────────────────╮
253
+ │ goal: running [12m47s 18.2K] (+2 open) │
254
+ ├─ Tasks · ✓3 done · 2 open ──────────── [█████░░░] · Sub 2/3 [██░░] ┤
256
255
  │ ✓ t1 Review reports page and data source │
257
256
  │ ✓ t2 Implement filtered CSV export │
258
- │ ▸ t3 Add the download button │
257
+ │ ▸ t3 Add the download button ☑ ▸ 2/3 │
259
258
  │ · t4 Add documentation │
260
259
  │ … +1 more task │
261
260
  │ Current t3 · Add the download button │
262
- │ Subtasks [███████░░░] 2/3 · 67% │
263
261
  │ Verify Run npm test with zero failures. │
264
262
  │ File .pi/goals/active_goal_... │
265
263
  ╰─ Ctrl+Shift+T: expand tasks ───────────────────────────────────────╯
@@ -0,0 +1 @@
1
+ {"version":1,"dirMtimeMs":1786087947377.8738,"goals":[]}
@@ -10,13 +10,12 @@
10
10
  },
11
11
  "sisyphus": false,
12
12
  "createdAt": "2026-08-06T11:41:19.497Z",
13
- "updatedAt": "2026-08-06T12:54:33.984Z",
14
- "activePath": ".pi/goals/active_goal_2026080612411949_mshg3jex-svqfyt.md",
15
- "stopReason": "agent",
13
+ "updatedAt": "2026-08-06T13:26:28.723Z",
14
+ "stopReason": "user",
16
15
  "pauseReason": "All deliverables verified and committed; the goal cannot complete without the user's signoff (criterion 7) and their acceptance of the documented B4 content-floor exemption (recommendation (a)). Presenting for review in the terminal.",
17
16
  "pauseSuggestedAction": "Open specs/2026-08-06-non-agent-flow-optimization/BENCH-DIFF.md + HEADROOM.md and sign off: confirm the B4 content-floor exemption (or run /goal-tweak to relax targets / authorize guidance removal instead). Then tell me to complete the goal.",
18
17
  "skipAuditor": true,
19
- "revision": 162,
18
+ "revision": 163,
20
19
  "taskList": {
21
20
  "tasks": [
22
21
  {
@@ -50,7 +49,8 @@
50
49
  "proposedAt": "2026-08-06T11:38:58.636Z"
51
50
  },
52
51
  "currentTaskId": "task-4",
53
- "verificationContract": "run `npm run bench` (fresh BEFORE committed, then AFTER), `npm run bench:gate` PASS (including the new ≥10x invariant), `npm test` 0 failures, `npm run check` clean, re-read this goal and confirm every success criterion is met, then user signoff in a real terminal."
52
+ "verificationContract": "run `npm run bench` (fresh BEFORE committed, then AFTER), `npm run bench:gate` PASS (including the new ≥10x invariant), `npm test` 0 failures, `npm run check` clean, re-read this goal and confirm every success criterion is met, then user signoff in a real terminal.",
53
+ "archivedPath": ".pi/goals/archived/goal_2026080613543398_mshg3jex-svqfyt.md"
54
54
  }
55
55
 
56
56
  # Goal Prompt
@@ -68,7 +68,7 @@ If blocked: stop and ask the user.
68
68
 
69
69
  ## Progress
70
70
 
71
- - Status: paused (agent)
71
+ - Status: paused
72
72
  - Auto-continue: off
73
73
  - Sisyphus mode: no
74
74
  - Time spent: 36m29s
@@ -10,11 +10,10 @@
10
10
  },
11
11
  "sisyphus": false,
12
12
  "createdAt": "2026-08-06T12:56:45.405Z",
13
- "updatedAt": "2026-08-06T13:05:26.542Z",
14
- "activePath": ".pi/goals/active_goal_2026080613564540_mshisjml-bl1gar.md",
13
+ "updatedAt": "2026-08-06T13:26:32.828Z",
15
14
  "stopReason": "user",
16
15
  "skipAuditor": true,
17
- "revision": 80,
16
+ "revision": 81,
18
17
  "taskList": {
19
18
  "tasks": [
20
19
  {
@@ -56,7 +55,8 @@
56
55
  "blockCompletion": true,
57
56
  "proposedAt": "2026-08-06T12:56:41.516Z"
58
57
  },
59
- "currentTaskId": "task-6"
58
+ "currentTaskId": "task-6",
59
+ "archivedPath": ".pi/goals/archived/goal_2026080614052654_mshisjml-bl1gar.md"
60
60
  }
61
61
 
62
62
  # Goal Prompt
@@ -0,0 +1,80 @@
1
+ {
2
+ "version": 3,
3
+ "id": "mshk5uvt-nnkztu",
4
+ "objective": "Move goal status information from the bottom of the pi screen into the goal widget: in both the compact and expanded dashboard status lines, replace the \"Focused: yes / Other goals: N\" bits with `goal: <statusLabel> [<elapsed> <tokens>] (+N open)` using the footer-status formatting (statusLabel + usage bracket, no objective preview — the header title already shows it, and the header's right-side usage folds away to avoid duplication), and stop rendering the focused-goal footer line (setStatus(\"goal\", undefined) when a goal is focused) while keeping the unfocused hint — with updated tests/goldens, spec docs, CHANGELOG entry, and a green validation, on a new branch off main.",
5
+ "status": "complete",
6
+ "autoContinue": true,
7
+ "usage": {
8
+ "tokensUsed": 120532,
9
+ "activeSeconds": 517
10
+ },
11
+ "sisyphus": false,
12
+ "skipAuditor": true,
13
+ "revision": 64,
14
+ "createdAt": "2026-08-06T13:35:06.137Z",
15
+ "updatedAt": "2026-08-06T13:44:18.147Z",
16
+ "taskList": {
17
+ "tasks": [
18
+ {
19
+ "id": "task-1",
20
+ "title": "New branch off main (e.g. feat/goal-widget-status) + scaffold specs/YYYY-MM-DD-goal-widget-status/ (PRODUCT.md, TECH.md, MILESTONES.md)",
21
+ "status": "complete",
22
+ "completedAt": "2026-08-06T13:35:37.864Z",
23
+ "evidence": "Branch feat/goal-widget-status created off main (15aa826); specs/2026-08-06-goal-widget-status/ scaffolded with PRODUCT.md, TECH.md, MILESTONES.md (user decisions recorded)."
24
+ },
25
+ {
26
+ "id": "task-2",
27
+ "title": "Renderer change: compact + expanded dashboard status lines show `goal: <statusLabel> [<elapsed> <tokens>] (+N open)` (footer-status formatting via statusLabel + usage bits, no objective preview); remove the Focused:/Other goals: bits and the header's right-side usage (now redundant)",
28
+ "status": "complete",
29
+ "completedAt": "2026-08-06T13:40:01.161Z",
30
+ "evidence": "Renderer (compact + expanded) + model updated: status lines now render `goal: <footerLabel> [<footerBits>] (+N open)` via shared statusLine() helper; Focused:/Other goals: bits, STATUS_SYMBOL, and dea"
31
+ },
32
+ {
33
+ "id": "task-3",
34
+ "title": "Footer removal: goal-state.ts renderUI stops calling setStatus('goal', ...) when a goal is focused (widget is the single home); keep the unfocused `goal: unfocused [N open]` hint",
35
+ "status": "complete",
36
+ "completedAt": "2026-08-06T13:40:31.928Z",
37
+ "evidence": "goal-state.ts renderUI: focused-goal branch now calls setStatus('goal', undefined) (clears stale unfocused hint); unfocused hint branch untouched; unused footerStatus import removed; tsc clean."
38
+ },
39
+ {
40
+ "id": "task-4",
41
+ "title": "Tests + goldens: update pinned assertions (In progress · Focused: yes, Other goals: 2) in goal-widget.test.ts, goal-dashboard-golden.test.ts, goal-status.test.ts; add new assertions pinning the `goal: <status> [<usage>] (+N open)` format in compact + expanded",
42
+ "status": "complete",
43
+ "completedAt": "2026-08-06T13:43:06.406Z",
44
+ "evidence": "Updated pinned assertions in goal-widget.test.ts (7), goal-dashboard-golden.test.ts (6 incl. new expanded status-line pin), goal-status.test.ts (1), goal-dashboard-model.test.ts (2: footerBits usage +"
45
+ },
46
+ {
47
+ "id": "task-5",
48
+ "title": "Docs + validation + commit: CHANGELOG Unreleased entry, PRODUCT/TECH/MILESTONES per AGENTS.md, npm run check clean, npm test 0 failures, grep confirms no Focused:/Other goals: text remains, commit on the new branch",
49
+ "status": "complete",
50
+ "completedAt": "2026-08-06T13:44:11.522Z",
51
+ "evidence": "CHANGELOG [Unreleased] entry added; PRODUCT/TECH/MILESTONES updated; npm run check clean; npm test 665/665 pass; grep confirms no functional Focused:/Other goals: remnants and no footerStatus(displayG"
52
+ }
53
+ ],
54
+ "blockCompletion": true,
55
+ "proposedAt": "2026-08-06T13:33:22.135Z"
56
+ },
57
+ "stopReason": "agent",
58
+ "archivedPath": ".pi/goals/archived/goal_2026080614441814_mshk5uvt-nnkztu.md"
59
+ }
60
+
61
+ # Goal Prompt
62
+
63
+ Move goal status information from the bottom of the pi screen into the goal widget: in both the compact and expanded dashboard status lines, replace the "Focused: yes / Other goals: N" bits with `goal: <statusLabel> [<elapsed> <tokens>] (+N open)` using the footer-status formatting (statusLabel + usage bracket, no objective preview — the header title already shows it, and the header's right-side usage folds away to avoid duplication), and stop rendering the focused-goal footer line (setStatus("goal", undefined) when a goal is focused) while keeping the unfocused hint — with updated tests/goldens, spec docs, CHANGELOG entry, and a green validation, on a new branch off main.
64
+
65
+ ## Progress
66
+
67
+ - Status: complete
68
+ - Auto-continue: on
69
+ - Sisyphus mode: no
70
+ - Time spent: 8m37s
71
+ - Tokens used: 121K (120,532) tokens
72
+ ## Tasks
73
+
74
+ <!-- blockCompletion: true -->
75
+ - [x] task-1: New branch off main (e.g. feat/goal-widget-status) + scaffold specs/YYYY-MM-DD-goal-widget-status/ (PRODUCT.md, TECH.md, MILESTONES.md) — evidence: Branch feat/goal-widget-status created off main (15aa826); specs/2026-08-06-goal-widget-status/ scaffolded with PRODUCT.md, TECH.md, MILESTONES.md (user decisions recorded).
76
+ - [x] task-2: Renderer change: compact + expanded dashboard status lines show `goal: <statusLabel> [<elapsed> <tokens>] (+N open)` (footer-status formatting via statusLabel + usage bits, no objective preview); remove the Focused:/Other goals: bits and the header's right-side usage (now redundant) — evidence: Renderer (compact + expanded) + model updated: status lines now render `goal: <footerLabel> [<footerBits>] (+N open)` via shared statusLine() helper; Focused:/Other goals: bits, STATUS_SYMBOL, and dea
77
+ - [x] task-3: Footer removal: goal-state.ts renderUI stops calling setStatus('goal', ...) when a goal is focused (widget is the single home); keep the unfocused `goal: unfocused [N open]` hint — evidence: goal-state.ts renderUI: focused-goal branch now calls setStatus('goal', undefined) (clears stale unfocused hint); unfocused hint branch untouched; unused footerStatus import removed; tsc clean.
78
+ - [x] task-4: Tests + goldens: update pinned assertions (In progress · Focused: yes, Other goals: 2) in goal-widget.test.ts, goal-dashboard-golden.test.ts, goal-status.test.ts; add new assertions pinning the `goal: <status> [<usage>] (+N open)` format in compact + expanded — evidence: Updated pinned assertions in goal-widget.test.ts (7), goal-dashboard-golden.test.ts (6 incl. new expanded status-line pin), goal-status.test.ts (1), goal-dashboard-model.test.ts (2: footerBits usage +
79
+ - [x] task-5: Docs + validation + commit: CHANGELOG Unreleased entry, PRODUCT/TECH/MILESTONES per AGENTS.md, npm run check clean, npm test 0 failures, grep confirms no Focused:/Other goals: text remains, commit on the new branch — evidence: CHANGELOG [Unreleased] entry added; PRODUCT/TECH/MILESTONES updated; npm run check clean; npm test 665/665 pass; grep confirms no functional Focused:/Other goals: remnants and no footerStatus(displayG
80
+
@@ -0,0 +1,76 @@
1
+ {
2
+ "version": 3,
3
+ "id": "msilpn44-qqa4fa",
4
+ "objective": "Add task counts and a progress bar to the compact goal dashboard's \"Tasks\" header row, and make subtask progress visible per task row: the header becomes a single row with `✓N done · M open` (skipped counted as done) followed by a compact progress bar at the end of the row, the redundant standalone compact `Tasks [bar] X/Y · %` line is removed (expanded dashboard keeps its Progress section unchanged), task rows with subtasks show a `▸ done/total` marker, and the current-task subtask bar is kept.",
5
+ "status": "complete",
6
+ "autoContinue": true,
7
+ "usage": {
8
+ "tokensUsed": 113743,
9
+ "activeSeconds": 421
10
+ },
11
+ "sisyphus": false,
12
+ "skipAuditor": true,
13
+ "revision": 69,
14
+ "createdAt": "2026-08-07T07:06:14.980Z",
15
+ "updatedAt": "2026-08-07T07:14:04.254Z",
16
+ "taskList": {
17
+ "tasks": [
18
+ {
19
+ "id": "spec-docs",
20
+ "title": "Write spec docs (PRODUCT.md, TECH.md, MILESTONES.md) under specs/2026-08-07-compact-task-counts/",
21
+ "status": "complete",
22
+ "verificationContract": "Spec dir exists per AGENTS.md convention; PRODUCT.md documents the header row (counts then bar at the end), removal of the standalone compact progress line, and subtask markers; TECH.md covers model/renderer changes; MILESTONES.md updated throughout implementation.",
23
+ "completedAt": "2026-08-07T07:08:11.771Z",
24
+ "evidence": "specs/2026-08-07-compact-task-counts/ created with PRODUCT.md (header row counts+bar-at-end, standalone-line removal, subtask markers), TECH.md (model/renderer/test changes), MILESTONES.md."
25
+ },
26
+ {
27
+ "id": "model",
28
+ "title": "Extend the dashboard model so each task node carries subtask counts (total + completed) for per-row markers; keep top-level progress derivation for the header bar/counts",
29
+ "status": "complete",
30
+ "verificationContract": "DashboardTaskNode gains subtask fields populated by flattenTaskTree (or equivalent); unit tests in goal-dashboard-model.test.ts pin the new fields; tsc clean.",
31
+ "completedAt": "2026-08-07T07:09:05.403Z",
32
+ "evidence": "DashboardTaskNode gains required totalSubtasks/completedSubtasks populated by flattenTaskTree (direct children, skipped=done); model tests pin parent 2/3, leaves 0, skipped-as-done; npm run check clea"
33
+ },
34
+ {
35
+ "id": "renderer",
36
+ "title": "Compact dashboard: single Tasks header row with `✓N done · M open` first and a compact progress bar at the end of the row, remove the standalone compact progress line, add per-row subtask markers in the windowed list without breaking width bounds",
37
+ "status": "complete",
38
+ "verificationContract": "Compact render shows a single `├─ Tasks …` header containing `✓N done · M open` (skipped counts as done) followed by a progress bar at the end of the row before the closing frame; the standalone `Tasks [bar]` line is gone from compact; rows with subtasks show `▸ done/total`; tasks without subtasks show no marker; current-task subtask bar intact; expanded dashboard unchanged; header/rows truncate safely within the bounded width across wide/medium/narrow/minimal modes.",
39
+ "completedAt": "2026-08-07T07:13:51.345Z",
40
+ "evidence": "Compact header is `├─ Tasks · ✓3 done · 2 open ──... [█████░░░] ┤` (counts then bar at end, skipped=done); standalone compact progress line removed; `▸ done/total` markers on rows with subtasks (leaf "
41
+ },
42
+ {
43
+ "id": "tests-docs",
44
+ "title": "Update unit + golden tests, run full validation, update CHANGELOG",
45
+ "status": "complete",
46
+ "verificationContract": "npm run check (tsc --noEmit) clean; npm test full suite green with goldens pinned to the new header format and subtask markers; grep confirms no standalone compact progress-line format remains; CHANGELOG entry added.",
47
+ "completedAt": "2026-08-07T07:13:51.346Z",
48
+ "evidence": "npm run check clean; npm test 667/667 green; test:integration 28/28 green; goldens pinned to new header/marker formats; grep: no `Tasks [` in extensions/tests; README + docs/unified-dashboard.md exam"
49
+ }
50
+ ],
51
+ "blockCompletion": false,
52
+ "proposedAt": "2026-08-07T07:05:48.948Z"
53
+ },
54
+ "stopReason": "agent",
55
+ "archivedPath": ".pi/goals/archived/goal_2026080708140425_msilpn44-qqa4fa.md"
56
+ }
57
+
58
+ # Goal Prompt
59
+
60
+ Add task counts and a progress bar to the compact goal dashboard's "Tasks" header row, and make subtask progress visible per task row: the header becomes a single row with `✓N done · M open` (skipped counted as done) followed by a compact progress bar at the end of the row, the redundant standalone compact `Tasks [bar] X/Y · %` line is removed (expanded dashboard keeps its Progress section unchanged), task rows with subtasks show a `▸ done/total` marker, and the current-task subtask bar is kept.
61
+
62
+ ## Progress
63
+
64
+ - Status: complete
65
+ - Auto-continue: on
66
+ - Sisyphus mode: no
67
+ - Time spent: 7m01s
68
+ - Tokens used: 114K (113,743) tokens
69
+ ## Tasks
70
+
71
+ <!-- blockCompletion: false -->
72
+ - [x] spec-docs: Write spec docs (PRODUCT.md, TECH.md, MILESTONES.md) under specs/2026-08-07-compact-task-counts/ — evidence: specs/2026-08-07-compact-task-counts/ created with PRODUCT.md (header row counts+bar-at-end, standalone-line removal, subtask markers), TECH.md (model/renderer/test changes), MILESTONES.md.
73
+ - [x] model: Extend the dashboard model so each task node carries subtask counts (total + completed) for per-row markers; keep top-level progress derivation for the header bar/counts — evidence: DashboardTaskNode gains required totalSubtasks/completedSubtasks populated by flattenTaskTree (direct children, skipped=done); model tests pin parent 2/3, leaves 0, skipped-as-done; npm run check clea
74
+ - [x] renderer: Compact dashboard: single Tasks header row with `✓N done · M open` first and a compact progress bar at the end of the row, remove the standalone compact progress line, add per-row subtask markers in the windowed list without breaking width bounds — evidence: Compact header is `├─ Tasks · ✓3 done · 2 open ──... [█████░░░] ┤` (counts then bar at end, skipped=done); standalone compact progress line removed; `▸ done/total` markers on rows with subtasks (leaf
75
+ - [x] tests-docs: Update unit + golden tests, run full validation, update CHANGELOG — evidence: npm run check clean; npm test 667/667 green; test:integration 28/28 green; goldens pinned to new header/marker formats; grep: no `Tasks [` in extensions/tests; README + docs/unified-dashboard.md exam
76
+
@@ -0,0 +1,92 @@
1
+ {
2
+ "version": 3,
3
+ "id": "msim9m81-5aa98r",
4
+ "objective": "Move the compact dashboard's subtask progress bar next to the task progress bar in the Tasks header row: the header becomes a single row with `✓N done · M open` (skipped counted as done), the task progress bar, then a short `Sub done/total` label with the subtask bar beside it (e.g. `├─ Tasks · ✓3 done · 2 open ─ [█████░░░] · Sub 2/3 [██░░] ┤`, shown only when the current task has subtasks); the standalone `Subtasks [bar] done/total · %` line is removed from the compact view; per-row `▸ done/total` markers and the expanded dashboard's Progress/current-task sections stay unchanged; the header remains width-safe in every layout mode.\n\nSuccess criteria:\n- The compact dashboard's Tasks section rule renders as one row: counts first (`✓N done · M open`, skipped counted as done), then a dash run, then the task progress bar, then ` · Sub done/total ` and the subtask progress bar beside it — only when the current task has subtasks; when the current task is a leaf (or there is no current task), the subtask segment is omitted.\n- The standalone compact `Subtasks [bar] done/total · %` line is gone from the compact dashboard; the current task's subtask data now lives only in the header segment and the per-row `▸ done/total` markers.\n- Per-row `▸ done/total` markers on tasks with direct subtasks remain; leaf rows show none.\n- The expanded dashboard is byte-identical to today: its Progress section and the Current-task block's `Subtasks [bar] 2/3 · 67%` line are unchanged.\n- The header truncates width-safely across all layout modes (wide/medium/narrow/minimal): the counts and both bars always survive; only the label/dash run may shrink.\n\nBoundaries:\n- In scope: extensions/widgets/goal-dashboard-renderer.ts (compact header rendering, removal of the standalone Subtasks line), spec docs under specs/2026-08-07-compact-task-counts/, golden/unit/e2e tests, README.md, docs/unified-dashboard.md, CHANGELOG.md.\n- Out of scope: the model layer (DashboardTaskNode subtask counts and taskProgress already provide everything needed), the expanded dashboard, per-row marker semantics, current-task block content, and any other widget.\n\nConstraints:\n- Follow AGENTS.md: spec docs live in specs/2026-08-07-compact-task-counts/ with MILESTONES.md updated throughout.\n- No new dependencies; no changes to expanded-dashboard output.\n- Header/rows must stay within bounded widths in every layout mode; bars use per-mode widths from LayoutSpec (task bar = headerBarWidth; subtask bar shorter, clamped to a readable minimum).\n- Keep the muted frame-tone styling of the counts/`Sub` segment consistent with the current header.\n\nVerification contract:\n- npm run check (tsc --noEmit) clean; npm test (667 cases) and npm run test:integration (28) green.\n- Golden tests pin the new header format (`· Sub 2/3 [bar]`) and the absence of the standalone compact Subtasks line.\n- grep/rg over extensions/tests confirms no `Subtasks \\[` compact standalone line remains (the expanded Current-task line and per-row markers are allowed to match only their own patterns).\n- README.md and docs/unified-dashboard.md compact examples refreshed to the new header; CHANGELOG entry added under [Unreleased].\n\nIf blocked: stop and ask the user.",
5
+ "status": "complete",
6
+ "autoContinue": true,
7
+ "usage": {
8
+ "tokensUsed": 170005,
9
+ "activeSeconds": 595
10
+ },
11
+ "sisyphus": false,
12
+ "skipAuditor": true,
13
+ "revision": 54,
14
+ "createdAt": "2026-08-07T07:21:46.945Z",
15
+ "updatedAt": "2026-08-07T07:32:27.376Z",
16
+ "taskList": {
17
+ "tasks": [
18
+ {
19
+ "id": "spec-docs",
20
+ "title": "Update spec docs (PRODUCT.md, TECH.md, MILESTONES.md) under specs/2026-08-07-compact-task-counts/ for the header layout change",
21
+ "status": "complete",
22
+ "verificationContract": "PRODUCT.md documents the header row carrying the task bar plus the `Sub done/total` subtask segment (current task only, omitted for leaf/no-current-task) and removal of the standalone compact Subtasks line; TECH.md covers the renderer change, per-mode subtask-bar widths, and truncation rules; MILESTONES.md gains the new milestone.",
23
+ "completedAt": "2026-08-07T07:26:30.409Z",
24
+ "evidence": "PRODUCT.md documents the header row with counts + task bar + `Sub done/total` subtask segment (current task only, omitted for leaf/minimal), narrow short form, and removal of both standalone compact l"
25
+ },
26
+ {
27
+ "id": "renderer",
28
+ "title": "Compact Tasks header: render task bar + `Sub done/total` + subtask bar beside it (current task with subtasks only), remove the standalone compact Subtasks line, keep expanded unchanged, stay width-safe",
29
+ "status": "complete",
30
+ "verificationContract": "Compact render shows `├─ Tasks · ✓N done · M open ─ [task bar] · Sub done/total [subtask bar] ┤` when the current task has subtasks; no subtask segment when the current task is a leaf or absent; the `Subtasks [bar] …` line is gone from compact; per-row `▸ done/total` markers intact; expanded dashboard's Progress + Current-task blocks byte-identical; header truncates width-safely across wide/medium/narrow/minimal with counts and both bars surviving.",
31
+ "completedAt": "2026-08-07T07:32:11.976Z",
32
+ "evidence": "Compact header is now `├─ Tasks · ✓N done · M open ─ [task bar] · Sub done/total [subtask bar] ┤` (wide/medium labeled, narrow short `· 2/3`, minimal omits segment); no segment for leaf/absent current"
33
+ },
34
+ {
35
+ "id": "tests-docs",
36
+ "title": "Update golden/unit/e2e tests, README + docs examples, CHANGELOG; run full validation",
37
+ "status": "complete",
38
+ "verificationContract": "npm run check clean; npm test (667) and npm run test:integration (28) green; goldens pin the new header segment and absence of the standalone compact Subtasks line; grep confirms no `Subtasks [` compact line remains in extensions/tests; README.md and docs/unified-dashboard.md compact examples and CHANGELOG updated.",
39
+ "completedAt": "2026-08-07T07:32:11.977Z",
40
+ "evidence": "npm run check clean; npm test 667/667; test:integration 28/28; goldens pin `· Sub 2/3 [bar]` and absence of standalone compact Subtasks line; grep: zero `Subtasks [` and zero `Tasks [` in extensions"
41
+ }
42
+ ],
43
+ "blockCompletion": false,
44
+ "proposedAt": "2026-08-07T07:21:38.166Z"
45
+ },
46
+ "stopReason": "agent",
47
+ "archivedPath": ".pi/goals/archived/goal_2026080708322737_msim9m81-5aa98r.md"
48
+ }
49
+
50
+ # Goal Prompt
51
+
52
+ Move the compact dashboard's subtask progress bar next to the task progress bar in the Tasks header row: the header becomes a single row with `✓N done · M open` (skipped counted as done), the task progress bar, then a short `Sub done/total` label with the subtask bar beside it (e.g. `├─ Tasks · ✓3 done · 2 open ─ [█████░░░] · Sub 2/3 [██░░] ┤`, shown only when the current task has subtasks); the standalone `Subtasks [bar] done/total · %` line is removed from the compact view; per-row `▸ done/total` markers and the expanded dashboard's Progress/current-task sections stay unchanged; the header remains width-safe in every layout mode.
53
+
54
+ Success criteria:
55
+ - The compact dashboard's Tasks section rule renders as one row: counts first (`✓N done · M open`, skipped counted as done), then a dash run, then the task progress bar, then ` · Sub done/total ` and the subtask progress bar beside it — only when the current task has subtasks; when the current task is a leaf (or there is no current task), the subtask segment is omitted.
56
+ - The standalone compact `Subtasks [bar] done/total · %` line is gone from the compact dashboard; the current task's subtask data now lives only in the header segment and the per-row `▸ done/total` markers.
57
+ - Per-row `▸ done/total` markers on tasks with direct subtasks remain; leaf rows show none.
58
+ - The expanded dashboard is byte-identical to today: its Progress section and the Current-task block's `Subtasks [bar] 2/3 · 67%` line are unchanged.
59
+ - The header truncates width-safely across all layout modes (wide/medium/narrow/minimal): the counts and both bars always survive; only the label/dash run may shrink.
60
+
61
+ Boundaries:
62
+ - In scope: extensions/widgets/goal-dashboard-renderer.ts (compact header rendering, removal of the standalone Subtasks line), spec docs under specs/2026-08-07-compact-task-counts/, golden/unit/e2e tests, README.md, docs/unified-dashboard.md, CHANGELOG.md.
63
+ - Out of scope: the model layer (DashboardTaskNode subtask counts and taskProgress already provide everything needed), the expanded dashboard, per-row marker semantics, current-task block content, and any other widget.
64
+
65
+ Constraints:
66
+ - Follow AGENTS.md: spec docs live in specs/2026-08-07-compact-task-counts/ with MILESTONES.md updated throughout.
67
+ - No new dependencies; no changes to expanded-dashboard output.
68
+ - Header/rows must stay within bounded widths in every layout mode; bars use per-mode widths from LayoutSpec (task bar = headerBarWidth; subtask bar shorter, clamped to a readable minimum).
69
+ - Keep the muted frame-tone styling of the counts/`Sub` segment consistent with the current header.
70
+
71
+ Verification contract:
72
+ - npm run check (tsc --noEmit) clean; npm test (667 cases) and npm run test:integration (28) green.
73
+ - Golden tests pin the new header format (`· Sub 2/3 [bar]`) and the absence of the standalone compact Subtasks line.
74
+ - grep/rg over extensions/tests confirms no `Subtasks \[` compact standalone line remains (the expanded Current-task line and per-row markers are allowed to match only their own patterns).
75
+ - README.md and docs/unified-dashboard.md compact examples refreshed to the new header; CHANGELOG entry added under [Unreleased].
76
+
77
+ If blocked: stop and ask the user.
78
+
79
+ ## Progress
80
+
81
+ - Status: complete
82
+ - Auto-continue: on
83
+ - Sisyphus mode: no
84
+ - Time spent: 9m55s
85
+ - Tokens used: 170K (170,005) tokens
86
+ ## Tasks
87
+
88
+ <!-- blockCompletion: false -->
89
+ - [x] spec-docs: Update spec docs (PRODUCT.md, TECH.md, MILESTONES.md) under specs/2026-08-07-compact-task-counts/ for the header layout change — evidence: PRODUCT.md documents the header row with counts + task bar + `Sub done/total` subtask segment (current task only, omitted for leaf/minimal), narrow short form, and removal of both standalone compact l
90
+ - [x] renderer: Compact Tasks header: render task bar + `Sub done/total` + subtask bar beside it (current task with subtasks only), remove the standalone compact Subtasks line, keep expanded unchanged, stay width-safe — evidence: Compact header is now `├─ Tasks · ✓N done · M open ─ [task bar] · Sub done/total [subtask bar] ┤` (wide/medium labeled, narrow short `· 2/3`, minimal omits segment); no segment for leaf/absent current
91
+ - [x] tests-docs: Update golden/unit/e2e tests, README + docs examples, CHANGELOG; run full validation — evidence: npm run check clean; npm test 667/667; test:integration 28/28; goldens pin `· Sub 2/3 [bar]` and absence of standalone compact Subtasks line; grep: zero `Subtasks [` and zero `Tasks [` in extensions
92
+
@@ -78,3 +78,45 @@
78
78
  {"type":"audit_skipped","goalId":"mshjewld-9z3e9p","reason":"disabled","at":"2026-08-06T13:17:34.172Z"}
79
79
  {"type":"goal_completed","goalId":"mshjewld-9z3e9p","archivePath":".pi/goals/archived/goal_2026080614173417_mshjewld-9z3e9p.md","at":"2026-08-06T13:17:34.177Z"}
80
80
  {"type":"goal_archived","goalId":"mshjewld-9z3e9p","archivePath":".pi/goals/archived/goal_2026080614173417_mshjewld-9z3e9p.md","at":"2026-08-06T13:17:34.178Z"}
81
+ {"type":"goal_focused","goalId":"mshg3jex-svqfyt","reason":"selected","at":"2026-08-06T13:26:27.232Z"}
82
+ {"type":"goal_focused","goalId":"mshisjml-bl1gar","reason":"selected","at":"2026-08-06T13:26:31.604Z"}
83
+ {"type":"goal_created","goalId":"mshk5uvt-nnkztu","objective":"Move goal status information from the bottom of the pi screen into the goal widget: in both the compact and expanded dashboard status lines, replace the \"Focused: yes / Other goals: N\" bits with `goal: <statusLabel> [<elapsed> <tokens>] (+N open)` using the footer-status formatting (statusLabel + usage bracket, no objective preview — the header title already shows it, and the header's right-side usage folds away to avoid duplication), and stop rendering the focused-goal footer line (setStatus(\"goal\", undefined) when a goal is focused) while keeping the unfocused hint — with updated tests/goldens, spec docs, CHANGELOG entry, and a green validation, on a new branch off main.","sisyphus":false,"autoContinue":true,"at":"2026-08-06T13:35:06.137Z"}
84
+ {"type":"task_list_set","goalId":"mshk5uvt-nnkztu","taskCount":5,"blockCompletion":true,"at":"2026-08-06T13:35:06.137Z"}
85
+ {"type":"task_started","goalId":"mshk5uvt-nnkztu","taskId":"task-1","at":"2026-08-06T13:35:13.881Z"}
86
+ {"type":"task_complete","goalId":"mshk5uvt-nnkztu","taskId":"task-1","evidence":"Branch feat/goal-widget-status created off main (15aa826); specs/2026-08-06-goal-widget-status/ scaffolded with PRODUCT.md, TECH.md, MILESTONES.md (user decisions recorded).","at":"2026-08-06T13:35:37.865Z"}
87
+ {"type":"task_started","goalId":"mshk5uvt-nnkztu","taskId":"task-2","at":"2026-08-06T13:35:37.866Z"}
88
+ {"type":"task_complete","goalId":"mshk5uvt-nnkztu","taskId":"task-2","evidence":"Renderer (compact + expanded) + model updated: status lines now render `goal: <footerLabel> [<footerBits>] (+N open)` via shared statusLine() helper; Focused:/Other goals: bits, STATUS_SYMBOL, and dea","at":"2026-08-06T13:40:01.162Z"}
89
+ {"type":"task_started","goalId":"mshk5uvt-nnkztu","taskId":"task-3","at":"2026-08-06T13:40:01.162Z"}
90
+ {"type":"task_complete","goalId":"mshk5uvt-nnkztu","taskId":"task-3","evidence":"goal-state.ts renderUI: focused-goal branch now calls setStatus('goal', undefined) (clears stale unfocused hint); unfocused hint branch untouched; unused footerStatus import removed; tsc clean.","at":"2026-08-06T13:40:31.928Z"}
91
+ {"type":"task_started","goalId":"mshk5uvt-nnkztu","taskId":"task-4","at":"2026-08-06T13:40:31.928Z"}
92
+ {"type":"task_complete","goalId":"mshk5uvt-nnkztu","taskId":"task-4","evidence":"Updated pinned assertions in goal-widget.test.ts (7), goal-dashboard-golden.test.ts (6 incl. new expanded status-line pin), goal-status.test.ts (1), goal-dashboard-model.test.ts (2: footerBits usage +","at":"2026-08-06T13:43:06.406Z"}
93
+ {"type":"task_started","goalId":"mshk5uvt-nnkztu","taskId":"task-5","at":"2026-08-06T13:43:06.407Z"}
94
+ {"type":"task_complete","goalId":"mshk5uvt-nnkztu","taskId":"task-5","evidence":"CHANGELOG [Unreleased] entry added; PRODUCT/TECH/MILESTONES updated; npm run check clean; npm test 665/665 pass; grep confirms no functional Focused:/Other goals: remnants and no footerStatus(displayG","at":"2026-08-06T13:44:11.522Z"}
95
+ {"type":"completion_requested","goalId":"mshk5uvt-nnkztu","at":"2026-08-06T13:44:18.145Z"}
96
+ {"type":"audit_skipped","goalId":"mshk5uvt-nnkztu","reason":"disabled","at":"2026-08-06T13:44:18.145Z"}
97
+ {"type":"goal_completed","goalId":"mshk5uvt-nnkztu","archivePath":".pi/goals/archived/goal_2026080614441814_mshk5uvt-nnkztu.md","at":"2026-08-06T13:44:18.148Z"}
98
+ {"type":"goal_archived","goalId":"mshk5uvt-nnkztu","archivePath":".pi/goals/archived/goal_2026080614441814_mshk5uvt-nnkztu.md","at":"2026-08-06T13:44:18.148Z"}
99
+ {"type":"goal_created","goalId":"msilpn44-qqa4fa","objective":"Add task counts and a progress bar to the compact goal dashboard's \"Tasks\" header row, and make subtask progress visible per task row: the header becomes a single row with `✓N done · M open` (skipped counted as done) followed by a compact progress bar at the end of the row, the redundant standalone compact `Tasks [bar] X/Y · %` line is removed (expanded dashboard keeps its Progress section unchanged), task rows with subtasks show a `▸ done/total` marker, and the current-task subtask bar is kept.","sisyphus":false,"autoContinue":true,"at":"2026-08-07T07:06:14.980Z"}
100
+ {"type":"task_list_set","goalId":"msilpn44-qqa4fa","taskCount":4,"blockCompletion":false,"at":"2026-08-07T07:06:14.980Z"}
101
+ {"type":"task_started","goalId":"msilpn44-qqa4fa","taskId":"spec-docs","at":"2026-08-07T07:06:20.796Z"}
102
+ {"type":"task_complete","goalId":"msilpn44-qqa4fa","taskId":"spec-docs","evidence":"specs/2026-08-07-compact-task-counts/ created with PRODUCT.md (header row counts+bar-at-end, standalone-line removal, subtask markers), TECH.md (model/renderer/test changes), MILESTONES.md.","at":"2026-08-07T07:08:11.771Z"}
103
+ {"type":"task_started","goalId":"msilpn44-qqa4fa","taskId":"model","at":"2026-08-07T07:08:11.773Z"}
104
+ {"type":"task_complete","goalId":"msilpn44-qqa4fa","taskId":"model","evidence":"DashboardTaskNode gains required totalSubtasks/completedSubtasks populated by flattenTaskTree (direct children, skipped=done); model tests pin parent 2/3, leaves 0, skipped-as-done; npm run check clea","at":"2026-08-07T07:09:05.403Z"}
105
+ {"type":"task_started","goalId":"msilpn44-qqa4fa","taskId":"renderer","at":"2026-08-07T07:09:05.405Z"}
106
+ {"type":"task_complete","goalId":"msilpn44-qqa4fa","taskId":"renderer","evidence":"Compact header is `├─ Tasks · ✓3 done · 2 open ──... [█████░░░] ┤` (counts then bar at end, skipped=done); standalone compact progress line removed; `▸ done/total` markers on rows with subtasks (leaf ","at":"2026-08-07T07:13:51.346Z"}
107
+ {"type":"task_complete","goalId":"msilpn44-qqa4fa","taskId":"tests-docs","evidence":"npm run check clean; npm test 667/667 green; test:integration 28/28 green; goldens pinned to new header/marker formats; grep: no `Tasks [` in extensions/tests; README + docs/unified-dashboard.md exam","at":"2026-08-07T07:13:51.347Z"}
108
+ {"type":"completion_requested","goalId":"msilpn44-qqa4fa","at":"2026-08-07T07:14:04.248Z"}
109
+ {"type":"audit_skipped","goalId":"msilpn44-qqa4fa","reason":"disabled","at":"2026-08-07T07:14:04.249Z"}
110
+ {"type":"goal_completed","goalId":"msilpn44-qqa4fa","archivePath":".pi/goals/archived/goal_2026080708140425_msilpn44-qqa4fa.md","at":"2026-08-07T07:14:04.255Z"}
111
+ {"type":"goal_archived","goalId":"msilpn44-qqa4fa","archivePath":".pi/goals/archived/goal_2026080708140425_msilpn44-qqa4fa.md","at":"2026-08-07T07:14:04.255Z"}
112
+ {"type":"goal_created","goalId":"msim9m81-5aa98r","objective":"Move the compact dashboard's subtask progress bar next to the task progress bar in the Tasks header row: the header becomes a single row with `✓N done · M open` (skipped counted as done), the task progress bar, then a short `Sub done/total` label with the subtask bar beside it (e.g. `├─ Tasks · ✓3 done · 2 open ─ [█████░░░] · Sub 2/3 [██░░] ┤`, shown only when the current task has subtasks); the standalone `Subtasks [bar] done/total · %` line is removed from the compact view; per-row `▸ done/total` markers and the expanded dashboard's Progress/current-task sections stay unchanged; the header remains width-safe in every layout mode.\n\nSuccess criteria:\n- The compact dashboard's Tasks section rule renders as one row: counts first (`✓N done · M open`, skipped counted as done), then a dash run, then the task progress bar, then ` · Sub done/total ` and the subtask progress bar beside it — only when the current task has subtasks; when the current task is a leaf (or there is no current task), the subtask segment is omitted.\n- The standalone compact `Subtasks [bar] done/total · %` line is gone from the compact dashboard; the current task's subtask data now lives only in the header segment and the per-row `▸ done/total` markers.\n- Per-row `▸ done/total` markers on tasks with direct subtasks remain; leaf rows show none.\n- The expanded dashboard is byte-identical to today: its Progress section and the Current-task block's `Subtasks [bar] 2/3 · 67%` line are unchanged.\n- The header truncates width-safely across all layout modes (wide/medium/narrow/minimal): the counts and both bars always survive; only the label/dash run may shrink.\n\nBoundaries:\n- In scope: extensions/widgets/goal-dashboard-renderer.ts (compact header rendering, removal of the standalone Subtasks line), spec docs under specs/2026-08-07-compact-task-counts/, golden/unit/e2e tests, README.md, docs/unified-dashboard.md, CHANGELOG.md.\n- Out of scope: the model layer (DashboardTaskNode subtask counts and taskProgress already provide everything needed), the expanded dashboard, per-row marker semantics, current-task block content, and any other widget.\n\nConstraints:\n- Follow AGENTS.md: spec docs live in specs/2026-08-07-compact-task-counts/ with MILESTONES.md updated throughout.\n- No new dependencies; no changes to expanded-dashboard output.\n- Header/rows must stay within bounded widths in every layout mode; bars use per-mode widths from LayoutSpec (task bar = headerBarWidth; subtask bar shorter, clamped to a readable minimum).\n- Keep the muted frame-tone styling of the counts/`Sub` segment consistent with the current header.\n\nVerification contract:\n- npm run check (tsc --noEmit) clean; npm test (667 cases) and npm run test:integration (28) green.\n- Golden tests pin the new header format (`· Sub 2/3 [bar]`) and the absence of the standalone compact Subtasks line.\n- grep/rg over extensions/tests confirms no `Subtasks \\[` compact standalone line remains (the expanded Current-task line and per-row markers are allowed to match only their own patterns).\n- README.md and docs/unified-dashboard.md compact examples refreshed to the new header; CHANGELOG entry added under [Unreleased].\n\nIf blocked: stop and ask the user.","sisyphus":false,"autoContinue":true,"at":"2026-08-07T07:21:46.945Z"}
113
+ {"type":"task_list_set","goalId":"msim9m81-5aa98r","taskCount":3,"blockCompletion":false,"at":"2026-08-07T07:21:46.945Z"}
114
+ {"type":"task_started","goalId":"msim9m81-5aa98r","taskId":"spec-docs","at":"2026-08-07T07:21:53.250Z"}
115
+ {"type":"task_complete","goalId":"msim9m81-5aa98r","taskId":"spec-docs","evidence":"PRODUCT.md documents the header row with counts + task bar + `Sub done/total` subtask segment (current task only, omitted for leaf/minimal), narrow short form, and removal of both standalone compact l","at":"2026-08-07T07:26:30.410Z"}
116
+ {"type":"task_started","goalId":"msim9m81-5aa98r","taskId":"renderer","at":"2026-08-07T07:26:30.410Z"}
117
+ {"type":"task_complete","goalId":"msim9m81-5aa98r","taskId":"renderer","evidence":"Compact header is now `├─ Tasks · ✓N done · M open ─ [task bar] · Sub done/total [subtask bar] ┤` (wide/medium labeled, narrow short `· 2/3`, minimal omits segment); no segment for leaf/absent current","at":"2026-08-07T07:32:11.976Z"}
118
+ {"type":"task_complete","goalId":"msim9m81-5aa98r","taskId":"tests-docs","evidence":"npm run check clean; npm test 667/667; test:integration 28/28; goldens pin `· Sub 2/3 [bar]` and absence of standalone compact Subtasks line; grep: zero `Subtasks [` and zero `Tasks [` in extensions","at":"2026-08-07T07:32:11.977Z"}
119
+ {"type":"completion_requested","goalId":"msim9m81-5aa98r","at":"2026-08-07T07:32:27.371Z"}
120
+ {"type":"audit_skipped","goalId":"msim9m81-5aa98r","reason":"disabled","at":"2026-08-07T07:32:27.371Z"}
121
+ {"type":"goal_completed","goalId":"msim9m81-5aa98r","archivePath":".pi/goals/archived/goal_2026080708322737_msim9m81-5aa98r.md","at":"2026-08-07T07:32:27.377Z"}
122
+ {"type":"goal_archived","goalId":"msim9m81-5aa98r","archivePath":".pi/goals/archived/goal_2026080708322737_msim9m81-5aa98r.md","at":"2026-08-07T07:32:27.378Z"}
@@ -21,49 +21,54 @@ activity are never fabricated for display.
21
21
  Always visible above the editor while a goal is focused:
22
22
 
23
23
  ```text
24
- ╭─ pi-goal-x ─ Add CSV export to reports────────────── 12m47s · 18.2K tok ─╮
25
- │ ● In progress · Focused: yes · Other goals: 2 │
26
- │ Tasks [███████████░░░░░░░] 3/5 · 60% │
27
- ├─ Tasks ─────────────────────────────────────────────────────────────────┤
28
- │ ✓ t1 Review reports page and data source │
29
- │ ✓ t2 Implement filtered CSV export │
30
- │ ▸ t3 Add the download button │
31
- │ · t4 Add documentation │
32
- │ … +1 more task │
33
- │ Current t3 · Add the download button │
34
- │ Subtasks [████████████░░░░░░] 2/3 · 67% │
35
- │ Verify Run npm test with zero failures. │
36
- │ File .pi/goals/active_goal_g1.md │
37
- ╰─ Ctrl+Shift+T: expand tasks─────────────────────────────────────────────╯
24
+ ╭─ pi-goal-x ─ Add CSV export to reports ────────────────────────────────────────────────╮
25
+ │ goal: running [12m47s 18.2K] (+2 open) │
26
+ ├─ Tasks · ✓3 done · 2 open ─────────────────────────────────── [████░░] · Sub 2/3 [██░] ┤
27
+ │ ✓ t1 Review reports page and data source │
28
+ │ ✓ t2 Implement filtered CSV export │
29
+ │ ▸ t3 Add the download button ☑ ▸ 2/3 │
30
+ │ · t4 Add documentation │
31
+ │ … +1 more task │
32
+ │ Current t3 · Add the download button │
33
+ │ Verify Run npm test with zero failures. │
34
+ │ File .pi/goals/active_goal_g1.md │
35
+ ╰─ Ctrl+Shift+T: expand tasks ───────────────────────────────────────────────────────────╯
38
36
  ```
39
37
 
40
38
  Compact rows, in order:
41
39
 
42
- 1. Header (rounded corners): title plus elapsed time and token usage.
43
- 2. Status: colored symbol + explicit label, focus state, other open goals
44
- (`·`-separated). Colors come from the theme with a monochrome fallback.
40
+ 1. Header (rounded corners): title. (Usage lives in the status line below,
41
+ not in the header.)
42
+ 2. Status: `goal: <status> [<elapsed> <tokens>] (+N open)` — the same
43
+ terminology as the pi footer status line.
45
44
  3. Token budget (when configured): `⛽ Budget 18.2K / 50K · 36%`.
46
- 4. Top-level task progress (`3/5 · 60%`); a skipped top-level task counts as
47
- done (§9.1). The whole progress element is neutral gray — bar fill and
48
- empty cells included — so nothing in the progress info is colourful.
45
+ 4. Tasks header row: `Tasks · ✓N done · M open` with the compact task
46
+ progress bar, then the current task's subtask bar beside it
47
+ (` · Sub done/total ` + bar — the word `Sub` is dropped in narrow mode so
48
+ the full counts still fit at 50 columns, and the segment is omitted
49
+ entirely in minimal mode, where the counts and task bar alone use all
50
+ available space). `N` counts top-level tasks complete **or skipped** — a
51
+ skipped top-level task counts as done (§9.1) — and `M` counts pending
52
+ tasks. The subtask segment appears only when the current task has direct
53
+ children. The whole header row is neutral gray (label, counts, bar fill,
54
+ and empty cells) so nothing in the progress info is colourful.
49
55
  5. Task list section: the top-level tasks shown by default in **pastel
50
56
  amber** (`mdHeading`) with colour-coded markers and ids (✓ complete muted
51
57
  green, ▸ current teal, ~ skipped gray, · pending amber), an aligned id
52
58
  column, truncated amber titles, and a `… +N more` overflow line when the
53
59
  list is longer than the row budget (5 rows at wide, 4 medium, 3 narrow, 2
54
- minimal). The current task is fully highlighted in the theme's accent
55
- color (marker, id, title). The whole box chrome — top/bottom frame,
56
- left/right edges, corners and interior rules — is drawn in one tone: the
57
- theme's neutral gray (`muted`). No blue tinge.
60
+ minimal). Tasks that have direct subtasks show a muted `▸ done/total`
61
+ marker at the end of their row (leaf tasks show none). The current task
62
+ is fully highlighted in the theme's accent color (marker, id, title). The
63
+ whole box chrome — top/bottom frame, left/right edges, corners and
64
+ interior rules — is drawn in one tone: the theme's neutral gray
65
+ (`muted`). No blue tinge.
58
66
  6. Current task: `Current t3 · Add the download button` (id and title in the
59
67
  accent color).
60
- 7. Current-task subtask progress (`2/3 · 67%`) when the current task has
61
- direct children (the current task's subtasks stay inline). The subtask
62
- bar and its fraction are neutral gray like the top-level progress.
63
- 8. Goal-level verification contract (first line, truncated).
64
- 9. Blocked or paused detail (reason, suggested action).
65
- 10. Active (or archived) goal file path.
66
- 11. Footer with the expansion shortcut. The whole footer line — leading dash,
68
+ 7. Goal-level verification contract (first line, truncated).
69
+ 8. Blocked or paused detail (reason, suggested action).
70
+ 9. Active (or archived) goal file path.
71
+ 10. Footer with the expansion shortcut. The whole footer line — leading dash,
67
72
  shortcut hint and trailing fill — is drawn in one frame tone (the
68
73
  neutral-gray `muted`), so the color spans the full width with no two-tone
69
74
  split; the header line is likewise one tone with only the `pi-goal-x`
@@ -1,5 +1,4 @@
1
1
  import { type AgentToolResult, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { footerStatus } from "./goal-core.ts";
3
2
  import { FOCUS_ENTRY, STATE_ENTRY, GOAL_EVENT_ENTRY, goalDetails } from "./goal-format.ts";
4
3
  import { loadGoalSettings, loadGoalSettingsFileConfig } from "./goal-settings.ts";
5
4
  import {
@@ -669,9 +668,12 @@ export function createGoalCore(
669
668
  return;
670
669
  }
671
670
 
672
- const displayGoal = goalForDisplay() ?? state.goal;
673
- const otherCount = otherOpenGoalCount(goalsById, focusedGoalId);
674
- ctx.ui.setStatus("goal", `${footerStatus(displayGoal)}${otherCount > 0 ? ` (+${otherCount} open)` : ""}`);
671
+ // The goal widget is the single home for goal status: the status line
672
+ // lives in the compact/expanded dashboard (goal: <label> [<usage>]
673
+ // (+N open)), so the focused-goal footer segment is cleared — a stale
674
+ // unfocused hint or earlier status must not linger once a goal is
675
+ // focused. The unfocused branch above still sets its own hint.
676
+ ctx.ui.setStatus("goal", undefined);
675
677
 
676
678
  if (!widgetRegistered) {
677
679
  ctx.ui.setWidget(
@@ -9,7 +9,7 @@
9
9
  * the durable ledger.
10
10
  */
11
11
 
12
- import { displayObjectiveTitle, formatDuration } from "../goal-core.ts";
12
+ import { displayObjectiveTitle, formatDuration, statusLabel } from "../goal-core.ts";
13
13
  import type { GoalLedgerEvent } from "../goal-ledger.ts";
14
14
  import type { GoalRecord, GoalTask } from "../goal-record.ts";
15
15
  import { deriveGoalActivity, type GoalActivityItem } from "../goal-activity.ts";
@@ -27,6 +27,8 @@ export interface GoalDashboardModel {
27
27
  status: {
28
28
  code: DashboardStatusCode;
29
29
  label: string;
30
+ /** Footer-status label (goal-core statusLabel), e.g. `running`, `paused (agent)`. */
31
+ footerLabel: string;
30
32
  reason?: string;
31
33
  suggestedAction?: string;
32
34
  };
@@ -36,9 +38,9 @@ export interface GoalDashboardModel {
36
38
 
37
39
  usage: {
38
40
  activeSeconds: number;
39
- elapsedLabel: string;
40
41
  tokens: number;
41
- tokenLabel: string;
42
+ /** Footer-style usage content, e.g. `49h49m36s 19M` (empty when unused). */
43
+ footerBits: string;
42
44
  };
43
45
 
44
46
  budget?: {
@@ -88,6 +90,10 @@ export interface DashboardTaskNode {
88
90
  evidence?: string;
89
91
  /** Completion timestamp (when status is complete) — the scroll anchor (§9.6). */
90
92
  completedAt?: string;
93
+ /** Direct-child subtask total — 0 for a leaf task (§9.3 rule: direct children only). */
94
+ totalSubtasks: number;
95
+ /** Direct children complete or skipped (same "done" rule as §9.1). */
96
+ completedSubtasks: number;
91
97
  }
92
98
 
93
99
  export interface GoalDashboardModelOptions {
@@ -106,6 +112,10 @@ export interface GoalDashboardModelOptions {
106
112
  export interface DashboardGoalStatus {
107
113
  code: DashboardStatusCode;
108
114
  label: string;
115
+ /** Footer-status label (goal-core statusLabel: running / paused (agent) /
116
+ * blocked / budget limited / active / complete, with sisyphus prefix) — the
117
+ * widget status line shows the same terminology as the pi footer. */
118
+ footerLabel: string;
109
119
  reason?: string;
110
120
  suggestedAction?: string;
111
121
  }
@@ -118,25 +128,25 @@ export function deriveGoalStatus(goal: GoalRecord): DashboardGoalStatus {
118
128
  switch (goal.status) {
119
129
  case "active":
120
130
  return goal.autoContinue
121
- ? { code: "running", label: "In progress" }
122
- : { code: "idle", label: "Idle" };
131
+ ? { code: "running", label: "In progress", footerLabel: statusLabel(goal) }
132
+ : { code: "idle", label: "Idle", footerLabel: statusLabel(goal) };
123
133
  case "paused": {
124
134
  const who = goal.stopReason === "agent" ? " (agent)" : goal.stopReason === "user" ? " (user)" : "";
125
- const status: DashboardGoalStatus = { code: "paused", label: `Paused${who}` };
135
+ const status: DashboardGoalStatus = { code: "paused", label: `Paused${who}`, footerLabel: statusLabel(goal) };
126
136
  if (goal.pauseReason) status.reason = goal.pauseReason;
127
137
  if (goal.pauseSuggestedAction) status.suggestedAction = goal.pauseSuggestedAction;
128
138
  return status;
129
139
  }
130
140
  case "blocked": {
131
- const status: DashboardGoalStatus = { code: "blocked", label: "Blocked" };
141
+ const status: DashboardGoalStatus = { code: "blocked", label: "Blocked", footerLabel: statusLabel(goal) };
132
142
  if (goal.pauseReason) status.reason = goal.pauseReason;
133
143
  if (goal.pauseSuggestedAction) status.suggestedAction = goal.pauseSuggestedAction;
134
144
  return status;
135
145
  }
136
146
  case "budget_limited":
137
- return { code: "budget_limited", label: "Budget limited" };
147
+ return { code: "budget_limited", label: "Budget limited", footerLabel: statusLabel(goal) };
138
148
  case "complete":
139
- return { code: "complete", label: "Complete" };
149
+ return { code: "complete", label: "Complete", footerLabel: statusLabel(goal) };
140
150
  }
141
151
  }
142
152
 
@@ -181,6 +191,8 @@ export function flattenTaskTree(tasks: readonly GoalTask[] | undefined, currentT
181
191
  if (!tasks) return nodes;
182
192
  const walk = (list: readonly GoalTask[], depth: number): void => {
183
193
  for (const t of list) {
194
+ const children = t.subtasks ?? [];
195
+ const completed = children.filter((c) => c.status === "complete" || c.status === "skipped").length;
184
196
  nodes.push({
185
197
  id: t.id,
186
198
  title: t.title,
@@ -190,8 +202,10 @@ export function flattenTaskTree(tasks: readonly GoalTask[] | undefined, currentT
190
202
  verificationContract: t.verificationContract,
191
203
  evidence: t.evidence,
192
204
  completedAt: t.completedAt,
205
+ totalSubtasks: children.length,
206
+ completedSubtasks: completed,
193
207
  });
194
- if (t.subtasks && t.subtasks.length > 0) walk(t.subtasks, depth + 1);
208
+ if (children.length > 0) walk(children, depth + 1);
195
209
  }
196
210
  };
197
211
  walk(tasks, 0);
@@ -472,6 +486,13 @@ export function deriveGoalDashboardModel(
472
486
  const taskTitles = new Map(taskTree.map((n) => [n.id, n.title]));
473
487
  const recentActivity = deriveGoalActivity(ledgerEvents, goal.id, { taskTitles, limit: activityLimit });
474
488
 
489
+ // Footer-status usage bits (goal-core footerStatus parity): compact
490
+ // duration + compact token count, e.g. `49h49m36s 19M`. The status line
491
+ // renders them inside brackets; empty when the goal has no usage yet.
492
+ const footerUsageBits: string[] = [];
493
+ if (goal.usage.activeSeconds > 0) footerUsageBits.push(formatDuration(goal.usage.activeSeconds));
494
+ if (goal.usage.tokensUsed > 0) footerUsageBits.push(formatCompactTokens(goal.usage.tokensUsed));
495
+
475
496
  return {
476
497
  goalId: goal.id,
477
498
  title: displayObjectiveTitle(goal.objective),
@@ -480,9 +501,8 @@ export function deriveGoalDashboardModel(
480
501
  filePath: goal.activePath ?? goal.archivedPath,
481
502
  usage: {
482
503
  activeSeconds: goal.usage.activeSeconds,
483
- elapsedLabel: formatDashboardDuration(goal.usage.activeSeconds),
484
504
  tokens: goal.usage.tokensUsed,
485
- tokenLabel: `${formatCompactTokens(goal.usage.tokensUsed)} tok`,
505
+ footerBits: footerUsageBits.join(" "),
486
506
  },
487
507
  budget,
488
508
  taskProgress,
@@ -77,12 +77,23 @@ function boxRule(theme: Theme, width: number): string {
77
77
  return frame(theme, `├${H.repeat(Math.max(1, width - 2))}┤`);
78
78
  }
79
79
 
80
- /** Section separator with a label: `├─ Tasks ──────────┤` (§5.1). */
81
- function boxSectionRule(theme: Theme, width: number, label: string): string {
80
+ /** Section separator with a label and optional right-side content:
81
+ * `├─ Tasks ──── [████░] · Sub 2/3 [██░░] ┤` (§5.1). The right content
82
+ * (e.g. the header progress bars) always survives; the label truncates when
83
+ * tight. Hardened so label + right never exceed the inner width: the fill is
84
+ * `max(0, …)` and the truncation budget leaves room for the `─ ` prefix and
85
+ * trailing space. */
86
+ function boxSectionRule(theme: Theme, width: number, label: string, right = ""): string {
82
87
  const inner = Math.max(4, width - 2);
83
88
  const left = frame(theme, `${H} ${label} `);
84
- const fill = Math.max(1, inner - visibleWidth(left));
85
- return `${frame(theme, "├")}${left}${frame(theme, H.repeat(fill))}${frame(theme, "┤")}`;
89
+ const r = right ? ` ${right} ` : "";
90
+ let l = left;
91
+ if (visibleWidth(left) + visibleWidth(r) > inner) {
92
+ const budget = Math.max(4, inner - visibleWidth(r) - 3);
93
+ l = frame(theme, `${H} ${fit(label, budget)} `);
94
+ }
95
+ const fill = Math.max(0, inner - visibleWidth(l) - visibleWidth(r));
96
+ return `${frame(theme, "├")}${l}${frame(theme, H.repeat(fill))}${r}${frame(theme, "┤")}`;
86
97
  }
87
98
 
88
99
  function boxFooter(theme: Theme, width: number, content: string): string {
@@ -97,16 +108,7 @@ function boxFooter(theme: Theme, width: number, content: string): string {
97
108
  return `${frame(theme, "╰")}${l}${frame(theme, H.repeat(fill))}${frame(theme, "╯")}`;
98
109
  }
99
110
 
100
- // ── §5.2 status symbols / colors ────────────────────────────────────────────
101
-
102
- const STATUS_SYMBOL: Record<DashboardStatusCode, string> = {
103
- running: "●",
104
- idle: "○",
105
- paused: "◐",
106
- blocked: "⊘",
107
- budget_limited: "⛽",
108
- complete: "✓",
109
- };
111
+ // ── §5.2 status colors ──────────────────────────────────────────────────────
110
112
 
111
113
  const STATUS_COLOR: Record<DashboardStatusCode, RenderColor> = {
112
114
  running: "accent",
@@ -117,6 +119,20 @@ const STATUS_COLOR: Record<DashboardStatusCode, RenderColor> = {
117
119
  complete: "success",
118
120
  };
119
121
 
122
+ /**
123
+ * Widget status line — footer-status parity: `goal: <label> [<usage>] (+N open)`.
124
+ * Same terminology/format as the pi footer status line (goal-core statusLabel +
125
+ * usage bracket), without the objective preview (the header title shows it) and
126
+ * without the old `Focused: yes` / `Other goals: N` bits. The open-goals count
127
+ * appears only when other open goals exist.
128
+ */
129
+ function statusLine(theme: Theme, model: GoalDashboardModel): string {
130
+ const color = STATUS_COLOR[model.status.code];
131
+ const usage = model.usage.footerBits ? ` [${model.usage.footerBits}]` : "";
132
+ const open = model.otherOpenGoals > 0 ? ` (+${model.otherOpenGoals} open)` : "";
133
+ return `${theme.fg(color, `goal: ${model.status.footerLabel}`)}${muted(theme, `${usage}${open}`)}`;
134
+ }
135
+
120
136
  // ── §5.3 progress bars ──────────────────────────────────────────────────────
121
137
 
122
138
  function progressBar(theme: Theme, pct: number, barWidth: number): string {
@@ -140,24 +156,26 @@ function layoutMode(width: number): LayoutMode {
140
156
 
141
157
  interface LayoutSpec {
142
158
  barWidth: number;
143
- showFocused: boolean;
144
- showOtherGoals: boolean;
159
+ /** Compact progress bar width inside the Tasks header row (bar at the end). */
160
+ headerBarWidth: number;
161
+ /** Subtask progress bar width inside the Tasks header row, beside the task
162
+ * bar (wide/medium/narrow; minimal omits the segment — unused there). */
163
+ subtaskBarWidth: number;
145
164
  showPath: boolean;
146
165
  showPauseAction: boolean;
147
166
  footerHint: string;
148
- statusLine: "full" | "brief";
149
167
  }
150
168
 
151
169
  function specFor(mode: LayoutMode): LayoutSpec {
152
170
  switch (mode) {
153
171
  case "wide":
154
- return { barWidth: 26, showFocused: true, showOtherGoals: true, showPath: true, showPauseAction: true, footerHint: "Ctrl+Shift+T: expand tasks", statusLine: "full" };
172
+ return { barWidth: 26, headerBarWidth: 8, subtaskBarWidth: 4, showPath: true, showPauseAction: true, footerHint: "Ctrl+Shift+T: expand tasks" };
155
173
  case "medium":
156
- return { barWidth: 18, showFocused: true, showOtherGoals: true, showPath: true, showPauseAction: true, footerHint: "Ctrl+Shift+T: expand tasks", statusLine: "full" };
174
+ return { barWidth: 18, headerBarWidth: 6, subtaskBarWidth: 3, showPath: true, showPauseAction: true, footerHint: "Ctrl+Shift+T: expand tasks" };
157
175
  case "narrow":
158
- return { barWidth: 12, showFocused: true, showOtherGoals: true, showPath: false, showPauseAction: false, footerHint: "Ctrl+Shift+T: expand", statusLine: "brief" };
176
+ return { barWidth: 12, headerBarWidth: 5, subtaskBarWidth: 2, showPath: false, showPauseAction: false, footerHint: "Ctrl+Shift+T: expand" };
159
177
  case "minimal":
160
- return { barWidth: 8, showFocused: false, showOtherGoals: false, showPath: false, showPauseAction: false, footerHint: "Ctrl+Shift+T: expand", statusLine: "brief" };
178
+ return { barWidth: 8, headerBarWidth: 4, subtaskBarWidth: 2, showPath: false, showPauseAction: false, footerHint: "Ctrl+Shift+T: expand" };
161
179
  }
162
180
  }
163
181
 
@@ -230,15 +248,19 @@ function renderCompactTaskRows(theme: Theme, nodes: DashboardTaskNode[], viewpor
230
248
  for (const node of shown) {
231
249
  const marker = taskMarker(node);
232
250
  const contractMark = node.verificationContract ? dim(theme, " ☑") : "";
251
+ // §9.3 compact subtask marker: direct-child done/total for tasks that
252
+ // have subtasks, muted so it stays annotation — the current-task bar
253
+ // below carries the visual weight.
254
+ const subtaskMark = node.totalSubtasks > 0 ? muted(theme, ` ▸ ${node.completedSubtasks}/${node.totalSubtasks}`) : "";
233
255
  const id = node.id.padEnd(idWidth);
234
256
  const prefix = `${marker.symbol} ${id} `;
235
- const titleBudget = Math.max(4, available - visibleWidth(prefix) - visibleWidth(contractMark));
257
+ const titleBudget = Math.max(4, available - visibleWidth(prefix) - visibleWidth(contractMark) - visibleWidth(subtaskMark));
236
258
  const title = fit(node.title, titleBudget);
237
259
  const markerText = theme.fg(marker.color, marker.symbol);
238
260
  // Colour-coded: id shares the marker color; titles amber; current accent.
239
261
  const idText = node.isCurrent ? accent(theme, id) : theme.fg(marker.color, id);
240
262
  const body = node.isCurrent ? accent(theme, title) : amber(theme, title);
241
- rows.push(`${markerText} ${idText} ${body}${contractMark}`);
263
+ rows.push(`${markerText} ${idText} ${body}${contractMark}${subtaskMark}`);
242
264
  }
243
265
  if (viewport.hiddenBelow > 0) {
244
266
  rows.push(muted(theme, `… +${viewport.hiddenBelow} more task${viewport.hiddenBelow === 1 ? "" : "s"}`));
@@ -273,21 +295,13 @@ export function renderCompactDashboard(
273
295
  const inner = safeWidth - 2;
274
296
  const lines: string[] = [];
275
297
 
276
- const usageRight = mode !== "minimal" && (model.usage.activeSeconds > 0 || model.usage.tokens > 0)
277
- ? `${model.usage.elapsedLabel} · ${model.usage.tokenLabel}`
278
- : "";
279
- lines.push(boxHeader(theme, safeWidth, `${accent(theme, "pi-goal-x")} ${frame(theme, `─ ${model.title}`)}`, usageRight ? frame(theme, usageRight) : ""));
298
+ lines.push(boxHeader(theme, safeWidth, `${accent(theme, "pi-goal-x")} ${frame(theme, `─ ${model.title}`)}`));
280
299
 
281
300
  // Status line.
282
301
  if (model.status.code === "complete") {
283
302
  lines.push(boxLine(theme, safeWidth, `${success(theme, "✓")} ${success(theme, "All required work is complete.")}`));
284
303
  } else {
285
- const symbol = STATUS_SYMBOL[model.status.code];
286
- const color = STATUS_COLOR[model.status.code];
287
- const bits = [`${theme.fg(color, symbol)} ${theme.fg(color, model.status.label)}`];
288
- if (spec.showFocused) bits.push(muted(theme, `Focused: ${model.focused ? "yes" : "no"}`));
289
- if (spec.showOtherGoals && model.otherOpenGoals > 0) bits.push(muted(theme, `Other goals: ${model.otherOpenGoals}`));
290
- lines.push(boxLine(theme, safeWidth, bits.join(" · ")));
304
+ lines.push(boxLine(theme, safeWidth, statusLine(theme, model)));
291
305
  }
292
306
 
293
307
  // Budget (when configured): fuel gauge + amount, amber until the budget is
@@ -297,25 +311,38 @@ export function renderCompactDashboard(
297
311
  lines.push(boxLine(theme, safeWidth, `${theme.fg(fuel as RenderColor, "⛽")} ${muted(theme, "Budget")} ${theme.fg(fuel as RenderColor, formatBudget(model.budget.used, model.budget.total))}`));
298
312
  }
299
313
 
300
- // Overall task progress (§9.1). The fraction is muted like the percent:
301
- // only the bar itself stays colourful.
302
- if (model.taskProgress) {
303
- const bar = progressBar(theme, model.taskProgress.percentage, spec.barWidth);
304
- lines.push(boxLine(theme, safeWidth, `${muted(theme, "Tasks")} ${bar} ${muted(theme, `${model.taskProgress.completed}/${model.taskProgress.total} · ${model.taskProgress.percentage}%`)}`));
305
- }
314
+ // §9.1 compact task counts + progress bars live in the Tasks header row
315
+ // (below); the standalone progress lines were removed in favor of it.
306
316
 
307
317
  // §9.2/§9.6 compact task list: a window over the top-level tasks, anchored
308
318
  // by default so the most recently completed tasks are visible; when the
309
319
  // list overflows the window, the footer advertises Ctrl+Shift+↑↓ to scroll
310
- // it. Subtasks of the current task stay inline via the subtask progress
311
- // line below.
320
+ // it. The current task's subtask progress lives in the header segment, and
321
+ // per-row subtask markers show which tasks have subtasks.
312
322
  const topLevel = model.taskTree.filter((node) => node.depth === 0);
313
323
  const compactRows = compactTaskViewportRows(safeWidth);
314
324
  const listOverflows = topLevel.length > compactRows;
315
325
  if (topLevel.length > 0) {
316
326
  const offset = opts.scrollOffset ?? anchoredScrollOffset(topLevel, compactRows);
317
327
  const viewport = deriveTaskListViewport(topLevel.length, compactRows, offset);
318
- lines.push(boxSectionRule(theme, safeWidth, "Tasks"));
328
+ // §9.1/§9.3 header row: counts first (`✓N done · M open`, skipped
329
+ // counts as done), compact task progress bar, then the current task's
330
+ // subtask progress bar beside it (` · Sub done/total `, wide/medium;
331
+ // narrow drops the word `Sub` so the full counts fit at 50 cols;
332
+ // minimal omits the segment — counts + task bar alone cannot share 38
333
+ // inner columns with a second bar). All muted: the header stays one
334
+ // frame-tone block. The standalone compact progress lines were removed
335
+ // in favor of this row.
336
+ const headerLabel = model.taskProgress
337
+ ? `Tasks · ✓${model.taskProgress.completed} done · ${model.taskProgress.total - model.taskProgress.completed} open`
338
+ : "Tasks";
339
+ let headerRight = model.taskProgress ? progressBar(theme, model.taskProgress.percentage, spec.headerBarWidth) : "";
340
+ if (model.currentTask && model.currentTask.totalSubtasks > 0 && mode !== "minimal") {
341
+ const subBar = progressBar(theme, model.currentTask.subtaskPercentage, spec.subtaskBarWidth);
342
+ const subWord = mode === "narrow" ? "" : "Sub ";
343
+ headerRight += `${muted(theme, ` · ${subWord}`)}${muted(theme, `${model.currentTask.completedSubtasks}/${model.currentTask.totalSubtasks}`)} ${subBar}`;
344
+ }
345
+ lines.push(boxSectionRule(theme, safeWidth, headerLabel, headerRight));
319
346
  for (const row of renderCompactTaskRows(theme, topLevel, viewport, inner)) {
320
347
  lines.push(boxLine(theme, safeWidth, row));
321
348
  }
@@ -336,11 +363,9 @@ export function renderCompactDashboard(
336
363
  }
337
364
  }
338
365
 
339
- // Current-task subtask progress (§9.3).
340
- if (model.currentTask && model.currentTask.totalSubtasks > 0 && mode !== "minimal") {
341
- const bar = progressBar(theme, model.currentTask.subtaskPercentage, spec.barWidth);
342
- lines.push(boxLine(theme, safeWidth, `${muted(theme, "Subtasks")} ${bar} ${muted(theme, `${model.currentTask.completedSubtasks}/${model.currentTask.totalSubtasks}`)} · ${muted(theme, `${model.currentTask.subtaskPercentage}%`)}`));
343
- }
366
+ // Current-task subtask progress moved into the Tasks header row (§9.3);
367
+ // the standalone compact Subtasks progress line was removed. (The
368
+ // expanded dashboard's Current-task block still shows it.)
344
369
 
345
370
  // Goal-level verification (§11.1): truncated first line in compact.
346
371
  if (model.goalVerificationContract && mode !== "minimal") {
@@ -397,19 +422,9 @@ export function renderExpandedDashboard(
397
422
  const inner = safeWidth - 2;
398
423
  const lines: string[] = [];
399
424
 
400
- const usageRight = mode !== "minimal" && (model.usage.activeSeconds > 0 || model.usage.tokens > 0)
401
- ? `${model.usage.elapsedLabel} · ${model.usage.tokenLabel}`
402
- : "";
403
- lines.push(boxHeader(theme, safeWidth, `${accent(theme, "pi-goal-x")} ${frame(theme, `─ ${model.title}`)}`, usageRight ? frame(theme, usageRight) : ""));
425
+ lines.push(boxHeader(theme, safeWidth, `${accent(theme, "pi-goal-x")} ${frame(theme, `─ ${model.title}`)}`));
404
426
 
405
- const symbol = STATUS_SYMBOL[model.status.code];
406
- const color = STATUS_COLOR[model.status.code];
407
- const statusBits = [
408
- `Status: ${theme.fg(color, symbol)} ${theme.fg(color, model.status.label)}`,
409
- ...((spec.showFocused ? [muted(theme, `Focused: ${model.focused ? "yes" : "no"}`)] : []) as string[]),
410
- ...((spec.showOtherGoals && model.otherOpenGoals > 0 ? [muted(theme, `Other goals: ${model.otherOpenGoals}`)] : []) as string[]),
411
- ];
412
- lines.push(boxLine(theme, safeWidth, statusBits.join(" · ")));
427
+ lines.push(boxLine(theme, safeWidth, statusLine(theme, model)));
413
428
 
414
429
  if (spec.showPath && model.filePath) {
415
430
  lines.push(boxLine(theme, safeWidth, dim(theme, `File: ${model.filePath}`)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-x",
3
- "version": "0.25.1",
3
+ "version": "0.25.2",
4
4
  "description": "Goal mode extension for pi: durable long-running objectives, five model tools, structured tasks, independent completion audit, Sisyphus mode, auto-continue, and a status overlay.",
5
5
  "license": "MIT",
6
6
  "author": "pi-goal-x contributors",