gm-gc 2.0.80 → 2.0.81

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.80",
3
+ "version": "2.0.81",
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/hooks/hooks.json CHANGED
@@ -13,18 +13,6 @@
13
13
  ]
14
14
  }
15
15
  ],
16
- "SessionStart": [
17
- {
18
- "matcher": "*",
19
- "hooks": [
20
- {
21
- "type": "command",
22
- "command": "node ${extensionPath}/hooks/session-start-hook.js",
23
- "timeout": 10000
24
- }
25
- ]
26
- }
27
- ],
28
16
  "BeforeAgent": [
29
17
  {
30
18
  "matcher": "*",
@@ -32,7 +20,7 @@
32
20
  {
33
21
  "type": "command",
34
22
  "command": "node ${extensionPath}/hooks/prompt-submit-hook.js",
35
- "timeout": 3600
23
+ "timeout": 180000
36
24
  }
37
25
  ]
38
26
  }
@@ -1,14 +1,36 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const fs = require('fs');
4
+ const path = require('path');
4
5
  const { execSync } = require('child_process');
5
6
 
6
- const projectDir = process.env.CLAUDE_PROJECT_DIR || process.env.GEMINI_PROJECT_DIR || process.env.OC_PROJECT_DIR;
7
+ const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || process.env.GEMINI_PROJECT_DIR || process.env.OC_PLUGIN_ROOT || process.env.KILO_PLUGIN_ROOT || path.join(__dirname, '..');
8
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.env.GEMINI_PROJECT_DIR || process.env.OC_PROJECT_DIR || process.env.KILO_PROJECT_DIR;
7
9
 
8
10
  const COMPACT_CONTEXT = 'use gm agent | ref: TOOL_INVARIANTS | codesearch for exploration | Bash for execution';
9
-
10
11
  const PLAN_MODE_BLOCK = 'DO NOT use EnterPlanMode or any plan mode tool. Use GM agent planning (PLAN→EXECUTE→EMIT→VERIFY→COMPLETE state machine) instead. Plan mode is blocked.';
11
12
 
13
+ const ensureGitignore = () => {
14
+ if (!projectDir) return;
15
+ const gitignorePath = path.join(projectDir, '.gitignore');
16
+ const entry = '.gm-stop-verified';
17
+ try {
18
+ let content = '';
19
+ if (fs.existsSync(gitignorePath)) {
20
+ content = fs.readFileSync(gitignorePath, 'utf-8');
21
+ }
22
+ if (!content.split('\n').some(line => line.trim() === entry)) {
23
+ const newContent = content.endsWith('\n') || content === ''
24
+ ? content + entry + '\n'
25
+ : content + '\n' + entry + '\n';
26
+ fs.writeFileSync(gitignorePath, newContent);
27
+ }
28
+ } catch (e) {
29
+ // Silently fail - not critical
30
+ }
31
+ };
32
+
33
+
12
34
  const getBaseContext = (resetMsg = '') => {
13
35
  let ctx = 'use gm agent';
14
36
  if (resetMsg) ctx += ' - ' + resetMsg;
@@ -25,6 +47,54 @@ const readStdinPrompt = () => {
25
47
  }
26
48
  };
27
49
 
50
+ const readGmAgent = () => {
51
+ if (!pluginRoot) return '';
52
+ try {
53
+ return fs.readFileSync(path.join(pluginRoot, 'agents/gm.md'), 'utf-8');
54
+ } catch (e) {
55
+ return '';
56
+ }
57
+ };
58
+
59
+ const runMcpThorns = () => {
60
+ if (!projectDir || !fs.existsSync(projectDir)) return '';
61
+ try {
62
+ let thornOutput;
63
+ try {
64
+ thornOutput = execSync('bun x mcp-thorns', {
65
+ encoding: 'utf-8',
66
+ stdio: ['pipe', 'pipe', 'pipe'],
67
+ cwd: projectDir,
68
+ timeout: 180000,
69
+ killSignal: 'SIGTERM'
70
+ });
71
+ } catch (bunErr) {
72
+ if (bunErr.killed && bunErr.signal === 'SIGTERM') {
73
+ thornOutput = '=== mcp-thorns ===\nSkipped (3min timeout)';
74
+ } else {
75
+ try {
76
+ thornOutput = execSync('npx -y mcp-thorns', {
77
+ encoding: 'utf-8',
78
+ stdio: ['pipe', 'pipe', 'pipe'],
79
+ cwd: projectDir,
80
+ timeout: 180000,
81
+ killSignal: 'SIGTERM'
82
+ });
83
+ } catch (npxErr) {
84
+ if (npxErr.killed && npxErr.signal === 'SIGTERM') {
85
+ thornOutput = '=== mcp-thorns ===\nSkipped (3min timeout)';
86
+ } else {
87
+ thornOutput = `=== mcp-thorns ===\nSkipped (error: ${bunErr.message.split('\n')[0]})`;
88
+ }
89
+ }
90
+ }
91
+ }
92
+ return `=== Repository analysis ===\n${thornOutput}`;
93
+ } catch (e) {
94
+ return `=== mcp-thorns ===\nSkipped (error: ${e.message.split('\n')[0]})`;
95
+ }
96
+ };
97
+
28
98
  const runCodeSearch = (query, cwd) => {
29
99
  if (!query || !cwd || !fs.existsSync(cwd)) return '';
30
100
  try {
@@ -58,11 +128,12 @@ const runCodeSearch = (query, cwd) => {
58
128
 
59
129
  const emit = (additionalContext) => {
60
130
  const isGemini = process.env.GEMINI_PROJECT_DIR !== undefined;
61
- const isOpenCode = process.env.OC_PLUGIN_ROOT !== undefined;
131
+ const isOpenCode = process.env.OC_PROJECT_DIR !== undefined;
132
+ const isKilo = process.env.KILO_PROJECT_DIR !== undefined;
62
133
 
63
134
  if (isGemini) {
64
135
  console.log(JSON.stringify({ systemMessage: additionalContext }, null, 2));
65
- } else if (isOpenCode) {
136
+ } else if (isOpenCode || isKilo) {
66
137
  console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: 'message.updated', additionalContext } }, null, 2));
67
138
  } else {
68
139
  console.log(JSON.stringify({ additionalContext }, null, 2));
@@ -70,13 +141,27 @@ const emit = (additionalContext) => {
70
141
  };
71
142
 
72
143
  try {
144
+ ensureGitignore();
145
+
73
146
  const prompt = readStdinPrompt();
74
- const parts = [getBaseContext() + ' | ' + COMPACT_CONTEXT + ' | ' + PLAN_MODE_BLOCK];
147
+ const parts = [];
148
+
149
+ // Always: include gm.md and mcp-thorns
150
+ const gmContent = readGmAgent();
151
+ if (gmContent) {
152
+ parts.push(gmContent);
153
+ }
154
+
155
+ const thornOutput = runMcpThorns();
156
+ parts.push(thornOutput);
157
+
158
+ // Always: base context and codebasesearch
159
+ parts.push(getBaseContext() + ' | ' + COMPACT_CONTEXT + ' | ' + PLAN_MODE_BLOCK);
75
160
 
76
161
  if (prompt && projectDir) {
77
162
  const searchResults = runCodeSearch(prompt, projectDir);
78
163
  if (searchResults) {
79
- parts.push(`=== Semantic code search results for initial prompt ===\n${searchResults}`);
164
+ parts.push(`=== Semantic code search results ===\n${searchResults}`);
80
165
  }
81
166
  }
82
167
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-gc",
3
- "version": "2.0.80",
3
+ "version": "2.0.81",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
@@ -1,146 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const { execSync } = require('child_process');
6
-
7
- const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || process.env.GEMINI_PROJECT_DIR || process.env.OC_PLUGIN_ROOT || process.env.KILO_PLUGIN_ROOT || path.join(__dirname, '..');
8
- const projectDir = process.env.CLAUDE_PROJECT_DIR || process.env.GEMINI_PROJECT_DIR || process.env.OC_PROJECT_DIR || process.env.KILO_PROJECT_DIR;
9
-
10
- const ensureGitignore = () => {
11
- if (!projectDir) return;
12
- const gitignorePath = path.join(projectDir, '.gitignore');
13
- const entry = '.gm-stop-verified';
14
- try {
15
- let content = '';
16
- if (fs.existsSync(gitignorePath)) {
17
- content = fs.readFileSync(gitignorePath, 'utf-8');
18
- }
19
- if (!content.split('\n').some(line => line.trim() === entry)) {
20
- const newContent = content.endsWith('\n') || content === ''
21
- ? content + entry + '\n'
22
- : content + '\n' + entry + '\n';
23
- fs.writeFileSync(gitignorePath, newContent);
24
- }
25
- } catch (e) {
26
- // Silently fail - not critical
27
- }
28
- };
29
-
30
- ensureGitignore();
31
-
32
- try {
33
- let outputs = [];
34
-
35
- // 1. Read ./start.md
36
- if (pluginRoot) {
37
- const startMdPath = path.join(pluginRoot, '/agents/gm.md');
38
- try {
39
- const startMdContent = fs.readFileSync(startMdPath, 'utf-8');
40
- outputs.push(startMdContent);
41
- } catch (e) {
42
- // File may not exist in this context
43
- }
44
- }
45
-
46
- // 2. Add semantic code-search explanation
47
- const codeSearchContext = `## 🔍 Semantic Code Search Now Available
48
-
49
- Your prompts will trigger **semantic code search** - intelligent, intent-based exploration of your codebase.
50
-
51
- ### How It Works
52
- Describe what you need in plain language, and the search understands your intent:
53
- - "Find authentication validation" → locates auth checks, guards, permission logic
54
- - "Where is database initialization?" → finds connection setup, migrations, schemas
55
- - "Show error handling patterns" → discovers try/catch patterns, error boundaries
56
-
57
- NOT syntax-based regex matching - truly semantic understanding across files.
58
-
59
- ### Example
60
- Instead of regex patterns, simply describe your intent:
61
- "Find where API authorization is checked"
62
-
63
- The search will find permission validations, role checks, authentication guards - however they're implemented.
64
-
65
- ### When to Use Code Search
66
- When exploring unfamiliar code, finding similar patterns, understanding integrations, or locating feature implementations across your codebase.`;
67
- outputs.push(codeSearchContext);
68
-
69
- // 3. Run mcp-thorns (bun x with npx fallback)
70
- if (projectDir && fs.existsSync(projectDir)) {
71
- try {
72
- let thornOutput;
73
- try {
74
- thornOutput = execSync(`bun x mcp-thorns`, {
75
- encoding: 'utf-8',
76
- stdio: ['pipe', 'pipe', 'pipe'],
77
- cwd: projectDir,
78
- timeout: 180000,
79
- killSignal: 'SIGTERM'
80
- });
81
- } catch (bunErr) {
82
- if (bunErr.killed && bunErr.signal === 'SIGTERM') {
83
- thornOutput = '=== mcp-thorns ===\nSkipped (3min timeout)';
84
- } else {
85
- try {
86
- thornOutput = execSync(`npx -y mcp-thorns`, {
87
- encoding: 'utf-8',
88
- stdio: ['pipe', 'pipe', 'pipe'],
89
- cwd: projectDir,
90
- timeout: 180000,
91
- killSignal: 'SIGTERM'
92
- });
93
- } catch (npxErr) {
94
- if (npxErr.killed && npxErr.signal === 'SIGTERM') {
95
- thornOutput = '=== mcp-thorns ===\nSkipped (3min timeout)';
96
- } else {
97
- thornOutput = `=== mcp-thorns ===\nSkipped (error: ${bunErr.message.split('\n')[0]})`;
98
- }
99
- }
100
- }
101
- }
102
- outputs.push(`=== This is your initial insight of the repository, look at every possible aspect of this for initial opinionation and to offset the need for code exploration ===\n${thornOutput}`);
103
- } catch (e) {
104
- if (e.killed && e.signal === 'SIGTERM') {
105
- outputs.push(`=== mcp-thorns ===\nSkipped (3min timeout)`);
106
- } else {
107
- outputs.push(`=== mcp-thorns ===\nSkipped (error: ${e.message.split('\n')[0]})`);
108
- }
109
- }
110
- }
111
- outputs.push('Use gm as a philosophy to coordinate all plans and the gm subagent to create and execute all plans');
112
- const additionalContext = outputs.join('\n\n');
113
-
114
- const isGemini = process.env.GEMINI_PROJECT_DIR !== undefined;
115
- const isOpenCode = process.env.OC_PLUGIN_ROOT !== undefined;
116
- const isKilo = process.env.KILO_PLUGIN_ROOT !== undefined;
117
-
118
- if (isGemini) {
119
- console.log(JSON.stringify({ systemMessage: additionalContext }, null, 2));
120
- } else if (isOpenCode || isKilo) {
121
- console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: 'session.created', additionalContext } }, null, 2));
122
- } else {
123
- console.log(JSON.stringify({ additionalContext }, null, 2));
124
- }
125
- } catch (error) {
126
- const isGemini = process.env.GEMINI_PROJECT_DIR !== undefined;
127
- const isOpenCode = process.env.OC_PLUGIN_ROOT !== undefined;
128
- const isKilo = process.env.KILO_PLUGIN_ROOT !== undefined;
129
-
130
- if (isGemini) {
131
- console.log(JSON.stringify({ systemMessage: `Error executing hook: ${error.message}` }, null, 2));
132
- } else if (isOpenCode || isKilo) {
133
- console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: 'session.created', additionalContext: `Error executing hook: ${error.message}` } }, null, 2));
134
- } else {
135
- console.log(JSON.stringify({ additionalContext: `Error executing hook: ${error.message}` }, null, 2));
136
- }
137
- process.exit(0);
138
- }
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-