gm-oc 2.0.1060 → 2.0.1062

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/README.md CHANGED
@@ -4,38 +4,59 @@
4
4
 
5
5
  ### One-liner (recommended)
6
6
 
7
- Install directly from npm using bun x:
7
+ Install directly from npm:
8
8
 
9
9
  ```bash
10
10
  bun x gm-oc@latest
11
11
  ```
12
12
 
13
- This command will automatically install gm-oc to the correct location for your platform and restart OpenCode to activate.
13
+ This copies the plugin to `~/.config/opencode/` and registers it in your config. Restart OpenCode to activate.
14
14
 
15
- ### Manual installation
15
+ ### Via npm install
16
16
 
17
- **Windows and Unix:**
18
17
  ```bash
19
- git clone https://github.com/AnEntrypoint/gm-oc ~/.config/opencode/plugin && cd ~/.config/opencode/plugin && bun install
18
+ npm install -g gm-oc
19
+ gm-oc
20
20
  ```
21
21
 
22
- **Windows PowerShell:**
23
- ```powershell
24
- git clone https://github.com/AnEntrypoint/gm-oc "\$env:APPDATA\opencode\plugin" && cd "\$env:APPDATA\opencode\plugin" && bun install
25
- ```
22
+ ### Manual installation
26
23
 
27
- ### Project-level
24
+ Clone to the global plugin directory:
28
25
 
29
- **Windows and Unix:**
30
26
  ```bash
31
- git clone https://github.com/AnEntrypoint/gm-oc .opencode/plugins && cd .opencode/plugins && bun install
27
+ git clone https://github.com/AnEntrypoint/gm-oc ~/.config/opencode/plugin
32
28
  ```
33
29
 
34
30
  ## Features
35
31
 
36
- - MCP tools for code execution and search
37
- - State machine agent policy (gm)
38
- - Git enforcement on session idle
39
- - AST analysis via thorns at session start
32
+ - **State machine agent** PLAN→EXECUTE→EMIT→VERIFY→COMPLETE orchestration
33
+ - **Skill chain** gm, planning, gm-execute, gm-emit, gm-complete, update-docs
34
+ - **exec: dispatch** code execution via spool watcher (nodejs, python, bash, typescript, go, rust)
35
+ - **Code search** semantic codebase exploration via exec:codesearch
36
+ - **Git enforcement** — blocks session end with uncommitted changes or unpushed commits
37
+ - **Memory** — rs-learn integration for cross-session knowledge retention
38
+
39
+ ## How it works
40
+
41
+ The plugin installs:
42
+ - **Agent** (`agents/gm.md`) — primary orchestrator with skill-chain instructions
43
+ - **Skills** (`skills/`) — PLAN, EXECUTE, EMIT, VERIFY, UPDATE-DOCS skill definitions
44
+ - **Plugin** (`plugins/gm-oc.mjs`) — hooks for session lifecycle, tool gating, exec: dispatch
45
+ - **Lang runners** (`lang/`) — language-specific execution plugins
46
+
47
+ All exec: commands route through the spool watcher at `.gm/exec-spool/` for session-isolated task execution.
48
+
49
+ ## Troubleshooting
50
+
51
+ **Plugin not loading:**
52
+ - Verify `~/.config/opencode/plugins/gm-oc.mjs` exists
53
+ - Check `opencode.json` has the plugin path in the `plugin` array
54
+ - Restart OpenCode completely
55
+
56
+ **Skills not appearing:**
57
+ - Verify `~/.config/opencode/skills/` contains skill directories with SKILL.md files
58
+ - Skill names must be lowercase with hyphens (e.g., `gm`, `gm-execute`)
40
59
 
41
- The plugin activates automatically on session start.
60
+ **exec: commands failing:**
61
+ - Check `.gm/exec-spool/` directory exists in your project
62
+ - Verify plugkit binary is present at `~/.config/opencode/bin/`
package/agents/gm.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: gm
3
2
  description: Agent (not skill) - immutable programming state machine. Always invoke for all work coordination.
3
+ mode: primary
4
4
  ---
5
5
 
6
6
  # GM — Skill-First Orchestrator
package/cli.js CHANGED
@@ -36,19 +36,24 @@ try {
36
36
  copyRecursive(path.join(srcDir, 'scripts'), path.join(ocConfigDir, 'scripts'));
37
37
 
38
38
  const ocJsonPath = path.join(ocConfigDir, 'opencode.json');
39
+ const configExisted = fs.existsSync(ocJsonPath);
39
40
  let ocConfig = {};
40
- try {
41
- const raw = fs.readFileSync(ocJsonPath, 'utf-8');
42
- ocConfig = JSON.parse(raw);
43
- if (ocConfig['']) { delete ocConfig['']; }
44
- } catch (e) {}
45
- delete ocConfig.mcp;
46
- ocConfig['$schema'] = 'https://opencode.ai/config.json';
47
- ocConfig.default_agent = 'gm';
41
+ if (configExisted) {
42
+ try {
43
+ const raw = fs.readFileSync(ocJsonPath, 'utf-8');
44
+ ocConfig = JSON.parse(raw);
45
+ if (ocConfig['']) { delete ocConfig['']; }
46
+ } catch (e) {}
47
+ }
48
+ if (!configExisted) {
49
+ ocConfig['$schema'] = 'https://opencode.ai/config.json';
50
+ ocConfig.default_agent = 'gm';
51
+ }
48
52
  const pluginMjsPath = path.join(ocConfigDir, 'plugins', 'gm-oc.mjs');
49
53
  if (!Array.isArray(ocConfig.plugin)) ocConfig.plugin = [];
50
- ocConfig.plugin = ocConfig.plugin.filter(p => !p.includes('gm-oc'));
51
- ocConfig.plugin.push(pluginMjsPath);
54
+ if (!ocConfig.plugin.some(p => typeof p === 'string' && p.includes('gm-oc'))) {
55
+ ocConfig.plugin.push(pluginMjsPath);
56
+ }
52
57
  fs.writeFileSync(ocJsonPath, JSON.stringify(ocConfig, null, 2) + '\n');
53
58
 
54
59
  const oldDir = process.platform === 'win32'
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'];
@@ -57,6 +91,26 @@ export async function GmPlugin({ directory }) {
57
91
  const injectedSessions = new Set();
58
92
 
59
93
  return {
94
+ 'session.created': async () => {
95
+ if (!sessionStarted) {
96
+ sessionStarted = true;
97
+ try { runPlugkit(['hook', 'session-start']); } 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) {}
111
+ }
112
+ },
113
+
60
114
  'experimental.chat.system.transform': async (input, output) => {
61
115
  const gmDir = join(directory, '.gm');
62
116
  try {
@@ -73,7 +127,7 @@ export async function GmPlugin({ directory }) {
73
127
  if (!sessionStarted) {
74
128
  sessionStarted = true;
75
129
  try { runPlugkit(['hook', 'session-start']); } catch(e) {}
76
- try { runPlugkit(['bootstrap', directory]); } catch(e) {}
130
+ try { ensureSpoolWatcher(directory); } catch(e) {}
77
131
  }
78
132
  try { const rules = readFileSync(agentMd,'utf-8'); if (rules) output.system.unshift(rules); } catch(e) {}
79
133
  try {
@@ -124,7 +178,7 @@ export async function GmPlugin({ directory }) {
124
178
  if (!sessionStarted) {
125
179
  sessionStarted = true;
126
180
  try { runPlugkit(['hook', 'session-start']); } catch(e) {}
127
- try { runPlugkit(['bootstrap', directory]); } catch(e) {}
181
+ try { ensureSpoolWatcher(directory); } catch(e) {}
128
182
  }
129
183
  const skillName = (args.skill || args.name || '').toString();
130
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.1060",
3
+ "version": "2.0.1062",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -30,9 +30,6 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {},
33
- "scripts": {
34
- "postinstall": "node scripts/postinstall-oc.js"
35
- },
36
33
  "files": [
37
34
  "agents/",
38
35
  "bin/",
@@ -2,17 +2,6 @@
2
2
  name: gm
3
3
  description: Orchestrator dispatching PLAN→EXECUTE→EMIT→VERIFY→UPDATE-DOCS skill chain; spool-driven task execution with session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-cc
7
- - gm-gc
8
- - gm-oc
9
- - gm-kilo
10
- - gm-codex
11
- - gm-copilot-cli
12
- - gm-vscode
13
- - gm-cursor
14
- - gm-zed
15
- - gm-jetbrains
16
5
  end-to-end: true
17
6
  ---
18
7
 
@@ -2,8 +2,6 @@
2
2
  name: gm-cc
3
3
  description: AI-native software engineering via skill-driven orchestration on cc; bootstraps plugkit for task execution and session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-cc
7
5
  ---
8
6
 
9
7
  # GM — cc Platform
@@ -2,8 +2,6 @@
2
2
  name: gm-codex
3
3
  description: AI-native software engineering via skill-driven orchestration on codex; bootstraps plugkit for task execution and session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-codex
7
5
  ---
8
6
 
9
7
  # GM — codex Platform
@@ -2,8 +2,6 @@
2
2
  name: gm-copilot-cli
3
3
  description: AI-native software engineering via skill-driven orchestration on copilot-cli; bootstraps plugkit for task execution and session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-copilot-cli
7
5
  ---
8
6
 
9
7
  # GM — copilot-cli Platform
@@ -2,8 +2,6 @@
2
2
  name: gm-cursor
3
3
  description: AI-native software engineering via skill-driven orchestration on cursor; bootstraps plugkit for task execution and session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-cursor
7
5
  ---
8
6
 
9
7
  # GM — cursor Platform
@@ -2,8 +2,6 @@
2
2
  name: gm-gc
3
3
  description: AI-native software engineering via skill-driven orchestration on gc; bootstraps plugkit for task execution and session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-gc
7
5
  ---
8
6
 
9
7
  # GM — gc Platform
@@ -2,8 +2,6 @@
2
2
  name: gm-jetbrains
3
3
  description: AI-native software engineering via skill-driven orchestration on jetbrains; bootstraps plugkit for task execution and session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-jetbrains
7
5
  ---
8
6
 
9
7
  # GM — jetbrains Platform
@@ -2,8 +2,6 @@
2
2
  name: gm-kilo
3
3
  description: AI-native software engineering via skill-driven orchestration on kilo; bootstraps plugkit for task execution and session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-kilo
7
5
  ---
8
6
 
9
7
  # GM — kilo Platform
@@ -2,8 +2,6 @@
2
2
  name: gm-oc
3
3
  description: AI-native software engineering via skill-driven orchestration on oc; bootstraps plugkit for task execution and session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-oc
7
5
  ---
8
6
 
9
7
  # GM — oc Platform
@@ -2,8 +2,6 @@
2
2
  name: gm-vscode
3
3
  description: AI-native software engineering via skill-driven orchestration on vscode; bootstraps plugkit for task execution and session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-vscode
7
5
  ---
8
6
 
9
7
  # GM — vscode Platform
@@ -2,8 +2,6 @@
2
2
  name: gm-zed
3
3
  description: AI-native software engineering via skill-driven orchestration on zed; bootstraps plugkit for task execution and session isolation
4
4
  allowed-tools: Skill
5
- compatible-platforms:
6
- - gm-zed
7
5
  ---
8
6
 
9
7
  # GM — zed Platform