@junghanacs/entwurf 0.12.4 → 0.12.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/CHANGELOG.md +36 -0
- package/README.md +9 -1
- package/VERIFY.md +18 -28
- package/docs/setup-clean-host.md +27 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js +218 -0
- package/mcp/entwurf-bridge/tsconfig.build.json +18 -1
- package/package.json +4 -4
- package/pi/meta-bridge/entwurf-meta-receive/hooks/hooks.json +3 -3
- package/pi-extensions/meta-bridge-hook.ts +8 -3
- package/run.sh +351 -42
- package/scripts/__pycache__/meta-bridge-state.cpython-312.pyc +0 -0
- package/scripts/__pycache__/meta-bridge-state.cpython-313.pyc +0 -0
- package/scripts/__pycache__/register-pi-package.cpython-313.pyc +0 -0
- package/scripts/meta-bridge-doctor.sh +54 -9
- package/scripts/meta-bridge-install.sh +50 -13
- package/scripts/meta-bridge-state.py +60 -3
- package/scripts/meta-bridge-uninstall.sh +24 -0
- package/scripts/register-pi-package.py +149 -0
- package/scripts/smoke-meta-install-state.sh +164 -8
- package/scripts/smoke-user-scope-citizen.sh +129 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.12.6 — 2026-07-03
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Claude Code meta-bridge live marketplace sources now live outside the checkout for dev and npm installs alike.** Development clones and installed packages both assemble the plugin bundle under `$XDG_DATA_HOME/entwurf/meta-bridge/.assembled`; the checkout or `node_modules` tree is now only the source origin. This removes the class where `pnpm check`, `git clean -xfd`, or a repo-local uninstall smoke could delete a user-scope Claude marketplace source and leave new Claude Code sessions at statusline `?`. The installer still chooses `.ts` vs compiled `.js` hook artifacts by source origin, but the live registration path is the same XDG artifact lane.
|
|
12
|
+
- **Meta-bridge uninstall now uses the recorded live-artifact path and fails before side effects when the state is corrupt.** The install state's `assembledMarketplacePath` is the SSOT for uninstall, doctor, and state checks. Uninstall validates the full `…/entwurf/meta-bridge/.assembled` suffix before removing Claude plugin/MCP registrations or deleting state, so malformed paths such as `/` or `…/entwurf/meta-bridge/not-assembled` fail loud with zero Claude side effects and leave state/artifacts intact. `state.py check` also rejects missing, empty, or malformed recorded paths even when settings agree with the corruption.
|
|
13
|
+
- **User-scope pi package registration is now part of setup.** `./run.sh setup` registers `entwurf` in user-scope pi settings so `--entwurf-control` and the provider surface load from foreign cwd sessions, not only from a project-local `.pi/settings.json`. A hermetic `smoke-user-scope-citizen` covers idempotency, stale normalization, remove symmetry, unrelated-key preservation, and corrupt-shape fail-loud behavior.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **The install toolchain is now aligned to one pnpm 11 lane and one setup surface.** The repo dropped the obsolete `packageManager` pin and `.npmrc`, moved pnpm policy to `pnpm-workspace.yaml`, updated CI to pnpm 11.9, and treats `./run.sh setup` as the single local install path instead of a split `pi install` flow.
|
|
18
|
+
- **The next delivery lane is pinned before implementation.** `NEXT.md` now records the agy/Antigravity delivery adapter constraints: direct-inject/native-push is separate from pi's control-socket liveness domain, registration is explicit, receiver markers are not reused for native-push replyability, and install/doctor/uninstall must inherit the source-origin/live-artifact boundary proven by this release.
|
|
19
|
+
- **The future fresh-spawn lane stays mux-visible by default.** The mux driver plan now keeps minting and mux transport separate, treats tmux/zmx as drivers behind a leaf interface, and keeps fresh pi-native GPT launch visible instead of reviving hidden background one-shots as the default.
|
|
20
|
+
|
|
21
|
+
### Verification
|
|
22
|
+
|
|
23
|
+
- `pnpm check` passed on 2026-07-03 after the XDG live-artifact move, recorded-path uninstall hardening, user-scope citizen smoke, pnpm 11 setup cleanup, and NEXT lane updates.
|
|
24
|
+
- `./run.sh smoke-meta-install-state` passed with the new direct install→XDG proof, recorded-path mismatch case, corrupt basename/missing-field fail-loud cases, and checkout-internal `.assembled` boundary fingerprint.
|
|
25
|
+
- `./run.sh doctor-meta-bridge` passed on 2026-07-03 with source/assembled/installed writer parity all v2 and the assembled bundle under `$XDG_DATA_HOME/entwurf/meta-bridge/.assembled`.
|
|
26
|
+
- `./run.sh check-pack-install` passed on 2026-07-03, proving the npm consumer path keeps the stable XDG marketplace source and user-scope pi citizen registration.
|
|
27
|
+
- `LIVE=1 ./run.sh release-gate /tmp/psa-release-gate-0.12.6.ddCcmz` passed on 2026-07-03; log `/tmp/pi-tmux-entwurf-release-gate-0126.log`; summary `MUST: PASS=17 FAIL=0 SKIP=0`, advisory `BEHAVIOR: PASS=0 FAIL=1` (`smoke-resident-garden-guard` positive `/gnew T3` model-in-loop identity turn; non-blocking).
|
|
28
|
+
|
|
29
|
+
## 0.12.5 — 2026-07-01
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **The plugin hook now runs as compiled JS when installed — the strip-types class is closed for good.** The SessionStart/UserPromptSubmit hook ran as a raw `meta-bridge-hook.ts`. When the marketplace source sits below `node_modules` (a global npm/pnpm install), Claude executes that `.ts` directly and Node refuses strip-types there (`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`), so every session failed. The hook was the last `.ts`-at-runtime surface; `build-bridge` now emits `dist/pi-extensions/meta-bridge-hook.js` and the installer copies that compiled closure (dev clones still run the `.ts` for transparent editing), mirroring the same installed-vs-dev split `start.sh` (0.12.1) and the store-doctor (0.12.4) already use. `check-pack-install` now runs the installed compiled hook **from under `node_modules` with plain `node`** and proves a raw `.ts` at the same location is refused — the exact fence that broke real hosts is a green regression. The doctor's writer-version parity compares `meta-session.<js|ts>` by install mode so a compiled bundle no longer false-STALEs against the `.ts` source.
|
|
34
|
+
- **Installed meta-bridge paths are now stable across package upgrades.** The installed statusline uses a new `entwurf-statusline` bin shim (matching the existing `entwurf-bridge` MCP shim), and installed meta-bridge marketplace assemblies move out of versioned pnpm store paths into a version-stable operator data directory. Package upgrades still require `entwurf install-meta-bridge` to refresh the Claude plugin bundle/cache, but old-store dead links no longer strand `statusLine` or marketplace source paths. The doctor now also executes the cached SessionStart hook once in an isolated temp agent dir, catching stale plugin-cache / hook-syntax failures before the next real Claude Code session.
|
|
35
|
+
|
|
36
|
+
### Verification
|
|
37
|
+
|
|
38
|
+
- Oracle and local review both reproduced the installed hook fence: compiled `meta-bridge-hook.js` runs from under `node_modules` with plain `node`, while the raw `.ts` at the same location fails specifically with `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`.
|
|
39
|
+
- `./run.sh check-pack` and `./run.sh check-pack-install` passed on 2026-07-01 with the hook-JS tarball requirements, baked `hooks.json` `.js` assertion, and raw-`.ts` fence reproduction.
|
|
40
|
+
- `pnpm check` passed on 2026-07-01 after the hook-JS + stable installed-path changes.
|
|
41
|
+
- `./run.sh install-meta-bridge && ./run.sh doctor-meta-bridge` passed from the development clone after re-materializing the gitignored `.assembled` bundle.
|
|
42
|
+
|
|
7
43
|
## 0.12.4 — 2026-07-01
|
|
8
44
|
|
|
9
45
|
### Fixed
|
package/README.md
CHANGED
|
@@ -80,10 +80,11 @@ A few words that look unusual for a coding tool.
|
|
|
80
80
|
adapter that hosts the ACP plugin and live control-socket surface, but the base
|
|
81
81
|
install is **not** `pi install npm:...` anymore.
|
|
82
82
|
|
|
83
|
-
The package exposes
|
|
83
|
+
The package exposes three bins:
|
|
84
84
|
|
|
85
85
|
- `entwurf` → `run.sh` (installer, checks, meta-bridge doctor/install)
|
|
86
86
|
- `entwurf-bridge` → the MCP stdio launcher (`mcp/entwurf-bridge/start.sh`)
|
|
87
|
+
- `entwurf-statusline` → the Claude Code statusline renderer (`scripts/meta-bridge-statusline.sh`); a stable bin shim so a package upgrade tracks the current version without rewriting Claude settings
|
|
87
88
|
|
|
88
89
|
The bridge does not provide Claude credentials, tokens, or subscription access,
|
|
89
90
|
and does not bypass any backend auth. Whatever the operator's local `claude` /
|
|
@@ -187,6 +188,13 @@ raw `.ts` helpers under `node_modules`. If the doctor reports
|
|
|
187
188
|
`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`, reinstall a current package before
|
|
188
189
|
trusting the floor result.
|
|
189
190
|
|
|
191
|
+
After upgrading a globally installed package, run `entwurf install-meta-bridge`
|
|
192
|
+
again before trusting native Claude Code sessions. The installed statusline and
|
|
193
|
+
MCP entries use stable bin shims, and the marketplace source uses a version-stable
|
|
194
|
+
operator data dir, but the Claude plugin bundle/cache still has to be
|
|
195
|
+
re-materialized for the new package writer. Existing Claude Code sessions should
|
|
196
|
+
be restarted after reinstall.
|
|
197
|
+
|
|
190
198
|
For manual configuration, [`pi/settings.reference.json`](./pi/settings.reference.json)
|
|
191
199
|
shows the pi adapter settings shape, and the external-host examples below show
|
|
192
200
|
plain MCP registrations.
|
package/VERIFY.md
CHANGED
|
@@ -88,46 +88,36 @@ The goal is not merely "invoke Claude Code." We want:
|
|
|
88
88
|
|
|
89
89
|
## 1. Setup
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
**One install command to remember: `./run.sh setup <project>`.** It is idempotent — re-run the exact same command whenever anything looks wrong. There is no second install surface to juggle: from a clone `setup` runs the whole floor in order.
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
### 1.1 Path A — consumer install
|
|
93
|
+
1. `pnpm install` — bundles pi (a dev/peer dependency; no separate `pi install` step) and builds the bridge
|
|
94
|
+
2. project wiring → `<project>/.pi/settings.json` `entwurfProvider.mcpServers.entwurf-bridge`
|
|
95
|
+
3. meta-bridge global plugin — only when a native harness (`claude`) is on PATH; a pi-only host skips it cleanly
|
|
96
|
+
4. `entwurf-bridge` install smoke (`validate_entwurf_bridge`)
|
|
99
97
|
|
|
100
98
|
```bash
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
pi --list-models entwurf # curated model surface
|
|
105
|
-
pi --provider entwurf --model claude-sonnet-5 -p "reply with ok only" # one-turn smoke
|
|
99
|
+
git clone https://github.com/junghan0611/entwurf /path/to/entwurf && cd $_
|
|
100
|
+
./run.sh setup /path/to/consumer-project
|
|
101
|
+
# re-run the SAME command any time to repair a broken install
|
|
106
102
|
```
|
|
107
103
|
|
|
108
|
-
Expected:
|
|
104
|
+
Expected tail: `DONE: entwurf setup (pi package + meta-bridge + v2 install smoke) green.` On a host with `claude`, verify the native wiring with `./run.sh doctor-meta-bridge`.
|
|
105
|
+
|
|
106
|
+
The wiring / meta-bridge / smoke steps are internal building blocks of `setup` (`install_local_package`, `scripts/meta-bridge-install.sh`, `validate_entwurf_bridge`) — call `setup`, never the parts. Consumers who `npm install @junghanacs/entwurf` get the obvious npm surface; that path is not the developer concern here.
|
|
109
107
|
|
|
110
|
-
### 1.
|
|
108
|
+
### 1.1 Variables (optional)
|
|
111
109
|
|
|
112
110
|
```bash
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
./run.sh install /path/to/consumer-project
|
|
117
|
-
pnpm check # full deterministic floor
|
|
118
|
-
pi --provider entwurf --model claude-sonnet-5 -p "reply with ok only"
|
|
119
|
-
LIVE=1 ./run.sh release-gate /path/to/consumer-project
|
|
111
|
+
export REPO_DIR=/path/to/entwurf
|
|
112
|
+
export PROJECT_DIR=/path/to/consumer-project
|
|
113
|
+
cd "$REPO_DIR" && ./run.sh setup "$PROJECT_DIR"
|
|
120
114
|
```
|
|
121
115
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
### 1.3 Setup shortcut + variables
|
|
116
|
+
### 1.2 Live acceptance (optional)
|
|
125
117
|
|
|
126
118
|
```bash
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
export CACHE_DIR=$HOME/.pi/agent/cache/entwurf/sessions
|
|
130
|
-
cd "$REPO_DIR" && ./run.sh setup "$PROJECT_DIR"
|
|
119
|
+
LIVE=1 ./run.sh release-gate /path/to/consumer-project
|
|
120
|
+
pi --provider entwurf --model claude-sonnet-5 -p "reply with ok only" # one-turn smoke
|
|
131
121
|
```
|
|
132
122
|
|
|
133
123
|
`setup` runs `pnpm install` + `install` + meta-bridge (native harness) + the v2 install smoke; a green `setup` implies the settings.json wiring and install surface are healthy. The full live floor is still `LIVE=1 ./run.sh release-gate`.
|
package/docs/setup-clean-host.md
CHANGED
|
@@ -4,11 +4,11 @@ End-to-end install of **entwurf** on a host with only `git` available — no
|
|
|
4
4
|
node, no npm package, no pi binary, no dotfiles. The point is to validate the
|
|
5
5
|
public install surface as an outside user would experience it.
|
|
6
6
|
|
|
7
|
-
> **Scope.** This is the entwurf 0.12.
|
|
7
|
+
> **Scope.** This is the entwurf 0.12.5 install recipe. The base package install
|
|
8
8
|
> is **neutral npm**, not `pi install npm:...`. Pi is an optional adapter lane for
|
|
9
|
-
> the ACP provider / control-socket runtime. The 0.12.
|
|
9
|
+
> the ACP provider / control-socket runtime. The 0.12.5 floor fix is explicitly
|
|
10
10
|
> covered here: installed packages under `node_modules` must not run raw `.ts`
|
|
11
|
-
> doctor helpers through Node strip-types.
|
|
11
|
+
> bridge, doctor, or plugin-hook helpers through Node strip-types.
|
|
12
12
|
|
|
13
13
|
`entwurf` is a garden-citizen dispatch substrate and meta-bridge. It does not
|
|
14
14
|
provide, copy, or mediate backend credentials — it lets the official backend CLI
|
|
@@ -31,7 +31,7 @@ ssh cleanhost 'uname -a; whoami; which git node npm pi claude 2>/dev/null'
|
|
|
31
31
|
|---|---|---|
|
|
32
32
|
| Node | **24** recommended; `>=22.6.0` minimum | `engines.node` (Node strip-types / ESM runtime) |
|
|
33
33
|
| npm | bundled with Node 24 | public package install path |
|
|
34
|
-
| entwurf | `@junghanacs/entwurf` | neutral npm package; exposes `entwurf` and `entwurf-
|
|
34
|
+
| entwurf | `@junghanacs/entwurf` | neutral npm package; exposes `entwurf`, `entwurf-bridge`, and `entwurf-statusline` bins |
|
|
35
35
|
| pi binary | **optional**, `@earendil-works/pi-coding-agent >=0.80.3 <0.81` | needed only for the pi adapter / ACP provider / spawn-bg resume lane |
|
|
36
36
|
|
|
37
37
|
## Stage 0 — Node 24 via nvm
|
|
@@ -64,6 +64,7 @@ npm install -g @junghanacs/entwurf
|
|
|
64
64
|
|
|
65
65
|
which entwurf
|
|
66
66
|
which entwurf-bridge
|
|
67
|
+
which entwurf-statusline
|
|
67
68
|
entwurf --help | head -5
|
|
68
69
|
```
|
|
69
70
|
|
|
@@ -77,8 +78,11 @@ Prove the installed MCP server answers `tools/list` from inside `node_modules`.
|
|
|
77
78
|
This is the first `node_modules` strip-types regression fixed in 0.12.0: Node
|
|
78
79
|
refuses `--experimental-strip-types` for `.ts` under `node_modules`, so the
|
|
79
80
|
installed package must boot the prebuilt JS under `mcp/entwurf-bridge/dist/`.
|
|
80
|
-
The
|
|
81
|
-
`doctor-meta-bridge` helper
|
|
81
|
+
The same installed-vs-dev split then closes the other two `.ts`-at-runtime
|
|
82
|
+
surfaces on that same fence: the `doctor-meta-bridge` store-scan helper (0.12.4)
|
|
83
|
+
and the plugin `SessionStart`/`UserPromptSubmit` hook (0.12.5, runs the compiled
|
|
84
|
+
`dist/pi-extensions/meta-bridge-hook.js` when installed). Installed packages run
|
|
85
|
+
tsc-emitted JS on every one of these; dev clones keep the `.ts` source.
|
|
82
86
|
|
|
83
87
|
```bash
|
|
84
88
|
node --input-type=module <<'JS'
|
|
@@ -146,17 +150,29 @@ entwurf doctor-meta-bridge
|
|
|
146
150
|
```
|
|
147
151
|
|
|
148
152
|
On an installed package (`.../node_modules/@junghanacs/entwurf`), the doctor must
|
|
149
|
-
not try to strip-types-run raw `.ts` helpers. In the output, check for
|
|
150
|
-
0.12.
|
|
153
|
+
not try to strip-types-run raw `.ts` helpers or hooks. In the output, check for
|
|
154
|
+
these 0.12.5 floor-regression signals:
|
|
151
155
|
|
|
152
156
|
```text
|
|
157
|
+
ok cached SessionStart hook executes cleanly in an isolated temp agent dir
|
|
153
158
|
ok full store scan: no corrupt records, duplicate nativeSessionId, body/filename drift, or backend↔wakeMode contradiction
|
|
154
159
|
ok check-entwurf-v2-surface: shipped surface source present; exhaustive source-shape gate is a repo/release invariant (not run under node_modules)
|
|
155
160
|
```
|
|
156
161
|
|
|
157
|
-
If
|
|
158
|
-
is still running a pre-0.12.
|
|
159
|
-
package and re-run `entwurf install-meta-bridge && entwurf doctor-meta-bridge`.
|
|
162
|
+
If any of those sections reports `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`,
|
|
163
|
+
the host is still running a pre-0.12.5 package or a broken tarball. Reinstall the
|
|
164
|
+
current package and re-run `entwurf install-meta-bridge && entwurf doctor-meta-bridge`.
|
|
165
|
+
|
|
166
|
+
Upgrade invariant: every global npm/pnpm package upgrade must be followed by
|
|
167
|
+
`entwurf install-meta-bridge` from that same installed binary and then
|
|
168
|
+
`entwurf doctor-meta-bridge`. Installed statusline/MCP entries use stable bin
|
|
169
|
+
shims and the marketplace source lives in a version-stable operator data dir, but
|
|
170
|
+
package managers still do not re-materialize Claude's plugin bundle/cache. If a
|
|
171
|
+
dev checkout's `./run.sh doctor-meta-bridge` expects repo-owned paths while the
|
|
172
|
+
global install intentionally owns the meta-bridge (or the reverse), that is an
|
|
173
|
+
ownership mismatch — run the doctor from the surface that intentionally owns the
|
|
174
|
+
install, or reinstall from the other surface. Restart already-open Claude Code
|
|
175
|
+
sessions after changing the meta-bridge install.
|
|
160
176
|
|
|
161
177
|
A plain external MCP host can call tools but is non-replyable. A garden-native
|
|
162
178
|
meta-session has a garden id, a mailbox, and a trusted sender marker; it can call
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meta-bridge-hook — the Claude Code native-session entry shell (1.0.0 step 4).
|
|
3
|
+
*
|
|
4
|
+
* THE load-bearing hook. Shipped inside a plugin bundle whose `hooks/hooks.json`
|
|
5
|
+
* wires it to `SessionStart` (and `CwdChanged` / `UserPromptSubmit`). Because a
|
|
6
|
+
* plugin's hooks load at STARTUP (a bare skill's load only on invocation, after
|
|
7
|
+
* SessionStart has passed), this fires on every native Claude Code open and makes
|
|
8
|
+
* that session a garden citizen WITHOUT any pi JSONL of its own:
|
|
9
|
+
*
|
|
10
|
+
* stdin {session_id, transcript_path, cwd} (Claude hook envelope)
|
|
11
|
+
* -> upsertMetaSession(claude-code) idempotent create/attach the record
|
|
12
|
+
* -> gardenId the session's garden address
|
|
13
|
+
* -> arm watchPath <mailbox>/<gardenId>/inbox.signal idle-wake doorbell
|
|
14
|
+
* -> stdout hookSpecificOutput.watchPaths (SessionStart / CwdChanged only)
|
|
15
|
+
*
|
|
16
|
+
* The record is keyed/looked-up by `native_session_id` (THE authority,
|
|
17
|
+
* scanByNativeId); the idle-wake mailbox is keyed by GARDEN id — that is the
|
|
18
|
+
* whole point of the meta-bridge over the raw per-session prototype: one garden
|
|
19
|
+
* address a sender can target, decoupled from the backend's native id grammar.
|
|
20
|
+
*
|
|
21
|
+
* FAILURE POLICY (decided, see NEXT.md step 4): the runtime hook is
|
|
22
|
+
* BEST-EFFORT + LOG. It never screams into the user's terminal and never blocks
|
|
23
|
+
* startup — on any error it appends a line to `<pi-agent-dir>/meta-bridge-hook.log`
|
|
24
|
+
* and emits an empty `{}` (no arm). The fail-LOUD surface is the doctor
|
|
25
|
+
* (step 5), which reads that log + the meta-record dir to catch a silent miss.
|
|
26
|
+
*
|
|
27
|
+
* watchPaths can be emitted only from SessionStart / CwdChanged / FileChanged, so
|
|
28
|
+
* a UserPromptSubmit fire does a degraded RECORD backfill (upsert) but cannot
|
|
29
|
+
* re-arm the idle watch — the record's address is restored, the wake is not.
|
|
30
|
+
*
|
|
31
|
+
* Run (dev clone): `<node> --experimental-strip-types <plugin-root>/meta-bridge-hook.ts`.
|
|
32
|
+
* Run (installed): `<node> <plugin-root>/meta-bridge-hook.js` — the tsc-emitted
|
|
33
|
+
* closure (build-bridge → dist), because Node REFUSES strip-types on a `.ts` below
|
|
34
|
+
* node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING). The installer picks
|
|
35
|
+
* the artifact by layout (0.12.5), mirroring start.sh / store-doctor.
|
|
36
|
+
* Imports `./lib/meta-session.ts` (+ `./lib/session-id.js`); `rewriteRelativeImportExtensions`
|
|
37
|
+
* rewrites that to `./lib/meta-session.js` in the emitted closure. The installer
|
|
38
|
+
* copies the lib dir alongside this file so `${CLAUDE_PLUGIN_ROOT}` self-locates it.
|
|
39
|
+
*/
|
|
40
|
+
import * as fs from "node:fs";
|
|
41
|
+
import * as path from "node:path";
|
|
42
|
+
import { defaultMetaMailboxDir, defaultMetaSessionsDir, upsertMetaSession, writeMetaReceiverMarker, writeMetaSenderMarker, } from "./lib/meta-session.js";
|
|
43
|
+
function logLine(level, message) {
|
|
44
|
+
try {
|
|
45
|
+
// dirname(meta-sessions) == the pi agent dir — no extra resolver export needed.
|
|
46
|
+
const file = path.join(path.dirname(defaultMetaSessionsDir()), "meta-bridge-hook.log");
|
|
47
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
48
|
+
fs.appendFileSync(file, `${new Date().toISOString()} ${level} ${message}\n`);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
/* logging is best-effort; a broken log must not break the session */
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Emit a hook response on stdout and exit 0. `{}` means "did nothing, do not block startup". */
|
|
55
|
+
function emit(payload) {
|
|
56
|
+
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Map the hook event to a receiver-marker arm provenance. ONLY the genuinely
|
|
61
|
+
* arm-capable events map; any other (a future/unknown hook event) returns null so we
|
|
62
|
+
* never mint an "active receiver" presence we cannot back — fail-closed, not an
|
|
63
|
+
* optimistic session-start. UserPromptSubmit never reaches here (it early-returns
|
|
64
|
+
* before the arm block).
|
|
65
|
+
*/
|
|
66
|
+
function armProvenanceFor(eventName) {
|
|
67
|
+
if (eventName === "SessionStart")
|
|
68
|
+
return "session-start";
|
|
69
|
+
if (eventName === "CwdChanged")
|
|
70
|
+
return "cwd-changed";
|
|
71
|
+
if (eventName === "FileChanged")
|
|
72
|
+
return "file-changed";
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
function main() {
|
|
76
|
+
let raw;
|
|
77
|
+
try {
|
|
78
|
+
raw = fs.readFileSync(0, "utf8"); // fd 0 = stdin (the Claude hook envelope)
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
logLine("ERROR", `stdin read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
82
|
+
emit({});
|
|
83
|
+
}
|
|
84
|
+
let env;
|
|
85
|
+
try {
|
|
86
|
+
const parsed = JSON.parse(raw);
|
|
87
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
88
|
+
throw new Error("envelope is not an object");
|
|
89
|
+
env = parsed;
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
logLine("ERROR", `envelope parse failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
93
|
+
emit({});
|
|
94
|
+
}
|
|
95
|
+
const sessionId = typeof env.session_id === "string" ? env.session_id : "";
|
|
96
|
+
const transcriptPath = typeof env.transcript_path === "string" ? env.transcript_path : "";
|
|
97
|
+
// cwd: prefer the envelope's, fall back to the process cwd (the hook runs in the session's cwd).
|
|
98
|
+
const cwd = typeof env.cwd === "string" && env.cwd.length > 0 ? env.cwd : process.cwd();
|
|
99
|
+
const modelEnvelope = env.model;
|
|
100
|
+
const model = typeof modelEnvelope === "object" &&
|
|
101
|
+
modelEnvelope !== null &&
|
|
102
|
+
typeof modelEnvelope.id === "string"
|
|
103
|
+
? modelEnvelope.id
|
|
104
|
+
: typeof env.model_id === "string"
|
|
105
|
+
? env.model_id
|
|
106
|
+
: undefined;
|
|
107
|
+
const eventName = typeof env.hook_event_name === "string" ? env.hook_event_name : "SessionStart";
|
|
108
|
+
if (!sessionId || !transcriptPath) {
|
|
109
|
+
// A degraded envelope: cannot mint an honest reference record. Log + no-op
|
|
110
|
+
// rather than write a half-record or guess a transcript path. LEVEL depends
|
|
111
|
+
// on the event: a degraded SessionStart / CwdChanged means the session FAILED
|
|
112
|
+
// to become (or refresh) a garden citizen — that is the silent registration
|
|
113
|
+
// miss the doctor must catch (blocker #2), so ERROR. UserPromptSubmit only
|
|
114
|
+
// ever does a best-effort record backfill, so a degraded one is just WARN.
|
|
115
|
+
const degradedLevel = eventName === "UserPromptSubmit" ? "WARN" : "ERROR";
|
|
116
|
+
logLine(degradedLevel, `degraded envelope (event=${eventName}, session_id=${sessionId ? "set" : "MISSING"}, transcript_path=${transcriptPath ? "set" : "MISSING"})`);
|
|
117
|
+
emit({});
|
|
118
|
+
}
|
|
119
|
+
let gardenId;
|
|
120
|
+
try {
|
|
121
|
+
const result = upsertMetaSession({
|
|
122
|
+
input: { backend: "claude-code", nativeSessionId: sessionId, transcriptPath, cwd, model },
|
|
123
|
+
onSkip: (filename, e) => logLine("WARN", `scan skipped ${filename}: ${e.message}`),
|
|
124
|
+
});
|
|
125
|
+
gardenId = result.record.gardenId;
|
|
126
|
+
logLine("INFO", `${result.action} record ${path.basename(result.path)} (event=${eventName}, native=${sessionId})`);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
// Best-effort: a broken record store must surface via the doctor, not by
|
|
130
|
+
// breaking the user's session open. Log and continue with no arm. This is
|
|
131
|
+
// the silent-registration-miss (blocker #2): the session opened fine but is
|
|
132
|
+
// NOT a garden citizen — the doctor catches it via this ERROR line.
|
|
133
|
+
logLine("ERROR", `upsert failed (event=${eventName}, native=${sessionId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
134
|
+
emit({});
|
|
135
|
+
}
|
|
136
|
+
// Sender marker, keyed by the shared Claude Code parent pid: the user-scope
|
|
137
|
+
// MCP child (same parent) reads it at entwurf_v2 send time to promote this
|
|
138
|
+
// session from anonymous external-mcp to a REPLYABLE meta-session sender —
|
|
139
|
+
// process ancestry, not cwd inference (same repo + multiple sessions would be
|
|
140
|
+
// ambiguous). Best-effort: a failed marker only costs reply-addressability
|
|
141
|
+
// (WARN), it does not break the session or the receiver path.
|
|
142
|
+
//
|
|
143
|
+
// SE-1/SE-2 (dual-owner fix): write ONLY for the direct parent (process.ppid =
|
|
144
|
+
// the Claude CLI that ran this hook, verified the native tree is direct — the
|
|
145
|
+
// plugin host is not in between). The old code ALSO wrote a marker for the
|
|
146
|
+
// grandparent. That grandparent is the login shell (e.g. bash under ghostty/i3),
|
|
147
|
+
// which OUTLIVES the Claude session: when Claude exits, the grandparent marker's
|
|
148
|
+
// ownerStartKey still matches a live pid, so it passes readMetaSenderMarker's
|
|
149
|
+
// reuse guard and the dead session keeps looking like a live, replyable receiver
|
|
150
|
+
// — a false-positive "active receiver" leak. The owner must be the watchPaths
|
|
151
|
+
// subscriber (the Claude CLI), nothing higher. If a stray topology means the MCP
|
|
152
|
+
// child's shared ancestor is not process.ppid, that resolves to "no marker"
|
|
153
|
+
// (fail-closed, honest) rather than a wrong-but-live grandparent identity.
|
|
154
|
+
const ownerPid = process.ppid;
|
|
155
|
+
if (typeof ownerPid === "number" && ownerPid > 0) {
|
|
156
|
+
try {
|
|
157
|
+
writeMetaSenderMarker({ backend: "claude-code", gardenId, nativeSessionId: sessionId, cwd, ownerPid });
|
|
158
|
+
logLine("INFO", `sender marker ${ownerPid} -> ${gardenId} (event=${eventName})`);
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
logLine("WARN", `sender marker write failed (event=${eventName}, pid=${ownerPid}, garden=${gardenId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// watchPaths is emittable only from SessionStart / CwdChanged / FileChanged.
|
|
165
|
+
// UserPromptSubmit reaches here only to backfill the record above; it must NOT
|
|
166
|
+
// claim to arm a watch it cannot (decided).
|
|
167
|
+
if (eventName === "UserPromptSubmit") {
|
|
168
|
+
emit({});
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const mailbox = path.join(defaultMetaMailboxDir(), gardenId);
|
|
172
|
+
fs.mkdirSync(mailbox, { recursive: true });
|
|
173
|
+
const signal = path.join(mailbox, "inbox.signal");
|
|
174
|
+
if (!fs.existsSync(signal))
|
|
175
|
+
fs.writeFileSync(signal, "", { mode: 0o600 });
|
|
176
|
+
logLine("INFO", `armed watch ${signal}`);
|
|
177
|
+
// Receiver presence marker (SE-2): written on the arm-capable hook path that
|
|
178
|
+
// emits watchPaths, keyed by garden id with the watch owner pid (= the Claude
|
|
179
|
+
// CLI, process.ppid — same single owner as the sender marker, never the
|
|
180
|
+
// grandparent). It records that a LIVE owner reached the watch-arm emit; it is
|
|
181
|
+
// not proof the host ack'd the watch registration. This is what lets a sender
|
|
182
|
+
// tell a live receiver from a terminated one whose record still lingers.
|
|
183
|
+
// Best-effort: a failed/skipped marker only costs deliverability detection
|
|
184
|
+
// (WARN), it does not break the arm. An unknown event maps to null provenance →
|
|
185
|
+
// no marker (fail-closed: never claim an active receiver we cannot back).
|
|
186
|
+
const ownerPid = process.ppid;
|
|
187
|
+
const armProvenance = armProvenanceFor(eventName);
|
|
188
|
+
if (armProvenance === null) {
|
|
189
|
+
logLine("WARN", `receiver marker skipped — non-arm event ${eventName} (garden=${gardenId})`);
|
|
190
|
+
}
|
|
191
|
+
else if (typeof ownerPid === "number" && ownerPid > 0) {
|
|
192
|
+
try {
|
|
193
|
+
writeMetaReceiverMarker({
|
|
194
|
+
gardenId,
|
|
195
|
+
backend: "claude-code",
|
|
196
|
+
nativeSessionId: sessionId,
|
|
197
|
+
ownerPid,
|
|
198
|
+
armProvenance,
|
|
199
|
+
});
|
|
200
|
+
logLine("INFO", `receiver marker ${gardenId} owner=${ownerPid} arm=${eventName}`);
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
logLine("WARN", `receiver marker write failed (event=${eventName}, garden=${gardenId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
emit({
|
|
207
|
+
hookSpecificOutput: {
|
|
208
|
+
hookEventName: eventName,
|
|
209
|
+
watchPaths: [signal],
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
logLine("ERROR", `arm failed (event=${eventName}, garden=${gardenId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
215
|
+
emit({}); // record landed; only the arm failed — the doctor will flag the missing watch.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
main();
|
|
@@ -46,5 +46,22 @@
|
|
|
46
46
|
// emits here, so it adds ONE leaf JS (dist/scripts/meta-bridge-store-doctor.js)
|
|
47
47
|
// and ZERO new deps. It is NOT in index.ts's boot closure, so
|
|
48
48
|
// check-entwurf-bridge-pi-free (which walks the index eager closure) is unaffected.
|
|
49
|
-
|
|
49
|
+
//
|
|
50
|
+
// WHY meta-bridge-hook rides THIS build (0.12.5): the SessionStart/UserPromptSubmit
|
|
51
|
+
// plugin hook ran as raw `.ts` (`node ${CLAUDE_PLUGIN_ROOT}/meta-bridge-hook.ts`).
|
|
52
|
+
// Under an installed marketplace source below node_modules that is
|
|
53
|
+
// ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING — the SAME strip-types fence
|
|
54
|
+
// start.sh (0.12.1) and the store-doctor (0.12.4) already cross by shipping JS.
|
|
55
|
+
// The hook was the last `.ts`-at-runtime surface; emitting it here closes that
|
|
56
|
+
// class for good (installer copies the dist JS when installed, keeps `.ts` for a
|
|
57
|
+
// dev clone). Its only repo import is meta-session.ts, ALREADY emitted for the
|
|
58
|
+
// store-doctor, so this adds ONE leaf JS (dist/pi-extensions/meta-bridge-hook.js)
|
|
59
|
+
// and ZERO new deps. It is a plugin hook, never in index.ts's boot closure, so
|
|
60
|
+
// check-entwurf-bridge-pi-free is unaffected.
|
|
61
|
+
"include": [
|
|
62
|
+
"./src/index.ts",
|
|
63
|
+
"../../protocol.js",
|
|
64
|
+
"../../scripts/meta-bridge-store-doctor.ts",
|
|
65
|
+
"../../pi-extensions/meta-bridge-hook.ts"
|
|
66
|
+
]
|
|
50
67
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junghanacs/entwurf",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.6",
|
|
4
4
|
"description": "Garden-citizen dispatch substrate and meta-bridge for Claude Code, Codex, Antigravity, and pi harnesses.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -65,7 +65,6 @@
|
|
|
65
65
|
"engines": {
|
|
66
66
|
"node": ">=22.6.0"
|
|
67
67
|
},
|
|
68
|
-
"packageManager": "pnpm@10.33.0",
|
|
69
68
|
"dependencies": {
|
|
70
69
|
"@agentclientprotocol/claude-agent-acp": "0.54.1",
|
|
71
70
|
"@agentclientprotocol/sdk": "1.1.0",
|
|
@@ -120,7 +119,7 @@
|
|
|
120
119
|
"format": "biome check --write .",
|
|
121
120
|
"check-meta-manifest-schema": "./run.sh check-meta-manifest-schema",
|
|
122
121
|
"check-package-source-routing": "./run.sh check-package-source-routing",
|
|
123
|
-
"check": "pnpm lint && pnpm typecheck && ./run.sh check-shell-quote && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-record-v2 && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-meta-dual-read && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-meta-migration && ./run.sh check-meta-dual-consumers && ./run.sh check-meta-capability-source && ./run.sh check-socket-probe && ./run.sh smoke-meta-honesty && ./run.sh smoke-meta-install-state && ./run.sh smoke-meta-prune && ./run.sh smoke-meta-keyset-guard && ./run.sh check-meta-manifest-schema && ./run.sh check-package-source-routing && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-install-preflight && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-lock && ./run.sh check-entwurf-v2-decider && ./run.sh check-entwurf-v2-matrix && ./run.sh check-entwurf-v2-release && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-send-fallback && ./run.sh check-entwurf-v2-mailbox && ./run.sh check-entwurf-v2-runner && ./run.sh check-entwurf-control-rpc && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-boot && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-entwurf-v2-spawn && ./run.sh check-entwurf-resume-args && ./run.sh check-entwurf-v2-spawn-production && ./run.sh check-entwurf-facts && ./run.sh check-socket-discovery && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-entwurf-mailbox-guard && ./run.sh check-auth-boundary && ./run.sh check-acp-provider-surface && ./run.sh check-acp-sdk-surface && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-backend-preflight && ./run.sh check-acp-session-reuse && ./run.sh check-acp-carrier-augment && ./run.sh check-pack"
|
|
122
|
+
"check": "pnpm lint && pnpm typecheck && ./run.sh check-shell-quote && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-record-v2 && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-meta-dual-read && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-meta-migration && ./run.sh check-meta-dual-consumers && ./run.sh check-meta-capability-source && ./run.sh check-socket-probe && ./run.sh smoke-meta-honesty && ./run.sh smoke-meta-install-state && ./run.sh smoke-user-scope-citizen && ./run.sh smoke-meta-prune && ./run.sh smoke-meta-keyset-guard && ./run.sh check-meta-manifest-schema && ./run.sh check-package-source-routing && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-install-preflight && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-lock && ./run.sh check-entwurf-v2-decider && ./run.sh check-entwurf-v2-matrix && ./run.sh check-entwurf-v2-release && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-send-fallback && ./run.sh check-entwurf-v2-mailbox && ./run.sh check-entwurf-v2-runner && ./run.sh check-entwurf-control-rpc && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-boot && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-entwurf-v2-spawn && ./run.sh check-entwurf-resume-args && ./run.sh check-entwurf-v2-spawn-production && ./run.sh check-entwurf-facts && ./run.sh check-socket-discovery && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-entwurf-mailbox-guard && ./run.sh check-auth-boundary && ./run.sh check-acp-provider-surface && ./run.sh check-acp-sdk-surface && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-backend-preflight && ./run.sh check-acp-session-reuse && ./run.sh check-acp-carrier-augment && ./run.sh check-pack"
|
|
124
123
|
},
|
|
125
124
|
"pi": {
|
|
126
125
|
"extensions": [
|
|
@@ -132,7 +131,8 @@
|
|
|
132
131
|
},
|
|
133
132
|
"bin": {
|
|
134
133
|
"entwurf": "./run.sh",
|
|
135
|
-
"entwurf-bridge": "./mcp/entwurf-bridge/start.sh"
|
|
134
|
+
"entwurf-bridge": "./mcp/entwurf-bridge/start.sh",
|
|
135
|
+
"entwurf-statusline": "./scripts/meta-bridge-statusline.sh"
|
|
136
136
|
},
|
|
137
137
|
"peerDependenciesMeta": {
|
|
138
138
|
"@earendil-works/pi-ai": {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"hooks": [
|
|
7
7
|
{
|
|
8
8
|
"type": "command",
|
|
9
|
-
"command": "__NODE_BIN__ ${CLAUDE_PLUGIN_ROOT}/
|
|
9
|
+
"command": "__NODE_BIN__ ${CLAUDE_PLUGIN_ROOT}/__HOOK_ENTRY__"
|
|
10
10
|
}
|
|
11
11
|
]
|
|
12
12
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"hooks": [
|
|
18
18
|
{
|
|
19
19
|
"type": "command",
|
|
20
|
-
"command": "__NODE_BIN__ ${CLAUDE_PLUGIN_ROOT}/
|
|
20
|
+
"command": "__NODE_BIN__ ${CLAUDE_PLUGIN_ROOT}/__HOOK_ENTRY__"
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
23
|
}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"hooks": [
|
|
29
29
|
{
|
|
30
30
|
"type": "command",
|
|
31
|
-
"command": "__NODE_BIN__ ${CLAUDE_PLUGIN_ROOT}/
|
|
31
|
+
"command": "__NODE_BIN__ ${CLAUDE_PLUGIN_ROOT}/__HOOK_ENTRY__"
|
|
32
32
|
}
|
|
33
33
|
]
|
|
34
34
|
}
|
|
@@ -28,9 +28,14 @@
|
|
|
28
28
|
* a UserPromptSubmit fire does a degraded RECORD backfill (upsert) but cannot
|
|
29
29
|
* re-arm the idle watch — the record's address is restored, the wake is not.
|
|
30
30
|
*
|
|
31
|
-
* Run: `<node> --experimental-strip-types <plugin-root>/meta-bridge-hook.ts`.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* Run (dev clone): `<node> --experimental-strip-types <plugin-root>/meta-bridge-hook.ts`.
|
|
32
|
+
* Run (installed): `<node> <plugin-root>/meta-bridge-hook.js` — the tsc-emitted
|
|
33
|
+
* closure (build-bridge → dist), because Node REFUSES strip-types on a `.ts` below
|
|
34
|
+
* node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING). The installer picks
|
|
35
|
+
* the artifact by layout (0.12.5), mirroring start.sh / store-doctor.
|
|
36
|
+
* Imports `./lib/meta-session.ts` (+ `./lib/session-id.js`); `rewriteRelativeImportExtensions`
|
|
37
|
+
* rewrites that to `./lib/meta-session.js` in the emitted closure. The installer
|
|
38
|
+
* copies the lib dir alongside this file so `${CLAUDE_PLUGIN_ROOT}` self-locates it.
|
|
34
39
|
*/
|
|
35
40
|
|
|
36
41
|
import * as fs from "node:fs";
|