gm-gc 2.0.77 → 2.0.79

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/agents/gm.md CHANGED
@@ -365,6 +365,14 @@ Scope: Runtime process execution. Governs how all applications are started, moni
365
365
  - Windows 11+: `spawn wmic ENOENT` in daemon logs is cosmetic — app processes work; fix with `npm install -g pm2@latest`
366
366
  - Linux watch exhaustion: `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`
367
367
 
368
+ **Windows Terminal Suppression (CRITICAL)**:
369
+ - All terminal spawning in code MUST use `windowsHide: true` in spawn/exec options
370
+ - Prevents popup windows on Windows during subprocess execution
371
+ - Example: `spawn('node', [...], { windowsHide: true })`
372
+ - Applies to all `child_process.spawn()`, `child_process.exec()`, and similar calls
373
+ - PM2 processes automatically hide windows; code-spawned subprocesses must explicitly set this
374
+ - Forgetting this creates visible popup windows during automation—unacceptable UX
375
+
368
376
  **Log monitoring**:
369
377
  ```bash
370
378
  pm2 logs <name> # stream live output
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.77",
3
+ "version": "2.0.79",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "homepage": "https://github.com/AnEntrypoint/gm",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-gc",
3
- "version": "2.0.77",
3
+ "version": "2.0.79",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+
7
+ function isInsideNodeModules() {
8
+ return __dirname.includes(path.sep + 'node_modules' + path.sep);
9
+ }
10
+
11
+ function getProjectRoot() {
12
+ if (!isInsideNodeModules()) return null;
13
+ let current = __dirname;
14
+ while (current !== path.dirname(current)) {
15
+ current = path.dirname(current);
16
+ if (path.basename(current) === 'node_modules') {
17
+ return path.dirname(current);
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+
23
+ function safeCopyFile(src, dst) {
24
+ try {
25
+ const content = fs.readFileSync(src, 'utf-8');
26
+ const dstDir = path.dirname(dst);
27
+ if (!fs.existsSync(dstDir)) fs.mkdirSync(dstDir, { recursive: true });
28
+ fs.writeFileSync(dst, content, 'utf-8');
29
+ return true;
30
+ } catch (e) {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ function safeCopyDirectory(src, dst) {
36
+ try {
37
+ if (!fs.existsSync(src)) return false;
38
+ fs.mkdirSync(dst, { recursive: true });
39
+ fs.readdirSync(src, { withFileTypes: true }).forEach(entry => {
40
+ const srcPath = path.join(src, entry.name);
41
+ const dstPath = path.join(dst, entry.name);
42
+ if (entry.isDirectory()) safeCopyDirectory(srcPath, dstPath);
43
+ else if (entry.isFile()) safeCopyFile(srcPath, dstPath);
44
+ });
45
+ return true;
46
+ } catch (e) {
47
+ return false;
48
+ }
49
+ }
50
+
51
+ function updateGitignore(projectRoot) {
52
+ try {
53
+ const gitignorePath = path.join(projectRoot, '.gitignore');
54
+ const entry = '.gm-stop-verified';
55
+ let content = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
56
+ if (content.includes(entry)) return true;
57
+ if (content && !content.endsWith('\n')) content += '\n';
58
+ fs.writeFileSync(gitignorePath, content + entry + '\n', 'utf-8');
59
+ return true;
60
+ } catch (e) {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ function install() {
66
+ if (!isInsideNodeModules()) return;
67
+ const projectRoot = getProjectRoot();
68
+ if (!projectRoot) return;
69
+ const kiloDir = path.join(projectRoot, '.config', 'kilo');
70
+ const sourceDir = __dirname.replace(/[/\\]scripts$/, '');
71
+
72
+ // Copy files
73
+ safeCopyDirectory(path.join(sourceDir, 'agents'), path.join(kiloDir, 'agents'));
74
+ safeCopyDirectory(path.join(sourceDir, 'hooks'), path.join(kiloDir, 'hooks'));
75
+ safeCopyDirectory(path.join(sourceDir, 'skills'), path.join(kiloDir, 'skills'));
76
+ safeCopyFile(path.join(sourceDir, 'kilocode.json'), path.join(kiloDir, 'kilocode.json'));
77
+ safeCopyFile(path.join(sourceDir, '.mcp.json'), path.join(kiloDir, '.mcp.json'));
78
+ safeCopyFile(path.join(sourceDir, 'gm.mjs'), path.join(kiloDir, 'gm.mjs'));
79
+ safeCopyFile(path.join(sourceDir, 'index.mjs'), path.join(kiloDir, 'index.mjs'));
80
+ safeCopyFile(path.join(sourceDir, 'README.md'), path.join(kiloDir, 'README.md'));
81
+ safeCopyFile(path.join(sourceDir, 'LICENSE'), path.join(kiloDir, 'LICENSE'));
82
+ safeCopyFile(path.join(sourceDir, 'CONTRIBUTING.md'), path.join(kiloDir, 'CONTRIBUTING.md'));
83
+ safeCopyFile(path.join(sourceDir, '.gitignore'), path.join(kiloDir, '.gitignore'));
84
+ safeCopyFile(path.join(sourceDir, '.editorconfig'), path.join(kiloDir, '.editorconfig'));
85
+
86
+ // Also write plugin/ directory - Kilo loads from ~/.config/kilo/plugin/ as a local file plugin
87
+ const pluginDir = path.join(kiloDir, 'plugin');
88
+ if (!fs.existsSync(pluginDir)) fs.mkdirSync(pluginDir, { recursive: true });
89
+ const gmMjsSrc = path.join(sourceDir, 'gm.mjs');
90
+ if (fs.existsSync(gmMjsSrc)) {
91
+ safeCopyFile(gmMjsSrc, path.join(pluginDir, 'gm.mjs'));
92
+ }
93
+ fs.writeFileSync(path.join(pluginDir, 'index.js'), "export { default } from './gm.mjs';\n", 'utf-8');
94
+
95
+ // Update .gitignore
96
+ updateGitignore(projectRoot);
97
+
98
+ // Warm bun x cache for packages used by hooks
99
+ warmBunCache();
100
+
101
+ // Silent success
102
+ }
103
+
104
+ function warmBunCache() {
105
+ const packages = ['mcp-thorns@latest', 'codebasesearch@latest'];
106
+ for (const pkg of packages) {
107
+ try {
108
+ execSync(`bun x ${pkg} --version`, {
109
+ encoding: 'utf-8',
110
+ stdio: 'pipe',
111
+ timeout: 60000
112
+ });
113
+ } catch (e) {
114
+ // Silent - cache warming is best-effort
115
+ }
116
+ }
117
+ }
118
+
119
+ install();
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+
7
+ function isInsideNodeModules() {
8
+ return __dirname.includes(path.sep + 'node_modules' + path.sep);
9
+ }
10
+
11
+ function getProjectRoot() {
12
+ if (!isInsideNodeModules()) return null;
13
+ let current = __dirname;
14
+ while (current !== path.dirname(current)) {
15
+ current = path.dirname(current);
16
+ if (path.basename(current) === 'node_modules') {
17
+ return path.dirname(current);
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+
23
+ function safeCopyFile(src, dst) {
24
+ try {
25
+ const content = fs.readFileSync(src, 'utf-8');
26
+ const dstDir = path.dirname(dst);
27
+ if (!fs.existsSync(dstDir)) fs.mkdirSync(dstDir, { recursive: true });
28
+ fs.writeFileSync(dst, content, 'utf-8');
29
+ return true;
30
+ } catch (e) {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ function safeCopyDirectory(src, dst) {
36
+ try {
37
+ if (!fs.existsSync(src)) return false;
38
+ fs.mkdirSync(dst, { recursive: true });
39
+ fs.readdirSync(src, { withFileTypes: true }).forEach(entry => {
40
+ const srcPath = path.join(src, entry.name);
41
+ const dstPath = path.join(dst, entry.name);
42
+ if (entry.isDirectory()) safeCopyDirectory(srcPath, dstPath);
43
+ else if (entry.isFile()) safeCopyFile(srcPath, dstPath);
44
+ });
45
+ return true;
46
+ } catch (e) {
47
+ return false;
48
+ }
49
+ }
50
+
51
+ function updateGitignore(projectRoot) {
52
+ try {
53
+ const gitignorePath = path.join(projectRoot, '.gitignore');
54
+ const entry = '.gm-stop-verified';
55
+ let content = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
56
+ if (content.includes(entry)) return true;
57
+ if (content && !content.endsWith('\n')) content += '\n';
58
+ fs.writeFileSync(gitignorePath, content + entry + '\n', 'utf-8');
59
+ return true;
60
+ } catch (e) {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ function install() {
66
+ if (!isInsideNodeModules()) return;
67
+ const projectRoot = getProjectRoot();
68
+ if (!projectRoot) return;
69
+ const ocDir = path.join(projectRoot, '.config', 'opencode');
70
+ const sourceDir = __dirname.replace(/[/\\]scripts$/, '');
71
+
72
+ // Copy files
73
+ safeCopyDirectory(path.join(sourceDir, 'agents'), path.join(ocDir, 'agents'));
74
+ safeCopyDirectory(path.join(sourceDir, 'hooks'), path.join(ocDir, 'hooks'));
75
+ safeCopyDirectory(path.join(sourceDir, 'skills'), path.join(ocDir, 'skills'));
76
+ safeCopyFile(path.join(sourceDir, 'opencode.json'), path.join(ocDir, 'opencode.json'));
77
+ safeCopyFile(path.join(sourceDir, '.mcp.json'), path.join(ocDir, '.mcp.json'));
78
+ safeCopyFile(path.join(sourceDir, 'gm.mjs'), path.join(ocDir, 'gm.mjs'));
79
+ safeCopyFile(path.join(sourceDir, 'index.mjs'), path.join(ocDir, 'index.mjs'));
80
+ safeCopyFile(path.join(sourceDir, 'README.md'), path.join(ocDir, 'README.md'));
81
+ safeCopyFile(path.join(sourceDir, 'LICENSE'), path.join(ocDir, 'LICENSE'));
82
+ safeCopyFile(path.join(sourceDir, 'CONTRIBUTING.md'), path.join(ocDir, 'CONTRIBUTING.md'));
83
+ safeCopyFile(path.join(sourceDir, '.gitignore'), path.join(ocDir, '.gitignore'));
84
+ safeCopyFile(path.join(sourceDir, '.editorconfig'), path.join(ocDir, '.editorconfig'));
85
+
86
+ // Also write to plugins/gm-oc.mjs - the actual file OpenCode loads
87
+ const pluginsDir = path.join(ocDir, 'plugins');
88
+ if (!fs.existsSync(pluginsDir)) fs.mkdirSync(pluginsDir, { recursive: true });
89
+ const gmMjsSrc = path.join(sourceDir, 'gm.mjs');
90
+ if (fs.existsSync(gmMjsSrc)) {
91
+ safeCopyFile(gmMjsSrc, path.join(pluginsDir, 'gm-oc.mjs'));
92
+ }
93
+
94
+ // Update .gitignore
95
+ updateGitignore(projectRoot);
96
+
97
+ // Warm bun x cache for packages used by hooks
98
+ warmBunCache();
99
+
100
+ // Silent success
101
+ }
102
+
103
+ function warmBunCache() {
104
+ const packages = ['mcp-thorns@latest', 'codebasesearch@latest'];
105
+ for (const pkg of packages) {
106
+ try {
107
+ execSync(`bun x ${pkg} --version`, {
108
+ encoding: 'utf-8',
109
+ stdio: 'pipe',
110
+ timeout: 60000
111
+ });
112
+ } catch (e) {
113
+ // Silent - cache warming is best-effort
114
+ }
115
+ }
116
+ }
117
+
118
+ install();
@@ -93,6 +93,21 @@ All execution via Bash tool or `agent-browser` skill. Every hypothesis proven by
93
93
  - Starting/stopping system services
94
94
  - Everything else → Bash tool
95
95
 
96
+ **CRITICAL: Windows Terminal Suppression**:
97
+ When code spawns subprocesses, ALWAYS use `windowsHide: true` to prevent popup windows on Windows:
98
+
99
+ ```javascript
100
+ // ❌ WRONG - popup windows on Windows
101
+ const { spawn } = require('child_process');
102
+ spawn('node', ['script.js']);
103
+
104
+ // ✅ CORRECT - hides windows, works cross-platform
105
+ const { spawn } = require('child_process');
106
+ spawn('node', ['script.js'], { windowsHide: true });
107
+ ```
108
+
109
+ Applies to: `spawn()`, `exec()`, `execFile()`, `fork()`. See `process-management` skill for full details.
110
+
96
111
  ## CHARTER 3: GROUND TRUTH
97
112
 
98
113
  Scope: Data integrity and testing methodology. Governs what constitutes valid evidence.
@@ -168,6 +168,26 @@ Get-Command myapp | Select-Object -ExpandProperty Source
168
168
  ```
169
169
  Point `script` at the resolved `.js` file — never at the `.cmd` wrapper.
170
170
 
171
+ ### Terminal Suppression on Windows (CRITICAL)
172
+
173
+ All code that spawns subprocesses MUST use `windowsHide: true` to prevent popup windows.
174
+
175
+ ```javascript
176
+ // ❌ WRONG - will show popup windows on Windows
177
+ spawn('node', ['script.js']);
178
+
179
+ // ✅ CORRECT - hides windows, safe for all platforms
180
+ spawn('node', ['script.js'], { windowsHide: true });
181
+ ```
182
+
183
+ Applies to all subprocess execution:
184
+ - `child_process.spawn()` → `{ windowsHide: true }`
185
+ - `child_process.exec()` → `{ windowsHide: true }`
186
+ - `child_process.execFile()` → `{ windowsHide: true }`
187
+ - `child_process.fork()` → `{ silent: true }` (alternative for fork)
188
+
189
+ PM2-started processes automatically hide windows. Code-spawned subprocesses must explicitly set this. Forgetting creates visible popups during automation—unacceptable UX.
190
+
171
191
  ### Windows 11+ wmic Error
172
192
 
173
193
  PM2 uses `wmic` for process stats — removed in Windows 11+.