@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/README.md +9 -2
- package/dist/cli/index.js +583 -371
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +401 -170
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
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,
|
|
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(
|
|
283
|
-
await mkdir(dirname(
|
|
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(
|
|
362
|
-
return join(
|
|
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 (
|
|
613
|
-
await runInit(
|
|
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(
|
|
693
|
-
const name =
|
|
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(
|
|
846
|
+
async function checkExistingProject(path39) {
|
|
701
847
|
try {
|
|
702
|
-
const
|
|
703
|
-
await
|
|
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(
|
|
7652
|
-
const
|
|
7653
|
-
return
|
|
7797
|
+
async read(path39) {
|
|
7798
|
+
const fs41 = await import('fs/promises');
|
|
7799
|
+
return fs41.readFile(path39, "utf-8");
|
|
7654
7800
|
},
|
|
7655
|
-
async write(
|
|
7656
|
-
const
|
|
7801
|
+
async write(path39, content) {
|
|
7802
|
+
const fs41 = await import('fs/promises');
|
|
7657
7803
|
const nodePath = await import('path');
|
|
7658
|
-
await
|
|
7659
|
-
await
|
|
7804
|
+
await fs41.mkdir(nodePath.dirname(path39), { recursive: true });
|
|
7805
|
+
await fs41.writeFile(path39, content, "utf-8");
|
|
7660
7806
|
},
|
|
7661
|
-
async exists(
|
|
7662
|
-
const
|
|
7807
|
+
async exists(path39) {
|
|
7808
|
+
const fs41 = await import('fs/promises');
|
|
7663
7809
|
try {
|
|
7664
|
-
await
|
|
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
|
|
8049
|
+
const fs41 = await import('fs/promises');
|
|
7904
8050
|
let hasProject = false;
|
|
7905
8051
|
let hasPlan = false;
|
|
7906
8052
|
try {
|
|
7907
|
-
await
|
|
8053
|
+
await fs41.access(".coco");
|
|
7908
8054
|
hasProject = true;
|
|
7909
8055
|
} catch {
|
|
7910
8056
|
}
|
|
7911
8057
|
try {
|
|
7912
|
-
await
|
|
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
|
|
8017
|
-
const
|
|
8018
|
-
const statePath =
|
|
8019
|
-
const backlogPath =
|
|
8020
|
-
const checkpointDir =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
8192
|
-
await
|
|
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
|
|
8732
|
-
await
|
|
8733
|
-
await
|
|
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,
|
|
8736
|
-
const keys =
|
|
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,
|
|
8747
|
-
const keys =
|
|
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(
|
|
8969
|
-
await mkdir(dirname(
|
|
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(
|
|
9634
|
+
function createMissingMemoryFile(path39, level) {
|
|
9489
9635
|
return {
|
|
9490
|
-
path:
|
|
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
|
-
|
|
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
|
|
12182
|
-
p9.log.message(` [${level}] ${
|
|
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
|
|
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
|
|
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:
|
|
13443
|
-
const fileName =
|
|
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
|
|
14877
|
-
return `"${pattern}"${
|
|
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
|
|
16362
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
20348
|
+
for (const path39 of possiblePaths) {
|
|
20310
20349
|
try {
|
|
20311
|
-
await access(
|
|
20312
|
-
const content = await readFile(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
(
|
|
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
|
-
|
|
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
|
-
|
|
24748
|
-
|
|
24749
|
-
|
|
24750
|
-
|
|
24751
|
-
|
|
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
|
|
24954
|
-
var
|
|
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 =
|
|
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 =
|
|
25452
|
+
const absPath = path28.resolve(rootPath);
|
|
25390
25453
|
try {
|
|
25391
|
-
const stat2 = await
|
|
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 =
|
|
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
|
|
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
|
|
25469
|
-
var
|
|
25470
|
-
var
|
|
25471
|
-
var GLOBAL_MEMORIES_DIR =
|
|
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
|
|
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 =
|
|
25545
|
+
const indexPath = path29.join(dir, "index.json");
|
|
25483
25546
|
try {
|
|
25484
|
-
const content = await
|
|
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 =
|
|
25494
|
-
await
|
|
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 =
|
|
25561
|
+
const memPath = path29.join(dir, `${id}.json`);
|
|
25499
25562
|
try {
|
|
25500
|
-
const content = await
|
|
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 =
|
|
25510
|
-
await
|
|
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 =
|
|
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
|
|
25663
|
-
var
|
|
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
|
|
25731
|
+
await fs31.mkdir(".coco", { recursive: true });
|
|
25669
25732
|
}
|
|
25670
25733
|
async function loadCheckpoints() {
|
|
25671
25734
|
try {
|
|
25672
|
-
const content = await
|
|
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
|
|
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 =
|
|
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
|
|
25841
|
-
var
|
|
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 =
|
|
25968
|
-
const content = await
|
|
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
|
|
25976
|
-
const indexPath =
|
|
25977
|
-
await
|
|
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(
|
|
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 =
|
|
26006
|
-
const indexDir =
|
|
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 =
|
|
26082
|
+
const fullPath = path30.join(absPath, file);
|
|
26020
26083
|
try {
|
|
26021
|
-
const stat2 = await
|
|
26022
|
-
const content = await
|
|
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
|
|
26082
|
-
var
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 ?
|
|
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
|
|
26416
|
-
var
|
|
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 =
|
|
26514
|
+
const absPath = path32.resolve(filePath);
|
|
26452
26515
|
try {
|
|
26453
|
-
const stat2 = await
|
|
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
|
|
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
|
|
26531
|
-
var
|
|
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 =
|
|
26622
|
+
const absPath = path33.resolve(filePath);
|
|
26560
26623
|
const cwd = process.cwd();
|
|
26561
|
-
if (!absPath.startsWith(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 =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
26985
|
-
var
|
|
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
|
|
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 =
|
|
27158
|
+
const ext = path35.extname(file);
|
|
27096
27159
|
filesByType[ext] = (filesByType[ext] || 0) + 1;
|
|
27097
27160
|
fileStats.push({
|
|
27098
|
-
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
27644
|
-
var
|
|
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
|
|
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 =
|
|
27665
|
-
await
|
|
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
|
|
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
|
|
27838
|
-
var
|
|
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
|
|
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
|
|
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] ||
|
|
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(
|
|
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
|
-
|
|
28028
|
-
const status =
|
|
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 =
|
|
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 =
|
|
28047
|
-
const local =
|
|
28048
|
-
const remote =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
28081
|
-
const authors =
|
|
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 =
|
|
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
|
-
|
|
28608
|
-
|
|
28609
|
-
|
|
28610
|
-
|
|
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 -
|
|
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
|
|
28948
|
-
|
|
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
|
-
|
|
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} ${
|
|
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}
|
|
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"));
|