@corbat-tech/coco 1.2.0 → 1.2.3

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
@@ -4,13 +4,13 @@ import { homedir } from 'os';
4
4
  import * as path20 from 'path';
5
5
  import path20__default, { join, dirname, basename } from 'path';
6
6
  import * as fs19 from 'fs';
7
- import fs19__default, { readFileSync, existsSync, constants } from 'fs';
7
+ import fs19__default, { readFileSync, constants } from 'fs';
8
8
  import * as fs22 from 'fs/promises';
9
9
  import fs22__default, { writeFile, access, readFile, mkdir, readdir, rm } from 'fs/promises';
10
+ import chalk12 from 'chalk';
11
+ import * as p9 from '@clack/prompts';
10
12
  import { Command } from 'commander';
11
13
  import { fileURLToPath, URL as URL$1 } from 'url';
12
- import * as p9 from '@clack/prompts';
13
- import chalk12 from 'chalk';
14
14
  import JSON5 from 'json5';
15
15
  import { z } from 'zod';
16
16
  import * as crypto from 'crypto';
@@ -21,6 +21,7 @@ import * as http from 'http';
21
21
  import { execFile, exec, execSync, execFileSync, spawn } from 'child_process';
22
22
  import { promisify } from 'util';
23
23
  import { GoogleGenerativeAI, FunctionCallingMode } from '@google/generative-ai';
24
+ import stringWidth from 'string-width';
24
25
  import ansiEscapes3 from 'ansi-escapes';
25
26
  import * as readline from 'readline';
26
27
  import { execa } from 'execa';
@@ -45,6 +46,8 @@ import { glob } from 'glob';
45
46
  import { simpleGit } from 'simple-git';
46
47
  import { parse } from '@typescript-eslint/typescript-estree';
47
48
  import 'events';
49
+ import { Marked } from 'marked';
50
+ import { markedTerminal } from 'marked-terminal';
48
51
 
49
52
  var __defProp = Object.defineProperty;
50
53
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -279,8 +282,8 @@ __export(trust_store_exports, {
279
282
  saveTrustStore: () => saveTrustStore,
280
283
  updateLastAccessed: () => updateLastAccessed
281
284
  });
282
- async function ensureDir(path37) {
283
- await mkdir(dirname(path37), { recursive: true });
285
+ async function ensureDir(path39) {
286
+ await mkdir(dirname(path39), { recursive: true });
284
287
  }
285
288
  async function loadTrustStore(storePath = TRUST_STORE_PATH) {
286
289
  try {
@@ -358,8 +361,8 @@ function canPerformOperation(store, projectPath, operation) {
358
361
  };
359
362
  return permissions[level]?.includes(operation) ?? false;
360
363
  }
361
- function normalizePath(path37) {
362
- return join(path37);
364
+ function normalizePath(path39) {
365
+ return join(path39);
363
366
  }
364
367
  function createTrustStore(storePath = TRUST_STORE_PATH) {
365
368
  let store = null;
@@ -451,6 +454,149 @@ var init_trust_store = __esm({
451
454
  TRUST_STORE_PATH = CONFIG_PATHS.projects;
452
455
  }
453
456
  });
457
+ function getAllowedPaths() {
458
+ return [...sessionAllowedPaths];
459
+ }
460
+ function isWithinAllowedPath(absolutePath, operation) {
461
+ const normalizedTarget = path20__default.normalize(absolutePath);
462
+ for (const entry of sessionAllowedPaths) {
463
+ const normalizedAllowed = path20__default.normalize(entry.path);
464
+ if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path20__default.sep)) {
465
+ if (operation === "read") return true;
466
+ if (entry.level === "write") return true;
467
+ }
468
+ }
469
+ return false;
470
+ }
471
+ function addAllowedPathToSession(dirPath, level) {
472
+ const absolute = path20__default.resolve(dirPath);
473
+ if (sessionAllowedPaths.some((e) => path20__default.normalize(e.path) === path20__default.normalize(absolute))) {
474
+ return;
475
+ }
476
+ sessionAllowedPaths.push({
477
+ path: absolute,
478
+ authorizedAt: (/* @__PURE__ */ new Date()).toISOString(),
479
+ level
480
+ });
481
+ }
482
+ function removeAllowedPathFromSession(dirPath) {
483
+ const absolute = path20__default.resolve(dirPath);
484
+ const normalized = path20__default.normalize(absolute);
485
+ const before = sessionAllowedPaths.length;
486
+ sessionAllowedPaths = sessionAllowedPaths.filter((e) => path20__default.normalize(e.path) !== normalized);
487
+ return sessionAllowedPaths.length < before;
488
+ }
489
+ async function loadAllowedPaths(projectPath) {
490
+ currentProjectPath = path20__default.resolve(projectPath);
491
+ const store = await loadStore();
492
+ const entries = store.projects[currentProjectPath] ?? [];
493
+ for (const entry of entries) {
494
+ addAllowedPathToSession(entry.path, entry.level);
495
+ }
496
+ }
497
+ async function persistAllowedPath(dirPath, level) {
498
+ if (!currentProjectPath) return;
499
+ const absolute = path20__default.resolve(dirPath);
500
+ const store = await loadStore();
501
+ if (!store.projects[currentProjectPath]) {
502
+ store.projects[currentProjectPath] = [];
503
+ }
504
+ const entries = store.projects[currentProjectPath];
505
+ const normalized = path20__default.normalize(absolute);
506
+ if (entries.some((e) => path20__default.normalize(e.path) === normalized)) {
507
+ return;
508
+ }
509
+ entries.push({
510
+ path: absolute,
511
+ authorizedAt: (/* @__PURE__ */ new Date()).toISOString(),
512
+ level
513
+ });
514
+ await saveStore(store);
515
+ }
516
+ async function removePersistedAllowedPath(dirPath) {
517
+ if (!currentProjectPath) return false;
518
+ const absolute = path20__default.resolve(dirPath);
519
+ const normalized = path20__default.normalize(absolute);
520
+ const store = await loadStore();
521
+ const entries = store.projects[currentProjectPath];
522
+ if (!entries) return false;
523
+ const before = entries.length;
524
+ store.projects[currentProjectPath] = entries.filter((e) => path20__default.normalize(e.path) !== normalized);
525
+ if (store.projects[currentProjectPath].length < before) {
526
+ await saveStore(store);
527
+ return true;
528
+ }
529
+ return false;
530
+ }
531
+ async function loadStore() {
532
+ try {
533
+ const content = await fs22__default.readFile(STORE_FILE, "utf-8");
534
+ return { ...DEFAULT_STORE, ...JSON.parse(content) };
535
+ } catch {
536
+ return { ...DEFAULT_STORE };
537
+ }
538
+ }
539
+ async function saveStore(store) {
540
+ try {
541
+ await fs22__default.mkdir(path20__default.dirname(STORE_FILE), { recursive: true });
542
+ await fs22__default.writeFile(STORE_FILE, JSON.stringify(store, null, 2), "utf-8");
543
+ } catch {
544
+ }
545
+ }
546
+ var STORE_FILE, DEFAULT_STORE, sessionAllowedPaths, currentProjectPath;
547
+ var init_allowed_paths = __esm({
548
+ "src/tools/allowed-paths.ts"() {
549
+ init_paths();
550
+ STORE_FILE = path20__default.join(CONFIG_PATHS.home, "allowed-paths.json");
551
+ DEFAULT_STORE = {
552
+ version: 1,
553
+ projects: {}
554
+ };
555
+ sessionAllowedPaths = [];
556
+ currentProjectPath = "";
557
+ }
558
+ });
559
+
560
+ // src/cli/repl/allow-path-prompt.ts
561
+ var allow_path_prompt_exports = {};
562
+ __export(allow_path_prompt_exports, {
563
+ promptAllowPath: () => promptAllowPath
564
+ });
565
+ async function promptAllowPath(dirPath) {
566
+ const absolute = path20__default.resolve(dirPath);
567
+ console.log();
568
+ console.log(chalk12.yellow(" \u26A0 Access denied \u2014 path is outside the project directory"));
569
+ console.log(chalk12.dim(` \u{1F4C1} ${absolute}`));
570
+ console.log();
571
+ const action = await p9.select({
572
+ message: "Grant access to this directory?",
573
+ options: [
574
+ { value: "session-write", label: "\u2713 Allow write (this session)" },
575
+ { value: "session-read", label: "\u25D0 Allow read-only (this session)" },
576
+ { value: "persist-write", label: "\u26A1 Allow write (remember for this project)" },
577
+ { value: "persist-read", label: "\u{1F4BE} Allow read-only (remember for this project)" },
578
+ { value: "no", label: "\u2717 Deny" }
579
+ ]
580
+ });
581
+ if (p9.isCancel(action) || action === "no") {
582
+ return false;
583
+ }
584
+ const level = action.includes("read") ? "read" : "write";
585
+ const persist = action.startsWith("persist");
586
+ addAllowedPathToSession(absolute, level);
587
+ if (persist) {
588
+ await persistAllowedPath(absolute, level);
589
+ }
590
+ const levelLabel = level === "write" ? "write" : "read-only";
591
+ const persistLabel = persist ? " (remembered)" : "";
592
+ console.log(chalk12.green(` \u2713 Access granted: ${levelLabel}${persistLabel}`));
593
+ return true;
594
+ }
595
+ var init_allow_path_prompt = __esm({
596
+ "src/cli/repl/allow-path-prompt.ts"() {
597
+ init_allowed_paths();
598
+ }
599
+ });
454
600
  function findPackageJson() {
455
601
  let dir = dirname(fileURLToPath(import.meta.url));
456
602
  for (let i = 0; i < 10; i++) {
@@ -609,8 +755,8 @@ Generated by Corbat-Coco v0.1.0
609
755
 
610
756
  // src/cli/commands/init.ts
611
757
  function registerInitCommand(program2) {
612
- 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 (path37, options) => {
613
- await runInit(path37, options);
758
+ 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 (path39, options) => {
759
+ await runInit(path39, options);
614
760
  });
615
761
  }
616
762
  async function runInit(projectPath, options) {
@@ -689,18 +835,18 @@ async function gatherProjectInfo() {
689
835
  language
690
836
  };
691
837
  }
692
- function getDefaultProjectInfo(path37) {
693
- const name = path37 === "." ? "my-project" : path37.split("/").pop() || "my-project";
838
+ function getDefaultProjectInfo(path39) {
839
+ const name = path39 === "." ? "my-project" : path39.split("/").pop() || "my-project";
694
840
  return {
695
841
  name,
696
842
  description: "",
697
843
  language: "typescript"
698
844
  };
699
845
  }
700
- async function checkExistingProject(path37) {
846
+ async function checkExistingProject(path39) {
701
847
  try {
702
- const fs39 = await import('fs/promises');
703
- await fs39.access(`${path37}/.coco`);
848
+ const fs41 = await import('fs/promises');
849
+ await fs41.access(`${path39}/.coco`);
704
850
  return true;
705
851
  } catch {
706
852
  return false;
@@ -7648,20 +7794,20 @@ async function createCliPhaseContext(projectPath, _onUserInput) {
7648
7794
  },
7649
7795
  tools: {
7650
7796
  file: {
7651
- async read(path37) {
7652
- const fs39 = await import('fs/promises');
7653
- return fs39.readFile(path37, "utf-8");
7797
+ async read(path39) {
7798
+ const fs41 = await import('fs/promises');
7799
+ return fs41.readFile(path39, "utf-8");
7654
7800
  },
7655
- async write(path37, content) {
7656
- const fs39 = await import('fs/promises');
7801
+ async write(path39, content) {
7802
+ const fs41 = await import('fs/promises');
7657
7803
  const nodePath = await import('path');
7658
- await fs39.mkdir(nodePath.dirname(path37), { recursive: true });
7659
- await fs39.writeFile(path37, content, "utf-8");
7804
+ await fs41.mkdir(nodePath.dirname(path39), { recursive: true });
7805
+ await fs41.writeFile(path39, content, "utf-8");
7660
7806
  },
7661
- async exists(path37) {
7662
- const fs39 = await import('fs/promises');
7807
+ async exists(path39) {
7808
+ const fs41 = await import('fs/promises');
7663
7809
  try {
7664
- await fs39.access(path37);
7810
+ await fs41.access(path39);
7665
7811
  return true;
7666
7812
  } catch {
7667
7813
  return false;
@@ -7900,16 +8046,16 @@ async function loadTasks(_options) {
7900
8046
  ];
7901
8047
  }
7902
8048
  async function checkProjectState() {
7903
- const fs39 = await import('fs/promises');
8049
+ const fs41 = await import('fs/promises');
7904
8050
  let hasProject = false;
7905
8051
  let hasPlan = false;
7906
8052
  try {
7907
- await fs39.access(".coco");
8053
+ await fs41.access(".coco");
7908
8054
  hasProject = true;
7909
8055
  } catch {
7910
8056
  }
7911
8057
  try {
7912
- await fs39.access(".coco/planning/backlog.json");
8058
+ await fs41.access(".coco/planning/backlog.json");
7913
8059
  hasPlan = true;
7914
8060
  } catch {
7915
8061
  }
@@ -8013,24 +8159,24 @@ function getPhaseStatusForPhase(phase) {
8013
8159
  return "in_progress";
8014
8160
  }
8015
8161
  async function loadProjectState(cwd, config) {
8016
- const fs39 = await import('fs/promises');
8017
- const path37 = await import('path');
8018
- const statePath = path37.join(cwd, ".coco", "state.json");
8019
- const backlogPath = path37.join(cwd, ".coco", "planning", "backlog.json");
8020
- const checkpointDir = path37.join(cwd, ".coco", "checkpoints");
8162
+ const fs41 = await import('fs/promises');
8163
+ const path39 = await import('path');
8164
+ const statePath = path39.join(cwd, ".coco", "state.json");
8165
+ const backlogPath = path39.join(cwd, ".coco", "planning", "backlog.json");
8166
+ const checkpointDir = path39.join(cwd, ".coco", "checkpoints");
8021
8167
  let currentPhase = "idle";
8022
8168
  let metrics;
8023
8169
  let sprint;
8024
8170
  let checkpoints = [];
8025
8171
  try {
8026
- const stateContent = await fs39.readFile(statePath, "utf-8");
8172
+ const stateContent = await fs41.readFile(statePath, "utf-8");
8027
8173
  const stateData = JSON.parse(stateContent);
8028
8174
  currentPhase = stateData.currentPhase || "idle";
8029
8175
  metrics = stateData.metrics;
8030
8176
  } catch {
8031
8177
  }
8032
8178
  try {
8033
- const backlogContent = await fs39.readFile(backlogPath, "utf-8");
8179
+ const backlogContent = await fs41.readFile(backlogPath, "utf-8");
8034
8180
  const backlogData = JSON.parse(backlogContent);
8035
8181
  if (backlogData.currentSprint) {
8036
8182
  const tasks = backlogData.tasks || [];
@@ -8052,7 +8198,7 @@ async function loadProjectState(cwd, config) {
8052
8198
  } catch {
8053
8199
  }
8054
8200
  try {
8055
- const files = await fs39.readdir(checkpointDir);
8201
+ const files = await fs41.readdir(checkpointDir);
8056
8202
  checkpoints = files.filter((f) => f.endsWith(".json")).sort().reverse();
8057
8203
  } catch {
8058
8204
  }
@@ -8188,8 +8334,8 @@ async function restoreFromCheckpoint(_checkpoint) {
8188
8334
  }
8189
8335
  async function checkProjectExists() {
8190
8336
  try {
8191
- const fs39 = await import('fs/promises');
8192
- await fs39.access(".coco");
8337
+ const fs41 = await import('fs/promises');
8338
+ await fs41.access(".coco");
8193
8339
  return true;
8194
8340
  } catch {
8195
8341
  return false;
@@ -8728,12 +8874,12 @@ async function loadConfig2() {
8728
8874
  };
8729
8875
  }
8730
8876
  async function saveConfig(config) {
8731
- const fs39 = await import('fs/promises');
8732
- await fs39.mkdir(".coco", { recursive: true });
8733
- await fs39.writeFile(".coco/config.json", JSON.stringify(config, null, 2));
8877
+ const fs41 = await import('fs/promises');
8878
+ await fs41.mkdir(".coco", { recursive: true });
8879
+ await fs41.writeFile(".coco/config.json", JSON.stringify(config, null, 2));
8734
8880
  }
8735
- function getNestedValue(obj, path37) {
8736
- const keys = path37.split(".");
8881
+ function getNestedValue(obj, path39) {
8882
+ const keys = path39.split(".");
8737
8883
  let current = obj;
8738
8884
  for (const key of keys) {
8739
8885
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -8743,8 +8889,8 @@ function getNestedValue(obj, path37) {
8743
8889
  }
8744
8890
  return current;
8745
8891
  }
8746
- function setNestedValue(obj, path37, value) {
8747
- const keys = path37.split(".");
8892
+ function setNestedValue(obj, path39, value) {
8893
+ const keys = path39.split(".");
8748
8894
  let current = obj;
8749
8895
  for (let i = 0; i < keys.length - 1; i++) {
8750
8896
  const key = keys[i];
@@ -8965,8 +9111,8 @@ var MCPRegistryImpl = class {
8965
9111
  /**
8966
9112
  * Ensure directory exists
8967
9113
  */
8968
- async ensureDir(path37) {
8969
- await mkdir(dirname(path37), { recursive: true });
9114
+ async ensureDir(path39) {
9115
+ await mkdir(dirname(path39), { recursive: true });
8970
9116
  }
8971
9117
  };
8972
9118
  function createMCPRegistry(registryPath) {
@@ -9485,9 +9631,9 @@ function createEmptyMemoryContext() {
9485
9631
  errors: []
9486
9632
  };
9487
9633
  }
9488
- function createMissingMemoryFile(path37, level) {
9634
+ function createMissingMemoryFile(path39, level) {
9489
9635
  return {
9490
- path: path37,
9636
+ path: path39,
9491
9637
  level,
9492
9638
  content: "",
9493
9639
  sections: [],
@@ -9571,10 +9717,12 @@ When the user asks you to do something, follow this process:
9571
9717
  - Always verify your work by reading files after editing or running tests after changes
9572
9718
  - You can use multiple tools together for complex tasks
9573
9719
  - When uncertain which tool to use, check the full list above
9720
+ - **Never** add "Co-Authored-By", "Generated by", or any AI attribution to commits, code comments, documentation, or PR descriptions. All output must read as if written by the developer.
9574
9721
 
9575
9722
  ## File Access
9576
9723
  File operations are restricted to the project directory by default.
9577
- If a tool fails with "outside project directory", tell the user to run \`/allow-path <directory>\` to grant access to that directory. Do NOT retry the operation until the user has granted access.
9724
+ When you need to access a path outside the project, use the **authorize_path** tool first \u2014 it will ask the user for permission interactively. Once authorized, proceed with the file operation.
9725
+ If a file tool fails with "outside project directory", the system will automatically prompt the user to authorize the path and retry. You do NOT need to tell the user to run any command manually.
9578
9726
 
9579
9727
  ## Output Formatting Rules
9580
9728
 
@@ -12178,8 +12326,8 @@ async function listTrustedProjects2(trustStore) {
12178
12326
  p9.log.message("");
12179
12327
  for (const project of projects) {
12180
12328
  const level = project.approvalLevel.toUpperCase().padEnd(5);
12181
- const path37 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
12182
- p9.log.message(` [${level}] ${path37}`);
12329
+ const path39 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
12330
+ p9.log.message(` [${level}] ${path39}`);
12183
12331
  p9.log.message(` Last accessed: ${new Date(project.lastAccessed).toLocaleString()}`);
12184
12332
  }
12185
12333
  p9.log.message("");
@@ -13349,7 +13497,7 @@ async function getCheckpoint(session, checkpointId) {
13349
13497
  return store.checkpoints.find((cp) => cp.id === checkpointId) ?? null;
13350
13498
  }
13351
13499
  async function restoreFiles(checkpoint, excludeFiles) {
13352
- const fs39 = await import('fs/promises');
13500
+ const fs41 = await import('fs/promises');
13353
13501
  const restored = [];
13354
13502
  const failed = [];
13355
13503
  for (const fileCheckpoint of checkpoint.files) {
@@ -13357,7 +13505,7 @@ async function restoreFiles(checkpoint, excludeFiles) {
13357
13505
  continue;
13358
13506
  }
13359
13507
  try {
13360
- await fs39.writeFile(fileCheckpoint.filePath, fileCheckpoint.originalContent, "utf-8");
13508
+ await fs41.writeFile(fileCheckpoint.filePath, fileCheckpoint.originalContent, "utf-8");
13361
13509
  restored.push(fileCheckpoint.filePath);
13362
13510
  } catch (error) {
13363
13511
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -13439,8 +13587,8 @@ function displayRewindResult(result) {
13439
13587
  const fileName = filePath.split("/").pop() ?? filePath;
13440
13588
  console.log(`${chalk12.green(String.fromCodePoint(10003))} Restored: ${fileName}`);
13441
13589
  }
13442
- for (const { path: path37, error } of result.filesFailed) {
13443
- const fileName = path37.split("/").pop() ?? path37;
13590
+ for (const { path: path39, error } of result.filesFailed) {
13591
+ const fileName = path39.split("/").pop() ?? path39;
13444
13592
  console.log(`${chalk12.red(String.fromCodePoint(10007))} Failed: ${fileName} (${error})`);
13445
13593
  }
13446
13594
  if (result.conversationRestored) {
@@ -14873,8 +15021,8 @@ function formatToolSummary(toolName, input) {
14873
15021
  return String(input.path || ".");
14874
15022
  case "search_files": {
14875
15023
  const pattern = String(input.pattern || "");
14876
- const path37 = input.path ? ` in ${input.path}` : "";
14877
- return `"${pattern}"${path37}`;
15024
+ const path39 = input.path ? ` in ${input.path}` : "";
15025
+ return `"${pattern}"${path39}`;
14878
15026
  }
14879
15027
  case "bash_exec": {
14880
15028
  const cmd = String(input.command || "");
@@ -15176,106 +15324,8 @@ var copyCommand = {
15176
15324
  }
15177
15325
  };
15178
15326
 
15179
- // src/tools/allowed-paths.ts
15180
- init_paths();
15181
- var STORE_FILE = path20__default.join(CONFIG_PATHS.home, "allowed-paths.json");
15182
- var DEFAULT_STORE = {
15183
- version: 1,
15184
- projects: {}
15185
- };
15186
- var sessionAllowedPaths = [];
15187
- var currentProjectPath = "";
15188
- function getAllowedPaths() {
15189
- return [...sessionAllowedPaths];
15190
- }
15191
- function isWithinAllowedPath(absolutePath, operation) {
15192
- const normalizedTarget = path20__default.normalize(absolutePath);
15193
- for (const entry of sessionAllowedPaths) {
15194
- const normalizedAllowed = path20__default.normalize(entry.path);
15195
- if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path20__default.sep)) {
15196
- if (operation === "read") return true;
15197
- if (entry.level === "write") return true;
15198
- }
15199
- }
15200
- return false;
15201
- }
15202
- function addAllowedPathToSession(dirPath, level) {
15203
- const absolute = path20__default.resolve(dirPath);
15204
- if (sessionAllowedPaths.some((e) => path20__default.normalize(e.path) === path20__default.normalize(absolute))) {
15205
- return;
15206
- }
15207
- sessionAllowedPaths.push({
15208
- path: absolute,
15209
- authorizedAt: (/* @__PURE__ */ new Date()).toISOString(),
15210
- level
15211
- });
15212
- }
15213
- function removeAllowedPathFromSession(dirPath) {
15214
- const absolute = path20__default.resolve(dirPath);
15215
- const normalized = path20__default.normalize(absolute);
15216
- const before = sessionAllowedPaths.length;
15217
- sessionAllowedPaths = sessionAllowedPaths.filter((e) => path20__default.normalize(e.path) !== normalized);
15218
- return sessionAllowedPaths.length < before;
15219
- }
15220
- async function loadAllowedPaths(projectPath) {
15221
- currentProjectPath = path20__default.resolve(projectPath);
15222
- const store = await loadStore();
15223
- const entries = store.projects[currentProjectPath] ?? [];
15224
- for (const entry of entries) {
15225
- addAllowedPathToSession(entry.path, entry.level);
15226
- }
15227
- }
15228
- async function persistAllowedPath(dirPath, level) {
15229
- if (!currentProjectPath) return;
15230
- const absolute = path20__default.resolve(dirPath);
15231
- const store = await loadStore();
15232
- if (!store.projects[currentProjectPath]) {
15233
- store.projects[currentProjectPath] = [];
15234
- }
15235
- const entries = store.projects[currentProjectPath];
15236
- const normalized = path20__default.normalize(absolute);
15237
- if (entries.some((e) => path20__default.normalize(e.path) === normalized)) {
15238
- return;
15239
- }
15240
- entries.push({
15241
- path: absolute,
15242
- authorizedAt: (/* @__PURE__ */ new Date()).toISOString(),
15243
- level
15244
- });
15245
- await saveStore(store);
15246
- }
15247
- async function removePersistedAllowedPath(dirPath) {
15248
- if (!currentProjectPath) return false;
15249
- const absolute = path20__default.resolve(dirPath);
15250
- const normalized = path20__default.normalize(absolute);
15251
- const store = await loadStore();
15252
- const entries = store.projects[currentProjectPath];
15253
- if (!entries) return false;
15254
- const before = entries.length;
15255
- store.projects[currentProjectPath] = entries.filter((e) => path20__default.normalize(e.path) !== normalized);
15256
- if (store.projects[currentProjectPath].length < before) {
15257
- await saveStore(store);
15258
- return true;
15259
- }
15260
- return false;
15261
- }
15262
- async function loadStore() {
15263
- try {
15264
- const content = await fs22__default.readFile(STORE_FILE, "utf-8");
15265
- return { ...DEFAULT_STORE, ...JSON.parse(content) };
15266
- } catch {
15267
- return { ...DEFAULT_STORE };
15268
- }
15269
- }
15270
- async function saveStore(store) {
15271
- try {
15272
- await fs22__default.mkdir(path20__default.dirname(STORE_FILE), { recursive: true });
15273
- await fs22__default.writeFile(STORE_FILE, JSON.stringify(store, null, 2), "utf-8");
15274
- } catch {
15275
- }
15276
- }
15277
-
15278
15327
  // src/cli/repl/commands/allow-path.ts
15328
+ init_allowed_paths();
15279
15329
  var BLOCKED_SYSTEM_PATHS = [
15280
15330
  "/etc",
15281
15331
  "/var",
@@ -16253,6 +16303,7 @@ function createInputHandler(_session) {
16253
16303
  let tempLine = "";
16254
16304
  let lastMenuLines = 0;
16255
16305
  let lastCursorRow = 0;
16306
+ let lastContentRows = 1;
16256
16307
  let isFirstRender = true;
16257
16308
  let isPasting = false;
16258
16309
  let pasteBuffer = "";
@@ -16304,7 +16355,12 @@ function createInputHandler(_session) {
16304
16355
  output += chalk12.dim.gray(ghost);
16305
16356
  }
16306
16357
  }
16358
+ const totalContentLen = prompt.visualLen + currentLine.length;
16359
+ const contentRows = totalContentLen === 0 ? 1 : Math.ceil(totalContentLen / termCols);
16360
+ const contentExactFill = totalContentLen > 0 && totalContentLen % termCols === 0;
16361
+ output += (contentExactFill ? "" : "\n") + separator;
16307
16362
  const showMenu = completions.length > 0 && currentLine.startsWith("/") && currentLine.length >= 1;
16363
+ let extraLinesBelow = 0;
16308
16364
  if (showMenu) {
16309
16365
  const cols = getColumnCount();
16310
16366
  const maxVisibleItems = MAX_ROWS * cols;
@@ -16323,9 +16379,11 @@ function createInputHandler(_session) {
16323
16379
  output += "\n";
16324
16380
  output += chalk12.dim(` \u2191 ${startIndex} more above`);
16325
16381
  lastMenuLines++;
16382
+ extraLinesBelow++;
16326
16383
  }
16327
16384
  for (let row = 0; row < rowCount; row++) {
16328
16385
  output += "\n";
16386
+ extraLinesBelow++;
16329
16387
  for (let col = 0; col < cols; col++) {
16330
16388
  const itemIndex = row * cols + col;
16331
16389
  if (itemIndex >= visibleItems.length) break;
@@ -16344,22 +16402,22 @@ function createInputHandler(_session) {
16344
16402
  output += "\n";
16345
16403
  output += chalk12.dim(` \u2193 ${completions.length - endIndex} more below`);
16346
16404
  lastMenuLines++;
16405
+ extraLinesBelow++;
16347
16406
  }
16348
- for (let i = 0; i < BOTTOM_MARGIN; i++) {
16349
- output += "\n";
16350
- }
16351
- output += ansiEscapes3.cursorUp(lastMenuLines + BOTTOM_MARGIN + 1);
16352
16407
  } else {
16353
16408
  lastMenuLines = 0;
16354
- for (let i = 0; i < BOTTOM_MARGIN; i++) {
16355
- output += "\n";
16356
- }
16357
- output += ansiEscapes3.cursorUp(BOTTOM_MARGIN + 1);
16358
16409
  }
16410
+ for (let i = 0; i < BOTTOM_MARGIN; i++) {
16411
+ output += "\n";
16412
+ extraLinesBelow++;
16413
+ }
16414
+ const totalUp = extraLinesBelow + 1 + contentRows;
16415
+ output += ansiEscapes3.cursorUp(totalUp);
16359
16416
  output += ansiEscapes3.cursorDown(1);
16360
16417
  const cursorAbsolutePos = prompt.visualLen + cursorPos;
16361
- const finalLine = Math.floor(cursorAbsolutePos / termCols);
16362
- const finalCol = cursorAbsolutePos % termCols;
16418
+ const onExactBoundary = cursorAbsolutePos > 0 && cursorAbsolutePos % termCols === 0;
16419
+ const finalLine = onExactBoundary ? cursorAbsolutePos / termCols - 1 : Math.floor(cursorAbsolutePos / termCols);
16420
+ const finalCol = onExactBoundary ? 0 : cursorAbsolutePos % termCols;
16363
16421
  output += "\r";
16364
16422
  if (finalLine > 0) {
16365
16423
  output += ansiEscapes3.cursorDown(finalLine);
@@ -16368,10 +16426,15 @@ function createInputHandler(_session) {
16368
16426
  output += ansiEscapes3.cursorForward(finalCol);
16369
16427
  }
16370
16428
  lastCursorRow = finalLine;
16429
+ lastContentRows = contentRows;
16371
16430
  process.stdout.write(output);
16372
16431
  }
16373
16432
  function clearMenu() {
16374
- process.stdout.write(ansiEscapes3.eraseDown);
16433
+ const rowsDown = lastContentRows - lastCursorRow + 1;
16434
+ if (rowsDown > 0) {
16435
+ process.stdout.write(ansiEscapes3.cursorDown(rowsDown));
16436
+ }
16437
+ process.stdout.write("\r" + ansiEscapes3.eraseDown);
16375
16438
  lastMenuLines = 0;
16376
16439
  }
16377
16440
  function insertTextAtCursor(text9) {
@@ -16395,6 +16458,7 @@ function createInputHandler(_session) {
16395
16458
  tempLine = "";
16396
16459
  lastMenuLines = 0;
16397
16460
  lastCursorRow = 0;
16461
+ lastContentRows = 1;
16398
16462
  isFirstRender = true;
16399
16463
  render();
16400
16464
  if (process.stdin.isTTY) {
@@ -16537,9 +16601,7 @@ function createInputHandler(_session) {
16537
16601
  currentLine = completions[selectedCompletion].cmd;
16538
16602
  }
16539
16603
  cleanup();
16540
- const termWidth = process.stdout.columns || 80;
16541
16604
  console.log();
16542
- console.log(chalk12.dim("\u2500".repeat(termWidth)));
16543
16605
  const result = currentLine.trim();
16544
16606
  if (result) {
16545
16607
  sessionHistory.push(result);
@@ -18014,38 +18076,9 @@ function extractDeniedPath(error) {
18014
18076
  const match = error.match(/Use \/allow-path (.+?) to grant access/);
18015
18077
  return match?.[1] ?? null;
18016
18078
  }
18017
- async function promptAllowPath(dirPath) {
18018
- const absolute = path20__default.resolve(dirPath);
18019
- console.log();
18020
- console.log(chalk12.yellow(" \u26A0 Access denied \u2014 path is outside the project directory"));
18021
- console.log(chalk12.dim(` \u{1F4C1} ${absolute}`));
18022
- console.log();
18023
- const action = await p9.select({
18024
- message: "Grant access to this directory?",
18025
- options: [
18026
- { value: "session-write", label: "\u2713 Allow write (this session)" },
18027
- { value: "session-read", label: "\u25D0 Allow read-only (this session)" },
18028
- { value: "persist-write", label: "\u26A1 Allow write (remember for this project)" },
18029
- { value: "persist-read", label: "\u{1F4BE} Allow read-only (remember for this project)" },
18030
- { value: "no", label: "\u2717 Deny" }
18031
- ]
18032
- });
18033
- if (p9.isCancel(action) || action === "no") {
18034
- return false;
18035
- }
18036
- const level = action.includes("read") ? "read" : "write";
18037
- const persist = action.startsWith("persist");
18038
- addAllowedPathToSession(absolute, level);
18039
- if (persist) {
18040
- await persistAllowedPath(absolute, level);
18041
- }
18042
- const levelLabel = level === "write" ? "write" : "read-only";
18043
- const persistLabel = persist ? " (remembered)" : "";
18044
- console.log(chalk12.green(` \u2713 Access granted: ${levelLabel}${persistLabel}`));
18045
- return true;
18046
- }
18047
18079
 
18048
18080
  // src/cli/repl/agent-loop.ts
18081
+ init_allow_path_prompt();
18049
18082
  async function executeAgentTurn(session, userMessage, provider, toolRegistry, options = {}) {
18050
18083
  resetLineBuffer();
18051
18084
  addMessage(session, { role: "user", content: userMessage });
@@ -18329,6 +18362,7 @@ function formatAbortSummary(executedTools) {
18329
18362
  }
18330
18363
  return summary;
18331
18364
  }
18365
+ init_allowed_paths();
18332
18366
  var SENSITIVE_PATTERNS = [
18333
18367
  /\.env(?:\.\w+)?$/,
18334
18368
  // .env, .env.local, etc.
@@ -19192,7 +19226,8 @@ function truncateOutput(output, maxLength = 5e4) {
19192
19226
  [Output truncated - ${output.length - maxLength} more characters]`;
19193
19227
  }
19194
19228
  function getGit(cwd) {
19195
- return simpleGit(cwd ?? process.cwd());
19229
+ const baseDir = cwd ?? process.cwd();
19230
+ return simpleGit({ baseDir });
19196
19231
  }
19197
19232
  var gitStatusTool = defineTool({
19198
19233
  name: "git_status",
@@ -19553,9 +19588,11 @@ var gitTools = [
19553
19588
  gitInitTool
19554
19589
  ];
19555
19590
  function generateSimpleCommitMessage() {
19591
+ const cwd = process.cwd();
19556
19592
  try {
19557
19593
  const diff = execSync("git diff --cached --name-only", {
19558
19594
  encoding: "utf-8",
19595
+ cwd,
19559
19596
  stdio: ["pipe", "pipe", "ignore"]
19560
19597
  });
19561
19598
  const files = diff.trim().split("\n").filter(Boolean);
@@ -19591,6 +19628,7 @@ var checkProtectedBranchTool = defineTool({
19591
19628
  try {
19592
19629
  const branch = execSync("git rev-parse --abbrev-ref HEAD", {
19593
19630
  encoding: "utf-8",
19631
+ cwd: process.cwd(),
19594
19632
  stdio: ["pipe", "pipe", "ignore"]
19595
19633
  }).trim();
19596
19634
  const protected_branches = ["main", "master", "develop", "production"];
@@ -19626,7 +19664,7 @@ var simpleAutoCommitTool = defineTool({
19626
19664
  async execute(input) {
19627
19665
  try {
19628
19666
  try {
19629
- execSync("git diff --cached --quiet", { stdio: "ignore" });
19667
+ execSync("git diff --cached --quiet", { cwd: process.cwd(), stdio: "ignore" });
19630
19668
  return {
19631
19669
  stdout: "",
19632
19670
  stderr: "No staged changes to commit",
@@ -19638,6 +19676,7 @@ var simpleAutoCommitTool = defineTool({
19638
19676
  const message = input.message || generateSimpleCommitMessage();
19639
19677
  execSync(`git commit -m "${message}"`, {
19640
19678
  encoding: "utf-8",
19679
+ cwd: process.cwd(),
19641
19680
  stdio: "pipe"
19642
19681
  });
19643
19682
  return {
@@ -20306,10 +20345,10 @@ var CoverageAnalyzer = class {
20306
20345
  join(this.projectPath, ".coverage", "coverage-summary.json"),
20307
20346
  join(this.projectPath, "coverage", "lcov-report", "coverage-summary.json")
20308
20347
  ];
20309
- for (const path37 of possiblePaths) {
20348
+ for (const path39 of possiblePaths) {
20310
20349
  try {
20311
- await access(path37, constants.R_OK);
20312
- const content = await readFile(path37, "utf-8");
20350
+ await access(path39, constants.R_OK);
20351
+ const content = await readFile(path39, "utf-8");
20313
20352
  const report = JSON.parse(content);
20314
20353
  return parseCoverageSummary(report);
20315
20354
  } catch {
@@ -24491,9 +24530,17 @@ Examples:
24491
24530
  }
24492
24531
  });
24493
24532
  var diffTools = [showDiffTool];
24494
- async function fileExists(path37) {
24533
+ async function fileExists(filePath) {
24534
+ try {
24535
+ await fs22__default.access(filePath);
24536
+ return true;
24537
+ } catch {
24538
+ return false;
24539
+ }
24540
+ }
24541
+ async function fileExists2(path39) {
24495
24542
  try {
24496
- await access(path37);
24543
+ await access(path39);
24497
24544
  return true;
24498
24545
  } catch {
24499
24546
  return false;
@@ -24508,7 +24555,7 @@ async function dirHasFiles(dir) {
24508
24555
  }
24509
24556
  }
24510
24557
  async function detectMaturity(cwd) {
24511
- const hasPackageJson = await fileExists(join(cwd, "package.json"));
24558
+ const hasPackageJson = await fileExists2(join(cwd, "package.json"));
24512
24559
  if (!hasPackageJson) {
24513
24560
  const otherManifests = [
24514
24561
  "go.mod",
@@ -24521,7 +24568,7 @@ async function detectMaturity(cwd) {
24521
24568
  ];
24522
24569
  let hasAnyManifest = false;
24523
24570
  for (const m of otherManifests) {
24524
- if (await fileExists(join(cwd, m))) {
24571
+ if (await fileExists2(join(cwd, m))) {
24525
24572
  hasAnyManifest = true;
24526
24573
  break;
24527
24574
  }
@@ -24562,7 +24609,7 @@ async function detectMaturity(cwd) {
24562
24609
  cwd,
24563
24610
  ignore: ["node_modules/**", "dist/**", "build/**"]
24564
24611
  });
24565
- const hasCI = await fileExists(join(cwd, ".github/workflows")) && await dirHasFiles(join(cwd, ".github/workflows"));
24612
+ const hasCI = await fileExists2(join(cwd, ".github/workflows")) && await dirHasFiles(join(cwd, ".github/workflows"));
24566
24613
  const lintConfigs = [
24567
24614
  ".eslintrc.js",
24568
24615
  ".eslintrc.json",
@@ -24575,7 +24622,7 @@ async function detectMaturity(cwd) {
24575
24622
  ];
24576
24623
  let hasLintConfig = false;
24577
24624
  for (const config of lintConfigs) {
24578
- if (await fileExists(join(cwd, config))) {
24625
+ if (await fileExists2(join(cwd, config))) {
24579
24626
  hasLintConfig = true;
24580
24627
  break;
24581
24628
  }
@@ -24583,7 +24630,7 @@ async function detectMaturity(cwd) {
24583
24630
  if (!hasLintConfig && hasPackageJson) {
24584
24631
  try {
24585
24632
  const pkgRaw = await import('fs/promises').then(
24586
- (fs39) => fs39.readFile(join(cwd, "package.json"), "utf-8")
24633
+ (fs41) => fs41.readFile(join(cwd, "package.json"), "utf-8")
24587
24634
  );
24588
24635
  const pkg = JSON.parse(pkgRaw);
24589
24636
  if (pkg.scripts?.lint || pkg.scripts?.["lint:fix"]) {
@@ -24628,7 +24675,8 @@ var SECURITY_PATTERNS2 = [
24628
24675
  regex: /console\.(log|debug|info)\(/,
24629
24676
  severity: "minor",
24630
24677
  category: "best-practice",
24631
- message: "Remove console.log \u2014 use structured logging instead"
24678
+ message: "Remove console.log \u2014 use structured logging instead",
24679
+ excludePaths: /\/(cli|repl|bin|scripts)\//
24632
24680
  }
24633
24681
  ];
24634
24682
  var CORRECTNESS_PATTERNS = [
@@ -24708,6 +24756,7 @@ function analyzePatterns(diff) {
24708
24756
  for (const line of hunk.lines) {
24709
24757
  if (line.type !== "add") continue;
24710
24758
  for (const pattern of ALL_PATTERNS) {
24759
+ if (pattern.excludePaths?.test(file.path)) continue;
24711
24760
  if (pattern.regex.test(line.content)) {
24712
24761
  findings.push({
24713
24762
  file: file.path,
@@ -24724,7 +24773,8 @@ function analyzePatterns(diff) {
24724
24773
  }
24725
24774
  return findings;
24726
24775
  }
24727
- function checkTestCoverage(diff) {
24776
+ var TEST_COVERAGE_LARGE_CHANGE_THRESHOLD = 15;
24777
+ async function checkTestCoverage(diff, cwd) {
24728
24778
  const findings = [];
24729
24779
  const changedSrc = [];
24730
24780
  const changedTests = /* @__PURE__ */ new Set();
@@ -24734,22 +24784,35 @@ function checkTestCoverage(diff) {
24734
24784
  changedTests.add(file.path);
24735
24785
  } else if (/\.(ts|tsx|js|jsx)$/.test(file.path)) {
24736
24786
  if (file.additions > 5) {
24737
- changedSrc.push(file.path);
24787
+ changedSrc.push({ path: file.path, additions: file.additions });
24738
24788
  }
24739
24789
  }
24740
24790
  }
24741
24791
  for (const src of changedSrc) {
24742
- const baseName = src.replace(/\.(ts|tsx|js|jsx)$/, "");
24792
+ const baseName = src.path.replace(/\.(ts|tsx|js|jsx)$/, "");
24743
24793
  const hasTestChange = [...changedTests].some(
24744
24794
  (t) => t.includes(baseName.split("/").pop()) || t.startsWith(baseName)
24745
24795
  );
24746
24796
  if (!hasTestChange) {
24747
- findings.push({
24748
- file: src,
24749
- severity: "minor",
24750
- category: "testing",
24751
- message: "Logic changes without corresponding test updates"
24752
- });
24797
+ const ext = src.path.match(/\.(ts|tsx|js|jsx)$/)?.[0] ?? ".ts";
24798
+ const testExists = await fileExists(path20__default.join(cwd, `${baseName}.test${ext}`)) || await fileExists(path20__default.join(cwd, `${baseName}.spec${ext}`));
24799
+ if (testExists) {
24800
+ if (src.additions >= TEST_COVERAGE_LARGE_CHANGE_THRESHOLD) {
24801
+ findings.push({
24802
+ file: src.path,
24803
+ severity: "info",
24804
+ category: "testing",
24805
+ message: "Test file exists but was not updated \u2014 verify existing tests cover these changes"
24806
+ });
24807
+ }
24808
+ } else {
24809
+ findings.push({
24810
+ file: src.path,
24811
+ severity: "minor",
24812
+ category: "testing",
24813
+ message: "Logic changes without corresponding test updates"
24814
+ });
24815
+ }
24753
24816
  }
24754
24817
  }
24755
24818
  return findings;
@@ -24899,7 +24962,7 @@ Examples:
24899
24962
  const maturity = maturityInfo.level;
24900
24963
  let allFindings = [];
24901
24964
  allFindings.push(...analyzePatterns(diff));
24902
- allFindings.push(...checkTestCoverage(diff));
24965
+ allFindings.push(...await checkTestCoverage(diff, projectDir));
24903
24966
  allFindings.push(...checkDocumentation(diff));
24904
24967
  if (runLinter) {
24905
24968
  try {
@@ -24950,8 +25013,8 @@ Examples:
24950
25013
  }
24951
25014
  });
24952
25015
  var reviewTools = [reviewCodeTool];
24953
- var fs28 = await import('fs/promises');
24954
- var path26 = await import('path');
25016
+ var fs29 = await import('fs/promises');
25017
+ var path28 = await import('path');
24955
25018
  var { glob: glob12 } = await import('glob');
24956
25019
  var DEFAULT_MAX_FILES = 200;
24957
25020
  var LANGUAGE_EXTENSIONS = {
@@ -24977,7 +25040,7 @@ var DEFAULT_EXCLUDES = [
24977
25040
  "**/*.d.ts"
24978
25041
  ];
24979
25042
  function detectLanguage2(filePath) {
24980
- const ext = path26.extname(filePath).toLowerCase();
25043
+ const ext = path28.extname(filePath).toLowerCase();
24981
25044
  for (const [lang, extensions] of Object.entries(LANGUAGE_EXTENSIONS)) {
24982
25045
  if (extensions.includes(ext)) return lang;
24983
25046
  }
@@ -25386,9 +25449,9 @@ Examples:
25386
25449
  }),
25387
25450
  async execute({ path: rootPath, include, exclude, languages, maxFiles, depth }) {
25388
25451
  const startTime = performance.now();
25389
- const absPath = path26.resolve(rootPath);
25452
+ const absPath = path28.resolve(rootPath);
25390
25453
  try {
25391
- const stat2 = await fs28.stat(absPath);
25454
+ const stat2 = await fs29.stat(absPath);
25392
25455
  if (!stat2.isDirectory()) {
25393
25456
  throw new ToolError(`Path is not a directory: ${absPath}`, {
25394
25457
  tool: "codebase_map"
@@ -25425,14 +25488,14 @@ Examples:
25425
25488
  let totalDefinitions = 0;
25426
25489
  let exportedSymbols = 0;
25427
25490
  for (const file of limitedFiles) {
25428
- const fullPath = path26.join(absPath, file);
25491
+ const fullPath = path28.join(absPath, file);
25429
25492
  const language = detectLanguage2(file);
25430
25493
  if (!language) continue;
25431
25494
  if (languages && !languages.includes(language)) {
25432
25495
  continue;
25433
25496
  }
25434
25497
  try {
25435
- const content = await fs28.readFile(fullPath, "utf-8");
25498
+ const content = await fs29.readFile(fullPath, "utf-8");
25436
25499
  const lineCount = content.split("\n").length;
25437
25500
  const parsed = parseFile(content, language);
25438
25501
  const definitions = depth === "overview" ? parsed.definitions.filter((d) => d.exported) : parsed.definitions;
@@ -25465,23 +25528,23 @@ Examples:
25465
25528
  });
25466
25529
  var codebaseMapTools = [codebaseMapTool];
25467
25530
  init_paths();
25468
- var fs29 = await import('fs/promises');
25469
- var path27 = await import('path');
25470
- var crypto2 = await import('crypto');
25471
- var GLOBAL_MEMORIES_DIR = path27.join(COCO_HOME, "memories");
25531
+ var fs30 = await import('fs/promises');
25532
+ var path29 = await import('path');
25533
+ var crypto3 = await import('crypto');
25534
+ var GLOBAL_MEMORIES_DIR = path29.join(COCO_HOME, "memories");
25472
25535
  var PROJECT_MEMORIES_DIR = ".coco/memories";
25473
25536
  var DEFAULT_MAX_MEMORIES = 1e3;
25474
25537
  async function ensureDir2(dirPath) {
25475
- await fs29.mkdir(dirPath, { recursive: true });
25538
+ await fs30.mkdir(dirPath, { recursive: true });
25476
25539
  }
25477
25540
  function getMemoriesDir(scope) {
25478
25541
  return scope === "global" ? GLOBAL_MEMORIES_DIR : PROJECT_MEMORIES_DIR;
25479
25542
  }
25480
25543
  async function loadIndex(scope) {
25481
25544
  const dir = getMemoriesDir(scope);
25482
- const indexPath = path27.join(dir, "index.json");
25545
+ const indexPath = path29.join(dir, "index.json");
25483
25546
  try {
25484
- const content = await fs29.readFile(indexPath, "utf-8");
25547
+ const content = await fs30.readFile(indexPath, "utf-8");
25485
25548
  return JSON.parse(content);
25486
25549
  } catch {
25487
25550
  return [];
@@ -25490,14 +25553,14 @@ async function loadIndex(scope) {
25490
25553
  async function saveIndex(scope, index) {
25491
25554
  const dir = getMemoriesDir(scope);
25492
25555
  await ensureDir2(dir);
25493
- const indexPath = path27.join(dir, "index.json");
25494
- await fs29.writeFile(indexPath, JSON.stringify(index, null, 2), "utf-8");
25556
+ const indexPath = path29.join(dir, "index.json");
25557
+ await fs30.writeFile(indexPath, JSON.stringify(index, null, 2), "utf-8");
25495
25558
  }
25496
25559
  async function loadMemory(scope, id) {
25497
25560
  const dir = getMemoriesDir(scope);
25498
- const memPath = path27.join(dir, `${id}.json`);
25561
+ const memPath = path29.join(dir, `${id}.json`);
25499
25562
  try {
25500
- const content = await fs29.readFile(memPath, "utf-8");
25563
+ const content = await fs30.readFile(memPath, "utf-8");
25501
25564
  return JSON.parse(content);
25502
25565
  } catch {
25503
25566
  return null;
@@ -25506,8 +25569,8 @@ async function loadMemory(scope, id) {
25506
25569
  async function saveMemory(scope, memory) {
25507
25570
  const dir = getMemoriesDir(scope);
25508
25571
  await ensureDir2(dir);
25509
- const memPath = path27.join(dir, `${memory.id}.json`);
25510
- await fs29.writeFile(memPath, JSON.stringify(memory, null, 2), "utf-8");
25572
+ const memPath = path29.join(dir, `${memory.id}.json`);
25573
+ await fs30.writeFile(memPath, JSON.stringify(memory, null, 2), "utf-8");
25511
25574
  }
25512
25575
  var createMemoryTool = defineTool({
25513
25576
  name: "create_memory",
@@ -25549,7 +25612,7 @@ Examples:
25549
25612
  { tool: "create_memory" }
25550
25613
  );
25551
25614
  }
25552
- const id = crypto2.randomUUID();
25615
+ const id = crypto3.randomUUID();
25553
25616
  const memory = {
25554
25617
  id,
25555
25618
  key,
@@ -25659,17 +25722,17 @@ Examples:
25659
25722
  }
25660
25723
  });
25661
25724
  var memoryTools = [createMemoryTool, recallMemoryTool, listMemoriesTool];
25662
- var fs30 = await import('fs/promises');
25663
- var crypto3 = await import('crypto');
25725
+ var fs31 = await import('fs/promises');
25726
+ var crypto4 = await import('crypto');
25664
25727
  var CHECKPOINT_FILE = ".coco/checkpoints.json";
25665
25728
  var DEFAULT_MAX_CHECKPOINTS = 50;
25666
25729
  var STASH_PREFIX = "coco-cp";
25667
25730
  async function ensureCocoDir() {
25668
- await fs30.mkdir(".coco", { recursive: true });
25731
+ await fs31.mkdir(".coco", { recursive: true });
25669
25732
  }
25670
25733
  async function loadCheckpoints() {
25671
25734
  try {
25672
- const content = await fs30.readFile(CHECKPOINT_FILE, "utf-8");
25735
+ const content = await fs31.readFile(CHECKPOINT_FILE, "utf-8");
25673
25736
  return JSON.parse(content);
25674
25737
  } catch {
25675
25738
  return [];
@@ -25677,7 +25740,7 @@ async function loadCheckpoints() {
25677
25740
  }
25678
25741
  async function saveCheckpoints(checkpoints) {
25679
25742
  await ensureCocoDir();
25680
- await fs30.writeFile(CHECKPOINT_FILE, JSON.stringify(checkpoints, null, 2), "utf-8");
25743
+ await fs31.writeFile(CHECKPOINT_FILE, JSON.stringify(checkpoints, null, 2), "utf-8");
25681
25744
  }
25682
25745
  async function execGit(args) {
25683
25746
  const { execaCommand } = await import('execa');
@@ -25715,7 +25778,7 @@ Examples:
25715
25778
  description: z.string().min(1).max(200).describe("Description of this checkpoint")
25716
25779
  }),
25717
25780
  async execute({ description }) {
25718
- const id = crypto3.randomUUID().slice(0, 8);
25781
+ const id = crypto4.randomUUID().slice(0, 8);
25719
25782
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
25720
25783
  const stashMessage = `${STASH_PREFIX}-${id}-${description.replace(/\s+/g, "-").slice(0, 50)}`;
25721
25784
  const changedFiles = await getChangedFiles();
@@ -25837,8 +25900,8 @@ Examples:
25837
25900
  }
25838
25901
  });
25839
25902
  var checkpointTools = [createCheckpointTool, restoreCheckpointTool, listCheckpointsTool];
25840
- var fs31 = await import('fs/promises');
25841
- var path28 = await import('path');
25903
+ var fs32 = await import('fs/promises');
25904
+ var path30 = await import('path');
25842
25905
  var { glob: glob13 } = await import('glob');
25843
25906
  var INDEX_DIR = ".coco/search-index";
25844
25907
  var DEFAULT_CHUNK_SIZE = 20;
@@ -25964,20 +26027,20 @@ async function getEmbedding(text9) {
25964
26027
  }
25965
26028
  async function loadIndex2(indexDir) {
25966
26029
  try {
25967
- const indexPath = path28.join(indexDir, "index.json");
25968
- const content = await fs31.readFile(indexPath, "utf-8");
26030
+ const indexPath = path30.join(indexDir, "index.json");
26031
+ const content = await fs32.readFile(indexPath, "utf-8");
25969
26032
  return JSON.parse(content);
25970
26033
  } catch {
25971
26034
  return null;
25972
26035
  }
25973
26036
  }
25974
26037
  async function saveIndex2(indexDir, index) {
25975
- await fs31.mkdir(indexDir, { recursive: true });
25976
- const indexPath = path28.join(indexDir, "index.json");
25977
- await fs31.writeFile(indexPath, JSON.stringify(index), "utf-8");
26038
+ await fs32.mkdir(indexDir, { recursive: true });
26039
+ const indexPath = path30.join(indexDir, "index.json");
26040
+ await fs32.writeFile(indexPath, JSON.stringify(index), "utf-8");
25978
26041
  }
25979
26042
  function isBinary(filePath) {
25980
- return BINARY_EXTENSIONS.has(path28.extname(filePath).toLowerCase());
26043
+ return BINARY_EXTENSIONS.has(path30.extname(filePath).toLowerCase());
25981
26044
  }
25982
26045
  var semanticSearchTool = defineTool({
25983
26046
  name: "semantic_search",
@@ -26002,8 +26065,8 @@ Examples:
26002
26065
  const effectivePath = rootPath ?? ".";
26003
26066
  const effectiveMaxResults = maxResults ?? 10;
26004
26067
  const effectiveThreshold = threshold ?? 0.3;
26005
- const absPath = path28.resolve(effectivePath);
26006
- const indexDir = path28.join(absPath, INDEX_DIR);
26068
+ const absPath = path30.resolve(effectivePath);
26069
+ const indexDir = path30.join(absPath, INDEX_DIR);
26007
26070
  let index = reindex ? null : await loadIndex2(indexDir);
26008
26071
  if (!index) {
26009
26072
  const pattern = include ?? "**/*";
@@ -26016,10 +26079,10 @@ Examples:
26016
26079
  const chunks = [];
26017
26080
  for (const file of files) {
26018
26081
  if (isBinary(file)) continue;
26019
- const fullPath = path28.join(absPath, file);
26082
+ const fullPath = path30.join(absPath, file);
26020
26083
  try {
26021
- const stat2 = await fs31.stat(fullPath);
26022
- const content = await fs31.readFile(fullPath, "utf-8");
26084
+ const stat2 = await fs32.stat(fullPath);
26085
+ const content = await fs32.readFile(fullPath, "utf-8");
26023
26086
  if (content.length > 1e5) continue;
26024
26087
  const fileChunks = chunkContent(content, DEFAULT_CHUNK_SIZE);
26025
26088
  for (const chunk of fileChunks) {
@@ -26078,8 +26141,8 @@ Examples:
26078
26141
  }
26079
26142
  });
26080
26143
  var semanticSearchTools = [semanticSearchTool];
26081
- var fs32 = await import('fs/promises');
26082
- var path29 = await import('path');
26144
+ var fs33 = await import('fs/promises');
26145
+ var path31 = await import('path');
26083
26146
  var { glob: glob14 } = await import('glob');
26084
26147
  async function parseClassRelationships(rootPath, include) {
26085
26148
  const pattern = include ?? "**/*.{ts,tsx,js,jsx}";
@@ -26092,7 +26155,7 @@ async function parseClassRelationships(rootPath, include) {
26092
26155
  const interfaces = [];
26093
26156
  for (const file of files.slice(0, 100)) {
26094
26157
  try {
26095
- const content = await fs32.readFile(path29.join(rootPath, file), "utf-8");
26158
+ const content = await fs33.readFile(path31.join(rootPath, file), "utf-8");
26096
26159
  const lines = content.split("\n");
26097
26160
  for (let i = 0; i < lines.length; i++) {
26098
26161
  const line = lines[i];
@@ -26211,14 +26274,14 @@ async function generateClassDiagram(rootPath, include) {
26211
26274
  };
26212
26275
  }
26213
26276
  async function generateArchitectureDiagram(rootPath) {
26214
- const entries = await fs32.readdir(rootPath, { withFileTypes: true });
26277
+ const entries = await fs33.readdir(rootPath, { withFileTypes: true });
26215
26278
  const dirs = entries.filter(
26216
26279
  (e) => e.isDirectory() && !e.name.startsWith(".") && !["node_modules", "dist", "build", "coverage", "__pycache__", "target"].includes(e.name)
26217
26280
  );
26218
26281
  const lines = ["graph TD"];
26219
26282
  let nodeCount = 0;
26220
26283
  let edgeCount = 0;
26221
- const rootName = path29.basename(rootPath);
26284
+ const rootName = path31.basename(rootPath);
26222
26285
  lines.push(` ROOT["${rootName}"]`);
26223
26286
  nodeCount++;
26224
26287
  for (const dir of dirs) {
@@ -26228,7 +26291,7 @@ async function generateArchitectureDiagram(rootPath) {
26228
26291
  nodeCount++;
26229
26292
  edgeCount++;
26230
26293
  try {
26231
- const subEntries = await fs32.readdir(path29.join(rootPath, dir.name), {
26294
+ const subEntries = await fs33.readdir(path31.join(rootPath, dir.name), {
26232
26295
  withFileTypes: true
26233
26296
  });
26234
26297
  const subDirs = subEntries.filter(
@@ -26351,7 +26414,7 @@ Examples:
26351
26414
  tool: "generate_diagram"
26352
26415
  });
26353
26416
  }
26354
- const absPath = rootPath ? path29.resolve(rootPath) : process.cwd();
26417
+ const absPath = rootPath ? path31.resolve(rootPath) : process.cwd();
26355
26418
  switch (type) {
26356
26419
  case "class":
26357
26420
  return generateClassDiagram(absPath, include);
@@ -26412,8 +26475,8 @@ Examples:
26412
26475
  }
26413
26476
  });
26414
26477
  var diagramTools = [generateDiagramTool];
26415
- var fs33 = await import('fs/promises');
26416
- var path30 = await import('path');
26478
+ var fs34 = await import('fs/promises');
26479
+ var path32 = await import('path');
26417
26480
  var DEFAULT_MAX_PAGES = 20;
26418
26481
  var MAX_FILE_SIZE = 50 * 1024 * 1024;
26419
26482
  function parsePageRange(rangeStr, totalPages) {
@@ -26448,9 +26511,9 @@ Examples:
26448
26511
  }),
26449
26512
  async execute({ path: filePath, pages, maxPages }) {
26450
26513
  const startTime = performance.now();
26451
- const absPath = path30.resolve(filePath);
26514
+ const absPath = path32.resolve(filePath);
26452
26515
  try {
26453
- const stat2 = await fs33.stat(absPath);
26516
+ const stat2 = await fs34.stat(absPath);
26454
26517
  if (!stat2.isFile()) {
26455
26518
  throw new ToolError(`Path is not a file: ${absPath}`, {
26456
26519
  tool: "read_pdf"
@@ -26481,7 +26544,7 @@ Examples:
26481
26544
  }
26482
26545
  try {
26483
26546
  const pdfParse = await import('pdf-parse');
26484
- const dataBuffer = await fs33.readFile(absPath);
26547
+ const dataBuffer = await fs34.readFile(absPath);
26485
26548
  const pdfData = await pdfParse.default(dataBuffer, {
26486
26549
  max: maxPages
26487
26550
  });
@@ -26527,8 +26590,8 @@ Examples:
26527
26590
  }
26528
26591
  });
26529
26592
  var pdfTools = [readPdfTool];
26530
- var fs34 = await import('fs/promises');
26531
- var path31 = await import('path');
26593
+ var fs35 = await import('fs/promises');
26594
+ var path33 = await import('path');
26532
26595
  var SUPPORTED_FORMATS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
26533
26596
  var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
26534
26597
  var MIME_TYPES = {
@@ -26556,15 +26619,15 @@ Examples:
26556
26619
  async execute({ path: filePath, prompt, provider }) {
26557
26620
  const startTime = performance.now();
26558
26621
  const effectivePrompt = prompt ?? "Describe this image in detail. If it's code or a UI, identify the key elements.";
26559
- const absPath = path31.resolve(filePath);
26622
+ const absPath = path33.resolve(filePath);
26560
26623
  const cwd = process.cwd();
26561
- if (!absPath.startsWith(cwd + path31.sep) && absPath !== cwd) {
26624
+ if (!absPath.startsWith(cwd + path33.sep) && absPath !== cwd) {
26562
26625
  throw new ToolError(
26563
26626
  `Path traversal denied: '${filePath}' resolves outside the project directory`,
26564
26627
  { tool: "read_image" }
26565
26628
  );
26566
26629
  }
26567
- const ext = path31.extname(absPath).toLowerCase();
26630
+ const ext = path33.extname(absPath).toLowerCase();
26568
26631
  if (!SUPPORTED_FORMATS.has(ext)) {
26569
26632
  throw new ToolError(
26570
26633
  `Unsupported image format '${ext}'. Supported: ${Array.from(SUPPORTED_FORMATS).join(", ")}`,
@@ -26572,7 +26635,7 @@ Examples:
26572
26635
  );
26573
26636
  }
26574
26637
  try {
26575
- const stat2 = await fs34.stat(absPath);
26638
+ const stat2 = await fs35.stat(absPath);
26576
26639
  if (!stat2.isFile()) {
26577
26640
  throw new ToolError(`Path is not a file: ${absPath}`, {
26578
26641
  tool: "read_image"
@@ -26593,7 +26656,7 @@ Examples:
26593
26656
  if (error instanceof ToolError) throw error;
26594
26657
  throw error;
26595
26658
  }
26596
- const imageBuffer = await fs34.readFile(absPath);
26659
+ const imageBuffer = await fs35.readFile(absPath);
26597
26660
  const base64 = imageBuffer.toString("base64");
26598
26661
  const mimeType = MIME_TYPES[ext] ?? "image/png";
26599
26662
  const selectedProvider = provider ?? "anthropic";
@@ -26706,7 +26769,7 @@ Examples:
26706
26769
  }
26707
26770
  });
26708
26771
  var imageTools = [readImageTool];
26709
- var path32 = await import('path');
26772
+ var path34 = await import('path');
26710
26773
  var DANGEROUS_PATTERNS2 = [
26711
26774
  /\bDROP\s+(?:TABLE|DATABASE|INDEX|VIEW)\b/i,
26712
26775
  /\bTRUNCATE\b/i,
@@ -26737,7 +26800,7 @@ Examples:
26737
26800
  async execute({ database, query, params, readonly: isReadonlyParam }) {
26738
26801
  const isReadonly = isReadonlyParam ?? true;
26739
26802
  const startTime = performance.now();
26740
- const absPath = path32.resolve(database);
26803
+ const absPath = path34.resolve(database);
26741
26804
  if (isReadonly && isDangerousSql(query)) {
26742
26805
  throw new ToolError(
26743
26806
  "Write operations (INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE) are blocked in readonly mode. Set readonly: false to allow writes.",
@@ -26810,7 +26873,7 @@ Examples:
26810
26873
  }),
26811
26874
  async execute({ database, table }) {
26812
26875
  const startTime = performance.now();
26813
- const absPath = path32.resolve(database);
26876
+ const absPath = path34.resolve(database);
26814
26877
  try {
26815
26878
  const { default: Database } = await import('better-sqlite3');
26816
26879
  const db = new Database(absPath, { readonly: true, fileMustExist: true });
@@ -26981,14 +27044,14 @@ var findMissingImportsTool = defineTool({
26981
27044
  }
26982
27045
  });
26983
27046
  var astValidatorTools = [validateCodeTool, findMissingImportsTool];
26984
- var fs35 = await import('fs/promises');
26985
- var path33 = await import('path');
27047
+ var fs36 = await import('fs/promises');
27048
+ var path35 = await import('path');
26986
27049
  var AnalyzeFileSchema = z.object({
26987
27050
  filePath: z.string().describe("Path to file to analyze"),
26988
27051
  includeAst: z.boolean().default(false).describe("Include AST in result")
26989
27052
  });
26990
27053
  async function analyzeFile(filePath, includeAst = false) {
26991
- const content = await fs35.readFile(filePath, "utf-8");
27054
+ const content = await fs36.readFile(filePath, "utf-8");
26992
27055
  const lines = content.split("\n").length;
26993
27056
  const functions = [];
26994
27057
  const classes = [];
@@ -27092,10 +27155,10 @@ async function analyzeDirectory(dirPath) {
27092
27155
  try {
27093
27156
  const analysis = await analyzeFile(file, false);
27094
27157
  totalLines += analysis.lines;
27095
- const ext = path33.extname(file);
27158
+ const ext = path35.extname(file);
27096
27159
  filesByType[ext] = (filesByType[ext] || 0) + 1;
27097
27160
  fileStats.push({
27098
- file: path33.relative(dirPath, file),
27161
+ file: path35.relative(dirPath, file),
27099
27162
  lines: analysis.lines,
27100
27163
  complexity: analysis.complexity.cyclomatic
27101
27164
  });
@@ -27451,13 +27514,13 @@ ${completed.map((r) => `- ${r.agentId}: Success`).join("\n")}`;
27451
27514
  }
27452
27515
  });
27453
27516
  var agentCoordinatorTools = [createAgentPlanTool, delegateTaskTool, aggregateResultsTool];
27454
- var fs36 = await import('fs/promises');
27517
+ var fs37 = await import('fs/promises');
27455
27518
  var SuggestImprovementsSchema = z.object({
27456
27519
  filePath: z.string().describe("File to analyze for improvement suggestions"),
27457
27520
  context: z.string().optional().describe("Additional context about the code")
27458
27521
  });
27459
27522
  async function analyzeAndSuggest(filePath, _context) {
27460
- const content = await fs36.readFile(filePath, "utf-8");
27523
+ const content = await fs37.readFile(filePath, "utf-8");
27461
27524
  const lines = content.split("\n");
27462
27525
  const suggestions = [];
27463
27526
  for (let i = 0; i < lines.length; i++) {
@@ -27549,7 +27612,7 @@ async function analyzeAndSuggest(filePath, _context) {
27549
27612
  if (filePath.endsWith(".ts") && !filePath.includes("test") && !filePath.includes(".d.ts") && line.includes("export ")) {
27550
27613
  const testPath = filePath.replace(".ts", ".test.ts");
27551
27614
  try {
27552
- await fs36.access(testPath);
27615
+ await fs37.access(testPath);
27553
27616
  } catch {
27554
27617
  suggestions.push({
27555
27618
  type: "testing",
@@ -27606,7 +27669,7 @@ var calculateCodeScoreTool = defineTool({
27606
27669
  async execute(input) {
27607
27670
  const { filePath } = input;
27608
27671
  const suggestions = await analyzeAndSuggest(filePath);
27609
- const content = await fs36.readFile(filePath, "utf-8");
27672
+ const content = await fs37.readFile(filePath, "utf-8");
27610
27673
  const lines = content.split("\n");
27611
27674
  const nonEmptyLines = lines.filter((l) => l.trim()).length;
27612
27675
  let score = 100;
@@ -27640,8 +27703,8 @@ var calculateCodeScoreTool = defineTool({
27640
27703
  }
27641
27704
  });
27642
27705
  var smartSuggestionsTools = [suggestImprovementsTool, calculateCodeScoreTool];
27643
- var fs37 = await import('fs/promises');
27644
- var path34 = await import('path');
27706
+ var fs38 = await import('fs/promises');
27707
+ var path36 = await import('path');
27645
27708
  var ContextMemoryStore = class {
27646
27709
  items = /* @__PURE__ */ new Map();
27647
27710
  learnings = /* @__PURE__ */ new Map();
@@ -27653,7 +27716,7 @@ var ContextMemoryStore = class {
27653
27716
  }
27654
27717
  async load() {
27655
27718
  try {
27656
- const content = await fs37.readFile(this.storePath, "utf-8");
27719
+ const content = await fs38.readFile(this.storePath, "utf-8");
27657
27720
  const data = JSON.parse(content);
27658
27721
  this.items = new Map(Object.entries(data.items || {}));
27659
27722
  this.learnings = new Map(Object.entries(data.learnings || {}));
@@ -27661,15 +27724,15 @@ var ContextMemoryStore = class {
27661
27724
  }
27662
27725
  }
27663
27726
  async save() {
27664
- const dir = path34.dirname(this.storePath);
27665
- await fs37.mkdir(dir, { recursive: true });
27727
+ const dir = path36.dirname(this.storePath);
27728
+ await fs38.mkdir(dir, { recursive: true });
27666
27729
  const data = {
27667
27730
  sessionId: this.sessionId,
27668
27731
  items: Object.fromEntries(this.items),
27669
27732
  learnings: Object.fromEntries(this.learnings),
27670
27733
  savedAt: Date.now()
27671
27734
  };
27672
- await fs37.writeFile(this.storePath, JSON.stringify(data, null, 2));
27735
+ await fs38.writeFile(this.storePath, JSON.stringify(data, null, 2));
27673
27736
  }
27674
27737
  addContext(id, item) {
27675
27738
  this.items.set(id, item);
@@ -27834,11 +27897,11 @@ var contextEnhancerTools = [
27834
27897
  recordLearningTool,
27835
27898
  getLearnedPatternsTool
27836
27899
  ];
27837
- var fs38 = await import('fs/promises');
27838
- var path35 = await import('path');
27900
+ var fs39 = await import('fs/promises');
27901
+ var path37 = await import('path');
27839
27902
  async function discoverSkills(skillsDir) {
27840
27903
  try {
27841
- const files = await fs38.readdir(skillsDir);
27904
+ const files = await fs39.readdir(skillsDir);
27842
27905
  return files.filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
27843
27906
  } catch {
27844
27907
  return [];
@@ -27846,12 +27909,12 @@ async function discoverSkills(skillsDir) {
27846
27909
  }
27847
27910
  async function loadSkillMetadata(skillPath) {
27848
27911
  try {
27849
- const content = await fs38.readFile(skillPath, "utf-8");
27912
+ const content = await fs39.readFile(skillPath, "utf-8");
27850
27913
  const nameMatch = content.match(/@name\s+(\S+)/);
27851
27914
  const descMatch = content.match(/@description\s+(.+)/);
27852
27915
  const versionMatch = content.match(/@version\s+(\S+)/);
27853
27916
  return {
27854
- name: nameMatch?.[1] || path35.basename(skillPath, path35.extname(skillPath)),
27917
+ name: nameMatch?.[1] || path37.basename(skillPath, path37.extname(skillPath)),
27855
27918
  description: descMatch?.[1] || "No description",
27856
27919
  version: versionMatch?.[1] || "1.0.0",
27857
27920
  dependencies: []
@@ -27895,7 +27958,7 @@ var discoverSkillsTool = defineTool({
27895
27958
  const { skillsDir } = input;
27896
27959
  const skills = await discoverSkills(skillsDir);
27897
27960
  const metadata = await Promise.all(
27898
- skills.map((s) => loadSkillMetadata(path35.join(skillsDir, s)))
27961
+ skills.map((s) => loadSkillMetadata(path37.join(skillsDir, s)))
27899
27962
  );
27900
27963
  return {
27901
27964
  skillsDir,
@@ -28018,14 +28081,17 @@ export const ${typedInput.name}Tool = defineTool({
28018
28081
  }
28019
28082
  });
28020
28083
  var skillEnhancerTools = [discoverSkillsTool, validateSkillTool, createCustomToolTool];
28084
+ function gitExec(cmd, opts = {}) {
28085
+ return execSync(cmd, { encoding: "utf-8", cwd: process.cwd(), ...opts });
28086
+ }
28021
28087
  function analyzeRepoHealth() {
28022
28088
  const issues = [];
28023
28089
  const recommendations = [];
28024
28090
  let score = 100;
28025
28091
  try {
28026
28092
  try {
28027
- execSync("git status --porcelain", { stdio: "pipe" });
28028
- const status = execSync("git status --porcelain", { encoding: "utf-8" });
28093
+ gitExec("git status --porcelain", { stdio: "pipe" });
28094
+ const status = gitExec("git status --porcelain");
28029
28095
  if (status.trim()) {
28030
28096
  issues.push("Uncommitted changes present");
28031
28097
  score -= 10;
@@ -28033,7 +28099,7 @@ function analyzeRepoHealth() {
28033
28099
  } catch {
28034
28100
  }
28035
28101
  try {
28036
- const untracked = execSync("git ls-files --others --exclude-standard", { encoding: "utf-8" });
28102
+ const untracked = gitExec("git ls-files --others --exclude-standard");
28037
28103
  if (untracked.trim()) {
28038
28104
  const count = untracked.trim().split("\n").length;
28039
28105
  issues.push(`${count} untracked files`);
@@ -28043,9 +28109,9 @@ function analyzeRepoHealth() {
28043
28109
  } catch {
28044
28110
  }
28045
28111
  try {
28046
- const branch = execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8" }).trim();
28047
- const local = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
28048
- const remote = execSync(`git rev-parse origin/${branch}`, { encoding: "utf-8" }).trim();
28112
+ const branch = gitExec("git rev-parse --abbrev-ref HEAD").trim();
28113
+ const local = gitExec("git rev-parse HEAD").trim();
28114
+ const remote = gitExec(`git rev-parse origin/${branch}`).trim();
28049
28115
  if (local !== remote) {
28050
28116
  issues.push("Branch is not up-to-date with remote");
28051
28117
  score -= 15;
@@ -28054,14 +28120,14 @@ function analyzeRepoHealth() {
28054
28120
  } catch {
28055
28121
  }
28056
28122
  try {
28057
- const files = execSync("git ls-files", { encoding: "utf-8" }).trim().split("\n");
28123
+ const files = gitExec("git ls-files").trim().split("\n");
28058
28124
  if (files.length > 1e3) {
28059
28125
  recommendations.push("Repository has many files, consider using .gitignore");
28060
28126
  }
28061
28127
  } catch {
28062
28128
  }
28063
28129
  try {
28064
- const conflicts = execSync("git diff --name-only --diff-filter=U", { encoding: "utf-8" });
28130
+ const conflicts = gitExec("git diff --name-only --diff-filter=U");
28065
28131
  if (conflicts.trim()) {
28066
28132
  issues.push("Merge conflicts present");
28067
28133
  score -= 30;
@@ -28077,12 +28143,11 @@ function analyzeRepoHealth() {
28077
28143
  }
28078
28144
  function getCommitStats() {
28079
28145
  try {
28080
- const count = execSync("git rev-list --count HEAD", { encoding: "utf-8" }).trim();
28081
- const authors = execSync('git log --format="%an" | sort -u', {
28082
- encoding: "utf-8",
28146
+ const count = gitExec("git rev-list --count HEAD").trim();
28147
+ const authors = gitExec('git log --format="%an" | sort -u', {
28083
28148
  shell: "/bin/bash"
28084
28149
  }).trim().split("\n");
28085
- const lastCommit = execSync('git log -1 --format="%cr"', { encoding: "utf-8" }).trim();
28150
+ const lastCommit = gitExec('git log -1 --format="%cr"').trim();
28086
28151
  return {
28087
28152
  totalCommits: parseInt(count, 10),
28088
28153
  authors,
@@ -28155,7 +28220,7 @@ var recommendBranchTool = defineTool({
28155
28220
  const branchName = `${prefix}/${slug}`;
28156
28221
  let exists = false;
28157
28222
  try {
28158
- execSync(`git rev-parse --verify ${branchName}`, { stdio: "ignore" });
28223
+ execSync(`git rev-parse --verify ${branchName}`, { cwd: process.cwd(), stdio: "ignore" });
28159
28224
  exists = true;
28160
28225
  } catch {
28161
28226
  exists = false;
@@ -28170,6 +28235,104 @@ var recommendBranchTool = defineTool({
28170
28235
  }
28171
28236
  });
28172
28237
  var gitEnhancedTools = [analyzeRepoHealthTool, getCommitStatsTool, recommendBranchTool];
28238
+ init_allowed_paths();
28239
+ var BLOCKED_SYSTEM_PATHS2 = [
28240
+ "/etc",
28241
+ "/var",
28242
+ "/usr",
28243
+ "/root",
28244
+ "/sys",
28245
+ "/proc",
28246
+ "/boot",
28247
+ "/bin",
28248
+ "/sbin"
28249
+ ];
28250
+ var authorizePathTool = defineTool({
28251
+ name: "authorize_path",
28252
+ description: `Request user permission to access a directory outside the project root.
28253
+
28254
+ Use this BEFORE attempting file operations on external directories. The user will see
28255
+ an interactive prompt where they choose to allow or deny access.
28256
+
28257
+ Returns whether the path was authorized. If authorized, subsequent file operations
28258
+ on that directory will succeed.
28259
+
28260
+ Examples:
28261
+ - Need to read config from another project: authorize_path({ path: "/home/user/other-project" })
28262
+ - Need to access shared libraries: authorize_path({ path: "/opt/shared/libs", reason: "Read shared type definitions" })`,
28263
+ category: "config",
28264
+ parameters: z.object({
28265
+ path: z.string().min(1).describe("Absolute path to the directory to authorize"),
28266
+ reason: z.string().optional().describe("Why access is needed (shown to user for context)")
28267
+ }),
28268
+ async execute({ path: dirPath, reason }) {
28269
+ const absolute = path20__default.resolve(dirPath);
28270
+ if (isWithinAllowedPath(absolute, "read")) {
28271
+ return {
28272
+ authorized: true,
28273
+ path: absolute,
28274
+ message: "Path is already authorized."
28275
+ };
28276
+ }
28277
+ for (const blocked of BLOCKED_SYSTEM_PATHS2) {
28278
+ const normalizedBlocked = path20__default.normalize(blocked);
28279
+ if (absolute === normalizedBlocked || absolute.startsWith(normalizedBlocked + path20__default.sep)) {
28280
+ return {
28281
+ authorized: false,
28282
+ path: absolute,
28283
+ message: `System path '${blocked}' cannot be authorized for security reasons.`
28284
+ };
28285
+ }
28286
+ }
28287
+ const cwd = process.cwd();
28288
+ if (absolute === path20__default.normalize(cwd) || absolute.startsWith(path20__default.normalize(cwd) + path20__default.sep)) {
28289
+ return {
28290
+ authorized: true,
28291
+ path: absolute,
28292
+ message: "Path is within the project directory \u2014 already accessible."
28293
+ };
28294
+ }
28295
+ try {
28296
+ const stat2 = await fs22__default.stat(absolute);
28297
+ if (!stat2.isDirectory()) {
28298
+ return {
28299
+ authorized: false,
28300
+ path: absolute,
28301
+ message: `Not a directory: ${absolute}`
28302
+ };
28303
+ }
28304
+ } catch {
28305
+ return {
28306
+ authorized: false,
28307
+ path: absolute,
28308
+ message: `Directory not found: ${absolute}`
28309
+ };
28310
+ }
28311
+ const existing = getAllowedPaths();
28312
+ if (existing.some((e) => path20__default.normalize(e.path) === path20__default.normalize(absolute))) {
28313
+ return {
28314
+ authorized: true,
28315
+ path: absolute,
28316
+ message: "Path is already authorized."
28317
+ };
28318
+ }
28319
+ const { promptAllowPath: promptAllowPath2 } = await Promise.resolve().then(() => (init_allow_path_prompt(), allow_path_prompt_exports));
28320
+ const wasAuthorized = await promptAllowPath2(absolute);
28321
+ if (wasAuthorized) {
28322
+ return {
28323
+ authorized: true,
28324
+ path: absolute,
28325
+ message: `Access granted to ${absolute}.${reason ? ` Reason: ${reason}` : ""}`
28326
+ };
28327
+ }
28328
+ return {
28329
+ authorized: false,
28330
+ path: absolute,
28331
+ message: "User denied access to this directory."
28332
+ };
28333
+ }
28334
+ });
28335
+ var authorizePathTools = [authorizePathTool];
28173
28336
 
28174
28337
  // src/tools/index.ts
28175
28338
  function registerAllTools(registry) {
@@ -28202,7 +28365,8 @@ function registerAllTools(registry) {
28202
28365
  ...smartSuggestionsTools,
28203
28366
  ...contextEnhancerTools,
28204
28367
  ...skillEnhancerTools,
28205
- ...gitEnhancedTools
28368
+ ...gitEnhancedTools,
28369
+ ...authorizePathTools
28206
28370
  ];
28207
28371
  for (const tool of allTools) {
28208
28372
  registry.register(tool);
@@ -28602,13 +28766,63 @@ function createIntentRecognizer(config = {}) {
28602
28766
 
28603
28767
  // src/cli/repl/index.ts
28604
28768
  init_env();
28769
+ init_allowed_paths();
28605
28770
  init_trust_store();
28606
- ({
28607
- critical: chalk12.red.bold("CRITICAL"),
28608
- major: chalk12.red("MAJOR"),
28609
- minor: chalk12.yellow("minor"),
28610
- info: chalk12.dim("info")
28611
- });
28771
+ var terminalOptions = {
28772
+ // Code blocks
28773
+ code: chalk12.bgGray.white,
28774
+ blockquote: chalk12.gray.italic,
28775
+ // HTML elements
28776
+ html: chalk12.gray,
28777
+ // Headings
28778
+ heading: chalk12.bold.green,
28779
+ firstHeading: chalk12.bold.magenta,
28780
+ // Horizontal rule
28781
+ hr: chalk12.dim,
28782
+ // Lists
28783
+ listitem: chalk12.white,
28784
+ // Tables
28785
+ table: chalk12.white,
28786
+ tableOptions: {
28787
+ chars: {
28788
+ top: "\u2500",
28789
+ "top-mid": "\u252C",
28790
+ "top-left": "\u256D",
28791
+ "top-right": "\u256E",
28792
+ bottom: "\u2500",
28793
+ "bottom-mid": "\u2534",
28794
+ "bottom-left": "\u2570",
28795
+ "bottom-right": "\u256F",
28796
+ left: "\u2502",
28797
+ "left-mid": "\u251C",
28798
+ mid: "\u2500",
28799
+ "mid-mid": "\u253C",
28800
+ right: "\u2502",
28801
+ "right-mid": "\u2524",
28802
+ middle: "\u2502"
28803
+ }
28804
+ },
28805
+ // Emphasis
28806
+ strong: chalk12.bold,
28807
+ em: chalk12.italic,
28808
+ codespan: chalk12.cyan,
28809
+ del: chalk12.strikethrough,
28810
+ // Links
28811
+ link: chalk12.blue.underline,
28812
+ href: chalk12.blue.dim,
28813
+ // Text
28814
+ text: chalk12.white,
28815
+ // Indentation
28816
+ unescape: true,
28817
+ width: 80,
28818
+ showSectionPrefix: false,
28819
+ reflowText: true,
28820
+ tab: 2,
28821
+ // Emoji support
28822
+ emoji: true
28823
+ };
28824
+ var marked = new Marked();
28825
+ marked.use(markedTerminal(terminalOptions));
28612
28826
  var pc = chalk12;
28613
28827
  ({
28614
28828
  pending: pc.dim("\u25CB"),
@@ -28624,24 +28838,6 @@ var pc = chalk12;
28624
28838
  });
28625
28839
 
28626
28840
  // src/cli/repl/index.ts
28627
- function visualWidth(str) {
28628
- const stripped = str.replace(/\x1b\[[0-9;]*m/g, "");
28629
- let width = 0;
28630
- for (const char of stripped) {
28631
- const cp = char.codePointAt(0) || 0;
28632
- if (cp > 128512 || cp >= 9728 && cp <= 10175 || cp >= 127744 && cp <= 129750 || cp >= 129280 && cp <= 129535 || cp >= 9986 && cp <= 10160 || cp >= 65024 && cp <= 65039 || cp === 8205) {
28633
- width += 2;
28634
- } else if (
28635
- // CJK Unified Ideographs and other wide characters
28636
- cp >= 4352 && cp <= 4447 || cp >= 11904 && cp <= 12350 || cp >= 12352 && cp <= 13247 || cp >= 19968 && cp <= 40959 || cp >= 63744 && cp <= 64255 || cp >= 65072 && cp <= 65135 || cp >= 65281 && cp <= 65376 || cp >= 65504 && cp <= 65510
28637
- ) {
28638
- width += 2;
28639
- } else {
28640
- width += 1;
28641
- }
28642
- }
28643
- return width;
28644
- }
28645
28841
  async function startRepl(options = {}) {
28646
28842
  const projectPath = options.projectPath ?? process.cwd();
28647
28843
  const session = createSession(projectPath, options.config);
@@ -28781,11 +28977,21 @@ async function startRepl(options = {}) {
28781
28977
  activeSpinner.start();
28782
28978
  }
28783
28979
  };
28980
+ let thinkingInterval = null;
28981
+ let thinkingStartTime = null;
28982
+ const clearThinkingInterval = () => {
28983
+ if (thinkingInterval) {
28984
+ clearInterval(thinkingInterval);
28985
+ thinkingInterval = null;
28986
+ }
28987
+ thinkingStartTime = null;
28988
+ };
28784
28989
  const abortController = new AbortController();
28785
28990
  let wasAborted = false;
28786
28991
  const sigintHandler = () => {
28787
28992
  wasAborted = true;
28788
28993
  abortController.abort();
28994
+ clearThinkingInterval();
28789
28995
  clearSpinner();
28790
28996
  renderInfo("\nOperation cancelled");
28791
28997
  };
@@ -28824,8 +29030,19 @@ async function startRepl(options = {}) {
28824
29030
  },
28825
29031
  onThinkingStart: () => {
28826
29032
  setSpinner("Thinking...");
29033
+ thinkingStartTime = Date.now();
29034
+ thinkingInterval = setInterval(() => {
29035
+ if (!thinkingStartTime) return;
29036
+ const elapsed = Math.floor((Date.now() - thinkingStartTime) / 1e3);
29037
+ if (elapsed < 4) return;
29038
+ if (elapsed < 8) setSpinner("Analyzing request...");
29039
+ else if (elapsed < 12) setSpinner("Planning approach...");
29040
+ else if (elapsed < 16) setSpinner("Preparing tools...");
29041
+ else setSpinner(`Still working... (${elapsed}s)`);
29042
+ }, 2e3);
28827
29043
  },
28828
29044
  onThinkingEnd: () => {
29045
+ clearThinkingInterval();
28829
29046
  clearSpinner();
28830
29047
  },
28831
29048
  onToolPreparing: (toolName) => {
@@ -28836,6 +29053,7 @@ async function startRepl(options = {}) {
28836
29053
  },
28837
29054
  signal: abortController.signal
28838
29055
  });
29056
+ clearThinkingInterval();
28839
29057
  process.off("SIGINT", sigintHandler);
28840
29058
  if (wasAborted || result.aborted) {
28841
29059
  if (result.partialContent) {
@@ -28888,6 +29106,7 @@ async function startRepl(options = {}) {
28888
29106
  }
28889
29107
  console.log();
28890
29108
  } catch (error) {
29109
+ clearThinkingInterval();
28891
29110
  clearSpinner();
28892
29111
  process.off("SIGINT", sigintHandler);
28893
29112
  if (error instanceof Error && error.name === "AbortError") {
@@ -28920,40 +29139,30 @@ async function startRepl(options = {}) {
28920
29139
  inputHandler.close();
28921
29140
  }
28922
29141
  async function printWelcome(session) {
28923
- const providerType = session.config.provider.type;
28924
- const model = session.config.provider.model || "default";
28925
- const isReturningUser = existsSync(path20__default.join(session.projectPath, ".coco"));
28926
- if (isReturningUser) {
28927
- const versionStr = chalk12.dim(`v${VERSION}`);
28928
- const providerStr = chalk12.dim(`${providerType}/${model}`);
28929
- console.log(
28930
- `
28931
- \u{1F965} Coco ${versionStr} ${chalk12.dim("\u2502")} ${providerStr} ${chalk12.dim("\u2502")} ${chalk12.yellow("/help")}
28932
- `
28933
- );
28934
- return;
28935
- }
28936
29142
  const trustStore = createTrustStore();
28937
29143
  await trustStore.init();
28938
29144
  const trustLevel = trustStore.getLevel(session.projectPath);
28939
29145
  const boxWidth = 41;
28940
- const innerWidth = boxWidth - 4;
28941
- const titleContent = "\u{1F965} CORBAT-COCO";
29146
+ const innerWidth = boxWidth - 2;
28942
29147
  const versionText = `v${VERSION}`;
28943
- const titleContentVisualWidth = visualWidth(titleContent);
28944
- const versionVisualWidth = visualWidth(versionText);
28945
- const titlePadding = innerWidth - titleContentVisualWidth - versionVisualWidth;
28946
29148
  const subtitleText = "open source \u2022 corbat.tech";
28947
- const subtitleVisualWidth = visualWidth(subtitleText);
28948
- const subtitlePadding = innerWidth - subtitleVisualWidth;
29149
+ const boxLine = (content) => {
29150
+ const pad = Math.max(0, innerWidth - stringWidth(content));
29151
+ return chalk12.magenta("\u2502") + content + " ".repeat(pad) + chalk12.magenta("\u2502");
29152
+ };
29153
+ const titleLeftRaw = " COCO";
29154
+ const titleRightRaw = versionText + " ";
29155
+ const titleLeftStyled = " " + chalk12.bold.white("COCO");
29156
+ const titleGap = Math.max(1, innerWidth - stringWidth(titleLeftRaw) - stringWidth(titleRightRaw));
29157
+ const titleContent = titleLeftStyled + " ".repeat(titleGap) + chalk12.dim(titleRightRaw);
29158
+ const taglineText = "code that converges to quality";
29159
+ const taglineContent = " " + chalk12.magenta(taglineText) + " ";
29160
+ const subtitleContent = " " + chalk12.dim(subtitleText) + " ";
28949
29161
  console.log();
28950
29162
  console.log(chalk12.magenta(" \u256D" + "\u2500".repeat(boxWidth - 2) + "\u256E"));
28951
- console.log(
28952
- chalk12.magenta(" \u2502 ") + "\u{1F965} " + chalk12.bold.white("CORBAT-COCO") + " ".repeat(Math.max(0, titlePadding)) + chalk12.dim(versionText) + chalk12.magenta(" \u2502")
28953
- );
28954
- console.log(
28955
- chalk12.magenta(" \u2502 ") + chalk12.dim(subtitleText) + " ".repeat(Math.max(0, subtitlePadding)) + chalk12.magenta(" \u2502")
28956
- );
29163
+ console.log(" " + boxLine(titleContent));
29164
+ console.log(" " + boxLine(taglineContent));
29165
+ console.log(" " + boxLine(subtitleContent));
28957
29166
  console.log(chalk12.magenta(" \u2570" + "\u2500".repeat(boxWidth - 2) + "\u256F"));
28958
29167
  const updateInfo = await checkForUpdates();
28959
29168
  if (updateInfo) {
@@ -28968,11 +29177,14 @@ async function printWelcome(session) {
28968
29177
  if (displayPath.length > maxPathLen) {
28969
29178
  displayPath = "..." + displayPath.slice(-maxPathLen + 3);
28970
29179
  }
29180
+ const lastSep = displayPath.lastIndexOf("/");
29181
+ const parentPath = lastSep > 0 ? displayPath.slice(0, lastSep + 1) : "";
29182
+ const projectName = lastSep > 0 ? displayPath.slice(lastSep + 1) : displayPath;
28971
29183
  const providerName = session.config.provider.type;
28972
29184
  const modelName = session.config.provider.model || "default";
28973
29185
  const trustText = trustLevel === "full" ? "full" : trustLevel === "write" ? "write" : trustLevel === "read" ? "read" : "";
28974
29186
  console.log();
28975
- console.log(chalk12.dim(` \u{1F4C1} ${displayPath}`));
29187
+ console.log(chalk12.dim(` \u{1F4C1} ${parentPath}`) + chalk12.magenta.bold(projectName));
28976
29188
  console.log(
28977
29189
  chalk12.dim(` \u{1F916} ${providerName}/`) + chalk12.magenta(modelName) + (trustText ? chalk12.dim(` \u2022 \u{1F510} ${trustText}`) : "")
28978
29190
  );
@@ -28994,7 +29206,7 @@ async function checkProjectTrust(projectPath) {
28994
29206
  return true;
28995
29207
  }
28996
29208
  console.log();
28997
- console.log(chalk12.cyan.bold(" \u{1F965} Corbat-Coco") + chalk12.dim(` v${VERSION}`));
29209
+ console.log(chalk12.cyan.bold(" \u{1F965} Coco") + chalk12.dim(` v${VERSION}`));
28998
29210
  console.log(chalk12.dim(` \u{1F4C1} ${projectPath}`));
28999
29211
  console.log();
29000
29212
  console.log(chalk12.yellow(" \u26A0 First time accessing this directory"));