gm-thebird 2.0.1012

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/bin/plugkit.js ADDED
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
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');
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+ const os = require('os');
12
+
13
+ const wrapperDir = __dirname;
14
+
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);
19
+ }
20
+
21
+ function sha256OfFileSync(filePath) {
22
+ try {
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
+ }
34
+
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';
41
+ }
42
+
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 (_) {}
58
+ return null;
59
+ }
60
+
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();
@@ -0,0 +1,6 @@
1
+ a93716d43136550aa45d03cf50e6f0dac8d7e7f918f5898fa7cc445abb004cb7 plugkit-win32-x64.exe
2
+ 049215ba681d8ac9e01b138e425b24e8b7b9f1b4f034f5709d12838a55c63ce6 plugkit-win32-arm64.exe
3
+ d9d2d11d9e6ef1cac046218e2b07b2ddb591523cbce275fe3e9a1af20d55c47f plugkit-darwin-x64
4
+ 8f91a939056f27a56e42cc378a0d6d38e0b4259547852c7954b5758d3315d3b7 plugkit-darwin-arm64
5
+ 3d9391b8f674a7bbb4cf380973597a271d9d4ac62d16fb6e40f52d5697ef9f67 plugkit-linux-x64
6
+ af3270e3503400575e29347ea128656a6664fc1f319c4719716ec5fdb0428708 plugkit-linux-arm64
@@ -0,0 +1 @@
1
+ 0.1.354
package/bin/rtk.sha256 ADDED
@@ -0,0 +1,6 @@
1
+ 934e8e8b092e56e191807e2eaad90b197444639458d555cfbc09c24e75ea57a0 rtk-win32-x64.exe
2
+ c5c73d0c3669fcd88f199f55f060125d79fd50cbff36674bf95d18c63116db9a rtk-win32-arm64.exe
3
+ 1b1e792767ed0e1e6ca0e2f0a8de02e77b06dea2f5ae667278b94baf239fcdc3 rtk-darwin-x64
4
+ 9717978d9d6216ea50c94444e00e359479b6315a17bd48c16064b267c8b0b60d rtk-darwin-arm64
5
+ a100d3defac54194144e5723aec57e6f286b42298c67145c8428815246c9ee56 rtk-linux-x64
6
+ d3a9990cf468153cfb6ab2a240bf9ce2e22ac4bf7f7fe75c2fbbd5d84ac799eb rtk-linux-arm64
@@ -0,0 +1 @@
1
+ 0.39.0
package/cli.js ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const https = require('https');
6
+
7
+ const homeDir = process.env.HOME || process.env.USERPROFILE || os.homedir();
8
+ const destDir = path.join(homeDir, '.freddie', 'plugins', 'gm-thebird');
9
+ const srcDir = __dirname;
10
+ const isUpgrade = fs.existsSync(destDir);
11
+
12
+ console.log(isUpgrade ? 'Upgrading gm-thebird...' : 'Installing gm-thebird...');
13
+
14
+ function copyRecursive(src, dst) {
15
+ if (!fs.existsSync(src)) return;
16
+ if (fs.statSync(src).isDirectory()) {
17
+ fs.mkdirSync(dst, { recursive: true });
18
+ fs.readdirSync(src).forEach(f => copyRecursive(path.join(src, f), path.join(dst, f)));
19
+ } else {
20
+ fs.copyFileSync(src, dst);
21
+ }
22
+ }
23
+
24
+ function downloadPlugkitWasm(version, target) {
25
+ return new Promise((resolve, reject) => {
26
+ const url = 'https://github.com/AnEntrypoint/plugkit-bin/releases/download/v' + version + '/plugkit.wasm';
27
+ function get(u, redirectsLeft) {
28
+ https.get(u, { headers: { 'User-Agent': 'gm-thebird-installer' } }, res => {
29
+ if ([301,302,303,307,308].includes(res.statusCode) && res.headers.location && redirectsLeft > 0) {
30
+ return get(res.headers.location, redirectsLeft - 1);
31
+ }
32
+ if (res.statusCode !== 200) { reject(new Error('plugkit.wasm fetch failed: HTTP ' + res.statusCode)); return; }
33
+ const out = fs.createWriteStream(target);
34
+ res.pipe(out);
35
+ out.on('finish', () => out.close(resolve));
36
+ out.on('error', reject);
37
+ }).on('error', reject);
38
+ }
39
+ get(url, 5);
40
+ });
41
+ }
42
+
43
+ (async () => {
44
+ try {
45
+ fs.mkdirSync(destDir, { recursive: true });
46
+ fs.mkdirSync(path.join(destDir, 'bin'), { recursive: true });
47
+ copyRecursive(path.join(srcDir, 'agents'), path.join(destDir, 'agents'));
48
+ copyRecursive(path.join(srcDir, 'hooks'), path.join(destDir, 'hooks'));
49
+ copyRecursive(path.join(srcDir, 'skills'), path.join(destDir, 'skills'));
50
+ try { fs.copyFileSync(path.join(srcDir, 'plugin.json'), path.join(destDir, 'plugin.json')); } catch {}
51
+ try { fs.copyFileSync(path.join(srcDir, 'gm.json'), path.join(destDir, 'gm.json')); } catch {}
52
+ try { fs.copyFileSync(path.join(srcDir, 'README.md'), path.join(destDir, 'README.md')); } catch {}
53
+
54
+ const version = fs.readFileSync(path.join(srcDir, 'bin', 'plugkit.version'), 'utf-8').trim();
55
+ const wasmTarget = path.join(destDir, 'bin', 'plugkit.wasm');
56
+ if (!fs.existsSync(wasmTarget)) {
57
+ console.log('Fetching plugkit.wasm v' + version + '...');
58
+ await downloadPlugkitWasm(version, wasmTarget);
59
+ }
60
+ fs.writeFileSync(path.join(destDir, 'bin', 'plugkit.version'), version);
61
+
62
+ console.log('✓ gm-thebird ' + (isUpgrade ? 'upgraded' : 'installed') + ' to ' + destDir);
63
+ console.log('Restart Freddie to load the plugin.');
64
+ } catch (e) {
65
+ console.error('Installation failed:', e.message);
66
+ process.exit(1);
67
+ }
68
+ })();
package/gm.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "gm",
3
+ "version": "2.0.1012",
4
+ "description": "State machine agent with hooks, skills, and automated git enforcement",
5
+ "author": "AnEntrypoint",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "claude-code",
9
+ "claude-plugin",
10
+ "wfgy",
11
+ "automation"
12
+ ],
13
+ "homepage": "https://github.com/AnEntrypoint/gm",
14
+ "agents": [
15
+ {
16
+ "name": "gm",
17
+ "description": "Agent (not skill) - immutable programming state machine. Always invoke for all work coordination."
18
+ }
19
+ ],
20
+ "engines": {
21
+ "node": ">=16.0.0"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "plugkitVersion": "0.1.354"
27
+ }
@@ -0,0 +1,102 @@
1
+ {
2
+ "description": "Hooks for gm thebird extension",
3
+ "hooks": {
4
+ "PreToolUse": [
5
+ {
6
+ "matcher": "*",
7
+ "hooks": [
8
+ {
9
+ "type": "wasm",
10
+ "module": "${CLAUDE_PLUGIN_ROOT}/bin/plugkit.wasm",
11
+ "export": "hook_pre_tool_use",
12
+ "timeout": 3600
13
+ }
14
+ ]
15
+ }
16
+ ],
17
+ "PostToolUse": [
18
+ {
19
+ "matcher": "*",
20
+ "hooks": [
21
+ {
22
+ "type": "wasm",
23
+ "module": "${CLAUDE_PLUGIN_ROOT}/bin/plugkit.wasm",
24
+ "export": "hook_post_tool_use",
25
+ "timeout": 35000
26
+ }
27
+ ]
28
+ }
29
+ ],
30
+ "SessionStart": [
31
+ {
32
+ "matcher": "*",
33
+ "hooks": [
34
+ {
35
+ "type": "wasm",
36
+ "module": "${CLAUDE_PLUGIN_ROOT}/bin/plugkit.wasm",
37
+ "export": "hook_session_start",
38
+ "timeout": 180000
39
+ }
40
+ ]
41
+ }
42
+ ],
43
+ "UserPromptSubmit": [
44
+ {
45
+ "matcher": "*",
46
+ "hooks": [
47
+ {
48
+ "type": "wasm",
49
+ "module": "${CLAUDE_PLUGIN_ROOT}/bin/plugkit.wasm",
50
+ "export": "hook_prompt_submit",
51
+ "timeout": 60000
52
+ }
53
+ ]
54
+ }
55
+ ],
56
+ "PreCompact": [
57
+ {
58
+ "matcher": "*",
59
+ "hooks": [
60
+ {
61
+ "type": "wasm",
62
+ "module": "${CLAUDE_PLUGIN_ROOT}/bin/plugkit.wasm",
63
+ "export": "hook_pre_compact",
64
+ "timeout": 30000
65
+ }
66
+ ]
67
+ }
68
+ ],
69
+ "PostCompact": [
70
+ {
71
+ "matcher": "*",
72
+ "hooks": [
73
+ {
74
+ "type": "wasm",
75
+ "module": "${CLAUDE_PLUGIN_ROOT}/bin/plugkit.wasm",
76
+ "export": "hook_post_compact",
77
+ "timeout": 5000
78
+ }
79
+ ]
80
+ }
81
+ ],
82
+ "Stop": [
83
+ {
84
+ "matcher": "*",
85
+ "hooks": [
86
+ {
87
+ "type": "wasm",
88
+ "module": "${CLAUDE_PLUGIN_ROOT}/bin/plugkit.wasm",
89
+ "export": "hook_stop",
90
+ "timeout": 15000
91
+ },
92
+ {
93
+ "type": "wasm",
94
+ "module": "${CLAUDE_PLUGIN_ROOT}/bin/plugkit.wasm",
95
+ "export": "hook_stop_git",
96
+ "timeout": 210000
97
+ }
98
+ ]
99
+ }
100
+ ]
101
+ }
102
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "description": "Hook spec for gm thebird extension",
4
+ "envVar": "CLAUDE_PLUGIN_ROOT",
5
+ "plugkitInvoker": "node",
6
+ "events": [
7
+ {
8
+ "eventKey": "PreToolUse",
9
+ "commands": [
10
+ {
11
+ "kind": "wasm",
12
+ "subcommand": "pre-tool-use",
13
+ "timeout": 3600
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "eventKey": "PostToolUse",
19
+ "commands": [
20
+ {
21
+ "kind": "wasm",
22
+ "subcommand": "post-tool-use",
23
+ "timeout": 35000
24
+ }
25
+ ]
26
+ },
27
+ {
28
+ "eventKey": "SessionStart",
29
+ "commands": [
30
+ {
31
+ "kind": "wasm",
32
+ "subcommand": "session-start",
33
+ "timeout": 180000
34
+ }
35
+ ]
36
+ },
37
+ {
38
+ "eventKey": "UserPromptSubmit",
39
+ "commands": [
40
+ {
41
+ "kind": "wasm",
42
+ "subcommand": "prompt-submit",
43
+ "timeout": 60000
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ "eventKey": "PreCompact",
49
+ "commands": [
50
+ {
51
+ "kind": "wasm",
52
+ "subcommand": "pre-compact",
53
+ "timeout": 30000
54
+ }
55
+ ]
56
+ },
57
+ {
58
+ "eventKey": "PostCompact",
59
+ "commands": [
60
+ {
61
+ "kind": "wasm",
62
+ "subcommand": "post-compact",
63
+ "timeout": 5000
64
+ }
65
+ ]
66
+ },
67
+ {
68
+ "eventKey": "Stop",
69
+ "commands": [
70
+ {
71
+ "kind": "wasm",
72
+ "subcommand": "stop",
73
+ "timeout": 15000
74
+ },
75
+ {
76
+ "kind": "wasm",
77
+ "subcommand": "stop-git",
78
+ "timeout": 210000
79
+ }
80
+ ]
81
+ }
82
+ ]
83
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "gm-thebird",
3
+ "version": "2.0.1012",
4
+ "description": "State machine agent with hooks, skills, and automated git enforcement",
5
+ "author": "AnEntrypoint",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/AnEntrypoint/gm-thebird.git"
10
+ },
11
+ "homepage": "https://github.com/AnEntrypoint/gm-thebird#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/AnEntrypoint/gm-thebird/issues"
14
+ },
15
+ "engines": {
16
+ "node": ">=16.0.0"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "bin": {
22
+ "gm-thebird": "./cli.js"
23
+ },
24
+ "files": [
25
+ "agents/",
26
+ "bin/",
27
+ "hooks/",
28
+ "skills/",
29
+ "plugin.json",
30
+ "cli.js",
31
+ "README.md",
32
+ "AGENTS.md",
33
+ "gm.json"
34
+ ],
35
+ "keywords": [
36
+ "thebird",
37
+ "freddie",
38
+ "plugsdk",
39
+ "wasm",
40
+ "agent",
41
+ "state-machine",
42
+ "gm"
43
+ ]
44
+ }
package/plugin.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "gm",
3
+ "version": "2.0.1012",
4
+ "description": "State machine agent with hooks, skills, and automated git enforcement",
5
+ "author": {
6
+ "name": "AnEntrypoint",
7
+ "url": "https://github.com/AnEntrypoint"
8
+ },
9
+ "homepage": "https://github.com/AnEntrypoint/gm",
10
+ "hooks": "./hooks/hooks.json",
11
+ "skills": "./skills",
12
+ "mcpServers": {}
13
+ }
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: browser
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: Skill, Bash, Read, Write, Edit, Agent
5
+ ---
6
+
7
+ # Browser automation
8
+
9
+ Two pathways — never mix in the same Bash call.
10
+
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
+
13
+ `browser:` prefix is playwriter session management. One command per block.
14
+
15
+ ## Core
16
+
17
+ ```
18
+ exec:browser
19
+ await page.goto('https://example.com')
20
+ await snapshot({ page })
21
+ ```
22
+
23
+ ```
24
+ browser:
25
+ playwriter session new --direct
26
+ ```
27
+
28
+ ```
29
+ browser:
30
+ playwriter -s 1 -e 'await page.goto("http://example.com")'
31
+ ```
32
+
33
+ Session state persists across `browser:` calls. `-e` arg uses single quotes outside, double inside JS strings.
34
+
35
+ ## Timing
36
+
37
+ Never `await setTimeout(N)` with N > 10000. Poll instead.
38
+
39
+ ```
40
+ exec:browser
41
+ const start = Date.now()
42
+ while (!state.done && Date.now() - start < 12000) {
43
+ await new Promise(r => setTimeout(r, 500))
44
+ }
45
+ console.log(state.result)
46
+ ```
47
+
48
+ `Assertion failed: UV_HANDLE_CLOSING` is normal background-on-exit noise; ignore it.
49
+
50
+ ## Patterns
51
+
52
+ Data extraction:
53
+
54
+ ```
55
+ exec:browser
56
+ const items = await page.$$eval('.title', els => els.map(e => e.textContent))
57
+ console.log(JSON.stringify(items))
58
+ ```
59
+
60
+ Console monitoring — set listeners first, then poll:
61
+
62
+ ```
63
+ exec:browser
64
+ state.logs = []
65
+ page.on('console', msg => state.logs.push({ type: msg.type(), text: msg.text() }))
66
+ ```
67
+
68
+ ```
69
+ exec:browser
70
+ console.log(JSON.stringify(state.logs.slice(-20)))
71
+ ```
72
+
73
+ ## Constraints
74
+
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
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: code-search
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
+ ---
5
+
6
+ # Codebase search
7
+
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
+
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
+
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`.
15
+
16
+ ## Syntax
17
+
18
+ ```
19
+ exec:codesearch
20
+ <two-word query>
21
+ ```
22
+
23
+ ## Iteration
24
+
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.
26
+
27
+ ## Examples
28
+
29
+ ```
30
+ exec:codesearch
31
+ session cleanup idle
32
+ ```
33
+
34
+ No results, then:
35
+
36
+ ```
37
+ exec:codesearch
38
+ cleanup sessions timeout
39
+ ```
40
+
41
+ PDF:
42
+
43
+ ```
44
+ exec:codesearch
45
+ usb descriptor endpoint
46
+ ```
47
+
48
+ Returns `docs/usb-spec.pdf:42` — cite the page; `Read` if surrounding text is needed.