@visorcraft/idlehands 1.4.5 → 2.0.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/dist/agent/constants.js +12 -0
- package/dist/agent/constants.js.map +1 -0
- package/dist/agent/errors.js +8 -0
- package/dist/agent/errors.js.map +1 -0
- package/dist/agent/exec-helpers.js +105 -0
- package/dist/agent/exec-helpers.js.map +1 -0
- package/dist/agent/model-pick.js +21 -0
- package/dist/agent/model-pick.js.map +1 -0
- package/dist/agent/session-utils.js +63 -0
- package/dist/agent/session-utils.js.map +1 -0
- package/dist/agent/subagent-context.js +78 -0
- package/dist/agent/subagent-context.js.map +1 -0
- package/dist/agent/tool-loop-guard.js.map +1 -1
- package/dist/agent/tool-policy.js +54 -0
- package/dist/agent/tool-policy.js.map +1 -0
- package/dist/agent/tools-schema.js +281 -0
- package/dist/agent/tools-schema.js.map +1 -0
- package/dist/agent.js +136 -630
- package/dist/agent.js.map +1 -1
- package/dist/anton/controller.js +42 -139
- package/dist/anton/controller.js.map +1 -1
- package/dist/anton/lint-baseline.js +64 -0
- package/dist/anton/lint-baseline.js.map +1 -0
- package/dist/anton/preflight.js.map +1 -1
- package/dist/anton/prompt.js +71 -71
- package/dist/anton/reporter.js.map +1 -1
- package/dist/anton/runtime-ready.js +120 -0
- package/dist/anton/runtime-ready.js.map +1 -0
- package/dist/anton/session.js +8 -6
- package/dist/anton/session.js.map +1 -1
- package/dist/anton/verifier-utils.js +148 -0
- package/dist/anton/verifier-utils.js.map +1 -0
- package/dist/anton/verifier.js +26 -227
- package/dist/anton/verifier.js.map +1 -1
- package/dist/bot/anton-auto-pin.js +12 -0
- package/dist/bot/anton-auto-pin.js.map +1 -0
- package/dist/bot/anton-commands.js +137 -0
- package/dist/bot/anton-commands.js.map +1 -0
- package/dist/bot/anton-run.js +144 -0
- package/dist/bot/anton-run.js.map +1 -0
- package/dist/bot/anton-status-format.js +18 -0
- package/dist/bot/anton-status-format.js.map +1 -0
- package/dist/bot/basic-commands.js +114 -0
- package/dist/bot/basic-commands.js.map +1 -0
- package/dist/bot/command-format.js.map +1 -1
- package/dist/bot/command-logic.js +8 -728
- package/dist/bot/command-logic.js.map +1 -1
- package/dist/bot/commands.js +18 -1
- package/dist/bot/commands.js.map +1 -1
- package/dist/bot/discord-anton-autopin.js +29 -0
- package/dist/bot/discord-anton-autopin.js.map +1 -0
- package/dist/bot/discord-anton.js +45 -0
- package/dist/bot/discord-anton.js.map +1 -0
- package/dist/bot/discord-commands.js +20 -52
- package/dist/bot/discord-commands.js.map +1 -1
- package/dist/bot/discord-result.js +9 -0
- package/dist/bot/discord-result.js.map +1 -0
- package/dist/bot/discord-routing.js.map +1 -1
- package/dist/bot/discord.js +42 -12
- package/dist/bot/discord.js.map +1 -1
- package/dist/bot/escalation-commands.js +145 -0
- package/dist/bot/escalation-commands.js.map +1 -0
- package/dist/bot/escalation.js.map +1 -1
- package/dist/bot/git-status-command.js +28 -0
- package/dist/bot/git-status-command.js.map +1 -0
- package/dist/bot/model-endpoint.js +25 -0
- package/dist/bot/model-endpoint.js.map +1 -0
- package/dist/bot/session-history.js +61 -0
- package/dist/bot/session-history.js.map +1 -0
- package/dist/bot/session-settings.js +89 -0
- package/dist/bot/session-settings.js.map +1 -0
- package/dist/bot/telegram-commands.js +15 -7
- package/dist/bot/telegram-commands.js.map +1 -1
- package/dist/bot/telegram.js +13 -28
- package/dist/bot/telegram.js.map +1 -1
- package/dist/cli/agent-turn.js +8 -2
- package/dist/cli/agent-turn.js.map +1 -1
- package/dist/cli/commands/anton.js +8 -3
- package/dist/cli/commands/anton.js.map +1 -1
- package/dist/cli/commands/model.js +1 -3
- package/dist/cli/commands/model.js.map +1 -1
- package/dist/cli/commands/project.js +1 -1
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/secrets.js +1 -1
- package/dist/cli/commands/secrets.js.map +1 -1
- package/dist/cli/commands/session.js +22 -12
- package/dist/cli/commands/session.js.map +1 -1
- package/dist/cli/guided-onboarding.js +20 -0
- package/dist/cli/guided-onboarding.js.map +1 -0
- package/dist/cli/runtime-cmds.js +8 -133
- package/dist/cli/runtime-cmds.js.map +1 -1
- package/dist/cli/runtime-common.js +35 -0
- package/dist/cli/runtime-common.js.map +1 -0
- package/dist/cli/runtime-detect.js +12 -0
- package/dist/cli/runtime-detect.js.map +1 -0
- package/dist/cli/runtime-host-command.js +7 -0
- package/dist/cli/runtime-host-command.js.map +1 -0
- package/dist/cli/runtime-probe-defaults.js +63 -0
- package/dist/cli/runtime-probe-defaults.js.map +1 -0
- package/dist/cli/runtime-scan-ports.js +30 -0
- package/dist/cli/runtime-scan-ports.js.map +1 -0
- package/dist/cli/setup-bot-step.js +51 -0
- package/dist/cli/setup-bot-step.js.map +1 -0
- package/dist/cli/setup-runtime-forms.js +214 -0
- package/dist/cli/setup-runtime-forms.js.map +1 -0
- package/dist/cli/setup-style.js +8 -0
- package/dist/cli/setup-style.js.map +1 -0
- package/dist/cli/setup-ui.js +146 -0
- package/dist/cli/setup-ui.js.map +1 -0
- package/dist/cli/setup.js +11 -449
- package/dist/cli/setup.js.map +1 -1
- package/dist/client/error-utils.js +37 -0
- package/dist/client/error-utils.js.map +1 -0
- package/dist/client/pressure.js +77 -0
- package/dist/client/pressure.js.map +1 -0
- package/dist/client.js +24 -122
- package/dist/client.js.map +1 -1
- package/dist/config.js +34 -17
- package/dist/config.js.map +1 -1
- package/dist/git.js +8 -2
- package/dist/git.js.map +1 -1
- package/dist/hooks/types.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/progress/message-edit-scheduler.js.map +1 -1
- package/dist/progress/turn-progress.js.map +1 -1
- package/dist/runtime/executor.js +4 -1
- package/dist/runtime/executor.js.map +1 -1
- package/dist/runtime/health.js.map +1 -1
- package/dist/runtime/host-runner.js.map +1 -1
- package/dist/safety.js +3 -2
- package/dist/safety.js.map +1 -1
- package/dist/shared/config-utils.js.map +1 -1
- package/dist/tools/exec-core.js +252 -0
- package/dist/tools/exec-core.js.map +1 -0
- package/dist/tools/exec-pty.js +89 -0
- package/dist/tools/exec-pty.js.map +1 -0
- package/dist/tools/exec-utils.js +94 -0
- package/dist/tools/exec-utils.js.map +1 -0
- package/dist/tools/file-discovery.js +144 -0
- package/dist/tools/file-discovery.js.map +1 -0
- package/dist/tools/file-mutations.js +326 -0
- package/dist/tools/file-mutations.js.map +1 -0
- package/dist/tools/file-read.js +133 -0
- package/dist/tools/file-read.js.map +1 -0
- package/dist/tools/patch-apply.js +168 -0
- package/dist/tools/patch-apply.js.map +1 -0
- package/dist/tools/path-safety.js.map +1 -1
- package/dist/tools/replay-utils.js +25 -0
- package/dist/tools/replay-utils.js.map +1 -0
- package/dist/tools/search-utils.js +55 -0
- package/dist/tools/search-utils.js.map +1 -0
- package/dist/tools/sys-notes.js +34 -0
- package/dist/tools/sys-notes.js.map +1 -0
- package/dist/tools/text-utils.js +164 -0
- package/dist/tools/text-utils.js.map +1 -0
- package/dist/tools/undo.js +1 -1
- package/dist/tools/undo.js.map +1 -1
- package/dist/tools/vault-tools.js +36 -0
- package/dist/tools/vault-tools.js.map +1 -0
- package/dist/tools.js +19 -1460
- package/dist/tools.js.map +1 -1
- package/dist/tui/controller.js +5 -2
- package/dist/tui/controller.js.map +1 -1
- package/dist/tui/render.js.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/utils.js.map +1 -1
- package/dist/vault.js +1 -1
- package/dist/vault.js.map +1 -1
- package/dist/watchdog.js +1 -3
- package/dist/watchdog.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,733 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Surface-agnostic command logic shared by Telegram and Discord bots.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* This file now primarily defines shared types and re-exports concrete
|
|
5
|
+
* command handlers from focused modules.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import { WATCHDOG_RECOMMENDED_TUNING_TEXT, resolveWatchdogSettings, shouldRecommendWatchdogTuning, } from '../watchdog.js';
|
|
14
|
-
export function versionCommand(info) {
|
|
15
|
-
return {
|
|
16
|
-
title: `Idle Hands v${info.version || 'unknown'}`,
|
|
17
|
-
kv: [
|
|
18
|
-
['Model', info.model || 'auto', true],
|
|
19
|
-
['Endpoint', info.endpoint || '?', true],
|
|
20
|
-
],
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
export function startCommand(info) {
|
|
24
|
-
const kv = [];
|
|
25
|
-
if (info.agentName)
|
|
26
|
-
kv.push(['Agent', info.agentName]);
|
|
27
|
-
kv.push(['Model', info.model || 'auto', true], ['Endpoint', info.endpoint || '?', true], ['Default dir', info.defaultDir || '~', true]);
|
|
28
|
-
return {
|
|
29
|
-
title: 'Idle Hands — Local-first coding agent',
|
|
30
|
-
kv,
|
|
31
|
-
lines: ['', 'Send me a coding task, or use /help for commands.'],
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
export function helpCommand(surface) {
|
|
35
|
-
const lines = [
|
|
36
|
-
'/start — Welcome + config summary',
|
|
37
|
-
'/help — This message',
|
|
38
|
-
'/version — Show version',
|
|
39
|
-
'/new — Start a new session',
|
|
40
|
-
'/cancel — Abort current generation',
|
|
41
|
-
'/status — Session stats',
|
|
42
|
-
'/watchdog [status] — Show watchdog settings/status',
|
|
43
|
-
'/agent — Show current agent',
|
|
44
|
-
'/agents — List all configured agents',
|
|
45
|
-
'/escalate [model] — Use larger model for next message',
|
|
46
|
-
'/deescalate — Return to base model',
|
|
47
|
-
'/dir [path] — Get/set working directory',
|
|
48
|
-
'/pin — Pin current working directory',
|
|
49
|
-
'/unpin — Unpin working directory',
|
|
50
|
-
'/model — Show current model',
|
|
51
|
-
'/approval [mode] — Get/set approval mode',
|
|
52
|
-
'/mode [code|sys] — Get/set mode',
|
|
53
|
-
'/compact — Trigger context compaction',
|
|
54
|
-
'/changes — Show files modified this session',
|
|
55
|
-
'/undo — Undo last edit',
|
|
56
|
-
'/subagents [on|off] — Toggle sub-agents',
|
|
57
|
-
'/vault <query> — Search vault entries',
|
|
58
|
-
'/anton <file> — Start autonomous task runner',
|
|
59
|
-
'/anton status | /anton stop | /anton last',
|
|
60
|
-
];
|
|
61
|
-
if (surface === 'telegram') {
|
|
62
|
-
lines.push('/git_status — Show git status for working directory', '/restart_bot — Restart the bot service');
|
|
63
|
-
}
|
|
64
|
-
lines.push('', 'Or just send any text as a coding task.');
|
|
65
|
-
return { title: 'Commands', lines };
|
|
66
|
-
}
|
|
67
|
-
export function modelCommand(managed) {
|
|
68
|
-
return {
|
|
69
|
-
kv: [
|
|
70
|
-
['Model', managed.session.model, true],
|
|
71
|
-
['Harness', managed.session.harness, true],
|
|
72
|
-
],
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
export function compactCommand(managed) {
|
|
76
|
-
managed.session.reset();
|
|
77
|
-
return { success: '🗜 Session context compacted (reset to system prompt).' };
|
|
78
|
-
}
|
|
79
|
-
export function statusCommand(managed, extra) {
|
|
80
|
-
const s = managed.session;
|
|
81
|
-
const contextPct = s.contextWindow > 0
|
|
82
|
-
? Math.min(100, (s.currentContextTokens / s.contextWindow) * 100).toFixed(1)
|
|
83
|
-
: '?';
|
|
84
|
-
const kv = [];
|
|
85
|
-
if (managed.agentPersona) {
|
|
86
|
-
kv.push(['Agent', managed.agentPersona.display_name || managed.agentId]);
|
|
87
|
-
}
|
|
88
|
-
kv.push(['Mode', managed.config.mode ?? 'code'], ['Approval', managed.config.approval_mode ?? managed.approvalMode ?? 'auto-edit'], ['Model', s.model, true], ['Harness', s.harness, true], ['Dir', managed.workingDir, true], ['Dir pinned', managed.dirPinned ? 'yes' : 'no'], [
|
|
89
|
-
'Context',
|
|
90
|
-
`~${s.currentContextTokens.toLocaleString()} / ${s.contextWindow.toLocaleString()} (${contextPct}%)`,
|
|
91
|
-
], [
|
|
92
|
-
'Tokens',
|
|
93
|
-
`prompt=${s.usage.prompt.toLocaleString()}, completion=${s.usage.completion.toLocaleString()}`,
|
|
94
|
-
], ['In-flight', managed.inFlight ? 'yes' : 'no'], ['State', managed.state], [
|
|
95
|
-
'Queue',
|
|
96
|
-
extra?.maxQueue != null
|
|
97
|
-
? `${managed.pendingQueue.length}/${extra.maxQueue}`
|
|
98
|
-
: `${managed.pendingQueue.length} pending`,
|
|
99
|
-
]);
|
|
100
|
-
return { title: 'Session Status', kv };
|
|
101
|
-
}
|
|
102
|
-
export function watchdogCommand(managed, watchdogCfg) {
|
|
103
|
-
const cfg = watchdogCfg ?? resolveWatchdogSettings();
|
|
104
|
-
const kv = [
|
|
105
|
-
['Timeout', `${cfg.timeoutMs.toLocaleString()} ms (${Math.round(cfg.timeoutMs / 1000)}s)`],
|
|
106
|
-
['Max compactions', String(cfg.maxCompactions)],
|
|
107
|
-
['Grace windows', String(cfg.idleGraceTimeouts)],
|
|
108
|
-
['Debug abort reason', cfg.debugAbortReason ? 'on' : 'off'],
|
|
109
|
-
];
|
|
110
|
-
const lines = [];
|
|
111
|
-
if (shouldRecommendWatchdogTuning(cfg)) {
|
|
112
|
-
lines.push('');
|
|
113
|
-
kv.push(['Recommended tuning', WATCHDOG_RECOMMENDED_TUNING_TEXT]);
|
|
114
|
-
}
|
|
115
|
-
if (managed) {
|
|
116
|
-
const idleSec = managed.lastProgressAt > 0
|
|
117
|
-
? ((Date.now() - managed.lastProgressAt) / 1000).toFixed(1)
|
|
118
|
-
: 'n/a';
|
|
119
|
-
lines.push('');
|
|
120
|
-
kv.push(['In-flight', managed.inFlight ? 'yes' : 'no'], ['State', managed.state], ['Compaction attempts (turn)', String(managed.watchdogCompactAttempts ?? 0)], ['Idle since progress', `${idleSec}s`]);
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
lines.push('', 'No active session yet. Send a message to start one.');
|
|
124
|
-
}
|
|
125
|
-
return { title: 'Watchdog Status', kv, lines };
|
|
126
|
-
}
|
|
127
|
-
// ── Dir / pin / unpin ───────────────────────────────────────────────
|
|
128
|
-
export function dirShowCommand(managed) {
|
|
129
|
-
const dir = managed?.workingDir ?? '(no session)';
|
|
130
|
-
const kv = [['Working directory', dir, true]];
|
|
131
|
-
const lines = [];
|
|
132
|
-
if (managed) {
|
|
133
|
-
kv.push(['Directory pinned', managed.dirPinned ? 'yes' : 'no']);
|
|
134
|
-
if (!managed.dirPinned && managed.repoCandidates.length > 1) {
|
|
135
|
-
lines.push('Action required: run /dir <repo-root> before file edits.');
|
|
136
|
-
lines.push(`Detected repos: ${managed.repoCandidates.slice(0, 5).join(', ')}`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return { kv, lines: lines.length ? lines : undefined };
|
|
140
|
-
}
|
|
141
|
-
export function dirSetOk(resolvedDir) {
|
|
142
|
-
return { success: `✅ Working directory pinned to ${resolvedDir}` };
|
|
143
|
-
}
|
|
144
|
-
export function dirSetFail() {
|
|
145
|
-
return {
|
|
146
|
-
error: '❌ Directory not allowed or session error. Check bot.telegram.allowed_dirs / persona.allowed_dirs.',
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
export function pinOk(dir) {
|
|
150
|
-
return { success: `✅ Working directory pinned to ${dir}` };
|
|
151
|
-
}
|
|
152
|
-
export function pinFail() {
|
|
153
|
-
return dirSetFail();
|
|
154
|
-
}
|
|
155
|
-
export function unpinOk(dir) {
|
|
156
|
-
return { success: `✅ Directory unpinned. Working directory remains at ${dir}` };
|
|
157
|
-
}
|
|
158
|
-
export function unpinNotPinned() {
|
|
159
|
-
return { error: 'Directory is not pinned.' };
|
|
160
|
-
}
|
|
161
|
-
export function unpinFail() {
|
|
162
|
-
return { error: '❌ Failed to unpin directory.' };
|
|
163
|
-
}
|
|
164
|
-
// ── Approval / mode / subagents ─────────────────────────────────────
|
|
165
|
-
const APPROVAL_MODES = ['plan', 'default', 'auto-edit', 'yolo'];
|
|
166
|
-
export function approvalShowCommand(managed, fallback) {
|
|
167
|
-
const current = managed.config.approval_mode ?? managed.approvalMode ?? fallback ?? 'auto-edit';
|
|
168
|
-
return {
|
|
169
|
-
kv: [['Approval mode', current, true]],
|
|
170
|
-
lines: [`Options: ${APPROVAL_MODES.join(', ')}`],
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
export function approvalSetCommand(managed, arg) {
|
|
174
|
-
if (!APPROVAL_MODES.includes(arg)) {
|
|
175
|
-
return { error: `Invalid mode. Options: ${APPROVAL_MODES.join(', ')}` };
|
|
176
|
-
}
|
|
177
|
-
managed.config.approval_mode = arg;
|
|
178
|
-
managed.config.no_confirm = arg === 'yolo';
|
|
179
|
-
if ('approvalMode' in managed)
|
|
180
|
-
managed.approvalMode = arg;
|
|
181
|
-
return { success: `✅ Approval mode set to ${arg}` };
|
|
182
|
-
}
|
|
183
|
-
export function modeShowCommand(managed) {
|
|
184
|
-
return { kv: [['Mode', managed.config.mode ?? 'code', true]] };
|
|
185
|
-
}
|
|
186
|
-
export function modeSetCommand(managed, arg) {
|
|
187
|
-
if (arg !== 'code' && arg !== 'sys') {
|
|
188
|
-
return { error: 'Invalid mode. Options: code, sys' };
|
|
189
|
-
}
|
|
190
|
-
managed.config.mode = arg;
|
|
191
|
-
if (arg === 'sys' && managed.config.approval_mode === 'auto-edit') {
|
|
192
|
-
managed.config.approval_mode = 'default';
|
|
193
|
-
if ('approvalMode' in managed)
|
|
194
|
-
managed.approvalMode = 'default';
|
|
195
|
-
}
|
|
196
|
-
return { success: `✅ Mode set to ${arg}` };
|
|
197
|
-
}
|
|
198
|
-
export function subagentsShowCommand(managed) {
|
|
199
|
-
const current = managed.config.sub_agents?.enabled !== false;
|
|
200
|
-
return {
|
|
201
|
-
kv: [['Sub-agents', current ? 'on' : 'off', true]],
|
|
202
|
-
lines: ['Usage: /subagents on | off'],
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
export function subagentsSetCommand(managed, arg) {
|
|
206
|
-
if (arg !== 'on' && arg !== 'off') {
|
|
207
|
-
return { error: 'Invalid value. Usage: /subagents on | off' };
|
|
208
|
-
}
|
|
209
|
-
const enabled = arg === 'on';
|
|
210
|
-
managed.config.sub_agents = { ...(managed.config.sub_agents ?? {}), enabled };
|
|
211
|
-
return {
|
|
212
|
-
success: `✅ Sub-agents ${enabled ? 'on' : 'off'}${!enabled ? ' — spawn_task disabled for this session' : ''}`,
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
// ── Changes / undo ──────────────────────────────────────────────────
|
|
216
|
-
export async function changesCommand(managed) {
|
|
217
|
-
const replay = managed.session.replay;
|
|
218
|
-
if (!replay)
|
|
219
|
-
return { error: 'Replay is disabled. No change tracking available.' };
|
|
220
|
-
try {
|
|
221
|
-
const checkpoints = await replay.list(50);
|
|
222
|
-
if (!checkpoints.length)
|
|
223
|
-
return { lines: ['No file changes this session.'] };
|
|
224
|
-
const byFile = new Map();
|
|
225
|
-
for (const cp of checkpoints) {
|
|
226
|
-
byFile.set(cp.filePath, (byFile.get(cp.filePath) ?? 0) + 1);
|
|
227
|
-
}
|
|
228
|
-
const lines = [];
|
|
229
|
-
for (const [fp, count] of byFile) {
|
|
230
|
-
lines.push(` ✎ ${fp} (${count} edit${count > 1 ? 's' : ''})`);
|
|
231
|
-
}
|
|
232
|
-
return { title: `Session changes (${byFile.size} files)`, lines };
|
|
233
|
-
}
|
|
234
|
-
catch (e) {
|
|
235
|
-
return { error: `Error listing changes: ${e?.message ?? e}` };
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
export async function undoCommand(managed) {
|
|
239
|
-
const lastPath = managed.session.lastEditedPath;
|
|
240
|
-
if (!lastPath)
|
|
241
|
-
return { error: 'No recent edits to undo.' };
|
|
242
|
-
try {
|
|
243
|
-
const { undo_path } = await import('../tools.js');
|
|
244
|
-
const ctx = { cwd: managed.workingDir, noConfirm: true, dryRun: false };
|
|
245
|
-
const result = await undo_path(ctx, { path: lastPath });
|
|
246
|
-
return { success: `✅ ${result}` };
|
|
247
|
-
}
|
|
248
|
-
catch (e) {
|
|
249
|
-
return { error: `❌ Undo failed: ${e?.message ?? e}` };
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
// ── Vault ───────────────────────────────────────────────────────────
|
|
253
|
-
export async function vaultCommand(managed, query) {
|
|
254
|
-
const vault = managed.session.vault;
|
|
255
|
-
if (!vault)
|
|
256
|
-
return { error: 'Vault is disabled.' };
|
|
257
|
-
if (!query)
|
|
258
|
-
return { lines: ['Usage: /vault <search query>'] };
|
|
259
|
-
try {
|
|
260
|
-
const results = await vault.search(query, 5);
|
|
261
|
-
if (!results.length)
|
|
262
|
-
return { lines: [`No vault results for "${query}"`] };
|
|
263
|
-
const lines = [];
|
|
264
|
-
for (const r of results) {
|
|
265
|
-
const title = r.kind === 'note' ? `note:${r.key}` : `tool:${r.tool || r.key || '?'}`;
|
|
266
|
-
const body = (r.value ?? r.snippet ?? r.content ?? '').replace(/\s+/g, ' ').slice(0, 120);
|
|
267
|
-
lines.push(`• ${title}: ${body}`);
|
|
268
|
-
}
|
|
269
|
-
return { title: `Vault results for "${query}"`, lines };
|
|
270
|
-
}
|
|
271
|
-
catch (e) {
|
|
272
|
-
return { error: `Error searching vault: ${e?.message ?? e}` };
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
// ── Agent / agents ──────────────────────────────────────────────────
|
|
276
|
-
export function agentCommand(managed) {
|
|
277
|
-
if (!managed.agentPersona) {
|
|
278
|
-
return { lines: ['No agent configured. Using global config.'] };
|
|
279
|
-
}
|
|
280
|
-
const p = managed.agentPersona;
|
|
281
|
-
const kv = [];
|
|
282
|
-
if (p.model)
|
|
283
|
-
kv.push(['Model', p.model, true]);
|
|
284
|
-
if (p.endpoint)
|
|
285
|
-
kv.push(['Endpoint', p.endpoint, true]);
|
|
286
|
-
if (p.approval_mode)
|
|
287
|
-
kv.push(['Approval', p.approval_mode, true]);
|
|
288
|
-
if (p.default_dir)
|
|
289
|
-
kv.push(['Default dir', p.default_dir, true]);
|
|
290
|
-
if (p.allowed_dirs?.length)
|
|
291
|
-
kv.push(['Allowed dirs', p.allowed_dirs.join(', ')]);
|
|
292
|
-
const lines = [];
|
|
293
|
-
if (p.escalation?.models?.length) {
|
|
294
|
-
lines.push('');
|
|
295
|
-
kv.push(['Escalation models', p.escalation.models.join(', ')]);
|
|
296
|
-
if (managed.currentModelIndex > 0) {
|
|
297
|
-
kv.push(['Current tier', `${managed.currentModelIndex} (escalated)`]);
|
|
298
|
-
}
|
|
299
|
-
if (managed.pendingEscalation) {
|
|
300
|
-
kv.push(['Pending escalation', managed.pendingEscalation]);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return {
|
|
304
|
-
title: `Agent: ${p.display_name || managed.agentId} (${managed.agentId})`,
|
|
305
|
-
kv,
|
|
306
|
-
lines: lines.length ? lines : undefined,
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
export function agentsCommand(managed, surfaceConfig) {
|
|
310
|
-
const agents = surfaceConfig.agents;
|
|
311
|
-
if (!agents || Object.keys(agents).length === 0) {
|
|
312
|
-
return { lines: ['No agents configured. Using global config.'] };
|
|
313
|
-
}
|
|
314
|
-
const lines = [];
|
|
315
|
-
for (const [id, agent] of Object.entries(agents)) {
|
|
316
|
-
const current = id === managed.agentId ? ' <- current' : '';
|
|
317
|
-
const model = agent.model ? ` (${agent.model})` : '';
|
|
318
|
-
lines.push(`• ${agent.display_name || id} (${id})${model}${current}`);
|
|
319
|
-
}
|
|
320
|
-
const routing = surfaceConfig.routing;
|
|
321
|
-
if (routing) {
|
|
322
|
-
lines.push('', 'Routing:');
|
|
323
|
-
if (routing.default)
|
|
324
|
-
lines.push(`Default: ${routing.default}`);
|
|
325
|
-
if (routing.users && Object.keys(routing.users).length > 0) {
|
|
326
|
-
lines.push(`Users: ${Object.entries(routing.users)
|
|
327
|
-
.map(([u, a]) => `${u}→${a}`)
|
|
328
|
-
.join(', ')}`);
|
|
329
|
-
}
|
|
330
|
-
if (routing.chats && Object.keys(routing.chats).length > 0) {
|
|
331
|
-
lines.push(`Chats: ${Object.entries(routing.chats)
|
|
332
|
-
.map(([c, a]) => `${c}→${a}`)
|
|
333
|
-
.join(', ')}`);
|
|
334
|
-
}
|
|
335
|
-
if (routing.channels && Object.keys(routing.channels).length > 0) {
|
|
336
|
-
lines.push(`Channels: ${Object.entries(routing.channels)
|
|
337
|
-
.map(([c, a]) => `${c}→${a}`)
|
|
338
|
-
.join(', ')}`);
|
|
339
|
-
}
|
|
340
|
-
if (routing.guilds && Object.keys(routing.guilds).length > 0) {
|
|
341
|
-
lines.push(`Guilds: ${Object.entries(routing.guilds)
|
|
342
|
-
.map(([g, a]) => `${g}→${a}`)
|
|
343
|
-
.join(', ')}`);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return { title: 'Configured Agents', lines };
|
|
347
|
-
}
|
|
348
|
-
// ── Escalation / de-escalation ──────────────────────────────────────
|
|
349
|
-
export function escalateShowCommand(managed, baseModel) {
|
|
350
|
-
const escalation = managed.agentPersona?.escalation;
|
|
351
|
-
if (!escalation?.models?.length) {
|
|
352
|
-
return { error: '❌ No escalation models configured for this agent.' };
|
|
353
|
-
}
|
|
354
|
-
const kv = [
|
|
355
|
-
['Current model', baseModel, true],
|
|
356
|
-
['Escalation models', escalation.models.join(', ')],
|
|
357
|
-
];
|
|
358
|
-
const lines = ['', 'Usage: /escalate <model> or /escalate next', 'Then send your message - it will use the escalated model.'];
|
|
359
|
-
if (managed.pendingEscalation) {
|
|
360
|
-
lines.push('', `⚡ Pending escalation: ${managed.pendingEscalation} (next message will use this)`);
|
|
361
|
-
}
|
|
362
|
-
return { kv, lines };
|
|
363
|
-
}
|
|
364
|
-
export function escalateSetCommand(managed, arg) {
|
|
365
|
-
const escalation = managed.agentPersona?.escalation;
|
|
366
|
-
if (!escalation?.models?.length) {
|
|
367
|
-
return { error: '❌ No escalation models configured for this agent.' };
|
|
368
|
-
}
|
|
369
|
-
let targetModel;
|
|
370
|
-
let targetEndpoint;
|
|
371
|
-
if (arg.toLowerCase() === 'next') {
|
|
372
|
-
const nextIndex = Math.min(managed.currentModelIndex, escalation.models.length - 1);
|
|
373
|
-
targetModel = escalation.models[nextIndex];
|
|
374
|
-
targetEndpoint = escalation.tiers?.[nextIndex]?.endpoint;
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
if (!escalation.models.includes(arg)) {
|
|
378
|
-
return {
|
|
379
|
-
error: `❌ Model ${arg} not in escalation chain. Available: ${escalation.models.join(', ')}`,
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
targetModel = arg;
|
|
383
|
-
const idx = escalation.models.indexOf(arg);
|
|
384
|
-
targetEndpoint = escalation.tiers?.[idx]?.endpoint;
|
|
385
|
-
}
|
|
386
|
-
managed.pendingEscalation = targetModel;
|
|
387
|
-
if ('pendingEscalationEndpoint' in managed) {
|
|
388
|
-
managed.pendingEscalationEndpoint = targetEndpoint || null;
|
|
389
|
-
}
|
|
390
|
-
return { success: `⚡ Next message will use ${targetModel}. Send your request now.` };
|
|
391
|
-
}
|
|
392
|
-
export function deescalateCommand(managed, baseModel) {
|
|
393
|
-
if (managed.currentModelIndex === 0 && !managed.pendingEscalation) {
|
|
394
|
-
return { lines: ['Already using base model.'] };
|
|
395
|
-
}
|
|
396
|
-
managed.pendingEscalation = null;
|
|
397
|
-
if ('pendingEscalationEndpoint' in managed) {
|
|
398
|
-
managed.pendingEscalationEndpoint = null;
|
|
399
|
-
}
|
|
400
|
-
managed.currentModelIndex = 0;
|
|
401
|
-
// Caller must handle session recreation with baseModel
|
|
402
|
-
return 'recreate';
|
|
403
|
-
}
|
|
404
|
-
// ── Git status ──────────────────────────────────────────────────────
|
|
405
|
-
export async function gitStatusCommand(cwd) {
|
|
406
|
-
if (!cwd)
|
|
407
|
-
return { error: 'No working directory set. Use /dir to set one.' };
|
|
408
|
-
const { spawnSync } = await import('node:child_process');
|
|
409
|
-
const statusResult = spawnSync('git', ['status', '-s'], {
|
|
410
|
-
cwd,
|
|
411
|
-
encoding: 'utf8',
|
|
412
|
-
timeout: 5000,
|
|
413
|
-
});
|
|
414
|
-
if (statusResult.status !== 0) {
|
|
415
|
-
const err = String(statusResult.stderr || statusResult.error || 'Unknown error');
|
|
416
|
-
if (err.includes('not a git repository') || err.includes('not in a git')) {
|
|
417
|
-
return { error: '❌ Not a git repository.' };
|
|
418
|
-
}
|
|
419
|
-
return { error: `❌ git status failed: ${err.slice(0, 200)}` };
|
|
420
|
-
}
|
|
421
|
-
const statusOut = String(statusResult.stdout || '').trim();
|
|
422
|
-
const branchResult = spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
423
|
-
cwd,
|
|
424
|
-
encoding: 'utf8',
|
|
425
|
-
timeout: 2000,
|
|
426
|
-
});
|
|
427
|
-
const branch = branchResult.status === 0 ? String(branchResult.stdout || '').trim() : 'unknown';
|
|
428
|
-
if (!statusOut) {
|
|
429
|
-
return {
|
|
430
|
-
lines: [`📁 ${cwd}`, `🌿 Branch: ${branch}`, '', '✅ Working tree clean'],
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
const allLines = statusOut.split('\n');
|
|
434
|
-
const lines = allLines.slice(0, 30);
|
|
435
|
-
const truncated = allLines.length > 30;
|
|
436
|
-
return {
|
|
437
|
-
lines: [`📁 ${cwd}`, `🌿 Branch: ${branch}`],
|
|
438
|
-
preformatted: lines.join('\n') + (truncated ? '\n...' : ''),
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
// ── Anton ───────────────────────────────────────────────────────────
|
|
442
|
-
function formatAgeShort(msAgo) {
|
|
443
|
-
if (msAgo < 60_000)
|
|
444
|
-
return `${Math.max(1, Math.round(msAgo / 1000))}s ago`;
|
|
445
|
-
if (msAgo < 3_600_000)
|
|
446
|
-
return `${Math.round(msAgo / 60_000)}m ago`;
|
|
447
|
-
return `${Math.round(msAgo / 3_600_000)}h ago`;
|
|
448
|
-
}
|
|
449
|
-
function summarizeLoopEvent(ev) {
|
|
450
|
-
const emoji = ev.kind === 'final-failure' ? '🔴' : ev.kind === 'auto-recovered' ? '🟠' : '🟡';
|
|
451
|
-
const kind = ev.kind === 'final-failure'
|
|
452
|
-
? 'final failure'
|
|
453
|
-
: ev.kind === 'auto-recovered'
|
|
454
|
-
? 'auto-recovered'
|
|
455
|
-
: 'loop event';
|
|
456
|
-
const msg = ev.message.length > 120 ? ev.message.slice(0, 117) + '...' : ev.message;
|
|
457
|
-
return `${emoji} Last loop: ${kind} (${formatAgeShort(Date.now() - ev.at)})\n${msg}`;
|
|
458
|
-
}
|
|
459
|
-
export function antonStatusCommand(managed) {
|
|
460
|
-
if (!managed.antonActive)
|
|
461
|
-
return { lines: ['No Anton run in progress.'] };
|
|
462
|
-
if (managed.antonAbortSignal?.aborted) {
|
|
463
|
-
return { lines: ['🛑 Anton is stopping. Please wait for the current attempt to unwind.'] };
|
|
464
|
-
}
|
|
465
|
-
if (managed.antonProgress) {
|
|
466
|
-
const line1 = formatProgressBar(managed.antonProgress);
|
|
467
|
-
const lines = [line1];
|
|
468
|
-
if (managed.antonProgress.currentTask) {
|
|
469
|
-
lines.push('', `Working on: ${managed.antonProgress.currentTask} (Attempt ${managed.antonProgress.currentAttempt})`);
|
|
470
|
-
}
|
|
471
|
-
if (managed.antonLastLoopEvent) {
|
|
472
|
-
lines.push('', summarizeLoopEvent(managed.antonLastLoopEvent));
|
|
473
|
-
}
|
|
474
|
-
return { lines };
|
|
475
|
-
}
|
|
476
|
-
if (managed.antonLastLoopEvent) {
|
|
477
|
-
return {
|
|
478
|
-
lines: [
|
|
479
|
-
'🤖 Anton is running (no progress data yet).',
|
|
480
|
-
'',
|
|
481
|
-
summarizeLoopEvent(managed.antonLastLoopEvent),
|
|
482
|
-
],
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
return { lines: ['🤖 Anton is running (no progress data yet).'] };
|
|
486
|
-
}
|
|
487
|
-
export function antonStopCommand(managed) {
|
|
488
|
-
if (!managed.antonActive || !managed.antonAbortSignal) {
|
|
489
|
-
return { lines: ['No Anton run in progress.'] };
|
|
490
|
-
}
|
|
491
|
-
managed.lastActivity = Date.now();
|
|
492
|
-
managed.antonAbortSignal.aborted = true;
|
|
493
|
-
return { success: '🛑 Anton stop requested. Run will halt after the current task.' };
|
|
494
|
-
}
|
|
495
|
-
export function antonLastCommand(managed) {
|
|
496
|
-
if (!managed.antonLastResult)
|
|
497
|
-
return { lines: ['No previous Anton run.'] };
|
|
498
|
-
return { lines: [formatRunSummary(managed.antonLastResult)] };
|
|
499
|
-
}
|
|
500
|
-
export function antonHelpCommand() {
|
|
501
|
-
return {
|
|
502
|
-
title: '/anton — Autonomous task runner',
|
|
503
|
-
lines: [
|
|
504
|
-
'/anton <file> — Start run',
|
|
505
|
-
'/anton status — Show progress',
|
|
506
|
-
'/anton stop — Stop running',
|
|
507
|
-
'/anton last — Last run results',
|
|
508
|
-
],
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
export function buildAntonRunConfig(defaults, cwd, filePath) {
|
|
512
|
-
return {
|
|
513
|
-
taskFile: filePath,
|
|
514
|
-
preflightEnabled: defaults.preflight?.enabled ?? false,
|
|
515
|
-
preflightRequirementsReview: defaults.preflight?.requirements_review ?? true,
|
|
516
|
-
preflightDiscoveryTimeoutSec: defaults.preflight?.discovery_timeout_sec ?? defaults.task_timeout_sec ?? 600,
|
|
517
|
-
preflightReviewTimeoutSec: defaults.preflight?.review_timeout_sec ?? defaults.task_timeout_sec ?? 600,
|
|
518
|
-
preflightMaxRetries: defaults.preflight?.max_retries ?? 1,
|
|
519
|
-
preflightSessionMaxIterations: defaults.preflight?.session_max_iterations ?? 3,
|
|
520
|
-
preflightSessionTimeoutSec: defaults.preflight?.session_timeout_sec ?? 120,
|
|
521
|
-
projectDir: defaults.project_dir || cwd,
|
|
522
|
-
maxRetriesPerTask: defaults.max_retries ?? 3,
|
|
523
|
-
maxIterations: defaults.max_iterations ?? 200,
|
|
524
|
-
taskMaxIterations: defaults.task_max_iterations ?? 50,
|
|
525
|
-
taskTimeoutSec: defaults.task_timeout_sec ?? 600,
|
|
526
|
-
totalTimeoutSec: defaults.total_timeout_sec ?? 7200,
|
|
527
|
-
maxTotalTokens: defaults.max_total_tokens ?? Infinity,
|
|
528
|
-
maxPromptTokensPerAttempt: defaults.max_prompt_tokens_per_attempt ?? 999_999_999,
|
|
529
|
-
autoCommit: defaults.auto_commit ?? true,
|
|
530
|
-
branch: false,
|
|
531
|
-
allowDirty: false,
|
|
532
|
-
aggressiveCleanOnFail: false,
|
|
533
|
-
verifyAi: defaults.verify_ai ?? true,
|
|
534
|
-
verifyModel: undefined,
|
|
535
|
-
decompose: defaults.decompose ?? true,
|
|
536
|
-
maxDecomposeDepth: defaults.max_decompose_depth ?? 2,
|
|
537
|
-
maxTotalTasks: defaults.max_total_tasks ?? 500,
|
|
538
|
-
buildCommand: defaults.build_command ?? undefined,
|
|
539
|
-
testCommand: defaults.test_command ?? undefined,
|
|
540
|
-
lintCommand: defaults.lint_command ?? undefined,
|
|
541
|
-
skipOnFail: defaults.skip_on_fail ?? false,
|
|
542
|
-
skipOnBlocked: defaults.skip_on_blocked ?? true,
|
|
543
|
-
rollbackOnFail: defaults.rollback_on_fail ?? false,
|
|
544
|
-
maxIdenticalFailures: defaults.max_identical_failures ?? 3,
|
|
545
|
-
approvalMode: (defaults.approval_mode ?? 'yolo'),
|
|
546
|
-
verbose: false,
|
|
547
|
-
dryRun: false,
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
export function makeAntonProgress(managed, defaults, send, rateLimitMs) {
|
|
551
|
-
const heartbeatSecRaw = Number(defaults.progress_heartbeat_sec ?? 30);
|
|
552
|
-
const heartbeatIntervalMs = Number.isFinite(heartbeatSecRaw)
|
|
553
|
-
? Math.max(5000, Math.floor(heartbeatSecRaw * 1000))
|
|
554
|
-
: 30_000;
|
|
555
|
-
let lastProgressAt = 0;
|
|
556
|
-
let lastHeartbeatNoticeAt = 0;
|
|
557
|
-
let runStartMs = 0;
|
|
558
|
-
let lastHeartbeatText = '';
|
|
559
|
-
return {
|
|
560
|
-
onTaskStart(task, attempt, prog) {
|
|
561
|
-
const now = Date.now();
|
|
562
|
-
if (!runStartMs)
|
|
563
|
-
runStartMs = now;
|
|
564
|
-
managed.antonProgress = prog;
|
|
565
|
-
managed.lastActivity = now;
|
|
566
|
-
lastProgressAt = now;
|
|
567
|
-
send(formatTaskStart(task, attempt, prog));
|
|
568
|
-
},
|
|
569
|
-
onTaskEnd(task, result, prog) {
|
|
570
|
-
const now = Date.now();
|
|
571
|
-
managed.antonProgress = prog;
|
|
572
|
-
managed.lastActivity = now;
|
|
573
|
-
lastProgressAt = now;
|
|
574
|
-
send(formatTaskEnd(task, result, prog));
|
|
575
|
-
},
|
|
576
|
-
onTaskSkip(task, reason) {
|
|
577
|
-
managed.lastActivity = Date.now();
|
|
578
|
-
send(formatTaskSkip(task, reason));
|
|
579
|
-
},
|
|
580
|
-
onRunComplete(result) {
|
|
581
|
-
managed.lastActivity = Date.now();
|
|
582
|
-
managed.antonLastResult = result;
|
|
583
|
-
managed.antonActive = false;
|
|
584
|
-
managed.antonAbortSignal = null;
|
|
585
|
-
managed.antonProgress = null;
|
|
586
|
-
runStartMs = 0;
|
|
587
|
-
lastHeartbeatText = '';
|
|
588
|
-
send(formatRunSummary(result));
|
|
589
|
-
},
|
|
590
|
-
onHeartbeat() {
|
|
591
|
-
const now = Date.now();
|
|
592
|
-
managed.lastActivity = now;
|
|
593
|
-
if (defaults.progress_events === false)
|
|
594
|
-
return;
|
|
595
|
-
if (!managed.antonProgress?.currentTask)
|
|
596
|
-
return;
|
|
597
|
-
if (now - lastProgressAt < rateLimitMs)
|
|
598
|
-
return;
|
|
599
|
-
if (now - lastHeartbeatNoticeAt < heartbeatIntervalMs)
|
|
600
|
-
return;
|
|
601
|
-
// Keep elapsed time live between task start/end callbacks.
|
|
602
|
-
if (!runStartMs)
|
|
603
|
-
runStartMs = now;
|
|
604
|
-
managed.antonProgress = {
|
|
605
|
-
...managed.antonProgress,
|
|
606
|
-
elapsedMs: now - runStartMs,
|
|
607
|
-
};
|
|
608
|
-
const hb = formatTaskHeartbeat(managed.antonProgress);
|
|
609
|
-
// Avoid repeated identical status lines across Telegram/TUI/Discord.
|
|
610
|
-
if (hb === lastHeartbeatText)
|
|
611
|
-
return;
|
|
612
|
-
lastHeartbeatNoticeAt = now;
|
|
613
|
-
lastHeartbeatText = hb;
|
|
614
|
-
send(hb);
|
|
615
|
-
},
|
|
616
|
-
onToolLoop(taskText, event) {
|
|
617
|
-
const now = Date.now();
|
|
618
|
-
managed.lastActivity = now;
|
|
619
|
-
const detail = String(event.message ?? '');
|
|
620
|
-
const kind = /final loop failure|retries exhausted/i.test(detail)
|
|
621
|
-
? 'final-failure'
|
|
622
|
-
: /auto-?recover|auto-?continu/i.test(detail)
|
|
623
|
-
? 'auto-recovered'
|
|
624
|
-
: 'other';
|
|
625
|
-
managed.antonLastLoopEvent = {
|
|
626
|
-
kind,
|
|
627
|
-
taskText,
|
|
628
|
-
message: detail,
|
|
629
|
-
at: now,
|
|
630
|
-
};
|
|
631
|
-
if (defaults.progress_events !== false) {
|
|
632
|
-
send(formatToolLoopEvent(taskText, event));
|
|
633
|
-
}
|
|
634
|
-
},
|
|
635
|
-
onCompaction(taskText, event) {
|
|
636
|
-
managed.lastActivity = Date.now();
|
|
637
|
-
if (defaults.progress_events !== false && event.droppedMessages >= 5) {
|
|
638
|
-
send(formatCompactionEvent(taskText, event));
|
|
639
|
-
}
|
|
640
|
-
},
|
|
641
|
-
onVerification(taskText, verification) {
|
|
642
|
-
managed.lastActivity = Date.now();
|
|
643
|
-
if (defaults.progress_events !== false) {
|
|
644
|
-
send(formatVerificationDetail(taskText, verification));
|
|
645
|
-
}
|
|
646
|
-
},
|
|
647
|
-
onStage(message) {
|
|
648
|
-
managed.lastActivity = Date.now();
|
|
649
|
-
if (defaults.progress_events !== false) {
|
|
650
|
-
send(message);
|
|
651
|
-
}
|
|
652
|
-
},
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Prepare and start an Anton run. Returns a CmdResult for the initial message,
|
|
657
|
-
* or an error CmdResult. The run itself proceeds asynchronously.
|
|
658
|
-
*/
|
|
659
|
-
export async function antonStartCommand(managed, args, send, rateLimitMs) {
|
|
660
|
-
const sub = firstToken(args);
|
|
661
|
-
const filePart = sub === 'run' ? args.replace(/^\S+\s*/, '').trim() : args;
|
|
662
|
-
if (!filePart)
|
|
663
|
-
return antonHelpCommand();
|
|
664
|
-
// Check stale run
|
|
665
|
-
if (managed.antonActive) {
|
|
666
|
-
const staleMs = Date.now() - managed.lastActivity;
|
|
667
|
-
if (staleMs > 120_000) {
|
|
668
|
-
managed.antonActive = false;
|
|
669
|
-
managed.antonAbortSignal = null;
|
|
670
|
-
managed.antonProgress = null;
|
|
671
|
-
managed.antonLastLoopEvent = null;
|
|
672
|
-
send('♻️ Recovered stale Anton run state. Starting a fresh run...');
|
|
673
|
-
}
|
|
674
|
-
else {
|
|
675
|
-
const msg = managed.antonAbortSignal?.aborted
|
|
676
|
-
? '🛑 Anton is still stopping. Please wait a moment, then try again.'
|
|
677
|
-
: '⚠️ Anton is already running. Use /anton stop first.';
|
|
678
|
-
return { error: msg };
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
const cwd = managed.workingDir || managed.config.dir || process.cwd();
|
|
682
|
-
const filePath = path.resolve(cwd, filePart);
|
|
683
|
-
try {
|
|
684
|
-
await fs.stat(filePath);
|
|
685
|
-
}
|
|
686
|
-
catch {
|
|
687
|
-
return { error: `File not found: ${filePath}` };
|
|
688
|
-
}
|
|
689
|
-
const defaults = managed.config.anton || {};
|
|
690
|
-
const runConfig = buildAntonRunConfig(defaults, cwd, filePath);
|
|
691
|
-
const abortSignal = { aborted: false };
|
|
692
|
-
managed.antonActive = true;
|
|
693
|
-
managed.antonAbortSignal = abortSignal;
|
|
694
|
-
managed.antonProgress = null;
|
|
695
|
-
managed.antonLastLoopEvent = null;
|
|
696
|
-
const progress = makeAntonProgress(managed, defaults, send, rateLimitMs);
|
|
697
|
-
let pendingCount = 0;
|
|
698
|
-
try {
|
|
699
|
-
const tf = await parseTaskFile(filePath);
|
|
700
|
-
pendingCount = tf.pending.length;
|
|
701
|
-
}
|
|
702
|
-
catch { /* non-fatal */ }
|
|
703
|
-
// Fire and forget — the run proceeds in the background
|
|
704
|
-
runAnton({
|
|
705
|
-
config: runConfig,
|
|
706
|
-
idlehandsConfig: managed.config,
|
|
707
|
-
progress,
|
|
708
|
-
abortSignal,
|
|
709
|
-
vault: managed.session.vault,
|
|
710
|
-
lens: managed.session.lens,
|
|
711
|
-
}).catch((err) => {
|
|
712
|
-
managed.lastActivity = Date.now();
|
|
713
|
-
managed.antonActive = false;
|
|
714
|
-
managed.antonAbortSignal = null;
|
|
715
|
-
managed.antonProgress = null;
|
|
716
|
-
send(`Anton error: ${err.message}`);
|
|
717
|
-
});
|
|
718
|
-
return { success: `🤖 Anton started on ${filePart} (${pendingCount} tasks pending)` };
|
|
719
|
-
}
|
|
720
|
-
/**
|
|
721
|
-
* Route an /anton command to the appropriate handler.
|
|
722
|
-
*/
|
|
723
|
-
export async function antonCommand(managed, args, send, rateLimitMs) {
|
|
724
|
-
const sub = firstToken(args);
|
|
725
|
-
if (!sub || sub === 'status')
|
|
726
|
-
return antonStatusCommand(managed);
|
|
727
|
-
if (sub === 'stop')
|
|
728
|
-
return antonStopCommand(managed);
|
|
729
|
-
if (sub === 'last')
|
|
730
|
-
return antonLastCommand(managed);
|
|
731
|
-
return antonStartCommand(managed, args, send, rateLimitMs);
|
|
732
|
-
}
|
|
7
|
+
export { approvalSetCommand, approvalShowCommand, dirSetFail, dirSetOk, dirShowCommand, modeSetCommand, modeShowCommand, pinFail, pinOk, subagentsSetCommand, subagentsShowCommand, unpinFail, unpinNotPinned, unpinOk, } from './session-settings.js';
|
|
8
|
+
export { changesCommand, undoCommand, vaultCommand } from './session-history.js';
|
|
9
|
+
export { agentCommand, agentsCommand, deescalateCommand, escalateSetCommand, escalateShowCommand, } from './escalation-commands.js';
|
|
10
|
+
export { gitStatusCommand } from './git-status-command.js';
|
|
11
|
+
export { compactCommand, helpCommand, modelCommand, startCommand, statusCommand, versionCommand, watchdogCommand, } from './basic-commands.js';
|
|
12
|
+
export { antonCommand, antonHelpCommand, antonLastCommand, antonStartCommand, antonStatusCommand, antonStopCommand, } from './anton-commands.js';
|
|
733
13
|
//# sourceMappingURL=command-logic.js.map
|