@doingdev/opencode-claude-manager-plugin 0.1.30 → 0.1.32
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/dist/claude/claude-agent-sdk-adapter.js +20 -23
- package/dist/claude/claude-session.service.d.ts +2 -7
- package/dist/claude/claude-session.service.js +1 -6
- package/dist/claude/tool-approval-manager.js +3 -9
- package/dist/index.d.ts +1 -1
- package/dist/manager/context-tracker.js +2 -4
- package/dist/manager/persistent-manager.d.ts +3 -0
- package/dist/manager/persistent-manager.js +3 -0
- package/dist/plugin/agent-hierarchy.d.ts +1 -1
- package/dist/plugin/agent-hierarchy.js +3 -14
- package/dist/plugin/claude-manager.plugin.js +36 -59
- package/dist/plugin/service-factory.js +1 -4
- package/dist/state/file-run-state-store.js +2 -4
- package/dist/state/transcript-store.js +2 -2
- package/dist/types/contracts.d.ts +1 -24
- package/dist/util/fs-helpers.js +1 -3
- package/package.json +1 -2
|
@@ -19,9 +19,7 @@ function mergeSkillIntoAllowedTools(allowedTools, disallowedTools) {
|
|
|
19
19
|
if (allowedTools === undefined) {
|
|
20
20
|
return ['Skill'];
|
|
21
21
|
}
|
|
22
|
-
return allowedTools.includes('Skill')
|
|
23
|
-
? allowedTools
|
|
24
|
-
: [...allowedTools, 'Skill'];
|
|
22
|
+
return allowedTools.includes('Skill') ? allowedTools : [...allowedTools, 'Skill'];
|
|
25
23
|
}
|
|
26
24
|
export class ClaudeAgentSdkAdapter {
|
|
27
25
|
sdkFacade;
|
|
@@ -262,11 +260,8 @@ function normalizeSdkMessages(message, includePartials) {
|
|
|
262
260
|
return normalizeUserSdkMessage(message, sessionId);
|
|
263
261
|
}
|
|
264
262
|
if (message.type === 'tool_progress') {
|
|
265
|
-
const toolName = 'tool_name' in message && typeof message.tool_name === 'string'
|
|
266
|
-
|
|
267
|
-
: 'tool';
|
|
268
|
-
const elapsed = 'elapsed_time_seconds' in message &&
|
|
269
|
-
typeof message.elapsed_time_seconds === 'number'
|
|
263
|
+
const toolName = 'tool_name' in message && typeof message.tool_name === 'string' ? message.tool_name : 'tool';
|
|
264
|
+
const elapsed = 'elapsed_time_seconds' in message && typeof message.elapsed_time_seconds === 'number'
|
|
270
265
|
? message.elapsed_time_seconds
|
|
271
266
|
: 0;
|
|
272
267
|
return [
|
|
@@ -279,9 +274,7 @@ function normalizeSdkMessages(message, includePartials) {
|
|
|
279
274
|
];
|
|
280
275
|
}
|
|
281
276
|
if (message.type === 'tool_use_summary') {
|
|
282
|
-
const summary = 'summary' in message && typeof message.summary === 'string'
|
|
283
|
-
? message.summary
|
|
284
|
-
: '';
|
|
277
|
+
const summary = 'summary' in message && typeof message.summary === 'string' ? message.summary : '';
|
|
285
278
|
return [
|
|
286
279
|
{
|
|
287
280
|
type: 'tool_summary',
|
|
@@ -359,9 +352,7 @@ function normalizeUserSdkMessage(message, sessionId) {
|
|
|
359
352
|
if (message.tool_use_result !== undefined) {
|
|
360
353
|
const extra = truncateJsonish(message.tool_use_result, 1500);
|
|
361
354
|
if (extra) {
|
|
362
|
-
payload = payload
|
|
363
|
-
? `${payload}\n[tool_use_result] ${extra}`
|
|
364
|
-
: `[tool_use_result] ${extra}`;
|
|
355
|
+
payload = payload ? `${payload}\n[tool_use_result] ${extra}` : `[tool_use_result] ${extra}`;
|
|
365
356
|
}
|
|
366
357
|
}
|
|
367
358
|
payload = truncateString(payload, USER_MESSAGE_MAX);
|
|
@@ -439,8 +430,7 @@ function extractPartialEventText(event) {
|
|
|
439
430
|
if (typeof delta.text === 'string' && delta.text.length > 0) {
|
|
440
431
|
return delta.text;
|
|
441
432
|
}
|
|
442
|
-
if (typeof delta.partial_json === 'string' &&
|
|
443
|
-
delta.partial_json.length > 0) {
|
|
433
|
+
if (typeof delta.partial_json === 'string' && delta.partial_json.length > 0) {
|
|
444
434
|
return delta.partial_json;
|
|
445
435
|
}
|
|
446
436
|
}
|
|
@@ -462,8 +452,7 @@ function extractText(payload) {
|
|
|
462
452
|
if (typeof contentPart.text === 'string') {
|
|
463
453
|
return contentPart.text;
|
|
464
454
|
}
|
|
465
|
-
if (contentPart.type === 'tool_use' &&
|
|
466
|
-
typeof contentPart.name === 'string') {
|
|
455
|
+
if (contentPart.type === 'tool_use' && typeof contentPart.name === 'string') {
|
|
467
456
|
return `[tool:${contentPart.name}]`;
|
|
468
457
|
}
|
|
469
458
|
return '';
|
|
@@ -506,13 +495,21 @@ function extractUsageFromResult(message) {
|
|
|
506
495
|
result.outputTokens = usage.output_tokens;
|
|
507
496
|
}
|
|
508
497
|
}
|
|
509
|
-
// Extract contextWindow from modelUsage
|
|
510
|
-
const modelUsage = message.
|
|
498
|
+
// Extract contextWindow from modelUsage (camelCase) or model_usage (snake_case)
|
|
499
|
+
const modelUsage = message.modelUsage ??
|
|
500
|
+
message.model_usage;
|
|
511
501
|
if (isRecord(modelUsage)) {
|
|
512
502
|
for (const model of Object.values(modelUsage)) {
|
|
513
|
-
if (isRecord(model)
|
|
514
|
-
|
|
515
|
-
|
|
503
|
+
if (isRecord(model)) {
|
|
504
|
+
const cw = typeof model.contextWindow === 'number'
|
|
505
|
+
? model.contextWindow
|
|
506
|
+
: typeof model.context_window === 'number'
|
|
507
|
+
? model.context_window
|
|
508
|
+
: undefined;
|
|
509
|
+
if (cw !== undefined) {
|
|
510
|
+
result.contextWindowSize = cw;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
516
513
|
}
|
|
517
514
|
}
|
|
518
515
|
}
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import type { ClaudeCapabilitySnapshot,
|
|
2
|
-
import type { ClaudeMetadataService } from '../metadata/claude-metadata.service.js';
|
|
1
|
+
import type { ClaudeCapabilitySnapshot, ClaudeSessionRunResult, ClaudeSessionSummary, ClaudeSessionTranscriptMessage, RunClaudeSessionInput } from '../types/contracts.js';
|
|
3
2
|
import type { ClaudeAgentSdkAdapter, ClaudeSessionEventHandler } from './claude-agent-sdk-adapter.js';
|
|
4
3
|
export declare class ClaudeSessionService {
|
|
5
4
|
private readonly sdkAdapter;
|
|
6
|
-
|
|
7
|
-
constructor(sdkAdapter: ClaudeAgentSdkAdapter, metadataService: ClaudeMetadataService);
|
|
5
|
+
constructor(sdkAdapter: ClaudeAgentSdkAdapter);
|
|
8
6
|
runTask(input: RunClaudeSessionInput, onEvent?: ClaudeSessionEventHandler): Promise<ClaudeSessionRunResult>;
|
|
9
7
|
listSessions(cwd?: string): Promise<ClaudeSessionSummary[]>;
|
|
10
8
|
getTranscript(sessionId: string, cwd?: string): Promise<ClaudeSessionTranscriptMessage[]>;
|
|
11
|
-
inspectRepository(cwd: string, options?: {
|
|
12
|
-
includeSdkProbe?: boolean;
|
|
13
|
-
}): Promise<ClaudeMetadataSnapshot>;
|
|
14
9
|
probeCapabilities(cwd: string): Promise<ClaudeCapabilitySnapshot>;
|
|
15
10
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export class ClaudeSessionService {
|
|
2
2
|
sdkAdapter;
|
|
3
|
-
|
|
4
|
-
constructor(sdkAdapter, metadataService) {
|
|
3
|
+
constructor(sdkAdapter) {
|
|
5
4
|
this.sdkAdapter = sdkAdapter;
|
|
6
|
-
this.metadataService = metadataService;
|
|
7
5
|
}
|
|
8
6
|
runTask(input, onEvent) {
|
|
9
7
|
return this.sdkAdapter.runSession(input, onEvent);
|
|
@@ -14,9 +12,6 @@ export class ClaudeSessionService {
|
|
|
14
12
|
getTranscript(sessionId, cwd) {
|
|
15
13
|
return this.sdkAdapter.getTranscript(sessionId, cwd);
|
|
16
14
|
}
|
|
17
|
-
inspectRepository(cwd, options) {
|
|
18
|
-
return this.metadataService.collect(cwd, options);
|
|
19
|
-
}
|
|
20
15
|
probeCapabilities(cwd) {
|
|
21
16
|
return this.sdkAdapter.probeCapabilities(cwd);
|
|
22
17
|
}
|
|
@@ -137,9 +137,7 @@ export class ToolApprovalManager {
|
|
|
137
137
|
const matchedRule = this.findMatchingRule(toolName, inputJson);
|
|
138
138
|
const action = matchedRule?.action ?? this.policy.defaultAction;
|
|
139
139
|
const denyMessage = action === 'deny'
|
|
140
|
-
? (matchedRule?.denyMessage ??
|
|
141
|
-
this.policy.defaultDenyMessage ??
|
|
142
|
-
'Denied by policy.')
|
|
140
|
+
? (matchedRule?.denyMessage ?? this.policy.defaultDenyMessage ?? 'Denied by policy.')
|
|
143
141
|
: undefined;
|
|
144
142
|
this.recordDecision({
|
|
145
143
|
timestamp: new Date().toISOString(),
|
|
@@ -174,9 +172,7 @@ export class ToolApprovalManager {
|
|
|
174
172
|
this.policy = { ...policy, rules: [...policy.rules] };
|
|
175
173
|
}
|
|
176
174
|
addRule(rule, position) {
|
|
177
|
-
if (position !== undefined &&
|
|
178
|
-
position >= 0 &&
|
|
179
|
-
position < this.policy.rules.length) {
|
|
175
|
+
if (position !== undefined && position >= 0 && position < this.policy.rules.length) {
|
|
180
176
|
this.policy.rules.splice(position, 0, rule);
|
|
181
177
|
}
|
|
182
178
|
else {
|
|
@@ -223,9 +219,7 @@ function matchesToolPattern(pattern, toolName) {
|
|
|
223
219
|
if (!pattern.includes('*')) {
|
|
224
220
|
return pattern === toolName;
|
|
225
221
|
}
|
|
226
|
-
const regex = new RegExp('^' +
|
|
227
|
-
pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') +
|
|
228
|
-
'$');
|
|
222
|
+
const regex = new RegExp('^' + pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') + '$');
|
|
229
223
|
return regex.test(toolName);
|
|
230
224
|
}
|
|
231
225
|
function safeJsonStringify(value) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Plugin } from '@opencode-ai/plugin';
|
|
2
2
|
import { ClaudeManagerPlugin } from './plugin/claude-manager.plugin.js';
|
|
3
|
-
export type { ClaudeCapabilitySnapshot,
|
|
3
|
+
export type { ClaudeCapabilitySnapshot, ClaudeSessionRunResult, ClaudeSessionSummary, ClaudeSessionTranscriptMessage, ManagerPromptRegistry, RunClaudeSessionInput, SessionContextSnapshot, GitDiffResult, GitOperationResult, PersistentRunRecord, PersistentRunResult, ActiveSessionState, ContextWarningLevel, SessionMode, LiveTailEvent, ToolOutputPreview, ToolApprovalRule, ToolApprovalPolicy, ToolApprovalDecision, } from './types/contracts.js';
|
|
4
4
|
export { SessionLiveTailer } from './claude/session-live-tailer.js';
|
|
5
5
|
export { ClaudeManagerPlugin };
|
|
6
6
|
export declare const plugin: Plugin;
|
|
@@ -19,8 +19,7 @@ export class ContextTracker {
|
|
|
19
19
|
}
|
|
20
20
|
if (result.inputTokens !== undefined) {
|
|
21
21
|
// If input tokens dropped significantly, compaction likely occurred
|
|
22
|
-
if (this.latestInputTokens !== null &&
|
|
23
|
-
result.inputTokens < this.latestInputTokens * 0.5) {
|
|
22
|
+
if (this.latestInputTokens !== null && result.inputTokens < this.latestInputTokens * 0.5) {
|
|
24
23
|
this.compactionCount++;
|
|
25
24
|
}
|
|
26
25
|
this.latestInputTokens = result.inputTokens;
|
|
@@ -85,8 +84,7 @@ export class ContextTracker {
|
|
|
85
84
|
return null;
|
|
86
85
|
}
|
|
87
86
|
isAboveTokenThreshold(thresholdTokens = 200_000) {
|
|
88
|
-
return
|
|
89
|
-
this.latestInputTokens >= thresholdTokens);
|
|
87
|
+
return this.latestInputTokens !== null && this.latestInputTokens >= thresholdTokens;
|
|
90
88
|
}
|
|
91
89
|
reset() {
|
|
92
90
|
this.totalTurns = 0;
|
|
@@ -26,6 +26,9 @@ export class PersistentManager {
|
|
|
26
26
|
finalText: result.finalText,
|
|
27
27
|
turns: result.turns,
|
|
28
28
|
totalCostUsd: result.totalCostUsd,
|
|
29
|
+
inputTokens: result.inputTokens,
|
|
30
|
+
outputTokens: result.outputTokens,
|
|
31
|
+
contextWindowSize: result.contextWindowSize,
|
|
29
32
|
context: this.sessionController.getContextSnapshot(),
|
|
30
33
|
};
|
|
31
34
|
}
|
|
@@ -11,7 +11,7 @@ export declare const AGENT_CTO = "cto";
|
|
|
11
11
|
export declare const AGENT_ENGINEER_PLAN = "engineer_plan";
|
|
12
12
|
export declare const AGENT_ENGINEER_BUILD = "engineer_build";
|
|
13
13
|
/** All restricted tool IDs (union of all domain groups) */
|
|
14
|
-
export declare const ALL_RESTRICTED_TOOL_IDS: readonly ["engineer_send", "engineer_send_plan", "engineer_send_build", "engineer_compact", "engineer_clear", "engineer_status", "
|
|
14
|
+
export declare const ALL_RESTRICTED_TOOL_IDS: readonly ["engineer_send", "engineer_send_plan", "engineer_send_build", "engineer_compact", "engineer_clear", "engineer_status", "engineer_sessions", "engineer_runs", "git_diff", "git_commit", "git_reset", "approval_policy", "approval_decisions", "approval_update"];
|
|
15
15
|
type ToolPermission = 'allow' | 'ask' | 'deny';
|
|
16
16
|
type AgentPermission = {
|
|
17
17
|
'*'?: ToolPermission;
|
|
@@ -20,7 +20,6 @@ const ENGINEER_SHARED_TOOL_IDS = [
|
|
|
20
20
|
'engineer_compact',
|
|
21
21
|
'engineer_clear',
|
|
22
22
|
'engineer_status',
|
|
23
|
-
'engineer_metadata',
|
|
24
23
|
'engineer_sessions',
|
|
25
24
|
'engineer_runs',
|
|
26
25
|
];
|
|
@@ -32,23 +31,13 @@ const ENGINEER_TOOL_IDS = [
|
|
|
32
31
|
...ENGINEER_SHARED_TOOL_IDS,
|
|
33
32
|
];
|
|
34
33
|
/** Tools for the engineer_plan wrapper (plan-mode send + shared) */
|
|
35
|
-
const ENGINEER_PLAN_TOOL_IDS = [
|
|
36
|
-
'engineer_send_plan',
|
|
37
|
-
...ENGINEER_SHARED_TOOL_IDS,
|
|
38
|
-
];
|
|
34
|
+
const ENGINEER_PLAN_TOOL_IDS = ['engineer_send_plan', ...ENGINEER_SHARED_TOOL_IDS];
|
|
39
35
|
/** Tools for the engineer_build wrapper (build-mode send + shared) */
|
|
40
|
-
const ENGINEER_BUILD_TOOL_IDS = [
|
|
41
|
-
'engineer_send_build',
|
|
42
|
-
...ENGINEER_SHARED_TOOL_IDS,
|
|
43
|
-
];
|
|
36
|
+
const ENGINEER_BUILD_TOOL_IDS = ['engineer_send_build', ...ENGINEER_SHARED_TOOL_IDS];
|
|
44
37
|
/** Git tools — owned by CTO */
|
|
45
38
|
const GIT_TOOL_IDS = ['git_diff', 'git_commit', 'git_reset'];
|
|
46
39
|
/** Approval tools — owned by CTO */
|
|
47
|
-
const APPROVAL_TOOL_IDS = [
|
|
48
|
-
'approval_policy',
|
|
49
|
-
'approval_decisions',
|
|
50
|
-
'approval_update',
|
|
51
|
-
];
|
|
40
|
+
const APPROVAL_TOOL_IDS = ['approval_policy', 'approval_decisions', 'approval_update'];
|
|
52
41
|
/** All restricted tool IDs (union of all domain groups) */
|
|
53
42
|
export const ALL_RESTRICTED_TOOL_IDS = [
|
|
54
43
|
...ENGINEER_TOOL_IDS,
|
|
@@ -10,20 +10,16 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
10
10
|
await services.manager.clearSession(cwd);
|
|
11
11
|
}
|
|
12
12
|
const hasActiveSession = services.manager.getStatus().sessionId !== null;
|
|
13
|
-
const promptPreview = args.message.length > 100
|
|
14
|
-
? args.message.slice(0, 100) + '...'
|
|
15
|
-
: args.message;
|
|
13
|
+
const promptPreview = args.message.length > 100 ? args.message.slice(0, 100) + '...' : args.message;
|
|
16
14
|
context.metadata({
|
|
17
|
-
title: hasActiveSession
|
|
18
|
-
? 'Claude Code: Resuming session...'
|
|
19
|
-
: 'Claude Code: Initializing...',
|
|
15
|
+
title: hasActiveSession ? 'Claude Code: Resuming session...' : 'Claude Code: Initializing...',
|
|
20
16
|
metadata: {
|
|
21
17
|
sessionId: services.manager.getStatus().sessionId,
|
|
22
18
|
prompt: promptPreview,
|
|
23
19
|
},
|
|
24
20
|
});
|
|
25
|
-
let turnsSoFar
|
|
26
|
-
let costSoFar
|
|
21
|
+
let turnsSoFar;
|
|
22
|
+
let costSoFar;
|
|
27
23
|
const result = await services.manager.sendMessage(cwd, args.message, {
|
|
28
24
|
model: args.model,
|
|
29
25
|
effort: args.effort,
|
|
@@ -36,7 +32,7 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
36
32
|
if (event.totalCostUsd !== undefined) {
|
|
37
33
|
costSoFar = event.totalCostUsd;
|
|
38
34
|
}
|
|
39
|
-
const
|
|
35
|
+
const usageSuffix = formatLiveUsage(turnsSoFar, costSoFar);
|
|
40
36
|
if (event.type === 'tool_call') {
|
|
41
37
|
let toolName = 'tool';
|
|
42
38
|
let inputPreview = '';
|
|
@@ -44,20 +40,15 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
44
40
|
const parsed = JSON.parse(event.text);
|
|
45
41
|
toolName = parsed.name ?? 'tool';
|
|
46
42
|
if (parsed.input) {
|
|
47
|
-
const inputStr = typeof parsed.input === 'string'
|
|
48
|
-
|
|
49
|
-
: JSON.stringify(parsed.input);
|
|
50
|
-
inputPreview =
|
|
51
|
-
inputStr.length > 150
|
|
52
|
-
? inputStr.slice(0, 150) + '...'
|
|
53
|
-
: inputStr;
|
|
43
|
+
const inputStr = typeof parsed.input === 'string' ? parsed.input : JSON.stringify(parsed.input);
|
|
44
|
+
inputPreview = inputStr.length > 150 ? inputStr.slice(0, 150) + '...' : inputStr;
|
|
54
45
|
}
|
|
55
46
|
}
|
|
56
47
|
catch {
|
|
57
48
|
// ignore parse errors
|
|
58
49
|
}
|
|
59
50
|
context.metadata({
|
|
60
|
-
title: `Claude Code: Running ${toolName}
|
|
51
|
+
title: `Claude Code: Running ${toolName}...${usageSuffix}`,
|
|
61
52
|
metadata: {
|
|
62
53
|
sessionId: event.sessionId,
|
|
63
54
|
type: event.type,
|
|
@@ -67,11 +58,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
67
58
|
});
|
|
68
59
|
}
|
|
69
60
|
else if (event.type === 'assistant') {
|
|
70
|
-
const thinkingPreview = event.text.length > 150
|
|
71
|
-
? event.text.slice(0, 150) + '...'
|
|
72
|
-
: event.text;
|
|
61
|
+
const thinkingPreview = event.text.length > 150 ? event.text.slice(0, 150) + '...' : event.text;
|
|
73
62
|
context.metadata({
|
|
74
|
-
title: `Claude Code: Thinking
|
|
63
|
+
title: `Claude Code: Thinking...${usageSuffix}`,
|
|
75
64
|
metadata: {
|
|
76
65
|
sessionId: event.sessionId,
|
|
77
66
|
type: event.type,
|
|
@@ -89,11 +78,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
89
78
|
});
|
|
90
79
|
}
|
|
91
80
|
else if (event.type === 'user') {
|
|
92
|
-
const preview = event.text.length > 200
|
|
93
|
-
? event.text.slice(0, 200) + '...'
|
|
94
|
-
: event.text;
|
|
81
|
+
const preview = event.text.length > 200 ? event.text.slice(0, 200) + '...' : event.text;
|
|
95
82
|
context.metadata({
|
|
96
|
-
title: `Claude Code: Tool result
|
|
83
|
+
title: `Claude Code: Tool result${usageSuffix}`,
|
|
97
84
|
metadata: {
|
|
98
85
|
sessionId: event.sessionId,
|
|
99
86
|
type: event.type,
|
|
@@ -113,7 +100,7 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
113
100
|
// ignore
|
|
114
101
|
}
|
|
115
102
|
context.metadata({
|
|
116
|
-
title: `Claude Code: ${toolName} running ${elapsed > 0 ? `(${elapsed.toFixed(0)}s)` : ''}
|
|
103
|
+
title: `Claude Code: ${toolName} running ${elapsed > 0 ? `(${elapsed.toFixed(0)}s)` : ''}...${usageSuffix}`,
|
|
117
104
|
metadata: {
|
|
118
105
|
sessionId: event.sessionId,
|
|
119
106
|
type: event.type,
|
|
@@ -123,11 +110,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
123
110
|
});
|
|
124
111
|
}
|
|
125
112
|
else if (event.type === 'tool_summary') {
|
|
126
|
-
const summary = event.text.length > 200
|
|
127
|
-
? event.text.slice(0, 200) + '...'
|
|
128
|
-
: event.text;
|
|
113
|
+
const summary = event.text.length > 200 ? event.text.slice(0, 200) + '...' : event.text;
|
|
129
114
|
context.metadata({
|
|
130
|
-
title: `Claude Code: Tool done
|
|
115
|
+
title: `Claude Code: Tool done${usageSuffix}`,
|
|
131
116
|
metadata: {
|
|
132
117
|
sessionId: event.sessionId,
|
|
133
118
|
type: event.type,
|
|
@@ -136,11 +121,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
136
121
|
});
|
|
137
122
|
}
|
|
138
123
|
else if (event.type === 'partial') {
|
|
139
|
-
const delta = event.text.length > 200
|
|
140
|
-
? event.text.slice(0, 200) + '...'
|
|
141
|
-
: event.text;
|
|
124
|
+
const delta = event.text.length > 200 ? event.text.slice(0, 200) + '...' : event.text;
|
|
142
125
|
context.metadata({
|
|
143
|
-
title: `Claude Code: Writing
|
|
126
|
+
title: `Claude Code: Writing...${usageSuffix}`,
|
|
144
127
|
metadata: {
|
|
145
128
|
sessionId: event.sessionId,
|
|
146
129
|
type: event.type,
|
|
@@ -187,6 +170,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
187
170
|
finalText: result.finalText,
|
|
188
171
|
turns: result.turns,
|
|
189
172
|
totalCostUsd: result.totalCostUsd,
|
|
173
|
+
inputTokens: result.inputTokens,
|
|
174
|
+
outputTokens: result.outputTokens,
|
|
175
|
+
contextWindowSize: result.contextWindowSize,
|
|
190
176
|
context: result.context,
|
|
191
177
|
contextWarning,
|
|
192
178
|
toolOutputs: toolOutputs.length > 0 ? toolOutputs : undefined,
|
|
@@ -210,9 +196,7 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
210
196
|
model: tool.schema
|
|
211
197
|
.enum(['claude-opus-4-6', 'claude-sonnet-4-6', 'claude-sonnet-4-5'])
|
|
212
198
|
.optional(),
|
|
213
|
-
effort: tool.schema
|
|
214
|
-
.enum(['low', 'medium', 'high', 'max'])
|
|
215
|
-
.default('high'),
|
|
199
|
+
effort: tool.schema.enum(['low', 'medium', 'high', 'max']).default('high'),
|
|
216
200
|
mode: tool.schema.enum(['plan', 'free']).default('free'),
|
|
217
201
|
freshSession: tool.schema.boolean().default(false),
|
|
218
202
|
cwd: tool.schema.string().optional(),
|
|
@@ -231,9 +215,7 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
231
215
|
model: tool.schema
|
|
232
216
|
.enum(['claude-opus-4-6', 'claude-sonnet-4-6', 'claude-sonnet-4-5'])
|
|
233
217
|
.optional(),
|
|
234
|
-
effort: tool.schema
|
|
235
|
-
.enum(['low', 'medium', 'high', 'max'])
|
|
236
|
-
.default('high'),
|
|
218
|
+
effort: tool.schema.enum(['low', 'medium', 'high', 'max']).default('high'),
|
|
237
219
|
freshSession: tool.schema.boolean().default(false),
|
|
238
220
|
cwd: tool.schema.string().optional(),
|
|
239
221
|
},
|
|
@@ -253,9 +235,7 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
253
235
|
model: tool.schema
|
|
254
236
|
.enum(['claude-opus-4-6', 'claude-sonnet-4-6', 'claude-sonnet-4-5'])
|
|
255
237
|
.optional(),
|
|
256
|
-
effort: tool.schema
|
|
257
|
-
.enum(['low', 'medium', 'high', 'max'])
|
|
258
|
-
.default('high'),
|
|
238
|
+
effort: tool.schema.enum(['low', 'medium', 'high', 'max']).default('high'),
|
|
259
239
|
freshSession: tool.schema.boolean().default(false),
|
|
260
240
|
cwd: tool.schema.string().optional(),
|
|
261
241
|
},
|
|
@@ -361,22 +341,6 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
361
341
|
}, null, 2);
|
|
362
342
|
},
|
|
363
343
|
}),
|
|
364
|
-
engineer_metadata: tool({
|
|
365
|
-
description: 'Inspect Claude slash commands, skills, hooks, and repo settings.',
|
|
366
|
-
args: {
|
|
367
|
-
cwd: tool.schema.string().optional(),
|
|
368
|
-
includeSdkProbe: tool.schema.boolean().default(false),
|
|
369
|
-
},
|
|
370
|
-
async execute(args, context) {
|
|
371
|
-
annotateToolRun(context, 'Collecting Claude metadata', {
|
|
372
|
-
includeSdkProbe: args.includeSdkProbe,
|
|
373
|
-
});
|
|
374
|
-
const metadata = await services.sessions.inspectRepository(args.cwd ?? context.worktree, {
|
|
375
|
-
includeSdkProbe: args.includeSdkProbe,
|
|
376
|
-
});
|
|
377
|
-
return JSON.stringify(metadata, null, 2);
|
|
378
|
-
},
|
|
379
|
-
}),
|
|
380
344
|
engineer_sessions: tool({
|
|
381
345
|
description: 'List Claude sessions or inspect a saved transcript. ' +
|
|
382
346
|
'When sessionId is provided, returns both SDK transcript and local events.',
|
|
@@ -511,6 +475,19 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
511
475
|
function annotateToolRun(context, title, metadata) {
|
|
512
476
|
context.metadata({ title, metadata });
|
|
513
477
|
}
|
|
478
|
+
function formatLiveUsage(turns, cost) {
|
|
479
|
+
if (turns === undefined && cost === undefined) {
|
|
480
|
+
return '';
|
|
481
|
+
}
|
|
482
|
+
const parts = [];
|
|
483
|
+
if (turns !== undefined) {
|
|
484
|
+
parts.push(`${turns} turns`);
|
|
485
|
+
}
|
|
486
|
+
if (cost !== undefined) {
|
|
487
|
+
parts.push(`$${cost.toFixed(4)}`);
|
|
488
|
+
}
|
|
489
|
+
return ` (${parts.join(', ')})`;
|
|
490
|
+
}
|
|
514
491
|
function formatContextWarning(context) {
|
|
515
492
|
const { warningLevel, estimatedContextPercent, totalTurns, totalCostUsd } = context;
|
|
516
493
|
if (warningLevel === 'ok' || estimatedContextPercent === null) {
|
|
@@ -2,8 +2,6 @@ import { ClaudeAgentSdkAdapter } from '../claude/claude-agent-sdk-adapter.js';
|
|
|
2
2
|
import { ClaudeSessionService } from '../claude/claude-session.service.js';
|
|
3
3
|
import { SessionLiveTailer } from '../claude/session-live-tailer.js';
|
|
4
4
|
import { ToolApprovalManager } from '../claude/tool-approval-manager.js';
|
|
5
|
-
import { ClaudeMetadataService } from '../metadata/claude-metadata.service.js';
|
|
6
|
-
import { RepoClaudeConfigReader } from '../metadata/repo-claude-config-reader.js';
|
|
7
5
|
import { FileRunStateStore } from '../state/file-run-state-store.js';
|
|
8
6
|
import { TranscriptStore } from '../state/transcript-store.js';
|
|
9
7
|
import { ContextTracker } from '../manager/context-tracker.js';
|
|
@@ -19,8 +17,7 @@ export function getOrCreatePluginServices(worktree) {
|
|
|
19
17
|
}
|
|
20
18
|
const approvalManager = new ToolApprovalManager();
|
|
21
19
|
const sdkAdapter = new ClaudeAgentSdkAdapter(undefined, approvalManager);
|
|
22
|
-
const
|
|
23
|
-
const sessionService = new ClaudeSessionService(sdkAdapter, metadataService);
|
|
20
|
+
const sessionService = new ClaudeSessionService(sdkAdapter);
|
|
24
21
|
const contextTracker = new ContextTracker();
|
|
25
22
|
const sessionController = new SessionController(sdkAdapter, contextTracker, managerPromptRegistry.engineerSessionPrompt, managerPromptRegistry.modePrefixes);
|
|
26
23
|
const gitOps = new GitOperations(worktree);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { isFileNotFoundError, writeJsonAtomically
|
|
3
|
+
import { isFileNotFoundError, writeJsonAtomically } from '../util/fs-helpers.js';
|
|
4
4
|
export class FileRunStateStore {
|
|
5
5
|
baseDirectoryName;
|
|
6
6
|
writeQueues = new Map();
|
|
@@ -70,9 +70,7 @@ export class FileRunStateStore {
|
|
|
70
70
|
}
|
|
71
71
|
async enqueueWrite(key, operation) {
|
|
72
72
|
const previousOperation = this.writeQueues.get(key) ?? Promise.resolve();
|
|
73
|
-
const resultPromise = previousOperation
|
|
74
|
-
.catch(() => undefined)
|
|
75
|
-
.then(operation);
|
|
73
|
+
const resultPromise = previousOperation.catch(() => undefined).then(operation);
|
|
76
74
|
const settledPromise = resultPromise.then(() => undefined, () => undefined);
|
|
77
75
|
this.writeQueues.set(key, settledPromise);
|
|
78
76
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { appendTranscriptEvents, stripTrailingPartials
|
|
4
|
-
import { isFileNotFoundError, writeJsonAtomically
|
|
3
|
+
import { appendTranscriptEvents, stripTrailingPartials } from '../util/transcript-append.js';
|
|
4
|
+
import { isFileNotFoundError, writeJsonAtomically } from '../util/fs-helpers.js';
|
|
5
5
|
export class TranscriptStore {
|
|
6
6
|
baseDirectoryName;
|
|
7
7
|
constructor(baseDirectoryName = '.claude-manager') {
|
|
@@ -13,7 +13,6 @@ export interface ManagerPromptRegistry {
|
|
|
13
13
|
critical: string;
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
|
-
export type ClaudeSettingSource = 'user' | 'project' | 'local';
|
|
17
16
|
export type SessionMode = 'plan' | 'free';
|
|
18
17
|
export interface ClaudeCommandMetadata {
|
|
19
18
|
name: string;
|
|
@@ -22,34 +21,12 @@ export interface ClaudeCommandMetadata {
|
|
|
22
21
|
source: 'sdk' | 'skill' | 'command';
|
|
23
22
|
path?: string;
|
|
24
23
|
}
|
|
25
|
-
export interface ClaudeSkillMetadata {
|
|
26
|
-
name: string;
|
|
27
|
-
description: string;
|
|
28
|
-
path: string;
|
|
29
|
-
source: 'skill' | 'command';
|
|
30
|
-
}
|
|
31
|
-
export interface ClaudeHookMetadata {
|
|
32
|
-
name: string;
|
|
33
|
-
matcher?: string;
|
|
34
|
-
sourcePath: string;
|
|
35
|
-
commandCount: number;
|
|
36
|
-
}
|
|
37
24
|
export interface ClaudeAgentMetadata {
|
|
38
25
|
name: string;
|
|
39
26
|
description: string;
|
|
40
27
|
model?: string;
|
|
41
28
|
source: 'sdk' | 'filesystem';
|
|
42
29
|
}
|
|
43
|
-
export interface ClaudeMetadataSnapshot {
|
|
44
|
-
collectedAt: string;
|
|
45
|
-
cwd: string;
|
|
46
|
-
commands: ClaudeCommandMetadata[];
|
|
47
|
-
skills: ClaudeSkillMetadata[];
|
|
48
|
-
hooks: ClaudeHookMetadata[];
|
|
49
|
-
agents: ClaudeAgentMetadata[];
|
|
50
|
-
claudeMdPath?: string;
|
|
51
|
-
settingsPaths: string[];
|
|
52
|
-
}
|
|
53
30
|
export interface ClaudeSessionEvent {
|
|
54
31
|
type: 'init' | 'assistant' | 'partial' | 'user' | 'tool_call' | 'tool_progress' | 'tool_summary' | 'status' | 'system' | 'result' | 'error';
|
|
55
32
|
sessionId?: string;
|
|
@@ -75,7 +52,7 @@ export interface RunClaudeSessionInput {
|
|
|
75
52
|
forkSession?: boolean;
|
|
76
53
|
persistSession?: boolean;
|
|
77
54
|
includePartialMessages?: boolean;
|
|
78
|
-
settingSources?:
|
|
55
|
+
settingSources?: Array<'user' | 'project' | 'local'>;
|
|
79
56
|
maxTurns?: number;
|
|
80
57
|
abortSignal?: AbortSignal;
|
|
81
58
|
}
|
package/dist/util/fs-helpers.js
CHANGED
|
@@ -6,7 +6,5 @@ export async function writeJsonAtomically(filePath, data) {
|
|
|
6
6
|
await fs.rename(tempPath, filePath);
|
|
7
7
|
}
|
|
8
8
|
export function isFileNotFoundError(error) {
|
|
9
|
-
return (error instanceof Error &&
|
|
10
|
-
'code' in error &&
|
|
11
|
-
error.code === 'ENOENT');
|
|
9
|
+
return (error instanceof Error && 'code' in error && error.code === 'ENOENT');
|
|
12
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doingdev/opencode-claude-manager-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "OpenCode plugin that orchestrates Claude Code sessions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode",
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@anthropic-ai/claude-agent-sdk": "^0.2.81",
|
|
33
33
|
"@opencode-ai/plugin": "^1.2.27",
|
|
34
|
-
"json5": "^2.2.3",
|
|
35
34
|
"zod": "^4.1.8"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|