skimpyclaw 0.3.14 → 0.4.0
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 +47 -37
- package/dist/__tests__/adapter-types.test.d.ts +4 -0
- package/dist/__tests__/adapter-types.test.js +63 -0
- package/dist/__tests__/anthropic-adapter.test.d.ts +4 -0
- package/dist/__tests__/anthropic-adapter.test.js +264 -0
- package/dist/__tests__/api.test.js +0 -1
- package/dist/__tests__/cli.integration.test.js +2 -4
- package/dist/__tests__/cli.test.js +0 -1
- package/dist/__tests__/code-agents-notifications.test.js +137 -0
- package/dist/__tests__/code-agents-parser.test.js +19 -1
- package/dist/__tests__/code-agents-preflight.test.js +3 -28
- package/dist/__tests__/code-agents-utils.test.js +34 -9
- package/dist/__tests__/code-agents-worktrees.test.js +116 -0
- package/dist/__tests__/codex-adapter.test.js +184 -0
- package/dist/__tests__/codex-auth.test.js +66 -0
- package/dist/__tests__/codex-provider-gating.test.js +35 -0
- package/dist/__tests__/codex-unified-loop.test.js +111 -0
- package/dist/__tests__/config-security.test.js +127 -0
- package/dist/__tests__/config.test.js +23 -0
- package/dist/__tests__/context-manager.test.js +243 -164
- package/dist/__tests__/cron-run.test.js +250 -0
- package/dist/__tests__/cron.test.js +12 -38
- package/dist/__tests__/digests.test.js +67 -0
- package/dist/__tests__/discord-attachments.test.js +211 -0
- package/dist/__tests__/discord-docs.test.d.ts +1 -0
- package/dist/__tests__/discord-docs.test.js +27 -0
- package/dist/__tests__/discord-thread-agents.test.d.ts +1 -0
- package/dist/__tests__/discord-thread-agents.test.js +115 -0
- package/dist/__tests__/discord-thread-context.test.d.ts +1 -0
- package/dist/__tests__/discord-thread-context.test.js +42 -0
- package/dist/__tests__/doctor.formatters.test.js +4 -4
- package/dist/__tests__/doctor.index.test.js +1 -1
- package/dist/__tests__/doctor.runner.test.js +3 -15
- package/dist/__tests__/env-sanitizer.test.d.ts +1 -0
- package/dist/__tests__/env-sanitizer.test.js +45 -0
- package/dist/__tests__/exec-approval.test.js +61 -0
- package/dist/__tests__/fetch-tool.test.d.ts +1 -0
- package/dist/__tests__/fetch-tool.test.js +85 -0
- package/dist/__tests__/gateway-status-auth.test.d.ts +1 -0
- package/dist/__tests__/gateway-status-auth.test.js +72 -0
- package/dist/__tests__/heartbeat.test.js +3 -3
- package/dist/__tests__/interactive-sessions.test.d.ts +1 -0
- package/dist/__tests__/interactive-sessions.test.js +96 -0
- package/dist/__tests__/langfuse.test.js +6 -18
- package/dist/__tests__/model-selection.test.js +3 -4
- package/dist/__tests__/providers-init.test.js +2 -8
- package/dist/__tests__/providers-routing.test.js +1 -1
- package/dist/__tests__/providers-utils.test.js +13 -3
- package/dist/__tests__/sessions.test.js +14 -10
- package/dist/__tests__/setup.test.js +12 -29
- package/dist/__tests__/skills.test.js +10 -7
- package/dist/__tests__/stream-formatter.test.d.ts +1 -0
- package/dist/__tests__/stream-formatter.test.js +114 -0
- package/dist/__tests__/token-efficiency.test.js +131 -15
- package/dist/__tests__/tool-loop.test.d.ts +4 -0
- package/dist/__tests__/tool-loop.test.js +505 -0
- package/dist/__tests__/tools.test.js +101 -276
- package/dist/__tests__/utils.test.d.ts +1 -0
- package/dist/__tests__/utils.test.js +14 -0
- package/dist/__tests__/voice.test.js +21 -0
- package/dist/agent.js +35 -4
- package/dist/api.js +113 -37
- package/dist/channels/discord/attachments.d.ts +50 -0
- package/dist/channels/discord/attachments.js +137 -0
- package/dist/channels/discord/delegation.d.ts +5 -0
- package/dist/channels/discord/delegation.js +136 -0
- package/dist/channels/discord/handlers.js +694 -7
- package/dist/channels/discord/index.d.ts +16 -1
- package/dist/channels/discord/index.js +64 -1
- package/dist/channels/discord/thread-agents.d.ts +54 -0
- package/dist/channels/discord/thread-agents.js +323 -0
- package/dist/channels/discord/threads.d.ts +58 -0
- package/dist/channels/discord/threads.js +192 -0
- package/dist/channels/discord/types.js +4 -2
- package/dist/channels/discord/utils.d.ts +16 -0
- package/dist/channels/discord/utils.js +86 -6
- package/dist/channels/telegram/index.d.ts +1 -1
- package/dist/channels/telegram/types.js +1 -1
- package/dist/channels/telegram/utils.js +9 -3
- package/dist/channels.d.ts +1 -1
- package/dist/cli.js +20 -400
- package/dist/code-agents/executor.d.ts +1 -1
- package/dist/code-agents/executor.js +101 -45
- package/dist/code-agents/index.d.ts +2 -7
- package/dist/code-agents/index.js +111 -80
- package/dist/code-agents/interactive-resume.d.ts +6 -0
- package/dist/code-agents/interactive-resume.js +98 -0
- package/dist/code-agents/interactive-sessions.d.ts +20 -0
- package/dist/code-agents/interactive-sessions.js +132 -0
- package/dist/code-agents/parser.js +5 -1
- package/dist/code-agents/registry.d.ts +7 -1
- package/dist/code-agents/registry.js +11 -23
- package/dist/code-agents/stream-formatter.d.ts +8 -0
- package/dist/code-agents/stream-formatter.js +92 -0
- package/dist/code-agents/types.d.ts +16 -24
- package/dist/code-agents/utils.d.ts +35 -11
- package/dist/code-agents/utils.js +349 -95
- package/dist/code-agents/worktrees.d.ts +37 -0
- package/dist/code-agents/worktrees.js +116 -0
- package/dist/config.d.ts +2 -4
- package/dist/config.js +123 -23
- package/dist/cron.d.ts +1 -6
- package/dist/cron.js +175 -82
- package/dist/dashboard/assets/index-B345aOO-.js +65 -0
- package/dist/dashboard/assets/index-ZWK4dalJ.css +1 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/digests.d.ts +1 -0
- package/dist/digests.js +132 -42
- package/dist/doctor/checks.d.ts +0 -3
- package/dist/doctor/checks.js +1 -108
- package/dist/doctor/runner.js +1 -4
- package/dist/env-sanitizer.d.ts +2 -0
- package/dist/env-sanitizer.js +61 -0
- package/dist/exec-approval.d.ts +11 -1
- package/dist/exec-approval.js +17 -4
- package/dist/gateway.d.ts +3 -1
- package/dist/gateway.js +17 -7
- package/dist/heartbeat.js +1 -6
- package/dist/langfuse.js +3 -29
- package/dist/model-selection.js +3 -1
- package/dist/providers/adapter.d.ts +118 -0
- package/dist/providers/adapter.js +6 -0
- package/dist/providers/adapters/anthropic-adapter.d.ts +22 -0
- package/dist/providers/adapters/anthropic-adapter.js +204 -0
- package/dist/providers/adapters/codex-adapter.d.ts +26 -0
- package/dist/providers/adapters/codex-adapter.js +203 -0
- package/dist/providers/anthropic.d.ts +1 -0
- package/dist/providers/anthropic.js +10 -272
- package/dist/providers/codex.d.ts +21 -0
- package/dist/providers/codex.js +149 -330
- package/dist/providers/content.d.ts +1 -1
- package/dist/providers/content.js +2 -2
- package/dist/providers/context-manager.d.ts +18 -6
- package/dist/providers/context-manager.js +199 -223
- package/dist/providers/index.d.ts +9 -1
- package/dist/providers/index.js +73 -64
- package/dist/providers/loop-utils.d.ts +20 -0
- package/dist/providers/loop-utils.js +30 -0
- package/dist/providers/tool-loop.d.ts +12 -0
- package/dist/providers/tool-loop.js +251 -0
- package/dist/providers/utils.d.ts +19 -3
- package/dist/providers/utils.js +100 -29
- package/dist/secure-store.d.ts +8 -0
- package/dist/secure-store.js +80 -0
- package/dist/service.js +3 -28
- package/dist/sessions.d.ts +3 -0
- package/dist/sessions.js +147 -18
- package/dist/setup-templates.js +13 -25
- package/dist/setup.d.ts +10 -6
- package/dist/setup.js +84 -292
- package/dist/skills.js +3 -11
- package/dist/tools/agent-delegation.d.ts +19 -0
- package/dist/tools/agent-delegation.js +49 -0
- package/dist/tools/bash-tool.js +89 -34
- package/dist/tools/definitions.d.ts +199 -302
- package/dist/tools/definitions.js +70 -123
- package/dist/tools/execute-context.d.ts +13 -4
- package/dist/tools/fetch-tool.js +109 -13
- package/dist/tools/file-tools.js +7 -1
- package/dist/tools.d.ts +7 -7
- package/dist/tools.js +133 -151
- package/dist/types.d.ts +37 -30
- package/dist/utils.js +4 -6
- package/dist/voice.d.ts +1 -1
- package/dist/voice.js +17 -4
- package/package.json +33 -23
- package/templates/TOOLS.md +0 -27
- package/dist/__tests__/audit.test.js +0 -122
- package/dist/__tests__/code-agents-orchestrator.test.js +0 -216
- package/dist/__tests__/code-agents-sandbox.test.js +0 -163
- package/dist/__tests__/orchestrator.test.js +0 -425
- package/dist/__tests__/sandbox-bridge.test.js +0 -116
- package/dist/__tests__/sandbox-manager.test.js +0 -144
- package/dist/__tests__/sandbox-mount-security.test.js +0 -139
- package/dist/__tests__/sandbox-runtime.test.js +0 -176
- package/dist/__tests__/subagent.test.js +0 -240
- package/dist/__tests__/telegram.test.js +0 -42
- package/dist/code-agents/orchestrator.d.ts +0 -29
- package/dist/code-agents/orchestrator.js +0 -694
- package/dist/code-agents/worktree.d.ts +0 -40
- package/dist/code-agents/worktree.js +0 -215
- package/dist/dashboard/assets/index-BoTHPby4.js +0 -65
- package/dist/dashboard/assets/index-D4mufvBg.css +0 -1
- package/dist/dashboard.d.ts +0 -8
- package/dist/dashboard.js +0 -4071
- package/dist/discord.d.ts +0 -8
- package/dist/discord.js +0 -792
- package/dist/mcp-context-a8c.d.ts +0 -13
- package/dist/mcp-context-a8c.js +0 -34
- package/dist/orchestrator.d.ts +0 -15
- package/dist/orchestrator.js +0 -676
- package/dist/providers/openai.d.ts +0 -10
- package/dist/providers/openai.js +0 -355
- package/dist/sandbox/bridge.d.ts +0 -5
- package/dist/sandbox/bridge.js +0 -63
- package/dist/sandbox/index.d.ts +0 -5
- package/dist/sandbox/index.js +0 -4
- package/dist/sandbox/manager.d.ts +0 -7
- package/dist/sandbox/manager.js +0 -100
- package/dist/sandbox/mount-security.d.ts +0 -12
- package/dist/sandbox/mount-security.js +0 -122
- package/dist/sandbox/runtime.d.ts +0 -39
- package/dist/sandbox/runtime.js +0 -192
- package/dist/sandbox-utils.d.ts +0 -6
- package/dist/sandbox-utils.js +0 -36
- package/dist/subagent.d.ts +0 -19
- package/dist/subagent.js +0 -407
- package/dist/telegram.d.ts +0 -2
- package/dist/telegram.js +0 -11
- package/dist/tools/browser-tool.d.ts +0 -3
- package/dist/tools/browser-tool.js +0 -266
- package/sandbox/Dockerfile +0 -40
- /package/dist/__tests__/{audit.test.d.ts → code-agents-notifications.test.d.ts} +0 -0
- /package/dist/__tests__/{code-agents-orchestrator.test.d.ts → code-agents-worktrees.test.d.ts} +0 -0
- /package/dist/__tests__/{code-agents-sandbox.test.d.ts → codex-adapter.test.d.ts} +0 -0
- /package/dist/__tests__/{orchestrator.test.d.ts → codex-auth.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-bridge.test.d.ts → codex-provider-gating.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-manager.test.d.ts → codex-unified-loop.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-mount-security.test.d.ts → config-security.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-runtime.test.d.ts → cron-run.test.d.ts} +0 -0
- /package/dist/__tests__/{subagent.test.d.ts → digests.test.d.ts} +0 -0
- /package/dist/__tests__/{telegram.test.d.ts → discord-attachments.test.d.ts} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Code Agent Utilities
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
3
4
|
import { resolve, join } from 'path';
|
|
4
5
|
import { homedir } from 'os';
|
|
5
6
|
import { buildValidationCommand } from './executor.js';
|
|
@@ -21,9 +22,25 @@ function isCommandAvailable(name) {
|
|
|
21
22
|
return false;
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
|
-
const CLAUDE_CLI_PATH = resolveCliPath('claude');
|
|
25
|
+
export const CLAUDE_CLI_PATH = resolveCliPath('claude');
|
|
25
26
|
const CODEX_CLI_PATH = resolveCliPath('codex');
|
|
26
|
-
const
|
|
27
|
+
const DEFAULT_CODEX_HOME = join(homedir(), '.codex');
|
|
28
|
+
/**
|
|
29
|
+
* Prepare env for spawning a coding-agent CLI (claude/codex):
|
|
30
|
+
* - Drops CLAUDECODE so nested `claude` invocations start cleanly.
|
|
31
|
+
* - Drops GH_TOKEN/GITHUB_TOKEN so `gh` falls back to keychain auth instead
|
|
32
|
+
* of a stale process token.
|
|
33
|
+
* - Pins Codex to the standard CLI state directory so auth/config come from ~/.codex.
|
|
34
|
+
*/
|
|
35
|
+
export function buildCodeAgentSpawnEnv(base = process.env) {
|
|
36
|
+
const env = { ...base };
|
|
37
|
+
env.HOME ||= homedir();
|
|
38
|
+
env.CODEX_HOME = DEFAULT_CODEX_HOME;
|
|
39
|
+
delete env.CLAUDECODE;
|
|
40
|
+
delete env.GH_TOKEN;
|
|
41
|
+
delete env.GITHUB_TOKEN;
|
|
42
|
+
return env;
|
|
43
|
+
}
|
|
27
44
|
/** Return supported coding CLIs currently available on PATH. */
|
|
28
45
|
export function getAvailableCodingCliTools(commandChecker = isCommandAvailable) {
|
|
29
46
|
const available = [];
|
|
@@ -31,19 +48,17 @@ export function getAvailableCodingCliTools(commandChecker = isCommandAvailable)
|
|
|
31
48
|
available.push('codex');
|
|
32
49
|
if (commandChecker('claude') || commandChecker('claude-code'))
|
|
33
50
|
available.push('claude');
|
|
34
|
-
if (commandChecker('kimi'))
|
|
35
|
-
available.push('kimi');
|
|
36
51
|
return available;
|
|
37
52
|
}
|
|
38
53
|
/** Return preflight error when no supported coding CLI is installed. */
|
|
39
54
|
export function getCodingCliPreflightError(commandChecker = isCommandAvailable) {
|
|
40
55
|
if (getAvailableCodingCliTools(commandChecker).length > 0)
|
|
41
56
|
return null;
|
|
42
|
-
return 'Error: No supported coding CLI found on PATH. Install Codex CLI (`codex`)
|
|
57
|
+
return 'Error: No supported coding CLI found on PATH. Install Codex CLI (`codex`) or Claude Code CLI (`claude` or `claude-code`).';
|
|
43
58
|
}
|
|
44
59
|
/**
|
|
45
60
|
* Normalize legacy/default agent values to supported CLI agent IDs.
|
|
46
|
-
* Accepts strict IDs and older alias-like values (e.g. "claude-
|
|
61
|
+
* Accepts strict IDs and older alias-like values (e.g. "claude-coder").
|
|
47
62
|
*/
|
|
48
63
|
export function normalizeCodeAgent(agent) {
|
|
49
64
|
if (!agent)
|
|
@@ -53,16 +68,25 @@ export function normalizeCodeAgent(agent) {
|
|
|
53
68
|
return 'claude';
|
|
54
69
|
if (value === 'codex' || value.startsWith('codex'))
|
|
55
70
|
return 'codex';
|
|
56
|
-
if (value === 'kimi' || value.startsWith('kimi'))
|
|
57
|
-
return 'kimi';
|
|
58
71
|
return null;
|
|
59
72
|
}
|
|
60
73
|
/**
|
|
61
74
|
* Resolve requested/default agent selection to a supported CLI agent ID.
|
|
62
75
|
* Preference order: explicit request -> configured default -> "claude".
|
|
63
|
-
* If no agent is explicit and the model is a GPT/
|
|
64
|
-
* If the model is a kimi model, auto-select kimi.
|
|
76
|
+
* If no agent is explicit and the model is a GPT/Codex model, auto-select codex.
|
|
65
77
|
*/
|
|
78
|
+
/**
|
|
79
|
+
* Check if a model string is compatible with a given agent CLI.
|
|
80
|
+
* e.g. gpt-5.3-codex is NOT compatible with 'claude', claude-opus IS.
|
|
81
|
+
*/
|
|
82
|
+
export function isModelCompatibleWithAgent(model, agent) {
|
|
83
|
+
const m = model.toLowerCase();
|
|
84
|
+
if (agent === 'codex') {
|
|
85
|
+
return /^(gpt|codex|o[134]|openai\/)/i.test(m);
|
|
86
|
+
}
|
|
87
|
+
// claude: compatible if NOT a known non-Claude model
|
|
88
|
+
return !/^(gpt|codex|o[134]|openai\/)/i.test(m);
|
|
89
|
+
}
|
|
66
90
|
export function resolveSelectedCodeAgent(requestedAgent, defaultAgent, model) {
|
|
67
91
|
// If no explicit agent was requested, infer from model
|
|
68
92
|
if (!requestedAgent && model) {
|
|
@@ -70,8 +94,6 @@ export function resolveSelectedCodeAgent(requestedAgent, defaultAgent, model) {
|
|
|
70
94
|
if (m.includes('gpt') || m.includes('codex') || m.startsWith('openai/') || m.startsWith('o1') || m.startsWith('o3') || m.startsWith('o4')) {
|
|
71
95
|
return 'codex';
|
|
72
96
|
}
|
|
73
|
-
if (m.includes('kimi'))
|
|
74
|
-
return 'kimi';
|
|
75
97
|
}
|
|
76
98
|
const candidate = requestedAgent || defaultAgent || 'claude';
|
|
77
99
|
return normalizeCodeAgent(candidate);
|
|
@@ -85,6 +107,41 @@ export function setCodeAgentConfig(config) {
|
|
|
85
107
|
export function getCodeAgentConfig() {
|
|
86
108
|
return _codeAgentConfig;
|
|
87
109
|
}
|
|
110
|
+
function stripProviderPrefix(model) {
|
|
111
|
+
return model.includes('/') ? model.split('/').slice(1).join('/') : model;
|
|
112
|
+
}
|
|
113
|
+
/** Read Claude Code's configured default model, if present. */
|
|
114
|
+
export function readClaudeCodeDefaultModel(settingsPath = join(homedir(), '.claude', 'settings.json')) {
|
|
115
|
+
try {
|
|
116
|
+
if (!existsSync(settingsPath))
|
|
117
|
+
return undefined;
|
|
118
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
119
|
+
const model = settings?.model;
|
|
120
|
+
return typeof model === 'string' && model.trim() ? model.trim() : undefined;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Resolve the model text shown in dashboards/reports without changing CLI args. */
|
|
127
|
+
export function resolveCodeAgentModelLabel(agent, model) {
|
|
128
|
+
if (model)
|
|
129
|
+
return stripProviderPrefix(model);
|
|
130
|
+
const normalizedAgent = normalizeCodeAgent(agent);
|
|
131
|
+
if (normalizedAgent === 'claude') {
|
|
132
|
+
const defaultModel = readClaudeCodeDefaultModel();
|
|
133
|
+
return defaultModel ? `${defaultModel} default` : 'claude default';
|
|
134
|
+
}
|
|
135
|
+
if (normalizedAgent === 'codex')
|
|
136
|
+
return 'codex default';
|
|
137
|
+
return `${agent || 'agent'} default`;
|
|
138
|
+
}
|
|
139
|
+
export function withCodeAgentModelLabel(task) {
|
|
140
|
+
return {
|
|
141
|
+
...task,
|
|
142
|
+
modelLabel: task.modelLabel || resolveCodeAgentModelLabel(task.agent, task.model),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
88
145
|
/** Build CLI args for code_with_agent. Exported for testing. */
|
|
89
146
|
export function buildCodeAgentArgs(input) {
|
|
90
147
|
const agent = input.agent || 'claude';
|
|
@@ -95,56 +152,38 @@ export function buildCodeAgentArgs(input) {
|
|
|
95
152
|
'--full-auto',
|
|
96
153
|
'--json',
|
|
97
154
|
'--color', 'never',
|
|
155
|
+
'--skip-git-repo-check',
|
|
98
156
|
];
|
|
99
157
|
if (input.workdir)
|
|
100
158
|
args.push('-C', input.workdir);
|
|
101
159
|
if (input.model)
|
|
102
160
|
args.push('-m', input.model);
|
|
161
|
+
if (input.effort)
|
|
162
|
+
args.push('-c', `model_reasoning_effort=${input.effort}`);
|
|
103
163
|
args.push(input.task);
|
|
104
164
|
return { cmd: CODEX_CLI_PATH, args };
|
|
105
165
|
}
|
|
106
|
-
if (agent === 'kimi') {
|
|
107
|
-
const args = [
|
|
108
|
-
'--yolo',
|
|
109
|
-
'-p', input.task,
|
|
110
|
-
];
|
|
111
|
-
if (input.workdir)
|
|
112
|
-
args.push('-w', input.workdir);
|
|
113
|
-
if (input.model)
|
|
114
|
-
args.push('-m', input.model);
|
|
115
|
-
return { cmd: KIMI_CLI_PATH, args };
|
|
116
|
-
}
|
|
117
166
|
// Default: claude
|
|
118
167
|
// Each --allowedTools flag takes one tool name — repeat the flag per tool
|
|
119
168
|
// --allowedTools restricts which tools are available (not just permissions).
|
|
120
|
-
|
|
121
|
-
const allowedTools = ['Edit', 'Read', 'Write', 'Bash', 'Glob', 'Grep', 'mcp__playwright__*'];
|
|
169
|
+
const allowedTools = ['Edit', 'Read', 'Write', 'Bash', 'Glob', 'Grep'];
|
|
122
170
|
const toolArgs = allowedTools.flatMap(t => ['--allowedTools', t]);
|
|
123
|
-
// Pass Playwright MCP server so coding agents share SkimpyClaw's browser profile
|
|
124
|
-
// Must use chromium (not chrome) to match SkimpyClaw's browser-tool.ts persistent context
|
|
125
|
-
const playwrightMcp = JSON.stringify({
|
|
126
|
-
mcpServers: {
|
|
127
|
-
playwright: {
|
|
128
|
-
command: 'npx',
|
|
129
|
-
args: ['-y', '@playwright/mcp@latest', '--browser', 'chromium',
|
|
130
|
-
'--user-data-dir', join(homedir(), '.skimpyclaw', 'browser-profile'),
|
|
131
|
-
'--caps', 'vision'],
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
171
|
const args = [
|
|
136
172
|
'-p',
|
|
137
173
|
'--verbose',
|
|
138
174
|
'--output-format', 'stream-json',
|
|
139
175
|
'--dangerously-skip-permissions',
|
|
140
|
-
'--mcp-config', playwrightMcp,
|
|
141
176
|
...toolArgs,
|
|
142
177
|
'--max-turns', maxTurns,
|
|
143
178
|
'--append-system-prompt', `Output text only. Never use say or TTS. Focus on the coding task. Run ${buildValidationCommand(input.workdir || process.cwd())} to verify changes.`,
|
|
144
179
|
];
|
|
180
|
+
// Interactive mode: pin this turn to a known session UUID so follow-ups can --resume it.
|
|
181
|
+
if (input.sessionId) {
|
|
182
|
+
args.push('--session-id', input.sessionId);
|
|
183
|
+
}
|
|
145
184
|
// Only pass model to Claude CLI if it's not a known non-Claude model.
|
|
146
|
-
// GPT/Codex/
|
|
147
|
-
if (input.model && !/^(gpt|codex|
|
|
185
|
+
// GPT/Codex/o-series models would be rejected by the Claude CLI.
|
|
186
|
+
if (input.model && !/^(gpt|codex|o[134]|openai\/)/i.test(input.model)) {
|
|
148
187
|
args.push('--model', input.model);
|
|
149
188
|
}
|
|
150
189
|
args.push(input.task);
|
|
@@ -156,42 +195,6 @@ export function formatDuration(seconds) {
|
|
|
156
195
|
return '?';
|
|
157
196
|
return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
158
197
|
}
|
|
159
|
-
/** Build notification for a team-coordinator task with child results. */
|
|
160
|
-
export function buildTeamNotification(task, getChildTask) {
|
|
161
|
-
const dur = formatDuration(task.durationSeconds);
|
|
162
|
-
const taskPreview = task.task.length > 100 ? task.task.slice(0, 100) + '...' : task.task;
|
|
163
|
-
const statusIcon = task.status === 'completed' ? '✅' : task.status === 'timeout' ? '⏰' : '❌';
|
|
164
|
-
const validation = task.validationPassed ? ' Tests pass.' : '';
|
|
165
|
-
const lines = [];
|
|
166
|
-
lines.push(`${statusIcon} Team ${task.id} ${task.status} (${dur}).${validation}`);
|
|
167
|
-
lines.push(`Task: ${taskPreview}`);
|
|
168
|
-
// Per-child summary
|
|
169
|
-
const childIds = task.childTaskIds || [];
|
|
170
|
-
if (childIds.length > 0) {
|
|
171
|
-
lines.push('');
|
|
172
|
-
for (const childId of childIds) {
|
|
173
|
-
const child = getChildTask(childId);
|
|
174
|
-
if (!child)
|
|
175
|
-
continue;
|
|
176
|
-
const childIcon = child.status === 'completed' ? '✅' : child.status === 'failed' ? '❌' : child.status === 'timeout' ? '⏰' : '❓';
|
|
177
|
-
const childDur = formatDuration(child.durationSeconds);
|
|
178
|
-
const subtask = (child.subtask || child.task || '').slice(0, 80);
|
|
179
|
-
lines.push(` ${childIcon} ${child.id} (${childDur}): ${subtask}`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
// Synthesis result
|
|
183
|
-
if (task.outputPreview) {
|
|
184
|
-
lines.push(`\nResult: ${task.outputPreview}`);
|
|
185
|
-
}
|
|
186
|
-
// Errors
|
|
187
|
-
if (task.error && task.error !== 'Validation failed') {
|
|
188
|
-
lines.push(`\nError: ${task.error}`);
|
|
189
|
-
}
|
|
190
|
-
if (task.validationOutput) {
|
|
191
|
-
lines.push(`\nValidation:\n${task.validationOutput.slice(0, 800)}`);
|
|
192
|
-
}
|
|
193
|
-
return lines.join('\n');
|
|
194
|
-
}
|
|
195
198
|
/** Build notification for a single code agent. */
|
|
196
199
|
export function buildSoloNotification(task) {
|
|
197
200
|
const dur = formatDuration(task.durationSeconds);
|
|
@@ -218,30 +221,282 @@ export function buildSoloNotification(task) {
|
|
|
218
221
|
return message;
|
|
219
222
|
}
|
|
220
223
|
}
|
|
224
|
+
function shorten(value, maxChars) {
|
|
225
|
+
const trimmed = value.trim();
|
|
226
|
+
if (trimmed.length <= maxChars)
|
|
227
|
+
return trimmed;
|
|
228
|
+
return `${trimmed.slice(0, maxChars - 3).trim()}...`;
|
|
229
|
+
}
|
|
230
|
+
function normalizeOutputPaths(value, workdir) {
|
|
231
|
+
if (!value)
|
|
232
|
+
return value;
|
|
233
|
+
const normalizedWorkdir = workdir.replace(/\/+$/, '');
|
|
234
|
+
if (!normalizedWorkdir)
|
|
235
|
+
return value;
|
|
236
|
+
return value
|
|
237
|
+
.split(`${normalizedWorkdir}/`).join('')
|
|
238
|
+
.split(normalizedWorkdir).join('.');
|
|
239
|
+
}
|
|
240
|
+
function extractStreamJsonText(line) {
|
|
241
|
+
let event;
|
|
242
|
+
try {
|
|
243
|
+
event = JSON.parse(line);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
if (!event || typeof event !== 'object' || typeof event.type !== 'string') {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
if ((event.type === 'output_text' || event.output_text) && (event.output_text || event.text)) {
|
|
252
|
+
return String(event.output_text || event.text).trim() || null;
|
|
253
|
+
}
|
|
254
|
+
if (event.type === 'result' && typeof event.result === 'string') {
|
|
255
|
+
return event.result.trim() || null;
|
|
256
|
+
}
|
|
257
|
+
if ((event.type === 'item.completed' || event.type === 'item.delta') && event.item?.type === 'agent_message' && event.item?.text) {
|
|
258
|
+
return String(event.item.text).trim() || null;
|
|
259
|
+
}
|
|
260
|
+
return '';
|
|
261
|
+
}
|
|
262
|
+
function looksLikeRawStreamJson(line) {
|
|
263
|
+
const trimmed = line.trim();
|
|
264
|
+
return trimmed.startsWith('{') && /"type"\s*:|"item"\s*:|"thread_id"\s*:/.test(trimmed);
|
|
265
|
+
}
|
|
266
|
+
function stripStreamJsonNoise(value) {
|
|
267
|
+
const lines = value.split('\n');
|
|
268
|
+
const cleaned = [];
|
|
269
|
+
let sawStreamJson = false;
|
|
270
|
+
for (const line of lines) {
|
|
271
|
+
const extracted = extractStreamJsonText(line);
|
|
272
|
+
if (extracted !== null) {
|
|
273
|
+
sawStreamJson = true;
|
|
274
|
+
if (extracted)
|
|
275
|
+
cleaned.push(extracted);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (looksLikeRawStreamJson(line)) {
|
|
279
|
+
sawStreamJson = true;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
cleaned.push(line);
|
|
283
|
+
}
|
|
284
|
+
const result = cleaned.join('\n').trim();
|
|
285
|
+
return sawStreamJson ? result : value.trim();
|
|
286
|
+
}
|
|
287
|
+
function normalizeHeading(line) {
|
|
288
|
+
const normalized = line
|
|
289
|
+
.trim()
|
|
290
|
+
.replace(/^#{1,6}\s*/, '')
|
|
291
|
+
.replace(/^\*\*/, '')
|
|
292
|
+
.replace(/\*\*$/, '')
|
|
293
|
+
.replace(/:$/, '')
|
|
294
|
+
.trim()
|
|
295
|
+
.toLowerCase();
|
|
296
|
+
const allowed = new Set(['decision', 'summary', 'findings', 'risk checks', 'tests', 'recommendation']);
|
|
297
|
+
return allowed.has(normalized) ? normalized : null;
|
|
298
|
+
}
|
|
299
|
+
function extractReportSections(value) {
|
|
300
|
+
const sections = {};
|
|
301
|
+
let current = 'summary';
|
|
302
|
+
for (const line of value.split('\n')) {
|
|
303
|
+
const heading = normalizeHeading(line);
|
|
304
|
+
if (heading) {
|
|
305
|
+
current = heading;
|
|
306
|
+
sections[current] ||= [];
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
sections[current] ||= [];
|
|
310
|
+
sections[current].push(line);
|
|
311
|
+
}
|
|
312
|
+
return Object.fromEntries(Object.entries(sections)
|
|
313
|
+
.map(([key, lines]) => [key, lines.join('\n').trim()])
|
|
314
|
+
.filter(([, text]) => text));
|
|
315
|
+
}
|
|
316
|
+
function firstMeaningfulLines(value, maxChars) {
|
|
317
|
+
const lines = value
|
|
318
|
+
.split('\n')
|
|
319
|
+
.map(line => line.trim())
|
|
320
|
+
.filter(Boolean)
|
|
321
|
+
.filter(line => !looksLikeRawStreamJson(line))
|
|
322
|
+
.filter(line => !/^i('|’)ll\b/i.test(line))
|
|
323
|
+
.filter(line => !/^i am\b/i.test(line));
|
|
324
|
+
return shorten(lines.join('\n'), maxChars);
|
|
325
|
+
}
|
|
326
|
+
function formatAgentDisplay(task) {
|
|
327
|
+
const agent = task.agent === 'team-coordinator' ? 'TEAM' : task.agent.toUpperCase();
|
|
328
|
+
const model = task.modelLabel || resolveCodeAgentModelLabel(task.agent, task.model);
|
|
329
|
+
const effort = task.effort ? ` · effort ${task.effort}` : '';
|
|
330
|
+
return `${agent} · ${model}${effort}`;
|
|
331
|
+
}
|
|
332
|
+
function buildReportMarkdown(task, result) {
|
|
333
|
+
const validation = task.validationPassed === true
|
|
334
|
+
? 'pass'
|
|
335
|
+
: task.validationPassed === false
|
|
336
|
+
? 'fail'
|
|
337
|
+
: 'not run';
|
|
338
|
+
return [
|
|
339
|
+
`# Code Agent ${task.id}`,
|
|
340
|
+
'',
|
|
341
|
+
`- Status: ${task.status}`,
|
|
342
|
+
`- Agent: ${task.agent}`,
|
|
343
|
+
`- Model: ${task.modelLabel || resolveCodeAgentModelLabel(task.agent, task.model)}`,
|
|
344
|
+
`- Effort: ${task.effort || 'default'}`,
|
|
345
|
+
`- Duration: ${formatDuration(task.durationSeconds)}`,
|
|
346
|
+
`- Validation: ${validation}`,
|
|
347
|
+
`- Workdir: ${task.workdir}`,
|
|
348
|
+
task.sourceWorkdir ? `- Source Workdir: ${task.sourceWorkdir}` : '',
|
|
349
|
+
task.worktreePath ? `- Worktree: ${task.worktreePath}` : '',
|
|
350
|
+
task.worktreeCleanup ? `- Worktree Cleanup: ${task.worktreeCleanup.status}${task.worktreeCleanup.reason ? ` (${task.worktreeCleanup.reason})` : ''}` : '',
|
|
351
|
+
'',
|
|
352
|
+
'## Task',
|
|
353
|
+
'',
|
|
354
|
+
task.task,
|
|
355
|
+
'',
|
|
356
|
+
'## Result',
|
|
357
|
+
'',
|
|
358
|
+
result || task.error || '(No result captured.)',
|
|
359
|
+
task.validationOutput ? `\n## Validation Output\n\n${task.validationOutput}` : '',
|
|
360
|
+
].filter(Boolean).join('\n');
|
|
361
|
+
}
|
|
362
|
+
function buildDiscordResultSummary(task, result) {
|
|
363
|
+
if (!result.trim())
|
|
364
|
+
return task.error ? `Error: ${shorten(task.error, 700)}` : '';
|
|
365
|
+
const sections = extractReportSections(result);
|
|
366
|
+
const lines = [];
|
|
367
|
+
const decision = sections.decision || sections.recommendation;
|
|
368
|
+
if (decision) {
|
|
369
|
+
lines.push(`**Decision**\n${shorten(firstMeaningfulLines(decision, 450), 450)}`);
|
|
370
|
+
}
|
|
371
|
+
if (sections.findings) {
|
|
372
|
+
lines.push(`**Findings**\n${shorten(firstMeaningfulLines(sections.findings, 650), 650)}`);
|
|
373
|
+
}
|
|
374
|
+
if (sections['risk checks']) {
|
|
375
|
+
lines.push(`**Risk Checks**\n${shorten(firstMeaningfulLines(sections['risk checks'], 420), 420)}`);
|
|
376
|
+
}
|
|
377
|
+
if (sections.tests) {
|
|
378
|
+
lines.push(`**Tests**\n${shorten(firstMeaningfulLines(sections.tests, 240), 240)}`);
|
|
379
|
+
}
|
|
380
|
+
if (lines.length > 0)
|
|
381
|
+
return lines.join('\n\n');
|
|
382
|
+
return shorten(firstMeaningfulLines(result, 950), 950);
|
|
383
|
+
}
|
|
384
|
+
export function buildCodeAgentDiscordNotification(task) {
|
|
385
|
+
const dur = formatDuration(task.durationSeconds);
|
|
386
|
+
const icon = task.status === 'completed' ? '✅' : task.status === 'timeout' ? '⏰' : '❌';
|
|
387
|
+
const status = task.status === 'completed'
|
|
388
|
+
? 'completed'
|
|
389
|
+
: task.status === 'timeout'
|
|
390
|
+
? 'timed out'
|
|
391
|
+
: 'failed';
|
|
392
|
+
const validation = task.validationPassed === true
|
|
393
|
+
? 'Build/tests pass'
|
|
394
|
+
: task.validationPassed === false
|
|
395
|
+
? 'Build/tests failed'
|
|
396
|
+
: task.status === 'completed'
|
|
397
|
+
? 'Validation not run'
|
|
398
|
+
: undefined;
|
|
399
|
+
const rawResult = normalizeOutputPaths(stripStreamJsonNoise(task.outputPreview || task.liveOutput || task.validationOutput || task.error || ''), task.workdir);
|
|
400
|
+
const taskPreview = normalizeOutputPaths(shorten(task.task, 350), task.workdir);
|
|
401
|
+
const summary = buildDiscordResultSummary(task, rawResult);
|
|
402
|
+
const contentParts = [
|
|
403
|
+
`${icon} \`${task.id}\` ${status} · ${formatAgentDisplay(task)} · ${dur}`,
|
|
404
|
+
validation ? `**Validation:** ${validation}` : undefined,
|
|
405
|
+
task.retryCount ? `**Retries:** ${task.retryCount}` : undefined,
|
|
406
|
+
`**Task:** ${taskPreview}`,
|
|
407
|
+
summary ? `\n${summary}` : undefined,
|
|
408
|
+
].filter(Boolean);
|
|
409
|
+
const needsAttachment = rawResult.length > 1_200 || task.validationOutput || task.task.length > 350;
|
|
410
|
+
const attachment = needsAttachment
|
|
411
|
+
? {
|
|
412
|
+
name: `${task.id}-report.md`,
|
|
413
|
+
description: `Full report for ${task.id}`,
|
|
414
|
+
content: buildReportMarkdown(task, rawResult),
|
|
415
|
+
}
|
|
416
|
+
: undefined;
|
|
417
|
+
const content = [
|
|
418
|
+
contentParts.join('\n'),
|
|
419
|
+
attachment ? '\nFull report attached.' : undefined,
|
|
420
|
+
].filter(Boolean).join('\n');
|
|
421
|
+
return { content, attachment };
|
|
422
|
+
}
|
|
423
|
+
function resolveDiscordThreadId(task) {
|
|
424
|
+
return task.discordThreadId;
|
|
425
|
+
}
|
|
426
|
+
function resolveDiscordChannelId(task) {
|
|
427
|
+
return task.discordChannelId;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Try to send a notification to a Discord thread associated with this task.
|
|
431
|
+
* Returns true if successfully sent to thread.
|
|
432
|
+
*/
|
|
433
|
+
async function trySendToDiscordThread(task, notification) {
|
|
434
|
+
const threadId = resolveDiscordThreadId(task);
|
|
435
|
+
if (!threadId)
|
|
436
|
+
return false;
|
|
437
|
+
try {
|
|
438
|
+
const { sendToDiscordThread, sendToDiscordThreadWithAttachments } = await import('../channels/discord/index.js');
|
|
439
|
+
const sent = notification.attachment
|
|
440
|
+
? await sendToDiscordThreadWithAttachments(threadId, notification.content, [notification.attachment])
|
|
441
|
+
: await sendToDiscordThread(threadId, notification.content);
|
|
442
|
+
if (sent) {
|
|
443
|
+
console.log(`[code-agent] Notification for ${task.id} sent to thread ${threadId}`);
|
|
444
|
+
}
|
|
445
|
+
return sent;
|
|
446
|
+
}
|
|
447
|
+
catch (err) {
|
|
448
|
+
console.error(`[code-agent] Failed to send to Discord thread for ${task.id}:`, err);
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Try to send a notification to the originating Discord channel for this task.
|
|
454
|
+
* Returns true if successfully sent to channel.
|
|
455
|
+
*/
|
|
456
|
+
async function trySendToDiscordChannel(task, notification) {
|
|
457
|
+
const channelId = resolveDiscordChannelId(task);
|
|
458
|
+
if (!channelId)
|
|
459
|
+
return false;
|
|
460
|
+
try {
|
|
461
|
+
const { sendDiscordProactiveMessage, sendDiscordProactiveMessageWithAttachments } = await import('../channels/discord/index.js');
|
|
462
|
+
if (notification.attachment) {
|
|
463
|
+
await sendDiscordProactiveMessageWithAttachments(channelId, notification.content, [notification.attachment]);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
await sendDiscordProactiveMessage(channelId, notification.content);
|
|
467
|
+
}
|
|
468
|
+
console.log(`[code-agent] Notification for ${task.id} sent to Discord channel ${channelId}`);
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
catch (err) {
|
|
472
|
+
console.error(`[code-agent] Failed to send to Discord channel for ${task.id}:`, err);
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function hasDiscordRouting(task) {
|
|
477
|
+
return !!(resolveDiscordThreadId(task) || resolveDiscordChannelId(task));
|
|
478
|
+
}
|
|
221
479
|
/** Send auto-notification to active channel on completion/failure. */
|
|
222
|
-
export async function notifyCodeAgentResult(task
|
|
480
|
+
export async function notifyCodeAgentResult(task) {
|
|
223
481
|
if (!_codeAgentConfig)
|
|
224
482
|
return;
|
|
225
483
|
const { sendActiveChannelProactiveMessage } = await import('../channels.js');
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
484
|
+
const message = buildSoloNotification(task);
|
|
485
|
+
const discordNotification = buildCodeAgentDiscordNotification(task);
|
|
486
|
+
// Prefer task-scoped Discord routing over global active-channel fallback.
|
|
487
|
+
const threadSent = await trySendToDiscordThread(task, discordNotification);
|
|
488
|
+
const channelSent = threadSent ? true : await trySendToDiscordChannel(task, discordNotification);
|
|
489
|
+
if (!threadSent && !channelSent && !hasDiscordRouting(task)) {
|
|
230
490
|
const sent = await sendActiveChannelProactiveMessage(_codeAgentConfig, message).catch((err) => {
|
|
231
|
-
console.error(`[code-agent] Failed to send
|
|
491
|
+
console.error(`[code-agent] Failed to send notification for ${task.id}:`, err);
|
|
232
492
|
return false;
|
|
233
493
|
});
|
|
234
494
|
if (!sent)
|
|
235
|
-
console.warn(`[code-agent]
|
|
236
|
-
|
|
495
|
+
console.warn(`[code-agent] Notification not delivered for ${task.id} (no active channel or target)`);
|
|
496
|
+
}
|
|
497
|
+
else if (!threadSent && !channelSent) {
|
|
498
|
+
console.warn(`[code-agent] Notification not delivered for ${task.id} (Discord thread/channel unavailable)`);
|
|
237
499
|
}
|
|
238
|
-
message = buildSoloNotification(task);
|
|
239
|
-
const sent = await sendActiveChannelProactiveMessage(_codeAgentConfig, message).catch((err) => {
|
|
240
|
-
console.error(`[code-agent] Failed to send notification for ${task.id}:`, err);
|
|
241
|
-
return false;
|
|
242
|
-
});
|
|
243
|
-
if (!sent)
|
|
244
|
-
console.warn(`[code-agent] Notification not delivered for ${task.id} (no active channel or target)`);
|
|
245
500
|
}
|
|
246
501
|
/** Check workdir against allowed paths. */
|
|
247
502
|
export function resolveWorkdir(rawWorkdir, projects, skimpyclawRoot) {
|
|
@@ -268,9 +523,8 @@ export function resolveModelAlias(model, aliases) {
|
|
|
268
523
|
if (/^claude[-.]3[-.]5[-.]haiku(?:[-_.].*)?$/i.test(model)) {
|
|
269
524
|
return 'claude-haiku-4-5';
|
|
270
525
|
}
|
|
526
|
+
if (/^claude[-.]opus[-.]4[-_.]6$/i.test(model)) {
|
|
527
|
+
return 'claude-opus-4-6';
|
|
528
|
+
}
|
|
271
529
|
return model;
|
|
272
530
|
}
|
|
273
|
-
/** @deprecated Removed — old Claude CLI team state reader. Kept for backward compat. */
|
|
274
|
-
export function readTeamState() {
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type CodeAgentWorktreeMode = 'off' | 'auto' | 'always';
|
|
2
|
+
export type CodeAgentWorktreeRequest = boolean | 'auto' | undefined;
|
|
3
|
+
export interface CodeAgentWorktreeConfig {
|
|
4
|
+
enabled?: boolean;
|
|
5
|
+
mode?: CodeAgentWorktreeMode;
|
|
6
|
+
root?: string;
|
|
7
|
+
cleanup?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface PreparedCodeAgentWorktree {
|
|
10
|
+
sourceWorkdir: string;
|
|
11
|
+
worktreePath: string;
|
|
12
|
+
runWorkdir: string;
|
|
13
|
+
worktreeRef: string;
|
|
14
|
+
gitRoot: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CodeAgentWorktreeCleanupResult {
|
|
17
|
+
status: 'removed' | 'preserved' | 'skipped' | 'failed';
|
|
18
|
+
path?: string;
|
|
19
|
+
reason?: string;
|
|
20
|
+
at: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function normalizeWorktreeRequest(value: unknown): CodeAgentWorktreeRequest;
|
|
23
|
+
export declare function shouldAutoWorktreeTask(task: string): boolean;
|
|
24
|
+
export declare function shouldUseCodeAgentWorktree(task: string, request: CodeAgentWorktreeRequest, config?: CodeAgentWorktreeConfig): boolean;
|
|
25
|
+
export declare function findGitRoot(workdir: string): string | null;
|
|
26
|
+
export declare function prepareCodeAgentWorktree(options: {
|
|
27
|
+
id: string;
|
|
28
|
+
sourceWorkdir: string;
|
|
29
|
+
config?: CodeAgentWorktreeConfig;
|
|
30
|
+
required?: boolean;
|
|
31
|
+
}): PreparedCodeAgentWorktree | undefined;
|
|
32
|
+
export declare function cleanupCodeAgentWorktree(options: {
|
|
33
|
+
sourceWorkdir?: string;
|
|
34
|
+
worktreePath?: string;
|
|
35
|
+
worktreeRef?: string;
|
|
36
|
+
config?: CodeAgentWorktreeConfig;
|
|
37
|
+
}): CodeAgentWorktreeCleanupResult;
|