xtrm-tools 2.1.21 → 2.1.23

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/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xtrm-cli",
3
- "version": "2.1.21",
3
+ "version": "2.1.23",
4
4
  "description": "Claude Code tools installer (skills, hooks, MCP servers)",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",
@@ -2,7 +2,7 @@ import type { ExtensionAPI, ToolCallEvent, ToolResultEvent } from "@mariozechner
2
2
  import { isToolCallEventType, isBashToolResult } from "@mariozechner/pi-coding-agent";
3
3
  import * as path from "node:path";
4
4
  import * as fs from "node:fs";
5
- import { SubprocessRunner, EventAdapter, Logger } from "./core";
5
+ import { SubprocessRunner, EventAdapter, Logger } from "./core/lib";
6
6
 
7
7
  const logger = new Logger({ namespace: "beads" });
8
8
 
@@ -16,24 +16,6 @@ export default function (pi: ExtensionAPI) {
16
16
  return null;
17
17
  };
18
18
 
19
- const setSessionClaim = async (sessionId: string, issueId: string, cwd: string): Promise<boolean> => {
20
- const result = await SubprocessRunner.run("bd", ["kv", "set", `claimed:${sessionId}`, issueId], { cwd });
21
- return result.code === 0;
22
- };
23
-
24
- const clearSessionClaim = async (sessionId: string, cwd: string): Promise<boolean> => {
25
- const result = await SubprocessRunner.run("bd", ["kv", "clear", `claimed:${sessionId}`], { cwd });
26
- return result.code === 0;
27
- };
28
-
29
- const getInProgressSummary = async (cwd: string): Promise<string | null> => {
30
- const result = await SubprocessRunner.run("bd", ["list", "--status=in_progress"], { cwd });
31
- if (result.code === 0 && result.stdout.includes("Total:")) {
32
- return result.stdout.trim();
33
- }
34
- return null;
35
- };
36
-
37
19
  const hasTrackableWork = async (cwd: string): Promise<boolean> => {
38
20
  const result = await SubprocessRunner.run("bd", ["list"], { cwd });
39
21
  if (result.code === 0 && result.stdout.includes("Total:")) {
@@ -47,83 +29,37 @@ export default function (pi: ExtensionAPI) {
47
29
  return false;
48
30
  };
49
31
 
50
- // 0. Register Custom Commands
51
- pi.registerCommand({
52
- name: "claim",
53
- description: "Claim a beads issue for this session",
54
- async execute(args, ctx) {
55
- const cwd = getCwd(ctx);
56
- if (!isBeadsProject(cwd)) {
57
- ctx.ui.notify("Not a beads project.", "error");
58
- return;
59
- }
60
-
61
- const issueId = args[0];
62
- if (!issueId) {
63
- ctx.ui.notify("Usage: /claim <issue-id>", "warning");
64
- return;
65
- }
66
-
67
- // Ensure issue is in_progress first
68
- await SubprocessRunner.run("bd", ["update", issueId, "--status=in_progress"], { cwd });
69
-
70
- const ok = await setSessionClaim(ctx.sessionManager.sessionId, issueId, cwd);
71
- if (ok) {
72
- ctx.ui.notify(`Claimed issue: ${issueId}`, "info");
73
- } else {
74
- ctx.ui.notify(`Failed to claim issue: ${issueId}`, "error");
75
- }
76
- }
77
- });
78
-
79
- pi.registerCommand({
80
- name: "unclaim",
81
- description: "Clear the beads issue claim for this session",
82
- async execute(_args, ctx) {
83
- const cwd = getCwd(ctx);
84
- if (!isBeadsProject(cwd)) return;
85
-
86
- const ok = await clearSessionClaim(ctx.sessionManager.sessionId, cwd);
87
- if (ok) {
88
- ctx.ui.notify("Claim cleared.", "info");
89
- }
90
- }
91
- });
92
-
93
- // 1. Tool Call Interception (Edit Gate & Commit Gate)
94
32
  pi.on("tool_call", async (event, ctx) => {
95
33
  const cwd = getCwd(ctx);
96
34
  if (!isBeadsProject(cwd)) return undefined;
97
35
 
98
36
  const sessionId = ctx.sessionManager.sessionId;
99
37
 
100
- // A. Edit Gate
101
38
  if (EventAdapter.isMutatingFileTool(event)) {
102
39
  const claim = await getSessionClaim(sessionId, cwd);
103
- if (claim) return undefined;
104
-
105
- const hasWork = await hasTrackableWork(cwd);
106
- if (!hasWork) return undefined;
107
-
108
- const reason = "No active issue claim for this session. Use `/claim <id>` to track your work.";
109
- if (ctx.hasUI) {
110
- ctx.ui.notify("Beads: Edit blocked. Claim an issue first.", "warning");
111
- }
112
- return { block: true, reason };
40
+ if (!claim) {
41
+ const hasWork = await hasTrackableWork(cwd);
42
+ if (hasWork) {
43
+ if (ctx.hasUI) {
44
+ ctx.ui.notify("Beads: Edit blocked. Claim an issue first.", "warning");
45
+ }
46
+ return {
47
+ block: true,
48
+ reason: `No active issue claim for this session (${sessionId}).\n bd update <id> --claim\n bd kv set "claimed:${sessionId}" "<id>"`,
49
+ };
50
+ }
51
+ }
113
52
  }
114
53
 
115
- // B. Commit Gate
116
54
  if (isToolCallEventType("bash", event)) {
117
55
  const command = event.input.command;
118
- if (/\bgit\s+commit\b/.test(command)) {
119
- const claim = await getSessionClaim(sessionId, cwd);
56
+ if (command && /\bgit\s+commit\b/.test(command)) {
57
+ const claim = await getSessionClaim(sessionId, cwd);
120
58
  if (claim) {
121
- const inProgress = await getInProgressSummary(cwd);
122
- const reason = `Resolve open claim [${claim}] before committing. Use \`bd close ${claim}\` first.\n\n${inProgress || ""}`;
123
- if (ctx.hasUI) {
124
- ctx.ui.notify("Beads: Commit blocked. Close active claim first.", "warning");
125
- }
126
- return { block: true, reason };
59
+ return {
60
+ block: true,
61
+ reason: `Resolve open claim [${claim}] before committing.`,
62
+ };
127
63
  }
128
64
  }
129
65
  }
@@ -131,55 +67,28 @@ export default function (pi: ExtensionAPI) {
131
67
  return undefined;
132
68
  });
133
69
 
134
- // 2. Tool Result Interception (Memory Gate)
135
70
  pi.on("tool_result", async (event, ctx) => {
136
- const cwd = getCwd(ctx);
137
- if (!isBeadsProject(cwd)) return undefined;
138
-
139
71
  if (isBashToolResult(event)) {
140
72
  const command = event.input.command;
141
- // Also clear claim on bd close
142
- if (/\bbd\s+close\b/.test(command) && !event.isError) {
143
- await clearSessionClaim(ctx.sessionManager.sessionId, cwd);
144
73
 
74
+ if (command && /\bbd\s+update\b/.test(command) && /--claim\b/.test(command) && !event.isError) {
75
+ const issueMatch = command.match(/\bbd\s+update\s+(\S+)/);
76
+ if (issueMatch) {
77
+ const issueId = issueMatch[1];
78
+ const cwd = getCwd(ctx);
79
+ const sessionId = ctx.sessionManager.sessionId;
80
+ await SubprocessRunner.run("bd", ["kv", "set", `claimed:${sessionId}`, issueId], { cwd });
81
+ const claimNotice = `\n\n✅ **Beads**: Session \`${sessionId}\` claimed issue \`${issueId}\`. File edits are now unblocked.`;
82
+ return { content: [...event.content, { type: "text", text: claimNotice }] };
83
+ }
84
+ }
85
+
86
+ if (command && /\bbd\s+close\b/.test(command) && !event.isError) {
145
87
  const reminder = "\n\n**Beads Insight**: Work completed. Consider if this session produced insights worth persisting via `bd remember`.";
146
- const newContent = [...event.content];
147
- newContent.push({ type: "text", text: reminder });
88
+ const newContent = [...event.content, { type: "text", text: reminder }];
148
89
  return { content: newContent };
149
90
  }
150
91
  }
151
92
  return undefined;
152
93
  });
153
-
154
- // 3. Compaction Support
155
- pi.on("session_before_compact", async (event, ctx) => {
156
- const cwd = getCwd(ctx);
157
- if (!isBeadsProject(cwd)) return undefined;
158
-
159
- const sessionId = ctx.sessionManager.sessionId;
160
- const claim = await getSessionClaim(sessionId, cwd);
161
-
162
- if (claim) {
163
- return {
164
- compaction: {
165
- summary: (event.compaction?.summary || "") + `\n\nActive Beads Claim: ${claim}`,
166
- firstKeptEntryId: event.preparation.firstKeptEntryId,
167
- }
168
- };
169
- }
170
- return undefined;
171
- });
172
-
173
- // 4. Shutdown Warning
174
- pi.on("session_shutdown", async (_event, ctx) => {
175
- const cwd = getCwd(ctx);
176
- if (!isBeadsProject(cwd)) return;
177
-
178
- const sessionId = ctx.sessionManager.sessionId;
179
- const claim = await getSessionClaim(sessionId, cwd);
180
-
181
- if (claim && ctx.hasUI) {
182
- ctx.ui.notify(`Warning: Exiting with active Beads claim [${claim}].`, "warning");
183
- }
184
- });
185
94
  }
@@ -1,6 +1,6 @@
1
1
  import type { ExtensionAPI, ToolResultEvent } from "@mariozechner/pi-coding-agent";
2
2
  import { isBashToolResult } from "@mariozechner/pi-coding-agent";
3
- import { SubprocessRunner, Logger } from "./core";
3
+ import { SubprocessRunner, Logger } from "./core/lib";
4
4
 
5
5
  const logger = new Logger({ namespace: "main-guard-post-push" });
6
6
 
@@ -1,6 +1,6 @@
1
1
  import type { ExtensionAPI, ToolCallEvent } from "@mariozechner/pi-coding-agent";
2
2
  import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
3
- import { SubprocessRunner, EventAdapter, Logger } from "./core";
3
+ import { SubprocessRunner, EventAdapter, Logger } from "./core/lib";
4
4
 
5
5
  const logger = new Logger({ namespace: "main-guard" });
6
6
 
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionAPI, ToolResultEvent } from "@mariozechner/pi-coding-agent";
2
- import { SubprocessRunner, EventAdapter, Logger } from "./core";
2
+ import { SubprocessRunner, EventAdapter, Logger } from "./core/lib";
3
3
  import * as path from "node:path";
4
4
  import * as fs from "node:fs";
5
5
 
@@ -1,6 +1,6 @@
1
1
  import type { ExtensionAPI, ToolCallEvent, ToolResultEvent } from "@mariozechner/pi-coding-agent";
2
2
  import { isToolCallEventType, isBashToolResult } from "@mariozechner/pi-coding-agent";
3
- import { SubprocessRunner, Logger } from "./core";
3
+ import { SubprocessRunner, Logger } from "./core/lib";
4
4
  import * as path from "node:path";
5
5
  import * as fs from "node:fs";
6
6
 
@@ -1,7 +1,7 @@
1
1
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { Logger } from "./core";
4
+ import { Logger } from "./core/lib";
5
5
 
6
6
  const logger = new Logger({ namespace: "xtrm-loader" });
7
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xtrm-tools",
3
- "version": "2.1.21",
3
+ "version": "2.1.23",
4
4
  "description": "Claude Code tools installer (skills, hooks, MCP servers)",
5
5
  "license": "MIT",
6
6
  "type": "module",
File without changes