xtrm-tools 0.5.44 → 0.5.46

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xtrm-tools",
3
- "version": "0.5.44",
3
+ "version": "0.5.46",
4
4
  "description": "xtrm-tools: dual-runtime workflow enforcement (Claude Code + Pi) — hooks, extensions, skills, and MCP servers",
5
5
  "author": {
6
6
  "name": "jaggers"
package/CHANGELOG.md CHANGED
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.5.45] - 2026-03-25
11
+
12
+ ### Changed
13
+ - **`xt memory update`**: Replaced raw specialist stream with ora spinner + final summary output. Shows animated spinner while specialist runs; on finish prints `✓ .xtrm/memory.md written.` (or `✗`) followed by the last 10 meaningful lines dimmed.
14
+
15
+ ---
16
+
17
+ ## [0.5.44] - 2026-03-25
18
+
19
+ ### Added
20
+ - **`xt help`**: `xtrm memory update` entry added to PRIMARY COMMANDS section.
21
+
22
+ ---
23
+
10
24
  ## [0.5.43] - 2026-03-25
11
25
 
12
26
  ### Fixed
package/README.md CHANGED
@@ -70,6 +70,23 @@ claude plugin list
70
70
  npx -y github:Jaggerxtrm/xtrm-tools install
71
71
  ```
72
72
 
73
+ **Typical workflow after install:**
74
+ ```bash
75
+ # Start a sandboxed session in a worktree
76
+ xt claude my-feature
77
+
78
+ # Publish that worktree: rebase, push, open PR, optional cleanup
79
+ xt end
80
+
81
+ # Refresh project memory from bd memories + current repo state
82
+ xt memory update
83
+
84
+ # If multiple xt/* PRs are open, drain the merge queue oldest-first
85
+ xt merge
86
+ ```
87
+
88
+ `xt end` handles one worktree session at a time. `xt merge` is the follow-up queue operator: it inspects open `xt/*` PRs, processes them FIFO, waits for green CI on the oldest PR, merges it with `--rebase`, then rebases the remaining queued xt branches and repeats. `xt memory update` shells out to the `memory-processor` specialist, which condenses bd memories and current project state into `.xtrm/memory.md`; use `--dry-run` to inspect without writing.
89
+
73
90
  ---
74
91
 
75
92
  ## What's Included
@@ -176,6 +193,8 @@ xtrm <command> [options]
176
193
  | `pi [name]` | Launch Pi in a sandboxed `xt/<name>` worktree |
177
194
  | `attach [slug]` | Re-attach to an existing worktree and resume the Claude or Pi session |
178
195
  | `end` | Close worktree session: rebase, push, PR, cleanup |
196
+ | `memory update` | Run `memory-processor` to synthesize bd memories + repo state into `.xtrm/memory.md` |
197
+ | `merge` | Drain queued `xt/*` PRs via `xt-merge`: FIFO CI gate → rebase merge → rebase cascade |
179
198
  | `worktree list` | List active `xt/*` worktrees with runtime, last activity, and resume hint |
180
199
  | `worktree clean` | Remove merged worktrees |
181
200
  | `docs` | Documentation inspection and drift-check suite (`xtrm docs --help`) |
@@ -56240,9 +56240,12 @@ function createHelpCommand() {
56240
56240
  " 2) Do your work in that worktree/branch.",
56241
56241
  " 3) If the session closes unexpectedly, re-attach:",
56242
56242
  " xt attach [slug]",
56243
- " 4) Finish with:",
56243
+ " 4) Publish that worktree with:",
56244
56244
  " xt end",
56245
- " 5) Manage old worktrees when needed:",
56245
+ " 5) Optional follow-up operators:",
56246
+ " xt memory update (refresh .xtrm/memory.md from bd memories + repo state)",
56247
+ " xt merge (drain queued xt/* PRs oldest-first after CI passes)",
56248
+ " 6) Manage old worktrees when needed:",
56246
56249
  " xt worktree list | xt worktree clean"
56247
56250
  ]));
56248
56251
  blocks.push(section("PRIMARY COMMANDS", [
@@ -56271,6 +56274,10 @@ function createHelpCommand() {
56271
56274
  " Run memory-processor specialist to synthesize bd memories into .xtrm/memory.md.",
56272
56275
  " --dry-run: classify and report without writing memory.md or pruning.",
56273
56276
  "",
56277
+ " xtrm merge [--dry-run] [--no-beads]",
56278
+ " Drain the xt worktree PR merge queue via the xt-merge specialist (FIFO, --rebase).",
56279
+ " --dry-run: list queue and CI status without merging.",
56280
+ "",
56274
56281
  " xtrm debug [options]",
56275
56282
  " Stream xtrm event log (tool calls, gates, session/bd lifecycle).",
56276
56283
  " Options: --follow, --all, --session <id>, --type <domain>, --json",
@@ -56313,7 +56320,13 @@ function createHelpCommand() {
56313
56320
  blocks.push(section("SESSION CLOSE", [
56314
56321
  " xt end [options]",
56315
56322
  " Rebase to origin/main, push, open PR, link issues, and optionally clean worktree.",
56316
- " Options: --draft, --keep, --yes/-y"
56323
+ " Options: --draft, --keep, --yes/-y",
56324
+ "",
56325
+ " xt memory update [--dry-run] [--no-beads]",
56326
+ " Run memory-processor to synthesize .xtrm/memory.md from bd memories + repo state.",
56327
+ "",
56328
+ " xt merge [--dry-run] [--no-beads]",
56329
+ " Run xt-merge to drain queued xt/* PRs FIFO: CI gate \u2192 rebase merge \u2192 rebase cascade."
56317
56330
  ]));
56318
56331
  blocks.push(section("NOTES", [
56319
56332
  " - Banner is shown only for xtrm install.",
@@ -57743,7 +57756,7 @@ function createMemoryCommand() {
57743
57756
  return new Command("memory").description("Manage project memory (.xtrm/memory.md)").addCommand(createMemoryUpdateCommand());
57744
57757
  }
57745
57758
  function createMemoryUpdateCommand() {
57746
- return new Command("update").description("Run memory-processor specialist to synthesize bd memories into .xtrm/memory.md").option("--dry-run", "Report only \u2014 do not modify memories or write memory.md", false).option("--no-beads", "Skip creating a tracking bead for this run", false).action((opts) => {
57759
+ return new Command("update").description("Run memory-processor specialist to synthesize bd memories into .xtrm/memory.md").option("--dry-run", "Report only \u2014 do not modify memories or write memory.md", false).option("--no-beads", "Skip creating a tracking bead for this run", false).action(async (opts) => {
57747
57760
  const cwd = process.cwd();
57748
57761
  const check2 = (0, import_node_child_process12.spawnSync)("specialists", ["--version"], { encoding: "utf8", stdio: "pipe" });
57749
57762
  if (check2.status !== 0) {
@@ -57758,7 +57771,7 @@ function createMemoryUpdateCommand() {
57758
57771
  const specialists = JSON.parse(list.stdout);
57759
57772
  if (!specialists.some((s) => s.name === "memory-processor")) {
57760
57773
  console.error(kleur_default.red(
57761
- "\n \u2717 memory-processor specialist not found.\n Add specialists/memory-processor.specialist.yaml to this project.\n"
57774
+ "\n \u2717 memory-processor specialist not found.\n Run `specialists init` to install canonical specialists.\n"
57762
57775
  ));
57763
57776
  process.exit(1);
57764
57777
  }
@@ -57768,22 +57781,87 @@ function createMemoryUpdateCommand() {
57768
57781
  const prompt = opts.dryRun ? "Dry run: classify all memories and show the full report. Do not call bd forget or write .xtrm/memory.md." : "Run the full memory processor workflow.";
57769
57782
  const args = ["run", "memory-processor", "--prompt", prompt];
57770
57783
  if (!opts.beads) args.push("--no-beads");
57784
+ const memPath = (0, import_node_path7.join)(cwd, ".xtrm", "memory.md");
57785
+ const spinnerText = opts.dryRun ? "Analyzing memories..." : `${(0, import_node_fs6.existsSync)(memPath) ? "Updating" : "Creating"} .xtrm/memory.md...`;
57771
57786
  console.log(kleur_default.bold(`
57772
57787
  xt memory update${opts.dryRun ? " (dry run)" : ""}
57773
57788
  `));
57774
- if (!opts.dryRun) {
57775
- const memPath = (0, import_node_path7.join)(cwd, ".xtrm", "memory.md");
57776
- const action = (0, import_node_fs6.existsSync)(memPath) ? "Updating" : "Creating";
57777
- console.log(kleur_default.dim(` ${action} .xtrm/memory.md...
57789
+ const spinner = ora({ text: spinnerText, color: "cyan" }).start();
57790
+ const chunks = [];
57791
+ const exitCode = await new Promise((resolve2) => {
57792
+ const proc = (0, import_node_child_process12.spawn)("specialists", args, { cwd, stdio: ["inherit", "pipe", "pipe"] });
57793
+ proc.stdout.on("data", (d) => chunks.push(d.toString()));
57794
+ proc.stderr.on("data", (d) => chunks.push(d.toString()));
57795
+ proc.on("close", (code) => resolve2(code ?? 0));
57796
+ });
57797
+ if (exitCode === 0) {
57798
+ spinner.succeed(opts.dryRun ? "Analysis complete." : ".xtrm/memory.md written.");
57799
+ } else {
57800
+ spinner.fail("memory-processor failed.");
57801
+ }
57802
+ const lines = chunks.join("").split("\n").filter((l) => l.trim());
57803
+ const tail = lines.slice(-10).map((l) => kleur_default.dim(" " + l)).join("\n");
57804
+ if (tail) console.log("\n" + tail + "\n");
57805
+ process.exit(exitCode);
57806
+ });
57807
+ }
57808
+
57809
+ // src/commands/merge.ts
57810
+ var import_node_child_process13 = require("child_process");
57811
+ function createMergeCommand() {
57812
+ return new Command("merge").description("Drain the xt worktree PR merge queue via the xt-merge specialist").option("--dry-run", "List queue and CI status without merging", false).option("--no-beads", "Skip creating a tracking bead for this run", false).action(async (opts) => {
57813
+ const cwd = process.cwd();
57814
+ const check2 = (0, import_node_child_process13.spawnSync)("specialists", ["--version"], { encoding: "utf8", stdio: "pipe" });
57815
+ if (check2.status !== 0) {
57816
+ console.error(kleur_default.red(
57817
+ "\n \u2717 specialists CLI not found.\n Install with: npm install -g @jaggerxtrm/specialists\n"
57818
+ ));
57819
+ process.exit(1);
57820
+ }
57821
+ const list = (0, import_node_child_process13.spawnSync)("specialists", ["list", "--json"], { cwd, encoding: "utf8", stdio: "pipe" });
57822
+ if (list.status === 0) {
57823
+ try {
57824
+ const specialists = JSON.parse(list.stdout);
57825
+ if (!specialists.some((s) => s.name === "xt-merge")) {
57826
+ console.error(kleur_default.red(
57827
+ "\n \u2717 xt-merge specialist not found.\n Run `specialists init` to install canonical specialists.\n"
57828
+ ));
57829
+ process.exit(1);
57830
+ }
57831
+ } catch {
57832
+ }
57833
+ }
57834
+ const prompt = opts.dryRun ? "List all open xt/ PRs sorted by creation time and check CI status on each. Do not merge anything." : "Drain the xt worktree PR merge queue.";
57835
+ const args = ["run", "xt-merge", "--prompt", prompt];
57836
+ if (!opts.beads) args.push("--no-beads");
57837
+ console.log(kleur_default.bold(`
57838
+ xt merge${opts.dryRun ? " (dry run)" : ""}
57778
57839
  `));
57840
+ const spinner = ora({
57841
+ text: opts.dryRun ? "Checking PR queue..." : "Merging PR queue...",
57842
+ color: "cyan"
57843
+ }).start();
57844
+ const chunks = [];
57845
+ const exitCode = await new Promise((resolve2) => {
57846
+ const proc = (0, import_node_child_process13.spawn)("specialists", args, { cwd, stdio: ["inherit", "pipe", "pipe"] });
57847
+ proc.stdout.on("data", (d) => chunks.push(d.toString()));
57848
+ proc.stderr.on("data", (d) => chunks.push(d.toString()));
57849
+ proc.on("close", (code) => resolve2(code ?? 0));
57850
+ });
57851
+ if (exitCode === 0) {
57852
+ spinner.succeed(opts.dryRun ? "Queue check complete." : "PR queue drained.");
57853
+ } else {
57854
+ spinner.fail("xt-merge failed.");
57779
57855
  }
57780
- const result = (0, import_node_child_process12.spawnSync)("specialists", args, { cwd, stdio: "inherit", encoding: "utf8" });
57781
- process.exit(result.status ?? 0);
57856
+ const lines = chunks.join("").split("\n").filter((l) => l.trim());
57857
+ const tail = lines.slice(-10).map((l) => kleur_default.dim(" " + l)).join("\n");
57858
+ if (tail) console.log("\n" + tail + "\n");
57859
+ process.exit(exitCode);
57782
57860
  });
57783
57861
  }
57784
57862
 
57785
57863
  // src/commands/debug.ts
57786
- var import_node_child_process13 = require("child_process");
57864
+ var import_node_child_process14 = require("child_process");
57787
57865
  var import_node_fs7 = require("fs");
57788
57866
  var import_node_path8 = require("path");
57789
57867
  var KIND_LABELS = {
@@ -57931,7 +58009,7 @@ function buildWhere(opts, base) {
57931
58009
  }
57932
58010
  function queryEvents(dbPath, where, limit) {
57933
58011
  const sql = `SELECT id,ts,session_id,runtime,worktree,kind,tool_name,outcome,issue_id,duration_ms,data FROM events${where ? ` WHERE ${where}` : ""} ORDER BY id ASC LIMIT ${limit}`;
57934
- const result = (0, import_node_child_process13.spawnSync)("sqlite3", [dbPath, "-json", sql], {
58012
+ const result = (0, import_node_child_process14.spawnSync)("sqlite3", [dbPath, "-json", sql], {
57935
58013
  stdio: ["pipe", "pipe", "pipe"],
57936
58014
  encoding: "utf8",
57937
58015
  timeout: 5e3
@@ -58184,6 +58262,7 @@ program2.addCommand(createWorktreeCommand());
58184
58262
  program2.addCommand(createAttachCommand());
58185
58263
  program2.addCommand(createDocsCommand());
58186
58264
  program2.addCommand(createMemoryCommand());
58265
+ program2.addCommand(createMergeCommand());
58187
58266
  program2.addCommand(createDebugCommand());
58188
58267
  program2.addCommand(createHelpCommand());
58189
58268
  program2.action(async () => {