@doingdev/opencode-claude-manager-plugin 0.1.46 → 0.1.47
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 +29 -31
- package/dist/index.d.ts +1 -1
- package/dist/manager/team-orchestrator.d.ts +50 -0
- package/dist/manager/team-orchestrator.js +360 -0
- package/dist/plugin/agent-hierarchy.d.ts +12 -34
- package/dist/plugin/agent-hierarchy.js +36 -129
- package/dist/plugin/claude-manager.plugin.js +190 -423
- package/dist/plugin/service-factory.d.ts +18 -3
- package/dist/plugin/service-factory.js +32 -1
- package/dist/prompts/registry.d.ts +1 -10
- package/dist/prompts/registry.js +42 -261
- package/dist/src/claude/claude-agent-sdk-adapter.js +2 -1
- package/dist/src/claude/session-live-tailer.js +2 -2
- package/dist/src/index.d.ts +1 -1
- package/dist/src/manager/git-operations.d.ts +10 -1
- package/dist/src/manager/git-operations.js +18 -3
- package/dist/src/manager/persistent-manager.d.ts +18 -6
- package/dist/src/manager/persistent-manager.js +19 -13
- package/dist/src/manager/session-controller.d.ts +7 -10
- package/dist/src/manager/session-controller.js +12 -62
- package/dist/src/manager/team-orchestrator.d.ts +50 -0
- package/dist/src/manager/team-orchestrator.js +360 -0
- package/dist/src/plugin/agent-hierarchy.d.ts +12 -26
- package/dist/src/plugin/agent-hierarchy.js +36 -99
- package/dist/src/plugin/claude-manager.plugin.js +214 -393
- package/dist/src/plugin/service-factory.d.ts +18 -3
- package/dist/src/plugin/service-factory.js +33 -9
- package/dist/src/prompts/registry.d.ts +1 -10
- package/dist/src/prompts/registry.js +41 -246
- package/dist/src/state/team-state-store.d.ts +14 -0
- package/dist/src/state/team-state-store.js +85 -0
- package/dist/src/team/roster.d.ts +5 -0
- package/dist/src/team/roster.js +38 -0
- package/dist/src/types/contracts.d.ts +55 -13
- package/dist/src/types/contracts.js +1 -1
- package/dist/state/team-state-store.d.ts +14 -0
- package/dist/state/team-state-store.js +85 -0
- package/dist/team/roster.d.ts +5 -0
- package/dist/team/roster.js +38 -0
- package/dist/test/claude-manager.plugin.test.js +55 -280
- package/dist/test/git-operations.test.js +65 -1
- package/dist/test/persistent-manager.test.js +3 -3
- package/dist/test/prompt-registry.test.js +32 -252
- package/dist/test/session-controller.test.js +27 -27
- package/dist/test/team-orchestrator.test.d.ts +1 -0
- package/dist/test/team-orchestrator.test.js +146 -0
- package/dist/test/team-state-store.test.d.ts +1 -0
- package/dist/test/team-state-store.test.js +54 -0
- package/dist/types/contracts.d.ts +54 -3
- package/dist/types/contracts.js +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import { ClaudeSessionService } from '../claude/claude-session.service.js';
|
|
2
2
|
import { SessionLiveTailer } from '../claude/session-live-tailer.js';
|
|
3
3
|
import { ToolApprovalManager } from '../claude/tool-approval-manager.js';
|
|
4
|
+
import { TeamStateStore } from '../state/team-state-store.js';
|
|
4
5
|
import { PersistentManager } from '../manager/persistent-manager.js';
|
|
5
|
-
|
|
6
|
+
import { TeamOrchestrator } from '../manager/team-orchestrator.js';
|
|
7
|
+
import type { DiscoveredClaudeFile, EngineerName } from '../types/contracts.js';
|
|
8
|
+
export interface ClaudeManagerPluginServices {
|
|
6
9
|
manager: PersistentManager;
|
|
7
10
|
sessions: ClaudeSessionService;
|
|
8
11
|
approvalManager: ToolApprovalManager;
|
|
9
12
|
liveTailer: SessionLiveTailer;
|
|
13
|
+
teamStore: TeamStateStore;
|
|
14
|
+
orchestrator: TeamOrchestrator;
|
|
10
15
|
}
|
|
11
|
-
export declare function getOrCreatePluginServices(worktree: string): ClaudeManagerPluginServices;
|
|
12
|
-
export
|
|
16
|
+
export declare function getOrCreatePluginServices(worktree: string, projectClaudeFiles?: DiscoveredClaudeFile[]): ClaudeManagerPluginServices;
|
|
17
|
+
export declare function clearPluginServices(): void;
|
|
18
|
+
export declare function setActiveTeamSession(worktree: string, teamId: string): void;
|
|
19
|
+
export declare function getActiveTeamSession(worktree: string): string | null;
|
|
20
|
+
export declare function setWrapperSessionMapping(worktree: string, wrapperSessionId: string, mapping: {
|
|
21
|
+
teamId: string;
|
|
22
|
+
engineer: EngineerName;
|
|
23
|
+
}): void;
|
|
24
|
+
export declare function getWrapperSessionMapping(worktree: string, wrapperSessionId: string): {
|
|
25
|
+
teamId: string;
|
|
26
|
+
engineer: EngineerName;
|
|
27
|
+
} | null;
|
|
@@ -3,36 +3,60 @@ import { ClaudeSessionService } from '../claude/claude-session.service.js';
|
|
|
3
3
|
import { SessionLiveTailer } from '../claude/session-live-tailer.js';
|
|
4
4
|
import { ToolApprovalManager } from '../claude/tool-approval-manager.js';
|
|
5
5
|
import { FileRunStateStore } from '../state/file-run-state-store.js';
|
|
6
|
+
import { TeamStateStore } from '../state/team-state-store.js';
|
|
6
7
|
import { TranscriptStore } from '../state/transcript-store.js';
|
|
7
8
|
import { ContextTracker } from '../manager/context-tracker.js';
|
|
8
9
|
import { GitOperations } from '../manager/git-operations.js';
|
|
9
10
|
import { SessionController } from '../manager/session-controller.js';
|
|
10
11
|
import { PersistentManager } from '../manager/persistent-manager.js';
|
|
11
12
|
import { managerPromptRegistry } from '../prompts/registry.js';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
import { TeamOrchestrator } from '../manager/team-orchestrator.js';
|
|
14
|
+
const serviceRegistry = new Map();
|
|
15
|
+
const activeTeamRegistry = new Map();
|
|
16
|
+
const wrapperSessionRegistry = new Map();
|
|
17
|
+
export function getOrCreatePluginServices(worktree, projectClaudeFiles = []) {
|
|
18
|
+
const existing = serviceRegistry.get(worktree);
|
|
19
|
+
if (existing) {
|
|
20
|
+
return existing;
|
|
17
21
|
}
|
|
18
22
|
const approvalManager = new ToolApprovalManager();
|
|
19
23
|
const sdkAdapter = new ClaudeAgentSdkAdapter(undefined, approvalManager);
|
|
20
24
|
const sessionService = new ClaudeSessionService(sdkAdapter);
|
|
21
25
|
const contextTracker = new ContextTracker();
|
|
22
|
-
const sessionController = new SessionController(sdkAdapter, contextTracker,
|
|
26
|
+
const sessionController = new SessionController(sdkAdapter, contextTracker, undefined, // session prompt is now constructed dynamically by the wrapper
|
|
27
|
+
'default', worktree, managerPromptRegistry.modePrefixes);
|
|
23
28
|
const gitOps = new GitOperations(worktree);
|
|
24
29
|
const stateStore = new FileRunStateStore();
|
|
30
|
+
const teamStore = new TeamStateStore();
|
|
25
31
|
const transcriptStore = new TranscriptStore();
|
|
26
32
|
const manager = new PersistentManager(sessionController, gitOps, stateStore, contextTracker, transcriptStore);
|
|
27
|
-
// Try to restore active session state (fire and forget)
|
|
28
|
-
manager.tryRestore(worktree).catch(() => { });
|
|
29
33
|
const liveTailer = new SessionLiveTailer();
|
|
34
|
+
const orchestrator = new TeamOrchestrator(sessionService, teamStore, transcriptStore, managerPromptRegistry.engineerSessionPrompt, projectClaudeFiles);
|
|
30
35
|
const services = {
|
|
31
36
|
manager,
|
|
32
37
|
sessions: sessionService,
|
|
33
38
|
approvalManager,
|
|
34
39
|
liveTailer,
|
|
40
|
+
teamStore,
|
|
41
|
+
orchestrator,
|
|
35
42
|
};
|
|
36
|
-
|
|
43
|
+
serviceRegistry.set(worktree, services);
|
|
37
44
|
return services;
|
|
38
45
|
}
|
|
46
|
+
export function clearPluginServices() {
|
|
47
|
+
serviceRegistry.clear();
|
|
48
|
+
activeTeamRegistry.clear();
|
|
49
|
+
wrapperSessionRegistry.clear();
|
|
50
|
+
}
|
|
51
|
+
export function setActiveTeamSession(worktree, teamId) {
|
|
52
|
+
activeTeamRegistry.set(worktree, teamId);
|
|
53
|
+
}
|
|
54
|
+
export function getActiveTeamSession(worktree) {
|
|
55
|
+
return activeTeamRegistry.get(worktree) ?? null;
|
|
56
|
+
}
|
|
57
|
+
export function setWrapperSessionMapping(worktree, wrapperSessionId, mapping) {
|
|
58
|
+
wrapperSessionRegistry.set(`${worktree}:${wrapperSessionId}`, mapping);
|
|
59
|
+
}
|
|
60
|
+
export function getWrapperSessionMapping(worktree, wrapperSessionId) {
|
|
61
|
+
return wrapperSessionRegistry.get(`${worktree}:${wrapperSessionId}`) ?? null;
|
|
62
|
+
}
|
|
@@ -1,11 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
/**
|
|
3
|
-
* Compose a wrapper agent prompt from the base prompt plus discovered
|
|
4
|
-
* project Claude files. Returns the base prompt unchanged when no
|
|
5
|
-
* files are provided.
|
|
6
|
-
*
|
|
7
|
-
* Each file is rendered under a clear path-labeled section so the
|
|
8
|
-
* wrapper agent knows exactly where each instruction came from.
|
|
9
|
-
*/
|
|
10
|
-
export declare function composeWrapperPrompt(basePrompt: string, claudeFiles: DiscoveredClaudeFile[]): string;
|
|
1
|
+
import type { ManagerPromptRegistry } from '../types/contracts.js';
|
|
11
2
|
export declare const managerPromptRegistry: ManagerPromptRegistry;
|
|
@@ -1,260 +1,55 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compose a wrapper agent prompt from the base prompt plus discovered
|
|
3
|
-
* project Claude files. Returns the base prompt unchanged when no
|
|
4
|
-
* files are provided.
|
|
5
|
-
*
|
|
6
|
-
* Each file is rendered under a clear path-labeled section so the
|
|
7
|
-
* wrapper agent knows exactly where each instruction came from.
|
|
8
|
-
*/
|
|
9
|
-
export function composeWrapperPrompt(basePrompt, claudeFiles) {
|
|
10
|
-
if (claudeFiles.length === 0) {
|
|
11
|
-
return basePrompt;
|
|
12
|
-
}
|
|
13
|
-
const sections = claudeFiles.map((f) => `### ${f.relativePath}\n${f.content}`).join('\n\n');
|
|
14
|
-
return `${basePrompt}\n\n## Project Claude Files\nThe following project-level instructions were discovered from the repository.\n\n${sections}`;
|
|
15
|
-
}
|
|
16
1
|
export const managerPromptRegistry = {
|
|
17
2
|
ctoSystemPrompt: [
|
|
18
|
-
'You are
|
|
19
|
-
'
|
|
20
|
-
'',
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'-
|
|
25
|
-
'-
|
|
26
|
-
'-
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'',
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'-
|
|
33
|
-
'-
|
|
34
|
-
'
|
|
35
|
-
'',
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'This is the single highest-leverage thing you do.',
|
|
40
|
-
'Never delegate without telling the engineer how to prove it worked.',
|
|
41
|
-
'',
|
|
42
|
-
'## Right-size your approach',
|
|
43
|
-
'Not every task needs a plan. Assess complexity, then act:',
|
|
44
|
-
'',
|
|
45
|
-
'**Simple tasks** (typo, rename, add a log line, one-file fix):',
|
|
46
|
-
' Skip investigation. Delegate directly with specific context.',
|
|
47
|
-
'',
|
|
48
|
-
'**Medium+ tasks** (bug fix, feature, refactor, architecture change):',
|
|
49
|
-
' Before delegating, determine:',
|
|
50
|
-
' 1. What the user asked vs. what is actually needed.',
|
|
51
|
-
' 2. What is underspecified or conflicting.',
|
|
52
|
-
' 3. What the cleanest architecture is.',
|
|
53
|
-
' 4. What should be clarified before proceeding.',
|
|
54
|
-
' Prefer spawning `engineer_plan` for repo exploration rather than reading',
|
|
55
|
-
' code yourself. Use at most 1-2 spot-check reads to sharpen the delegation.',
|
|
56
|
-
' Then delegate with file paths, line numbers, patterns, and verification.',
|
|
57
|
-
'',
|
|
58
|
-
'**Complex tasks** (multi-file feature, large refactor):',
|
|
59
|
-
' 1. Spawn `engineer_plan` to explore the repo, map dependencies, and analyze impact.',
|
|
60
|
-
' 2. If requirements are unclear, ask the user ONE high-leverage question —',
|
|
61
|
-
' only when it materially changes architecture, ownership, or destructive behavior.',
|
|
62
|
-
' Prefer the question tool when discrete options exist.',
|
|
63
|
-
' 3. Write a plan to the todo list (todowrite). Share it with the user.',
|
|
64
|
-
' 4. Execute steps sequentially, committing after each.',
|
|
65
|
-
'',
|
|
66
|
-
'## Missed-opportunity check',
|
|
67
|
-
'Before finalizing any medium+ delegation, check:',
|
|
68
|
-
'- One cleaner alternative you considered.',
|
|
69
|
-
'- One risk the current approach carries.',
|
|
70
|
-
'- One thing the requester likely missed.',
|
|
71
|
-
'If any of these materially improve the outcome, surface them.',
|
|
72
|
-
'',
|
|
73
|
-
'## How to delegate effectively',
|
|
74
|
-
'The engineer does not have your context. Every instruction must be self-contained.',
|
|
75
|
-
'Include:',
|
|
76
|
-
'- Exact files, functions, and line numbers to change.',
|
|
77
|
-
'- Current behavior and desired behavior.',
|
|
78
|
-
'- Code snippets showing the pattern or convention to follow.',
|
|
79
|
-
'- How to verify: "Run `npm test`, expect all green." or',
|
|
80
|
-
' "The function should return null instead of throwing."',
|
|
81
|
-
'',
|
|
82
|
-
'Bad: "Fix the auth bug"',
|
|
83
|
-
'Good: "In src/auth/session.ts, `validateToken` (line 42) throws on expired',
|
|
84
|
-
' tokens instead of returning null. Change it to return null.',
|
|
85
|
-
' Update the caller in src/routes/login.ts:87.',
|
|
86
|
-
' Follow the pattern in src/auth/refresh.ts:23.',
|
|
87
|
-
' Run `npm test -- --grep auth` to verify. All tests should pass."',
|
|
88
|
-
'',
|
|
89
|
-
'## Review every change',
|
|
90
|
-
'After each delegation:',
|
|
91
|
-
'1. git_diff — read the FULL diff. Check for unintended changes, missing tests,',
|
|
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.',
|
|
96
|
-
' On second failure: git_reset and rewrite the prompt from scratch.',
|
|
97
|
-
' Never send three corrections for the same problem.',
|
|
98
|
-
'',
|
|
99
|
-
'## Engineers (via the Task tool)',
|
|
100
|
-
'- `engineer_plan` — read-only investigation. Use for: exploring unfamiliar code,',
|
|
101
|
-
' mapping dependencies, analyzing impact, asking "how does X work?"',
|
|
102
|
-
'- `engineer_build` — implementation. Use for: all code changes, test runs, fixes.',
|
|
103
|
-
'- If steps are independent, spawn multiple engineers in parallel.',
|
|
104
|
-
'',
|
|
105
|
-
'## Context efficiency',
|
|
106
|
-
'- Use `engineer_plan` for broad exploration so your own context stays clean.',
|
|
107
|
-
'- When spawning engineers for unrelated tasks, tell them to use freshSession:true.',
|
|
108
|
-
'- Keep delegations focused — one concern per engineer invocation.',
|
|
109
|
-
'',
|
|
110
|
-
'## What you must NOT do',
|
|
111
|
-
'- Do NOT call any engineer_* tools directly. Use the Task tool.',
|
|
112
|
-
'- Do NOT edit files or run bash commands yourself.',
|
|
113
|
-
'- Do NOT skip review — always git_diff after delegation.',
|
|
114
|
-
'- Do NOT delegate without verification criteria.',
|
|
115
|
-
'',
|
|
116
|
-
'## Tools reference',
|
|
117
|
-
'todowrite / todoread — track multi-step work',
|
|
118
|
-
'question — ask the user structured questions with options',
|
|
119
|
-
'git_diff — review all uncommitted changes',
|
|
120
|
-
'git_commit — stage all + commit',
|
|
121
|
-
'git_reset — hard reset + clean (destructive)',
|
|
122
|
-
'approval_policy — view tool approval rules',
|
|
123
|
-
'approval_decisions — view recent approval decisions',
|
|
124
|
-
'approval_update — modify tool approval policy',
|
|
125
|
-
'',
|
|
126
|
-
'## Autonomy blockers',
|
|
127
|
-
'Surface these to the user immediately:',
|
|
128
|
-
'- Credentials, API keys, or secrets you do not have.',
|
|
129
|
-
'- Architectural decisions with trade-offs the user should weigh.',
|
|
130
|
-
'- Destructive actions on shared state (deploy, publish, force-push).',
|
|
131
|
-
'State the blocker, what you need, and a concrete suggestion to unblock.',
|
|
3
|
+
'You are the CTO. Own the outcome, not just the request.',
|
|
4
|
+
'',
|
|
5
|
+
'Default behavior:',
|
|
6
|
+
'- Find requirement gaps before implementation.',
|
|
7
|
+
'- Ask at most one high-leverage question when the answer changes architecture, scope, or destructive behavior.',
|
|
8
|
+
'- Use the Task tool to delegate to named engineer subagents.',
|
|
9
|
+
'- For medium or large work, spawn two engineers in parallel, compare both plans, and synthesize the strongest path.',
|
|
10
|
+
'- Reuse the same named engineer when follow-up work belongs to their prior context.',
|
|
11
|
+
'- Review diffs with `git_diff`, inspect changed files with `git_status`, and use `git_log` for recent context.',
|
|
12
|
+
'- Use the built-in `question` tool whenever you need a user decision and provide a recommendation.',
|
|
13
|
+
'',
|
|
14
|
+
'Team model:',
|
|
15
|
+
'- Tom, John, Maya, Sara, and Alex are persistent engineers.',
|
|
16
|
+
'- Each engineer keeps one Claude Code session for the active CTO team.',
|
|
17
|
+
'- The plugin maps engineer work back to the active CTO session automatically.',
|
|
18
|
+
'- Only one implementing engineer should modify the worktree at a time.',
|
|
19
|
+
'',
|
|
20
|
+
'Do not:',
|
|
21
|
+
'- Do not edit files or run bash directly.',
|
|
22
|
+
'- Do not expose session babysitting to the user.',
|
|
23
|
+
'- Do not delegate without clear success criteria.',
|
|
132
24
|
].join('\n'),
|
|
133
|
-
|
|
134
|
-
'You are a
|
|
135
|
-
'
|
|
136
|
-
'',
|
|
137
|
-
'
|
|
138
|
-
'
|
|
139
|
-
'
|
|
140
|
-
'- Rewrite weak or underspecified requests into precise prompts for the engineer.',
|
|
141
|
-
'- For medium+ tasks, determine: the actual problem, the cleanest architecture,',
|
|
142
|
-
' and what needs clarification before work begins.',
|
|
143
|
-
'- Ask ONE clarification first if it materially improves architecture.',
|
|
144
|
-
'',
|
|
145
|
-
'## Repo-context investigation',
|
|
146
|
-
'- Use read/grep/glob sparingly — only for spot-checks to sharpen a delegation.',
|
|
147
|
-
'- If more than 2 lookups are needed, send the investigation to the engineer.',
|
|
148
|
-
'- Do NOT implement changes yourself — investigation only.',
|
|
149
|
-
'',
|
|
150
|
-
'## Behavior',
|
|
151
|
-
'- Send the objective to the engineer using explore.',
|
|
152
|
-
"- Return the engineer's response verbatim. Do not summarize.",
|
|
153
|
-
'- Use freshSession:true on explore when the task is unrelated to prior work.',
|
|
154
|
-
'',
|
|
155
|
-
'## Context management',
|
|
156
|
-
'- Check session_health before sending.',
|
|
157
|
-
'- Under 50%: proceed. Over 70%: compact_context. Over 85%: clear_session.',
|
|
158
|
-
'',
|
|
159
|
-
'## Model selection',
|
|
160
|
-
'- claude-opus-4-6 + high: complex analysis (default).',
|
|
161
|
-
'- claude-sonnet-4-6: lighter analysis.',
|
|
162
|
-
'- effort "medium": simple lookups.',
|
|
163
|
-
'',
|
|
164
|
-
'## Using appended Project Claude Files',
|
|
165
|
-
'- Treat appended Project Claude Files as project guidance for the engineer.',
|
|
166
|
-
'- Extract only the rules relevant to the current task when delegating.',
|
|
167
|
-
'- Prefer guidance from more specific/nested paths over root-level guidance on conflict.',
|
|
168
|
-
'- Direct user instructions override Claude-file guidance.',
|
|
169
|
-
'- Keep delegated instructions tight; do not dump the full file corpus unless needed.',
|
|
170
|
-
'- Cite file paths (e.g. "per packages/core/CLAUDE.md") when the source matters.',
|
|
171
|
-
'',
|
|
172
|
-
'## What you must NOT do',
|
|
173
|
-
'- Do NOT call git_*, approval_*, or any non-engineer tools.',
|
|
174
|
-
'- Do NOT add commentary to the engineer response.',
|
|
175
|
-
].join('\n'),
|
|
176
|
-
engineerBuildPrompt: [
|
|
177
|
-
'You are a staff engineer managing a Claude Code session for implementation.',
|
|
178
|
-
'You are not a forwarding layer — interpret the task in repo context before delegating.',
|
|
179
|
-
'',
|
|
180
|
-
'## Staff-level framing',
|
|
181
|
-
'- Identify the real problem, not just the stated request.',
|
|
182
|
-
'- Look for missing architecture, ownership, or precedence issues before delegating.',
|
|
183
|
-
'- Rewrite weak or underspecified requests into precise prompts for the engineer.',
|
|
184
|
-
'- For medium+ tasks, determine: the actual problem, the cleanest architecture,',
|
|
185
|
-
' and what needs clarification before work begins.',
|
|
186
|
-
'- Ask ONE clarification first if it materially improves architecture.',
|
|
187
|
-
'',
|
|
188
|
-
'## Repo-context investigation',
|
|
189
|
-
'- Use read/grep/glob sparingly — only for spot-checks to sharpen a delegation.',
|
|
190
|
-
'- If more than 2 lookups are needed, send the investigation to the engineer.',
|
|
191
|
-
'- Do NOT implement changes yourself — investigation only.',
|
|
192
|
-
'',
|
|
193
|
-
'## Behavior',
|
|
194
|
-
'- Send the objective to the engineer using implement.',
|
|
195
|
-
"- Return the engineer's response verbatim. Do not summarize.",
|
|
196
|
-
'- Use freshSession:true on implement when the task is unrelated to prior work.',
|
|
197
|
-
'',
|
|
198
|
-
'## Context management',
|
|
199
|
-
'- Check session_health before sending.',
|
|
200
|
-
'- Under 50%: proceed. Over 70%: compact_context. Over 85%: clear_session.',
|
|
201
|
-
'',
|
|
202
|
-
'## Model selection',
|
|
203
|
-
'- claude-opus-4-6 + high: most coding tasks (default).',
|
|
204
|
-
'- claude-sonnet-4-6: simple renames, formatting, scaffolding.',
|
|
205
|
-
'- effort "max": complex refactors, subtle bugs, cross-cutting changes.',
|
|
206
|
-
'',
|
|
207
|
-
'## Using appended Project Claude Files',
|
|
208
|
-
'- Treat appended Project Claude Files as project guidance for the engineer.',
|
|
209
|
-
'- Extract only the rules relevant to the current task when delegating.',
|
|
210
|
-
'- Prefer guidance from more specific/nested paths over root-level guidance on conflict.',
|
|
211
|
-
'- Direct user instructions override Claude-file guidance.',
|
|
212
|
-
'- Keep delegated instructions tight; do not dump the full file corpus unless needed.',
|
|
213
|
-
'- Cite file paths (e.g. "per packages/core/CLAUDE.md") when the source matters.',
|
|
214
|
-
'',
|
|
215
|
-
'## What you must NOT do',
|
|
216
|
-
'- Do NOT call git_*, approval_*, or any non-engineer tools.',
|
|
217
|
-
'- Do NOT add commentary to the engineer response.',
|
|
25
|
+
engineerAgentPrompt: [
|
|
26
|
+
'You are a named engineer working for the CTO.',
|
|
27
|
+
'Use the `claude` tool for all real work.',
|
|
28
|
+
'The tool remembers prior turns for this engineer inside the active CTO team.',
|
|
29
|
+
'Your prior wrapper context is also reloaded automatically so you can frame Claude requests better over time.',
|
|
30
|
+
'Choose the mode that matches the assignment: explore, implement, or verify.',
|
|
31
|
+
'Return the tool result directly unless a short clarification is truly required.',
|
|
218
32
|
].join('\n'),
|
|
219
33
|
engineerSessionPrompt: [
|
|
220
|
-
'You are an expert engineer
|
|
221
|
-
'',
|
|
222
|
-
'
|
|
223
|
-
'
|
|
224
|
-
'
|
|
225
|
-
'
|
|
226
|
-
'
|
|
227
|
-
'',
|
|
228
|
-
'## Verification',
|
|
229
|
-
'- Always verify your own work before reporting done.',
|
|
230
|
-
'- Run tests, lint, or typecheck when the instruction says to.',
|
|
231
|
-
'- If no verification was specified, still run relevant tests if they exist.',
|
|
232
|
-
'- Report exact output on failure.',
|
|
233
|
-
'',
|
|
234
|
-
'## Git boundary — do NOT run:',
|
|
235
|
-
'git commit, git push, git reset, git checkout, git stash.',
|
|
236
|
-
'Git operations are handled externally.',
|
|
237
|
-
'',
|
|
238
|
-
'## Reporting',
|
|
239
|
-
'- End with: what was done, what was verified, what passed/failed.',
|
|
240
|
-
'- Report blockers immediately with specifics: file, line, error.',
|
|
241
|
-
'- If partially complete, state exactly what remains.',
|
|
34
|
+
'You are an expert software engineer working inside Claude Code.',
|
|
35
|
+
'Execute directly. No preamble.',
|
|
36
|
+
'Follow repository conventions and relevant project instructions.',
|
|
37
|
+
'Use the minimum necessary investigation before acting.',
|
|
38
|
+
'Verify your own work before reporting done.',
|
|
39
|
+
'Report blockers clearly and include exact command output on failure.',
|
|
40
|
+
'Do not run git commit, git push, git reset, git checkout, or git stash.',
|
|
242
41
|
].join('\n'),
|
|
243
42
|
modePrefixes: {
|
|
244
43
|
plan: [
|
|
245
|
-
'[PLAN MODE] Read-only.
|
|
246
|
-
'Do
|
|
247
|
-
'
|
|
248
|
-
'Analyze the codebase and produce a detailed implementation plan:',
|
|
249
|
-
'files to change, functions to modify, new files to create, test strategy,',
|
|
250
|
-
'and risks. End with a numbered step-by-step plan.',
|
|
251
|
-
'Return the entire plan inline in your response.',
|
|
44
|
+
'[PLAN MODE] Read-only.',
|
|
45
|
+
'Do not create or edit files.',
|
|
46
|
+
'Analyze the codebase and return the plan inline.',
|
|
252
47
|
].join(' '),
|
|
253
48
|
free: '',
|
|
254
49
|
},
|
|
255
50
|
contextWarnings: {
|
|
256
|
-
moderate: '
|
|
257
|
-
high: '
|
|
258
|
-
critical: '
|
|
51
|
+
moderate: 'Engineer context is getting full ({percent}% estimated). Reuse is still fine, but keep the next prompt focused.',
|
|
52
|
+
high: 'Engineer context is heavy ({percent}% estimated, {turns} turns, ${cost}). Prefer a narrowly scoped follow-up or internal compaction.',
|
|
53
|
+
critical: 'Engineer context is near capacity ({percent}% estimated). Avoid piling unrelated work into this engineer session.',
|
|
259
54
|
},
|
|
260
55
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TeamRecord } from '../types/contracts.js';
|
|
2
|
+
export declare class TeamStateStore {
|
|
3
|
+
private readonly baseDirectoryName;
|
|
4
|
+
private readonly writeQueues;
|
|
5
|
+
constructor(baseDirectoryName?: string);
|
|
6
|
+
saveTeam(team: TeamRecord): Promise<void>;
|
|
7
|
+
getTeam(cwd: string, teamId: string): Promise<TeamRecord | null>;
|
|
8
|
+
listTeams(cwd: string): Promise<TeamRecord[]>;
|
|
9
|
+
updateTeam(cwd: string, teamId: string, update: (team: TeamRecord) => TeamRecord): Promise<TeamRecord>;
|
|
10
|
+
private getTeamKey;
|
|
11
|
+
private getTeamsDirectory;
|
|
12
|
+
private getTeamPath;
|
|
13
|
+
private enqueueWrite;
|
|
14
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { isFileNotFoundError, writeJsonAtomically } from '../util/fs-helpers.js';
|
|
4
|
+
export class TeamStateStore {
|
|
5
|
+
baseDirectoryName;
|
|
6
|
+
writeQueues = new Map();
|
|
7
|
+
constructor(baseDirectoryName = '.claude-manager') {
|
|
8
|
+
this.baseDirectoryName = baseDirectoryName;
|
|
9
|
+
}
|
|
10
|
+
async saveTeam(team) {
|
|
11
|
+
await this.enqueueWrite(this.getTeamKey(team.cwd, team.id), async () => {
|
|
12
|
+
const filePath = this.getTeamPath(team.cwd, team.id);
|
|
13
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
14
|
+
await writeJsonAtomically(filePath, team);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async getTeam(cwd, teamId) {
|
|
18
|
+
const filePath = this.getTeamPath(cwd, teamId);
|
|
19
|
+
try {
|
|
20
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
21
|
+
return JSON.parse(content);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (isFileNotFoundError(error)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async listTeams(cwd) {
|
|
31
|
+
const directory = this.getTeamsDirectory(cwd);
|
|
32
|
+
try {
|
|
33
|
+
const entries = await fs.readdir(directory);
|
|
34
|
+
const teams = await Promise.all(entries
|
|
35
|
+
.filter((entry) => entry.endsWith('.json'))
|
|
36
|
+
.map(async (entry) => {
|
|
37
|
+
const content = await fs.readFile(path.join(directory, entry), 'utf8');
|
|
38
|
+
return JSON.parse(content);
|
|
39
|
+
}));
|
|
40
|
+
return teams.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (isFileNotFoundError(error)) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async updateTeam(cwd, teamId, update) {
|
|
50
|
+
return this.enqueueWrite(this.getTeamKey(cwd, teamId), async () => {
|
|
51
|
+
const existing = await this.getTeam(cwd, teamId);
|
|
52
|
+
if (!existing) {
|
|
53
|
+
throw new Error(`Team ${teamId} does not exist.`);
|
|
54
|
+
}
|
|
55
|
+
const updated = update(existing);
|
|
56
|
+
const filePath = this.getTeamPath(cwd, teamId);
|
|
57
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
58
|
+
await writeJsonAtomically(filePath, updated);
|
|
59
|
+
return updated;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
getTeamKey(cwd, teamId) {
|
|
63
|
+
return `${cwd}:${teamId}`;
|
|
64
|
+
}
|
|
65
|
+
getTeamsDirectory(cwd) {
|
|
66
|
+
return path.join(cwd, this.baseDirectoryName, 'teams');
|
|
67
|
+
}
|
|
68
|
+
getTeamPath(cwd, teamId) {
|
|
69
|
+
return path.join(this.getTeamsDirectory(cwd), `${teamId}.json`);
|
|
70
|
+
}
|
|
71
|
+
async enqueueWrite(key, operation) {
|
|
72
|
+
const previous = this.writeQueues.get(key) ?? Promise.resolve();
|
|
73
|
+
const resultPromise = previous.catch(() => undefined).then(operation);
|
|
74
|
+
const settledPromise = resultPromise.then(() => undefined, () => undefined);
|
|
75
|
+
this.writeQueues.set(key, settledPromise);
|
|
76
|
+
try {
|
|
77
|
+
return await resultPromise;
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
if (this.writeQueues.get(key) === settledPromise) {
|
|
81
|
+
this.writeQueues.delete(key);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type EngineerName, type TeamEngineerRecord, type TeamRecord } from '../types/contracts.js';
|
|
2
|
+
export declare const TEAM_ENGINEERS: readonly ["Tom", "John", "Maya", "Sara", "Alex"];
|
|
3
|
+
export declare function isEngineerName(value: string): value is EngineerName;
|
|
4
|
+
export declare function createEmptyTeamRecord(teamId: string, cwd: string): TeamRecord;
|
|
5
|
+
export declare function createEmptyEngineerRecord(name: EngineerName): TeamEngineerRecord;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { DEFAULT_ENGINEER_NAMES, } from '../types/contracts.js';
|
|
2
|
+
export const TEAM_ENGINEERS = DEFAULT_ENGINEER_NAMES;
|
|
3
|
+
export function isEngineerName(value) {
|
|
4
|
+
return TEAM_ENGINEERS.includes(value);
|
|
5
|
+
}
|
|
6
|
+
export function createEmptyTeamRecord(teamId, cwd) {
|
|
7
|
+
const timestamp = new Date().toISOString();
|
|
8
|
+
return {
|
|
9
|
+
id: teamId,
|
|
10
|
+
cwd,
|
|
11
|
+
createdAt: timestamp,
|
|
12
|
+
updatedAt: timestamp,
|
|
13
|
+
engineers: TEAM_ENGINEERS.map((name) => createEmptyEngineerRecord(name)),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function createEmptyEngineerRecord(name) {
|
|
17
|
+
return {
|
|
18
|
+
name,
|
|
19
|
+
wrapperSessionId: null,
|
|
20
|
+
claudeSessionId: null,
|
|
21
|
+
busy: false,
|
|
22
|
+
lastMode: null,
|
|
23
|
+
lastTaskSummary: null,
|
|
24
|
+
lastUsedAt: null,
|
|
25
|
+
wrapperHistory: [],
|
|
26
|
+
context: {
|
|
27
|
+
sessionId: null,
|
|
28
|
+
totalTurns: 0,
|
|
29
|
+
totalCostUsd: 0,
|
|
30
|
+
latestInputTokens: null,
|
|
31
|
+
latestOutputTokens: null,
|
|
32
|
+
contextWindowSize: null,
|
|
33
|
+
estimatedContextPercent: null,
|
|
34
|
+
warningLevel: 'ok',
|
|
35
|
+
compactionCount: 0,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|