gm-oc 2.0.1061 → 2.0.1063

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/gm-oc.mjs CHANGED
@@ -2,7 +2,6 @@ import { readFileSync, existsSync, writeFileSync, unlinkSync } from 'fs';
2
2
  import { join, dirname, extname, basename } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { spawnSync } from 'child_process';
5
- import { tmpdir } from 'os';
6
5
 
7
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
7
  const LANG_ALIASES = { js:'nodejs',javascript:'nodejs',ts:'typescript',node:'nodejs',py:'python',sh:'bash',shell:'bash',zsh:'bash' };
@@ -29,18 +28,53 @@ function stripFooter(s) { return s ? s.replace(/\n\[Running tools\][\s\S]*$/, ''
29
28
  function runExecSync(rawLang, code, cwd) {
30
29
  const lang = LANG_ALIASES[rawLang] || rawLang || 'nodejs';
31
30
  const projectDir = cwd || process.cwd();
32
- if (lang === 'codesearch' || lang === 'search') return runPlugkit(['search', '--path', projectDir, code.trim()]);
33
- if (['runner','status','sleep','close'].includes(lang)) return runPlugkit([lang, code.trim()]);
34
- if (['browser','tail','watch','wait','type','kill-port','health','recall','memorize','forget','feedback','discipline','pause'].includes(lang)) {
35
- return runPlugkit(['exec', '--lang', lang, '--code', code.trim(), '--cwd', projectDir]);
36
- }
37
- if (lang === 'cmd') {
38
- const opts = { encoding: 'utf-8', timeout: 30000, windowsHide: true, cwd: projectDir };
39
- const r = spawnSync('cmd', ['/c', code], opts);
40
- const o = (r.stdout || '').trimEnd(), e = stripFooter(r.stderr || '').trimEnd();
41
- return o && e ? o + '\n[stderr]\n' + e : o || e || '(no output)';
31
+ const spoolBase = join(projectDir, '.gm', 'exec-spool');
32
+ const taskId = Date.now() + '-' + Math.random().toString(16).slice(2, 8);
33
+
34
+ const isVerb = ['codesearch','recall','memorize','wait','sleep','status','close','browser','runner','type','kill-port','forget','feedback','discipline','pause','health'].includes(lang);
35
+ const langDir = lang.match(/^(nodejs|python|bash|typescript|go|rust|c|cpp|java|deno)$/) ? lang : 'nodejs';
36
+ const ext = {nodejs:'js',python:'py',bash:'sh',typescript:'ts',go:'go',rust:'rs',c:'c',cpp:'cpp',java:'java',deno:'ts'}[langDir] || 'js';
37
+
38
+ const inDir = join(spoolBase, 'in', isVerb ? lang : langDir);
39
+ const outDir = join(spoolBase, 'out');
40
+ const inFile = join(inDir, taskId + (isVerb ? '.txt' : '.' + ext));
41
+ const jsonFile = join(outDir, taskId + '.json');
42
+
43
+ try { fs.mkdirSync(inDir, { recursive: true }); fs.mkdirSync(outDir, { recursive: true }); } catch(e) {}
44
+ writeFileSync(inFile, code, 'utf-8');
45
+
46
+ const start = Date.now();
47
+ while (Date.now() - start < 28000) {
48
+ if (existsSync(jsonFile)) {
49
+ try {
50
+ const meta = JSON.parse(readFileSync(jsonFile, 'utf-8'));
51
+ const outFile = jsonFile.replace(/\.json$/, '.out');
52
+ const errFile = jsonFile.replace(/\.json$/, '.err');
53
+ const stdout = existsSync(outFile) ? readFileSync(outFile, 'utf-8') : '';
54
+ const stderr = existsSync(errFile) ? readFileSync(errFile, 'utf-8') : '';
55
+ const o = stdout.trimEnd(), e = stripFooter(stderr).trimEnd();
56
+ return o && e ? o + '\n[stderr]\n' + e : o || e || '(no output)';
57
+ } catch(e) {}
58
+ }
59
+ try { require('child_process').execSync('sleep 0.05', { stdio: 'ignore' }); } catch(e) { const s = Date.now(); while(Date.now()-s<50){} }
42
60
  }
43
- return runPlugkit(['exec', '--lang', lang, '--code', code.trim(), '--cwd', projectDir]);
61
+ return '[spool dispatch timeout after 28s]';
62
+ }
63
+
64
+ function ensureSpoolWatcher(dir) {
65
+ try {
66
+ const spoolBase = join(dir, '.gm', 'exec-spool');
67
+ const pidFile = join(spoolBase, '.watcher.pid');
68
+ try { fs.mkdirSync(spoolBase, { recursive: true }); } catch(e) {}
69
+ const alreadyRunning = existsSync(pidFile) && (() => { try { const p = parseInt(readFileSync(pidFile,'utf-8')); process.kill(p,0); return true; } catch(e) { return false; } })();
70
+ if (!alreadyRunning) {
71
+ const r = spawnSync('node', [join(__dirname, '..', 'bin', 'plugkit.js'), 'spool'], {
72
+ detached: true, stdio: 'ignore', windowsHide: true,
73
+ env: { ...process.env, GM_SPOOL_DIR: spoolBase }
74
+ });
75
+ if (r.pid) { try { writeFileSync(pidFile, String(r.pid), 'utf-8'); } catch(e) {} }
76
+ }
77
+ } catch(e) {}
44
78
  }
45
79
 
46
80
  const BANNED_BASH = ['grep','rg','find','glob','awk','sed','cat','head','tail'];
@@ -61,7 +95,19 @@ export async function GmPlugin({ directory }) {
61
95
  if (!sessionStarted) {
62
96
  sessionStarted = true;
63
97
  try { runPlugkit(['hook', 'session-start']); } catch(e) {}
64
- try { runPlugkit(['bootstrap', directory]); } catch(e) {}
98
+ try {
99
+ const spoolBase = join(directory, '.gm', 'exec-spool');
100
+ const pidFile = join(spoolBase, '.watcher.pid');
101
+ try { fs.mkdirSync(spoolBase, { recursive: true }); } catch(e) {}
102
+ const alreadyRunning = existsSync(pidFile) && (() => { try { const p = parseInt(readFileSync(pidFile,'utf-8')); process.kill(p,0); return true; } catch(e) { return false; } })();
103
+ if (!alreadyRunning) {
104
+ const r = spawnSync('node', [join(__dirname, '..', 'bin', 'plugkit.js'), 'spool'], {
105
+ detached: true, stdio: 'ignore', windowsHide: true,
106
+ env: { ...process.env, GM_SPOOL_DIR: spoolBase }
107
+ });
108
+ if (r.pid) { try { writeFileSync(pidFile, String(r.pid), 'utf-8'); } catch(e) {} }
109
+ }
110
+ } catch(e) {}
65
111
  }
66
112
  },
67
113
 
@@ -81,7 +127,7 @@ export async function GmPlugin({ directory }) {
81
127
  if (!sessionStarted) {
82
128
  sessionStarted = true;
83
129
  try { runPlugkit(['hook', 'session-start']); } catch(e) {}
84
- try { runPlugkit(['bootstrap', directory]); } catch(e) {}
130
+ try { ensureSpoolWatcher(directory); } catch(e) {}
85
131
  }
86
132
  try { const rules = readFileSync(agentMd,'utf-8'); if (rules) output.system.unshift(rules); } catch(e) {}
87
133
  try {
@@ -132,7 +178,7 @@ export async function GmPlugin({ directory }) {
132
178
  if (!sessionStarted) {
133
179
  sessionStarted = true;
134
180
  try { runPlugkit(['hook', 'session-start']); } catch(e) {}
135
- try { runPlugkit(['bootstrap', directory]); } catch(e) {}
181
+ try { ensureSpoolWatcher(directory); } catch(e) {}
136
182
  }
137
183
  const skillName = (args.skill || args.name || '').toString();
138
184
  if (FORBIDDEN_TOOLS.has(input.tool)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-oc",
3
- "version": "2.0.1061",
3
+ "version": "2.0.1063",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -8,7 +8,7 @@ allowed-tools: Skill
8
8
 
9
9
  AI-native software engineering orchestrated via skill chain: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
10
10
 
11
- **Bootstrap pattern**: bootstrapPlugkit() loads plugkit binary (`.gm-tools/plugkit`) at session start, verifies sha256 against manifest. Returns plugkit.version + plugkit.sha256. Failure blocks all downstream dispatch re-run bootstrap before retry.
11
+ **Bootstrap pattern**: `bun x gm-plugkit@latest --daemon` downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Call once at session start; idempotent on subsequent calls. All execution routes through the file-spool: write to `.gm/exec-spool/in/<lang>/<N>.<ext>` or `in/<verb>/<N>.txt`, poll `out/<N>.json` for results.
12
12
 
13
13
  **Session-ID threading (no session-start hook)**: At skill invoke time, generate or detect SESSION_ID (env var `SESSION_ID` or `uuid()`). Pass `sessionId: "<id>"` in every rs-exec RPC body (spawn, tail, watch, etc.) and every spool-written task body. All task-scoped cleanup (deleteTask, getTask, appendOutput, killSessionTasks) requires matching sessionId. Absence is forbidden — hard reject by rs-exec handler.
14
14
 
@@ -8,7 +8,7 @@ allowed-tools: Skill
8
8
 
9
9
  AI-native software engineering orchestrated via skill chain: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
10
10
 
11
- **Bootstrap pattern**: bootstrapPlugkit() loads plugkit binary (`.gm-tools/plugkit`) at session start, verifies sha256 against manifest. Returns plugkit.version + plugkit.sha256. Failure blocks all downstream dispatch re-run bootstrap before retry.
11
+ **Bootstrap pattern**: `bun x gm-plugkit@latest --daemon` downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Call once at session start; idempotent on subsequent calls. All execution routes through the file-spool: write to `.gm/exec-spool/in/<lang>/<N>.<ext>` or `in/<verb>/<N>.txt`, poll `out/<N>.json` for results.
12
12
 
13
13
  **Session-ID threading (no session-start hook)**: At skill invoke time, generate or detect SESSION_ID (env var `SESSION_ID` or `uuid()`). Pass `sessionId: "<id>"` in every rs-exec RPC body (spawn, tail, watch, etc.) and every spool-written task body. All task-scoped cleanup (deleteTask, getTask, appendOutput, killSessionTasks) requires matching sessionId. Absence is forbidden — hard reject by rs-exec handler.
14
14
 
@@ -8,7 +8,7 @@ allowed-tools: Skill
8
8
 
9
9
  AI-native software engineering orchestrated via skill chain: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
10
10
 
11
- **Bootstrap pattern**: bootstrapPlugkit() loads plugkit binary (`.gm-tools/plugkit`) at session start, verifies sha256 against manifest. Returns plugkit.version + plugkit.sha256. Failure blocks all downstream dispatch re-run bootstrap before retry.
11
+ **Bootstrap pattern**: `bun x gm-plugkit@latest --daemon` downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Call once at session start; idempotent on subsequent calls. All execution routes through the file-spool: write to `.gm/exec-spool/in/<lang>/<N>.<ext>` or `in/<verb>/<N>.txt`, poll `out/<N>.json` for results.
12
12
 
13
13
  **Session-ID threading (no session-start hook)**: At skill invoke time, generate or detect SESSION_ID (env var `SESSION_ID` or `uuid()`). Pass `sessionId: "<id>"` in every rs-exec RPC body (spawn, tail, watch, etc.) and every spool-written task body. All task-scoped cleanup (deleteTask, getTask, appendOutput, killSessionTasks) requires matching sessionId. Absence is forbidden — hard reject by rs-exec handler.
14
14
 
@@ -8,7 +8,7 @@ allowed-tools: Skill
8
8
 
9
9
  AI-native software engineering orchestrated via skill chain: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
10
10
 
11
- **Bootstrap pattern**: bootstrapPlugkit() loads plugkit binary (`.gm-tools/plugkit`) at session start, verifies sha256 against manifest. Returns plugkit.version + plugkit.sha256. Failure blocks all downstream dispatch re-run bootstrap before retry.
11
+ **Bootstrap pattern**: `bun x gm-plugkit@latest --daemon` downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Call once at session start; idempotent on subsequent calls. All execution routes through the file-spool: write to `.gm/exec-spool/in/<lang>/<N>.<ext>` or `in/<verb>/<N>.txt`, poll `out/<N>.json` for results.
12
12
 
13
13
  **Session-ID threading (no session-start hook)**: At skill invoke time, generate or detect SESSION_ID (env var `SESSION_ID` or `uuid()`). Pass `sessionId: "<id>"` in every rs-exec RPC body (spawn, tail, watch, etc.) and every spool-written task body. All task-scoped cleanup (deleteTask, getTask, appendOutput, killSessionTasks) requires matching sessionId. Absence is forbidden — hard reject by rs-exec handler.
14
14
 
@@ -8,7 +8,7 @@ allowed-tools: Skill
8
8
 
9
9
  AI-native software engineering orchestrated via skill chain: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
10
10
 
11
- **Bootstrap pattern**: bootstrapPlugkit() loads plugkit binary (`.gm-tools/plugkit`) at session start, verifies sha256 against manifest. Returns plugkit.version + plugkit.sha256. Failure blocks all downstream dispatch re-run bootstrap before retry.
11
+ **Bootstrap pattern**: `bun x gm-plugkit@latest --daemon` downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Call once at session start; idempotent on subsequent calls. All execution routes through the file-spool: write to `.gm/exec-spool/in/<lang>/<N>.<ext>` or `in/<verb>/<N>.txt`, poll `out/<N>.json` for results.
12
12
 
13
13
  **Session-ID threading (no session-start hook)**: At skill invoke time, generate or detect SESSION_ID (env var `SESSION_ID` or `uuid()`). Pass `sessionId: "<id>"` in every rs-exec RPC body (spawn, tail, watch, etc.) and every spool-written task body. All task-scoped cleanup (deleteTask, getTask, appendOutput, killSessionTasks) requires matching sessionId. Absence is forbidden — hard reject by rs-exec handler.
14
14
 
@@ -8,7 +8,7 @@ allowed-tools: Skill
8
8
 
9
9
  AI-native software engineering orchestrated via skill chain: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
10
10
 
11
- **Bootstrap pattern**: bootstrapPlugkit() loads plugkit binary (`.gm-tools/plugkit`) at session start, verifies sha256 against manifest. Returns plugkit.version + plugkit.sha256. Failure blocks all downstream dispatch re-run bootstrap before retry.
11
+ **Bootstrap pattern**: `bun x gm-plugkit@latest --daemon` downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Call once at session start; idempotent on subsequent calls. All execution routes through the file-spool: write to `.gm/exec-spool/in/<lang>/<N>.<ext>` or `in/<verb>/<N>.txt`, poll `out/<N>.json` for results.
12
12
 
13
13
  **Session-ID threading (no session-start hook)**: At skill invoke time, generate or detect SESSION_ID (env var `SESSION_ID` or `uuid()`). Pass `sessionId: "<id>"` in every rs-exec RPC body (spawn, tail, watch, etc.) and every spool-written task body. All task-scoped cleanup (deleteTask, getTask, appendOutput, killSessionTasks) requires matching sessionId. Absence is forbidden — hard reject by rs-exec handler.
14
14
 
@@ -8,7 +8,7 @@ allowed-tools: Skill
8
8
 
9
9
  AI-native software engineering orchestrated via skill chain: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
10
10
 
11
- **Bootstrap pattern**: bootstrapPlugkit() loads plugkit binary (`.gm-tools/plugkit`) at session start, verifies sha256 against manifest. Returns plugkit.version + plugkit.sha256. Failure blocks all downstream dispatch re-run bootstrap before retry.
11
+ **Bootstrap pattern**: `bun x gm-plugkit@latest --daemon` downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Call once at session start; idempotent on subsequent calls. All execution routes through the file-spool: write to `.gm/exec-spool/in/<lang>/<N>.<ext>` or `in/<verb>/<N>.txt`, poll `out/<N>.json` for results.
12
12
 
13
13
  **Session-ID threading (no session-start hook)**: At skill invoke time, generate or detect SESSION_ID (env var `SESSION_ID` or `uuid()`). Pass `sessionId: "<id>"` in every rs-exec RPC body (spawn, tail, watch, etc.) and every spool-written task body. All task-scoped cleanup (deleteTask, getTask, appendOutput, killSessionTasks) requires matching sessionId. Absence is forbidden — hard reject by rs-exec handler.
14
14
 
@@ -8,7 +8,7 @@ allowed-tools: Skill
8
8
 
9
9
  AI-native software engineering orchestrated via skill chain: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
10
10
 
11
- **Bootstrap pattern**: bootstrapPlugkit() loads plugkit binary (`.gm-tools/plugkit`) at session start, verifies sha256 against manifest. Returns plugkit.version + plugkit.sha256. Failure blocks all downstream dispatch re-run bootstrap before retry.
11
+ **Bootstrap pattern**: `bun x gm-plugkit@latest --daemon` downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Call once at session start; idempotent on subsequent calls. All execution routes through the file-spool: write to `.gm/exec-spool/in/<lang>/<N>.<ext>` or `in/<verb>/<N>.txt`, poll `out/<N>.json` for results.
12
12
 
13
13
  **Session-ID threading (no session-start hook)**: At skill invoke time, generate or detect SESSION_ID (env var `SESSION_ID` or `uuid()`). Pass `sessionId: "<id>"` in every rs-exec RPC body (spawn, tail, watch, etc.) and every spool-written task body. All task-scoped cleanup (deleteTask, getTask, appendOutput, killSessionTasks) requires matching sessionId. Absence is forbidden — hard reject by rs-exec handler.
14
14
 
@@ -8,7 +8,7 @@ allowed-tools: Skill
8
8
 
9
9
  AI-native software engineering orchestrated via skill chain: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
10
10
 
11
- **Bootstrap pattern**: bootstrapPlugkit() loads plugkit binary (`.gm-tools/plugkit`) at session start, verifies sha256 against manifest. Returns plugkit.version + plugkit.sha256. Failure blocks all downstream dispatch re-run bootstrap before retry.
11
+ **Bootstrap pattern**: `bun x gm-plugkit@latest --daemon` downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Call once at session start; idempotent on subsequent calls. All execution routes through the file-spool: write to `.gm/exec-spool/in/<lang>/<N>.<ext>` or `in/<verb>/<N>.txt`, poll `out/<N>.json` for results.
12
12
 
13
13
  **Session-ID threading (no session-start hook)**: At skill invoke time, generate or detect SESSION_ID (env var `SESSION_ID` or `uuid()`). Pass `sessionId: "<id>"` in every rs-exec RPC body (spawn, tail, watch, etc.) and every spool-written task body. All task-scoped cleanup (deleteTask, getTask, appendOutput, killSessionTasks) requires matching sessionId. Absence is forbidden — hard reject by rs-exec handler.
14
14
 
@@ -8,7 +8,7 @@ allowed-tools: Skill
8
8
 
9
9
  AI-native software engineering orchestrated via skill chain: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
10
10
 
11
- **Bootstrap pattern**: bootstrapPlugkit() loads plugkit binary (`.gm-tools/plugkit`) at session start, verifies sha256 against manifest. Returns plugkit.version + plugkit.sha256. Failure blocks all downstream dispatch re-run bootstrap before retry.
11
+ **Bootstrap pattern**: `bun x gm-plugkit@latest --daemon` downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Call once at session start; idempotent on subsequent calls. All execution routes through the file-spool: write to `.gm/exec-spool/in/<lang>/<N>.<ext>` or `in/<verb>/<N>.txt`, poll `out/<N>.json` for results.
12
12
 
13
13
  **Session-ID threading (no session-start hook)**: At skill invoke time, generate or detect SESSION_ID (env var `SESSION_ID` or `uuid()`). Pass `sessionId: "<id>"` in every rs-exec RPC body (spawn, tail, watch, etc.) and every spool-written task body. All task-scoped cleanup (deleteTask, getTask, appendOutput, killSessionTasks) requires matching sessionId. Absence is forbidden — hard reject by rs-exec handler.
14
14