@doingdev/opencode-claude-manager-plugin 0.1.54 → 0.1.55
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/manager/team-orchestrator.d.ts +2 -2
- package/dist/manager/team-orchestrator.js +6 -6
- package/dist/plugin/agent-hierarchy.d.ts +2 -2
- package/dist/plugin/agent-hierarchy.js +9 -18
- package/dist/plugin/claude-manager.plugin.js +13 -12
- package/dist/plugin/service-factory.js +1 -1
- package/dist/prompts/registry.js +11 -3
- package/dist/types/contracts.d.ts +4 -1
- package/package.json +1 -1
|
@@ -18,8 +18,8 @@ export declare class TeamOrchestrator {
|
|
|
18
18
|
private readonly teamStore;
|
|
19
19
|
private readonly transcriptStore;
|
|
20
20
|
private readonly engineerSessionPrompt;
|
|
21
|
-
private readonly
|
|
22
|
-
constructor(sessions: ClaudeSessionService, teamStore: TeamStateStore, transcriptStore: TranscriptStore, engineerSessionPrompt: string,
|
|
21
|
+
private readonly planSynthesisPrompt;
|
|
22
|
+
constructor(sessions: ClaudeSessionService, teamStore: TeamStateStore, transcriptStore: TranscriptStore, engineerSessionPrompt: string, planSynthesisPrompt: string);
|
|
23
23
|
getOrCreateTeam(cwd: string, teamId: string): Promise<TeamRecord>;
|
|
24
24
|
listTeams(cwd: string): Promise<TeamRecord[]>;
|
|
25
25
|
recordWrapperSession(cwd: string, teamId: string, engineer: EngineerName, wrapperSessionId: string): Promise<void>;
|
|
@@ -6,13 +6,13 @@ export class TeamOrchestrator {
|
|
|
6
6
|
teamStore;
|
|
7
7
|
transcriptStore;
|
|
8
8
|
engineerSessionPrompt;
|
|
9
|
-
|
|
10
|
-
constructor(sessions, teamStore, transcriptStore, engineerSessionPrompt,
|
|
9
|
+
planSynthesisPrompt;
|
|
10
|
+
constructor(sessions, teamStore, transcriptStore, engineerSessionPrompt, planSynthesisPrompt) {
|
|
11
11
|
this.sessions = sessions;
|
|
12
12
|
this.teamStore = teamStore;
|
|
13
13
|
this.transcriptStore = transcriptStore;
|
|
14
14
|
this.engineerSessionPrompt = engineerSessionPrompt;
|
|
15
|
-
this.
|
|
15
|
+
this.planSynthesisPrompt = planSynthesisPrompt;
|
|
16
16
|
}
|
|
17
17
|
async getOrCreateTeam(cwd, teamId) {
|
|
18
18
|
const existing = await this.teamStore.getTeam(cwd, teamId);
|
|
@@ -225,7 +225,7 @@ export class TeamOrchestrator {
|
|
|
225
225
|
const synthesisResult = await this.sessions.runTask({
|
|
226
226
|
cwd: input.cwd,
|
|
227
227
|
prompt: buildSynthesisPrompt(input.request, drafts),
|
|
228
|
-
systemPrompt: buildSynthesisSystemPrompt(this.
|
|
228
|
+
systemPrompt: buildSynthesisSystemPrompt(this.planSynthesisPrompt),
|
|
229
229
|
persistSession: false,
|
|
230
230
|
includePartialMessages: false,
|
|
231
231
|
permissionMode: 'acceptEdits',
|
|
@@ -365,8 +365,8 @@ function buildPlanDraftRequest(perspective, request) {
|
|
|
365
365
|
`User request: ${request}`,
|
|
366
366
|
].join('\n');
|
|
367
367
|
}
|
|
368
|
-
function buildSynthesisSystemPrompt(
|
|
369
|
-
return
|
|
368
|
+
function buildSynthesisSystemPrompt(planSynthesisPrompt) {
|
|
369
|
+
return planSynthesisPrompt;
|
|
370
370
|
}
|
|
371
371
|
function buildSynthesisPrompt(request, drafts) {
|
|
372
372
|
return [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EngineerName, ManagerPromptRegistry } from '../types/contracts.js';
|
|
2
2
|
export declare const AGENT_CTO = "cto";
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const AGENT_TEAM_PLANNER = "team-planner";
|
|
4
4
|
export declare const ENGINEER_AGENT_IDS: {
|
|
5
5
|
readonly Tom: "tom";
|
|
6
6
|
readonly John: "john";
|
|
@@ -42,7 +42,7 @@ export declare function buildEngineerAgentConfig(prompts: ManagerPromptRegistry,
|
|
|
42
42
|
permission: AgentPermission;
|
|
43
43
|
prompt: string;
|
|
44
44
|
};
|
|
45
|
-
export declare function
|
|
45
|
+
export declare function buildTeamPlannerAgentConfig(prompts: ManagerPromptRegistry): {
|
|
46
46
|
description: string;
|
|
47
47
|
mode: "subagent";
|
|
48
48
|
hidden: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TEAM_ENGINEERS } from '../team/roster.js';
|
|
2
2
|
export const AGENT_CTO = 'cto';
|
|
3
|
-
export const
|
|
3
|
+
export const AGENT_TEAM_PLANNER = 'team-planner';
|
|
4
4
|
export const ENGINEER_AGENT_IDS = {
|
|
5
5
|
Tom: 'tom',
|
|
6
6
|
John: 'john',
|
|
@@ -38,24 +38,15 @@ const CTO_READONLY_TOOLS = {
|
|
|
38
38
|
todoread: 'allow',
|
|
39
39
|
question: 'allow',
|
|
40
40
|
};
|
|
41
|
-
function
|
|
41
|
+
function buildTeamPlannerPermissions() {
|
|
42
42
|
const denied = {};
|
|
43
43
|
for (const toolId of ALL_RESTRICTED_TOOL_IDS) {
|
|
44
44
|
denied[toolId] = 'deny';
|
|
45
45
|
}
|
|
46
46
|
return {
|
|
47
47
|
'*': 'deny',
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
glob: 'allow',
|
|
51
|
-
list: 'allow',
|
|
52
|
-
codesearch: 'allow',
|
|
53
|
-
webfetch: 'deny',
|
|
54
|
-
websearch: 'deny',
|
|
55
|
-
lsp: 'deny',
|
|
56
|
-
todowrite: 'deny',
|
|
57
|
-
todoread: 'deny',
|
|
58
|
-
question: 'deny',
|
|
48
|
+
plan_with_team: 'allow',
|
|
49
|
+
question: 'allow',
|
|
59
50
|
...denied,
|
|
60
51
|
};
|
|
61
52
|
}
|
|
@@ -72,7 +63,7 @@ function buildCtoPermissions() {
|
|
|
72
63
|
for (const engineer of ENGINEER_AGENT_NAMES) {
|
|
73
64
|
taskPermissions[ENGINEER_AGENT_IDS[engineer]] = 'allow';
|
|
74
65
|
}
|
|
75
|
-
taskPermissions[
|
|
66
|
+
taskPermissions[AGENT_TEAM_PLANNER] = 'allow';
|
|
76
67
|
return {
|
|
77
68
|
'*': 'deny',
|
|
78
69
|
...CTO_READONLY_TOOLS,
|
|
@@ -111,14 +102,14 @@ export function buildEngineerAgentConfig(prompts, engineer) {
|
|
|
111
102
|
prompt: `You are ${engineer}.\n\n${prompts.engineerAgentPrompt}`,
|
|
112
103
|
};
|
|
113
104
|
}
|
|
114
|
-
export function
|
|
105
|
+
export function buildTeamPlannerAgentConfig(prompts) {
|
|
115
106
|
return {
|
|
116
|
-
description: '
|
|
107
|
+
description: 'Runs dual-engineer planning by calling plan_with_team. Asks for engineer names if not provided.',
|
|
117
108
|
mode: 'subagent',
|
|
118
109
|
hidden: false,
|
|
119
110
|
color: '#D97757',
|
|
120
|
-
permission:
|
|
121
|
-
prompt: prompts.
|
|
111
|
+
permission: buildTeamPlannerPermissions(),
|
|
112
|
+
prompt: prompts.teamPlannerPrompt,
|
|
122
113
|
};
|
|
123
114
|
}
|
|
124
115
|
export function denyRestrictedToolsGlobally(permissions) {
|
|
@@ -2,7 +2,7 @@ import { tool } from '@opencode-ai/plugin';
|
|
|
2
2
|
import { managerPromptRegistry } from '../prompts/registry.js';
|
|
3
3
|
import { isEngineerName } from '../team/roster.js';
|
|
4
4
|
import { TeamOrchestrator } from '../manager/team-orchestrator.js';
|
|
5
|
-
import { AGENT_CTO,
|
|
5
|
+
import { AGENT_CTO, AGENT_TEAM_PLANNER, ENGINEER_AGENT_IDS, ENGINEER_AGENT_NAMES, buildCtoAgentConfig, buildEngineerAgentConfig, buildTeamPlannerAgentConfig, denyRestrictedToolsGlobally, } from './agent-hierarchy.js';
|
|
6
6
|
import { getActiveTeamSession, getOrCreatePluginServices, getPersistedActiveTeam, getWrapperSessionMapping, setActiveTeamSession, setPersistedActiveTeam, setWrapperSessionMapping, } from './service-factory.js';
|
|
7
7
|
const MODEL_ENUM = ['claude-opus-4-6', 'claude-sonnet-4-6'];
|
|
8
8
|
const MODE_ENUM = ['explore', 'implement', 'verify'];
|
|
@@ -15,7 +15,7 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
15
15
|
config.permission ??= {};
|
|
16
16
|
denyRestrictedToolsGlobally(config.permission);
|
|
17
17
|
config.agent[AGENT_CTO] ??= buildCtoAgentConfig(managerPromptRegistry);
|
|
18
|
-
config.agent[
|
|
18
|
+
config.agent[AGENT_TEAM_PLANNER] ??= buildTeamPlannerAgentConfig(managerPromptRegistry);
|
|
19
19
|
for (const engineer of ENGINEER_AGENT_NAMES) {
|
|
20
20
|
config.agent[ENGINEER_AGENT_IDS[engineer]] ??= buildEngineerAgentConfig(managerPromptRegistry, engineer);
|
|
21
21
|
}
|
|
@@ -129,10 +129,10 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
129
129
|
abortSignal: context.abort,
|
|
130
130
|
onLeadEvent: (event) => reportClaudeEvent(context, args.leadEngineer, event),
|
|
131
131
|
onChallengerEvent: (event) => reportClaudeEvent(context, args.challengerEngineer, event),
|
|
132
|
-
onSynthesisEvent: (event) =>
|
|
132
|
+
onSynthesisEvent: (event) => reportPlanSynthesisEvent(context, event),
|
|
133
133
|
});
|
|
134
134
|
context.metadata({
|
|
135
|
-
title: '✅
|
|
135
|
+
title: '✅ Plan synthesis finished',
|
|
136
136
|
metadata: {
|
|
137
137
|
teamId: result.teamId,
|
|
138
138
|
lead: result.leadEngineer,
|
|
@@ -562,10 +562,10 @@ function reportClaudeEvent(context, engineer, event) {
|
|
|
562
562
|
});
|
|
563
563
|
}
|
|
564
564
|
}
|
|
565
|
-
function
|
|
565
|
+
function reportPlanSynthesisEvent(context, event) {
|
|
566
566
|
if (event.type === 'error') {
|
|
567
567
|
context.metadata({
|
|
568
|
-
title: `❌
|
|
568
|
+
title: `❌ Plan synthesis hit an error`,
|
|
569
569
|
metadata: {
|
|
570
570
|
sessionId: event.sessionId,
|
|
571
571
|
error: event.text.slice(0, 200),
|
|
@@ -575,7 +575,7 @@ function reportArchitectEvent(context, event) {
|
|
|
575
575
|
}
|
|
576
576
|
if (event.type === 'init') {
|
|
577
577
|
context.metadata({
|
|
578
|
-
title: `⚡
|
|
578
|
+
title: `⚡ Plan synthesis ready`,
|
|
579
579
|
metadata: {
|
|
580
580
|
sessionId: event.sessionId,
|
|
581
581
|
},
|
|
@@ -608,10 +608,10 @@ function reportArchitectEvent(context, event) {
|
|
|
608
608
|
const toolDescription = formatToolDescription(toolName ?? '', toolArgs);
|
|
609
609
|
context.metadata({
|
|
610
610
|
title: toolDescription
|
|
611
|
-
? `⚡
|
|
611
|
+
? `⚡ Plan synthesis → ${toolDescription}`
|
|
612
612
|
: toolName
|
|
613
|
-
? `⚡
|
|
614
|
-
: `⚡
|
|
613
|
+
? `⚡ Plan synthesis → ${toolName}`
|
|
614
|
+
: `⚡ Plan synthesis is running`,
|
|
615
615
|
metadata: {
|
|
616
616
|
sessionId: event.sessionId,
|
|
617
617
|
...(toolName !== undefined && { toolName }),
|
|
@@ -625,7 +625,7 @@ function reportArchitectEvent(context, event) {
|
|
|
625
625
|
const isThinking = event.text.startsWith('<thinking>');
|
|
626
626
|
const stateLabel = event.type === 'partial' && isThinking ? 'is thinking' : 'is working';
|
|
627
627
|
context.metadata({
|
|
628
|
-
title: `⚡
|
|
628
|
+
title: `⚡ Plan synthesis ${stateLabel}`,
|
|
629
629
|
metadata: {
|
|
630
630
|
sessionId: event.sessionId,
|
|
631
631
|
preview: event.text.slice(0, 160),
|
|
@@ -635,8 +635,9 @@ function reportArchitectEvent(context, event) {
|
|
|
635
635
|
}
|
|
636
636
|
}
|
|
637
637
|
function annotateToolRun(context, title, metadata) {
|
|
638
|
+
const agentLabel = context.agent === AGENT_CTO ? 'CTO' : undefined;
|
|
638
639
|
context.metadata({
|
|
639
|
-
title,
|
|
640
|
+
title: agentLabel ? `${agentLabel} → ${title}` : title,
|
|
640
641
|
metadata,
|
|
641
642
|
});
|
|
642
643
|
}
|
|
@@ -24,7 +24,7 @@ export function getOrCreatePluginServices(worktree) {
|
|
|
24
24
|
const teamStore = new TeamStateStore();
|
|
25
25
|
const transcriptStore = new TranscriptStore();
|
|
26
26
|
const manager = new PersistentManager(gitOps, transcriptStore);
|
|
27
|
-
const orchestrator = new TeamOrchestrator(sessionService, teamStore, transcriptStore, managerPromptRegistry.engineerSessionPrompt, managerPromptRegistry.
|
|
27
|
+
const orchestrator = new TeamOrchestrator(sessionService, teamStore, transcriptStore, managerPromptRegistry.engineerSessionPrompt, managerPromptRegistry.planSynthesisPrompt);
|
|
28
28
|
const services = {
|
|
29
29
|
manager,
|
|
30
30
|
sessions: sessionService,
|
package/dist/prompts/registry.js
CHANGED
|
@@ -27,7 +27,7 @@ export const managerPromptRegistry = {
|
|
|
27
27
|
'',
|
|
28
28
|
'Plan and decompose:',
|
|
29
29
|
'- Break work into independent pieces that can run in parallel. Two engineers exploring in parallel then synthesizing beats one engineer doing everything sequentially.',
|
|
30
|
-
'- For medium or large tasks, delegate dual-engineer exploration
|
|
30
|
+
'- For medium or large tasks, delegate dual-engineer exploration and synthesis to the `team-planner` subagent: task it with the request and the two engineer names (lead + challenger).',
|
|
31
31
|
'- Define clear success criteria before delegating. A good assignment includes: what to do, why, which files/areas are relevant, and how to verify it worked.',
|
|
32
32
|
'',
|
|
33
33
|
'Delegate through the Task tool:',
|
|
@@ -76,8 +76,8 @@ export const managerPromptRegistry = {
|
|
|
76
76
|
'Report blockers immediately with exact error output. Do not retry silently more than once.',
|
|
77
77
|
'Do not run git commit, git push, git reset, git checkout, or git stash.',
|
|
78
78
|
].join('\n'),
|
|
79
|
-
|
|
80
|
-
'You are
|
|
79
|
+
planSynthesisPrompt: [
|
|
80
|
+
'You are synthesizing two independent engineering plans into one stronger, unified plan.',
|
|
81
81
|
'Compare the lead and challenger plans on clarity, feasibility, risk, and fit to the user request.',
|
|
82
82
|
'Prefer the simplest path that fully addresses the goal. Surface tradeoffs honestly.',
|
|
83
83
|
'If the plans disagree on something only the user can decide, surface exactly one recommended question and one recommended answer.',
|
|
@@ -91,6 +91,14 @@ export const managerPromptRegistry = {
|
|
|
91
91
|
'## Recommended Answer',
|
|
92
92
|
'<answer or NONE>',
|
|
93
93
|
].join('\n'),
|
|
94
|
+
teamPlannerPrompt: [
|
|
95
|
+
'You are the team planner. Your only job is to invoke `plan_with_team`.',
|
|
96
|
+
'`plan_with_team` dispatches two engineers in parallel (lead + challenger) then synthesizes their plans.',
|
|
97
|
+
'',
|
|
98
|
+
'If the task includes a lead engineer and a challenger engineer, call `plan_with_team` immediately.',
|
|
99
|
+
'If either engineer name is missing, use `question` to ask: which engineers should lead and challenge (Tom, John, Maya, Sara, or Alex)?',
|
|
100
|
+
'Do not attempt any planning or analysis yourself. Delegate entirely to `plan_with_team`.',
|
|
101
|
+
].join('\n'),
|
|
94
102
|
contextWarnings: {
|
|
95
103
|
moderate: 'Engineer context is getting full ({percent}% estimated). Reuse is still fine, but keep the next prompt focused.',
|
|
96
104
|
high: 'Engineer context is heavy ({percent}% estimated, {turns} turns, ${cost}). Prefer a narrowly scoped follow-up or internal compaction.',
|
|
@@ -2,7 +2,10 @@ export interface ManagerPromptRegistry {
|
|
|
2
2
|
ctoSystemPrompt: string;
|
|
3
3
|
engineerAgentPrompt: string;
|
|
4
4
|
engineerSessionPrompt: string;
|
|
5
|
-
|
|
5
|
+
/** Prompt injected as the system prompt of the non-persistent synthesis runTask call inside plan_with_team. */
|
|
6
|
+
planSynthesisPrompt: string;
|
|
7
|
+
/** Visible subagent prompt for teamPlanner — thin bridge that calls plan_with_team. */
|
|
8
|
+
teamPlannerPrompt: string;
|
|
6
9
|
contextWarnings: {
|
|
7
10
|
moderate: string;
|
|
8
11
|
high: string;
|