@hailer/mcp 1.1.9 → 1.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +0,0 @@
1
- /**
2
- * Investigation Tool - Spawn Claude Code agents to investigate local repos
3
- *
4
- * Uses `claude -p` (headless mode, subscription-based) to autonomously
5
- * explore codebases, search for bugs, and analyze code.
6
- */
7
- import { Tool } from '../tool-registry';
8
- export declare const investigateRepoTool: Tool;
9
- //# sourceMappingURL=investigate.d.ts.map
@@ -1,254 +0,0 @@
1
- "use strict";
2
- /**
3
- * Investigation Tool - Spawn Claude Code agents to investigate local repos
4
- *
5
- * Uses `claude -p` (headless mode, subscription-based) to autonomously
6
- * explore codebases, search for bugs, and analyze code.
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.investigateRepoTool = void 0;
10
- const zod_1 = require("zod");
11
- const child_process_1 = require("child_process");
12
- const tool_registry_1 = require("../tool-registry");
13
- const logger_1 = require("../../lib/logger");
14
- const config_1 = require("../../config");
15
- const logger = (0, logger_1.createLogger)({ component: 'investigate-tool' });
16
- /**
17
- * Resolve claude CLI path at module load
18
- */
19
- const CLAUDE_BIN = (() => {
20
- try {
21
- return (0, child_process_1.execSync)('which claude', { encoding: 'utf-8' }).trim();
22
- }
23
- catch {
24
- return 'claude'; // fallback to PATH lookup
25
- }
26
- })();
27
- /**
28
- * Parse DEV_AI_REPOS env var into a name->path map.
29
- * Format: "name1:/path/one,name2:/path/two"
30
- */
31
- function parseRepoPaths() {
32
- const raw = config_1.environment.DEV_AI_REPOS;
33
- const map = new Map();
34
- if (!raw)
35
- return map;
36
- for (const entry of raw.split(',')) {
37
- const colonIdx = entry.indexOf(':');
38
- if (colonIdx === -1)
39
- continue;
40
- const name = entry.slice(0, colonIdx).trim();
41
- const repoPath = entry.slice(colonIdx + 1).trim();
42
- if (name && repoPath) {
43
- map.set(name, repoPath);
44
- }
45
- }
46
- return map;
47
- }
48
- /**
49
- * Resolve a repo name to its local filesystem path.
50
- */
51
- function resolveRepoPath(repoName) {
52
- const repos = parseRepoPaths();
53
- return repos.get(repoName) || null;
54
- }
55
- /**
56
- * Spawn `claude -p` in a repo directory and return its output.
57
- */
58
- async function spawnInvestigation(prompt, cwd, maxTurns, repoName) {
59
- const timeout = config_1.environment.DEV_AI_INVESTIGATION_TIMEOUT;
60
- return new Promise((resolve, reject) => {
61
- const proc = (0, child_process_1.spawn)(CLAUDE_BIN, [
62
- '-p', prompt,
63
- '--output-format', 'stream-json',
64
- '--verbose',
65
- '--allowedTools', 'Read,Glob,Grep,Bash,Task',
66
- '--max-turns', String(maxTurns),
67
- ], {
68
- cwd,
69
- timeout,
70
- stdio: ['ignore', 'pipe', 'pipe'],
71
- env: {
72
- ...process.env,
73
- // Strip MCP vars so investigation agent doesn't connect to our MCP server
74
- MCP_SERVER_URL: undefined,
75
- MCP_CLIENT_API_KEY: undefined,
76
- MCP_CLIENT_ENABLED: undefined,
77
- CLIENT_CONFIGS: undefined,
78
- BOT_EMAIL: undefined,
79
- BOT_PASSWORD: undefined,
80
- BOT_API_BASE_URL: undefined,
81
- ANTHROPIC_API_KEY: undefined,
82
- },
83
- });
84
- let lineBuffer = '';
85
- let resultText = '';
86
- let stderr = '';
87
- proc.stdout.on('data', (data) => {
88
- lineBuffer += data.toString();
89
- const lines = lineBuffer.split('\n');
90
- lineBuffer = lines.pop() || ''; // Keep incomplete last line in buffer
91
- for (const line of lines) {
92
- const trimmed = line.trim();
93
- if (!trimmed)
94
- continue;
95
- try {
96
- const event = JSON.parse(trimmed);
97
- if (event.type === 'system' && event.subtype === 'init') {
98
- logger.debug(`[investigation:${repoName}] Agent initialized | tools: ${(event.tools || []).length} | model: ${event.model || 'unknown'}`);
99
- }
100
- else if (event.type === 'assistant' && event.message?.content) {
101
- for (const block of event.message.content) {
102
- if (block.type === 'tool_use') {
103
- const inputStr = JSON.stringify(block.input || {});
104
- logger.debug(`[investigation:${repoName}] Tool: ${block.name} ${inputStr.length > 200 ? inputStr.slice(0, 200) + '...' : inputStr}`);
105
- }
106
- else if (block.type === 'text') {
107
- const text = (block.text || '').trim();
108
- if (text) {
109
- logger.debug(`[investigation:${repoName}] ${text.length > 300 ? text.slice(0, 300) + '...' : text}`);
110
- }
111
- }
112
- }
113
- }
114
- else if (event.type === 'tool') {
115
- const content = typeof event.content === 'string' ? event.content : JSON.stringify(event.content || '');
116
- logger.debug(`[investigation:${repoName}] Tool result (${event.tool_name}): ${content.length > 300 ? content.slice(0, 300) + '...' : content}`);
117
- }
118
- else if (event.type === 'result') {
119
- resultText = event.result || '';
120
- const cost = event.total_cost_usd ? `$${event.total_cost_usd.toFixed(4)}` : 'unknown';
121
- const duration = event.duration_ms ? `${(event.duration_ms / 1000).toFixed(1)}s` : 'unknown';
122
- logger.info(`[investigation:${repoName}] Complete | ${resultText.length} chars | ${duration} | cost: ${cost}`);
123
- }
124
- }
125
- catch {
126
- // Non-JSON line
127
- if (trimmed.length > 0) {
128
- logger.debug(`[investigation:${repoName}] ${trimmed.length > 300 ? trimmed.slice(0, 300) + '...' : trimmed}`);
129
- }
130
- }
131
- }
132
- });
133
- proc.stderr.on('data', (data) => {
134
- const chunk = data.toString();
135
- stderr += chunk;
136
- const trimmed = chunk.trim();
137
- if (trimmed) {
138
- logger.warn(`[investigation:${repoName}] ${trimmed.length > 500 ? trimmed.slice(0, 500) + '...' : trimmed}`);
139
- }
140
- });
141
- proc.on('error', (err) => {
142
- reject(new Error(`Failed to spawn claude: ${err.message}`));
143
- });
144
- proc.on('close', (code) => {
145
- // Process any remaining data in lineBuffer
146
- if (lineBuffer.trim()) {
147
- try {
148
- const event = JSON.parse(lineBuffer.trim());
149
- if (event.type === 'result') {
150
- resultText = event.result || '';
151
- }
152
- }
153
- catch {
154
- // ignore
155
- }
156
- }
157
- if (code === 0) {
158
- resolve(resultText || 'Investigation completed but returned no structured result.');
159
- }
160
- else {
161
- const errDetail = stderr.trim() || `exit code ${code}`;
162
- reject(new Error(`Investigation failed: ${errDetail}`));
163
- }
164
- });
165
- });
166
- }
167
- const investigateRepoSchema = zod_1.z.object({
168
- repoName: zod_1.z.string().describe('Target repo name (must match a configured repo). Available repos depend on DEV_AI_REPOS config.'),
169
- prompt: zod_1.z.string().describe('What to investigate - bug description, search query, code question, etc.'),
170
- maxTurns: zod_1.z.number().optional().default(30).describe('Max investigation depth (number of agentic turns). Default: 30.'),
171
- });
172
- exports.investigateRepoTool = {
173
- name: 'investigate_repo',
174
- group: tool_registry_1.ToolGroup.PLAYGROUND,
175
- description: 'Spawn a Claude Code agent to investigate code in a local repo. ' +
176
- 'Uses claude -p (headless mode, subscription-based). ' +
177
- 'The agent can explore files, search code, analyze the codebase autonomously, ' +
178
- 'and spawn investigation teams for parallel exploration of complex issues. ' +
179
- 'Returns the investigation findings as text.',
180
- schema: investigateRepoSchema,
181
- async execute(args, _context) {
182
- const repos = parseRepoPaths();
183
- // If no repos configured, return helpful error
184
- if (repos.size === 0) {
185
- return {
186
- content: [{
187
- type: 'text',
188
- text: 'No repos configured. Set DEV_AI_REPOS in .env.local (format: "name:/path,name2:/path2").',
189
- }],
190
- };
191
- }
192
- const repoPath = resolveRepoPath(args.repoName);
193
- if (!repoPath) {
194
- const available = Array.from(repos.keys()).join(', ');
195
- return {
196
- content: [{
197
- type: 'text',
198
- text: `Unknown repo: "${args.repoName}". Available repos: ${available}`,
199
- }],
200
- };
201
- }
202
- const enhancedPrompt = `You are a lead investigator. Use parallel agents to explore the codebase efficiently.
203
-
204
- HOW TO USE PARALLEL AGENTS:
205
- - Call multiple Task tools in a SINGLE response to run them in parallel
206
- - Each Task call blocks until that agent finishes and returns its findings directly to you
207
- - Use subagent_type: "Explore" with model: "haiku" — these are fast, read-only codebase search agents
208
- - Do NOT use TeamCreate, SendMessage, or TaskCreate — just call Task directly
209
-
210
- APPROACH:
211
- 1. In your FIRST response, call 2-3 Task tools simultaneously, each with a focused search prompt
212
- 2. You will receive all their findings in the next turn
213
- 3. If needed, do additional targeted searches yourself with Grep/Read
214
- 4. Write your FINAL REPORT
215
-
216
- REPORT FORMAT:
217
- - Root cause of the issue
218
- - Affected files with paths and line numbers
219
- - Suggested fix with specific code changes
220
-
221
- INVESTIGATION TASK:
222
- ${args.prompt}`;
223
- logger.info('Starting investigation', {
224
- repo: args.repoName,
225
- repoPath,
226
- maxTurns: args.maxTurns,
227
- promptLength: enhancedPrompt.length,
228
- });
229
- try {
230
- const result = await spawnInvestigation(enhancedPrompt, repoPath, args.maxTurns, args.repoName);
231
- logger.info('Investigation completed', {
232
- repo: args.repoName,
233
- resultLength: result.length,
234
- });
235
- return {
236
- content: [{
237
- type: 'text',
238
- text: result || 'Investigation completed but returned no output.',
239
- }],
240
- };
241
- }
242
- catch (error) {
243
- const errMsg = error instanceof Error ? error.message : String(error);
244
- logger.error('Investigation failed', { repo: args.repoName, error: errMsg });
245
- return {
246
- content: [{
247
- type: 'text',
248
- text: `Investigation failed: ${errMsg}`,
249
- }],
250
- };
251
- }
252
- },
253
- };
254
- //# sourceMappingURL=investigate.js.map
@@ -1 +0,0 @@
1
- [2026-02-16T05:32:29.204Z] agent-giuseppe-app-builder | skill | hailer-mcp | \"Field '{fieldLabel}' expects Unix ms timestamp (number), got: '{value}'. Convert the date to milliseconds first.\"\n```\n\n**Important:**\n- Read th
package/inbox/usage.jsonl DELETED
@@ -1,3 +0,0 @@
1
- {"ts":"2026-02-13T08:23:26.264Z","agent":"general-purpose","status":"unknown","project":"hailer-mcp","description":"Convert new agents to OpenCode"}
2
- {"ts":"2026-02-16T05:26:20.980Z","agent":"agent-svetlana-code-review","status":"unknown","project":"hailer-mcp","description":"Check bot date handling code"}
3
- {"ts":"2026-02-16T05:32:29.203Z","agent":"agent-giuseppe-app-builder","status":"error","project":"hailer-mcp","description":"Add date validation to bot+tools"}