@insitue/claude-plugin 0.6.0 → 0.6.2
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 +8 -0
- package/commands/connect.md +20 -2
- package/dist/mcp-server.js +63 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @insitue/claude-plugin
|
|
2
2
|
|
|
3
|
+
## 0.6.2
|
|
4
|
+
|
|
5
|
+
- **Echo the request before fixing.** The cloud-issue instructions (connect.md + the `claim_cloud_issue` tool) now require Claude to print the exact request verbatim ("Fixing locally: …") before any analysis or edits, so you always see what it's about to act on.
|
|
6
|
+
|
|
7
|
+
## 0.6.1
|
|
8
|
+
|
|
9
|
+
- **Fix: refuse to reuse a stale companion.** `ensureCompanion` now checks a reachable companion's version via the handshake and, if it is older than the cloud-feature floor (0.7.0), tears it down and spawns the current one — instead of silently attaching to a stale companion left on the port.
|
|
10
|
+
|
|
3
11
|
## 0.6.0
|
|
4
12
|
|
|
5
13
|
- **Sign in from Claude.** New `/insitue:login` command + `authenticate` / `complete_authentication` MCP tools open a browser, you approve, and a token is stored automatically — no manual paste.
|
package/commands/connect.md
CHANGED
|
@@ -160,8 +160,14 @@ action.
|
|
|
160
160
|
**Typical loop:**
|
|
161
161
|
|
|
162
162
|
1. `list_cloud_issues` → pick an issue with the user.
|
|
163
|
-
2. `claim_cloud_issue <id>` →
|
|
164
|
-
|
|
163
|
+
2. `claim_cloud_issue <id>` → **before doing anything else**, echo the
|
|
164
|
+
exact request verbatim to the developer:
|
|
165
|
+
|
|
166
|
+
> **Fixing locally:** [verbatim `note` from the claim response]
|
|
167
|
+
> (Source: `source.file:source.line`)
|
|
168
|
+
|
|
169
|
+
Then read `source.file` around `source.line`, understand the repro
|
|
170
|
+
from `note` + `consoleErrors`, make the fix.
|
|
165
171
|
3. Open a PR.
|
|
166
172
|
4. `resolve_cloud_issue <id> <prUrl>`.
|
|
167
173
|
|
|
@@ -169,6 +175,18 @@ The cloud-issue workflow follows the same guardrails as browser picks:
|
|
|
169
175
|
never auto-apply writes, always wait for explicit user approval before
|
|
170
176
|
editing files.
|
|
171
177
|
|
|
178
|
+
**Echo guardrail (cloud-issue path).** Mirroring the pick-loop rule
|
|
179
|
+
("Always echo the prompt back first"): after a successful
|
|
180
|
+
`claim_cloud_issue`, the FIRST thing you output to the developer MUST
|
|
181
|
+
be the exact issue `note` in the format above — verbatim, not
|
|
182
|
+
paraphrased. The CLI transcript only shows your output, not the
|
|
183
|
+
dashboard description the reporter wrote. Without the echo the
|
|
184
|
+
developer cannot confirm you're acting on the right issue before edits
|
|
185
|
+
begin. Note: when an issue arrives via the browser "Fix locally"
|
|
186
|
+
button it flows through the normal pick loop and the existing
|
|
187
|
+
pick-echo rule already applies — but the same requirement holds when
|
|
188
|
+
you claim via the cloud-issue tool path directly.
|
|
189
|
+
|
|
172
190
|
## Failure modes to handle gracefully
|
|
173
191
|
|
|
174
192
|
- **`source.file` doesn't exist**: tell the user the path the
|
package/dist/mcp-server.js
CHANGED
|
@@ -291,30 +291,64 @@ var PickBuffer = class {
|
|
|
291
291
|
this.waiters.length = 0;
|
|
292
292
|
}
|
|
293
293
|
};
|
|
294
|
+
var MIN_COMPANION = "0.7.0";
|
|
295
|
+
function semverGte(version, floor) {
|
|
296
|
+
function parse(v) {
|
|
297
|
+
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
298
|
+
if (!m) return [0, 0, 0];
|
|
299
|
+
return [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)];
|
|
300
|
+
}
|
|
301
|
+
const [vMaj, vMin, vPat] = parse(version);
|
|
302
|
+
const [fMaj, fMin, fPat] = parse(floor);
|
|
303
|
+
if (vMaj !== fMaj) return vMaj > fMaj;
|
|
304
|
+
if (vMin !== fMin) return vMin > fMin;
|
|
305
|
+
return vPat >= fPat;
|
|
306
|
+
}
|
|
294
307
|
async function probeCompanion(session2) {
|
|
295
308
|
try {
|
|
296
309
|
process.kill(session2.pid, 0);
|
|
297
310
|
} catch {
|
|
298
|
-
return
|
|
311
|
+
return null;
|
|
299
312
|
}
|
|
300
313
|
return new Promise((resolve) => {
|
|
314
|
+
let rawBody = "";
|
|
301
315
|
const req = httpRequest(
|
|
302
316
|
{
|
|
303
317
|
host: "127.0.0.1",
|
|
304
318
|
port: session2.port,
|
|
305
319
|
path: "/insitue/handshake",
|
|
306
320
|
method: "GET",
|
|
321
|
+
// Include an Origin so the handshake endpoint returns 200 + JSON
|
|
322
|
+
// instead of 403 (which it returns when Origin is absent). We
|
|
323
|
+
// use the loopback Origin — the companion's allowLocalhost flag
|
|
324
|
+
// accepts any localhost:* Origin, or else this falls back to 403
|
|
325
|
+
// which we still treat as "reachable" (non-null return).
|
|
326
|
+
headers: { origin: "http://localhost:5747" },
|
|
307
327
|
timeout: 1500
|
|
308
328
|
},
|
|
309
329
|
(res) => {
|
|
310
|
-
res.
|
|
311
|
-
|
|
330
|
+
res.setEncoding("utf8");
|
|
331
|
+
res.on("data", (chunk) => {
|
|
332
|
+
rawBody += chunk;
|
|
333
|
+
});
|
|
334
|
+
res.on("end", () => {
|
|
335
|
+
if (res.statusCode === 200) {
|
|
336
|
+
try {
|
|
337
|
+
const body = JSON.parse(rawBody);
|
|
338
|
+
resolve(body.companionVersion ?? "0.0.0");
|
|
339
|
+
} catch {
|
|
340
|
+
resolve("0.0.0");
|
|
341
|
+
}
|
|
342
|
+
} else {
|
|
343
|
+
resolve("0.0.0");
|
|
344
|
+
}
|
|
345
|
+
});
|
|
312
346
|
}
|
|
313
347
|
);
|
|
314
|
-
req.on("error", () => resolve(
|
|
348
|
+
req.on("error", () => resolve(null));
|
|
315
349
|
req.on("timeout", () => {
|
|
316
350
|
req.destroy();
|
|
317
|
-
resolve(
|
|
351
|
+
resolve(null);
|
|
318
352
|
});
|
|
319
353
|
req.end();
|
|
320
354
|
});
|
|
@@ -322,12 +356,30 @@ async function probeCompanion(session2) {
|
|
|
322
356
|
var ownedChild = null;
|
|
323
357
|
async function ensureCompanion(projectDir2) {
|
|
324
358
|
const existing = findSession(projectDir2);
|
|
325
|
-
if (existing
|
|
326
|
-
|
|
327
|
-
|
|
359
|
+
if (existing) {
|
|
360
|
+
const companionVersion = await probeCompanion(existing.session);
|
|
361
|
+
if (companionVersion !== null) {
|
|
362
|
+
if (semverGte(companionVersion, MIN_COMPANION)) {
|
|
363
|
+
process.stderr.write(
|
|
364
|
+
`[insitue-mcp] reusing companion at :${existing.session.port} (pid ${existing.session.pid}, v${companionVersion})
|
|
328
365
|
`
|
|
329
|
-
|
|
330
|
-
|
|
366
|
+
);
|
|
367
|
+
return existing.session;
|
|
368
|
+
}
|
|
369
|
+
process.stderr.write(
|
|
370
|
+
`[insitue-mcp] replacing stale companion (v${companionVersion} < ${MIN_COMPANION})
|
|
371
|
+
`
|
|
372
|
+
);
|
|
373
|
+
try {
|
|
374
|
+
process.kill(existing.session.pid);
|
|
375
|
+
} catch {
|
|
376
|
+
}
|
|
377
|
+
const sessionPath = join3(projectDir2, ".insitue", "session.json");
|
|
378
|
+
try {
|
|
379
|
+
rmSync(sessionPath);
|
|
380
|
+
} catch {
|
|
381
|
+
}
|
|
382
|
+
}
|
|
331
383
|
}
|
|
332
384
|
process.stderr.write(
|
|
333
385
|
`[insitue-mcp] starting companion via \`npx -y @insitue/companion@latest dev\` in ${projectDir2}\u2026
|
|
@@ -947,7 +999,7 @@ server.registerTool(
|
|
|
947
999
|
source,
|
|
948
1000
|
url,
|
|
949
1001
|
consoleErrors,
|
|
950
|
-
instructions: "
|
|
1002
|
+
instructions: 'FIRST, echo the exact issue note verbatim to the developer before any analysis or edits: output "**Fixing locally:** <note>" followed by "(Source: <file>:<line>)" so they can confirm what is being acted on. THEN read the source file around the line, reproduce the issue from the description, make the fix, open a PR, then call resolve_cloud_issue with the PR url.'
|
|
951
1003
|
})
|
|
952
1004
|
}
|
|
953
1005
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@insitue/claude-plugin",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
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
5
|
"keywords": [
|
|
6
6
|
"insitue",
|