backstep 0.1.32 → 0.1.33

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/CHANGES.json ADDED
@@ -0,0 +1,41 @@
1
+ [
2
+ {
3
+ "version": "0.1.33",
4
+ "notes": [
5
+ "After an update, Backstep shows what's new, like this."
6
+ ]
7
+ },
8
+ {
9
+ "version": "0.1.32",
10
+ "notes": [
11
+ "Undoing a Notion change in auto mode now finishes properly when Claude makes the last step.",
12
+ "Opening a page, file or app is no longer listed as something that couldn't be undone.",
13
+ "When something can't be undone, Backstep says why in plain words."
14
+ ]
15
+ },
16
+ {
17
+ "version": "0.1.31",
18
+ "notes": [
19
+ "In auto mode, Notion undos are finished by Claude straight away, with no extra question and no red error."
20
+ ]
21
+ },
22
+ {
23
+ "version": "0.1.30",
24
+ "notes": [
25
+ "Updates now say when they're done and it's safe to close the terminal.",
26
+ "Notion undos work in auto mode."
27
+ ]
28
+ },
29
+ {
30
+ "version": "0.1.29",
31
+ "notes": [
32
+ "If you edited a file after Claude's turn, undo asks before taking your changes along. Keep my changes undoes only Claude's."
33
+ ]
34
+ },
35
+ {
36
+ "version": "0.1.28",
37
+ "notes": [
38
+ "Updates install the new version even when Claude Code is open in a project named backstep."
39
+ ]
40
+ }
41
+ ]
package/README.md CHANGED
@@ -70,7 +70,7 @@ The undo key is ctrl+u (not ctrl+z: Claude Code suspends itself on ctrl+z in a t
70
70
 
71
71
  In the Claude Code desktop app, keys don't reach Backstep, so the line above the message box has an **Undo last turn** button to click instead (it asks once, then undoes). Typing `/backstep-undo` works everywhere and undoes straight away.
72
72
 
73
- 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).
73
+ 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). Once an update is in place, Backstep says what's new, from `CHANGES.json`.
74
74
 
75
75
  Other commands: `backstep ui`, `backstep log`, `backstep undo <action-id>`, `backstep undo --run <run-id>`, `backstep connect [supabase|firebase]` (connects what the project uses, the same as asking Claude), `backstep disconnect supabase <ref>`.
76
76
 
package/dist/cli.js CHANGED
@@ -57,7 +57,7 @@ import { connect } from "./connect.js";
57
57
  import { check, printCheck } from "./check.js";
58
58
  import { logo } from "./look.js";
59
59
  import { keyLabel, setUndoKey, undoKeys } from "./keys.js";
60
- import { checkUpdate, runUpdate, skipVersion } from "./update.js";
60
+ import { changesSince, checkUpdate, runUpdate, skipVersion } from "./update.js";
61
61
  import { askConnectionString } from "./setup.js";
62
62
  // Header values can name env vars, so tokens don't have to sit in agent config files.
63
63
  function parseHeaders(list) {
@@ -124,6 +124,8 @@ const HELP = `backstep: undo for everything your agents touch
124
124
  backstep undo --since <ms> [--until <ms>] [--json] [--via-claude-code] [--manual ask|undo|keep]
125
125
  undo everything any agent did in that window; files changed since
126
126
  (often by hand) are undone too, kept, or named first with ask
127
+ backstep changes --since <version> [--json]
128
+ what's new since that version
127
129
  backstep after <action-id> note how a tool left the files, once it ran (used by the Claude Code mod)
128
130
  backstep snap --session <id> --tool <name> [--cwd <dir>] -- <paths...>
129
131
  save paths before Claude Code's own tools change them (used by the Claude Code mod)
@@ -340,6 +342,19 @@ async function main() {
340
342
  return;
341
343
  }
342
344
  // `backstep update` installs the newest version; --skip <v> stops asking about that one.
345
+ // What's new since a version, for the mod to show once an update is in place.
346
+ if (cmd === "changes") {
347
+ const { values } = parseArgs({ args: rest, options: { since: { type: "string" }, json: { type: "boolean", default: false } } });
348
+ if (!values.since)
349
+ throw new Error("usage: backstep changes --since <version> [--json]");
350
+ const list = changesSince(values.since);
351
+ if (values.json)
352
+ console.log(JSON.stringify(list));
353
+ else
354
+ for (const c of list)
355
+ console.log(`${c.version}\n${c.notes.map((n) => ` - ${n}`).join("\n")}`);
356
+ return;
357
+ }
343
358
  if (cmd === "update") {
344
359
  const { values } = parseArgs({ args: rest, options: { skip: { type: "string" }, version: { type: "string" }, json: { type: "boolean", default: false } } });
345
360
  if (values.skip) {
package/dist/update.js CHANGED
@@ -4,7 +4,8 @@
4
4
  // Off with BACKSTEP_UPDATE_CHECK=0.
5
5
  import { spawnSync } from "./spawn.js";
6
6
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
- import { join } from "node:path";
7
+ import { dirname, join } from "node:path";
8
+ import { fileURLToPath } from "node:url";
8
9
  import { tmpdir } from "node:os";
9
10
  import { homeDir } from "./store.js";
10
11
  import { version } from "./telemetry.js";
@@ -48,6 +49,15 @@ export async function checkUpdate() {
48
49
  const available = latest !== null && newer(latest, current) && settings().skipVersion !== latest;
49
50
  return { current, latest, available };
50
51
  }
52
+ export function changesSince(from, upTo = version()) {
53
+ try {
54
+ const all = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "CHANGES.json"), "utf8"));
55
+ return all.filter((c) => newer(c.version, from) && !newer(c.version, upTo)).sort((a, b) => (newer(a.version, b.version) ? -1 : 1));
56
+ }
57
+ catch {
58
+ return [];
59
+ }
60
+ }
51
61
  export function skipVersion(v) {
52
62
  mkdirSync(homeDir(), { recursive: true });
53
63
  writeFileSync(file(), JSON.stringify({ ...settings(), skipVersion: v }, null, 2) + "\n");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backstep",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "description": "Press ctrl+z to rewind everything Claude did in its last turn: file edits, shell commands, and database and tool changes.",
5
5
  "types": "./types/index.d.ts",
6
6
  "userConfig": {
@@ -1277,6 +1277,8 @@ test('an update that reloads the mod partway is finished by the reloaded mod: it
1277
1277
  }
1278
1278
  const stdout = argv.includes('update-check')
1279
1279
  ? JSON.stringify({ current: started ? '0.2.0' : '0.1.2', latest: '0.2.0', available: !started })
1280
+ : argv.includes('changes')
1281
+ ? JSON.stringify(argv.includes('0.1.2') ? [{ version: '0.2.0', notes: ['Undo is faster.', 'Notion undos finish in auto mode.'] }] : [])
1280
1282
  : argv.includes('status')
1281
1283
  ? JSON.stringify({ items: [], suggest: [] })
1282
1284
  : '[]'
@@ -1320,6 +1322,8 @@ test('an update that reloads the mod partway is finished by the reloaded mod: it
1320
1322
  }
1321
1323
  expect(statuses.at(-1)).toBeUndefined()
1322
1324
  expect(logs.join('\n')).toContain('Backstep is updated to 0.2.0 (you had 0.1.2). It\'s safe to close this terminal now.')
1325
+ // Then what changed, from the new version's own notes.
1326
+ expect(logs.join('\n')).toContain("What's new in 0.2.0:\n • Undo is faster.\n • Notion undos finish in auto mode.")
1323
1327
  await ui.unmount()
1324
1328
  })
1325
1329
 
@@ -905,9 +905,26 @@ async function offerUpdate($: EngineInterface) {
905
905
  await installUpdate($, u.current, u.latest)
906
906
  }
907
907
 
908
- function updated($: EngineInterface, u: Updating) {
908
+ async function updated($: EngineInterface, u: Updating) {
909
909
  $.ui.log(`${MARK_TEXT} Backstep is updated to ${u.to}${u.from ? ` (you had ${u.from})` : ''}. It's safe to close ${u.win} now. Restart Claude Code to start using ${u.to}.`)
910
910
  $.ui.toast(`Backstep is updated to ${u.to}. Restart Claude Code to use it.`)
911
+ if (u.from) await whatsNew($, u.from)
912
+ }
913
+
914
+ // What changed since the version before, from the new version's own notes.
915
+ async function whatsNew($: EngineInterface, from: string) {
916
+ const ran = await $.process.run(backstep(['changes', '--since', from, '--json']), { timeoutMs: 15_000 }).catch(() => null)
917
+ let list: { version: string; notes: string[] }[] = []
918
+ try {
919
+ list = JSON.parse(ran?.stdout || '[]') as typeof list
920
+ } catch {
921
+ return
922
+ }
923
+ list = list.filter((c) => c.notes?.length)
924
+ if (!list.length) return
925
+ const one = list.length === 1
926
+ const lines = one ? list[0].notes.map((n) => ` • ${n}`) : list.flatMap((c) => [` ${c.version}`, ...c.notes.map((n) => ` • ${n}`)])
927
+ $.ui.log([`${MARK_TEXT} What's new${one ? ` in ${list[0].version}` : ` since ${from}`}:`, ...lines].join('\n'))
911
928
  }
912
929
 
913
930
  // After a reload in the middle of an update: once the new version is in place,
@@ -929,7 +946,7 @@ async function finishUpdate($: EngineInterface) {
929
946
  await $.clock.sleep(5000)
930
947
  $.ui.status(undefined)
931
948
  await update($, updating, () => null)
932
- updated($, u)
949
+ await updated($, u)
933
950
  return
934
951
  }
935
952
  await $.clock.sleep(5000)
@@ -961,7 +978,7 @@ async function installUpdate($: EngineInterface, current: string | undefined, la
961
978
  $.ui.status(undefined)
962
979
  await update($, updating, () => null)
963
980
  if (r.ok) {
964
- updated($, { ...(u.current ? { from: u.current } : {}), to: latest, win })
981
+ await updated($, { ...(u.current ? { from: u.current } : {}), to: latest, win })
965
982
  return true
966
983
  } else {
967
984
  $.ui.log(`${MARK_TEXT} Backstep couldn't update to ${u.latest}: ${r.detail}. You're still on ${u.current ?? 'the version you had'}${forced ? '' : ', which keeps working'}. To try again, run: npx backstep@latest setup`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backstep",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "description": "Undo for everything your agents touch: an MCP proxy that makes agent actions reversible.",
5
5
  "homepage": "https://getbackstep.com",
6
6
  "scripts": {
@@ -37,7 +37,8 @@
37
37
  "files": [
38
38
  "dist",
39
39
  "mod",
40
- "README.md"
40
+ "README.md",
41
+ "CHANGES.json"
41
42
  ],
42
43
  "keywords": [
43
44
  "mcp",