gm-oc 2.0.630 → 2.0.632

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-oc",
3
- "version": "2.0.630",
3
+ "version": "2.0.632",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -4,199 +4,74 @@ description: Browser automation via playwriter. Use when user needs to interact
4
4
  allowed-tools: Bash(browser:*), Bash(exec:browser*)
5
5
  ---
6
6
 
7
- # Browser Automation with playwriter
7
+ # Browser Automation
8
8
 
9
- **Use gm subagents for all independent work items. Invoke all skills in the chain: planning → gm-execute → gm-emit → gm-complete → update-docs.**
9
+ Two pathways never mix:
10
10
 
11
+ **`exec:browser`** — JS against `page`. `page`, `snapshot`, `screenshotWithAccessibilityLabels`, `state` globals available. 15s live window then backgrounds; drains auto on every subsequent plugkit call.
11
12
 
12
- ## Two Pathways
13
+ **`browser:` prefix** — playwriter session management. One command per block.
13
14
 
14
- **Session commands** (`browser:` prefix) — manage multi-step sessions via playwriter CLI. Each `browser:` block runs its commands sequentially.
15
-
16
- **JS execution** (`exec:browser`) — run JavaScript directly against `page`. State persists across calls via `state` global.
17
-
18
- **CRITICAL**: Never mix these two pathways. Each `browser:` block is a separate Bash call. Each `exec:browser` block is a separate Bash call.
19
-
20
- ## 15-Second Ceiling — How It Works
21
-
22
- Every `exec:browser` call has a 15s live window. During that window, all stdout/stderr is streamed to you in real time. After 15s the task backgrounds and you receive:
23
- - All output produced so far (live drain)
24
- - A task ID with `plugkit sleep/status/close` instructions
25
-
26
- **The task keeps running.** Every subsequent plugkit interaction automatically drains all running browser tasks — you will see new output without asking.
27
-
28
- **Never use `await new Promise(r => setTimeout(r, N))` with N > 10000.** Use short poll loops instead (see patterns below).
29
-
30
- **"Assertion failed: UV_HANDLE_CLOSING" in output** means the call exceeded 15s and was cut off — ignore the assertion noise, look at the output before it. The task was backgrounded normally.
31
-
32
- ## Idle Timeout & Session Reaper
33
-
34
- Playwriter kills idle browser sessions after 5-15 minutes of inactivity. The rs-exec tooling now automatically cleans up the spawned browser process when the Claude Code session ends, preventing zombie tabs.
35
-
36
- **Historical note**: Earlier versions left the browser running after session end, causing repeated tabs on reconnect. This is now fixed — the browser will be killed when your session idles and closes.
37
-
38
- ## Session Pathway (`browser:`)
39
-
40
- Create a session first, use `--direct` for CDP mode (requires Chrome with remote debugging):
41
-
42
- ```
43
- browser:
44
- playwriter session new --direct
45
- ```
46
-
47
- Returns a numeric session ID (e.g. `1`). Use that ID for all subsequent calls. **Each command must be a separate Bash call:**
48
-
49
- ```
50
- browser:
51
- playwriter -s 1 -e 'await page.goto("http://example.com")'
52
- ```
53
-
54
- ```
55
- browser:
56
- playwriter -s 1 -e 'await snapshot({ page })'
57
- ```
15
+ ## Core Usage
58
16
 
59
17
  ```
60
- browser:
61
- playwriter -s 1 -e 'await screenshotWithAccessibilityLabels({ page })'
18
+ exec:browser
19
+ await page.goto('https://example.com')
20
+ await snapshot({ page })
62
21
  ```
63
22
 
64
- State persists across session calls:
65
-
66
23
  ```
67
24
  browser:
68
- playwriter -s 1 -e 'state.x = 1'
25
+ playwriter session new --direct
69
26
  ```
70
27
 
71
28
  ```
72
29
  browser:
73
- playwriter -s 1 -e 'console.log(state.x)'
74
- ```
75
-
76
-
77
- **RULE**: The `-e` argument must use single quotes. The JS inside must use double quotes for strings.
78
-
79
- **RULE**: Never chain multiple `playwriter` commands in one `browser:` block — run one command per block.
80
-
81
- ## JS Execution Pathway (`exec:browser`)
82
-
83
- For direct page access, DOM queries, and data extraction. The runtime provides `page`, `snapshot`, `screenshotWithAccessibilityLabels`, and `state` as globals.
84
-
85
- ```
86
- exec:browser
87
- await page.goto('https://example.com')
88
- await snapshot({ page })
89
- ```
90
-
91
- ```
92
- exec:browser
93
- const title = await page.title()
94
- console.log(title)
30
+ playwriter -s 1 -e 'await page.goto("http://example.com")'
95
31
  ```
96
32
 
97
- Never add shell quoting write plain JavaScript directly.
98
-
99
- ## Core Workflow
100
-
101
- 1. **Navigate**: `exec:browser\nawait page.goto('url')` — session auto-created on first call
102
- 2. **Snapshot**: `exec:browser\nawait snapshot({ page })`
103
- 3. **Interact**: click, fill, type in subsequent `exec:browser` calls
104
- 4. **Extract data**: `exec:browser\nconsole.log(await page.evaluate(() => document.title))`
33
+ Session state persists across `browser:` calls. `-e` arg: single quotes outside, double quotes inside JS strings.
105
34
 
106
- ## Long-Running Operations — Poll Pattern
35
+ ## Timing
107
36
 
108
- For operations that take >10s (model loading, network fetches, animations):
37
+ Never `await setTimeout(N)` with N > 10000. Use poll loops:
109
38
 
110
- **Step 1** — set up listener and kick off the operation:
111
- ```
112
- exec:browser
113
- state.done = false
114
- state.result = null
115
- page.on('console', msg => {
116
- const t = msg.text()
117
- if (t.includes('loaded') || t.includes('ready')) { state.done = true; state.result = t }
118
- })
119
- await page.click('#start-button')
120
- console.log('started, waiting...')
121
- ```
122
-
123
- **Step 2** — poll in short bursts (this will background after 15s and keep draining):
124
39
  ```
125
40
  exec:browser
126
41
  const start = Date.now()
127
42
  while (!state.done && Date.now() - start < 12000) {
128
43
  await new Promise(r => setTimeout(r, 500))
129
44
  }
130
- console.log('done:', state.done, 'result:', state.result)
45
+ console.log(state.result)
131
46
  ```
132
47
 
133
- If step 2 backgrounds (takes >15s), every subsequent plugkit call will drain its output automatically. When you see the result in the drain log, close the task:
134
- ```
135
- exec:close
136
- task_N
137
- ```
48
+ "Assertion failed: UV_HANDLE_CLOSING" = backgrounded normally, ignore noise.
138
49
 
139
50
  ## Common Patterns
140
51
 
141
-
142
-
143
- ### Data Extraction
144
-
52
+ Data extraction:
145
53
  ```
146
54
  exec:browser
147
- const items = await page.$$eval('.product-title', els => els.map(e => e.textContent))
55
+ const items = await page.$$eval('.title', els => els.map(e => e.textContent))
148
56
  console.log(JSON.stringify(items))
149
57
  ```
150
58
 
151
-
152
- ### Console Monitoring — set up listener first, then poll
153
-
59
+ Console monitoring — set listeners first, then poll:
154
60
  ```
155
61
  exec:browser
156
62
  state.logs = []
157
- state.errors = []
158
63
  page.on('console', msg => state.logs.push({ type: msg.type(), text: msg.text() }))
159
- page.on('pageerror', e => state.errors.push(e.message))
160
- console.log('listeners attached')
161
- ```
162
-
163
- ```
164
- exec:browser
165
- console.log('logs so far:', JSON.stringify(state.logs.slice(-20)))
166
- console.log('errors:', JSON.stringify(state.errors))
167
64
  ```
168
65
 
169
66
  ```
170
67
  exec:browser
171
- if (page.workers().length > 0) {
172
- const r = await page.workers()[0].evaluate(() => JSON.stringify({ type: 'worker alive' }))
173
- console.log(r)
174
- }
175
- ```
176
-
177
- exec:browser
178
- const result = await page.evaluate(() => JSON.stringify({
179
- entityCount: window.debug?.scene?.children?.length,
180
- playerId: window.debug?.client?.playerId
181
- }))
182
- console.log(result)
183
- ```
184
-
185
- exec:browser
186
- const start = Date.now()
187
- while (Date.now() - start < 12000) {
188
- const el = await page.$('#status')
189
- if (el) { console.log('found:', await el.textContent()); break }
190
- await new Promise(r => setTimeout(r, 300))
191
- }
68
+ console.log(JSON.stringify(state.logs.slice(-20)))
192
69
  ```
193
70
 
194
- ## Key Rules
71
+ ## Rules
195
72
 
196
- - `browser:` prefix → playwriter session management (one command per block)
197
- - `exec:browser` → JS in page context (multi-line JS allowed, 15s live window)
198
- - Never mix pathways in the same Bash call
199
- - `-e` argument: single quotes on outside, double quotes inside for JS strings
200
73
  - One `playwriter` command per `browser:` block
201
- - Never `await setTimeout(N)` with N > 10000 — use short poll loops instead
202
- - All running browser tasks drain automatically on every plugkit interaction
74
+ - Never mix pathways in same Bash call
75
+ - `exec:browser` = plain JS, no shell quoting
76
+ - All browser tasks drain automatically on every plugkit interaction
77
+ - Sessions reap after 5-15min idle; browser cleaned up on session end
@@ -3,53 +3,33 @@ 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
- # CODEBASE SEARCH — Mandatory Workflow
6
+ # CODEBASE SEARCH
7
7
 
8
- **Use gm subagents for all independent work items. Invoke all skills in the chain: planning gm-execute gm-emit → gm-complete → update-docs.**
8
+ `exec:codesearch` is the only codebase search tool. `Grep`, `Glob`, `Find`, `Explore`, `grep`/`rg`/`find` inside `exec:bash` = ALL hook-blocked. No fallback path.
9
9
 
10
+ Handles: exact symbols, exact strings, file-name fragments, regex-ish patterns, natural-language queries, PDF pages (cite `path/doc.pdf:<page>`).
10
11
 
11
- `exec:codesearch` is the only way to search the codebase. **`Grep`, `Glob`, `Find`, `Explore`, and `grep`/`rg`/`find`/`ripgrep` inside `exec:bash` are ALL hook-blocked.** There is no fallback path for exact matches, regex, or file-name patterns — codesearch handles all of them. If you find yourself reaching for Grep or Glob, that reflex is wrong; replace with codesearch.
12
-
13
- **What codesearch handles** (every codebase-lookup need lands here):
14
- - Exact identifier / symbol lookup (function names, class names, constants) — symbols are extracted and indexed separately, exact matches rank top.
15
- - Exact string content — query tokens >1 trigger a literal-substring boost in content scoring.
16
- - File-name fragments — file paths are tokenized and matched with a score boost.
17
- - Regex-ish patterns — BM25 tokenization covers snake_case, camelCase, dot/dash splits; matching component words returns the file.
18
- - Natural-language concept queries — BM25 + vector re-ranking handle "find the hook that blocks grep", "where is PR stats calculated", etc.
19
- - PDF pages — specs, papers, manuals, RFCs, datasheets, design docs extracted page-by-page into the same index. Cite `path/to/doc.pdf:<page>`.
20
-
21
- **Direct-read exceptions** (no search required):
22
- - Known absolute path → `Read` tool.
23
- - Listing a known directory → `exec:nodejs` + `fs.readdirSync`.
24
-
25
- Unscanned digital PDFs are a search gap — if you know a doc exists and it isn't returning, check it is not under an ignored dir and that extraction succeeded (encrypted / image-only PDFs yield empty chunks silently).
12
+ Direct-read exceptions: known absolute path `Read`. Known dir listing `exec:nodejs` + `fs.readdirSync`.
26
13
 
27
14
  ## Syntax
28
15
 
29
16
  ```
30
17
  exec:codesearch
31
- <natural language query>
18
+ <two-word query>
32
19
  ```
33
20
 
34
- ## Mandatory Search Protocol
35
-
36
- **Start with exactly two words.** Never start broader. Never start with one word.
21
+ ## Protocol
37
22
 
38
- **Iterate by changing or adding words** — do not switch approach or give up until the content is found:
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
39
28
 
40
- 1. Start: two-word query most likely to match
41
- 2. No results → change one word (synonym, related term)
42
- 3. Still no results → add a third word (narrow scope)
43
- 4. Still no results → swap the changed word again
44
- 5. Keep iterating — changing or adding words each pass — until content is found
45
-
46
- **Never**: start with one word | start with a sentence | give up after one miss | switch to a different tool | declare content missing after fewer than 4 search attempts
47
-
48
- **Each search is one `exec:codesearch` call.** Run them sequentially — use each result to inform the next query.
29
+ Never: one word | full sentence | give up under 4 attempts | switch tools.
49
30
 
50
31
  ## Examples
51
32
 
52
- Finding where a function is defined:
53
33
  ```
54
34
  exec:codesearch
55
35
  session cleanup idle
@@ -59,23 +39,10 @@ session cleanup idle
59
39
  exec:codesearch
60
40
  cleanup sessions timeout
61
41
  ```
62
- → found.
63
-
64
- Finding config format:
65
- ```
66
- exec:codesearch
67
- plugin registration format
68
- ```
69
- → no results →
70
- ```
71
- exec:codesearch
72
- plugin config array
73
- ```
74
- → found.
75
42
 
76
- Finding content inside a spec PDF:
43
+ PDF search:
77
44
  ```
78
45
  exec:codesearch
79
46
  usb descriptor endpoint
80
47
  ```
81
- → returns `docs/usb-spec.pdf:42` — cite page, open via Read if you need the surrounding page text.
48
+ → returns `docs/usb-spec.pdf:42` — cite page, Read if you need surrounding text.
@@ -5,68 +5,54 @@ description: Create a lang/ plugin that wires any CLI tool or language runtime i
5
5
 
6
6
  # CREATE LANG PLUGIN
7
7
 
8
- **Use gm subagents for all independent work items. Invoke all skills in the chain: planning → gm-execute gm-emit gm-complete → update-docs.**
8
+ Single CommonJS file at `<projectDir>/lang/<id>.js`. Auto-discovered no hook editing.
9
9
 
10
-
11
- A lang plugin is a single CommonJS file at `<projectDir>/lang/<id>.js`. gm-cc's hooks auto-discover it — no hook editing, no settings changes. The plugin gets three integration points: **exec dispatch**, **LSP diagnostics**, and **context injection**.
12
-
13
- ## PLUGIN SHAPE
10
+ ## Plugin Shape
14
11
 
15
12
  ```js
16
13
  'use strict';
17
14
  module.exports = {
18
- id: 'mytool', // must match filename: lang/mytool.js
15
+ id: 'mytool', // must match filename
19
16
  exec: {
20
- match: /^exec:mytool/, // regex tested against full "exec:mytool\n<code>" string
21
- run(code, cwd) { // returns string or Promise<string>
22
- // ...
23
- }
17
+ match: /^exec:mytool/,
18
+ run(code, cwd) { /* returns string or Promise<string> */ }
24
19
  },
25
- lsp: { // optional — synchronous only
26
- check(fileContent, cwd) { // returns Diagnostic[] synchronously
27
- // ...
28
- }
20
+ lsp: { // optional — synchronous only
21
+ check(fileContent, cwd) { /* returns Diagnostic[] */ }
29
22
  },
30
- extensions: ['.ext'], // optional — file extensions lsp.check applies to
31
- context: `=== mytool ===\n...` // optional — string or () => string
23
+ extensions: ['.ext'], // optional — for lsp.check
24
+ context: `=== mytool ===\n...` // optional — string or () => string
32
25
  };
33
26
  ```
34
27
 
35
- ```ts
36
- type Diagnostic = { line: number; col: number; severity: 'error'|'warning'; message: string };
37
- ```
38
-
39
- ## HOW IT WORKS
40
-
41
- - **`exec.run`** is called in a child process (30s timeout) when Claude writes `exec:mytool\n<code>`. Output is returned as `exec:mytool output:\n\n<result>`. Async is fine here.
42
- - **`lsp.check`** is called synchronously in the hook process on each prompt submit — must NOT be async. Use `execFileSync` or `spawnSync`.
43
- - **`context`** is injected into every prompt's `additionalContext` (truncated to 2000 chars) and into the session-start context.
44
- - **`match`** regex is tested against the full command string `exec:mytool\n<code>` — keep it simple: `/^exec:mytool/`.
28
+ `type Diagnostic = { line: number; col: number; severity: 'error'|'warning'; message: string }`
45
29
 
46
- ## STEP 1 — IDENTIFY THE TOOL
30
+ ## How It Works
47
31
 
48
- Answer these before writing any code:
32
+ - `exec.run` child process, 30s timeout, async OK. Called when Claude writes `exec:mytool\n<code>`.
33
+ - `lsp.check` — synchronous, called per prompt submit. Use `spawnSync`/`execFileSync`. No async.
34
+ - `context` — injected into every prompt (truncated 2000 chars).
49
35
 
50
- 1. What is the tool's CLI name or npm package? (`gdlint`, `tsc`, `deno`, `ruff`, ...)
51
- 2. How do you run a single expression/snippet? (`tool eval <expr>`, `tool -e <code>`, HTTP POST, ...)
52
- 3. How do you run a file? (`tool run <file>`, `tool <file>`, ...)
53
- 4. Does it have a lint/check mode? What does its output format look like?
54
- 5. What file extensions does it apply to?
55
- 6. Is the game/server running required, or does it work headlessly?
36
+ ## Step 1 Identify Tool
56
37
 
57
- ## STEP 2 IMPLEMENT exec.run
38
+ 1. CLI name or npm package?
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?
58
44
 
59
- Pattern for **HTTP eval** (tool has a running server):
45
+ ## Step 2 exec.run Patterns
60
46
 
47
+ HTTP eval (running server):
61
48
  ```js
62
- const http = require('http');
63
49
  function httpPost(port, urlPath, body) {
64
50
  return new Promise((resolve, reject) => {
65
51
  const data = JSON.stringify(body);
66
52
  const req = http.request(
67
53
  { hostname: '127.0.0.1', port, path: urlPath, method: 'POST',
68
54
  headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } },
69
- (res) => { let raw = ''; res.on('data', c => raw += c); res.on('end', () => { try { resolve(JSON.parse(raw)); } catch { resolve({ raw }); } }); }
55
+ res => { let raw = ''; res.on('data', c => raw += c); res.on('end', () => resolve(JSON.parse(raw))); }
70
56
  );
71
57
  req.setTimeout(8000, () => { req.destroy(); reject(new Error('timeout')); });
72
58
  req.on('error', reject);
@@ -75,109 +61,65 @@ function httpPost(port, urlPath, body) {
75
61
  }
76
62
  ```
77
63
 
78
- Pattern for **file-based execution** (write temp file, run headlessly):
79
-
64
+ File-based (headless):
80
65
  ```js
81
- const fs = require('fs');
82
- const os = require('os');
83
- const path = require('path');
84
- const { execFileSync } = require('child_process');
85
-
86
66
  function runFile(code, cwd) {
87
67
  const tmp = path.join(os.tmpdir(), `plugin_${Date.now()}.ext`);
88
68
  fs.writeFileSync(tmp, code);
89
- try {
90
- return execFileSync('mytool', ['run', tmp], { cwd, encoding: 'utf8', timeout: 10000 });
91
- } finally {
92
- try { fs.unlinkSync(tmp); } catch (_) {}
93
- }
69
+ try { return execFileSync('mytool', ['run', tmp], { cwd, encoding: 'utf8', timeout: 10000 }); }
70
+ finally { try { fs.unlinkSync(tmp); } catch (_) {} }
94
71
  }
95
72
  ```
96
73
 
97
- **Distinguish single expression vs multi-line** when both modes exist:
98
-
74
+ Single expr detection:
99
75
  ```js
100
- function isSingleExpr(code) {
101
- return !code.trim().includes('\n') && !/\b(func|def|fn |class|import)\b/.test(code);
102
- }
76
+ const isSingleExpr = code => !code.trim().includes('\n') && !/\b(func|def|fn |class|import)\b/.test(code);
103
77
  ```
104
78
 
105
- ## STEP 3 — IMPLEMENT lsp.check (if applicable)
106
-
107
- Must be **synchronous**. Parse the tool's stderr/stdout for diagnostics:
79
+ ## Step 3 — lsp.check
108
80
 
109
81
  ```js
110
- const { spawnSync } = require('child_process');
111
- const fs = require('fs');
112
- const os = require('os');
113
- const path = require('path');
114
-
115
82
  function check(fileContent, cwd) {
116
83
  const tmp = path.join(os.tmpdir(), `lsp_${Math.random().toString(36).slice(2)}.ext`);
117
84
  try {
118
85
  fs.writeFileSync(tmp, fileContent);
119
86
  const r = spawnSync('mytool', ['check', tmp], { encoding: 'utf8', cwd });
120
- const output = r.stdout + r.stderr;
121
- return output.split('\n').reduce((acc, line) => {
87
+ return (r.stdout + r.stderr).split('\n').reduce((acc, line) => {
122
88
  const m = line.match(/^.+:(\d+):(\d+):\s+(error|warning):\s+(.+)$/);
123
- if (m) acc.push({ line: parseInt(m[1]), col: parseInt(m[2]), severity: m[3], message: m[4].trim() });
89
+ if (m) acc.push({ line: +m[1], col: +m[2], severity: m[3], message: m[4].trim() });
124
90
  return acc;
125
91
  }, []);
126
- } catch (_) {
127
- return [];
128
- } finally {
129
- try { fs.unlinkSync(tmp); } catch (_) {}
130
- }
92
+ } catch (_) { return []; }
93
+ finally { try { fs.unlinkSync(tmp); } catch (_) {} }
131
94
  }
132
95
  ```
133
96
 
134
- Common output patterns to parse:
135
- - `file:line:col: error: message` → standard
136
- - `file:line: E001: message` → gdlint style (`E`=error, `W`=warning)
137
- - JSON output → `JSON.parse(r.stdout).errors.map(...)`
138
-
139
- ## STEP 4 — WRITE context STRING
140
-
141
- Describe what `exec:<id>` does and when to use it. This appears in every prompt. Keep it under 300 chars:
97
+ ## Step 4 context String
142
98
 
99
+ Under 300 chars:
143
100
  ```js
144
- context: `=== mytool exec: support ===
145
- exec:mytool
146
- <expression or code block>
147
-
148
- Runs via <how>. Use for <when>.`
101
+ context: `=== mytool ===\nexec:mytool\n<expression>\n\nRuns via <how>. Use for <when>.`
149
102
  ```
150
103
 
151
- ## STEP 5 — WRITE THE FILE
152
-
153
- File goes at `lang/<id>.js` in the project root. The `id` field must match the filename (without `.js`).
154
-
155
- Verify after writing:
104
+ ## Step 5 — Write + Verify
156
105
 
157
106
  ```
158
107
  exec:nodejs
159
- const p = require('/abs/path/to/lang/mytool.js');
108
+ const p = require('/abs/path/lang/mytool.js');
160
109
  console.log(p.id, typeof p.exec.run, p.exec.match.toString());
161
110
  ```
162
111
 
163
112
  Then test dispatch:
164
-
165
113
  ```
166
114
  exec:mytool
167
- <a simple test expression>
115
+ <simple test expression>
168
116
  ```
169
117
 
170
- If it returns `exec:mytool output:` → working. If it errors → fix `exec.run`.
171
-
172
- ## CONSTRAINTS
173
-
174
- - `exec.run` may be async — it runs in a child process with a 30s timeout
175
- - `lsp.check` must be synchronous — no Promises, no async/await
176
- - Plugin must be CommonJS (`module.exports = { ... }`) — no ES module syntax
177
- - No persistent processes — `exec.run` must complete and exit cleanly
178
- - `id` must match the filename exactly
179
- - First match wins — if multiple plugins could match, make `match` specific
180
-
181
- ## EXAMPLE — gdscript plugin (reference implementation)
118
+ ## Constraints
182
119
 
183
- See `C:/dev/godot-kit/lang/gdscript.js` for a complete working example combining HTTP eval (single expressions via port 6009) with headless file execution fallback, synchronous gdlint LSP, and a context string.
120
+ - `exec.run` async OK (30s timeout)
121
+ - `lsp.check` synchronous only — no Promises
122
+ - CommonJS only — no ES module syntax
123
+ - No persistent processes
124
+ - `id` must match filename exactly
125
+ - First match wins — make `match` specific
@@ -5,68 +5,40 @@ description: Agent (not skill) - immutable programming state machine. Always inv
5
5
 
6
6
  # GM — Skill-First Orchestrator
7
7
 
8
- **Invoke the `planning` skill immediately.** Use the Skill tool with `skill: "planning"`.
8
+ Invoke `planning` skill immediately. Skill tool only — never Agent tool for skills.
9
9
 
10
- **CRITICAL: Skills are invoked via the Skill tool ONLY. Do NOT use the Agent tool to load skills.**
10
+ ## STATE MACHINE
11
11
 
12
- ## WHERE YOU ARE
12
+ Top of chain. No mutables resolved. Phases: PLAN → EXECUTE → EMIT → VERIFY → UPDATE-DOCS.
13
+ Each phase loads protocols via Skill invocation only. Reading summary ≠ being in phase.
13
14
 
14
- Top of state machine. No mutables resolved, no files read, no phase protocols loaded. Each phase (PLAN, EXECUTE, EMIT, VERIFY, UPDATE-DOCS) carries its own protocols (mutable discipline, pre-emit diagnostic, post-emit verify, hygiene sweep, CI watch). Protocols enter context only when you invoke the corresponding Skill. Reading a summary ≠ being in them.
15
+ `gm-execute` = execution contract (all phases). `governance` = route/legitimacy reference (load once).
15
16
 
16
- Transitions = state changes, not reminders. Phase exit condition met → next Skill invocation moves you. Without invocation: still in prior state, regardless of prose.
17
+ ## MEMORIZE HARD RULE
17
18
 
18
- `gm-execute` = execution contract. Defines "running code" across every phase: `exec:<lang>` = only runner; `exec:codesearch` = only exploration; witnessed output = only ground truth; import real modules over reimplementation. Execution happens in every phase, not only EXECUTE. About to run anything, `gm-execute` protocols not fresh in context → operating outside contract → reload `gm-execute` first.
19
+ Unknown→known = memorize same turn it resolves. Background, non-blocking.
19
20
 
20
- `governance` = governance reference. Route discovery (7 route families, 16 failure taxonomy) feeds `planning`. Weak-prior bridge (plausibility never equals authorization) constrains `gm-execute`. Legitimacy gate (earned specificity, lawful downgrade, five refused collapses) gates `gm-emit` and `gm-complete`. Load once at session start.
21
+ Triggers: exec: output answers prior unknown | code read confirms/refutes assumption | CI log reveals root cause | user states preference/constraint | fix worked for non-obvious reason | env quirk observed.
21
22
 
22
- ## FRAGILE LEARNINGS — HARD RULE
23
-
24
- Every unknown→known transition in this session = fact that dies on compaction unless handed off **the same turn it resolves**. Not end of phase. Not end of chain. Same turn.
25
-
26
- **Automatic trigger** — spawn `memorize` the moment any of these happens:
27
- - An `exec:` run's output answers an earlier "let me check" / "I don't know yet"
28
- - A code read confirms or refutes an assumption
29
- - A CI log reveals a root cause
30
- - User states a preference, constraint, deadline, or decision
31
- - A fix worked for a non-obvious reason
32
- - A tool / environment quirk bit once (blocked commands, path oddities, platform gotchas)
33
-
34
- **Invocation** (background, non-blocking, continue working in the same message):
35
23
  ```
36
- Agent(subagent_type='gm:memorize', model='haiku', run_in_background=true, prompt='## CONTEXT TO MEMORIZE\n<single fact with enough context to be useful cold>')
24
+ Agent(subagent_type='gm:memorize', model='haiku', run_in_background=true, prompt='## CONTEXT TO MEMORIZE\n<fact>')
37
25
  ```
38
26
 
39
- **Parallel**: multiple facts resolve in one turn spawn multiple memorize agents in the **same message** (parallel tool blocks). One call per fact. Never serialize, never batch into one prompt.
40
-
41
- **End-of-turn self-check** (mandatory before handing control back): scan the turn for resolved unknowns that were not memorized. Any found → spawn them now, parallel, before the response closes.
42
-
43
- Resolve an unknown, skip memorize = memory leak. Treat it as a bug, not a style choice.
44
-
45
- ## USER DONE TALKING
46
-
47
- User gave task. User waiting. Not co-pilot — person whose time you conserve by running chain end-to-end. Mid-chain questions ("should I proceed?", "which approach?", "look right before continue?") = chain breaks. Every break forces user back into loop they offloaded.
48
-
49
- Unknown resolution order — fixed:
50
- 1. **Code execution** — witnessed run (`exec:<lang>`, `exec:codesearch`, import real module). Covers 90%+ of mutables.
51
- 2. **Web** — `WebFetch` / `WebSearch` for API docs, spec PDFs, library versions, framework conventions. Covers environment facts not in this codebase.
52
- 3. **User** — only after code and web exhausted. Only for genuinely ambiguous scope that makes planning impossible, or destructive-irreversible decisions (force-push, drop prod table, publish). Not for preferences resolvable from existing code conventions.
53
-
54
- An unknown that could fall to step 1 or 2 is not a clarifying question — it is a missed run. "Want me to..." or "Should I..." mid-chain = invoke next skill instead.
55
-
56
- Clarification allowed at top of chain (before first `planning`) when scope is genuinely unreadable. After chain starts: policy carries it.
27
+ Multiple facts → parallel Agent calls in ONE message. End-of-turn: scan for un-memorized resolutions spawn now.
57
28
 
58
- All work coordination, planning, execution, and verification happens through the skill tree starting with `planning`:
59
- - `planning` skill → `gm-execute` skill → `gm-emit` skill → `gm-complete` skill → `update-docs` skill
60
- - `memorize` sub-agent — background only, non-sequential. `Agent(subagent_type='gm:memorize', model='haiku', run_in_background=true, prompt='## CONTEXT TO MEMORIZE\n<what was learned>')`
29
+ ## EXECUTION ORDER
61
30
 
62
- All code execution uses `exec:<lang>` via the Bash tool never direct `Bash(node ...)` or `Bash(npm ...)`.
31
+ 1. Code execution (exec:<lang>, exec:codesearch)90%+ of unknowns
32
+ 2. Web (WebFetch/WebSearch) — env facts not in codebase
33
+ 3. User — only when 1+2 exhausted AND decision is destructive-irreversible
63
34
 
64
- **Every `git push` triggers GitHub Actions. CI is auto-watched by the Stop hook — outcomes (green / failed / still-running) appear in next-turn context. No manual `gh run watch` needed. `gh run view <id> --log-failed` is for on-demand failure diagnosis only. Full behaviour in `gm-execute`.**
35
+ "Should I..." mid-chain = invoke next skill instead.
65
36
 
66
- Do not use `EnterPlanMode`. Do not run code directly via Bash. Invoke `planning` skill first.
37
+ Skill chain: `planning` `gm-execute` `gm-emit` `gm-complete` `update-docs`
67
38
 
68
- ## RESPONSE POLICY ALWAYS ACTIVE
39
+ exec:<lang> only. Never Bash(node/npm/npx/bun). git push = auto CI watch via Stop hook.
69
40
 
70
- Always terse. Technical substance stays. Fluff dies. Drop articles, filler, pleasantries, hedging. Fragments OK. Short synonyms. Technical terms exact. Pattern: `[thing] [action] [reason]. [next step].`
41
+ ## RESPONSE POLICY
71
42
 
72
- Code, commits, and PR descriptions write in normal prose. Security warnings, destructive confirmations, and genuinely ambiguous sequences also drop terseness. Everything else stays terse.
43
+ Terse. Drop filler. Fragments OK. Pattern: `[thing] [action] [reason]. [next step].`
44
+ Code/commits/PRs = normal prose. Security/destructive = drop terseness.