@doingdev/opencode-claude-manager-plugin 0.1.47 → 0.1.50
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/claude/claude-agent-sdk-adapter.d.ts +2 -3
- package/dist/claude/claude-agent-sdk-adapter.js +0 -44
- package/dist/claude/claude-session.service.d.ts +1 -2
- package/dist/claude/claude-session.service.js +0 -3
- package/dist/claude/tool-approval-manager.d.ts +9 -6
- package/dist/claude/tool-approval-manager.js +43 -6
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/manager/context-tracker.d.ts +0 -1
- package/dist/manager/context-tracker.js +0 -3
- package/dist/manager/git-operations.d.ts +1 -4
- package/dist/manager/git-operations.js +7 -12
- package/dist/manager/persistent-manager.d.ts +3 -53
- package/dist/manager/persistent-manager.js +3 -135
- package/dist/manager/team-orchestrator.d.ts +8 -1
- package/dist/manager/team-orchestrator.js +70 -11
- package/dist/plugin/agent-hierarchy.d.ts +1 -1
- package/dist/plugin/agent-hierarchy.js +3 -1
- package/dist/plugin/claude-manager.plugin.js +218 -25
- package/dist/plugin/service-factory.d.ts +2 -2
- package/dist/plugin/service-factory.js +18 -12
- package/dist/prompts/registry.js +42 -37
- package/dist/src/claude/claude-agent-sdk-adapter.d.ts +2 -3
- package/dist/src/claude/claude-agent-sdk-adapter.js +0 -44
- package/dist/src/claude/claude-session.service.d.ts +1 -2
- package/dist/src/claude/claude-session.service.js +0 -3
- package/dist/src/claude/tool-approval-manager.d.ts +9 -6
- package/dist/src/claude/tool-approval-manager.js +43 -6
- package/dist/src/index.d.ts +1 -2
- package/dist/src/index.js +0 -1
- package/dist/src/manager/context-tracker.d.ts +0 -1
- package/dist/src/manager/context-tracker.js +0 -3
- package/dist/src/manager/git-operations.d.ts +1 -4
- package/dist/src/manager/git-operations.js +7 -12
- package/dist/src/manager/persistent-manager.d.ts +3 -53
- package/dist/src/manager/persistent-manager.js +3 -135
- package/dist/src/manager/team-orchestrator.d.ts +8 -1
- package/dist/src/manager/team-orchestrator.js +70 -11
- package/dist/src/plugin/agent-hierarchy.d.ts +1 -1
- package/dist/src/plugin/agent-hierarchy.js +3 -1
- package/dist/src/plugin/claude-manager.plugin.js +218 -25
- package/dist/src/plugin/service-factory.d.ts +2 -2
- package/dist/src/plugin/service-factory.js +18 -12
- package/dist/src/prompts/registry.js +42 -37
- package/dist/src/state/team-state-store.d.ts +3 -0
- package/dist/src/state/team-state-store.js +26 -1
- package/dist/src/team/roster.js +1 -0
- package/dist/src/types/contracts.d.ts +9 -49
- package/dist/state/team-state-store.d.ts +3 -0
- package/dist/state/team-state-store.js +26 -1
- package/dist/team/roster.js +1 -0
- package/dist/test/claude-agent-sdk-adapter.test.js +0 -11
- package/dist/test/claude-manager.plugin.test.js +6 -1
- package/dist/test/context-tracker.test.js +0 -8
- package/dist/test/cto-active-team.test.d.ts +1 -0
- package/dist/test/cto-active-team.test.js +52 -0
- package/dist/test/git-operations.test.js +0 -21
- package/dist/test/persistent-manager.test.js +4 -164
- package/dist/test/prompt-registry.test.js +4 -9
- package/dist/test/report-claude-event.test.d.ts +1 -0
- package/dist/test/report-claude-event.test.js +246 -0
- package/dist/test/team-state-store.test.js +18 -0
- package/dist/test/tool-approval-manager.test.js +17 -17
- package/dist/types/contracts.d.ts +9 -49
- package/package.json +1 -1
package/dist/prompts/registry.js
CHANGED
|
@@ -1,52 +1,57 @@
|
|
|
1
1
|
export const managerPromptRegistry = {
|
|
2
2
|
ctoSystemPrompt: [
|
|
3
|
-
'You are
|
|
3
|
+
'You are a principal engineer orchestrating a team of AI-powered engineers.',
|
|
4
|
+
'You multiply your output by delegating precisely and reviewing critically.',
|
|
5
|
+
'Every prompt you send to an engineer costs time and tokens. Make each one count.',
|
|
4
6
|
'',
|
|
5
|
-
'
|
|
6
|
-
'-
|
|
7
|
-
'-
|
|
8
|
-
'-
|
|
9
|
-
'-
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'-
|
|
7
|
+
'Understand first:',
|
|
8
|
+
'- Ask questions. If the request is ambiguous, underspecified, or has multiple valid interpretations, ask before building. Any question whose answer would change what you build or how you build it is worth asking.',
|
|
9
|
+
'- Use the `question` tool to surface decisions with a concrete recommendation. Prefer one precise question over many vague ones.',
|
|
10
|
+
'- Identify what already exists in the codebase before creating anything new.',
|
|
11
|
+
'- Think about what could go wrong and address it upfront.',
|
|
12
|
+
'',
|
|
13
|
+
'Plan and decompose:',
|
|
14
|
+
'- Break work into independent pieces that can run in parallel. Two engineers exploring in parallel then synthesizing beats one engineer doing everything sequentially.',
|
|
15
|
+
'- For medium or large tasks, dispatch two engineers with complementary perspectives (lead plan + challenger review), then synthesize.',
|
|
16
|
+
'- 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.',
|
|
13
17
|
'',
|
|
14
|
-
'
|
|
15
|
-
'- Tom, John, Maya, Sara, and Alex are persistent engineers.',
|
|
16
|
-
'-
|
|
17
|
-
'-
|
|
18
|
-
'-
|
|
18
|
+
'Delegate through the Task tool:',
|
|
19
|
+
'- Tom, John, Maya, Sara, and Alex are persistent engineers. Each keeps a Claude Code session that remembers prior turns.',
|
|
20
|
+
'- Reuse the same engineer when follow-up work belongs to their prior context.',
|
|
21
|
+
'- Only one implementing engineer should modify the worktree at a time. Parallelize exploration freely.',
|
|
22
|
+
'- Do not delegate without telling the engineer what done looks like.',
|
|
23
|
+
'',
|
|
24
|
+
'Review and iterate:',
|
|
25
|
+
'- Review diffs with `git_diff`, inspect changed files with `git_status`, and use `git_log` for recent context.',
|
|
26
|
+
'- Give specific, actionable feedback. Not "this could be better" but "this is wrong because X, fix it by doing Y."',
|
|
27
|
+
'- Trust engineer findings but verify critical claims. Do not re-examine every file they already reviewed.',
|
|
28
|
+
'- If something fails, figure out what you missed in the assignment, not just what the engineer got wrong.',
|
|
19
29
|
'',
|
|
20
|
-
'
|
|
21
|
-
'- Do not edit files or run bash directly.',
|
|
22
|
-
'- Do not
|
|
23
|
-
'-
|
|
30
|
+
'Constraints:',
|
|
31
|
+
'- Do not edit files or run bash directly. Engineers do the hands-on work.',
|
|
32
|
+
'- Do not read files or grep when an engineer can answer the question faster.',
|
|
33
|
+
'- Communicate proactively. If the plan changes or you discover something unexpected, tell the user.',
|
|
24
34
|
].join('\n'),
|
|
25
35
|
engineerAgentPrompt: [
|
|
26
|
-
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
36
|
+
"You are a named engineer on the CTO's team.",
|
|
37
|
+
'Your job is to run assignments through the `claude` tool, which connects to a persistent Claude Code session that remembers your prior turns.',
|
|
38
|
+
'',
|
|
39
|
+
'Frame each assignment well:',
|
|
40
|
+
'- Include relevant context, file paths, and constraints the CTO provided.',
|
|
41
|
+
'- Specify the work mode: explore (investigate, no edits), implement (make changes and verify), or verify (run checks and report).',
|
|
42
|
+
"- If the CTO's assignment is unclear, ask for clarification before sending it to Claude Code.",
|
|
43
|
+
'',
|
|
44
|
+
'Your wrapper context from prior turns is reloaded automatically. Use it to avoid repeating work or re-explaining context that Claude Code already knows.',
|
|
45
|
+
"Return the tool result directly. Add your own commentary only when something was unexpected or needs the CTO's attention.",
|
|
32
46
|
].join('\n'),
|
|
33
47
|
engineerSessionPrompt: [
|
|
34
48
|
'You are an expert software engineer working inside Claude Code.',
|
|
35
|
-
'
|
|
36
|
-
'Follow repository conventions and
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'Report blockers clearly and include exact command output on failure.',
|
|
49
|
+
'Start with the smallest investigation that resolves the key uncertainty, then act.',
|
|
50
|
+
'Follow repository conventions, AGENTS.md, and any project-level instructions.',
|
|
51
|
+
'Verify your own work before reporting done. Run the most relevant check (test, lint, typecheck, build) for what you changed.',
|
|
52
|
+
'Report blockers immediately with exact error output. Do not retry silently more than once.',
|
|
40
53
|
'Do not run git commit, git push, git reset, git checkout, or git stash.',
|
|
41
54
|
].join('\n'),
|
|
42
|
-
modePrefixes: {
|
|
43
|
-
plan: [
|
|
44
|
-
'[PLAN MODE] Read-only.',
|
|
45
|
-
'Do not create or edit files.',
|
|
46
|
-
'Analyze the codebase and return the plan inline.',
|
|
47
|
-
].join(' '),
|
|
48
|
-
free: '',
|
|
49
|
-
},
|
|
50
55
|
contextWarnings: {
|
|
51
56
|
moderate: 'Engineer context is getting full ({percent}% estimated). Reuse is still fine, but keep the next prompt focused.',
|
|
52
57
|
high: 'Engineer context is heavy ({percent}% estimated, {turns} turns, ${cost}). Prefer a narrowly scoped follow-up or internal compaction.',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Options, type Query, type SDKSessionInfo, type SessionMessage
|
|
2
|
-
import type {
|
|
1
|
+
import { type Options, type Query, type SDKSessionInfo, type SessionMessage } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
import type { ClaudeSessionEvent, ClaudeSessionRunResult, ClaudeSessionSummary, ClaudeSessionTranscriptMessage, RunClaudeSessionInput } from '../types/contracts.js';
|
|
3
3
|
import type { ToolApprovalManager } from './tool-approval-manager.js';
|
|
4
4
|
export type ClaudeSessionEventHandler = (event: ClaudeSessionEvent) => void | Promise<void>;
|
|
5
5
|
interface ClaudeAgentSdkFacade {
|
|
@@ -21,7 +21,6 @@ export declare class ClaudeAgentSdkAdapter {
|
|
|
21
21
|
runSession(input: RunClaudeSessionInput, onEvent?: ClaudeSessionEventHandler): Promise<ClaudeSessionRunResult>;
|
|
22
22
|
listSavedSessions(cwd?: string): Promise<ClaudeSessionSummary[]>;
|
|
23
23
|
getTranscript(sessionId: string, cwd?: string): Promise<ClaudeSessionTranscriptMessage[]>;
|
|
24
|
-
probeCapabilities(cwd: string, settingSources?: SettingSource[]): Promise<ClaudeCapabilitySnapshot>;
|
|
25
24
|
private buildOptions;
|
|
26
25
|
}
|
|
27
26
|
export {};
|
|
@@ -123,34 +123,6 @@ export class ClaudeAgentSdkAdapter {
|
|
|
123
123
|
text: extractText(message.message),
|
|
124
124
|
}));
|
|
125
125
|
}
|
|
126
|
-
async probeCapabilities(cwd, settingSources = ['project']) {
|
|
127
|
-
const sessionQuery = this.sdkFacade.query({
|
|
128
|
-
prompt: 'Reply with OK.',
|
|
129
|
-
options: {
|
|
130
|
-
cwd,
|
|
131
|
-
maxTurns: 1,
|
|
132
|
-
permissionMode: 'plan',
|
|
133
|
-
persistSession: false,
|
|
134
|
-
tools: [],
|
|
135
|
-
settingSources,
|
|
136
|
-
},
|
|
137
|
-
});
|
|
138
|
-
try {
|
|
139
|
-
const [commands, agents, models] = await Promise.all([
|
|
140
|
-
sessionQuery.supportedCommands(),
|
|
141
|
-
sessionQuery.supportedAgents(),
|
|
142
|
-
sessionQuery.supportedModels(),
|
|
143
|
-
]);
|
|
144
|
-
return {
|
|
145
|
-
commands: commands.map(mapSlashCommand),
|
|
146
|
-
agents: agents.map(mapAgent),
|
|
147
|
-
models: models.map((model) => model.value),
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
finally {
|
|
151
|
-
sessionQuery.close();
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
126
|
buildOptions(input) {
|
|
155
127
|
const options = {
|
|
156
128
|
cwd: input.cwd,
|
|
@@ -465,22 +437,6 @@ function extractText(payload) {
|
|
|
465
437
|
}
|
|
466
438
|
return JSON.stringify(payload);
|
|
467
439
|
}
|
|
468
|
-
function mapSlashCommand(command) {
|
|
469
|
-
return {
|
|
470
|
-
name: command.name,
|
|
471
|
-
description: command.description,
|
|
472
|
-
argumentHint: command.argumentHint,
|
|
473
|
-
source: 'sdk',
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
function mapAgent(agent) {
|
|
477
|
-
return {
|
|
478
|
-
name: agent.name,
|
|
479
|
-
description: agent.description,
|
|
480
|
-
model: agent.model,
|
|
481
|
-
source: 'sdk',
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
440
|
function extractUsageFromResult(message) {
|
|
485
441
|
if (message.type !== 'result') {
|
|
486
442
|
return {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ClaudeSessionRunResult, ClaudeSessionSummary, ClaudeSessionTranscriptMessage, RunClaudeSessionInput } from '../types/contracts.js';
|
|
2
2
|
import type { ClaudeAgentSdkAdapter, ClaudeSessionEventHandler } from './claude-agent-sdk-adapter.js';
|
|
3
3
|
export declare class ClaudeSessionService {
|
|
4
4
|
private readonly sdkAdapter;
|
|
@@ -6,5 +6,4 @@ export declare class ClaudeSessionService {
|
|
|
6
6
|
runTask(input: RunClaudeSessionInput, onEvent?: ClaudeSessionEventHandler): Promise<ClaudeSessionRunResult>;
|
|
7
7
|
listSessions(cwd?: string): Promise<ClaudeSessionSummary[]>;
|
|
8
8
|
getTranscript(sessionId: string, cwd?: string): Promise<ClaudeSessionTranscriptMessage[]>;
|
|
9
|
-
probeCapabilities(cwd: string): Promise<ClaudeCapabilitySnapshot>;
|
|
10
9
|
}
|
|
@@ -3,7 +3,10 @@ export declare class ToolApprovalManager {
|
|
|
3
3
|
private policy;
|
|
4
4
|
private decisions;
|
|
5
5
|
private readonly maxDecisions;
|
|
6
|
-
|
|
6
|
+
private readonly persistPath;
|
|
7
|
+
constructor(policy?: Partial<ToolApprovalPolicy>, maxDecisions?: number, persistPath?: string);
|
|
8
|
+
loadPersistedPolicy(): Promise<void>;
|
|
9
|
+
private persistPolicy;
|
|
7
10
|
evaluate(toolName: string, input: Record<string, unknown>, options?: {
|
|
8
11
|
title?: string;
|
|
9
12
|
agentID?: string;
|
|
@@ -17,11 +20,11 @@ export declare class ToolApprovalManager {
|
|
|
17
20
|
getDeniedDecisions(limit?: number): ToolApprovalDecision[];
|
|
18
21
|
clearDecisions(): void;
|
|
19
22
|
getPolicy(): ToolApprovalPolicy;
|
|
20
|
-
setPolicy(policy: ToolApprovalPolicy): void
|
|
21
|
-
addRule(rule: ToolApprovalRule, position?: number): void
|
|
22
|
-
removeRule(ruleId: string): boolean
|
|
23
|
-
setDefaultAction(action: 'allow' | 'deny'): void
|
|
24
|
-
setEnabled(enabled: boolean): void
|
|
23
|
+
setPolicy(policy: ToolApprovalPolicy): Promise<void>;
|
|
24
|
+
addRule(rule: ToolApprovalRule, position?: number): Promise<void>;
|
|
25
|
+
removeRule(ruleId: string): Promise<boolean>;
|
|
26
|
+
setDefaultAction(action: 'allow' | 'deny'): Promise<void>;
|
|
27
|
+
setEnabled(enabled: boolean): Promise<void>;
|
|
25
28
|
private findMatchingRule;
|
|
26
29
|
private recordDecision;
|
|
27
30
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { isFileNotFoundError, writeJsonAtomically } from '../util/fs-helpers.js';
|
|
1
4
|
const DEFAULT_MAX_DECISIONS = 500;
|
|
2
5
|
const INPUT_PREVIEW_MAX = 300;
|
|
3
6
|
function getDefaultRules() {
|
|
@@ -120,7 +123,8 @@ export class ToolApprovalManager {
|
|
|
120
123
|
policy;
|
|
121
124
|
decisions = [];
|
|
122
125
|
maxDecisions;
|
|
123
|
-
|
|
126
|
+
persistPath;
|
|
127
|
+
constructor(policy, maxDecisions, persistPath) {
|
|
124
128
|
this.policy = {
|
|
125
129
|
rules: policy?.rules ?? getDefaultRules(),
|
|
126
130
|
defaultAction: policy?.defaultAction ?? 'allow',
|
|
@@ -128,6 +132,34 @@ export class ToolApprovalManager {
|
|
|
128
132
|
enabled: policy?.enabled ?? true,
|
|
129
133
|
};
|
|
130
134
|
this.maxDecisions = maxDecisions ?? DEFAULT_MAX_DECISIONS;
|
|
135
|
+
this.persistPath = persistPath ?? null;
|
|
136
|
+
}
|
|
137
|
+
async loadPersistedPolicy() {
|
|
138
|
+
if (!this.persistPath) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const content = await fs.readFile(this.persistPath, 'utf8');
|
|
143
|
+
const loaded = JSON.parse(content);
|
|
144
|
+
this.policy = {
|
|
145
|
+
rules: loaded.rules ?? getDefaultRules(),
|
|
146
|
+
defaultAction: loaded.defaultAction ?? 'allow',
|
|
147
|
+
defaultDenyMessage: loaded.defaultDenyMessage ?? 'Tool call denied by approval policy.',
|
|
148
|
+
enabled: loaded.enabled ?? true,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
if (!isFileNotFoundError(error)) {
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async persistPolicy() {
|
|
158
|
+
if (!this.persistPath) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
await fs.mkdir(path.dirname(this.persistPath), { recursive: true });
|
|
162
|
+
await writeJsonAtomically(this.persistPath, this.policy);
|
|
131
163
|
}
|
|
132
164
|
evaluate(toolName, input, options) {
|
|
133
165
|
if (!this.policy.enabled) {
|
|
@@ -168,30 +200,35 @@ export class ToolApprovalManager {
|
|
|
168
200
|
getPolicy() {
|
|
169
201
|
return { ...this.policy, rules: [...this.policy.rules] };
|
|
170
202
|
}
|
|
171
|
-
setPolicy(policy) {
|
|
203
|
+
async setPolicy(policy) {
|
|
172
204
|
this.policy = { ...policy, rules: [...policy.rules] };
|
|
205
|
+
await this.persistPolicy();
|
|
173
206
|
}
|
|
174
|
-
addRule(rule, position) {
|
|
207
|
+
async addRule(rule, position) {
|
|
175
208
|
if (position !== undefined && position >= 0 && position < this.policy.rules.length) {
|
|
176
209
|
this.policy.rules.splice(position, 0, rule);
|
|
177
210
|
}
|
|
178
211
|
else {
|
|
179
212
|
this.policy.rules.push(rule);
|
|
180
213
|
}
|
|
214
|
+
await this.persistPolicy();
|
|
181
215
|
}
|
|
182
|
-
removeRule(ruleId) {
|
|
216
|
+
async removeRule(ruleId) {
|
|
183
217
|
const index = this.policy.rules.findIndex((r) => r.id === ruleId);
|
|
184
218
|
if (index === -1) {
|
|
185
219
|
return false;
|
|
186
220
|
}
|
|
187
221
|
this.policy.rules.splice(index, 1);
|
|
222
|
+
await this.persistPolicy();
|
|
188
223
|
return true;
|
|
189
224
|
}
|
|
190
|
-
setDefaultAction(action) {
|
|
225
|
+
async setDefaultAction(action) {
|
|
191
226
|
this.policy.defaultAction = action;
|
|
227
|
+
await this.persistPolicy();
|
|
192
228
|
}
|
|
193
|
-
setEnabled(enabled) {
|
|
229
|
+
async setEnabled(enabled) {
|
|
194
230
|
this.policy.enabled = enabled;
|
|
231
|
+
await this.persistPolicy();
|
|
195
232
|
}
|
|
196
233
|
findMatchingRule(toolName, inputJson) {
|
|
197
234
|
for (const rule of this.policy.rules) {
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Plugin } from '@opencode-ai/plugin';
|
|
2
2
|
import { ClaudeManagerPlugin } from './plugin/claude-manager.plugin.js';
|
|
3
|
-
export type { ClaudeCapabilitySnapshot, ClaudeSessionRunResult, ClaudeSessionSummary, ClaudeSessionTranscriptMessage, ManagerPromptRegistry, RunClaudeSessionInput, SessionContextSnapshot, GitDiffResult, GitOperationResult,
|
|
4
|
-
export { SessionLiveTailer } from './claude/session-live-tailer.js';
|
|
3
|
+
export type { ClaudeCapabilitySnapshot, ClaudeSessionRunResult, ClaudeSessionSummary, ClaudeSessionTranscriptMessage, ManagerPromptRegistry, RunClaudeSessionInput, SessionContextSnapshot, GitDiffResult, GitOperationResult, ContextWarningLevel, SessionMode, EngineerName, EngineerWorkMode, EngineerFailureKind, EngineerFailureResult, WrapperHistoryEntry, TeamEngineerRecord, TeamRecord, EngineerTaskResult, PlanDraft, SynthesizedPlanResult, ToolApprovalRule, ToolApprovalPolicy, ToolApprovalDecision, } from './types/contracts.js';
|
|
5
4
|
export { ClaudeManagerPlugin };
|
|
6
5
|
export declare const plugin: Plugin;
|
package/dist/src/index.js
CHANGED
|
@@ -19,7 +19,6 @@ export declare class ContextTracker {
|
|
|
19
19
|
snapshot(): SessionContextSnapshot;
|
|
20
20
|
warningLevel(): ContextWarningLevel;
|
|
21
21
|
estimateContextPercent(): number | null;
|
|
22
|
-
isAboveTokenThreshold(thresholdTokens?: number): boolean;
|
|
23
22
|
reset(): void;
|
|
24
23
|
/** Restore from persisted active session state. */
|
|
25
24
|
restore(state: {
|
|
@@ -83,9 +83,6 @@ export class ContextTracker {
|
|
|
83
83
|
}
|
|
84
84
|
return null;
|
|
85
85
|
}
|
|
86
|
-
isAboveTokenThreshold(thresholdTokens = 200_000) {
|
|
87
|
-
return this.latestInputTokens !== null && this.latestInputTokens >= thresholdTokens;
|
|
88
|
-
}
|
|
89
86
|
reset() {
|
|
90
87
|
this.totalTurns = 0;
|
|
91
88
|
this.totalCostUsd = 0;
|
|
@@ -7,15 +7,12 @@ export declare class GitOperations {
|
|
|
7
7
|
staged?: boolean;
|
|
8
8
|
ref?: string;
|
|
9
9
|
}): Promise<GitDiffResult>;
|
|
10
|
-
|
|
11
|
-
commit(message: string): Promise<GitOperationResult>;
|
|
10
|
+
commit(message: string, paths?: string[]): Promise<GitOperationResult>;
|
|
12
11
|
resetHard(): Promise<GitOperationResult>;
|
|
13
12
|
status(): Promise<{
|
|
14
13
|
output: string;
|
|
15
14
|
isClean: boolean;
|
|
16
15
|
}>;
|
|
17
16
|
log(count?: number): Promise<string>;
|
|
18
|
-
currentBranch(): Promise<string>;
|
|
19
|
-
recentCommits(count?: number): Promise<string>;
|
|
20
17
|
private git;
|
|
21
18
|
}
|
|
@@ -23,12 +23,14 @@ export class GitOperations {
|
|
|
23
23
|
stats,
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
-
async
|
|
27
|
-
return this.git(['diff', 'HEAD', '--stat']);
|
|
28
|
-
}
|
|
29
|
-
async commit(message) {
|
|
26
|
+
async commit(message, paths) {
|
|
30
27
|
try {
|
|
31
|
-
|
|
28
|
+
if (paths && paths.length > 0) {
|
|
29
|
+
await this.git(['add', '--', ...paths]);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
await this.git(['add', '-A']);
|
|
33
|
+
}
|
|
32
34
|
const output = await this.git(['commit', '-m', message]);
|
|
33
35
|
return { success: true, output };
|
|
34
36
|
}
|
|
@@ -65,13 +67,6 @@ export class GitOperations {
|
|
|
65
67
|
async log(count = 5) {
|
|
66
68
|
return this.git(['log', '--oneline', `-${count}`]);
|
|
67
69
|
}
|
|
68
|
-
async currentBranch() {
|
|
69
|
-
const branch = await this.git(['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
70
|
-
return branch.trim();
|
|
71
|
-
}
|
|
72
|
-
async recentCommits(count = 5) {
|
|
73
|
-
return this.git(['log', `--oneline`, `-${count}`]);
|
|
74
|
-
}
|
|
75
70
|
async git(args) {
|
|
76
71
|
const { stdout } = await execFileAsync('git', args, { cwd: this.cwd });
|
|
77
72
|
return stdout;
|
|
@@ -1,38 +1,10 @@
|
|
|
1
|
-
import type { ClaudeSessionEvent,
|
|
2
|
-
import type { ClaudeSessionEventHandler } from '../claude/claude-agent-sdk-adapter.js';
|
|
3
|
-
import type { FileRunStateStore } from '../state/file-run-state-store.js';
|
|
1
|
+
import type { ClaudeSessionEvent, GitDiffResult, GitOperationResult } from '../types/contracts.js';
|
|
4
2
|
import type { TranscriptStore } from '../state/transcript-store.js';
|
|
5
|
-
import type { SessionController } from './session-controller.js';
|
|
6
3
|
import type { GitOperations } from './git-operations.js';
|
|
7
|
-
import type { ContextTracker } from './context-tracker.js';
|
|
8
|
-
type PersistentManagerProgressHandler = (run: PersistentRunRecord) => void | Promise<void>;
|
|
9
4
|
export declare class PersistentManager {
|
|
10
|
-
private readonly sessionController;
|
|
11
5
|
private readonly gitOps;
|
|
12
|
-
private readonly stateStore;
|
|
13
|
-
private readonly contextTracker;
|
|
14
6
|
private readonly transcriptStore;
|
|
15
|
-
constructor(
|
|
16
|
-
/**
|
|
17
|
-
* Send a message to the persistent Claude Code session.
|
|
18
|
-
* Creates a new session if none exists.
|
|
19
|
-
*/
|
|
20
|
-
sendMessage(cwd: string, message: string, options?: {
|
|
21
|
-
model?: string;
|
|
22
|
-
effort?: 'low' | 'medium' | 'high' | 'max';
|
|
23
|
-
mode?: 'plan' | 'free';
|
|
24
|
-
sessionSystemPrompt?: string;
|
|
25
|
-
abortSignal?: AbortSignal;
|
|
26
|
-
}, onEvent?: ClaudeSessionEventHandler): Promise<{
|
|
27
|
-
sessionId: string | undefined;
|
|
28
|
-
finalText: string;
|
|
29
|
-
turns?: number;
|
|
30
|
-
totalCostUsd?: number;
|
|
31
|
-
inputTokens?: number;
|
|
32
|
-
outputTokens?: number;
|
|
33
|
-
contextWindowSize?: number;
|
|
34
|
-
context: SessionContextSnapshot;
|
|
35
|
-
}>;
|
|
7
|
+
constructor(gitOps: GitOperations, transcriptStore: TranscriptStore);
|
|
36
8
|
/**
|
|
37
9
|
* Get the current git diff.
|
|
38
10
|
*/
|
|
@@ -44,7 +16,7 @@ export declare class PersistentManager {
|
|
|
44
16
|
/**
|
|
45
17
|
* Commit all current changes.
|
|
46
18
|
*/
|
|
47
|
-
gitCommit(message: string): Promise<GitOperationResult>;
|
|
19
|
+
gitCommit(message: string, paths?: string[]): Promise<GitOperationResult>;
|
|
48
20
|
/**
|
|
49
21
|
* Get git status summary.
|
|
50
22
|
*/
|
|
@@ -60,30 +32,8 @@ export declare class PersistentManager {
|
|
|
60
32
|
* Hard reset to discard all uncommitted changes.
|
|
61
33
|
*/
|
|
62
34
|
gitReset(): Promise<GitOperationResult>;
|
|
63
|
-
/**
|
|
64
|
-
* Get current session status and context health.
|
|
65
|
-
*/
|
|
66
|
-
getStatus(): SessionContextSnapshot;
|
|
67
|
-
/**
|
|
68
|
-
* Clear the active session. Next send creates a fresh one.
|
|
69
|
-
*/
|
|
70
|
-
clearSession(): Promise<string | null>;
|
|
71
|
-
/**
|
|
72
|
-
* Compact the current session to free context.
|
|
73
|
-
*/
|
|
74
|
-
compactSession(cwd: string, onEvent?: ClaudeSessionEventHandler): Promise<ClaudeSessionRunResult>;
|
|
75
35
|
/**
|
|
76
36
|
* Read persisted transcript events for a session.
|
|
77
37
|
*/
|
|
78
38
|
getTranscriptEvents(cwd: string, sessionId: string): Promise<ClaudeSessionEvent[]>;
|
|
79
|
-
/**
|
|
80
|
-
* Execute a full task with run tracking.
|
|
81
|
-
* Creates a run record, sends the message, and persists the result.
|
|
82
|
-
*/
|
|
83
|
-
executeTask(cwd: string, task: string, options?: {
|
|
84
|
-
model?: string;
|
|
85
|
-
}, onProgress?: PersistentManagerProgressHandler): Promise<PersistentRunResult>;
|
|
86
|
-
listRuns(cwd: string): Promise<PersistentRunRecord[]>;
|
|
87
|
-
getRun(cwd: string, runId: string): Promise<PersistentRunRecord | null>;
|
|
88
39
|
}
|
|
89
|
-
export {};
|
|
@@ -1,37 +1,10 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
1
|
export class PersistentManager {
|
|
3
|
-
sessionController;
|
|
4
2
|
gitOps;
|
|
5
|
-
stateStore;
|
|
6
|
-
contextTracker;
|
|
7
3
|
transcriptStore;
|
|
8
|
-
constructor(
|
|
9
|
-
this.sessionController = sessionController;
|
|
4
|
+
constructor(gitOps, transcriptStore) {
|
|
10
5
|
this.gitOps = gitOps;
|
|
11
|
-
this.stateStore = stateStore;
|
|
12
|
-
this.contextTracker = contextTracker;
|
|
13
6
|
this.transcriptStore = transcriptStore;
|
|
14
7
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Send a message to the persistent Claude Code session.
|
|
17
|
-
* Creates a new session if none exists.
|
|
18
|
-
*/
|
|
19
|
-
async sendMessage(cwd, message, options, onEvent) {
|
|
20
|
-
const result = await this.sessionController.sendMessage(message, options, onEvent);
|
|
21
|
-
if (result.sessionId && result.events.length > 0) {
|
|
22
|
-
await this.transcriptStore.appendEvents(cwd, result.sessionId, result.events);
|
|
23
|
-
}
|
|
24
|
-
return {
|
|
25
|
-
sessionId: result.sessionId,
|
|
26
|
-
finalText: result.finalText,
|
|
27
|
-
turns: result.turns,
|
|
28
|
-
totalCostUsd: result.totalCostUsd,
|
|
29
|
-
inputTokens: result.inputTokens,
|
|
30
|
-
outputTokens: result.outputTokens,
|
|
31
|
-
contextWindowSize: result.contextWindowSize,
|
|
32
|
-
context: this.sessionController.getContextSnapshot(),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
8
|
/**
|
|
36
9
|
* Get the current git diff.
|
|
37
10
|
*/
|
|
@@ -41,8 +14,8 @@ export class PersistentManager {
|
|
|
41
14
|
/**
|
|
42
15
|
* Commit all current changes.
|
|
43
16
|
*/
|
|
44
|
-
async gitCommit(message) {
|
|
45
|
-
return this.gitOps.commit(message);
|
|
17
|
+
async gitCommit(message, paths) {
|
|
18
|
+
return this.gitOps.commit(message, paths);
|
|
46
19
|
}
|
|
47
20
|
/**
|
|
48
21
|
* Get git status summary.
|
|
@@ -62,115 +35,10 @@ export class PersistentManager {
|
|
|
62
35
|
async gitReset() {
|
|
63
36
|
return this.gitOps.resetHard();
|
|
64
37
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Get current session status and context health.
|
|
67
|
-
*/
|
|
68
|
-
getStatus() {
|
|
69
|
-
return this.sessionController.getContextSnapshot();
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Clear the active session. Next send creates a fresh one.
|
|
73
|
-
*/
|
|
74
|
-
async clearSession() {
|
|
75
|
-
return this.sessionController.clearSession();
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Compact the current session to free context.
|
|
79
|
-
*/
|
|
80
|
-
async compactSession(cwd, onEvent) {
|
|
81
|
-
const result = await this.sessionController.compactSession(onEvent);
|
|
82
|
-
if (result.sessionId && result.events.length > 0) {
|
|
83
|
-
await this.transcriptStore.appendEvents(cwd, result.sessionId, result.events);
|
|
84
|
-
}
|
|
85
|
-
return result;
|
|
86
|
-
}
|
|
87
38
|
/**
|
|
88
39
|
* Read persisted transcript events for a session.
|
|
89
40
|
*/
|
|
90
41
|
getTranscriptEvents(cwd, sessionId) {
|
|
91
42
|
return this.transcriptStore.readEvents(cwd, sessionId);
|
|
92
43
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Execute a full task with run tracking.
|
|
95
|
-
* Creates a run record, sends the message, and persists the result.
|
|
96
|
-
*/
|
|
97
|
-
async executeTask(cwd, task, options, onProgress) {
|
|
98
|
-
const runId = randomUUID();
|
|
99
|
-
const createdAt = new Date().toISOString();
|
|
100
|
-
const runRecord = {
|
|
101
|
-
id: runId,
|
|
102
|
-
cwd,
|
|
103
|
-
task,
|
|
104
|
-
status: 'running',
|
|
105
|
-
createdAt,
|
|
106
|
-
updatedAt: createdAt,
|
|
107
|
-
sessionId: this.sessionController.sessionId,
|
|
108
|
-
sessionHistory: [],
|
|
109
|
-
messages: [
|
|
110
|
-
{
|
|
111
|
-
timestamp: createdAt,
|
|
112
|
-
direction: 'sent',
|
|
113
|
-
text: task,
|
|
114
|
-
},
|
|
115
|
-
],
|
|
116
|
-
actions: [],
|
|
117
|
-
commits: [],
|
|
118
|
-
context: this.sessionController.getContextSnapshot(),
|
|
119
|
-
};
|
|
120
|
-
await this.stateStore.saveRun(runRecord);
|
|
121
|
-
await onProgress?.(runRecord);
|
|
122
|
-
try {
|
|
123
|
-
const result = await this.sessionController.sendMessage(task, options, async (event) => {
|
|
124
|
-
// Update run record with progress events
|
|
125
|
-
const currentRun = await this.stateStore.getRun(cwd, runId);
|
|
126
|
-
if (currentRun) {
|
|
127
|
-
const updated = {
|
|
128
|
-
...currentRun,
|
|
129
|
-
updatedAt: new Date().toISOString(),
|
|
130
|
-
sessionId: event.sessionId ?? currentRun.sessionId,
|
|
131
|
-
context: this.sessionController.getContextSnapshot(),
|
|
132
|
-
};
|
|
133
|
-
await this.stateStore.saveRun(updated);
|
|
134
|
-
await onProgress?.(updated);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
const completedRun = await this.stateStore.updateRun(cwd, runId, (run) => ({
|
|
138
|
-
...run,
|
|
139
|
-
status: 'completed',
|
|
140
|
-
updatedAt: new Date().toISOString(),
|
|
141
|
-
sessionId: result.sessionId ?? run.sessionId,
|
|
142
|
-
messages: [
|
|
143
|
-
...run.messages,
|
|
144
|
-
{
|
|
145
|
-
timestamp: new Date().toISOString(),
|
|
146
|
-
direction: 'received',
|
|
147
|
-
text: result.finalText,
|
|
148
|
-
turns: result.turns,
|
|
149
|
-
totalCostUsd: result.totalCostUsd,
|
|
150
|
-
inputTokens: result.inputTokens,
|
|
151
|
-
outputTokens: result.outputTokens,
|
|
152
|
-
},
|
|
153
|
-
],
|
|
154
|
-
context: this.sessionController.getContextSnapshot(),
|
|
155
|
-
finalSummary: result.finalText,
|
|
156
|
-
}));
|
|
157
|
-
return { run: completedRun };
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
const failedRun = await this.stateStore.updateRun(cwd, runId, (run) => ({
|
|
161
|
-
...run,
|
|
162
|
-
status: 'failed',
|
|
163
|
-
updatedAt: new Date().toISOString(),
|
|
164
|
-
context: this.sessionController.getContextSnapshot(),
|
|
165
|
-
finalSummary: error instanceof Error ? error.message : String(error),
|
|
166
|
-
}));
|
|
167
|
-
return { run: failedRun };
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
listRuns(cwd) {
|
|
171
|
-
return this.stateStore.listRuns(cwd);
|
|
172
|
-
}
|
|
173
|
-
getRun(cwd, runId) {
|
|
174
|
-
return this.stateStore.getRun(cwd, runId);
|
|
175
|
-
}
|
|
176
44
|
}
|