@tuttiai/cli 0.11.1 → 0.13.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/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { config } from "dotenv";
5
- import { createLogger as createLogger11 } from "@tuttiai/core";
5
+ import { createLogger as createLogger14 } from "@tuttiai/core";
6
6
  import { Command } from "commander";
7
7
 
8
8
  // src/commands/init.ts
@@ -1865,19 +1865,597 @@ function printBanner(port, host, agentName, score, file, watch) {
1865
1865
  console.log();
1866
1866
  }
1867
1867
 
1868
+ // src/commands/schedule.ts
1869
+ import { existsSync as existsSync11 } from "fs";
1870
+ import { resolve as resolve11 } from "path";
1871
+ import chalk11 from "chalk";
1872
+ import {
1873
+ ScoreLoader as ScoreLoader7,
1874
+ SchedulerEngine,
1875
+ PostgresScheduleStore,
1876
+ MemoryScheduleStore,
1877
+ AgentRunner,
1878
+ EventBus,
1879
+ InMemorySessionStore as InMemorySessionStore3,
1880
+ SecretsManager as SecretsManager6,
1881
+ createLogger as createLogger11
1882
+ } from "@tuttiai/core";
1883
+ var logger11 = createLogger11("tutti-cli");
1884
+ function resolveStore() {
1885
+ const pgUrl = SecretsManager6.optional("TUTTI_PG_URL");
1886
+ if (pgUrl) {
1887
+ return new PostgresScheduleStore({ connection_string: pgUrl });
1888
+ }
1889
+ logger11.warn("TUTTI_PG_URL not set \u2014 using in-memory store (not durable across restarts)");
1890
+ return new MemoryScheduleStore();
1891
+ }
1892
+ async function scheduleCommand(scorePath) {
1893
+ const file = resolve11(scorePath ?? "./tutti.score.ts");
1894
+ if (!existsSync11(file)) {
1895
+ console.error(chalk11.red("Score file not found: " + file));
1896
+ console.error(chalk11.dim('Run "tutti-ai init" to create a new project.'));
1897
+ process.exit(1);
1898
+ }
1899
+ const score = await ScoreLoader7.load(file);
1900
+ const events = new EventBus();
1901
+ const sessions = new InMemorySessionStore3();
1902
+ const runner = new AgentRunner(
1903
+ score.provider,
1904
+ events,
1905
+ sessions
1906
+ );
1907
+ const store = resolveStore();
1908
+ const engine = new SchedulerEngine(store, runner, events);
1909
+ let registered = 0;
1910
+ for (const [agentId, agent] of Object.entries(score.agents)) {
1911
+ if (!agent.schedule) continue;
1912
+ const resolvedAgent = agent.model ? agent : { ...agent, model: score.default_model ?? "claude-sonnet-4-20250514" };
1913
+ await engine.schedule(agentId, resolvedAgent, agent.schedule);
1914
+ registered++;
1915
+ }
1916
+ if (registered === 0) {
1917
+ console.log(chalk11.yellow("No agents have a schedule config. Nothing to run."));
1918
+ console.log(chalk11.dim("Add schedule: { cron: '...', input: '...' } to an agent in your score."));
1919
+ process.exit(0);
1920
+ }
1921
+ events.onAny((e) => {
1922
+ if (e.type === "schedule:triggered") {
1923
+ const ev = e;
1924
+ console.log(
1925
+ chalk11.dim((/* @__PURE__ */ new Date()).toISOString()) + " " + chalk11.cyan("triggered") + " " + chalk11.bold(ev.schedule_id) + " \u2192 " + ev.agent_name
1926
+ );
1927
+ }
1928
+ if (e.type === "schedule:completed") {
1929
+ const ev = e;
1930
+ console.log(
1931
+ chalk11.dim((/* @__PURE__ */ new Date()).toISOString()) + " " + chalk11.green("completed") + " " + chalk11.bold(ev.schedule_id) + " " + chalk11.dim("(" + ev.duration_ms + "ms)")
1932
+ );
1933
+ }
1934
+ if (e.type === "schedule:error") {
1935
+ const ev = e;
1936
+ console.log(
1937
+ chalk11.dim((/* @__PURE__ */ new Date()).toISOString()) + " " + chalk11.red("error") + " " + chalk11.bold(ev.schedule_id) + " \u2014 " + ev.error.message
1938
+ );
1939
+ }
1940
+ });
1941
+ engine.start();
1942
+ console.log("");
1943
+ console.log(chalk11.cyan.bold(" Tutti Scheduler"));
1944
+ console.log(chalk11.dim(" Score: " + (score.name ?? file)));
1945
+ console.log(chalk11.dim(" Schedules: " + registered));
1946
+ console.log(chalk11.dim(" Store: " + (SecretsManager6.optional("TUTTI_PG_URL") ? "postgres" : "memory")));
1947
+ console.log("");
1948
+ console.log(chalk11.dim(" Press Ctrl+C to stop."));
1949
+ console.log("");
1950
+ const shutdown = () => {
1951
+ console.log(chalk11.dim("\n Shutting down scheduler..."));
1952
+ engine.stop();
1953
+ if ("close" in store && typeof store.close === "function") {
1954
+ void store.close();
1955
+ }
1956
+ process.exit(0);
1957
+ };
1958
+ process.on("SIGINT", shutdown);
1959
+ process.on("SIGTERM", shutdown);
1960
+ await new Promise(() => void 0);
1961
+ }
1962
+
1963
+ // src/commands/replay.ts
1964
+ import { existsSync as existsSync12 } from "fs";
1965
+ import { writeFile } from "fs/promises";
1966
+ import { resolve as resolve12 } from "path";
1967
+ import { createInterface as createInterface3 } from "readline/promises";
1968
+ import chalk12 from "chalk";
1969
+ import ora7 from "ora";
1970
+ import {
1971
+ PostgresSessionStore,
1972
+ ScoreLoader as ScoreLoader8,
1973
+ TuttiRuntime as TuttiRuntime5,
1974
+ SecretsManager as SecretsManager7,
1975
+ createLogger as createLogger12
1976
+ } from "@tuttiai/core";
1977
+ var logger12 = createLogger12("tutti-cli");
1978
+ function messageToText2(msg) {
1979
+ if (typeof msg.content === "string") return msg.content;
1980
+ const parts = [];
1981
+ for (const block of msg.content) {
1982
+ if (block.type === "text") {
1983
+ parts.push(block.text);
1984
+ } else if (block.type === "tool_use") {
1985
+ parts.push("[tool_use " + block.name + "]");
1986
+ } else if (block.type === "tool_result") {
1987
+ const preview = block.content.replace(/\s+/g, " ").trim();
1988
+ parts.push("[tool_result " + (preview.length > 60 ? preview.slice(0, 59) + "\u2026" : preview) + "]");
1989
+ }
1990
+ }
1991
+ return parts.join(" ");
1992
+ }
1993
+ function excerpt2(text, max) {
1994
+ const oneLine = text.replace(/\s+/g, " ").trim();
1995
+ return oneLine.length > max ? oneLine.slice(0, max - 1) + "\u2026" : oneLine;
1996
+ }
1997
+ function renderList(messages) {
1998
+ const lines = [];
1999
+ for (let i = 0; i < messages.length; i++) {
2000
+ const msg = messages[i];
2001
+ const role = msg.role === "user" ? chalk12.blue("user ") : chalk12.green("assistant");
2002
+ const text = excerpt2(messageToText2(msg), 80);
2003
+ lines.push(
2004
+ chalk12.dim(String(i).padStart(3)) + " " + role + " " + text
2005
+ );
2006
+ }
2007
+ return lines.join("\n");
2008
+ }
2009
+ function renderShow(messages, index) {
2010
+ if (index < 0 || index >= messages.length) {
2011
+ return chalk12.red("Index out of range. Valid: 0\u2013" + (messages.length - 1));
2012
+ }
2013
+ const msg = messages[index];
2014
+ const lines = [];
2015
+ lines.push(chalk12.cyan.bold("Turn " + index) + " " + chalk12.dim("[" + msg.role + "]"));
2016
+ lines.push("");
2017
+ if (typeof msg.content === "string") {
2018
+ lines.push(msg.content);
2019
+ } else {
2020
+ for (const block of msg.content) {
2021
+ if (block.type === "text") {
2022
+ lines.push(block.text);
2023
+ } else if (block.type === "tool_use") {
2024
+ lines.push(chalk12.yellow(" tool_use: " + block.name));
2025
+ lines.push(chalk12.dim(" id: " + block.id));
2026
+ lines.push(chalk12.dim(" input: " + JSON.stringify(block.input, null, 2)));
2027
+ } else if (block.type === "tool_result") {
2028
+ const label = block.is_error ? chalk12.red(" tool_result (error):") : chalk12.green(" tool_result:");
2029
+ lines.push(label);
2030
+ lines.push(chalk12.dim(" tool_use_id: " + block.tool_use_id));
2031
+ lines.push(" " + block.content);
2032
+ }
2033
+ }
2034
+ }
2035
+ return lines.join("\n");
2036
+ }
2037
+ function renderInspect(messages, index) {
2038
+ if (index < 0 || index >= messages.length) {
2039
+ return chalk12.red("Index out of range.");
2040
+ }
2041
+ return JSON.stringify(messages[index], null, 2);
2042
+ }
2043
+ function exportJSON(session) {
2044
+ return JSON.stringify(
2045
+ {
2046
+ id: session.id,
2047
+ agent_name: session.agent_name,
2048
+ created_at: session.created_at,
2049
+ messages: session.messages
2050
+ },
2051
+ null,
2052
+ 2
2053
+ );
2054
+ }
2055
+ function exportMarkdown(session) {
2056
+ const lines = [];
2057
+ lines.push("# Session " + session.id);
2058
+ lines.push("");
2059
+ lines.push("**Agent:** " + session.agent_name);
2060
+ lines.push("**Created:** " + session.created_at.toISOString());
2061
+ lines.push("**Messages:** " + session.messages.length);
2062
+ lines.push("");
2063
+ lines.push("---");
2064
+ lines.push("");
2065
+ for (let i = 0; i < session.messages.length; i++) {
2066
+ const msg = session.messages[i];
2067
+ lines.push("## Turn " + i + " (" + msg.role + ")");
2068
+ lines.push("");
2069
+ if (typeof msg.content === "string") {
2070
+ lines.push(msg.content);
2071
+ } else {
2072
+ for (const block of msg.content) {
2073
+ if (block.type === "text") {
2074
+ lines.push(block.text);
2075
+ } else if (block.type === "tool_use") {
2076
+ lines.push("**Tool call:** `" + block.name + "`");
2077
+ lines.push("```json\n" + JSON.stringify(block.input, null, 2) + "\n```");
2078
+ } else if (block.type === "tool_result") {
2079
+ const label = block.is_error ? "**Tool error:**" : "**Tool result:**";
2080
+ lines.push(label);
2081
+ lines.push("```\n" + block.content + "\n```");
2082
+ }
2083
+ }
2084
+ }
2085
+ lines.push("");
2086
+ }
2087
+ return lines.join("\n");
2088
+ }
2089
+ async function replayCommand(sessionId, opts = {}) {
2090
+ const pgUrl = SecretsManager7.optional("TUTTI_PG_URL");
2091
+ if (!pgUrl) {
2092
+ console.error(chalk12.red("TUTTI_PG_URL is not set."));
2093
+ console.error(
2094
+ chalk12.dim(
2095
+ "The replay command requires PostgreSQL for session persistence.\nSet TUTTI_PG_URL=postgres://user:pass@host/db in your environment."
2096
+ )
2097
+ );
2098
+ process.exit(1);
2099
+ }
2100
+ const store = new PostgresSessionStore(pgUrl);
2101
+ const spinner = ora7({ color: "cyan" }).start("Loading session...");
2102
+ let session;
2103
+ try {
2104
+ session = await store.getAsync(sessionId);
2105
+ } catch (err) {
2106
+ spinner.fail("Failed to load session");
2107
+ logger12.error(
2108
+ { error: err instanceof Error ? err.message : String(err) },
2109
+ "Session store error"
2110
+ );
2111
+ process.exit(1);
2112
+ }
2113
+ spinner.stop();
2114
+ if (!session) {
2115
+ console.error(chalk12.red("Session not found: " + sessionId));
2116
+ console.error(chalk12.dim("Check the session ID and ensure TUTTI_PG_URL points to the correct database."));
2117
+ await store.close();
2118
+ process.exit(1);
2119
+ }
2120
+ const messages = session.messages;
2121
+ console.log("");
2122
+ console.log(chalk12.cyan.bold(" Tutti Replay"));
2123
+ console.log(chalk12.dim(" Session: " + session.id));
2124
+ console.log(chalk12.dim(" Agent: " + session.agent_name));
2125
+ console.log(chalk12.dim(" Created: " + session.created_at.toISOString()));
2126
+ console.log(chalk12.dim(" Messages: " + messages.length));
2127
+ console.log("");
2128
+ console.log(chalk12.dim(" Commands: list, show <n>, next, prev, inspect, replay-from <n>, export <json|md>, quit"));
2129
+ console.log("");
2130
+ let cursor = 0;
2131
+ const rl = createInterface3({ input: process.stdin, output: process.stdout });
2132
+ try {
2133
+ while (true) {
2134
+ const prompt3 = chalk12.cyan("replay [" + cursor + "/" + (messages.length - 1) + "]> ");
2135
+ const raw = await rl.question(prompt3);
2136
+ const input = raw.trim();
2137
+ if (!input) continue;
2138
+ const [cmd, ...args] = input.split(/\s+/);
2139
+ switch (cmd) {
2140
+ case "quit":
2141
+ case "exit":
2142
+ case "q":
2143
+ console.log(chalk12.dim("Bye."));
2144
+ return;
2145
+ case "list":
2146
+ console.log(renderList(messages));
2147
+ break;
2148
+ case "show": {
2149
+ const n = parseInt(args[0] ?? String(cursor), 10);
2150
+ console.log(renderShow(messages, n));
2151
+ if (n >= 0 && n < messages.length) cursor = n;
2152
+ break;
2153
+ }
2154
+ case "next":
2155
+ if (cursor < messages.length - 1) {
2156
+ cursor++;
2157
+ console.log(renderShow(messages, cursor));
2158
+ } else {
2159
+ console.log(chalk12.dim("Already at last message."));
2160
+ }
2161
+ break;
2162
+ case "prev":
2163
+ if (cursor > 0) {
2164
+ cursor--;
2165
+ console.log(renderShow(messages, cursor));
2166
+ } else {
2167
+ console.log(chalk12.dim("Already at first message."));
2168
+ }
2169
+ break;
2170
+ case "inspect":
2171
+ console.log(renderInspect(messages, cursor));
2172
+ break;
2173
+ case "replay-from": {
2174
+ const turn = parseInt(args[0] ?? "", 10);
2175
+ if (isNaN(turn) || turn < 0 || turn >= messages.length) {
2176
+ console.log(chalk12.red("Usage: replay-from <turn-number>"));
2177
+ break;
2178
+ }
2179
+ await handleReplayFrom(turn, session, rl, opts);
2180
+ break;
2181
+ }
2182
+ case "export": {
2183
+ const format = args[0];
2184
+ if (format === "json") {
2185
+ const filename = "session-" + session.id.slice(0, 8) + ".json";
2186
+ await writeFile(filename, exportJSON(session));
2187
+ console.log(chalk12.green("Exported to " + filename));
2188
+ } else if (format === "md" || format === "markdown") {
2189
+ const filename = "session-" + session.id.slice(0, 8) + ".md";
2190
+ await writeFile(filename, exportMarkdown(session));
2191
+ console.log(chalk12.green("Exported to " + filename));
2192
+ } else {
2193
+ console.log(chalk12.dim("Usage: export <json|md>"));
2194
+ }
2195
+ break;
2196
+ }
2197
+ default:
2198
+ console.log(
2199
+ chalk12.dim("Unknown command. Available: list, show <n>, next, prev, inspect, replay-from <n>, export <json|md>, quit")
2200
+ );
2201
+ }
2202
+ }
2203
+ } finally {
2204
+ rl.close();
2205
+ await store.close();
2206
+ }
2207
+ }
2208
+ async function handleReplayFrom(turn, session, rl, opts) {
2209
+ const scoreFile = resolve12(opts.score ?? "./tutti.score.ts");
2210
+ if (!existsSync12(scoreFile)) {
2211
+ console.log(chalk12.red("Score file not found: " + scoreFile));
2212
+ console.log(chalk12.dim("Use --score to specify the score file."));
2213
+ return;
2214
+ }
2215
+ const originalMsg = session.messages[turn];
2216
+ const originalInput = originalMsg ? messageToText2(originalMsg) : "";
2217
+ const answer = await rl.question(
2218
+ chalk12.cyan("Replay from turn " + turn + " with original input? ") + chalk12.dim("(y / enter new input) ")
2219
+ );
2220
+ const input = answer.trim().toLowerCase() === "y" || answer.trim() === "" ? originalInput : answer.trim();
2221
+ if (!input) {
2222
+ console.log(chalk12.dim("No input provided. Cancelled."));
2223
+ return;
2224
+ }
2225
+ const spinnerLoad = ora7({ color: "cyan" }).start("Loading score...");
2226
+ let score;
2227
+ try {
2228
+ score = await ScoreLoader8.load(scoreFile);
2229
+ } catch (err) {
2230
+ spinnerLoad.fail("Failed to load score");
2231
+ logger12.error({ error: err instanceof Error ? err.message : String(err) }, "Score load error");
2232
+ return;
2233
+ }
2234
+ spinnerLoad.stop();
2235
+ const restoredMessages = session.messages.slice(0, turn);
2236
+ const runtime = new TuttiRuntime5(score);
2237
+ const sessions = runtime.sessions;
2238
+ if ("save" in sessions && typeof sessions.save === "function") {
2239
+ sessions.save({
2240
+ id: session.id,
2241
+ agent_name: session.agent_name,
2242
+ messages: restoredMessages,
2243
+ created_at: session.created_at,
2244
+ updated_at: /* @__PURE__ */ new Date()
2245
+ });
2246
+ }
2247
+ const agentName = session.agent_name;
2248
+ const agent = score.agents[agentName];
2249
+ if (!agent) {
2250
+ console.log(chalk12.red('Agent "' + agentName + '" not found in score.'));
2251
+ return;
2252
+ }
2253
+ const spinnerRun = ora7({ color: "cyan" }).start("Running from turn " + turn + "...");
2254
+ runtime.events.on("token:stream", (e) => {
2255
+ spinnerRun.stop();
2256
+ process.stdout.write(e.text);
2257
+ });
2258
+ try {
2259
+ const result = await runtime.run(agentName, input, session.id);
2260
+ spinnerRun.stop();
2261
+ console.log("");
2262
+ console.log(chalk12.green("Replay complete."));
2263
+ console.log(chalk12.dim(" Turns: " + result.turns));
2264
+ console.log(chalk12.dim(" Tokens: " + result.usage.input_tokens + " in / " + result.usage.output_tokens + " out"));
2265
+ console.log("");
2266
+ console.log(result.output);
2267
+ } catch (err) {
2268
+ spinnerRun.fail("Replay failed");
2269
+ logger12.error({ error: err instanceof Error ? err.message : String(err) }, "Replay error");
2270
+ }
2271
+ }
2272
+
2273
+ // src/commands/schedules.ts
2274
+ import { existsSync as existsSync13 } from "fs";
2275
+ import { resolve as resolve13 } from "path";
2276
+ import chalk13 from "chalk";
2277
+ import {
2278
+ ScoreLoader as ScoreLoader9,
2279
+ SchedulerEngine as SchedulerEngine2,
2280
+ PostgresScheduleStore as PostgresScheduleStore2,
2281
+ MemoryScheduleStore as MemoryScheduleStore2,
2282
+ AgentRunner as AgentRunner2,
2283
+ EventBus as EventBus2,
2284
+ InMemorySessionStore as InMemorySessionStore4,
2285
+ SecretsManager as SecretsManager8,
2286
+ createLogger as createLogger13
2287
+ } from "@tuttiai/core";
2288
+ var logger13 = createLogger13("tutti-cli");
2289
+ function resolveStore2() {
2290
+ const pgUrl = SecretsManager8.optional("TUTTI_PG_URL");
2291
+ if (pgUrl) {
2292
+ return new PostgresScheduleStore2({ connection_string: pgUrl });
2293
+ }
2294
+ logger13.warn("TUTTI_PG_URL not set \u2014 using in-memory store (schedules are ephemeral)");
2295
+ return new MemoryScheduleStore2();
2296
+ }
2297
+ async function closeStore(store) {
2298
+ if ("close" in store && typeof store.close === "function") {
2299
+ await store.close();
2300
+ }
2301
+ }
2302
+ function formatTrigger(r) {
2303
+ if (r.config.cron) return "cron: " + r.config.cron;
2304
+ if (r.config.every) return "every " + r.config.every;
2305
+ if (r.config.at) return "at " + r.config.at;
2306
+ return "?";
2307
+ }
2308
+ function pad(s, len) {
2309
+ return s.length >= len ? s : s + " ".repeat(len - s.length);
2310
+ }
2311
+ async function schedulesListCommand() {
2312
+ const store = resolveStore2();
2313
+ try {
2314
+ const records = await store.list();
2315
+ if (records.length === 0) {
2316
+ console.log(chalk13.dim("No schedules found."));
2317
+ console.log(chalk13.dim('Run "tutti-ai schedule" to start the scheduler daemon.'));
2318
+ return;
2319
+ }
2320
+ console.log("");
2321
+ console.log(
2322
+ chalk13.dim(
2323
+ " " + pad("ID", 20) + pad("AGENT", 16) + pad("TRIGGER", 22) + pad("ENABLED", 10) + pad("RUNS", 8) + "CREATED"
2324
+ )
2325
+ );
2326
+ console.log(chalk13.dim(" " + "\u2500".repeat(90)));
2327
+ for (const r of records) {
2328
+ const enabled = r.enabled ? chalk13.green("yes") : chalk13.red("no") + " ";
2329
+ const maxLabel = r.config.max_runs ? r.run_count + "/" + r.config.max_runs : String(r.run_count);
2330
+ console.log(
2331
+ " " + chalk13.bold(pad(r.id, 20)) + pad(r.agent_id, 16) + pad(formatTrigger(r), 22) + pad(enabled, 10) + pad(maxLabel, 8) + chalk13.dim(r.created_at.toISOString().slice(0, 10))
2332
+ );
2333
+ }
2334
+ console.log("");
2335
+ } finally {
2336
+ await closeStore(store);
2337
+ }
2338
+ }
2339
+ async function schedulesEnableCommand(id) {
2340
+ const store = resolveStore2();
2341
+ try {
2342
+ const record = await store.get(id);
2343
+ if (!record) {
2344
+ console.error(chalk13.red('Schedule "' + id + '" not found.'));
2345
+ process.exit(1);
2346
+ }
2347
+ await store.setEnabled(id, true);
2348
+ console.log(chalk13.green('Schedule "' + id + '" enabled.'));
2349
+ } finally {
2350
+ await closeStore(store);
2351
+ }
2352
+ }
2353
+ async function schedulesDisableCommand(id) {
2354
+ const store = resolveStore2();
2355
+ try {
2356
+ const record = await store.get(id);
2357
+ if (!record) {
2358
+ console.error(chalk13.red('Schedule "' + id + '" not found.'));
2359
+ process.exit(1);
2360
+ }
2361
+ await store.setEnabled(id, false);
2362
+ console.log(chalk13.yellow('Schedule "' + id + '" disabled.'));
2363
+ } finally {
2364
+ await closeStore(store);
2365
+ }
2366
+ }
2367
+ async function schedulesTriggerCommand(id, scorePath) {
2368
+ const file = resolve13(scorePath ?? "./tutti.score.ts");
2369
+ if (!existsSync13(file)) {
2370
+ console.error(chalk13.red("Score file not found: " + file));
2371
+ process.exit(1);
2372
+ }
2373
+ const score = await ScoreLoader9.load(file);
2374
+ const events = new EventBus2();
2375
+ const sessions = new InMemorySessionStore4();
2376
+ const runner = new AgentRunner2(score.provider, events, sessions);
2377
+ const store = resolveStore2();
2378
+ try {
2379
+ const record = await store.get(id);
2380
+ if (!record) {
2381
+ console.error(chalk13.red('Schedule "' + id + '" not found.'));
2382
+ process.exit(1);
2383
+ }
2384
+ const agent = score.agents[record.agent_id];
2385
+ if (!agent) {
2386
+ console.error(chalk13.red('Agent "' + record.agent_id + '" not found in score.'));
2387
+ process.exit(1);
2388
+ }
2389
+ const resolvedAgent = agent.model ? agent : { ...agent, model: score.default_model ?? "claude-sonnet-4-20250514" };
2390
+ const engine = new SchedulerEngine2(store, runner, events);
2391
+ await engine.schedule(id, resolvedAgent, record.config);
2392
+ engine.start();
2393
+ console.log(chalk13.cyan('Triggering "' + id + '" (' + record.agent_id + ")..."));
2394
+ const run2 = await engine.trigger(id);
2395
+ engine.stop();
2396
+ if (run2.error) {
2397
+ console.log(chalk13.red(" Error: " + run2.error));
2398
+ process.exit(1);
2399
+ }
2400
+ const duration = run2.completed_at && run2.triggered_at ? run2.completed_at.getTime() - run2.triggered_at.getTime() : 0;
2401
+ console.log(chalk13.green(" Completed in " + duration + "ms"));
2402
+ if (run2.result) {
2403
+ const preview = run2.result.length > 200 ? run2.result.slice(0, 200) + "..." : run2.result;
2404
+ console.log(chalk13.dim(" Output: " + preview));
2405
+ }
2406
+ } finally {
2407
+ await closeStore(store);
2408
+ }
2409
+ }
2410
+ async function schedulesRunsCommand(id) {
2411
+ const store = resolveStore2();
2412
+ try {
2413
+ const record = await store.get(id);
2414
+ if (!record) {
2415
+ console.error(chalk13.red('Schedule "' + id + '" not found.'));
2416
+ process.exit(1);
2417
+ }
2418
+ if ("getRuns" in store && typeof store.getRuns === "function") {
2419
+ const runs = store.getRuns(id);
2420
+ if (runs.length === 0) {
2421
+ console.log(chalk13.dim("No runs recorded for this schedule."));
2422
+ return;
2423
+ }
2424
+ const recent = runs.slice(-20);
2425
+ console.log("");
2426
+ console.log(chalk13.dim(" Showing last " + recent.length + " of " + runs.length + " runs:"));
2427
+ console.log("");
2428
+ for (const run2 of recent) {
2429
+ const duration = run2.completed_at && run2.triggered_at ? run2.completed_at.getTime() - run2.triggered_at.getTime() + "ms" : "?";
2430
+ const status = run2.error ? chalk13.red("error") : chalk13.green("ok");
2431
+ const preview = run2.error ? run2.error.slice(0, 80) : (run2.result ?? "").slice(0, 80);
2432
+ console.log(
2433
+ " " + chalk13.dim(run2.triggered_at.toISOString()) + " " + status + " " + chalk13.dim(duration) + " " + preview
2434
+ );
2435
+ }
2436
+ console.log("");
2437
+ } else {
2438
+ console.log(chalk13.dim('Schedule "' + id + '" has completed ' + record.run_count + " runs."));
2439
+ console.log(chalk13.dim("Full run history requires the MemoryScheduleStore or a future tutti_schedule_runs table."));
2440
+ }
2441
+ } finally {
2442
+ await closeStore(store);
2443
+ }
2444
+ }
2445
+
1868
2446
  // src/index.ts
1869
2447
  config();
1870
- var logger11 = createLogger11("tutti-cli");
2448
+ var logger14 = createLogger14("tutti-cli");
1871
2449
  process.on("unhandledRejection", (reason) => {
1872
- logger11.error({ error: reason instanceof Error ? reason.message : String(reason) }, "Unhandled rejection");
2450
+ logger14.error({ error: reason instanceof Error ? reason.message : String(reason) }, "Unhandled rejection");
1873
2451
  process.exit(1);
1874
2452
  });
1875
2453
  process.on("uncaughtException", (err) => {
1876
- logger11.error({ error: err.message }, "Fatal error");
2454
+ logger14.error({ error: err.message }, "Fatal error");
1877
2455
  process.exit(1);
1878
2456
  });
1879
2457
  var program = new Command();
1880
- program.name("tutti-ai").description("Tutti \u2014 multi-agent orchestration. All agents. All together.").version("0.11.1");
2458
+ program.name("tutti-ai").description("Tutti \u2014 multi-agent orchestration. All agents. All together.").version("0.13.0");
1881
2459
  program.command("init [project-name]").description("Create a new Tutti project").option("-t, --template <id>", "Project template to use").action(async (projectName, opts) => {
1882
2460
  await initCommand(projectName, opts.template);
1883
2461
  });
@@ -1933,5 +2511,27 @@ program.command("publish").description("Publish the current voice to npm and the
1933
2511
  program.command("eval <suite-file>").description("Run an evaluation suite against a score").option("--ci", "Exit with code 1 if any case fails").option("-s, --score <path>", "Path to score file (default: ./tutti.score.ts)").action(async (suitePath, opts) => {
1934
2512
  await evalCommand(suitePath, opts);
1935
2513
  });
2514
+ program.command("replay <session-id>").description("Time-travel debugger \u2014 navigate and replay a session from PostgreSQL").option("-s, --score <path>", "Path to score file for replay-from (default: ./tutti.score.ts)").action(async (sessionId, opts) => {
2515
+ await replayCommand(sessionId, { score: opts.score });
2516
+ });
2517
+ program.command("schedule [score]").description("Start the scheduler daemon \u2014 runs agents on their configured schedules").action(async (score) => {
2518
+ await scheduleCommand(score);
2519
+ });
2520
+ var schedulesCmd = program.command("schedules").description("Manage scheduled agents");
2521
+ schedulesCmd.command("list").description("Show all registered schedules").action(async () => {
2522
+ await schedulesListCommand();
2523
+ });
2524
+ schedulesCmd.command("enable <id>").description("Enable a disabled schedule").action(async (id) => {
2525
+ await schedulesEnableCommand(id);
2526
+ });
2527
+ schedulesCmd.command("disable <id>").description("Disable a schedule without deleting it").action(async (id) => {
2528
+ await schedulesDisableCommand(id);
2529
+ });
2530
+ schedulesCmd.command("trigger <id>").description("Manually trigger a scheduled run immediately").option("-s, --score <path>", "Path to score file (default: ./tutti.score.ts)").action(async (id, opts) => {
2531
+ await schedulesTriggerCommand(id, opts.score);
2532
+ });
2533
+ schedulesCmd.command("runs <id>").description("Show run history for a schedule (last 20 runs)").action(async (id) => {
2534
+ await schedulesRunsCommand(id);
2535
+ });
1936
2536
  program.parse();
1937
2537
  //# sourceMappingURL=index.js.map