@insitue/claude-plugin 0.4.4 → 0.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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +33 -0
- package/README.md +68 -17
- package/commands/connect.md +1 -5
- package/dist/{chunk-RF5Q55CG.js → chunk-U4C5CDNQ.js} +2 -2
- package/dist/mcp-server.js +31 -18
- package/dist/setup-cli.js +1 -1
- package/package.json +21 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @insitue/claude-plugin
|
|
2
|
+
|
|
3
|
+
## 0.4.6
|
|
4
|
+
|
|
5
|
+
- **Fix (Windows):** `isInsideProject` now uses `path.sep` instead of a
|
|
6
|
+
hardcoded `/`, so the project-dir sandbox check actually holds on
|
|
7
|
+
Windows. Previously every `apply_edit` / `write_file` call would be
|
|
8
|
+
rejected on Windows because `C:\proj/` never matches `C:\proj\file`.
|
|
9
|
+
- **Fix:** the MCP server's reported version (`McpServer({ version })`)
|
|
10
|
+
is now read from `package.json` at startup, eliminating the stale
|
|
11
|
+
`"0.3.0"` literal that drifted from the actual 0.4.x releases.
|
|
12
|
+
- **Fix:** stderr message on spawn-timeout now correctly reports "8 s"
|
|
13
|
+
instead of "5 s" (the loop ceiling is 8 s).
|
|
14
|
+
- **Docs:** rewrote the README's Architecture section to match the
|
|
15
|
+
current tool surface (next_pick, list_recent_picks, start_session,
|
|
16
|
+
end_session, diagnose, read_file, apply_edit, write_file) — the
|
|
17
|
+
previous text claimed only two tools and said "the bridge never
|
|
18
|
+
writes files" (no longer true on Desktop).
|
|
19
|
+
- **Docs:** added Stability, Versioning, and Security sections to the
|
|
20
|
+
README.
|
|
21
|
+
- **Docs:** swapped a leaked internal CMS-slug example
|
|
22
|
+
(`briefings:hairspray-chipping:body`) for a generic one in
|
|
23
|
+
`mcp-server.ts` JSDoc.
|
|
24
|
+
- **Docs:** clarified `project-dir.ts` JSDoc — said "three forms",
|
|
25
|
+
listed six.
|
|
26
|
+
- **Metadata:** added `keywords`, `homepage`, `bugs` to `package.json`;
|
|
27
|
+
included `CHANGELOG.md` in the published tarball.
|
|
28
|
+
- Bumped `.claude-plugin/plugin.json` version in lockstep.
|
|
29
|
+
|
|
30
|
+
## 0.4.x and earlier
|
|
31
|
+
|
|
32
|
+
Pre-launch versions — see [git history](https://github.com/InSitue/insitue/commits/main/packages/claude-plugin)
|
|
33
|
+
for changes. Real entries start at the next minor bump.
|
package/README.md
CHANGED
|
@@ -237,32 +237,83 @@ extensively to stderr; claude surfaces them in the transcript.
|
|
|
237
237
|
|
|
238
238
|
The plugin is a stdio MCP server that:
|
|
239
239
|
|
|
240
|
-
1. On startup,
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
server started.
|
|
240
|
+
1. On startup, resolves the project dir (`--project-dir` argv →
|
|
241
|
+
`INSITUE_PROJECT_DIR` env → `CLAUDE_PROJECT_DIR` env →
|
|
242
|
+
walk-up for `.insitue/session.json` → walk-up for
|
|
243
|
+
`package.json` → cwd).
|
|
244
|
+
2. Reads `.insitue/session.json` to find a running companion.
|
|
245
|
+
If one's alive (PID up + port responsive), reuse it.
|
|
246
|
+
3. Otherwise spawns `npx -y @insitue/companion@latest dev` as a
|
|
247
|
+
child process, polls for the new `session.json` to appear
|
|
248
|
+
(8 s ceiling), then connects.
|
|
249
|
+
4. Subscribes to the companion's WS broadcast channel. Every
|
|
250
|
+
pick the browser sends arrives here, gets summarised, and
|
|
251
|
+
buffers (cap: 32) for the polling loop to pull.
|
|
253
252
|
5. Auto-reconnects if the companion restarts (HMR, manual
|
|
254
253
|
stop). The widget reconnects too.
|
|
255
254
|
6. Cleans up on `process.exit` / `SIGTERM` — kills only the
|
|
256
255
|
companion it spawned, leaves user-started companions
|
|
257
256
|
untouched.
|
|
258
257
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
258
|
+
### MCP tools exposed
|
|
259
|
+
|
|
260
|
+
| Tool | Purpose |
|
|
261
|
+
|---|---|
|
|
262
|
+
| `next_pick` | Long-poll for the next browser pick (default 25 s; max 30 min). |
|
|
263
|
+
| `list_recent_picks` | Up to N buffered picks since the MCP server started. |
|
|
264
|
+
| `start_session` | Returns the operating instructions + current state. Desktop entry point. |
|
|
265
|
+
| `end_session` | Cleanly disconnect: close WS, suppress reconnect, kill spawned companion, drop session file. |
|
|
266
|
+
| `diagnose` | Health check — companion reachability, SDK + SWC-plugin versions + wiring, recommendations. |
|
|
267
|
+
| `read_file` | Project-scoped file read. Desktop fallback (Code has native Read). |
|
|
268
|
+
| `apply_edit` | Project-scoped string-replacement edit. Desktop fallback (Code has native Edit). |
|
|
269
|
+
| `write_file` | Project-scoped full-file write. Desktop fallback. |
|
|
270
|
+
|
|
271
|
+
All file tools resolve paths against the project dir and refuse
|
|
272
|
+
anything that resolves outside it (realpath-checked, so `..` games
|
|
273
|
+
are blocked). On Claude Code the agent prefers its native tools;
|
|
274
|
+
the project-scoped ones exist primarily for Claude Desktop, which
|
|
275
|
+
has no built-in file tools.
|
|
276
|
+
|
|
277
|
+
### Trust boundary
|
|
278
|
+
|
|
279
|
+
The companion is the only process that holds the user's edit
|
|
280
|
+
authority — it spawns from the project dir, writes only inside it,
|
|
281
|
+
and is gated by the WS handshake's loopback bind + per-session
|
|
282
|
+
token. The MCP bridge here is a read-side subscriber that
|
|
283
|
+
broadcasts picks into the chat. Writes still require the human
|
|
284
|
+
in the terminal to say "yes" before the agent calls `apply_edit`
|
|
285
|
+
(or its native equivalent).
|
|
263
286
|
|
|
264
287
|
---
|
|
265
288
|
|
|
289
|
+
## Stability
|
|
290
|
+
|
|
291
|
+
The plugin's MCP tool surface is what consumers depend on. We
|
|
292
|
+
treat tool name/argument changes as breaking; descriptions and
|
|
293
|
+
internal behaviour can evolve in patch releases.
|
|
294
|
+
|
|
295
|
+
## Versioning
|
|
296
|
+
|
|
297
|
+
- **Major** — backwards-incompatible changes to the MCP tool
|
|
298
|
+
surface (renames, removed tools, argument shape changes), or
|
|
299
|
+
changes that require the user to re-run `setup`.
|
|
300
|
+
- **Minor** — additive tools, optional new arguments, new env
|
|
301
|
+
vars, new CLI subcommands.
|
|
302
|
+
- **Patch** — bug fixes, docs, internal refactors, version-pin
|
|
303
|
+
bumps for transitive deps.
|
|
304
|
+
|
|
305
|
+
The plugin pins the InSitue WS protocol version (`5` at the time
|
|
306
|
+
of writing) against the companion. A mismatch is rejected at the
|
|
307
|
+
handshake rather than silently degraded.
|
|
308
|
+
|
|
309
|
+
## Security
|
|
310
|
+
|
|
311
|
+
Report vulnerabilities privately — see [SECURITY.md](../../SECURITY.md)
|
|
312
|
+
in the repo root. Especially relevant for this package: anything
|
|
313
|
+
that lets the bridge accept frames over a non-loopback bind, that
|
|
314
|
+
lets `apply_edit` / `write_file` escape the resolved project dir,
|
|
315
|
+
or that leaks the session token outside the local machine.
|
|
316
|
+
|
|
266
317
|
## License
|
|
267
318
|
|
|
268
319
|
MIT. Same as the rest of InSitue.
|
package/commands/connect.md
CHANGED
|
@@ -77,11 +77,7 @@ Either path is fine; pick whichever your runtime has.
|
|
|
77
77
|
question while you're between calls, answer it first (since
|
|
78
78
|
the chat is responsive), then resume the loop with
|
|
79
79
|
`next_pick`.
|
|
80
|
-
4.
|
|
81
|
-
`[insitue]` (e.g. "companion disconnected"), tell the user
|
|
82
|
-
what happened in one sentence and call `next_pick` again —
|
|
83
|
-
the bridge auto-reconnects.
|
|
84
|
-
5. **End the session properly.** When the user says "stop",
|
|
80
|
+
4. **End the session properly.** When the user says "stop",
|
|
85
81
|
"done", "quit", "thanks", "exit", "disconnect", "stop
|
|
86
82
|
insitue", or anything else that clearly ends the InSitue
|
|
87
83
|
session, do BOTH of these:
|
|
@@ -121,7 +121,7 @@ async function diagnose(projectDir) {
|
|
|
121
121
|
|
|
122
122
|
// src/project-dir.ts
|
|
123
123
|
import { existsSync as existsSync2, realpathSync } from "fs";
|
|
124
|
-
import { dirname, isAbsolute, join as join2, resolve } from "path";
|
|
124
|
+
import { dirname, isAbsolute, join as join2, resolve, sep } from "path";
|
|
125
125
|
function readProjectDirArg(argv) {
|
|
126
126
|
for (let i = 0; i < argv.length; i++) {
|
|
127
127
|
const a = argv[i];
|
|
@@ -183,7 +183,7 @@ function isInsideProject(root, target) {
|
|
|
183
183
|
t = resolve(target);
|
|
184
184
|
}
|
|
185
185
|
if (!isAbsolute(r) || !isAbsolute(t)) return false;
|
|
186
|
-
const rWithSep = r.endsWith(
|
|
186
|
+
const rWithSep = r.endsWith(sep) ? r : r + sep;
|
|
187
187
|
return t === r || t.startsWith(rWithSep);
|
|
188
188
|
}
|
|
189
189
|
|
package/dist/mcp-server.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
diagnose,
|
|
4
4
|
isInsideProject,
|
|
5
5
|
resolveProjectDir
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-U4C5CDNQ.js";
|
|
7
7
|
|
|
8
8
|
// src/mcp-server.ts
|
|
9
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -166,6 +166,20 @@ function loadInstructions() {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
// src/mcp-server.ts
|
|
169
|
+
function readPackageVersion() {
|
|
170
|
+
const here = dirname3(fileURLToPath2(import.meta.url));
|
|
171
|
+
for (const base of [join3(here, ".."), here, join3(here, "..", "..")]) {
|
|
172
|
+
const p = join3(base, "package.json");
|
|
173
|
+
if (existsSync2(p)) {
|
|
174
|
+
try {
|
|
175
|
+
const v = JSON.parse(readFileSync3(p, "utf8")).version;
|
|
176
|
+
if (v) return v;
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return "unknown";
|
|
182
|
+
}
|
|
169
183
|
var MAX_BUFFERED_PICKS = 32;
|
|
170
184
|
var NEXT_PICK_DEFAULT_TIMEOUT_MS = 25 * 1e3;
|
|
171
185
|
var NEXT_PICK_MAX_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
@@ -238,22 +252,18 @@ var PickBuffer = class {
|
|
|
238
252
|
recent(limit) {
|
|
239
253
|
return this.picks.slice(-limit);
|
|
240
254
|
}
|
|
241
|
-
/** WS
|
|
242
|
-
*
|
|
243
|
-
|
|
255
|
+
/** WS dropped — release pending waiters silently. They resolve to
|
|
256
|
+
* `null` (same shape as a natural timeout), so the agent's
|
|
257
|
+
* next_pick loop just calls again and the auto-reconnect 2s
|
|
258
|
+
* timer heals the bridge invisibly. We previously synthesized a
|
|
259
|
+
* fake "reconnect" pick here; that surfaced HMR / plugin-reload
|
|
260
|
+
* blips to the user as if they were real picks and trained the
|
|
261
|
+
* agent to stop looping. The stderr trace at the close call site
|
|
262
|
+
* is the operator-visible signal; the agent stays quiet. */
|
|
263
|
+
dropWaiters() {
|
|
244
264
|
for (const w of this.waiters) {
|
|
245
265
|
clearTimeout(w.timer);
|
|
246
|
-
w.resolve(
|
|
247
|
-
id: "reconnect",
|
|
248
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
249
|
-
source: null,
|
|
250
|
-
confidence: "n/a",
|
|
251
|
-
target: `[insitue] ${reason}`,
|
|
252
|
-
selector: null,
|
|
253
|
-
userNote: null,
|
|
254
|
-
url: null,
|
|
255
|
-
componentStack: []
|
|
256
|
-
});
|
|
266
|
+
w.resolve(null);
|
|
257
267
|
}
|
|
258
268
|
this.waiters.length = 0;
|
|
259
269
|
}
|
|
@@ -331,7 +341,7 @@ async function ensureCompanion(projectDir2) {
|
|
|
331
341
|
await new Promise((r) => setTimeout(r, 200));
|
|
332
342
|
}
|
|
333
343
|
process.stderr.write(
|
|
334
|
-
"[insitue-mcp] companion didn't come up in
|
|
344
|
+
"[insitue-mcp] companion didn't come up in 8s \u2014 see [companion] / [companion err] above\n"
|
|
335
345
|
);
|
|
336
346
|
return null;
|
|
337
347
|
}
|
|
@@ -418,8 +428,11 @@ function connectToCompanion(s) {
|
|
|
418
428
|
});
|
|
419
429
|
ws.on("close", () => {
|
|
420
430
|
if (activeWs === ws) activeWs = null;
|
|
421
|
-
buffer.
|
|
431
|
+
buffer.dropWaiters();
|
|
422
432
|
if (disconnecting) return;
|
|
433
|
+
process.stderr.write(
|
|
434
|
+
"[insitue-mcp] companion link dropped \u2014 reconnecting in 2s\n"
|
|
435
|
+
);
|
|
423
436
|
reconnectTimer = setTimeout(() => connectToCompanion(s), 2e3);
|
|
424
437
|
});
|
|
425
438
|
ws.on("error", () => {
|
|
@@ -485,7 +498,7 @@ function endSession() {
|
|
|
485
498
|
}
|
|
486
499
|
var server = new McpServer({
|
|
487
500
|
name: "insitue",
|
|
488
|
-
version:
|
|
501
|
+
version: readPackageVersion()
|
|
489
502
|
});
|
|
490
503
|
server.registerTool(
|
|
491
504
|
"next_pick",
|
package/dist/setup-cli.js
CHANGED
package/package.json
CHANGED
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@insitue/claude-plugin",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Drive Claude (Code AND Desktop) from the InSitue browser overlay — pick an element in your app, claude reads the file and proposes the edit.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"insitue",
|
|
7
|
+
"claude",
|
|
8
|
+
"mcp",
|
|
9
|
+
"claude-code",
|
|
10
|
+
"claude-desktop",
|
|
11
|
+
"visual-feedback",
|
|
12
|
+
"browser-overlay"
|
|
13
|
+
],
|
|
5
14
|
"license": "MIT",
|
|
15
|
+
"homepage": "https://www.insitue.com",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/InSitue/insitue.git",
|
|
19
|
+
"directory": "packages/claude-plugin"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/InSitue/insitue/issues"
|
|
23
|
+
},
|
|
6
24
|
"type": "module",
|
|
7
25
|
"files": [
|
|
8
26
|
".claude-plugin",
|
|
9
27
|
"commands",
|
|
10
28
|
"dist",
|
|
11
|
-
"README.md"
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md"
|
|
12
31
|
],
|
|
13
32
|
"exports": {
|
|
14
33
|
".": "./dist/mcp-server.js"
|