@doingdev/opencode-claude-manager-plugin 0.1.43 → 0.1.46

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/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, 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, 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;
@@ -83,10 +83,6 @@ export declare class PersistentManager {
83
83
  executeTask(cwd: string, task: string, options?: {
84
84
  model?: string;
85
85
  }, onProgress?: PersistentManagerProgressHandler): Promise<PersistentRunResult>;
86
- /**
87
- * Try to restore session state from disk on startup.
88
- */
89
- tryRestore(): Promise<boolean>;
90
86
  listRuns(cwd: string): Promise<PersistentRunRecord[]>;
91
87
  getRun(cwd: string, runId: string): Promise<PersistentRunRecord | null>;
92
88
  }
@@ -167,12 +167,6 @@ export class PersistentManager {
167
167
  return { run: failedRun };
168
168
  }
169
169
  }
170
- /**
171
- * Try to restore session state from disk on startup.
172
- */
173
- async tryRestore() {
174
- return this.sessionController.tryRestore();
175
- }
176
170
  listRuns(cwd) {
177
171
  return this.stateStore.listRuns(cwd);
178
172
  }
@@ -38,10 +38,4 @@ export declare class SessionController {
38
38
  * Get current context tracking snapshot.
39
39
  */
40
40
  getContextSnapshot(): SessionContextSnapshot;
41
- /**
42
- * Try to restore active session from persisted state on startup.
43
- */
44
- tryRestore(): Promise<boolean>;
45
- private persistActiveSession;
46
- private removeActiveSession;
47
41
  }
@@ -1,8 +1,3 @@
1
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
- import { dirname } from 'node:path';
3
- function activeSessionFile(_wrapperType) {
4
- return `.claude-manager/active-session.json`;
5
- }
6
1
  export class SessionController {
7
2
  sdkAdapter;
8
3
  contextTracker;
@@ -71,8 +66,6 @@ export class SessionController {
71
66
  outputTokens: result.outputTokens,
72
67
  contextWindowSize: result.contextWindowSize,
73
68
  });
74
- // Persist active session state
75
- await this.persistActiveSession();
76
69
  return result;
77
70
  }
78
71
  /**
@@ -93,7 +86,6 @@ export class SessionController {
93
86
  const clearedId = this.activeSessionId;
94
87
  this.activeSessionId = null;
95
88
  this.contextTracker.reset();
96
- await this.removeActiveSession();
97
89
  return clearedId;
98
90
  }
99
91
  /**
@@ -102,51 +94,4 @@ export class SessionController {
102
94
  getContextSnapshot() {
103
95
  return this.contextTracker.snapshot();
104
96
  }
105
- /**
106
- * Try to restore active session from persisted state on startup.
107
- */
108
- async tryRestore() {
109
- const filePath = activeSessionFile(this.wrapperType);
110
- try {
111
- const raw = await readFile(filePath, 'utf-8');
112
- const state = JSON.parse(raw);
113
- if (state.sessionId) {
114
- this.activeSessionId = state.sessionId;
115
- this.contextTracker.restore(state);
116
- return true;
117
- }
118
- }
119
- catch {
120
- // File doesn't exist or is corrupt — start fresh
121
- }
122
- return false;
123
- }
124
- async persistActiveSession() {
125
- if (!this.activeSessionId) {
126
- return;
127
- }
128
- const snap = this.contextTracker.snapshot();
129
- const state = {
130
- sessionId: this.activeSessionId,
131
- startedAt: new Date().toISOString(),
132
- totalTurns: snap.totalTurns,
133
- totalCostUsd: snap.totalCostUsd,
134
- estimatedContextPercent: snap.estimatedContextPercent,
135
- contextWindowSize: snap.contextWindowSize,
136
- latestInputTokens: snap.latestInputTokens,
137
- };
138
- const filePath = activeSessionFile(this.wrapperType);
139
- await mkdir(dirname(filePath), { recursive: true });
140
- await writeFile(filePath, JSON.stringify(state, null, 2));
141
- }
142
- async removeActiveSession() {
143
- const filePath = activeSessionFile(this.wrapperType);
144
- try {
145
- const { unlink } = await import('node:fs/promises');
146
- await unlink(filePath);
147
- }
148
- catch {
149
- // File doesn't exist — that's fine
150
- }
151
- }
152
97
  }
@@ -10,8 +10,9 @@ import type { ManagerPromptRegistry } from '../types/contracts.js';
10
10
  export declare const AGENT_CTO = "cto";
11
11
  export declare const AGENT_ENGINEER_EXPLORE = "engineer_explore";
12
12
  export declare const AGENT_ENGINEER_IMPLEMENT = "engineer_implement";
13
+ export declare const AGENT_ENGINEER_VERIFY = "engineer_verify";
13
14
  /** All restricted tool IDs (union of all domain groups) */
14
- export declare const ALL_RESTRICTED_TOOL_IDS: readonly ["explore", "implement", "compact_context", "clear_session", "session_health", "list_transcripts", "list_history", "git_diff", "git_commit", "git_reset", "git_status", "git_log", "approval_policy", "approval_decisions", "approval_update"];
15
+ export declare const ALL_RESTRICTED_TOOL_IDS: readonly ["explore", "implement", "verify", "compact_context", "clear_session", "session_health", "list_transcripts", "list_history", "git_diff", "git_commit", "git_reset", "git_status", "git_log", "approval_policy", "approval_decisions", "approval_update"];
15
16
  type ToolPermission = 'allow' | 'ask' | 'deny';
16
17
  type AgentPermission = {
17
18
  '*'?: ToolPermission;
@@ -55,6 +56,13 @@ export declare function buildEngineerImplementAgentConfig(prompts: ManagerPrompt
55
56
  permission: AgentPermission;
56
57
  prompt: string;
57
58
  };
59
+ export declare function buildEngineerVerifyAgentConfig(prompts: ManagerPromptRegistry): {
60
+ description: string;
61
+ mode: "subagent";
62
+ color: string;
63
+ permission: AgentPermission;
64
+ prompt: string;
65
+ };
58
66
  /** Deny all restricted tools at the global level so only designated agents can use them. */
59
67
  export declare function denyRestrictedToolsGlobally(permissions: Record<string, ToolPermission>): void;
60
68
  export {};
@@ -12,6 +12,7 @@
12
12
  export const AGENT_CTO = 'cto';
13
13
  export const AGENT_ENGINEER_EXPLORE = 'engineer_explore';
14
14
  export const AGENT_ENGINEER_IMPLEMENT = 'engineer_implement';
15
+ export const AGENT_ENGINEER_VERIFY = 'engineer_verify';
15
16
  // ---------------------------------------------------------------------------
16
17
  // Tool IDs — grouped by domain
17
18
  // ---------------------------------------------------------------------------
@@ -24,11 +25,13 @@ const ENGINEER_SHARED_TOOL_IDS = [
24
25
  'list_history',
25
26
  ];
26
27
  /** All engineer tools — mode-locked sends + shared session tools */
27
- const ENGINEER_TOOL_IDS = ['explore', 'implement', ...ENGINEER_SHARED_TOOL_IDS];
28
+ const ENGINEER_TOOL_IDS = ['explore', 'implement', 'verify', ...ENGINEER_SHARED_TOOL_IDS];
28
29
  /** Tools for the engineer_explore wrapper (explore-mode send + shared) */
29
30
  const ENGINEER_EXPLORE_TOOL_IDS = ['explore', ...ENGINEER_SHARED_TOOL_IDS];
30
31
  /** Tools for the engineer_implement wrapper (implement-mode send + shared) */
31
32
  const ENGINEER_IMPLEMENT_TOOL_IDS = ['implement', ...ENGINEER_SHARED_TOOL_IDS];
33
+ /** Tools for the engineer_verify wrapper (verify-mode send + shared) */
34
+ const ENGINEER_VERIFY_TOOL_IDS = ['verify', ...ENGINEER_SHARED_TOOL_IDS];
32
35
  /** Git tools — owned by CTO */
33
36
  const GIT_TOOL_IDS = ['git_diff', 'git_commit', 'git_reset', 'git_status', 'git_log'];
34
37
  /** Approval tools — owned by CTO */
@@ -79,6 +82,7 @@ function buildCtoPermissions() {
79
82
  '*': 'deny',
80
83
  [AGENT_ENGINEER_EXPLORE]: 'allow',
81
84
  [AGENT_ENGINEER_IMPLEMENT]: 'allow',
85
+ [AGENT_ENGINEER_VERIFY]: 'allow',
82
86
  },
83
87
  };
84
88
  }
@@ -116,6 +120,23 @@ function buildEngineerImplementPermissions() {
116
120
  ...allowed,
117
121
  };
118
122
  }
123
+ /** Engineer verify wrapper: read-only + verify + restricted bash for test/lint/typecheck/build. */
124
+ function buildEngineerVerifyPermissions() {
125
+ const denied = {};
126
+ for (const toolId of ALL_RESTRICTED_TOOL_IDS) {
127
+ denied[toolId] = 'deny';
128
+ }
129
+ const allowed = {};
130
+ for (const toolId of ENGINEER_VERIFY_TOOL_IDS) {
131
+ allowed[toolId] = 'allow';
132
+ }
133
+ return {
134
+ '*': 'deny',
135
+ ...READONLY_TOOLS,
136
+ ...denied,
137
+ ...allowed,
138
+ };
139
+ }
119
140
  // ---------------------------------------------------------------------------
120
141
  // Agent config builders
121
142
  // ---------------------------------------------------------------------------
@@ -146,6 +167,15 @@ export function buildEngineerImplementAgentConfig(prompts) {
146
167
  prompt: prompts.engineerImplementPrompt,
147
168
  };
148
169
  }
170
+ export function buildEngineerVerifyAgentConfig(prompts) {
171
+ return {
172
+ description: 'Thin high-judgment wrapper that runs verification commands (tests, lint, typecheck, build) and reports pass/fail.',
173
+ mode: 'subagent',
174
+ color: '#D97757',
175
+ permission: buildEngineerVerifyPermissions(),
176
+ prompt: prompts.engineerVerifyPrompt,
177
+ };
178
+ }
149
179
  // ---------------------------------------------------------------------------
150
180
  // Global permission helper
151
181
  // ---------------------------------------------------------------------------
@@ -1,7 +1,7 @@
1
1
  import { tool } from '@opencode-ai/plugin';
2
2
  import { composeWrapperPrompt, managerPromptRegistry } from '../prompts/registry.js';
3
3
  import { discoverProjectClaudeFiles } from '../util/project-context.js';
4
- import { AGENT_CTO, AGENT_ENGINEER_EXPLORE, AGENT_ENGINEER_IMPLEMENT, buildCtoAgentConfig, buildEngineerExploreAgentConfig, buildEngineerImplementAgentConfig, denyRestrictedToolsGlobally, } from './agent-hierarchy.js';
4
+ import { AGENT_CTO, AGENT_ENGINEER_EXPLORE, AGENT_ENGINEER_IMPLEMENT, AGENT_ENGINEER_VERIFY, buildCtoAgentConfig, buildEngineerExploreAgentConfig, buildEngineerImplementAgentConfig, buildEngineerVerifyAgentConfig, denyRestrictedToolsGlobally, } from './agent-hierarchy.js';
5
5
  import { getOrCreatePluginServices } from './service-factory.js';
6
6
  export const ClaudeManagerPlugin = async ({ worktree }) => {
7
7
  const services = getOrCreatePluginServices(worktree);
@@ -213,10 +213,12 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
213
213
  ...managerPromptRegistry,
214
214
  engineerExplorePrompt: composeWrapperPrompt(managerPromptRegistry.engineerExplorePrompt, claudeFiles),
215
215
  engineerImplementPrompt: composeWrapperPrompt(managerPromptRegistry.engineerImplementPrompt, claudeFiles),
216
+ engineerVerifyPrompt: composeWrapperPrompt(managerPromptRegistry.engineerVerifyPrompt, claudeFiles),
216
217
  };
217
218
  config.agent[AGENT_CTO] ??= buildCtoAgentConfig(managerPromptRegistry);
218
219
  config.agent[AGENT_ENGINEER_EXPLORE] ??= buildEngineerExploreAgentConfig(derivedPrompts);
219
220
  config.agent[AGENT_ENGINEER_IMPLEMENT] ??= buildEngineerImplementAgentConfig(derivedPrompts);
221
+ config.agent[AGENT_ENGINEER_VERIFY] ??= buildEngineerVerifyAgentConfig(derivedPrompts);
220
222
  },
221
223
  tool: {
222
224
  explore: tool({
@@ -236,6 +238,22 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
236
238
  return executeDelegate({ ...args, mode: 'plan', wrapperType: 'explore' }, context);
237
239
  },
238
240
  }),
241
+ verify: tool({
242
+ description: 'Run verification commands (tests, lint, typecheck, build) and report pass/fail. ' +
243
+ 'Use after implementation to verify correctness.',
244
+ args: {
245
+ message: tool.schema.string().min(1),
246
+ model: tool.schema
247
+ .enum(['claude-opus-4-6', 'claude-sonnet-4-6'])
248
+ .optional(),
249
+ effort: tool.schema.enum(['medium', 'high', 'max']).default('high'),
250
+ freshSession: tool.schema.boolean().default(false),
251
+ sessionSystemPrompt: tool.schema.string().optional(),
252
+ },
253
+ async execute(args, context) {
254
+ return executeDelegate({ ...args, mode: 'free', wrapperType: 'engineer_verify' }, context);
255
+ },
256
+ }),
239
257
  implement: tool({
240
258
  description: 'Implement code changes - can read, edit, and create files. ' +
241
259
  'Use after exploration to make changes.',
@@ -20,8 +20,6 @@ export function getOrCreatePluginServices(worktree) {
20
20
  const stateStore = new FileRunStateStore();
21
21
  const transcriptStore = new TranscriptStore();
22
22
  const manager = new PersistentManager(sessionController, gitOps, stateStore, contextTracker, transcriptStore);
23
- // Try to restore active session state (fire and forget)
24
- manager.tryRestore().catch(() => { });
25
23
  const liveTailer = new SessionLiveTailer();
26
24
  const services = {
27
25
  manager,
@@ -162,7 +162,12 @@ export const managerPromptRegistry = {
162
162
  '- `engineer_explore` — read-only investigation. Use for: exploring unfamiliar code,',
163
163
  ' mapping dependencies, analyzing impact, asking "how does X work?"',
164
164
  '- `engineer_implement` — implementation. Use for: all code changes, test runs, fixes.',
165
- '- If steps are independent, spawn multiple engineers in parallel.',
165
+ '- `engineer_verify` verification. Use for: running tests, lint, typecheck, build.',
166
+ ' Use after implementation to confirm correctness.',
167
+ '- You may run MULTIPLE `engineer_explore` agents in parallel for independent investigations.',
168
+ '- You may run MULTIPLE `engineer_verify` agents in parallel for independent verification tasks.',
169
+ '- Only run ONE `engineer_implement` agent at a time — it makes changes to the worktree.',
170
+ '- Coordinate so that only one implement/verify runs at a time to avoid git conflicts.',
166
171
  '',
167
172
  '## Context efficiency',
168
173
  '- Use `engineer_explore` for broad exploration so your own context stays clean.',
@@ -217,6 +222,18 @@ export const managerPromptRegistry = {
217
222
  '- effort "max": complex refactors, subtle bugs, cross-cutting changes.',
218
223
  ],
219
224
  }),
225
+ engineerVerifyPrompt: buildEngineerWrapperPrompt({
226
+ purpose: 'verification',
227
+ toolName: 'verify',
228
+ roleDescription: 'You are an expert engineer verifying changes. ' +
229
+ 'Execute directly. No preamble. Follow existing repo conventions. ' +
230
+ 'Do not run git commit, git push, git reset, git checkout, or git stash — git operations are handled externally.',
231
+ modelDefaults: [
232
+ '- claude-opus-4-6 + high: most verification tasks (default).',
233
+ '- claude-sonnet-4-6: simple test runs, lint checks.',
234
+ '- effort "medium": straightforward verification.',
235
+ ],
236
+ }),
220
237
  engineerSessionPrompt: [
221
238
  'You are an expert engineer. Execute instructions precisely.',
222
239
  '',
@@ -2,6 +2,7 @@ export interface ManagerPromptRegistry {
2
2
  ctoSystemPrompt: string;
3
3
  engineerExplorePrompt: string;
4
4
  engineerImplementPrompt: string;
5
+ engineerVerifyPrompt: string;
5
6
  engineerSessionPrompt: string;
6
7
  modePrefixes: {
7
8
  plan: string;
@@ -111,15 +112,6 @@ export interface GitOperationResult {
111
112
  output: string;
112
113
  error?: string;
113
114
  }
114
- export interface ActiveSessionState {
115
- sessionId: string;
116
- startedAt: string;
117
- totalTurns: number;
118
- totalCostUsd: number;
119
- estimatedContextPercent: number | null;
120
- contextWindowSize: number | null;
121
- latestInputTokens: number | null;
122
- }
123
115
  export type PersistentRunStatus = 'running' | 'completed' | 'failed';
124
116
  export interface PersistentRunMessageRecord {
125
117
  timestamp: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doingdev/opencode-claude-manager-plugin",
3
- "version": "0.1.43",
3
+ "version": "0.1.46",
4
4
  "description": "OpenCode plugin that orchestrates Claude Code sessions.",
5
5
  "keywords": [
6
6
  "opencode",