@sns-myagent/cli 0.2.0 → 0.3.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/CHANGELOG.md +12 -1
- package/README.md +7 -8
- package/package.json +3 -3
- package/scripts/apply-pi-natives-patch.js +82 -56
- package/src/adapters/telegram/bot.ts +41 -1
- package/src/adapters/telegram/bridge.ts +186 -0
- package/src/adapters/telegram/handler.ts +138 -13
- package/src/adapters/telegram/index.ts +12 -2
- package/src/agents/__tests__/config.test.ts +79 -0
- package/src/agents/__tests__/resilience.test.ts +119 -0
- package/src/agents/__tests__/strategies.test.ts +83 -0
- package/src/agents/config.ts +367 -0
- package/src/agents/ensemble.ts +224 -0
- package/src/agents/resilience.ts +332 -0
- package/src/agents/strategies/best-of-n.ts +108 -0
- package/src/agents/strategies/consensus.ts +105 -0
- package/src/agents/strategies/critic.ts +131 -0
- package/src/agents/strategies/types.ts +68 -0
- package/src/async/__tests__/task-runner.test.ts +162 -0
- package/src/async/__tests__/task-store.test.ts +146 -0
- package/src/async/index.ts +28 -1
- package/src/async/notifier.ts +133 -0
- package/src/async/task-runner.ts +175 -0
- package/src/async/task-store.ts +170 -0
- package/src/async/types.ts +70 -0
- package/src/cli/entry.ts +3 -1
- package/src/cli/index.ts +69 -54
- package/src/config/index.ts +1 -1
- package/src/debug/index.ts +1 -1
- package/src/modes/components/welcome.ts +13 -6
- package/src/modes/controllers/event-controller.ts +1 -1
- package/src/modes/setup-wizard/scenes/splash.ts +1 -1
- package/src/session/agent-session.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +63 -0
- package/src/slash-commands/helpers/task.ts +181 -0
- package/src/tbm/__tests__/tbm.test.ts +660 -0
- package/src/tbm/comm-modes.ts +165 -0
- package/src/tbm/config.ts +136 -0
- package/src/tbm/context-delta.ts +146 -0
- package/src/tbm/context-pyramid.ts +202 -0
- package/src/tbm/dashboard.ts +131 -0
- package/src/tbm/index.ts +247 -0
- package/src/tbm/lazy-skills.ts +182 -0
- package/src/tbm/response-cache.ts +220 -0
- package/src/tbm/tombstone.ts +189 -0
- package/src/tbm/tool-compress.ts +230 -0
- package/src/tools/ask.ts +1 -1
- package/src/tui/chat-blocks.ts +205 -0
- package/src/tui/chat-ui.ts +270 -0
- package/src/tui/code-cell.ts +90 -1
- package/src/tui/command-palette.ts +189 -0
- package/src/tui/index.ts +4 -0
- package/src/tui/splash.ts +130 -0
- package/src/ui/banner.ts +69 -29
- package/src/ui/colors.ts +24 -0
- package/src/ui/error-display.ts +130 -0
- package/src/ui/gradient.ts +104 -0
- package/src/ui/index.ts +15 -0
- package/src/ui/memory-toast.ts +102 -0
- package/src/ui/status-bar.ts +36 -30
- package/bin/snscoder.js +0 -98
|
@@ -32,6 +32,7 @@ import { urlHyperlinkAlways } from "../tui";
|
|
|
32
32
|
import { getChangelogPath, parseChangelog } from "../utils/changelog";
|
|
33
33
|
import { CollabQrCodeComponent } from "./helpers/collab-qrcode";
|
|
34
34
|
import { handleCronCommand } from "./helpers/cron";
|
|
35
|
+
import { handleTaskCommand } from "./helpers/task";
|
|
35
36
|
import { buildContextReportText } from "./helpers/context-report";
|
|
36
37
|
import { formatDuration } from "./helpers/format";
|
|
37
38
|
import { createMarketplaceManager } from "./helpers/marketplace-manager";
|
|
@@ -2233,11 +2234,73 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
2233
2234
|
],
|
|
2234
2235
|
handle: handleCronCommand,
|
|
2235
2236
|
},
|
|
2237
|
+
{
|
|
2238
|
+
name: "task",
|
|
2239
|
+
description: "Manage async background tasks",
|
|
2240
|
+
allowArgs: true,
|
|
2241
|
+
subcommands: [
|
|
2242
|
+
{ name: "run", description: "Spawn async task in background", usage: "<description>" },
|
|
2243
|
+
{ name: "list", description: "Show all tasks" },
|
|
2244
|
+
{ name: "status", description: "Check specific task", usage: "<id>" },
|
|
2245
|
+
{ name: "cancel", description: "Cancel running task", usage: "<id>" },
|
|
2246
|
+
{ name: "result", description: "Get task result", usage: "<id>" },
|
|
2247
|
+
],
|
|
2248
|
+
handle: handleTaskCommand,
|
|
2249
|
+
},
|
|
2236
2250
|
{
|
|
2237
2251
|
name: "quit",
|
|
2238
2252
|
description: "Quit the application",
|
|
2239
2253
|
handleTui: shutdownHandlerTui,
|
|
2240
2254
|
},
|
|
2255
|
+
{
|
|
2256
|
+
name: "tokens",
|
|
2257
|
+
description: "Show Token Budget Manager dashboard — session stats, cache hits, compression, pyramid level",
|
|
2258
|
+
inlineHint: "[compact]",
|
|
2259
|
+
allowArgs: true,
|
|
2260
|
+
handle: async (command, runtime) => {
|
|
2261
|
+
try {
|
|
2262
|
+
// TBM is accessed via session.tbm if available
|
|
2263
|
+
const session = runtime.session as unknown as Record<string, unknown>;
|
|
2264
|
+
const tbm = session.tbm as { renderDashboard(): string; renderCompactDashboard(): string } | undefined;
|
|
2265
|
+
if (!tbm) {
|
|
2266
|
+
await runtime.output("TBM not initialized. Check config tbm.enabled.");
|
|
2267
|
+
return commandConsumed();
|
|
2268
|
+
}
|
|
2269
|
+
const compact = command.args.trim() === "compact";
|
|
2270
|
+
await runtime.output(compact ? tbm.renderCompactDashboard() : tbm.renderDashboard());
|
|
2271
|
+
return commandConsumed();
|
|
2272
|
+
} catch (err) {
|
|
2273
|
+
await runtime.output(`Failed to render TBM dashboard: ${errorMessage(err)}`);
|
|
2274
|
+
return commandConsumed();
|
|
2275
|
+
}
|
|
2276
|
+
},
|
|
2277
|
+
},
|
|
2278
|
+
{
|
|
2279
|
+
name: "mode",
|
|
2280
|
+
description: "Set communication mode: caveman (terse), normal, verbose, or auto",
|
|
2281
|
+
inlineHint: "[caveman|normal|verbose|auto|status]",
|
|
2282
|
+
allowArgs: true,
|
|
2283
|
+
handle: async (command, runtime) => {
|
|
2284
|
+
const arg = command.args.trim().toLowerCase();
|
|
2285
|
+
const session = runtime.session as unknown as Record<string, unknown>;
|
|
2286
|
+
const tbm = session.tbm as { setMode(mode: string): void; renderCompactDashboard(): string } | undefined;
|
|
2287
|
+
if (!tbm) {
|
|
2288
|
+
await runtime.output("TBM not initialized.");
|
|
2289
|
+
return commandConsumed();
|
|
2290
|
+
}
|
|
2291
|
+
if (!arg || arg === "status") {
|
|
2292
|
+
await runtime.output(tbm.renderCompactDashboard());
|
|
2293
|
+
return commandConsumed();
|
|
2294
|
+
}
|
|
2295
|
+
if (!["caveman", "normal", "verbose", "auto"].includes(arg)) {
|
|
2296
|
+
await runtime.output("Usage: /mode [caveman|normal|verbose|auto|status]");
|
|
2297
|
+
return commandConsumed();
|
|
2298
|
+
}
|
|
2299
|
+
tbm.setMode(arg);
|
|
2300
|
+
await runtime.output(`Communication mode set to: ${arg}`);
|
|
2301
|
+
return commandConsumed();
|
|
2302
|
+
},
|
|
2303
|
+
},
|
|
2241
2304
|
];
|
|
2242
2305
|
|
|
2243
2306
|
const BUILTIN_SLASH_COMMAND_LOOKUP = new Map<string, SlashCommandSpec>();
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// /task slash command handler
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
4
|
+
|
|
5
|
+
import { getTaskStore } from "../../async/task-store";
|
|
6
|
+
import { getTaskRunner } from "../../async/task-runner";
|
|
7
|
+
import { formatTaskList, formatTaskStatus, cliNotify } from "../../async/notifier";
|
|
8
|
+
import type { ParsedSlashCommand, SlashCommandResult, SlashCommandRuntime } from "../types";
|
|
9
|
+
import { parseSubcommand } from "./parse";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Handle /task subcommands: run, list, status, cancel, result.
|
|
13
|
+
*/
|
|
14
|
+
export async function handleTaskCommand(
|
|
15
|
+
command: ParsedSlashCommand,
|
|
16
|
+
runtime: SlashCommandRuntime,
|
|
17
|
+
): Promise<SlashCommandResult> {
|
|
18
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
19
|
+
const store = getTaskStore();
|
|
20
|
+
|
|
21
|
+
switch (verb) {
|
|
22
|
+
case "run":
|
|
23
|
+
return taskRun(rest, store, runtime);
|
|
24
|
+
case "list":
|
|
25
|
+
case "ls":
|
|
26
|
+
return taskList(store, runtime);
|
|
27
|
+
case "status":
|
|
28
|
+
return taskStatus(rest, store, runtime);
|
|
29
|
+
case "cancel":
|
|
30
|
+
case "stop":
|
|
31
|
+
return taskCancel(rest, store, runtime);
|
|
32
|
+
case "result":
|
|
33
|
+
case "out":
|
|
34
|
+
return taskResult(rest, store, runtime);
|
|
35
|
+
default:
|
|
36
|
+
await runtime.output(
|
|
37
|
+
`Usage: /task <subcommand>\n` +
|
|
38
|
+
` run <description> Spawn async task in background\n` +
|
|
39
|
+
` list Show all tasks (pending + running)\n` +
|
|
40
|
+
` status <id> Check specific task status\n` +
|
|
41
|
+
` cancel <id> Cancel running/pending task\n` +
|
|
42
|
+
` result <id> Get full task result\n`,
|
|
43
|
+
);
|
|
44
|
+
return { consumed: true };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function taskRun(
|
|
49
|
+
description: string,
|
|
50
|
+
store: ReturnType<typeof getTaskStore>,
|
|
51
|
+
runtime: SlashCommandRuntime,
|
|
52
|
+
): Promise<SlashCommandResult> {
|
|
53
|
+
if (!description.trim()) {
|
|
54
|
+
await runtime.output("Usage: /task run <description>");
|
|
55
|
+
return { consumed: true };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const task = store.create({ description: description.trim() });
|
|
59
|
+
const runner = getTaskRunner(store);
|
|
60
|
+
|
|
61
|
+
// Register a default prompt executor (idempotent — overwrites if exists)
|
|
62
|
+
runner.registerExecutor("prompt", async (t) => {
|
|
63
|
+
// Default executor: simulate work (real executor wired by agent session)
|
|
64
|
+
return { success: true, result: `Task "${t.description}" completed by default executor.` };
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
runner.start();
|
|
68
|
+
await runner.submit(task);
|
|
69
|
+
|
|
70
|
+
await runtime.output(
|
|
71
|
+
`✅ Task created: ${task.id.slice(0, 8)}\n` +
|
|
72
|
+
` Description: ${task.description}\n` +
|
|
73
|
+
` Status: pending → running in background\n` +
|
|
74
|
+
` Check: /task status ${task.id.slice(0, 8)}`,
|
|
75
|
+
);
|
|
76
|
+
return { consumed: true };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function taskList(
|
|
80
|
+
store: ReturnType<typeof getTaskStore>,
|
|
81
|
+
runtime: SlashCommandRuntime,
|
|
82
|
+
): Promise<SlashCommandResult> {
|
|
83
|
+
const active = store.list("running");
|
|
84
|
+
const pending = store.list("pending", 10);
|
|
85
|
+
const recent = store.list("completed", 5);
|
|
86
|
+
|
|
87
|
+
const tasks = [...active, ...pending, ...recent];
|
|
88
|
+
await runtime.output(formatTaskList(tasks));
|
|
89
|
+
|
|
90
|
+
const counts = store.count();
|
|
91
|
+
await runtime.output(
|
|
92
|
+
`Total: ${counts.total} | Pending: ${counts.pending} | Running: ${counts.running} | Done: ${counts.completed} | Failed: ${counts.failed}`,
|
|
93
|
+
);
|
|
94
|
+
return { consumed: true };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function taskStatus(
|
|
98
|
+
idPrefix: string,
|
|
99
|
+
store: ReturnType<typeof getTaskStore>,
|
|
100
|
+
runtime: SlashCommandRuntime,
|
|
101
|
+
): Promise<SlashCommandResult> {
|
|
102
|
+
const id = idPrefix.trim();
|
|
103
|
+
if (!id) {
|
|
104
|
+
await runtime.output("Usage: /task status <id>");
|
|
105
|
+
return { consumed: true };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const task = findTaskByIdPrefix(store, id);
|
|
109
|
+
if (!task) {
|
|
110
|
+
await runtime.output(`Task not found: ${id}`);
|
|
111
|
+
return { consumed: true };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await runtime.output(formatTaskStatus(task));
|
|
115
|
+
return { consumed: true };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function taskCancel(
|
|
119
|
+
idPrefix: string,
|
|
120
|
+
store: ReturnType<typeof getTaskStore>,
|
|
121
|
+
runtime: SlashCommandRuntime,
|
|
122
|
+
): Promise<SlashCommandResult> {
|
|
123
|
+
const id = idPrefix.trim();
|
|
124
|
+
if (!id) {
|
|
125
|
+
await runtime.output("Usage: /task cancel <id>");
|
|
126
|
+
return { consumed: true };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const task = findTaskByIdPrefix(store, id);
|
|
130
|
+
if (!task) {
|
|
131
|
+
await runtime.output(`Task not found: ${id}`);
|
|
132
|
+
return { consumed: true };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const runner = getTaskRunner(store);
|
|
136
|
+
const cancelled = runner.cancel(task.id);
|
|
137
|
+
if (cancelled) {
|
|
138
|
+
await runtime.output(`🚫 Task ${task.id.slice(0, 8)} cancelled.`);
|
|
139
|
+
} else {
|
|
140
|
+
await runtime.output(`Task ${task.id.slice(0, 8)} is already ${task.status}, cannot cancel.`);
|
|
141
|
+
}
|
|
142
|
+
return { consumed: true };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function taskResult(
|
|
146
|
+
idPrefix: string,
|
|
147
|
+
store: ReturnType<typeof getTaskStore>,
|
|
148
|
+
runtime: SlashCommandRuntime,
|
|
149
|
+
): Promise<SlashCommandResult> {
|
|
150
|
+
const id = idPrefix.trim();
|
|
151
|
+
if (!id) {
|
|
152
|
+
await runtime.output("Usage: /task result <id>");
|
|
153
|
+
return { consumed: true };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const task = findTaskByIdPrefix(store, id);
|
|
157
|
+
if (!task) {
|
|
158
|
+
await runtime.output(`Task not found: ${id}`);
|
|
159
|
+
return { consumed: true };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (task.status === "completed" && task.result) {
|
|
163
|
+
await runtime.output(`Result for ${task.id.slice(0, 8)}:\n${task.result}`);
|
|
164
|
+
} else if (task.status === "failed" && task.error) {
|
|
165
|
+
await runtime.output(`Error for ${task.id.slice(0, 8)}:\n${task.error}`);
|
|
166
|
+
} else {
|
|
167
|
+
await runtime.output(`Task ${task.id.slice(0, 8)} is ${task.status} — no result yet.`);
|
|
168
|
+
}
|
|
169
|
+
return { consumed: true };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Find task by full ID or prefix (first 8 chars) */
|
|
173
|
+
function findTaskByIdPrefix(store: ReturnType<typeof getTaskStore>, prefix: string) {
|
|
174
|
+
// Try full ID first
|
|
175
|
+
const full = store.getById(prefix);
|
|
176
|
+
if (full) return full;
|
|
177
|
+
|
|
178
|
+
// Try prefix match — search all recent tasks
|
|
179
|
+
const all = store.list(undefined, 100);
|
|
180
|
+
return all.find((t) => t.id.startsWith(prefix)) ?? null;
|
|
181
|
+
}
|