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 +38 -17
- package/agents/gm.md +1 -1
- package/cli.js +15 -10
- package/gm-oc.mjs +68 -14
- package/package.json +1 -4
- package/skills/gm/SKILL.md +0 -11
- package/skills/gm-cc/SKILL.md +0 -2
- package/skills/gm-codex/SKILL.md +0 -2
- package/skills/gm-copilot-cli/SKILL.md +0 -2
- package/skills/gm-cursor/SKILL.md +0 -2
- package/skills/gm-gc/SKILL.md +0 -2
- package/skills/gm-jetbrains/SKILL.md +0 -2
- package/skills/gm-kilo/SKILL.md +0 -2
- package/skills/gm-oc/SKILL.md +0 -2
- package/skills/gm-vscode/SKILL.md +0 -2
- package/skills/gm-zed/SKILL.md +0 -2
package/README.md
CHANGED
|
@@ -4,38 +4,59 @@
|
|
|
4
4
|
|
|
5
5
|
### One-liner (recommended)
|
|
6
6
|
|
|
7
|
-
Install directly from npm
|
|
7
|
+
Install directly from npm:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
bun x gm-oc@latest
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
This
|
|
13
|
+
This copies the plugin to `~/.config/opencode/` and registers it in your config. Restart OpenCode to activate.
|
|
14
14
|
|
|
15
|
-
###
|
|
15
|
+
### Via npm install
|
|
16
16
|
|
|
17
|
-
**Windows and Unix:**
|
|
18
17
|
```bash
|
|
19
|
-
|
|
18
|
+
npm install -g gm-oc
|
|
19
|
+
gm-oc
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
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
|
-
|
|
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
|
|
27
|
+
git clone https://github.com/AnEntrypoint/gm-oc ~/.config/opencode/plugin
|
|
32
28
|
```
|
|
33
29
|
|
|
34
30
|
## Features
|
|
35
31
|
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
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
|
-
|
|
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
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
51
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
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 {
|
|
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 {
|
|
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.
|
|
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/",
|
package/skills/gm/SKILL.md
CHANGED
|
@@ -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
|
|
package/skills/gm-cc/SKILL.md
CHANGED
package/skills/gm-codex/SKILL.md
CHANGED
|
@@ -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
|
package/skills/gm-gc/SKILL.md
CHANGED
|
@@ -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
|
package/skills/gm-kilo/SKILL.md
CHANGED
package/skills/gm-oc/SKILL.md
CHANGED
package/skills/gm-zed/SKILL.md
CHANGED