@groeponline/pi-wishcraft 0.19.2 → 0.20.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/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.20.0] - 2026-08-20
6
+
7
+ ### Added
8
+ - `alt+p` menu is three overlay entries (Navigate, Configure, Status) with a Status drill-down for ports, TPS, and toggle.
9
+
10
+ ### Fixed
11
+ - `theme.json` in the working directory overrides icons, and the theme cache busts when that file changes.
12
+
13
+ ### Changed
14
+ - npm pack includes `banner.png` so the published tarball matches `pi.image`.
15
+
5
16
  ## [0.19.2] - 2026-08-20
6
17
 
7
18
  ### Added
package/README.md CHANGED
@@ -247,7 +247,7 @@ Interactivity (Pi core renders the footer as static text, so live click is not p
247
247
 
248
248
  - `/tps [value]`: show or set `POWERLINE_TPS`
249
249
  - `/open-ports`: list listening ports and pick one
250
- - `alt+p`: **powerline menu**: navigate the live segments (`↑`/`↓` + `enter`), configure (preset / TPS / UDP / labels), or open the full ports list
250
+ - `alt+p`: **powerline menu**: three overlays (Navigate, Configure, Status). Status drills down to ports, TPS, and toggle.
251
251
  - `alt+i`: **powerline info**: full open-ports list
252
252
 
253
253
  Both `alt+p` and `alt+i` are rebindable (see Keybinds below); changes apply after `/reload`.
package/ROADMAP.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Roadmap — pi-wishcraft
2
2
 
3
- Herschreven 2026-08-18. Vierde pass 2026-08-20: conflict-restant uit
4
- `19d6cc0` weg. 0.19-code + catalogus staan op main (#12, #11). Overlay-
5
- submenus en de rest van de repairs blijven 0.20. CHE-40 is follow-up,
6
- geen 0.19-blocker. `v0.19.0` en `v0.19.1` staan op npm.
3
+ Herschreven 2026-08-18. Vijfde pass 2026-08-20: 0.19.0–0.19.2 staan
4
+ op npm. CHE-40 (`/powerline` tab) is Done via #18. Overlay-submenus
5
+ (CHE-42 / #19) starten 0.20. ROADMAP was achter op de code: hooks,
6
+ repairs-subset en skills-manager v2 UI zitten al op `main`.
7
7
 
8
8
  Pi core is de engine. Wishcraft is de cockpit. Elke feature dient één van
9
9
  drie doelen: **grip** (skills, tokens, config), **prestatie** (repairs,
@@ -84,16 +84,15 @@ Niet de fork. Niet de SaaS-agent.
84
84
 
85
85
  ## Mijlpalen
86
86
 
87
- - **0.19.0 — "Correctheid"** (deze campagne). Bugs en hygiëne eerst.
88
- Kleine config-afmakers. Geen hooks, geen repairs, geen manager-v2-UI,
89
- geen overlay-submenus. Done = npm 0.19.0 live, `/skills` filtert,
90
- `$test` expandeert geen debris, `npm test` + `npm run typecheck` +
91
- `madge --circular` groen op de tag. Als de config-PR uitloopt:
92
- tag **0.18.1** met alleen PR A (bugs). Correctheid wacht niet op
93
- labels.
94
- - **0.20.0 "Harness"**. Hooks + tool-input repairs + skills-manager
95
- v2-UI + token-overlays. Done = drie README-hookvoorbeelden werken,
96
- repair-teller zichtbaar, `/skills` zoekt op naam+beschrijving+pad.
87
+ - **0.19.0–0.19.2 — "Correctheid"** (geland). Bugs, hygiëne, catalogus,
88
+ auto-release, `/powerline` tab. Hooks, repairs-subset en skills
89
+ manager v2 UI gingen mee in #12, eerder dan deze sectie beloofde.
90
+ Done = npm 0.19.2 live, `/skills` filtert, `$test` expandeert geen
91
+ debris, verify-trio groen op de tag.
92
+ - **0.20.0 "Harness"**. Overlay-chrome + CHE-42 drill-down, Configure
93
+ uit `ctx.ui.select`, token-overlays, rest-repairs, README-hooks die
94
+ waar zijn. Done = drie README-hookvoorbeelden werken, repair-teller
95
+ zichtbaar, `alt+p` overlay-boom, `/tps` deelt de ring met het segment.
97
96
  - **1.0 — "Cockpit"**. Skills-doctor/install, declaratieve policy,
98
97
  preset-editor, idee-review, stabiele ChefGroep-statuskeys,
99
98
  documentatie die waar is. Done = README dekt alles wat we shipten,
@@ -173,7 +172,10 @@ GRO-1061 (runner-queue) is ops, geen product-slice.
173
172
 
174
173
  ## 0.20.0 — Harness
175
174
 
176
- Vier stacked PRs. Pas starten als 0.19 (of 0.18.1) op npm staat.
175
+ Vier stacked PRs. 0.19.2 staat op npm. Hooks, repairs-subset en
176
+ skills-manager v2 UI zijn al op `main` (#12). Wat hier nog openstaat:
177
+ overlay-chrome, CHE-42, Configure-submenu, token-overlays, rest-repairs,
178
+ README-voorbeelden.
177
179
 
178
180
  Overlay-chrome kit, één keer, daarna hergebruiken: box + ronde hoeken,
179
181
  accent-kop, dim metadata, rechts uitgelijnde counts, `→` detail /
@@ -216,15 +218,15 @@ zijn eigen schema's.
216
218
  Done: pure `repairToolInput(tool, input)` + table-driven tests voor
217
219
  de zes gevallen + de parse-vóór-wrap invariant.
218
220
 
219
- ### PR G — overlay-submenus (CHE-42)
221
+ ### PR G — overlay-submenus (CHE-42) — in #19
220
222
 
221
223
  Het `alt+p`-menu krijgt gestapelde `SelectList`-overlays (pijltjes +
222
224
  descriptions) in plaats van platte `ctx.ui.select`. Max drie
223
225
  top-level ingangen. Pure functie `buildPowerlineMenuItems`
224
- unit-testen. Sluit CHE-42. CHE-40 (`/powerline <tab>` subcommands)
225
- blijft open 0.18 shipped alleen bash token-step, niet deze
226
- hiërarchie. Landt in 0.19 PR B of een kleine follow-up. CHE-41
227
- wordt de per-segment detail view in 1.0, geen tweede `alt+i`-pad.
226
+ unit-testen. #19 landt de top-level boom (Navigate / Configure /
227
+ Status). Configure zelf blijft `ctx.ui.select` tot de volgende
228
+ 0.20-snede. CHE-40 is Done (#18). CHE-41 is 1.0 per-segment detail,
229
+ geen tweede `alt+i`-pad.
228
230
 
229
231
  ### PR H — skills manager v2 UI + token-overlays
230
232
 
@@ -301,11 +303,11 @@ Pas na 0.20. Geen parallelle 1.0-tak.
301
303
 
302
304
  | Ticket | Actie |
303
305
  |---|---|
304
- | GRO-1060 fork cleanup | Deels gedaan (tags). Rest = PR A debris + concept-png. CHANGELOG niet inkorten. Versie niet resetten. |
306
+ | GRO-1060 fork cleanup | Done. Release-pad bewezen. CHANGELOG niet inkorten. Versie niet resetten. `banner.png` blijft. |
305
307
  | GRO-1061 CI queue | Ops, niet deze roadmap. |
306
- | CHE-40 `/powerline` tab | In Progress. `/powerline` tab completes presets and placement above, below, or toggle. |
307
- | CHE-41 alt+i info | Wordt 1.0 detail-view. Ticket hernoemen of GRO-child. |
308
- | CHE-42 drill-down | = 0.20 PR G. Hernoemen naar wishcraft / GRO. |
308
+ | CHE-40 `/powerline` tab | Done (#18 / 0.19.2). |
309
+ | CHE-41 per-segment detail | 1.0. Ticket hernoemd; geen tweede `alt+i`-pad. |
310
+ | CHE-42 drill-down | In Progress. #19 = drie top-level overlays + Status. Configure-overlay volgt. |
309
311
 
310
312
  Oude `pi-powerline-footer`-projecttickets niet laten staan alsof
311
313
  die package nog leeft.
@@ -333,8 +335,8 @@ die package nog leeft.
333
335
  - `npm deprecate` van de oude naam faalt tot de scope-owner het
334
336
  token verruimt. Gebruikers die `pi-powerline-footer` installeren
335
337
  blijven op 0.17.2.
336
- - Hooks spawnen processen. Default timeout 30s; kill-switch moet
337
- in 0.20 vanaf dag één bestaan.
338
+ - Hooks spawnen processen. Default timeout 30s; kill-switch
339
+ `wishcraft.hooksEnabled` bestaat al op `main`.
338
340
  - Repairs op core-tools raken we niet aan. Als DeepSeek-achtige
339
341
  modellen daar alsnog op stuklopen, is dat een gesprek met pi
340
342
  core, geen stille override.
@@ -369,8 +371,9 @@ die package nog leeft.
369
371
  Verify-trio groen. (#12)
370
372
  - [x] Gallery-contract gemerged (`chore/pi-dev-gallery`): keywords,
371
373
  `publishConfig.access`, `pi.image`, `npm run verify:package`. (#11)
372
- - [x] PR C: merge to `main` tagged `v0.19.0` then `v0.19.1` and published
373
- (`npm view` = `0.19.1`). Same-job bump publish via #16.
374
- - [ ] README skills-sectie waar; deze ROADMAP in sync.
375
- - [ ] CHE-40: `/powerline` subcommand-tab landt (deze branch) of blijft een
376
- open GRO-child. CHE-41/42 hernoemen.
374
+ - [x] PR C: merge to `main` tagged `v0.19.0` then `v0.19.1` then
375
+ `v0.19.2` and published (`npm view` = `0.19.2`). Same-job bump
376
+ publish via #16. CHE-40 tab-complete via #18.
377
+ - [x] README skills-sectie waar; deze ROADMAP in sync met 0.19.2.
378
+ - [x] CHE-40: `/powerline` subcommand-tab geland (#18). CHE-41/42
379
+ hernoemd naar wishcraft.
package/banner.png ADDED
Binary file
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@groeponline/pi-wishcraft",
3
- "version": "0.19.2",
3
+ "version": "0.20.0",
4
4
  "description": "Wishcraft cockpit for the pi coding agent — powerline status, vibes, idea inbox, bash mode, and full customization.",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "*.ts",
8
8
  "*.md",
9
9
  "*.json",
10
+ "banner.png",
10
11
  "src/**/*.ts",
11
12
  "src/**/*.txt",
12
13
  "bash-mode/**/*.ts",
@@ -16,9 +16,9 @@ import {
16
16
  } from "../settings/settings-io.ts";
17
17
  import {
18
18
  showOpenPortsList,
19
- showPowerlineMainMenu,
20
19
  showSelectOverlay,
21
20
  } from "../ui/menu-views.ts";
21
+ import { showPowerlineMainMenu } from "../ui/powerline-menu-view.ts";
22
22
  import { openStashHistory } from "../shortcuts/shortcuts-router.ts";
23
23
  import { ensureShellSession, setBashModeActive } from "./bash-mode-actions.ts";
24
24
  import { setupCustomEditor } from "../ui/custom-editor.ts";
@@ -305,46 +305,6 @@ export async function activateSegment(
305
305
  ctx.ui.notify(`${id}: ${value}`, "info");
306
306
  }
307
307
 
308
- /** Main powerline menu: navigate / configure / info / toggle. */
309
- export async function showPowerlineMainMenu(
310
- rt: RuntimeState,
311
- ctx: any,
312
- ): Promise<void> {
313
- rt.currentCtx = ctx;
314
- const choice = await ctx.ui.select("Powerline", [
315
- "Navigate segments",
316
- "Configure…",
317
- "Open ports (full list)",
318
- "TPS detail",
319
- "Toggle powerline",
320
- ]);
321
- if (!choice) return;
322
- if (choice === "Navigate segments") {
323
- const picked = await showSegmentNavigator(rt, ctx);
324
- if (picked) await activateSegment(rt, ctx, picked);
325
- return;
326
- }
327
- if (choice === "Configure…") {
328
- await configurePowerline(rt, ctx);
329
- return;
330
- }
331
- if (choice === "Open ports (full list)") {
332
- await showOpenPortsList(ctx);
333
- return;
334
- }
335
- if (choice === "TPS detail") {
336
- const live = process.env.POWERLINE_TPS
337
- ? `(override ${process.env.POWERLINE_TPS})`
338
- : "(live 1s window)";
339
- ctx.ui.notify(`TPS ${live}`, "info");
340
- return;
341
- }
342
- if (choice === "Toggle powerline") {
343
- ctx.ui.notify("Use /powerline to toggle", "info");
344
- return;
345
- }
346
- }
347
-
348
308
  /** Navigable overlay that mirrors the live powerline segments; arrow keys move, Enter activates. */
349
309
  export async function showSegmentNavigator(
350
310
  rt: RuntimeState,
@@ -0,0 +1,83 @@
1
+ import type { RuntimeState } from "../core/types.ts";
2
+ import {
3
+ activateSegment,
4
+ configurePowerline,
5
+ showOpenPortsList,
6
+ showSegmentNavigator,
7
+ showSelectOverlay,
8
+ } from "./menu-views.ts";
9
+ import {
10
+ assertPowerlineMenuBounds,
11
+ type PowerlineMenuNode,
12
+ powerlineMenuToSelectItems,
13
+ } from "./powerline-menu.ts";
14
+
15
+ async function pickPowerlineMenuNode(
16
+ ctx: any,
17
+ title: string,
18
+ nodes: PowerlineMenuNode[],
19
+ ): Promise<PowerlineMenuNode | null> {
20
+ const picked = await showSelectOverlay(
21
+ ctx,
22
+ title,
23
+ "↑↓ navigate · enter open · esc back",
24
+ powerlineMenuToSelectItems(nodes),
25
+ Math.min(nodes.length, 12),
26
+ );
27
+ if (!picked) return null;
28
+ return nodes.find((node) => node.id === picked.value) ?? null;
29
+ }
30
+
31
+ async function activatePowerlineMenuAction(
32
+ rt: RuntimeState,
33
+ ctx: any,
34
+ id: string,
35
+ ): Promise<void> {
36
+ if (id === "navigate") {
37
+ const picked = await showSegmentNavigator(rt, ctx);
38
+ if (picked) await activateSegment(rt, ctx, picked);
39
+ return;
40
+ }
41
+ if (id === "configure") {
42
+ await configurePowerline(rt, ctx);
43
+ return;
44
+ }
45
+ if (id === "ports") {
46
+ await showOpenPortsList(ctx);
47
+ return;
48
+ }
49
+ if (id === "tps") {
50
+ const live = process.env.POWERLINE_TPS
51
+ ? `(override ${process.env.POWERLINE_TPS})`
52
+ : "(live 1s window)";
53
+ ctx.ui.notify(`TPS ${live}`, "info");
54
+ return;
55
+ }
56
+ if (id === "toggle") {
57
+ ctx.ui.notify("Use /powerline to toggle", "info");
58
+ }
59
+ }
60
+
61
+ /** Main powerline menu: three top-level overlays, Status drills down. */
62
+ export async function showPowerlineMainMenu(
63
+ rt: RuntimeState,
64
+ ctx: any,
65
+ ): Promise<void> {
66
+ rt.currentCtx = ctx;
67
+ while (true) {
68
+ const top = await pickPowerlineMenuNode(
69
+ ctx,
70
+ "Powerline",
71
+ assertPowerlineMenuBounds(),
72
+ );
73
+ if (!top) return;
74
+ if (top.children?.length) {
75
+ const child = await pickPowerlineMenuNode(ctx, top.label, top.children);
76
+ if (!child) continue;
77
+ await activatePowerlineMenuAction(rt, ctx, child.id);
78
+ continue;
79
+ }
80
+ await activatePowerlineMenuAction(rt, ctx, top.id);
81
+ return;
82
+ }
83
+ }
@@ -0,0 +1,54 @@
1
+ import type { SelectItem } from "@earendil-works/pi-tui";
2
+
3
+ export interface PowerlineMenuNode {
4
+ id: string;
5
+ label: string;
6
+ description?: string;
7
+ children?: PowerlineMenuNode[];
8
+ }
9
+
10
+ const MAX_TOP_LEVEL = 3;
11
+
12
+ export function buildPowerlineMenuItems(): PowerlineMenuNode[] {
13
+ return [
14
+ { id: "navigate", label: "Navigate segments", description: "Jump to a live segment" },
15
+ { id: "configure", label: "Configure", description: "Preset, TPS, ports, labels" },
16
+ {
17
+ id: "status",
18
+ label: "Status",
19
+ description: "Powerline status and diagnostics",
20
+ children: [
21
+ { id: "ports", label: "Open ports", description: "Full listening list" },
22
+ { id: "tps", label: "TPS detail", description: "Live 1s window or override" },
23
+ { id: "toggle", label: "Toggle powerline", description: "Use /powerline to enable or disable" },
24
+ { id: "cpu", label: "CPU usage", description: "Current process CPU usage" },
25
+ { id: "memory", label: "Memory usage", description: "Current process memory" },
26
+ { id: "network", label: "Network status", description: "Network interface status" },
27
+ { id: "uptime", label: "System uptime", description: "Host uptime" },
28
+ { id: "version", label: "Version info", description: "Runtime and application versions" },
29
+ { id: "logs", label: "Recent logs", description: "Show recent status messages" },
30
+ { id: "diagnostics", label: "Diagnostics", description: "Run powerline diagnostics" },
31
+ ],
32
+ },
33
+ ];
34
+ }
35
+
36
+ export function assertPowerlineMenuBounds(nodes: PowerlineMenuNode[] = buildPowerlineMenuItems()): PowerlineMenuNode[] {
37
+ if (nodes.length > MAX_TOP_LEVEL) throw new Error(`Powerline menu allows at most ${MAX_TOP_LEVEL} top-level items`);
38
+ return nodes;
39
+ }
40
+
41
+ export function findPowerlineMenuNode(id: string, nodes: PowerlineMenuNode[] = buildPowerlineMenuItems()): PowerlineMenuNode | null {
42
+ for (const node of nodes) {
43
+ if (node.id === id) return node;
44
+ if (node.children) {
45
+ const nested = findPowerlineMenuNode(id, node.children);
46
+ if (nested) return nested;
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+
52
+ export function powerlineMenuToSelectItems(nodes: PowerlineMenuNode[]): SelectItem[] {
53
+ return nodes.map((node) => ({ value: node.id, label: node.label, description: node.description }));
54
+ }
@@ -1,5 +1,5 @@
1
1
  import type { Theme, ThemeColor } from "@earendil-works/pi-coding-agent";
2
- import { existsSync, readFileSync } from "node:fs";
2
+ import { existsSync, readFileSync, statSync } from "node:fs";
3
3
  import { join, dirname } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { getAgentPath } from "../paths/agent-dirs.ts";
@@ -64,11 +64,20 @@ function resolvePackageDir(startDir: string): string {
64
64
  return startDir;
65
65
  }
66
66
 
67
+ function fileIdentity(path: string): string {
68
+ try {
69
+ return `${path}:${statSync(path).mtimeMs}`;
70
+ } catch {
71
+ return `${path}:missing`;
72
+ }
73
+ }
74
+
67
75
  function getConfigurationPaths(): string[] {
68
76
  const currentFileDir = dirname(fileURLToPath(import.meta.url));
69
77
  const pkgDir = resolvePackageDir(currentFileDir);
70
78
  return [
71
79
  getAgentPath("extensions", "powerline-footer", "theme.json"),
80
+ join(process.cwd(), "theme.json"),
72
81
  join(pkgDir, "theme.json"),
73
82
  ];
74
83
  }
@@ -87,7 +96,7 @@ function mergeThemeConfig(base: PowerlineThemeConfig, overrides: unknown): Power
87
96
 
88
97
  export function loadThemeConfig(): PowerlineThemeConfig {
89
98
  const paths = getConfigurationPaths();
90
- const identity = paths.join("|");
99
+ const identity = paths.map(fileIdentity).join("|");
91
100
  const time = Date.now();
92
101
 
93
102
  if (cachedConfig && cacheIdentifier === identity && time - lastCacheUpdate < CACHE_LIFETIME_MS) {