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,178 @@
1
+ // ============================================================
2
+ // NeuroCLI - Advisor Model
3
+ // Second model consultation during tasks (like Claude Code's Advisor)
4
+ // ============================================================
5
+ export class AdvisorSystem {
6
+ client;
7
+ config;
8
+ consultationCount = 0;
9
+ recentErrors = [];
10
+ constructor(client, config) {
11
+ this.client = client;
12
+ this.config = {
13
+ enabled: config?.enabled ?? true,
14
+ model: config?.model ?? 'nvidia/nemotron-3-super-120b-a12b:free',
15
+ consultOn: config?.consultOn ?? ['before_complete', 'recurring_error', 'security_sensitive'],
16
+ maxConsultations: config?.maxConsultations ?? 5,
17
+ };
18
+ }
19
+ /**
20
+ * Check if advisor should be consulted for a given trigger
21
+ */
22
+ shouldConsult(trigger) {
23
+ if (!this.config.enabled)
24
+ return false;
25
+ if (this.consultationCount >= this.config.maxConsultations)
26
+ return false;
27
+ return this.config.consultOn.includes(trigger);
28
+ }
29
+ /**
30
+ * Consult the advisor model
31
+ */
32
+ async consult(conversation, trigger, currentAction, context) {
33
+ this.consultationCount++;
34
+ const triggerDescriptions = {
35
+ 'before_approach': 'The agent is about to commit to a specific approach. Review the plan.',
36
+ 'recurring_error': 'The agent has encountered a recurring error. Suggest alternative approaches.',
37
+ 'before_complete': 'The agent is about to declare the task complete. Verify quality.',
38
+ 'complex_decision': 'The agent faces a complex architectural decision. Provide guidance.',
39
+ 'security_sensitive': 'The agent is modifying security-sensitive code. Review for vulnerabilities.',
40
+ 'manual': 'The user requested an advisor consultation.',
41
+ };
42
+ // Build advisor prompt
43
+ const advisorMessages = [
44
+ {
45
+ role: 'system',
46
+ content: `You are an expert advisor AI. Your role is to review the agent's work and provide constructive guidance.
47
+
48
+ You have access to the full conversation including every tool call and result.
49
+
50
+ When advising:
51
+ 1. Identify potential issues or risks
52
+ 2. Suggest better approaches if applicable
53
+ 3. Point out anything the agent might have missed
54
+ 4. Assess code quality and correctness
55
+ 5. Consider security implications
56
+
57
+ Be concise but thorough. End with a clear recommendation: PROCEED or REVISE.`,
58
+ timestamp: Date.now(),
59
+ },
60
+ {
61
+ role: 'user',
62
+ content: `## Advisor Consultation: ${trigger}
63
+
64
+ ${triggerDescriptions[trigger]}
65
+
66
+ ## Current Action
67
+ ${currentAction}
68
+
69
+ ${context ? `## Additional Context\n${context}\n` : ''}
70
+
71
+ ## Conversation So Far
72
+ ${this.summarizeConversation(conversation)}
73
+
74
+ Please provide your assessment and recommendation.`,
75
+ timestamp: Date.now(),
76
+ },
77
+ ];
78
+ try {
79
+ const response = await this.client.quickChat(this.config.model, advisorMessages);
80
+ const advice = response.content;
81
+ const shouldProceed = advice.toUpperCase().includes('PROCEED') &&
82
+ !advice.toUpperCase().includes('REVISE');
83
+ return {
84
+ advice,
85
+ shouldProceed,
86
+ suggestedChanges: shouldProceed ? undefined : this.extractSuggestions(advice),
87
+ usage: response.usage,
88
+ };
89
+ }
90
+ catch (error) {
91
+ return {
92
+ advice: 'Advisor consultation failed. Proceed with caution.',
93
+ shouldProceed: true,
94
+ usage: { inputTokens: 0, outputTokens: 0, cost: 0 },
95
+ };
96
+ }
97
+ }
98
+ /**
99
+ * Track errors for recurring error detection
100
+ */
101
+ trackError(error) {
102
+ this.recentErrors.push(error);
103
+ // Keep last 5 errors
104
+ if (this.recentErrors.length > 5) {
105
+ this.recentErrors.shift();
106
+ }
107
+ }
108
+ /**
109
+ * Check if an error is recurring
110
+ */
111
+ isRecurringError(error) {
112
+ const similar = this.recentErrors.filter(e => this.computeSimilarity(e, error) > 0.5);
113
+ return similar.length >= 2;
114
+ }
115
+ /**
116
+ * Get consultation count
117
+ */
118
+ getConsultationCount() {
119
+ return this.consultationCount;
120
+ }
121
+ /**
122
+ * Reset for new task
123
+ */
124
+ reset() {
125
+ this.consultationCount = 0;
126
+ this.recentErrors = [];
127
+ }
128
+ // ---- Private ----
129
+ summarizeConversation(messages) {
130
+ const parts = [];
131
+ const maxMessages = 30;
132
+ const recent = messages.slice(-maxMessages);
133
+ for (const msg of recent) {
134
+ switch (msg.role) {
135
+ case 'user':
136
+ parts.push(`[User]: ${msg.content.slice(0, 200)}`);
137
+ break;
138
+ case 'assistant':
139
+ if (msg.toolCalls?.length) {
140
+ parts.push(`[Assistant called: ${msg.toolCalls.map(tc => tc.function.name).join(', ')}]`);
141
+ }
142
+ else {
143
+ parts.push(`[Assistant]: ${msg.content.slice(0, 300)}`);
144
+ }
145
+ break;
146
+ case 'tool':
147
+ parts.push(`[Tool result]: ${msg.content.slice(0, 200)}`);
148
+ break;
149
+ }
150
+ }
151
+ return parts.join('\n');
152
+ }
153
+ extractSuggestions(advice) {
154
+ // Extract suggested changes from advisor response
155
+ const lines = advice.split('\n');
156
+ const suggestions = [];
157
+ let inSuggestion = false;
158
+ for (const line of lines) {
159
+ if (line.match(/^#{1,3}\s+(suggestion|recommendation|change|fix)/i)) {
160
+ inSuggestion = true;
161
+ continue;
162
+ }
163
+ if (inSuggestion && line.trim()) {
164
+ suggestions.push(line.trim());
165
+ }
166
+ }
167
+ return suggestions.join('\n') || advice.slice(0, 500);
168
+ }
169
+ computeSimilarity(a, b) {
170
+ // Simple similarity: word overlap
171
+ const wordsA = new Set(a.toLowerCase().split(/\W+/));
172
+ const wordsB = new Set(b.toLowerCase().split(/\W+/));
173
+ const intersection = new Set([...wordsA].filter(w => wordsB.has(w)));
174
+ const union = new Set([...wordsA, ...wordsB]);
175
+ return union.size === 0 ? 0 : intersection.size / union.size;
176
+ }
177
+ }
178
+ //# sourceMappingURL=advisor.js.map
@@ -0,0 +1,62 @@
1
+ import { Message, ToolCall, AgentConfig, AgentExecution } from '../core/types.js';
2
+ import { OpenRouterClient, TokenUsage } from '../api/openrouter.js';
3
+ import { ToolRegistry } from '../tools/registry.js';
4
+ export interface AgentRunResult {
5
+ content: string;
6
+ toolCallsMade: number;
7
+ iterations: number;
8
+ usage: TokenUsage;
9
+ execution: AgentExecution;
10
+ }
11
+ export interface AgentCallbacks {
12
+ onThinking?: (thinking: string) => void;
13
+ onToken?: (token: string) => void;
14
+ onToolCall?: (toolName: string, args: Record<string, unknown>) => void;
15
+ onToolResult?: (toolName: string, result: string, isError: boolean) => void;
16
+ onApprovalNeeded?: (toolName: string, args: Record<string, unknown>, risk: string) => Promise<boolean>;
17
+ onIteration?: (iteration: number, maxIterations: number) => void;
18
+ onComplete?: (result: AgentRunResult) => void;
19
+ }
20
+ export declare class BaseAgent {
21
+ protected config: AgentConfig;
22
+ protected client: OpenRouterClient;
23
+ protected registry: ToolRegistry;
24
+ protected workingDirectory: string;
25
+ protected sessionId: string;
26
+ protected messages: Message[];
27
+ constructor(config: AgentConfig, client: OpenRouterClient, registry: ToolRegistry, workingDirectory: string, sessionId: string);
28
+ get name(): string;
29
+ get description(): string;
30
+ /** Access to agent config for model overrides etc. */
31
+ get configModel(): string | undefined;
32
+ set configModel(model: string | undefined);
33
+ /**
34
+ * Initialize agent with system prompt and context
35
+ */
36
+ protected initializeMessages(taskContext?: string): Message[];
37
+ /**
38
+ * Build the system prompt with context
39
+ */
40
+ protected buildSystemPrompt(taskContext?: string): string;
41
+ /**
42
+ * Add a user message to the conversation
43
+ */
44
+ addUserMessage(content: string): void;
45
+ /**
46
+ * Add an assistant message
47
+ */
48
+ protected addAssistantMessage(content: string, toolCalls?: ToolCall[]): void;
49
+ /**
50
+ * Add a tool result message
51
+ */
52
+ protected addToolResult(toolCallId: string, content: string, name: string, isError?: boolean): void;
53
+ /**
54
+ * Run the agent loop
55
+ */
56
+ run(task: string, callbacks?: AgentCallbacks, maxIterations?: number): Promise<AgentRunResult>;
57
+ /**
58
+ * Quick single-turn query (no tool loop)
59
+ */
60
+ query(prompt: string): Promise<string>;
61
+ }
62
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1,215 @@
1
+ // ============================================================
2
+ // NeuroCLI - Base Agent
3
+ // Foundation class for all agents
4
+ // ============================================================
5
+ import { MODELS } from '../api/models.js';
6
+ export class BaseAgent {
7
+ config;
8
+ client;
9
+ registry;
10
+ workingDirectory;
11
+ sessionId;
12
+ messages = [];
13
+ constructor(config, client, registry, workingDirectory, sessionId) {
14
+ this.config = config;
15
+ this.client = client;
16
+ this.registry = registry;
17
+ this.workingDirectory = workingDirectory;
18
+ this.sessionId = sessionId;
19
+ }
20
+ get name() {
21
+ return this.config.name;
22
+ }
23
+ get description() {
24
+ return this.config.description;
25
+ }
26
+ /** Access to agent config for model overrides etc. */
27
+ get configModel() {
28
+ return this.config.model;
29
+ }
30
+ set configModel(model) {
31
+ this.config.model = model;
32
+ }
33
+ /**
34
+ * Initialize agent with system prompt and context
35
+ */
36
+ initializeMessages(taskContext) {
37
+ const systemContent = this.buildSystemPrompt(taskContext);
38
+ this.messages = [
39
+ { role: 'system', content: systemContent, timestamp: Date.now() },
40
+ ];
41
+ return this.messages;
42
+ }
43
+ /**
44
+ * Build the system prompt with context
45
+ */
46
+ buildSystemPrompt(taskContext) {
47
+ const model = MODELS[this.config.model || ''];
48
+ const cwd = this.workingDirectory;
49
+ let prompt = this.config.systemPrompt;
50
+ prompt += `\n\n## Environment`;
51
+ prompt += `\n- Working Directory: ${cwd}`;
52
+ prompt += `\n- Current Model: ${model?.name || this.config.model}`;
53
+ prompt += `\n- Agent: ${this.config.name} (${this.config.description})`;
54
+ prompt += `\n- Available Tools: ${(this.config.tools || []).join(', ')}`;
55
+ if (taskContext) {
56
+ prompt += `\n\n## Task Context\n${taskContext}`;
57
+ }
58
+ prompt += `\n\n## Guidelines
59
+ - Be precise and thorough in your responses
60
+ - Use tools to verify your assumptions before making changes
61
+ - When modifying files, make minimal, targeted changes
62
+ - Always read a file before editing it
63
+ - Report what you've done and what still needs to be done
64
+ - If you encounter errors, analyze them before retrying
65
+ - Do NOT repeat the same action if it failed - try a different approach`;
66
+ return prompt;
67
+ }
68
+ /**
69
+ * Add a user message to the conversation
70
+ */
71
+ addUserMessage(content) {
72
+ this.messages.push({ role: 'user', content, timestamp: Date.now() });
73
+ }
74
+ /**
75
+ * Add an assistant message
76
+ */
77
+ addAssistantMessage(content, toolCalls) {
78
+ this.messages.push({ role: 'assistant', content, toolCalls, timestamp: Date.now() });
79
+ }
80
+ /**
81
+ * Add a tool result message
82
+ */
83
+ addToolResult(toolCallId, content, name, isError = false) {
84
+ this.messages.push({
85
+ role: 'tool',
86
+ content: isError ? `ERROR: ${content}` : content,
87
+ toolCallId,
88
+ name,
89
+ timestamp: Date.now(),
90
+ });
91
+ }
92
+ /**
93
+ * Run the agent loop
94
+ */
95
+ async run(task, callbacks, maxIterations) {
96
+ const maxIter = maxIterations || this.config.maxIterations || 10;
97
+ const startTime = Date.now();
98
+ let totalUsage = { inputTokens: 0, outputTokens: 0, cost: 0 };
99
+ let toolCallsMade = 0;
100
+ let iteration = 0;
101
+ const execution = {
102
+ agentName: this.config.name,
103
+ task,
104
+ startTime,
105
+ iterations: 0,
106
+ tokensUsed: 0,
107
+ status: 'running',
108
+ };
109
+ // Initialize messages
110
+ this.initializeMessages();
111
+ this.addUserMessage(task);
112
+ for (iteration = 1; iteration <= maxIter; iteration++) {
113
+ callbacks?.onIteration?.(iteration, maxIter);
114
+ // Get tool definitions for this agent
115
+ const toolDefs = this.registry.getDefinitions(this.config.tools);
116
+ // Prepare stream callbacks
117
+ const streamCallbacks = {
118
+ onToken: (token) => callbacks?.onToken?.(token),
119
+ onThinking: (thinking) => callbacks?.onThinking?.(thinking),
120
+ onToolCall: (tc) => callbacks?.onToolCall?.(tc.function.name, JSON.parse(tc.function.arguments)),
121
+ };
122
+ // Call LLM
123
+ try {
124
+ const response = await this.client.chat({
125
+ model: this.config.model || 'qwen/qwen3-coder:free',
126
+ messages: this.messages,
127
+ tools: toolDefs.length > 0 ? toolDefs : undefined,
128
+ temperature: this.config.temperature,
129
+ maxTokens: this.config.maxTokens,
130
+ stream: false, // Disable streaming for better compatibility with free models
131
+ }, streamCallbacks);
132
+ totalUsage.inputTokens += response.usage.inputTokens;
133
+ totalUsage.outputTokens += response.usage.outputTokens;
134
+ totalUsage.cost += response.usage.cost;
135
+ // If no tool calls, we're done
136
+ if (response.toolCalls.length === 0) {
137
+ this.addAssistantMessage(response.content);
138
+ execution.status = 'completed';
139
+ execution.result = response.content;
140
+ break;
141
+ }
142
+ // Process tool calls
143
+ this.addAssistantMessage(response.content, response.toolCalls);
144
+ for (const toolCall of response.toolCalls) {
145
+ let args;
146
+ try {
147
+ args = JSON.parse(toolCall.function.arguments);
148
+ }
149
+ catch {
150
+ args = {};
151
+ }
152
+ const toolName = toolCall.function.name;
153
+ toolCallsMade++;
154
+ callbacks?.onToolCall?.(toolName, args);
155
+ // Check if approval is needed
156
+ const needsApproval = this.registry.getApprovalRequest(toolName, args);
157
+ if (needsApproval && this.config.autoApprove !== true) {
158
+ const approved = await callbacks?.onApprovalNeeded?.(toolName, args, needsApproval.risk) ?? true; // Default to approve if no callback
159
+ if (!approved) {
160
+ this.addToolResult(toolCall.id, 'User denied this tool call.', toolName);
161
+ callbacks?.onToolResult?.(toolName, 'Denied by user', false);
162
+ continue;
163
+ }
164
+ }
165
+ // Execute tool
166
+ const toolContext = {
167
+ workingDirectory: this.workingDirectory,
168
+ sessionId: this.sessionId,
169
+ agentName: this.config.name,
170
+ onProgress: (msg) => callbacks?.onThinking?.(msg),
171
+ };
172
+ const result = await this.registry.execute(toolName, args, toolContext);
173
+ // Use the original tool_call_id from the assistant's tool call, not the registry's generated one
174
+ this.addToolResult(toolCall.id, result.content, toolName, result.isError);
175
+ callbacks?.onToolResult?.(toolName, result.content, result.isError || false);
176
+ }
177
+ }
178
+ catch (error) {
179
+ const errMsg = error instanceof Error ? error.message : String(error);
180
+ this.addAssistantMessage(`Error encountered: ${errMsg}`);
181
+ execution.status = 'failed';
182
+ execution.result = errMsg;
183
+ break;
184
+ }
185
+ }
186
+ if (iteration > maxIter) {
187
+ execution.status = 'completed';
188
+ execution.result = 'Max iterations reached';
189
+ }
190
+ execution.endTime = Date.now();
191
+ execution.iterations = iteration;
192
+ execution.tokensUsed = totalUsage.inputTokens + totalUsage.outputTokens;
193
+ const result = {
194
+ content: this.messages.filter(m => m.role === 'assistant' && !m.toolCalls?.length).pop()?.content || '',
195
+ toolCallsMade,
196
+ iterations: iteration,
197
+ usage: totalUsage,
198
+ execution,
199
+ };
200
+ callbacks?.onComplete?.(result);
201
+ return result;
202
+ }
203
+ /**
204
+ * Quick single-turn query (no tool loop)
205
+ */
206
+ async query(prompt) {
207
+ const messages = [
208
+ { role: 'system', content: this.config.systemPrompt, timestamp: Date.now() },
209
+ { role: 'user', content: prompt, timestamp: Date.now() },
210
+ ];
211
+ const response = await this.client.quickChat(this.config.model || 'anthropic/claude-sonnet-4', messages);
212
+ return response.content;
213
+ }
214
+ }
215
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1,46 @@
1
+ import { BaseAgent, AgentRunResult, AgentCallbacks } from './base.js';
2
+ import { OpenRouterClient, TokenUsage } from '../api/openrouter.js';
3
+ import { ToolRegistry } from '../tools/registry.js';
4
+ import { AgentConfig, AgentExecution } from '../core/types.js';
5
+ export interface SubAgentTask {
6
+ agent: string;
7
+ task: string;
8
+ context?: string;
9
+ dependsOn?: string[];
10
+ }
11
+ export interface OrchestratedPlan {
12
+ tasks: SubAgentTask[];
13
+ reasoning: string;
14
+ }
15
+ export interface OrchestratorResult {
16
+ content: string;
17
+ plan: OrchestratedPlan | null;
18
+ agentResults: Map<string, AgentRunResult>;
19
+ totalUsage: TokenUsage;
20
+ execution: AgentExecution;
21
+ }
22
+ export declare class Orchestrator extends BaseAgent {
23
+ private agents;
24
+ constructor(config: AgentConfig, client: OpenRouterClient, registry: ToolRegistry, workingDirectory: string, sessionId: string);
25
+ /**
26
+ * Register a sub-agent
27
+ */
28
+ registerAgent(agent: BaseAgent): void;
29
+ /**
30
+ * Get all registered agents
31
+ */
32
+ getAgents(): BaseAgent[];
33
+ /**
34
+ * Plan the task decomposition using the orchestrator model
35
+ */
36
+ plan(task: string, callbacks?: AgentCallbacks): Promise<OrchestratedPlan>;
37
+ /**
38
+ * Execute a planned task with sub-agents
39
+ */
40
+ orchestrate(task: string, callbacks?: AgentCallbacks, autoPlan?: boolean): Promise<OrchestratorResult>;
41
+ /**
42
+ * Direct run - delegate to the most appropriate agent
43
+ */
44
+ runDirect(task: string, agentName: string, callbacks?: AgentCallbacks): Promise<AgentRunResult>;
45
+ }
46
+ //# sourceMappingURL=orchestrator.d.ts.map
@@ -0,0 +1,192 @@
1
+ // ============================================================
2
+ // NeuroCLI - Orchestrator Agent
3
+ // Central coordinator that manages sub-agents
4
+ // ============================================================
5
+ import { BaseAgent } from './base.js';
6
+ export class Orchestrator extends BaseAgent {
7
+ agents = new Map();
8
+ constructor(config, client, registry, workingDirectory, sessionId) {
9
+ super(config, client, registry, workingDirectory, sessionId);
10
+ }
11
+ /**
12
+ * Register a sub-agent
13
+ */
14
+ registerAgent(agent) {
15
+ this.agents.set(agent.name, agent);
16
+ }
17
+ /**
18
+ * Get all registered agents
19
+ */
20
+ getAgents() {
21
+ return Array.from(this.agents.values());
22
+ }
23
+ /**
24
+ * Plan the task decomposition using the orchestrator model
25
+ */
26
+ async plan(task, callbacks) {
27
+ const agentDescriptions = Array.from(this.agents.entries())
28
+ .map(([name, agent]) => `- ${name}: ${agent.description}`)
29
+ .join('\n');
30
+ const planPrompt = `You are the Orchestrator. Analyze the following task and create an execution plan using the available agents.
31
+
32
+ ## Available Agents
33
+ ${agentDescriptions}
34
+
35
+ ## Task
36
+ ${task}
37
+
38
+ ## Instructions
39
+ 1. Break down the task into specific sub-tasks
40
+ 2. Assign each sub-task to the most appropriate agent
41
+ 3. Specify dependencies between tasks (which tasks must complete before others can start)
42
+ 4. Provide any context each agent needs
43
+
44
+ Respond with a JSON plan in this exact format:
45
+ \`\`\`json
46
+ {
47
+ "reasoning": "Your analysis of the task and why you chose this approach",
48
+ "tasks": [
49
+ {
50
+ "agent": "agent_name",
51
+ "task": "specific task description",
52
+ "context": "any additional context",
53
+ "dependsOn": []
54
+ }
55
+ ]
56
+ }
57
+ \`\`\``;
58
+ const messages = [
59
+ { role: 'system', content: 'You are an expert task orchestrator. Always respond with valid JSON.', timestamp: Date.now() },
60
+ { role: 'user', content: planPrompt, timestamp: Date.now() },
61
+ ];
62
+ const response = await this.client.quickChat(this.config.model || 'anthropic/claude-sonnet-4', messages);
63
+ try {
64
+ // Extract JSON from response
65
+ const jsonMatch = response.content.match(/```json\n([\s\S]*?)\n```/) ||
66
+ response.content.match(/\{[\s\S]*\}/);
67
+ const jsonStr = jsonMatch ? (jsonMatch[1] || jsonMatch[0]) : response.content;
68
+ return JSON.parse(jsonStr);
69
+ }
70
+ catch {
71
+ // Fallback: use coder agent directly
72
+ return {
73
+ reasoning: 'Could not parse plan, defaulting to direct coding approach',
74
+ tasks: [{ agent: 'Coder', task, dependsOn: [] }],
75
+ };
76
+ }
77
+ }
78
+ /**
79
+ * Execute a planned task with sub-agents
80
+ */
81
+ async orchestrate(task, callbacks, autoPlan = true) {
82
+ const startTime = Date.now();
83
+ const totalUsage = { inputTokens: 0, outputTokens: 0, cost: 0 };
84
+ const agentResults = new Map();
85
+ const execution = {
86
+ agentName: 'Orchestrator',
87
+ task,
88
+ startTime,
89
+ iterations: 0,
90
+ tokensUsed: 0,
91
+ status: 'running',
92
+ };
93
+ // Step 1: Create execution plan
94
+ let plan;
95
+ if (autoPlan) {
96
+ callbacks?.onThinking?.('🧠 Planning task decomposition...');
97
+ plan = await this.plan(task, callbacks);
98
+ }
99
+ else {
100
+ plan = {
101
+ reasoning: 'Direct execution without planning',
102
+ tasks: [{ agent: 'Coder', task }],
103
+ };
104
+ }
105
+ callbacks?.onThinking?.(`šŸ“‹ Plan: ${plan.tasks.length} sub-tasks`);
106
+ callbacks?.onThinking?.(`šŸ’­ ${plan.reasoning}`);
107
+ // Step 2: Execute tasks respecting dependencies
108
+ const completedTasks = new Set();
109
+ let taskIndex = 0;
110
+ while (completedTasks.size < plan.tasks.length) {
111
+ // Find tasks whose dependencies are all completed
112
+ const readyTasks = plan.tasks.filter(t => {
113
+ if (completedTasks.has(t.agent + ':' + t.task))
114
+ return false;
115
+ if (!t.dependsOn || t.dependsOn.length === 0)
116
+ return true;
117
+ return t.dependsOn.every(dep => completedTasks.has(dep));
118
+ });
119
+ if (readyTasks.length === 0) {
120
+ // Deadlock or all tasks completed
121
+ break;
122
+ }
123
+ // Execute ready tasks (could be parallelized in future)
124
+ for (const subTask of readyTasks) {
125
+ const agent = this.agents.get(subTask.agent);
126
+ if (!agent) {
127
+ callbacks?.onThinking?.(`āš ļø Agent not found: ${subTask.agent}`);
128
+ completedTasks.add(subTask.agent + ':' + subTask.task);
129
+ continue;
130
+ }
131
+ taskIndex++;
132
+ callbacks?.onThinking?.(`\nšŸ¤– [${taskIndex}/${plan.tasks.length}] Running ${subTask.agent}: ${subTask.task.slice(0, 80)}...`);
133
+ // Build context from previous agent results
134
+ let fullTask = subTask.task;
135
+ if (subTask.context) {
136
+ fullTask = `${subTask.context}\n\nTask: ${subTask.task}`;
137
+ }
138
+ // Add results from dependent tasks as context
139
+ if (subTask.dependsOn && subTask.dependsOn.length > 0) {
140
+ const depResults = subTask.dependsOn
141
+ .map(dep => agentResults.get(dep))
142
+ .filter(Boolean)
143
+ .map(r => `Previous agent result:\n${r.content}`)
144
+ .join('\n\n');
145
+ if (depResults) {
146
+ fullTask = `${fullTask}\n\n## Context from previous agents:\n${depResults}`;
147
+ }
148
+ }
149
+ const result = await agent.run(fullTask, {
150
+ onToken: (token) => callbacks?.onToken?.(token),
151
+ onToolCall: (name, args) => callbacks?.onToolCall?.(name, args),
152
+ onToolResult: (name, result, isError) => callbacks?.onToolResult?.(name, result, isError),
153
+ onApprovalNeeded: async (name, args, risk) => {
154
+ return callbacks?.onApprovalNeeded?.(name, args, risk) ?? true;
155
+ },
156
+ onThinking: (thinking) => callbacks?.onThinking?.(thinking),
157
+ });
158
+ agentResults.set(subTask.agent + ':' + subTask.task, result);
159
+ totalUsage.inputTokens += result.usage.inputTokens;
160
+ totalUsage.outputTokens += result.usage.outputTokens;
161
+ totalUsage.cost += result.usage.cost;
162
+ completedTasks.add(subTask.agent + ':' + subTask.task);
163
+ }
164
+ }
165
+ execution.endTime = Date.now();
166
+ execution.iterations = taskIndex;
167
+ execution.tokensUsed = totalUsage.inputTokens + totalUsage.outputTokens;
168
+ execution.status = 'completed';
169
+ // Step 3: Synthesize final result
170
+ const resultSummary = Array.from(agentResults.entries())
171
+ .map(([key, result]) => `### ${key}\n${result.content}`)
172
+ .join('\n\n');
173
+ return {
174
+ content: resultSummary,
175
+ plan,
176
+ agentResults,
177
+ totalUsage,
178
+ execution,
179
+ };
180
+ }
181
+ /**
182
+ * Direct run - delegate to the most appropriate agent
183
+ */
184
+ async runDirect(task, agentName, callbacks) {
185
+ const agent = this.agents.get(agentName);
186
+ if (!agent) {
187
+ throw new Error(`Agent not found: ${agentName}. Available: ${Array.from(this.agents.keys()).join(', ')}`);
188
+ }
189
+ return agent.run(task, callbacks);
190
+ }
191
+ }
192
+ //# sourceMappingURL=orchestrator.js.map