@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.
@@ -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 architectSystemPrompt;
22
- constructor(sessions: ClaudeSessionService, teamStore: TeamStateStore, transcriptStore: TranscriptStore, engineerSessionPrompt: string, architectSystemPrompt: 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
- architectSystemPrompt;
10
- constructor(sessions, teamStore, transcriptStore, engineerSessionPrompt, architectSystemPrompt) {
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.architectSystemPrompt = architectSystemPrompt;
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.architectSystemPrompt),
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(architectSystemPrompt) {
369
- return architectSystemPrompt;
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 AGENT_ARCHITECT = "architect";
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 buildArchitectAgentConfig(prompts: ManagerPromptRegistry): {
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 AGENT_ARCHITECT = 'architect';
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 buildArchitectPermissions() {
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
- read: 'allow',
49
- grep: 'allow',
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[AGENT_ARCHITECT] = 'allow';
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 buildArchitectAgentConfig(prompts) {
105
+ export function buildTeamPlannerAgentConfig(prompts) {
115
106
  return {
116
- description: 'Synthesizes two engineer plan drafts into one stronger, actionable plan.',
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: buildArchitectPermissions(),
121
- prompt: prompts.architectSystemPrompt,
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, AGENT_ARCHITECT, ENGINEER_AGENT_IDS, ENGINEER_AGENT_NAMES, buildCtoAgentConfig, buildEngineerAgentConfig, buildArchitectAgentConfig, denyRestrictedToolsGlobally, } from './agent-hierarchy.js';
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[AGENT_ARCHITECT] ??= buildArchitectAgentConfig(managerPromptRegistry);
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) => reportArchitectEvent(context, event),
132
+ onSynthesisEvent: (event) => reportPlanSynthesisEvent(context, event),
133
133
  });
134
134
  context.metadata({
135
- title: '✅ Architect finished',
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 reportArchitectEvent(context, event) {
565
+ function reportPlanSynthesisEvent(context, event) {
566
566
  if (event.type === 'error') {
567
567
  context.metadata({
568
- title: `❌ Architect hit an error`,
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: `⚡ Architect session ready`,
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
- ? `⚡ Architect → ${toolDescription}`
611
+ ? `⚡ Plan synthesis → ${toolDescription}`
612
612
  : toolName
613
- ? `⚡ Architect → ${toolName}`
614
- : `⚡ Architect is using Claude Code tools`,
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: `⚡ Architect ${stateLabel}`,
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.architectSystemPrompt);
27
+ const orchestrator = new TeamOrchestrator(sessionService, teamStore, transcriptStore, managerPromptRegistry.engineerSessionPrompt, managerPromptRegistry.planSynthesisPrompt);
28
28
  const services = {
29
29
  manager,
30
30
  sessions: sessionService,
@@ -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 to two engineers, then task the `architect` subagent to synthesize their independent plans into one stronger plan.',
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
- architectSystemPrompt: [
80
- 'You are the Architect. Your role is to synthesize two independent engineering plans into one stronger, unified plan.',
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
- architectSystemPrompt: string;
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doingdev/opencode-claude-manager-plugin",
3
- "version": "0.1.54",
3
+ "version": "0.1.55",
4
4
  "description": "OpenCode plugin that orchestrates Claude Code sessions.",
5
5
  "keywords": [
6
6
  "opencode",