backstep 0.1.4 → 0.1.5
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/README.md +1 -1
- package/dist/keys.js +1 -4
- package/dist/setup.js +1 -1
- package/dist/update.js +34 -12
- package/mod/.claude-plugin/plugin.json +1 -1
- package/mod/hooks/register.tsx +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ With Claude Code: `claude mcp add files -- backstep wrap --name files -- <the se
|
|
|
53
53
|
| `--rules <file>` | undo rules (or set `BACKSTEP_RULES`) |
|
|
54
54
|
| `--pg <url>` | the Postgres database the wrapped server writes to. Enables database undo. Without it, SQL writes are recorded but can't be undone. |
|
|
55
55
|
|
|
56
|
-
The undo key is
|
|
56
|
+
The undo key is ctrl+z. The first press asks "Undo Claude's last turn?" above the prompt and a second press within 5 seconds undoes, so a stray press does nothing. Change the key whenever you like: ask Claude ("change my Backstep undo key to ctrl+u"), or run `backstep key ctrl+u`. The line above the prompt is always there, even with nothing to undo, because the key only reaches Backstep while it's on screen; otherwise ctrl+z would fall through to the terminal and suspend Claude Code.
|
|
57
57
|
|
|
58
58
|
Backstep checks npm for a newer version as each Claude Code session starts and asks before updating (Update, Not now, or Skip this version; `backstep update` does it by hand, `BACKSTEP_UPDATE_CHECK=0` turns the check off).
|
|
59
59
|
|
package/dist/keys.js
CHANGED
|
@@ -8,10 +8,7 @@ import { homedir } from "node:os";
|
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
export const UNDO_ACTION = "app:cycleDiffBase";
|
|
10
10
|
export const DEFAULT_KEY = "ctrl+z";
|
|
11
|
-
|
|
12
|
-
// Claude Code's desktop app); Terminal.app and iTerm2 keep ⌘Z for their own
|
|
13
|
-
// Edit menu, so ctrl+z stays bound alongside it.
|
|
14
|
-
export const defaultKeys = () => (process.platform === "darwin" ? ["cmd+z", DEFAULT_KEY] : [DEFAULT_KEY]);
|
|
11
|
+
export const defaultKeys = () => [DEFAULT_KEY];
|
|
15
12
|
const CONTEXTS = ["Global", "Chat"];
|
|
16
13
|
// Claude Code never hands these to anything else.
|
|
17
14
|
const RESERVED = new Set(["ctrl+c", "ctrl+d", "ctrl+m", "ctrl+i", "ctrl+h", "ctrl+[", "ctrl+\\"]);
|
package/dist/setup.js
CHANGED
|
@@ -29,7 +29,7 @@ function keepCopy() {
|
|
|
29
29
|
cpSync(dirname(ROOT), modules, { recursive: true });
|
|
30
30
|
return { root: join(modules, "backstep"), onPath: false };
|
|
31
31
|
}
|
|
32
|
-
// Keeps a key the person picked;
|
|
32
|
+
// Keeps a key the person picked; binds ctrl+z on a first install.
|
|
33
33
|
function bindUndoKey() {
|
|
34
34
|
if (undoKey())
|
|
35
35
|
return "";
|
package/dist/update.js
CHANGED
|
@@ -51,17 +51,39 @@ export function skipVersion(v) {
|
|
|
51
51
|
mkdirSync(homeDir(), { recursive: true });
|
|
52
52
|
writeFileSync(file(), JSON.stringify({ ...settings(), skipVersion: v }, null, 2) + "\n");
|
|
53
53
|
}
|
|
54
|
+
// What went wrong, in a line a person can act on: npm's own reason, not its
|
|
55
|
+
// pointer to a log file.
|
|
56
|
+
export function npmReason(out) {
|
|
57
|
+
if (/ETARGET|notarget|No matching version/i.test(out))
|
|
58
|
+
return "npm hasn't finished publishing it yet. Try again in a few minutes";
|
|
59
|
+
if (/ENOTFOUND|EAI_AGAIN|ECONNREFUSED|ETIMEDOUT|network/i.test(out))
|
|
60
|
+
return "couldn't reach npm. Check your internet connection";
|
|
61
|
+
if (/EACCES|permission denied/i.test(out))
|
|
62
|
+
return "npm wasn't allowed to install it (a permissions problem with npm's global folder)";
|
|
63
|
+
const lines = out.split("\n").map((l) => l.trim()).filter((l) => l && !/complete log of this run|^npm (error|ERR!)\s*$/i.test(l));
|
|
64
|
+
return (lines.find((l) => /^backstep:/.test(l)) ?? lines.findLast((l) => /error/i.test(l)) ?? lines.at(-1) ?? "npx failed").replace(/^(backstep:|npm (error|ERR!))\s*/i, "");
|
|
65
|
+
}
|
|
66
|
+
const pause = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
54
67
|
// The new version's setup puts its own copy in place, points Claude Code's mod
|
|
55
|
-
// at it and re-wraps servers, exactly as a fresh install would.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
// at it and re-wraps servers, exactly as a fresh install would. npm is asked
|
|
69
|
+
// online, never from its cache, which can be older than a version just
|
|
70
|
+
// published; and asked again for a while if it still hasn't heard of it.
|
|
71
|
+
export function runUpdate(v, tries = 4) {
|
|
72
|
+
let out = "";
|
|
73
|
+
for (let i = 0; i < tries; i++) {
|
|
74
|
+
if (i)
|
|
75
|
+
pause(15_000);
|
|
76
|
+
const r = spawnSync("npx", ["-y", "--prefer-online", `backstep@${v}`, "setup"], {
|
|
77
|
+
encoding: "utf8",
|
|
78
|
+
timeout: 5 * 60 * 1000,
|
|
79
|
+
env: { ...process.env, NO_COLOR: "1", BACKSTEP_TELEMETRY: "0" },
|
|
80
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
81
|
+
});
|
|
82
|
+
if (r.status === 0)
|
|
83
|
+
return { ok: true, detail: `updated to ${v}` };
|
|
84
|
+
out = `${r.stderr ?? ""}\n${r.stdout ?? ""}`;
|
|
85
|
+
if (!/ETARGET|notarget|No matching version/i.test(out))
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
return { ok: false, detail: npmReason(out) };
|
|
67
89
|
}
|
package/mod/hooks/register.tsx
CHANGED
|
@@ -403,7 +403,7 @@ async function offerUpdate($: EngineInterface) {
|
|
|
403
403
|
if (answer !== UPDATE.yes) return
|
|
404
404
|
$.ui.toast(`Updating Backstep to ${u.latest}…`)
|
|
405
405
|
const done = await $.process
|
|
406
|
-
.run(backstep(['update', '--version', u.latest, '--json']), { timeoutMs:
|
|
406
|
+
.run(backstep(['update', '--version', u.latest, '--json']), { timeoutMs: 600_000 })
|
|
407
407
|
.catch((err: unknown) => ({ exitCode: 1, stdout: '', stderr: err instanceof Error ? err.message : String(err) }))
|
|
408
408
|
let r: { ok: boolean; detail: string } = { ok: false, detail: (done.stderr || 'it failed').trim().split('\n').at(-1) ?? 'it failed' }
|
|
409
409
|
try {
|
|
@@ -419,7 +419,7 @@ const KEY = 'mcp__backstep__undo_key'
|
|
|
419
419
|
const KEY_TOOL = {
|
|
420
420
|
name: 'undo_key',
|
|
421
421
|
description:
|
|
422
|
-
"Change the key that undoes Claude's last turn with Backstep (
|
|
422
|
+
"Change the key that undoes Claude's last turn with Backstep (ctrl+z unless the user changed it). " +
|
|
423
423
|
'Use it when the user asks to change, rebind or pick a different Backstep or undo shortcut. ' +
|
|
424
424
|
'Keys are written like ctrl+u, cmd+z (⌘ on a Mac; most Mac terminals keep ⌘ keys for themselves, so pair it with a ctrl key), meta+z (Option) or ctrl+shift+z. ' +
|
|
425
425
|
'Several keys can share undo: separate them with commas. It takes effect right away, no restart.',
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backstep",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Undo for everything your agents touch: an MCP proxy that makes agent actions reversible.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "node src/cli.ts",
|
|
7
7
|
"ui": "node --disable-warning=ExperimentalWarning src/cli.ts ui",
|
|
8
8
|
"demo": "node --disable-warning=ExperimentalWarning examples/demo-db.ts",
|
|
9
|
-
"build": "tsc -p tsconfig.json && cp src/ui/index.html dist/ui/index.html && chmod +x dist/cli.js",
|
|
9
|
+
"build": "tsc -p tsconfig.json && cp src/ui/index.html dist/ui/index.html && chmod +x dist/cli.js && node -e \"const f='mod/.claude-plugin/plugin.json',p=JSON.parse(require('fs').readFileSync(f));p.version=require('./package.json').version;require('fs').writeFileSync(f,JSON.stringify(p,null,2)+'\\\\n')\"",
|
|
10
10
|
"test": "node --disable-warning=ExperimentalWarning --test test/*.test.ts",
|
|
11
11
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
12
12
|
},
|