gm-cc 2.0.727 → 2.0.1064
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/.claude-plugin/marketplace.json +1 -1
- package/agents/gm.md +1 -3
- package/agents/memorize.md +22 -2
- package/agents/research-worker.md +36 -0
- package/agents/textprocessing.md +47 -0
- package/bin/bootstrap.js +624 -34
- package/bin/plugkit.js +95 -53
- package/bin/plugkit.sha256 +6 -6
- package/bin/plugkit.version +1 -1
- package/bin/rtk.sha256 +6 -0
- package/bin/rtk.version +1 -0
- package/hooks/hooks.json +2 -46
- package/hooks/hooks.spec.json +48 -0
- package/package.json +2 -2
- package/plugin.json +1 -1
- package/skills/browser/SKILL.md +18 -16
- package/skills/code-search/SKILL.md +15 -15
- package/skills/create-lang-plugin/SKILL.md +22 -26
- package/skills/gm/SKILL.md +31 -66
- package/skills/gm-cc/SKILL.md +19 -0
- package/skills/gm-codex/SKILL.md +19 -0
- package/skills/gm-complete/SKILL.md +52 -69
- package/skills/gm-copilot-cli/SKILL.md +19 -0
- package/skills/gm-cursor/SKILL.md +19 -0
- package/skills/gm-emit/SKILL.md +44 -61
- package/skills/gm-execute/SKILL.md +42 -84
- package/skills/gm-gc/SKILL.md +19 -0
- package/skills/gm-jetbrains/SKILL.md +19 -0
- package/skills/gm-kilo/SKILL.md +19 -0
- package/skills/gm-oc/SKILL.md +19 -0
- package/skills/gm-vscode/SKILL.md +19 -0
- package/skills/gm-zed/SKILL.md +19 -0
- package/skills/governance/SKILL.md +24 -23
- package/skills/pages/SKILL.md +42 -92
- package/skills/planning/SKILL.md +83 -80
- package/skills/research/SKILL.md +43 -0
- package/skills/ssh/SKILL.md +15 -9
- package/skills/textprocessing/SKILL.md +40 -0
- package/skills/update-docs/SKILL.md +27 -21
- package/.github/workflows/publish-npm.yml +0 -44
- package/hooks/post-tool-use-hook.js +0 -34
- package/hooks/pre-tool-use-hook.js +0 -45
- package/hooks/prompt-submit-hook.js +0 -19
- package/hooks/session-start-hook.js +0 -23
package/bin/plugkit.js
CHANGED
|
@@ -1,68 +1,110 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
-
|
|
3
|
+
// Hot path: spawnSync to ~/.claude/gm-tools/plugkit.exe with inherited stdio.
|
|
4
|
+
// Cold path (session-start / prompt-submit OR missing binary): synchronously
|
|
5
|
+
// ensure gm-tools/plugkit{.exe} matches the pinned version, then run hook.
|
|
6
|
+
// Cache-aware: when local matches the pin (sha-checked), zero network calls.
|
|
7
|
+
|
|
8
|
+
const { spawnSync } = require('child_process');
|
|
4
9
|
const path = require('path');
|
|
5
10
|
const fs = require('fs');
|
|
6
|
-
const
|
|
11
|
+
const os = require('os');
|
|
7
12
|
|
|
8
|
-
const
|
|
13
|
+
const wrapperDir = __dirname;
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
return
|
|
15
|
+
function toolsBin() {
|
|
16
|
+
const home = process.env.USERPROFILE || process.env.HOME || os.homedir();
|
|
17
|
+
const exe = process.platform === 'win32' ? 'plugkit.exe' : 'plugkit';
|
|
18
|
+
return path.join(home, '.claude', 'gm-tools', exe);
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
const args = process.argv.slice(2);
|
|
18
|
-
const isHook = args[0] === 'hook';
|
|
19
|
-
let bin;
|
|
21
|
+
function sha256OfFileSync(filePath) {
|
|
20
22
|
try {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
const crypto = require('crypto');
|
|
24
|
+
const h = crypto.createHash('sha256');
|
|
25
|
+
const fd = fs.openSync(filePath, 'r');
|
|
26
|
+
try {
|
|
27
|
+
const buf = Buffer.alloc(1 << 20);
|
|
28
|
+
let n;
|
|
29
|
+
while ((n = fs.readSync(fd, buf, 0, buf.length, null)) > 0) h.update(buf.subarray(0, n));
|
|
30
|
+
} finally { fs.closeSync(fd); }
|
|
31
|
+
return h.digest('hex');
|
|
32
|
+
} catch (_) { return null; }
|
|
33
|
+
}
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
child.on('close', code => process.exit(code ?? 1));
|
|
37
|
-
child.on('error', () => process.exit(1));
|
|
38
|
-
});
|
|
39
|
-
process.stdin.on('error', () => process.exit(1));
|
|
40
|
-
} else {
|
|
41
|
-
const result = spawnSync(bin, args, { stdio: 'inherit', windowsHide: true });
|
|
42
|
-
process.exit(result.status ?? 1);
|
|
43
|
-
}
|
|
35
|
+
function platformAsset() {
|
|
36
|
+
const p = process.platform;
|
|
37
|
+
const a = process.arch;
|
|
38
|
+
if (p === 'win32') return a === 'arm64' ? 'plugkit-win32-arm64.exe' : 'plugkit-win32-x64.exe';
|
|
39
|
+
if (p === 'darwin') return a === 'arm64' ? 'plugkit-darwin-arm64' : 'plugkit-darwin-x64';
|
|
40
|
+
return (a === 'arm64' || a === 'aarch64') ? 'plugkit-linux-arm64' : 'plugkit-linux-x64';
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
for (const c of candidates) if (fs.existsSync(c)) return c;
|
|
43
|
+
function readPinnedVersion() {
|
|
44
|
+
try { return fs.readFileSync(path.join(wrapperDir, 'plugkit.version'), 'utf8').trim(); } catch (_) { return null; }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function readExpectedSha() {
|
|
48
|
+
try {
|
|
49
|
+
const manifest = fs.readFileSync(path.join(wrapperDir, 'plugkit.sha256'), 'utf8');
|
|
50
|
+
const asset = platformAsset();
|
|
51
|
+
for (const line of manifest.split(/\r?\n/)) {
|
|
52
|
+
const parts = line.trim().split(/\s+/);
|
|
53
|
+
if (parts.length >= 2 && parts[parts.length - 1].replace(/^\*/, '') === asset) {
|
|
54
|
+
return parts[0].toLowerCase();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch (_) {}
|
|
62
58
|
return null;
|
|
63
59
|
}
|
|
64
60
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
// Returns true if gm-tools binary matches pinned version by sha. Fast: no network.
|
|
62
|
+
function isReady() {
|
|
63
|
+
const bin = toolsBin();
|
|
64
|
+
if (!fs.existsSync(bin)) return false;
|
|
65
|
+
const expected = readExpectedSha();
|
|
66
|
+
if (!expected) return true; // no manifest to compare against — trust existence
|
|
67
|
+
const actual = sha256OfFileSync(bin);
|
|
68
|
+
return actual && actual.toLowerCase() === expected;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Synchronously run bootstrap.js in a child node. Blocks until install finishes
|
|
72
|
+
// (or fails). Bootstrap itself is cache-aware: re-download only when sha differs
|
|
73
|
+
// from manifest. Wraps stdio:inherit so the user sees progress.
|
|
74
|
+
function ensureReady(silent) {
|
|
75
|
+
if (isReady()) return true;
|
|
76
|
+
const bootstrap = path.join(wrapperDir, 'bootstrap.js');
|
|
77
|
+
const r = spawnSync(process.execPath, [bootstrap], {
|
|
78
|
+
stdio: silent ? ['ignore', 'pipe', 'pipe'] : ['ignore', 'inherit', 'inherit'],
|
|
79
|
+
windowsHide: true,
|
|
80
|
+
});
|
|
81
|
+
return r.status === 0 && isReady();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function main() {
|
|
85
|
+
const args = process.argv.slice(2);
|
|
86
|
+
const isHook = args[0] === 'hook';
|
|
87
|
+
const hookSubcmd = isHook ? (args[1] || '') : '';
|
|
88
|
+
|
|
89
|
+
// Synchronous readiness check on these hooks. Hot path: isReady() is sha-match
|
|
90
|
+
// against pinned manifest, returns true in <50ms with no network.
|
|
91
|
+
const blocksUntilReady = hookSubcmd === 'session-start' || hookSubcmd === 'prompt-submit';
|
|
92
|
+
|
|
93
|
+
if (blocksUntilReady) {
|
|
94
|
+
if (!ensureReady(false)) {
|
|
95
|
+
process.stderr.write('[plugkit] bootstrap failed; aborting hook\n');
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
} else if (!fs.existsSync(toolsBin())) {
|
|
99
|
+
// For non-blocking hooks (pre-tool-use, post-tool-use, stop, etc.): if the
|
|
100
|
+
// binary doesn't exist yet, exit cleanly — session-start will populate it.
|
|
101
|
+
if (isHook) process.exit(0);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const bin = toolsBin();
|
|
106
|
+
const r = spawnSync(bin, args, { stdio: 'inherit', windowsHide: true });
|
|
107
|
+
process.exit(r.status ?? 1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main();
|
package/bin/plugkit.sha256
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
e5569efe81e4ef06c8349678253ca2845571495e619babb0a79be9268ea83c2a plugkit-win32-x64.exe
|
|
2
|
+
ce2f09f8ea0dd522345a9d9c3e5b04ba44bc37b2046d311d7ff1737f1b3fbf1a plugkit-win32-arm64.exe
|
|
3
|
+
a1a1d376986551828e5a39e4ae931accf66f00663aceac1439b2778cb4fffd27 plugkit-darwin-x64
|
|
4
|
+
7c36d730edab5cddf678211146ca670c9ce1def17d8b454234ce4bc04a4d7e85 plugkit-darwin-arm64
|
|
5
|
+
c9db60a399caf53c490dc08705713c7d83a1f62db057585a3950d64ab8fa449a plugkit-linux-x64
|
|
6
|
+
b9ebabaace995b1768d1d96ae13ca18a6dc5e2ca65b774fcdd457f069a7d115c plugkit-linux-arm64
|
package/bin/plugkit.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.366
|
package/bin/rtk.sha256
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
53224d66572e937507a8d2877c768e4e6cc3da66aa6f8d0f132afaab2edc2a10 rtk-win32-x64.exe
|
|
2
|
+
dedabc1d89641c60c91f09570353b6270dba4f5d53f8597018a708e515265d53 rtk-win32-arm64.exe
|
|
3
|
+
cf3190554b82c7395948b7a478c78bbe2241549b00777e660deff4cbb9e0c4b6 rtk-darwin-x64
|
|
4
|
+
c815bad459b4eaccc8be4a5d74dba397fdfe7d3716e0b6023b188d2351128b82 rtk-darwin-arm64
|
|
5
|
+
7d60dd5abc15f6d46ffd89b5de7253a067e2a3ef6f1cd8ae5a236eda05a504f4 rtk-linux-x64
|
|
6
|
+
cd5dd78035845eef4b362927c61f61e23925af3c12779131024d8334bad87a6b rtk-linux-arm64
|
package/bin/rtk.version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.40.0
|
package/hooks/hooks.json
CHANGED
|
@@ -9,11 +9,6 @@
|
|
|
9
9
|
"type": "command",
|
|
10
10
|
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook pre-tool-use",
|
|
11
11
|
"timeout": 3600
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"type": "command",
|
|
15
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/pre-tool-use-hook.js",
|
|
16
|
-
"timeout": 2000
|
|
17
12
|
}
|
|
18
13
|
]
|
|
19
14
|
}
|
|
@@ -24,8 +19,8 @@
|
|
|
24
19
|
"hooks": [
|
|
25
20
|
{
|
|
26
21
|
"type": "command",
|
|
27
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/
|
|
28
|
-
"timeout":
|
|
22
|
+
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook post-tool-use",
|
|
23
|
+
"timeout": 35000
|
|
29
24
|
}
|
|
30
25
|
]
|
|
31
26
|
}
|
|
@@ -38,11 +33,6 @@
|
|
|
38
33
|
"type": "command",
|
|
39
34
|
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook session-start",
|
|
40
35
|
"timeout": 180000
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"type": "command",
|
|
44
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-start-hook.js",
|
|
45
|
-
"timeout": 3000
|
|
46
36
|
}
|
|
47
37
|
]
|
|
48
38
|
}
|
|
@@ -55,40 +45,6 @@
|
|
|
55
45
|
"type": "command",
|
|
56
46
|
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook prompt-submit",
|
|
57
47
|
"timeout": 60000
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"type": "command",
|
|
61
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/prompt-submit-hook.js",
|
|
62
|
-
"timeout": 3000
|
|
63
|
-
}
|
|
64
|
-
]
|
|
65
|
-
}
|
|
66
|
-
],
|
|
67
|
-
"PreCompact": [
|
|
68
|
-
{
|
|
69
|
-
"matcher": "*",
|
|
70
|
-
"hooks": [
|
|
71
|
-
{
|
|
72
|
-
"type": "command",
|
|
73
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook pre-compact",
|
|
74
|
-
"timeout": 30000
|
|
75
|
-
}
|
|
76
|
-
]
|
|
77
|
-
}
|
|
78
|
-
],
|
|
79
|
-
"Stop": [
|
|
80
|
-
{
|
|
81
|
-
"matcher": "*",
|
|
82
|
-
"hooks": [
|
|
83
|
-
{
|
|
84
|
-
"type": "command",
|
|
85
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook stop",
|
|
86
|
-
"timeout": 15000
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
"type": "command",
|
|
90
|
-
"command": "node ${CLAUDE_PLUGIN_ROOT}/bin/plugkit.js hook stop-git",
|
|
91
|
-
"timeout": 210000
|
|
92
48
|
}
|
|
93
49
|
]
|
|
94
50
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"description": "Hook spec for gm Claude Code extension",
|
|
4
|
+
"envVar": "CLAUDE_PLUGIN_ROOT",
|
|
5
|
+
"plugkitInvoker": "node",
|
|
6
|
+
"events": [
|
|
7
|
+
{
|
|
8
|
+
"eventKey": "PreToolUse",
|
|
9
|
+
"commands": [
|
|
10
|
+
{
|
|
11
|
+
"kind": "plugkit",
|
|
12
|
+
"subcommand": "pre-tool-use",
|
|
13
|
+
"timeout": 3600
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"eventKey": "PostToolUse",
|
|
19
|
+
"commands": [
|
|
20
|
+
{
|
|
21
|
+
"kind": "plugkit",
|
|
22
|
+
"subcommand": "post-tool-use",
|
|
23
|
+
"timeout": 35000
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"eventKey": "SessionStart",
|
|
29
|
+
"commands": [
|
|
30
|
+
{
|
|
31
|
+
"kind": "plugkit",
|
|
32
|
+
"subcommand": "session-start",
|
|
33
|
+
"timeout": 180000
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"eventKey": "UserPromptSubmit",
|
|
39
|
+
"commands": [
|
|
40
|
+
{
|
|
41
|
+
"kind": "plugkit",
|
|
42
|
+
"subcommand": "prompt-submit",
|
|
43
|
+
"timeout": 60000
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-cc",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1064",
|
|
4
4
|
"description": "State machine agent with hooks, skills, and automated git enforcement",
|
|
5
5
|
"author": "AnEntrypoint",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,4 +58,4 @@
|
|
|
58
58
|
"optional": true
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
-
}
|
|
61
|
+
}
|
package/plugin.json
CHANGED
package/skills/browser/SKILL.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: browser
|
|
3
3
|
description: Browser automation via playwriter. Use when user needs to interact with websites, navigate pages, fill forms, click buttons, take screenshots, extract data, test web apps, or automate any browser task.
|
|
4
|
-
allowed-tools:
|
|
4
|
+
allowed-tools: Skill
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# Browser
|
|
7
|
+
# Browser automation
|
|
8
8
|
|
|
9
|
-
Two pathways — never mix
|
|
9
|
+
Two pathways — never mix in the same Bash call.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
`exec:browser` runs JS against `page`. Globals available: `page`, `snapshot`, `screenshotWithAccessibilityLabels`, `state`. 15s live window, then backgrounds; output drains automatically on every subsequent plugkit call.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
`browser:` prefix is playwriter session management. One command per block.
|
|
14
14
|
|
|
15
|
-
## Core
|
|
15
|
+
## Core
|
|
16
16
|
|
|
17
17
|
```
|
|
18
18
|
exec:browser
|
|
@@ -30,11 +30,11 @@ browser:
|
|
|
30
30
|
playwriter -s 1 -e 'await page.goto("http://example.com")'
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Session state persists across `browser:` calls. `-e` arg
|
|
33
|
+
Session state persists across `browser:` calls. `-e` arg uses single quotes outside, double inside JS strings.
|
|
34
34
|
|
|
35
35
|
## Timing
|
|
36
36
|
|
|
37
|
-
Never `await setTimeout(N)` with N > 10000.
|
|
37
|
+
Never `await setTimeout(N)` with N > 10000. Poll instead.
|
|
38
38
|
|
|
39
39
|
```
|
|
40
40
|
exec:browser
|
|
@@ -45,11 +45,12 @@ while (!state.done && Date.now() - start < 12000) {
|
|
|
45
45
|
console.log(state.result)
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
`Assertion failed: UV_HANDLE_CLOSING` is normal background-on-exit noise; ignore it.
|
|
49
49
|
|
|
50
|
-
##
|
|
50
|
+
## Patterns
|
|
51
51
|
|
|
52
52
|
Data extraction:
|
|
53
|
+
|
|
53
54
|
```
|
|
54
55
|
exec:browser
|
|
55
56
|
const items = await page.$$eval('.title', els => els.map(e => e.textContent))
|
|
@@ -57,6 +58,7 @@ console.log(JSON.stringify(items))
|
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
Console monitoring — set listeners first, then poll:
|
|
61
|
+
|
|
60
62
|
```
|
|
61
63
|
exec:browser
|
|
62
64
|
state.logs = []
|
|
@@ -68,10 +70,10 @@ exec:browser
|
|
|
68
70
|
console.log(JSON.stringify(state.logs.slice(-20)))
|
|
69
71
|
```
|
|
70
72
|
|
|
71
|
-
##
|
|
73
|
+
## Constraints
|
|
72
74
|
|
|
73
|
-
- One
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
75
|
+
- One playwriter command per `browser:` block
|
|
76
|
+
- `exec:browser` is plain JS, no shell quoting
|
|
77
|
+
- Browser tasks drain automatically on every plugkit interaction
|
|
78
|
+
- Sessions reap after 5–15 min idle; cleaned up on session end
|
|
79
|
+
- Never write standalone `.mjs`/`.js` Playwright scripts as a fallback — `exec:browser` errors must be debugged through `exec:browser` retries, not by creating test files on disk
|
|
@@ -3,13 +3,15 @@ name: code-search
|
|
|
3
3
|
description: Mandatory codebase search workflow. Use whenever you need to find anything in the codebase. Start with two words, iterate by changing or adding words until found.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Codebase search
|
|
7
7
|
|
|
8
|
-
`exec:codesearch` is the only codebase search tool.
|
|
8
|
+
`exec:codesearch` is the only codebase search tool. Grep, Glob, Find, Explore, raw `grep`/`rg`/`find` inside `exec:bash` are all hook-blocked. No fallback.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
A `@<discipline>` first-token after the verb scopes the search to that discipline's index; absent the sigil, results fan across default plus enabled disciplines, prefixed by source.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Handles exact symbols, exact strings, file-name fragments, regex-ish patterns, natural-language queries, and PDF pages (cite `path/doc.pdf:<page>`).
|
|
13
|
+
|
|
14
|
+
Direct-read exceptions: known absolute path → `Read`. Known directory listing → `exec:nodejs` + `fs.readdirSync`.
|
|
13
15
|
|
|
14
16
|
## Syntax
|
|
15
17
|
|
|
@@ -18,15 +20,9 @@ exec:codesearch
|
|
|
18
20
|
<two-word query>
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
##
|
|
22
|
-
|
|
23
|
-
1. Start: exactly two words
|
|
24
|
-
2. No results → change one word
|
|
25
|
-
3. Still no → add third word
|
|
26
|
-
4. Still no → swap changed word again
|
|
27
|
-
5. Minimum 4 attempts before concluding absent
|
|
23
|
+
## Iteration
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
Start at exactly two words. No results → change one word. Still none → add a third. Still none → swap the changed word again. Minimum four attempts before concluding absent. Never one word, never a full sentence, never switch tools.
|
|
30
26
|
|
|
31
27
|
## Examples
|
|
32
28
|
|
|
@@ -34,15 +30,19 @@ Never: one word | full sentence | give up under 4 attempts | switch tools.
|
|
|
34
30
|
exec:codesearch
|
|
35
31
|
session cleanup idle
|
|
36
32
|
```
|
|
37
|
-
|
|
33
|
+
|
|
34
|
+
No results, then:
|
|
35
|
+
|
|
38
36
|
```
|
|
39
37
|
exec:codesearch
|
|
40
38
|
cleanup sessions timeout
|
|
41
39
|
```
|
|
42
40
|
|
|
43
|
-
PDF
|
|
41
|
+
PDF:
|
|
42
|
+
|
|
44
43
|
```
|
|
45
44
|
exec:codesearch
|
|
46
45
|
usb descriptor endpoint
|
|
47
46
|
```
|
|
48
|
-
|
|
47
|
+
|
|
48
|
+
Returns `docs/usb-spec.pdf:42` — cite the page; `Read` if surrounding text is needed.
|
|
@@ -3,48 +3,40 @@ name: create-lang-plugin
|
|
|
3
3
|
description: Create a lang/ plugin that wires any CLI tool or language runtime into gm-cc — adds exec:<id> dispatch, optional LSP diagnostics, and optional prompt context injection. Zero hook configuration required.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Create lang plugin
|
|
7
7
|
|
|
8
8
|
Single CommonJS file at `<projectDir>/lang/<id>.js`. Auto-discovered — no hook editing.
|
|
9
9
|
|
|
10
|
-
## Plugin
|
|
10
|
+
## Plugin shape
|
|
11
11
|
|
|
12
12
|
```js
|
|
13
13
|
'use strict';
|
|
14
14
|
module.exports = {
|
|
15
|
-
id: 'mytool',
|
|
15
|
+
id: 'mytool',
|
|
16
16
|
exec: {
|
|
17
17
|
match: /^exec:mytool/,
|
|
18
18
|
run(code, cwd) { /* returns string or Promise<string> */ }
|
|
19
19
|
},
|
|
20
|
-
lsp: {
|
|
20
|
+
lsp: {
|
|
21
21
|
check(fileContent, cwd) { /* returns Diagnostic[] */ }
|
|
22
22
|
},
|
|
23
|
-
extensions: ['.ext'],
|
|
24
|
-
context: `=== mytool ===\n...`
|
|
23
|
+
extensions: ['.ext'],
|
|
24
|
+
context: `=== mytool ===\n...`
|
|
25
25
|
};
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
`type Diagnostic = { line: number; col: number; severity: 'error'|'warning'; message: string }`
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
`exec.run` runs in a child process, 30s timeout, async OK. Called when Claude writes `exec:mytool\n<code>`. `lsp.check` is synchronous-only, called per prompt-submit. `context` is injected into every prompt, truncated to 2000 chars.
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
- `lsp.check` — synchronous, called per prompt submit. Use `spawnSync`/`execFileSync`. No async.
|
|
34
|
-
- `context` — injected into every prompt (truncated 2000 chars).
|
|
32
|
+
## Identify the tool
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
What is the CLI name or npm package? Does it run a single expression (`tool eval`, `tool -e`, HTTP POST) or a file (`tool run <file>`)? What is its lint/check mode and output format? File extensions? Does it require a running server, or does it run headless?
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
2. Run single expression? (`tool eval <expr>`, `tool -e <code>`, HTTP POST...)
|
|
40
|
-
3. Run file? (`tool run <file>`)
|
|
41
|
-
4. Lint/check mode + output format?
|
|
42
|
-
5. File extensions?
|
|
43
|
-
6. Requires running server or headless?
|
|
36
|
+
## exec.run patterns
|
|
44
37
|
|
|
45
|
-
|
|
38
|
+
HTTP eval against a running server:
|
|
46
39
|
|
|
47
|
-
HTTP eval (running server):
|
|
48
40
|
```js
|
|
49
41
|
function httpPost(port, urlPath, body) {
|
|
50
42
|
return new Promise((resolve, reject) => {
|
|
@@ -61,7 +53,8 @@ function httpPost(port, urlPath, body) {
|
|
|
61
53
|
}
|
|
62
54
|
```
|
|
63
55
|
|
|
64
|
-
File-based
|
|
56
|
+
File-based, headless:
|
|
57
|
+
|
|
65
58
|
```js
|
|
66
59
|
function runFile(code, cwd) {
|
|
67
60
|
const tmp = path.join(os.tmpdir(), `plugin_${Date.now()}.ext`);
|
|
@@ -71,12 +64,13 @@ function runFile(code, cwd) {
|
|
|
71
64
|
}
|
|
72
65
|
```
|
|
73
66
|
|
|
74
|
-
Single
|
|
67
|
+
Single-expression detection:
|
|
68
|
+
|
|
75
69
|
```js
|
|
76
70
|
const isSingleExpr = code => !code.trim().includes('\n') && !/\b(func|def|fn |class|import)\b/.test(code);
|
|
77
71
|
```
|
|
78
72
|
|
|
79
|
-
##
|
|
73
|
+
## lsp.check
|
|
80
74
|
|
|
81
75
|
```js
|
|
82
76
|
function check(fileContent, cwd) {
|
|
@@ -94,14 +88,15 @@ function check(fileContent, cwd) {
|
|
|
94
88
|
}
|
|
95
89
|
```
|
|
96
90
|
|
|
97
|
-
##
|
|
91
|
+
## context
|
|
98
92
|
|
|
99
93
|
Under 300 chars:
|
|
94
|
+
|
|
100
95
|
```js
|
|
101
96
|
context: `=== mytool ===\nexec:mytool\n<expression>\n\nRuns via <how>. Use for <when>.`
|
|
102
97
|
```
|
|
103
98
|
|
|
104
|
-
##
|
|
99
|
+
## Verify
|
|
105
100
|
|
|
106
101
|
```
|
|
107
102
|
exec:nodejs
|
|
@@ -110,6 +105,7 @@ console.log(p.id, typeof p.exec.run, p.exec.match.toString());
|
|
|
110
105
|
```
|
|
111
106
|
|
|
112
107
|
Then test dispatch:
|
|
108
|
+
|
|
113
109
|
```
|
|
114
110
|
exec:mytool
|
|
115
111
|
<simple test expression>
|
|
@@ -117,9 +113,9 @@ exec:mytool
|
|
|
117
113
|
|
|
118
114
|
## Constraints
|
|
119
115
|
|
|
120
|
-
- `exec.run` async OK
|
|
116
|
+
- `exec.run` async OK, 30s timeout
|
|
121
117
|
- `lsp.check` synchronous only — no Promises
|
|
122
118
|
- CommonJS only — no ES module syntax
|
|
123
119
|
- No persistent processes
|
|
124
120
|
- `id` must match filename exactly
|
|
125
|
-
- First match wins —
|
|
121
|
+
- First match wins — keep `match` specific
|