@phuetz/code-buddy 0.1.0 → 0.1.1

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.
Files changed (206) hide show
  1. package/.codebuddy/skills/bundled/brave-search/SKILL.md +490 -0
  2. package/.codebuddy/skills/bundled/exa-search/SKILL.md +1122 -0
  3. package/.codebuddy/skills/bundled/perplexity/SKILL.md +748 -0
  4. package/.codebuddy/skills/bundled/playwright/SKILL.md +520 -0
  5. package/.codebuddy/skills/bundled/puppeteer/SKILL.md +708 -0
  6. package/.codebuddy/skills/bundled/web-fetch/SKILL.md +1003 -0
  7. package/README.md +56 -0
  8. package/dist/agent/agent-state.d.ts +3 -3
  9. package/dist/agent/agent-state.js +6 -6
  10. package/dist/agent/agent-state.js.map +1 -1
  11. package/dist/agent/base-agent.d.ts +4 -4
  12. package/dist/agent/base-agent.js +22 -9
  13. package/dist/agent/base-agent.js.map +1 -1
  14. package/dist/agent/cache-trace.d.ts +56 -0
  15. package/dist/agent/cache-trace.js +98 -0
  16. package/dist/agent/cache-trace.js.map +1 -0
  17. package/dist/agent/codebuddy-agent.js +3 -2
  18. package/dist/agent/codebuddy-agent.js.map +1 -1
  19. package/dist/agent/execution/agent-executor.d.ts +4 -4
  20. package/dist/agent/execution/agent-executor.js +41 -7
  21. package/dist/agent/execution/agent-executor.js.map +1 -1
  22. package/dist/agent/facades/agent-context-facade.js +1 -3
  23. package/dist/agent/facades/agent-context-facade.js.map +1 -1
  24. package/dist/agent/facades/message-history-manager.js +14 -12
  25. package/dist/agent/facades/message-history-manager.js.map +1 -1
  26. package/dist/agent/facades/session-facade.d.ts +3 -3
  27. package/dist/agent/facades/session-facade.js +6 -6
  28. package/dist/agent/facades/session-facade.js.map +1 -1
  29. package/dist/agent/history-repair.d.ts +37 -0
  30. package/dist/agent/history-repair.js +124 -0
  31. package/dist/agent/history-repair.js.map +1 -0
  32. package/dist/agent/specialized/archive-agent.d.ts +3 -0
  33. package/dist/agent/specialized/archive-agent.js +71 -31
  34. package/dist/agent/specialized/archive-agent.js.map +1 -1
  35. package/dist/agent/specialized/security-review/agent.js +19 -8
  36. package/dist/agent/specialized/security-review/agent.js.map +1 -1
  37. package/dist/agent/tool-executor.js +5 -0
  38. package/dist/agent/tool-executor.js.map +1 -1
  39. package/dist/agent/turn-diff-tracker.d.ts +79 -0
  40. package/dist/agent/turn-diff-tracker.js +195 -0
  41. package/dist/agent/turn-diff-tracker.js.map +1 -0
  42. package/dist/checkpoints/checkpoint-versioning.js +78 -20
  43. package/dist/checkpoints/checkpoint-versioning.js.map +1 -1
  44. package/dist/cli/config-loader.js +2 -4
  45. package/dist/cli/config-loader.js.map +1 -1
  46. package/dist/commands/handlers/fcs-handlers.js +1 -1
  47. package/dist/commands/handlers/fcs-handlers.js.map +1 -1
  48. package/dist/commands/handlers/memory-handlers.js +2 -1
  49. package/dist/commands/handlers/memory-handlers.js.map +1 -1
  50. package/dist/commands/handlers/worktree-handlers.js +11 -0
  51. package/dist/commands/handlers/worktree-handlers.js.map +1 -1
  52. package/dist/commands/mcp.d.ts +1 -0
  53. package/dist/commands/mcp.js +66 -7
  54. package/dist/commands/mcp.js.map +1 -1
  55. package/dist/commands/pipeline.js +25 -13
  56. package/dist/commands/pipeline.js.map +1 -1
  57. package/dist/config/model-tools.d.ts +41 -0
  58. package/dist/config/model-tools.js +194 -0
  59. package/dist/config/model-tools.js.map +1 -0
  60. package/dist/context/context-manager-v2.d.ts +2 -1
  61. package/dist/context/context-manager-v2.js +34 -5
  62. package/dist/context/context-manager-v2.js.map +1 -1
  63. package/dist/daemon/daemon-manager.js +23 -19
  64. package/dist/daemon/daemon-manager.js.map +1 -1
  65. package/dist/database/database-manager.d.ts +4 -0
  66. package/dist/database/database-manager.js +16 -7
  67. package/dist/database/database-manager.js.map +1 -1
  68. package/dist/desktop-automation/nutjs-provider.js +89 -0
  69. package/dist/desktop-automation/nutjs-provider.js.map +1 -1
  70. package/dist/fcs/builtins.d.ts +2 -6
  71. package/dist/fcs/builtins.js +2 -568
  72. package/dist/fcs/builtins.js.map +1 -1
  73. package/dist/fcs/codebuddy-bindings.d.ts +3 -43
  74. package/dist/fcs/codebuddy-bindings.js +2 -606
  75. package/dist/fcs/codebuddy-bindings.js.map +1 -1
  76. package/dist/fcs/index.d.ts +2 -27
  77. package/dist/fcs/index.js +2 -53
  78. package/dist/fcs/index.js.map +1 -1
  79. package/dist/fcs/lexer.d.ts +2 -37
  80. package/dist/fcs/lexer.js +2 -459
  81. package/dist/fcs/lexer.js.map +1 -1
  82. package/dist/fcs/parser.d.ts +2 -68
  83. package/dist/fcs/parser.js +2 -893
  84. package/dist/fcs/parser.js.map +1 -1
  85. package/dist/fcs/runtime.d.ts +2 -59
  86. package/dist/fcs/runtime.js +2 -623
  87. package/dist/fcs/runtime.js.map +1 -1
  88. package/dist/fcs/script-registry.d.ts +3 -69
  89. package/dist/fcs/script-registry.js +2 -219
  90. package/dist/fcs/script-registry.js.map +1 -1
  91. package/dist/fcs/sync-bindings.d.ts +3 -101
  92. package/dist/fcs/sync-bindings.js +2 -410
  93. package/dist/fcs/sync-bindings.js.map +1 -1
  94. package/dist/fcs/types.d.ts +2 -285
  95. package/dist/fcs/types.js +2 -103
  96. package/dist/fcs/types.js.map +1 -1
  97. package/dist/hooks/use-input-handler.d.ts +1 -1
  98. package/dist/index.js +5 -2
  99. package/dist/index.js.map +1 -1
  100. package/dist/input/voice-control.js +11 -5
  101. package/dist/input/voice-control.js.map +1 -1
  102. package/dist/integrations/json-rpc/server.js +5 -5
  103. package/dist/integrations/json-rpc/server.js.map +1 -1
  104. package/dist/integrations/mcp/mcp-server.js +1 -1
  105. package/dist/integrations/mcp/mcp-server.js.map +1 -1
  106. package/dist/mcp/client.js +2 -1
  107. package/dist/mcp/client.js.map +1 -1
  108. package/dist/mcp/config.js +89 -5
  109. package/dist/mcp/config.js.map +1 -1
  110. package/dist/mcp/mcp-client.js +65 -14
  111. package/dist/mcp/mcp-client.js.map +1 -1
  112. package/dist/mcp/transports.d.ts +0 -1
  113. package/dist/mcp/transports.js +1 -5
  114. package/dist/mcp/transports.js.map +1 -1
  115. package/dist/mcp/types.d.ts +2 -0
  116. package/dist/persistence/session-lock.d.ts +42 -0
  117. package/dist/persistence/session-lock.js +165 -0
  118. package/dist/persistence/session-lock.js.map +1 -0
  119. package/dist/persistence/session-store.d.ts +18 -3
  120. package/dist/persistence/session-store.js +90 -21
  121. package/dist/persistence/session-store.js.map +1 -1
  122. package/dist/plugins/isolated-plugin-runner.d.ts +6 -0
  123. package/dist/plugins/isolated-plugin-runner.js +19 -1
  124. package/dist/plugins/isolated-plugin-runner.js.map +1 -1
  125. package/dist/providers/local-llm-provider.js +21 -4
  126. package/dist/providers/local-llm-provider.js.map +1 -1
  127. package/dist/sandbox/docker-sandbox.js +7 -4
  128. package/dist/sandbox/docker-sandbox.js.map +1 -1
  129. package/dist/scripting/builtins.d.ts +8 -3
  130. package/dist/scripting/builtins.js +506 -355
  131. package/dist/scripting/builtins.js.map +1 -1
  132. package/dist/scripting/codebuddy-bindings.d.ts +47 -0
  133. package/dist/scripting/codebuddy-bindings.js +487 -0
  134. package/dist/scripting/codebuddy-bindings.js.map +1 -0
  135. package/dist/scripting/index.d.ts +33 -30
  136. package/dist/scripting/index.js +41 -36
  137. package/dist/scripting/index.js.map +1 -1
  138. package/dist/scripting/lexer.d.ts +31 -13
  139. package/dist/scripting/lexer.js +379 -292
  140. package/dist/scripting/lexer.js.map +1 -1
  141. package/dist/scripting/parser.d.ts +63 -44
  142. package/dist/scripting/parser.js +700 -473
  143. package/dist/scripting/parser.js.map +1 -1
  144. package/dist/scripting/runtime.d.ts +55 -24
  145. package/dist/scripting/runtime.js +600 -288
  146. package/dist/scripting/runtime.js.map +1 -1
  147. package/dist/scripting/script-registry.d.ts +54 -0
  148. package/dist/scripting/script-registry.js +202 -0
  149. package/dist/scripting/script-registry.js.map +1 -0
  150. package/dist/scripting/sync-bindings.d.ts +105 -0
  151. package/dist/scripting/sync-bindings.js +353 -0
  152. package/dist/scripting/sync-bindings.js.map +1 -0
  153. package/dist/scripting/types.d.ts +297 -199
  154. package/dist/scripting/types.js +86 -60
  155. package/dist/scripting/types.js.map +1 -1
  156. package/dist/search/usearch-index.js +42 -7
  157. package/dist/search/usearch-index.js.map +1 -1
  158. package/dist/security/bash-parser.d.ts +51 -0
  159. package/dist/security/bash-parser.js +327 -0
  160. package/dist/security/bash-parser.js.map +1 -0
  161. package/dist/security/skill-scanner.d.ts +36 -0
  162. package/dist/security/skill-scanner.js +149 -0
  163. package/dist/security/skill-scanner.js.map +1 -0
  164. package/dist/security/trust-folders.d.ts +1 -0
  165. package/dist/security/trust-folders.js +19 -1
  166. package/dist/security/trust-folders.js.map +1 -1
  167. package/dist/server/websocket/handler.js +15 -5
  168. package/dist/server/websocket/handler.js.map +1 -1
  169. package/dist/skills/eligibility.js +26 -4
  170. package/dist/skills/eligibility.js.map +1 -1
  171. package/dist/tasks/background-tasks.js +5 -1
  172. package/dist/tasks/background-tasks.js.map +1 -1
  173. package/dist/tools/apply-patch.d.ts +55 -0
  174. package/dist/tools/apply-patch.js +273 -0
  175. package/dist/tools/apply-patch.js.map +1 -0
  176. package/dist/tools/registry/bash-tools.js +6 -3
  177. package/dist/tools/registry/bash-tools.js.map +1 -1
  178. package/dist/tools/registry/misc-tools.js +1 -2
  179. package/dist/tools/registry/misc-tools.js.map +1 -1
  180. package/dist/tools/registry/search-tools.js +1 -1
  181. package/dist/tools/registry/search-tools.js.map +1 -1
  182. package/dist/tools/registry/text-editor-tools.js +1 -1
  183. package/dist/tools/registry/text-editor-tools.js.map +1 -1
  184. package/dist/tools/registry/todo-tools.js +37 -5
  185. package/dist/tools/registry/todo-tools.js.map +1 -1
  186. package/dist/tools/registry/tool-registry.js +5 -4
  187. package/dist/tools/registry/tool-registry.js.map +1 -1
  188. package/dist/tools/registry/web-tools.d.ts +1 -1
  189. package/dist/tools/registry/web-tools.js +28 -8
  190. package/dist/tools/registry/web-tools.js.map +1 -1
  191. package/dist/tools/text-editor.d.ts +1 -1
  192. package/dist/tools/text-editor.js +23 -5
  193. package/dist/tools/text-editor.js.map +1 -1
  194. package/dist/tools/web-search.d.ts +52 -37
  195. package/dist/tools/web-search.js +368 -163
  196. package/dist/tools/web-search.js.map +1 -1
  197. package/dist/ui/components/ChatInterface.d.ts +1 -1
  198. package/dist/utils/head-tail-truncation.d.ts +34 -0
  199. package/dist/utils/head-tail-truncation.js +98 -0
  200. package/dist/utils/head-tail-truncation.js.map +1 -0
  201. package/dist/utils/sanitize.d.ts +5 -0
  202. package/dist/utils/sanitize.js +19 -0
  203. package/dist/utils/sanitize.js.map +1 -1
  204. package/dist/utils/settings-manager.js +4 -4
  205. package/dist/utils/settings-manager.js.map +1 -1
  206. package/package.json +3 -1
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Bash Command Parser (Vibe-inspired)
3
+ *
4
+ * Parses bash commands to extract individual commands from complex
5
+ * expressions (pipelines, chains, subshells, command substitutions).
6
+ *
7
+ * Uses tree-sitter-bash if available, otherwise falls back to a
8
+ * robust regex/state-machine parser that handles common patterns.
9
+ *
10
+ * This is critical for security: `rm -rf / && echo safe` should
11
+ * detect the `rm -rf /` component, not just see "echo safe".
12
+ */
13
+ import { logger } from '../utils/logger.js';
14
+ /**
15
+ * Parse bash commands using a state-machine approach.
16
+ * Handles quotes, escapes, pipelines, chains, subshells.
17
+ */
18
+ function fallbackParse(input, depth = 0) {
19
+ if (depth > 10) {
20
+ return { commands: [{ command: input, args: [], raw: input, connector: null, isSubshell: false }], usedTreeSitter: false, warnings: ['Parse depth limit exceeded'] };
21
+ }
22
+ const commands = [];
23
+ const warnings = [];
24
+ // First, handle `bash -c "..."` and `sh -c "..."` wrapper
25
+ const bashCMatch = input.match(/^(bash|sh|zsh)\s+(-[a-z]*c)\s+(['"])([\s\S]*?)\3\s*$/);
26
+ if (bashCMatch) {
27
+ const innerResult = fallbackParse(bashCMatch[4], depth + 1);
28
+ // Also add the outer shell command
29
+ return {
30
+ commands: [
31
+ { command: bashCMatch[1], args: [bashCMatch[2], bashCMatch[4]], raw: input, connector: null, isSubshell: false },
32
+ ...innerResult.commands.map(c => ({ ...c, isSubshell: true })),
33
+ ],
34
+ usedTreeSitter: false,
35
+ warnings: innerResult.warnings,
36
+ };
37
+ }
38
+ // Tokenize: split on unquoted separators (&&, ||, |, ;)
39
+ const segments = [];
40
+ let current = '';
41
+ let quoteState = 'none';
42
+ let escaped = false;
43
+ let parenDepth = 0;
44
+ for (let i = 0; i < input.length; i++) {
45
+ const ch = input[i];
46
+ const next = input[i + 1];
47
+ if (escaped) {
48
+ current += ch;
49
+ escaped = false;
50
+ continue;
51
+ }
52
+ if (ch === '\\') {
53
+ escaped = true;
54
+ current += ch;
55
+ continue;
56
+ }
57
+ // Quote handling
58
+ if (quoteState === 'none') {
59
+ if (ch === "'") {
60
+ quoteState = 'single';
61
+ current += ch;
62
+ continue;
63
+ }
64
+ if (ch === '"') {
65
+ quoteState = 'double';
66
+ current += ch;
67
+ continue;
68
+ }
69
+ if (ch === '`') {
70
+ quoteState = 'backtick';
71
+ current += ch;
72
+ continue;
73
+ }
74
+ }
75
+ else if (quoteState === 'single' && ch === "'") {
76
+ quoteState = 'none';
77
+ current += ch;
78
+ continue;
79
+ }
80
+ else if (quoteState === 'double' && ch === '"') {
81
+ quoteState = 'none';
82
+ current += ch;
83
+ continue;
84
+ }
85
+ else if (quoteState === 'backtick' && ch === '`') {
86
+ quoteState = 'none';
87
+ current += ch;
88
+ continue;
89
+ }
90
+ if (quoteState !== 'none') {
91
+ current += ch;
92
+ continue;
93
+ }
94
+ // Subshell/group tracking
95
+ if (ch === '(' || ch === '{') {
96
+ parenDepth++;
97
+ current += ch;
98
+ continue;
99
+ }
100
+ if (ch === ')' || ch === '}') {
101
+ parenDepth--;
102
+ current += ch;
103
+ continue;
104
+ }
105
+ if (parenDepth > 0) {
106
+ current += ch;
107
+ continue;
108
+ }
109
+ // Separators (only at top level, outside quotes)
110
+ if (ch === '&' && next === '&') {
111
+ segments.push({ text: current.trim(), connector: '&&' });
112
+ current = '';
113
+ i++; // skip next &
114
+ continue;
115
+ }
116
+ if (ch === '|' && next === '|') {
117
+ segments.push({ text: current.trim(), connector: '||' });
118
+ current = '';
119
+ i++; // skip next |
120
+ continue;
121
+ }
122
+ if (ch === '|') {
123
+ segments.push({ text: current.trim(), connector: '|' });
124
+ current = '';
125
+ continue;
126
+ }
127
+ if (ch === ';') {
128
+ segments.push({ text: current.trim(), connector: ';' });
129
+ current = '';
130
+ continue;
131
+ }
132
+ current += ch;
133
+ }
134
+ if (current.trim()) {
135
+ segments.push({ text: current.trim(), connector: null });
136
+ }
137
+ // Parse each segment into a command
138
+ for (const seg of segments) {
139
+ if (!seg.text)
140
+ continue;
141
+ // Handle command substitution $(...) recursively
142
+ const subCmdMatch = seg.text.match(/\$\((.+)\)/);
143
+ if (subCmdMatch) {
144
+ const innerResult = fallbackParse(subCmdMatch[1], depth + 1);
145
+ commands.push(...innerResult.commands.map(c => ({ ...c, isSubshell: true })));
146
+ }
147
+ // Handle subshell (...)
148
+ const subshellMatch = seg.text.match(/^\((.+)\)$/);
149
+ if (subshellMatch) {
150
+ const innerResult = fallbackParse(subshellMatch[1], depth + 1);
151
+ commands.push(...innerResult.commands.map(c => ({ ...c, isSubshell: true })));
152
+ continue;
153
+ }
154
+ // Strip env var assignments at the start: VAR=value cmd args
155
+ let cmdText = seg.text;
156
+ while (/^\w+=\S*\s/.test(cmdText)) {
157
+ cmdText = cmdText.replace(/^\w+=\S*\s+/, '');
158
+ }
159
+ // Strip redirections from the end for command detection
160
+ const cleanText = cmdText
161
+ .replace(/\s*[0-9]*>[>&]*\s*\S+/g, '')
162
+ .replace(/\s*<\s*\S+/g, '')
163
+ .trim();
164
+ if (!cleanText)
165
+ continue;
166
+ // Split into command and args
167
+ const parts = tokenizeSimple(cleanText);
168
+ if (parts.length === 0)
169
+ continue;
170
+ commands.push({
171
+ command: parts[0],
172
+ args: parts.slice(1),
173
+ raw: seg.text,
174
+ connector: seg.connector,
175
+ isSubshell: false,
176
+ });
177
+ }
178
+ if (quoteState !== 'none') {
179
+ warnings.push(`Unclosed ${quoteState} quote`);
180
+ }
181
+ return { commands, usedTreeSitter: false, warnings };
182
+ }
183
+ /**
184
+ * Simple tokenizer that respects quotes.
185
+ */
186
+ function tokenizeSimple(input) {
187
+ const tokens = [];
188
+ let current = '';
189
+ let inSingle = false;
190
+ let inDouble = false;
191
+ for (let i = 0; i < input.length; i++) {
192
+ const ch = input[i];
193
+ if (ch === "'" && !inDouble) {
194
+ inSingle = !inSingle;
195
+ continue;
196
+ }
197
+ if (ch === '"' && !inSingle) {
198
+ inDouble = !inDouble;
199
+ continue;
200
+ }
201
+ if (ch === ' ' && !inSingle && !inDouble) {
202
+ if (current) {
203
+ tokens.push(current);
204
+ current = '';
205
+ }
206
+ continue;
207
+ }
208
+ current += ch;
209
+ }
210
+ if (current)
211
+ tokens.push(current);
212
+ return tokens;
213
+ }
214
+ // ============================================================================
215
+ // Public API
216
+ // ============================================================================
217
+ /**
218
+ * Parse a bash command string into individual commands.
219
+ * Uses tree-sitter if available, falls back to state-machine parser.
220
+ */
221
+ export function parseBashCommand(input) {
222
+ if (!input || !input.trim()) {
223
+ return { commands: [], usedTreeSitter: false, warnings: [] };
224
+ }
225
+ // Try tree-sitter first (optional dependency)
226
+ try {
227
+ // Dynamic import — only works if tree-sitter + tree-sitter-bash are installed
228
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
229
+ const Parser = require('tree-sitter');
230
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
231
+ const Bash = require('tree-sitter-bash');
232
+ const parser = new Parser();
233
+ parser.setLanguage(Bash);
234
+ const tree = parser.parse(input);
235
+ const commands = extractCommandsFromTree(tree.rootNode, input);
236
+ logger.debug('Parsed bash with tree-sitter', { commandCount: commands.length });
237
+ return { commands, usedTreeSitter: true, warnings: [] };
238
+ }
239
+ catch {
240
+ // tree-sitter not available — use fallback
241
+ }
242
+ return fallbackParse(input);
243
+ }
244
+ /**
245
+ * Extract commands from a tree-sitter AST node.
246
+ */
247
+ function extractCommandsFromTree(node, _source, isSubshell = false) {
248
+ const commands = [];
249
+ function walk(n, subshell) {
250
+ switch (n.type) {
251
+ case 'command': {
252
+ const parts = [];
253
+ for (let i = 0; i < n.childCount; i++) {
254
+ const child = n.child(i);
255
+ if (child.type === 'command_name' || child.type === 'word' || child.type === 'string') {
256
+ parts.push(child.text);
257
+ }
258
+ }
259
+ if (parts.length > 0) {
260
+ commands.push({
261
+ command: parts[0],
262
+ args: parts.slice(1),
263
+ raw: n.text,
264
+ connector: null,
265
+ isSubshell: subshell,
266
+ });
267
+ }
268
+ break;
269
+ }
270
+ case 'pipeline':
271
+ case 'list':
272
+ case 'compound_statement':
273
+ for (let i = 0; i < n.childCount; i++) {
274
+ walk(n.child(i), subshell);
275
+ }
276
+ break;
277
+ case 'subshell':
278
+ case 'command_substitution':
279
+ for (let i = 0; i < n.childCount; i++) {
280
+ walk(n.child(i), true);
281
+ }
282
+ break;
283
+ default:
284
+ for (let i = 0; i < n.childCount; i++) {
285
+ walk(n.child(i), subshell);
286
+ }
287
+ }
288
+ }
289
+ walk(node, isSubshell);
290
+ return commands;
291
+ }
292
+ /**
293
+ * Extract just the command names from a bash string (convenience).
294
+ */
295
+ export function extractCommandNames(input) {
296
+ const result = parseBashCommand(input);
297
+ return result.commands.map(c => c.command);
298
+ }
299
+ /**
300
+ * Check if a bash command string contains any of the given commands.
301
+ */
302
+ export function containsCommand(input, commands) {
303
+ const names = extractCommandNames(input);
304
+ return names.some(name => commands.includes(name));
305
+ }
306
+ /**
307
+ * Check if a bash command string contains dangerous commands.
308
+ */
309
+ export function containsDangerousCommand(input) {
310
+ const DANGEROUS = [
311
+ 'rm', 'rmdir', 'mkfs', 'dd', 'fdisk', 'parted',
312
+ 'shutdown', 'reboot', 'poweroff', 'halt',
313
+ 'kill', 'killall', 'pkill',
314
+ 'chmod', 'chown', 'chgrp',
315
+ 'iptables', 'ip6tables', 'nft',
316
+ 'useradd', 'userdel', 'usermod', 'groupadd',
317
+ 'mount', 'umount',
318
+ 'systemctl', 'service',
319
+ 'crontab',
320
+ ];
321
+ const result = parseBashCommand(input);
322
+ const found = result.commands
323
+ .map(c => c.command)
324
+ .filter(name => DANGEROUS.includes(name));
325
+ return { dangerous: found.length > 0, commands: [...new Set(found)] };
326
+ }
327
+ //# sourceMappingURL=bash-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bash-parser.js","sourceRoot":"","sources":["../../src/security/bash-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Skill Code Scanner (OpenClaw-inspired)
3
+ *
4
+ * Static analysis of skill files for dangerous patterns.
5
+ * Scans SKILL.md files and any referenced code for security issues.
6
+ */
7
+ export type FindingSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
8
+ export interface ScanFinding {
9
+ severity: FindingSeverity;
10
+ pattern: string;
11
+ description: string;
12
+ file: string;
13
+ line: number;
14
+ evidence: string;
15
+ }
16
+ export interface ScanResult {
17
+ file: string;
18
+ findings: ScanFinding[];
19
+ scannedAt: number;
20
+ }
21
+ /**
22
+ * Scan a single file for dangerous patterns.
23
+ */
24
+ export declare function scanFile(filePath: string): ScanResult;
25
+ /**
26
+ * Scan a directory of skill files recursively.
27
+ */
28
+ export declare function scanDirectory(dirPath: string): ScanResult[];
29
+ /**
30
+ * Scan all skill locations (bundled, managed, workspace).
31
+ */
32
+ export declare function scanAllSkills(projectRoot?: string): ScanResult[];
33
+ /**
34
+ * Format scan results as a human-readable report.
35
+ */
36
+ export declare function formatScanReport(results: ScanResult[]): string;
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Skill Code Scanner (OpenClaw-inspired)
3
+ *
4
+ * Static analysis of skill files for dangerous patterns.
5
+ * Scans SKILL.md files and any referenced code for security issues.
6
+ */
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { logger } from '../utils/logger.js';
10
+ const DANGEROUS_PATTERNS = [
11
+ // Code execution
12
+ { pattern: /\beval\s*\(/, severity: 'critical', description: 'Dynamic code execution via eval()', name: 'eval' },
13
+ { pattern: /\bnew\s+Function\s*\(/, severity: 'critical', description: 'Dynamic function creation', name: 'new-function' },
14
+ { pattern: /\bchild_process\b/, severity: 'high', description: 'Child process module usage', name: 'child_process' },
15
+ { pattern: /\bexecSync\s*\(/, severity: 'high', description: 'Synchronous command execution', name: 'execSync' },
16
+ { pattern: /\bexecFile\s*\(/, severity: 'high', description: 'File execution', name: 'execFile' },
17
+ { pattern: /\bspawn\s*\(/, severity: 'medium', description: 'Process spawning', name: 'spawn' },
18
+ { pattern: /\bexec\s*\(/, severity: 'high', description: 'Command execution', name: 'exec' },
19
+ // File system dangers
20
+ { pattern: /\brm\s+-rf\b/, severity: 'critical', description: 'Recursive force delete', name: 'rm-rf' },
21
+ { pattern: /\bunlinkSync\s*\(/, severity: 'medium', description: 'Synchronous file deletion', name: 'unlinkSync' },
22
+ { pattern: /\bwriteFileSync\s*\(/, severity: 'low', description: 'Synchronous file write', name: 'writeFileSync' },
23
+ { pattern: /\brmdirSync\s*\(/, severity: 'medium', description: 'Directory removal', name: 'rmdirSync' },
24
+ // Network
25
+ { pattern: /\bfetch\s*\(\s*['"`]http/, severity: 'medium', description: 'External HTTP request', name: 'fetch-http' },
26
+ { pattern: /\baxios\b/, severity: 'low', description: 'HTTP client library usage', name: 'axios' },
27
+ { pattern: /\brequire\s*\(\s*['"`]https?['"`]\s*\)/, severity: 'medium', description: 'HTTP module import', name: 'http-require' },
28
+ { pattern: /\bWebSocket\b/, severity: 'medium', description: 'WebSocket usage', name: 'websocket' },
29
+ // Dynamic imports
30
+ { pattern: /\brequire\s*\([^'"`]/, severity: 'high', description: 'Dynamic require with variable', name: 'dynamic-require' },
31
+ { pattern: /\bimport\s*\([^'"`]/, severity: 'high', description: 'Dynamic import with variable', name: 'dynamic-import' },
32
+ // Environment/secrets
33
+ { pattern: /process\.env\[/, severity: 'low', description: 'Dynamic environment variable access', name: 'env-dynamic' },
34
+ { pattern: /\b(API_KEY|SECRET|PASSWORD|TOKEN)\b/i, severity: 'info', description: 'Possible secret reference', name: 'secret-ref' },
35
+ // Prototype pollution
36
+ { pattern: /__proto__/, severity: 'high', description: 'Prototype pollution risk', name: 'proto' },
37
+ { pattern: /\bconstructor\s*\[/, severity: 'high', description: 'Constructor access via bracket notation', name: 'constructor-bracket' },
38
+ // Shell injection
39
+ { pattern: /`\$\{.*\}`/, severity: 'medium', description: 'Template literal with interpolation (potential injection)', name: 'template-injection' },
40
+ { pattern: /\$\(.*\)/, severity: 'medium', description: 'Shell command substitution', name: 'shell-subst' },
41
+ ];
42
+ /**
43
+ * Scan a single file for dangerous patterns.
44
+ */
45
+ export function scanFile(filePath) {
46
+ const findings = [];
47
+ try {
48
+ const content = fs.readFileSync(filePath, 'utf-8');
49
+ const lines = content.split('\n');
50
+ for (let i = 0; i < lines.length; i++) {
51
+ const line = lines[i];
52
+ const lineNum = i + 1;
53
+ // Skip markdown comments and frontmatter delimiters
54
+ if (line.trim().startsWith('<!--') || line.trim() === '---')
55
+ continue;
56
+ for (const dp of DANGEROUS_PATTERNS) {
57
+ if (dp.pattern.test(line)) {
58
+ findings.push({
59
+ severity: dp.severity,
60
+ pattern: dp.name,
61
+ description: dp.description,
62
+ file: filePath,
63
+ line: lineNum,
64
+ evidence: line.trim().slice(0, 120),
65
+ });
66
+ }
67
+ }
68
+ }
69
+ }
70
+ catch (error) {
71
+ logger.debug(`Failed to scan file: ${filePath}`, { error });
72
+ }
73
+ return {
74
+ file: filePath,
75
+ findings,
76
+ scannedAt: Date.now(),
77
+ };
78
+ }
79
+ /**
80
+ * Scan a directory of skill files recursively.
81
+ */
82
+ export function scanDirectory(dirPath) {
83
+ const results = [];
84
+ if (!fs.existsSync(dirPath))
85
+ return results;
86
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
87
+ for (const entry of entries) {
88
+ const fullPath = path.join(dirPath, entry.name);
89
+ if (entry.isDirectory()) {
90
+ results.push(...scanDirectory(fullPath));
91
+ }
92
+ else if (entry.name.endsWith('.skill.md') ||
93
+ entry.name === 'SKILL.md' ||
94
+ entry.name.endsWith('.ts') ||
95
+ entry.name.endsWith('.js')) {
96
+ const result = scanFile(fullPath);
97
+ if (result.findings.length > 0) {
98
+ results.push(result);
99
+ }
100
+ }
101
+ }
102
+ return results;
103
+ }
104
+ /**
105
+ * Scan all skill locations (bundled, managed, workspace).
106
+ */
107
+ export function scanAllSkills(projectRoot = process.cwd()) {
108
+ const skillDirs = [
109
+ path.join(projectRoot, '.codebuddy', 'skills', 'bundled'),
110
+ path.join(projectRoot, '.codebuddy', 'skills', 'managed'),
111
+ path.join(projectRoot, '.codebuddy', 'skills', 'workspace'),
112
+ ];
113
+ const results = [];
114
+ for (const dir of skillDirs) {
115
+ results.push(...scanDirectory(dir));
116
+ }
117
+ return results;
118
+ }
119
+ /**
120
+ * Format scan results as a human-readable report.
121
+ */
122
+ export function formatScanReport(results) {
123
+ if (results.length === 0) {
124
+ return 'Skill scan: No security issues found.';
125
+ }
126
+ const allFindings = results.flatMap(r => r.findings);
127
+ const bySeverity = {
128
+ critical: allFindings.filter(f => f.severity === 'critical'),
129
+ high: allFindings.filter(f => f.severity === 'high'),
130
+ medium: allFindings.filter(f => f.severity === 'medium'),
131
+ low: allFindings.filter(f => f.severity === 'low'),
132
+ info: allFindings.filter(f => f.severity === 'info'),
133
+ };
134
+ const lines = [];
135
+ lines.push(`Skill Security Scan: ${allFindings.length} findings in ${results.length} files`);
136
+ lines.push(` Critical: ${bySeverity.critical.length} | High: ${bySeverity.high.length} | Medium: ${bySeverity.medium.length} | Low: ${bySeverity.low.length} | Info: ${bySeverity.info.length}`);
137
+ lines.push('');
138
+ for (const result of results) {
139
+ lines.push(`${path.basename(result.file)}:`);
140
+ for (const finding of result.findings) {
141
+ const sev = finding.severity.toUpperCase().padEnd(8);
142
+ lines.push(` [${sev}] L${finding.line}: ${finding.description}`);
143
+ lines.push(` ${finding.evidence}`);
144
+ }
145
+ lines.push('');
146
+ }
147
+ return lines.join('\n');
148
+ }
149
+ //# sourceMappingURL=skill-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-scanner.js","sourceRoot":"","sources":["../../src/security/skill-scanner.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@
10
10
  export declare class TrustFolderManager {
11
11
  private trustedFolders;
12
12
  private enforcementEnabled;
13
+ private readonly isTestMode;
13
14
  constructor();
14
15
  /**
15
16
  * Check if a given path is within a trusted directory.
@@ -33,7 +33,13 @@ const ALWAYS_BLOCKED = [
33
33
  export class TrustFolderManager {
34
34
  trustedFolders = new Set();
35
35
  enforcementEnabled = true;
36
+ isTestMode = process.env.NODE_ENV === 'test';
36
37
  constructor() {
38
+ if (this.isTestMode) {
39
+ // Keep tests hermetic: avoid mutating user config and disable enforcement by default.
40
+ this.enforcementEnabled = false;
41
+ return;
42
+ }
37
43
  this.load();
38
44
  }
39
45
  /**
@@ -42,7 +48,13 @@ export class TrustFolderManager {
42
48
  isTrusted(targetPath) {
43
49
  if (!this.enforcementEnabled)
44
50
  return true;
45
- const resolved = path.resolve(targetPath);
51
+ let resolved;
52
+ try {
53
+ resolved = fs.realpathSync(targetPath);
54
+ }
55
+ catch {
56
+ resolved = path.resolve(targetPath);
57
+ }
46
58
  // Check if the path is within any trusted folder
47
59
  for (const trusted of this.trustedFolders) {
48
60
  if (resolved === trusted || resolved.startsWith(trusted + path.sep)) {
@@ -106,6 +118,9 @@ export class TrustFolderManager {
106
118
  return this.enforcementEnabled;
107
119
  }
108
120
  load() {
121
+ if (this.isTestMode) {
122
+ return;
123
+ }
109
124
  try {
110
125
  if (fs.existsSync(TRUST_FILE)) {
111
126
  const data = JSON.parse(fs.readFileSync(TRUST_FILE, 'utf-8'));
@@ -126,6 +141,9 @@ export class TrustFolderManager {
126
141
  }
127
142
  }
128
143
  save() {
144
+ if (this.isTestMode) {
145
+ return;
146
+ }
129
147
  try {
130
148
  if (!fs.existsSync(CONFIG_DIR)) {
131
149
  fs.mkdirSync(CONFIG_DIR, { recursive: true });
@@ -1 +1 @@
1
- {"version":3,"file":"trust-folders.js","sourceRoot":"","sources":["../../src/security/trust-folders.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACzD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,cAAc,GAAa;IAC/B,GAAG;IACH,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,EAAE,CAAC,OAAO,EAAE;IACZ,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC;IAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC;IACjC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,OAAO,kBAAkB;IACrB,cAAc,GAAgB,IAAI,GAAG,EAAE,CAAC;IACxC,kBAAkB,GAAY,IAAI,CAAC;IAE3C;QACE,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,UAAkB;QAC1B,IAAI,CAAC,IAAI,CAAC,kBAAkB;YAAE,OAAO,IAAI,CAAC;QAE1C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAE1C,iDAAiD;QACjD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1C,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpE,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC1B,IAAI,QAAQ,KAAK,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,OAAe;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvC,OAAO,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,OAAe;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,mCAAmC,QAAQ,EAAE,CAAC,CAAC;YAC3D,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,OAAe;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,OAAO;YAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,iBAAiB;QACf,OAAO,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,OAAgB;QAC7B,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,oBAAoB;QAClB,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACjC,CAAC;IAEO,IAAI;QACV,IAAI,CAAC;YACH,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;gBAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;oBAChC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;wBAClC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;4BAC1D,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;wBAChD,CAAC;oBACH,CAAC;gBACH,CAAC;gBACD,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;oBAC1C,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,WAAW,CAAC;gBAC7C,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAEO,IAAI;QACV,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC/B,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,CAAC;YACD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC;gBAC1C,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC;gBACjC,WAAW,EAAE,IAAI,CAAC,kBAAkB;aACrC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;CACF;AAED,YAAY;AACZ,IAAI,mBAAmB,GAA8B,IAAI,CAAC;AAE1D,MAAM,UAAU,qBAAqB;IACnC,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,mBAAmB,GAAG,IAAI,kBAAkB,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,mBAAmB,GAAG,IAAI,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"trust-folders.js","sourceRoot":"","sources":["../../src/security/trust-folders.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACzD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,cAAc,GAAa;IAC/B,GAAG;IACH,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,EAAE,CAAC,OAAO,EAAE;IACZ,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC;IAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC;IACjC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,OAAO,kBAAkB;IACrB,cAAc,GAAgB,IAAI,GAAG,EAAE,CAAC;IACxC,kBAAkB,GAAY,IAAI,CAAC;IAC1B,UAAU,GAAY,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC;IAEvE;QACE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,sFAAsF;YACtF,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;YAChC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,UAAkB;QAC1B,IAAI,CAAC,IAAI,CAAC,kBAAkB;YAAE,OAAO,IAAI,CAAC;QAE1C,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC;QAED,iDAAiD;QACjD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1C,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpE,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC1B,IAAI,QAAQ,KAAK,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,OAAe;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvC,OAAO,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,OAAe;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,mCAAmC,QAAQ,EAAE,CAAC,CAAC;YAC3D,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,OAAe;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,OAAO;YAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,iBAAiB;QACf,OAAO,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,OAAgB;QAC7B,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,oBAAoB;QAClB,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACjC,CAAC;IAEO,IAAI;QACV,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;gBAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;oBAChC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;wBAClC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;4BAC1D,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;wBAChD,CAAC;oBACH,CAAC;gBACH,CAAC;gBACD,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;oBAC1C,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,WAAW,CAAC;gBAC7C,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAEO,IAAI;QACV,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC/B,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,CAAC;YACD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC;gBAC1C,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC;gBACjC,WAAW,EAAE,IAAI,CAAC,kBAAkB;aACrC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;CACF;AAED,YAAY;AACZ,IAAI,mBAAmB,GAA8B,IAAI,CAAC;AAE1D,MAAM,UAAU,qBAAqB;IACnC,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,mBAAmB,GAAG,IAAI,kBAAkB,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,mBAAmB,GAAG,IAAI,CAAC;AAC7B,CAAC"}
@@ -114,10 +114,15 @@ messageHandlers.set('chat', async (ws, state, payload) => {
114
114
  }
115
115
  }
116
116
  try {
117
- // Lazy load agent
117
+ // Lazy load agent (with mutex to prevent duplicate creation)
118
118
  if (!state.agent) {
119
- const { CodeBuddyAgent } = await import('../../agent/codebuddy-agent.js');
120
- state.agent = new CodeBuddyAgent(process.env.GROK_API_KEY || '', process.env.GROK_BASE_URL, model || process.env.GROK_MODEL || 'grok-3-latest');
119
+ if (!state.agentInitializing) {
120
+ state.agentInitializing = (async () => {
121
+ const { CodeBuddyAgent } = await import('../../agent/codebuddy-agent.js');
122
+ state.agent = new CodeBuddyAgent(process.env.GROK_API_KEY || '', process.env.GROK_BASE_URL, model || process.env.GROK_MODEL || 'grok-3-latest');
123
+ })();
124
+ }
125
+ await state.agentInitializing;
121
126
  }
122
127
  if (stream) {
123
128
  state.streaming = true;
@@ -221,8 +226,13 @@ messageHandlers.set('execute_tool', async (ws, state, payload) => {
221
226
  }
222
227
  try {
223
228
  if (!state.agent) {
224
- const { CodeBuddyAgent } = await import('../../agent/codebuddy-agent.js');
225
- state.agent = new CodeBuddyAgent(process.env.GROK_API_KEY || '', process.env.GROK_BASE_URL, process.env.GROK_MODEL || 'grok-3-latest');
229
+ if (!state.agentInitializing) {
230
+ state.agentInitializing = (async () => {
231
+ const { CodeBuddyAgent } = await import('../../agent/codebuddy-agent.js');
232
+ state.agent = new CodeBuddyAgent(process.env.GROK_API_KEY || '', process.env.GROK_BASE_URL, process.env.GROK_MODEL || 'grok-3-latest');
233
+ })();
234
+ }
235
+ await state.agentInitializing;
226
236
  }
227
237
  const result = await state.agent.executeTool(name, parameters || {});
228
238
  send(ws, {