@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/README.md +9 -2
- package/dist/cli/index.js +539 -312
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +401 -170
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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(
|
|
284
|
-
await mkdir(dirname(
|
|
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(
|
|
363
|
-
return join(
|
|
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 (
|
|
614
|
-
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);
|
|
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(
|
|
694
|
-
const name =
|
|
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(
|
|
846
|
+
async function checkExistingProject(path39) {
|
|
702
847
|
try {
|
|
703
|
-
const
|
|
704
|
-
await
|
|
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(
|
|
7653
|
-
const
|
|
7654
|
-
return
|
|
7797
|
+
async read(path39) {
|
|
7798
|
+
const fs41 = await import('fs/promises');
|
|
7799
|
+
return fs41.readFile(path39, "utf-8");
|
|
7655
7800
|
},
|
|
7656
|
-
async write(
|
|
7657
|
-
const
|
|
7801
|
+
async write(path39, content) {
|
|
7802
|
+
const fs41 = await import('fs/promises');
|
|
7658
7803
|
const nodePath = await import('path');
|
|
7659
|
-
await
|
|
7660
|
-
await
|
|
7804
|
+
await fs41.mkdir(nodePath.dirname(path39), { recursive: true });
|
|
7805
|
+
await fs41.writeFile(path39, content, "utf-8");
|
|
7661
7806
|
},
|
|
7662
|
-
async exists(
|
|
7663
|
-
const
|
|
7807
|
+
async exists(path39) {
|
|
7808
|
+
const fs41 = await import('fs/promises');
|
|
7664
7809
|
try {
|
|
7665
|
-
await
|
|
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
|
|
8049
|
+
const fs41 = await import('fs/promises');
|
|
7905
8050
|
let hasProject = false;
|
|
7906
8051
|
let hasPlan = false;
|
|
7907
8052
|
try {
|
|
7908
|
-
await
|
|
8053
|
+
await fs41.access(".coco");
|
|
7909
8054
|
hasProject = true;
|
|
7910
8055
|
} catch {
|
|
7911
8056
|
}
|
|
7912
8057
|
try {
|
|
7913
|
-
await
|
|
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
|
|
8018
|
-
const
|
|
8019
|
-
const statePath =
|
|
8020
|
-
const backlogPath =
|
|
8021
|
-
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");
|
|
8022
8167
|
let currentPhase = "idle";
|
|
8023
8168
|
let metrics;
|
|
8024
8169
|
let sprint;
|
|
8025
8170
|
let checkpoints = [];
|
|
8026
8171
|
try {
|
|
8027
|
-
const stateContent = await
|
|
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
|
|
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
|
|
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
|
|
8193
|
-
await
|
|
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
|
|
8733
|
-
await
|
|
8734
|
-
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));
|
|
8735
8880
|
}
|
|
8736
|
-
function getNestedValue(obj,
|
|
8737
|
-
const keys =
|
|
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,
|
|
8748
|
-
const keys =
|
|
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(
|
|
8970
|
-
await mkdir(dirname(
|
|
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(
|
|
9634
|
+
function createMissingMemoryFile(path39, level) {
|
|
9490
9635
|
return {
|
|
9491
|
-
path:
|
|
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
|
-
|
|
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
|
|
12183
|
-
p9.log.message(` [${level}] ${
|
|
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
|
|
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
|
|
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:
|
|
13444
|
-
const fileName =
|
|
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
|
|
14878
|
-
return `"${pattern}"${
|
|
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
|
-
|
|
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
|
|
20348
|
+
for (const path39 of possiblePaths) {
|
|
20323
20349
|
try {
|
|
20324
|
-
await access(
|
|
20325
|
-
const content = await readFile(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
(
|
|
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
|
-
|
|
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
|
-
|
|
24761
|
-
|
|
24762
|
-
|
|
24763
|
-
|
|
24764
|
-
|
|
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
|
|
24967
|
-
var
|
|
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 =
|
|
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 =
|
|
25452
|
+
const absPath = path28.resolve(rootPath);
|
|
25403
25453
|
try {
|
|
25404
|
-
const stat2 = await
|
|
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 =
|
|
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
|
|
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
|
|
25482
|
-
var
|
|
25483
|
-
var
|
|
25484
|
-
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");
|
|
25485
25535
|
var PROJECT_MEMORIES_DIR = ".coco/memories";
|
|
25486
25536
|
var DEFAULT_MAX_MEMORIES = 1e3;
|
|
25487
25537
|
async function ensureDir2(dirPath) {
|
|
25488
|
-
await
|
|
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 =
|
|
25545
|
+
const indexPath = path29.join(dir, "index.json");
|
|
25496
25546
|
try {
|
|
25497
|
-
const content = await
|
|
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 =
|
|
25507
|
-
await
|
|
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 =
|
|
25561
|
+
const memPath = path29.join(dir, `${id}.json`);
|
|
25512
25562
|
try {
|
|
25513
|
-
const content = await
|
|
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 =
|
|
25523
|
-
await
|
|
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 =
|
|
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
|
|
25676
|
-
var
|
|
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
|
|
25731
|
+
await fs31.mkdir(".coco", { recursive: true });
|
|
25682
25732
|
}
|
|
25683
25733
|
async function loadCheckpoints() {
|
|
25684
25734
|
try {
|
|
25685
|
-
const content = await
|
|
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
|
|
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 =
|
|
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
|
|
25854
|
-
var
|
|
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 =
|
|
25981
|
-
const content = await
|
|
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
|
|
25989
|
-
const indexPath =
|
|
25990
|
-
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");
|
|
25991
26041
|
}
|
|
25992
26042
|
function isBinary(filePath) {
|
|
25993
|
-
return BINARY_EXTENSIONS.has(
|
|
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 =
|
|
26019
|
-
const indexDir =
|
|
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 =
|
|
26082
|
+
const fullPath = path30.join(absPath, file);
|
|
26033
26083
|
try {
|
|
26034
|
-
const stat2 = await
|
|
26035
|
-
const content = await
|
|
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
|
|
26095
|
-
var
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 ?
|
|
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
|
|
26429
|
-
var
|
|
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 =
|
|
26514
|
+
const absPath = path32.resolve(filePath);
|
|
26465
26515
|
try {
|
|
26466
|
-
const stat2 = await
|
|
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
|
|
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
|
|
26544
|
-
var
|
|
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 =
|
|
26622
|
+
const absPath = path33.resolve(filePath);
|
|
26573
26623
|
const cwd = process.cwd();
|
|
26574
|
-
if (!absPath.startsWith(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 =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
26998
|
-
var
|
|
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
|
|
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 =
|
|
27158
|
+
const ext = path35.extname(file);
|
|
27109
27159
|
filesByType[ext] = (filesByType[ext] || 0) + 1;
|
|
27110
27160
|
fileStats.push({
|
|
27111
|
-
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
27657
|
-
var
|
|
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
|
|
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 =
|
|
27678
|
-
await
|
|
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
|
|
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
|
|
27851
|
-
var
|
|
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
|
|
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
|
|
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] ||
|
|
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(
|
|
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
|
-
|
|
28041
|
-
const status =
|
|
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 =
|
|
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 =
|
|
28060
|
-
const local =
|
|
28061
|
-
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();
|
|
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 =
|
|
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 =
|
|
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 =
|
|
28094
|
-
const authors =
|
|
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 =
|
|
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
|
-
|
|
28621
|
-
|
|
28622
|
-
|
|
28623
|
-
|
|
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} ${
|
|
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
|
);
|