@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.
@@ -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
- ? message.tool_name
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.model_usage;
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) && typeof model.context_window === 'number') {
514
- result.contextWindowSize = model.context_window;
515
- break;
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, ClaudeMetadataSnapshot, ClaudeSessionRunResult, ClaudeSessionSummary, ClaudeSessionTranscriptMessage, RunClaudeSessionInput } from '../types/contracts.js';
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
- private readonly metadataService;
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
- metadataService;
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, ClaudeMetadataSnapshot, ClaudeSessionRunResult, ClaudeSessionSummary, ClaudeSessionTranscriptMessage, ManagerPromptRegistry, RunClaudeSessionInput, SessionContextSnapshot, GitDiffResult, GitOperationResult, PersistentRunRecord, PersistentRunResult, ActiveSessionState, ContextWarningLevel, SessionMode, LiveTailEvent, ToolOutputPreview, ToolApprovalRule, ToolApprovalPolicy, ToolApprovalDecision, } from './types/contracts.js';
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 (this.latestInputTokens !== null &&
89
- this.latestInputTokens >= thresholdTokens);
87
+ return this.latestInputTokens !== null && this.latestInputTokens >= thresholdTokens;
90
88
  }
91
89
  reset() {
92
90
  this.totalTurns = 0;
@@ -27,6 +27,9 @@ export declare class PersistentManager {
27
27
  finalText: string;
28
28
  turns?: number;
29
29
  totalCostUsd?: number;
30
+ inputTokens?: number;
31
+ outputTokens?: number;
32
+ contextWindowSize?: number;
30
33
  context: SessionContextSnapshot;
31
34
  }>;
32
35
  /**
@@ -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", "engineer_metadata", "engineer_sessions", "engineer_runs", "git_diff", "git_commit", "git_reset", "approval_policy", "approval_decisions", "approval_update"];
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 = 0;
26
- let costSoFar = 0;
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 costLabel = `$${costSoFar.toFixed(4)}`;
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
- ? parsed.input
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}... (${turnsSoFar} turns, ${costLabel})`,
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... (${turnsSoFar} turns, ${costLabel})`,
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 (${turnsSoFar} turns, ${costLabel})`,
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)` : ''}... (${turnsSoFar} turns, ${costLabel})`,
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 (${turnsSoFar} turns, ${costLabel})`,
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... (${turnsSoFar} turns, ${costLabel})`,
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 metadataService = new ClaudeMetadataService(new RepoClaudeConfigReader(), sdkAdapter);
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, } from '../util/fs-helpers.js';
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, } from '../util/transcript-append.js';
4
- import { isFileNotFoundError, writeJsonAtomically, } from '../util/fs-helpers.js';
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?: ClaudeSettingSource[];
55
+ settingSources?: Array<'user' | 'project' | 'local'>;
79
56
  maxTurns?: number;
80
57
  abortSignal?: AbortSignal;
81
58
  }
@@ -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.30",
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": {