@corbat-tech/coco 1.2.2 → 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
@@ -7,10 +7,10 @@ import * as fs19 from 'fs';
7
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';
@@ -46,6 +46,8 @@ import { glob } from 'glob';
46
46
  import { simpleGit } from 'simple-git';
47
47
  import { parse } from '@typescript-eslint/typescript-estree';
48
48
  import 'events';
49
+ import { Marked } from 'marked';
50
+ import { markedTerminal } from 'marked-terminal';
49
51
 
50
52
  var __defProp = Object.defineProperty;
51
53
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -280,8 +282,8 @@ __export(trust_store_exports, {
280
282
  saveTrustStore: () => saveTrustStore,
281
283
  updateLastAccessed: () => updateLastAccessed
282
284
  });
283
- async function ensureDir(path36) {
284
- await mkdir(dirname(path36), { recursive: true });
285
+ async function ensureDir(path39) {
286
+ await mkdir(dirname(path39), { recursive: true });
285
287
  }
286
288
  async function loadTrustStore(storePath = TRUST_STORE_PATH) {
287
289
  try {
@@ -359,8 +361,8 @@ function canPerformOperation(store, projectPath, operation) {
359
361
  };
360
362
  return permissions[level]?.includes(operation) ?? false;
361
363
  }
362
- function normalizePath(path36) {
363
- return join(path36);
364
+ function normalizePath(path39) {
365
+ return join(path39);
364
366
  }
365
367
  function createTrustStore(storePath = TRUST_STORE_PATH) {
366
368
  let store = null;
@@ -452,6 +454,149 @@ var init_trust_store = __esm({
452
454
  TRUST_STORE_PATH = CONFIG_PATHS.projects;
453
455
  }
454
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
+ });
455
600
  function findPackageJson() {
456
601
  let dir = dirname(fileURLToPath(import.meta.url));
457
602
  for (let i = 0; i < 10; i++) {
@@ -610,8 +755,8 @@ Generated by Corbat-Coco v0.1.0
610
755
 
611
756
  // src/cli/commands/init.ts
612
757
  function registerInitCommand(program2) {
613
- 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 (path36, options) => {
614
- await runInit(path36, 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);
615
760
  });
616
761
  }
617
762
  async function runInit(projectPath, options) {
@@ -690,18 +835,18 @@ async function gatherProjectInfo() {
690
835
  language
691
836
  };
692
837
  }
693
- function getDefaultProjectInfo(path36) {
694
- const name = path36 === "." ? "my-project" : path36.split("/").pop() || "my-project";
838
+ function getDefaultProjectInfo(path39) {
839
+ const name = path39 === "." ? "my-project" : path39.split("/").pop() || "my-project";
695
840
  return {
696
841
  name,
697
842
  description: "",
698
843
  language: "typescript"
699
844
  };
700
845
  }
701
- async function checkExistingProject(path36) {
846
+ async function checkExistingProject(path39) {
702
847
  try {
703
- const fs39 = await import('fs/promises');
704
- await fs39.access(`${path36}/.coco`);
848
+ const fs41 = await import('fs/promises');
849
+ await fs41.access(`${path39}/.coco`);
705
850
  return true;
706
851
  } catch {
707
852
  return false;
@@ -7649,20 +7794,20 @@ async function createCliPhaseContext(projectPath, _onUserInput) {
7649
7794
  },
7650
7795
  tools: {
7651
7796
  file: {
7652
- async read(path36) {
7653
- const fs39 = await import('fs/promises');
7654
- return fs39.readFile(path36, "utf-8");
7797
+ async read(path39) {
7798
+ const fs41 = await import('fs/promises');
7799
+ return fs41.readFile(path39, "utf-8");
7655
7800
  },
7656
- async write(path36, content) {
7657
- const fs39 = await import('fs/promises');
7801
+ async write(path39, content) {
7802
+ const fs41 = await import('fs/promises');
7658
7803
  const nodePath = await import('path');
7659
- await fs39.mkdir(nodePath.dirname(path36), { recursive: true });
7660
- await fs39.writeFile(path36, content, "utf-8");
7804
+ await fs41.mkdir(nodePath.dirname(path39), { recursive: true });
7805
+ await fs41.writeFile(path39, content, "utf-8");
7661
7806
  },
7662
- async exists(path36) {
7663
- const fs39 = await import('fs/promises');
7807
+ async exists(path39) {
7808
+ const fs41 = await import('fs/promises');
7664
7809
  try {
7665
- await fs39.access(path36);
7810
+ await fs41.access(path39);
7666
7811
  return true;
7667
7812
  } catch {
7668
7813
  return false;
@@ -7901,16 +8046,16 @@ async function loadTasks(_options) {
7901
8046
  ];
7902
8047
  }
7903
8048
  async function checkProjectState() {
7904
- const fs39 = await import('fs/promises');
8049
+ const fs41 = await import('fs/promises');
7905
8050
  let hasProject = false;
7906
8051
  let hasPlan = false;
7907
8052
  try {
7908
- await fs39.access(".coco");
8053
+ await fs41.access(".coco");
7909
8054
  hasProject = true;
7910
8055
  } catch {
7911
8056
  }
7912
8057
  try {
7913
- await fs39.access(".coco/planning/backlog.json");
8058
+ await fs41.access(".coco/planning/backlog.json");
7914
8059
  hasPlan = true;
7915
8060
  } catch {
7916
8061
  }
@@ -8014,24 +8159,24 @@ function getPhaseStatusForPhase(phase) {
8014
8159
  return "in_progress";
8015
8160
  }
8016
8161
  async function loadProjectState(cwd, config) {
8017
- const fs39 = await import('fs/promises');
8018
- const path36 = await import('path');
8019
- const statePath = path36.join(cwd, ".coco", "state.json");
8020
- const backlogPath = path36.join(cwd, ".coco", "planning", "backlog.json");
8021
- const checkpointDir = path36.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");
8022
8167
  let currentPhase = "idle";
8023
8168
  let metrics;
8024
8169
  let sprint;
8025
8170
  let checkpoints = [];
8026
8171
  try {
8027
- const stateContent = await fs39.readFile(statePath, "utf-8");
8172
+ const stateContent = await fs41.readFile(statePath, "utf-8");
8028
8173
  const stateData = JSON.parse(stateContent);
8029
8174
  currentPhase = stateData.currentPhase || "idle";
8030
8175
  metrics = stateData.metrics;
8031
8176
  } catch {
8032
8177
  }
8033
8178
  try {
8034
- const backlogContent = await fs39.readFile(backlogPath, "utf-8");
8179
+ const backlogContent = await fs41.readFile(backlogPath, "utf-8");
8035
8180
  const backlogData = JSON.parse(backlogContent);
8036
8181
  if (backlogData.currentSprint) {
8037
8182
  const tasks = backlogData.tasks || [];
@@ -8053,7 +8198,7 @@ async function loadProjectState(cwd, config) {
8053
8198
  } catch {
8054
8199
  }
8055
8200
  try {
8056
- const files = await fs39.readdir(checkpointDir);
8201
+ const files = await fs41.readdir(checkpointDir);
8057
8202
  checkpoints = files.filter((f) => f.endsWith(".json")).sort().reverse();
8058
8203
  } catch {
8059
8204
  }
@@ -8189,8 +8334,8 @@ async function restoreFromCheckpoint(_checkpoint) {
8189
8334
  }
8190
8335
  async function checkProjectExists() {
8191
8336
  try {
8192
- const fs39 = await import('fs/promises');
8193
- await fs39.access(".coco");
8337
+ const fs41 = await import('fs/promises');
8338
+ await fs41.access(".coco");
8194
8339
  return true;
8195
8340
  } catch {
8196
8341
  return false;
@@ -8729,12 +8874,12 @@ async function loadConfig2() {
8729
8874
  };
8730
8875
  }
8731
8876
  async function saveConfig(config) {
8732
- const fs39 = await import('fs/promises');
8733
- await fs39.mkdir(".coco", { recursive: true });
8734
- 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));
8735
8880
  }
8736
- function getNestedValue(obj, path36) {
8737
- const keys = path36.split(".");
8881
+ function getNestedValue(obj, path39) {
8882
+ const keys = path39.split(".");
8738
8883
  let current = obj;
8739
8884
  for (const key of keys) {
8740
8885
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -8744,8 +8889,8 @@ function getNestedValue(obj, path36) {
8744
8889
  }
8745
8890
  return current;
8746
8891
  }
8747
- function setNestedValue(obj, path36, value) {
8748
- const keys = path36.split(".");
8892
+ function setNestedValue(obj, path39, value) {
8893
+ const keys = path39.split(".");
8749
8894
  let current = obj;
8750
8895
  for (let i = 0; i < keys.length - 1; i++) {
8751
8896
  const key = keys[i];
@@ -8966,8 +9111,8 @@ var MCPRegistryImpl = class {
8966
9111
  /**
8967
9112
  * Ensure directory exists
8968
9113
  */
8969
- async ensureDir(path36) {
8970
- await mkdir(dirname(path36), { recursive: true });
9114
+ async ensureDir(path39) {
9115
+ await mkdir(dirname(path39), { recursive: true });
8971
9116
  }
8972
9117
  };
8973
9118
  function createMCPRegistry(registryPath) {
@@ -9486,9 +9631,9 @@ function createEmptyMemoryContext() {
9486
9631
  errors: []
9487
9632
  };
9488
9633
  }
9489
- function createMissingMemoryFile(path36, level) {
9634
+ function createMissingMemoryFile(path39, level) {
9490
9635
  return {
9491
- path: path36,
9636
+ path: path39,
9492
9637
  level,
9493
9638
  content: "",
9494
9639
  sections: [],
@@ -9572,10 +9717,12 @@ When the user asks you to do something, follow this process:
9572
9717
  - Always verify your work by reading files after editing or running tests after changes
9573
9718
  - You can use multiple tools together for complex tasks
9574
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.
9575
9721
 
9576
9722
  ## File Access
9577
9723
  File operations are restricted to the project directory by default.
9578
- 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.
9579
9726
 
9580
9727
  ## Output Formatting Rules
9581
9728
 
@@ -12179,8 +12326,8 @@ async function listTrustedProjects2(trustStore) {
12179
12326
  p9.log.message("");
12180
12327
  for (const project of projects) {
12181
12328
  const level = project.approvalLevel.toUpperCase().padEnd(5);
12182
- const path36 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
12183
- p9.log.message(` [${level}] ${path36}`);
12329
+ const path39 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
12330
+ p9.log.message(` [${level}] ${path39}`);
12184
12331
  p9.log.message(` Last accessed: ${new Date(project.lastAccessed).toLocaleString()}`);
12185
12332
  }
12186
12333
  p9.log.message("");
@@ -13350,7 +13497,7 @@ async function getCheckpoint(session, checkpointId) {
13350
13497
  return store.checkpoints.find((cp) => cp.id === checkpointId) ?? null;
13351
13498
  }
13352
13499
  async function restoreFiles(checkpoint, excludeFiles) {
13353
- const fs39 = await import('fs/promises');
13500
+ const fs41 = await import('fs/promises');
13354
13501
  const restored = [];
13355
13502
  const failed = [];
13356
13503
  for (const fileCheckpoint of checkpoint.files) {
@@ -13358,7 +13505,7 @@ async function restoreFiles(checkpoint, excludeFiles) {
13358
13505
  continue;
13359
13506
  }
13360
13507
  try {
13361
- await fs39.writeFile(fileCheckpoint.filePath, fileCheckpoint.originalContent, "utf-8");
13508
+ await fs41.writeFile(fileCheckpoint.filePath, fileCheckpoint.originalContent, "utf-8");
13362
13509
  restored.push(fileCheckpoint.filePath);
13363
13510
  } catch (error) {
13364
13511
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -13440,8 +13587,8 @@ function displayRewindResult(result) {
13440
13587
  const fileName = filePath.split("/").pop() ?? filePath;
13441
13588
  console.log(`${chalk12.green(String.fromCodePoint(10003))} Restored: ${fileName}`);
13442
13589
  }
13443
- for (const { path: path36, error } of result.filesFailed) {
13444
- const fileName = path36.split("/").pop() ?? path36;
13590
+ for (const { path: path39, error } of result.filesFailed) {
13591
+ const fileName = path39.split("/").pop() ?? path39;
13445
13592
  console.log(`${chalk12.red(String.fromCodePoint(10007))} Failed: ${fileName} (${error})`);
13446
13593
  }
13447
13594
  if (result.conversationRestored) {
@@ -14874,8 +15021,8 @@ function formatToolSummary(toolName, input) {
14874
15021
  return String(input.path || ".");
14875
15022
  case "search_files": {
14876
15023
  const pattern = String(input.pattern || "");
14877
- const path36 = input.path ? ` in ${input.path}` : "";
14878
- return `"${pattern}"${path36}`;
15024
+ const path39 = input.path ? ` in ${input.path}` : "";
15025
+ return `"${pattern}"${path39}`;
14879
15026
  }
14880
15027
  case "bash_exec": {
14881
15028
  const cmd = String(input.command || "");
@@ -15177,106 +15324,8 @@ var copyCommand = {
15177
15324
  }
15178
15325
  };
15179
15326
 
15180
- // src/tools/allowed-paths.ts
15181
- init_paths();
15182
- var STORE_FILE = path20__default.join(CONFIG_PATHS.home, "allowed-paths.json");
15183
- var DEFAULT_STORE = {
15184
- version: 1,
15185
- projects: {}
15186
- };
15187
- var sessionAllowedPaths = [];
15188
- var currentProjectPath = "";
15189
- function getAllowedPaths() {
15190
- return [...sessionAllowedPaths];
15191
- }
15192
- function isWithinAllowedPath(absolutePath, operation) {
15193
- const normalizedTarget = path20__default.normalize(absolutePath);
15194
- for (const entry of sessionAllowedPaths) {
15195
- const normalizedAllowed = path20__default.normalize(entry.path);
15196
- if (normalizedTarget === normalizedAllowed || normalizedTarget.startsWith(normalizedAllowed + path20__default.sep)) {
15197
- if (operation === "read") return true;
15198
- if (entry.level === "write") return true;
15199
- }
15200
- }
15201
- return false;
15202
- }
15203
- function addAllowedPathToSession(dirPath, level) {
15204
- const absolute = path20__default.resolve(dirPath);
15205
- if (sessionAllowedPaths.some((e) => path20__default.normalize(e.path) === path20__default.normalize(absolute))) {
15206
- return;
15207
- }
15208
- sessionAllowedPaths.push({
15209
- path: absolute,
15210
- authorizedAt: (/* @__PURE__ */ new Date()).toISOString(),
15211
- level
15212
- });
15213
- }
15214
- function removeAllowedPathFromSession(dirPath) {
15215
- const absolute = path20__default.resolve(dirPath);
15216
- const normalized = path20__default.normalize(absolute);
15217
- const before = sessionAllowedPaths.length;
15218
- sessionAllowedPaths = sessionAllowedPaths.filter((e) => path20__default.normalize(e.path) !== normalized);
15219
- return sessionAllowedPaths.length < before;
15220
- }
15221
- async function loadAllowedPaths(projectPath) {
15222
- currentProjectPath = path20__default.resolve(projectPath);
15223
- const store = await loadStore();
15224
- const entries = store.projects[currentProjectPath] ?? [];
15225
- for (const entry of entries) {
15226
- addAllowedPathToSession(entry.path, entry.level);
15227
- }
15228
- }
15229
- async function persistAllowedPath(dirPath, level) {
15230
- if (!currentProjectPath) return;
15231
- const absolute = path20__default.resolve(dirPath);
15232
- const store = await loadStore();
15233
- if (!store.projects[currentProjectPath]) {
15234
- store.projects[currentProjectPath] = [];
15235
- }
15236
- const entries = store.projects[currentProjectPath];
15237
- const normalized = path20__default.normalize(absolute);
15238
- if (entries.some((e) => path20__default.normalize(e.path) === normalized)) {
15239
- return;
15240
- }
15241
- entries.push({
15242
- path: absolute,
15243
- authorizedAt: (/* @__PURE__ */ new Date()).toISOString(),
15244
- level
15245
- });
15246
- await saveStore(store);
15247
- }
15248
- async function removePersistedAllowedPath(dirPath) {
15249
- if (!currentProjectPath) return false;
15250
- const absolute = path20__default.resolve(dirPath);
15251
- const normalized = path20__default.normalize(absolute);
15252
- const store = await loadStore();
15253
- const entries = store.projects[currentProjectPath];
15254
- if (!entries) return false;
15255
- const before = entries.length;
15256
- store.projects[currentProjectPath] = entries.filter((e) => path20__default.normalize(e.path) !== normalized);
15257
- if (store.projects[currentProjectPath].length < before) {
15258
- await saveStore(store);
15259
- return true;
15260
- }
15261
- return false;
15262
- }
15263
- async function loadStore() {
15264
- try {
15265
- const content = await fs22__default.readFile(STORE_FILE, "utf-8");
15266
- return { ...DEFAULT_STORE, ...JSON.parse(content) };
15267
- } catch {
15268
- return { ...DEFAULT_STORE };
15269
- }
15270
- }
15271
- async function saveStore(store) {
15272
- try {
15273
- await fs22__default.mkdir(path20__default.dirname(STORE_FILE), { recursive: true });
15274
- await fs22__default.writeFile(STORE_FILE, JSON.stringify(store, null, 2), "utf-8");
15275
- } catch {
15276
- }
15277
- }
15278
-
15279
15327
  // src/cli/repl/commands/allow-path.ts
15328
+ init_allowed_paths();
15280
15329
  var BLOCKED_SYSTEM_PATHS = [
15281
15330
  "/etc",
15282
15331
  "/var",
@@ -18027,38 +18076,9 @@ function extractDeniedPath(error) {
18027
18076
  const match = error.match(/Use \/allow-path (.+?) to grant access/);
18028
18077
  return match?.[1] ?? null;
18029
18078
  }
18030
- async function promptAllowPath(dirPath) {
18031
- const absolute = path20__default.resolve(dirPath);
18032
- console.log();
18033
- console.log(chalk12.yellow(" \u26A0 Access denied \u2014 path is outside the project directory"));
18034
- console.log(chalk12.dim(` \u{1F4C1} ${absolute}`));
18035
- console.log();
18036
- const action = await p9.select({
18037
- message: "Grant access to this directory?",
18038
- options: [
18039
- { value: "session-write", label: "\u2713 Allow write (this session)" },
18040
- { value: "session-read", label: "\u25D0 Allow read-only (this session)" },
18041
- { value: "persist-write", label: "\u26A1 Allow write (remember for this project)" },
18042
- { value: "persist-read", label: "\u{1F4BE} Allow read-only (remember for this project)" },
18043
- { value: "no", label: "\u2717 Deny" }
18044
- ]
18045
- });
18046
- if (p9.isCancel(action) || action === "no") {
18047
- return false;
18048
- }
18049
- const level = action.includes("read") ? "read" : "write";
18050
- const persist = action.startsWith("persist");
18051
- addAllowedPathToSession(absolute, level);
18052
- if (persist) {
18053
- await persistAllowedPath(absolute, level);
18054
- }
18055
- const levelLabel = level === "write" ? "write" : "read-only";
18056
- const persistLabel = persist ? " (remembered)" : "";
18057
- console.log(chalk12.green(` \u2713 Access granted: ${levelLabel}${persistLabel}`));
18058
- return true;
18059
- }
18060
18079
 
18061
18080
  // src/cli/repl/agent-loop.ts
18081
+ init_allow_path_prompt();
18062
18082
  async function executeAgentTurn(session, userMessage, provider, toolRegistry, options = {}) {
18063
18083
  resetLineBuffer();
18064
18084
  addMessage(session, { role: "user", content: userMessage });
@@ -18342,6 +18362,7 @@ function formatAbortSummary(executedTools) {
18342
18362
  }
18343
18363
  return summary;
18344
18364
  }
18365
+ init_allowed_paths();
18345
18366
  var SENSITIVE_PATTERNS = [
18346
18367
  /\.env(?:\.\w+)?$/,
18347
18368
  // .env, .env.local, etc.
@@ -19205,7 +19226,8 @@ function truncateOutput(output, maxLength = 5e4) {
19205
19226
  [Output truncated - ${output.length - maxLength} more characters]`;
19206
19227
  }
19207
19228
  function getGit(cwd) {
19208
- return simpleGit(cwd ?? process.cwd());
19229
+ const baseDir = cwd ?? process.cwd();
19230
+ return simpleGit({ baseDir });
19209
19231
  }
19210
19232
  var gitStatusTool = defineTool({
19211
19233
  name: "git_status",
@@ -19566,9 +19588,11 @@ var gitTools = [
19566
19588
  gitInitTool
19567
19589
  ];
19568
19590
  function generateSimpleCommitMessage() {
19591
+ const cwd = process.cwd();
19569
19592
  try {
19570
19593
  const diff = execSync("git diff --cached --name-only", {
19571
19594
  encoding: "utf-8",
19595
+ cwd,
19572
19596
  stdio: ["pipe", "pipe", "ignore"]
19573
19597
  });
19574
19598
  const files = diff.trim().split("\n").filter(Boolean);
@@ -19604,6 +19628,7 @@ var checkProtectedBranchTool = defineTool({
19604
19628
  try {
19605
19629
  const branch = execSync("git rev-parse --abbrev-ref HEAD", {
19606
19630
  encoding: "utf-8",
19631
+ cwd: process.cwd(),
19607
19632
  stdio: ["pipe", "pipe", "ignore"]
19608
19633
  }).trim();
19609
19634
  const protected_branches = ["main", "master", "develop", "production"];
@@ -19639,7 +19664,7 @@ var simpleAutoCommitTool = defineTool({
19639
19664
  async execute(input) {
19640
19665
  try {
19641
19666
  try {
19642
- execSync("git diff --cached --quiet", { stdio: "ignore" });
19667
+ execSync("git diff --cached --quiet", { cwd: process.cwd(), stdio: "ignore" });
19643
19668
  return {
19644
19669
  stdout: "",
19645
19670
  stderr: "No staged changes to commit",
@@ -19651,6 +19676,7 @@ var simpleAutoCommitTool = defineTool({
19651
19676
  const message = input.message || generateSimpleCommitMessage();
19652
19677
  execSync(`git commit -m "${message}"`, {
19653
19678
  encoding: "utf-8",
19679
+ cwd: process.cwd(),
19654
19680
  stdio: "pipe"
19655
19681
  });
19656
19682
  return {
@@ -20319,10 +20345,10 @@ var CoverageAnalyzer = class {
20319
20345
  join(this.projectPath, ".coverage", "coverage-summary.json"),
20320
20346
  join(this.projectPath, "coverage", "lcov-report", "coverage-summary.json")
20321
20347
  ];
20322
- for (const path36 of possiblePaths) {
20348
+ for (const path39 of possiblePaths) {
20323
20349
  try {
20324
- await access(path36, constants.R_OK);
20325
- const content = await readFile(path36, "utf-8");
20350
+ await access(path39, constants.R_OK);
20351
+ const content = await readFile(path39, "utf-8");
20326
20352
  const report = JSON.parse(content);
20327
20353
  return parseCoverageSummary(report);
20328
20354
  } catch {
@@ -24504,9 +24530,17 @@ Examples:
24504
24530
  }
24505
24531
  });
24506
24532
  var diffTools = [showDiffTool];
24507
- async function fileExists(path36) {
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) {
24508
24542
  try {
24509
- await access(path36);
24543
+ await access(path39);
24510
24544
  return true;
24511
24545
  } catch {
24512
24546
  return false;
@@ -24521,7 +24555,7 @@ async function dirHasFiles(dir) {
24521
24555
  }
24522
24556
  }
24523
24557
  async function detectMaturity(cwd) {
24524
- const hasPackageJson = await fileExists(join(cwd, "package.json"));
24558
+ const hasPackageJson = await fileExists2(join(cwd, "package.json"));
24525
24559
  if (!hasPackageJson) {
24526
24560
  const otherManifests = [
24527
24561
  "go.mod",
@@ -24534,7 +24568,7 @@ async function detectMaturity(cwd) {
24534
24568
  ];
24535
24569
  let hasAnyManifest = false;
24536
24570
  for (const m of otherManifests) {
24537
- if (await fileExists(join(cwd, m))) {
24571
+ if (await fileExists2(join(cwd, m))) {
24538
24572
  hasAnyManifest = true;
24539
24573
  break;
24540
24574
  }
@@ -24575,7 +24609,7 @@ async function detectMaturity(cwd) {
24575
24609
  cwd,
24576
24610
  ignore: ["node_modules/**", "dist/**", "build/**"]
24577
24611
  });
24578
- 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"));
24579
24613
  const lintConfigs = [
24580
24614
  ".eslintrc.js",
24581
24615
  ".eslintrc.json",
@@ -24588,7 +24622,7 @@ async function detectMaturity(cwd) {
24588
24622
  ];
24589
24623
  let hasLintConfig = false;
24590
24624
  for (const config of lintConfigs) {
24591
- if (await fileExists(join(cwd, config))) {
24625
+ if (await fileExists2(join(cwd, config))) {
24592
24626
  hasLintConfig = true;
24593
24627
  break;
24594
24628
  }
@@ -24596,7 +24630,7 @@ async function detectMaturity(cwd) {
24596
24630
  if (!hasLintConfig && hasPackageJson) {
24597
24631
  try {
24598
24632
  const pkgRaw = await import('fs/promises').then(
24599
- (fs39) => fs39.readFile(join(cwd, "package.json"), "utf-8")
24633
+ (fs41) => fs41.readFile(join(cwd, "package.json"), "utf-8")
24600
24634
  );
24601
24635
  const pkg = JSON.parse(pkgRaw);
24602
24636
  if (pkg.scripts?.lint || pkg.scripts?.["lint:fix"]) {
@@ -24641,7 +24675,8 @@ var SECURITY_PATTERNS2 = [
24641
24675
  regex: /console\.(log|debug|info)\(/,
24642
24676
  severity: "minor",
24643
24677
  category: "best-practice",
24644
- 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)\//
24645
24680
  }
24646
24681
  ];
24647
24682
  var CORRECTNESS_PATTERNS = [
@@ -24721,6 +24756,7 @@ function analyzePatterns(diff) {
24721
24756
  for (const line of hunk.lines) {
24722
24757
  if (line.type !== "add") continue;
24723
24758
  for (const pattern of ALL_PATTERNS) {
24759
+ if (pattern.excludePaths?.test(file.path)) continue;
24724
24760
  if (pattern.regex.test(line.content)) {
24725
24761
  findings.push({
24726
24762
  file: file.path,
@@ -24737,7 +24773,8 @@ function analyzePatterns(diff) {
24737
24773
  }
24738
24774
  return findings;
24739
24775
  }
24740
- function checkTestCoverage(diff) {
24776
+ var TEST_COVERAGE_LARGE_CHANGE_THRESHOLD = 15;
24777
+ async function checkTestCoverage(diff, cwd) {
24741
24778
  const findings = [];
24742
24779
  const changedSrc = [];
24743
24780
  const changedTests = /* @__PURE__ */ new Set();
@@ -24747,22 +24784,35 @@ function checkTestCoverage(diff) {
24747
24784
  changedTests.add(file.path);
24748
24785
  } else if (/\.(ts|tsx|js|jsx)$/.test(file.path)) {
24749
24786
  if (file.additions > 5) {
24750
- changedSrc.push(file.path);
24787
+ changedSrc.push({ path: file.path, additions: file.additions });
24751
24788
  }
24752
24789
  }
24753
24790
  }
24754
24791
  for (const src of changedSrc) {
24755
- const baseName = src.replace(/\.(ts|tsx|js|jsx)$/, "");
24792
+ const baseName = src.path.replace(/\.(ts|tsx|js|jsx)$/, "");
24756
24793
  const hasTestChange = [...changedTests].some(
24757
24794
  (t) => t.includes(baseName.split("/").pop()) || t.startsWith(baseName)
24758
24795
  );
24759
24796
  if (!hasTestChange) {
24760
- findings.push({
24761
- file: src,
24762
- severity: "minor",
24763
- category: "testing",
24764
- message: "Logic changes without corresponding test updates"
24765
- });
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
+ }
24766
24816
  }
24767
24817
  }
24768
24818
  return findings;
@@ -24912,7 +24962,7 @@ Examples:
24912
24962
  const maturity = maturityInfo.level;
24913
24963
  let allFindings = [];
24914
24964
  allFindings.push(...analyzePatterns(diff));
24915
- allFindings.push(...checkTestCoverage(diff));
24965
+ allFindings.push(...await checkTestCoverage(diff, projectDir));
24916
24966
  allFindings.push(...checkDocumentation(diff));
24917
24967
  if (runLinter) {
24918
24968
  try {
@@ -24963,8 +25013,8 @@ Examples:
24963
25013
  }
24964
25014
  });
24965
25015
  var reviewTools = [reviewCodeTool];
24966
- var fs28 = await import('fs/promises');
24967
- var path26 = await import('path');
25016
+ var fs29 = await import('fs/promises');
25017
+ var path28 = await import('path');
24968
25018
  var { glob: glob12 } = await import('glob');
24969
25019
  var DEFAULT_MAX_FILES = 200;
24970
25020
  var LANGUAGE_EXTENSIONS = {
@@ -24990,7 +25040,7 @@ var DEFAULT_EXCLUDES = [
24990
25040
  "**/*.d.ts"
24991
25041
  ];
24992
25042
  function detectLanguage2(filePath) {
24993
- const ext = path26.extname(filePath).toLowerCase();
25043
+ const ext = path28.extname(filePath).toLowerCase();
24994
25044
  for (const [lang, extensions] of Object.entries(LANGUAGE_EXTENSIONS)) {
24995
25045
  if (extensions.includes(ext)) return lang;
24996
25046
  }
@@ -25399,9 +25449,9 @@ Examples:
25399
25449
  }),
25400
25450
  async execute({ path: rootPath, include, exclude, languages, maxFiles, depth }) {
25401
25451
  const startTime = performance.now();
25402
- const absPath = path26.resolve(rootPath);
25452
+ const absPath = path28.resolve(rootPath);
25403
25453
  try {
25404
- const stat2 = await fs28.stat(absPath);
25454
+ const stat2 = await fs29.stat(absPath);
25405
25455
  if (!stat2.isDirectory()) {
25406
25456
  throw new ToolError(`Path is not a directory: ${absPath}`, {
25407
25457
  tool: "codebase_map"
@@ -25438,14 +25488,14 @@ Examples:
25438
25488
  let totalDefinitions = 0;
25439
25489
  let exportedSymbols = 0;
25440
25490
  for (const file of limitedFiles) {
25441
- const fullPath = path26.join(absPath, file);
25491
+ const fullPath = path28.join(absPath, file);
25442
25492
  const language = detectLanguage2(file);
25443
25493
  if (!language) continue;
25444
25494
  if (languages && !languages.includes(language)) {
25445
25495
  continue;
25446
25496
  }
25447
25497
  try {
25448
- const content = await fs28.readFile(fullPath, "utf-8");
25498
+ const content = await fs29.readFile(fullPath, "utf-8");
25449
25499
  const lineCount = content.split("\n").length;
25450
25500
  const parsed = parseFile(content, language);
25451
25501
  const definitions = depth === "overview" ? parsed.definitions.filter((d) => d.exported) : parsed.definitions;
@@ -25478,23 +25528,23 @@ Examples:
25478
25528
  });
25479
25529
  var codebaseMapTools = [codebaseMapTool];
25480
25530
  init_paths();
25481
- var fs29 = await import('fs/promises');
25482
- var path27 = await import('path');
25483
- var crypto2 = await import('crypto');
25484
- 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");
25485
25535
  var PROJECT_MEMORIES_DIR = ".coco/memories";
25486
25536
  var DEFAULT_MAX_MEMORIES = 1e3;
25487
25537
  async function ensureDir2(dirPath) {
25488
- await fs29.mkdir(dirPath, { recursive: true });
25538
+ await fs30.mkdir(dirPath, { recursive: true });
25489
25539
  }
25490
25540
  function getMemoriesDir(scope) {
25491
25541
  return scope === "global" ? GLOBAL_MEMORIES_DIR : PROJECT_MEMORIES_DIR;
25492
25542
  }
25493
25543
  async function loadIndex(scope) {
25494
25544
  const dir = getMemoriesDir(scope);
25495
- const indexPath = path27.join(dir, "index.json");
25545
+ const indexPath = path29.join(dir, "index.json");
25496
25546
  try {
25497
- const content = await fs29.readFile(indexPath, "utf-8");
25547
+ const content = await fs30.readFile(indexPath, "utf-8");
25498
25548
  return JSON.parse(content);
25499
25549
  } catch {
25500
25550
  return [];
@@ -25503,14 +25553,14 @@ async function loadIndex(scope) {
25503
25553
  async function saveIndex(scope, index) {
25504
25554
  const dir = getMemoriesDir(scope);
25505
25555
  await ensureDir2(dir);
25506
- const indexPath = path27.join(dir, "index.json");
25507
- 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");
25508
25558
  }
25509
25559
  async function loadMemory(scope, id) {
25510
25560
  const dir = getMemoriesDir(scope);
25511
- const memPath = path27.join(dir, `${id}.json`);
25561
+ const memPath = path29.join(dir, `${id}.json`);
25512
25562
  try {
25513
- const content = await fs29.readFile(memPath, "utf-8");
25563
+ const content = await fs30.readFile(memPath, "utf-8");
25514
25564
  return JSON.parse(content);
25515
25565
  } catch {
25516
25566
  return null;
@@ -25519,8 +25569,8 @@ async function loadMemory(scope, id) {
25519
25569
  async function saveMemory(scope, memory) {
25520
25570
  const dir = getMemoriesDir(scope);
25521
25571
  await ensureDir2(dir);
25522
- const memPath = path27.join(dir, `${memory.id}.json`);
25523
- 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");
25524
25574
  }
25525
25575
  var createMemoryTool = defineTool({
25526
25576
  name: "create_memory",
@@ -25562,7 +25612,7 @@ Examples:
25562
25612
  { tool: "create_memory" }
25563
25613
  );
25564
25614
  }
25565
- const id = crypto2.randomUUID();
25615
+ const id = crypto3.randomUUID();
25566
25616
  const memory = {
25567
25617
  id,
25568
25618
  key,
@@ -25672,17 +25722,17 @@ Examples:
25672
25722
  }
25673
25723
  });
25674
25724
  var memoryTools = [createMemoryTool, recallMemoryTool, listMemoriesTool];
25675
- var fs30 = await import('fs/promises');
25676
- var crypto3 = await import('crypto');
25725
+ var fs31 = await import('fs/promises');
25726
+ var crypto4 = await import('crypto');
25677
25727
  var CHECKPOINT_FILE = ".coco/checkpoints.json";
25678
25728
  var DEFAULT_MAX_CHECKPOINTS = 50;
25679
25729
  var STASH_PREFIX = "coco-cp";
25680
25730
  async function ensureCocoDir() {
25681
- await fs30.mkdir(".coco", { recursive: true });
25731
+ await fs31.mkdir(".coco", { recursive: true });
25682
25732
  }
25683
25733
  async function loadCheckpoints() {
25684
25734
  try {
25685
- const content = await fs30.readFile(CHECKPOINT_FILE, "utf-8");
25735
+ const content = await fs31.readFile(CHECKPOINT_FILE, "utf-8");
25686
25736
  return JSON.parse(content);
25687
25737
  } catch {
25688
25738
  return [];
@@ -25690,7 +25740,7 @@ async function loadCheckpoints() {
25690
25740
  }
25691
25741
  async function saveCheckpoints(checkpoints) {
25692
25742
  await ensureCocoDir();
25693
- 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");
25694
25744
  }
25695
25745
  async function execGit(args) {
25696
25746
  const { execaCommand } = await import('execa');
@@ -25728,7 +25778,7 @@ Examples:
25728
25778
  description: z.string().min(1).max(200).describe("Description of this checkpoint")
25729
25779
  }),
25730
25780
  async execute({ description }) {
25731
- const id = crypto3.randomUUID().slice(0, 8);
25781
+ const id = crypto4.randomUUID().slice(0, 8);
25732
25782
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
25733
25783
  const stashMessage = `${STASH_PREFIX}-${id}-${description.replace(/\s+/g, "-").slice(0, 50)}`;
25734
25784
  const changedFiles = await getChangedFiles();
@@ -25850,8 +25900,8 @@ Examples:
25850
25900
  }
25851
25901
  });
25852
25902
  var checkpointTools = [createCheckpointTool, restoreCheckpointTool, listCheckpointsTool];
25853
- var fs31 = await import('fs/promises');
25854
- var path28 = await import('path');
25903
+ var fs32 = await import('fs/promises');
25904
+ var path30 = await import('path');
25855
25905
  var { glob: glob13 } = await import('glob');
25856
25906
  var INDEX_DIR = ".coco/search-index";
25857
25907
  var DEFAULT_CHUNK_SIZE = 20;
@@ -25977,20 +26027,20 @@ async function getEmbedding(text9) {
25977
26027
  }
25978
26028
  async function loadIndex2(indexDir) {
25979
26029
  try {
25980
- const indexPath = path28.join(indexDir, "index.json");
25981
- 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");
25982
26032
  return JSON.parse(content);
25983
26033
  } catch {
25984
26034
  return null;
25985
26035
  }
25986
26036
  }
25987
26037
  async function saveIndex2(indexDir, index) {
25988
- await fs31.mkdir(indexDir, { recursive: true });
25989
- const indexPath = path28.join(indexDir, "index.json");
25990
- 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");
25991
26041
  }
25992
26042
  function isBinary(filePath) {
25993
- return BINARY_EXTENSIONS.has(path28.extname(filePath).toLowerCase());
26043
+ return BINARY_EXTENSIONS.has(path30.extname(filePath).toLowerCase());
25994
26044
  }
25995
26045
  var semanticSearchTool = defineTool({
25996
26046
  name: "semantic_search",
@@ -26015,8 +26065,8 @@ Examples:
26015
26065
  const effectivePath = rootPath ?? ".";
26016
26066
  const effectiveMaxResults = maxResults ?? 10;
26017
26067
  const effectiveThreshold = threshold ?? 0.3;
26018
- const absPath = path28.resolve(effectivePath);
26019
- const indexDir = path28.join(absPath, INDEX_DIR);
26068
+ const absPath = path30.resolve(effectivePath);
26069
+ const indexDir = path30.join(absPath, INDEX_DIR);
26020
26070
  let index = reindex ? null : await loadIndex2(indexDir);
26021
26071
  if (!index) {
26022
26072
  const pattern = include ?? "**/*";
@@ -26029,10 +26079,10 @@ Examples:
26029
26079
  const chunks = [];
26030
26080
  for (const file of files) {
26031
26081
  if (isBinary(file)) continue;
26032
- const fullPath = path28.join(absPath, file);
26082
+ const fullPath = path30.join(absPath, file);
26033
26083
  try {
26034
- const stat2 = await fs31.stat(fullPath);
26035
- const content = await fs31.readFile(fullPath, "utf-8");
26084
+ const stat2 = await fs32.stat(fullPath);
26085
+ const content = await fs32.readFile(fullPath, "utf-8");
26036
26086
  if (content.length > 1e5) continue;
26037
26087
  const fileChunks = chunkContent(content, DEFAULT_CHUNK_SIZE);
26038
26088
  for (const chunk of fileChunks) {
@@ -26091,8 +26141,8 @@ Examples:
26091
26141
  }
26092
26142
  });
26093
26143
  var semanticSearchTools = [semanticSearchTool];
26094
- var fs32 = await import('fs/promises');
26095
- var path29 = await import('path');
26144
+ var fs33 = await import('fs/promises');
26145
+ var path31 = await import('path');
26096
26146
  var { glob: glob14 } = await import('glob');
26097
26147
  async function parseClassRelationships(rootPath, include) {
26098
26148
  const pattern = include ?? "**/*.{ts,tsx,js,jsx}";
@@ -26105,7 +26155,7 @@ async function parseClassRelationships(rootPath, include) {
26105
26155
  const interfaces = [];
26106
26156
  for (const file of files.slice(0, 100)) {
26107
26157
  try {
26108
- const content = await fs32.readFile(path29.join(rootPath, file), "utf-8");
26158
+ const content = await fs33.readFile(path31.join(rootPath, file), "utf-8");
26109
26159
  const lines = content.split("\n");
26110
26160
  for (let i = 0; i < lines.length; i++) {
26111
26161
  const line = lines[i];
@@ -26224,14 +26274,14 @@ async function generateClassDiagram(rootPath, include) {
26224
26274
  };
26225
26275
  }
26226
26276
  async function generateArchitectureDiagram(rootPath) {
26227
- const entries = await fs32.readdir(rootPath, { withFileTypes: true });
26277
+ const entries = await fs33.readdir(rootPath, { withFileTypes: true });
26228
26278
  const dirs = entries.filter(
26229
26279
  (e) => e.isDirectory() && !e.name.startsWith(".") && !["node_modules", "dist", "build", "coverage", "__pycache__", "target"].includes(e.name)
26230
26280
  );
26231
26281
  const lines = ["graph TD"];
26232
26282
  let nodeCount = 0;
26233
26283
  let edgeCount = 0;
26234
- const rootName = path29.basename(rootPath);
26284
+ const rootName = path31.basename(rootPath);
26235
26285
  lines.push(` ROOT["${rootName}"]`);
26236
26286
  nodeCount++;
26237
26287
  for (const dir of dirs) {
@@ -26241,7 +26291,7 @@ async function generateArchitectureDiagram(rootPath) {
26241
26291
  nodeCount++;
26242
26292
  edgeCount++;
26243
26293
  try {
26244
- const subEntries = await fs32.readdir(path29.join(rootPath, dir.name), {
26294
+ const subEntries = await fs33.readdir(path31.join(rootPath, dir.name), {
26245
26295
  withFileTypes: true
26246
26296
  });
26247
26297
  const subDirs = subEntries.filter(
@@ -26364,7 +26414,7 @@ Examples:
26364
26414
  tool: "generate_diagram"
26365
26415
  });
26366
26416
  }
26367
- const absPath = rootPath ? path29.resolve(rootPath) : process.cwd();
26417
+ const absPath = rootPath ? path31.resolve(rootPath) : process.cwd();
26368
26418
  switch (type) {
26369
26419
  case "class":
26370
26420
  return generateClassDiagram(absPath, include);
@@ -26425,8 +26475,8 @@ Examples:
26425
26475
  }
26426
26476
  });
26427
26477
  var diagramTools = [generateDiagramTool];
26428
- var fs33 = await import('fs/promises');
26429
- var path30 = await import('path');
26478
+ var fs34 = await import('fs/promises');
26479
+ var path32 = await import('path');
26430
26480
  var DEFAULT_MAX_PAGES = 20;
26431
26481
  var MAX_FILE_SIZE = 50 * 1024 * 1024;
26432
26482
  function parsePageRange(rangeStr, totalPages) {
@@ -26461,9 +26511,9 @@ Examples:
26461
26511
  }),
26462
26512
  async execute({ path: filePath, pages, maxPages }) {
26463
26513
  const startTime = performance.now();
26464
- const absPath = path30.resolve(filePath);
26514
+ const absPath = path32.resolve(filePath);
26465
26515
  try {
26466
- const stat2 = await fs33.stat(absPath);
26516
+ const stat2 = await fs34.stat(absPath);
26467
26517
  if (!stat2.isFile()) {
26468
26518
  throw new ToolError(`Path is not a file: ${absPath}`, {
26469
26519
  tool: "read_pdf"
@@ -26494,7 +26544,7 @@ Examples:
26494
26544
  }
26495
26545
  try {
26496
26546
  const pdfParse = await import('pdf-parse');
26497
- const dataBuffer = await fs33.readFile(absPath);
26547
+ const dataBuffer = await fs34.readFile(absPath);
26498
26548
  const pdfData = await pdfParse.default(dataBuffer, {
26499
26549
  max: maxPages
26500
26550
  });
@@ -26540,8 +26590,8 @@ Examples:
26540
26590
  }
26541
26591
  });
26542
26592
  var pdfTools = [readPdfTool];
26543
- var fs34 = await import('fs/promises');
26544
- var path31 = await import('path');
26593
+ var fs35 = await import('fs/promises');
26594
+ var path33 = await import('path');
26545
26595
  var SUPPORTED_FORMATS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
26546
26596
  var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
26547
26597
  var MIME_TYPES = {
@@ -26569,15 +26619,15 @@ Examples:
26569
26619
  async execute({ path: filePath, prompt, provider }) {
26570
26620
  const startTime = performance.now();
26571
26621
  const effectivePrompt = prompt ?? "Describe this image in detail. If it's code or a UI, identify the key elements.";
26572
- const absPath = path31.resolve(filePath);
26622
+ const absPath = path33.resolve(filePath);
26573
26623
  const cwd = process.cwd();
26574
- if (!absPath.startsWith(cwd + path31.sep) && absPath !== cwd) {
26624
+ if (!absPath.startsWith(cwd + path33.sep) && absPath !== cwd) {
26575
26625
  throw new ToolError(
26576
26626
  `Path traversal denied: '${filePath}' resolves outside the project directory`,
26577
26627
  { tool: "read_image" }
26578
26628
  );
26579
26629
  }
26580
- const ext = path31.extname(absPath).toLowerCase();
26630
+ const ext = path33.extname(absPath).toLowerCase();
26581
26631
  if (!SUPPORTED_FORMATS.has(ext)) {
26582
26632
  throw new ToolError(
26583
26633
  `Unsupported image format '${ext}'. Supported: ${Array.from(SUPPORTED_FORMATS).join(", ")}`,
@@ -26585,7 +26635,7 @@ Examples:
26585
26635
  );
26586
26636
  }
26587
26637
  try {
26588
- const stat2 = await fs34.stat(absPath);
26638
+ const stat2 = await fs35.stat(absPath);
26589
26639
  if (!stat2.isFile()) {
26590
26640
  throw new ToolError(`Path is not a file: ${absPath}`, {
26591
26641
  tool: "read_image"
@@ -26606,7 +26656,7 @@ Examples:
26606
26656
  if (error instanceof ToolError) throw error;
26607
26657
  throw error;
26608
26658
  }
26609
- const imageBuffer = await fs34.readFile(absPath);
26659
+ const imageBuffer = await fs35.readFile(absPath);
26610
26660
  const base64 = imageBuffer.toString("base64");
26611
26661
  const mimeType = MIME_TYPES[ext] ?? "image/png";
26612
26662
  const selectedProvider = provider ?? "anthropic";
@@ -26719,7 +26769,7 @@ Examples:
26719
26769
  }
26720
26770
  });
26721
26771
  var imageTools = [readImageTool];
26722
- var path32 = await import('path');
26772
+ var path34 = await import('path');
26723
26773
  var DANGEROUS_PATTERNS2 = [
26724
26774
  /\bDROP\s+(?:TABLE|DATABASE|INDEX|VIEW)\b/i,
26725
26775
  /\bTRUNCATE\b/i,
@@ -26750,7 +26800,7 @@ Examples:
26750
26800
  async execute({ database, query, params, readonly: isReadonlyParam }) {
26751
26801
  const isReadonly = isReadonlyParam ?? true;
26752
26802
  const startTime = performance.now();
26753
- const absPath = path32.resolve(database);
26803
+ const absPath = path34.resolve(database);
26754
26804
  if (isReadonly && isDangerousSql(query)) {
26755
26805
  throw new ToolError(
26756
26806
  "Write operations (INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE) are blocked in readonly mode. Set readonly: false to allow writes.",
@@ -26823,7 +26873,7 @@ Examples:
26823
26873
  }),
26824
26874
  async execute({ database, table }) {
26825
26875
  const startTime = performance.now();
26826
- const absPath = path32.resolve(database);
26876
+ const absPath = path34.resolve(database);
26827
26877
  try {
26828
26878
  const { default: Database } = await import('better-sqlite3');
26829
26879
  const db = new Database(absPath, { readonly: true, fileMustExist: true });
@@ -26994,14 +27044,14 @@ var findMissingImportsTool = defineTool({
26994
27044
  }
26995
27045
  });
26996
27046
  var astValidatorTools = [validateCodeTool, findMissingImportsTool];
26997
- var fs35 = await import('fs/promises');
26998
- var path33 = await import('path');
27047
+ var fs36 = await import('fs/promises');
27048
+ var path35 = await import('path');
26999
27049
  var AnalyzeFileSchema = z.object({
27000
27050
  filePath: z.string().describe("Path to file to analyze"),
27001
27051
  includeAst: z.boolean().default(false).describe("Include AST in result")
27002
27052
  });
27003
27053
  async function analyzeFile(filePath, includeAst = false) {
27004
- const content = await fs35.readFile(filePath, "utf-8");
27054
+ const content = await fs36.readFile(filePath, "utf-8");
27005
27055
  const lines = content.split("\n").length;
27006
27056
  const functions = [];
27007
27057
  const classes = [];
@@ -27105,10 +27155,10 @@ async function analyzeDirectory(dirPath) {
27105
27155
  try {
27106
27156
  const analysis = await analyzeFile(file, false);
27107
27157
  totalLines += analysis.lines;
27108
- const ext = path33.extname(file);
27158
+ const ext = path35.extname(file);
27109
27159
  filesByType[ext] = (filesByType[ext] || 0) + 1;
27110
27160
  fileStats.push({
27111
- file: path33.relative(dirPath, file),
27161
+ file: path35.relative(dirPath, file),
27112
27162
  lines: analysis.lines,
27113
27163
  complexity: analysis.complexity.cyclomatic
27114
27164
  });
@@ -27464,13 +27514,13 @@ ${completed.map((r) => `- ${r.agentId}: Success`).join("\n")}`;
27464
27514
  }
27465
27515
  });
27466
27516
  var agentCoordinatorTools = [createAgentPlanTool, delegateTaskTool, aggregateResultsTool];
27467
- var fs36 = await import('fs/promises');
27517
+ var fs37 = await import('fs/promises');
27468
27518
  var SuggestImprovementsSchema = z.object({
27469
27519
  filePath: z.string().describe("File to analyze for improvement suggestions"),
27470
27520
  context: z.string().optional().describe("Additional context about the code")
27471
27521
  });
27472
27522
  async function analyzeAndSuggest(filePath, _context) {
27473
- const content = await fs36.readFile(filePath, "utf-8");
27523
+ const content = await fs37.readFile(filePath, "utf-8");
27474
27524
  const lines = content.split("\n");
27475
27525
  const suggestions = [];
27476
27526
  for (let i = 0; i < lines.length; i++) {
@@ -27562,7 +27612,7 @@ async function analyzeAndSuggest(filePath, _context) {
27562
27612
  if (filePath.endsWith(".ts") && !filePath.includes("test") && !filePath.includes(".d.ts") && line.includes("export ")) {
27563
27613
  const testPath = filePath.replace(".ts", ".test.ts");
27564
27614
  try {
27565
- await fs36.access(testPath);
27615
+ await fs37.access(testPath);
27566
27616
  } catch {
27567
27617
  suggestions.push({
27568
27618
  type: "testing",
@@ -27619,7 +27669,7 @@ var calculateCodeScoreTool = defineTool({
27619
27669
  async execute(input) {
27620
27670
  const { filePath } = input;
27621
27671
  const suggestions = await analyzeAndSuggest(filePath);
27622
- const content = await fs36.readFile(filePath, "utf-8");
27672
+ const content = await fs37.readFile(filePath, "utf-8");
27623
27673
  const lines = content.split("\n");
27624
27674
  const nonEmptyLines = lines.filter((l) => l.trim()).length;
27625
27675
  let score = 100;
@@ -27653,8 +27703,8 @@ var calculateCodeScoreTool = defineTool({
27653
27703
  }
27654
27704
  });
27655
27705
  var smartSuggestionsTools = [suggestImprovementsTool, calculateCodeScoreTool];
27656
- var fs37 = await import('fs/promises');
27657
- var path34 = await import('path');
27706
+ var fs38 = await import('fs/promises');
27707
+ var path36 = await import('path');
27658
27708
  var ContextMemoryStore = class {
27659
27709
  items = /* @__PURE__ */ new Map();
27660
27710
  learnings = /* @__PURE__ */ new Map();
@@ -27666,7 +27716,7 @@ var ContextMemoryStore = class {
27666
27716
  }
27667
27717
  async load() {
27668
27718
  try {
27669
- const content = await fs37.readFile(this.storePath, "utf-8");
27719
+ const content = await fs38.readFile(this.storePath, "utf-8");
27670
27720
  const data = JSON.parse(content);
27671
27721
  this.items = new Map(Object.entries(data.items || {}));
27672
27722
  this.learnings = new Map(Object.entries(data.learnings || {}));
@@ -27674,15 +27724,15 @@ var ContextMemoryStore = class {
27674
27724
  }
27675
27725
  }
27676
27726
  async save() {
27677
- const dir = path34.dirname(this.storePath);
27678
- await fs37.mkdir(dir, { recursive: true });
27727
+ const dir = path36.dirname(this.storePath);
27728
+ await fs38.mkdir(dir, { recursive: true });
27679
27729
  const data = {
27680
27730
  sessionId: this.sessionId,
27681
27731
  items: Object.fromEntries(this.items),
27682
27732
  learnings: Object.fromEntries(this.learnings),
27683
27733
  savedAt: Date.now()
27684
27734
  };
27685
- await fs37.writeFile(this.storePath, JSON.stringify(data, null, 2));
27735
+ await fs38.writeFile(this.storePath, JSON.stringify(data, null, 2));
27686
27736
  }
27687
27737
  addContext(id, item) {
27688
27738
  this.items.set(id, item);
@@ -27847,11 +27897,11 @@ var contextEnhancerTools = [
27847
27897
  recordLearningTool,
27848
27898
  getLearnedPatternsTool
27849
27899
  ];
27850
- var fs38 = await import('fs/promises');
27851
- var path35 = await import('path');
27900
+ var fs39 = await import('fs/promises');
27901
+ var path37 = await import('path');
27852
27902
  async function discoverSkills(skillsDir) {
27853
27903
  try {
27854
- const files = await fs38.readdir(skillsDir);
27904
+ const files = await fs39.readdir(skillsDir);
27855
27905
  return files.filter((f) => f.endsWith(".ts") || f.endsWith(".js"));
27856
27906
  } catch {
27857
27907
  return [];
@@ -27859,12 +27909,12 @@ async function discoverSkills(skillsDir) {
27859
27909
  }
27860
27910
  async function loadSkillMetadata(skillPath) {
27861
27911
  try {
27862
- const content = await fs38.readFile(skillPath, "utf-8");
27912
+ const content = await fs39.readFile(skillPath, "utf-8");
27863
27913
  const nameMatch = content.match(/@name\s+(\S+)/);
27864
27914
  const descMatch = content.match(/@description\s+(.+)/);
27865
27915
  const versionMatch = content.match(/@version\s+(\S+)/);
27866
27916
  return {
27867
- name: nameMatch?.[1] || path35.basename(skillPath, path35.extname(skillPath)),
27917
+ name: nameMatch?.[1] || path37.basename(skillPath, path37.extname(skillPath)),
27868
27918
  description: descMatch?.[1] || "No description",
27869
27919
  version: versionMatch?.[1] || "1.0.0",
27870
27920
  dependencies: []
@@ -27908,7 +27958,7 @@ var discoverSkillsTool = defineTool({
27908
27958
  const { skillsDir } = input;
27909
27959
  const skills = await discoverSkills(skillsDir);
27910
27960
  const metadata = await Promise.all(
27911
- skills.map((s) => loadSkillMetadata(path35.join(skillsDir, s)))
27961
+ skills.map((s) => loadSkillMetadata(path37.join(skillsDir, s)))
27912
27962
  );
27913
27963
  return {
27914
27964
  skillsDir,
@@ -28031,14 +28081,17 @@ export const ${typedInput.name}Tool = defineTool({
28031
28081
  }
28032
28082
  });
28033
28083
  var skillEnhancerTools = [discoverSkillsTool, validateSkillTool, createCustomToolTool];
28084
+ function gitExec(cmd, opts = {}) {
28085
+ return execSync(cmd, { encoding: "utf-8", cwd: process.cwd(), ...opts });
28086
+ }
28034
28087
  function analyzeRepoHealth() {
28035
28088
  const issues = [];
28036
28089
  const recommendations = [];
28037
28090
  let score = 100;
28038
28091
  try {
28039
28092
  try {
28040
- execSync("git status --porcelain", { stdio: "pipe" });
28041
- const status = execSync("git status --porcelain", { encoding: "utf-8" });
28093
+ gitExec("git status --porcelain", { stdio: "pipe" });
28094
+ const status = gitExec("git status --porcelain");
28042
28095
  if (status.trim()) {
28043
28096
  issues.push("Uncommitted changes present");
28044
28097
  score -= 10;
@@ -28046,7 +28099,7 @@ function analyzeRepoHealth() {
28046
28099
  } catch {
28047
28100
  }
28048
28101
  try {
28049
- const untracked = execSync("git ls-files --others --exclude-standard", { encoding: "utf-8" });
28102
+ const untracked = gitExec("git ls-files --others --exclude-standard");
28050
28103
  if (untracked.trim()) {
28051
28104
  const count = untracked.trim().split("\n").length;
28052
28105
  issues.push(`${count} untracked files`);
@@ -28056,9 +28109,9 @@ function analyzeRepoHealth() {
28056
28109
  } catch {
28057
28110
  }
28058
28111
  try {
28059
- const branch = execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8" }).trim();
28060
- const local = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
28061
- 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();
28062
28115
  if (local !== remote) {
28063
28116
  issues.push("Branch is not up-to-date with remote");
28064
28117
  score -= 15;
@@ -28067,14 +28120,14 @@ function analyzeRepoHealth() {
28067
28120
  } catch {
28068
28121
  }
28069
28122
  try {
28070
- const files = execSync("git ls-files", { encoding: "utf-8" }).trim().split("\n");
28123
+ const files = gitExec("git ls-files").trim().split("\n");
28071
28124
  if (files.length > 1e3) {
28072
28125
  recommendations.push("Repository has many files, consider using .gitignore");
28073
28126
  }
28074
28127
  } catch {
28075
28128
  }
28076
28129
  try {
28077
- const conflicts = execSync("git diff --name-only --diff-filter=U", { encoding: "utf-8" });
28130
+ const conflicts = gitExec("git diff --name-only --diff-filter=U");
28078
28131
  if (conflicts.trim()) {
28079
28132
  issues.push("Merge conflicts present");
28080
28133
  score -= 30;
@@ -28090,12 +28143,11 @@ function analyzeRepoHealth() {
28090
28143
  }
28091
28144
  function getCommitStats() {
28092
28145
  try {
28093
- const count = execSync("git rev-list --count HEAD", { encoding: "utf-8" }).trim();
28094
- const authors = execSync('git log --format="%an" | sort -u', {
28095
- encoding: "utf-8",
28146
+ const count = gitExec("git rev-list --count HEAD").trim();
28147
+ const authors = gitExec('git log --format="%an" | sort -u', {
28096
28148
  shell: "/bin/bash"
28097
28149
  }).trim().split("\n");
28098
- const lastCommit = execSync('git log -1 --format="%cr"', { encoding: "utf-8" }).trim();
28150
+ const lastCommit = gitExec('git log -1 --format="%cr"').trim();
28099
28151
  return {
28100
28152
  totalCommits: parseInt(count, 10),
28101
28153
  authors,
@@ -28168,7 +28220,7 @@ var recommendBranchTool = defineTool({
28168
28220
  const branchName = `${prefix}/${slug}`;
28169
28221
  let exists = false;
28170
28222
  try {
28171
- execSync(`git rev-parse --verify ${branchName}`, { stdio: "ignore" });
28223
+ execSync(`git rev-parse --verify ${branchName}`, { cwd: process.cwd(), stdio: "ignore" });
28172
28224
  exists = true;
28173
28225
  } catch {
28174
28226
  exists = false;
@@ -28183,6 +28235,104 @@ var recommendBranchTool = defineTool({
28183
28235
  }
28184
28236
  });
28185
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];
28186
28336
 
28187
28337
  // src/tools/index.ts
28188
28338
  function registerAllTools(registry) {
@@ -28215,7 +28365,8 @@ function registerAllTools(registry) {
28215
28365
  ...smartSuggestionsTools,
28216
28366
  ...contextEnhancerTools,
28217
28367
  ...skillEnhancerTools,
28218
- ...gitEnhancedTools
28368
+ ...gitEnhancedTools,
28369
+ ...authorizePathTools
28219
28370
  ];
28220
28371
  for (const tool of allTools) {
28221
28372
  registry.register(tool);
@@ -28615,13 +28766,63 @@ function createIntentRecognizer(config = {}) {
28615
28766
 
28616
28767
  // src/cli/repl/index.ts
28617
28768
  init_env();
28769
+ init_allowed_paths();
28618
28770
  init_trust_store();
28619
- ({
28620
- critical: chalk12.red.bold("CRITICAL"),
28621
- major: chalk12.red("MAJOR"),
28622
- minor: chalk12.yellow("minor"),
28623
- info: chalk12.dim("info")
28624
- });
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));
28625
28826
  var pc = chalk12;
28626
28827
  ({
28627
28828
  pending: pc.dim("\u25CB"),
@@ -28776,11 +28977,21 @@ async function startRepl(options = {}) {
28776
28977
  activeSpinner.start();
28777
28978
  }
28778
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
+ };
28779
28989
  const abortController = new AbortController();
28780
28990
  let wasAborted = false;
28781
28991
  const sigintHandler = () => {
28782
28992
  wasAborted = true;
28783
28993
  abortController.abort();
28994
+ clearThinkingInterval();
28784
28995
  clearSpinner();
28785
28996
  renderInfo("\nOperation cancelled");
28786
28997
  };
@@ -28819,8 +29030,19 @@ async function startRepl(options = {}) {
28819
29030
  },
28820
29031
  onThinkingStart: () => {
28821
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);
28822
29043
  },
28823
29044
  onThinkingEnd: () => {
29045
+ clearThinkingInterval();
28824
29046
  clearSpinner();
28825
29047
  },
28826
29048
  onToolPreparing: (toolName) => {
@@ -28831,6 +29053,7 @@ async function startRepl(options = {}) {
28831
29053
  },
28832
29054
  signal: abortController.signal
28833
29055
  });
29056
+ clearThinkingInterval();
28834
29057
  process.off("SIGINT", sigintHandler);
28835
29058
  if (wasAborted || result.aborted) {
28836
29059
  if (result.partialContent) {
@@ -28883,6 +29106,7 @@ async function startRepl(options = {}) {
28883
29106
  }
28884
29107
  console.log();
28885
29108
  } catch (error) {
29109
+ clearThinkingInterval();
28886
29110
  clearSpinner();
28887
29111
  process.off("SIGINT", sigintHandler);
28888
29112
  if (error instanceof Error && error.name === "AbortError") {
@@ -28953,11 +29177,14 @@ async function printWelcome(session) {
28953
29177
  if (displayPath.length > maxPathLen) {
28954
29178
  displayPath = "..." + displayPath.slice(-maxPathLen + 3);
28955
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;
28956
29183
  const providerName = session.config.provider.type;
28957
29184
  const modelName = session.config.provider.model || "default";
28958
29185
  const trustText = trustLevel === "full" ? "full" : trustLevel === "write" ? "write" : trustLevel === "read" ? "read" : "";
28959
29186
  console.log();
28960
- console.log(chalk12.dim(` \u{1F4C1} ${displayPath}`));
29187
+ console.log(chalk12.dim(` \u{1F4C1} ${parentPath}`) + chalk12.magenta.bold(projectName));
28961
29188
  console.log(
28962
29189
  chalk12.dim(` \u{1F916} ${providerName}/`) + chalk12.magenta(modelName) + (trustText ? chalk12.dim(` \u2022 \u{1F510} ${trustText}`) : "")
28963
29190
  );