@tuttiai/cli 0.12.0 → 0.14.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 createLogger13 } from "@tuttiai/core";
5
+ import { createLogger as createLogger15 } from "@tuttiai/core";
6
6
  import { Command } from "commander";
7
7
 
8
8
  // src/commands/init.ts
@@ -718,7 +718,8 @@ async function resumeCommand(sessionId, opts) {
718
718
  }
719
719
  }
720
720
  const agentName = resolveAgentName(score, opts.agent);
721
- const agent = score.agents[agentName];
721
+ const agentMap = new Map(Object.entries(score.agents));
722
+ const agent = agentMap.get(agentName);
722
723
  if (!agent) {
723
724
  logger3.error(
724
725
  { agent: agentName, available: Object.keys(score.agents) },
@@ -938,14 +939,14 @@ import chalk4 from "chalk";
938
939
  import ora3 from "ora";
939
940
  import { createLogger as createLogger4 } from "@tuttiai/core";
940
941
  var logger4 = createLogger4("tutti-cli");
941
- var OFFICIAL_VOICES = {
942
- filesystem: {
942
+ var OFFICIAL_VOICES = /* @__PURE__ */ new Map([
943
+ ["filesystem", {
943
944
  package: "@tuttiai/filesystem",
944
945
  setup: ` Add to your score:
945
946
  ${chalk4.cyan('import { FilesystemVoice } from "@tuttiai/filesystem"')}
946
947
  ${chalk4.cyan("voices: [new FilesystemVoice()]")}`
947
- },
948
- github: {
948
+ }],
949
+ ["github", {
949
950
  package: "@tuttiai/github",
950
951
  setup: ` Add ${chalk4.bold("GITHUB_TOKEN")} to your .env file:
951
952
  ${chalk4.cyan("GITHUB_TOKEN=ghp_your_token_here")}
@@ -953,8 +954,8 @@ var OFFICIAL_VOICES = {
953
954
  Add to your score:
954
955
  ${chalk4.cyan('import { GitHubVoice } from "@tuttiai/github"')}
955
956
  ${chalk4.cyan("voices: [new GitHubVoice()]")}`
956
- },
957
- playwright: {
957
+ }],
958
+ ["playwright", {
958
959
  package: "@tuttiai/playwright",
959
960
  setup: ` Install the browser:
960
961
  ${chalk4.cyan("npx playwright install chromium")}
@@ -962,8 +963,8 @@ var OFFICIAL_VOICES = {
962
963
  Add to your score:
963
964
  ${chalk4.cyan('import { PlaywrightVoice } from "@tuttiai/playwright"')}
964
965
  ${chalk4.cyan("voices: [new PlaywrightVoice()]")}`
965
- },
966
- postgres: {
966
+ }],
967
+ ["postgres", {
967
968
  package: "pg",
968
969
  setup: ` Add ${chalk4.bold("DATABASE_URL")} to your .env file:
969
970
  ${chalk4.cyan("DATABASE_URL=postgres://user:pass@localhost:5432/tutti")}
@@ -976,11 +977,12 @@ var OFFICIAL_VOICES = {
976
977
 
977
978
  Use the async factory for initialization:
978
979
  ${chalk4.cyan("const tutti = await TuttiRuntime.create(score)")}`
979
- }
980
- };
980
+ }]
981
+ ]);
981
982
  function resolvePackageName(input) {
982
- if (OFFICIAL_VOICES[input]) {
983
- return OFFICIAL_VOICES[input].package;
983
+ const voice = OFFICIAL_VOICES.get(input);
984
+ if (voice) {
985
+ return voice.package;
984
986
  }
985
987
  if (input.startsWith("@")) {
986
988
  return input;
@@ -1023,7 +1025,7 @@ function addCommand(voiceName) {
1023
1025
  logger4.error({ error: message, package: packageName }, "Installation failed");
1024
1026
  process.exit(1);
1025
1027
  }
1026
- const official = OFFICIAL_VOICES[voiceName];
1028
+ const official = OFFICIAL_VOICES.get(voiceName);
1027
1029
  if (official) {
1028
1030
  console.log();
1029
1031
  console.log(" Setup:");
@@ -1051,8 +1053,12 @@ import {
1051
1053
  createLogger as createLogger5
1052
1054
  } from "@tuttiai/core";
1053
1055
  var logger5 = createLogger5("tutti-cli");
1054
- var ok = (msg) => console.log(chalk5.green(" \u2714 " + msg));
1055
- var fail = (msg) => console.log(chalk5.red(" \u2718 " + msg));
1056
+ var ok = (msg) => {
1057
+ console.log(chalk5.green(" \u2714 " + msg));
1058
+ };
1059
+ var fail = (msg) => {
1060
+ console.log(chalk5.red(" \u2718 " + msg));
1061
+ };
1056
1062
  async function checkCommand(scorePath) {
1057
1063
  const file = resolve5(scorePath ?? "./tutti.score.ts");
1058
1064
  console.log(chalk5.cyan(`
@@ -1101,10 +1107,10 @@ Checking ${file}...
1101
1107
  for (const [agentKey, agent] of Object.entries(score.agents)) {
1102
1108
  for (const voice of agent.voices) {
1103
1109
  const voiceName = voice.name;
1104
- const voiceEnvMap = {
1105
- github: "GITHUB_TOKEN"
1106
- };
1107
- const envVar = voiceEnvMap[voiceName];
1110
+ const voiceEnvMap = /* @__PURE__ */ new Map([
1111
+ ["github", "GITHUB_TOKEN"]
1112
+ ]);
1113
+ const envVar = voiceEnvMap.get(voiceName);
1108
1114
  if (envVar) {
1109
1115
  const key = SecretsManager3.optional(envVar);
1110
1116
  if (key) {
@@ -1360,8 +1366,8 @@ async function fetchRegistry() {
1360
1366
  }
1361
1367
  }
1362
1368
  function toolCount(name) {
1363
- const counts = { filesystem: 7, github: 10, playwright: 12 };
1364
- return counts[name] ?? 0;
1369
+ const counts = /* @__PURE__ */ new Map([["filesystem", 7], ["github", 10], ["playwright", 12]]);
1370
+ return counts.get(name) ?? 0;
1365
1371
  }
1366
1372
  function matchesQuery(voice, query) {
1367
1373
  const q = query.toLowerCase();
@@ -1464,7 +1470,9 @@ function fail2(msg) {
1464
1470
  console.error(chalk8.red(" " + msg));
1465
1471
  process.exit(1);
1466
1472
  }
1467
- var ok2 = (msg) => console.log(chalk8.green(" \u2714 " + msg));
1473
+ var ok2 = (msg) => {
1474
+ console.log(chalk8.green(" \u2714 " + msg));
1475
+ };
1468
1476
  async function publishCommand(opts) {
1469
1477
  const cwd = process.cwd();
1470
1478
  const pkg = readPkg(cwd);
@@ -1481,8 +1489,8 @@ async function publishCommand(opts) {
1481
1489
  if (!pkg.license) missing.push("license");
1482
1490
  if (!pkg.exports) missing.push("exports");
1483
1491
  if (missing.length > 0) fail2("package.json is missing: " + missing.join(", "));
1484
- const name = pkg.name;
1485
- const version = pkg.version;
1492
+ const name = pkg.name ?? "";
1493
+ const version = pkg.version ?? "";
1486
1494
  const validName = name.startsWith("@tuttiai/") || name.startsWith("tutti");
1487
1495
  if (!validName) fail2("Package name must start with @tuttiai/ or tutti \u2014 got: " + name);
1488
1496
  const src = readFileSync3(resolve8(cwd, "src/index.ts"), "utf-8");
@@ -1527,7 +1535,7 @@ async function publishCommand(opts) {
1527
1535
  console.error(chalk8.dim(" " + msg));
1528
1536
  process.exit(1);
1529
1537
  }
1530
- const fileLines = packOutput.split("\n").filter((l) => l.includes("npm notice") && /\d+(\.\d+)?\s*[kM]?B\s/.test(l)).map((l) => l.replace(/npm notice\s*/, ""));
1538
+ const fileLines = packOutput.split("\n").filter((l) => l.includes("npm notice") && /\d+(?:\.\d+)?\s*[kM]?B\s/.test(l)).map((l) => l.replace(/npm notice\s*/, ""));
1531
1539
  if (fileLines.length > 0) {
1532
1540
  console.log(chalk8.dim(" Files:"));
1533
1541
  for (const line of fileLines) {
@@ -1613,13 +1621,19 @@ async function openRegistryPR(packageName, version, description, token) {
1613
1621
  author: isOfficial ? "tuttiai" : packageName.split("/")[0]?.replace("@", "") ?? "community",
1614
1622
  tags: [shortName]
1615
1623
  };
1616
- if (!registry[section]) registry[section] = [];
1617
- const exists = registry[section].some((v) => v.package === packageName);
1618
- if (exists) {
1619
- const idx = registry[section].findIndex((v) => v.package === packageName);
1620
- registry[section][idx] = { ...registry[section][idx], ...entry };
1624
+ const sectionList = section === "official" ? registry.official : registry.community;
1625
+ const list = sectionList ?? [];
1626
+ if (section === "official") {
1627
+ registry.official = list;
1621
1628
  } else {
1622
- registry[section].push(entry);
1629
+ registry.community = list;
1630
+ }
1631
+ const existingIdx = list.findIndex((v) => v.package === packageName);
1632
+ if (existingIdx >= 0) {
1633
+ const existing = list.at(existingIdx);
1634
+ list.splice(existingIdx, 1, { ...existing, ...entry });
1635
+ } else {
1636
+ list.push(entry);
1623
1637
  }
1624
1638
  const updatedContent = Buffer.from(JSON.stringify(registry, null, 2) + "\n").toString("base64");
1625
1639
  const mainRes = await fetch(
@@ -1764,7 +1778,7 @@ async function serveCommand(scorePath, options = {}) {
1764
1778
  }
1765
1779
  const agentNames = Object.keys(score.agents);
1766
1780
  const agentName = options.agent ?? (typeof score.entry === "string" ? score.entry : void 0) ?? agentNames[0];
1767
- if (!agentName || !score.agents[agentName]) {
1781
+ if (!agentName || !Object.hasOwn(score.agents, agentName)) {
1768
1782
  logger10.error(
1769
1783
  { requested: agentName, available: agentNames },
1770
1784
  "Agent not found in score"
@@ -1785,7 +1799,8 @@ async function serveCommand(scorePath, options = {}) {
1785
1799
  reactive.on("reloaded", () => {
1786
1800
  void (async () => {
1787
1801
  try {
1788
- const nextScore = reactive.current;
1802
+ const nextScore = reactive?.current;
1803
+ if (!nextScore) return;
1789
1804
  const nextRuntime = buildRuntime2(nextScore, sharedSessions);
1790
1805
  const nextApp = await buildApp(nextRuntime, agentName, port, host, options.apiKey);
1791
1806
  await app.close();
@@ -1960,28 +1975,796 @@ async function scheduleCommand(scorePath) {
1960
1975
  await new Promise(() => void 0);
1961
1976
  }
1962
1977
 
1963
- // src/commands/schedules.ts
1964
- import { existsSync as existsSync12 } from "fs";
1978
+ // src/commands/update.ts
1979
+ import { execSync as execSync3 } from "child_process";
1980
+ import { existsSync as existsSync12, readFileSync as readFileSync5 } from "fs";
1965
1981
  import { resolve as resolve12 } from "path";
1966
1982
  import chalk12 from "chalk";
1983
+ import ora7 from "ora";
1984
+ var TUTTI_PACKAGES = [
1985
+ "@tuttiai/core",
1986
+ "@tuttiai/cli",
1987
+ "@tuttiai/types",
1988
+ "@tuttiai/server",
1989
+ "@tuttiai/filesystem",
1990
+ "@tuttiai/github",
1991
+ "@tuttiai/playwright",
1992
+ "@tuttiai/mcp",
1993
+ "@tuttiai/web",
1994
+ "@tuttiai/sandbox",
1995
+ "@tuttiai/rag"
1996
+ ];
1997
+ function getInstalledVersion(pkg) {
1998
+ try {
1999
+ const out = execSync3(`npm list ${pkg} --depth=0 --json 2>/dev/null`, {
2000
+ encoding: "utf-8",
2001
+ stdio: ["pipe", "pipe", "pipe"]
2002
+ });
2003
+ const data = JSON.parse(out);
2004
+ const deps = data.dependencies;
2005
+ if (!deps) return null;
2006
+ for (const [name, info] of Object.entries(deps)) {
2007
+ if (name === pkg && info.version) return info.version;
2008
+ }
2009
+ return null;
2010
+ } catch {
2011
+ return null;
2012
+ }
2013
+ }
2014
+ function getLatestVersion(pkg) {
2015
+ try {
2016
+ return execSync3(`npm view ${pkg} version 2>/dev/null`, {
2017
+ encoding: "utf-8",
2018
+ stdio: ["pipe", "pipe", "pipe"]
2019
+ }).trim();
2020
+ } catch {
2021
+ return null;
2022
+ }
2023
+ }
2024
+ function detectPackageManager() {
2025
+ const cwd = process.cwd();
2026
+ if (existsSync12(resolve12(cwd, "yarn.lock"))) return "yarn";
2027
+ if (existsSync12(resolve12(cwd, "pnpm-lock.yaml"))) return "pnpm";
2028
+ return "npm";
2029
+ }
2030
+ function isGlobalInstall() {
2031
+ try {
2032
+ const globalPrefix = execSync3("npm prefix -g", {
2033
+ encoding: "utf-8",
2034
+ stdio: ["pipe", "pipe", "pipe"]
2035
+ }).trim();
2036
+ return process.argv[1]?.startsWith(globalPrefix) ?? false;
2037
+ } catch {
2038
+ return false;
2039
+ }
2040
+ }
2041
+ function updateCommand() {
2042
+ console.log();
2043
+ console.log(chalk12.cyan.bold(" Tutti Update"));
2044
+ console.log();
2045
+ const spinner = ora7("Checking for updates...").start();
2046
+ const cliCurrent = getInstalledVersion("@tuttiai/cli") ?? "unknown";
2047
+ const cliLatest = getLatestVersion("@tuttiai/cli");
2048
+ spinner.stop();
2049
+ if (cliLatest && cliCurrent !== cliLatest) {
2050
+ console.log(
2051
+ chalk12.yellow(" CLI update available: ") + chalk12.dim(cliCurrent) + " \u2192 " + chalk12.green(cliLatest)
2052
+ );
2053
+ if (isGlobalInstall()) {
2054
+ const updateSpinner2 = ora7("Updating global CLI...").start();
2055
+ try {
2056
+ execSync3("npm install -g tutti-ai@latest", { stdio: "pipe" });
2057
+ updateSpinner2.succeed("CLI updated to " + cliLatest);
2058
+ } catch {
2059
+ updateSpinner2.fail("Failed to update global CLI");
2060
+ console.log(chalk12.dim(" Run manually: npm install -g tutti-ai@latest"));
2061
+ }
2062
+ } else {
2063
+ console.log(chalk12.dim(" Global: npm install -g tutti-ai@latest"));
2064
+ }
2065
+ } else {
2066
+ console.log(chalk12.green(" CLI is up to date") + chalk12.dim(" (" + cliCurrent + ")"));
2067
+ }
2068
+ const pkgPath = resolve12(process.cwd(), "package.json");
2069
+ if (!existsSync12(pkgPath)) {
2070
+ console.log();
2071
+ console.log(chalk12.dim(" No package.json found \u2014 skipping project dependency check."));
2072
+ console.log();
2073
+ return;
2074
+ }
2075
+ let pkg;
2076
+ try {
2077
+ pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
2078
+ } catch {
2079
+ console.log(chalk12.dim(" Could not read package.json"));
2080
+ return;
2081
+ }
2082
+ const allDeps = /* @__PURE__ */ new Map();
2083
+ if (pkg.dependencies) {
2084
+ for (const [name, version] of Object.entries(pkg.dependencies)) {
2085
+ allDeps.set(name, version);
2086
+ }
2087
+ }
2088
+ if (pkg.devDependencies) {
2089
+ for (const [name, version] of Object.entries(pkg.devDependencies)) {
2090
+ allDeps.set(name, version);
2091
+ }
2092
+ }
2093
+ const installed = TUTTI_PACKAGES.filter((p) => allDeps.has(p));
2094
+ if (installed.length === 0) {
2095
+ console.log();
2096
+ console.log(chalk12.dim(" No @tuttiai packages found in this project."));
2097
+ console.log();
2098
+ return;
2099
+ }
2100
+ console.log();
2101
+ console.log(" " + chalk12.bold("Project packages:"));
2102
+ const toUpdate = [];
2103
+ for (const name of installed) {
2104
+ const current = allDeps.get(name) ?? "?";
2105
+ const latest = getLatestVersion(name);
2106
+ if (!latest) {
2107
+ console.log(" " + chalk12.dim(name) + " " + current + chalk12.dim(" (could not check)"));
2108
+ continue;
2109
+ }
2110
+ const cleanCurrent = current.replace(/^[\^~]/, "");
2111
+ if (cleanCurrent === latest) {
2112
+ console.log(" " + chalk12.green("\u2714") + " " + name + " " + chalk12.dim(latest));
2113
+ } else {
2114
+ console.log(
2115
+ " " + chalk12.yellow("\u2191") + " " + name + " " + chalk12.dim(cleanCurrent) + " \u2192 " + chalk12.green(latest)
2116
+ );
2117
+ toUpdate.push(name + "@latest");
2118
+ }
2119
+ }
2120
+ if (toUpdate.length === 0) {
2121
+ console.log();
2122
+ console.log(chalk12.green(" All packages are up to date."));
2123
+ console.log();
2124
+ return;
2125
+ }
2126
+ console.log();
2127
+ const pm = detectPackageManager();
2128
+ const installCmd = pm === "yarn" ? "yarn add " + toUpdate.join(" ") : pm === "pnpm" ? "pnpm add " + toUpdate.join(" ") : "npm install " + toUpdate.join(" ");
2129
+ const updateSpinner = ora7("Updating " + toUpdate.length + " package(s)...").start();
2130
+ try {
2131
+ execSync3(installCmd, { cwd: process.cwd(), stdio: "pipe" });
2132
+ updateSpinner.succeed("Updated " + toUpdate.length + " package(s)");
2133
+ } catch {
2134
+ updateSpinner.fail("Update failed");
2135
+ console.log(chalk12.dim(" Run manually: " + installCmd));
2136
+ }
2137
+ console.log();
2138
+ }
2139
+
2140
+ // src/commands/outdated.ts
2141
+ import { execSync as execSync4 } from "child_process";
2142
+ import { existsSync as existsSync13, readFileSync as readFileSync6 } from "fs";
2143
+ import { resolve as resolve13 } from "path";
2144
+ import chalk13 from "chalk";
2145
+ import ora8 from "ora";
2146
+ function getLatestVersion2(pkg) {
2147
+ try {
2148
+ return execSync4(`npm view ${pkg} version 2>/dev/null`, {
2149
+ encoding: "utf-8",
2150
+ stdio: ["pipe", "pipe", "pipe"]
2151
+ }).trim();
2152
+ } catch {
2153
+ return null;
2154
+ }
2155
+ }
2156
+ function pad(s, len) {
2157
+ return s.length >= len ? s : s + " ".repeat(len - s.length);
2158
+ }
2159
+ function outdatedCommand() {
2160
+ const pkgPath = resolve13(process.cwd(), "package.json");
2161
+ if (!existsSync13(pkgPath)) {
2162
+ console.error(chalk13.red("No package.json found in the current directory."));
2163
+ console.error(chalk13.dim('Run "tutti-ai init" to create a new project.'));
2164
+ process.exit(1);
2165
+ }
2166
+ let pkg;
2167
+ try {
2168
+ pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
2169
+ } catch {
2170
+ console.error(chalk13.red("Could not parse package.json"));
2171
+ process.exit(1);
2172
+ }
2173
+ const allDeps = /* @__PURE__ */ new Map();
2174
+ if (pkg.dependencies) {
2175
+ for (const [name, version] of Object.entries(pkg.dependencies)) {
2176
+ allDeps.set(name, version);
2177
+ }
2178
+ }
2179
+ if (pkg.devDependencies) {
2180
+ for (const [name, version] of Object.entries(pkg.devDependencies)) {
2181
+ allDeps.set(name, version);
2182
+ }
2183
+ }
2184
+ const tuttiDeps = [...allDeps.entries()].filter(([name]) => name.startsWith("@tuttiai/"));
2185
+ if (tuttiDeps.length === 0) {
2186
+ console.log(chalk13.dim("No @tuttiai packages found in this project."));
2187
+ return;
2188
+ }
2189
+ const spinner = ora8("Checking npm registry...").start();
2190
+ const results = [];
2191
+ for (const [name, version] of tuttiDeps) {
2192
+ const latest = getLatestVersion2(name);
2193
+ const current = version.replace(/^[\^~]/, "");
2194
+ results.push({
2195
+ name,
2196
+ current,
2197
+ latest: latest ?? "?",
2198
+ outdated: latest !== null && current !== latest
2199
+ });
2200
+ }
2201
+ spinner.stop();
2202
+ console.log();
2203
+ console.log(
2204
+ chalk13.dim(
2205
+ " " + pad("PACKAGE", 28) + pad("CURRENT", 12) + pad("LATEST", 12) + "STATUS"
2206
+ )
2207
+ );
2208
+ console.log(chalk13.dim(" " + "\u2500".repeat(64)));
2209
+ let outdatedCount = 0;
2210
+ for (const r of results) {
2211
+ const status = r.outdated ? chalk13.yellow("update available") : chalk13.green("up to date");
2212
+ if (r.outdated) outdatedCount++;
2213
+ console.log(
2214
+ " " + pad(r.name, 28) + pad(r.current, 12) + pad(r.latest, 12) + status
2215
+ );
2216
+ }
2217
+ console.log();
2218
+ if (outdatedCount > 0) {
2219
+ console.log(
2220
+ chalk13.yellow(" " + outdatedCount + " package(s) can be updated.") + chalk13.dim(" Run: tutti-ai update")
2221
+ );
2222
+ } else {
2223
+ console.log(chalk13.green(" All packages are up to date."));
2224
+ }
2225
+ console.log();
2226
+ }
2227
+
2228
+ // src/commands/info.ts
2229
+ import { existsSync as existsSync14, readFileSync as readFileSync7 } from "fs";
2230
+ import { resolve as resolve14 } from "path";
2231
+ import chalk14 from "chalk";
2232
+ import { ScoreLoader as ScoreLoader8, createLogger as createLogger12 } from "@tuttiai/core";
2233
+ var logger12 = createLogger12("tutti-cli");
2234
+ function pad2(s, len) {
2235
+ return s.length >= len ? s : s + " ".repeat(len - s.length);
2236
+ }
2237
+ async function infoCommand(scorePath) {
2238
+ const pkgPath = resolve14(process.cwd(), "package.json");
2239
+ let projectName = "(unknown)";
2240
+ let projectVersion = "(unknown)";
2241
+ const installedDeps = /* @__PURE__ */ new Map();
2242
+ if (existsSync14(pkgPath)) {
2243
+ try {
2244
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
2245
+ projectName = pkg.name ?? "(unnamed)";
2246
+ projectVersion = pkg.version ?? "0.0.0";
2247
+ if (pkg.dependencies) {
2248
+ for (const [name, version] of Object.entries(pkg.dependencies)) {
2249
+ installedDeps.set(name, version);
2250
+ }
2251
+ }
2252
+ if (pkg.devDependencies) {
2253
+ for (const [name, version] of Object.entries(pkg.devDependencies)) {
2254
+ installedDeps.set(name, version);
2255
+ }
2256
+ }
2257
+ } catch {
2258
+ }
2259
+ }
2260
+ console.log();
2261
+ console.log(chalk14.cyan.bold(" Tutti Project Info"));
2262
+ console.log();
2263
+ console.log(" " + chalk14.dim("Project:") + " " + chalk14.bold(projectName) + " " + chalk14.dim(projectVersion));
2264
+ const tuttiPkgs = [...installedDeps.entries()].filter(([name]) => name.startsWith("@tuttiai/"));
2265
+ if (tuttiPkgs.length > 0) {
2266
+ console.log();
2267
+ console.log(" " + chalk14.bold("Packages:"));
2268
+ for (const [name, version] of tuttiPkgs) {
2269
+ console.log(" " + pad2(name, 28) + chalk14.dim(version));
2270
+ }
2271
+ }
2272
+ const scoreFile = resolve14(scorePath ?? "./tutti.score.ts");
2273
+ if (!existsSync14(scoreFile)) {
2274
+ console.log();
2275
+ console.log(chalk14.dim(" No score file found at " + scoreFile));
2276
+ console.log(chalk14.dim(' Run "tutti-ai init" to create a new project.'));
2277
+ console.log();
2278
+ return;
2279
+ }
2280
+ let score;
2281
+ try {
2282
+ score = await ScoreLoader8.load(scoreFile);
2283
+ } catch (err) {
2284
+ logger12.error(
2285
+ { error: err instanceof Error ? err.message : String(err) },
2286
+ "Failed to load score"
2287
+ );
2288
+ console.log(chalk14.dim(" Score file found but failed to load."));
2289
+ console.log();
2290
+ return;
2291
+ }
2292
+ console.log(" " + chalk14.dim("Score:") + " " + (score.name ?? scoreFile));
2293
+ const agentEntries = Object.entries(score.agents);
2294
+ console.log();
2295
+ console.log(" " + chalk14.bold("Agents:") + chalk14.dim(" (" + agentEntries.length + ")"));
2296
+ for (const [id, agent] of agentEntries) {
2297
+ const voiceNames = agent.voices.map((v) => v.name).join(", ") || "none";
2298
+ const model = agent.model ?? score.default_model ?? "(default)";
2299
+ const flags = [];
2300
+ if (agent.streaming) flags.push("streaming");
2301
+ if (agent.allow_human_input) flags.push("hitl");
2302
+ if (agent.durable) flags.push("durable");
2303
+ if (agent.schedule) flags.push("scheduled");
2304
+ if (agent.outputSchema) flags.push("structured");
2305
+ if (agent.beforeRun ?? agent.afterRun) flags.push("guardrails");
2306
+ console.log();
2307
+ console.log(" " + chalk14.bold(id) + chalk14.dim(" (" + agent.name + ")"));
2308
+ console.log(" " + chalk14.dim("Model: ") + model);
2309
+ console.log(" " + chalk14.dim("Voices: ") + voiceNames);
2310
+ if (flags.length > 0) {
2311
+ console.log(" " + chalk14.dim("Flags: ") + flags.join(", "));
2312
+ }
2313
+ if (agent.schedule) {
2314
+ const sched = agent.schedule;
2315
+ const trigger = sched.cron ?? sched.every ?? sched.at ?? "?";
2316
+ console.log(" " + chalk14.dim("Schedule: ") + trigger);
2317
+ }
2318
+ }
2319
+ if (score.entry) {
2320
+ const entry = typeof score.entry === "string" ? score.entry : "parallel";
2321
+ console.log();
2322
+ console.log(" " + chalk14.dim("Entry:") + " " + entry);
2323
+ }
2324
+ console.log();
2325
+ }
2326
+
2327
+ // src/commands/upgrade.ts
2328
+ import { execSync as execSync5 } from "child_process";
2329
+ import { existsSync as existsSync15, readFileSync as readFileSync8 } from "fs";
2330
+ import { resolve as resolve15 } from "path";
2331
+ import chalk15 from "chalk";
2332
+ import ora9 from "ora";
2333
+ function detectPackageManager2() {
2334
+ const cwd = process.cwd();
2335
+ if (existsSync15(resolve15(cwd, "yarn.lock"))) return "yarn";
2336
+ if (existsSync15(resolve15(cwd, "pnpm-lock.yaml"))) return "pnpm";
2337
+ return "npm";
2338
+ }
2339
+ function resolvePackageName2(input) {
2340
+ const KNOWN = /* @__PURE__ */ new Map([
2341
+ ["filesystem", "@tuttiai/filesystem"],
2342
+ ["github", "@tuttiai/github"],
2343
+ ["playwright", "@tuttiai/playwright"],
2344
+ ["mcp", "@tuttiai/mcp"],
2345
+ ["web", "@tuttiai/web"],
2346
+ ["sandbox", "@tuttiai/sandbox"],
2347
+ ["rag", "@tuttiai/rag"],
2348
+ ["core", "@tuttiai/core"],
2349
+ ["types", "@tuttiai/types"],
2350
+ ["server", "@tuttiai/server"]
2351
+ ]);
2352
+ return KNOWN.get(input) ?? (input.startsWith("@") ? input : `@tuttiai/${input}`);
2353
+ }
2354
+ function getInstalledTuttiPackages() {
2355
+ const pkgPath = resolve15(process.cwd(), "package.json");
2356
+ if (!existsSync15(pkgPath)) return /* @__PURE__ */ new Map();
2357
+ try {
2358
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
2359
+ const result = /* @__PURE__ */ new Map();
2360
+ if (pkg.dependencies) {
2361
+ for (const [name, version] of Object.entries(pkg.dependencies)) {
2362
+ if (name.startsWith("@tuttiai/")) result.set(name, version);
2363
+ }
2364
+ }
2365
+ if (pkg.devDependencies) {
2366
+ for (const [name, version] of Object.entries(pkg.devDependencies)) {
2367
+ if (name.startsWith("@tuttiai/")) result.set(name, version);
2368
+ }
2369
+ }
2370
+ return result;
2371
+ } catch {
2372
+ return /* @__PURE__ */ new Map();
2373
+ }
2374
+ }
2375
+ function upgradeCommand(target) {
2376
+ const pkgPath = resolve15(process.cwd(), "package.json");
2377
+ if (!existsSync15(pkgPath)) {
2378
+ console.error(chalk15.red("No package.json found in the current directory."));
2379
+ console.error(chalk15.dim('Run "tutti-ai init" to create a new project.'));
2380
+ process.exit(1);
2381
+ }
2382
+ const pm = detectPackageManager2();
2383
+ const installed = getInstalledTuttiPackages();
2384
+ if (installed.size === 0) {
2385
+ console.log(chalk15.dim("No @tuttiai packages found in this project."));
2386
+ return;
2387
+ }
2388
+ let packages;
2389
+ if (target) {
2390
+ const resolved = resolvePackageName2(target);
2391
+ if (!installed.has(resolved)) {
2392
+ console.error(chalk15.red(resolved + " is not installed in this project."));
2393
+ console.error(chalk15.dim("Installed: " + [...installed.keys()].join(", ")));
2394
+ process.exit(1);
2395
+ }
2396
+ packages = [resolved + "@latest"];
2397
+ console.log(chalk15.cyan(" Upgrading " + resolved + "..."));
2398
+ } else {
2399
+ packages = [...installed.keys()].map((p) => p + "@latest");
2400
+ console.log(chalk15.cyan(" Upgrading all " + packages.length + " @tuttiai packages..."));
2401
+ }
2402
+ const installCmd = pm === "yarn" ? "yarn add " + packages.join(" ") : pm === "pnpm" ? "pnpm add " + packages.join(" ") : "npm install " + packages.join(" ");
2403
+ const spinner = ora9("Installing...").start();
2404
+ try {
2405
+ execSync5(installCmd, { cwd: process.cwd(), stdio: "pipe" });
2406
+ spinner.succeed("Upgraded " + packages.length + " package(s)");
2407
+ } catch {
2408
+ spinner.fail("Upgrade failed");
2409
+ console.log(chalk15.dim(" Run manually: " + installCmd));
2410
+ process.exit(1);
2411
+ }
2412
+ console.log();
2413
+ const newInstalled = getInstalledTuttiPackages();
2414
+ for (const [name, oldVersion] of installed) {
2415
+ const newVersion = newInstalled.get(name) ?? oldVersion;
2416
+ const oldClean = oldVersion.replace(/^[\^~]/, "");
2417
+ const newClean = newVersion.replace(/^[\^~]/, "");
2418
+ if (oldClean !== newClean) {
2419
+ console.log(" " + chalk15.green("\u2191") + " " + name + " " + chalk15.dim(oldClean) + " \u2192 " + chalk15.green(newClean));
2420
+ } else {
2421
+ console.log(" " + chalk15.dim("=") + " " + name + " " + chalk15.dim(newClean) + chalk15.dim(" (already latest)"));
2422
+ }
2423
+ }
2424
+ console.log();
2425
+ }
2426
+
2427
+ // src/commands/replay.ts
2428
+ import { existsSync as existsSync16 } from "fs";
2429
+ import { writeFile } from "fs/promises";
2430
+ import { resolve as resolve16 } from "path";
2431
+ import { createInterface as createInterface3 } from "readline/promises";
2432
+ import chalk17 from "chalk";
2433
+ import ora10 from "ora";
2434
+ import {
2435
+ PostgresSessionStore,
2436
+ ScoreLoader as ScoreLoader9,
2437
+ TuttiRuntime as TuttiRuntime5,
2438
+ SecretsManager as SecretsManager7,
2439
+ createLogger as createLogger13
2440
+ } from "@tuttiai/core";
2441
+
2442
+ // src/commands/replay-render.ts
2443
+ import chalk16 from "chalk";
2444
+ function messageToText2(msg) {
2445
+ if (typeof msg.content === "string") return msg.content;
2446
+ const parts = [];
2447
+ for (const block of msg.content) {
2448
+ if (block.type === "text") {
2449
+ parts.push(block.text);
2450
+ } else if (block.type === "tool_use") {
2451
+ parts.push("[tool_use " + block.name + "]");
2452
+ } else if (block.type === "tool_result") {
2453
+ const preview = block.content.replace(/\s+/g, " ").trim();
2454
+ parts.push("[tool_result " + (preview.length > 60 ? preview.slice(0, 59) + "\u2026" : preview) + "]");
2455
+ }
2456
+ }
2457
+ return parts.join(" ");
2458
+ }
2459
+ function excerpt2(text, max) {
2460
+ const oneLine = text.replace(/\s+/g, " ").trim();
2461
+ return oneLine.length > max ? oneLine.slice(0, max - 1) + "\u2026" : oneLine;
2462
+ }
2463
+ function renderList(messages) {
2464
+ const lines = [];
2465
+ for (let i = 0; i < messages.length; i++) {
2466
+ const msg = messages.at(i);
2467
+ if (!msg) continue;
2468
+ const role = msg.role === "user" ? chalk16.blue("user ") : chalk16.green("assistant");
2469
+ const text = excerpt2(messageToText2(msg), 80);
2470
+ lines.push(
2471
+ chalk16.dim(String(i).padStart(3)) + " " + role + " " + text
2472
+ );
2473
+ }
2474
+ return lines.join("\n");
2475
+ }
2476
+ function renderShow(messages, index) {
2477
+ if (index < 0 || index >= messages.length) {
2478
+ return chalk16.red("Index out of range. Valid: 0\u2013" + (messages.length - 1));
2479
+ }
2480
+ const msg = messages.at(index);
2481
+ if (!msg) return chalk16.red("Index out of range.");
2482
+ const lines = [];
2483
+ lines.push(chalk16.cyan.bold("Turn " + index) + " " + chalk16.dim("[" + msg.role + "]"));
2484
+ lines.push("");
2485
+ if (typeof msg.content === "string") {
2486
+ lines.push(msg.content);
2487
+ } else {
2488
+ for (const block of msg.content) {
2489
+ if (block.type === "text") {
2490
+ lines.push(block.text);
2491
+ } else if (block.type === "tool_use") {
2492
+ lines.push(chalk16.yellow(" tool_use: " + block.name));
2493
+ lines.push(chalk16.dim(" id: " + block.id));
2494
+ lines.push(chalk16.dim(" input: " + JSON.stringify(block.input, null, 2)));
2495
+ } else if (block.type === "tool_result") {
2496
+ const label = block.is_error ? chalk16.red(" tool_result (error):") : chalk16.green(" tool_result:");
2497
+ lines.push(label);
2498
+ lines.push(chalk16.dim(" tool_use_id: " + block.tool_use_id));
2499
+ lines.push(" " + block.content);
2500
+ }
2501
+ }
2502
+ }
2503
+ return lines.join("\n");
2504
+ }
2505
+ function renderInspect(messages, index) {
2506
+ if (index < 0 || index >= messages.length) {
2507
+ return chalk16.red("Index out of range.");
2508
+ }
2509
+ return JSON.stringify(messages.at(index), null, 2);
2510
+ }
2511
+ function exportJSON(session) {
2512
+ return JSON.stringify(
2513
+ {
2514
+ id: session.id,
2515
+ agent_name: session.agent_name,
2516
+ created_at: session.created_at,
2517
+ messages: session.messages
2518
+ },
2519
+ null,
2520
+ 2
2521
+ );
2522
+ }
2523
+ function exportMarkdown(session) {
2524
+ const lines = [];
2525
+ lines.push("# Session " + session.id);
2526
+ lines.push("");
2527
+ lines.push("**Agent:** " + session.agent_name);
2528
+ lines.push("**Created:** " + session.created_at.toISOString());
2529
+ lines.push("**Messages:** " + session.messages.length);
2530
+ lines.push("");
2531
+ lines.push("---");
2532
+ lines.push("");
2533
+ for (let i = 0; i < session.messages.length; i++) {
2534
+ const msg = session.messages.at(i);
2535
+ if (!msg) continue;
2536
+ lines.push("## Turn " + i + " (" + msg.role + ")");
2537
+ lines.push("");
2538
+ if (typeof msg.content === "string") {
2539
+ lines.push(msg.content);
2540
+ } else {
2541
+ for (const block of msg.content) {
2542
+ if (block.type === "text") {
2543
+ lines.push(block.text);
2544
+ } else if (block.type === "tool_use") {
2545
+ lines.push("**Tool call:** `" + block.name + "`");
2546
+ lines.push("```json\n" + JSON.stringify(block.input, null, 2) + "\n```");
2547
+ } else if (block.type === "tool_result") {
2548
+ const label = block.is_error ? "**Tool error:**" : "**Tool result:**";
2549
+ lines.push(label);
2550
+ lines.push("```\n" + block.content + "\n```");
2551
+ }
2552
+ }
2553
+ }
2554
+ lines.push("");
2555
+ }
2556
+ return lines.join("\n");
2557
+ }
2558
+
2559
+ // src/commands/replay.ts
2560
+ var logger13 = createLogger13("tutti-cli");
2561
+ async function replayCommand(sessionId, opts = {}) {
2562
+ const pgUrl = SecretsManager7.optional("TUTTI_PG_URL");
2563
+ if (!pgUrl) {
2564
+ console.error(chalk17.red("TUTTI_PG_URL is not set."));
2565
+ console.error(
2566
+ chalk17.dim(
2567
+ "The replay command requires PostgreSQL for session persistence.\nSet TUTTI_PG_URL=postgres://user:pass@host/db in your environment."
2568
+ )
2569
+ );
2570
+ process.exit(1);
2571
+ }
2572
+ const store = new PostgresSessionStore(pgUrl);
2573
+ const spinner = ora10({ color: "cyan" }).start("Loading session...");
2574
+ let session;
2575
+ try {
2576
+ session = await store.getAsync(sessionId);
2577
+ } catch (err) {
2578
+ spinner.fail("Failed to load session");
2579
+ logger13.error(
2580
+ { error: err instanceof Error ? err.message : String(err) },
2581
+ "Session store error"
2582
+ );
2583
+ process.exit(1);
2584
+ }
2585
+ spinner.stop();
2586
+ if (!session) {
2587
+ console.error(chalk17.red("Session not found: " + sessionId));
2588
+ console.error(chalk17.dim("Check the session ID and ensure TUTTI_PG_URL points to the correct database."));
2589
+ await store.close();
2590
+ process.exit(1);
2591
+ }
2592
+ const messages = session.messages;
2593
+ console.log("");
2594
+ console.log(chalk17.cyan.bold(" Tutti Replay"));
2595
+ console.log(chalk17.dim(" Session: " + session.id));
2596
+ console.log(chalk17.dim(" Agent: " + session.agent_name));
2597
+ console.log(chalk17.dim(" Created: " + session.created_at.toISOString()));
2598
+ console.log(chalk17.dim(" Messages: " + messages.length));
2599
+ console.log("");
2600
+ console.log(chalk17.dim(" Commands: list, show <n>, next, prev, inspect, replay-from <n>, export <json|md>, quit"));
2601
+ console.log("");
2602
+ let cursor = 0;
2603
+ const rl = createInterface3({ input: process.stdin, output: process.stdout });
2604
+ try {
2605
+ while (true) {
2606
+ const prompt3 = chalk17.cyan("replay [" + cursor + "/" + (messages.length - 1) + "]> ");
2607
+ const raw = await rl.question(prompt3);
2608
+ const input = raw.trim();
2609
+ if (!input) continue;
2610
+ const [cmd, ...args] = input.split(/\s+/);
2611
+ switch (cmd) {
2612
+ case "quit":
2613
+ case "exit":
2614
+ case "q":
2615
+ console.log(chalk17.dim("Bye."));
2616
+ return;
2617
+ case "list":
2618
+ console.log(renderList(messages));
2619
+ break;
2620
+ case "show": {
2621
+ const n = parseInt(args[0] ?? String(cursor), 10);
2622
+ console.log(renderShow(messages, n));
2623
+ if (n >= 0 && n < messages.length) cursor = n;
2624
+ break;
2625
+ }
2626
+ case "next":
2627
+ if (cursor < messages.length - 1) {
2628
+ cursor++;
2629
+ console.log(renderShow(messages, cursor));
2630
+ } else {
2631
+ console.log(chalk17.dim("Already at last message."));
2632
+ }
2633
+ break;
2634
+ case "prev":
2635
+ if (cursor > 0) {
2636
+ cursor--;
2637
+ console.log(renderShow(messages, cursor));
2638
+ } else {
2639
+ console.log(chalk17.dim("Already at first message."));
2640
+ }
2641
+ break;
2642
+ case "inspect":
2643
+ console.log(renderInspect(messages, cursor));
2644
+ break;
2645
+ case "replay-from": {
2646
+ const turn = parseInt(args[0] ?? "", 10);
2647
+ if (isNaN(turn) || turn < 0 || turn >= messages.length) {
2648
+ console.log(chalk17.red("Usage: replay-from <turn-number>"));
2649
+ break;
2650
+ }
2651
+ await handleReplayFrom(turn, session, rl, opts);
2652
+ break;
2653
+ }
2654
+ case "export": {
2655
+ const format = args[0];
2656
+ if (format === "json") {
2657
+ const filename = "session-" + session.id.slice(0, 8) + ".json";
2658
+ await writeFile(filename, exportJSON(session));
2659
+ console.log(chalk17.green("Exported to " + filename));
2660
+ } else if (format === "md" || format === "markdown") {
2661
+ const filename = "session-" + session.id.slice(0, 8) + ".md";
2662
+ await writeFile(filename, exportMarkdown(session));
2663
+ console.log(chalk17.green("Exported to " + filename));
2664
+ } else {
2665
+ console.log(chalk17.dim("Usage: export <json|md>"));
2666
+ }
2667
+ break;
2668
+ }
2669
+ default:
2670
+ console.log(
2671
+ chalk17.dim("Unknown command. Available: list, show <n>, next, prev, inspect, replay-from <n>, export <json|md>, quit")
2672
+ );
2673
+ }
2674
+ }
2675
+ } finally {
2676
+ rl.close();
2677
+ await store.close();
2678
+ }
2679
+ }
2680
+ async function handleReplayFrom(turn, session, rl, opts) {
2681
+ const scoreFile = resolve16(opts.score ?? "./tutti.score.ts");
2682
+ if (!existsSync16(scoreFile)) {
2683
+ console.log(chalk17.red("Score file not found: " + scoreFile));
2684
+ console.log(chalk17.dim("Use --score to specify the score file."));
2685
+ return;
2686
+ }
2687
+ const originalMsg = session.messages.at(turn);
2688
+ const originalInput = originalMsg ? messageToText2(originalMsg) : "";
2689
+ const answer = await rl.question(
2690
+ chalk17.cyan("Replay from turn " + turn + " with original input? ") + chalk17.dim("(y / enter new input) ")
2691
+ );
2692
+ const input = answer.trim().toLowerCase() === "y" || answer.trim() === "" ? originalInput : answer.trim();
2693
+ if (!input) {
2694
+ console.log(chalk17.dim("No input provided. Cancelled."));
2695
+ return;
2696
+ }
2697
+ const spinnerLoad = ora10({ color: "cyan" }).start("Loading score...");
2698
+ let score;
2699
+ try {
2700
+ score = await ScoreLoader9.load(scoreFile);
2701
+ } catch (err) {
2702
+ spinnerLoad.fail("Failed to load score");
2703
+ logger13.error({ error: err instanceof Error ? err.message : String(err) }, "Score load error");
2704
+ return;
2705
+ }
2706
+ spinnerLoad.stop();
2707
+ const restoredMessages = session.messages.slice(0, turn);
2708
+ const runtime = new TuttiRuntime5(score);
2709
+ const sessions = runtime.sessions;
2710
+ if ("save" in sessions && typeof sessions.save === "function") {
2711
+ sessions.save({
2712
+ id: session.id,
2713
+ agent_name: session.agent_name,
2714
+ messages: restoredMessages,
2715
+ created_at: session.created_at,
2716
+ updated_at: /* @__PURE__ */ new Date()
2717
+ });
2718
+ }
2719
+ const agentName = session.agent_name;
2720
+ const agentMap = new Map(Object.entries(score.agents));
2721
+ const agent = agentMap.get(agentName);
2722
+ if (!agent) {
2723
+ console.log(chalk17.red('Agent "' + agentName + '" not found in score.'));
2724
+ return;
2725
+ }
2726
+ const spinnerRun = ora10({ color: "cyan" }).start("Running from turn " + turn + "...");
2727
+ runtime.events.on("token:stream", (e) => {
2728
+ spinnerRun.stop();
2729
+ process.stdout.write(e.text);
2730
+ });
2731
+ try {
2732
+ const result = await runtime.run(agentName, input, session.id);
2733
+ spinnerRun.stop();
2734
+ console.log("");
2735
+ console.log(chalk17.green("Replay complete."));
2736
+ console.log(chalk17.dim(" Turns: " + result.turns));
2737
+ console.log(chalk17.dim(" Tokens: " + result.usage.input_tokens + " in / " + result.usage.output_tokens + " out"));
2738
+ console.log("");
2739
+ console.log(result.output);
2740
+ } catch (err) {
2741
+ spinnerRun.fail("Replay failed");
2742
+ logger13.error({ error: err instanceof Error ? err.message : String(err) }, "Replay error");
2743
+ }
2744
+ }
2745
+
2746
+ // src/commands/schedules.ts
2747
+ import { existsSync as existsSync17 } from "fs";
2748
+ import { resolve as resolve17 } from "path";
2749
+ import chalk18 from "chalk";
1967
2750
  import {
1968
- ScoreLoader as ScoreLoader8,
2751
+ ScoreLoader as ScoreLoader10,
1969
2752
  SchedulerEngine as SchedulerEngine2,
1970
2753
  PostgresScheduleStore as PostgresScheduleStore2,
1971
2754
  MemoryScheduleStore as MemoryScheduleStore2,
1972
2755
  AgentRunner as AgentRunner2,
1973
2756
  EventBus as EventBus2,
1974
2757
  InMemorySessionStore as InMemorySessionStore4,
1975
- SecretsManager as SecretsManager7,
1976
- createLogger as createLogger12
2758
+ SecretsManager as SecretsManager8,
2759
+ createLogger as createLogger14
1977
2760
  } from "@tuttiai/core";
1978
- var logger12 = createLogger12("tutti-cli");
2761
+ var logger14 = createLogger14("tutti-cli");
1979
2762
  function resolveStore2() {
1980
- const pgUrl = SecretsManager7.optional("TUTTI_PG_URL");
2763
+ const pgUrl = SecretsManager8.optional("TUTTI_PG_URL");
1981
2764
  if (pgUrl) {
1982
2765
  return new PostgresScheduleStore2({ connection_string: pgUrl });
1983
2766
  }
1984
- logger12.warn("TUTTI_PG_URL not set \u2014 using in-memory store (schedules are ephemeral)");
2767
+ logger14.warn("TUTTI_PG_URL not set \u2014 using in-memory store (schedules are ephemeral)");
1985
2768
  return new MemoryScheduleStore2();
1986
2769
  }
1987
2770
  async function closeStore(store) {
@@ -1995,7 +2778,7 @@ function formatTrigger(r) {
1995
2778
  if (r.config.at) return "at " + r.config.at;
1996
2779
  return "?";
1997
2780
  }
1998
- function pad(s, len) {
2781
+ function pad3(s, len) {
1999
2782
  return s.length >= len ? s : s + " ".repeat(len - s.length);
2000
2783
  }
2001
2784
  async function schedulesListCommand() {
@@ -2003,22 +2786,22 @@ async function schedulesListCommand() {
2003
2786
  try {
2004
2787
  const records = await store.list();
2005
2788
  if (records.length === 0) {
2006
- console.log(chalk12.dim("No schedules found."));
2007
- console.log(chalk12.dim('Run "tutti-ai schedule" to start the scheduler daemon.'));
2789
+ console.log(chalk18.dim("No schedules found."));
2790
+ console.log(chalk18.dim('Run "tutti-ai schedule" to start the scheduler daemon.'));
2008
2791
  return;
2009
2792
  }
2010
2793
  console.log("");
2011
2794
  console.log(
2012
- chalk12.dim(
2013
- " " + pad("ID", 20) + pad("AGENT", 16) + pad("TRIGGER", 22) + pad("ENABLED", 10) + pad("RUNS", 8) + "CREATED"
2795
+ chalk18.dim(
2796
+ " " + pad3("ID", 20) + pad3("AGENT", 16) + pad3("TRIGGER", 22) + pad3("ENABLED", 10) + pad3("RUNS", 8) + "CREATED"
2014
2797
  )
2015
2798
  );
2016
- console.log(chalk12.dim(" " + "\u2500".repeat(90)));
2799
+ console.log(chalk18.dim(" " + "\u2500".repeat(90)));
2017
2800
  for (const r of records) {
2018
- const enabled = r.enabled ? chalk12.green("yes") : chalk12.red("no") + " ";
2801
+ const enabled = r.enabled ? chalk18.green("yes") : chalk18.red("no") + " ";
2019
2802
  const maxLabel = r.config.max_runs ? r.run_count + "/" + r.config.max_runs : String(r.run_count);
2020
2803
  console.log(
2021
- " " + chalk12.bold(pad(r.id, 20)) + pad(r.agent_id, 16) + pad(formatTrigger(r), 22) + pad(enabled, 10) + pad(maxLabel, 8) + chalk12.dim(r.created_at.toISOString().slice(0, 10))
2804
+ " " + chalk18.bold(pad3(r.id, 20)) + pad3(r.agent_id, 16) + pad3(formatTrigger(r), 22) + pad3(enabled, 10) + pad3(maxLabel, 8) + chalk18.dim(r.created_at.toISOString().slice(0, 10))
2022
2805
  );
2023
2806
  }
2024
2807
  console.log("");
@@ -2031,11 +2814,11 @@ async function schedulesEnableCommand(id) {
2031
2814
  try {
2032
2815
  const record = await store.get(id);
2033
2816
  if (!record) {
2034
- console.error(chalk12.red('Schedule "' + id + '" not found.'));
2817
+ console.error(chalk18.red('Schedule "' + id + '" not found.'));
2035
2818
  process.exit(1);
2036
2819
  }
2037
2820
  await store.setEnabled(id, true);
2038
- console.log(chalk12.green('Schedule "' + id + '" enabled.'));
2821
+ console.log(chalk18.green('Schedule "' + id + '" enabled.'));
2039
2822
  } finally {
2040
2823
  await closeStore(store);
2041
2824
  }
@@ -2045,22 +2828,22 @@ async function schedulesDisableCommand(id) {
2045
2828
  try {
2046
2829
  const record = await store.get(id);
2047
2830
  if (!record) {
2048
- console.error(chalk12.red('Schedule "' + id + '" not found.'));
2831
+ console.error(chalk18.red('Schedule "' + id + '" not found.'));
2049
2832
  process.exit(1);
2050
2833
  }
2051
2834
  await store.setEnabled(id, false);
2052
- console.log(chalk12.yellow('Schedule "' + id + '" disabled.'));
2835
+ console.log(chalk18.yellow('Schedule "' + id + '" disabled.'));
2053
2836
  } finally {
2054
2837
  await closeStore(store);
2055
2838
  }
2056
2839
  }
2057
2840
  async function schedulesTriggerCommand(id, scorePath) {
2058
- const file = resolve12(scorePath ?? "./tutti.score.ts");
2059
- if (!existsSync12(file)) {
2060
- console.error(chalk12.red("Score file not found: " + file));
2841
+ const file = resolve17(scorePath ?? "./tutti.score.ts");
2842
+ if (!existsSync17(file)) {
2843
+ console.error(chalk18.red("Score file not found: " + file));
2061
2844
  process.exit(1);
2062
2845
  }
2063
- const score = await ScoreLoader8.load(file);
2846
+ const score = await ScoreLoader10.load(file);
2064
2847
  const events = new EventBus2();
2065
2848
  const sessions = new InMemorySessionStore4();
2066
2849
  const runner = new AgentRunner2(score.provider, events, sessions);
@@ -2068,30 +2851,30 @@ async function schedulesTriggerCommand(id, scorePath) {
2068
2851
  try {
2069
2852
  const record = await store.get(id);
2070
2853
  if (!record) {
2071
- console.error(chalk12.red('Schedule "' + id + '" not found.'));
2854
+ console.error(chalk18.red('Schedule "' + id + '" not found.'));
2072
2855
  process.exit(1);
2073
2856
  }
2074
2857
  const agent = score.agents[record.agent_id];
2075
2858
  if (!agent) {
2076
- console.error(chalk12.red('Agent "' + record.agent_id + '" not found in score.'));
2859
+ console.error(chalk18.red('Agent "' + record.agent_id + '" not found in score.'));
2077
2860
  process.exit(1);
2078
2861
  }
2079
2862
  const resolvedAgent = agent.model ? agent : { ...agent, model: score.default_model ?? "claude-sonnet-4-20250514" };
2080
2863
  const engine = new SchedulerEngine2(store, runner, events);
2081
2864
  await engine.schedule(id, resolvedAgent, record.config);
2082
2865
  engine.start();
2083
- console.log(chalk12.cyan('Triggering "' + id + '" (' + record.agent_id + ")..."));
2866
+ console.log(chalk18.cyan('Triggering "' + id + '" (' + record.agent_id + ")..."));
2084
2867
  const run2 = await engine.trigger(id);
2085
2868
  engine.stop();
2086
2869
  if (run2.error) {
2087
- console.log(chalk12.red(" Error: " + run2.error));
2870
+ console.log(chalk18.red(" Error: " + run2.error));
2088
2871
  process.exit(1);
2089
2872
  }
2090
2873
  const duration = run2.completed_at && run2.triggered_at ? run2.completed_at.getTime() - run2.triggered_at.getTime() : 0;
2091
- console.log(chalk12.green(" Completed in " + duration + "ms"));
2874
+ console.log(chalk18.green(" Completed in " + duration + "ms"));
2092
2875
  if (run2.result) {
2093
2876
  const preview = run2.result.length > 200 ? run2.result.slice(0, 200) + "..." : run2.result;
2094
- console.log(chalk12.dim(" Output: " + preview));
2877
+ console.log(chalk18.dim(" Output: " + preview));
2095
2878
  }
2096
2879
  } finally {
2097
2880
  await closeStore(store);
@@ -2102,50 +2885,316 @@ async function schedulesRunsCommand(id) {
2102
2885
  try {
2103
2886
  const record = await store.get(id);
2104
2887
  if (!record) {
2105
- console.error(chalk12.red('Schedule "' + id + '" not found.'));
2888
+ console.error(chalk18.red('Schedule "' + id + '" not found.'));
2106
2889
  process.exit(1);
2107
2890
  }
2108
2891
  if ("getRuns" in store && typeof store.getRuns === "function") {
2109
2892
  const runs = store.getRuns(id);
2110
2893
  if (runs.length === 0) {
2111
- console.log(chalk12.dim("No runs recorded for this schedule."));
2894
+ console.log(chalk18.dim("No runs recorded for this schedule."));
2112
2895
  return;
2113
2896
  }
2114
2897
  const recent = runs.slice(-20);
2115
2898
  console.log("");
2116
- console.log(chalk12.dim(" Showing last " + recent.length + " of " + runs.length + " runs:"));
2899
+ console.log(chalk18.dim(" Showing last " + recent.length + " of " + runs.length + " runs:"));
2117
2900
  console.log("");
2118
2901
  for (const run2 of recent) {
2119
2902
  const duration = run2.completed_at && run2.triggered_at ? run2.completed_at.getTime() - run2.triggered_at.getTime() + "ms" : "?";
2120
- const status = run2.error ? chalk12.red("error") : chalk12.green("ok");
2903
+ const status = run2.error ? chalk18.red("error") : chalk18.green("ok");
2121
2904
  const preview = run2.error ? run2.error.slice(0, 80) : (run2.result ?? "").slice(0, 80);
2122
2905
  console.log(
2123
- " " + chalk12.dim(run2.triggered_at.toISOString()) + " " + status + " " + chalk12.dim(duration) + " " + preview
2906
+ " " + chalk18.dim(run2.triggered_at.toISOString()) + " " + status + " " + chalk18.dim(duration) + " " + preview
2124
2907
  );
2125
2908
  }
2126
2909
  console.log("");
2127
2910
  } else {
2128
- console.log(chalk12.dim('Schedule "' + id + '" has completed ' + record.run_count + " runs."));
2129
- console.log(chalk12.dim("Full run history requires the MemoryScheduleStore or a future tutti_schedule_runs table."));
2911
+ console.log(chalk18.dim('Schedule "' + id + '" has completed ' + record.run_count + " runs."));
2912
+ console.log(chalk18.dim("Full run history requires the MemoryScheduleStore or a future tutti_schedule_runs table."));
2130
2913
  }
2131
2914
  } finally {
2132
2915
  await closeStore(store);
2133
2916
  }
2134
2917
  }
2135
2918
 
2919
+ // src/commands/traces.ts
2920
+ import chalk20 from "chalk";
2921
+ import { SecretsManager as SecretsManager9 } from "@tuttiai/core";
2922
+
2923
+ // src/commands/traces-render.ts
2924
+ import chalk19 from "chalk";
2925
+ function visibleLen(s) {
2926
+ return s.replace(/\u001b\[[0-9;]*m/g, "").length;
2927
+ }
2928
+ function pad4(s, len) {
2929
+ const v = visibleLen(s);
2930
+ return v >= len ? s : s + " ".repeat(len - v);
2931
+ }
2932
+ function colorStatus(status) {
2933
+ if (status === "ok") return chalk19.green("ok");
2934
+ if (status === "error") return chalk19.red("error");
2935
+ return chalk19.yellow("running");
2936
+ }
2937
+ function formatCost(cost) {
2938
+ if (cost === null) return chalk19.dim("\u2014");
2939
+ if (cost === 0) return "$0";
2940
+ return "$" + cost.toFixed(6);
2941
+ }
2942
+ function formatTokens(n) {
2943
+ return n > 0 ? String(n) : chalk19.dim("\u2014");
2944
+ }
2945
+ function formatDuration(ms) {
2946
+ if (ms === null) return chalk19.dim("\u2014");
2947
+ return ms + "ms";
2948
+ }
2949
+ function renderTracesList(traces) {
2950
+ if (traces.length === 0) {
2951
+ return chalk19.dim("No traces found.");
2952
+ }
2953
+ const lines = [];
2954
+ lines.push("");
2955
+ lines.push(
2956
+ chalk19.dim(
2957
+ " " + pad4("TRACE", 10) + pad4("AGENT", 18) + pad4("STARTED", 12) + pad4("DURATION", 12) + pad4("STATUS", 12) + pad4("TOKENS", 10) + "COST"
2958
+ )
2959
+ );
2960
+ lines.push(chalk19.dim(" " + "\u2500".repeat(80)));
2961
+ for (const t of traces) {
2962
+ const traceShort = t.trace_id.slice(0, 8);
2963
+ const startedShort = t.started_at.slice(11, 19);
2964
+ lines.push(
2965
+ " " + chalk19.bold(pad4(traceShort, 10)) + pad4(t.agent_id ?? chalk19.dim("\u2014"), 18) + pad4(startedShort, 12) + pad4(formatDuration(t.duration_ms), 12) + pad4(colorStatus(t.status), 12) + pad4(formatTokens(t.total_tokens), 10) + formatCost(t.cost_usd)
2966
+ );
2967
+ }
2968
+ lines.push("");
2969
+ return lines.join("\n");
2970
+ }
2971
+ var SPAN_ICONS = {
2972
+ agent: "\u25B6",
2973
+ llm: "\u25C6",
2974
+ tool: "\u2699",
2975
+ guardrail: "\u{1F6E1}",
2976
+ checkpoint: "\u{1F4BE}"
2977
+ };
2978
+ function renderSpanLine(span, indent) {
2979
+ const icon = SPAN_ICONS[span.kind];
2980
+ const indentStr = " ".repeat(indent);
2981
+ const dur = span.duration_ms !== void 0 ? chalk19.dim(" " + span.duration_ms + "ms ") : chalk19.dim(" (running) ");
2982
+ const status = colorStatus(span.status);
2983
+ const attrs = formatAttrs(span);
2984
+ const attrSuffix = attrs ? chalk19.dim(" \xB7 " + attrs) : "";
2985
+ return indentStr + icon + " " + chalk19.bold(span.name) + dur + status + attrSuffix;
2986
+ }
2987
+ function formatAttrs(span) {
2988
+ const a = span.attributes;
2989
+ const parts = [];
2990
+ if (span.kind === "agent") {
2991
+ if (a.agent_id !== void 0) parts.push("agent=" + a.agent_id);
2992
+ if (a.model !== void 0) parts.push("model=" + a.model);
2993
+ } else if (span.kind === "llm") {
2994
+ if (a.model !== void 0) parts.push("model=" + a.model);
2995
+ if (a.total_tokens !== void 0) parts.push(a.total_tokens + " tok");
2996
+ if (a.cost_usd !== void 0) parts.push(formatCost(a.cost_usd));
2997
+ } else if (span.kind === "tool") {
2998
+ if (a.tool_name !== void 0) parts.push(a.tool_name);
2999
+ } else if (span.kind === "guardrail") {
3000
+ if (a.guardrail_name !== void 0) parts.push(a.guardrail_name);
3001
+ if (a.guardrail_action !== void 0) parts.push("\u2192 " + a.guardrail_action);
3002
+ } else if (span.kind === "checkpoint") {
3003
+ if (a.session_id !== void 0) parts.push("session=" + a.session_id.slice(0, 8));
3004
+ }
3005
+ if (span.error?.message) {
3006
+ parts.push(chalk19.red("error: " + span.error.message));
3007
+ }
3008
+ return parts.join(" \xB7 ");
3009
+ }
3010
+ function renderTraceShow(spans) {
3011
+ if (spans.length === 0) {
3012
+ return chalk19.dim("No spans found for this trace.");
3013
+ }
3014
+ const childrenByParent = /* @__PURE__ */ new Map();
3015
+ const presentSpanIds = new Set(spans.map((s) => s.span_id));
3016
+ const roots = [];
3017
+ for (const span of spans) {
3018
+ const parent = span.parent_span_id;
3019
+ if (parent === void 0 || !presentSpanIds.has(parent)) {
3020
+ roots.push(span);
3021
+ continue;
3022
+ }
3023
+ const arr = childrenByParent.get(parent) ?? [];
3024
+ arr.push(span);
3025
+ childrenByParent.set(parent, arr);
3026
+ }
3027
+ roots.sort((a, b) => a.started_at.getTime() - b.started_at.getTime());
3028
+ const lines = [""];
3029
+ function walk(span, indent) {
3030
+ lines.push(renderSpanLine(span, indent));
3031
+ const kids = childrenByParent.get(span.span_id);
3032
+ if (!kids) return;
3033
+ for (const child of kids) {
3034
+ walk(child, indent + 1);
3035
+ }
3036
+ }
3037
+ for (const root of roots) walk(root, 0);
3038
+ let total_tokens = 0;
3039
+ let total_cost = 0;
3040
+ let any_cost = false;
3041
+ for (const s of spans) {
3042
+ if (s.name !== "llm.completion") continue;
3043
+ total_tokens += s.attributes.total_tokens ?? 0;
3044
+ if (s.attributes.cost_usd !== void 0) {
3045
+ total_cost += s.attributes.cost_usd;
3046
+ any_cost = true;
3047
+ }
3048
+ }
3049
+ const wall_ms = roots.map((r) => r.duration_ms).find((d) => d !== void 0);
3050
+ lines.push("");
3051
+ lines.push(chalk19.dim("\u2500".repeat(60)));
3052
+ lines.push(
3053
+ chalk19.dim("Total: ") + chalk19.bold(formatTokens(total_tokens)) + chalk19.dim(" tokens \xB7 ") + chalk19.bold(any_cost ? formatCost(total_cost) : chalk19.dim("\u2014")) + chalk19.dim(" cost \xB7 ") + chalk19.bold(wall_ms !== void 0 ? wall_ms + "ms" : chalk19.dim("\u2014")) + chalk19.dim(" wall")
3054
+ );
3055
+ lines.push("");
3056
+ return lines.join("\n");
3057
+ }
3058
+
3059
+ // src/commands/traces.ts
3060
+ var DEFAULT_SERVER_URL = "http://127.0.0.1:3847";
3061
+ function resolveUrl(opts) {
3062
+ return opts.url ?? SecretsManager9.optional("TUTTI_SERVER_URL") ?? DEFAULT_SERVER_URL;
3063
+ }
3064
+ function resolveAuthHeader(opts) {
3065
+ const key = opts.apiKey ?? SecretsManager9.optional("TUTTI_API_KEY");
3066
+ return key ? { Authorization: "Bearer " + key } : {};
3067
+ }
3068
+ function explainConnectionError(err, baseUrl) {
3069
+ const msg = err instanceof Error ? err.message : String(err);
3070
+ console.error(chalk20.red("Failed to reach Tutti server at " + baseUrl));
3071
+ console.error(chalk20.dim(" " + msg));
3072
+ console.error(chalk20.dim(" Is `tutti-ai serve` running? Set --url or TUTTI_SERVER_URL to override."));
3073
+ process.exit(1);
3074
+ }
3075
+ async function tracesListCommand(opts) {
3076
+ const baseUrl = resolveUrl(opts);
3077
+ const url = baseUrl.replace(/\/$/, "") + "/traces";
3078
+ let res;
3079
+ try {
3080
+ res = await fetch(url, { headers: resolveAuthHeader(opts) });
3081
+ } catch (err) {
3082
+ explainConnectionError(err, baseUrl);
3083
+ }
3084
+ if (res.status === 401) {
3085
+ console.error(chalk20.red("Unauthorized \u2014 set --api-key or TUTTI_API_KEY."));
3086
+ process.exit(1);
3087
+ }
3088
+ if (!res.ok) {
3089
+ console.error(chalk20.red("Server returned " + res.status + " " + res.statusText));
3090
+ process.exit(1);
3091
+ }
3092
+ const body = await res.json();
3093
+ console.log(renderTracesList(body.traces));
3094
+ }
3095
+ async function tracesShowCommand(traceId, opts) {
3096
+ const baseUrl = resolveUrl(opts);
3097
+ const url = baseUrl.replace(/\/$/, "") + "/traces/" + encodeURIComponent(traceId);
3098
+ let res;
3099
+ try {
3100
+ res = await fetch(url, { headers: resolveAuthHeader(opts) });
3101
+ } catch (err) {
3102
+ explainConnectionError(err, baseUrl);
3103
+ }
3104
+ if (res.status === 404) {
3105
+ console.error(chalk20.red('Trace "' + traceId + '" not found.'));
3106
+ process.exit(1);
3107
+ }
3108
+ if (res.status === 401) {
3109
+ console.error(chalk20.red("Unauthorized \u2014 set --api-key or TUTTI_API_KEY."));
3110
+ process.exit(1);
3111
+ }
3112
+ if (!res.ok) {
3113
+ console.error(chalk20.red("Server returned " + res.status + " " + res.statusText));
3114
+ process.exit(1);
3115
+ }
3116
+ const body = await res.json();
3117
+ const spans = body.spans.map(reviveSpanDates);
3118
+ console.log(renderTraceShow(spans));
3119
+ }
3120
+ async function tracesTailCommand(opts) {
3121
+ const baseUrl = resolveUrl(opts);
3122
+ const url = baseUrl.replace(/\/$/, "") + "/traces/stream";
3123
+ console.error(chalk20.dim("Tailing traces from " + baseUrl + " \u2014 Ctrl+C to exit"));
3124
+ console.error("");
3125
+ const controller = new AbortController();
3126
+ process.once("SIGINT", () => {
3127
+ controller.abort();
3128
+ console.error("");
3129
+ console.error(chalk20.dim("Disconnected."));
3130
+ process.exit(0);
3131
+ });
3132
+ let res;
3133
+ try {
3134
+ res = await fetch(url, {
3135
+ headers: { ...resolveAuthHeader(opts), Accept: "text/event-stream" },
3136
+ signal: controller.signal
3137
+ });
3138
+ } catch (err) {
3139
+ if (controller.signal.aborted) return;
3140
+ explainConnectionError(err, baseUrl);
3141
+ }
3142
+ if (res.status === 401) {
3143
+ console.error(chalk20.red("Unauthorized \u2014 set --api-key or TUTTI_API_KEY."));
3144
+ process.exit(1);
3145
+ }
3146
+ if (!res.ok || !res.body) {
3147
+ console.error(chalk20.red("Server returned " + res.status + " " + res.statusText));
3148
+ process.exit(1);
3149
+ }
3150
+ const reader = res.body.getReader();
3151
+ const decoder = new TextDecoder();
3152
+ let buffer = "";
3153
+ for (; ; ) {
3154
+ let chunk;
3155
+ try {
3156
+ chunk = await reader.read();
3157
+ } catch {
3158
+ return;
3159
+ }
3160
+ if (chunk.done) break;
3161
+ buffer += decoder.decode(chunk.value, { stream: true });
3162
+ let frameEnd;
3163
+ while ((frameEnd = buffer.indexOf("\n\n")) !== -1) {
3164
+ const frame = buffer.slice(0, frameEnd);
3165
+ buffer = buffer.slice(frameEnd + 2);
3166
+ const dataLine = frame.split("\n").find((l) => l.startsWith("data: "));
3167
+ if (!dataLine) continue;
3168
+ try {
3169
+ const span = reviveSpanDates(JSON.parse(dataLine.slice(6)));
3170
+ console.log(renderSpanLine(span, 0));
3171
+ } catch (err) {
3172
+ console.error(chalk20.red("Bad SSE frame: " + (err instanceof Error ? err.message : String(err))));
3173
+ }
3174
+ }
3175
+ }
3176
+ }
3177
+ function reviveSpanDates(span) {
3178
+ return {
3179
+ ...span,
3180
+ started_at: new Date(span.started_at),
3181
+ ...span.ended_at !== void 0 ? { ended_at: new Date(span.ended_at) } : {}
3182
+ };
3183
+ }
3184
+
2136
3185
  // src/index.ts
2137
3186
  config();
2138
- var logger13 = createLogger13("tutti-cli");
3187
+ var logger15 = createLogger15("tutti-cli");
2139
3188
  process.on("unhandledRejection", (reason) => {
2140
- logger13.error({ error: reason instanceof Error ? reason.message : String(reason) }, "Unhandled rejection");
3189
+ logger15.error({ error: reason instanceof Error ? reason.message : String(reason) }, "Unhandled rejection");
2141
3190
  process.exit(1);
2142
3191
  });
2143
3192
  process.on("uncaughtException", (err) => {
2144
- logger13.error({ error: err.message }, "Fatal error");
3193
+ logger15.error({ error: err.message }, "Fatal error");
2145
3194
  process.exit(1);
2146
3195
  });
2147
3196
  var program = new Command();
2148
- program.name("tutti-ai").description("Tutti \u2014 multi-agent orchestration. All agents. All together.").version("0.12.0");
3197
+ program.name("tutti-ai").description("Tutti \u2014 multi-agent orchestration. All agents. All together.").version("0.13.0");
2149
3198
  program.command("init [project-name]").description("Create a new Tutti project").option("-t, --template <id>", "Project template to use").action(async (projectName, opts) => {
2150
3199
  await initCommand(projectName, opts.template);
2151
3200
  });
@@ -2201,6 +3250,21 @@ program.command("publish").description("Publish the current voice to npm and the
2201
3250
  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) => {
2202
3251
  await evalCommand(suitePath, opts);
2203
3252
  });
3253
+ program.command("update").description("Update @tuttiai packages to their latest versions").action(() => {
3254
+ updateCommand();
3255
+ });
3256
+ program.command("outdated").description("Check installed @tuttiai packages for newer versions").action(() => {
3257
+ outdatedCommand();
3258
+ });
3259
+ program.command("info [score]").description("Show project info \u2014 agents, voices, models, and package versions").action(async (score) => {
3260
+ await infoCommand(score);
3261
+ });
3262
+ program.command("upgrade [voice]").description("Upgrade a specific voice or all @tuttiai packages to latest").action((voice) => {
3263
+ upgradeCommand(voice);
3264
+ });
3265
+ 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) => {
3266
+ await replayCommand(sessionId, { score: opts.score });
3267
+ });
2204
3268
  program.command("schedule [score]").description("Start the scheduler daemon \u2014 runs agents on their configured schedules").action(async (score) => {
2205
3269
  await scheduleCommand(score);
2206
3270
  });
@@ -2220,5 +3284,27 @@ schedulesCmd.command("trigger <id>").description("Manually trigger a scheduled r
2220
3284
  schedulesCmd.command("runs <id>").description("Show run history for a schedule (last 20 runs)").action(async (id) => {
2221
3285
  await schedulesRunsCommand(id);
2222
3286
  });
3287
+ var tracesCmd = program.command("traces").description("Inspect spans emitted by a running tutti-ai serve process");
3288
+ tracesCmd.command("list").description("Show the last 20 traces (most recent first)").option("-u, --url <url>", "Server URL (default: http://127.0.0.1:3847)").option("-k, --api-key <key>", "Bearer token (default: TUTTI_API_KEY env)").action(async (opts) => {
3289
+ const resolved = {
3290
+ ...opts.url !== void 0 ? { url: opts.url } : {},
3291
+ ...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {}
3292
+ };
3293
+ await tracesListCommand(resolved);
3294
+ });
3295
+ tracesCmd.command("show <trace-id>").description("Render every span in a trace as an indented tree").option("-u, --url <url>", "Server URL (default: http://127.0.0.1:3847)").option("-k, --api-key <key>", "Bearer token (default: TUTTI_API_KEY env)").action(async (traceId, opts) => {
3296
+ const resolved = {
3297
+ ...opts.url !== void 0 ? { url: opts.url } : {},
3298
+ ...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {}
3299
+ };
3300
+ await tracesShowCommand(traceId, resolved);
3301
+ });
3302
+ tracesCmd.command("tail").description("Live-tail spans as they are emitted (Ctrl+C to exit)").option("-u, --url <url>", "Server URL (default: http://127.0.0.1:3847)").option("-k, --api-key <key>", "Bearer token (default: TUTTI_API_KEY env)").action(async (opts) => {
3303
+ const resolved = {
3304
+ ...opts.url !== void 0 ? { url: opts.url } : {},
3305
+ ...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {}
3306
+ };
3307
+ await tracesTailCommand(resolved);
3308
+ });
2223
3309
  program.parse();
2224
3310
  //# sourceMappingURL=index.js.map