backstep 0.1.24 → 0.1.25

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/dist/cli.js CHANGED
@@ -17,7 +17,7 @@ import { snapshotRest } from "./adapters/rest.js";
17
17
  const PUSH = /\bgit\b[^;&|]*\bpush\b/;
18
18
  import { classifyShell, commandDir, supabaseTarget, touchesDatabase, touchesFirebase, touchesSqlite, touchesSupabase } from "./shell.js";
19
19
  import { watchEnd, watchStart } from "./watch.js";
20
- import { sendActive } from "./telemetry.js";
20
+ import { sendActive, version } from "./telemetry.js";
21
21
  import { readFileSync, rmSync } from "node:fs";
22
22
  import { loadRules } from "./rules.js";
23
23
  import { setup } from "./setup.js";
@@ -315,8 +315,15 @@ async function main() {
315
315
  const target = values.version ?? (await checkUpdate()).latest;
316
316
  if (!target)
317
317
  throw new Error("couldn't reach npm to find the newest version");
318
+ const was = version();
319
+ if (!values.json)
320
+ console.log(`Updating Backstep from ${was} to ${target}… Don't close this terminal until it says it's done (about a minute).`);
318
321
  const out = runUpdate(target);
319
- console.log(values.json ? JSON.stringify(out) : out.ok ? `Backstep ${out.detail}. Restart Claude Code to use it.` : `Update failed: ${out.detail}`);
322
+ console.log(values.json
323
+ ? JSON.stringify(out)
324
+ : out.ok
325
+ ? `Backstep is updated to ${target} (you had ${was}). It's safe to close the terminal now. Restart Claude Code to start using ${target}.`
326
+ : `Backstep couldn't update to ${target}: ${out.detail}. You're still on ${was}, which keeps working.`);
320
327
  if (!out.ok)
321
328
  process.exitCode = 1;
322
329
  return;
package/dist/setup.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from "./spawn.js";
2
- import { cpSync, readFileSync, rmSync } from "node:fs";
2
+ import { cpSync, existsSync, readFileSync, renameSync, rmSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { homeDir } from "./store.js";
@@ -24,9 +24,28 @@ function keepCopy() {
24
24
  if (run("npm", ["install", "-g", `backstep@${version}`]).ok) {
25
25
  return { root: join(run("npm", ["root", "-g"]).out, "backstep"), onPath: true };
26
26
  }
27
- const modules = join(homeDir(), "app", "node_modules");
28
- rmSync(modules, { recursive: true, force: true });
29
- cpSync(dirname(ROOT), modules, { recursive: true });
27
+ // The new copy is made beside the old one, then swapped in: closing the
28
+ // terminal partway through leaves the old copy whole and working.
29
+ const app = join(homeDir(), "app");
30
+ const modules = join(app, "node_modules");
31
+ const fresh = join(app, "node_modules.new");
32
+ const old = join(app, "node_modules.old");
33
+ rmSync(fresh, { recursive: true, force: true });
34
+ cpSync(dirname(ROOT), fresh, { recursive: true });
35
+ rmSync(old, { recursive: true, force: true });
36
+ try {
37
+ if (existsSync(modules))
38
+ renameSync(modules, old);
39
+ renameSync(fresh, modules);
40
+ }
41
+ catch {
42
+ // A file still open (Windows won't move it): copy over the old copy where it is instead.
43
+ if (!existsSync(modules) && existsSync(old))
44
+ renameSync(old, modules);
45
+ cpSync(fresh, modules, { recursive: true, force: true });
46
+ }
47
+ rmSync(fresh, { recursive: true, force: true });
48
+ rmSync(old, { recursive: true, force: true });
30
49
  return { root: join(modules, "backstep"), onPath: false };
31
50
  }
32
51
  const WHERE = `In Supabase, open your project, press Connect at the top, and copy the URI under
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backstep",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
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": {
@@ -589,7 +589,16 @@ function updates(on: On, answer: string) {
589
589
  toasts.push(e.text)
590
590
  return { value: undefined }
591
591
  })
592
- on('ui.log', () => ({ value: undefined }))
592
+ const logs: string[] = []
593
+ const statuses: (string | undefined)[] = []
594
+ on('ui.log', (_, e) => {
595
+ logs.push(e.text)
596
+ return { value: undefined }
597
+ })
598
+ on('ui.status', (_, e) => {
599
+ statuses.push(e.text)
600
+ return { value: undefined }
601
+ })
593
602
  on('tool.register', (_, e) => ({ value: { tool: `mcp__backstep__${e.name}` } }))
594
603
  on('tool.call', { tool: 'AskUserQuestion' }, (_, e) => {
595
604
  const questions = (e as unknown as { questions: { question: string }[] }).questions
@@ -600,7 +609,7 @@ function updates(on: On, answer: string) {
600
609
  on('session.cwd', () => ({ value: '/repo' }))
601
610
  on('session.id', () => ({ value: 's1' }))
602
611
  on('tool.call', () => ({ result: {} }))
603
- return { runs, toasts, asked }
612
+ return { runs, toasts, asked, logs, statuses }
604
613
  }
605
614
 
606
615
  // Claude is about to change the database: the moment Backstep asks.
@@ -614,12 +623,17 @@ async function changeDatabase($: Parameters<Parameters<typeof test>[2]>[0], on:
614
623
  }
615
624
 
616
625
  test('a newer version is offered as a session starts, and Update installs it', { options: { cli: CLI } }, async ($, on) => {
617
- const { runs, toasts, asked } = updates(on, 'Update')
626
+ const { runs, toasts, asked, logs, statuses } = updates(on, 'Update')
618
627
  await $.session.start({ cwd: '/repo', surface: 'terminal', hasPerson: true } as never)
619
628
  await settle()
620
629
  expect(asked).toEqual(['Backstep 0.2.0 is out (you have 0.1.2). Update now?'])
621
630
  expect(runs.some((r) => r.slice(3).join(' ') === 'update --version 0.2.0 --json')).toBe(true)
622
- expect(toasts.at(-1)).toBe('Backstep updated to 0.2.0. Restart Claude Code to use it.')
631
+ expect(toasts.at(-1)).toBe('Backstep is updated to 0.2.0. Restart Claude Code to use it.')
632
+ // While it runs it says not to close the terminal; once done, which version it updated to.
633
+ expect(logs.find((l) => l.includes('Updating Backstep from 0.1.2 to 0.2.0'))).toContain("Don't close this terminal")
634
+ expect(statuses[0]).toContain("don't close this terminal")
635
+ expect(statuses.at(-1)).toBeUndefined()
636
+ expect(logs.at(-1)).toContain('Backstep is updated to 0.2.0 (you had 0.1.2)')
623
637
  })
624
638
 
625
639
  test('Skip this version stops asking about it, and Not now changes nothing', { options: { cli: CLI } }, async ($, on) => {
@@ -661,7 +661,11 @@ async function offerUpdate($: EngineInterface) {
661
661
  return
662
662
  }
663
663
  if (answer !== UPDATE.yes) return
664
- $.ui.toast(`Updating Backstep to ${u.latest}…`)
664
+ // Said where it stays (the conversation) and where it's seen while it runs (the status line).
665
+ const from = u.current ? ` from ${u.current}` : ''
666
+ $.ui.log(`${MARK_TEXT} Updating Backstep${from} to ${u.latest}… Don't close this terminal until it says it's done (about a minute).`)
667
+ $.ui.toast(`Updating Backstep to ${u.latest}. Don't close this terminal until it's done.`, { timeoutMs: 15_000 })
668
+ $.ui.status(`◀◀ Updating Backstep to ${u.latest}… don't close this terminal`)
665
669
  const done = await $.process
666
670
  .run(backstep(['update', '--version', u.latest, '--json']), { timeoutMs: 600_000 })
667
671
  .catch((err: unknown) => ({ exitCode: 1, stdout: '', stderr: err instanceof Error ? err.message : String(err) }))
@@ -671,7 +675,14 @@ async function offerUpdate($: EngineInterface) {
671
675
  } catch {
672
676
  // keep the error above
673
677
  }
674
- $.ui.toast(r.ok ? `Backstep ${r.detail}. Restart Claude Code to use it.` : `Backstep couldn't update: ${r.detail}. Try: npx backstep@latest setup`)
678
+ $.ui.status(undefined)
679
+ if (r.ok) {
680
+ $.ui.log(`${MARK_TEXT} Backstep is updated to ${u.latest}${u.current ? ` (you had ${u.current})` : ''}. It's safe to close the terminal now. Restart Claude Code to start using ${u.latest}.`)
681
+ $.ui.toast(`Backstep is updated to ${u.latest}. Restart Claude Code to use it.`)
682
+ } else {
683
+ $.ui.log(`${MARK_TEXT} Backstep couldn't update to ${u.latest}: ${r.detail}. You're still on ${u.current ?? 'the version you had'}, which keeps working. To try again, run: npx backstep@latest setup`)
684
+ $.ui.toast(`Backstep couldn't update: ${r.detail}. Try: npx backstep@latest setup`)
685
+ }
675
686
  }
676
687
 
677
688
  const KEY = 'mcp__backstep__undo_key'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backstep",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
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": {