@doingdev/opencode-claude-manager-plugin 0.1.42 → 0.1.43
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/persistent-manager.d.ts +3 -2
- package/dist/manager/persistent-manager.js +7 -7
- package/dist/manager/session-controller.d.ts +8 -5
- package/dist/manager/session-controller.js +25 -20
- package/dist/plugin/agent-hierarchy.d.ts +8 -8
- package/dist/plugin/agent-hierarchy.js +24 -24
- package/dist/plugin/claude-manager.plugin.js +40 -36
- package/dist/plugin/service-factory.js +3 -8
- package/dist/prompts/registry.js +94 -101
- package/dist/types/contracts.d.ts +2 -3
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@ export declare class PersistentManager {
|
|
|
21
21
|
model?: string;
|
|
22
22
|
effort?: 'low' | 'medium' | 'high' | 'max';
|
|
23
23
|
mode?: 'plan' | 'free';
|
|
24
|
+
sessionSystemPrompt?: string;
|
|
24
25
|
abortSignal?: AbortSignal;
|
|
25
26
|
}, onEvent?: ClaudeSessionEventHandler): Promise<{
|
|
26
27
|
sessionId: string | undefined;
|
|
@@ -66,7 +67,7 @@ export declare class PersistentManager {
|
|
|
66
67
|
/**
|
|
67
68
|
* Clear the active session. Next send creates a fresh one.
|
|
68
69
|
*/
|
|
69
|
-
clearSession(
|
|
70
|
+
clearSession(): Promise<string | null>;
|
|
70
71
|
/**
|
|
71
72
|
* Compact the current session to free context.
|
|
72
73
|
*/
|
|
@@ -85,7 +86,7 @@ export declare class PersistentManager {
|
|
|
85
86
|
/**
|
|
86
87
|
* Try to restore session state from disk on startup.
|
|
87
88
|
*/
|
|
88
|
-
tryRestore(
|
|
89
|
+
tryRestore(): Promise<boolean>;
|
|
89
90
|
listRuns(cwd: string): Promise<PersistentRunRecord[]>;
|
|
90
91
|
getRun(cwd: string, runId: string): Promise<PersistentRunRecord | null>;
|
|
91
92
|
}
|
|
@@ -17,7 +17,7 @@ export class PersistentManager {
|
|
|
17
17
|
* Creates a new session if none exists.
|
|
18
18
|
*/
|
|
19
19
|
async sendMessage(cwd, message, options, onEvent) {
|
|
20
|
-
const result = await this.sessionController.sendMessage(
|
|
20
|
+
const result = await this.sessionController.sendMessage(message, options, onEvent);
|
|
21
21
|
if (result.sessionId && result.events.length > 0) {
|
|
22
22
|
await this.transcriptStore.appendEvents(cwd, result.sessionId, result.events);
|
|
23
23
|
}
|
|
@@ -71,14 +71,14 @@ export class PersistentManager {
|
|
|
71
71
|
/**
|
|
72
72
|
* Clear the active session. Next send creates a fresh one.
|
|
73
73
|
*/
|
|
74
|
-
async clearSession(
|
|
75
|
-
return this.sessionController.clearSession(
|
|
74
|
+
async clearSession() {
|
|
75
|
+
return this.sessionController.clearSession();
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
78
|
* Compact the current session to free context.
|
|
79
79
|
*/
|
|
80
80
|
async compactSession(cwd, onEvent) {
|
|
81
|
-
const result = await this.sessionController.compactSession(
|
|
81
|
+
const result = await this.sessionController.compactSession(onEvent);
|
|
82
82
|
if (result.sessionId && result.events.length > 0) {
|
|
83
83
|
await this.transcriptStore.appendEvents(cwd, result.sessionId, result.events);
|
|
84
84
|
}
|
|
@@ -120,7 +120,7 @@ export class PersistentManager {
|
|
|
120
120
|
await this.stateStore.saveRun(runRecord);
|
|
121
121
|
await onProgress?.(runRecord);
|
|
122
122
|
try {
|
|
123
|
-
const result = await this.sessionController.sendMessage(
|
|
123
|
+
const result = await this.sessionController.sendMessage(task, options, async (event) => {
|
|
124
124
|
// Update run record with progress events
|
|
125
125
|
const currentRun = await this.stateStore.getRun(cwd, runId);
|
|
126
126
|
if (currentRun) {
|
|
@@ -170,8 +170,8 @@ export class PersistentManager {
|
|
|
170
170
|
/**
|
|
171
171
|
* Try to restore session state from disk on startup.
|
|
172
172
|
*/
|
|
173
|
-
async tryRestore(
|
|
174
|
-
return this.sessionController.tryRestore(
|
|
173
|
+
async tryRestore() {
|
|
174
|
+
return this.sessionController.tryRestore();
|
|
175
175
|
}
|
|
176
176
|
listRuns(cwd) {
|
|
177
177
|
return this.stateStore.listRuns(cwd);
|
|
@@ -5,9 +5,11 @@ export declare class SessionController {
|
|
|
5
5
|
private readonly sdkAdapter;
|
|
6
6
|
private readonly contextTracker;
|
|
7
7
|
private readonly sessionPrompt;
|
|
8
|
+
private readonly wrapperType;
|
|
9
|
+
private readonly worktree;
|
|
8
10
|
private readonly modePrefixes;
|
|
9
11
|
private activeSessionId;
|
|
10
|
-
constructor(sdkAdapter: ClaudeAgentSdkAdapter, contextTracker: ContextTracker, sessionPrompt: string, modePrefixes?: {
|
|
12
|
+
constructor(sdkAdapter: ClaudeAgentSdkAdapter, contextTracker: ContextTracker, sessionPrompt: string | undefined, wrapperType: string, worktree: string, modePrefixes?: {
|
|
11
13
|
plan: string;
|
|
12
14
|
free: string;
|
|
13
15
|
});
|
|
@@ -17,20 +19,21 @@ export declare class SessionController {
|
|
|
17
19
|
* Send a message to the persistent session. Creates one if none exists.
|
|
18
20
|
* Returns the session result including usage data.
|
|
19
21
|
*/
|
|
20
|
-
sendMessage(
|
|
22
|
+
sendMessage(message: string, options?: {
|
|
21
23
|
model?: string;
|
|
22
24
|
effort?: 'low' | 'medium' | 'high' | 'max';
|
|
23
25
|
mode?: SessionMode;
|
|
26
|
+
sessionSystemPrompt?: string;
|
|
24
27
|
abortSignal?: AbortSignal;
|
|
25
28
|
}, onEvent?: ClaudeSessionEventHandler): Promise<ClaudeSessionRunResult>;
|
|
26
29
|
/**
|
|
27
30
|
* Send /compact to the current session to compress context.
|
|
28
31
|
*/
|
|
29
|
-
compactSession(
|
|
32
|
+
compactSession(onEvent?: ClaudeSessionEventHandler): Promise<ClaudeSessionRunResult>;
|
|
30
33
|
/**
|
|
31
34
|
* Clear the current session. The next sendMessage will create a fresh one.
|
|
32
35
|
*/
|
|
33
|
-
clearSession(
|
|
36
|
+
clearSession(): Promise<string | null>;
|
|
34
37
|
/**
|
|
35
38
|
* Get current context tracking snapshot.
|
|
36
39
|
*/
|
|
@@ -38,7 +41,7 @@ export declare class SessionController {
|
|
|
38
41
|
/**
|
|
39
42
|
* Try to restore active session from persisted state on startup.
|
|
40
43
|
*/
|
|
41
|
-
tryRestore(
|
|
44
|
+
tryRestore(): Promise<boolean>;
|
|
42
45
|
private persistActiveSession;
|
|
43
46
|
private removeActiveSession;
|
|
44
47
|
}
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { dirname
|
|
3
|
-
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
function activeSessionFile(_wrapperType) {
|
|
4
|
+
return `.claude-manager/active-session.json`;
|
|
5
|
+
}
|
|
4
6
|
export class SessionController {
|
|
5
7
|
sdkAdapter;
|
|
6
8
|
contextTracker;
|
|
7
9
|
sessionPrompt;
|
|
10
|
+
wrapperType;
|
|
11
|
+
worktree;
|
|
8
12
|
modePrefixes;
|
|
9
13
|
activeSessionId = null;
|
|
10
|
-
constructor(sdkAdapter, contextTracker, sessionPrompt, modePrefixes = {
|
|
14
|
+
constructor(sdkAdapter, contextTracker, sessionPrompt, wrapperType, worktree, modePrefixes = {
|
|
11
15
|
plan: '',
|
|
12
16
|
free: '',
|
|
13
17
|
}) {
|
|
14
18
|
this.sdkAdapter = sdkAdapter;
|
|
15
19
|
this.contextTracker = contextTracker;
|
|
16
20
|
this.sessionPrompt = sessionPrompt;
|
|
21
|
+
this.wrapperType = wrapperType;
|
|
22
|
+
this.worktree = worktree;
|
|
17
23
|
this.modePrefixes = modePrefixes;
|
|
18
24
|
}
|
|
19
25
|
get isActive() {
|
|
@@ -26,12 +32,12 @@ export class SessionController {
|
|
|
26
32
|
* Send a message to the persistent session. Creates one if none exists.
|
|
27
33
|
* Returns the session result including usage data.
|
|
28
34
|
*/
|
|
29
|
-
async sendMessage(
|
|
35
|
+
async sendMessage(message, options, onEvent) {
|
|
30
36
|
const mode = options?.mode ?? 'free';
|
|
31
37
|
const prefix = this.modePrefixes[mode];
|
|
32
38
|
const prompt = prefix ? `${prefix}\n\n${message}` : message;
|
|
33
39
|
const input = {
|
|
34
|
-
cwd,
|
|
40
|
+
cwd: this.worktree,
|
|
35
41
|
prompt,
|
|
36
42
|
persistSession: true,
|
|
37
43
|
permissionMode: mode === 'plan' ? 'plan' : 'acceptEdits',
|
|
@@ -46,8 +52,8 @@ export class SessionController {
|
|
|
46
52
|
input.resumeSessionId = this.activeSessionId;
|
|
47
53
|
}
|
|
48
54
|
else {
|
|
49
|
-
// New session —
|
|
50
|
-
input.systemPrompt = this.sessionPrompt;
|
|
55
|
+
// New session — prefer dynamically constructed prompt from wrapper, fall back to static default
|
|
56
|
+
input.systemPrompt = options?.sessionSystemPrompt ?? this.sessionPrompt;
|
|
51
57
|
input.model ??= 'claude-opus-4-6';
|
|
52
58
|
input.effort ??= 'high';
|
|
53
59
|
}
|
|
@@ -66,28 +72,28 @@ export class SessionController {
|
|
|
66
72
|
contextWindowSize: result.contextWindowSize,
|
|
67
73
|
});
|
|
68
74
|
// Persist active session state
|
|
69
|
-
await this.persistActiveSession(
|
|
75
|
+
await this.persistActiveSession();
|
|
70
76
|
return result;
|
|
71
77
|
}
|
|
72
78
|
/**
|
|
73
79
|
* Send /compact to the current session to compress context.
|
|
74
80
|
*/
|
|
75
|
-
async compactSession(
|
|
81
|
+
async compactSession(onEvent) {
|
|
76
82
|
if (!this.activeSessionId) {
|
|
77
83
|
throw new Error('No active session to compact');
|
|
78
84
|
}
|
|
79
|
-
const result = await this.sendMessage(
|
|
85
|
+
const result = await this.sendMessage('/compact', undefined, onEvent);
|
|
80
86
|
this.contextTracker.recordCompaction();
|
|
81
87
|
return result;
|
|
82
88
|
}
|
|
83
89
|
/**
|
|
84
90
|
* Clear the current session. The next sendMessage will create a fresh one.
|
|
85
91
|
*/
|
|
86
|
-
async clearSession(
|
|
92
|
+
async clearSession() {
|
|
87
93
|
const clearedId = this.activeSessionId;
|
|
88
94
|
this.activeSessionId = null;
|
|
89
95
|
this.contextTracker.reset();
|
|
90
|
-
await this.removeActiveSession(
|
|
96
|
+
await this.removeActiveSession();
|
|
91
97
|
return clearedId;
|
|
92
98
|
}
|
|
93
99
|
/**
|
|
@@ -99,12 +105,12 @@ export class SessionController {
|
|
|
99
105
|
/**
|
|
100
106
|
* Try to restore active session from persisted state on startup.
|
|
101
107
|
*/
|
|
102
|
-
async tryRestore(
|
|
103
|
-
const filePath =
|
|
108
|
+
async tryRestore() {
|
|
109
|
+
const filePath = activeSessionFile(this.wrapperType);
|
|
104
110
|
try {
|
|
105
111
|
const raw = await readFile(filePath, 'utf-8');
|
|
106
112
|
const state = JSON.parse(raw);
|
|
107
|
-
if (state.sessionId
|
|
113
|
+
if (state.sessionId) {
|
|
108
114
|
this.activeSessionId = state.sessionId;
|
|
109
115
|
this.contextTracker.restore(state);
|
|
110
116
|
return true;
|
|
@@ -115,14 +121,13 @@ export class SessionController {
|
|
|
115
121
|
}
|
|
116
122
|
return false;
|
|
117
123
|
}
|
|
118
|
-
async persistActiveSession(
|
|
124
|
+
async persistActiveSession() {
|
|
119
125
|
if (!this.activeSessionId) {
|
|
120
126
|
return;
|
|
121
127
|
}
|
|
122
128
|
const snap = this.contextTracker.snapshot();
|
|
123
129
|
const state = {
|
|
124
130
|
sessionId: this.activeSessionId,
|
|
125
|
-
cwd,
|
|
126
131
|
startedAt: new Date().toISOString(),
|
|
127
132
|
totalTurns: snap.totalTurns,
|
|
128
133
|
totalCostUsd: snap.totalCostUsd,
|
|
@@ -130,12 +135,12 @@ export class SessionController {
|
|
|
130
135
|
contextWindowSize: snap.contextWindowSize,
|
|
131
136
|
latestInputTokens: snap.latestInputTokens,
|
|
132
137
|
};
|
|
133
|
-
const filePath =
|
|
138
|
+
const filePath = activeSessionFile(this.wrapperType);
|
|
134
139
|
await mkdir(dirname(filePath), { recursive: true });
|
|
135
140
|
await writeFile(filePath, JSON.stringify(state, null, 2));
|
|
136
141
|
}
|
|
137
|
-
async removeActiveSession(
|
|
138
|
-
const filePath =
|
|
142
|
+
async removeActiveSession() {
|
|
143
|
+
const filePath = activeSessionFile(this.wrapperType);
|
|
139
144
|
try {
|
|
140
145
|
const { unlink } = await import('node:fs/promises');
|
|
141
146
|
await unlink(filePath);
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent hierarchy configuration for the CTO + Engineer Wrapper architecture.
|
|
3
3
|
*
|
|
4
|
-
* CTO (cto)
|
|
5
|
-
* Engineer
|
|
6
|
-
* Engineer
|
|
7
|
-
* Claude Code session
|
|
4
|
+
* CTO (cto) — pure orchestrator, spawns engineers, reviews diffs, commits
|
|
5
|
+
* Engineer Explore (engineer_explore) — manages a Claude Code session for read-only investigation
|
|
6
|
+
* Engineer Implement (engineer_implement) — manages a Claude Code session for implementation
|
|
7
|
+
* Claude Code session — the underlying AI session (prompt only, no OpenCode agent)
|
|
8
8
|
*/
|
|
9
9
|
import type { ManagerPromptRegistry } from '../types/contracts.js';
|
|
10
10
|
export declare const AGENT_CTO = "cto";
|
|
11
|
-
export declare const
|
|
12
|
-
export declare const
|
|
11
|
+
export declare const AGENT_ENGINEER_EXPLORE = "engineer_explore";
|
|
12
|
+
export declare const AGENT_ENGINEER_IMPLEMENT = "engineer_implement";
|
|
13
13
|
/** All restricted tool IDs (union of all domain groups) */
|
|
14
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
15
|
type ToolPermission = 'allow' | 'ask' | 'deny';
|
|
@@ -41,14 +41,14 @@ export declare function buildCtoAgentConfig(prompts: ManagerPromptRegistry): {
|
|
|
41
41
|
permission: AgentPermission;
|
|
42
42
|
prompt: string;
|
|
43
43
|
};
|
|
44
|
-
export declare function
|
|
44
|
+
export declare function buildEngineerExploreAgentConfig(prompts: ManagerPromptRegistry): {
|
|
45
45
|
description: string;
|
|
46
46
|
mode: "subagent";
|
|
47
47
|
color: string;
|
|
48
48
|
permission: AgentPermission;
|
|
49
49
|
prompt: string;
|
|
50
50
|
};
|
|
51
|
-
export declare function
|
|
51
|
+
export declare function buildEngineerImplementAgentConfig(prompts: ManagerPromptRegistry): {
|
|
52
52
|
description: string;
|
|
53
53
|
mode: "subagent";
|
|
54
54
|
color: string;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent hierarchy configuration for the CTO + Engineer Wrapper architecture.
|
|
3
3
|
*
|
|
4
|
-
* CTO (cto)
|
|
5
|
-
* Engineer
|
|
6
|
-
* Engineer
|
|
7
|
-
* Claude Code session
|
|
4
|
+
* CTO (cto) — pure orchestrator, spawns engineers, reviews diffs, commits
|
|
5
|
+
* Engineer Explore (engineer_explore) — manages a Claude Code session for read-only investigation
|
|
6
|
+
* Engineer Implement (engineer_implement) — manages a Claude Code session for implementation
|
|
7
|
+
* Claude Code session — the underlying AI session (prompt only, no OpenCode agent)
|
|
8
8
|
*/
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
// Agent names
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
export const AGENT_CTO = 'cto';
|
|
13
|
-
export const
|
|
14
|
-
export const
|
|
13
|
+
export const AGENT_ENGINEER_EXPLORE = 'engineer_explore';
|
|
14
|
+
export const AGENT_ENGINEER_IMPLEMENT = 'engineer_implement';
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
16
16
|
// Tool IDs — grouped by domain
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
@@ -25,10 +25,10 @@ const ENGINEER_SHARED_TOOL_IDS = [
|
|
|
25
25
|
];
|
|
26
26
|
/** All engineer tools — mode-locked sends + shared session tools */
|
|
27
27
|
const ENGINEER_TOOL_IDS = ['explore', 'implement', ...ENGINEER_SHARED_TOOL_IDS];
|
|
28
|
-
/** Tools for the
|
|
29
|
-
const
|
|
30
|
-
/** Tools for the
|
|
31
|
-
const
|
|
28
|
+
/** Tools for the engineer_explore wrapper (explore-mode send + shared) */
|
|
29
|
+
const ENGINEER_EXPLORE_TOOL_IDS = ['explore', ...ENGINEER_SHARED_TOOL_IDS];
|
|
30
|
+
/** Tools for the engineer_implement wrapper (implement-mode send + shared) */
|
|
31
|
+
const ENGINEER_IMPLEMENT_TOOL_IDS = ['implement', ...ENGINEER_SHARED_TOOL_IDS];
|
|
32
32
|
/** Git tools — owned by CTO */
|
|
33
33
|
const GIT_TOOL_IDS = ['git_diff', 'git_commit', 'git_reset', 'git_status', 'git_log'];
|
|
34
34
|
/** Approval tools — owned by CTO */
|
|
@@ -77,19 +77,19 @@ function buildCtoPermissions() {
|
|
|
77
77
|
...allowed,
|
|
78
78
|
task: {
|
|
79
79
|
'*': 'deny',
|
|
80
|
-
[
|
|
81
|
-
[
|
|
80
|
+
[AGENT_ENGINEER_EXPLORE]: 'allow',
|
|
81
|
+
[AGENT_ENGINEER_IMPLEMENT]: 'allow',
|
|
82
82
|
},
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
|
-
/** Engineer
|
|
86
|
-
function
|
|
85
|
+
/** Engineer explore wrapper: read-only investigation + explore + shared session tools. */
|
|
86
|
+
function buildEngineerExplorePermissions() {
|
|
87
87
|
const denied = {};
|
|
88
88
|
for (const toolId of ALL_RESTRICTED_TOOL_IDS) {
|
|
89
89
|
denied[toolId] = 'deny';
|
|
90
90
|
}
|
|
91
91
|
const allowed = {};
|
|
92
|
-
for (const toolId of
|
|
92
|
+
for (const toolId of ENGINEER_EXPLORE_TOOL_IDS) {
|
|
93
93
|
allowed[toolId] = 'allow';
|
|
94
94
|
}
|
|
95
95
|
return {
|
|
@@ -99,14 +99,14 @@ function buildEngineerPlanPermissions() {
|
|
|
99
99
|
...allowed,
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
|
-
/** Engineer
|
|
103
|
-
function
|
|
102
|
+
/** Engineer implement wrapper: read-only investigation + implement + shared session tools. */
|
|
103
|
+
function buildEngineerImplementPermissions() {
|
|
104
104
|
const denied = {};
|
|
105
105
|
for (const toolId of ALL_RESTRICTED_TOOL_IDS) {
|
|
106
106
|
denied[toolId] = 'deny';
|
|
107
107
|
}
|
|
108
108
|
const allowed = {};
|
|
109
|
-
for (const toolId of
|
|
109
|
+
for (const toolId of ENGINEER_IMPLEMENT_TOOL_IDS) {
|
|
110
110
|
allowed[toolId] = 'allow';
|
|
111
111
|
}
|
|
112
112
|
return {
|
|
@@ -128,22 +128,22 @@ export function buildCtoAgentConfig(prompts) {
|
|
|
128
128
|
prompt: prompts.ctoSystemPrompt,
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
|
-
export function
|
|
131
|
+
export function buildEngineerExploreAgentConfig(prompts) {
|
|
132
132
|
return {
|
|
133
133
|
description: 'Thin high-judgment wrapper that frames work quickly and dispatches to Claude Code in plan mode for read-only investigation.',
|
|
134
134
|
mode: 'subagent',
|
|
135
135
|
color: '#D97757',
|
|
136
|
-
permission:
|
|
137
|
-
prompt: prompts.
|
|
136
|
+
permission: buildEngineerExplorePermissions(),
|
|
137
|
+
prompt: prompts.engineerExplorePrompt,
|
|
138
138
|
};
|
|
139
139
|
}
|
|
140
|
-
export function
|
|
140
|
+
export function buildEngineerImplementAgentConfig(prompts) {
|
|
141
141
|
return {
|
|
142
142
|
description: 'Thin high-judgment wrapper that frames work quickly and dispatches to Claude Code in free mode for implementation.',
|
|
143
143
|
mode: 'subagent',
|
|
144
144
|
color: '#D97757',
|
|
145
|
-
permission:
|
|
146
|
-
prompt: prompts.
|
|
145
|
+
permission: buildEngineerImplementPermissions(),
|
|
146
|
+
prompt: prompts.engineerImplementPrompt,
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
149
|
// ---------------------------------------------------------------------------
|
|
@@ -1,16 +1,16 @@
|
|
|
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,
|
|
4
|
+
import { AGENT_CTO, AGENT_ENGINEER_EXPLORE, AGENT_ENGINEER_IMPLEMENT, buildCtoAgentConfig, buildEngineerExploreAgentConfig, buildEngineerImplementAgentConfig, 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);
|
|
8
8
|
async function executeDelegate(args, context) {
|
|
9
|
-
const
|
|
9
|
+
const wrapperServices = getOrCreatePluginServices(context.worktree);
|
|
10
10
|
if (args.freshSession) {
|
|
11
|
-
await
|
|
11
|
+
await wrapperServices.manager.clearSession();
|
|
12
12
|
}
|
|
13
|
-
const hasActiveSession =
|
|
13
|
+
const hasActiveSession = wrapperServices.manager.getStatus().sessionId !== null;
|
|
14
14
|
const promptPreview = args.message.length > 100 ? args.message.slice(0, 100) + '...' : args.message;
|
|
15
15
|
context.metadata({
|
|
16
16
|
title: hasActiveSession
|
|
@@ -18,16 +18,17 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
18
18
|
: '⚡ Claude Code: Initializing...',
|
|
19
19
|
metadata: {
|
|
20
20
|
status: 'running',
|
|
21
|
-
sessionId:
|
|
21
|
+
sessionId: wrapperServices.manager.getStatus().sessionId,
|
|
22
22
|
prompt: promptPreview,
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
25
|
let turnsSoFar;
|
|
26
26
|
let costSoFar;
|
|
27
|
-
const result = await
|
|
27
|
+
const result = await wrapperServices.manager.sendMessage(context.worktree, args.message, {
|
|
28
28
|
model: args.model,
|
|
29
29
|
effort: args.effort,
|
|
30
30
|
mode: args.mode,
|
|
31
|
+
sessionSystemPrompt: args.sessionSystemPrompt,
|
|
31
32
|
abortSignal: context.abort,
|
|
32
33
|
}, (event) => {
|
|
33
34
|
if (event.turns !== undefined) {
|
|
@@ -182,7 +183,7 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
182
183
|
let toolOutputs = [];
|
|
183
184
|
if (result.sessionId) {
|
|
184
185
|
try {
|
|
185
|
-
toolOutputs = await
|
|
186
|
+
toolOutputs = await wrapperServices.liveTailer.getToolOutputPreview(result.sessionId, context.worktree, 3);
|
|
186
187
|
}
|
|
187
188
|
catch {
|
|
188
189
|
// Non-critical — the JSONL file may not exist yet.
|
|
@@ -210,12 +211,12 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
210
211
|
const claudeFiles = await discoverProjectClaudeFiles(worktree);
|
|
211
212
|
const derivedPrompts = {
|
|
212
213
|
...managerPromptRegistry,
|
|
213
|
-
|
|
214
|
-
|
|
214
|
+
engineerExplorePrompt: composeWrapperPrompt(managerPromptRegistry.engineerExplorePrompt, claudeFiles),
|
|
215
|
+
engineerImplementPrompt: composeWrapperPrompt(managerPromptRegistry.engineerImplementPrompt, claudeFiles),
|
|
215
216
|
};
|
|
216
217
|
config.agent[AGENT_CTO] ??= buildCtoAgentConfig(managerPromptRegistry);
|
|
217
|
-
config.agent[
|
|
218
|
-
config.agent[
|
|
218
|
+
config.agent[AGENT_ENGINEER_EXPLORE] ??= buildEngineerExploreAgentConfig(derivedPrompts);
|
|
219
|
+
config.agent[AGENT_ENGINEER_IMPLEMENT] ??= buildEngineerImplementAgentConfig(derivedPrompts);
|
|
219
220
|
},
|
|
220
221
|
tool: {
|
|
221
222
|
explore: tool({
|
|
@@ -225,14 +226,14 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
225
226
|
args: {
|
|
226
227
|
message: tool.schema.string().min(1),
|
|
227
228
|
model: tool.schema
|
|
228
|
-
.enum(['claude-opus-4-6', 'claude-sonnet-4-6'
|
|
229
|
+
.enum(['claude-opus-4-6', 'claude-sonnet-4-6'])
|
|
229
230
|
.optional(),
|
|
230
|
-
effort: tool.schema.enum(['
|
|
231
|
+
effort: tool.schema.enum(['medium', 'high', 'max']).default('high'),
|
|
231
232
|
freshSession: tool.schema.boolean().default(false),
|
|
232
|
-
|
|
233
|
+
sessionSystemPrompt: tool.schema.string().optional(),
|
|
233
234
|
},
|
|
234
235
|
async execute(args, context) {
|
|
235
|
-
return executeDelegate({ ...args, mode: 'plan' }, context);
|
|
236
|
+
return executeDelegate({ ...args, mode: 'plan', wrapperType: 'explore' }, context);
|
|
236
237
|
},
|
|
237
238
|
}),
|
|
238
239
|
implement: tool({
|
|
@@ -241,27 +242,27 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
241
242
|
args: {
|
|
242
243
|
message: tool.schema.string().min(1),
|
|
243
244
|
model: tool.schema
|
|
244
|
-
.enum(['claude-opus-4-6', 'claude-sonnet-4-6'
|
|
245
|
+
.enum(['claude-opus-4-6', 'claude-sonnet-4-6'])
|
|
245
246
|
.optional(),
|
|
246
|
-
effort: tool.schema.enum(['
|
|
247
|
+
effort: tool.schema.enum(['medium', 'high', 'max']).default('high'),
|
|
247
248
|
freshSession: tool.schema.boolean().default(false),
|
|
248
|
-
|
|
249
|
+
sessionSystemPrompt: tool.schema.string().optional(),
|
|
249
250
|
},
|
|
250
251
|
async execute(args, context) {
|
|
251
|
-
return executeDelegate({ ...args, mode: 'free' }, context);
|
|
252
|
+
return executeDelegate({ ...args, mode: 'free', wrapperType: 'implement' }, context);
|
|
252
253
|
},
|
|
253
254
|
}),
|
|
254
255
|
compact_context: tool({
|
|
255
256
|
description: 'Compress session history to reclaim context window space. ' +
|
|
256
257
|
'Preserves state while reducing token usage.',
|
|
257
258
|
args: {
|
|
258
|
-
|
|
259
|
+
wrapperType: tool.schema.string().optional(),
|
|
259
260
|
},
|
|
260
261
|
async execute(args, context) {
|
|
261
|
-
const
|
|
262
|
+
const wrapperServices = getOrCreatePluginServices(context.worktree);
|
|
262
263
|
annotateToolRun(context, 'Compacting session', {});
|
|
263
|
-
const result = await
|
|
264
|
-
const snap =
|
|
264
|
+
const result = await wrapperServices.manager.compactSession(context.worktree);
|
|
265
|
+
const snap = wrapperServices.manager.getStatus();
|
|
265
266
|
const contextWarning = formatContextWarning(snap);
|
|
266
267
|
context.metadata({
|
|
267
268
|
title: contextWarning
|
|
@@ -355,25 +356,27 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
355
356
|
description: 'Clear the active session to start fresh. ' +
|
|
356
357
|
'Use when context is full or starting a new task.',
|
|
357
358
|
args: {
|
|
358
|
-
|
|
359
|
+
wrapperType: tool.schema.string().optional(),
|
|
359
360
|
reason: tool.schema.string().optional(),
|
|
360
361
|
},
|
|
361
362
|
async execute(args, context) {
|
|
363
|
+
const wrapperServices = getOrCreatePluginServices(context.worktree);
|
|
362
364
|
annotateToolRun(context, 'Clearing session', {
|
|
363
365
|
reason: args.reason,
|
|
364
366
|
});
|
|
365
|
-
const clearedId = await
|
|
367
|
+
const clearedId = await wrapperServices.manager.clearSession();
|
|
366
368
|
return JSON.stringify({ clearedSessionId: clearedId });
|
|
367
369
|
},
|
|
368
370
|
}),
|
|
369
371
|
session_health: tool({
|
|
370
372
|
description: 'Check session health metrics: context usage %, turn count, cost, and session ID.',
|
|
371
373
|
args: {
|
|
372
|
-
|
|
374
|
+
wrapperType: tool.schema.string().optional(),
|
|
373
375
|
},
|
|
374
|
-
async execute(
|
|
376
|
+
async execute(args, context) {
|
|
377
|
+
const wrapperServices = getOrCreatePluginServices(context.worktree);
|
|
375
378
|
annotateToolRun(context, 'Checking session status', {});
|
|
376
|
-
const status =
|
|
379
|
+
const status = wrapperServices.manager.getStatus();
|
|
377
380
|
return JSON.stringify({
|
|
378
381
|
...status,
|
|
379
382
|
transcriptFile: status.sessionId
|
|
@@ -386,39 +389,40 @@ export const ClaudeManagerPlugin = async ({ worktree }) => {
|
|
|
386
389
|
list_transcripts: tool({
|
|
387
390
|
description: 'List available session transcripts or inspect a specific transcript by ID.',
|
|
388
391
|
args: {
|
|
389
|
-
|
|
392
|
+
wrapperType: tool.schema.string().optional(),
|
|
390
393
|
sessionId: tool.schema.string().optional(),
|
|
391
394
|
},
|
|
392
395
|
async execute(args, context) {
|
|
396
|
+
const wrapperServices = getOrCreatePluginServices(context.worktree);
|
|
393
397
|
annotateToolRun(context, 'Inspecting Claude session history', {});
|
|
394
|
-
const cwd = args.cwd ?? context.worktree;
|
|
395
398
|
if (args.sessionId) {
|
|
396
399
|
const [sdkTranscript, localEvents] = await Promise.all([
|
|
397
|
-
|
|
398
|
-
|
|
400
|
+
wrapperServices.sessions.getTranscript(args.sessionId, context.worktree),
|
|
401
|
+
wrapperServices.manager.getTranscriptEvents(context.worktree, args.sessionId),
|
|
399
402
|
]);
|
|
400
403
|
return JSON.stringify({
|
|
401
404
|
sdkTranscript,
|
|
402
405
|
localEvents: localEvents.length > 0 ? localEvents : undefined,
|
|
403
406
|
}, null, 2);
|
|
404
407
|
}
|
|
405
|
-
const sessions = await
|
|
408
|
+
const sessions = await wrapperServices.sessions.listSessions(context.worktree);
|
|
406
409
|
return JSON.stringify(sessions, null, 2);
|
|
407
410
|
},
|
|
408
411
|
}),
|
|
409
412
|
list_history: tool({
|
|
410
413
|
description: 'List persistent run records from the manager or inspect a specific run.',
|
|
411
414
|
args: {
|
|
412
|
-
|
|
415
|
+
wrapperType: tool.schema.string().optional(),
|
|
413
416
|
runId: tool.schema.string().optional(),
|
|
414
417
|
},
|
|
415
418
|
async execute(args, context) {
|
|
419
|
+
const wrapperServices = getOrCreatePluginServices(context.worktree);
|
|
416
420
|
annotateToolRun(context, 'Reading manager run state', {});
|
|
417
421
|
if (args.runId) {
|
|
418
|
-
const run = await
|
|
422
|
+
const run = await wrapperServices.manager.getRun(context.worktree, args.runId);
|
|
419
423
|
return JSON.stringify(run, null, 2);
|
|
420
424
|
}
|
|
421
|
-
const runs = await
|
|
425
|
+
const runs = await wrapperServices.manager.listRuns(context.worktree);
|
|
422
426
|
return JSON.stringify(runs, null, 2);
|
|
423
427
|
},
|
|
424
428
|
}),
|
|
@@ -9,23 +9,19 @@ import { GitOperations } from '../manager/git-operations.js';
|
|
|
9
9
|
import { SessionController } from '../manager/session-controller.js';
|
|
10
10
|
import { PersistentManager } from '../manager/persistent-manager.js';
|
|
11
11
|
import { managerPromptRegistry } from '../prompts/registry.js';
|
|
12
|
-
const serviceCache = new Map();
|
|
13
12
|
export function getOrCreatePluginServices(worktree) {
|
|
14
|
-
const cachedServices = serviceCache.get(worktree);
|
|
15
|
-
if (cachedServices) {
|
|
16
|
-
return cachedServices;
|
|
17
|
-
}
|
|
18
13
|
const approvalManager = new ToolApprovalManager();
|
|
19
14
|
const sdkAdapter = new ClaudeAgentSdkAdapter(undefined, approvalManager);
|
|
20
15
|
const sessionService = new ClaudeSessionService(sdkAdapter);
|
|
21
16
|
const contextTracker = new ContextTracker();
|
|
22
|
-
const sessionController = new SessionController(sdkAdapter, contextTracker,
|
|
17
|
+
const sessionController = new SessionController(sdkAdapter, contextTracker, undefined, // session prompt is now constructed dynamically by the wrapper
|
|
18
|
+
'default', worktree, managerPromptRegistry.modePrefixes);
|
|
23
19
|
const gitOps = new GitOperations(worktree);
|
|
24
20
|
const stateStore = new FileRunStateStore();
|
|
25
21
|
const transcriptStore = new TranscriptStore();
|
|
26
22
|
const manager = new PersistentManager(sessionController, gitOps, stateStore, contextTracker, transcriptStore);
|
|
27
23
|
// Try to restore active session state (fire and forget)
|
|
28
|
-
manager.tryRestore(
|
|
24
|
+
manager.tryRestore().catch(() => { });
|
|
29
25
|
const liveTailer = new SessionLiveTailer();
|
|
30
26
|
const services = {
|
|
31
27
|
manager,
|
|
@@ -33,6 +29,5 @@ export function getOrCreatePluginServices(worktree) {
|
|
|
33
29
|
approvalManager,
|
|
34
30
|
liveTailer,
|
|
35
31
|
};
|
|
36
|
-
serviceCache.set(worktree, services);
|
|
37
32
|
return services;
|
|
38
33
|
}
|
package/dist/prompts/registry.js
CHANGED
|
@@ -13,6 +13,66 @@ export function composeWrapperPrompt(basePrompt, claudeFiles) {
|
|
|
13
13
|
const sections = claudeFiles.map((f) => `### ${f.relativePath}\n${f.content}`).join('\n\n');
|
|
14
14
|
return `${basePrompt}\n\n## Project Claude Files\nThe following project-level instructions were discovered from the repository.\n\n${sections}`;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Build an engineer wrapper prompt from shared sections plus mode-specific overrides.
|
|
18
|
+
*/
|
|
19
|
+
function buildEngineerWrapperPrompt(opts) {
|
|
20
|
+
return [
|
|
21
|
+
`You are a staff engineer managing a Claude Code session for ${opts.purpose}.`,
|
|
22
|
+
'You are not a forwarding layer — interpret the task in repo context before delegating.',
|
|
23
|
+
'',
|
|
24
|
+
'## Staff-level framing',
|
|
25
|
+
'- Identify the real problem, not just the stated request.',
|
|
26
|
+
'- Look for missing architecture, ownership, or precedence issues before delegating.',
|
|
27
|
+
'- Rewrite weak or underspecified requests into precise prompts for the engineer.',
|
|
28
|
+
'- For medium+ tasks, determine: the actual problem, the cleanest architecture,',
|
|
29
|
+
' and what needs clarification before work begins.',
|
|
30
|
+
'- Ask ONE clarification first if it materially improves architecture.',
|
|
31
|
+
'',
|
|
32
|
+
'## Repo-context investigation',
|
|
33
|
+
'- Use read/grep/glob sparingly — only for spot-checks to sharpen a delegation.',
|
|
34
|
+
'- Do NOT implement changes yourself — investigation only.',
|
|
35
|
+
'',
|
|
36
|
+
'## Behavior',
|
|
37
|
+
`- Send the objective to the engineer using ${opts.toolName}.`,
|
|
38
|
+
"- Return the engineer's response verbatim. Do not summarize.",
|
|
39
|
+
`- Use freshSession:true on ${opts.toolName} when the task is unrelated to prior work.`,
|
|
40
|
+
'',
|
|
41
|
+
'## Constructing the engineer session prompt',
|
|
42
|
+
`When you call \`${opts.toolName}\`, you MUST include a \`sessionSystemPrompt\` argument.`,
|
|
43
|
+
'This becomes the system prompt for the underlying Claude Code session.',
|
|
44
|
+
'Build it as a self-contained description that equips the engineer to execute precisely.',
|
|
45
|
+
'',
|
|
46
|
+
'Structure it as:',
|
|
47
|
+
`1. **Role**: "${opts.roleDescription}"`,
|
|
48
|
+
'2. **Project rules**: Extract only the rules from the Project Claude Files below that',
|
|
49
|
+
' are relevant to this specific task. Synthesize — do not dump everything.',
|
|
50
|
+
' Cite file paths when the source matters (e.g. "per packages/core/CLAUDE.md").',
|
|
51
|
+
'3. **Verification**: How to verify the work (tests, lint, typecheck, expected behavior).',
|
|
52
|
+
'',
|
|
53
|
+
'Keep it concise: 3-6 sentences for role + relevant rules + verification.',
|
|
54
|
+
'The task itself goes in `message`, not in `sessionSystemPrompt`.',
|
|
55
|
+
'',
|
|
56
|
+
'## Context management',
|
|
57
|
+
'- Check session_health before sending.',
|
|
58
|
+
'- Under 50%: proceed. Over 70%: compact_context. Over 85%: clear_session.',
|
|
59
|
+
'',
|
|
60
|
+
'## Model selection',
|
|
61
|
+
...opts.modelDefaults,
|
|
62
|
+
'',
|
|
63
|
+
'## Using appended Project Claude Files',
|
|
64
|
+
'- Treat appended Project Claude Files as project guidance for the engineer.',
|
|
65
|
+
'- Extract only the rules relevant to the current task when constructing sessionSystemPrompt.',
|
|
66
|
+
'- Prefer guidance from more specific/nested paths over root-level guidance on conflict.',
|
|
67
|
+
'- Direct user instructions override Claude-file guidance.',
|
|
68
|
+
'- Keep delegated instructions tight; do not dump the full file corpus unless needed.',
|
|
69
|
+
'- Cite file paths (e.g. "per packages/core/CLAUDE.md") when the source matters.',
|
|
70
|
+
'',
|
|
71
|
+
'## What you must NOT do',
|
|
72
|
+
'- Do NOT call git_*, approval_*, or any non-engineer tools.',
|
|
73
|
+
'- Do NOT add commentary to the engineer response.',
|
|
74
|
+
].join('\n');
|
|
75
|
+
}
|
|
16
76
|
export const managerPromptRegistry = {
|
|
17
77
|
ctoSystemPrompt: [
|
|
18
78
|
'You are a staff+ technical owner who uses Claude Code better than anyone.',
|
|
@@ -25,7 +85,7 @@ export const managerPromptRegistry = {
|
|
|
25
85
|
'- Verifying a result after an engineer returns.',
|
|
26
86
|
'- Resolving one high-leverage ambiguity before dispatching.',
|
|
27
87
|
'If you need more than 2 direct read/grep/glob lookups, stop and delegate',
|
|
28
|
-
'the investigation to `
|
|
88
|
+
'the investigation to `engineer_explore` instead.',
|
|
29
89
|
'',
|
|
30
90
|
'## Core principle: technical ownership',
|
|
31
91
|
'You are not a ticket-taker. Before acting, look for:',
|
|
@@ -51,12 +111,12 @@ export const managerPromptRegistry = {
|
|
|
51
111
|
' 2. What is underspecified or conflicting.',
|
|
52
112
|
' 3. What the cleanest architecture is.',
|
|
53
113
|
' 4. What should be clarified before proceeding.',
|
|
54
|
-
' Prefer spawning `
|
|
114
|
+
' Prefer spawning `engineer_explore` for repo exploration rather than reading',
|
|
55
115
|
' code yourself. Use at most 1-2 spot-check reads to sharpen the delegation.',
|
|
56
116
|
' Then delegate with file paths, line numbers, patterns, and verification.',
|
|
57
117
|
'',
|
|
58
118
|
'**Complex tasks** (multi-file feature, large refactor):',
|
|
59
|
-
' 1. Spawn `
|
|
119
|
+
' 1. Spawn `engineer_explore` to explore the repo, map dependencies, and analyze impact.',
|
|
60
120
|
' 2. If requirements are unclear, ask the user ONE high-leverage question —',
|
|
61
121
|
' only when it materially changes architecture, ownership, or destructive behavior.',
|
|
62
122
|
' Prefer the question tool when discrete options exist.',
|
|
@@ -92,20 +152,20 @@ export const managerPromptRegistry = {
|
|
|
92
152
|
' style violations.',
|
|
93
153
|
'2. git_status — check what files changed (quick overview)',
|
|
94
154
|
'3. git_log -n 5 — see recent commits (understand context)',
|
|
95
|
-
'4. If correct: spawn `
|
|
155
|
+
'4. If correct: spawn `engineer_implement` to run tests/lint/typecheck.',
|
|
96
156
|
'5. If tests pass: git_commit to checkpoint.',
|
|
97
|
-
'6. If wrong: spawn `
|
|
157
|
+
'6. If wrong: spawn `engineer_implement` with a specific correction.',
|
|
98
158
|
' On second failure: git_reset and rewrite the prompt from scratch.',
|
|
99
159
|
' Never send three corrections for the same problem.',
|
|
100
160
|
'',
|
|
101
161
|
'## Engineers (via the Task tool)',
|
|
102
|
-
'- `
|
|
162
|
+
'- `engineer_explore` — read-only investigation. Use for: exploring unfamiliar code,',
|
|
103
163
|
' mapping dependencies, analyzing impact, asking "how does X work?"',
|
|
104
|
-
'- `
|
|
164
|
+
'- `engineer_implement` — implementation. Use for: all code changes, test runs, fixes.',
|
|
105
165
|
'- If steps are independent, spawn multiple engineers in parallel.',
|
|
106
166
|
'',
|
|
107
167
|
'## Context efficiency',
|
|
108
|
-
'- Use `
|
|
168
|
+
'- Use `engineer_explore` for broad exploration so your own context stays clean.',
|
|
109
169
|
'- When spawning engineers for unrelated tasks, tell them to use freshSession:true.',
|
|
110
170
|
'- Keep delegations focused — one concern per engineer invocation.',
|
|
111
171
|
'',
|
|
@@ -134,106 +194,40 @@ export const managerPromptRegistry = {
|
|
|
134
194
|
'- Destructive actions on shared state (deploy, publish, force-push).',
|
|
135
195
|
'State the blocker, what you need, and a concrete suggestion to unblock.',
|
|
136
196
|
].join('\n'),
|
|
137
|
-
|
|
138
|
-
'
|
|
139
|
-
|
|
140
|
-
''
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
'
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
'- Check session_health before sending.',
|
|
161
|
-
'- Under 50%: proceed. Over 70%: compact_context. Over 85%: clear_session.',
|
|
162
|
-
'',
|
|
163
|
-
'## Model selection',
|
|
164
|
-
'- claude-opus-4-6 + high: complex analysis (default).',
|
|
165
|
-
'- claude-sonnet-4-6: lighter analysis.',
|
|
166
|
-
'- effort "medium": simple lookups.',
|
|
167
|
-
'',
|
|
168
|
-
'## Using appended Project Claude Files',
|
|
169
|
-
'- Treat appended Project Claude Files as project guidance for the engineer.',
|
|
170
|
-
'- Extract only the rules relevant to the current task when delegating.',
|
|
171
|
-
'- Prefer guidance from more specific/nested paths over root-level guidance on conflict.',
|
|
172
|
-
'- Direct user instructions override Claude-file guidance.',
|
|
173
|
-
'- Keep delegated instructions tight; do not dump the full file corpus unless needed.',
|
|
174
|
-
'- Cite file paths (e.g. "per packages/core/CLAUDE.md") when the source matters.',
|
|
175
|
-
'',
|
|
176
|
-
'## What you must NOT do',
|
|
177
|
-
'- Do NOT call git_*, approval_*, or any non-engineer tools.',
|
|
178
|
-
'- Do NOT add commentary to the engineer response.',
|
|
179
|
-
].join('\n'),
|
|
180
|
-
engineerBuildPrompt: [
|
|
181
|
-
'You are a staff engineer managing a Claude Code session for implementation.',
|
|
182
|
-
'You are not a forwarding layer — interpret the task in repo context before delegating.',
|
|
183
|
-
'',
|
|
184
|
-
'## Staff-level framing',
|
|
185
|
-
'- Identify the real problem, not just the stated request.',
|
|
186
|
-
'- Look for missing architecture, ownership, or precedence issues before delegating.',
|
|
187
|
-
'- Rewrite weak or underspecified requests into precise prompts for the engineer.',
|
|
188
|
-
'- For medium+ tasks, determine: the actual problem, the cleanest architecture,',
|
|
189
|
-
' and what needs clarification before work begins.',
|
|
190
|
-
'- Ask ONE clarification first if it materially improves architecture.',
|
|
191
|
-
'',
|
|
192
|
-
'## Repo-context investigation',
|
|
193
|
-
'- Use read/grep/glob sparingly — only for spot-checks to sharpen a delegation.',
|
|
194
|
-
'- If more than 2 lookups are needed, send the investigation to the engineer.',
|
|
195
|
-
'- Do NOT implement changes yourself — investigation only.',
|
|
196
|
-
'',
|
|
197
|
-
'## Behavior',
|
|
198
|
-
'- Send the objective to the engineer using implement.',
|
|
199
|
-
"- Return the engineer's response verbatim. Do not summarize.",
|
|
200
|
-
'- Use freshSession:true on implement when the task is unrelated to prior work.',
|
|
201
|
-
'',
|
|
202
|
-
'## Context management',
|
|
203
|
-
'- Check session_health before sending.',
|
|
204
|
-
'- Under 50%: proceed. Over 70%: compact_context. Over 85%: clear_session.',
|
|
205
|
-
'',
|
|
206
|
-
'## Model selection',
|
|
207
|
-
'- claude-opus-4-6 + high: most coding tasks (default).',
|
|
208
|
-
'- claude-sonnet-4-6: simple renames, formatting, scaffolding.',
|
|
209
|
-
'- effort "max": complex refactors, subtle bugs, cross-cutting changes.',
|
|
210
|
-
'',
|
|
211
|
-
'## Using appended Project Claude Files',
|
|
212
|
-
'- Treat appended Project Claude Files as project guidance for the engineer.',
|
|
213
|
-
'- Extract only the rules relevant to the current task when delegating.',
|
|
214
|
-
'- Prefer guidance from more specific/nested paths over root-level guidance on conflict.',
|
|
215
|
-
'- Direct user instructions override Claude-file guidance.',
|
|
216
|
-
'- Keep delegated instructions tight; do not dump the full file corpus unless needed.',
|
|
217
|
-
'- Cite file paths (e.g. "per packages/core/CLAUDE.md") when the source matters.',
|
|
218
|
-
'',
|
|
219
|
-
'## What you must NOT do',
|
|
220
|
-
'- Do NOT call git_*, approval_*, or any non-engineer tools.',
|
|
221
|
-
'- Do NOT add commentary to the engineer response.',
|
|
222
|
-
].join('\n'),
|
|
197
|
+
engineerExplorePrompt: buildEngineerWrapperPrompt({
|
|
198
|
+
purpose: 'read-only investigation',
|
|
199
|
+
toolName: 'explore',
|
|
200
|
+
roleDescription: 'You are an expert engineer performing read-only investigation. ' +
|
|
201
|
+
'Execute directly. No preamble. Follow existing repo conventions.',
|
|
202
|
+
modelDefaults: [
|
|
203
|
+
'- claude-opus-4-6 + high: complex analysis (default).',
|
|
204
|
+
'- claude-sonnet-4-6: lighter analysis.',
|
|
205
|
+
'- effort "medium": simple lookups.',
|
|
206
|
+
],
|
|
207
|
+
}),
|
|
208
|
+
engineerImplementPrompt: buildEngineerWrapperPrompt({
|
|
209
|
+
purpose: 'implementation',
|
|
210
|
+
toolName: 'implement',
|
|
211
|
+
roleDescription: 'You are an expert engineer implementing changes. ' +
|
|
212
|
+
'Execute directly. No preamble. Follow existing repo conventions. ' +
|
|
213
|
+
'Do not run git commit, git push, git reset, git checkout, or git stash — git operations are handled externally.',
|
|
214
|
+
modelDefaults: [
|
|
215
|
+
'- claude-opus-4-6 + high: most coding tasks (default).',
|
|
216
|
+
'- claude-sonnet-4-6: simple renames, formatting, scaffolding.',
|
|
217
|
+
'- effort "max": complex refactors, subtle bugs, cross-cutting changes.',
|
|
218
|
+
],
|
|
219
|
+
}),
|
|
223
220
|
engineerSessionPrompt: [
|
|
224
221
|
'You are an expert engineer. Execute instructions precisely.',
|
|
225
222
|
'',
|
|
226
223
|
'## Rules',
|
|
227
224
|
'- Execute directly. No preamble, no restating the task.',
|
|
228
|
-
'-
|
|
229
|
-
'-
|
|
230
|
-
'- Follow existing repo conventions (naming, style, patterns).',
|
|
225
|
+
'- Follow existing repo conventions.',
|
|
226
|
+
'- Report exact output on failure.',
|
|
231
227
|
'',
|
|
232
228
|
'## Verification',
|
|
233
229
|
'- Always verify your own work before reporting done.',
|
|
234
|
-
'- Run tests, lint, or typecheck when the instruction says to.',
|
|
235
230
|
'- If no verification was specified, still run relevant tests if they exist.',
|
|
236
|
-
'- Report exact output on failure.',
|
|
237
231
|
'',
|
|
238
232
|
'## Git boundary — do NOT run:',
|
|
239
233
|
'git commit, git push, git reset, git checkout, git stash.',
|
|
@@ -241,8 +235,7 @@ export const managerPromptRegistry = {
|
|
|
241
235
|
'',
|
|
242
236
|
'## Reporting',
|
|
243
237
|
'- End with: what was done, what was verified, what passed/failed.',
|
|
244
|
-
'- Report blockers immediately with specifics
|
|
245
|
-
'- If partially complete, state exactly what remains.',
|
|
238
|
+
'- Report blockers immediately with specifics.',
|
|
246
239
|
].join('\n'),
|
|
247
240
|
modePrefixes: {
|
|
248
241
|
plan: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export interface ManagerPromptRegistry {
|
|
2
2
|
ctoSystemPrompt: string;
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
engineerExplorePrompt: string;
|
|
4
|
+
engineerImplementPrompt: string;
|
|
5
5
|
engineerSessionPrompt: string;
|
|
6
6
|
modePrefixes: {
|
|
7
7
|
plan: string;
|
|
@@ -113,7 +113,6 @@ export interface GitOperationResult {
|
|
|
113
113
|
}
|
|
114
114
|
export interface ActiveSessionState {
|
|
115
115
|
sessionId: string;
|
|
116
|
-
cwd: string;
|
|
117
116
|
startedAt: string;
|
|
118
117
|
totalTurns: number;
|
|
119
118
|
totalCostUsd: number;
|