@link-assistant/hive-mind 0.46.1 → 0.47.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.
- package/CHANGELOG.md +20 -15
- package/README.md +42 -8
- package/package.json +16 -3
- package/src/agent.lib.mjs +49 -70
- package/src/agent.prompts.lib.mjs +6 -20
- package/src/buildUserMention.lib.mjs +4 -17
- package/src/claude-limits.lib.mjs +15 -15
- package/src/claude.lib.mjs +617 -626
- package/src/claude.prompts.lib.mjs +7 -22
- package/src/codex.lib.mjs +39 -71
- package/src/codex.prompts.lib.mjs +6 -20
- package/src/config.lib.mjs +3 -16
- package/src/contributing-guidelines.lib.mjs +5 -18
- package/src/exit-handler.lib.mjs +4 -4
- package/src/git.lib.mjs +7 -7
- package/src/github-issue-creator.lib.mjs +17 -17
- package/src/github-linking.lib.mjs +8 -33
- package/src/github.batch.lib.mjs +20 -16
- package/src/github.graphql.lib.mjs +18 -18
- package/src/github.lib.mjs +89 -91
- package/src/hive.config.lib.mjs +50 -50
- package/src/hive.mjs +1293 -1296
- package/src/instrument.mjs +7 -11
- package/src/interactive-mode.lib.mjs +112 -138
- package/src/lenv-reader.lib.mjs +1 -6
- package/src/lib.mjs +36 -45
- package/src/lino.lib.mjs +2 -2
- package/src/local-ci-checks.lib.mjs +15 -14
- package/src/memory-check.mjs +52 -60
- package/src/model-mapping.lib.mjs +25 -32
- package/src/model-validation.lib.mjs +31 -31
- package/src/opencode.lib.mjs +37 -62
- package/src/opencode.prompts.lib.mjs +7 -21
- package/src/protect-branch.mjs +14 -15
- package/src/review.mjs +28 -27
- package/src/reviewers-hive.mjs +64 -69
- package/src/sentry.lib.mjs +13 -10
- package/src/solve.auto-continue.lib.mjs +48 -38
- package/src/solve.auto-pr.lib.mjs +111 -69
- package/src/solve.branch-errors.lib.mjs +17 -46
- package/src/solve.branch.lib.mjs +16 -23
- package/src/solve.config.lib.mjs +263 -261
- package/src/solve.error-handlers.lib.mjs +21 -79
- package/src/solve.execution.lib.mjs +10 -18
- package/src/solve.feedback.lib.mjs +25 -46
- package/src/solve.mjs +59 -60
- package/src/solve.preparation.lib.mjs +10 -36
- package/src/solve.repo-setup.lib.mjs +4 -19
- package/src/solve.repository.lib.mjs +37 -37
- package/src/solve.results.lib.mjs +32 -46
- package/src/solve.session.lib.mjs +7 -22
- package/src/solve.validation.lib.mjs +19 -17
- package/src/solve.watch.lib.mjs +20 -33
- package/src/start-screen.mjs +24 -24
- package/src/task.mjs +38 -44
- package/src/telegram-bot.mjs +125 -121
- package/src/telegram-top-command.lib.mjs +32 -48
- package/src/usage-limit.lib.mjs +9 -13
- package/src/version-info.lib.mjs +1 -1
- package/src/version.lib.mjs +1 -1
- package/src/youtrack/solve.youtrack.lib.mjs +3 -8
- package/src/youtrack/youtrack-sync.mjs +8 -14
- package/src/youtrack/youtrack.lib.mjs +26 -28
package/src/instrument.mjs
CHANGED
|
@@ -46,19 +46,15 @@ if (!shouldDisableSentry()) {
|
|
|
46
46
|
const { sentry, version } = await import('./config.lib.mjs');
|
|
47
47
|
|
|
48
48
|
// Dynamically import Sentry packages only when needed
|
|
49
|
-
|
|
50
|
-
const sentryModule = await import("@sentry/node");
|
|
49
|
+
const sentryModule = await import('@sentry/node');
|
|
51
50
|
Sentry = sentryModule;
|
|
52
|
-
|
|
53
|
-
const profilingModule = await import("@sentry/profiling-node");
|
|
51
|
+
const profilingModule = await import('@sentry/profiling-node');
|
|
54
52
|
nodeProfilingIntegration = profilingModule.nodeProfilingIntegration;
|
|
55
53
|
|
|
56
54
|
// Initialize Sentry with configuration
|
|
57
55
|
Sentry.init({
|
|
58
56
|
dsn: sentry.dsn,
|
|
59
|
-
integrations: [
|
|
60
|
-
nodeProfilingIntegration(),
|
|
61
|
-
],
|
|
57
|
+
integrations: [nodeProfilingIntegration()],
|
|
62
58
|
|
|
63
59
|
// Application name
|
|
64
60
|
environment: process.env.NODE_ENV || 'production',
|
|
@@ -125,7 +121,7 @@ if (!shouldDisableSentry()) {
|
|
|
125
121
|
}
|
|
126
122
|
context.name = `hive-mind.${context.name || 'unknown'}`;
|
|
127
123
|
return context;
|
|
128
|
-
}
|
|
124
|
+
},
|
|
129
125
|
});
|
|
130
126
|
|
|
131
127
|
// Log that Sentry has been initialized
|
|
@@ -157,7 +153,7 @@ export const isSentryEnabled = () => Sentry !== null && Sentry.getClient() !== u
|
|
|
157
153
|
export const captureException = (error, context = {}) => {
|
|
158
154
|
if (isSentryEnabled()) {
|
|
159
155
|
Sentry.captureException(error, {
|
|
160
|
-
extra: context
|
|
156
|
+
extra: context,
|
|
161
157
|
});
|
|
162
158
|
}
|
|
163
159
|
};
|
|
@@ -166,7 +162,7 @@ export const captureException = (error, context = {}) => {
|
|
|
166
162
|
export const captureMessage = (message, level = 'info', context = {}) => {
|
|
167
163
|
if (isSentryEnabled()) {
|
|
168
164
|
Sentry.captureMessage(message, level, {
|
|
169
|
-
extra: context
|
|
165
|
+
extra: context,
|
|
170
166
|
});
|
|
171
167
|
}
|
|
172
168
|
};
|
|
@@ -188,4 +184,4 @@ export const startTransaction = (name, op = 'task') => {
|
|
|
188
184
|
setStatus: () => {},
|
|
189
185
|
setData: () => {},
|
|
190
186
|
};
|
|
191
|
-
};
|
|
187
|
+
};
|
|
@@ -39,7 +39,7 @@ const CONFIG = {
|
|
|
39
39
|
// Lines to keep at end when truncating
|
|
40
40
|
LINES_TO_KEEP_END: 20,
|
|
41
41
|
// Maximum JSON depth for raw JSON display
|
|
42
|
-
MAX_JSON_DEPTH: 10
|
|
42
|
+
MAX_JSON_DEPTH: 10,
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
/**
|
|
@@ -54,11 +54,7 @@ const CONFIG = {
|
|
|
54
54
|
* @returns {string} Truncated content with ellipsis indicator
|
|
55
55
|
*/
|
|
56
56
|
const truncateMiddle = (content, options = {}) => {
|
|
57
|
-
const {
|
|
58
|
-
maxLines = CONFIG.MAX_LINES_BEFORE_TRUNCATION,
|
|
59
|
-
keepStart = CONFIG.LINES_TO_KEEP_START,
|
|
60
|
-
keepEnd = CONFIG.LINES_TO_KEEP_END
|
|
61
|
-
} = options;
|
|
57
|
+
const { maxLines = CONFIG.MAX_LINES_BEFORE_TRUNCATION, keepStart = CONFIG.LINES_TO_KEEP_START, keepEnd = CONFIG.LINES_TO_KEEP_END } = options;
|
|
62
58
|
|
|
63
59
|
if (!content || typeof content !== 'string') {
|
|
64
60
|
return content || '';
|
|
@@ -73,13 +69,7 @@ const truncateMiddle = (content, options = {}) => {
|
|
|
73
69
|
const endLines = lines.slice(-keepEnd);
|
|
74
70
|
const removedCount = lines.length - keepStart - keepEnd;
|
|
75
71
|
|
|
76
|
-
return [
|
|
77
|
-
...startLines,
|
|
78
|
-
'',
|
|
79
|
-
`... [${removedCount} lines truncated] ...`,
|
|
80
|
-
'',
|
|
81
|
-
...endLines
|
|
82
|
-
].join('\n');
|
|
72
|
+
return [...startLines, '', `... [${removedCount} lines truncated] ...`, '', ...endLines].join('\n');
|
|
83
73
|
};
|
|
84
74
|
|
|
85
75
|
/**
|
|
@@ -91,15 +81,19 @@ const truncateMiddle = (content, options = {}) => {
|
|
|
91
81
|
*/
|
|
92
82
|
const safeJsonStringify = (obj, indent = 2) => {
|
|
93
83
|
const seen = new WeakSet();
|
|
94
|
-
return JSON.stringify(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
return JSON.stringify(
|
|
85
|
+
obj,
|
|
86
|
+
(key, value) => {
|
|
87
|
+
if (typeof value === 'object' && value !== null) {
|
|
88
|
+
if (seen.has(value)) {
|
|
89
|
+
return '[Circular]';
|
|
90
|
+
}
|
|
91
|
+
seen.add(value);
|
|
98
92
|
}
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
93
|
+
return value;
|
|
94
|
+
},
|
|
95
|
+
indent
|
|
96
|
+
);
|
|
103
97
|
};
|
|
104
98
|
|
|
105
99
|
/**
|
|
@@ -127,18 +121,15 @@ ${content}
|
|
|
127
121
|
* @param {Object|Array} data - JSON data to display (will be wrapped in array if not already)
|
|
128
122
|
* @returns {string} Collapsible JSON block
|
|
129
123
|
*/
|
|
130
|
-
const createRawJsonSection =
|
|
124
|
+
const createRawJsonSection = data => {
|
|
131
125
|
// Ensure data is always an array at root level for easier merging
|
|
132
126
|
const dataArray = Array.isArray(data) ? data : [data];
|
|
133
127
|
const jsonContent = truncateMiddle(safeJsonStringify(dataArray, 2), {
|
|
134
128
|
maxLines: 100,
|
|
135
129
|
keepStart: 40,
|
|
136
|
-
keepEnd: 40
|
|
130
|
+
keepEnd: 40,
|
|
137
131
|
});
|
|
138
|
-
return createCollapsible(
|
|
139
|
-
'📄 Raw JSON',
|
|
140
|
-
'```json\n' + jsonContent + '\n```'
|
|
141
|
-
);
|
|
132
|
+
return createCollapsible('📄 Raw JSON', '```json\n' + jsonContent + '\n```');
|
|
142
133
|
};
|
|
143
134
|
|
|
144
135
|
/**
|
|
@@ -147,7 +138,7 @@ const createRawJsonSection = (data) => {
|
|
|
147
138
|
* @param {number} ms - Duration in milliseconds
|
|
148
139
|
* @returns {string} Formatted duration (e.g., "12m 7s")
|
|
149
140
|
*/
|
|
150
|
-
const formatDuration =
|
|
141
|
+
const formatDuration = ms => {
|
|
151
142
|
if (!ms || ms < 0) return 'unknown';
|
|
152
143
|
|
|
153
144
|
const seconds = Math.floor(ms / 1000);
|
|
@@ -169,7 +160,7 @@ const formatDuration = (ms) => {
|
|
|
169
160
|
* @param {number} cost - Cost in USD
|
|
170
161
|
* @returns {string} Formatted cost (e.g., "$1.60")
|
|
171
162
|
*/
|
|
172
|
-
const formatCost =
|
|
163
|
+
const formatCost = cost => {
|
|
173
164
|
if (typeof cost !== 'number' || isNaN(cost)) return 'unknown';
|
|
174
165
|
return `$${cost.toFixed(2)}`;
|
|
175
166
|
};
|
|
@@ -180,7 +171,7 @@ const formatCost = (cost) => {
|
|
|
180
171
|
* @param {string} text - Text to escape
|
|
181
172
|
* @returns {string} Escaped text
|
|
182
173
|
*/
|
|
183
|
-
const escapeMarkdown =
|
|
174
|
+
const escapeMarkdown = text => {
|
|
184
175
|
if (!text || typeof text !== 'string') return '';
|
|
185
176
|
// Escape backticks that would break code blocks
|
|
186
177
|
return text.replace(/```/g, '\\`\\`\\`');
|
|
@@ -192,20 +183,20 @@ const escapeMarkdown = (text) => {
|
|
|
192
183
|
* @param {string} toolName - Name of the tool
|
|
193
184
|
* @returns {string} Emoji icon
|
|
194
185
|
*/
|
|
195
|
-
const getToolIcon =
|
|
186
|
+
const getToolIcon = toolName => {
|
|
196
187
|
const icons = {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
188
|
+
Bash: '💻',
|
|
189
|
+
Read: '📖',
|
|
190
|
+
Write: '✏️',
|
|
191
|
+
Edit: '📝',
|
|
192
|
+
Glob: '🔍',
|
|
193
|
+
Grep: '🔎',
|
|
194
|
+
WebFetch: '🌐',
|
|
195
|
+
WebSearch: '🔍',
|
|
196
|
+
TodoWrite: '📋',
|
|
197
|
+
Task: '🎯',
|
|
198
|
+
NotebookEdit: '📓',
|
|
199
|
+
default: '🔧',
|
|
209
200
|
};
|
|
210
201
|
return icons[toolName] || icons.default;
|
|
211
202
|
};
|
|
@@ -222,7 +213,7 @@ const getToolIcon = (toolName) => {
|
|
|
222
213
|
* @param {boolean} [options.verbose=false] - Enable verbose logging
|
|
223
214
|
* @returns {Object} Handler object with event processing methods
|
|
224
215
|
*/
|
|
225
|
-
export const createInteractiveHandler =
|
|
216
|
+
export const createInteractiveHandler = options => {
|
|
226
217
|
const { owner, repo, prNumber, $, log, verbose = false } = options;
|
|
227
218
|
|
|
228
219
|
// State tracking for the handler
|
|
@@ -243,7 +234,7 @@ export const createInteractiveHandler = (options) => {
|
|
|
243
234
|
pendingToolCalls: new Map(),
|
|
244
235
|
// Simple map of tool_use_id -> { toolName, toolIcon } for standalone tool results
|
|
245
236
|
// This is preserved even after pendingToolCalls entry is deleted
|
|
246
|
-
toolUseRegistry: new Map()
|
|
237
|
+
toolUseRegistry: new Map(),
|
|
247
238
|
};
|
|
248
239
|
|
|
249
240
|
/**
|
|
@@ -364,7 +355,9 @@ export const createInteractiveHandler = (options) => {
|
|
|
364
355
|
pendingCall.resolveCommentId(commentId);
|
|
365
356
|
}
|
|
366
357
|
if (verbose) {
|
|
367
|
-
await log(`📋 Interactive mode: Updated pending tool call ${toolId} with comment ID ${commentId}`, {
|
|
358
|
+
await log(`📋 Interactive mode: Updated pending tool call ${toolId} with comment ID ${commentId}`, {
|
|
359
|
+
verbose: true,
|
|
360
|
+
});
|
|
368
361
|
}
|
|
369
362
|
}
|
|
370
363
|
}
|
|
@@ -378,32 +371,24 @@ export const createInteractiveHandler = (options) => {
|
|
|
378
371
|
* Handle system.init event
|
|
379
372
|
* @param {Object} data - Event data
|
|
380
373
|
*/
|
|
381
|
-
const handleSystemInit = async
|
|
374
|
+
const handleSystemInit = async data => {
|
|
382
375
|
state.sessionId = data.session_id;
|
|
383
376
|
state.startTime = Date.now();
|
|
384
377
|
|
|
385
378
|
const tools = data.tools || [];
|
|
386
|
-
const toolsList = tools.length > 0
|
|
387
|
-
? tools.map(t => `\`${t}\``).join(', ')
|
|
388
|
-
: '_No tools available_';
|
|
379
|
+
const toolsList = tools.length > 0 ? tools.map(t => `\`${t}\``).join(', ') : '_No tools available_';
|
|
389
380
|
|
|
390
381
|
// Format MCP servers
|
|
391
382
|
const mcpServers = data.mcp_servers || [];
|
|
392
|
-
const mcpServersList = mcpServers.length > 0
|
|
393
|
-
? mcpServers.map(s => `\`${s.name}\` (${s.status || 'unknown'})`).join(', ')
|
|
394
|
-
: '_None_';
|
|
383
|
+
const mcpServersList = mcpServers.length > 0 ? mcpServers.map(s => `\`${s.name}\` (${s.status || 'unknown'})`).join(', ') : '_None_';
|
|
395
384
|
|
|
396
385
|
// Format slash commands
|
|
397
386
|
const slashCommands = data.slash_commands || [];
|
|
398
|
-
const slashCommandsList = slashCommands.length > 0
|
|
399
|
-
? slashCommands.map(c => `\`/${c}\``).join(', ')
|
|
400
|
-
: '_None_';
|
|
387
|
+
const slashCommandsList = slashCommands.length > 0 ? slashCommands.map(c => `\`/${c}\``).join(', ') : '_None_';
|
|
401
388
|
|
|
402
389
|
// Format agents
|
|
403
390
|
const agents = data.agents || [];
|
|
404
|
-
const agentsList = agents.length > 0
|
|
405
|
-
? agents.map(a => `\`${a}\``).join(', ')
|
|
406
|
-
: '_None_';
|
|
391
|
+
const agentsList = agents.length > 0 ? agents.map(a => `\`${a}\``).join(', ') : '_None_';
|
|
407
392
|
|
|
408
393
|
const comment = `## 🚀 Interactive session started
|
|
409
394
|
|
|
@@ -442,7 +427,7 @@ ${createRawJsonSection(data)}`;
|
|
|
442
427
|
const displayText = truncateMiddle(text, {
|
|
443
428
|
maxLines: 80,
|
|
444
429
|
keepStart: 35,
|
|
445
|
-
keepEnd: 35
|
|
430
|
+
keepEnd: 35,
|
|
446
431
|
});
|
|
447
432
|
|
|
448
433
|
// Simple format: just the message and collapsed Raw JSON
|
|
@@ -482,13 +467,9 @@ ${createRawJsonSection(data)}`;
|
|
|
482
467
|
const truncatedCommand = truncateMiddle(input.command, {
|
|
483
468
|
maxLines: 30,
|
|
484
469
|
keepStart: 12,
|
|
485
|
-
keepEnd: 12
|
|
470
|
+
keepEnd: 12,
|
|
486
471
|
});
|
|
487
|
-
inputDisplay = createCollapsible(
|
|
488
|
-
'📋 Executed command',
|
|
489
|
-
'```bash\n' + escapeMarkdown(truncatedCommand) + '\n```',
|
|
490
|
-
true
|
|
491
|
-
);
|
|
472
|
+
inputDisplay = createCollapsible('📋 Executed command', '```bash\n' + escapeMarkdown(truncatedCommand) + '\n```', true);
|
|
492
473
|
} else if (toolName === 'Read' && input.file_path) {
|
|
493
474
|
inputDisplay = `**File:** \`${input.file_path}\``;
|
|
494
475
|
if (input.offset || input.limit) {
|
|
@@ -500,14 +481,14 @@ ${createRawJsonSection(data)}`;
|
|
|
500
481
|
const truncatedContent = truncateMiddle(input.content, {
|
|
501
482
|
maxLines: 30,
|
|
502
483
|
keepStart: 12,
|
|
503
|
-
keepEnd: 12
|
|
484
|
+
keepEnd: 12,
|
|
504
485
|
});
|
|
505
486
|
// Format content as diff with + prefix for added lines
|
|
506
|
-
const diffContent = truncatedContent
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
'
|
|
510
|
-
);
|
|
487
|
+
const diffContent = truncatedContent
|
|
488
|
+
.split('\n')
|
|
489
|
+
.map(line => `+ ${line}`)
|
|
490
|
+
.join('\n');
|
|
491
|
+
inputDisplay += '\n\n' + createCollapsible('📄 Content', '```diff\n' + escapeMarkdown(diffContent) + '\n```');
|
|
511
492
|
}
|
|
512
493
|
} else if (toolName === 'Edit' && input.file_path) {
|
|
513
494
|
inputDisplay = `**File:** \`${input.file_path}\``;
|
|
@@ -515,13 +496,15 @@ ${createRawJsonSection(data)}`;
|
|
|
515
496
|
const truncatedOld = truncateMiddle(input.old_string, { maxLines: 15, keepStart: 6, keepEnd: 6 });
|
|
516
497
|
const truncatedNew = truncateMiddle(input.new_string, { maxLines: 15, keepStart: 6, keepEnd: 6 });
|
|
517
498
|
// Format as unified diff with - for removed lines and + for added lines
|
|
518
|
-
const diffOld = truncatedOld
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
'
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
499
|
+
const diffOld = truncatedOld
|
|
500
|
+
.split('\n')
|
|
501
|
+
.map(line => `- ${line}`)
|
|
502
|
+
.join('\n');
|
|
503
|
+
const diffNew = truncatedNew
|
|
504
|
+
.split('\n')
|
|
505
|
+
.map(line => `+ ${line}`)
|
|
506
|
+
.join('\n');
|
|
507
|
+
inputDisplay += '\n\n' + createCollapsible('🔄 Change', '```diff\n' + escapeMarkdown(diffOld + '\n' + diffNew) + '\n```', true);
|
|
525
508
|
}
|
|
526
509
|
} else if ((toolName === 'Glob' || toolName === 'Grep') && input.pattern) {
|
|
527
510
|
inputDisplay = `**Pattern:** \`${input.pattern}\``;
|
|
@@ -549,18 +532,10 @@ ${createRawJsonSection(data)}`;
|
|
|
549
532
|
const startTodos = todos.slice(0, KEEP_START).map(t => `- [${t.status === 'completed' ? 'x' : ' '}] ${t.content}`);
|
|
550
533
|
const endTodos = todos.slice(-KEEP_END).map(t => `- [${t.status === 'completed' ? 'x' : ' '}] ${t.content}`);
|
|
551
534
|
|
|
552
|
-
todosPreview = [
|
|
553
|
-
...startTodos,
|
|
554
|
-
`- _...and ${skipped} more_`,
|
|
555
|
-
...endTodos
|
|
556
|
-
].join('\n');
|
|
535
|
+
todosPreview = [...startTodos, `- _...and ${skipped} more_`, ...endTodos].join('\n');
|
|
557
536
|
}
|
|
558
537
|
|
|
559
|
-
inputDisplay = createCollapsible(
|
|
560
|
-
`📋 Todos (${todos.length} items)`,
|
|
561
|
-
todosPreview,
|
|
562
|
-
true
|
|
563
|
-
);
|
|
538
|
+
inputDisplay = createCollapsible(`📋 Todos (${todos.length} items)`, todosPreview, true);
|
|
564
539
|
} else if (toolName === 'Task') {
|
|
565
540
|
inputDisplay = `**Description:** ${input.description || 'N/A'}`;
|
|
566
541
|
if (input.prompt) {
|
|
@@ -572,12 +547,9 @@ ${createRawJsonSection(data)}`;
|
|
|
572
547
|
const inputJson = truncateMiddle(safeJsonStringify(input, 2), {
|
|
573
548
|
maxLines: 30,
|
|
574
549
|
keepStart: 12,
|
|
575
|
-
keepEnd: 12
|
|
550
|
+
keepEnd: 12,
|
|
576
551
|
});
|
|
577
|
-
inputDisplay = createCollapsible(
|
|
578
|
-
'📥 Input',
|
|
579
|
-
'```json\n' + inputJson + '\n```'
|
|
580
|
-
);
|
|
552
|
+
inputDisplay = createCollapsible('📥 Input', '```json\n' + inputJson + '\n```');
|
|
581
553
|
}
|
|
582
554
|
|
|
583
555
|
// Post the tool use comment and store info for merging with result later
|
|
@@ -594,7 +566,7 @@ ${createRawJsonSection(data)}`;
|
|
|
594
566
|
// Create a promise that will resolve with the comment ID
|
|
595
567
|
// This handles both immediate posting and queued posting
|
|
596
568
|
let resolveCommentId;
|
|
597
|
-
const commentIdPromise = new Promise(
|
|
569
|
+
const commentIdPromise = new Promise(resolve => {
|
|
598
570
|
resolveCommentId = resolve;
|
|
599
571
|
});
|
|
600
572
|
|
|
@@ -607,7 +579,7 @@ ${createRawJsonSection(data)}`;
|
|
|
607
579
|
toolData: data,
|
|
608
580
|
inputDisplay,
|
|
609
581
|
toolName,
|
|
610
|
-
toolIcon
|
|
582
|
+
toolIcon,
|
|
611
583
|
});
|
|
612
584
|
|
|
613
585
|
// Post the comment, passing toolId for queue tracking
|
|
@@ -624,7 +596,9 @@ ${createRawJsonSection(data)}`;
|
|
|
624
596
|
// If queued (commentId is null), processQueue will update it later
|
|
625
597
|
|
|
626
598
|
if (verbose) {
|
|
627
|
-
await log(`🔧 Interactive mode: Tool use - ${toolName}${commentId ? ` (comment: ${commentId})` : ' (queued)'}`, {
|
|
599
|
+
await log(`🔧 Interactive mode: Tool use - ${toolName}${commentId ? ` (comment: ${commentId})` : ' (queued)'}`, {
|
|
600
|
+
verbose: true,
|
|
601
|
+
});
|
|
628
602
|
}
|
|
629
603
|
};
|
|
630
604
|
|
|
@@ -646,18 +620,20 @@ ${createRawJsonSection(data)}`;
|
|
|
646
620
|
if (typeof toolResult.content === 'string') {
|
|
647
621
|
content = toolResult.content;
|
|
648
622
|
} else if (Array.isArray(toolResult.content)) {
|
|
649
|
-
content = toolResult.content
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
623
|
+
content = toolResult.content
|
|
624
|
+
.map(c => {
|
|
625
|
+
if (typeof c === 'string') return c;
|
|
626
|
+
if (c.type === 'text') return c.text || '';
|
|
627
|
+
return safeJsonStringify(c);
|
|
628
|
+
})
|
|
629
|
+
.join('\n');
|
|
654
630
|
}
|
|
655
631
|
|
|
656
632
|
// Truncate large outputs
|
|
657
633
|
const truncatedContent = truncateMiddle(content, {
|
|
658
634
|
maxLines: 60,
|
|
659
635
|
keepStart: 25,
|
|
660
|
-
keepEnd: 25
|
|
636
|
+
keepEnd: 25,
|
|
661
637
|
});
|
|
662
638
|
|
|
663
639
|
// Check if we have a pending tool call to merge with
|
|
@@ -671,15 +647,19 @@ ${createRawJsonSection(data)}`;
|
|
|
671
647
|
// But use a timeout to avoid blocking forever
|
|
672
648
|
if (!commentId && commentIdPromise) {
|
|
673
649
|
if (verbose) {
|
|
674
|
-
await log(`⏳ Interactive mode: Waiting for tool use comment to be posted (tool: ${toolUseId})`, {
|
|
650
|
+
await log(`⏳ Interactive mode: Waiting for tool use comment to be posted (tool: ${toolUseId})`, {
|
|
651
|
+
verbose: true,
|
|
652
|
+
});
|
|
675
653
|
}
|
|
676
654
|
// Wait for the comment to be posted (with 30 second timeout)
|
|
677
|
-
const timeoutPromise = new Promise(
|
|
655
|
+
const timeoutPromise = new Promise(resolve => setTimeout(() => resolve(null), 30000));
|
|
678
656
|
commentId = await Promise.race([commentIdPromise, timeoutPromise]);
|
|
679
657
|
|
|
680
658
|
if (!commentId) {
|
|
681
659
|
if (verbose) {
|
|
682
|
-
await log('⚠️ Interactive mode: Timeout waiting for tool use comment, posting result separately', {
|
|
660
|
+
await log('⚠️ Interactive mode: Timeout waiting for tool use comment, posting result separately', {
|
|
661
|
+
verbose: true,
|
|
662
|
+
});
|
|
683
663
|
}
|
|
684
664
|
}
|
|
685
665
|
}
|
|
@@ -690,11 +670,7 @@ ${createRawJsonSection(data)}`;
|
|
|
690
670
|
|
|
691
671
|
${inputDisplay}
|
|
692
672
|
|
|
693
|
-
${createCollapsible(
|
|
694
|
-
`📤 Output (${statusIcon} ${statusText.toLowerCase()})`,
|
|
695
|
-
'```\n' + escapeMarkdown(truncatedContent) + '\n```',
|
|
696
|
-
true
|
|
697
|
-
)}
|
|
673
|
+
${createCollapsible(`📤 Output (${statusIcon} ${statusText.toLowerCase()})`, '```\n' + escapeMarkdown(truncatedContent) + '\n```', true)}
|
|
698
674
|
|
|
699
675
|
---
|
|
700
676
|
|
|
@@ -706,13 +682,17 @@ ${createRawJsonSection([toolData, data])}`;
|
|
|
706
682
|
if (editSuccess) {
|
|
707
683
|
state.pendingToolCalls.delete(toolUseId);
|
|
708
684
|
if (verbose) {
|
|
709
|
-
await log(`📋 Interactive mode: Tool result merged into comment ${commentId} (${content.length} chars)`, {
|
|
685
|
+
await log(`📋 Interactive mode: Tool result merged into comment ${commentId} (${content.length} chars)`, {
|
|
686
|
+
verbose: true,
|
|
687
|
+
});
|
|
710
688
|
}
|
|
711
689
|
return;
|
|
712
690
|
}
|
|
713
691
|
// If edit failed, fall through to posting new comment
|
|
714
692
|
if (verbose) {
|
|
715
|
-
await log(`⚠️ Interactive mode: Failed to edit comment ${commentId}, posting result separately`, {
|
|
693
|
+
await log(`⚠️ Interactive mode: Failed to edit comment ${commentId}, posting result separately`, {
|
|
694
|
+
verbose: true,
|
|
695
|
+
});
|
|
716
696
|
}
|
|
717
697
|
}
|
|
718
698
|
|
|
@@ -725,17 +705,11 @@ ${createRawJsonSection([toolData, data])}`;
|
|
|
725
705
|
const registryEntry = state.toolUseRegistry.get(toolUseId);
|
|
726
706
|
const standaloneToolName = registryEntry?.toolName;
|
|
727
707
|
const standaloneToolIcon = registryEntry?.toolIcon || '🔧';
|
|
728
|
-
const standaloneHeader = standaloneToolName
|
|
729
|
-
? `${standaloneToolIcon} ${standaloneToolName} tool result`
|
|
730
|
-
: 'Tool result';
|
|
708
|
+
const standaloneHeader = standaloneToolName ? `${standaloneToolIcon} ${standaloneToolName} tool result` : 'Tool result';
|
|
731
709
|
|
|
732
710
|
const comment = `## ${standaloneHeader}
|
|
733
711
|
|
|
734
|
-
${createCollapsible(
|
|
735
|
-
`📤 Output (${statusIcon} ${statusText.toLowerCase()})`,
|
|
736
|
-
'```\n' + escapeMarkdown(truncatedContent) + '\n```',
|
|
737
|
-
true
|
|
738
|
-
)}
|
|
712
|
+
${createCollapsible(`📤 Output (${statusIcon} ${statusText.toLowerCase()})`, '```\n' + escapeMarkdown(truncatedContent) + '\n```', true)}
|
|
739
713
|
|
|
740
714
|
---
|
|
741
715
|
|
|
@@ -745,7 +719,9 @@ ${createRawJsonSection(data)}`;
|
|
|
745
719
|
|
|
746
720
|
if (verbose) {
|
|
747
721
|
const contentLength = content.length;
|
|
748
|
-
await log(`📋 Interactive mode: Tool result posted as separate comment (${contentLength} chars)`, {
|
|
722
|
+
await log(`📋 Interactive mode: Tool result posted as separate comment (${contentLength} chars)`, {
|
|
723
|
+
verbose: true,
|
|
724
|
+
});
|
|
749
725
|
}
|
|
750
726
|
};
|
|
751
727
|
|
|
@@ -753,7 +729,7 @@ ${createRawJsonSection(data)}`;
|
|
|
753
729
|
* Handle result event (session complete)
|
|
754
730
|
* @param {Object} data - Event data
|
|
755
731
|
*/
|
|
756
|
-
const handleResult = async
|
|
732
|
+
const handleResult = async data => {
|
|
757
733
|
const isError = data.is_error || false;
|
|
758
734
|
const statusIcon = isError ? '❌' : '✅';
|
|
759
735
|
const statusText = isError ? 'Session Failed' : 'Session Complete';
|
|
@@ -763,7 +739,7 @@ ${createRawJsonSection(data)}`;
|
|
|
763
739
|
const truncatedResult = truncateMiddle(resultText, {
|
|
764
740
|
maxLines: 50,
|
|
765
741
|
keepStart: 20,
|
|
766
|
-
keepEnd: 20
|
|
742
|
+
keepEnd: 20,
|
|
767
743
|
});
|
|
768
744
|
|
|
769
745
|
// Build stats table
|
|
@@ -819,7 +795,7 @@ ${createRawJsonSection(data)}`;
|
|
|
819
795
|
* Handle unrecognized event types
|
|
820
796
|
* @param {Object} data - Event data
|
|
821
797
|
*/
|
|
822
|
-
const handleUnrecognized = async
|
|
798
|
+
const handleUnrecognized = async data => {
|
|
823
799
|
const eventType = data.type || 'unknown';
|
|
824
800
|
const subtype = data.subtype ? `.${data.subtype}` : '';
|
|
825
801
|
|
|
@@ -842,7 +818,7 @@ ${createRawJsonSection(data)}`;
|
|
|
842
818
|
* @param {Object} data - Parsed JSON object from Claude CLI output
|
|
843
819
|
* @returns {Promise<void>}
|
|
844
820
|
*/
|
|
845
|
-
const processEvent = async
|
|
821
|
+
const processEvent = async data => {
|
|
846
822
|
if (!data || typeof data !== 'object') {
|
|
847
823
|
return;
|
|
848
824
|
}
|
|
@@ -865,9 +841,7 @@ ${createRawJsonSection(data)}`;
|
|
|
865
841
|
|
|
866
842
|
case 'assistant':
|
|
867
843
|
if (data.message && data.message.content) {
|
|
868
|
-
const content = Array.isArray(data.message.content)
|
|
869
|
-
? data.message.content
|
|
870
|
-
: [data.message.content];
|
|
844
|
+
const content = Array.isArray(data.message.content) ? data.message.content : [data.message.content];
|
|
871
845
|
|
|
872
846
|
for (const item of content) {
|
|
873
847
|
if (item.type === 'text' && item.text) {
|
|
@@ -881,9 +855,7 @@ ${createRawJsonSection(data)}`;
|
|
|
881
855
|
|
|
882
856
|
case 'user':
|
|
883
857
|
if (data.message && data.message.content) {
|
|
884
|
-
const content = Array.isArray(data.message.content)
|
|
885
|
-
? data.message.content
|
|
886
|
-
: [data.message.content];
|
|
858
|
+
const content = Array.isArray(data.message.content) ? data.message.content : [data.message.content];
|
|
887
859
|
|
|
888
860
|
for (const item of content) {
|
|
889
861
|
if (item.type === 'tool_result') {
|
|
@@ -933,8 +905,8 @@ ${createRawJsonSection(data)}`;
|
|
|
933
905
|
handleToolUse,
|
|
934
906
|
handleToolResult,
|
|
935
907
|
handleResult,
|
|
936
|
-
handleUnrecognized
|
|
937
|
-
}
|
|
908
|
+
handleUnrecognized,
|
|
909
|
+
},
|
|
938
910
|
};
|
|
939
911
|
};
|
|
940
912
|
|
|
@@ -944,7 +916,7 @@ ${createRawJsonSection(data)}`;
|
|
|
944
916
|
* @param {string} tool - Tool name (claude, opencode, codex)
|
|
945
917
|
* @returns {boolean} Whether interactive mode is supported
|
|
946
918
|
*/
|
|
947
|
-
export const isInteractiveModeSupported =
|
|
919
|
+
export const isInteractiveModeSupported = tool => {
|
|
948
920
|
// Currently only supported for Claude
|
|
949
921
|
return tool === 'claude';
|
|
950
922
|
};
|
|
@@ -963,7 +935,9 @@ export const validateInteractiveModeConfig = async (argv, log) => {
|
|
|
963
935
|
|
|
964
936
|
// Check tool support
|
|
965
937
|
if (!isInteractiveModeSupported(argv.tool)) {
|
|
966
|
-
await log(`⚠️ --interactive-mode is only supported for --tool claude (current: ${argv.tool})`, {
|
|
938
|
+
await log(`⚠️ --interactive-mode is only supported for --tool claude (current: ${argv.tool})`, {
|
|
939
|
+
level: 'warning',
|
|
940
|
+
});
|
|
967
941
|
await log(' Interactive mode will be disabled for this session.', { level: 'warning' });
|
|
968
942
|
return false;
|
|
969
943
|
}
|
|
@@ -988,7 +962,7 @@ export const utils = {
|
|
|
988
962
|
formatCost,
|
|
989
963
|
escapeMarkdown,
|
|
990
964
|
getToolIcon,
|
|
991
|
-
CONFIG
|
|
965
|
+
CONFIG,
|
|
992
966
|
};
|
|
993
967
|
|
|
994
968
|
// Export all functions
|
|
@@ -996,5 +970,5 @@ export default {
|
|
|
996
970
|
createInteractiveHandler,
|
|
997
971
|
isInteractiveModeSupported,
|
|
998
972
|
validateInteractiveModeConfig,
|
|
999
|
-
utils
|
|
973
|
+
utils,
|
|
1000
974
|
};
|
package/src/lenv-reader.lib.mjs
CHANGED
|
@@ -134,12 +134,7 @@ export class LenvReader {
|
|
|
134
134
|
* @returns {Object} - Object with loaded variables
|
|
135
135
|
*/
|
|
136
136
|
async config(options = {}) {
|
|
137
|
-
const {
|
|
138
|
-
path: configPath = '.lenv',
|
|
139
|
-
configuration = null,
|
|
140
|
-
override = false,
|
|
141
|
-
quiet = false
|
|
142
|
-
} = options;
|
|
137
|
+
const { path: configPath = '.lenv', configuration = null, override = false, quiet = false } = options;
|
|
143
138
|
|
|
144
139
|
let envVars = {};
|
|
145
140
|
|