neuro-cli 4.1.0

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 (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +510 -0
  3. package/dist/advisor/advisor.d.ts +50 -0
  4. package/dist/advisor/advisor.js +178 -0
  5. package/dist/agents/base.d.ts +62 -0
  6. package/dist/agents/base.js +215 -0
  7. package/dist/agents/orchestrator.d.ts +46 -0
  8. package/dist/agents/orchestrator.js +192 -0
  9. package/dist/agents/team.d.ts +51 -0
  10. package/dist/agents/team.js +210 -0
  11. package/dist/api/models.d.ts +23 -0
  12. package/dist/api/models.js +514 -0
  13. package/dist/api/ollama.d.ts +153 -0
  14. package/dist/api/ollama.js +751 -0
  15. package/dist/api/openrouter.d.ts +55 -0
  16. package/dist/api/openrouter.js +223 -0
  17. package/dist/commands/commands.d.ts +43 -0
  18. package/dist/commands/commands.js +308 -0
  19. package/dist/config/config.d.ts +8 -0
  20. package/dist/config/config.js +311 -0
  21. package/dist/context/compaction.d.ts +54 -0
  22. package/dist/context/compaction.js +251 -0
  23. package/dist/context/custom-agents.d.ts +107 -0
  24. package/dist/context/custom-agents.js +397 -0
  25. package/dist/context/custom-tools.d.ts +120 -0
  26. package/dist/context/custom-tools.js +564 -0
  27. package/dist/context/git-checkpoint.d.ts +52 -0
  28. package/dist/context/git-checkpoint.js +240 -0
  29. package/dist/context/neuro-md.d.ts +48 -0
  30. package/dist/context/neuro-md.js +202 -0
  31. package/dist/context/neuroignore.d.ts +102 -0
  32. package/dist/context/neuroignore.js +441 -0
  33. package/dist/context/repo-map.d.ts +38 -0
  34. package/dist/context/repo-map.js +220 -0
  35. package/dist/context/skill-standard.d.ts +262 -0
  36. package/dist/context/skill-standard.js +1156 -0
  37. package/dist/context/skill-system.d.ts +75 -0
  38. package/dist/context/skill-system.js +578 -0
  39. package/dist/context/tree-sitter.d.ts +99 -0
  40. package/dist/context/tree-sitter.js +1956 -0
  41. package/dist/core/acp.d.ts +325 -0
  42. package/dist/core/acp.js +1498 -0
  43. package/dist/core/api-server.d.ts +143 -0
  44. package/dist/core/api-server.js +550 -0
  45. package/dist/core/approval.d.ts +81 -0
  46. package/dist/core/approval.js +432 -0
  47. package/dist/core/auto-compact.d.ts +127 -0
  48. package/dist/core/auto-compact.js +436 -0
  49. package/dist/core/auto-mode.d.ts +232 -0
  50. package/dist/core/auto-mode.js +831 -0
  51. package/dist/core/background-session.d.ts +166 -0
  52. package/dist/core/background-session.js +696 -0
  53. package/dist/core/cicd.d.ts +257 -0
  54. package/dist/core/cicd.js +1443 -0
  55. package/dist/core/cloud-sync.d.ts +156 -0
  56. package/dist/core/cloud-sync.js +582 -0
  57. package/dist/core/code-review.d.ts +132 -0
  58. package/dist/core/code-review.js +1191 -0
  59. package/dist/core/completion.d.ts +49 -0
  60. package/dist/core/completion.js +384 -0
  61. package/dist/core/context.d.ts +38 -0
  62. package/dist/core/context.js +144 -0
  63. package/dist/core/diff-preview.d.ts +35 -0
  64. package/dist/core/diff-preview.js +173 -0
  65. package/dist/core/doom-loop.d.ts +51 -0
  66. package/dist/core/doom-loop.js +179 -0
  67. package/dist/core/engine.d.ts +183 -0
  68. package/dist/core/engine.js +942 -0
  69. package/dist/core/extended-thinking.d.ts +103 -0
  70. package/dist/core/extended-thinking.js +269 -0
  71. package/dist/core/fallback.d.ts +54 -0
  72. package/dist/core/fallback.js +104 -0
  73. package/dist/core/git-worktree.d.ts +139 -0
  74. package/dist/core/git-worktree.js +614 -0
  75. package/dist/core/headless.d.ts +30 -0
  76. package/dist/core/headless.js +101 -0
  77. package/dist/core/i18n.d.ts +83 -0
  78. package/dist/core/i18n.js +739 -0
  79. package/dist/core/linting.d.ts +129 -0
  80. package/dist/core/linting.js +699 -0
  81. package/dist/core/model-router.d.ts +109 -0
  82. package/dist/core/model-router.js +581 -0
  83. package/dist/core/multi-model.d.ts +243 -0
  84. package/dist/core/multi-model.js +1099 -0
  85. package/dist/core/multi-session.d.ts +144 -0
  86. package/dist/core/multi-session.js +442 -0
  87. package/dist/core/multimodal.d.ts +125 -0
  88. package/dist/core/multimodal.js +286 -0
  89. package/dist/core/observability.d.ts +93 -0
  90. package/dist/core/observability.js +737 -0
  91. package/dist/core/os-sandbox.d.ts +122 -0
  92. package/dist/core/os-sandbox.js +1193 -0
  93. package/dist/core/outcome-grading.d.ts +228 -0
  94. package/dist/core/outcome-grading.js +1123 -0
  95. package/dist/core/output-styles.d.ts +57 -0
  96. package/dist/core/output-styles.js +382 -0
  97. package/dist/core/parallel-agents.d.ts +183 -0
  98. package/dist/core/parallel-agents.js +563 -0
  99. package/dist/core/plugin-bundle.d.ts +236 -0
  100. package/dist/core/plugin-bundle.js +887 -0
  101. package/dist/core/plugin-sdk.d.ts +139 -0
  102. package/dist/core/plugin-sdk.js +273 -0
  103. package/dist/core/prompt-cache.d.ts +163 -0
  104. package/dist/core/prompt-cache.js +599 -0
  105. package/dist/core/sandbox.d.ts +127 -0
  106. package/dist/core/sandbox.js +369 -0
  107. package/dist/core/scheduled-tasks.d.ts +151 -0
  108. package/dist/core/scheduled-tasks.js +502 -0
  109. package/dist/core/security-scanner.d.ts +160 -0
  110. package/dist/core/security-scanner.js +1494 -0
  111. package/dist/core/session.d.ts +83 -0
  112. package/dist/core/session.js +269 -0
  113. package/dist/core/shell-completion.d.ts +51 -0
  114. package/dist/core/shell-completion.js +674 -0
  115. package/dist/core/smart-monitor.d.ts +146 -0
  116. package/dist/core/smart-monitor.js +1199 -0
  117. package/dist/core/spec-driven.d.ts +233 -0
  118. package/dist/core/spec-driven.js +1485 -0
  119. package/dist/core/spending-warnings.d.ts +123 -0
  120. package/dist/core/spending-warnings.js +456 -0
  121. package/dist/core/sub-agent.d.ts +298 -0
  122. package/dist/core/sub-agent.js +1023 -0
  123. package/dist/core/telemetry.d.ts +157 -0
  124. package/dist/core/telemetry.js +412 -0
  125. package/dist/core/terminal-ux.d.ts +134 -0
  126. package/dist/core/terminal-ux.js +649 -0
  127. package/dist/core/testing.d.ts +146 -0
  128. package/dist/core/testing.js +1200 -0
  129. package/dist/core/types.d.ts +439 -0
  130. package/dist/core/types.js +6 -0
  131. package/dist/core/undo-redo.d.ts +112 -0
  132. package/dist/core/undo-redo.js +290 -0
  133. package/dist/core/updater.d.ts +159 -0
  134. package/dist/core/updater.js +608 -0
  135. package/dist/core/vim-mode.d.ts +151 -0
  136. package/dist/core/vim-mode.js +771 -0
  137. package/dist/core/voice.d.ts +137 -0
  138. package/dist/core/voice.js +538 -0
  139. package/dist/core/web-dashboard.d.ts +109 -0
  140. package/dist/core/web-dashboard.js +484 -0
  141. package/dist/hooks/hooks.d.ts +74 -0
  142. package/dist/hooks/hooks.js +160 -0
  143. package/dist/hooks/llm-evaluator.d.ts +165 -0
  144. package/dist/hooks/llm-evaluator.js +560 -0
  145. package/dist/index.d.ts +3 -0
  146. package/dist/index.js +1186 -0
  147. package/dist/lsp/lsp-manager.d.ts +63 -0
  148. package/dist/lsp/lsp-manager.js +351 -0
  149. package/dist/mcp/client.d.ts +133 -0
  150. package/dist/mcp/client.js +684 -0
  151. package/dist/mcp/mcp-apps.d.ts +70 -0
  152. package/dist/mcp/mcp-apps.js +1007 -0
  153. package/dist/tools/bash.d.ts +5 -0
  154. package/dist/tools/bash.js +195 -0
  155. package/dist/tools/browser.d.ts +92 -0
  156. package/dist/tools/browser.js +1570 -0
  157. package/dist/tools/extended.d.ts +6 -0
  158. package/dist/tools/extended.js +191 -0
  159. package/dist/tools/file.d.ts +10 -0
  160. package/dist/tools/file.js +382 -0
  161. package/dist/tools/github.d.ts +389 -0
  162. package/dist/tools/github.js +759 -0
  163. package/dist/tools/index.d.ts +9 -0
  164. package/dist/tools/index.js +40 -0
  165. package/dist/tools/memory.d.ts +6 -0
  166. package/dist/tools/memory.js +197 -0
  167. package/dist/tools/registry.d.ts +29 -0
  168. package/dist/tools/registry.js +64 -0
  169. package/dist/tools/web.d.ts +6 -0
  170. package/dist/tools/web.js +150 -0
  171. package/dist/ui/renderer.d.ts +97 -0
  172. package/dist/ui/renderer.js +279 -0
  173. package/dist/ui/theme.d.ts +27 -0
  174. package/dist/ui/theme.js +106 -0
  175. package/package.json +83 -0
@@ -0,0 +1,107 @@
1
+ export interface CustomAgentDefinition {
2
+ name: string;
3
+ description: string;
4
+ systemPrompt: string;
5
+ model?: string;
6
+ temperature?: number;
7
+ maxTokens?: number;
8
+ tools?: string[];
9
+ maxIterations?: number;
10
+ tags?: string[];
11
+ isCustom: boolean;
12
+ source: string;
13
+ }
14
+ export declare class CustomAgentLoader {
15
+ private agents;
16
+ private projectAgentsDir;
17
+ private globalAgentsDir;
18
+ private watchers;
19
+ private onChange?;
20
+ constructor(projectRoot: string, onChange?: () => void);
21
+ /**
22
+ * Discover all custom agents from project and global directories.
23
+ * Global agents are loaded first so project-level agents with the same
24
+ * name take precedence (last-write-wins).
25
+ */
26
+ discover(): CustomAgentDefinition[];
27
+ /**
28
+ * Get a single agent definition by name.
29
+ */
30
+ get(name: string): CustomAgentDefinition | undefined;
31
+ /**
32
+ * Get all discovered custom agent definitions.
33
+ */
34
+ getAll(): CustomAgentDefinition[];
35
+ /**
36
+ * Convert a CustomAgentDefinition into a standard AgentConfig object
37
+ * merged with default values.
38
+ */
39
+ toAgentConfig(def: CustomAgentDefinition, defaultModel: string): {
40
+ name: string;
41
+ description: string;
42
+ systemPrompt: string;
43
+ model: string;
44
+ temperature: number;
45
+ maxTokens: number;
46
+ tools: string[];
47
+ maxIterations: number;
48
+ isCustom: boolean;
49
+ tags: string[];
50
+ };
51
+ /**
52
+ * Print a formatted list of discovered custom agents to stdout.
53
+ */
54
+ printAgents(): void;
55
+ /**
56
+ * Enable file watching for hot-reload of agent definitions.
57
+ * Calls the onChange callback (or re-discovers) when files change.
58
+ */
59
+ enableWatch(): void;
60
+ /**
61
+ * Disable all file watchers.
62
+ */
63
+ disableWatch(): void;
64
+ /**
65
+ * Load agent definitions from a single directory.
66
+ */
67
+ private loadFromDirectory;
68
+ /**
69
+ * Parse a markdown file with YAML frontmatter into a CustomAgentDefinition.
70
+ *
71
+ * Expected format:
72
+ * ---
73
+ * name: my-agent
74
+ * description: A helpful description
75
+ * model: claude-sonnet-4-20250514
76
+ * temperature: 0.5
77
+ * maxTokens: 2048
78
+ * tools:
79
+ * - read
80
+ * - write
81
+ * maxIterations: 5
82
+ * tags:
83
+ * - coding
84
+ * - review
85
+ * ---
86
+ * You are a specialized code review assistant...
87
+ */
88
+ private parseAgentFile;
89
+ /**
90
+ * Minimal YAML parser that handles the flat key-value structures and
91
+ * simple arrays typical of agent frontmatter.
92
+ *
93
+ * Supports:
94
+ * key: value
95
+ * key: "quoted value"
96
+ * key:
97
+ * - item1
98
+ * - item2
99
+ * key:
100
+ * - "quoted item"
101
+ *
102
+ * Does NOT support:
103
+ * Nested objects, multiline strings (|, >), anchors, etc.
104
+ */
105
+ private parseSimpleYaml;
106
+ }
107
+ //# sourceMappingURL=custom-agents.d.ts.map
@@ -0,0 +1,397 @@
1
+ // ============================================================
2
+ // NeuroCLI - Custom Agents Loader
3
+ // Loads agent definitions from .neuro/agents/ (project) and
4
+ // ~/.neuro/agents/ (global). Supports markdown files with YAML
5
+ // frontmatter, similar to Claude Code's agent system.
6
+ // ============================================================
7
+ import { readFileSync, readdirSync, existsSync, statSync, watchFile, unwatchFile } from 'fs';
8
+ import { join, basename, extname } from 'path';
9
+ import { homedir } from 'os';
10
+ // ---------------------------------------------------------------------------
11
+ // Custom Agent Loader
12
+ // ---------------------------------------------------------------------------
13
+ export class CustomAgentLoader {
14
+ agents;
15
+ projectAgentsDir;
16
+ globalAgentsDir;
17
+ watchers = [];
18
+ onChange;
19
+ constructor(projectRoot, onChange) {
20
+ this.agents = new Map();
21
+ this.projectAgentsDir = join(projectRoot, '.neuro', 'agents');
22
+ this.globalAgentsDir = join(homedir(), '.neuro', 'agents');
23
+ this.onChange = onChange;
24
+ }
25
+ // -------------------------------------------------------------------------
26
+ // Public API
27
+ // -------------------------------------------------------------------------
28
+ /**
29
+ * Discover all custom agents from project and global directories.
30
+ * Global agents are loaded first so project-level agents with the same
31
+ * name take precedence (last-write-wins).
32
+ */
33
+ discover() {
34
+ this.agents.clear();
35
+ // Load global agents first (lower priority)
36
+ this.loadFromDirectory(this.globalAgentsDir);
37
+ // Load project agents second (higher priority, overrides global)
38
+ this.loadFromDirectory(this.projectAgentsDir);
39
+ return this.getAll();
40
+ }
41
+ /**
42
+ * Get a single agent definition by name.
43
+ */
44
+ get(name) {
45
+ return this.agents.get(name);
46
+ }
47
+ /**
48
+ * Get all discovered custom agent definitions.
49
+ */
50
+ getAll() {
51
+ return Array.from(this.agents.values());
52
+ }
53
+ /**
54
+ * Convert a CustomAgentDefinition into a standard AgentConfig object
55
+ * merged with default values.
56
+ */
57
+ toAgentConfig(def, defaultModel) {
58
+ return {
59
+ name: def.name,
60
+ description: def.description,
61
+ systemPrompt: def.systemPrompt,
62
+ model: def.model ?? defaultModel,
63
+ temperature: def.temperature ?? 0.7,
64
+ maxTokens: def.maxTokens ?? 4096,
65
+ tools: def.tools ?? [],
66
+ maxIterations: def.maxIterations ?? 10,
67
+ isCustom: true,
68
+ tags: def.tags ?? [],
69
+ };
70
+ }
71
+ /**
72
+ * Print a formatted list of discovered custom agents to stdout.
73
+ */
74
+ printAgents() {
75
+ const agents = this.getAll();
76
+ if (agents.length === 0) {
77
+ console.log('No custom agents found.');
78
+ console.log('');
79
+ console.log(' Place agent definition files in:');
80
+ console.log(` Project: ${this.projectAgentsDir}/`);
81
+ console.log(` Global: ${this.globalAgentsDir}/`);
82
+ console.log('');
83
+ console.log(' Agent files are markdown with YAML frontmatter:');
84
+ console.log(' ---');
85
+ console.log(' name: my-agent');
86
+ console.log(' description: Does something useful');
87
+ console.log(' ---');
88
+ console.log(' System prompt body goes here...');
89
+ return;
90
+ }
91
+ console.log(`Custom agents (${agents.length}):`);
92
+ console.log('');
93
+ for (const agent of agents) {
94
+ const tags = agent.tags && agent.tags.length > 0
95
+ ? ` [${agent.tags.join(', ')}]`
96
+ : '';
97
+ const model = agent.model ? ` (model: ${agent.model})` : '';
98
+ const source = agent.source.includes('.neuro/agents/')
99
+ ? agent.source.split('.neuro/agents/')[1]
100
+ : agent.source;
101
+ console.log(` ${agent.name}${tags}${model}`);
102
+ console.log(` ${agent.description}`);
103
+ console.log(` source: ${source}`);
104
+ console.log('');
105
+ }
106
+ }
107
+ // -------------------------------------------------------------------------
108
+ // Hot-reload
109
+ // -------------------------------------------------------------------------
110
+ /**
111
+ * Enable file watching for hot-reload of agent definitions.
112
+ * Calls the onChange callback (or re-discovers) when files change.
113
+ */
114
+ enableWatch() {
115
+ this.disableWatch();
116
+ const watchDir = (dir) => {
117
+ if (!existsSync(dir))
118
+ return;
119
+ try {
120
+ const entries = readdirSync(dir);
121
+ for (const entry of entries) {
122
+ const fullPath = join(dir, entry);
123
+ if (!statSync(fullPath).isFile())
124
+ continue;
125
+ const ext = extname(entry).toLowerCase();
126
+ if (ext !== '.md' && ext !== '.markdown')
127
+ continue;
128
+ watchFile(fullPath, { interval: 2000 }, () => {
129
+ // Re-discover on change
130
+ this.discover();
131
+ this.onChange?.();
132
+ });
133
+ this.watchers.push({
134
+ path: fullPath,
135
+ close: () => unwatchFile(fullPath),
136
+ });
137
+ }
138
+ }
139
+ catch {
140
+ // Directory may not be readable; skip silently
141
+ }
142
+ };
143
+ watchDir(this.globalAgentsDir);
144
+ watchDir(this.projectAgentsDir);
145
+ }
146
+ /**
147
+ * Disable all file watchers.
148
+ */
149
+ disableWatch() {
150
+ for (const w of this.watchers) {
151
+ w.close();
152
+ }
153
+ this.watchers = [];
154
+ }
155
+ // -------------------------------------------------------------------------
156
+ // Private helpers
157
+ // -------------------------------------------------------------------------
158
+ /**
159
+ * Load agent definitions from a single directory.
160
+ */
161
+ loadFromDirectory(dir) {
162
+ if (!existsSync(dir))
163
+ return;
164
+ let entries;
165
+ try {
166
+ entries = readdirSync(dir);
167
+ }
168
+ catch {
169
+ // Directory not readable; skip
170
+ return;
171
+ }
172
+ for (const entry of entries) {
173
+ const fullPath = join(dir, entry);
174
+ let stat;
175
+ try {
176
+ stat = statSync(fullPath);
177
+ }
178
+ catch {
179
+ continue;
180
+ }
181
+ if (!stat.isFile())
182
+ continue;
183
+ const ext = extname(entry).toLowerCase();
184
+ if (ext !== '.md' && ext !== '.markdown')
185
+ continue;
186
+ try {
187
+ const content = readFileSync(fullPath, 'utf-8');
188
+ const agent = this.parseAgentFile(content, fullPath);
189
+ if (agent) {
190
+ this.agents.set(agent.name, agent);
191
+ }
192
+ }
193
+ catch (err) {
194
+ console.warn(`[custom-agents] Failed to parse ${fullPath}: ${err instanceof Error ? err.message : String(err)}`);
195
+ }
196
+ }
197
+ }
198
+ /**
199
+ * Parse a markdown file with YAML frontmatter into a CustomAgentDefinition.
200
+ *
201
+ * Expected format:
202
+ * ---
203
+ * name: my-agent
204
+ * description: A helpful description
205
+ * model: claude-sonnet-4-20250514
206
+ * temperature: 0.5
207
+ * maxTokens: 2048
208
+ * tools:
209
+ * - read
210
+ * - write
211
+ * maxIterations: 5
212
+ * tags:
213
+ * - coding
214
+ * - review
215
+ * ---
216
+ * You are a specialized code review assistant...
217
+ */
218
+ parseAgentFile(content, filePath) {
219
+ // Match frontmatter delimited by --- at the start of the file
220
+ const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---/;
221
+ const match = content.match(frontmatterRegex);
222
+ if (!match) {
223
+ console.warn(`[custom-agents] No YAML frontmatter found in ${filePath}`);
224
+ return null;
225
+ }
226
+ const yamlStr = match[1];
227
+ const body = content.slice(match[0].length).trim();
228
+ const raw = this.parseSimpleYaml(yamlStr);
229
+ // Derive name: explicit > filename fallback
230
+ const derivedName = typeof raw.name === 'string' && raw.name.trim()
231
+ ? raw.name.trim()
232
+ : basename(filePath, extname(filePath));
233
+ // Derive description
234
+ const description = typeof raw.description === 'string' && raw.description.trim()
235
+ ? raw.description.trim()
236
+ : '';
237
+ // Validate required fields
238
+ if (!body) {
239
+ console.warn(`[custom-agents] Missing system prompt body in ${filePath}`);
240
+ return null;
241
+ }
242
+ if (!description) {
243
+ console.warn(`[custom-agents] Missing "description" field in frontmatter of ${filePath}`);
244
+ return null;
245
+ }
246
+ // Parse optional fields with type coercion
247
+ const model = typeof raw.model === 'string' ? raw.model : undefined;
248
+ const temperature = typeof raw.temperature === 'number'
249
+ ? raw.temperature
250
+ : typeof raw.temperature === 'string'
251
+ ? parseFloat(raw.temperature)
252
+ : undefined;
253
+ const validTemperature = temperature !== undefined && !isNaN(temperature)
254
+ ? temperature
255
+ : undefined;
256
+ const maxTokens = typeof raw.maxTokens === 'number'
257
+ ? raw.maxTokens
258
+ : typeof raw.maxTokens === 'string'
259
+ ? parseInt(raw.maxTokens, 10)
260
+ : undefined;
261
+ const validMaxTokens = maxTokens !== undefined && !isNaN(maxTokens) && maxTokens > 0
262
+ ? maxTokens
263
+ : undefined;
264
+ const maxIterations = typeof raw.maxIterations === 'number'
265
+ ? raw.maxIterations
266
+ : typeof raw.maxIterations === 'string'
267
+ ? parseInt(raw.maxIterations, 10)
268
+ : undefined;
269
+ const validMaxIterations = maxIterations !== undefined && !isNaN(maxIterations) && maxIterations > 0
270
+ ? maxIterations
271
+ : undefined;
272
+ const tools = Array.isArray(raw.tools)
273
+ ? raw.tools.filter((t) => typeof t === 'string')
274
+ : undefined;
275
+ const tags = Array.isArray(raw.tags)
276
+ ? raw.tags.filter((t) => typeof t === 'string')
277
+ : undefined;
278
+ return {
279
+ name: derivedName,
280
+ description,
281
+ systemPrompt: body,
282
+ model,
283
+ temperature: validTemperature,
284
+ maxTokens: validMaxTokens,
285
+ tools,
286
+ maxIterations: validMaxIterations,
287
+ tags,
288
+ isCustom: true,
289
+ source: filePath,
290
+ };
291
+ }
292
+ /**
293
+ * Minimal YAML parser that handles the flat key-value structures and
294
+ * simple arrays typical of agent frontmatter.
295
+ *
296
+ * Supports:
297
+ * key: value
298
+ * key: "quoted value"
299
+ * key:
300
+ * - item1
301
+ * - item2
302
+ * key:
303
+ * - "quoted item"
304
+ *
305
+ * Does NOT support:
306
+ * Nested objects, multiline strings (|, >), anchors, etc.
307
+ */
308
+ parseSimpleYaml(yaml) {
309
+ const result = {};
310
+ const lines = yaml.split(/\r?\n/);
311
+ let currentKey = null;
312
+ let currentArray = null;
313
+ const unquote = (s) => {
314
+ const trimmed = s.trim();
315
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
316
+ (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
317
+ return trimmed.slice(1, -1);
318
+ }
319
+ return trimmed;
320
+ };
321
+ const coerceValue = (s) => {
322
+ const trimmed = s.trim();
323
+ if (trimmed === '')
324
+ return null;
325
+ // Quoted strings stay as strings
326
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
327
+ (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
328
+ return unquote(trimmed);
329
+ }
330
+ // Booleans
331
+ if (trimmed.toLowerCase() === 'true')
332
+ return true;
333
+ if (trimmed.toLowerCase() === 'false')
334
+ return false;
335
+ // Null
336
+ if (trimmed.toLowerCase() === 'null' || trimmed === '~')
337
+ return null;
338
+ // Numbers
339
+ const asNum = Number(trimmed);
340
+ if (trimmed !== '' && !isNaN(asNum) && isFinite(asNum)) {
341
+ return asNum;
342
+ }
343
+ return trimmed;
344
+ };
345
+ for (const line of lines) {
346
+ // Skip empty lines and comments
347
+ if (/^\s*$/.test(line) || /^\s*#/.test(line)) {
348
+ continue;
349
+ }
350
+ // Array item: " - value"
351
+ const arrayMatch = line.match(/^(\s+)-\s+(.*)$/);
352
+ if (arrayMatch && currentKey !== null && currentArray !== null) {
353
+ const itemValue = coerceValue(arrayMatch[2]);
354
+ if (itemValue !== null) {
355
+ currentArray.push(itemValue);
356
+ }
357
+ continue;
358
+ }
359
+ // Key-value pair: "key: value"
360
+ const kvMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\s*:\s*(.*)$/);
361
+ if (kvMatch) {
362
+ // Flush previous array if any
363
+ if (currentKey !== null && currentArray !== null) {
364
+ result[currentKey] = currentArray;
365
+ }
366
+ currentKey = kvMatch[1];
367
+ const rawValue = kvMatch[2].trim();
368
+ // Key with no value on this line -> start array
369
+ if (rawValue === '') {
370
+ currentArray = [];
371
+ }
372
+ else {
373
+ const value = coerceValue(rawValue);
374
+ result[currentKey] = value;
375
+ currentArray = null;
376
+ }
377
+ continue;
378
+ }
379
+ // Fallback: treat indented lines under an array key as array items
380
+ if (currentKey !== null && currentArray !== null) {
381
+ const stripped = line.trim();
382
+ if (stripped.startsWith('- ')) {
383
+ const itemValue = coerceValue(stripped.slice(2));
384
+ if (itemValue !== null) {
385
+ currentArray.push(itemValue);
386
+ }
387
+ }
388
+ }
389
+ }
390
+ // Flush last array
391
+ if (currentKey !== null && currentArray !== null) {
392
+ result[currentKey] = currentArray;
393
+ }
394
+ return result;
395
+ }
396
+ }
397
+ //# sourceMappingURL=custom-agents.js.map
@@ -0,0 +1,120 @@
1
+ export interface CustomToolDefinition {
2
+ name: string;
3
+ description: string;
4
+ parameters: {
5
+ type: 'object';
6
+ properties: Record<string, {
7
+ type: string;
8
+ description: string;
9
+ enum?: string[];
10
+ }>;
11
+ required: string[];
12
+ };
13
+ risk?: 'low' | 'medium' | 'high';
14
+ execute?: string;
15
+ command?: string;
16
+ source: string;
17
+ }
18
+ export declare class CustomToolLoader {
19
+ private tools;
20
+ private projectToolsDir;
21
+ private globalToolsDir;
22
+ constructor(projectRoot: string);
23
+ /** Discover and load all custom tools (global then project, project wins). */
24
+ discover(): CustomToolDefinition[];
25
+ /** Retrieve a single tool definition by name. */
26
+ get(name: string): CustomToolDefinition | undefined;
27
+ /** Return all discovered tool definitions, sorted by name. */
28
+ getAll(): CustomToolDefinition[];
29
+ /**
30
+ * Create an executor function for a tool definition.
31
+ *
32
+ * If the tool defines `execute`, the source is run inside a sandboxed
33
+ * function body with only safe globals. The `args` object is passed as
34
+ * the first argument and the executor must return a string.
35
+ *
36
+ * If the tool defines `command`, the template string has {{arg}} tokens
37
+ * replaced with the corresponding argument values and is executed via
38
+ * a restricted shell invocation.
39
+ */
40
+ createExecutor(def: CustomToolDefinition): (args: Record<string, unknown>) => Promise<string>;
41
+ /** Print a human-readable list of all discovered custom tools. */
42
+ printTools(): void;
43
+ /** Scan a directory for .json, .js, .mjs, .ts tool definition files. */
44
+ private loadFromDirectory;
45
+ /**
46
+ * Load a tool definition from a JSON file.
47
+ *
48
+ * Expected format:
49
+ * {
50
+ * "name": "my-tool",
51
+ * "description": "Does something useful",
52
+ * "parameters": {
53
+ * "type": "object",
54
+ * "properties": { ... },
55
+ * "required": [ ... ]
56
+ * },
57
+ * "risk": "low",
58
+ * "command": "echo {{input}}"
59
+ * }
60
+ */
61
+ private loadJsonTool;
62
+ /**
63
+ * Load a tool definition from a JS/TS file.
64
+ *
65
+ * The file should export a `default` or named `toolDefinition` object
66
+ * conforming to CustomToolDefinition (without `source`).
67
+ *
68
+ * Example (ESM):
69
+ * export const toolDefinition = { name: ..., ... };
70
+ * export default toolDefinition;
71
+ *
72
+ * Because we cannot natively import arbitrary ESM at runtime without
73
+ * a dynamic import that may fail on TS files, we take a pragmatic
74
+ * approach: read the file source and attempt to extract the exported
75
+ * definition via a sandboxed evaluation for .js/.mjs, or parse the
76
+ * object literal for .ts files.
77
+ */
78
+ private loadJsTool;
79
+ /**
80
+ * Attempt to extract a tool definition object from source code text.
81
+ * Looks for common export patterns and parses the object literal.
82
+ */
83
+ private extractDefinitionFromSource;
84
+ /**
85
+ * Strip TypeScript-specific syntax from an object literal string
86
+ * so it can be parsed as plain JavaScript.
87
+ */
88
+ private sanitizeTsObjectLiteral;
89
+ /**
90
+ * Evaluate a JS source file in a sandboxed context and extract
91
+ * the exported tool definition.
92
+ */
93
+ private evaluateJsSource;
94
+ /**
95
+ * Validate that a partial tool definition has all required fields
96
+ * and that their shapes are correct.
97
+ */
98
+ private validateDefinition;
99
+ /**
100
+ * Create an executor that runs the tool's JavaScript source in a sandbox.
101
+ *
102
+ * The sandbox restricts access to only safe, deterministic globals.
103
+ * Network access, filesystem, and process APIs are not available.
104
+ */
105
+ private createJsExecutor;
106
+ /**
107
+ * Create an executor that runs a shell command template.
108
+ *
109
+ * Template tokens like {{input}} are replaced with the corresponding
110
+ * argument values. Shell metacharacters in argument values are escaped
111
+ * to reduce injection risk.
112
+ */
113
+ private createCommandExecutor;
114
+ /**
115
+ * Escape a string for safe inclusion in a shell command argument.
116
+ * Uses single-quoting with internal single-quotes escaped.
117
+ */
118
+ private escapeShellArg;
119
+ }
120
+ //# sourceMappingURL=custom-tools.d.ts.map