@groeponline/pi-wishcraft 1.4.1 → 1.4.6
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/AGENTS.md +28 -0
- package/CHANGELOG.md +36 -0
- package/README.md +22 -18
- package/ROADMAP.md +3 -3
- package/STRATEGY.md +54 -0
- package/bash-mode/editor-input.ts +3 -1
- package/bash-mode/editor.ts +44 -7
- package/bash-mode/forward.ts +64 -0
- package/bash-mode/pty-session.ts +356 -0
- package/bash-mode/ptyshell-managed.ts +145 -0
- package/bash-mode/session-factory.ts +64 -0
- package/bash-mode/transcript-v2.ts +77 -0
- package/bash-mode/transcript.ts +18 -0
- package/bash-mode/types.ts +27 -0
- package/docs/bash-mode.md +4 -0
- package/docs/configuration.md +45 -1
- package/docs/index.md +4 -1
- package/package.json +1 -1
- package/src/config/effective.ts +122 -0
- package/src/config/settings-registry.ts +2 -0
- package/src/config/structural-presets.ts +1 -1
- package/src/extension/commands/bash-mode-actions.ts +11 -10
- package/src/extension/commands/commands.ts +8 -0
- package/src/extension/core/state.ts +0 -1
- package/src/extension/core/types.ts +2 -2
- package/src/extension/session/session-lifecycle.ts +21 -0
- package/src/extension/skills/skill-templates.ts +12 -3
- package/src/extension/ui/custom-editor.ts +4 -0
- package/src/extension/ui/status-line-renderers.ts +12 -9
- package/src/extension/welcome/welcome-integration.ts +23 -1
- package/src/motion/catalog.ts +4 -0
- package/src/motion/frames.ts +19 -9
- package/src/motion/scheduler.ts +7 -0
- package/src/render/layout.ts +137 -0
- package/src/render/motion-candidates.ts +52 -0
- package/src/render/motion-rail.ts +99 -0
- package/src/render/paint.ts +64 -0
- package/src/render/v2-adapter.ts +70 -0
- package/src/render/v2-entry.ts +132 -0
- package/src/segments/pipeline.ts +164 -0
- package/src/segments/types.ts +36 -0
- package/src/segments/usage.ts +42 -3
- package/src/signal/controller.ts +37 -21
- package/src/studio/actions.ts +105 -0
- package/src/studio/advice-pane.ts +73 -0
- package/src/studio/advise/context.ts +89 -0
- package/src/studio/advise/engine.ts +117 -0
- package/src/studio/advise/prompts.ts +68 -0
- package/src/studio/component.ts +105 -0
- package/src/studio/deepwiki/cache.ts +74 -0
- package/src/studio/deepwiki/client.ts +73 -0
- package/src/studio/deepwiki/extract.ts +66 -0
- package/src/studio/inspect.ts +38 -0
- package/src/studio/list.ts +48 -0
- package/src/studio/open.ts +44 -0
- package/src/studio/state.ts +100 -0
- package/src/studio/types.ts +30 -0
- package/src/welcome/banner.ts +6 -0
- package/src/welcome/index.ts +7 -0
- package/src/welcome/overlay.ts +6 -0
- package/src/welcome/renderer.ts +6 -39
- package/src/welcome/types.ts +6 -0
- package/src/welcome/welcome-art.ts +148 -0
- package/src/working-vibes/frames.ts +25 -0
- package/src/working-vibes/index.ts +2 -0
- package/src/working-vibes/storage.ts +11 -0
- package/bash-mode/shell-session.ts +0 -294
- package/src/signal/render.ts +0 -221
package/AGENTS.md
CHANGED
|
@@ -72,3 +72,31 @@ npm test # node --experimental-strip-types --test tests/**/*.test.
|
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
Run both before proposing any non-trivial change.
|
|
75
|
+
|
|
76
|
+
Compound Engineering overlay: `.compound-engineering/` (tracked `config.yaml`, gitignored `config.local.yaml`). Artifact root `.compound-engineering/artifacts/`. Portable skills `~/.agents/skills/ce-*`; native Cursor plugin is fallback only when this overlay is absent.
|
|
77
|
+
|
|
78
|
+
## Cursor Cloud specific instructions
|
|
79
|
+
|
|
80
|
+
The Cloud Agent environment is provisioned so the extension can be tested both as
|
|
81
|
+
a library and inside real `pi`, with Docker for containerized/parallel runs.
|
|
82
|
+
|
|
83
|
+
- **Node:** the platform `node` on `PATH` is v22.14 (fine for `npm ci`,
|
|
84
|
+
`typecheck`, and the test suite, which use `--experimental-strip-types`).
|
|
85
|
+
Node 24 is available via `nvm` and is what CI uses.
|
|
86
|
+
- **pi CLI:** installed and pinned to the wishcraft peer range
|
|
87
|
+
(`@earendil-works/pi-coding-agent` `>=0.81.0 <0.85.0`). pi requires Node
|
|
88
|
+
`>=22.19`, so the `pi` launcher is wrapped to run under the `nvm` Node 24.
|
|
89
|
+
`~/.pi/agent/settings.json` loads this checkout (`packages: ["/workspace"]`,
|
|
90
|
+
`preset: chef`) so `pi` renders the wishcraft welcome/powerline on startup.
|
|
91
|
+
Running a model needs a provider key (`/login`); the extension loads without
|
|
92
|
+
one.
|
|
93
|
+
- **Docker:** `dockerd` runs with the `fuse-overlayfs` storage driver (required
|
|
94
|
+
in the nested VM). `scripts/cloud-agent-start.sh` starts it per boot;
|
|
95
|
+
`scripts/cloud-agent-install.sh` is the idempotent bootstrap (`npm ci` plus
|
|
96
|
+
self-healing pi setup).
|
|
97
|
+
- **Containerized / parallel testing:** `scripts/docker-test.sh [-n N]` runs the
|
|
98
|
+
full check suite (`typecheck` + `test` + `circular`) in `node:24` containers,
|
|
99
|
+
each with an isolated copy of the tree, so `N` runs are safe in parallel.
|
|
100
|
+
- **Publishing / pi.dev:** merges to `main` auto-publish to npm via the org
|
|
101
|
+
`NPM_TOKEN` (`.github/workflows/release.yml`), and pi.dev mirrors npm. Gate
|
|
102
|
+
the catalog contract with `npm run verify:package`.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.4.6] - 2026-08-28
|
|
6
|
+
|
|
7
|
+
## [1.4.5] - 2026-08-28
|
|
8
|
+
|
|
9
|
+
## [1.4.4] - 2026-08-28
|
|
10
|
+
|
|
11
|
+
## [1.4.3] - 2026-08-28
|
|
12
|
+
|
|
13
|
+
### Added (v2 Platform — wishcraft-v2-platform plan)
|
|
14
|
+
- Powerline v2: the status line now renders through a single path (`renderStatusLineV2` -> `computeLaneLayout` -> `paintLayout`) with the motion rail as a first-class layout segment (reading order left -> rail -> right preserved; under width pressure the right lane yields first, the rail later). The legacy v1 three-lane renderer (`src/signal/render.ts`) is deleted; rail semantics live in `src/render/motion-rail.ts`. Deliberate visible delta: the configured separator now joins every primary segment. Golden pinned post-cutover in `tests/signal-golden.test.ts` (pre-cutover baseline preserved in git history at 84b2e2a).
|
|
15
|
+
- Multi-row layout: segments declare a visual row count (`LayoutSegment.height?` + auto-derive from embedded `\n`); `computeLaneLayout` returns `primaryRowCount`/`secondaryRowCount`; `paintLayout`/`paintSecondary` render one string per row with the separator on row 0 only.
|
|
16
|
+
- Motion rail: lantern sigil as 3-row active rail for the streaming state — a portable `#`-block lantern (sway + breath) that replaces the braille version that rendered as shade-block blobs on terminals without braille fonts. ASCII fallback stays a directional comet; `ready` = calm flat `─` track; `compacting` = inward heads.
|
|
17
|
+
- Bash v2: PTY session core (`bash-mode/pty-session.ts`, SGR-safe ANSI filter, sentinel-based command boundary) plus the long-lived `PtyManagedShellSession` cutover: `session-factory.ts` routes `auto`/`v2` through the PTY-backed session and the editor forwards printable input to the running command's stdin while it runs (interactive programs work; Ctrl-C stays an interrupt). When `script(1)` is missing, commands degrade per-run to plain pipes with a one-time warning. The legacy pipe-based `ManagedShellSession` is deleted.
|
|
18
|
+
- Skill Studio modules: shell + state machine + `/studio` command, list/inspect/actions (create from template, overwrite confirm, doctor), DeepWiki client with 7-day disk cache (`src/studio/deepwiki/`), AI advice engine with pi-ai streaming and char-capped context (`src/studio/advise/`), and an advice pane that streams + inserts into the session. Operator exposure remains deferred and fail-closed until the Studio panes are connected.
|
|
19
|
+
- Versioned autoresearch harness (`.auto/measure.sh`, `checks.sh`, `config.json`, `prompt.md`): one-run `METRIC` output for test/typecheck/circular plus signal-render and registry micro-benchmarks.
|
|
20
|
+
- Tests: 706 passing across new and existing suites; full `typecheck`, `madge --circular`, and `npm test` green.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- `segmentHeight` counted trailing empty lines as extra rows, forcing an empty full-width row below a segment — the "big block under every line" render bug.
|
|
24
|
+
- `paintLayout` row-1+ separator padding used `separator.length` (ANSI bytes) instead of `visibleWidth(separator)` — a dead column between lanes on multi-row content.
|
|
25
|
+
- Multi-row rail color: `renderActivity` now wraps each sigil row with its own ANSI color+reset, so rows 1+ render in the lane accent instead of falling back to the terminal default.
|
|
26
|
+
- `activityForEvent` and `defaultMotionFor` had no `default` case — unknown events leaked the literal text `undefined` into the powerline.
|
|
27
|
+
- `MotionScheduler.subscribe` now calls `onDone` on an existing consumer with the same id before replacing it (a silent leak on re-subscribe).
|
|
28
|
+
- `setSignalEvent` wraps `scheduler.subscribe` in try/catch so a throw leaves the runtime in a clean idle state instead of `active=true` with `release=null`.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- Version numbering: the ladder had raced up to 1.7.x while the shipped feature depth was a handful of patches. This release restores the honest number (1.4.3) and `chooseBump` now defaults to `patch` for normal `feat:` commits — only an explicit breaking marker (`feat!:`, `BREAKING CHANGE`) auto-promotes (to `major`); `minor` is an explicit manual choice.
|
|
32
|
+
|
|
33
|
+
### Deferred
|
|
34
|
+
- Editor live-tail and v1 session cleanup.
|
|
35
|
+
- PTY echo/privacy.
|
|
36
|
+
- Paste while running.
|
|
37
|
+
- Managed-suite pipe-mode safeguards.
|
|
38
|
+
- Studio operator exposure until its panes are connected.
|
|
39
|
+
- Deck left-rail navigation bug (arrow-down skipping straight to the skills list).
|
|
40
|
+
|
|
5
41
|
## [1.4.1] - 2026-08-27
|
|
6
42
|
|
|
7
43
|
## [1.4.0] - 2026-08-27
|
package/README.md
CHANGED
|
@@ -12,12 +12,17 @@
|
|
|
12
12
|
# pi-wishcraft
|
|
13
13
|
|
|
14
14
|
Cockpit and harness for the [pi](https://github.com/badlogic/pi-mono) coding agent: a live status bar, overlay menus, skills, an idea inbox, sticky bash, hooks, and tool-input repairs. Stock pi stays the engine. This package is the operator layer.
|
|
15
|
-
|
|
16
15
|
Kongming lanterns started as battlefield signals and later carried wishes. Wishcraft is that split in a coding session: telemetry on the bar, thoughts you can park without interrupting the run.
|
|
17
16
|
|
|
18
17
|
Install `@groeponline/pi-wishcraft`. It is listed on the [Pi package catalog](https://pi.dev/packages/@groeponline/pi-wishcraft). Grew out of [`nicobailon/pi-powerline-footer`](https://github.com/nicobailon/pi-powerline-footer). Maintained by [GroepOnline](https://github.com/GroepOnline).
|
|
19
18
|
|
|
20
|
-
Guides live in [`docs/`](docs/index.md). This
|
|
19
|
+
Guides live in [`docs/`](docs/index.md). This is the Pi extension contract: what ships, how to install it, and what can fail.
|
|
20
|
+
|
|
21
|
+
## Quick start tutorial
|
|
22
|
+
|
|
23
|
+
Install, restart Pi (or run `/reload`), then press `alt+p` for the Deck. Try `/signal menu`, `/skills`, `/ideas`, and `ctrl+shift+b`. Full setup: [docs/configuration.md](docs/configuration.md).
|
|
24
|
+
|
|
25
|
+
## v2 Platform (in this release)
|
|
21
26
|
|
|
22
27
|
## Install
|
|
23
28
|
|
|
@@ -27,13 +32,13 @@ Pi package manager (usual path):
|
|
|
27
32
|
pi install npm:@groeponline/pi-wishcraft
|
|
28
33
|
```
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
For a local checkout or CI environment, run the repository bootstrap script with Bash:
|
|
31
36
|
|
|
32
37
|
```bash
|
|
33
|
-
|
|
38
|
+
bash ./scripts/install.sh
|
|
34
39
|
```
|
|
35
40
|
|
|
36
|
-
Then restart
|
|
41
|
+
Then restart Pi or `/reload`. Pi host packages are declared as `peerDependencies: "*"`, matching the Pi package contract.
|
|
37
42
|
|
|
38
43
|
## What you get
|
|
39
44
|
|
|
@@ -48,6 +53,8 @@ Then restart pi or `/reload`. Pi host packages are declared as `peerDependencies
|
|
|
48
53
|
| Hooks + repairs | Command hooks on pi events. Custom-tool input repairs before execution. Kill-switch: `wishcraft.hooksEnabled`. |
|
|
49
54
|
| Read hints | Appends a one-line continuation hint after a partial `read`, so the model knows the next offset. Opt-out: `wishcraft.readHints: false`. |
|
|
50
55
|
| Policy | In-process deny/inject rules in global settings. No spawn. Kill-switch: `wishcraft.policyEnabled`. |
|
|
56
|
+
| Working indicator | Four deterministic styles (`dots`, `pulse`, `bar`, `ascii`) with accessibility-aware static fallbacks. |
|
|
57
|
+
| Welcome art | `lantern`, `balloon`, and `normal` opening artwork, each with a different silhouette and narrow-terminal fallback. |
|
|
51
58
|
|
|
52
59
|
Pi owns the footer chrome, feed scrolling, and input. Wishcraft supplies widgets, overlays, and the bash/stash/editor integrations. The bar is not clickable; actions are commands and overlays.
|
|
53
60
|
|
|
@@ -126,7 +133,9 @@ At 80% the cost segment warns; at 100% it goes red and welcome notifies. `/usage
|
|
|
126
133
|
|
|
127
134
|
## Hooks
|
|
128
135
|
|
|
129
|
-
Hooks are commands that read JSON on stdin.
|
|
136
|
+
Hooks are commands that read JSON on stdin. See [docs/configuration.md](docs/configuration.md) for examples.
|
|
137
|
+
|
|
138
|
+
Definitions come from the **global** agent settings file only. Project `.pi/settings.json` cannot install new hook commands. `wishcraft.hooksEnabled: false` disables every hook without deleting the config.
|
|
130
139
|
|
|
131
140
|
```json
|
|
132
141
|
{
|
|
@@ -147,7 +156,7 @@ Hooks are commands that read JSON on stdin. Definitions come from the **global**
|
|
|
147
156
|
}
|
|
148
157
|
```
|
|
149
158
|
|
|
150
|
-
**
|
|
159
|
+
**Example hook** (exit 2 = deny):
|
|
151
160
|
|
|
152
161
|
```bash
|
|
153
162
|
#!/usr/bin/env bash
|
|
@@ -161,7 +170,7 @@ fi
|
|
|
161
170
|
exit 0
|
|
162
171
|
```
|
|
163
172
|
|
|
164
|
-
**
|
|
173
|
+
**Another example** (append-only, never blocks):
|
|
165
174
|
|
|
166
175
|
```bash
|
|
167
176
|
#!/usr/bin/env bash
|
|
@@ -169,7 +178,7 @@ mkdir -p "$HOME/.pi/agent/logs"
|
|
|
169
178
|
cat >> "$HOME/.pi/agent/logs/write-audit.jsonl"
|
|
170
179
|
```
|
|
171
180
|
|
|
172
|
-
**SessionStart
|
|
181
|
+
**SessionStart example** (extra context, never blocks):
|
|
173
182
|
|
|
174
183
|
```bash
|
|
175
184
|
#!/usr/bin/env bash
|
|
@@ -188,7 +197,8 @@ Repairs run on custom/extension tools only, before hooks: drop null optionals, p
|
|
|
188
197
|
|
|
189
198
|
## Policy
|
|
190
199
|
|
|
191
|
-
Declarative deny/inject rules in the
|
|
200
|
+
Declarative deny/inject rules live in the global settings file; see [docs/configuration.md](docs/configuration.md).
|
|
201
|
+
No shell commands — pure in-process regex. Evaluated before command hooks. `wishcraft.policyEnabled: false` disables policy without deleting rules.
|
|
192
202
|
|
|
193
203
|
```json
|
|
194
204
|
{
|
|
@@ -211,24 +221,18 @@ Declarative deny/inject rules in the **global** agent settings file. No shell co
|
|
|
211
221
|
}
|
|
212
222
|
```
|
|
213
223
|
|
|
214
|
-
**deny** — regex on tool input (`bash` uses `command`; other tools use JSON-serialized input). First match wins; the tool call is blocked with `reason`.
|
|
215
|
-
|
|
216
|
-
**inject** — regex on file path after a matching tool completes; context is appended to the tool result (same shape as postToolUse hook `additionalContext`).
|
|
217
224
|
|
|
218
225
|
## Limits
|
|
219
226
|
|
|
220
227
|
- No mouse on the live footer. Pi core owns that surface.
|
|
221
228
|
- No second `alt+i` product. Ports stay on `alt+i`; other detail is `→` in the navigator.
|
|
222
229
|
- ChefGroep status keys (`powerline.preset`, `powerline.tps`, `powerline.ports`) exist for other extensions. They are not the public pitch.
|
|
223
|
-
- The legacy `@groeponline/pi-powerline-footer` package is deprecated
|
|
230
|
+
- The legacy `@groeponline/pi-powerline-footer` package is deprecated in favor of `@groeponline/pi-wishcraft`.
|
|
224
231
|
- Tags are not rewritten. 0.19.x through current stay on the timeline.
|
|
225
232
|
|
|
226
233
|
## vNext Direction
|
|
227
234
|
|
|
228
|
-
Wishcraft is
|
|
229
|
-
|
|
230
|
-
- **[vNext Stacked PR Release Plan](docs/design/vnext-release-plan.md)**: Specifications for PR0 through PR8.
|
|
231
|
-
- **[Design Corpus](docs/index.md#design-system--vnext-specifications)**: Deck, Signal, Motion Gallery, tokens, and the 10 structural presets. `npm run preview` renders the Deck frames.
|
|
235
|
+
Wishcraft is Pi's animated operator layer. See the [release plan](docs/design/vnext-release-plan.md) and [design corpus](docs/index.md#design-system--vnext-specifications).
|
|
232
236
|
|
|
233
237
|
## Docs
|
|
234
238
|
|
package/ROADMAP.md
CHANGED
|
@@ -14,11 +14,11 @@ Baseline: **v1.3.1** (`e79737f`, tag `v1.3.1` 2026-08-25, npm `1.3.1`) — `orig
|
|
|
14
14
|
|
|
15
15
|
## 1.4 — Extension Contract (current, split)
|
|
16
16
|
|
|
17
|
-
**1.4a — Settings registry** — *shipped* `0bfa5a2` `feat(config): typed settings registry` on `origin/main`. `src/config/settings-registry.ts` (28 defs, `SETTING_DEFAULTS`, `get/validate` O(1) maps, `SETTING_GROUPS`). `src/extension/settings/wishcraft-config-items.ts` now renders from registry (124 lines removed). Tests `tests/settings-registry.test.ts` + `wishcraft-config.test.ts` green
|
|
17
|
+
**1.4a — Settings registry** — *partly shipped* `0bfa5a2` `feat(config): typed settings registry` on `origin/main`. `src/config/settings-registry.ts` (28 defs, `SETTING_DEFAULTS`, `get/validate` O(1) maps, `SETTING_GROUPS`). `src/extension/settings/wishcraft-config-items.ts` now renders from registry (124 lines removed). Tests `tests/settings-registry.test.ts` + `wishcraft-config.test.ts` green. Still TODO: `contributeSettings()` Pi-contribution + `defaults/migrations` + docgen from registry.
|
|
18
18
|
|
|
19
|
-
**1.4b — Contribution API** — *
|
|
19
|
+
**1.4b — Contribution API** — *shipped on main* (1.4 line; `src/extension/contrib/{types,registry,appearance}.ts`, tests `contrib-registry.test.ts` + `appearance-contrib.test.ts`). `registerDeckRoute`/`registerSignalSource`/`registerAppearanceContribution` (validate `id /^[a-z0-9][a-z0-9-_]*$/`, dedup, `false` never throw). Deck wired via `getAllDeckRouteDefs()`/`isDeckRoute()` (calls `getContributedDeckRoutes()`); Signal render calls `getContributedSignalSources()` fault-isolated (render.ts:196-203; a throwing/empty source never takes down Signal). Still TODO: `registerMotion()`, `registerRecipeOrAction()` as separate slices (one PR per contract).
|
|
20
20
|
|
|
21
|
-
**1.4c — Signal wiring** — *
|
|
21
|
+
**1.4c — Signal wiring** — *shipped* (with 1.4b): both Deck and Signal contributions render fault-isolated; covered by `contrib-registry.test.ts` + `appearance-contrib.test.ts`.
|
|
22
22
|
|
|
23
23
|
**1.4d — Preset composer** — Decouple `PresetDef` `tokens/chrome/signal/motion/deck/welcome/glyphs` from `src/config/structural-presets.ts` to allow `contributedAppearance` overrides per preset with validation. Done when `tests/structural-presets.test.ts` + `tokens.test.ts` cover contributed palette/motion.
|
|
24
24
|
|
package/STRATEGY.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pi-wishcraft
|
|
3
|
+
last_updated: 2026-08-28
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# pi-wishcraft Strategy
|
|
7
|
+
|
|
8
|
+
## Target problem
|
|
9
|
+
|
|
10
|
+
pi-wishcraft today is a bundle of loosely-coupled segments (motion, ports, bash, vibes, skills) — each works on its own, none hangs together. There is no "this is how pi should feel" layer. The crux: wishcraft is not yet a default skin, it is a grab-bag of features.
|
|
11
|
+
|
|
12
|
+
## Our approach
|
|
13
|
+
|
|
14
|
+
pi-wishcraft is a customizable, feel-good default skin for pi — vibrant, warm, and tunable down to the last spinner. Where a tool must be finished to ship, a skin evolves, so we ship fast and iterate; perfection is the wrong goal. The bet: **vibe + customization depth** — the difference between wishcraft and any other pi extension is not feature count, it is that the skin lives.
|
|
15
|
+
|
|
16
|
+
## Who it's for
|
|
17
|
+
|
|
18
|
+
**Primary:** someone installing pi on day 1, typing `/wishcraft`, and choosing their first preset within five minutes — without reading the manual. They are hiring pi-wishcraft to make pi feel like their terminal, with essentials (bash, /cd, skills, health) already working on day one.
|
|
19
|
+
|
|
20
|
+
## Key metrics
|
|
21
|
+
|
|
22
|
+
- **Essentials-reliability** — bash-mode session breaks + ports/health crashes per release, measured via GitHub issues and npm feedback. Regresses the moment the foundation rots.
|
|
23
|
+
- **First-touch customization** — % of fresh installs where a settings-file write happens within 7 days (opt-in local usage file). Stand-in for "felt at home" until real telemetry is in place.
|
|
24
|
+
- **Customization depth** — average number of active theme / segment / preset tweaks per user after 30 days (settings file). Indicates "made it theirs."
|
|
25
|
+
|
|
26
|
+
## Tracks
|
|
27
|
+
|
|
28
|
+
### How we work
|
|
29
|
+
|
|
30
|
+
Every track runs the same loop: **observe → root-cause → simplify → ship**. Half-finished work is removed, not shipped. Presets are data, not code, so the community can contribute via PR.
|
|
31
|
+
|
|
32
|
+
### Skin & feel
|
|
33
|
+
|
|
34
|
+
The v2 cutover landing this week: powerline, welcome, bash, ports, skills, motion as one coherent skin — not a pile of segments. The first preset must work out of the box.
|
|
35
|
+
|
|
36
|
+
_Loop in this track:_ segment crashes and motion stalls are bugs, not features; we trace and remove them before adding more.
|
|
37
|
+
|
|
38
|
+
### Customization depth
|
|
39
|
+
|
|
40
|
+
Every detail tunable — colour, font, animation, spinner, preset. Deck and (soon) Studio are the two entry points. Presets and segments live as data so users and the community can extend without forking.
|
|
41
|
+
|
|
42
|
+
_Loop in this track:_ presets that no one uses get simplified or removed; a knob nobody touches is dead weight.
|
|
43
|
+
|
|
44
|
+
### Skill Studio & modes
|
|
45
|
+
|
|
46
|
+
Studio (in progress, U5+) as the in-process workshop for skills, plus extension modes like creator, PTC, minimal, code, verbose. Joins up with deepseek, codex, claude, and oh-my-pi conventions — wishcraft does not ship its own harness.
|
|
47
|
+
|
|
48
|
+
_Loop in this track:_ advice that users ignore is a signal; modes that feel "cool" but unused get cut.
|
|
49
|
+
|
|
50
|
+
## Not working on
|
|
51
|
+
|
|
52
|
+
- A standalone agents / harness runtime — wishcraft plugs into existing harnesses, never replaces one.
|
|
53
|
+
- Tool-product features that don't make the skin more beautiful or more tunable (analytics dashboards, productivity trackers).
|
|
54
|
+
- Mobile / web / cloud variants — pi is terminal-native; that stays the case.
|
|
@@ -11,7 +11,9 @@ export const DEFAULT_EDITOR_BOUNDARY_SHORTCUTS: EditorBoundaryShortcuts = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export function isPrintableInput(data: string): boolean {
|
|
14
|
-
|
|
14
|
+
const points = [...data];
|
|
15
|
+
if (points.length !== 1) return false;
|
|
16
|
+
return (points[0]?.codePointAt(0) ?? 0) >= 32;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export function isCommandUndoShortcut(data: string): boolean {
|
package/bash-mode/editor.ts
CHANGED
|
@@ -42,6 +42,7 @@ export class BashModeEditor extends CustomEditor {
|
|
|
42
42
|
private ghost: GhostSuggestion | null = null;
|
|
43
43
|
private ghostAbort: AbortController | null = null;
|
|
44
44
|
private ghostToken = 0;
|
|
45
|
+
private ghostSchedule: ReturnType<typeof setTimeout> | null = null;
|
|
45
46
|
|
|
46
47
|
constructor(
|
|
47
48
|
tui: any,
|
|
@@ -76,9 +77,26 @@ export class BashModeEditor extends CustomEditor {
|
|
|
76
77
|
this.scheduleGhostUpdate();
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
/** Resolve a ghost immediately for callers that need a settled refresh. */
|
|
81
|
+
async refreshGhostSuggestionNow(): Promise<void> {
|
|
82
|
+
const text = this.getExpandedText();
|
|
83
|
+
const currentToken = ++this.ghostToken;
|
|
84
|
+
this.ghostAbort?.abort();
|
|
85
|
+
this.ghostSchedule = null;
|
|
86
|
+
|
|
87
|
+
const controller = new AbortController();
|
|
88
|
+
this.ghostAbort = controller;
|
|
89
|
+
const ghost = await this.optionsRef.resolveGhostSuggestion(text, controller.signal);
|
|
90
|
+
if (controller.signal.aborted || currentToken !== this.ghostToken) return;
|
|
91
|
+
this.ghost = ghost;
|
|
92
|
+
this.tui.requestRender();
|
|
93
|
+
}
|
|
94
|
+
|
|
79
95
|
clearGhostSuggestion(): void {
|
|
80
96
|
this.ghostAbort?.abort();
|
|
81
97
|
this.ghostAbort = null;
|
|
98
|
+
if (this.ghostSchedule) clearTimeout(this.ghostSchedule);
|
|
99
|
+
this.ghostSchedule = null;
|
|
82
100
|
this.ghost = null;
|
|
83
101
|
}
|
|
84
102
|
|
|
@@ -145,6 +163,24 @@ export class BashModeEditor extends CustomEditor {
|
|
|
145
163
|
return;
|
|
146
164
|
}
|
|
147
165
|
|
|
166
|
+
// v2 forward-mode: while a command runs, printable input goes to the
|
|
167
|
+
// PTY stdin (AE1). Interrupt stays a keybinding above; key-releases
|
|
168
|
+
// and multi-key sequences stay in the editor. Enter (\r) and EOF
|
|
169
|
+
// (\x04) must also forward — line-oriented stdin programs (read,
|
|
170
|
+
// sudo, git rebase -i) cannot proceed without a line terminator.
|
|
171
|
+
// Opt-in via forwardWhileRunning so v1 run-blocked behavior unchanged.
|
|
172
|
+
if (
|
|
173
|
+
bashMode &&
|
|
174
|
+
this.optionsRef.isShellRunning() &&
|
|
175
|
+
(this.optionsRef.forwardWhileRunning?.() ?? false) &&
|
|
176
|
+
this.optionsRef.onForwardInput != null &&
|
|
177
|
+
!isKeyRelease(data) &&
|
|
178
|
+
(isPrintableInput(data) || data === "\r" || data === "\n" || data === "\x04")
|
|
179
|
+
) {
|
|
180
|
+
this.optionsRef.onForwardInput(data);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
148
184
|
if (
|
|
149
185
|
bashMode &&
|
|
150
186
|
this.keybindingsRef.matches(data, "tui.editor.cursorUp")
|
|
@@ -338,23 +374,24 @@ export class BashModeEditor extends CustomEditor {
|
|
|
338
374
|
const text = this.getExpandedText();
|
|
339
375
|
const currentToken = ++this.ghostToken;
|
|
340
376
|
this.ghostAbort?.abort();
|
|
377
|
+
if (this.ghostSchedule) clearTimeout(this.ghostSchedule);
|
|
378
|
+
this.ghostSchedule = null;
|
|
341
379
|
|
|
342
380
|
const controller = new AbortController();
|
|
343
381
|
this.ghostAbort = controller;
|
|
382
|
+
// Start immediately so editor actions have deterministic refresh semantics.
|
|
383
|
+
// The abort signal and token still discard stale results when typing races
|
|
384
|
+
// with an in-flight history/filesystem lookup.
|
|
344
385
|
this.optionsRef
|
|
345
386
|
.resolveGhostSuggestion(text, controller.signal)
|
|
346
387
|
.then((ghost) => {
|
|
347
|
-
if (controller.signal.aborted || currentToken !== this.ghostToken)
|
|
348
|
-
return;
|
|
388
|
+
if (controller.signal.aborted || currentToken !== this.ghostToken) return;
|
|
349
389
|
this.ghost = ghost;
|
|
350
390
|
this.tui.requestRender();
|
|
351
391
|
})
|
|
352
392
|
.catch((error) => {
|
|
353
|
-
if (error instanceof Error && error.message === "aborted") return;
|
|
354
|
-
console.debug(
|
|
355
|
-
"[wishcraft] Failed to resolve bash ghost suggestion:",
|
|
356
|
-
error,
|
|
357
|
-
);
|
|
393
|
+
if (controller.signal.aborted || (error instanceof Error && error.message === "aborted")) return;
|
|
394
|
+
console.debug("[wishcraft] Failed to resolve bash ghost suggestion:", error);
|
|
358
395
|
});
|
|
359
396
|
}
|
|
360
397
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forward state for bash v2 (U4). Pure reducer: given a ForwardState and
|
|
3
|
+
* raw input data, decide what the PTY session should do (stdin, interrupt,
|
|
4
|
+
* nothing) and what the new state should be. The BashForwardEditor drives
|
|
5
|
+
* this; it has no TUI imports so the unit is fully testable.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { matchesKey } from "@earendil-works/pi-tui";
|
|
9
|
+
|
|
10
|
+
export interface ForwardState {
|
|
11
|
+
running: boolean;
|
|
12
|
+
lastExitCode: number | null;
|
|
13
|
+
cwd: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type PtyAction = "stdin" | "interrupt";
|
|
17
|
+
|
|
18
|
+
export interface ForwardDecision {
|
|
19
|
+
state: ForwardState;
|
|
20
|
+
ptyAction?: PtyAction;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const DONE_SENTINEL = "__PI_DONE__";
|
|
24
|
+
|
|
25
|
+
export function createForwardState(): ForwardState {
|
|
26
|
+
return { running: false, lastExitCode: null, cwd: "" };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseSentinel(data: string): { exitCode: number; cwd: string } | null {
|
|
30
|
+
if (!data.startsWith(`${DONE_SENTINEL}:`)) return null;
|
|
31
|
+
const rest = data.slice(DONE_SENTINEL.length + 1);
|
|
32
|
+
const colon = rest.indexOf(":");
|
|
33
|
+
if (colon === -1) return null;
|
|
34
|
+
const exitCode = Number.parseInt(rest.slice(0, colon), 10);
|
|
35
|
+
const cwd = rest.slice(colon + 1);
|
|
36
|
+
if (!Number.isFinite(exitCode)) return null;
|
|
37
|
+
return { exitCode, cwd };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function handleForwardInput(
|
|
41
|
+
state: ForwardState,
|
|
42
|
+
data: string,
|
|
43
|
+
): ForwardDecision {
|
|
44
|
+
if (data.length === 0) return { state };
|
|
45
|
+
|
|
46
|
+
const sentinel = parseSentinel(data);
|
|
47
|
+
if (sentinel) {
|
|
48
|
+
return {
|
|
49
|
+
state: {
|
|
50
|
+
running: false,
|
|
51
|
+
lastExitCode: sentinel.exitCode,
|
|
52
|
+
cwd: sentinel.cwd,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!state.running) return { state };
|
|
58
|
+
|
|
59
|
+
if (matchesKey(data, "ctrl+c") || data === "\x03") {
|
|
60
|
+
return { state, ptyAction: "interrupt" };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { state, ptyAction: "stdin" };
|
|
64
|
+
}
|