@corbat-tech/coco 1.4.0 → 1.6.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/cli/index.js CHANGED
@@ -12,13 +12,14 @@ import { z } from 'zod';
12
12
  import chalk42 from 'chalk';
13
13
  import * as p9 from '@clack/prompts';
14
14
  import { execa } from 'execa';
15
+ import * as crypto from 'crypto';
16
+ import { randomUUID } from 'crypto';
15
17
  import { Command } from 'commander';
16
18
  import { fileURLToPath, URL as URL$1 } from 'url';
17
19
  import JSON5 from 'json5';
18
- import * as crypto from 'crypto';
19
- import { randomUUID } from 'crypto';
20
20
  import Anthropic from '@anthropic-ai/sdk';
21
21
  import OpenAI from 'openai';
22
+ import { jsonrepair } from 'jsonrepair';
22
23
  import * as http from 'http';
23
24
  import { execFile, exec, execSync, execFileSync, spawn } from 'child_process';
24
25
  import { promisify } from 'util';
@@ -448,8 +449,8 @@ __export(trust_store_exports, {
448
449
  saveTrustStore: () => saveTrustStore,
449
450
  updateLastAccessed: () => updateLastAccessed
450
451
  });
451
- async function ensureDir(path43) {
452
- await mkdir(dirname(path43), { recursive: true });
452
+ async function ensureDir(path44) {
453
+ await mkdir(dirname(path44), { recursive: true });
453
454
  }
454
455
  async function loadTrustStore(storePath = TRUST_STORE_PATH) {
455
456
  try {
@@ -527,8 +528,8 @@ function canPerformOperation(store, projectPath, operation) {
527
528
  };
528
529
  return permissions[level]?.includes(operation) ?? false;
529
530
  }
530
- function normalizePath(path43) {
531
- return join(path43);
531
+ function normalizePath(path44) {
532
+ return join(path44);
532
533
  }
533
534
  function createTrustStore(storePath = TRUST_STORE_PATH) {
534
535
  let store = null;
@@ -981,6 +982,71 @@ var init_allow_path_prompt = __esm({
981
982
  init_allowed_paths();
982
983
  }
983
984
  });
985
+
986
+ // src/tools/utils/heartbeat.ts
987
+ var heartbeat_exports = {};
988
+ __export(heartbeat_exports, {
989
+ CommandHeartbeat: () => CommandHeartbeat
990
+ });
991
+ var CommandHeartbeat;
992
+ var init_heartbeat = __esm({
993
+ "src/tools/utils/heartbeat.ts"() {
994
+ CommandHeartbeat = class {
995
+ startTime = 0;
996
+ lastActivityTime = 0;
997
+ updateInterval = null;
998
+ warnThreshold = 30;
999
+ // seconds
1000
+ updateIntervalSeconds = 10;
1001
+ // seconds
1002
+ callbacks;
1003
+ constructor(callbacks = {}) {
1004
+ this.callbacks = callbacks;
1005
+ }
1006
+ /**
1007
+ * Start monitoring - begins periodic updates and silence warnings
1008
+ */
1009
+ start() {
1010
+ this.startTime = Date.now();
1011
+ this.lastActivityTime = Date.now();
1012
+ this.updateInterval = setInterval(() => {
1013
+ const stats = this.getStats();
1014
+ this.callbacks.onUpdate?.(stats);
1015
+ if (stats.silentSeconds >= this.warnThreshold) {
1016
+ this.callbacks.onWarn?.(`\u26A0\uFE0F Command silent for ${stats.silentSeconds}s`);
1017
+ }
1018
+ }, this.updateIntervalSeconds * 1e3);
1019
+ }
1020
+ /**
1021
+ * Register activity - call this when command produces output
1022
+ * Resets the silence timer
1023
+ */
1024
+ activity() {
1025
+ this.lastActivityTime = Date.now();
1026
+ }
1027
+ /**
1028
+ * Stop monitoring - clears periodic interval
1029
+ * Should be called in finally{} block to ensure cleanup
1030
+ */
1031
+ stop() {
1032
+ if (this.updateInterval) {
1033
+ clearInterval(this.updateInterval);
1034
+ this.updateInterval = null;
1035
+ }
1036
+ }
1037
+ /**
1038
+ * Get current heartbeat statistics
1039
+ */
1040
+ getStats() {
1041
+ const now = Date.now();
1042
+ return {
1043
+ elapsedSeconds: Math.floor((now - this.startTime) / 1e3),
1044
+ silentSeconds: Math.floor((now - this.lastActivityTime) / 1e3)
1045
+ };
1046
+ }
1047
+ };
1048
+ }
1049
+ });
984
1050
  function isEnvVarSafe(name) {
985
1051
  if (SAFE_ENV_VARS.has(name)) {
986
1052
  return true;
@@ -1117,23 +1183,50 @@ Examples:
1117
1183
  }
1118
1184
  const startTime = performance.now();
1119
1185
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS;
1186
+ const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
1187
+ const heartbeat = new CommandHeartbeat2({
1188
+ onUpdate: (stats) => {
1189
+ if (stats.elapsedSeconds > 10) {
1190
+ process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
1191
+ }
1192
+ },
1193
+ onWarn: (message) => {
1194
+ process.stderr.write(`
1195
+ ${message}
1196
+ `);
1197
+ }
1198
+ });
1120
1199
  try {
1200
+ heartbeat.start();
1121
1201
  const options = {
1122
1202
  cwd: cwd ?? process.cwd(),
1123
1203
  timeout: timeoutMs,
1124
1204
  env: { ...process.env, ...env2 },
1125
1205
  shell: true,
1126
1206
  reject: false,
1207
+ buffer: false,
1208
+ // Enable streaming
1127
1209
  maxBuffer: MAX_OUTPUT_SIZE
1128
1210
  };
1129
- const result = await execa(command, options);
1211
+ const subprocess = execa(command, options);
1212
+ let stdoutBuffer = "";
1213
+ let stderrBuffer = "";
1214
+ subprocess.stdout?.on("data", (chunk) => {
1215
+ const text11 = chunk.toString();
1216
+ stdoutBuffer += text11;
1217
+ process.stdout.write(text11);
1218
+ heartbeat.activity();
1219
+ });
1220
+ subprocess.stderr?.on("data", (chunk) => {
1221
+ const text11 = chunk.toString();
1222
+ stderrBuffer += text11;
1223
+ process.stderr.write(text11);
1224
+ heartbeat.activity();
1225
+ });
1226
+ const result = await subprocess;
1130
1227
  return {
1131
- stdout: truncateOutput(
1132
- typeof result.stdout === "string" ? result.stdout : String(result.stdout ?? "")
1133
- ),
1134
- stderr: truncateOutput(
1135
- typeof result.stderr === "string" ? result.stderr : String(result.stderr ?? "")
1136
- ),
1228
+ stdout: truncateOutput(stdoutBuffer),
1229
+ stderr: truncateOutput(stderrBuffer),
1137
1230
  exitCode: result.exitCode ?? 0,
1138
1231
  duration: performance.now() - startTime
1139
1232
  };
@@ -1148,6 +1241,9 @@ Examples:
1148
1241
  `Command execution failed: ${error instanceof Error ? error.message : String(error)}`,
1149
1242
  { tool: "bash_exec", cause: error instanceof Error ? error : void 0 }
1150
1243
  );
1244
+ } finally {
1245
+ heartbeat.stop();
1246
+ process.stderr.write("\r \r");
1151
1247
  }
1152
1248
  }
1153
1249
  });
@@ -1260,6 +1356,224 @@ Examples:
1260
1356
  bashTools = [bashExecTool, bashBackgroundTool, commandExistsTool, getEnvTool];
1261
1357
  }
1262
1358
  });
1359
+ async function fileExists(filePath) {
1360
+ try {
1361
+ await fs23__default.access(filePath);
1362
+ return true;
1363
+ } catch {
1364
+ return false;
1365
+ }
1366
+ }
1367
+ var init_files = __esm({
1368
+ "src/utils/files.ts"() {
1369
+ init_errors();
1370
+ }
1371
+ });
1372
+
1373
+ // src/cli/repl/context/stack-detector.ts
1374
+ var stack_detector_exports = {};
1375
+ __export(stack_detector_exports, {
1376
+ detectProjectStack: () => detectProjectStack
1377
+ });
1378
+ async function detectStack(cwd) {
1379
+ if (await fileExists(path20__default.join(cwd, "package.json"))) return "node";
1380
+ if (await fileExists(path20__default.join(cwd, "Cargo.toml"))) return "rust";
1381
+ if (await fileExists(path20__default.join(cwd, "pyproject.toml"))) return "python";
1382
+ if (await fileExists(path20__default.join(cwd, "go.mod"))) return "go";
1383
+ if (await fileExists(path20__default.join(cwd, "pom.xml"))) return "java";
1384
+ if (await fileExists(path20__default.join(cwd, "build.gradle"))) return "java";
1385
+ if (await fileExists(path20__default.join(cwd, "build.gradle.kts"))) return "java";
1386
+ return "unknown";
1387
+ }
1388
+ async function detectPackageManager2(cwd, stack) {
1389
+ if (stack === "rust") return "cargo";
1390
+ if (stack === "python") return "pip";
1391
+ if (stack === "go") return "go";
1392
+ if (stack === "java") {
1393
+ if (await fileExists(path20__default.join(cwd, "build.gradle")) || await fileExists(path20__default.join(cwd, "build.gradle.kts"))) {
1394
+ return "gradle";
1395
+ }
1396
+ if (await fileExists(path20__default.join(cwd, "pom.xml"))) {
1397
+ return "maven";
1398
+ }
1399
+ }
1400
+ if (stack === "node") {
1401
+ if (await fileExists(path20__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
1402
+ if (await fileExists(path20__default.join(cwd, "yarn.lock"))) return "yarn";
1403
+ if (await fileExists(path20__default.join(cwd, "bun.lockb"))) return "bun";
1404
+ return "npm";
1405
+ }
1406
+ return null;
1407
+ }
1408
+ async function parsePackageJson(cwd) {
1409
+ const packageJsonPath = path20__default.join(cwd, "package.json");
1410
+ try {
1411
+ const content = await fs23__default.readFile(packageJsonPath, "utf-8");
1412
+ const pkg = JSON.parse(content);
1413
+ const allDeps = {
1414
+ ...pkg.dependencies,
1415
+ ...pkg.devDependencies
1416
+ };
1417
+ const frameworks = [];
1418
+ if (allDeps.react) frameworks.push("React");
1419
+ if (allDeps.vue) frameworks.push("Vue");
1420
+ if (allDeps["@angular/core"]) frameworks.push("Angular");
1421
+ if (allDeps.next) frameworks.push("Next.js");
1422
+ if (allDeps.nuxt) frameworks.push("Nuxt");
1423
+ if (allDeps.express) frameworks.push("Express");
1424
+ if (allDeps.fastify) frameworks.push("Fastify");
1425
+ if (allDeps.nestjs || allDeps["@nestjs/core"]) frameworks.push("NestJS");
1426
+ const buildTools2 = [];
1427
+ if (allDeps.webpack) buildTools2.push("webpack");
1428
+ if (allDeps.vite) buildTools2.push("vite");
1429
+ if (allDeps.rollup) buildTools2.push("rollup");
1430
+ if (allDeps.tsup) buildTools2.push("tsup");
1431
+ if (allDeps.esbuild) buildTools2.push("esbuild");
1432
+ if (pkg.scripts?.build) buildTools2.push("build");
1433
+ const testingFrameworks = [];
1434
+ if (allDeps.vitest) testingFrameworks.push("vitest");
1435
+ if (allDeps.jest) testingFrameworks.push("jest");
1436
+ if (allDeps.mocha) testingFrameworks.push("mocha");
1437
+ if (allDeps.chai) testingFrameworks.push("chai");
1438
+ if (allDeps["@playwright/test"]) testingFrameworks.push("playwright");
1439
+ if (allDeps.cypress) testingFrameworks.push("cypress");
1440
+ const languages = ["JavaScript"];
1441
+ if (allDeps.typescript || await fileExists(path20__default.join(cwd, "tsconfig.json"))) {
1442
+ languages.push("TypeScript");
1443
+ }
1444
+ return {
1445
+ dependencies: allDeps,
1446
+ frameworks,
1447
+ buildTools: buildTools2,
1448
+ testingFrameworks,
1449
+ languages
1450
+ };
1451
+ } catch {
1452
+ return {
1453
+ dependencies: {},
1454
+ frameworks: [],
1455
+ buildTools: [],
1456
+ testingFrameworks: [],
1457
+ languages: []
1458
+ };
1459
+ }
1460
+ }
1461
+ async function parsePomXml(cwd) {
1462
+ const pomPath = path20__default.join(cwd, "pom.xml");
1463
+ try {
1464
+ const content = await fs23__default.readFile(pomPath, "utf-8");
1465
+ const dependencies = {};
1466
+ const frameworks = [];
1467
+ const buildTools2 = ["maven"];
1468
+ const testingFrameworks = [];
1469
+ const depRegex = /<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>/g;
1470
+ let match;
1471
+ while ((match = depRegex.exec(content)) !== null) {
1472
+ const groupId = match[1];
1473
+ const artifactId = match[2];
1474
+ if (!groupId || !artifactId) continue;
1475
+ const fullName = `${groupId}:${artifactId}`;
1476
+ dependencies[fullName] = "unknown";
1477
+ if (artifactId.includes("spring-boot")) {
1478
+ if (!frameworks.includes("Spring Boot")) frameworks.push("Spring Boot");
1479
+ }
1480
+ if (artifactId.includes("spring-webmvc") || artifactId.includes("spring-web")) {
1481
+ if (!frameworks.includes("Spring MVC")) frameworks.push("Spring MVC");
1482
+ }
1483
+ if (artifactId.includes("hibernate")) {
1484
+ if (!frameworks.includes("Hibernate")) frameworks.push("Hibernate");
1485
+ }
1486
+ if (artifactId === "junit-jupiter" || artifactId === "junit") {
1487
+ if (!testingFrameworks.includes("JUnit")) testingFrameworks.push("JUnit");
1488
+ }
1489
+ if (artifactId === "mockito-core") {
1490
+ if (!testingFrameworks.includes("Mockito")) testingFrameworks.push("Mockito");
1491
+ }
1492
+ }
1493
+ return { dependencies, frameworks, buildTools: buildTools2, testingFrameworks };
1494
+ } catch {
1495
+ return { dependencies: {}, frameworks: [], buildTools: ["maven"], testingFrameworks: [] };
1496
+ }
1497
+ }
1498
+ async function parsePyprojectToml(cwd) {
1499
+ const pyprojectPath = path20__default.join(cwd, "pyproject.toml");
1500
+ try {
1501
+ const content = await fs23__default.readFile(pyprojectPath, "utf-8");
1502
+ const dependencies = {};
1503
+ const frameworks = [];
1504
+ const buildTools2 = ["pip"];
1505
+ const testingFrameworks = [];
1506
+ const lines = content.split("\n");
1507
+ for (const line of lines) {
1508
+ const trimmed = line.trim();
1509
+ if (trimmed.match(/^["']?[\w-]+["']?\s*=\s*["'][\^~>=<]+[\d.]+["']/)) {
1510
+ const depMatch = trimmed.match(/^["']?([\w-]+)["']?\s*=\s*["']([\^~>=<]+[\d.]+)["']/);
1511
+ if (depMatch && depMatch[1] && depMatch[2]) {
1512
+ dependencies[depMatch[1]] = depMatch[2];
1513
+ }
1514
+ }
1515
+ if (trimmed.includes("fastapi")) frameworks.push("FastAPI");
1516
+ if (trimmed.includes("django")) frameworks.push("Django");
1517
+ if (trimmed.includes("flask")) frameworks.push("Flask");
1518
+ if (trimmed.includes("pytest")) testingFrameworks.push("pytest");
1519
+ if (trimmed.includes("unittest")) testingFrameworks.push("unittest");
1520
+ }
1521
+ return { dependencies, frameworks, buildTools: buildTools2, testingFrameworks };
1522
+ } catch {
1523
+ return { dependencies: {}, frameworks: [], buildTools: ["pip"], testingFrameworks: [] };
1524
+ }
1525
+ }
1526
+ async function detectProjectStack(cwd) {
1527
+ const stack = await detectStack(cwd);
1528
+ const packageManager = await detectPackageManager2(cwd, stack);
1529
+ let dependencies = {};
1530
+ let frameworks = [];
1531
+ let buildTools2 = [];
1532
+ let testingFrameworks = [];
1533
+ let languages = [];
1534
+ if (stack === "node") {
1535
+ const parsed = await parsePackageJson(cwd);
1536
+ dependencies = parsed.dependencies;
1537
+ frameworks = parsed.frameworks;
1538
+ buildTools2 = parsed.buildTools;
1539
+ testingFrameworks = parsed.testingFrameworks;
1540
+ languages = parsed.languages;
1541
+ } else if (stack === "java") {
1542
+ const parsed = await parsePomXml(cwd);
1543
+ dependencies = parsed.dependencies;
1544
+ frameworks = parsed.frameworks;
1545
+ buildTools2 = parsed.buildTools;
1546
+ testingFrameworks = parsed.testingFrameworks;
1547
+ languages = ["Java"];
1548
+ } else if (stack === "python") {
1549
+ const parsed = await parsePyprojectToml(cwd);
1550
+ dependencies = parsed.dependencies;
1551
+ frameworks = parsed.frameworks;
1552
+ buildTools2 = parsed.buildTools;
1553
+ testingFrameworks = parsed.testingFrameworks;
1554
+ languages = ["Python"];
1555
+ } else if (stack === "go") {
1556
+ languages = ["Go"];
1557
+ buildTools2 = ["go"];
1558
+ } else if (stack === "rust") {
1559
+ languages = ["Rust"];
1560
+ buildTools2 = ["cargo"];
1561
+ }
1562
+ return {
1563
+ stack,
1564
+ packageManager,
1565
+ dependencies,
1566
+ frameworks,
1567
+ buildTools: buildTools2,
1568
+ testingFrameworks,
1569
+ languages
1570
+ };
1571
+ }
1572
+ var init_stack_detector = __esm({
1573
+ "src/cli/repl/context/stack-detector.ts"() {
1574
+ init_files();
1575
+ }
1576
+ });
1263
1577
  function findPackageJson() {
1264
1578
  let dir = dirname(fileURLToPath(import.meta.url));
1265
1579
  for (let i = 0; i < 10; i++) {
@@ -1418,8 +1732,8 @@ Generated by Corbat-Coco v0.1.0
1418
1732
 
1419
1733
  // src/cli/commands/init.ts
1420
1734
  function registerInitCommand(program2) {
1421
- program2.command("init").description("Initialize a new Corbat-Coco project").argument("[path]", "Project directory path", ".").option("-t, --template <template>", "Project template to use").option("-y, --yes", "Skip prompts and use defaults").option("--skip-discovery", "Skip the discovery phase (use existing spec)").action(async (path43, options) => {
1422
- await runInit(path43, options);
1735
+ program2.command("init").description("Initialize a new Corbat-Coco project").argument("[path]", "Project directory path", ".").option("-t, --template <template>", "Project template to use").option("-y, --yes", "Skip prompts and use defaults").option("--skip-discovery", "Skip the discovery phase (use existing spec)").action(async (path44, options) => {
1736
+ await runInit(path44, options);
1423
1737
  });
1424
1738
  }
1425
1739
  async function runInit(projectPath, options) {
@@ -1498,18 +1812,18 @@ async function gatherProjectInfo() {
1498
1812
  language
1499
1813
  };
1500
1814
  }
1501
- function getDefaultProjectInfo(path43) {
1502
- const name = path43 === "." ? "my-project" : path43.split("/").pop() || "my-project";
1815
+ function getDefaultProjectInfo(path44) {
1816
+ const name = path44 === "." ? "my-project" : path44.split("/").pop() || "my-project";
1503
1817
  return {
1504
1818
  name,
1505
1819
  description: "",
1506
1820
  language: "typescript"
1507
1821
  };
1508
1822
  }
1509
- async function checkExistingProject(path43) {
1823
+ async function checkExistingProject(path44) {
1510
1824
  try {
1511
- const fs43 = await import('fs/promises');
1512
- await fs43.access(`${path43}/.coco`);
1825
+ const fs44 = await import('fs/promises');
1826
+ await fs44.access(`${path44}/.coco`);
1513
1827
  return true;
1514
1828
  } catch {
1515
1829
  return false;
@@ -5973,8 +6287,10 @@ var OpenAIProvider = class {
5973
6287
  const timeoutInterval = setInterval(checkTimeout, 5e3);
5974
6288
  try {
5975
6289
  for await (const chunk of stream) {
5976
- lastActivityTime = Date.now();
5977
6290
  const delta = chunk.choices[0]?.delta;
6291
+ if (delta?.content || delta?.tool_calls) {
6292
+ lastActivityTime = Date.now();
6293
+ }
5978
6294
  if (delta?.content) {
5979
6295
  yield { type: "text", text: delta.content };
5980
6296
  }
@@ -6020,10 +6336,20 @@ var OpenAIProvider = class {
6020
6336
  let input = {};
6021
6337
  try {
6022
6338
  input = builder.arguments ? JSON.parse(builder.arguments) : {};
6023
- } catch {
6339
+ } catch (error) {
6024
6340
  console.warn(
6025
- `[OpenAI] Failed to parse tool call arguments: ${builder.arguments?.slice(0, 100)}`
6341
+ `[OpenAI] Failed to parse tool call arguments for ${builder.name}: ${builder.arguments?.slice(0, 100)}`
6026
6342
  );
6343
+ try {
6344
+ if (builder.arguments) {
6345
+ const repaired = jsonrepair(builder.arguments);
6346
+ input = JSON.parse(repaired);
6347
+ console.log(`[OpenAI] \u2713 Successfully repaired JSON for ${builder.name}`);
6348
+ }
6349
+ } catch (repairError) {
6350
+ console.error(`[OpenAI] Cannot repair JSON for ${builder.name}, using empty object`);
6351
+ console.error(`[OpenAI] Original error:`, error);
6352
+ }
6027
6353
  }
6028
6354
  yield {
6029
6355
  type: "tool_use_end",
@@ -8368,20 +8694,20 @@ async function createCliPhaseContext(projectPath, _onUserInput) {
8368
8694
  },
8369
8695
  tools: {
8370
8696
  file: {
8371
- async read(path43) {
8372
- const fs43 = await import('fs/promises');
8373
- return fs43.readFile(path43, "utf-8");
8697
+ async read(path44) {
8698
+ const fs44 = await import('fs/promises');
8699
+ return fs44.readFile(path44, "utf-8");
8374
8700
  },
8375
- async write(path43, content) {
8376
- const fs43 = await import('fs/promises');
8701
+ async write(path44, content) {
8702
+ const fs44 = await import('fs/promises');
8377
8703
  const nodePath = await import('path');
8378
- await fs43.mkdir(nodePath.dirname(path43), { recursive: true });
8379
- await fs43.writeFile(path43, content, "utf-8");
8704
+ await fs44.mkdir(nodePath.dirname(path44), { recursive: true });
8705
+ await fs44.writeFile(path44, content, "utf-8");
8380
8706
  },
8381
- async exists(path43) {
8382
- const fs43 = await import('fs/promises');
8707
+ async exists(path44) {
8708
+ const fs44 = await import('fs/promises');
8383
8709
  try {
8384
- await fs43.access(path43);
8710
+ await fs44.access(path44);
8385
8711
  return true;
8386
8712
  } catch {
8387
8713
  return false;
@@ -8620,16 +8946,16 @@ async function loadTasks(_options) {
8620
8946
  ];
8621
8947
  }
8622
8948
  async function checkProjectState() {
8623
- const fs43 = await import('fs/promises');
8949
+ const fs44 = await import('fs/promises');
8624
8950
  let hasProject = false;
8625
8951
  let hasPlan = false;
8626
8952
  try {
8627
- await fs43.access(".coco");
8953
+ await fs44.access(".coco");
8628
8954
  hasProject = true;
8629
8955
  } catch {
8630
8956
  }
8631
8957
  try {
8632
- await fs43.access(".coco/planning/backlog.json");
8958
+ await fs44.access(".coco/planning/backlog.json");
8633
8959
  hasPlan = true;
8634
8960
  } catch {
8635
8961
  }
@@ -8733,24 +9059,24 @@ function getPhaseStatusForPhase(phase) {
8733
9059
  return "in_progress";
8734
9060
  }
8735
9061
  async function loadProjectState(cwd, config) {
8736
- const fs43 = await import('fs/promises');
8737
- const path43 = await import('path');
8738
- const statePath = path43.join(cwd, ".coco", "state.json");
8739
- const backlogPath = path43.join(cwd, ".coco", "planning", "backlog.json");
8740
- const checkpointDir = path43.join(cwd, ".coco", "checkpoints");
9062
+ const fs44 = await import('fs/promises');
9063
+ const path44 = await import('path');
9064
+ const statePath = path44.join(cwd, ".coco", "state.json");
9065
+ const backlogPath = path44.join(cwd, ".coco", "planning", "backlog.json");
9066
+ const checkpointDir = path44.join(cwd, ".coco", "checkpoints");
8741
9067
  let currentPhase = "idle";
8742
9068
  let metrics;
8743
9069
  let sprint;
8744
9070
  let checkpoints = [];
8745
9071
  try {
8746
- const stateContent = await fs43.readFile(statePath, "utf-8");
9072
+ const stateContent = await fs44.readFile(statePath, "utf-8");
8747
9073
  const stateData = JSON.parse(stateContent);
8748
9074
  currentPhase = stateData.currentPhase || "idle";
8749
9075
  metrics = stateData.metrics;
8750
9076
  } catch {
8751
9077
  }
8752
9078
  try {
8753
- const backlogContent = await fs43.readFile(backlogPath, "utf-8");
9079
+ const backlogContent = await fs44.readFile(backlogPath, "utf-8");
8754
9080
  const backlogData = JSON.parse(backlogContent);
8755
9081
  if (backlogData.currentSprint) {
8756
9082
  const tasks = backlogData.tasks || [];
@@ -8772,7 +9098,7 @@ async function loadProjectState(cwd, config) {
8772
9098
  } catch {
8773
9099
  }
8774
9100
  try {
8775
- const files = await fs43.readdir(checkpointDir);
9101
+ const files = await fs44.readdir(checkpointDir);
8776
9102
  checkpoints = files.filter((f) => f.endsWith(".json")).sort().reverse();
8777
9103
  } catch {
8778
9104
  }
@@ -8908,8 +9234,8 @@ async function restoreFromCheckpoint(_checkpoint) {
8908
9234
  }
8909
9235
  async function checkProjectExists() {
8910
9236
  try {
8911
- const fs43 = await import('fs/promises');
8912
- await fs43.access(".coco");
9237
+ const fs44 = await import('fs/promises');
9238
+ await fs44.access(".coco");
8913
9239
  return true;
8914
9240
  } catch {
8915
9241
  return false;
@@ -9570,12 +9896,12 @@ async function loadConfig2() {
9570
9896
  };
9571
9897
  }
9572
9898
  async function saveConfig(config) {
9573
- const fs43 = await import('fs/promises');
9574
- await fs43.mkdir(".coco", { recursive: true });
9575
- await fs43.writeFile(".coco/config.json", JSON.stringify(config, null, 2));
9899
+ const fs44 = await import('fs/promises');
9900
+ await fs44.mkdir(".coco", { recursive: true });
9901
+ await fs44.writeFile(".coco/config.json", JSON.stringify(config, null, 2));
9576
9902
  }
9577
- function getNestedValue(obj, path43) {
9578
- const keys = path43.split(".");
9903
+ function getNestedValue(obj, path44) {
9904
+ const keys = path44.split(".");
9579
9905
  let current = obj;
9580
9906
  for (const key of keys) {
9581
9907
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -9585,8 +9911,8 @@ function getNestedValue(obj, path43) {
9585
9911
  }
9586
9912
  return current;
9587
9913
  }
9588
- function setNestedValue(obj, path43, value) {
9589
- const keys = path43.split(".");
9914
+ function setNestedValue(obj, path44, value) {
9915
+ const keys = path44.split(".");
9590
9916
  let current = obj;
9591
9917
  for (let i = 0; i < keys.length - 1; i++) {
9592
9918
  const key = keys[i];
@@ -9807,8 +10133,8 @@ var MCPRegistryImpl = class {
9807
10133
  /**
9808
10134
  * Ensure directory exists
9809
10135
  */
9810
- async ensureDir(path43) {
9811
- await mkdir(dirname(path43), { recursive: true });
10136
+ async ensureDir(path44) {
10137
+ await mkdir(dirname(path44), { recursive: true });
9812
10138
  }
9813
10139
  };
9814
10140
  function createMCPRegistry(registryPath) {
@@ -10327,9 +10653,9 @@ function createEmptyMemoryContext() {
10327
10653
  errors: []
10328
10654
  };
10329
10655
  }
10330
- function createMissingMemoryFile(path43, level) {
10656
+ function createMissingMemoryFile(path44, level) {
10331
10657
  return {
10332
- path: path43,
10658
+ path: path44,
10333
10659
  level,
10334
10660
  content: "",
10335
10661
  sections: [],
@@ -10383,14 +10709,42 @@ function generateToolCatalog(registry) {
10383
10709
  }
10384
10710
  var COCO_SYSTEM_PROMPT = `You are Corbat-Coco, an autonomous coding assistant with an extensive toolkit.
10385
10711
 
10386
- ## Your Approach: Tool-Aware Problem Solving
10712
+ ## YOUR PRIMARY DIRECTIVE: EXECUTE, DON'T TALK ABOUT EXECUTING
10713
+
10714
+ \u{1F6A8} **CRITICAL - READ THIS FIRST** \u{1F6A8}
10715
+ YOU ARE AN EXECUTION AGENT, NOT A CONVERSATIONAL ASSISTANT.
10716
+
10717
+ **WRONG BEHAVIOR (Never do this):**
10718
+ \u274C "I'll create a file called hello.js with a function..."
10719
+ \u274C "I created hello.js with the following code..."
10720
+ \u274C "Here's what the file would look like..."
10721
+ \u274C Showing code blocks without calling write_file tool
10722
+
10723
+ **CORRECT BEHAVIOR (Always do this):**
10724
+ \u2705 Immediately call write_file tool with the code
10725
+ \u2705 Then say "Created hello.js with greeting function"
10726
+ \u2705 TOOLS FIRST, then brief confirmation
10727
+
10728
+ **Core Principle: USE TOOLS, DON'T DESCRIBE**
10729
+ \u26A0\uFE0F CRITICAL: You MUST use your tools to perform actions. NEVER just describe what you would do or claim you did something without actually calling a tool.
10730
+
10731
+ **Tool Calling is MANDATORY:**
10732
+ - User says "create a file" \u2192 CALL write_file tool FIRST (don't show code, don't explain, just CALL THE TOOL)
10733
+ - User says "search the web" \u2192 CALL web_search tool FIRST (don't describe what you would search for)
10734
+ - User says "run tests" \u2192 CALL bash_exec tool FIRST (don't say you ran them, actually run them)
10735
+ - EVERY action requires a TOOL CALL. Text responses are ONLY for brief confirmations AFTER tools execute.
10736
+
10737
+ **Execution Process:**
10738
+ 1. **Analyze**: Understand what the user wants (in your head, don't output this)
10739
+ 2. **Execute**: IMMEDIATELY CALL THE APPROPRIATE TOOLS (this is mandatory, not optional)
10740
+ 3. **Respond**: Brief confirmation of what was done (AFTER tools executed)
10387
10741
 
10388
- When the user asks you to do something, follow this process:
10389
- 1. **Analyze the request**: Understand what the user needs
10390
- 2. **Scan your tools**: Review which of your available tools can help accomplish the task
10391
- 3. **Plan your approach**: Decide which tools to use and in what order
10392
- 4. **Execute**: Use the appropriate tools, combining multiple when needed
10393
- 5. **Verify**: Check your work (read files after editing, run tests after changes)
10742
+ **Critical Rules:**
10743
+ - User says "create X with Y" \u2192 Immediately call write_file/edit_file tool, no discussion
10744
+ - If a task needs data you don't have, fetch it with web_search/web_fetch FIRST, THEN complete the task with other tools
10745
+ - Never ask "should I do this?" or "do you want me to...?" - JUST DO IT (with tools)
10746
+ - If you don't call tools, you didn't do the task - showing code is NOT the same as creating files
10747
+ - NEVER show code blocks as examples - ALWAYS write them to files with tools
10394
10748
 
10395
10749
  **IMPORTANT**: You have many tools beyond basic file/bash/git. Before answering "I can't do that", check if any of your tools can help. For example:
10396
10750
  - Need information from the internet? Use **web_search** and **web_fetch**
@@ -10408,13 +10762,19 @@ When the user asks you to do something, follow this process:
10408
10762
  {TOOL_CATALOG}
10409
10763
 
10410
10764
  ## Guidelines
10411
- - Be helpful and direct
10412
- - If a task requires multiple steps, execute them one by one
10413
- - Always verify your work by reading files after editing or running tests after changes
10414
- - You can use multiple tools together for complex tasks
10415
- - When uncertain which tool to use, check the full list above
10765
+ - **Be action-oriented**: Execute tasks immediately without asking for confirmation
10766
+ - **Multi-step tasks**: Chain tools together to complete the full request
10767
+ - **Always verify**: Read files after editing, run tests after changes
10768
+ - **Don't present options**: If the user says "create X", create it with reasonable defaults
10769
+ - **Don't ask "should I..."**: The user already told you what to do by making the request
10770
+ - **Combine tools**: Use web_search + write_file, bash + read_file, etc. to complete tasks fully
10416
10771
  - **Never** add "Co-Authored-By", "Generated by", or any AI attribution to commits, code comments, documentation, or PR descriptions. All output must read as if written by the developer.
10417
10772
 
10773
+ **Example Flows:**
10774
+ - "Create an HTML with weather data" \u2192 web_search for weather \u2192 write_file with HTML \u2192 DONE
10775
+ - "Add tests for function X" \u2192 read_file to see X \u2192 write_file with tests \u2192 bash to run tests \u2192 DONE
10776
+ - "Fix the bug in Y" \u2192 read_file to understand \u2192 edit_file to fix \u2192 bash to test \u2192 DONE
10777
+
10418
10778
  ## File Access
10419
10779
  File operations are restricted to the project directory by default.
10420
10780
  When you need to access a path outside the project, use the **authorize_path** tool first \u2014 it will ask the user for permission interactively. Once authorized, proceed with the file operation.
@@ -10506,9 +10866,45 @@ function getConversationContext(session, toolRegistry) {
10506
10866
  # Project Instructions (from COCO.md/CLAUDE.md)
10507
10867
 
10508
10868
  ${session.memoryContext.combinedContent}`;
10869
+ }
10870
+ if (session.projectContext) {
10871
+ const stackInfo = formatStackContext(session.projectContext);
10872
+ systemPrompt = `${systemPrompt}
10873
+
10874
+ ${stackInfo}`;
10509
10875
  }
10510
10876
  return [{ role: "system", content: systemPrompt }, ...session.messages];
10511
10877
  }
10878
+ function formatStackContext(ctx) {
10879
+ const parts = [];
10880
+ parts.push("# Project Technology Stack");
10881
+ parts.push("");
10882
+ parts.push(`**Language/Runtime:** ${ctx.stack}`);
10883
+ if (ctx.packageManager) {
10884
+ parts.push(`**Package Manager:** ${ctx.packageManager}`);
10885
+ }
10886
+ if (ctx.frameworks.length > 0) {
10887
+ parts.push(`**Frameworks:** ${ctx.frameworks.join(", ")}`);
10888
+ }
10889
+ if (ctx.languages.length > 0) {
10890
+ parts.push(`**Languages:** ${ctx.languages.join(", ")}`);
10891
+ }
10892
+ if (ctx.testingFrameworks.length > 0) {
10893
+ parts.push(`**Testing Frameworks:** ${ctx.testingFrameworks.join(", ")}`);
10894
+ }
10895
+ if (ctx.buildTools.length > 0) {
10896
+ parts.push(`**Build Tools:** ${ctx.buildTools.join(", ")}`);
10897
+ }
10898
+ const keyDeps = Object.entries(ctx.dependencies).slice(0, 10).map(([name, version]) => `${name}@${version}`).join(", ");
10899
+ if (keyDeps) {
10900
+ parts.push(`**Key Dependencies:** ${keyDeps}`);
10901
+ }
10902
+ parts.push("");
10903
+ parts.push(
10904
+ "**IMPORTANT:** When suggesting libraries, frameworks, or dependencies, ONLY recommend technologies compatible with the stack above. Do not suggest installing Node.js packages in a Java project, or Java libraries in a Python project."
10905
+ );
10906
+ return parts.join("\n");
10907
+ }
10512
10908
  function clearSession(session) {
10513
10909
  session.messages = [];
10514
10910
  }
@@ -13259,8 +13655,8 @@ async function listTrustedProjects2(trustStore) {
13259
13655
  p9.log.message("");
13260
13656
  for (const project of projects) {
13261
13657
  const level = project.approvalLevel.toUpperCase().padEnd(5);
13262
- const path43 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
13263
- p9.log.message(` [${level}] ${path43}`);
13658
+ const path44 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
13659
+ p9.log.message(` [${level}] ${path44}`);
13264
13660
  p9.log.message(` Last accessed: ${new Date(project.lastAccessed).toLocaleString()}`);
13265
13661
  }
13266
13662
  p9.log.message("");
@@ -14430,7 +14826,7 @@ async function getCheckpoint(session, checkpointId) {
14430
14826
  return store.checkpoints.find((cp) => cp.id === checkpointId) ?? null;
14431
14827
  }
14432
14828
  async function restoreFiles(checkpoint, excludeFiles) {
14433
- const fs43 = await import('fs/promises');
14829
+ const fs44 = await import('fs/promises');
14434
14830
  const restored = [];
14435
14831
  const failed = [];
14436
14832
  for (const fileCheckpoint of checkpoint.files) {
@@ -14438,7 +14834,7 @@ async function restoreFiles(checkpoint, excludeFiles) {
14438
14834
  continue;
14439
14835
  }
14440
14836
  try {
14441
- await fs43.writeFile(fileCheckpoint.filePath, fileCheckpoint.originalContent, "utf-8");
14837
+ await fs44.writeFile(fileCheckpoint.filePath, fileCheckpoint.originalContent, "utf-8");
14442
14838
  restored.push(fileCheckpoint.filePath);
14443
14839
  } catch (error) {
14444
14840
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -14520,8 +14916,8 @@ function displayRewindResult(result) {
14520
14916
  const fileName = filePath.split("/").pop() ?? filePath;
14521
14917
  console.log(`${chalk42.green(String.fromCodePoint(10003))} Restored: ${fileName}`);
14522
14918
  }
14523
- for (const { path: path43, error } of result.filesFailed) {
14524
- const fileName = path43.split("/").pop() ?? path43;
14919
+ for (const { path: path44, error } of result.filesFailed) {
14920
+ const fileName = path44.split("/").pop() ?? path44;
14525
14921
  console.log(`${chalk42.red(String.fromCodePoint(10007))} Failed: ${fileName} (${error})`);
14526
14922
  }
14527
14923
  if (result.conversationRestored) {
@@ -15954,8 +16350,8 @@ function formatToolSummary(toolName, input) {
15954
16350
  return String(input.path || ".");
15955
16351
  case "search_files": {
15956
16352
  const pattern = String(input.pattern || "");
15957
- const path43 = input.path ? ` in ${input.path}` : "";
15958
- return `"${pattern}"${path43}`;
16353
+ const path44 = input.path ? ` in ${input.path}` : "";
16354
+ return `"${pattern}"${path44}`;
15959
16355
  }
15960
16356
  case "bash_exec": {
15961
16357
  const cmd = String(input.command || "");
@@ -20989,10 +21385,10 @@ var CoverageAnalyzer = class {
20989
21385
  join(this.projectPath, ".coverage", "coverage-summary.json"),
20990
21386
  join(this.projectPath, "coverage", "lcov-report", "coverage-summary.json")
20991
21387
  ];
20992
- for (const path43 of possiblePaths) {
21388
+ for (const path44 of possiblePaths) {
20993
21389
  try {
20994
- await access(path43, constants.R_OK);
20995
- const content = await readFile(path43, "utf-8");
21390
+ await access(path44, constants.R_OK);
21391
+ const content = await readFile(path44, "utf-8");
20996
21392
  const report = JSON.parse(content);
20997
21393
  return parseCoverageSummary(report);
20998
21394
  } catch {
@@ -24071,7 +24467,21 @@ Examples:
24071
24467
  const projectDir = cwd ?? process.cwd();
24072
24468
  const startTime = performance.now();
24073
24469
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS3;
24470
+ const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
24471
+ const heartbeat = new CommandHeartbeat2({
24472
+ onUpdate: (stats) => {
24473
+ if (stats.elapsedSeconds > 10) {
24474
+ process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
24475
+ }
24476
+ },
24477
+ onWarn: (message) => {
24478
+ process.stderr.write(`
24479
+ ${message}
24480
+ `);
24481
+ }
24482
+ });
24074
24483
  try {
24484
+ heartbeat.start();
24075
24485
  const pm = packageManager ?? await detectPackageManager(projectDir);
24076
24486
  const cmdArgs = ["run", script];
24077
24487
  if (args && args.length > 0) {
@@ -24082,13 +24492,30 @@ Examples:
24082
24492
  timeout: timeoutMs,
24083
24493
  env: { ...process.env, ...env2 },
24084
24494
  reject: false,
24495
+ buffer: false,
24496
+ // Enable streaming
24085
24497
  maxBuffer: MAX_OUTPUT_SIZE2
24086
24498
  };
24087
- const result = await execa(pm, cmdArgs, options);
24499
+ const subprocess = execa(pm, cmdArgs, options);
24500
+ let stdoutBuffer = "";
24501
+ let stderrBuffer = "";
24502
+ subprocess.stdout?.on("data", (chunk) => {
24503
+ const text11 = chunk.toString();
24504
+ stdoutBuffer += text11;
24505
+ process.stdout.write(text11);
24506
+ heartbeat.activity();
24507
+ });
24508
+ subprocess.stderr?.on("data", (chunk) => {
24509
+ const text11 = chunk.toString();
24510
+ stderrBuffer += text11;
24511
+ process.stderr.write(text11);
24512
+ heartbeat.activity();
24513
+ });
24514
+ const result = await subprocess;
24088
24515
  return {
24089
24516
  success: result.exitCode === 0,
24090
- stdout: truncateOutput2(String(result.stdout ?? "")),
24091
- stderr: truncateOutput2(String(result.stderr ?? "")),
24517
+ stdout: truncateOutput2(stdoutBuffer),
24518
+ stderr: truncateOutput2(stderrBuffer),
24092
24519
  exitCode: result.exitCode ?? 0,
24093
24520
  duration: performance.now() - startTime,
24094
24521
  packageManager: pm
@@ -24104,6 +24531,9 @@ Examples:
24104
24531
  `Failed to run script '${script}': ${error instanceof Error ? error.message : String(error)}`,
24105
24532
  { tool: "run_script", cause: error instanceof Error ? error : void 0 }
24106
24533
  );
24534
+ } finally {
24535
+ heartbeat.stop();
24536
+ process.stderr.write("\r \r");
24107
24537
  }
24108
24538
  }
24109
24539
  });
@@ -24129,7 +24559,21 @@ Examples:
24129
24559
  const projectDir = cwd ?? process.cwd();
24130
24560
  const startTime = performance.now();
24131
24561
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS3;
24562
+ const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
24563
+ const heartbeat = new CommandHeartbeat2({
24564
+ onUpdate: (stats) => {
24565
+ if (stats.elapsedSeconds > 10) {
24566
+ process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
24567
+ }
24568
+ },
24569
+ onWarn: (message) => {
24570
+ process.stderr.write(`
24571
+ ${message}
24572
+ `);
24573
+ }
24574
+ });
24132
24575
  try {
24576
+ heartbeat.start();
24133
24577
  const pm = packageManager ?? await detectPackageManager(projectDir);
24134
24578
  let cmdArgs;
24135
24579
  if (packages && packages.length > 0) {
@@ -24169,13 +24613,30 @@ Examples:
24169
24613
  cwd: projectDir,
24170
24614
  timeout: timeoutMs,
24171
24615
  reject: false,
24616
+ buffer: false,
24617
+ // Enable streaming
24172
24618
  maxBuffer: MAX_OUTPUT_SIZE2
24173
24619
  };
24174
- const result = await execa(pm, cmdArgs, options);
24620
+ const subprocess = execa(pm, cmdArgs, options);
24621
+ let stdoutBuffer = "";
24622
+ let stderrBuffer = "";
24623
+ subprocess.stdout?.on("data", (chunk) => {
24624
+ const text11 = chunk.toString();
24625
+ stdoutBuffer += text11;
24626
+ process.stdout.write(text11);
24627
+ heartbeat.activity();
24628
+ });
24629
+ subprocess.stderr?.on("data", (chunk) => {
24630
+ const text11 = chunk.toString();
24631
+ stderrBuffer += text11;
24632
+ process.stderr.write(text11);
24633
+ heartbeat.activity();
24634
+ });
24635
+ const result = await subprocess;
24175
24636
  return {
24176
24637
  success: result.exitCode === 0,
24177
- stdout: truncateOutput2(String(result.stdout ?? "")),
24178
- stderr: truncateOutput2(String(result.stderr ?? "")),
24638
+ stdout: truncateOutput2(stdoutBuffer),
24639
+ stderr: truncateOutput2(stderrBuffer),
24179
24640
  exitCode: result.exitCode ?? 0,
24180
24641
  duration: performance.now() - startTime,
24181
24642
  packageManager: pm
@@ -24191,6 +24652,9 @@ Examples:
24191
24652
  `Failed to install dependencies: ${error instanceof Error ? error.message : String(error)}`,
24192
24653
  { tool: "install_deps", cause: error instanceof Error ? error : void 0 }
24193
24654
  );
24655
+ } finally {
24656
+ heartbeat.stop();
24657
+ process.stderr.write("\r \r");
24194
24658
  }
24195
24659
  }
24196
24660
  });
@@ -24215,12 +24679,26 @@ Examples:
24215
24679
  const projectDir = cwd ?? process.cwd();
24216
24680
  const startTime = performance.now();
24217
24681
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS3;
24682
+ const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
24683
+ const heartbeat = new CommandHeartbeat2({
24684
+ onUpdate: (stats) => {
24685
+ if (stats.elapsedSeconds > 10) {
24686
+ process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
24687
+ }
24688
+ },
24689
+ onWarn: (message) => {
24690
+ process.stderr.write(`
24691
+ ${message}
24692
+ `);
24693
+ }
24694
+ });
24218
24695
  try {
24219
24696
  try {
24220
24697
  await fs23__default.access(path20__default.join(projectDir, "Makefile"));
24221
24698
  } catch {
24222
24699
  throw new ToolError("No Makefile found in directory", { tool: "make" });
24223
24700
  }
24701
+ heartbeat.start();
24224
24702
  const cmdArgs = [];
24225
24703
  if (target) {
24226
24704
  cmdArgs.push(...target.split(/\s+/));
@@ -24233,13 +24711,30 @@ Examples:
24233
24711
  timeout: timeoutMs,
24234
24712
  env: { ...process.env, ...env2 },
24235
24713
  reject: false,
24714
+ buffer: false,
24715
+ // Enable streaming
24236
24716
  maxBuffer: MAX_OUTPUT_SIZE2
24237
24717
  };
24238
- const result = await execa("make", cmdArgs, options);
24718
+ const subprocess = execa("make", cmdArgs, options);
24719
+ let stdoutBuffer = "";
24720
+ let stderrBuffer = "";
24721
+ subprocess.stdout?.on("data", (chunk) => {
24722
+ const text11 = chunk.toString();
24723
+ stdoutBuffer += text11;
24724
+ process.stdout.write(text11);
24725
+ heartbeat.activity();
24726
+ });
24727
+ subprocess.stderr?.on("data", (chunk) => {
24728
+ const text11 = chunk.toString();
24729
+ stderrBuffer += text11;
24730
+ process.stderr.write(text11);
24731
+ heartbeat.activity();
24732
+ });
24733
+ const result = await subprocess;
24239
24734
  return {
24240
24735
  success: result.exitCode === 0,
24241
- stdout: truncateOutput2(String(result.stdout ?? "")),
24242
- stderr: truncateOutput2(String(result.stderr ?? "")),
24736
+ stdout: truncateOutput2(stdoutBuffer),
24737
+ stderr: truncateOutput2(stderrBuffer),
24243
24738
  exitCode: result.exitCode ?? 0,
24244
24739
  duration: performance.now() - startTime
24245
24740
  };
@@ -24255,6 +24750,9 @@ Examples:
24255
24750
  `Make failed: ${error instanceof Error ? error.message : String(error)}`,
24256
24751
  { tool: "make", cause: error instanceof Error ? error : void 0 }
24257
24752
  );
24753
+ } finally {
24754
+ heartbeat.stop();
24755
+ process.stderr.write("\r \r");
24258
24756
  }
24259
24757
  }
24260
24758
  });
@@ -24280,7 +24778,21 @@ Examples:
24280
24778
  const projectDir = cwd ?? process.cwd();
24281
24779
  const startTime = performance.now();
24282
24780
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS3;
24781
+ const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
24782
+ const heartbeat = new CommandHeartbeat2({
24783
+ onUpdate: (stats) => {
24784
+ if (stats.elapsedSeconds > 10) {
24785
+ process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
24786
+ }
24787
+ },
24788
+ onWarn: (message) => {
24789
+ process.stderr.write(`
24790
+ ${message}
24791
+ `);
24792
+ }
24793
+ });
24283
24794
  try {
24795
+ heartbeat.start();
24284
24796
  const cmdArgs = [];
24285
24797
  if (project) {
24286
24798
  cmdArgs.push("--project", project);
@@ -24298,13 +24810,30 @@ Examples:
24298
24810
  cwd: projectDir,
24299
24811
  timeout: timeoutMs,
24300
24812
  reject: false,
24813
+ buffer: false,
24814
+ // Enable streaming
24301
24815
  maxBuffer: MAX_OUTPUT_SIZE2
24302
24816
  };
24303
- const result = await execa("npx", ["tsc", ...cmdArgs], options);
24817
+ const subprocess = execa("npx", ["tsc", ...cmdArgs], options);
24818
+ let stdoutBuffer = "";
24819
+ let stderrBuffer = "";
24820
+ subprocess.stdout?.on("data", (chunk) => {
24821
+ const text11 = chunk.toString();
24822
+ stdoutBuffer += text11;
24823
+ process.stdout.write(text11);
24824
+ heartbeat.activity();
24825
+ });
24826
+ subprocess.stderr?.on("data", (chunk) => {
24827
+ const text11 = chunk.toString();
24828
+ stderrBuffer += text11;
24829
+ process.stderr.write(text11);
24830
+ heartbeat.activity();
24831
+ });
24832
+ const result = await subprocess;
24304
24833
  return {
24305
24834
  success: result.exitCode === 0,
24306
- stdout: truncateOutput2(String(result.stdout ?? "")),
24307
- stderr: truncateOutput2(String(result.stderr ?? "")),
24835
+ stdout: truncateOutput2(stdoutBuffer),
24836
+ stderr: truncateOutput2(stderrBuffer),
24308
24837
  exitCode: result.exitCode ?? 0,
24309
24838
  duration: performance.now() - startTime
24310
24839
  };
@@ -24319,6 +24848,9 @@ Examples:
24319
24848
  `TypeScript compile failed: ${error instanceof Error ? error.message : String(error)}`,
24320
24849
  { tool: "tsc", cause: error instanceof Error ? error : void 0 }
24321
24850
  );
24851
+ } finally {
24852
+ heartbeat.stop();
24853
+ process.stderr.write("\r \r");
24322
24854
  }
24323
24855
  }
24324
24856
  });
@@ -25202,20 +25734,10 @@ var diffTools = [showDiffTool];
25202
25734
  // src/tools/review.ts
25203
25735
  init_registry();
25204
25736
  init_errors();
25205
-
25206
- // src/utils/files.ts
25207
- init_errors();
25208
- async function fileExists(filePath) {
25209
- try {
25210
- await fs23__default.access(filePath);
25211
- return true;
25212
- } catch {
25213
- return false;
25214
- }
25215
- }
25216
- async function fileExists2(path43) {
25737
+ init_files();
25738
+ async function fileExists2(path44) {
25217
25739
  try {
25218
- await access(path43);
25740
+ await access(path44);
25219
25741
  return true;
25220
25742
  } catch {
25221
25743
  return false;
@@ -25305,7 +25827,7 @@ async function detectMaturity(cwd) {
25305
25827
  if (!hasLintConfig && hasPackageJson) {
25306
25828
  try {
25307
25829
  const pkgRaw = await import('fs/promises').then(
25308
- (fs43) => fs43.readFile(join(cwd, "package.json"), "utf-8")
25830
+ (fs44) => fs44.readFile(join(cwd, "package.json"), "utf-8")
25309
25831
  );
25310
25832
  const pkg = JSON.parse(pkgRaw);
25311
25833
  if (pkg.scripts?.lint || pkg.scripts?.["lint:fix"]) {
@@ -29964,6 +30486,13 @@ var terminalOptions = {
29964
30486
  var marked = new Marked();
29965
30487
  marked.use(markedTerminal(terminalOptions));
29966
30488
  init_bash();
30489
+ init_files();
30490
+
30491
+ // src/cli/repl/skills/builtin/ship/version-detector.ts
30492
+ init_files();
30493
+
30494
+ // src/cli/repl/skills/builtin/ship/changelog.ts
30495
+ init_files();
29967
30496
 
29968
30497
  // src/cli/repl/skills/builtin/ship/steps/test-coverage.ts
29969
30498
  init_bash();
@@ -30038,6 +30567,8 @@ async function startRepl(options = {}) {
30038
30567
  process.exit(1);
30039
30568
  }
30040
30569
  initializeContextManager(session, provider);
30570
+ const { detectProjectStack: detectProjectStack2 } = await Promise.resolve().then(() => (init_stack_detector(), stack_detector_exports));
30571
+ session.projectContext = await detectProjectStack2(projectPath);
30041
30572
  await loadAllowedPaths(projectPath);
30042
30573
  if (await shouldShowPermissionSuggestion()) {
30043
30574
  await showPermissionSuggestion();
@@ -30189,7 +30720,11 @@ async function startRepl(options = {}) {
30189
30720
  clearSpinner();
30190
30721
  renderToolStart(result2.name, result2.input);
30191
30722
  renderToolEnd(result2);
30192
- setSpinner("Processing...");
30723
+ if (isCocoMode()) {
30724
+ setSpinner("Processing results & checking quality...");
30725
+ } else {
30726
+ setSpinner("Processing...");
30727
+ }
30193
30728
  },
30194
30729
  onToolSkipped: (tc, reason) => {
30195
30730
  clearSpinner();
@@ -30202,10 +30737,18 @@ async function startRepl(options = {}) {
30202
30737
  if (!thinkingStartTime) return;
30203
30738
  const elapsed = Math.floor((Date.now() - thinkingStartTime) / 1e3);
30204
30739
  if (elapsed < 4) return;
30205
- if (elapsed < 8) setSpinner("Analyzing request...");
30206
- else if (elapsed < 12) setSpinner("Planning approach...");
30207
- else if (elapsed < 16) setSpinner("Preparing tools...");
30208
- else setSpinner(`Still working... (${elapsed}s)`);
30740
+ if (isCocoMode()) {
30741
+ if (elapsed < 8) setSpinner("Analyzing request...");
30742
+ else if (elapsed < 15) setSpinner("Running quality checks...");
30743
+ else if (elapsed < 25) setSpinner("Iterating for quality...");
30744
+ else if (elapsed < 40) setSpinner("Verifying implementation...");
30745
+ else setSpinner(`Quality iteration in progress... (${elapsed}s)`);
30746
+ } else {
30747
+ if (elapsed < 8) setSpinner("Analyzing request...");
30748
+ else if (elapsed < 12) setSpinner("Planning approach...");
30749
+ else if (elapsed < 16) setSpinner("Preparing tools...");
30750
+ else setSpinner(`Still working... (${elapsed}s)`);
30751
+ }
30209
30752
  }, 2e3);
30210
30753
  },
30211
30754
  onThinkingEnd: () => {