@doingdev/opencode-claude-manager-plugin 0.1.34 → 0.1.42

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.
Files changed (79) hide show
  1. package/README.md +14 -15
  2. package/dist/claude/claude-agent-sdk-adapter.js +2 -1
  3. package/dist/claude/session-live-tailer.js +2 -2
  4. package/dist/manager/git-operations.d.ts +10 -1
  5. package/dist/manager/git-operations.js +18 -3
  6. package/dist/manager/persistent-manager.d.ts +16 -1
  7. package/dist/manager/persistent-manager.js +14 -2
  8. package/dist/plugin/agent-hierarchy.d.ts +1 -1
  9. package/dist/plugin/agent-hierarchy.js +12 -17
  10. package/dist/plugin/claude-manager.plugin.js +158 -79
  11. package/dist/prompts/registry.js +16 -12
  12. package/dist/src/claude/claude-agent-sdk-adapter.d.ts +27 -0
  13. package/dist/src/claude/claude-agent-sdk-adapter.js +517 -0
  14. package/dist/src/claude/claude-session.service.d.ts +10 -0
  15. package/dist/src/claude/claude-session.service.js +18 -0
  16. package/dist/src/claude/session-live-tailer.d.ts +51 -0
  17. package/dist/src/claude/session-live-tailer.js +269 -0
  18. package/dist/src/claude/tool-approval-manager.d.ts +27 -0
  19. package/dist/src/claude/tool-approval-manager.js +232 -0
  20. package/dist/src/index.d.ts +6 -0
  21. package/dist/src/index.js +4 -0
  22. package/dist/src/manager/context-tracker.d.ts +33 -0
  23. package/dist/src/manager/context-tracker.js +106 -0
  24. package/dist/src/manager/git-operations.d.ts +12 -0
  25. package/dist/src/manager/git-operations.js +76 -0
  26. package/dist/src/manager/persistent-manager.d.ts +77 -0
  27. package/dist/src/manager/persistent-manager.js +170 -0
  28. package/dist/src/manager/session-controller.d.ts +44 -0
  29. package/dist/src/manager/session-controller.js +147 -0
  30. package/dist/src/plugin/agent-hierarchy.d.ts +60 -0
  31. package/dist/src/plugin/agent-hierarchy.js +157 -0
  32. package/dist/src/plugin/claude-manager.plugin.d.ts +2 -0
  33. package/dist/src/plugin/claude-manager.plugin.js +563 -0
  34. package/dist/src/plugin/service-factory.d.ts +12 -0
  35. package/dist/src/plugin/service-factory.js +38 -0
  36. package/dist/src/prompts/registry.d.ts +11 -0
  37. package/dist/src/prompts/registry.js +260 -0
  38. package/dist/src/state/file-run-state-store.d.ts +14 -0
  39. package/dist/src/state/file-run-state-store.js +85 -0
  40. package/dist/src/state/transcript-store.d.ts +15 -0
  41. package/dist/src/state/transcript-store.js +44 -0
  42. package/dist/src/types/contracts.d.ts +200 -0
  43. package/dist/src/types/contracts.js +1 -0
  44. package/dist/src/util/fs-helpers.d.ts +2 -0
  45. package/dist/src/util/fs-helpers.js +10 -0
  46. package/dist/src/util/project-context.d.ts +10 -0
  47. package/dist/src/util/project-context.js +105 -0
  48. package/dist/src/util/transcript-append.d.ts +7 -0
  49. package/dist/src/util/transcript-append.js +29 -0
  50. package/dist/test/claude-agent-sdk-adapter.test.d.ts +1 -0
  51. package/dist/test/claude-agent-sdk-adapter.test.js +459 -0
  52. package/dist/test/claude-manager.plugin.test.d.ts +1 -0
  53. package/dist/test/claude-manager.plugin.test.js +331 -0
  54. package/dist/test/context-tracker.test.d.ts +1 -0
  55. package/dist/test/context-tracker.test.js +138 -0
  56. package/dist/test/file-run-state-store.test.d.ts +1 -0
  57. package/dist/test/file-run-state-store.test.js +82 -0
  58. package/dist/test/git-operations.test.d.ts +1 -0
  59. package/dist/test/git-operations.test.js +90 -0
  60. package/dist/test/persistent-manager.test.d.ts +1 -0
  61. package/dist/test/persistent-manager.test.js +208 -0
  62. package/dist/test/project-context.test.d.ts +1 -0
  63. package/dist/test/project-context.test.js +92 -0
  64. package/dist/test/prompt-registry.test.d.ts +1 -0
  65. package/dist/test/prompt-registry.test.js +256 -0
  66. package/dist/test/session-controller.test.d.ts +1 -0
  67. package/dist/test/session-controller.test.js +149 -0
  68. package/dist/test/session-live-tailer.test.d.ts +1 -0
  69. package/dist/test/session-live-tailer.test.js +313 -0
  70. package/dist/test/tool-approval-manager.test.d.ts +1 -0
  71. package/dist/test/tool-approval-manager.test.js +264 -0
  72. package/dist/test/transcript-append.test.d.ts +1 -0
  73. package/dist/test/transcript-append.test.js +37 -0
  74. package/dist/test/transcript-store.test.d.ts +1 -0
  75. package/dist/test/transcript-store.test.js +50 -0
  76. package/dist/types/contracts.d.ts +1 -1
  77. package/dist/vitest.config.d.ts +2 -0
  78. package/dist/vitest.config.js +11 -0
  79. package/package.json +2 -2
@@ -5,7 +5,7 @@ import { AGENT_CTO, AGENT_ENGINEER_BUILD, AGENT_ENGINEER_PLAN, buildCtoAgentConf
5
5
  import { getOrCreatePluginServices } from './service-factory.js';
6
6
  export const ClaudeManagerPlugin = async ({ worktree }) => {
7
7
  const services = getOrCreatePluginServices(worktree);
8
- async function executeEngineerSend(args, context) {
8
+ async function executeDelegate(args, context) {
9
9
  const cwd = args.cwd ?? context.worktree;
10
10
  if (args.freshSession) {
11
11
  await services.manager.clearSession(cwd);
@@ -13,8 +13,11 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
13
13
  const hasActiveSession = services.manager.getStatus().sessionId !== null;
14
14
  const promptPreview = args.message.length > 100 ? args.message.slice(0, 100) + '...' : args.message;
15
15
  context.metadata({
16
- title: hasActiveSession ? 'Claude Code: Resuming session...' : 'Claude Code: Initializing...',
16
+ title: hasActiveSession
17
+ ? '⚡ Claude Code: Resuming session...'
18
+ : '⚡ Claude Code: Initializing...',
17
19
  metadata: {
20
+ status: 'running',
18
21
  sessionId: services.manager.getStatus().sessionId,
19
22
  prompt: promptPreview,
20
23
  },
@@ -49,8 +52,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
49
52
  // ignore parse errors
50
53
  }
51
54
  context.metadata({
52
- title: `Claude Code: Running ${toolName}...${usageSuffix}`,
55
+ title: `⚡ Claude Code: Running ${toolName}...${usageSuffix}`,
53
56
  metadata: {
57
+ status: 'running',
54
58
  sessionId: event.sessionId,
55
59
  type: event.type,
56
60
  tool: toolName,
@@ -61,8 +65,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
61
65
  else if (event.type === 'assistant') {
62
66
  const thinkingPreview = event.text.length > 150 ? event.text.slice(0, 150) + '...' : event.text;
63
67
  context.metadata({
64
- title: `Claude Code: Thinking...${usageSuffix}`,
68
+ title: `⚡ Claude Code: Thinking...${usageSuffix}`,
65
69
  metadata: {
70
+ status: 'running',
66
71
  sessionId: event.sessionId,
67
72
  type: event.type,
68
73
  thinking: thinkingPreview,
@@ -71,8 +76,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
71
76
  }
72
77
  else if (event.type === 'init') {
73
78
  context.metadata({
74
- title: `Claude Code: Session started`,
79
+ title: `⚡ Claude Code: Session started`,
75
80
  metadata: {
81
+ status: 'running',
76
82
  sessionId: event.sessionId,
77
83
  prompt: promptPreview,
78
84
  },
@@ -80,9 +86,11 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
80
86
  }
81
87
  else if (event.type === 'user') {
82
88
  const preview = event.text.length > 200 ? event.text.slice(0, 200) + '...' : event.text;
89
+ const outputPreview = formatToolOutputPreview(event.text);
83
90
  context.metadata({
84
- title: `Claude Code: Tool result${usageSuffix}`,
91
+ title: `⚡ Claude Code: ${outputPreview}${usageSuffix}`,
85
92
  metadata: {
93
+ status: 'running',
86
94
  sessionId: event.sessionId,
87
95
  type: event.type,
88
96
  output: preview,
@@ -92,17 +100,25 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
92
100
  else if (event.type === 'tool_progress') {
93
101
  let toolName = 'tool';
94
102
  let elapsed = 0;
103
+ let progressCurrent;
104
+ let progressTotal;
95
105
  try {
96
106
  const parsed = JSON.parse(event.text);
97
107
  toolName = parsed.name ?? 'tool';
98
108
  elapsed = parsed.elapsed ?? 0;
109
+ progressCurrent = parsed.current;
110
+ progressTotal = parsed.total;
99
111
  }
100
112
  catch {
101
113
  // ignore
102
114
  }
115
+ const progressInfo = progressCurrent !== undefined && progressTotal !== undefined
116
+ ? ` [${progressCurrent}/${progressTotal}]`
117
+ : '';
103
118
  context.metadata({
104
- title: `Claude Code: ${toolName} running ${elapsed > 0 ? `(${elapsed.toFixed(0)}s)` : ''}...${usageSuffix}`,
119
+ title: `⚡ Claude Code: ${toolName} running ${elapsed > 0 ? `(${elapsed.toFixed(0)}s)` : ''}${progressInfo}...${usageSuffix}`,
105
120
  metadata: {
121
+ status: 'running',
106
122
  sessionId: event.sessionId,
107
123
  type: event.type,
108
124
  tool: toolName,
@@ -113,8 +129,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
113
129
  else if (event.type === 'tool_summary') {
114
130
  const summary = event.text.length > 200 ? event.text.slice(0, 200) + '...' : event.text;
115
131
  context.metadata({
116
- title: `Claude Code: Tool done${usageSuffix}`,
132
+ title: `✅ Claude Code: Tool done${usageSuffix}`,
117
133
  metadata: {
134
+ status: 'success',
118
135
  sessionId: event.sessionId,
119
136
  type: event.type,
120
137
  summary,
@@ -124,8 +141,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
124
141
  else if (event.type === 'partial') {
125
142
  const delta = event.text.length > 200 ? event.text.slice(0, 200) + '...' : event.text;
126
143
  context.metadata({
127
- title: `Claude Code: Writing...${usageSuffix}`,
144
+ title: `⚡ Claude Code: Writing...${usageSuffix}`,
128
145
  metadata: {
146
+ status: 'running',
129
147
  sessionId: event.sessionId,
130
148
  type: event.type,
131
149
  delta,
@@ -134,12 +152,14 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
134
152
  }
135
153
  else if (event.type === 'error') {
136
154
  context.metadata({
137
- title: `Claude Code: Error`,
155
+ title: `❌ Claude Code: Error`,
138
156
  metadata: {
157
+ status: 'error',
139
158
  sessionId: event.sessionId,
140
159
  error: event.text.slice(0, 200),
141
160
  },
142
161
  });
162
+ showToastIfAvailable(context, `Claude Code error: ${event.text.slice(0, 100)}`);
143
163
  }
144
164
  });
145
165
  const costLabel = `$${(result.totalCostUsd ?? 0).toFixed(4)}`;
@@ -147,15 +167,17 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
147
167
  const contextWarning = formatContextWarning(result.context);
148
168
  if (contextWarning) {
149
169
  context.metadata({
150
- title: `Claude Code: Context at ${result.context.estimatedContextPercent}% (${turns} turns)`,
151
- metadata: { sessionId: result.sessionId, contextWarning },
170
+ title: `⚠️ Claude Code: Context at ${result.context.estimatedContextPercent}% (${turns} turns)`,
171
+ metadata: { status: 'warning', sessionId: result.sessionId, contextWarning },
152
172
  });
173
+ showToastIfAvailable(context, `⚠️ Context usage at ${result.context.estimatedContextPercent}% — consider compacting`);
153
174
  }
154
175
  else {
155
176
  context.metadata({
156
- title: `Claude Code: Complete (${turns} turns, ${costLabel})`,
157
- metadata: { sessionId: result.sessionId },
177
+ title: `✅ Claude Code: Complete (${turns} turns, ${costLabel})`,
178
+ metadata: { status: 'success', sessionId: result.sessionId },
158
179
  });
180
+ showToastIfAvailable(context, `✅ Session complete (${turns} turns, ${costLabel})`);
159
181
  }
160
182
  let toolOutputs = [];
161
183
  if (result.sessionId) {
@@ -196,29 +218,26 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
196
218
  config.agent[AGENT_ENGINEER_BUILD] ??= buildEngineerBuildAgentConfig(derivedPrompts);
197
219
  },
198
220
  tool: {
199
- engineer_send: tool({
200
- description: 'Send a message to the persistent Claude Code session with explicit mode control. ' +
201
- 'Most agents should use engineer_send_plan or engineer_send_build instead.',
221
+ explore: tool({
222
+ description: 'Investigate and analyze code without making edits. ' +
223
+ 'Read-only exploration of the codebase. ' +
224
+ 'Preferred first step before implementation.',
202
225
  args: {
203
226
  message: tool.schema.string().min(1),
204
227
  model: tool.schema
205
228
  .enum(['claude-opus-4-6', 'claude-sonnet-4-6', 'claude-sonnet-4-5'])
206
229
  .optional(),
207
230
  effort: tool.schema.enum(['low', 'medium', 'high', 'max']).default('high'),
208
- mode: tool.schema.enum(['plan', 'free']).default('free'),
209
231
  freshSession: tool.schema.boolean().default(false),
210
232
  cwd: tool.schema.string().optional(),
211
233
  },
212
234
  async execute(args, context) {
213
- return executeEngineerSend(args, context);
235
+ return executeDelegate({ ...args, mode: 'plan' }, context);
214
236
  },
215
237
  }),
216
- engineer_send_plan: tool({
217
- description: 'Preferred path for repo exploration and investigation. ' +
218
- 'Send a read-only investigation message to the Claude Code session in plan mode. ' +
219
- 'The engineer will analyze code without making edits. ' +
220
- 'Auto-creates a session on first call. Resumes the existing session on subsequent calls. ' +
221
- 'Returns the assistant response and current context health snapshot.',
238
+ implement: tool({
239
+ description: 'Implement code changes - can read, edit, and create files. ' +
240
+ 'Use after exploration to make changes.',
222
241
  args: {
223
242
  message: tool.schema.string().min(1),
224
243
  model: tool.schema
@@ -229,35 +248,12 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
229
248
  cwd: tool.schema.string().optional(),
230
249
  },
231
250
  async execute(args, context) {
232
- return executeEngineerSend({ ...args, mode: 'plan' }, context);
251
+ return executeDelegate({ ...args, mode: 'free' }, context);
233
252
  },
234
253
  }),
235
- engineer_send_build: tool({
236
- description: 'Preferred path for implementation after framing. ' +
237
- 'Send an implementation message to the Claude Code session in free mode. ' +
238
- 'The engineer can read, edit, and create files. ' +
239
- 'Auto-creates a session on first call. Resumes the existing session on subsequent calls. ' +
240
- 'Returns the assistant response and current context health snapshot. ' +
241
- 'Prefer claude-opus-4-6 (default) for most coding work; use a Sonnet model for faster/lighter tasks. ' +
242
- 'Prefer effort "high" (default) for most work; use "medium" for lighter tasks and "max" for especially hard problems.',
243
- args: {
244
- message: tool.schema.string().min(1),
245
- model: tool.schema
246
- .enum(['claude-opus-4-6', 'claude-sonnet-4-6', 'claude-sonnet-4-5'])
247
- .optional(),
248
- effort: tool.schema.enum(['low', 'medium', 'high', 'max']).default('high'),
249
- freshSession: tool.schema.boolean().default(false),
250
- cwd: tool.schema.string().optional(),
251
- },
252
- async execute(args, context) {
253
- return executeEngineerSend({ ...args, mode: 'free' }, context);
254
- },
255
- }),
256
- engineer_compact: tool({
257
- description: 'Compact the active Claude Code session to reclaim context space. ' +
258
- 'Sends /compact to the session, which compresses prior conversation while preserving state. ' +
259
- 'Use before clearing when context is high but the session still has useful state. ' +
260
- 'Fails if there is no active session.',
254
+ compact_context: tool({
255
+ description: 'Compress session history to reclaim context window space. ' +
256
+ 'Preserves state while reducing token usage.',
261
257
  args: {
262
258
  cwd: tool.schema.string().optional(),
263
259
  },
@@ -269,9 +265,12 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
269
265
  const contextWarning = formatContextWarning(snap);
270
266
  context.metadata({
271
267
  title: contextWarning
272
- ? `Claude Code: Compacted — context at ${snap.estimatedContextPercent}%`
273
- : `Claude Code: Compacted (${snap.totalTurns} turns, $${(snap.totalCostUsd ?? 0).toFixed(4)})`,
274
- metadata: { sessionId: result.sessionId },
268
+ ? `⚠️ Claude Code: Compacted — context at ${snap.estimatedContextPercent}%`
269
+ : `✅ Claude Code: Compacted (${snap.totalTurns} turns, $${(snap.totalCostUsd ?? 0).toFixed(4)})`,
270
+ metadata: {
271
+ status: contextWarning ? 'warning' : 'success',
272
+ sessionId: result.sessionId,
273
+ },
275
274
  });
276
275
  return JSON.stringify({
277
276
  sessionId: result.sessionId,
@@ -284,13 +283,27 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
284
283
  },
285
284
  }),
286
285
  git_diff: tool({
287
- description: 'Run git diff to see all current changes (staged + unstaged) relative to HEAD.',
286
+ description: 'Show diff of uncommitted changes. ' +
287
+ 'Use paths to filter to specific files/dirs. ' +
288
+ 'Use staged=true to see staged changes. ' +
289
+ 'Use ref to compare against a branch/tag/commit (e.g., ref="main").',
288
290
  args: {
289
- cwd: tool.schema.string().optional(),
291
+ paths: tool.schema.string().array().optional(),
292
+ staged: tool.schema.boolean().optional(),
293
+ ref: tool.schema.string().optional(),
290
294
  },
291
- async execute(_args, context) {
292
- annotateToolRun(context, 'Running git diff', {});
293
- const result = await services.manager.gitDiff();
295
+ async execute(args, context) {
296
+ annotateToolRun(context, 'Running git diff', {
297
+ paths: args.paths,
298
+ staged: args.staged,
299
+ ref: args.ref,
300
+ });
301
+ const paths = args.paths?.filter((p) => p !== undefined);
302
+ const result = await services.manager.gitDiff({
303
+ paths,
304
+ staged: args.staged,
305
+ ref: args.ref,
306
+ });
294
307
  return JSON.stringify(result, null, 2);
295
308
  },
296
309
  }),
@@ -298,7 +311,6 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
298
311
  description: 'Stage all changes and commit with the given message.',
299
312
  args: {
300
313
  message: tool.schema.string().min(1),
301
- cwd: tool.schema.string().optional(),
302
314
  },
303
315
  async execute(args, context) {
304
316
  annotateToolRun(context, 'Committing changes', {
@@ -309,19 +321,39 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
309
321
  },
310
322
  }),
311
323
  git_reset: tool({
312
- description: 'Run git reset --hard HEAD and git clean -fd to discard ALL uncommitted changes and untracked files.',
313
- args: {
314
- cwd: tool.schema.string().optional(),
315
- },
324
+ description: 'Discard all uncommitted changes: runs git reset --hard HEAD and git clean -fd.',
325
+ args: {},
316
326
  async execute(_args, context) {
317
327
  annotateToolRun(context, 'Resetting working directory', {});
318
328
  const result = await services.manager.gitReset();
319
329
  return JSON.stringify(result, null, 2);
320
330
  },
321
331
  }),
322
- engineer_clear: tool({
323
- description: 'Clear the active Claude Code session. The next send will start a fresh session. ' +
324
- 'Use when context is full, the session is confused, or starting a different task.',
332
+ git_status: tool({
333
+ description: 'Show working tree status lists changed files in short format. ' +
334
+ 'Returns isClean=true if nothing changed.',
335
+ args: {},
336
+ async execute(_args, context) {
337
+ annotateToolRun(context, 'Checking git status', {});
338
+ const result = await services.manager.gitStatus();
339
+ return JSON.stringify(result, null, 2);
340
+ },
341
+ }),
342
+ git_log: tool({
343
+ description: 'Show recent commits in short format. ' +
344
+ 'Default shows last 5 commits. Use count to change.',
345
+ args: {
346
+ count: tool.schema.number().optional(),
347
+ },
348
+ async execute(args, context) {
349
+ annotateToolRun(context, 'Fetching git log', { count: args.count });
350
+ const result = await services.manager.gitLog(args.count ?? 5);
351
+ return result;
352
+ },
353
+ }),
354
+ clear_session: tool({
355
+ description: 'Clear the active session to start fresh. ' +
356
+ 'Use when context is full or starting a new task.',
325
357
  args: {
326
358
  cwd: tool.schema.string().optional(),
327
359
  reason: tool.schema.string().optional(),
@@ -334,8 +366,8 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
334
366
  return JSON.stringify({ clearedSessionId: clearedId });
335
367
  },
336
368
  }),
337
- engineer_status: tool({
338
- description: 'Get the current persistent session status: context usage %, turns, cost, active session ID.',
369
+ session_health: tool({
370
+ description: 'Check session health metrics: context usage %, turn count, cost, and session ID.',
339
371
  args: {
340
372
  cwd: tool.schema.string().optional(),
341
373
  },
@@ -351,9 +383,8 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
351
383
  }, null, 2);
352
384
  },
353
385
  }),
354
- engineer_sessions: tool({
355
- description: 'List Claude sessions or inspect a saved transcript. ' +
356
- 'When sessionId is provided, returns both SDK transcript and local events.',
386
+ list_transcripts: tool({
387
+ description: 'List available session transcripts or inspect a specific transcript by ID.',
357
388
  args: {
358
389
  cwd: tool.schema.string().optional(),
359
390
  sessionId: tool.schema.string().optional(),
@@ -375,8 +406,8 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
375
406
  return JSON.stringify(sessions, null, 2);
376
407
  },
377
408
  }),
378
- engineer_runs: tool({
379
- description: 'List persistent manager run records.',
409
+ list_history: tool({
410
+ description: 'List persistent run records from the manager or inspect a specific run.',
380
411
  args: {
381
412
  cwd: tool.schema.string().optional(),
382
413
  runId: tool.schema.string().optional(),
@@ -482,8 +513,12 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
482
513
  },
483
514
  };
484
515
  };
485
- function annotateToolRun(context, title, metadata) {
486
- context.metadata({ title, metadata });
516
+ function annotateToolRun(context, title, metadata, status) {
517
+ const emoji = status ? `${formatStatusEmoji(status)} ` : '';
518
+ context.metadata({
519
+ title: `${emoji}${title}`,
520
+ metadata: { ...metadata, ...(status ? { status } : {}) },
521
+ });
487
522
  }
488
523
  function formatLiveUsage(turns, cost) {
489
524
  if (turns === undefined && cost === undefined) {
@@ -491,10 +526,10 @@ function formatLiveUsage(turns, cost) {
491
526
  }
492
527
  const parts = [];
493
528
  if (turns !== undefined) {
494
- parts.push(`${turns} turns`);
529
+ parts.push(`🔄 ${turns} turns`);
495
530
  }
496
531
  if (cost !== undefined) {
497
- parts.push(`$${cost.toFixed(4)}`);
532
+ parts.push(`💰 $${cost.toFixed(4)}`);
498
533
  }
499
534
  return ` (${parts.join(', ')})`;
500
535
  }
@@ -514,3 +549,47 @@ function formatContextWarning(context) {
514
549
  .replace('{turns}', String(totalTurns))
515
550
  .replace('{cost}', totalCostUsd.toFixed(2));
516
551
  }
552
+ function formatStatusEmoji(status) {
553
+ switch (status) {
554
+ case 'running':
555
+ return '⚡';
556
+ case 'success':
557
+ return '✅';
558
+ case 'error':
559
+ return '❌';
560
+ case 'warning':
561
+ return '⚠️';
562
+ }
563
+ }
564
+ function formatToolOutputPreview(text) {
565
+ const lower = text.toLowerCase();
566
+ let prefix;
567
+ if (lower.includes('"tool":"read"') ||
568
+ lower.includes('"name":"read"') ||
569
+ lower.includes('file contents')) {
570
+ prefix = '↳ Read: ';
571
+ }
572
+ else if (lower.includes('"tool":"grep"') ||
573
+ lower.includes('"name":"grep"') ||
574
+ lower.includes('matches found')) {
575
+ prefix = '↳ Found: ';
576
+ }
577
+ else if (lower.includes('"tool":"write"') ||
578
+ lower.includes('"name":"write"') ||
579
+ lower.includes('"tool":"edit"') ||
580
+ lower.includes('"name":"edit"') ||
581
+ lower.includes('file written') ||
582
+ lower.includes('file updated')) {
583
+ prefix = '↳ Wrote: ';
584
+ }
585
+ else {
586
+ prefix = '↳ Result: ';
587
+ }
588
+ const snippet = text.replace(/\s+/g, ' ').trim();
589
+ const truncated = snippet.length > 60 ? snippet.slice(0, 60) + '...' : snippet;
590
+ return `${prefix}${truncated}`;
591
+ }
592
+ function showToastIfAvailable(context, message) {
593
+ const ctx = context;
594
+ ctx.client?.tui?.showToast?.(message);
595
+ }
@@ -90,9 +90,11 @@ export const managerPromptRegistry = {
90
90
  'After each delegation:',
91
91
  '1. git_diff — read the FULL diff. Check for unintended changes, missing tests,',
92
92
  ' style violations.',
93
- '2. If correct: spawn `engineer_build` to run tests/lint/typecheck.',
94
- '3. If tests pass: git_commit to checkpoint.',
95
- '4. If wrong: spawn `engineer_build` with a specific correction.',
93
+ '2. git_status check what files changed (quick overview)',
94
+ '3. git_log -n 5 see recent commits (understand context)',
95
+ '4. If correct: spawn `engineer_build` to run tests/lint/typecheck.',
96
+ '5. If tests pass: git_commit to checkpoint.',
97
+ '6. If wrong: spawn `engineer_build` with a specific correction.',
96
98
  ' On second failure: git_reset and rewrite the prompt from scratch.',
97
99
  ' Never send three corrections for the same problem.',
98
100
  '',
@@ -116,7 +118,9 @@ export const managerPromptRegistry = {
116
118
  '## Tools reference',
117
119
  'todowrite / todoread — track multi-step work',
118
120
  'question — ask the user structured questions with options',
119
- 'git_diff — review all uncommitted changes',
121
+ 'git_diff — review changes (filter with paths, staged, ref args)',
122
+ 'git_status — quick overview of changed files',
123
+ 'git_log — recent commits (default: last 5)',
120
124
  'git_commit — stage all + commit',
121
125
  'git_reset — hard reset + clean (destructive)',
122
126
  'approval_policy — view tool approval rules',
@@ -148,13 +152,13 @@ export const managerPromptRegistry = {
148
152
  '- Do NOT implement changes yourself — investigation only.',
149
153
  '',
150
154
  '## Behavior',
151
- '- Send the objective to the engineer using engineer_send_plan.',
155
+ '- Send the objective to the engineer using explore.',
152
156
  "- Return the engineer's response verbatim. Do not summarize.",
153
- '- Use freshSession:true on engineer_send_plan when the task is unrelated to prior work.',
157
+ '- Use freshSession:true on explore when the task is unrelated to prior work.',
154
158
  '',
155
159
  '## Context management',
156
- '- Check engineer_status before sending.',
157
- '- Under 50%: proceed. Over 70%: engineer_compact. Over 85%: engineer_clear.',
160
+ '- Check session_health before sending.',
161
+ '- Under 50%: proceed. Over 70%: compact_context. Over 85%: clear_session.',
158
162
  '',
159
163
  '## Model selection',
160
164
  '- claude-opus-4-6 + high: complex analysis (default).',
@@ -191,13 +195,13 @@ export const managerPromptRegistry = {
191
195
  '- Do NOT implement changes yourself — investigation only.',
192
196
  '',
193
197
  '## Behavior',
194
- '- Send the objective to the engineer using engineer_send_build.',
198
+ '- Send the objective to the engineer using implement.',
195
199
  "- Return the engineer's response verbatim. Do not summarize.",
196
- '- Use freshSession:true on engineer_send_build when the task is unrelated to prior work.',
200
+ '- Use freshSession:true on implement when the task is unrelated to prior work.',
197
201
  '',
198
202
  '## Context management',
199
- '- Check engineer_status before sending.',
200
- '- Under 50%: proceed. Over 70%: engineer_compact. Over 85%: engineer_clear.',
203
+ '- Check session_health before sending.',
204
+ '- Under 50%: proceed. Over 70%: compact_context. Over 85%: clear_session.',
201
205
  '',
202
206
  '## Model selection',
203
207
  '- claude-opus-4-6 + high: most coding tasks (default).',
@@ -0,0 +1,27 @@
1
+ import { type Options, type Query, type SDKSessionInfo, type SessionMessage, type SettingSource } from '@anthropic-ai/claude-agent-sdk';
2
+ import type { ClaudeCapabilitySnapshot, ClaudeSessionEvent, ClaudeSessionRunResult, ClaudeSessionSummary, ClaudeSessionTranscriptMessage, RunClaudeSessionInput } from '../types/contracts.js';
3
+ import type { ToolApprovalManager } from './tool-approval-manager.js';
4
+ export type ClaudeSessionEventHandler = (event: ClaudeSessionEvent) => void | Promise<void>;
5
+ interface ClaudeAgentSdkFacade {
6
+ query(params: {
7
+ prompt: string;
8
+ options?: Options;
9
+ }): Query;
10
+ listSessions(options?: {
11
+ dir?: string;
12
+ }): Promise<SDKSessionInfo[]>;
13
+ getSessionMessages(sessionId: string, options?: {
14
+ dir?: string;
15
+ }): Promise<SessionMessage[]>;
16
+ }
17
+ export declare class ClaudeAgentSdkAdapter {
18
+ private readonly sdkFacade;
19
+ private readonly approvalManager?;
20
+ constructor(sdkFacade?: ClaudeAgentSdkFacade, approvalManager?: ToolApprovalManager | undefined);
21
+ runSession(input: RunClaudeSessionInput, onEvent?: ClaudeSessionEventHandler): Promise<ClaudeSessionRunResult>;
22
+ listSavedSessions(cwd?: string): Promise<ClaudeSessionSummary[]>;
23
+ getTranscript(sessionId: string, cwd?: string): Promise<ClaudeSessionTranscriptMessage[]>;
24
+ probeCapabilities(cwd: string, settingSources?: SettingSource[]): Promise<ClaudeCapabilitySnapshot>;
25
+ private buildOptions;
26
+ }
27
+ export {};