backthread 0.5.0 → 0.5.1
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/dist-bundle/backthread.js +40 -13
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "backthread",
|
|
3
3
|
"displayName": "Backthread",
|
|
4
4
|
"description": "Backthread helps you understand your codebase while AI ships features. It captures the why behind every Claude Code session so you can ask \"how does X work?\" without digging through PRs.",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.1",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Backthread"
|
|
8
8
|
},
|
|
@@ -8333,6 +8333,9 @@ var execFileP = promisify(execFile);
|
|
|
8333
8333
|
var MCP_COMMAND = "npx";
|
|
8334
8334
|
var MCP_ARGS = ["-y", "backthread", "mcp"];
|
|
8335
8335
|
function hookCommand(agent) {
|
|
8336
|
+
return `npx -y backthread@latest capture --from-hook --agent ${agent} --detach`;
|
|
8337
|
+
}
|
|
8338
|
+
function legacyHookCommand(agent) {
|
|
8336
8339
|
return `npx -y backthread capture --from-hook --agent ${agent} --detach`;
|
|
8337
8340
|
}
|
|
8338
8341
|
var MIN_VERSION = {
|
|
@@ -8382,16 +8385,36 @@ function withMcpServer(settings) {
|
|
|
8382
8385
|
mcpServers.backthread = desired;
|
|
8383
8386
|
return { next: { ...settings, mcpServers }, changed: true };
|
|
8384
8387
|
}
|
|
8385
|
-
function
|
|
8388
|
+
function groupRunsCommand(group, command) {
|
|
8389
|
+
const inner = group?.hooks;
|
|
8390
|
+
return Array.isArray(inner) && inner.some((h) => h?.command === command);
|
|
8391
|
+
}
|
|
8392
|
+
function rewriteLegacyInGroup(group, legacyCommands, command) {
|
|
8393
|
+
const inner = group?.hooks;
|
|
8394
|
+
if (!Array.isArray(inner)) return group;
|
|
8395
|
+
let changed = false;
|
|
8396
|
+
const nextInner = inner.map((h) => {
|
|
8397
|
+
const cmd = h?.command;
|
|
8398
|
+
if (typeof cmd === "string" && cmd !== command && legacyCommands.includes(cmd)) {
|
|
8399
|
+
changed = true;
|
|
8400
|
+
return { ...h, command };
|
|
8401
|
+
}
|
|
8402
|
+
return h;
|
|
8403
|
+
});
|
|
8404
|
+
return changed ? { ...group, hooks: nextInner } : group;
|
|
8405
|
+
}
|
|
8406
|
+
function withNestedHook(settings, event, command, extra = {}, legacyCommands = []) {
|
|
8386
8407
|
const hooks = asObject(settings.hooks);
|
|
8387
8408
|
const list = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8409
|
+
if (list.some((g) => groupRunsCommand(g, command))) return { next: settings, changed: false };
|
|
8410
|
+
let migrated = false;
|
|
8411
|
+
const nextList = list.map((g) => {
|
|
8412
|
+
const rewritten = rewriteLegacyInGroup(g, legacyCommands, command);
|
|
8413
|
+
if (rewritten !== g) migrated = true;
|
|
8414
|
+
return rewritten;
|
|
8391
8415
|
});
|
|
8392
|
-
if (
|
|
8393
|
-
|
|
8394
|
-
hooks[event] = list;
|
|
8416
|
+
if (!migrated) nextList.push({ hooks: [{ type: "command", command, ...extra }] });
|
|
8417
|
+
hooks[event] = nextList;
|
|
8395
8418
|
return { next: { ...settings, hooks }, changed: true };
|
|
8396
8419
|
}
|
|
8397
8420
|
async function writeJson(deps, path, obj) {
|
|
@@ -8405,7 +8428,9 @@ async function installGemini(home, deps) {
|
|
|
8405
8428
|
const path = join8(home, ".gemini", "settings.json");
|
|
8406
8429
|
const current = await loadJsonObject(doRead, path);
|
|
8407
8430
|
const a = withMcpServer(current);
|
|
8408
|
-
const b = withNestedHook(a.next, "SessionEnd", hookCommand("gemini-cli"), { name: "backthread-capture" }
|
|
8431
|
+
const b = withNestedHook(a.next, "SessionEnd", hookCommand("gemini-cli"), { name: "backthread-capture" }, [
|
|
8432
|
+
legacyHookCommand("gemini-cli")
|
|
8433
|
+
]);
|
|
8409
8434
|
if (a.changed || b.changed) await writeJson(deps, path, b.next);
|
|
8410
8435
|
return [{ path, wrote: a.changed || b.changed }];
|
|
8411
8436
|
}
|
|
@@ -8435,7 +8460,7 @@ args = [${MCP_ARGS.map((a) => `"${a}"`).join(", ")}]
|
|
|
8435
8460
|
}
|
|
8436
8461
|
const hooksPath = join8(home, ".codex", "hooks.json");
|
|
8437
8462
|
const current = await loadJsonObject(doRead, hooksPath);
|
|
8438
|
-
const h = withNestedHook(current, "Stop", hookCommand("codex"), { timeout: 60 });
|
|
8463
|
+
const h = withNestedHook(current, "Stop", hookCommand("codex"), { timeout: 60 }, [legacyHookCommand("codex")]);
|
|
8439
8464
|
if (h.changed) await writeJson(deps, hooksPath, h.next);
|
|
8440
8465
|
writes.push({ path: hooksPath, wrote: h.changed });
|
|
8441
8466
|
return writes;
|
|
@@ -8451,7 +8476,8 @@ async function installCursor(home, deps) {
|
|
|
8451
8476
|
await writeCursorScript(
|
|
8452
8477
|
deps,
|
|
8453
8478
|
captureScriptPath,
|
|
8454
|
-
|
|
8479
|
+
// capture hook → self-updating (@latest), like the other agents' hooks (ARP-739).
|
|
8480
|
+
cursorWrapperScript(nodeBinDir, "capture --from-hook --agent cursor --detach", true)
|
|
8455
8481
|
)
|
|
8456
8482
|
);
|
|
8457
8483
|
writes.push(await writeCursorScript(deps, mcpScriptPath, cursorWrapperScript(nodeBinDir, "mcp")));
|
|
@@ -8470,7 +8496,8 @@ async function installCursor(home, deps) {
|
|
|
8470
8496
|
function shSingleQuote(s) {
|
|
8471
8497
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
8472
8498
|
}
|
|
8473
|
-
function cursorWrapperScript(nodeBinDir, backthreadArgs) {
|
|
8499
|
+
function cursorWrapperScript(nodeBinDir, backthreadArgs, latest = false) {
|
|
8500
|
+
const pkg = latest ? "backthread@latest" : "backthread";
|
|
8474
8501
|
return [
|
|
8475
8502
|
"#!/bin/sh",
|
|
8476
8503
|
"# Backthread wrapper for Cursor \u2014 generated by `backthread install --agent cursor` (ARP-692).",
|
|
@@ -8489,7 +8516,7 @@ function cursorWrapperScript(nodeBinDir, backthreadArgs) {
|
|
|
8489
8516
|
' PATH="$NODE_BIN_DIR:$PATH"',
|
|
8490
8517
|
" export PATH",
|
|
8491
8518
|
"fi",
|
|
8492
|
-
`exec npx -y
|
|
8519
|
+
`exec npx -y ${pkg} ${backthreadArgs}`
|
|
8493
8520
|
].join("\n") + "\n";
|
|
8494
8521
|
}
|
|
8495
8522
|
async function writeCursorScript(deps, path, content) {
|
|
@@ -8523,7 +8550,7 @@ function withCursorMcpServer(settings, mcpScriptPath) {
|
|
|
8523
8550
|
return { next: { ...settings, mcpServers }, changed: true };
|
|
8524
8551
|
}
|
|
8525
8552
|
function withCursorStopHook(settings, captureScriptPath) {
|
|
8526
|
-
const legacyInline =
|
|
8553
|
+
const legacyInline = legacyHookCommand("cursor");
|
|
8527
8554
|
const hooks = asObject(settings.hooks);
|
|
8528
8555
|
const stop = Array.isArray(hooks.stop) ? [...hooks.stop] : [];
|
|
8529
8556
|
const hadDesired = stop.some((h) => h?.command === captureScriptPath);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backthread",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Backthread helps you understand your codebase while AI ships features. The CLI captures the why behind every AI session and lets you ask how your codebase works, right from the terminal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Backthread",
|