@corbat-tech/coco 2.28.3 → 2.28.5

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
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import * as fs5 from 'fs';
3
- import fs5__default, { accessSync, readFileSync, constants } from 'fs';
3
+ import fs5__default, { accessSync, readFileSync, constants as constants$1 } from 'fs';
4
4
  import * as path39 from 'path';
5
5
  import path39__default, { join, dirname, resolve, basename } from 'path';
6
6
  import { URL as URL$1, fileURLToPath } from 'url';
@@ -8,7 +8,7 @@ import { z } from 'zod';
8
8
  import * as os4 from 'os';
9
9
  import os4__default, { homedir } from 'os';
10
10
  import * as fs35 from 'fs/promises';
11
- import fs35__default, { mkdir, writeFile, readFile, access, readdir, rm } from 'fs/promises';
11
+ import fs35__default, { mkdir, writeFile, readFile, access, constants, readdir, rm } from 'fs/promises';
12
12
  import JSON5 from 'json5';
13
13
  import * as crypto from 'crypto';
14
14
  import { randomUUID, randomBytes, createHash } from 'crypto';
@@ -707,8 +707,8 @@ async function configExists(configPath, scope = "any") {
707
707
  }
708
708
  return false;
709
709
  }
710
- function getConfigValue(config, path60) {
711
- const keys = path60.split(".");
710
+ function getConfigValue(config, path62) {
711
+ const keys = path62.split(".");
712
712
  let current = config;
713
713
  for (const key of keys) {
714
714
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -6980,7 +6980,7 @@ var init_fallback = __esm({
6980
6980
  */
6981
6981
  async initialize(config) {
6982
6982
  const results = await Promise.allSettled(
6983
- this.providers.map((p46) => p46.provider.initialize(config))
6983
+ this.providers.map((p47) => p47.provider.initialize(config))
6984
6984
  );
6985
6985
  const anySuccess = results.some((r) => r.status === "fulfilled");
6986
6986
  if (!anySuccess) {
@@ -7102,11 +7102,11 @@ var init_fallback = __esm({
7102
7102
  */
7103
7103
  async isAvailable() {
7104
7104
  const results = await Promise.all(
7105
- this.providers.map(async (p46) => {
7106
- if (p46.breaker.isOpen()) {
7105
+ this.providers.map(async (p47) => {
7106
+ if (p47.breaker.isOpen()) {
7107
7107
  return false;
7108
7108
  }
7109
- return p46.provider.isAvailable();
7109
+ return p47.provider.isAvailable();
7110
7110
  })
7111
7111
  );
7112
7112
  return results.some((available) => available);
@@ -7137,10 +7137,10 @@ var init_fallback = __esm({
7137
7137
  * @returns Array of provider status objects
7138
7138
  */
7139
7139
  getCircuitStatus() {
7140
- return this.providers.map((p46) => ({
7141
- providerId: p46.provider.id,
7142
- state: p46.breaker.getState(),
7143
- failureCount: p46.breaker.getFailureCount()
7140
+ return this.providers.map((p47) => ({
7141
+ providerId: p47.provider.id,
7142
+ state: p47.breaker.getState(),
7143
+ failureCount: p47.breaker.getFailureCount()
7144
7144
  }));
7145
7145
  }
7146
7146
  /**
@@ -7150,8 +7150,8 @@ var init_fallback = __esm({
7150
7150
  * previously failing providers to be tried again.
7151
7151
  */
7152
7152
  resetCircuits() {
7153
- for (const p46 of this.providers) {
7154
- p46.breaker.reset();
7153
+ for (const p47 of this.providers) {
7154
+ p47.breaker.reset();
7155
7155
  }
7156
7156
  }
7157
7157
  /**
@@ -7726,9 +7726,9 @@ async function migrateGlobalConfig(oldDir, newConfigPath) {
7726
7726
  );
7727
7727
  }
7728
7728
  }
7729
- async function fileExists(path60) {
7729
+ async function fileExists(path62) {
7730
7730
  try {
7731
- await access(path60);
7731
+ await access(path62);
7732
7732
  return true;
7733
7733
  } catch {
7734
7734
  return false;
@@ -8111,8 +8111,8 @@ var init_registry = __esm({
8111
8111
  /**
8112
8112
  * Ensure directory exists
8113
8113
  */
8114
- async ensureDir(path60) {
8115
- await mkdir(dirname(path60), { recursive: true });
8114
+ async ensureDir(path62) {
8115
+ await mkdir(dirname(path62), { recursive: true });
8116
8116
  }
8117
8117
  };
8118
8118
  }
@@ -9749,9 +9749,9 @@ function createEmptyMemoryContext() {
9749
9749
  errors: []
9750
9750
  };
9751
9751
  }
9752
- function createMissingMemoryFile(path60, level) {
9752
+ function createMissingMemoryFile(path62, level) {
9753
9753
  return {
9754
- path: path60,
9754
+ path: path62,
9755
9755
  level,
9756
9756
  content: "",
9757
9757
  sections: [],
@@ -10217,6 +10217,9 @@ After outputting the plan, the user will decide to approve, edit, or reject it.
10217
10217
  if (subcommand === "status") {
10218
10218
  if (session.planMode) {
10219
10219
  p26.log.info(chalk.cyan("Plan mode is ACTIVE (read-only tools only)"));
10220
+ if (session.config.agent.planModeStrict) {
10221
+ p26.log.info("Strict plan mode is ON. Only the strict read-only allowlist is available.");
10222
+ }
10220
10223
  if (session.pendingPlan) {
10221
10224
  p26.log.info("A plan is pending approval. Use /plan approve or /plan reject.");
10222
10225
  }
@@ -10271,6 +10274,9 @@ ${plan}`
10271
10274
  const instruction = args.join(" ");
10272
10275
  p26.log.success(chalk.cyan("Plan mode ACTIVATED"));
10273
10276
  p26.log.info("Agent will explore the codebase and create a plan.");
10277
+ if (session.config.agent.planModeStrict) {
10278
+ p26.log.info("Strict plan mode is ON. Execution tools remain blocked until approval.");
10279
+ }
10274
10280
  p26.log.info(chalk.dim(`Task: ${instruction}`));
10275
10281
  p26.log.info("After the plan is generated, use /plan approve or /plan reject.");
10276
10282
  session.messages.push({
@@ -10357,10 +10363,27 @@ async function createDefaultReplConfig() {
10357
10363
  systemPrompt: COCO_SYSTEM_PROMPT,
10358
10364
  maxToolIterations: 25,
10359
10365
  confirmDestructive: true,
10360
- enableAutoSwitchProvider: false
10366
+ enableAutoSwitchProvider: false,
10367
+ recoveryV2: readBooleanFlag("COCO_AGENT_RECOVERY_V2", true),
10368
+ planModeStrict: readBooleanFlag("COCO_AGENT_PLAN_MODE_STRICT", true),
10369
+ doctorV2: readBooleanFlag("COCO_AGENT_DOCTOR_V2", true),
10370
+ outputOffload: readBooleanFlag("COCO_AGENT_OUTPUT_OFFLOAD", false)
10361
10371
  }
10362
10372
  };
10363
10373
  }
10374
+ function readBooleanFlag(envName, defaultValue) {
10375
+ const value = process.env[envName]?.trim().toLowerCase();
10376
+ if (value === void 0) {
10377
+ return defaultValue;
10378
+ }
10379
+ if (value === "1" || value === "true" || value === "yes" || value === "on") {
10380
+ return true;
10381
+ }
10382
+ if (value === "0" || value === "false" || value === "no" || value === "off") {
10383
+ return false;
10384
+ }
10385
+ return defaultValue;
10386
+ }
10364
10387
  async function createSession(projectPath, config) {
10365
10388
  const defaultConfig = await createDefaultReplConfig();
10366
10389
  return {
@@ -11234,8 +11257,8 @@ __export(trust_store_exports, {
11234
11257
  saveTrustStore: () => saveTrustStore,
11235
11258
  updateLastAccessed: () => updateLastAccessed
11236
11259
  });
11237
- async function ensureDir(path60) {
11238
- await mkdir(dirname(path60), { recursive: true });
11260
+ async function ensureDir(path62) {
11261
+ await mkdir(dirname(path62), { recursive: true });
11239
11262
  }
11240
11263
  async function loadTrustStore(storePath = TRUST_STORE_PATH) {
11241
11264
  try {
@@ -11320,8 +11343,8 @@ function canPerformOperation(store, projectPath, operation) {
11320
11343
  };
11321
11344
  return permissions[level]?.includes(operation) ?? false;
11322
11345
  }
11323
- function normalizePath(path60) {
11324
- return join(path60);
11346
+ function normalizePath(path62) {
11347
+ return join(path62);
11325
11348
  }
11326
11349
  function createTrustStore(storePath = TRUST_STORE_PATH) {
11327
11350
  let store = null;
@@ -11677,12 +11700,12 @@ function humanizeError(message, toolName) {
11677
11700
  return msg;
11678
11701
  }
11679
11702
  if (/ENOENT/i.test(msg)) {
11680
- const path60 = extractQuotedPath(msg);
11681
- return path60 ? `File or directory not found: ${path60}` : "File or directory not found";
11703
+ const path62 = extractQuotedPath(msg);
11704
+ return path62 ? `File or directory not found: ${path62}` : "File or directory not found";
11682
11705
  }
11683
11706
  if (/EACCES/i.test(msg)) {
11684
- const path60 = extractQuotedPath(msg);
11685
- return path60 ? `Permission denied: ${path60}` : "Permission denied \u2014 check file permissions";
11707
+ const path62 = extractQuotedPath(msg);
11708
+ return path62 ? `Permission denied: ${path62}` : "Permission denied \u2014 check file permissions";
11686
11709
  }
11687
11710
  if (/EISDIR/i.test(msg)) {
11688
11711
  return "Expected a file but found a directory at the specified path";
@@ -11774,7 +11797,7 @@ function humanizeError(message, toolName) {
11774
11797
  return msg;
11775
11798
  }
11776
11799
  function looksLikeTechnicalJargon(message) {
11777
- return JARGON_PATTERNS.some((p46) => p46.test(message));
11800
+ return JARGON_PATTERNS.some((p47) => p47.test(message));
11778
11801
  }
11779
11802
  async function humanizeWithLLM(errorMessage, toolName, provider) {
11780
11803
  const prompt = [
@@ -12535,9 +12558,9 @@ function renderFileBlock(file, opts) {
12535
12558
  );
12536
12559
  }
12537
12560
  const pairs = pairAdjacentLines(hunk.lines);
12538
- const pairedDeleteIndices = new Set(pairs.map((p46) => p46.deleteIdx));
12539
- const pairedAddIndices = new Set(pairs.map((p46) => p46.addIdx));
12540
- const pairByAdd = new Map(pairs.map((p46) => [p46.addIdx, p46.deleteIdx]));
12561
+ const pairedDeleteIndices = new Set(pairs.map((p47) => p47.deleteIdx));
12562
+ const pairedAddIndices = new Set(pairs.map((p47) => p47.addIdx));
12563
+ const pairByAdd = new Map(pairs.map((p47) => [p47.addIdx, p47.deleteIdx]));
12541
12564
  const wordHighlights = /* @__PURE__ */ new Map();
12542
12565
  for (const pair of pairs) {
12543
12566
  const delLine = hunk.lines[pair.deleteIdx];
@@ -12616,9 +12639,9 @@ var init_diff_renderer = __esm({
12616
12639
  getTerminalWidth = () => process.stdout.columns || 80;
12617
12640
  }
12618
12641
  });
12619
- async function fileExists4(path60) {
12642
+ async function fileExists4(path62) {
12620
12643
  try {
12621
- await access(path60);
12644
+ await access(path62);
12622
12645
  return true;
12623
12646
  } catch {
12624
12647
  return false;
@@ -12816,13 +12839,13 @@ var init_subprocess_registry = __esm({
12816
12839
  });
12817
12840
  async function detectTestFramework(projectPath) {
12818
12841
  try {
12819
- await access(join(projectPath, "pom.xml"), constants.R_OK);
12842
+ await access(join(projectPath, "pom.xml"), constants$1.R_OK);
12820
12843
  return "maven";
12821
12844
  } catch {
12822
12845
  }
12823
12846
  for (const f of ["build.gradle", "build.gradle.kts"]) {
12824
12847
  try {
12825
- await access(join(projectPath, f), constants.R_OK);
12848
+ await access(join(projectPath, f), constants$1.R_OK);
12826
12849
  return "gradle";
12827
12850
  } catch {
12828
12851
  }
@@ -12980,7 +13003,7 @@ var init_coverage = __esm({
12980
13003
  ] : [join(this.projectPath, "build", "reports", "jacoco", "test", "jacocoTestReport.csv")];
12981
13004
  for (const csvPath of jacocoPaths) {
12982
13005
  try {
12983
- await access(csvPath, constants.R_OK);
13006
+ await access(csvPath, constants$1.R_OK);
12984
13007
  const csv = await readFile(csvPath, "utf-8");
12985
13008
  return parseJacocoCsv(csv);
12986
13009
  } catch {
@@ -12993,10 +13016,10 @@ var init_coverage = __esm({
12993
13016
  join(this.projectPath, ".coverage", "coverage-summary.json"),
12994
13017
  join(this.projectPath, "coverage", "lcov-report", "coverage-summary.json")
12995
13018
  ];
12996
- for (const p46 of possiblePaths) {
13019
+ for (const p47 of possiblePaths) {
12997
13020
  try {
12998
- await access(p46, constants.R_OK);
12999
- const content = await readFile(p46, "utf-8");
13021
+ await access(p47, constants$1.R_OK);
13022
+ const content = await readFile(p47, "utf-8");
13000
13023
  const report = JSON.parse(content);
13001
13024
  return parseCoverageSummary(report);
13002
13025
  } catch {
@@ -14198,7 +14221,7 @@ var init_completeness = __esm({
14198
14221
  ];
14199
14222
  for (const entry of entryPoints) {
14200
14223
  try {
14201
- await access(join(this.projectPath, entry), constants.R_OK);
14224
+ await access(join(this.projectPath, entry), constants$1.R_OK);
14202
14225
  return true;
14203
14226
  } catch {
14204
14227
  }
@@ -14691,7 +14714,7 @@ var init_documentation = __esm({
14691
14714
  }
14692
14715
  async fileExists(relativePath) {
14693
14716
  try {
14694
- await access(join(this.projectPath, relativePath), constants.R_OK);
14717
+ await access(join(this.projectPath, relativePath), constants$1.R_OK);
14695
14718
  return true;
14696
14719
  } catch {
14697
14720
  return false;
@@ -16998,16 +17021,16 @@ var init_evaluator = __esm({
16998
17021
  * Find source files in project, adapting to the detected language stack.
16999
17022
  */
17000
17023
  async findSourceFiles() {
17001
- const { access: access17 } = await import('fs/promises');
17024
+ const { access: access18 } = await import('fs/promises');
17002
17025
  const { join: join26 } = await import('path');
17003
17026
  let isJava = false;
17004
17027
  try {
17005
- await access17(join26(this.projectPath, "pom.xml"));
17028
+ await access18(join26(this.projectPath, "pom.xml"));
17006
17029
  isJava = true;
17007
17030
  } catch {
17008
17031
  for (const f of ["build.gradle", "build.gradle.kts"]) {
17009
17032
  try {
17010
- await access17(join26(this.projectPath, f));
17033
+ await access18(join26(this.projectPath, f));
17011
17034
  isJava = true;
17012
17035
  break;
17013
17036
  } catch {
@@ -17934,13 +17957,13 @@ function buildReviewMarkdown(result) {
17934
17957
  const { summary, required, suggestions, maturity } = result;
17935
17958
  const lines = [];
17936
17959
  lines.push("## Code Review\n");
17937
- const statusIcon = summary.status === "approved" ? "Approved" : "Needs Work";
17960
+ const statusIcon2 = summary.status === "approved" ? "Approved" : "Needs Work";
17938
17961
  lines.push(`**Branch:** \`${summary.branch}\` \u2192 \`${summary.baseBranch}\``);
17939
17962
  lines.push(
17940
17963
  `**Files:** ${summary.filesChanged} changed | +${summary.additions} -${summary.deletions}`
17941
17964
  );
17942
17965
  lines.push(`**Maturity:** ${maturity}`);
17943
- lines.push(`**Status:** ${statusIcon}`);
17966
+ lines.push(`**Status:** ${statusIcon2}`);
17944
17967
  lines.push("");
17945
17968
  if (required.length > 0) {
17946
17969
  lines.push(`### Required (${required.length})
@@ -18366,9 +18389,9 @@ Examples:
18366
18389
  if (file) {
18367
18390
  options.file = file;
18368
18391
  }
18369
- const log38 = await git.log(options);
18392
+ const log39 = await git.log(options);
18370
18393
  return {
18371
- commits: log38.all.map((commit) => ({
18394
+ commits: log39.all.map((commit) => ({
18372
18395
  hash: commit.hash,
18373
18396
  message: commit.message,
18374
18397
  author: commit.author_name,
@@ -19257,6 +19280,24 @@ async function writeVersion(cwd, versionFile, newVersion) {
19257
19280
  return;
19258
19281
  }
19259
19282
  await writeFile(fullPath, updated, "utf-8");
19283
+ if (versionFile.stack === "node" && versionFile.path === "package.json") {
19284
+ await syncCompanionNodeVersions(cwd, newVersion);
19285
+ }
19286
+ }
19287
+ async function syncCompanionNodeVersions(cwd, newVersion) {
19288
+ for (const relativePath of NODE_VERSION_SYNC_TARGETS) {
19289
+ const fullPath = path39__default.join(cwd, relativePath);
19290
+ if (!await fileExists3(fullPath)) {
19291
+ continue;
19292
+ }
19293
+ const content = await readFile(fullPath, "utf-8");
19294
+ const pkg = JSON.parse(content);
19295
+ if (typeof pkg["version"] !== "string") {
19296
+ continue;
19297
+ }
19298
+ pkg["version"] = newVersion;
19299
+ await writeFile(fullPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
19300
+ }
19260
19301
  }
19261
19302
  function detectBumpFromCommits(commitMessages) {
19262
19303
  let bump = "patch";
@@ -19271,7 +19312,7 @@ function detectBumpFromCommits(commitMessages) {
19271
19312
  }
19272
19313
  return bump;
19273
19314
  }
19274
- var VERSION_FILES;
19315
+ var VERSION_FILES, NODE_VERSION_SYNC_TARGETS;
19275
19316
  var init_version_detector = __esm({
19276
19317
  "src/cli/repl/skills/builtin/ship/version-detector.ts"() {
19277
19318
  init_files();
@@ -19281,6 +19322,7 @@ var init_version_detector = __esm({
19281
19322
  { file: "pyproject.toml", stack: "python", field: "version" },
19282
19323
  { file: "pom.xml", stack: "java", field: "version" }
19283
19324
  ];
19325
+ NODE_VERSION_SYNC_TARGETS = ["vscode-extension/package.json"];
19284
19326
  }
19285
19327
  });
19286
19328
  async function detectChangelog(cwd) {
@@ -20031,18 +20073,18 @@ async function runVersion(ctx) {
20031
20073
  });
20032
20074
  const tag = lastTag.stdout.trim();
20033
20075
  if (tag) {
20034
- const log38 = await bashExecTool.execute({
20076
+ const log39 = await bashExecTool.execute({
20035
20077
  command: `git log ${tag}..HEAD --oneline`,
20036
20078
  cwd: ctx.cwd
20037
20079
  });
20038
- commitMessages = log38.stdout.trim().split("\n").filter(Boolean);
20080
+ commitMessages = log39.stdout.trim().split("\n").filter(Boolean);
20039
20081
  } else {
20040
- const log38 = await gitLogTool.execute({ cwd: ctx.cwd, maxCount: 20 });
20041
- commitMessages = log38.commits.map((c) => c.message);
20082
+ const log39 = await gitLogTool.execute({ cwd: ctx.cwd, maxCount: 20 });
20083
+ commitMessages = log39.commits.map((c) => c.message);
20042
20084
  }
20043
20085
  } catch {
20044
- const log38 = await gitLogTool.execute({ cwd: ctx.cwd, maxCount: 10 });
20045
- commitMessages = log38.commits.map((c) => c.message);
20086
+ const log39 = await gitLogTool.execute({ cwd: ctx.cwd, maxCount: 10 });
20087
+ commitMessages = log39.commits.map((c) => c.message);
20046
20088
  }
20047
20089
  detectedBump = detectBumpFromCommits(commitMessages);
20048
20090
  }
@@ -20080,8 +20122,8 @@ async function runVersion(ctx) {
20080
20122
  p26.log.success(`Updated ${versionFile.path}: ${currentVersion} \u2192 ${finalVersion}`);
20081
20123
  if (ctx.profile.changelog && !ctx.options.noChangelog) {
20082
20124
  try {
20083
- const log38 = await gitLogTool.execute({ cwd: ctx.cwd, maxCount: 30 });
20084
- const entries = generateChangelogEntries(log38.commits.map((c) => c.message));
20125
+ const log39 = await gitLogTool.execute({ cwd: ctx.cwd, maxCount: 30 });
20126
+ const entries = generateChangelogEntries(log39.commits.map((c) => c.message));
20085
20127
  if (entries.length > 0) {
20086
20128
  await insertChangelogEntry(ctx.cwd, ctx.profile.changelog, finalVersion, entries);
20087
20129
  p26.log.success(`Updated ${ctx.profile.changelog.path} with ${entries.length} entries`);
@@ -20799,11 +20841,11 @@ function isBlockedPath(absolute) {
20799
20841
  return void 0;
20800
20842
  }
20801
20843
  function isBlockedExecFile(filePath) {
20802
- return BLOCKED_EXEC_PATTERNS.some((p46) => p46.test(filePath));
20844
+ return BLOCKED_EXEC_PATTERNS.some((p47) => p47.test(filePath));
20803
20845
  }
20804
20846
  function hasDangerousArgs(args) {
20805
20847
  const joined = args.join(" ");
20806
- return DANGEROUS_ARG_PATTERNS.some((p46) => p46.test(joined));
20848
+ return DANGEROUS_ARG_PATTERNS.some((p47) => p47.test(joined));
20807
20849
  }
20808
20850
  function getInterpreter(ext) {
20809
20851
  return INTERPRETER_MAP[ext.toLowerCase()];
@@ -23311,413 +23353,6 @@ var init_allow_path_prompt = __esm({
23311
23353
  }
23312
23354
  });
23313
23355
 
23314
- // src/cli/repl/interruptions/types.ts
23315
- var init_types7 = __esm({
23316
- "src/cli/repl/interruptions/types.ts"() {
23317
- }
23318
- });
23319
-
23320
- // src/cli/repl/interruptions/classifier.ts
23321
- function matchPatterns(text15, patterns) {
23322
- for (let i = 0; i < patterns.length; i++) {
23323
- if (patterns[i].test(text15)) {
23324
- return 1 - i * 0.1;
23325
- }
23326
- }
23327
- return 0;
23328
- }
23329
- function classifyInterruption(message) {
23330
- const text15 = message.text;
23331
- const abortConf = matchPatterns(text15, ABORT_PATTERNS);
23332
- const modifyConf = matchPatterns(text15, MODIFY_PATTERNS);
23333
- const correctConf = matchPatterns(text15, CORRECT_PATTERNS);
23334
- if (abortConf > 0 && abortConf >= modifyConf && abortConf >= correctConf) {
23335
- return {
23336
- text: text15,
23337
- type: "abort" /* Abort */,
23338
- confidence: Math.min(1, abortConf),
23339
- timestamp: message.timestamp
23340
- };
23341
- }
23342
- if (modifyConf > 0 && modifyConf >= correctConf) {
23343
- return {
23344
- text: text15,
23345
- type: "modify" /* Modify */,
23346
- confidence: Math.min(1, modifyConf),
23347
- timestamp: message.timestamp
23348
- };
23349
- }
23350
- if (correctConf > 0) {
23351
- return {
23352
- text: text15,
23353
- type: "correct" /* Correct */,
23354
- confidence: Math.min(1, correctConf),
23355
- timestamp: message.timestamp
23356
- };
23357
- }
23358
- return {
23359
- text: text15,
23360
- type: "info" /* Info */,
23361
- confidence: 0.5,
23362
- timestamp: message.timestamp
23363
- };
23364
- }
23365
- var ABORT_PATTERNS, MODIFY_PATTERNS, CORRECT_PATTERNS;
23366
- var init_classifier = __esm({
23367
- "src/cli/repl/interruptions/classifier.ts"() {
23368
- init_types7();
23369
- ABORT_PATTERNS = [
23370
- /^(para|stop|cancel|abort|quit|exit|detente|basta)$/i,
23371
- /^(para\s+ya|stop\s+it|cancel\s+that)$/i,
23372
- /\b(para|stop|cancel|abort)\b/i
23373
- ];
23374
- MODIFY_PATTERNS = [
23375
- /^(a[ñn]ade|add|incluye|include|pon|put|agrega)\b/i,
23376
- /^(cambia|change|modifica|modify|usa|use|haz|make)\b/i,
23377
- /^no[,.]?\s+/i,
23378
- // "no, mejor de la griega" — negation signals redirection
23379
- /^(prefiero|prefer|quiero|i\s+want)\b/i,
23380
- // "prefiero en gijon" — preference signals redirection
23381
- /\b(a[ñn]ade|add|incluye|include)\s+/i,
23382
- /\b(cambia|change|modifica|modify)\s+/i,
23383
- /\b(en\s+vez\s+de|instead\s+of|rather\s+than)\b/i,
23384
- /\b(prefiero|prefer|mejor|better|más|more|less|menos|bigger|smaller|larger)\b/i,
23385
- /\b(también|also|además|additionally)\b/i
23386
- ];
23387
- CORRECT_PATTERNS = [
23388
- /^(error|bug|fallo|wrong|mal|incorrect)\b/i,
23389
- /^(arregla|fix|corrige|correct|repara|repair)\b/i,
23390
- /\b(error\s+en|bug\s+in|fallo\s+en)\b/i,
23391
- /\b(está\s+mal|is\s+wrong|no\s+funciona|doesn'?t\s+work)\b/i,
23392
- /\b(arregla|fix|corrige|correct)\s+/i
23393
- ];
23394
- }
23395
- });
23396
- function mapClassificationToAction(type) {
23397
- switch (type) {
23398
- case "abort" /* Abort */:
23399
- return "abort" /* Abort */;
23400
- // Explicit modification/correction keywords → pre-select Modify
23401
- case "modify" /* Modify */:
23402
- case "correct" /* Correct */:
23403
- return "modify" /* Modify */;
23404
- // Info or unclassified → pre-select Queue (likely a new topic/task)
23405
- // The user can always switch to Modify via the menu if needed
23406
- case "info" /* Info */:
23407
- default:
23408
- return "queue" /* Queue */;
23409
- }
23410
- }
23411
- var init_action_selector = __esm({
23412
- "src/cli/repl/input/action-selector.ts"() {
23413
- init_types7();
23414
- }
23415
- });
23416
-
23417
- // src/cli/repl/interruptions/llm-classifier.ts
23418
- var llm_classifier_exports = {};
23419
- __export(llm_classifier_exports, {
23420
- createLLMClassifier: () => createLLMClassifier
23421
- });
23422
- function buildClassificationPrompt(userMessage, currentTask) {
23423
- if (currentTask) {
23424
- return `Current task: "${currentTask}"
23425
- User's new message: "${userMessage}"`;
23426
- }
23427
- return `User's new message: "${userMessage}"`;
23428
- }
23429
- function parseResponse(response) {
23430
- const normalized = response.trim().toUpperCase();
23431
- if (normalized.includes("STEER")) return "steer" /* Steer */;
23432
- if (normalized.includes("MODIFY")) return "modify" /* Modify */;
23433
- if (normalized.includes("QUEUE")) return "queue" /* Queue */;
23434
- if (normalized.includes("ABORT")) return "abort" /* Abort */;
23435
- return null;
23436
- }
23437
- function classifyWithKeywords(message) {
23438
- const classified = classifyInterruption(message);
23439
- return mapClassificationToAction(classified.type);
23440
- }
23441
- function createLLMClassifier(provider, config) {
23442
- const cfg = { ...DEFAULT_CONFIG6, ...config };
23443
- return {
23444
- /**
23445
- * Classify a user message captured during agent execution.
23446
- *
23447
- * Makes a fast parallel LLM call. If the LLM doesn't respond within
23448
- * the timeout, falls back to keyword-based classification.
23449
- *
23450
- * @param message - The captured message
23451
- * @param currentTask - The original task the agent is working on (for context)
23452
- * @returns Classification result with action and source
23453
- */
23454
- async classify(message, currentTask) {
23455
- const llmPromise = classifyWithLLM(provider, message, currentTask, cfg);
23456
- let timeoutId;
23457
- const timeoutPromise = new Promise((resolve4) => {
23458
- timeoutId = setTimeout(() => resolve4(null), cfg.timeoutMs);
23459
- });
23460
- try {
23461
- const llmResult = await Promise.race([llmPromise, timeoutPromise]);
23462
- if (llmResult !== null) {
23463
- return { action: llmResult, source: "llm" };
23464
- }
23465
- return {
23466
- action: classifyWithKeywords(message),
23467
- source: "keywords"
23468
- };
23469
- } finally {
23470
- clearTimeout(timeoutId);
23471
- }
23472
- }
23473
- };
23474
- }
23475
- async function classifyWithLLM(provider, message, currentTask, cfg) {
23476
- try {
23477
- const userPrompt = buildClassificationPrompt(message.text, currentTask);
23478
- const options = {
23479
- maxTokens: cfg.maxTokens,
23480
- temperature: cfg.temperature,
23481
- system: CLASSIFICATION_SYSTEM_PROMPT2,
23482
- timeout: cfg.timeoutMs
23483
- };
23484
- const response = await provider.chat([{ role: "user", content: userPrompt }], options);
23485
- return parseResponse(response.content);
23486
- } catch {
23487
- return null;
23488
- }
23489
- }
23490
- var DEFAULT_CONFIG6, CLASSIFICATION_SYSTEM_PROMPT2;
23491
- var init_llm_classifier = __esm({
23492
- "src/cli/repl/interruptions/llm-classifier.ts"() {
23493
- init_types7();
23494
- init_classifier();
23495
- init_action_selector();
23496
- DEFAULT_CONFIG6 = {
23497
- timeoutMs: 8e3,
23498
- maxTokens: 10,
23499
- temperature: 0
23500
- };
23501
- CLASSIFICATION_SYSTEM_PROMPT2 = `You are a message classifier for a coding assistant. The user sent a message while the assistant was working on a task.
23502
-
23503
- Classify the message into exactly ONE category. Reply with ONLY the category word, nothing else.
23504
-
23505
- Categories:
23506
- - STEER: The message provides a hint, preference, or minor adjustment to the CURRENT task without requiring a restart (e.g. "also add a test", "use camelCase", "make sure to handle errors", "btw the file is in src/", "prefer async/await")
23507
- - MODIFY: The message fundamentally changes the CURRENT task requiring a fresh start (e.g. "actually use Python instead of JS", "no, do it completely differently", "start over with a new approach")
23508
- - QUEUE: The message is a NEW, DIFFERENT task unrelated to what's being done (e.g. "what's 2+2", "tell me the weather", "create another file for X")
23509
- - ABORT: The message asks to stop/cancel the current work (e.g. "stop", "cancel", "para", "never mind")
23510
-
23511
- Reply with exactly one word: STEER, MODIFY, QUEUE, or ABORT`;
23512
- }
23513
- });
23514
-
23515
- // src/cli/repl/context/stack-detector.ts
23516
- var stack_detector_exports = {};
23517
- __export(stack_detector_exports, {
23518
- detectProjectStack: () => detectProjectStack
23519
- });
23520
- async function detectStack2(cwd) {
23521
- if (await fileExists3(path39__default.join(cwd, "package.json"))) return "node";
23522
- if (await fileExists3(path39__default.join(cwd, "Cargo.toml"))) return "rust";
23523
- if (await fileExists3(path39__default.join(cwd, "pyproject.toml"))) return "python";
23524
- if (await fileExists3(path39__default.join(cwd, "go.mod"))) return "go";
23525
- if (await fileExists3(path39__default.join(cwd, "pom.xml"))) return "java";
23526
- if (await fileExists3(path39__default.join(cwd, "build.gradle"))) return "java";
23527
- if (await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) return "java";
23528
- return "unknown";
23529
- }
23530
- async function detectPackageManager3(cwd, stack) {
23531
- if (stack === "rust") return "cargo";
23532
- if (stack === "python") return "pip";
23533
- if (stack === "go") return "go";
23534
- if (stack === "java") {
23535
- if (await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) {
23536
- return "gradle";
23537
- }
23538
- if (await fileExists3(path39__default.join(cwd, "pom.xml"))) {
23539
- return "maven";
23540
- }
23541
- }
23542
- if (stack === "node") {
23543
- if (await fileExists3(path39__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
23544
- if (await fileExists3(path39__default.join(cwd, "yarn.lock"))) return "yarn";
23545
- if (await fileExists3(path39__default.join(cwd, "bun.lockb"))) return "bun";
23546
- return "npm";
23547
- }
23548
- return null;
23549
- }
23550
- async function parsePackageJson(cwd) {
23551
- const packageJsonPath = path39__default.join(cwd, "package.json");
23552
- try {
23553
- const content = await fs35__default.readFile(packageJsonPath, "utf-8");
23554
- const pkg = JSON.parse(content);
23555
- const allDeps = {
23556
- ...pkg.dependencies,
23557
- ...pkg.devDependencies
23558
- };
23559
- const frameworks = [];
23560
- if (allDeps.react) frameworks.push("React");
23561
- if (allDeps.vue) frameworks.push("Vue");
23562
- if (allDeps["@angular/core"]) frameworks.push("Angular");
23563
- if (allDeps.next) frameworks.push("Next.js");
23564
- if (allDeps.nuxt) frameworks.push("Nuxt");
23565
- if (allDeps.express) frameworks.push("Express");
23566
- if (allDeps.fastify) frameworks.push("Fastify");
23567
- if (allDeps.nestjs || allDeps["@nestjs/core"]) frameworks.push("NestJS");
23568
- const buildTools2 = [];
23569
- if (allDeps.webpack) buildTools2.push("webpack");
23570
- if (allDeps.vite) buildTools2.push("vite");
23571
- if (allDeps.rollup) buildTools2.push("rollup");
23572
- if (allDeps.tsup) buildTools2.push("tsup");
23573
- if (allDeps.esbuild) buildTools2.push("esbuild");
23574
- if (pkg.scripts?.build) buildTools2.push("build");
23575
- const testingFrameworks = [];
23576
- if (allDeps.vitest) testingFrameworks.push("vitest");
23577
- if (allDeps.jest) testingFrameworks.push("jest");
23578
- if (allDeps.mocha) testingFrameworks.push("mocha");
23579
- if (allDeps.chai) testingFrameworks.push("chai");
23580
- if (allDeps["@playwright/test"]) testingFrameworks.push("playwright");
23581
- if (allDeps.cypress) testingFrameworks.push("cypress");
23582
- const languages = ["JavaScript"];
23583
- if (allDeps.typescript || await fileExists3(path39__default.join(cwd, "tsconfig.json"))) {
23584
- languages.push("TypeScript");
23585
- }
23586
- return {
23587
- dependencies: allDeps,
23588
- frameworks,
23589
- buildTools: buildTools2,
23590
- testingFrameworks,
23591
- languages
23592
- };
23593
- } catch {
23594
- return {
23595
- dependencies: {},
23596
- frameworks: [],
23597
- buildTools: [],
23598
- testingFrameworks: [],
23599
- languages: []
23600
- };
23601
- }
23602
- }
23603
- async function parsePomXml(cwd) {
23604
- const pomPath = path39__default.join(cwd, "pom.xml");
23605
- try {
23606
- const content = await fs35__default.readFile(pomPath, "utf-8");
23607
- const dependencies = {};
23608
- const frameworks = [];
23609
- const buildTools2 = ["maven"];
23610
- const testingFrameworks = [];
23611
- const depRegex = /<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>/g;
23612
- let match;
23613
- while ((match = depRegex.exec(content)) !== null) {
23614
- const groupId = match[1];
23615
- const artifactId = match[2];
23616
- if (!groupId || !artifactId) continue;
23617
- const fullName = `${groupId}:${artifactId}`;
23618
- dependencies[fullName] = "unknown";
23619
- if (artifactId.includes("spring-boot")) {
23620
- if (!frameworks.includes("Spring Boot")) frameworks.push("Spring Boot");
23621
- }
23622
- if (artifactId.includes("spring-webmvc") || artifactId.includes("spring-web")) {
23623
- if (!frameworks.includes("Spring MVC")) frameworks.push("Spring MVC");
23624
- }
23625
- if (artifactId.includes("hibernate")) {
23626
- if (!frameworks.includes("Hibernate")) frameworks.push("Hibernate");
23627
- }
23628
- if (artifactId === "junit-jupiter" || artifactId === "junit") {
23629
- if (!testingFrameworks.includes("JUnit")) testingFrameworks.push("JUnit");
23630
- }
23631
- if (artifactId === "mockito-core") {
23632
- if (!testingFrameworks.includes("Mockito")) testingFrameworks.push("Mockito");
23633
- }
23634
- }
23635
- return { dependencies, frameworks, buildTools: buildTools2, testingFrameworks };
23636
- } catch {
23637
- return { dependencies: {}, frameworks: [], buildTools: ["maven"], testingFrameworks: [] };
23638
- }
23639
- }
23640
- async function parsePyprojectToml(cwd) {
23641
- const pyprojectPath = path39__default.join(cwd, "pyproject.toml");
23642
- try {
23643
- const content = await fs35__default.readFile(pyprojectPath, "utf-8");
23644
- const dependencies = {};
23645
- const frameworks = [];
23646
- const buildTools2 = ["pip"];
23647
- const testingFrameworks = [];
23648
- const lines = content.split("\n");
23649
- for (const line of lines) {
23650
- const trimmed = line.trim();
23651
- if (trimmed.match(/^["']?[\w-]+["']?\s*=\s*["'][\^~>=<]+[\d.]+["']/)) {
23652
- const depMatch = trimmed.match(/^["']?([\w-]+)["']?\s*=\s*["']([\^~>=<]+[\d.]+)["']/);
23653
- if (depMatch && depMatch[1] && depMatch[2]) {
23654
- dependencies[depMatch[1]] = depMatch[2];
23655
- }
23656
- }
23657
- if (trimmed.includes("fastapi")) frameworks.push("FastAPI");
23658
- if (trimmed.includes("django")) frameworks.push("Django");
23659
- if (trimmed.includes("flask")) frameworks.push("Flask");
23660
- if (trimmed.includes("pytest")) testingFrameworks.push("pytest");
23661
- if (trimmed.includes("unittest")) testingFrameworks.push("unittest");
23662
- }
23663
- return { dependencies, frameworks, buildTools: buildTools2, testingFrameworks };
23664
- } catch {
23665
- return { dependencies: {}, frameworks: [], buildTools: ["pip"], testingFrameworks: [] };
23666
- }
23667
- }
23668
- async function detectProjectStack(cwd) {
23669
- const stack = await detectStack2(cwd);
23670
- const packageManager = await detectPackageManager3(cwd, stack);
23671
- let dependencies = {};
23672
- let frameworks = [];
23673
- let buildTools2 = [];
23674
- let testingFrameworks = [];
23675
- let languages = [];
23676
- if (stack === "node") {
23677
- const parsed = await parsePackageJson(cwd);
23678
- dependencies = parsed.dependencies;
23679
- frameworks = parsed.frameworks;
23680
- buildTools2 = parsed.buildTools;
23681
- testingFrameworks = parsed.testingFrameworks;
23682
- languages = parsed.languages;
23683
- } else if (stack === "java") {
23684
- const isGradle = await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"));
23685
- const parsed = isGradle ? { dependencies: {}, frameworks: [], buildTools: ["gradle"], testingFrameworks: ["JUnit"] } : await parsePomXml(cwd);
23686
- dependencies = parsed.dependencies;
23687
- frameworks = parsed.frameworks;
23688
- buildTools2 = parsed.buildTools;
23689
- testingFrameworks = parsed.testingFrameworks;
23690
- languages = ["Java"];
23691
- } else if (stack === "python") {
23692
- const parsed = await parsePyprojectToml(cwd);
23693
- dependencies = parsed.dependencies;
23694
- frameworks = parsed.frameworks;
23695
- buildTools2 = parsed.buildTools;
23696
- testingFrameworks = parsed.testingFrameworks;
23697
- languages = ["Python"];
23698
- } else if (stack === "go") {
23699
- languages = ["Go"];
23700
- buildTools2 = ["go"];
23701
- } else if (stack === "rust") {
23702
- languages = ["Rust"];
23703
- buildTools2 = ["cargo"];
23704
- }
23705
- return {
23706
- stack,
23707
- packageManager,
23708
- dependencies,
23709
- frameworks,
23710
- buildTools: buildTools2,
23711
- testingFrameworks,
23712
- languages
23713
- };
23714
- }
23715
- var init_stack_detector = __esm({
23716
- "src/cli/repl/context/stack-detector.ts"() {
23717
- init_files();
23718
- }
23719
- });
23720
-
23721
23356
  // src/cli/repl/hooks/types.ts
23722
23357
  function isHookEvent(value) {
23723
23358
  return typeof value === "string" && HOOK_EVENTS.includes(value);
@@ -23729,7 +23364,7 @@ function isHookAction(value) {
23729
23364
  return value === "allow" || value === "deny" || value === "modify";
23730
23365
  }
23731
23366
  var HOOK_EVENTS;
23732
- var init_types8 = __esm({
23367
+ var init_types7 = __esm({
23733
23368
  "src/cli/repl/hooks/types.ts"() {
23734
23369
  HOOK_EVENTS = [
23735
23370
  "PreToolUse",
@@ -23748,7 +23383,7 @@ function createHookRegistry() {
23748
23383
  var HookRegistry;
23749
23384
  var init_registry5 = __esm({
23750
23385
  "src/cli/repl/hooks/registry.ts"() {
23751
- init_types8();
23386
+ init_types7();
23752
23387
  HookRegistry = class {
23753
23388
  /** Hooks indexed by event type for O(1) lookup */
23754
23389
  hooksByEvent;
@@ -24386,12 +24021,419 @@ __export(hooks_exports, {
24386
24021
  });
24387
24022
  var init_hooks = __esm({
24388
24023
  "src/cli/repl/hooks/index.ts"() {
24389
- init_types8();
24024
+ init_types7();
24390
24025
  init_registry5();
24391
24026
  init_executor();
24392
24027
  }
24393
24028
  });
24394
24029
 
24030
+ // src/cli/repl/interruptions/types.ts
24031
+ var init_types8 = __esm({
24032
+ "src/cli/repl/interruptions/types.ts"() {
24033
+ }
24034
+ });
24035
+
24036
+ // src/cli/repl/interruptions/classifier.ts
24037
+ function matchPatterns(text15, patterns) {
24038
+ for (let i = 0; i < patterns.length; i++) {
24039
+ if (patterns[i].test(text15)) {
24040
+ return 1 - i * 0.1;
24041
+ }
24042
+ }
24043
+ return 0;
24044
+ }
24045
+ function classifyInterruption(message) {
24046
+ const text15 = message.text;
24047
+ const abortConf = matchPatterns(text15, ABORT_PATTERNS);
24048
+ const modifyConf = matchPatterns(text15, MODIFY_PATTERNS);
24049
+ const correctConf = matchPatterns(text15, CORRECT_PATTERNS);
24050
+ if (abortConf > 0 && abortConf >= modifyConf && abortConf >= correctConf) {
24051
+ return {
24052
+ text: text15,
24053
+ type: "abort" /* Abort */,
24054
+ confidence: Math.min(1, abortConf),
24055
+ timestamp: message.timestamp
24056
+ };
24057
+ }
24058
+ if (modifyConf > 0 && modifyConf >= correctConf) {
24059
+ return {
24060
+ text: text15,
24061
+ type: "modify" /* Modify */,
24062
+ confidence: Math.min(1, modifyConf),
24063
+ timestamp: message.timestamp
24064
+ };
24065
+ }
24066
+ if (correctConf > 0) {
24067
+ return {
24068
+ text: text15,
24069
+ type: "correct" /* Correct */,
24070
+ confidence: Math.min(1, correctConf),
24071
+ timestamp: message.timestamp
24072
+ };
24073
+ }
24074
+ return {
24075
+ text: text15,
24076
+ type: "info" /* Info */,
24077
+ confidence: 0.5,
24078
+ timestamp: message.timestamp
24079
+ };
24080
+ }
24081
+ var ABORT_PATTERNS, MODIFY_PATTERNS, CORRECT_PATTERNS;
24082
+ var init_classifier = __esm({
24083
+ "src/cli/repl/interruptions/classifier.ts"() {
24084
+ init_types8();
24085
+ ABORT_PATTERNS = [
24086
+ /^(para|stop|cancel|abort|quit|exit|detente|basta)$/i,
24087
+ /^(para\s+ya|stop\s+it|cancel\s+that)$/i,
24088
+ /\b(para|stop|cancel|abort)\b/i
24089
+ ];
24090
+ MODIFY_PATTERNS = [
24091
+ /^(a[ñn]ade|add|incluye|include|pon|put|agrega)\b/i,
24092
+ /^(cambia|change|modifica|modify|usa|use|haz|make)\b/i,
24093
+ /^no[,.]?\s+/i,
24094
+ // "no, mejor de la griega" — negation signals redirection
24095
+ /^(prefiero|prefer|quiero|i\s+want)\b/i,
24096
+ // "prefiero en gijon" — preference signals redirection
24097
+ /\b(a[ñn]ade|add|incluye|include)\s+/i,
24098
+ /\b(cambia|change|modifica|modify)\s+/i,
24099
+ /\b(en\s+vez\s+de|instead\s+of|rather\s+than)\b/i,
24100
+ /\b(prefiero|prefer|mejor|better|más|more|less|menos|bigger|smaller|larger)\b/i,
24101
+ /\b(también|also|además|additionally)\b/i
24102
+ ];
24103
+ CORRECT_PATTERNS = [
24104
+ /^(error|bug|fallo|wrong|mal|incorrect)\b/i,
24105
+ /^(arregla|fix|corrige|correct|repara|repair)\b/i,
24106
+ /\b(error\s+en|bug\s+in|fallo\s+en)\b/i,
24107
+ /\b(está\s+mal|is\s+wrong|no\s+funciona|doesn'?t\s+work)\b/i,
24108
+ /\b(arregla|fix|corrige|correct)\s+/i
24109
+ ];
24110
+ }
24111
+ });
24112
+ function mapClassificationToAction(type) {
24113
+ switch (type) {
24114
+ case "abort" /* Abort */:
24115
+ return "abort" /* Abort */;
24116
+ // Explicit modification/correction keywords → pre-select Modify
24117
+ case "modify" /* Modify */:
24118
+ case "correct" /* Correct */:
24119
+ return "modify" /* Modify */;
24120
+ // Info or unclassified → pre-select Queue (likely a new topic/task)
24121
+ // The user can always switch to Modify via the menu if needed
24122
+ case "info" /* Info */:
24123
+ default:
24124
+ return "queue" /* Queue */;
24125
+ }
24126
+ }
24127
+ var init_action_selector = __esm({
24128
+ "src/cli/repl/input/action-selector.ts"() {
24129
+ init_types8();
24130
+ }
24131
+ });
24132
+
24133
+ // src/cli/repl/interruptions/llm-classifier.ts
24134
+ var llm_classifier_exports = {};
24135
+ __export(llm_classifier_exports, {
24136
+ createLLMClassifier: () => createLLMClassifier
24137
+ });
24138
+ function buildClassificationPrompt(userMessage, currentTask) {
24139
+ if (currentTask) {
24140
+ return `Current task: "${currentTask}"
24141
+ User's new message: "${userMessage}"`;
24142
+ }
24143
+ return `User's new message: "${userMessage}"`;
24144
+ }
24145
+ function parseResponse(response) {
24146
+ const normalized = response.trim().toUpperCase();
24147
+ if (normalized.includes("STEER")) return "steer" /* Steer */;
24148
+ if (normalized.includes("MODIFY")) return "modify" /* Modify */;
24149
+ if (normalized.includes("QUEUE")) return "queue" /* Queue */;
24150
+ if (normalized.includes("ABORT")) return "abort" /* Abort */;
24151
+ return null;
24152
+ }
24153
+ function classifyWithKeywords(message) {
24154
+ const classified = classifyInterruption(message);
24155
+ return mapClassificationToAction(classified.type);
24156
+ }
24157
+ function createLLMClassifier(provider, config) {
24158
+ const cfg = { ...DEFAULT_CONFIG6, ...config };
24159
+ return {
24160
+ /**
24161
+ * Classify a user message captured during agent execution.
24162
+ *
24163
+ * Makes a fast parallel LLM call. If the LLM doesn't respond within
24164
+ * the timeout, falls back to keyword-based classification.
24165
+ *
24166
+ * @param message - The captured message
24167
+ * @param currentTask - The original task the agent is working on (for context)
24168
+ * @returns Classification result with action and source
24169
+ */
24170
+ async classify(message, currentTask) {
24171
+ const llmPromise = classifyWithLLM(provider, message, currentTask, cfg);
24172
+ let timeoutId;
24173
+ const timeoutPromise = new Promise((resolve4) => {
24174
+ timeoutId = setTimeout(() => resolve4(null), cfg.timeoutMs);
24175
+ });
24176
+ try {
24177
+ const llmResult = await Promise.race([llmPromise, timeoutPromise]);
24178
+ if (llmResult !== null) {
24179
+ return { action: llmResult, source: "llm" };
24180
+ }
24181
+ return {
24182
+ action: classifyWithKeywords(message),
24183
+ source: "keywords"
24184
+ };
24185
+ } finally {
24186
+ clearTimeout(timeoutId);
24187
+ }
24188
+ }
24189
+ };
24190
+ }
24191
+ async function classifyWithLLM(provider, message, currentTask, cfg) {
24192
+ try {
24193
+ const userPrompt = buildClassificationPrompt(message.text, currentTask);
24194
+ const options = {
24195
+ maxTokens: cfg.maxTokens,
24196
+ temperature: cfg.temperature,
24197
+ system: CLASSIFICATION_SYSTEM_PROMPT2,
24198
+ timeout: cfg.timeoutMs
24199
+ };
24200
+ const response = await provider.chat([{ role: "user", content: userPrompt }], options);
24201
+ return parseResponse(response.content);
24202
+ } catch {
24203
+ return null;
24204
+ }
24205
+ }
24206
+ var DEFAULT_CONFIG6, CLASSIFICATION_SYSTEM_PROMPT2;
24207
+ var init_llm_classifier = __esm({
24208
+ "src/cli/repl/interruptions/llm-classifier.ts"() {
24209
+ init_types8();
24210
+ init_classifier();
24211
+ init_action_selector();
24212
+ DEFAULT_CONFIG6 = {
24213
+ timeoutMs: 8e3,
24214
+ maxTokens: 10,
24215
+ temperature: 0
24216
+ };
24217
+ CLASSIFICATION_SYSTEM_PROMPT2 = `You are a message classifier for a coding assistant. The user sent a message while the assistant was working on a task.
24218
+
24219
+ Classify the message into exactly ONE category. Reply with ONLY the category word, nothing else.
24220
+
24221
+ Categories:
24222
+ - STEER: The message provides a hint, preference, or minor adjustment to the CURRENT task without requiring a restart (e.g. "also add a test", "use camelCase", "make sure to handle errors", "btw the file is in src/", "prefer async/await")
24223
+ - MODIFY: The message fundamentally changes the CURRENT task requiring a fresh start (e.g. "actually use Python instead of JS", "no, do it completely differently", "start over with a new approach")
24224
+ - QUEUE: The message is a NEW, DIFFERENT task unrelated to what's being done (e.g. "what's 2+2", "tell me the weather", "create another file for X")
24225
+ - ABORT: The message asks to stop/cancel the current work (e.g. "stop", "cancel", "para", "never mind")
24226
+
24227
+ Reply with exactly one word: STEER, MODIFY, QUEUE, or ABORT`;
24228
+ }
24229
+ });
24230
+
24231
+ // src/cli/repl/context/stack-detector.ts
24232
+ var stack_detector_exports = {};
24233
+ __export(stack_detector_exports, {
24234
+ detectProjectStack: () => detectProjectStack
24235
+ });
24236
+ async function detectStack2(cwd) {
24237
+ if (await fileExists3(path39__default.join(cwd, "package.json"))) return "node";
24238
+ if (await fileExists3(path39__default.join(cwd, "Cargo.toml"))) return "rust";
24239
+ if (await fileExists3(path39__default.join(cwd, "pyproject.toml"))) return "python";
24240
+ if (await fileExists3(path39__default.join(cwd, "go.mod"))) return "go";
24241
+ if (await fileExists3(path39__default.join(cwd, "pom.xml"))) return "java";
24242
+ if (await fileExists3(path39__default.join(cwd, "build.gradle"))) return "java";
24243
+ if (await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) return "java";
24244
+ return "unknown";
24245
+ }
24246
+ async function detectPackageManager3(cwd, stack) {
24247
+ if (stack === "rust") return "cargo";
24248
+ if (stack === "python") return "pip";
24249
+ if (stack === "go") return "go";
24250
+ if (stack === "java") {
24251
+ if (await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"))) {
24252
+ return "gradle";
24253
+ }
24254
+ if (await fileExists3(path39__default.join(cwd, "pom.xml"))) {
24255
+ return "maven";
24256
+ }
24257
+ }
24258
+ if (stack === "node") {
24259
+ if (await fileExists3(path39__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
24260
+ if (await fileExists3(path39__default.join(cwd, "yarn.lock"))) return "yarn";
24261
+ if (await fileExists3(path39__default.join(cwd, "bun.lockb"))) return "bun";
24262
+ return "npm";
24263
+ }
24264
+ return null;
24265
+ }
24266
+ async function parsePackageJson(cwd) {
24267
+ const packageJsonPath = path39__default.join(cwd, "package.json");
24268
+ try {
24269
+ const content = await fs35__default.readFile(packageJsonPath, "utf-8");
24270
+ const pkg = JSON.parse(content);
24271
+ const allDeps = {
24272
+ ...pkg.dependencies,
24273
+ ...pkg.devDependencies
24274
+ };
24275
+ const frameworks = [];
24276
+ if (allDeps.react) frameworks.push("React");
24277
+ if (allDeps.vue) frameworks.push("Vue");
24278
+ if (allDeps["@angular/core"]) frameworks.push("Angular");
24279
+ if (allDeps.next) frameworks.push("Next.js");
24280
+ if (allDeps.nuxt) frameworks.push("Nuxt");
24281
+ if (allDeps.express) frameworks.push("Express");
24282
+ if (allDeps.fastify) frameworks.push("Fastify");
24283
+ if (allDeps.nestjs || allDeps["@nestjs/core"]) frameworks.push("NestJS");
24284
+ const buildTools2 = [];
24285
+ if (allDeps.webpack) buildTools2.push("webpack");
24286
+ if (allDeps.vite) buildTools2.push("vite");
24287
+ if (allDeps.rollup) buildTools2.push("rollup");
24288
+ if (allDeps.tsup) buildTools2.push("tsup");
24289
+ if (allDeps.esbuild) buildTools2.push("esbuild");
24290
+ if (pkg.scripts?.build) buildTools2.push("build");
24291
+ const testingFrameworks = [];
24292
+ if (allDeps.vitest) testingFrameworks.push("vitest");
24293
+ if (allDeps.jest) testingFrameworks.push("jest");
24294
+ if (allDeps.mocha) testingFrameworks.push("mocha");
24295
+ if (allDeps.chai) testingFrameworks.push("chai");
24296
+ if (allDeps["@playwright/test"]) testingFrameworks.push("playwright");
24297
+ if (allDeps.cypress) testingFrameworks.push("cypress");
24298
+ const languages = ["JavaScript"];
24299
+ if (allDeps.typescript || await fileExists3(path39__default.join(cwd, "tsconfig.json"))) {
24300
+ languages.push("TypeScript");
24301
+ }
24302
+ return {
24303
+ dependencies: allDeps,
24304
+ frameworks,
24305
+ buildTools: buildTools2,
24306
+ testingFrameworks,
24307
+ languages
24308
+ };
24309
+ } catch {
24310
+ return {
24311
+ dependencies: {},
24312
+ frameworks: [],
24313
+ buildTools: [],
24314
+ testingFrameworks: [],
24315
+ languages: []
24316
+ };
24317
+ }
24318
+ }
24319
+ async function parsePomXml(cwd) {
24320
+ const pomPath = path39__default.join(cwd, "pom.xml");
24321
+ try {
24322
+ const content = await fs35__default.readFile(pomPath, "utf-8");
24323
+ const dependencies = {};
24324
+ const frameworks = [];
24325
+ const buildTools2 = ["maven"];
24326
+ const testingFrameworks = [];
24327
+ const depRegex = /<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>/g;
24328
+ let match;
24329
+ while ((match = depRegex.exec(content)) !== null) {
24330
+ const groupId = match[1];
24331
+ const artifactId = match[2];
24332
+ if (!groupId || !artifactId) continue;
24333
+ const fullName = `${groupId}:${artifactId}`;
24334
+ dependencies[fullName] = "unknown";
24335
+ if (artifactId.includes("spring-boot")) {
24336
+ if (!frameworks.includes("Spring Boot")) frameworks.push("Spring Boot");
24337
+ }
24338
+ if (artifactId.includes("spring-webmvc") || artifactId.includes("spring-web")) {
24339
+ if (!frameworks.includes("Spring MVC")) frameworks.push("Spring MVC");
24340
+ }
24341
+ if (artifactId.includes("hibernate")) {
24342
+ if (!frameworks.includes("Hibernate")) frameworks.push("Hibernate");
24343
+ }
24344
+ if (artifactId === "junit-jupiter" || artifactId === "junit") {
24345
+ if (!testingFrameworks.includes("JUnit")) testingFrameworks.push("JUnit");
24346
+ }
24347
+ if (artifactId === "mockito-core") {
24348
+ if (!testingFrameworks.includes("Mockito")) testingFrameworks.push("Mockito");
24349
+ }
24350
+ }
24351
+ return { dependencies, frameworks, buildTools: buildTools2, testingFrameworks };
24352
+ } catch {
24353
+ return { dependencies: {}, frameworks: [], buildTools: ["maven"], testingFrameworks: [] };
24354
+ }
24355
+ }
24356
+ async function parsePyprojectToml(cwd) {
24357
+ const pyprojectPath = path39__default.join(cwd, "pyproject.toml");
24358
+ try {
24359
+ const content = await fs35__default.readFile(pyprojectPath, "utf-8");
24360
+ const dependencies = {};
24361
+ const frameworks = [];
24362
+ const buildTools2 = ["pip"];
24363
+ const testingFrameworks = [];
24364
+ const lines = content.split("\n");
24365
+ for (const line of lines) {
24366
+ const trimmed = line.trim();
24367
+ if (trimmed.match(/^["']?[\w-]+["']?\s*=\s*["'][\^~>=<]+[\d.]+["']/)) {
24368
+ const depMatch = trimmed.match(/^["']?([\w-]+)["']?\s*=\s*["']([\^~>=<]+[\d.]+)["']/);
24369
+ if (depMatch && depMatch[1] && depMatch[2]) {
24370
+ dependencies[depMatch[1]] = depMatch[2];
24371
+ }
24372
+ }
24373
+ if (trimmed.includes("fastapi")) frameworks.push("FastAPI");
24374
+ if (trimmed.includes("django")) frameworks.push("Django");
24375
+ if (trimmed.includes("flask")) frameworks.push("Flask");
24376
+ if (trimmed.includes("pytest")) testingFrameworks.push("pytest");
24377
+ if (trimmed.includes("unittest")) testingFrameworks.push("unittest");
24378
+ }
24379
+ return { dependencies, frameworks, buildTools: buildTools2, testingFrameworks };
24380
+ } catch {
24381
+ return { dependencies: {}, frameworks: [], buildTools: ["pip"], testingFrameworks: [] };
24382
+ }
24383
+ }
24384
+ async function detectProjectStack(cwd) {
24385
+ const stack = await detectStack2(cwd);
24386
+ const packageManager = await detectPackageManager3(cwd, stack);
24387
+ let dependencies = {};
24388
+ let frameworks = [];
24389
+ let buildTools2 = [];
24390
+ let testingFrameworks = [];
24391
+ let languages = [];
24392
+ if (stack === "node") {
24393
+ const parsed = await parsePackageJson(cwd);
24394
+ dependencies = parsed.dependencies;
24395
+ frameworks = parsed.frameworks;
24396
+ buildTools2 = parsed.buildTools;
24397
+ testingFrameworks = parsed.testingFrameworks;
24398
+ languages = parsed.languages;
24399
+ } else if (stack === "java") {
24400
+ const isGradle = await fileExists3(path39__default.join(cwd, "build.gradle")) || await fileExists3(path39__default.join(cwd, "build.gradle.kts"));
24401
+ const parsed = isGradle ? { dependencies: {}, frameworks: [], buildTools: ["gradle"], testingFrameworks: ["JUnit"] } : await parsePomXml(cwd);
24402
+ dependencies = parsed.dependencies;
24403
+ frameworks = parsed.frameworks;
24404
+ buildTools2 = parsed.buildTools;
24405
+ testingFrameworks = parsed.testingFrameworks;
24406
+ languages = ["Java"];
24407
+ } else if (stack === "python") {
24408
+ const parsed = await parsePyprojectToml(cwd);
24409
+ dependencies = parsed.dependencies;
24410
+ frameworks = parsed.frameworks;
24411
+ buildTools2 = parsed.buildTools;
24412
+ testingFrameworks = parsed.testingFrameworks;
24413
+ languages = ["Python"];
24414
+ } else if (stack === "go") {
24415
+ languages = ["Go"];
24416
+ buildTools2 = ["go"];
24417
+ } else if (stack === "rust") {
24418
+ languages = ["Rust"];
24419
+ buildTools2 = ["cargo"];
24420
+ }
24421
+ return {
24422
+ stack,
24423
+ packageManager,
24424
+ dependencies,
24425
+ frameworks,
24426
+ buildTools: buildTools2,
24427
+ testingFrameworks,
24428
+ languages
24429
+ };
24430
+ }
24431
+ var init_stack_detector = __esm({
24432
+ "src/cli/repl/context/stack-detector.ts"() {
24433
+ init_files();
24434
+ }
24435
+ });
24436
+
24395
24437
  // src/cli/repl/input/message-queue.ts
24396
24438
  function createMessageQueue(maxSize = 50) {
24397
24439
  let messages = [];
@@ -24769,7 +24811,7 @@ function createFeedbackSystem(getSpinner, config) {
24769
24811
  var DEFAULT_FEEDBACK_CONFIG;
24770
24812
  var init_feedback_system = __esm({
24771
24813
  "src/cli/repl/feedback/feedback-system.ts"() {
24772
- init_types7();
24814
+ init_types8();
24773
24815
  DEFAULT_FEEDBACK_CONFIG = {
24774
24816
  method: "spinner",
24775
24817
  displayDurationMs: 2e3,
@@ -25045,8 +25087,8 @@ Generated by Corbat-Coco v0.1.0
25045
25087
 
25046
25088
  // src/cli/commands/init.ts
25047
25089
  function registerInitCommand(program2) {
25048
- 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 (path60, options) => {
25049
- await runInit(path60, options);
25090
+ 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 (path62, options) => {
25091
+ await runInit(path62, options);
25050
25092
  });
25051
25093
  }
25052
25094
  async function runInit(projectPath, options) {
@@ -25125,18 +25167,18 @@ async function gatherProjectInfo() {
25125
25167
  language
25126
25168
  };
25127
25169
  }
25128
- function getDefaultProjectInfo(path60) {
25129
- const name = path60 === "." ? "my-project" : path60.split("/").pop() || "my-project";
25170
+ function getDefaultProjectInfo(path62) {
25171
+ const name = path62 === "." ? "my-project" : path62.split("/").pop() || "my-project";
25130
25172
  return {
25131
25173
  name,
25132
25174
  description: "",
25133
25175
  language: "typescript"
25134
25176
  };
25135
25177
  }
25136
- async function checkExistingProject(path60) {
25178
+ async function checkExistingProject(path62) {
25137
25179
  try {
25138
25180
  const fs56 = await import('fs/promises');
25139
- await fs56.access(`${path60}/.coco`);
25181
+ await fs56.access(`${path62}/.coco`);
25140
25182
  return true;
25141
25183
  } catch {
25142
25184
  return false;
@@ -28637,20 +28679,20 @@ async function createCliPhaseContext(projectPath, _onUserInput) {
28637
28679
  },
28638
28680
  tools: {
28639
28681
  file: {
28640
- async read(path60) {
28682
+ async read(path62) {
28641
28683
  const fs56 = await import('fs/promises');
28642
- return fs56.readFile(path60, "utf-8");
28684
+ return fs56.readFile(path62, "utf-8");
28643
28685
  },
28644
- async write(path60, content) {
28686
+ async write(path62, content) {
28645
28687
  const fs56 = await import('fs/promises');
28646
28688
  const nodePath = await import('path');
28647
- await fs56.mkdir(nodePath.dirname(path60), { recursive: true });
28648
- await fs56.writeFile(path60, content, "utf-8");
28689
+ await fs56.mkdir(nodePath.dirname(path62), { recursive: true });
28690
+ await fs56.writeFile(path62, content, "utf-8");
28649
28691
  },
28650
- async exists(path60) {
28692
+ async exists(path62) {
28651
28693
  const fs56 = await import('fs/promises');
28652
28694
  try {
28653
- await fs56.access(path60);
28695
+ await fs56.access(path62);
28654
28696
  return true;
28655
28697
  } catch {
28656
28698
  return false;
@@ -29006,10 +29048,10 @@ function getPhaseStatusForPhase(phase) {
29006
29048
  }
29007
29049
  async function loadProjectState(cwd, config) {
29008
29050
  const fs56 = await import('fs/promises');
29009
- const path60 = await import('path');
29010
- const statePath = path60.join(cwd, ".coco", "state.json");
29011
- const backlogPath = path60.join(cwd, ".coco", "planning", "backlog.json");
29012
- const checkpointDir = path60.join(cwd, ".coco", "checkpoints");
29051
+ const path62 = await import('path');
29052
+ const statePath = path62.join(cwd, ".coco", "state.json");
29053
+ const backlogPath = path62.join(cwd, ".coco", "planning", "backlog.json");
29054
+ const checkpointDir = path62.join(cwd, ".coco", "checkpoints");
29013
29055
  let currentPhase = "idle";
29014
29056
  let metrics;
29015
29057
  let sprint;
@@ -30352,7 +30394,7 @@ function getProviderDefinition(type) {
30352
30394
  return PROVIDER_DEFINITIONS[type];
30353
30395
  }
30354
30396
  function getAllProviders() {
30355
- return Object.values(PROVIDER_DEFINITIONS).filter((p46) => !p46.internal);
30397
+ return Object.values(PROVIDER_DEFINITIONS).filter((p47) => !p47.internal);
30356
30398
  }
30357
30399
  function getRecommendedModel(type) {
30358
30400
  const provider = PROVIDER_DEFINITIONS[type];
@@ -30373,20 +30415,20 @@ function hasLocalProviderConfig(type) {
30373
30415
  return process.env["COCO_PROVIDER"] === "ollama" || !!process.env["OLLAMA_MODEL"] || !!process.env["OLLAMA_BASE_URL"];
30374
30416
  }
30375
30417
  function getConfiguredProviders() {
30376
- return getAllProviders().filter((p46) => {
30377
- if (p46.id === "copilot") {
30418
+ return getAllProviders().filter((p47) => {
30419
+ if (p47.id === "copilot") {
30378
30420
  return !!process.env["GITHUB_TOKEN"] || !!process.env["GH_TOKEN"] || hasCopilotCredentials();
30379
30421
  }
30380
- if (p46.id === "openai") {
30381
- return !!process.env[p46.envVar] || !!process.env["OPENAI_CODEX_TOKEN"] || !!process.env["OPENAI_ACCESS_TOKEN"];
30422
+ if (p47.id === "openai") {
30423
+ return !!process.env[p47.envVar] || !!process.env["OPENAI_CODEX_TOKEN"] || !!process.env["OPENAI_ACCESS_TOKEN"];
30382
30424
  }
30383
- if (p46.id === "lmstudio" || p46.id === "ollama") {
30384
- return hasLocalProviderConfig(p46.id);
30425
+ if (p47.id === "lmstudio" || p47.id === "ollama") {
30426
+ return hasLocalProviderConfig(p47.id);
30385
30427
  }
30386
- if (p46.id === "vertex") {
30428
+ if (p47.id === "vertex") {
30387
30429
  return !!(process.env["VERTEX_API_KEY"] ?? process.env["GOOGLE_API_KEY"] ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"]);
30388
30430
  }
30389
- return !!process.env[p46.envVar];
30431
+ return !!process.env[p47.envVar];
30390
30432
  });
30391
30433
  }
30392
30434
  function isProviderConfigured(type) {
@@ -30554,8 +30596,8 @@ async function saveConfig2(config) {
30554
30596
  await fs56.mkdir(dir, { recursive: true });
30555
30597
  await fs56.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
30556
30598
  }
30557
- function getNestedValue(obj, path60) {
30558
- const keys = path60.split(".");
30599
+ function getNestedValue(obj, path62) {
30600
+ const keys = path62.split(".");
30559
30601
  let current = obj;
30560
30602
  for (const key of keys) {
30561
30603
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -30565,8 +30607,8 @@ function getNestedValue(obj, path60) {
30565
30607
  }
30566
30608
  return current;
30567
30609
  }
30568
- function setNestedValue(obj, path60, value) {
30569
- const keys = path60.split(".");
30610
+ function setNestedValue(obj, path62, value) {
30611
+ const keys = path62.split(".");
30570
30612
  let current = obj;
30571
30613
  for (let i = 0; i < keys.length - 1; i++) {
30572
30614
  const key = keys[i];
@@ -31598,9 +31640,9 @@ function registerCheckCommand(program2) {
31598
31640
  // src/swarm/spec-parser.ts
31599
31641
  async function parseSwarmSpec(filePath) {
31600
31642
  const fs56 = await import('fs/promises');
31601
- const path60 = await import('path');
31643
+ const path62 = await import('path');
31602
31644
  const rawContent = await fs56.readFile(filePath, "utf-8");
31603
- const ext = path60.extname(filePath).toLowerCase();
31645
+ const ext = path62.extname(filePath).toLowerCase();
31604
31646
  if (ext === ".yaml" || ext === ".yml") {
31605
31647
  return parseYamlSpec(rawContent);
31606
31648
  }
@@ -31930,8 +31972,8 @@ var DEFAULT_AGENT_CONFIG = {
31930
31972
  };
31931
31973
  async function loadAgentConfig(projectPath) {
31932
31974
  const fs56 = await import('fs/promises');
31933
- const path60 = await import('path');
31934
- const configPath = path60.join(projectPath, ".coco", "swarm", "agents.json");
31975
+ const path62 = await import('path');
31976
+ const configPath = path62.join(projectPath, ".coco", "swarm", "agents.json");
31935
31977
  try {
31936
31978
  const raw = await fs56.readFile(configPath, "utf-8");
31937
31979
  const parsed = JSON.parse(raw);
@@ -32122,16 +32164,16 @@ async function createBoard(projectPath, spec) {
32122
32164
  }
32123
32165
  async function loadBoard(projectPath) {
32124
32166
  const fs56 = await import('fs/promises');
32125
- const path60 = await import('path');
32126
- const boardPath = path60.join(projectPath, ".coco", "swarm", "task-board.json");
32167
+ const path62 = await import('path');
32168
+ const boardPath = path62.join(projectPath, ".coco", "swarm", "task-board.json");
32127
32169
  const raw = await fs56.readFile(boardPath, "utf-8");
32128
32170
  return JSON.parse(raw);
32129
32171
  }
32130
32172
  async function saveBoard(projectPath, board) {
32131
32173
  const fs56 = await import('fs/promises');
32132
- const path60 = await import('path');
32133
- const boardDir = path60.join(projectPath, ".coco", "swarm");
32134
- const boardPath = path60.join(boardDir, "task-board.json");
32174
+ const path62 = await import('path');
32175
+ const boardDir = path62.join(projectPath, ".coco", "swarm");
32176
+ const boardPath = path62.join(boardDir, "task-board.json");
32135
32177
  await fs56.mkdir(boardDir, { recursive: true });
32136
32178
  await fs56.writeFile(boardPath, JSON.stringify(board, null, 2), "utf-8");
32137
32179
  }
@@ -32273,25 +32315,25 @@ Rules:
32273
32315
  }
32274
32316
  }
32275
32317
  async function defaultPromptHandler(q) {
32276
- const p46 = await import('@clack/prompts');
32318
+ const p47 = await import('@clack/prompts');
32277
32319
  if (q.options && q.options.length > 0) {
32278
- const result = await p46.select({
32320
+ const result = await p47.select({
32279
32321
  message: q.question,
32280
32322
  options: [
32281
32323
  ...q.options.map((o) => ({ value: o, label: o })),
32282
32324
  { value: q.assumedAnswer, label: `${q.assumedAnswer} (default)` }
32283
32325
  ]
32284
32326
  });
32285
- if (p46.isCancel(result)) {
32327
+ if (p47.isCancel(result)) {
32286
32328
  return q.assumedAnswer;
32287
32329
  }
32288
32330
  return result;
32289
32331
  } else {
32290
- const result = await p46.text({
32332
+ const result = await p47.text({
32291
32333
  message: q.question,
32292
32334
  placeholder: q.assumedAnswer
32293
32335
  });
32294
- if (p46.isCancel(result) || !result) {
32336
+ if (p47.isCancel(result) || !result) {
32295
32337
  return q.assumedAnswer;
32296
32338
  }
32297
32339
  return result;
@@ -32299,9 +32341,9 @@ async function defaultPromptHandler(q) {
32299
32341
  }
32300
32342
  async function writeAssumptionsFile(projectPath, projectName, questions, assumptions) {
32301
32343
  const fs56 = await import('fs/promises');
32302
- const path60 = await import('path');
32303
- const swarmDir = path60.join(projectPath, ".coco", "swarm");
32304
- const assumptionsPath = path60.join(swarmDir, "assumptions.md");
32344
+ const path62 = await import('path');
32345
+ const swarmDir = path62.join(projectPath, ".coco", "swarm");
32346
+ const assumptionsPath = path62.join(swarmDir, "assumptions.md");
32305
32347
  await fs56.mkdir(swarmDir, { recursive: true });
32306
32348
  const now = (/* @__PURE__ */ new Date()).toISOString();
32307
32349
  const content = [
@@ -32325,9 +32367,9 @@ async function writeAssumptionsFile(projectPath, projectName, questions, assumpt
32325
32367
  // src/swarm/events.ts
32326
32368
  async function appendSwarmEvent(projectPath, event) {
32327
32369
  const fs56 = await import('fs/promises');
32328
- const path60 = await import('path');
32329
- const eventsDir = path60.join(projectPath, ".coco", "swarm");
32330
- const eventsFile = path60.join(eventsDir, "events.jsonl");
32370
+ const path62 = await import('path');
32371
+ const eventsDir = path62.join(projectPath, ".coco", "swarm");
32372
+ const eventsFile = path62.join(eventsDir, "events.jsonl");
32331
32373
  await fs56.mkdir(eventsDir, { recursive: true });
32332
32374
  await fs56.appendFile(eventsFile, JSON.stringify(event) + "\n", "utf-8");
32333
32375
  }
@@ -32338,9 +32380,9 @@ function createEventId() {
32338
32380
  // src/swarm/knowledge.ts
32339
32381
  async function appendKnowledge(projectPath, entry) {
32340
32382
  const fs56 = await import('fs/promises');
32341
- const path60 = await import('path');
32342
- const knowledgeDir = path60.join(projectPath, ".coco", "swarm");
32343
- const knowledgeFile = path60.join(knowledgeDir, "knowledge.jsonl");
32383
+ const path62 = await import('path');
32384
+ const knowledgeDir = path62.join(projectPath, ".coco", "swarm");
32385
+ const knowledgeFile = path62.join(knowledgeDir, "knowledge.jsonl");
32344
32386
  await fs56.mkdir(knowledgeDir, { recursive: true });
32345
32387
  await fs56.appendFile(knowledgeFile, JSON.stringify(entry) + "\n", "utf-8");
32346
32388
  }
@@ -32647,10 +32689,10 @@ async function runSwarmLifecycle(options) {
32647
32689
  async function stageInit(ctx) {
32648
32690
  const { projectPath, spec } = ctx.options;
32649
32691
  const fs56 = await import('fs/promises');
32650
- const path60 = await import('path');
32651
- await fs56.mkdir(path60.join(projectPath, ".coco", "swarm"), { recursive: true });
32692
+ const path62 = await import('path');
32693
+ await fs56.mkdir(path62.join(projectPath, ".coco", "swarm"), { recursive: true });
32652
32694
  await fs56.mkdir(ctx.options.outputPath, { recursive: true });
32653
- const specSummaryPath = path60.join(projectPath, ".coco", "swarm", "spec-summary.json");
32695
+ const specSummaryPath = path62.join(projectPath, ".coco", "swarm", "spec-summary.json");
32654
32696
  const specSummary = {
32655
32697
  projectName: spec.projectName,
32656
32698
  description: spec.description,
@@ -32722,8 +32764,8 @@ async function stagePlan(ctx) {
32722
32764
  ]);
32723
32765
  await createBoard(projectPath, spec);
32724
32766
  const fs56 = await import('fs/promises');
32725
- const path60 = await import('path');
32726
- const planPath = path60.join(projectPath, ".coco", "swarm", "plan.json");
32767
+ const path62 = await import('path');
32768
+ const planPath = path62.join(projectPath, ".coco", "swarm", "plan.json");
32727
32769
  await fs56.writeFile(
32728
32770
  planPath,
32729
32771
  JSON.stringify({ pm: pmResult, architect: archResult, bestPractices: bpResult }, null, 2),
@@ -32940,7 +32982,7 @@ async function stageIntegrate(ctx) {
32940
32982
  async function stageOutput(ctx) {
32941
32983
  const { projectPath, outputPath } = ctx.options;
32942
32984
  const fs56 = await import('fs/promises');
32943
- const path60 = await import('path');
32985
+ const path62 = await import('path');
32944
32986
  const board = await loadBoard(projectPath);
32945
32987
  const featureResults = Array.from(ctx.featureResults.values());
32946
32988
  const summary = {
@@ -32955,7 +32997,7 @@ async function stageOutput(ctx) {
32955
32997
  globalScore: computeGlobalScore(featureResults)
32956
32998
  };
32957
32999
  await fs56.mkdir(outputPath, { recursive: true });
32958
- const summaryPath = path60.join(outputPath, "swarm-summary.json");
33000
+ const summaryPath = path62.join(outputPath, "swarm-summary.json");
32959
33001
  await fs56.writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
32960
33002
  const passed = summary.globalScore >= ctx.options.minScore;
32961
33003
  await emitGate(projectPath, "global-score", passed, `Global score: ${summary.globalScore}`);
@@ -33302,8 +33344,8 @@ var SwarmOrchestrator = class {
33302
33344
  noQuestions = false,
33303
33345
  onProgress
33304
33346
  } = options;
33305
- const path60 = await import('path');
33306
- const projectPath = path60.dirname(path60.resolve(specFile));
33347
+ const path62 = await import('path');
33348
+ const projectPath = path62.dirname(path62.resolve(specFile));
33307
33349
  onProgress?.("init", `Parsing spec file: ${specFile}`);
33308
33350
  const spec = await parseSwarmSpec(specFile);
33309
33351
  onProgress?.("init", `Initializing provider: ${providerType}`);
@@ -33314,7 +33356,7 @@ var SwarmOrchestrator = class {
33314
33356
  await runSwarmLifecycle({
33315
33357
  spec,
33316
33358
  projectPath,
33317
- outputPath: path60.resolve(outputPath),
33359
+ outputPath: path62.resolve(outputPath),
33318
33360
  provider,
33319
33361
  agentConfig,
33320
33362
  minScore,
@@ -33446,6 +33488,7 @@ var helpCommand = {
33446
33488
  commands: [
33447
33489
  { cmd: "/model, /m", desc: "View or change the current model" },
33448
33490
  { cmd: "/provider", desc: "View or change the LLM provider" },
33491
+ { cmd: "/doctor, /dr", desc: "Run local diagnostics for config, auth, hooks, and tools" },
33449
33492
  { cmd: "/compact", desc: "Toggle compact mode (less verbose)" },
33450
33493
  { cmd: "/cost, /tokens", desc: "Show token usage and cost" },
33451
33494
  { cmd: "/trust", desc: "Manage project trust permissions" },
@@ -34256,7 +34299,7 @@ async function runOnboardingV2() {
34256
34299
  console.log(chalk.magenta(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
34257
34300
  console.log();
34258
34301
  p26.log.info(
34259
- `Found ${configuredProviders.length} configured provider(s): ${configuredProviders.map((p46) => p46.emoji + " " + p46.name).join(", ")}`
34302
+ `Found ${configuredProviders.length} configured provider(s): ${configuredProviders.map((p47) => p47.emoji + " " + p47.name).join(", ")}`
34260
34303
  );
34261
34304
  const useExisting = await p26.confirm({
34262
34305
  message: "Use an existing provider?",
@@ -35008,9 +35051,9 @@ async function setupOllamaProvider(port) {
35008
35051
  return setupLocalProvider("ollama", port);
35009
35052
  }
35010
35053
  async function selectExistingProvider(providers) {
35011
- const options = providers.map((p46) => ({
35012
- value: p46.id,
35013
- label: `${p46.emoji} ${p46.name}`,
35054
+ const options = providers.map((p47) => ({
35055
+ value: p47.id,
35056
+ label: `${p47.emoji} ${p47.name}`,
35014
35057
  hint: "Configured"
35015
35058
  }));
35016
35059
  options.push({ value: "__new__", label: "\u2795 Setup new provider", hint: "" });
@@ -35402,7 +35445,7 @@ async function ensureConfiguredV2(config) {
35402
35445
  } catch {
35403
35446
  }
35404
35447
  }
35405
- const preferredProviderDef = providers.find((p46) => p46.id === config.provider.type);
35448
+ const preferredProviderDef = providers.find((p47) => p47.id === config.provider.type);
35406
35449
  const preferredIsLocal = preferredProviderDef?.requiresApiKey === false && preferredProviderDef?.id !== "copilot";
35407
35450
  const preferredHasApiKey = preferredProviderDef ? !!process.env[preferredProviderDef.envVar] : false;
35408
35451
  const preferredHasOpenAIOAuth = preferredProviderDef?.id === "openai" && hasOpenAIOAuthTokens;
@@ -35432,12 +35475,12 @@ async function ensureConfiguredV2(config) {
35432
35475
  preferredUnavailableWasLocal = preferredIsLocal;
35433
35476
  }
35434
35477
  if (!preferredWasConfiguredButUnavailable || !preferredUnavailableWasLocal) {
35435
- const configuredProviders = providers.filter((p46) => {
35436
- if (p46.id === "copilot") return isProviderConfigured();
35437
- if (p46.id === "openai") {
35438
- return hasOpenAIOAuthTokens || !!process.env[p46.envVar];
35478
+ const configuredProviders = providers.filter((p47) => {
35479
+ if (p47.id === "copilot") return isProviderConfigured();
35480
+ if (p47.id === "openai") {
35481
+ return hasOpenAIOAuthTokens || !!process.env[p47.envVar];
35439
35482
  }
35440
- return p46.requiresApiKey === false || !!process.env[p46.envVar];
35483
+ return p47.requiresApiKey === false || !!process.env[p47.envVar];
35441
35484
  });
35442
35485
  for (const prov of configuredProviders) {
35443
35486
  try {
@@ -35510,7 +35553,7 @@ init_auth();
35510
35553
  init_env();
35511
35554
  async function selectProviderInteractively(providers, currentProviderId) {
35512
35555
  return new Promise((resolve4) => {
35513
- let selectedIndex = providers.findIndex((p46) => p46.id === currentProviderId);
35556
+ let selectedIndex = providers.findIndex((p47) => p47.id === currentProviderId);
35514
35557
  if (selectedIndex === -1) selectedIndex = 0;
35515
35558
  let lastTotalLines = 0;
35516
35559
  const clearPrevious = () => {
@@ -35608,12 +35651,12 @@ var providerCommand = {
35608
35651
  `));
35609
35652
  const allProviders2 = getAllProviders();
35610
35653
  const configuredProviders = getConfiguredProviders();
35611
- const providerOptions = allProviders2.map((p46) => ({
35612
- id: p46.id,
35613
- name: p46.name,
35614
- emoji: p46.emoji,
35615
- description: p46.description,
35616
- isConfigured: configuredProviders.some((cp) => cp.id === p46.id)
35654
+ const providerOptions = allProviders2.map((p47) => ({
35655
+ id: p47.id,
35656
+ name: p47.name,
35657
+ emoji: p47.emoji,
35658
+ description: p47.description,
35659
+ isConfigured: configuredProviders.some((cp) => cp.id === p47.id)
35617
35660
  }));
35618
35661
  const selectedProviderId = await selectProviderInteractively(
35619
35662
  providerOptions,
@@ -35628,15 +35671,15 @@ var providerCommand = {
35628
35671
  `));
35629
35672
  return false;
35630
35673
  }
35631
- const newProvider2 = allProviders2.find((p46) => p46.id === selectedProviderId);
35674
+ const newProvider2 = allProviders2.find((p47) => p47.id === selectedProviderId);
35632
35675
  return await switchProvider(newProvider2, session);
35633
35676
  }
35634
35677
  const newProviderId = args[0]?.toLowerCase();
35635
35678
  const allProviders = getAllProviders();
35636
- const newProvider = allProviders.find((p46) => p46.id === newProviderId);
35679
+ const newProvider = allProviders.find((p47) => p47.id === newProviderId);
35637
35680
  if (!newProvider) {
35638
35681
  console.log(chalk.red(`Unknown provider: ${newProviderId}`));
35639
- console.log(chalk.dim(`Available: ${allProviders.map((p46) => p46.id).join(", ")}
35682
+ console.log(chalk.dim(`Available: ${allProviders.map((p47) => p47.id).join(", ")}
35640
35683
  `));
35641
35684
  return false;
35642
35685
  }
@@ -36368,9 +36411,9 @@ function formatChangeSummary(files, options = {}) {
36368
36411
  const filesToShow = opts.maxFiles && opts.maxFiles > 0 ? files.slice(0, opts.maxFiles) : files;
36369
36412
  const remaining = files.length - filesToShow.length;
36370
36413
  for (const file of filesToShow) {
36371
- const statusIcon = file.status === "added" ? chalk.green("A") : file.status === "deleted" ? chalk.red("D") : chalk.yellow("M");
36414
+ const statusIcon2 = file.status === "added" ? chalk.green("A") : file.status === "deleted" ? chalk.red("D") : chalk.yellow("M");
36372
36415
  const stats = `${chalk.green("+" + file.additions)} ${chalk.red("-" + file.deletions)}`;
36373
- output += ` ${statusIcon} ${chalk.white(file.path)} ${chalk.gray(stats)}
36416
+ output += ` ${statusIcon2} ${chalk.white(file.path)} ${chalk.gray(stats)}
36374
36417
  `;
36375
36418
  }
36376
36419
  if (remaining > 0) {
@@ -36795,7 +36838,7 @@ async function showTrustStatus(session, trustStore) {
36795
36838
  }
36796
36839
  const level = trustStore.getLevel(projectPath);
36797
36840
  const list = trustStore.list();
36798
- const project = list.find((p46) => p46.path === projectPath);
36841
+ const project = list.find((p47) => p47.path === projectPath);
36799
36842
  p26.log.message("");
36800
36843
  p26.log.message(`\u{1F510} Project Trust Status`);
36801
36844
  p26.log.message(` Path: ${projectPath}`);
@@ -36888,8 +36931,8 @@ async function listTrustedProjects2(trustStore) {
36888
36931
  p26.log.message("");
36889
36932
  for (const project of projects) {
36890
36933
  const level = project.approvalLevel.toUpperCase().padEnd(5);
36891
- const path60 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
36892
- p26.log.message(` [${level}] ${path60}`);
36934
+ const path62 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
36935
+ p26.log.message(` [${level}] ${path62}`);
36893
36936
  p26.log.message(` Last accessed: ${new Date(project.lastAccessed).toLocaleString()}`);
36894
36937
  }
36895
36938
  p26.log.message("");
@@ -38120,8 +38163,8 @@ function displayRewindResult(result) {
38120
38163
  const fileName = filePath.split("/").pop() ?? filePath;
38121
38164
  console.log(`${chalk.green(String.fromCodePoint(10003))} Restored: ${fileName}`);
38122
38165
  }
38123
- for (const { path: path60, error } of result.filesFailed) {
38124
- const fileName = path60.split("/").pop() ?? path60;
38166
+ for (const { path: path62, error } of result.filesFailed) {
38167
+ const fileName = path62.split("/").pop() ?? path62;
38125
38168
  console.log(`${chalk.red(String.fromCodePoint(10007))} Failed: ${fileName} (${error})`);
38126
38169
  }
38127
38170
  if (result.conversationRestored) {
@@ -38580,10 +38623,10 @@ function getStatusIcon(status) {
38580
38623
  function displaySessionEntry(index, session, isCurrentProject) {
38581
38624
  const timeAgo = formatRelativeTime2(new Date(session.lastSavedAt));
38582
38625
  const tokenStr = formatTokenCount(session.totalTokens);
38583
- const statusIcon = getStatusIcon(session.status);
38626
+ const statusIcon2 = getStatusIcon(session.status);
38584
38627
  console.log();
38585
38628
  console.log(
38586
- `${chalk.yellow(`${index}.`)} ${statusIcon} ${chalk.dim(`[${timeAgo}]`)} ${session.messageCount} messages, ${tokenStr}`
38629
+ `${chalk.yellow(`${index}.`)} ${statusIcon2} ${chalk.dim(`[${timeAgo}]`)} ${session.messageCount} messages, ${tokenStr}`
38587
38630
  );
38588
38631
  if (session.title) {
38589
38632
  console.log(` ${chalk.cyan(`"${session.title}"`)}`);
@@ -38762,8 +38805,8 @@ var STARTUP_TIMEOUT_MS = 5500;
38762
38805
  var CACHE_DIR = path39__default.join(os4__default.homedir(), ".coco");
38763
38806
  var CACHE_FILE = path39__default.join(CACHE_DIR, "version-check-cache.json");
38764
38807
  function compareVersions(a, b) {
38765
- const partsA = a.replace(/^v/, "").split(".").map((p46) => Number(p46.replace(/-.*$/, "")));
38766
- const partsB = b.replace(/^v/, "").split(".").map((p46) => Number(p46.replace(/-.*$/, "")));
38808
+ const partsA = a.replace(/^v/, "").split(".").map((p47) => Number(p47.replace(/-.*$/, "")));
38809
+ const partsB = b.replace(/^v/, "").split(".").map((p47) => Number(p47.replace(/-.*$/, "")));
38767
38810
  for (let i = 0; i < 3; i++) {
38768
38811
  const numA = partsA[i] ?? 0;
38769
38812
  const numB = partsB[i] ?? 0;
@@ -38887,13 +38930,13 @@ async function checkForUpdatesInteractive() {
38887
38930
  ]);
38888
38931
  clearTimeout(startupTimerId);
38889
38932
  if (!updateInfo) return;
38890
- const p46 = await import('@clack/prompts');
38933
+ const p47 = await import('@clack/prompts');
38891
38934
  printUpdateBanner(updateInfo);
38892
- const answer = await p46.confirm({
38935
+ const answer = await p47.confirm({
38893
38936
  message: "Exit now to update?",
38894
38937
  initialValue: true
38895
38938
  });
38896
- if (!p46.isCancel(answer) && answer) {
38939
+ if (!p47.isCancel(answer) && answer) {
38897
38940
  console.log();
38898
38941
  console.log(chalk.dim(` Running: ${updateInfo.updateCommand}`));
38899
38942
  console.log();
@@ -48530,11 +48573,11 @@ var getLearnedPatternsTool = defineTool({
48530
48573
  const patterns = store.getFrequentPatterns(typedInput.limit);
48531
48574
  return {
48532
48575
  totalPatterns: patterns.length,
48533
- patterns: patterns.map((p46) => ({
48534
- pattern: p46.pattern,
48535
- preference: p46.userPreference,
48536
- frequency: p46.frequency,
48537
- lastUsed: new Date(p46.lastUsed).toISOString()
48576
+ patterns: patterns.map((p47) => ({
48577
+ pattern: p47.pattern,
48578
+ preference: p47.userPreference,
48579
+ frequency: p47.frequency,
48580
+ lastUsed: new Date(p47.lastUsed).toISOString()
48538
48581
  }))
48539
48582
  };
48540
48583
  }
@@ -50047,6 +50090,188 @@ var bestOfNCommand = {
50047
50090
  }
50048
50091
  };
50049
50092
 
50093
+ // src/cli/repl/commands/doctor.ts
50094
+ init_paths();
50095
+ init_loader();
50096
+ init_hooks();
50097
+ init_auth();
50098
+ function statusIcon(status) {
50099
+ if (status === "pass") return "PASS";
50100
+ if (status === "warn") return "WARN";
50101
+ return "FAIL";
50102
+ }
50103
+ async function checkProjectAccess(projectPath) {
50104
+ try {
50105
+ await access(projectPath, constants.R_OK | constants.W_OK);
50106
+ return {
50107
+ name: "Project access",
50108
+ status: "pass",
50109
+ detail: `Readable and writable: ${projectPath}`
50110
+ };
50111
+ } catch (error) {
50112
+ return {
50113
+ name: "Project access",
50114
+ status: "fail",
50115
+ detail: error instanceof Error ? error.message : String(error)
50116
+ };
50117
+ }
50118
+ }
50119
+ async function checkConfig(projectPath) {
50120
+ try {
50121
+ const paths = await findAllConfigPaths(projectPath);
50122
+ await loadConfig(path39__default.join(projectPath, ".coco", "config.json"));
50123
+ const found = [paths.project, paths.global].filter(Boolean).length;
50124
+ return {
50125
+ name: "Configuration",
50126
+ status: "pass",
50127
+ detail: found > 0 ? `${found} config file(s) parsed successfully` : "Using built-in defaults"
50128
+ };
50129
+ } catch (error) {
50130
+ return {
50131
+ name: "Configuration",
50132
+ status: "fail",
50133
+ detail: error instanceof Error ? error.message : String(error)
50134
+ };
50135
+ }
50136
+ }
50137
+ async function checkAuth(session) {
50138
+ const provider = session.config.provider.type;
50139
+ if (provider === "anthropic") {
50140
+ return process.env["ANTHROPIC_API_KEY"] ? {
50141
+ name: "Provider auth",
50142
+ status: "pass",
50143
+ detail: "ANTHROPIC_API_KEY detected"
50144
+ } : {
50145
+ name: "Provider auth",
50146
+ status: "warn",
50147
+ detail: "Missing ANTHROPIC_API_KEY for current provider"
50148
+ };
50149
+ }
50150
+ if (provider === "openai" || provider === "codex") {
50151
+ const hasApiKey = !!process.env["OPENAI_API_KEY"];
50152
+ const hasOauth = await isOAuthConfigured("openai").catch(() => false);
50153
+ return hasApiKey || hasOauth ? {
50154
+ name: "Provider auth",
50155
+ status: "pass",
50156
+ detail: hasApiKey ? "OPENAI_API_KEY detected" : "OpenAI OAuth is configured"
50157
+ } : {
50158
+ name: "Provider auth",
50159
+ status: "warn",
50160
+ detail: "Missing OPENAI_API_KEY and no OpenAI OAuth configuration found"
50161
+ };
50162
+ }
50163
+ if (provider === "gemini") {
50164
+ const hasApiKey = !!process.env["GEMINI_API_KEY"] || !!process.env["GOOGLE_API_KEY"];
50165
+ const hasOauth = await isOAuthConfigured("gemini").catch(() => false);
50166
+ const hasAdc = await isADCConfigured().catch(() => false);
50167
+ return hasApiKey || hasOauth || hasAdc ? {
50168
+ name: "Provider auth",
50169
+ status: "pass",
50170
+ detail: hasApiKey ? "Gemini API key detected" : hasAdc ? "Google ADC is configured" : "Gemini OAuth is configured"
50171
+ } : {
50172
+ name: "Provider auth",
50173
+ status: "warn",
50174
+ detail: "Missing Gemini auth (API key, OAuth, or ADC)"
50175
+ };
50176
+ }
50177
+ if (provider === "copilot") {
50178
+ const hasOauth = await isOAuthConfigured("copilot").catch(() => false);
50179
+ return hasOauth ? {
50180
+ name: "Provider auth",
50181
+ status: "pass",
50182
+ detail: "Copilot auth is configured"
50183
+ } : {
50184
+ name: "Provider auth",
50185
+ status: "warn",
50186
+ detail: "Copilot auth not configured"
50187
+ };
50188
+ }
50189
+ return {
50190
+ name: "Provider auth",
50191
+ status: "warn",
50192
+ detail: `No doctor auth rule for provider '${provider}'`
50193
+ };
50194
+ }
50195
+ async function checkHooks(projectPath) {
50196
+ const hooksPath = path39__default.join(projectPath, ".coco", "hooks.json");
50197
+ try {
50198
+ await access(hooksPath, constants.R_OK);
50199
+ } catch {
50200
+ return {
50201
+ name: "Hooks",
50202
+ status: "pass",
50203
+ detail: "No project hooks configured"
50204
+ };
50205
+ }
50206
+ try {
50207
+ const registry = createHookRegistry();
50208
+ await registry.loadFromFile(hooksPath);
50209
+ return {
50210
+ name: "Hooks",
50211
+ status: "pass",
50212
+ detail: `${registry.size} hook(s) loaded from .coco/hooks.json`
50213
+ };
50214
+ } catch (error) {
50215
+ return {
50216
+ name: "Hooks",
50217
+ status: "fail",
50218
+ detail: error instanceof Error ? error.message : String(error)
50219
+ };
50220
+ }
50221
+ }
50222
+ async function checkTooling() {
50223
+ try {
50224
+ const registry = createFullToolRegistry();
50225
+ const defs = registry.getToolDefinitionsForLLM();
50226
+ return {
50227
+ name: "Tool registry",
50228
+ status: defs.length > 0 ? "pass" : "fail",
50229
+ detail: `${defs.length} tool(s) available to the agent`
50230
+ };
50231
+ } catch (error) {
50232
+ return {
50233
+ name: "Tool registry",
50234
+ status: "fail",
50235
+ detail: error instanceof Error ? error.message : String(error)
50236
+ };
50237
+ }
50238
+ }
50239
+ async function runDoctorChecks(session) {
50240
+ return Promise.all([
50241
+ checkProjectAccess(session.projectPath),
50242
+ checkConfig(session.projectPath),
50243
+ checkAuth(session),
50244
+ checkHooks(session.projectPath),
50245
+ checkTooling()
50246
+ ]);
50247
+ }
50248
+ var doctorCommand = {
50249
+ name: "doctor",
50250
+ aliases: ["dr"],
50251
+ description: "Run read-only diagnostics for config, auth, hooks, and tools",
50252
+ usage: "/doctor",
50253
+ async execute(_args, session) {
50254
+ if (!session.config.agent.doctorV2) {
50255
+ p26.log.warn("Doctor v2 is disabled. Set agent.doctorV2=true to enable it.");
50256
+ return false;
50257
+ }
50258
+ p26.intro(chalk.cyan("Coco Doctor"));
50259
+ const checks = await runDoctorChecks(session);
50260
+ const failures = checks.filter((check) => check.status === "fail").length;
50261
+ const warnings = checks.filter((check) => check.status === "warn").length;
50262
+ for (const check of checks) {
50263
+ p26.log.message(`${statusIcon(check.status)} ${check.name}: ${check.detail}`);
50264
+ }
50265
+ p26.log.message("");
50266
+ p26.log.info(
50267
+ `Summary: ${checks.length - failures - warnings} pass, ${warnings} warn, ${failures} fail`
50268
+ );
50269
+ p26.log.message(`Config home: ${CONFIG_PATHS.home}`);
50270
+ p26.outro(failures > 0 ? "Doctor finished with failures" : "Doctor finished");
50271
+ return false;
50272
+ }
50273
+ };
50274
+
50050
50275
  // src/cli/repl/output/renderer.ts
50051
50276
  init_syntax();
50052
50277
  var lineBuffer = "";
@@ -50815,9 +51040,9 @@ function printEditDiff(oldStr, newStr) {
50815
51040
  }
50816
51041
  if (diffLineList.length === 0) return;
50817
51042
  const pairs = pairAdjacentDiffLines(diffLineList);
50818
- const pairedDeletes = new Set(pairs.map((p46) => p46.deleteIdx));
50819
- const pairedAdds = new Set(pairs.map((p46) => p46.addIdx));
50820
- const pairByAdd = new Map(pairs.map((p46) => [p46.addIdx, p46.deleteIdx]));
51043
+ const pairedDeletes = new Set(pairs.map((p47) => p47.deleteIdx));
51044
+ const pairedAdds = new Set(pairs.map((p47) => p47.addIdx));
51045
+ const pairByAdd = new Map(pairs.map((p47) => [p47.addIdx, p47.deleteIdx]));
50821
51046
  const wordHighlights = /* @__PURE__ */ new Map();
50822
51047
  for (const pair of pairs) {
50823
51048
  const del = diffLineList[pair.deleteIdx];
@@ -50897,8 +51122,8 @@ function formatToolSummary(toolName, input) {
50897
51122
  case "grep":
50898
51123
  case "search_files": {
50899
51124
  const pattern = String(input.pattern || "");
50900
- const path60 = input.path ? ` in ${input.path}` : "";
50901
- return `"${pattern}"${path60}`;
51125
+ const path62 = input.path ? ` in ${input.path}` : "";
51126
+ return `"${pattern}"${path62}`;
50902
51127
  }
50903
51128
  case "bash_exec": {
50904
51129
  const cmd = String(input.command || "");
@@ -50919,8 +51144,8 @@ function formatToolSummary(toolName, input) {
50919
51144
  function formatUrl(url) {
50920
51145
  try {
50921
51146
  const u = new URL(url);
50922
- const path60 = u.pathname.replace(/\/$/, "");
50923
- const display = path60 ? `${u.hostname} \u203A ${path60.slice(1)}` : u.hostname;
51147
+ const path62 = u.pathname.replace(/\/$/, "");
51148
+ const display = path62 ? `${u.hostname} \u203A ${path62.slice(1)}` : u.hostname;
50924
51149
  const max = Math.max(getTerminalWidth2() - 20, 50);
50925
51150
  return display.length > max ? display.slice(0, max - 1) + "\u2026" : display;
50926
51151
  } catch {
@@ -51077,7 +51302,8 @@ var commands = [
51077
51302
  fullPowerRiskCommand,
51078
51303
  buildAppCommand,
51079
51304
  contextCommand,
51080
- bestOfNCommand
51305
+ bestOfNCommand,
51306
+ doctorCommand
51081
51307
  ];
51082
51308
  function isSlashCommand(input) {
51083
51309
  return input.startsWith("/");
@@ -51280,8 +51506,8 @@ function getCursorVisualPos(text15, cursorPos, promptLen, termCols) {
51280
51506
  function computeWordWrap(text15, startCol, termCols) {
51281
51507
  const passthrough = {
51282
51508
  display: text15,
51283
- toDisplayPos: (p46) => p46,
51284
- toOrigPos: (p46) => p46
51509
+ toDisplayPos: (p47) => p47,
51510
+ toOrigPos: (p47) => p47
51285
51511
  };
51286
51512
  if (!text15 || termCols <= 1) return passthrough;
51287
51513
  const origToDisp = new Int32Array(text15.length + 1);
@@ -51908,7 +52134,7 @@ function createInputHandler(_session) {
51908
52134
  }
51909
52135
 
51910
52136
  // src/cli/repl/index.ts
51911
- init_types7();
52137
+ init_types8();
51912
52138
  var COCO_SPINNER = {
51913
52139
  interval: 120,
51914
52140
  frames: ["\u{1F965} ", " \u{1F965} ", " \u{1F965} ", " \u{1F965} ", " \u{1F965}", " \u{1F965} ", " \u{1F965} ", " \u{1F965} "]
@@ -52540,16 +52766,16 @@ function formatToolCallForConfirmation(toolCall, metadata) {
52540
52766
  const reason = input.reason ? String(input.reason) : void 0;
52541
52767
  const actionLabel = action === "allow" ? chalk.green.bold("ALLOW") : chalk.red.bold(action.toUpperCase());
52542
52768
  const scopeLabel = scope === "global" ? chalk.blue("Global (all projects)") : chalk.magenta("Project (current only)");
52543
- const patternList = patterns.map((p46) => chalk.cyan(p46)).join(", ");
52769
+ const patternList = patterns.map((p47) => chalk.cyan(p47)).join(", ");
52544
52770
  const lines = [`${actionLabel}: ${patternList}`];
52545
52771
  lines.push(`${chalk.dim(" Scope:")} ${scopeLabel}`);
52546
52772
  if (reason) {
52547
52773
  lines.push(`${chalk.dim(" Reason:")} ${reason}`);
52548
52774
  }
52549
- for (const p46 of patterns) {
52550
- lines.push(`${chalk.dim(" Risk:")} ${getRiskDescription(p46)}`);
52775
+ for (const p47 of patterns) {
52776
+ lines.push(`${chalk.dim(" Risk:")} ${getRiskDescription(p47)}`);
52551
52777
  lines.push(
52552
- `${chalk.dim(" Effect:")} ${getEffectDescription(action, p46, scope)}`
52778
+ `${chalk.dim(" Effect:")} ${getEffectDescription(action, p47, scope)}`
52553
52779
  );
52554
52780
  }
52555
52781
  description = lines.join("\n ");
@@ -53219,7 +53445,9 @@ function computeTurnQualityMetrics(input) {
53219
53445
  successfulToolCalls,
53220
53446
  failedToolCalls,
53221
53447
  hadError: input.hadError,
53222
- repeatedOutputsSuppressed: input.repeatedOutputsSuppressed
53448
+ repeatedOutputsSuppressed: input.repeatedOutputsSuppressed,
53449
+ observedLargeOutputs: input.observedLargeOutputs ?? 0,
53450
+ observedLargeOutputChars: input.observedLargeOutputChars ?? 0
53223
53451
  };
53224
53452
  }
53225
53453
  var RepeatedOutputSuppressor = class {
@@ -53254,13 +53482,20 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
53254
53482
  let finalContent = "";
53255
53483
  let hadTurnError = false;
53256
53484
  let repeatedOutputsSuppressed = 0;
53485
+ let observedLargeOutputs = 0;
53486
+ let observedLargeOutputChars = 0;
53257
53487
  const repeatedOutputSuppressor = new RepeatedOutputSuppressor();
53488
+ const recoveryV2Enabled = session.config.agent.recoveryV2 === true;
53489
+ const strictPlanModeEnabled = session.planMode && session.config.agent.planModeStrict === true;
53490
+ const outputOffloadObservationEnabled = session.config.agent.outputOffload === true;
53258
53491
  const buildQualityMetrics = () => computeTurnQualityMetrics({
53259
53492
  iterationsUsed: iteration,
53260
53493
  maxIterations,
53261
53494
  executedTools,
53262
53495
  hadError: hadTurnError,
53263
- repeatedOutputsSuppressed
53496
+ repeatedOutputsSuppressed,
53497
+ observedLargeOutputs,
53498
+ observedLargeOutputChars
53264
53499
  });
53265
53500
  const abortReturn = () => {
53266
53501
  session.messages.length = messageSnapshot;
@@ -53275,7 +53510,7 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
53275
53510
  };
53276
53511
  };
53277
53512
  const allTools = toolRegistry.getToolDefinitionsForLLM();
53278
- const tools = session.planMode ? filterReadOnlyTools(allTools) : allTools;
53513
+ const tools = session.planMode ? strictPlanModeEnabled ? filterStrictPlanModeTools(allTools) : filterReadOnlyTools(allTools) : allTools;
53279
53514
  const availableMcpToolNames = allTools.map((t) => t.name).filter((name) => name.startsWith("mcp_"));
53280
53515
  function extractPlainText(content) {
53281
53516
  if (typeof content === "string") return content;
@@ -53318,6 +53553,38 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
53318
53553
  const INLINE_RESULT_MAX_CHARS = 8e3;
53319
53554
  const INLINE_RESULT_HEAD_CHARS = 6500;
53320
53555
  const INLINE_RESULT_TAIL_CHARS = 1e3;
53556
+ const OUTPUT_OFFLOAD_OBSERVATION_THRESHOLD = 12e3;
53557
+ const OUTPUT_OFFLOAD_PERSIST_THRESHOLD = 2e4;
53558
+ function shouldRetryStreamError(classification, responseSoFar, toolCallsSoFar, attempt, maxAttempts) {
53559
+ if (!recoveryV2Enabled) return false;
53560
+ if (attempt >= maxAttempts) return false;
53561
+ if (toolCallsSoFar.length > 0) return false;
53562
+ if (responseSoFar.trim().length > 0) return false;
53563
+ return classification.kind === "provider_retryable" || classification.kind === "unexpected";
53564
+ }
53565
+ async function pauseBeforeRetry(attempt) {
53566
+ const delayMs = Math.min(1200, 250 * attempt);
53567
+ await new Promise((resolve4) => setTimeout(resolve4, delayMs));
53568
+ }
53569
+ async function offloadLargeOutput(toolCall, content) {
53570
+ const artifactDir = path39__default.join(session.projectPath, ".coco", "session-artifacts", session.id);
53571
+ const safeToolName = toolCall.name.replace(/[^a-zA-Z0-9_-]/g, "_");
53572
+ const artifactPath = path39__default.join(artifactDir, `${safeToolName}-${toolCall.id}.txt`);
53573
+ const relativeArtifactPath = path39__default.relative(session.projectPath, artifactPath);
53574
+ await mkdir(artifactDir, { recursive: true });
53575
+ await writeFile(artifactPath, content, "utf-8");
53576
+ return {
53577
+ artifactPath,
53578
+ toolResultContent: [
53579
+ `Large tool output stored in local artifact: ${relativeArtifactPath}`,
53580
+ `Original size: ${content.length.toLocaleString()} characters.`,
53581
+ "Use read_file on the artifact path if you need the full output.",
53582
+ "",
53583
+ "Preview:",
53584
+ truncateInlineResult(content, toolCall.name)
53585
+ ].join("\n")
53586
+ };
53587
+ }
53321
53588
  function truncateInlineResult(content, toolName) {
53322
53589
  if (content.length <= INLINE_RESULT_MAX_CHARS) return content;
53323
53590
  const head = content.slice(0, INLINE_RESULT_HEAD_CHARS);
@@ -53445,80 +53712,110 @@ ${tail}`;
53445
53712
  const messages = getConversationContext(session, toolRegistry);
53446
53713
  options.onThinkingStart?.();
53447
53714
  let responseContent = "";
53448
- const collectedToolCalls = [];
53715
+ let collectedToolCalls = [];
53449
53716
  let thinkingEnded = false;
53450
53717
  let lastStopReason;
53451
- const toolCallBuilders = /* @__PURE__ */ new Map();
53718
+ const maxStreamAttempts = recoveryV2Enabled ? 2 : 1;
53452
53719
  try {
53453
- for await (const chunk of provider.streamWithTools(messages, {
53454
- tools,
53455
- maxTokens: session.config.provider.maxTokens,
53456
- signal: options.signal
53457
- })) {
53458
- if (options.signal?.aborted) {
53459
- break;
53460
- }
53720
+ let streamAttempt = 0;
53721
+ while (streamAttempt < maxStreamAttempts) {
53722
+ responseContent = "";
53723
+ collectedToolCalls = [];
53724
+ lastStopReason = void 0;
53725
+ const toolCallBuilders = /* @__PURE__ */ new Map();
53461
53726
  try {
53462
- if (chunk.type === "text" && chunk.text) {
53463
- if (!thinkingEnded) {
53464
- options.onThinkingEnd?.();
53465
- thinkingEnded = true;
53466
- }
53467
- responseContent += chunk.text;
53468
- finalContent += chunk.text;
53469
- iterationTextChunks.push(chunk);
53470
- }
53471
- if (chunk.type === "tool_use_start" && chunk.toolCall) {
53472
- flushLineBuffer();
53473
- if (!thinkingEnded) {
53474
- options.onThinkingEnd?.();
53475
- thinkingEnded = true;
53476
- }
53477
- const id = chunk.toolCall.id ?? `tool_${toolCallBuilders.size}`;
53478
- const toolName = chunk.toolCall.name ?? "";
53479
- toolCallBuilders.set(id, {
53480
- id,
53481
- name: toolName,
53482
- input: {},
53483
- geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
53484
- });
53485
- if (toolName) {
53486
- options.onToolPreparing?.(toolName);
53727
+ for await (const chunk of provider.streamWithTools(messages, {
53728
+ tools,
53729
+ maxTokens: session.config.provider.maxTokens,
53730
+ signal: options.signal
53731
+ })) {
53732
+ if (options.signal?.aborted) {
53733
+ break;
53487
53734
  }
53488
- }
53489
- if (chunk.type === "tool_use_end" && chunk.toolCall) {
53490
- const id = chunk.toolCall.id ?? "";
53491
- const builder = toolCallBuilders.get(id);
53492
- if (builder) {
53493
- const finalToolCall = {
53494
- id: builder.id,
53495
- name: chunk.toolCall.name ?? builder.name,
53496
- input: chunk.toolCall.input ?? builder.input,
53497
- geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature ?? builder.geminiThoughtSignature
53498
- };
53499
- collectedToolCalls.push(finalToolCall);
53500
- } else if (chunk.toolCall.id && chunk.toolCall.name) {
53501
- collectedToolCalls.push({
53502
- id: chunk.toolCall.id,
53503
- name: chunk.toolCall.name,
53504
- input: chunk.toolCall.input ?? {},
53505
- geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
53506
- });
53735
+ try {
53736
+ if (chunk.type === "text" && chunk.text) {
53737
+ if (!thinkingEnded) {
53738
+ options.onThinkingEnd?.();
53739
+ thinkingEnded = true;
53740
+ }
53741
+ responseContent += chunk.text;
53742
+ finalContent += chunk.text;
53743
+ iterationTextChunks.push(chunk);
53744
+ }
53745
+ if (chunk.type === "tool_use_start" && chunk.toolCall) {
53746
+ flushLineBuffer();
53747
+ if (!thinkingEnded) {
53748
+ options.onThinkingEnd?.();
53749
+ thinkingEnded = true;
53750
+ }
53751
+ const id = chunk.toolCall.id ?? `tool_${toolCallBuilders.size}`;
53752
+ const toolName = chunk.toolCall.name ?? "";
53753
+ toolCallBuilders.set(id, {
53754
+ id,
53755
+ name: toolName,
53756
+ input: {},
53757
+ geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
53758
+ });
53759
+ if (toolName) {
53760
+ options.onToolPreparing?.(toolName);
53761
+ }
53762
+ }
53763
+ if (chunk.type === "tool_use_end" && chunk.toolCall) {
53764
+ const id = chunk.toolCall.id ?? "";
53765
+ const builder = toolCallBuilders.get(id);
53766
+ if (builder) {
53767
+ const finalToolCall = {
53768
+ id: builder.id,
53769
+ name: chunk.toolCall.name ?? builder.name,
53770
+ input: chunk.toolCall.input ?? builder.input,
53771
+ geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature ?? builder.geminiThoughtSignature
53772
+ };
53773
+ collectedToolCalls.push(finalToolCall);
53774
+ } else if (chunk.toolCall.id && chunk.toolCall.name) {
53775
+ collectedToolCalls.push({
53776
+ id: chunk.toolCall.id,
53777
+ name: chunk.toolCall.name,
53778
+ input: chunk.toolCall.input ?? {},
53779
+ geminiThoughtSignature: chunk.toolCall.geminiThoughtSignature
53780
+ });
53781
+ }
53782
+ }
53783
+ if (chunk.type === "done") {
53784
+ if (chunk.stopReason) {
53785
+ lastStopReason = chunk.stopReason;
53786
+ }
53787
+ if (!thinkingEnded) {
53788
+ options.onThinkingEnd?.();
53789
+ thinkingEnded = true;
53790
+ }
53791
+ break;
53792
+ }
53793
+ } catch (chunkError) {
53794
+ const errorMsg = chunkError instanceof Error ? chunkError.message : String(chunkError);
53795
+ console.error(`[agent-loop] Error processing chunk: ${errorMsg}`);
53507
53796
  }
53508
53797
  }
53509
- if (chunk.type === "done") {
53510
- if (chunk.stopReason) {
53511
- lastStopReason = chunk.stopReason;
53512
- }
53513
- if (!thinkingEnded) {
53514
- options.onThinkingEnd?.();
53515
- thinkingEnded = true;
53516
- }
53517
- break;
53798
+ break;
53799
+ } catch (streamError) {
53800
+ const classification = classifyAgentLoopError(streamError, options.signal);
53801
+ if (classification.kind === "abort") {
53802
+ throw classification.original;
53803
+ }
53804
+ if (classification.kind === "provider_non_retryable") {
53805
+ throw classification.original;
53806
+ }
53807
+ streamAttempt++;
53808
+ if (shouldRetryStreamError(
53809
+ classification,
53810
+ responseContent,
53811
+ collectedToolCalls,
53812
+ streamAttempt,
53813
+ maxStreamAttempts
53814
+ )) {
53815
+ await pauseBeforeRetry(streamAttempt);
53816
+ continue;
53518
53817
  }
53519
- } catch (chunkError) {
53520
- const errorMsg = chunkError instanceof Error ? chunkError.message : String(chunkError);
53521
- console.error(`[agent-loop] Error processing chunk: ${errorMsg}`);
53818
+ throw classification.original;
53522
53819
  }
53523
53820
  }
53524
53821
  } catch (streamError) {
@@ -53651,6 +53948,14 @@ ${tail}`;
53651
53948
  options.onToolSkipped?.(toolCall, "Use mcp_list_servers instead of coco mcp CLI");
53652
53949
  continue;
53653
53950
  }
53951
+ if (strictPlanModeEnabled && !STRICT_PLAN_MODE_ALLOWED_TOOLS.has(toolCall.name)) {
53952
+ declinedTools.set(
53953
+ toolCall.id,
53954
+ `Blocked by strict plan mode: tool '${toolCall.name}' is not read-only`
53955
+ );
53956
+ options.onToolSkipped?.(toolCall, "Blocked by strict plan mode");
53957
+ continue;
53958
+ }
53654
53959
  const trustPattern = getTrustPattern(toolCall.name, toolCall.input);
53655
53960
  const needsConfirmation = !options.skipConfirmation && !session.trustedTools.has(trustPattern) && requiresConfirmation(toolCall.name, toolCall.input);
53656
53961
  if (needsConfirmation) {
@@ -53735,29 +54040,29 @@ ${tail}`;
53735
54040
  const patterns = executed.input.patterns;
53736
54041
  const scope = executed.input.scope || "project";
53737
54042
  if (Array.isArray(patterns)) {
53738
- for (const p46 of patterns) {
54043
+ for (const p47 of patterns) {
53739
54044
  if (action === "allow") {
53740
- session.trustedTools.add(p46);
54045
+ session.trustedTools.add(p47);
53741
54046
  if (scope === "global") {
53742
- saveTrustedTool(p46, null, true).catch(() => {
54047
+ saveTrustedTool(p47, null, true).catch(() => {
53743
54048
  });
53744
54049
  } else {
53745
- saveTrustedTool(p46, session.projectPath, false).catch(() => {
54050
+ saveTrustedTool(p47, session.projectPath, false).catch(() => {
53746
54051
  });
53747
54052
  }
53748
- removeDeniedTool(p46, session.projectPath).catch(() => {
54053
+ removeDeniedTool(p47, session.projectPath).catch(() => {
53749
54054
  });
53750
54055
  } else if (action === "deny") {
53751
- session.trustedTools.delete(p46);
54056
+ session.trustedTools.delete(p47);
53752
54057
  if (scope === "global") {
53753
- removeTrustedTool(p46, session.projectPath, true).catch(() => {
54058
+ removeTrustedTool(p47, session.projectPath, true).catch(() => {
53754
54059
  });
53755
54060
  } else {
53756
- saveDeniedTool(p46, session.projectPath).catch(() => {
54061
+ saveDeniedTool(p47, session.projectPath).catch(() => {
53757
54062
  });
53758
54063
  }
53759
54064
  } else {
53760
- session.trustedTools.delete(p46);
54065
+ session.trustedTools.delete(p47);
53761
54066
  }
53762
54067
  }
53763
54068
  }
@@ -53803,10 +54108,21 @@ ${tail}`;
53803
54108
  }
53804
54109
  const executedCall = executedTools.find((e) => e.id === toolCall.id);
53805
54110
  if (executedCall) {
53806
- const truncatedOutput = truncateInlineResult(executedCall.result.output, toolCall.name);
54111
+ if (outputOffloadObservationEnabled && executedCall.result.output.length > OUTPUT_OFFLOAD_OBSERVATION_THRESHOLD) {
54112
+ observedLargeOutputs++;
54113
+ observedLargeOutputChars += executedCall.result.output.length;
54114
+ }
54115
+ let toolResultContent = truncateInlineResult(executedCall.result.output, toolCall.name);
54116
+ if (outputOffloadObservationEnabled && executedCall.result.success && executedCall.result.output.length > OUTPUT_OFFLOAD_PERSIST_THRESHOLD) {
54117
+ try {
54118
+ const offloaded = await offloadLargeOutput(toolCall, executedCall.result.output);
54119
+ toolResultContent = offloaded.toolResultContent;
54120
+ } catch {
54121
+ }
54122
+ }
53807
54123
  const transformedOutput = repeatedOutputSuppressor.transform(
53808
54124
  toolCall.name,
53809
- truncatedOutput
54125
+ toolResultContent
53810
54126
  );
53811
54127
  if (transformedOutput.suppressed) {
53812
54128
  repeatedOutputsSuppressed++;
@@ -54054,9 +54370,30 @@ var PLAN_MODE_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
54054
54370
  "spawnSimpleAgent",
54055
54371
  "checkAgentCapability"
54056
54372
  ]);
54373
+ var STRICT_PLAN_MODE_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
54374
+ "glob",
54375
+ "read_file",
54376
+ "list_dir",
54377
+ "tree",
54378
+ "grep",
54379
+ "find_in_file",
54380
+ "semantic_search",
54381
+ "codebase_map",
54382
+ "git_status",
54383
+ "git_log",
54384
+ "git_diff",
54385
+ "git_show",
54386
+ "git_branch",
54387
+ "recall_memory",
54388
+ "list_memories",
54389
+ "list_checkpoints"
54390
+ ]);
54057
54391
  function filterReadOnlyTools(tools) {
54058
54392
  return tools.filter((tool) => PLAN_MODE_ALLOWED_TOOLS.has(tool.name));
54059
54393
  }
54394
+ function filterStrictPlanModeTools(tools) {
54395
+ return tools.filter((tool) => STRICT_PLAN_MODE_ALLOWED_TOOLS.has(tool.name));
54396
+ }
54060
54397
 
54061
54398
  // src/cli/repl/index.ts
54062
54399
  init_error_resilience();
@@ -54836,8 +55173,8 @@ async function startRepl(options = {}) {
54836
55173
  };
54837
55174
  const getAutoSwitchCandidates = (current) => {
54838
55175
  const ordered = [];
54839
- const push = (p46) => {
54840
- if (p46 !== current && !ordered.includes(p46)) ordered.push(p46);
55176
+ const push = (p47) => {
55177
+ if (p47 !== current && !ordered.includes(p47)) ordered.push(p47);
54841
55178
  };
54842
55179
  if (current === "openai") {
54843
55180
  push("codex");
@@ -54875,7 +55212,7 @@ async function startRepl(options = {}) {
54875
55212
  "lmstudio",
54876
55213
  "ollama"
54877
55214
  ];
54878
- for (const p46 of genericOrder) push(p46);
55215
+ for (const p47 of genericOrder) push(p47);
54879
55216
  return ordered;
54880
55217
  };
54881
55218
  const attemptAutoProviderSwitch = async (reason, originalMessage) => {