@doingdev/opencode-claude-manager-plugin 0.1.18 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,15 +4,17 @@ This package provides an OpenCode plugin that lets an OpenCode-side manager agen
4
4
 
5
5
  ## Overview
6
6
 
7
- Use this when you want OpenCode to act as a manager over Claude Code instead of talking to Claude directly. The plugin gives OpenCode a stable tool surface for discovering Claude metadata, delegating work to Claude sessions, and splitting tasks into subagents (all using the same working directory).
7
+ Use this when you want OpenCode to act as a manager over Claude Code instead of talking to Claude directly. The plugin gives OpenCode a stable tool surface for delegating work to Claude Code sessions, managing session lifecycle (compact, clear, fresh start), reviewing changes via git, and inspecting session history.
8
8
 
9
9
  ## Features
10
10
 
11
11
  - Runs Claude Code tasks from OpenCode through `@anthropic-ai/claude-agent-sdk`.
12
+ - Persistent sessions with `freshSession`, model, and effort controls for safe task isolation.
13
+ - Context lifecycle: compact (preserve state) or clear (start fresh).
12
14
  - Discovers repo-local Claude metadata from `.claude/skills`, `.claude/commands`, `CLAUDE.md`, and settings hooks.
13
- - Splits multi-step tasks into subagents that run sequentially in the same repository directory.
14
- - Persists manager runs under `.claude-manager/runs` so sessions can be inspected later.
15
- - Exposes manager-facing tools instead of relying on undocumented plugin-defined slash commands.
15
+ - Git integration: diff, commit, and reset from the manager layer.
16
+ - Tool approval policy for governing which Claude Code tools are allowed.
17
+ - Optionally persists manager run records under `.claude-manager/runs` for post-hoc inspection (populated when tasks are executed through the run-tracking path).
16
18
 
17
19
  ## Requirements
18
20
 
@@ -49,10 +51,35 @@ If you are testing locally, point OpenCode at the local package or plugin file u
49
51
 
50
52
  ## OpenCode tools
51
53
 
52
- - `claude_manager_run` - run a task through Claude with optional splitting into subagents; returns a compact output summary and a `runId` for deeper inspection
53
- - `claude_manager_metadata` - inspect available Claude commands, skills, hooks, and settings
54
- - `claude_manager_sessions` - list Claude sessions or inspect a saved transcript
55
- - `claude_manager_runs` - inspect persisted manager run records
54
+ ### Session management
55
+
56
+ - `claude_manager_send` send a message to the persistent Claude Code session. Auto-creates on first call, resumes on subsequent calls.
57
+ - `message` (required) the instruction to send.
58
+ - `mode` — `"plan"` (read-only investigation) or `"free"` (default, normal execution with edits).
59
+ - `freshSession` — set to `true` to clear the active session before sending. Use when switching to an unrelated task or when context is contaminated.
60
+ - `model` — `"claude-opus-4-6"` (default, recommended for most coding work), `"claude-sonnet-4-6"`, or `"claude-sonnet-4-5"` (faster/lighter tasks).
61
+ - `effort` — `"high"` (default), `"medium"` (lighter tasks), `"low"`, or `"max"` (especially hard problems).
62
+ - `claude_manager_compact` — compress the active session context while preserving session state. Use before clearing when context is high but salvageable.
63
+ - `claude_manager_clear` — drop the active session entirely; next send starts fresh.
64
+ - `claude_manager_status` — get current session health: context %, turns, cost, session ID.
65
+
66
+ ### Git operations
67
+
68
+ - `claude_manager_git_diff` — review all uncommitted changes (staged + unstaged).
69
+ - `claude_manager_git_commit` — stage all changes and commit with a message.
70
+ - `claude_manager_git_reset` — hard reset + clean (destructive).
71
+
72
+ ### Inspection
73
+
74
+ - `claude_manager_metadata` — inspect available Claude commands, skills, hooks, and settings.
75
+ - `claude_manager_sessions` — list Claude sessions or inspect a saved transcript.
76
+ - `claude_manager_runs` — list or inspect persisted manager run records (may be empty if tasks were sent directly via `claude_manager_send` rather than the run-tracking path).
77
+
78
+ ### Tool approval
79
+
80
+ - `claude_manager_approval_policy` — view the current tool approval policy.
81
+ - `claude_manager_approval_decisions` — view recent tool approval decisions.
82
+ - `claude_manager_approval_update` — add/remove rules, change default action, or enable/disable approval.
56
83
 
57
84
  ## Plugin-provided agents and commands
58
85
 
@@ -69,13 +96,26 @@ These are added to OpenCode config at runtime by the plugin, so they do not requ
69
96
  Typical flow inside OpenCode:
70
97
 
71
98
  1. Inspect Claude capabilities with `claude_manager_metadata`.
72
- 2. Delegate work with `claude_manager_run`.
73
- 3. Inspect saved Claude history with `claude_manager_sessions` or prior orchestration records with `claude_manager_runs`.
99
+ 2. Delegate work with `claude_manager_send`.
100
+ 3. Review changes with `claude_manager_git_diff`, then commit or reset.
101
+ 4. Inspect saved Claude history with `claude_manager_sessions` or prior orchestration records with `claude_manager_runs`.
102
+
103
+ Example tasks:
104
+
105
+ ```text
106
+ Use claude_manager_send to implement the new validation logic in src/auth.ts, then review with claude_manager_git_diff.
107
+ ```
108
+
109
+ Start a fresh session for an unrelated task:
110
+
111
+ ```text
112
+ Use claude_manager_send with freshSession:true to investigate the failing CI test in test/api.test.ts using mode:"plan".
113
+ ```
74
114
 
75
- Example task:
115
+ Reclaim context mid-session:
76
116
 
77
117
  ```text
78
- Use claude_manager_run to split this implementation into subagents and summarize the final result.
118
+ Use claude_manager_compact to free up context, then continue with the next implementation step.
79
119
  ```
80
120
 
81
121
  ## Local Development
@@ -136,8 +176,8 @@ After trusted publishing is working, you can tighten npm package security by dis
136
176
  ## Limitations
137
177
 
138
178
  - Claude slash commands and skills come primarily from filesystem discovery; SDK probing is available but optional.
139
- - Multiple subagents share one working directory and run one after another to avoid overlapping edits.
140
- - Run state is local to the repo under `.claude-manager/` and is ignored by git.
179
+ - Session state is local to the repo under `.claude-manager/` and is ignored by git.
180
+ - Context tracking is heuristic-based; actual SDK context usage may differ slightly.
141
181
 
142
182
  ## Scripts
143
183
 
@@ -0,0 +1,49 @@
1
+ import type { ClaudeAgentSdkAdapter } from '../claude/claude-agent-sdk-adapter.js';
2
+ import type { ParallelSessionJobRecord, SessionMode } from '../types/contracts.js';
3
+ /**
4
+ * Runs additional Claude Code SDK sessions concurrently with the main persistent session.
5
+ * Same worktree from multiple jobs can conflict on git — callers should use plan mode or branches.
6
+ */
7
+ export declare class ParallelSessionJobManager {
8
+ private readonly sdkAdapter;
9
+ private readonly sessionPrompt;
10
+ private readonly modePrefixes;
11
+ private readonly jobs;
12
+ constructor(sdkAdapter: ClaudeAgentSdkAdapter, sessionPrompt: string, modePrefixes: {
13
+ plan: string;
14
+ free: string;
15
+ });
16
+ static get maxConcurrent(): number;
17
+ startJob(params: {
18
+ cwd: string;
19
+ message: string;
20
+ model?: string;
21
+ mode?: SessionMode;
22
+ resumeSessionId?: string;
23
+ }): {
24
+ ok: true;
25
+ jobId: string;
26
+ } | {
27
+ ok: false;
28
+ error: string;
29
+ };
30
+ private runJob;
31
+ abortJob(jobId: string): {
32
+ ok: true;
33
+ } | {
34
+ ok: false;
35
+ error: string;
36
+ };
37
+ getJob(jobId: string): ParallelSessionJobRecord | undefined;
38
+ listJobs(): ParallelSessionJobRecord[];
39
+ private runningCount;
40
+ waitForJobs(params: {
41
+ jobIds: string[];
42
+ mode: 'any' | 'all';
43
+ timeoutMs?: number;
44
+ }): Promise<{
45
+ finishedJobIds: string[];
46
+ timedOut: boolean;
47
+ pendingJobIds: string[];
48
+ }>;
49
+ }
@@ -0,0 +1,177 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ const MAX_CONCURRENT = 5;
3
+ function createCompletionLatch() {
4
+ let resolve;
5
+ const promise = new Promise((r) => {
6
+ resolve = r;
7
+ });
8
+ return { promise, resolve };
9
+ }
10
+ /**
11
+ * Runs additional Claude Code SDK sessions concurrently with the main persistent session.
12
+ * Same worktree from multiple jobs can conflict on git — callers should use plan mode or branches.
13
+ */
14
+ export class ParallelSessionJobManager {
15
+ sdkAdapter;
16
+ sessionPrompt;
17
+ modePrefixes;
18
+ jobs = new Map();
19
+ constructor(sdkAdapter, sessionPrompt, modePrefixes) {
20
+ this.sdkAdapter = sdkAdapter;
21
+ this.sessionPrompt = sessionPrompt;
22
+ this.modePrefixes = modePrefixes;
23
+ }
24
+ static get maxConcurrent() {
25
+ return MAX_CONCURRENT;
26
+ }
27
+ startJob(params) {
28
+ if (this.runningCount() >= MAX_CONCURRENT) {
29
+ return {
30
+ ok: false,
31
+ error: `At most ${MAX_CONCURRENT} parallel Claude Code jobs may run at once. Wait or abort a job first.`,
32
+ };
33
+ }
34
+ const jobId = randomUUID();
35
+ const now = new Date().toISOString();
36
+ const mode = params.mode ?? 'free';
37
+ const prefix = this.modePrefixes[mode];
38
+ const prompt = prefix ? `${prefix}\n\n${params.message}` : params.message;
39
+ const input = {
40
+ cwd: params.cwd,
41
+ prompt,
42
+ persistSession: true,
43
+ permissionMode: mode === 'plan' ? 'plan' : 'acceptEdits',
44
+ includePartialMessages: true,
45
+ model: params.model,
46
+ resumeSessionId: params.resumeSessionId,
47
+ settingSources: ['user', 'project', 'local'],
48
+ agentProgressSummaries: true,
49
+ };
50
+ if (!params.resumeSessionId) {
51
+ input.systemPrompt = this.sessionPrompt;
52
+ input.model ??= 'claude-opus-4-6';
53
+ input.effort ??= 'high';
54
+ }
55
+ const abortController = new AbortController();
56
+ input.abortSignal = abortController.signal;
57
+ const preview = params.message.length > 120
58
+ ? `${params.message.slice(0, 120)}...`
59
+ : params.message;
60
+ const { promise: completion, resolve: resolveCompletion } = createCompletionLatch();
61
+ const record = {
62
+ id: jobId,
63
+ cwd: params.cwd,
64
+ status: 'running',
65
+ createdAt: now,
66
+ updatedAt: now,
67
+ promptPreview: preview,
68
+ resumeSessionId: params.resumeSessionId,
69
+ };
70
+ this.jobs.set(jobId, {
71
+ record,
72
+ abortController,
73
+ completion,
74
+ resolveCompletion,
75
+ });
76
+ void this.runJob(jobId, input, resolveCompletion);
77
+ return { ok: true, jobId };
78
+ }
79
+ async runJob(jobId, input, resolveCompletion) {
80
+ const entry = this.jobs.get(jobId);
81
+ if (!entry) {
82
+ resolveCompletion();
83
+ return;
84
+ }
85
+ try {
86
+ const result = await this.sdkAdapter.runSession(input);
87
+ const rec = entry.record;
88
+ rec.updatedAt = new Date().toISOString();
89
+ rec.status = 'completed';
90
+ rec.sessionId = result.sessionId;
91
+ rec.finalText = result.finalText;
92
+ rec.turns = result.turns;
93
+ rec.totalCostUsd = result.totalCostUsd;
94
+ }
95
+ catch (error) {
96
+ const rec = entry.record;
97
+ rec.updatedAt = new Date().toISOString();
98
+ if (rec.status !== 'aborted') {
99
+ rec.status = 'failed';
100
+ rec.error = error instanceof Error ? error.message : String(error);
101
+ }
102
+ }
103
+ finally {
104
+ resolveCompletion();
105
+ }
106
+ }
107
+ abortJob(jobId) {
108
+ const entry = this.jobs.get(jobId);
109
+ if (!entry) {
110
+ return { ok: false, error: 'Unknown job id' };
111
+ }
112
+ if (entry.record.status !== 'running') {
113
+ return { ok: false, error: 'Job is not running' };
114
+ }
115
+ entry.record.status = 'aborted';
116
+ entry.record.updatedAt = new Date().toISOString();
117
+ entry.abortController.abort();
118
+ return { ok: true };
119
+ }
120
+ getJob(jobId) {
121
+ return this.jobs.get(jobId)?.record;
122
+ }
123
+ listJobs() {
124
+ return [...this.jobs.values()]
125
+ .map((j) => j.record)
126
+ .sort((a, b) => a.createdAt.localeCompare(b.createdAt));
127
+ }
128
+ runningCount() {
129
+ let n = 0;
130
+ for (const { record } of this.jobs.values()) {
131
+ if (record.status === 'running') {
132
+ n += 1;
133
+ }
134
+ }
135
+ return n;
136
+ }
137
+ async waitForJobs(params) {
138
+ const unique = [...new Set(params.jobIds)].filter((id) => this.jobs.has(id));
139
+ if (unique.length === 0) {
140
+ return {
141
+ finishedJobIds: [],
142
+ timedOut: false,
143
+ pendingJobIds: [],
144
+ };
145
+ }
146
+ const waitWork = async () => {
147
+ if (params.mode === 'any') {
148
+ await Promise.race(unique.map((id) => this.jobs.get(id).completion));
149
+ }
150
+ else {
151
+ await Promise.all(unique.map((id) => this.jobs.get(id).completion));
152
+ }
153
+ };
154
+ let timedOut = false;
155
+ if (params.timeoutMs !== undefined && params.timeoutMs > 0) {
156
+ await Promise.race([
157
+ waitWork(),
158
+ new Promise((resolve) => {
159
+ setTimeout(() => {
160
+ timedOut = true;
161
+ resolve();
162
+ }, params.timeoutMs);
163
+ }),
164
+ ]);
165
+ }
166
+ else {
167
+ await waitWork();
168
+ }
169
+ const finishedJobIds = unique.filter((id) => this.jobs.get(id)?.record.status !== 'running');
170
+ const pendingJobIds = unique.filter((id) => this.jobs.get(id)?.record.status === 'running');
171
+ return {
172
+ finishedJobIds,
173
+ timedOut,
174
+ pendingJobIds,
175
+ };
176
+ }
177
+ }
@@ -19,6 +19,7 @@ export declare class PersistentManager {
19
19
  */
20
20
  sendMessage(cwd: string, message: string, options?: {
21
21
  model?: string;
22
+ effort?: 'low' | 'medium' | 'high' | 'max';
22
23
  mode?: 'plan' | 'free';
23
24
  abortSignal?: AbortSignal;
24
25
  }, onEvent?: ClaudeSessionEventHandler): Promise<{
@@ -3,6 +3,7 @@ import { managerPromptRegistry } from '../prompts/registry.js';
3
3
  import { getOrCreatePluginServices } from './service-factory.js';
4
4
  const MANAGER_TOOL_IDS = [
5
5
  'claude_manager_send',
6
+ 'claude_manager_compact',
6
7
  'claude_manager_git_diff',
7
8
  'claude_manager_git_commit',
8
9
  'claude_manager_git_reset',
@@ -31,6 +32,7 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
31
32
  // Research agent can inspect but not send or modify
32
33
  researchPermissions[toolId] =
33
34
  toolId === 'claude_manager_send' ||
35
+ toolId === 'claude_manager_compact' ||
34
36
  toolId === 'claude_manager_git_commit' ||
35
37
  toolId === 'claude_manager_git_reset' ||
36
38
  toolId === 'claude_manager_clear' ||
@@ -50,6 +52,9 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
50
52
  codesearch: 'allow',
51
53
  webfetch: 'allow',
52
54
  websearch: 'allow',
55
+ todowrite: 'allow',
56
+ todoread: 'allow',
57
+ question: 'allow',
53
58
  ...managerPermissions,
54
59
  },
55
60
  prompt: managerPromptRegistry.managerSystemPrompt,
@@ -109,15 +114,27 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
109
114
  'Auto-creates a session on first call. Resumes the existing session on subsequent calls. ' +
110
115
  'Returns the assistant response and current context health snapshot. ' +
111
116
  'Use mode "plan" for read-only investigation and planning (no edits), ' +
112
- 'or "free" (default) for normal execution with edit permissions.',
117
+ 'or "free" (default) for normal execution with edit permissions. ' +
118
+ 'Set freshSession to clear the active session before sending (use for unrelated tasks). ' +
119
+ 'Prefer claude-opus-4-6 (default) for most coding work; use a Sonnet model for faster/lighter tasks. ' +
120
+ 'Prefer effort "high" (default) for most work; use "medium" for lighter tasks and "max" for especially hard problems.',
113
121
  args: {
114
122
  message: tool.schema.string().min(1),
115
- model: tool.schema.string().optional(),
123
+ model: tool.schema
124
+ .enum(['claude-opus-4-6', 'claude-sonnet-4-6', 'claude-sonnet-4-5'])
125
+ .optional(),
126
+ effort: tool.schema
127
+ .enum(['low', 'medium', 'high', 'max'])
128
+ .default('high'),
116
129
  mode: tool.schema.enum(['plan', 'free']).default('free'),
130
+ freshSession: tool.schema.boolean().default(false),
117
131
  cwd: tool.schema.string().optional(),
118
132
  },
119
133
  async execute(args, context) {
120
134
  const cwd = args.cwd ?? context.worktree;
135
+ if (args.freshSession) {
136
+ await services.manager.clearSession(cwd);
137
+ }
121
138
  const hasActiveSession = services.manager.getStatus().sessionId !== null;
122
139
  const promptPreview = args.message.length > 100
123
140
  ? args.message.slice(0, 100) + '...'
@@ -133,7 +150,12 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
133
150
  });
134
151
  let turnsSoFar = 0;
135
152
  let costSoFar = 0;
136
- const result = await services.manager.sendMessage(cwd, args.message, { model: args.model, mode: args.mode, abortSignal: context.abort }, (event) => {
153
+ const result = await services.manager.sendMessage(cwd, args.message, {
154
+ model: args.model,
155
+ effort: args.effort,
156
+ mode: args.mode,
157
+ abortSignal: context.abort,
158
+ }, (event) => {
137
159
  if (event.turns !== undefined) {
138
160
  turnsSoFar = event.turns;
139
161
  }
@@ -298,6 +320,36 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
298
320
  }, null, 2);
299
321
  },
300
322
  }),
323
+ claude_manager_compact: tool({
324
+ description: 'Compact the active Claude Code session to reclaim context space. ' +
325
+ 'Sends /compact to the session, which compresses prior conversation while preserving state. ' +
326
+ 'Use before clearing when context is high but the session still has useful state. ' +
327
+ 'Fails if there is no active session.',
328
+ args: {
329
+ cwd: tool.schema.string().optional(),
330
+ },
331
+ async execute(args, context) {
332
+ const cwd = args.cwd ?? context.worktree;
333
+ annotateToolRun(context, 'Compacting session', {});
334
+ const result = await services.manager.compactSession(cwd);
335
+ const snap = services.manager.getStatus();
336
+ const contextWarning = formatContextWarning(snap);
337
+ context.metadata({
338
+ title: contextWarning
339
+ ? `Claude Code: Compacted — context at ${snap.estimatedContextPercent}%`
340
+ : `Claude Code: Compacted (${snap.totalTurns} turns, $${(snap.totalCostUsd ?? 0).toFixed(4)})`,
341
+ metadata: { sessionId: result.sessionId },
342
+ });
343
+ return JSON.stringify({
344
+ sessionId: result.sessionId,
345
+ finalText: result.finalText,
346
+ turns: result.turns,
347
+ totalCostUsd: result.totalCostUsd,
348
+ context: snap,
349
+ contextWarning,
350
+ }, null, 2);
351
+ },
352
+ }),
301
353
  claude_manager_git_diff: tool({
302
354
  description: 'Run git diff to see all current changes (staged + unstaged) relative to HEAD.',
303
355
  args: {
@@ -14,7 +14,7 @@ export const managerPromptRegistry = {
14
14
  ' commit — checkpoint good work with claude_manager_git_commit',
15
15
  ' correct — send a targeted fix instruction (never "try again")',
16
16
  ' reset — discard bad work with claude_manager_git_reset',
17
- ' ask — ask the user one narrow, high-value question',
17
+ ' ask — use the question tool for structured choices, or one narrow text question',
18
18
  '',
19
19
  'Default order: investigate → delegate → review → validate → commit.',
20
20
  'Skip steps only when you have strong evidence they are unnecessary.',
@@ -46,6 +46,7 @@ export const managerPromptRegistry = {
46
46
  'When requirements are unclear:',
47
47
  '1. First, try to resolve it yourself — read code, check tests, grep for usage.',
48
48
  '2. If ambiguity remains, ask the user ONE specific question.',
49
+ ' Prefer the question tool when discrete options exist (OpenCode shows choices in the UI).',
49
50
  ' Bad: "What should I do?"',
50
51
  ' Good: "The `UserService` has both `deactivate()` and `softDelete()` —',
51
52
  ' should the new endpoint use deactivation (reversible) or',
@@ -60,6 +61,7 @@ export const managerPromptRegistry = {
60
61
  'Never send three corrections for the same problem in one session.',
61
62
  '',
62
63
  '## Multi-step tasks',
64
+ '- Use todowrite / todoread to track steps in OpenCode; keep items concrete and few.',
63
65
  '- Decompose large tasks into sequential focused instructions.',
64
66
  '- Commit after each successful step (checkpoint for rollback).',
65
67
  '- Tell Claude Code to use subagents for independent parallel work.',
@@ -70,11 +72,37 @@ export const managerPromptRegistry = {
70
72
  'Check the context snapshot returned by each send:',
71
73
  '- Under 50%: proceed freely.',
72
74
  '- 50–70%: finish current step, then evaluate if a fresh session is needed.',
73
- '- Over 70%: compact or clear before sending heavy instructions.',
75
+ '- Over 70%: use claude_manager_compact to reclaim context if the session',
76
+ ' still has useful state. Only clear if compaction is insufficient.',
74
77
  '- Over 85%: clear the session immediately.',
78
+ 'Use freshSession:true on claude_manager_send when switching to an unrelated',
79
+ 'task or when the session context is contaminated. Prefer this over a manual',
80
+ 'clear+send sequence — it is atomic and self-documenting.',
81
+ '',
82
+ '## Model and effort selection',
83
+ 'Choose model and effort deliberately before each delegation:',
84
+ '- claude-opus-4-6 + high effort: default for most coding tasks.',
85
+ '- claude-sonnet-4-6 or claude-sonnet-4-5: faster/lighter work (simple renames,',
86
+ ' formatting, test scaffolding, quick investigations).',
87
+ '- effort "medium": acceptable for lighter tasks that do not require deep reasoning.',
88
+ '- effort "max": reserve for unusually hard problems (complex refactors,',
89
+ ' subtle concurrency bugs, large cross-cutting changes).',
90
+ "- Do not use Haiku for this plugin's coding-agent role.",
91
+ '',
92
+ '## Plan mode',
93
+ 'When delegating with mode:"plan", Claude Code returns a read-only',
94
+ 'implementation plan. The plan MUST be returned inline in the assistant',
95
+ 'response — do NOT write plan artifacts to disk, create files, or rely on',
96
+ 'ExitPlanMode. Treat the returned finalText as the plan. If the plan is',
97
+ 'acceptable, switch to mode:"free" and delegate the implementation steps.',
75
98
  '',
76
99
  '## Tools reference',
100
+ 'todowrite / todoread — OpenCode session todo list (track multi-step work)',
101
+ 'question — OpenCode user prompt with options (clarify trade-offs)',
77
102
  'claude_manager_send — send instruction (creates or resumes session)',
103
+ ' freshSession:true — clear session first (use for unrelated tasks)',
104
+ ' model / effort — choose deliberately (see "Model and effort selection")',
105
+ 'claude_manager_compact — compress session context (preserves session state)',
78
106
  'claude_manager_git_diff — review all uncommitted changes',
79
107
  'claude_manager_git_commit — stage all + commit',
80
108
  'claude_manager_git_reset — hard reset + clean (destructive)',
@@ -121,10 +149,12 @@ export const managerPromptRegistry = {
121
149
  modePrefixes: {
122
150
  plan: [
123
151
  '[PLAN MODE] You are in read-only planning mode. Do NOT create or edit any files.',
152
+ 'Do NOT use ExitPlanMode or write plan artifacts to disk.',
124
153
  'Use read, grep, glob, and search tools only.',
125
154
  'Analyze the codebase and produce a detailed implementation plan:',
126
155
  'files to change, functions to modify, new files to create, test strategy,',
127
156
  'and potential risks. End with a numbered step-by-step plan.',
157
+ 'Return the entire plan inline in your response text.',
128
158
  ].join(' '),
129
159
  free: '',
130
160
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doingdev/opencode-claude-manager-plugin",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "OpenCode plugin that orchestrates Claude Code sessions.",
5
5
  "keywords": [
6
6
  "opencode",