@doingdev/opencode-claude-manager-plugin 0.1.5 → 0.1.7
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/README.md
CHANGED
|
@@ -50,7 +50,7 @@ If you are testing locally, point OpenCode at the local package or plugin file u
|
|
|
50
50
|
|
|
51
51
|
## OpenCode tools
|
|
52
52
|
|
|
53
|
-
- `claude_manager_run` - run a task through Claude with optional splitting and worktrees
|
|
53
|
+
- `claude_manager_run` - run a task through Claude with optional splitting and worktrees; returns a compact output summary and a `runId` for deeper inspection
|
|
54
54
|
- `claude_manager_metadata` - inspect available Claude commands, skills, hooks, and settings
|
|
55
55
|
- `claude_manager_sessions` - list Claude sessions or inspect a saved transcript
|
|
56
56
|
- `claude_manager_runs` - inspect persisted manager run records
|
|
@@ -136,10 +136,14 @@ function normalizeSdkMessage(message) {
|
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
138
|
if (message.type === 'stream_event') {
|
|
139
|
+
const partialText = extractPartialEventText(message.event);
|
|
140
|
+
if (!partialText) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
139
143
|
return {
|
|
140
144
|
type: 'partial',
|
|
141
145
|
sessionId,
|
|
142
|
-
text:
|
|
146
|
+
text: partialText,
|
|
143
147
|
rawType: message.type,
|
|
144
148
|
};
|
|
145
149
|
}
|
|
@@ -191,21 +195,21 @@ function normalizeSdkMessage(message) {
|
|
|
191
195
|
}
|
|
192
196
|
function extractPartialEventText(event) {
|
|
193
197
|
if (!event || typeof event !== 'object') {
|
|
194
|
-
return
|
|
198
|
+
return null;
|
|
195
199
|
}
|
|
196
200
|
const eventRecord = event;
|
|
197
|
-
const eventType = typeof eventRecord.type === 'string' ? eventRecord.type : 'stream_event';
|
|
198
201
|
const delta = eventRecord.delta;
|
|
199
202
|
if (delta && typeof delta === 'object') {
|
|
200
203
|
const deltaRecord = delta;
|
|
201
|
-
if (typeof deltaRecord.text === 'string') {
|
|
204
|
+
if (typeof deltaRecord.text === 'string' && deltaRecord.text.length > 0) {
|
|
202
205
|
return deltaRecord.text;
|
|
203
206
|
}
|
|
204
|
-
if (typeof deltaRecord.partial_json === 'string'
|
|
207
|
+
if (typeof deltaRecord.partial_json === 'string' &&
|
|
208
|
+
deltaRecord.partial_json.length > 0) {
|
|
205
209
|
return deltaRecord.partial_json;
|
|
206
210
|
}
|
|
207
211
|
}
|
|
208
|
-
return
|
|
212
|
+
return null;
|
|
209
213
|
}
|
|
210
214
|
function extractText(payload) {
|
|
211
215
|
if (typeof payload === 'string') {
|
|
@@ -8,6 +8,7 @@ const MANAGER_TOOL_IDS = [
|
|
|
8
8
|
'claude_manager_runs',
|
|
9
9
|
'claude_manager_cleanup_run',
|
|
10
10
|
];
|
|
11
|
+
const RESTRICTED_AGENT_TOOLS = buildRestrictedToolMap(MANAGER_TOOL_IDS);
|
|
11
12
|
export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
12
13
|
const services = getOrCreatePluginServices(worktree);
|
|
13
14
|
return {
|
|
@@ -37,6 +38,7 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
37
38
|
description: 'Primary agent that manages Claude Code sessions through the bundled plugin tools.',
|
|
38
39
|
mode: 'primary',
|
|
39
40
|
color: 'accent',
|
|
41
|
+
tools: RESTRICTED_AGENT_TOOLS,
|
|
40
42
|
permission: managerPermissions,
|
|
41
43
|
prompt: [
|
|
42
44
|
managerPromptRegistry.managerSystemPrompt,
|
|
@@ -49,6 +51,11 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
49
51
|
description: 'Subagent that inspects Claude metadata, prior sessions, and manager runs without changing repository state.',
|
|
50
52
|
mode: 'subagent',
|
|
51
53
|
color: 'info',
|
|
54
|
+
tools: {
|
|
55
|
+
...RESTRICTED_AGENT_TOOLS,
|
|
56
|
+
claude_manager_run: false,
|
|
57
|
+
claude_manager_cleanup_run: false,
|
|
58
|
+
},
|
|
52
59
|
permission: researchPermissions,
|
|
53
60
|
prompt: [
|
|
54
61
|
managerPromptRegistry.subagentSystemPrompt,
|
|
@@ -127,7 +134,7 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
127
134
|
lastProgressSignature = signature;
|
|
128
135
|
context.metadata(progressView);
|
|
129
136
|
});
|
|
130
|
-
return
|
|
137
|
+
return formatManagerRunToolResult(result.run);
|
|
131
138
|
},
|
|
132
139
|
}),
|
|
133
140
|
claude_manager_metadata: tool({
|
|
@@ -243,6 +250,39 @@ function rewriteCommandParts(parts, text) {
|
|
|
243
250
|
});
|
|
244
251
|
return rewrittenParts;
|
|
245
252
|
}
|
|
253
|
+
function buildRestrictedToolMap(toolIds) {
|
|
254
|
+
const tools = { '*': false };
|
|
255
|
+
for (const toolId of toolIds) {
|
|
256
|
+
tools[toolId] = true;
|
|
257
|
+
}
|
|
258
|
+
return tools;
|
|
259
|
+
}
|
|
260
|
+
export function formatManagerRunToolResult(run) {
|
|
261
|
+
const finalSummary = run.finalSummary ?? summarizeSessionOutputs(run.sessions);
|
|
262
|
+
const output = run.sessions.length === 1
|
|
263
|
+
? resolveSessionOutput(run.sessions[0])
|
|
264
|
+
: finalSummary;
|
|
265
|
+
return JSON.stringify({
|
|
266
|
+
runId: run.id,
|
|
267
|
+
status: run.status,
|
|
268
|
+
output,
|
|
269
|
+
finalSummary,
|
|
270
|
+
sessions: run.sessions.map((session) => ({
|
|
271
|
+
title: session.title,
|
|
272
|
+
status: session.status,
|
|
273
|
+
output: resolveSessionOutput(session),
|
|
274
|
+
claudeSessionId: session.claudeSessionId,
|
|
275
|
+
worktreeMode: session.worktreeMode,
|
|
276
|
+
branchName: session.branchName,
|
|
277
|
+
turns: session.turns,
|
|
278
|
+
totalCostUsd: session.totalCostUsd,
|
|
279
|
+
})),
|
|
280
|
+
inspectRun: {
|
|
281
|
+
tool: 'claude_manager_runs',
|
|
282
|
+
runId: run.id,
|
|
283
|
+
},
|
|
284
|
+
}, null, 2);
|
|
285
|
+
}
|
|
246
286
|
function buildRunProgressView(run) {
|
|
247
287
|
const completed = run.sessions.filter((session) => session.status === 'completed').length;
|
|
248
288
|
const failed = run.sessions.filter((session) => session.status === 'failed').length;
|
|
@@ -284,18 +324,26 @@ function formatSessionActivity(session) {
|
|
|
284
324
|
parts.push(session.claudeSessionId);
|
|
285
325
|
}
|
|
286
326
|
const latestEvent = findLatestDisplayEvent(session.events);
|
|
287
|
-
if (
|
|
288
|
-
parts.push(`${latestEvent.type}: ${truncateForDisplay(latestEvent.text, 120)}`);
|
|
289
|
-
}
|
|
290
|
-
else if (session.finalText) {
|
|
327
|
+
if (session.finalText) {
|
|
291
328
|
parts.push(truncateForDisplay(session.finalText, 120));
|
|
292
329
|
}
|
|
293
330
|
else if (session.error) {
|
|
294
331
|
parts.push(truncateForDisplay(session.error, 120));
|
|
295
332
|
}
|
|
333
|
+
else if (latestEvent) {
|
|
334
|
+
parts.push(`${latestEvent.type}: ${truncateForDisplay(latestEvent.text, 120)}`);
|
|
335
|
+
}
|
|
296
336
|
return parts.join(' | ');
|
|
297
337
|
}
|
|
298
338
|
function findLatestDisplayEvent(events) {
|
|
339
|
+
const reversedEvents = [...events].reverse();
|
|
340
|
+
const preferredEvent = reversedEvents.find((event) => (event.type === 'result' ||
|
|
341
|
+
event.type === 'error' ||
|
|
342
|
+
event.type === 'assistant') &&
|
|
343
|
+
Boolean(event.text.trim()));
|
|
344
|
+
if (preferredEvent) {
|
|
345
|
+
return preferredEvent;
|
|
346
|
+
}
|
|
299
347
|
return [...events]
|
|
300
348
|
.reverse()
|
|
301
349
|
.find((event) => event.type !== 'partial' && Boolean(event.text.trim()));
|
|
@@ -307,3 +355,15 @@ function truncateForDisplay(text, maxLength) {
|
|
|
307
355
|
}
|
|
308
356
|
return `${normalized.slice(0, maxLength - 3)}...`;
|
|
309
357
|
}
|
|
358
|
+
function summarizeSessionOutputs(sessions) {
|
|
359
|
+
return sessions
|
|
360
|
+
.map((session) => `${session.title}: ${resolveSessionOutput(session)}`)
|
|
361
|
+
.join('\n');
|
|
362
|
+
}
|
|
363
|
+
function resolveSessionOutput(session) {
|
|
364
|
+
const latestEvent = findLatestDisplayEvent(session.events);
|
|
365
|
+
return (session.finalText?.trim() ||
|
|
366
|
+
session.error?.trim() ||
|
|
367
|
+
latestEvent?.text.trim() ||
|
|
368
|
+
session.status);
|
|
369
|
+
}
|