gm-gc 2.0.241 → 2.0.245

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": "gm",
3
- "version": "2.0.241",
3
+ "version": "2.0.245",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "homepage": "https://github.com/AnEntrypoint/gm",
@@ -13,7 +13,7 @@ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.env.GEMINI_PROJECT_
13
13
  const TOOLS_DIR = path.join(os.homedir(), '.claude', 'gm-tools');
14
14
  const CHECK_STAMP = path.join(TOOLS_DIR, '.last-check');
15
15
  const PKG_JSON = path.join(TOOLS_DIR, 'package.json');
16
- const MANAGED_PKGS = ['gm-exec', 'codebasesearch', 'mcp-thorns', 'agent-browser'];
16
+ const MANAGED_PKGS = ['codebasesearch', 'mcp-thorns', 'agent-browser'];
17
17
  const CHECK_INTERVAL_MS = 60 * 1000; // 60 seconds
18
18
 
19
19
  function ensureToolsDir() {
@@ -124,12 +124,27 @@ function loadLangPlugins(projectDir) {
124
124
  }
125
125
 
126
126
  // Helper: run a local binary (falls back to bunx if not installed)
127
+ function pkgEntry(name) {
128
+ try {
129
+ const pkg = JSON.parse(fs.readFileSync(path.join(TOOLS_DIR, 'node_modules', name, 'package.json'), 'utf8'));
130
+ const binVal = pkg.bin;
131
+ const rel = typeof binVal === 'string' ? binVal : (binVal?.[name] || Object.values(binVal || {})[0]);
132
+ if (rel) return path.join(TOOLS_DIR, 'node_modules', name, rel);
133
+ } catch {}
134
+ return null;
135
+ }
136
+
127
137
  function runLocal(name, args, opts = {}) {
138
+ if (IS_WIN) {
139
+ const entry = pkgEntry(name);
140
+ if (entry && fs.existsSync(entry)) {
141
+ return spawnSync('bun', [entry, ...args], { encoding: 'utf8', windowsHide: true, timeout: 65000, ...opts });
142
+ }
143
+ }
128
144
  const bin = localBin(name);
129
145
  if (fs.existsSync(bin)) {
130
146
  return spawnSync(bin, args, { encoding: 'utf8', windowsHide: true, timeout: 65000, ...opts });
131
147
  }
132
- // Fallback to bunx
133
148
  return spawnSync('bun', ['x', name, ...args], { encoding: 'utf8', windowsHide: true, timeout: 65000, ...opts });
134
149
  }
135
150
 
@@ -163,13 +178,15 @@ const allowWithNoop = (context) => {
163
178
  };
164
179
  };
165
180
 
166
- // ─── gm-exec runner helper ────────────────────────────────────────────────────
181
+ // ─── rs-exec runner helper ────────────────────────────────────────────────────
182
+ function rsExecBin() { return path.join(TOOLS_DIR, IS_WIN ? 'rs-exec.exe' : 'rs-exec'); }
183
+
167
184
  function runGmExec(args, opts = {}) {
168
- const bin = localBin('gm-exec');
185
+ const bin = rsExecBin();
169
186
  if (fs.existsSync(bin)) {
170
187
  return spawnSync(bin, args, { encoding: 'utf8', windowsHide: true, timeout: 65000, ...opts });
171
188
  }
172
- return spawnSync('bun', ['x', 'gm-exec', ...args], { encoding: 'utf8', windowsHide: true, timeout: 65000, ...opts });
189
+ return spawnSync('rs-exec', args, { encoding: 'utf8', windowsHide: true, timeout: 65000, ...opts });
173
190
  }
174
191
 
175
192
  // ─── Main hook ────────────────────────────────────────────────────────────────
@@ -258,7 +275,8 @@ const run = () => {
258
275
  return { globalArgs, rest };
259
276
  }
260
277
  const spawnAb = (bin, args, stdin) => {
261
- const opts = { encoding: 'utf-8', timeout: 60000, windowsHide: true, ...(IS_WIN && { shell: true }), cwd: process.cwd(), ...(stdin !== undefined && { input: stdin }) };
278
+ const headed = args.includes('--headed');
279
+ const opts = { encoding: 'utf-8', timeout: 60000, windowsHide: !headed, ...(IS_WIN && { shell: true }), cwd: process.cwd(), ...(stdin !== undefined && { input: stdin }) };
262
280
  const r = spawnSync(bin, args, opts);
263
281
  if (!r.stdout && !r.stderr && r.error) return `[spawn error: ${r.error.message}]`;
264
282
  const out = (r.stdout || '').trimEnd(), err = stripFooter(r.stderr || '').trimEnd();
@@ -283,17 +301,19 @@ const run = () => {
283
301
  } else {
284
302
  const hasClose = lines.some(l => { const w = (parseAbLine(l).rest[0]||'').toLowerCase(); return ['close','quit','exit'].includes(w); });
285
303
  const batchGlobals = firstParsed.globalArgs;
286
- const cmds = lines.map(l => {
304
+ const results = [];
305
+ for (const l of lines) {
287
306
  const { globalArgs, rest } = parseAbLine(l);
288
307
  const mergedGlobals = [...batchGlobals.filter(f => !globalArgs.includes(f)), ...globalArgs];
289
308
  const w = (rest[0]||'').toLowerCase();
290
309
  if (['open','goto','navigate'].includes(w)) sessions[sessionName] = { url: rest[1]||'?', ts: Date.now() };
291
310
  if (['close','quit','exit'].includes(w)) delete sessions[sessionName];
292
- if (!AB_CMDS.has(w)) return [...mergedGlobals, 'eval', l.trim()];
293
- return [...mergedGlobals, ...rest];
294
- });
311
+ const args = AB_CMDS.has(w) ? [...mergedGlobals, ...rest] : [...mergedGlobals, 'eval', '--stdin'];
312
+ const stdin = AB_CMDS.has(w) ? undefined : l.trim();
313
+ results.push(spawnAb(abBin, args, stdin));
314
+ }
295
315
  writeAbSessions(sessions);
296
- result = spawnAb(abBin, ['batch'], JSON.stringify(cmds));
316
+ result = results.filter(Boolean).join('\n');
297
317
  if (!hasClose && openSessions.length > 0) result += `\n\n[tab] Browser session "${sessionName}" still open. Close when done:\n agent-browser:\n close`;
298
318
  }
299
319
  } else {
@@ -457,7 +477,7 @@ const run = () => {
457
477
  }
458
478
  }
459
479
 
460
- if (/^bun\s+x\s+(gm-exec|codebasesearch)/.test(command)) {
480
+ if (/^bun\s+x\s+(gm-exec|rs-exec|codebasesearch)/.test(command)) {
461
481
  return deny(`Do not call ${command.match(/^bun\s+x\s+(\S+)/)[1]} directly. Use exec:<lang> syntax instead.\n\nExamples:\n exec:nodejs\n console.log("hello")\n\n exec:codesearch\n find all database queries\n\n exec:bash\n ls -la\n\nThe exec: prefix routes through the hook dispatcher which handles language detection, background tasks, and tool management automatically.`);
462
482
  }
463
483
 
@@ -13,7 +13,23 @@ function localBin(name) {
13
13
  return path.join(TOOLS_DIR, 'node_modules', '.bin', name + ext);
14
14
  }
15
15
 
16
+ function pkgEntry(name) {
17
+ try {
18
+ const pkg = JSON.parse(fs.readFileSync(path.join(TOOLS_DIR, 'node_modules', name, 'package.json'), 'utf8'));
19
+ const binVal = pkg.bin;
20
+ const rel = typeof binVal === 'string' ? binVal : (binVal?.[name] || Object.values(binVal || {})[0]);
21
+ if (rel) return path.join(TOOLS_DIR, 'node_modules', name, rel);
22
+ } catch {}
23
+ return null;
24
+ }
25
+
16
26
  function runLocal(name, args, opts = {}) {
27
+ if (IS_WIN) {
28
+ const entry = pkgEntry(name);
29
+ if (entry && fs.existsSync(entry)) {
30
+ return spawnSync('bun', [entry, ...args], { encoding: 'utf8', windowsHide: true, timeout: 30000, ...opts });
31
+ }
32
+ }
17
33
  const bin = localBin(name);
18
34
  if (fs.existsSync(bin)) {
19
35
  return spawnSync(bin, args, { encoding: 'utf8', windowsHide: true, timeout: 30000, ...opts });
@@ -21,9 +37,50 @@ function runLocal(name, args, opts = {}) {
21
37
  return spawnSync('bun', ['x', name, ...args], { encoding: 'utf8', windowsHide: true, timeout: 30000, ...opts });
22
38
  }
23
39
 
24
- const MANAGED_PKGS = ['gm-exec', 'codebasesearch', 'mcp-thorns', 'agent-browser'];
40
+ const MANAGED_PKGS = ['codebasesearch', 'mcp-thorns', 'agent-browser'];
25
41
  const PKG_JSON = path.join(TOOLS_DIR, 'package.json');
26
42
 
43
+ const RS_EXEC_REPO = 'AnEntrypoint/rs-exec';
44
+ const archMap = { x64: 'x86_64', arm64: 'aarch64', ia32: 'x86_64' };
45
+ const osTargets = {
46
+ win32: a => `rs-exec-${a}-pc-windows-msvcexe`,
47
+ darwin: a => `rs-exec-${a}-apple-darwin`,
48
+ linux: a => `rs-exec-${a}-unknown-linux-gnu`,
49
+ };
50
+ const osProcTargets = {
51
+ win32: a => `rs-exec-process-${a}-pc-windows-msvcexe`,
52
+ darwin: a => `rs-exec-process-${a}-apple-darwin`,
53
+ linux: a => `rs-exec-process-${a}-unknown-linux-gnu`,
54
+ };
55
+
56
+ function rsExecBin() { return path.join(TOOLS_DIR, IS_WIN ? 'rs-exec.exe' : 'rs-exec'); }
57
+ function rsExecProcessBin() { return path.join(TOOLS_DIR, IS_WIN ? 'rs-exec-process.exe' : 'rs-exec-process'); }
58
+
59
+ function downloadBin(assetName, dest) {
60
+ const https = require('https');
61
+ const url = `https://github.com/${RS_EXEC_REPO}/releases/latest/download/${assetName}`;
62
+ return new Promise((resolve) => {
63
+ const follow = (u) => https.get(u, { headers: { 'User-Agent': 'gm' } }, res => {
64
+ if (res.statusCode >= 300 && res.statusCode < 400) return follow(res.headers.location);
65
+ const chunks = [];
66
+ res.on('data', c => chunks.push(c));
67
+ res.on('end', () => { try { fs.writeFileSync(dest, Buffer.concat(chunks)); fs.chmodSync(dest, 0o755); } catch {} resolve(); });
68
+ }).on('error', () => resolve());
69
+ follow(url);
70
+ });
71
+ }
72
+
73
+ async function ensureRsExec() {
74
+ const arch = archMap[process.arch] || 'x86_64';
75
+ const plat = process.platform;
76
+ const mainBin = rsExecBin();
77
+ const procBin = rsExecProcessBin();
78
+ const downloads = [];
79
+ if (!fs.existsSync(mainBin)) downloads.push(downloadBin(osTargets[plat]?.(arch) || osTargets.linux(arch), mainBin));
80
+ if (!fs.existsSync(procBin)) downloads.push(downloadBin(osProcTargets[plat]?.(arch) || osProcTargets.linux(arch), procBin));
81
+ if (downloads.length) await Promise.all(downloads);
82
+ }
83
+
27
84
  function ensureTools() {
28
85
  try { fs.mkdirSync(TOOLS_DIR, { recursive: true }); } catch {}
29
86
  if (!fs.existsSync(PKG_JSON)) {
@@ -40,6 +97,7 @@ function ensureTools() {
40
97
  }
41
98
 
42
99
  ensureTools();
100
+ ensureRsExec().catch(() => {});
43
101
 
44
102
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.env.GEMINI_PROJECT_DIR || process.env.OC_PROJECT_DIR || process.env.KILO_PROJECT_DIR;
45
103
 
@@ -92,7 +150,7 @@ ensureGitignore();
92
150
  try {
93
151
  let outputs = [];
94
152
 
95
- outputs.push('Use the Skill tool with skill: "gm" to begin — do NOT use the Agent tool to load skills. Skills are invoked via the Skill tool only, never as agents. All code execution uses exec:<lang> via the Bash tool — never direct Bash(node ...) or Bash(npm ...) or Bash(npx ...) or Bash(bun x gm-exec ...).');
153
+ outputs.push('Use the Skill tool with skill: "gm" to begin — do NOT use the Agent tool to load skills. Skills are invoked via the Skill tool only, never as agents. All code execution uses exec:<lang> via the Bash tool — never direct Bash(node ...) or Bash(npm ...) or Bash(npx ...) or Bash(bun x rs-exec ...).');
96
154
 
97
155
  if (projectDir && fs.existsSync(projectDir)) {
98
156
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-gc",
3
- "version": "2.0.241",
3
+ "version": "2.0.245",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",