@groeponline/pi-wishcraft 1.4.7 → 1.4.9

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,10 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.4.9] - 2026-08-29
6
+
7
+ ## [1.4.8] - 2026-08-29
8
+
5
9
  ## [1.4.7] - 2026-08-28
6
10
 
7
11
  ## [1.4.6] - 2026-08-28
@@ -279,17 +279,34 @@ export class BashModeEditor extends CustomEditor {
279
279
  }
280
280
 
281
281
  if (
282
- bashMode &&
282
+ (bashMode || oneOffBashCommand) &&
283
283
  this.keybindingsRef.matches(data, "tui.input.submit") &&
284
284
  !this.keybindingsRef.matches(data, "tui.input.newLine")
285
285
  ) {
286
+ // One-off bang prompts run the text after the prefix (mirrors pi's
287
+ // own !/!! handling in interactive-mode.js); anything else submits
288
+ // verbatim. Without the strip the bang would hit bash history
289
+ // expansion (`!cmd` = replay a previous command) instead of `cmd`.
290
+ const raw = this.getExpandedText().trim();
291
+ const oneOff = getOneOffBashCommandContext(raw);
292
+ const command = oneOff ? oneOff.command.trim() : raw;
293
+ if (!command) {
294
+ // Bash-off bare `!`: fall back to pi's own submit handling, whose
295
+ // `if (command)` guard turns an empty command into a normal prompt
296
+ // (pre-interception behavior). Full bash mode keeps the return.
297
+ // This must run before the shell-running guard: a bare `!` while a
298
+ // managed shell job runs should still delegate to pi, not be
299
+ // swallowed by the "already running" warning.
300
+ if (!bashMode) {
301
+ super.handleInput(data);
302
+ }
303
+ return;
304
+ }
305
+
286
306
  if (this.optionsRef.isShellRunning()) {
287
307
  this.optionsRef.onNotify("Shell command already running", "warning");
288
308
  return;
289
309
  }
290
-
291
- const command = this.getExpandedText().trim();
292
- if (!command) return;
293
310
  this.clearGhostSuggestion();
294
311
  resetShellHistoryBrowse(this);
295
312
  this.optionsRef.onEditorSubmit?.();
package/docs/bash-mode.md CHANGED
@@ -32,6 +32,8 @@ At command position, short stems first resolve from the newest successful local
32
32
 
33
33
  If the bash prompt is empty, bash mode shows the newest successful project-history ghost suggestion immediately when one exists, including right after mode entry or after the prompt is cleared again. One-off `!command` and `!!command` prompts reuse the same shell prediction pipeline, including ghost text. Right Arrow or Tab accepts ghost text into the editor, and Enter runs the current shell command. Mode entry stays quiet: there is no automatic or manual dropdown completion surface, and ghost suggestions do not run shell-native completion probes.
34
34
 
35
+ One-off `!command` and `!!command` prompts also work when bash mode is off: Enter routes them through the managed shell with the prefix stripped, so `!cmd` runs `cmd` (in a real PTY) instead of a headless shell failing on TUI programs or hitting bash history expansion. Output lands in the transcript below the editor, not in the model conversation — pi's own `!`-prefix bash (headless, context-injected) is superseded while this extension's editor is active. Full bash mode (`ctrl+shift+b`) is only needed for running plain commands or keeping the shell session visible in the footer.
36
+
35
37
  ## Configuration
36
38
 
37
39
  In `~/.pi/agent/settings.json` (or under `PI_CODING_AGENT_DIR` when that environment variable is set):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groeponline/pi-wishcraft",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
4
4
  "description": "Operator cockpit for Pi: live powerline status, searchable skills, idea queue, sticky Bash, hooks, policy controls, and session UX.",
5
5
  "type": "module",
6
6
  "files": [
@@ -31,6 +31,18 @@ const CHEF_COLORS: ColorScheme = {
31
31
  queue: "dim",
32
32
  };
33
33
 
34
+ // chef-calm: same operational essentials, visually quieter. Accent stays
35
+ // reserved for state (context/token states), everything else blends into
36
+ // text/dim so the line reads as one row instead of a rainbow.
37
+ const CHEF_CALM_COLORS: ColorScheme = {
38
+ ...DEFAULT_COLORS,
39
+ model: "text",
40
+ path: "text",
41
+ gitClean: "dim",
42
+ queue: "dim",
43
+ context: "accent",
44
+ };
45
+
34
46
  // ---------------------------------------------------------------------------
35
47
  // 10 vNext Structural Signature Presets
36
48
  // ---------------------------------------------------------------------------
@@ -372,6 +384,42 @@ export const PRESETS: Record<StatusLinePreset, PresetDef> = {
372
384
  },
373
385
  },
374
386
 
387
+ // chef-calm: five segments, dot separator, quiet motion. The operational
388
+ // row without the noise — tps/open_ports/subagents/cost/hostname dropped,
389
+ // git commit subjects off (ahead/behind stays). Colours: text/dim with a
390
+ // single accent for context so the line reads as one calm row.
391
+ "chef-calm": {
392
+ leftSegments: ["model", "path", "git"],
393
+ rightSegments: ["context_pct", "time"],
394
+ secondarySegments: [],
395
+ separator: "dot",
396
+ colors: CHEF_CALM_COLORS,
397
+ segmentOptions: {
398
+ model: { showThinkingLevel: false },
399
+ path: { mode: "basename" },
400
+ git: {
401
+ showBranch: true,
402
+ showStaged: false,
403
+ showUnstaged: false,
404
+ showUntracked: false,
405
+ polling: "branch",
406
+ hostIcon: false,
407
+ showAheadBehind: true,
408
+ showCommit: false,
409
+ },
410
+ context: { format: "percent" },
411
+ time: { format: "24h", showSeconds: false },
412
+ },
413
+ motion: {
414
+ idle: "nimbus",
415
+ thinking: "nimbus",
416
+ streaming: "nimbus",
417
+ "tool.end": "copper-switch",
418
+ success: "copper-switch",
419
+ error: "copper-switch",
420
+ },
421
+ },
422
+
375
423
  // 10 vNext Structural Presets
376
424
  lanternwake: {
377
425
  leftSegments: ["model", "thinking", "shell_mode", "path", "git", "session", "queue"],
@@ -243,7 +243,11 @@ export type StatusLineSeparatorStyle =
243
243
  | "ascii"
244
244
  | "dot"
245
245
  | "chevron"
246
- | "star";
246
+ | "star"
247
+ | "blunt"
248
+ | "rounded"
249
+ | "diamond"
250
+ | "double";
247
251
 
248
252
  // Preset names
249
253
  export type PowerlinePlacement = "above" | "below";
@@ -256,6 +260,7 @@ export type StatusLinePreset =
256
260
  | "nerd"
257
261
  | "ascii"
258
262
  | "chef"
263
+ | "chef-calm"
259
264
  // vNext Structural Signature Presets
260
265
  | "lanternwake"
261
266
  | "threadbound"
@@ -426,4 +426,28 @@ export const EXTRA_MOTIONS: readonly MotionDef[] = [
426
426
  frames: ["┌", "┐", "┘", "└"],
427
427
  description: "Corner-box spinner.",
428
428
  },
429
+ {
430
+ id: "nimbus",
431
+ name: "Nimbus",
432
+ category: "wishcraft",
433
+ kind: "frames",
434
+ channels: ["workingGlyph", "ambient"],
435
+ colorRole: "motionHot",
436
+ fallbackGlyph: "O",
437
+ loop: "ambient",
438
+ frames: ["·", "∘", "○", "◌", "○", "∘"],
439
+ description: "Calm phase drift — a softer alternative to wisp for quiet presets.",
440
+ },
441
+ {
442
+ id: "copper-switch",
443
+ name: "Copper Switch",
444
+ category: "wishcraft",
445
+ kind: "frames",
446
+ channels: ["signal", "deckTransient", "borderEmphasis"],
447
+ colorRole: "accent",
448
+ fallbackGlyph: "|",
449
+ loop: "finite",
450
+ frames: ["▁", "▃", "▅", "▇", "▅", "▃", "▁"],
451
+ description: "A one-shot rising-and-falling bar on tool end or success — no lingering motion.",
452
+ },
429
453
  ];
@@ -124,6 +124,13 @@ export interface SeparatorChars {
124
124
  asciiLeft: string;
125
125
  asciiRight: string;
126
126
  dot: string;
127
+ bluntLeft: string;
128
+ bluntRight: string;
129
+ roundedLeft: string;
130
+ roundedRight: string;
131
+ diamond: string;
132
+ doubleLeft: string;
133
+ doubleRight: string;
127
134
  }
128
135
 
129
136
  export const NERD_SEPARATORS: SeparatorChars = {
@@ -138,6 +145,13 @@ export const NERD_SEPARATORS: SeparatorChars = {
138
145
  asciiLeft: ">",
139
146
  asciiRight: "<",
140
147
  dot: "·",
148
+ bluntLeft: "▌",
149
+ bluntRight: "▐",
150
+ roundedLeft: "\uE0B6",
151
+ roundedRight: "\uE0B4",
152
+ diamond: "◇",
153
+ doubleLeft: "»",
154
+ doubleRight: "«",
141
155
  };
142
156
 
143
157
  export const ASCII_SEPARATORS: SeparatorChars = {
@@ -152,6 +166,13 @@ export const ASCII_SEPARATORS: SeparatorChars = {
152
166
  asciiLeft: ">",
153
167
  asciiRight: "<",
154
168
  dot: ".",
169
+ bluntLeft: "[",
170
+ bluntRight: "]",
171
+ roundedLeft: "(",
172
+ roundedRight: ")",
173
+ diamond: "*",
174
+ doubleLeft: ">",
175
+ doubleRight: "<",
155
176
  };
156
177
 
157
178
  export function hasNerdFonts(): boolean {
@@ -34,6 +34,26 @@ export function getSeparator(style: StatusLineSeparatorStyle): SeparatorDef {
34
34
  ["dot", { left: chars.dot, right: chars.dot }],
35
35
  ["chevron", { left: "›", right: "‹" }],
36
36
  ["star", { left: "✦", right: "✦" }],
37
+ ["blunt", {
38
+ left: chars.bluntLeft,
39
+ right: chars.bluntRight,
40
+ endCaps: {
41
+ left: chars.doubleRight,
42
+ right: chars.doubleLeft,
43
+ useBgAsFg: true,
44
+ },
45
+ }],
46
+ ["rounded", {
47
+ left: chars.roundedLeft,
48
+ right: chars.roundedRight,
49
+ endCaps: {
50
+ left: chars.roundedRight,
51
+ right: chars.roundedLeft,
52
+ useBgAsFg: true,
53
+ },
54
+ }],
55
+ ["diamond", { left: ` ${chars.diamond} `, right: ` ${chars.diamond} ` }],
56
+ ["double", { left: chars.doubleLeft, right: chars.doubleRight }],
37
57
  ]);
38
58
 
39
59
  const definition = registry.get(style) ?? registry.get("powerline-thin")!;