jinzd-ai-cli 0.4.215 → 0.4.217
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 +5 -2
- package/dist/{batch-74H5SA7P.js → batch-YCOVKTXD.js} +2 -2
- package/dist/{chat-index-R2E27VXN.js → chat-index-PS274XM7.js} +1 -1
- package/dist/{chunk-ZOF5NFKW.js → chunk-524WZOKS.js} +1 -1
- package/dist/{ci-3ALK2XJN.js → chunk-KOPUCJXM.js} +40 -46
- package/dist/{chunk-6C3JYNM6.js → chunk-MGBMNCHG.js} +279 -54
- package/dist/{chunk-KL7UBVSQ.js → chunk-OUAZOE5U.js} +2 -2
- package/dist/{chunk-RS4WBI73.js → chunk-QAYOI57M.js} +3 -1
- package/dist/{chunk-X6OXS7KU.js → chunk-SNJAOXFT.js} +1 -1
- package/dist/{chunk-OQGVGPEK.js → chunk-VHY6NVMQ.js} +2 -0
- package/dist/{chunk-3TSHNZKI.js → chunk-VTH7BLXK.js} +1 -1
- package/dist/{chunk-2224JGA6.js → chunk-WKOQ5CYC.js} +1 -1
- package/dist/chunk-WZ3VKLF3.js +105 -0
- package/dist/{chunk-7JES2NWR.js → chunk-XJGEQIYS.js} +3 -1
- package/dist/ci-52RZIYWB.js +19 -0
- package/dist/ci-format-73UXKE65.js +18 -0
- package/dist/{constants-IN2HXJK7.js → constants-DIXAD35W.js} +3 -1
- package/dist/{doctor-cli-7XL4TCVT.js → doctor-cli-7GOQPULZ.js} +4 -4
- package/dist/electron-server.js +562 -318
- package/dist/{hub-VAOG5EY6.js → hub-GIGBITZN.js} +1 -1
- package/dist/index.js +169 -73
- package/dist/pr-KPQ5RPKB.js +267 -0
- package/dist/{run-tests-J2JQ57R3.js → run-tests-K7QR5QN4.js} +1 -1
- package/dist/{run-tests-QJD43AML.js → run-tests-ZDSA3QES.js} +2 -2
- package/dist/{server-WDLV3W4F.js → server-22YF3U34.js} +5 -4
- package/dist/{server-VGPZOISQ.js → server-QT3SC2KI.js} +129 -86
- package/dist/{task-orchestrator-QFOCO3N7.js → task-orchestrator-BHQQCVTY.js} +5 -4
- package/dist/{usage-IEB476NE.js → usage-WZZFSFLM.js} +2 -2
- package/dist/web/client/app.js +53 -2
- package/dist/web/client/index.html +11 -1
- package/package.json +1 -1
package/dist/electron-server.js
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
MCP_TOOL_PREFIX,
|
|
28
28
|
MEMORY_FILE_NAME,
|
|
29
29
|
MEMORY_MAX_CHARS,
|
|
30
|
+
MEMORY_STORE_FILE_NAME,
|
|
30
31
|
PLAN_MODE_READONLY_TOOLS,
|
|
31
32
|
PLAN_MODE_SYSTEM_ADDON,
|
|
32
33
|
PLUGINS_DIR_NAME,
|
|
@@ -37,7 +38,7 @@ import {
|
|
|
37
38
|
VERSION,
|
|
38
39
|
buildUserIdentityPrompt,
|
|
39
40
|
runTestsTool
|
|
40
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-QAYOI57M.js";
|
|
41
42
|
import {
|
|
42
43
|
hasSemanticIndex,
|
|
43
44
|
semanticSearch
|
|
@@ -47,11 +48,13 @@ import {
|
|
|
47
48
|
} from "./chunk-RWM2GFRC.js";
|
|
48
49
|
import "./chunk-IEQAE3QG.js";
|
|
49
50
|
import {
|
|
51
|
+
DEFAULT_PATTERNS,
|
|
50
52
|
loadChatIndex,
|
|
51
53
|
redactJson,
|
|
54
|
+
redactString,
|
|
52
55
|
scanString,
|
|
53
56
|
searchChatMemory
|
|
54
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-VHY6NVMQ.js";
|
|
55
58
|
import "./chunk-JV5N65KN.js";
|
|
56
59
|
import {
|
|
57
60
|
atomicWriteFileSync
|
|
@@ -61,8 +64,8 @@ import {
|
|
|
61
64
|
import express from "express";
|
|
62
65
|
import { createServer } from "http";
|
|
63
66
|
import { WebSocketServer } from "ws";
|
|
64
|
-
import { join as join22, dirname as
|
|
65
|
-
import { existsSync as
|
|
67
|
+
import { join as join22, dirname as dirname7, resolve as resolve10, relative as relative4, sep as sep3 } from "path";
|
|
68
|
+
import { existsSync as existsSync27, readFileSync as readFileSync21, readdirSync as readdirSync13, statSync as statSync9, realpathSync } from "fs";
|
|
66
69
|
import { networkInterfaces } from "os";
|
|
67
70
|
|
|
68
71
|
// src/config/config-manager.ts
|
|
@@ -7179,7 +7182,7 @@ var ToolExecutor = class {
|
|
|
7179
7182
|
rl.resume();
|
|
7180
7183
|
process.stdout.write(prompt);
|
|
7181
7184
|
this.confirming = true;
|
|
7182
|
-
return new Promise((
|
|
7185
|
+
return new Promise((resolve11) => {
|
|
7183
7186
|
let completed = false;
|
|
7184
7187
|
const cleanup = (result) => {
|
|
7185
7188
|
if (completed) return;
|
|
@@ -7189,7 +7192,7 @@ var ToolExecutor = class {
|
|
|
7189
7192
|
rl.pause();
|
|
7190
7193
|
rlAny.output = savedOutput;
|
|
7191
7194
|
this.confirming = false;
|
|
7192
|
-
|
|
7195
|
+
resolve11(result);
|
|
7193
7196
|
};
|
|
7194
7197
|
const onLine = (line) => {
|
|
7195
7198
|
const trimmed = line.trim();
|
|
@@ -7363,7 +7366,7 @@ var ToolExecutor = class {
|
|
|
7363
7366
|
rl.resume();
|
|
7364
7367
|
process.stdout.write(color("Proceed? [y/N] (type y + Enter to confirm) "));
|
|
7365
7368
|
this.confirming = true;
|
|
7366
|
-
return new Promise((
|
|
7369
|
+
return new Promise((resolve11) => {
|
|
7367
7370
|
let completed = false;
|
|
7368
7371
|
const cleanup = (answer) => {
|
|
7369
7372
|
if (completed) return;
|
|
@@ -7373,7 +7376,7 @@ var ToolExecutor = class {
|
|
|
7373
7376
|
rl.pause();
|
|
7374
7377
|
rlAny.output = savedOutput;
|
|
7375
7378
|
this.confirming = false;
|
|
7376
|
-
|
|
7379
|
+
resolve11(answer === "y");
|
|
7377
7380
|
};
|
|
7378
7381
|
const onLine = (line) => {
|
|
7379
7382
|
const trimmed = line.trim();
|
|
@@ -8622,7 +8625,7 @@ var runInteractiveTool = {
|
|
|
8622
8625
|
PYTHONDONTWRITEBYTECODE: "1"
|
|
8623
8626
|
};
|
|
8624
8627
|
const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
|
|
8625
|
-
return new Promise((
|
|
8628
|
+
return new Promise((resolve11) => {
|
|
8626
8629
|
const child = spawn2(executable, cmdArgs.map(String), {
|
|
8627
8630
|
cwd: process.cwd(),
|
|
8628
8631
|
env,
|
|
@@ -8655,22 +8658,22 @@ var runInteractiveTool = {
|
|
|
8655
8658
|
setTimeout(writeNextLine, 400);
|
|
8656
8659
|
const timer = setTimeout(() => {
|
|
8657
8660
|
child.kill();
|
|
8658
|
-
|
|
8661
|
+
resolve11(`${prefixWarnings}[Timeout after ${timeout}ms]
|
|
8659
8662
|
${buildOutput(stdout, stderr)}`);
|
|
8660
8663
|
}, timeout);
|
|
8661
8664
|
child.on("close", (code) => {
|
|
8662
8665
|
clearTimeout(timer);
|
|
8663
8666
|
const output = buildOutput(stdout, stderr);
|
|
8664
8667
|
if (code !== 0 && code !== null) {
|
|
8665
|
-
|
|
8668
|
+
resolve11(`${prefixWarnings}Exit code ${code}:
|
|
8666
8669
|
${output}`);
|
|
8667
8670
|
} else {
|
|
8668
|
-
|
|
8671
|
+
resolve11(`${prefixWarnings}${output || "(no output)"}`);
|
|
8669
8672
|
}
|
|
8670
8673
|
});
|
|
8671
8674
|
child.on("error", (err) => {
|
|
8672
8675
|
clearTimeout(timer);
|
|
8673
|
-
|
|
8676
|
+
resolve11(
|
|
8674
8677
|
`${prefixWarnings}Failed to start process "${executable}": ${err.message}
|
|
8675
8678
|
Hint: On Windows, use the full path to the executable, e.g.:
|
|
8676
8679
|
C:\\Users\\Jinzd\\anaconda3\\envs\\python312\\python.exe`
|
|
@@ -9338,26 +9341,328 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
9338
9341
|
};
|
|
9339
9342
|
|
|
9340
9343
|
// src/tools/builtin/save-memory.ts
|
|
9341
|
-
import {
|
|
9342
|
-
import { join as join9 } from "path";
|
|
9344
|
+
import { join as join11 } from "path";
|
|
9343
9345
|
import { homedir as homedir5 } from "os";
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
+
|
|
9347
|
+
// src/memory/persistent-memory.ts
|
|
9348
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync11 } from "fs";
|
|
9349
|
+
import { randomUUID, createHash as createHash2 } from "crypto";
|
|
9350
|
+
import { dirname as dirname5, join as join10, resolve as resolve6 } from "path";
|
|
9351
|
+
|
|
9352
|
+
// src/tools/git-context.ts
|
|
9353
|
+
import { execSync as execSync2 } from "child_process";
|
|
9354
|
+
import { existsSync as existsSync14 } from "fs";
|
|
9355
|
+
import { join as join9 } from "path";
|
|
9356
|
+
function runGit(cmd, cwd) {
|
|
9357
|
+
try {
|
|
9358
|
+
return execSync2(`git ${cmd}`, {
|
|
9359
|
+
cwd,
|
|
9360
|
+
encoding: "utf-8",
|
|
9361
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
9362
|
+
timeout: 5e3
|
|
9363
|
+
}).trim();
|
|
9364
|
+
} catch {
|
|
9365
|
+
return null;
|
|
9366
|
+
}
|
|
9346
9367
|
}
|
|
9347
|
-
function
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9368
|
+
function getGitRoot(cwd = process.cwd()) {
|
|
9369
|
+
return runGit("rev-parse --show-toplevel", cwd);
|
|
9370
|
+
}
|
|
9371
|
+
function getGitContext(cwd = process.cwd()) {
|
|
9372
|
+
if (!existsSync14(join9(cwd, ".git"))) {
|
|
9373
|
+
const result = runGit("rev-parse --git-dir", cwd);
|
|
9374
|
+
if (!result) return null;
|
|
9375
|
+
}
|
|
9376
|
+
const branch = runGit("rev-parse --abbrev-ref HEAD", cwd);
|
|
9377
|
+
if (!branch) return null;
|
|
9378
|
+
const statusOutput = runGit("status --porcelain", cwd) ?? "";
|
|
9379
|
+
const statusLines = statusOutput ? statusOutput.split("\n").filter(Boolean) : [];
|
|
9380
|
+
const stagedFiles = [];
|
|
9381
|
+
const changedFiles = [];
|
|
9382
|
+
for (const line of statusLines) {
|
|
9383
|
+
const xy = line.slice(0, 2);
|
|
9384
|
+
const file = line.slice(3).trim();
|
|
9385
|
+
const indexStatus = xy[0];
|
|
9386
|
+
const workStatus = xy[1];
|
|
9387
|
+
if (indexStatus && indexStatus !== " " && indexStatus !== "?") {
|
|
9388
|
+
stagedFiles.push(`${indexStatus} ${file}`);
|
|
9389
|
+
}
|
|
9390
|
+
if (workStatus && workStatus !== " ") {
|
|
9391
|
+
changedFiles.push(`${workStatus} ${file}`);
|
|
9392
|
+
}
|
|
9393
|
+
}
|
|
9394
|
+
const logOutput = runGit("log --oneline -3", cwd) ?? "";
|
|
9395
|
+
const recentCommits = logOutput ? logOutput.split("\n").filter(Boolean) : [];
|
|
9396
|
+
const unpushedOutput = runGit("log @{u}..HEAD --oneline", cwd);
|
|
9397
|
+
const hasUnpushed = unpushedOutput !== null && unpushedOutput.trim().length > 0;
|
|
9398
|
+
return {
|
|
9399
|
+
branch,
|
|
9400
|
+
changedFiles,
|
|
9401
|
+
stagedFiles,
|
|
9402
|
+
recentCommits,
|
|
9403
|
+
hasUnpushed
|
|
9404
|
+
};
|
|
9405
|
+
}
|
|
9406
|
+
function formatGitContextForPrompt(ctx) {
|
|
9407
|
+
const lines = ["# Git Repository Status", ""];
|
|
9408
|
+
lines.push(`- **Branch**: \`${ctx.branch}\``);
|
|
9409
|
+
if (ctx.stagedFiles.length > 0) {
|
|
9410
|
+
lines.push(`- **Staged** (${ctx.stagedFiles.length} files):`);
|
|
9411
|
+
for (const f of ctx.stagedFiles.slice(0, 10)) {
|
|
9412
|
+
lines.push(` - ${f}`);
|
|
9413
|
+
}
|
|
9414
|
+
if (ctx.stagedFiles.length > 10) {
|
|
9415
|
+
lines.push(` - ... and ${ctx.stagedFiles.length - 10} more`);
|
|
9416
|
+
}
|
|
9417
|
+
}
|
|
9418
|
+
if (ctx.changedFiles.length > 0) {
|
|
9419
|
+
lines.push(`- **Modified** (${ctx.changedFiles.length} files):`);
|
|
9420
|
+
for (const f of ctx.changedFiles.slice(0, 10)) {
|
|
9421
|
+
lines.push(` - ${f}`);
|
|
9422
|
+
}
|
|
9423
|
+
if (ctx.changedFiles.length > 10) {
|
|
9424
|
+
lines.push(` - ... and ${ctx.changedFiles.length - 10} more`);
|
|
9425
|
+
}
|
|
9426
|
+
}
|
|
9427
|
+
if (ctx.stagedFiles.length === 0 && ctx.changedFiles.length === 0) {
|
|
9428
|
+
lines.push("- **Working tree**: clean");
|
|
9429
|
+
}
|
|
9430
|
+
if (ctx.recentCommits.length > 0) {
|
|
9431
|
+
lines.push("- **Recent commits**:");
|
|
9432
|
+
for (const c of ctx.recentCommits) {
|
|
9433
|
+
lines.push(` - ${c}`);
|
|
9434
|
+
}
|
|
9435
|
+
}
|
|
9436
|
+
if (ctx.hasUnpushed) {
|
|
9437
|
+
lines.push("- \u26A0\uFE0F Has unpushed commits");
|
|
9438
|
+
}
|
|
9439
|
+
return lines.join("\n");
|
|
9440
|
+
}
|
|
9441
|
+
|
|
9442
|
+
// src/memory/persistent-memory.ts
|
|
9443
|
+
function memoryStorePath(configDir) {
|
|
9444
|
+
return join10(configDir, MEMORY_STORE_FILE_NAME);
|
|
9445
|
+
}
|
|
9446
|
+
function memoryMarkdownPath(configDir) {
|
|
9447
|
+
return join10(configDir, MEMORY_FILE_NAME);
|
|
9448
|
+
}
|
|
9449
|
+
function getProjectKey(cwd = process.cwd()) {
|
|
9450
|
+
return resolve6(getGitRoot(cwd) ?? cwd);
|
|
9451
|
+
}
|
|
9452
|
+
function nowIso() {
|
|
9453
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
9454
|
+
}
|
|
9455
|
+
function isValidScope(value) {
|
|
9456
|
+
return value === "personal" || value === "project" || value === "session" || value === "team";
|
|
9457
|
+
}
|
|
9458
|
+
function isValidSensitivity(value) {
|
|
9459
|
+
return value === "low" || value === "medium" || value === "high";
|
|
9460
|
+
}
|
|
9461
|
+
function normalizeEntry(value) {
|
|
9462
|
+
if (!value || typeof value !== "object") return null;
|
|
9463
|
+
const raw = value;
|
|
9464
|
+
const content = typeof raw.content === "string" ? raw.content.trim() : "";
|
|
9465
|
+
if (!content) return null;
|
|
9466
|
+
const createdAt = typeof raw.createdAt === "string" ? raw.createdAt : nowIso();
|
|
9467
|
+
return {
|
|
9468
|
+
id: typeof raw.id === "string" && raw.id ? raw.id : randomUUID(),
|
|
9469
|
+
content,
|
|
9470
|
+
scope: isValidScope(raw.scope) ? raw.scope : "personal",
|
|
9471
|
+
sourceSession: typeof raw.sourceSession === "string" && raw.sourceSession ? raw.sourceSession : void 0,
|
|
9472
|
+
sourceProject: typeof raw.sourceProject === "string" && raw.sourceProject ? raw.sourceProject : void 0,
|
|
9473
|
+
createdAt,
|
|
9474
|
+
updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : createdAt,
|
|
9475
|
+
expiresAt: typeof raw.expiresAt === "string" && raw.expiresAt ? raw.expiresAt : void 0,
|
|
9476
|
+
sensitivity: isValidSensitivity(raw.sensitivity) ? raw.sensitivity : "low",
|
|
9477
|
+
approved: raw.approved === true,
|
|
9478
|
+
redactedKinds: Array.isArray(raw.redactedKinds) ? raw.redactedKinds.filter((x) => typeof x === "string") : void 0
|
|
9479
|
+
};
|
|
9480
|
+
}
|
|
9481
|
+
function legacyEntriesFromMarkdown(configDir) {
|
|
9482
|
+
const file = memoryMarkdownPath(configDir);
|
|
9483
|
+
if (!existsSync15(file)) return [];
|
|
9484
|
+
const content = readFileSync11(file, "utf-8").trim();
|
|
9485
|
+
if (!content) return [];
|
|
9486
|
+
const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
|
|
9487
|
+
const chunks = parts.length > 1 ? parts : [content];
|
|
9488
|
+
return chunks.map((chunk, index) => {
|
|
9489
|
+
const match = chunk.match(/^##\s+([^\n]+)\n([\s\S]*)$/);
|
|
9490
|
+
const body = (match ? match[2] : chunk).trim();
|
|
9491
|
+
const parsed = match ? Date.parse(match[1].replace(" ", "T")) : NaN;
|
|
9492
|
+
const createdAt = Number.isFinite(parsed) ? new Date(parsed).toISOString() : nowIso();
|
|
9493
|
+
const id = "legacy-" + createHash2("sha1").update(`${index}
|
|
9494
|
+
${chunk}`).digest("hex").slice(0, 12);
|
|
9495
|
+
return normalizeEntry({ id, content: body, scope: "personal", createdAt, updatedAt: createdAt, sensitivity: "low", approved: true });
|
|
9496
|
+
}).filter((entry) => Boolean(entry));
|
|
9497
|
+
}
|
|
9498
|
+
function loadMemoryEntries(configDir) {
|
|
9499
|
+
const file = memoryStorePath(configDir);
|
|
9500
|
+
if (!existsSync15(file)) return legacyEntriesFromMarkdown(configDir);
|
|
9501
|
+
const text = readFileSync11(file, "utf-8").trim();
|
|
9502
|
+
if (!text) return [];
|
|
9503
|
+
const entries = [];
|
|
9504
|
+
for (const line of text.split("\n")) {
|
|
9505
|
+
const trimmed = line.trim();
|
|
9506
|
+
if (!trimmed) continue;
|
|
9507
|
+
try {
|
|
9508
|
+
const entry = normalizeEntry(JSON.parse(trimmed));
|
|
9509
|
+
if (entry) entries.push(entry);
|
|
9510
|
+
} catch {
|
|
9511
|
+
}
|
|
9512
|
+
}
|
|
9513
|
+
return entries;
|
|
9514
|
+
}
|
|
9515
|
+
function saveMemoryEntries(configDir, entries) {
|
|
9516
|
+
mkdirSync7(configDir, { recursive: true });
|
|
9517
|
+
const jsonl = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
9518
|
+
atomicWriteFileSync(memoryStorePath(configDir), jsonl ? jsonl + "\n" : "");
|
|
9519
|
+
syncLegacyMarkdown(configDir, entries);
|
|
9520
|
+
}
|
|
9521
|
+
function isMemoryExpired(entry, at = /* @__PURE__ */ new Date()) {
|
|
9522
|
+
return Boolean(entry.expiresAt && Date.parse(entry.expiresAt) <= at.getTime());
|
|
9523
|
+
}
|
|
9524
|
+
function isMemoryVisibleInProject(entry, cwd = process.cwd()) {
|
|
9525
|
+
if (entry.scope !== "project") return true;
|
|
9526
|
+
return entry.sourceProject === getProjectKey(cwd);
|
|
9527
|
+
}
|
|
9528
|
+
function listMemoryEntries(configDir, options = {}) {
|
|
9529
|
+
const cwd = options.cwd ?? process.cwd();
|
|
9530
|
+
return loadMemoryEntries(configDir).filter((entry) => options.includeRejected || entry.approved).filter((entry) => options.includeExpired || !isMemoryExpired(entry)).filter((entry) => isMemoryVisibleInProject(entry, cwd));
|
|
9531
|
+
}
|
|
9532
|
+
function addMemoryEntry(configDir, content, options = {}) {
|
|
9533
|
+
const original = content.trim();
|
|
9534
|
+
if (!original) throw new Error("memory content is required");
|
|
9535
|
+
const redact = options.redact ?? true;
|
|
9536
|
+
const redacted = redact ? redactString(original, { enabled: true, patterns: options.patterns ?? DEFAULT_PATTERNS, customRegexes: options.customPatterns }) : { redacted: original, hits: [] };
|
|
9537
|
+
const sensitivity = options.sensitivity ?? (redacted.hits.length > 0 ? "high" : "low");
|
|
9538
|
+
const approved = options.approved ?? (sensitivity === "low" && redacted.hits.length === 0);
|
|
9539
|
+
const scope = options.scope ?? "personal";
|
|
9540
|
+
const timestamp = nowIso();
|
|
9541
|
+
const entry = {
|
|
9542
|
+
id: randomUUID(),
|
|
9543
|
+
content: redacted.redacted.trim(),
|
|
9544
|
+
scope,
|
|
9545
|
+
sourceSession: options.sourceSession,
|
|
9546
|
+
sourceProject: scope === "project" ? getProjectKey(options.cwd) : void 0,
|
|
9547
|
+
createdAt: timestamp,
|
|
9548
|
+
updatedAt: timestamp,
|
|
9549
|
+
expiresAt: options.expiresAt,
|
|
9550
|
+
sensitivity,
|
|
9551
|
+
approved,
|
|
9552
|
+
redactedKinds: [...new Set(redacted.hits.map((hit) => hit.kind))]
|
|
9553
|
+
};
|
|
9554
|
+
const entries = loadMemoryEntries(configDir);
|
|
9555
|
+
entries.push(entry);
|
|
9556
|
+
saveMemoryEntries(configDir, entries);
|
|
9557
|
+
return entry;
|
|
9558
|
+
}
|
|
9559
|
+
function updateMemoryApproval(configDir, idPrefix, approved) {
|
|
9560
|
+
const entries = loadMemoryEntries(configDir);
|
|
9561
|
+
const entry = findUnique(entries, idPrefix);
|
|
9562
|
+
entry.approved = approved;
|
|
9563
|
+
entry.updatedAt = nowIso();
|
|
9564
|
+
saveMemoryEntries(configDir, entries);
|
|
9565
|
+
return entry;
|
|
9566
|
+
}
|
|
9567
|
+
function deleteMemoryEntry(configDir, idPrefix) {
|
|
9568
|
+
const entries = loadMemoryEntries(configDir);
|
|
9569
|
+
const entry = findUnique(entries, idPrefix);
|
|
9570
|
+
saveMemoryEntries(configDir, entries.filter((candidate) => candidate.id !== entry.id));
|
|
9571
|
+
return entry;
|
|
9572
|
+
}
|
|
9573
|
+
function expireMemoryEntry(configDir, idPrefix, expiresAt = nowIso()) {
|
|
9574
|
+
const entries = loadMemoryEntries(configDir);
|
|
9575
|
+
const entry = findUnique(entries, idPrefix);
|
|
9576
|
+
entry.expiresAt = expiresAt;
|
|
9577
|
+
entry.updatedAt = nowIso();
|
|
9578
|
+
saveMemoryEntries(configDir, entries);
|
|
9579
|
+
return entry;
|
|
9580
|
+
}
|
|
9581
|
+
function exportMemoryEntries(configDir, cwd = process.cwd()) {
|
|
9582
|
+
return JSON.stringify(listMemoryEntries(configDir, { cwd, includeExpired: true, includeRejected: true }), null, 2);
|
|
9583
|
+
}
|
|
9584
|
+
function findUnique(entries, idPrefix) {
|
|
9585
|
+
const matches = entries.filter((entry) => entry.id.startsWith(idPrefix));
|
|
9586
|
+
if (matches.length === 0) throw new Error(`memory id not found: ${idPrefix}`);
|
|
9587
|
+
if (matches.length > 1) throw new Error(`memory id is ambiguous: ${idPrefix}`);
|
|
9588
|
+
return matches[0];
|
|
9589
|
+
}
|
|
9590
|
+
function syncLegacyMarkdown(configDir, entries = loadMemoryEntries(configDir)) {
|
|
9591
|
+
mkdirSync7(dirname5(memoryMarkdownPath(configDir)), { recursive: true });
|
|
9592
|
+
const active = entries.filter((entry) => entry.approved && !isMemoryExpired(entry));
|
|
9593
|
+
const markdown = active.map((entry) => {
|
|
9594
|
+
const date = entry.createdAt.replace("T", " ").slice(0, 19);
|
|
9595
|
+
const source = [
|
|
9596
|
+
`id:${entry.id.slice(0, 8)}`,
|
|
9597
|
+
`scope:${entry.scope}`,
|
|
9598
|
+
entry.sourceSession ? `session:${entry.sourceSession.slice(0, 8)}` : "",
|
|
9599
|
+
entry.sourceProject ? `project:${entry.sourceProject}` : "",
|
|
9600
|
+
`sensitivity:${entry.sensitivity}`
|
|
9601
|
+
].filter(Boolean).join(" \xB7 ");
|
|
9602
|
+
return `## ${date}
|
|
9603
|
+
<!-- ${source} -->
|
|
9604
|
+
${entry.content}
|
|
9605
|
+
`;
|
|
9606
|
+
}).join("\n");
|
|
9607
|
+
atomicWriteFileSync(memoryMarkdownPath(configDir), markdown);
|
|
9608
|
+
}
|
|
9609
|
+
function formatMemoryForPrompt(configDir, cwd = process.cwd()) {
|
|
9610
|
+
const entries = listMemoryEntries(configDir, { cwd }).filter((entry) => entry.approved && !isMemoryExpired(entry));
|
|
9611
|
+
if (entries.length === 0) return null;
|
|
9612
|
+
let content = entries.map((entry) => {
|
|
9613
|
+
const source = [
|
|
9614
|
+
`id ${entry.id.slice(0, 8)}`,
|
|
9615
|
+
`scope ${entry.scope}`,
|
|
9616
|
+
entry.sourceSession ? `source session ${entry.sourceSession.slice(0, 8)}` : "source manual/tool",
|
|
9617
|
+
entry.sourceProject ? `project ${entry.sourceProject}` : "",
|
|
9618
|
+
`sensitivity ${entry.sensitivity}`
|
|
9619
|
+
].filter(Boolean).join(" \xB7 ");
|
|
9620
|
+
return `## ${entry.createdAt.replace("T", " ").slice(0, 19)}
|
|
9621
|
+
Source: ${source}
|
|
9622
|
+
${entry.content}`;
|
|
9623
|
+
}).join("\n\n");
|
|
9624
|
+
if (content.length > MEMORY_MAX_CHARS) {
|
|
9625
|
+
content = content.slice(-MEMORY_MAX_CHARS);
|
|
9626
|
+
const firstEntry = content.indexOf("\n## ");
|
|
9627
|
+
if (firstEntry !== -1) content = content.slice(firstEntry + 1);
|
|
9628
|
+
}
|
|
9629
|
+
return { content, entryCount: entries.length };
|
|
9630
|
+
}
|
|
9631
|
+
|
|
9632
|
+
// src/tools/builtin/save-memory.ts
|
|
9633
|
+
function getConfigDir() {
|
|
9634
|
+
return join11(homedir5(), CONFIG_DIR_NAME);
|
|
9635
|
+
}
|
|
9636
|
+
function parseScope(value) {
|
|
9637
|
+
return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
|
|
9638
|
+
}
|
|
9639
|
+
function parseSensitivity(value) {
|
|
9640
|
+
return value === "low" || value === "medium" || value === "high" ? value : void 0;
|
|
9351
9641
|
}
|
|
9352
9642
|
var saveMemoryTool = {
|
|
9353
9643
|
definition: {
|
|
9354
9644
|
name: "save_memory",
|
|
9355
|
-
description: "Save important information to persistent memory
|
|
9645
|
+
description: "Save important information to governed persistent memory. Low-risk memories are approved automatically; sensitive entries are stored as pending and require /memory approve <id>. Use concise entries for user preferences, project decisions, recurring patterns, and key findings.",
|
|
9356
9646
|
parameters: {
|
|
9357
9647
|
content: {
|
|
9358
9648
|
type: "string",
|
|
9359
9649
|
description: "The information to save to persistent memory. Keep it concise and actionable.",
|
|
9360
9650
|
required: true
|
|
9651
|
+
},
|
|
9652
|
+
scope: {
|
|
9653
|
+
type: "string",
|
|
9654
|
+
description: "Memory scope: personal, project, session, or team. Defaults to personal. Project memories are only injected in the same project.",
|
|
9655
|
+
required: false
|
|
9656
|
+
},
|
|
9657
|
+
sensitivity: {
|
|
9658
|
+
type: "string",
|
|
9659
|
+
description: "Sensitivity: low, medium, or high. Low-risk entries may be auto-approved; medium/high remain pending unless explicitly approved.",
|
|
9660
|
+
required: false
|
|
9661
|
+
},
|
|
9662
|
+
expiresAt: {
|
|
9663
|
+
type: "string",
|
|
9664
|
+
description: "Optional ISO timestamp after which this memory should no longer be injected.",
|
|
9665
|
+
required: false
|
|
9361
9666
|
}
|
|
9362
9667
|
},
|
|
9363
9668
|
dangerous: false
|
|
@@ -9365,20 +9670,16 @@ var saveMemoryTool = {
|
|
|
9365
9670
|
async execute(args) {
|
|
9366
9671
|
const content = String(args["content"] ?? "").trim();
|
|
9367
9672
|
if (!content) throw new ToolError("save_memory", "content is required");
|
|
9368
|
-
const
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
${
|
|
9377
|
-
`;
|
|
9378
|
-
const previous = existsSync14(memoryPath) ? readFileSync11(memoryPath, "utf-8") : "";
|
|
9379
|
-
atomicWriteFileSync(memoryPath, previous + entry);
|
|
9380
|
-
const byteSize = statSync6(memoryPath).size;
|
|
9381
|
-
return `Memory saved successfully. File size: ${byteSize} bytes in ${MEMORY_FILE_NAME}`;
|
|
9673
|
+
const entry = addMemoryEntry(getConfigDir(), content, {
|
|
9674
|
+
scope: parseScope(args["scope"]) ?? "personal",
|
|
9675
|
+
sensitivity: parseSensitivity(args["sensitivity"]),
|
|
9676
|
+
expiresAt: typeof args["expiresAt"] === "string" ? args["expiresAt"] : void 0,
|
|
9677
|
+
cwd: process.cwd(),
|
|
9678
|
+
redact: true
|
|
9679
|
+
});
|
|
9680
|
+
const status = entry.approved ? "approved and active" : "pending approval";
|
|
9681
|
+
const redacted = entry.redactedKinds?.length ? ` Redacted: ${entry.redactedKinds.join(", ")}.` : "";
|
|
9682
|
+
return `Memory saved (${status}). id=${entry.id.slice(0, 8)} scope=${entry.scope} sensitivity=${entry.sensitivity}.${redacted}`;
|
|
9382
9683
|
}
|
|
9383
9684
|
};
|
|
9384
9685
|
|
|
@@ -9422,7 +9723,7 @@ function promptUser(rl, question) {
|
|
|
9422
9723
|
console.log();
|
|
9423
9724
|
console.log(chalk4.cyan("\u2753 ") + chalk4.bold(question));
|
|
9424
9725
|
process.stdout.write(chalk4.cyan("> "));
|
|
9425
|
-
return new Promise((
|
|
9726
|
+
return new Promise((resolve11) => {
|
|
9426
9727
|
let completed = false;
|
|
9427
9728
|
const cleanup = (answer) => {
|
|
9428
9729
|
if (completed) return;
|
|
@@ -9432,7 +9733,7 @@ function promptUser(rl, question) {
|
|
|
9432
9733
|
rl.pause();
|
|
9433
9734
|
rlAny.output = savedOutput;
|
|
9434
9735
|
askUserContext.prompting = false;
|
|
9435
|
-
|
|
9736
|
+
resolve11(answer);
|
|
9436
9737
|
};
|
|
9437
9738
|
const onLine = (line) => {
|
|
9438
9739
|
cleanup(line);
|
|
@@ -9664,8 +9965,8 @@ function formatResults2(query, data, _requested) {
|
|
|
9664
9965
|
}
|
|
9665
9966
|
|
|
9666
9967
|
// src/agents/agent-config.ts
|
|
9667
|
-
import { existsSync as
|
|
9668
|
-
import { join as
|
|
9968
|
+
import { existsSync as existsSync16, readdirSync as readdirSync7, readFileSync as readFileSync12 } from "fs";
|
|
9969
|
+
import { join as join12 } from "path";
|
|
9669
9970
|
import { homedir as homedir6 } from "os";
|
|
9670
9971
|
var READ_ONLY_TOOLS2 = /* @__PURE__ */ new Set([
|
|
9671
9972
|
"read_file",
|
|
@@ -9760,11 +10061,11 @@ function parseAgentConfig(raw, source, path3) {
|
|
|
9760
10061
|
return cfg;
|
|
9761
10062
|
}
|
|
9762
10063
|
function loadAgentDir(dir, source) {
|
|
9763
|
-
if (!
|
|
10064
|
+
if (!existsSync16(dir)) return [];
|
|
9764
10065
|
const out = [];
|
|
9765
10066
|
for (const file of readdirSync7(dir)) {
|
|
9766
10067
|
if (!file.endsWith(".json")) continue;
|
|
9767
|
-
const path3 =
|
|
10068
|
+
const path3 = join12(dir, file);
|
|
9768
10069
|
try {
|
|
9769
10070
|
const parsed = JSON.parse(readFileSync12(path3, "utf-8"));
|
|
9770
10071
|
const cfg = parseAgentConfig(parsed, source, path3);
|
|
@@ -9776,8 +10077,8 @@ function loadAgentDir(dir, source) {
|
|
|
9776
10077
|
}
|
|
9777
10078
|
function getAgentDirs(configDir, cwd = process.cwd()) {
|
|
9778
10079
|
return {
|
|
9779
|
-
user:
|
|
9780
|
-
project:
|
|
10080
|
+
user: join12(configDir ?? join12(homedir6(), CONFIG_DIR_NAME), "agents"),
|
|
10081
|
+
project: join12(cwd, CONFIG_DIR_NAME, "agents")
|
|
9781
10082
|
};
|
|
9782
10083
|
}
|
|
9783
10084
|
function listAgentConfigs(configDir, cwd = process.cwd()) {
|
|
@@ -9819,7 +10120,7 @@ var nextId = 1;
|
|
|
9819
10120
|
var preferredAgentName = "worker";
|
|
9820
10121
|
var runs = [];
|
|
9821
10122
|
var MAX_RUNS = 50;
|
|
9822
|
-
function
|
|
10123
|
+
function nowIso2() {
|
|
9823
10124
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
9824
10125
|
}
|
|
9825
10126
|
function startAgentRun(input) {
|
|
@@ -9828,7 +10129,7 @@ function startAgentRun(input) {
|
|
|
9828
10129
|
agentName: input.agentName,
|
|
9829
10130
|
task: input.task,
|
|
9830
10131
|
status: "running",
|
|
9831
|
-
startedAt:
|
|
10132
|
+
startedAt: nowIso2(),
|
|
9832
10133
|
toolNames: [],
|
|
9833
10134
|
agentIndex: input.agentIndex,
|
|
9834
10135
|
stopRequested: false
|
|
@@ -9846,7 +10147,7 @@ function finishAgentRun(runId, status, summary, error) {
|
|
|
9846
10147
|
const run = runs.find((r) => r.id === runId);
|
|
9847
10148
|
if (!run) return;
|
|
9848
10149
|
run.status = status;
|
|
9849
|
-
run.finishedAt =
|
|
10150
|
+
run.finishedAt = nowIso2();
|
|
9850
10151
|
if (summary) run.summary = summary;
|
|
9851
10152
|
if (error) run.error = error;
|
|
9852
10153
|
}
|
|
@@ -10272,7 +10573,7 @@ var spawnAgentTool = {
|
|
|
10272
10573
|
|
|
10273
10574
|
// src/tools/builtin/task-manager.ts
|
|
10274
10575
|
import { spawn as spawn3 } from "child_process";
|
|
10275
|
-
import { randomUUID } from "crypto";
|
|
10576
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
10276
10577
|
import { platform as platform4 } from "os";
|
|
10277
10578
|
var MAX_OUTPUT_CHARS = 1e4;
|
|
10278
10579
|
var MAX_OUTPUT_BYTES = 64 * 1024;
|
|
@@ -10305,7 +10606,7 @@ function createTask(command, description) {
|
|
|
10305
10606
|
}
|
|
10306
10607
|
}
|
|
10307
10608
|
}
|
|
10308
|
-
const id =
|
|
10609
|
+
const id = randomUUID2().slice(0, 8);
|
|
10309
10610
|
const { shell, args } = shellInvocation(command);
|
|
10310
10611
|
const proc = spawn3(shell, args, {
|
|
10311
10612
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -10487,20 +10788,20 @@ var taskStopTool = {
|
|
|
10487
10788
|
|
|
10488
10789
|
// src/tools/builtin/git-tools.ts
|
|
10489
10790
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
10490
|
-
import { existsSync as
|
|
10491
|
-
import { join as
|
|
10791
|
+
import { existsSync as existsSync17 } from "fs";
|
|
10792
|
+
import { join as join13 } from "path";
|
|
10492
10793
|
function assertGitRepo(cwd) {
|
|
10493
10794
|
let dir = cwd;
|
|
10494
10795
|
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
10495
10796
|
while (dir && dir !== root) {
|
|
10496
|
-
if (
|
|
10497
|
-
const parent =
|
|
10797
|
+
if (existsSync17(join13(dir, ".git"))) return;
|
|
10798
|
+
const parent = join13(dir, "..");
|
|
10498
10799
|
if (parent === dir) break;
|
|
10499
10800
|
dir = parent;
|
|
10500
10801
|
}
|
|
10501
10802
|
throw new ToolError("git", "Not inside a git repository. Run this command from a directory under a git-tracked project.");
|
|
10502
10803
|
}
|
|
10503
|
-
function
|
|
10804
|
+
function runGit2(args, cwd, maxBuffer = 10 * 1024 * 1024) {
|
|
10504
10805
|
try {
|
|
10505
10806
|
const output = execFileSync2("git", args, {
|
|
10506
10807
|
cwd,
|
|
@@ -10535,11 +10836,11 @@ var gitStatusTool = {
|
|
|
10535
10836
|
assertGitRepo(cwd);
|
|
10536
10837
|
let branch = "(unknown)";
|
|
10537
10838
|
try {
|
|
10538
|
-
branch =
|
|
10839
|
+
branch = runGit2(["symbolic-ref", "--short", "HEAD"], cwd).trim();
|
|
10539
10840
|
} catch {
|
|
10540
10841
|
branch = "(detached)";
|
|
10541
10842
|
}
|
|
10542
|
-
const porcelain =
|
|
10843
|
+
const porcelain = runGit2(["status", "--porcelain=v1", "-b"], cwd);
|
|
10543
10844
|
const lines = porcelain.split("\n").filter((l) => l.length > 0);
|
|
10544
10845
|
const branchLine = lines[0]?.startsWith("##") ? lines[0].slice(3) : `${branch}`;
|
|
10545
10846
|
const fileLines = lines.slice(lines[0]?.startsWith("##") ? 1 : 0);
|
|
@@ -10626,7 +10927,7 @@ var gitDiffTool = {
|
|
|
10626
10927
|
if (args["file"]) {
|
|
10627
10928
|
gitArgs.push("--", String(args["file"]));
|
|
10628
10929
|
}
|
|
10629
|
-
const output =
|
|
10930
|
+
const output = runGit2(gitArgs, cwd);
|
|
10630
10931
|
if (output.trim().length === 0) {
|
|
10631
10932
|
const label = args["staged"] ? "staged" : args["commit_range"] ? String(args["commit_range"]) : "unstaged";
|
|
10632
10933
|
return `No ${label} changes.`;
|
|
@@ -10688,7 +10989,7 @@ var gitLogTool = {
|
|
|
10688
10989
|
if (args["file"]) {
|
|
10689
10990
|
gitArgs.push("--", String(args["file"]));
|
|
10690
10991
|
}
|
|
10691
|
-
const output =
|
|
10992
|
+
const output = runGit2(gitArgs, cwd);
|
|
10692
10993
|
if (output.trim().length === 0) {
|
|
10693
10994
|
return "No commits found matching the filter.";
|
|
10694
10995
|
}
|
|
@@ -10735,9 +11036,9 @@ var gitCommitTool = {
|
|
|
10735
11036
|
throw new ToolError("git_commit", "Cannot use both stage_all and files at the same time.");
|
|
10736
11037
|
}
|
|
10737
11038
|
if (files.length > 0) {
|
|
10738
|
-
|
|
11039
|
+
runGit2(["add", "--", ...files], cwd);
|
|
10739
11040
|
}
|
|
10740
|
-
const statusBefore =
|
|
11041
|
+
const statusBefore = runGit2(["status", "--porcelain"], cwd);
|
|
10741
11042
|
const hasStaged = statusBefore.split("\n").some((l) => l.length > 0 && l[0] !== " " && l[0] !== "?");
|
|
10742
11043
|
const hasUnstaged = statusBefore.split("\n").some((l) => l.length > 1 && l[1] !== " " && l[1] !== "?");
|
|
10743
11044
|
if (!hasStaged && !stageAll) {
|
|
@@ -10748,9 +11049,9 @@ var gitCommitTool = {
|
|
|
10748
11049
|
}
|
|
10749
11050
|
const commitArgs = ["commit", "-m", message];
|
|
10750
11051
|
if (stageAll) commitArgs.splice(1, 0, "-a");
|
|
10751
|
-
const commitOutput =
|
|
10752
|
-
const hash =
|
|
10753
|
-
const summary =
|
|
11052
|
+
const commitOutput = runGit2(commitArgs, cwd);
|
|
11053
|
+
const hash = runGit2(["rev-parse", "--short", "HEAD"], cwd).trim();
|
|
11054
|
+
const summary = runGit2(["log", "-1", "--pretty=format:%h %s"], cwd).trim();
|
|
10754
11055
|
return `\u2713 Committed ${hash}
|
|
10755
11056
|
${summary}
|
|
10756
11057
|
|
|
@@ -10759,9 +11060,9 @@ ${commitOutput.trim()}`;
|
|
|
10759
11060
|
};
|
|
10760
11061
|
|
|
10761
11062
|
// src/tools/builtin/notebook-edit.ts
|
|
10762
|
-
import { readFileSync as readFileSync13, existsSync as
|
|
11063
|
+
import { readFileSync as readFileSync13, existsSync as existsSync18 } from "fs";
|
|
10763
11064
|
import { writeFile } from "fs/promises";
|
|
10764
|
-
import { resolve as
|
|
11065
|
+
import { resolve as resolve7, extname as extname2 } from "path";
|
|
10765
11066
|
var notebookEditTool = {
|
|
10766
11067
|
definition: {
|
|
10767
11068
|
name: "notebook_edit",
|
|
@@ -10810,11 +11111,11 @@ var notebookEditTool = {
|
|
|
10810
11111
|
if (!Number.isInteger(cellIndexRaw) || cellIndexRaw < 1) {
|
|
10811
11112
|
throw new ToolError("notebook_edit", "cell_index must be a positive integer (1-based)");
|
|
10812
11113
|
}
|
|
10813
|
-
const absPath =
|
|
11114
|
+
const absPath = resolve7(filePath);
|
|
10814
11115
|
if (extname2(absPath).toLowerCase() !== ".ipynb") {
|
|
10815
11116
|
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
10816
11117
|
}
|
|
10817
|
-
if (!
|
|
11118
|
+
if (!existsSync18(absPath)) {
|
|
10818
11119
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
10819
11120
|
}
|
|
10820
11121
|
const raw = readFileSync13(absPath, "utf-8");
|
|
@@ -11234,8 +11535,8 @@ function estimateToolDefinitionTokens(def) {
|
|
|
11234
11535
|
|
|
11235
11536
|
// src/tools/registry.ts
|
|
11236
11537
|
import { pathToFileURL } from "url";
|
|
11237
|
-
import { existsSync as
|
|
11238
|
-
import { join as
|
|
11538
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync8, readdirSync as readdirSync8 } from "fs";
|
|
11539
|
+
import { join as join14 } from "path";
|
|
11239
11540
|
var ToolRegistry = class {
|
|
11240
11541
|
tools = /* @__PURE__ */ new Map();
|
|
11241
11542
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -11396,7 +11697,7 @@ var ToolRegistry = class {
|
|
|
11396
11697
|
* Returns the number of successfully loaded plugins.
|
|
11397
11698
|
*/
|
|
11398
11699
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
11399
|
-
if (!
|
|
11700
|
+
if (!existsSync19(pluginsDir)) {
|
|
11400
11701
|
try {
|
|
11401
11702
|
mkdirSync8(pluginsDir, { recursive: true });
|
|
11402
11703
|
} catch {
|
|
@@ -11422,12 +11723,12 @@ var ToolRegistry = class {
|
|
|
11422
11723
|
process.stderr.write(
|
|
11423
11724
|
`
|
|
11424
11725
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
11425
|
-
` + files.map((f) => ` + ${
|
|
11726
|
+
` + files.map((f) => ` + ${join14(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
11426
11727
|
);
|
|
11427
11728
|
let loaded = 0;
|
|
11428
11729
|
for (const file of files) {
|
|
11429
11730
|
try {
|
|
11430
|
-
const fileUrl = pathToFileURL(
|
|
11731
|
+
const fileUrl = pathToFileURL(join14(pluginsDir, file)).href;
|
|
11431
11732
|
const mod = await import(fileUrl);
|
|
11432
11733
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
11433
11734
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -11589,7 +11890,7 @@ var McpClient = class {
|
|
|
11589
11890
|
// 内部方法:JSON-RPC 通信
|
|
11590
11891
|
// ══════════════════════════════════════════════════════════════════
|
|
11591
11892
|
sendRequest(method, params) {
|
|
11592
|
-
return new Promise((
|
|
11893
|
+
return new Promise((resolve11, reject) => {
|
|
11593
11894
|
if (!this.process?.stdin?.writable) {
|
|
11594
11895
|
return reject(new Error(`MCP server [${this.serverId}] stdin not writable`));
|
|
11595
11896
|
}
|
|
@@ -11613,7 +11914,7 @@ var McpClient = class {
|
|
|
11613
11914
|
this.pendingRequests.set(id, {
|
|
11614
11915
|
resolve: (result) => {
|
|
11615
11916
|
cleanup();
|
|
11616
|
-
|
|
11917
|
+
resolve11(result);
|
|
11617
11918
|
},
|
|
11618
11919
|
reject: (error) => {
|
|
11619
11920
|
cleanup();
|
|
@@ -11690,13 +11991,13 @@ var McpClient = class {
|
|
|
11690
11991
|
}
|
|
11691
11992
|
/** Promise 超时包装 */
|
|
11692
11993
|
withTimeout(promise, ms, label) {
|
|
11693
|
-
return new Promise((
|
|
11994
|
+
return new Promise((resolve11, reject) => {
|
|
11694
11995
|
const timer = setTimeout(() => {
|
|
11695
11996
|
reject(new Error(`MCP [${this.serverId}] ${label} timed out after ${ms}ms`));
|
|
11696
11997
|
}, ms);
|
|
11697
11998
|
promise.then((val) => {
|
|
11698
11999
|
clearTimeout(timer);
|
|
11699
|
-
|
|
12000
|
+
resolve11(val);
|
|
11700
12001
|
}).catch((err) => {
|
|
11701
12002
|
clearTimeout(timer);
|
|
11702
12003
|
reject(err);
|
|
@@ -11973,8 +12274,8 @@ var McpManager = class {
|
|
|
11973
12274
|
};
|
|
11974
12275
|
|
|
11975
12276
|
// src/skills/manager.ts
|
|
11976
|
-
import { existsSync as
|
|
11977
|
-
import { join as
|
|
12277
|
+
import { existsSync as existsSync20, readdirSync as readdirSync9, mkdirSync as mkdirSync9, statSync as statSync6 } from "fs";
|
|
12278
|
+
import { join as join15 } from "path";
|
|
11978
12279
|
|
|
11979
12280
|
// src/skills/types.ts
|
|
11980
12281
|
import { readFileSync as readFileSync14 } from "fs";
|
|
@@ -12042,7 +12343,7 @@ var SkillManager = class {
|
|
|
12042
12343
|
/** 发现并加载 skillsDir 下所有 .md 文件,返回加载数量 */
|
|
12043
12344
|
loadSkills() {
|
|
12044
12345
|
this.skills.clear();
|
|
12045
|
-
if (!
|
|
12346
|
+
if (!existsSync20(this.skillsDir)) {
|
|
12046
12347
|
try {
|
|
12047
12348
|
mkdirSync9(this.skillsDir, { recursive: true });
|
|
12048
12349
|
} catch {
|
|
@@ -12057,14 +12358,14 @@ var SkillManager = class {
|
|
|
12057
12358
|
}
|
|
12058
12359
|
for (const entry of entries) {
|
|
12059
12360
|
let filePath;
|
|
12060
|
-
const fullPath =
|
|
12361
|
+
const fullPath = join15(this.skillsDir, entry);
|
|
12061
12362
|
if (entry.endsWith(".md")) {
|
|
12062
12363
|
filePath = fullPath;
|
|
12063
12364
|
} else {
|
|
12064
12365
|
try {
|
|
12065
|
-
if (
|
|
12066
|
-
const skillMd =
|
|
12067
|
-
if (
|
|
12366
|
+
if (statSync6(fullPath).isDirectory()) {
|
|
12367
|
+
const skillMd = join15(fullPath, "SKILL.md");
|
|
12368
|
+
if (existsSync20(skillMd)) {
|
|
12068
12369
|
filePath = skillMd;
|
|
12069
12370
|
} else {
|
|
12070
12371
|
continue;
|
|
@@ -12127,9 +12428,9 @@ var SkillManager = class {
|
|
|
12127
12428
|
};
|
|
12128
12429
|
|
|
12129
12430
|
// src/web/tool-executor-web.ts
|
|
12130
|
-
import { randomUUID as
|
|
12431
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
12131
12432
|
import { tmpdir as tmpdir2 } from "os";
|
|
12132
|
-
import { existsSync as
|
|
12433
|
+
import { existsSync as existsSync21, readFileSync as readFileSync15 } from "fs";
|
|
12133
12434
|
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
12134
12435
|
constructor(registry, ws) {
|
|
12135
12436
|
this.registry = registry;
|
|
@@ -12199,33 +12500,33 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12199
12500
|
}
|
|
12200
12501
|
/** Resolve a pending confirm from client response */
|
|
12201
12502
|
resolveConfirm(requestId, approved) {
|
|
12202
|
-
const
|
|
12203
|
-
if (
|
|
12503
|
+
const resolve11 = this.pendingConfirms.get(requestId);
|
|
12504
|
+
if (resolve11) {
|
|
12204
12505
|
this.clearPendingTimer(requestId);
|
|
12205
12506
|
this.pendingConfirms.delete(requestId);
|
|
12206
12507
|
this.confirming = false;
|
|
12207
|
-
|
|
12508
|
+
resolve11(approved);
|
|
12208
12509
|
}
|
|
12209
12510
|
}
|
|
12210
12511
|
/** Resolve a pending batch confirm from client response */
|
|
12211
12512
|
resolveBatchConfirm(requestId, decision) {
|
|
12212
|
-
const
|
|
12213
|
-
if (
|
|
12513
|
+
const resolve11 = this.pendingBatchConfirms.get(requestId);
|
|
12514
|
+
if (resolve11) {
|
|
12214
12515
|
this.clearPendingTimer(requestId);
|
|
12215
12516
|
this.pendingBatchConfirms.delete(requestId);
|
|
12216
12517
|
this.confirming = false;
|
|
12217
12518
|
if (decision === "all" || decision === "none") {
|
|
12218
|
-
|
|
12519
|
+
resolve11(decision);
|
|
12219
12520
|
} else {
|
|
12220
|
-
|
|
12521
|
+
resolve11(new Set(decision));
|
|
12221
12522
|
}
|
|
12222
12523
|
}
|
|
12223
12524
|
}
|
|
12224
12525
|
/** Cancel all pending confirms (e.g., on disconnect) */
|
|
12225
12526
|
cancelAll() {
|
|
12226
|
-
for (const
|
|
12527
|
+
for (const resolve11 of this.pendingConfirms.values()) resolve11(false);
|
|
12227
12528
|
this.pendingConfirms.clear();
|
|
12228
|
-
for (const
|
|
12529
|
+
for (const resolve11 of this.pendingBatchConfirms.values()) resolve11("none");
|
|
12229
12530
|
this.pendingBatchConfirms.clear();
|
|
12230
12531
|
this.confirming = false;
|
|
12231
12532
|
}
|
|
@@ -12268,7 +12569,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12268
12569
|
if (call.name === "write_file") {
|
|
12269
12570
|
const filePath = String(call.arguments["path"] ?? "");
|
|
12270
12571
|
const newContent = String(call.arguments["content"] ?? "");
|
|
12271
|
-
if (filePath &&
|
|
12572
|
+
if (filePath && existsSync21(filePath)) {
|
|
12272
12573
|
try {
|
|
12273
12574
|
const old = readFileSync15(filePath, "utf-8");
|
|
12274
12575
|
return renderDiff(old, newContent, { filePath });
|
|
@@ -12290,7 +12591,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12290
12591
|
}
|
|
12291
12592
|
/** WebSocket-based confirm — sends request, returns Promise resolved by client */
|
|
12292
12593
|
confirm(call, level) {
|
|
12293
|
-
const requestId =
|
|
12594
|
+
const requestId = randomUUID3();
|
|
12294
12595
|
this.confirming = true;
|
|
12295
12596
|
const msg = {
|
|
12296
12597
|
type: "confirm_request",
|
|
@@ -12301,8 +12602,8 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12301
12602
|
diff: this.getDiffPreview(call)
|
|
12302
12603
|
};
|
|
12303
12604
|
this.send(msg);
|
|
12304
|
-
return new Promise((
|
|
12305
|
-
this.pendingConfirms.set(requestId,
|
|
12605
|
+
return new Promise((resolve11) => {
|
|
12606
|
+
this.pendingConfirms.set(requestId, resolve11);
|
|
12306
12607
|
this.pendingTimers.set(requestId, setTimeout(() => {
|
|
12307
12608
|
if (this.pendingConfirms.has(requestId)) {
|
|
12308
12609
|
this.resolveConfirm(requestId, false);
|
|
@@ -12312,7 +12613,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12312
12613
|
}
|
|
12313
12614
|
/** WebSocket-based batch confirm */
|
|
12314
12615
|
batchConfirm(calls) {
|
|
12315
|
-
const requestId =
|
|
12616
|
+
const requestId = randomUUID3();
|
|
12316
12617
|
this.confirming = true;
|
|
12317
12618
|
const files = calls.map((call, i) => ({
|
|
12318
12619
|
index: i + 1,
|
|
@@ -12326,8 +12627,8 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12326
12627
|
files
|
|
12327
12628
|
};
|
|
12328
12629
|
this.send(msg);
|
|
12329
|
-
return new Promise((
|
|
12330
|
-
this.pendingBatchConfirms.set(requestId,
|
|
12630
|
+
return new Promise((resolve11) => {
|
|
12631
|
+
this.pendingBatchConfirms.set(requestId, resolve11);
|
|
12331
12632
|
this.pendingTimers.set(requestId, setTimeout(() => {
|
|
12332
12633
|
if (this.pendingBatchConfirms.has(requestId)) {
|
|
12333
12634
|
this.resolveBatchConfirm(requestId, "none");
|
|
@@ -12572,20 +12873,16 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12572
12873
|
}
|
|
12573
12874
|
};
|
|
12574
12875
|
|
|
12575
|
-
// src/core/system-prompt-builder.ts
|
|
12576
|
-
import { existsSync as existsSync22, readFileSync as readFileSync17 } from "fs";
|
|
12577
|
-
import { join as join15 } from "path";
|
|
12578
|
-
|
|
12579
12876
|
// src/repl/dev-state.ts
|
|
12580
|
-
import { existsSync as
|
|
12581
|
-
import { join as
|
|
12877
|
+
import { existsSync as existsSync22, readFileSync as readFileSync16, unlinkSync as unlinkSync4, mkdirSync as mkdirSync10 } from "fs";
|
|
12878
|
+
import { join as join16 } from "path";
|
|
12582
12879
|
import { homedir as homedir7 } from "os";
|
|
12583
12880
|
function getDevStatePath() {
|
|
12584
|
-
return
|
|
12881
|
+
return join16(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
|
|
12585
12882
|
}
|
|
12586
12883
|
function loadDevState() {
|
|
12587
12884
|
const path3 = getDevStatePath();
|
|
12588
|
-
if (!
|
|
12885
|
+
if (!existsSync22(path3)) return null;
|
|
12589
12886
|
const content = readFileSync16(path3, "utf-8").trim();
|
|
12590
12887
|
return content || null;
|
|
12591
12888
|
}
|
|
@@ -12644,19 +12941,7 @@ ${ctx.activeSkill.content}`);
|
|
|
12644
12941
|
return { stable: stableParts.join("\n\n---\n\n"), volatile };
|
|
12645
12942
|
}
|
|
12646
12943
|
function loadMemoryContent(configDir) {
|
|
12647
|
-
|
|
12648
|
-
if (!existsSync22(memoryPath)) return null;
|
|
12649
|
-
let content = readFileSync17(memoryPath, "utf-8").trim();
|
|
12650
|
-
if (!content) return null;
|
|
12651
|
-
if (content.length > MEMORY_MAX_CHARS) {
|
|
12652
|
-
content = content.slice(-MEMORY_MAX_CHARS);
|
|
12653
|
-
const firstEntry = content.indexOf("\n## ");
|
|
12654
|
-
if (firstEntry !== -1) {
|
|
12655
|
-
content = content.slice(firstEntry + 1);
|
|
12656
|
-
}
|
|
12657
|
-
}
|
|
12658
|
-
const entryCount = (content.match(/^## \d{4}-\d{2}-\d{2}/gm) ?? []).length;
|
|
12659
|
-
return { content, entryCount };
|
|
12944
|
+
return formatMemoryForPrompt(configDir, process.cwd());
|
|
12660
12945
|
}
|
|
12661
12946
|
|
|
12662
12947
|
// src/core/pricing.ts
|
|
@@ -12855,8 +13140,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
12855
13140
|
}
|
|
12856
13141
|
|
|
12857
13142
|
// src/core/context-files.ts
|
|
12858
|
-
import { existsSync as existsSync23, readFileSync as
|
|
12859
|
-
import { join as
|
|
13143
|
+
import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
|
|
13144
|
+
import { join as join17, relative as relative3, resolve as resolve8 } from "path";
|
|
12860
13145
|
function uniqueNonEmpty(values) {
|
|
12861
13146
|
const seen = /* @__PURE__ */ new Set();
|
|
12862
13147
|
const out = [];
|
|
@@ -12879,14 +13164,14 @@ function displayPath(filePath, cwd) {
|
|
|
12879
13164
|
return filePath;
|
|
12880
13165
|
}
|
|
12881
13166
|
function isInsideOrEqual(parent, child) {
|
|
12882
|
-
const rel = relative3(
|
|
13167
|
+
const rel = relative3(resolve8(parent), resolve8(child));
|
|
12883
13168
|
return rel === "" || !!rel && !rel.startsWith("..") && !rel.startsWith("/") && !rel.startsWith("\\");
|
|
12884
13169
|
}
|
|
12885
13170
|
function readContextFile(level, filePath, cwd, maxBytes) {
|
|
12886
13171
|
const name = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
12887
13172
|
const shown = displayPath(filePath, cwd);
|
|
12888
13173
|
try {
|
|
12889
|
-
const raw =
|
|
13174
|
+
const raw = readFileSync17(filePath);
|
|
12890
13175
|
const byteCount = raw.byteLength;
|
|
12891
13176
|
if (byteCount === 0) {
|
|
12892
13177
|
return {
|
|
@@ -12933,7 +13218,7 @@ function readContextFile(level, filePath, cwd, maxBytes) {
|
|
|
12933
13218
|
function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
|
|
12934
13219
|
const skipped = [];
|
|
12935
13220
|
for (const candidate of candidates) {
|
|
12936
|
-
const filePath =
|
|
13221
|
+
const filePath = join17(dir, candidate);
|
|
12937
13222
|
if (!existsSync23(filePath)) continue;
|
|
12938
13223
|
const result = readContextFile(level, filePath, cwd, maxBytes);
|
|
12939
13224
|
if (result.skipped) skipped.push(result.skipped);
|
|
@@ -12942,7 +13227,7 @@ function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
|
|
|
12942
13227
|
return { layer: null, skipped };
|
|
12943
13228
|
}
|
|
12944
13229
|
function loadContextFiles(options) {
|
|
12945
|
-
const cwd =
|
|
13230
|
+
const cwd = resolve8(options.cwd);
|
|
12946
13231
|
const maxBytes = options.maxBytes ?? CONTEXT_FILE_MAX_BYTES;
|
|
12947
13232
|
const candidates = options.candidates ?? buildContextFileCandidates(options.fallbackFilenames);
|
|
12948
13233
|
const skipped = [];
|
|
@@ -12950,7 +13235,7 @@ function loadContextFiles(options) {
|
|
|
12950
13235
|
return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
|
|
12951
13236
|
}
|
|
12952
13237
|
if (options.setting && options.setting !== "auto") {
|
|
12953
|
-
const filePath =
|
|
13238
|
+
const filePath = resolve8(cwd, String(options.setting));
|
|
12954
13239
|
if (!isInsideOrEqual(cwd, filePath)) {
|
|
12955
13240
|
skipped.push({
|
|
12956
13241
|
level: "single",
|
|
@@ -12994,7 +13279,7 @@ function loadContextFiles(options) {
|
|
|
12994
13279
|
skipped.push(...result.skipped);
|
|
12995
13280
|
if (result.layer) layers.push(result.layer);
|
|
12996
13281
|
}
|
|
12997
|
-
if (
|
|
13282
|
+
if (resolve8(options.cwd) !== resolve8(options.projectRoot)) {
|
|
12998
13283
|
const result = findFirstContextFile("local", options.cwd, cwd, candidates, maxBytes);
|
|
12999
13284
|
skipped.push(...result.skipped);
|
|
13000
13285
|
if (result.layer) layers.push(result.layer);
|
|
@@ -13012,98 +13297,8 @@ function loadContextFiles(options) {
|
|
|
13012
13297
|
}
|
|
13013
13298
|
|
|
13014
13299
|
// src/web/session-handler.ts
|
|
13015
|
-
import { existsSync as
|
|
13016
|
-
import { join as join20, resolve as
|
|
13017
|
-
|
|
13018
|
-
// src/tools/git-context.ts
|
|
13019
|
-
import { execSync as execSync2 } from "child_process";
|
|
13020
|
-
import { existsSync as existsSync24 } from "fs";
|
|
13021
|
-
import { join as join17 } from "path";
|
|
13022
|
-
function runGit2(cmd, cwd) {
|
|
13023
|
-
try {
|
|
13024
|
-
return execSync2(`git ${cmd}`, {
|
|
13025
|
-
cwd,
|
|
13026
|
-
encoding: "utf-8",
|
|
13027
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
13028
|
-
timeout: 5e3
|
|
13029
|
-
}).trim();
|
|
13030
|
-
} catch {
|
|
13031
|
-
return null;
|
|
13032
|
-
}
|
|
13033
|
-
}
|
|
13034
|
-
function getGitRoot(cwd = process.cwd()) {
|
|
13035
|
-
return runGit2("rev-parse --show-toplevel", cwd);
|
|
13036
|
-
}
|
|
13037
|
-
function getGitContext(cwd = process.cwd()) {
|
|
13038
|
-
if (!existsSync24(join17(cwd, ".git"))) {
|
|
13039
|
-
const result = runGit2("rev-parse --git-dir", cwd);
|
|
13040
|
-
if (!result) return null;
|
|
13041
|
-
}
|
|
13042
|
-
const branch = runGit2("rev-parse --abbrev-ref HEAD", cwd);
|
|
13043
|
-
if (!branch) return null;
|
|
13044
|
-
const statusOutput = runGit2("status --porcelain", cwd) ?? "";
|
|
13045
|
-
const statusLines = statusOutput ? statusOutput.split("\n").filter(Boolean) : [];
|
|
13046
|
-
const stagedFiles = [];
|
|
13047
|
-
const changedFiles = [];
|
|
13048
|
-
for (const line of statusLines) {
|
|
13049
|
-
const xy = line.slice(0, 2);
|
|
13050
|
-
const file = line.slice(3).trim();
|
|
13051
|
-
const indexStatus = xy[0];
|
|
13052
|
-
const workStatus = xy[1];
|
|
13053
|
-
if (indexStatus && indexStatus !== " " && indexStatus !== "?") {
|
|
13054
|
-
stagedFiles.push(`${indexStatus} ${file}`);
|
|
13055
|
-
}
|
|
13056
|
-
if (workStatus && workStatus !== " ") {
|
|
13057
|
-
changedFiles.push(`${workStatus} ${file}`);
|
|
13058
|
-
}
|
|
13059
|
-
}
|
|
13060
|
-
const logOutput = runGit2("log --oneline -3", cwd) ?? "";
|
|
13061
|
-
const recentCommits = logOutput ? logOutput.split("\n").filter(Boolean) : [];
|
|
13062
|
-
const unpushedOutput = runGit2("log @{u}..HEAD --oneline", cwd);
|
|
13063
|
-
const hasUnpushed = unpushedOutput !== null && unpushedOutput.trim().length > 0;
|
|
13064
|
-
return {
|
|
13065
|
-
branch,
|
|
13066
|
-
changedFiles,
|
|
13067
|
-
stagedFiles,
|
|
13068
|
-
recentCommits,
|
|
13069
|
-
hasUnpushed
|
|
13070
|
-
};
|
|
13071
|
-
}
|
|
13072
|
-
function formatGitContextForPrompt(ctx) {
|
|
13073
|
-
const lines = ["# Git Repository Status", ""];
|
|
13074
|
-
lines.push(`- **Branch**: \`${ctx.branch}\``);
|
|
13075
|
-
if (ctx.stagedFiles.length > 0) {
|
|
13076
|
-
lines.push(`- **Staged** (${ctx.stagedFiles.length} files):`);
|
|
13077
|
-
for (const f of ctx.stagedFiles.slice(0, 10)) {
|
|
13078
|
-
lines.push(` - ${f}`);
|
|
13079
|
-
}
|
|
13080
|
-
if (ctx.stagedFiles.length > 10) {
|
|
13081
|
-
lines.push(` - ... and ${ctx.stagedFiles.length - 10} more`);
|
|
13082
|
-
}
|
|
13083
|
-
}
|
|
13084
|
-
if (ctx.changedFiles.length > 0) {
|
|
13085
|
-
lines.push(`- **Modified** (${ctx.changedFiles.length} files):`);
|
|
13086
|
-
for (const f of ctx.changedFiles.slice(0, 10)) {
|
|
13087
|
-
lines.push(` - ${f}`);
|
|
13088
|
-
}
|
|
13089
|
-
if (ctx.changedFiles.length > 10) {
|
|
13090
|
-
lines.push(` - ... and ${ctx.changedFiles.length - 10} more`);
|
|
13091
|
-
}
|
|
13092
|
-
}
|
|
13093
|
-
if (ctx.stagedFiles.length === 0 && ctx.changedFiles.length === 0) {
|
|
13094
|
-
lines.push("- **Working tree**: clean");
|
|
13095
|
-
}
|
|
13096
|
-
if (ctx.recentCommits.length > 0) {
|
|
13097
|
-
lines.push("- **Recent commits**:");
|
|
13098
|
-
for (const c of ctx.recentCommits) {
|
|
13099
|
-
lines.push(` - ${c}`);
|
|
13100
|
-
}
|
|
13101
|
-
}
|
|
13102
|
-
if (ctx.hasUnpushed) {
|
|
13103
|
-
lines.push("- \u26A0\uFE0F Has unpushed commits");
|
|
13104
|
-
}
|
|
13105
|
-
return lines.join("\n");
|
|
13106
|
-
}
|
|
13300
|
+
import { existsSync as existsSync25, readFileSync as readFileSync19, writeFileSync as writeFileSync2, mkdirSync as mkdirSync11, readdirSync as readdirSync11, statSync as statSync8, createWriteStream } from "fs";
|
|
13301
|
+
import { join as join20, resolve as resolve9, dirname as dirname6 } from "path";
|
|
13107
13302
|
|
|
13108
13303
|
// src/cli/review-prompts.ts
|
|
13109
13304
|
function buildReviewPrompt(diff, gitContextStr, detailed) {
|
|
@@ -13164,7 +13359,7 @@ If no security issues found, state "\u2705 No security vulnerabilities detected"
|
|
|
13164
13359
|
}
|
|
13165
13360
|
|
|
13166
13361
|
// src/repl/commands/project-init.ts
|
|
13167
|
-
import { existsSync as
|
|
13362
|
+
import { existsSync as existsSync24, readFileSync as readFileSync18, readdirSync as readdirSync10, statSync as statSync7 } from "fs";
|
|
13168
13363
|
import { join as join18 } from "path";
|
|
13169
13364
|
var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
13170
13365
|
"node_modules",
|
|
@@ -13209,11 +13404,11 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
13209
13404
|
const sorted = filtered.sort((a, b) => {
|
|
13210
13405
|
let aIsDir = false, bIsDir = false;
|
|
13211
13406
|
try {
|
|
13212
|
-
aIsDir =
|
|
13407
|
+
aIsDir = statSync7(join18(d, a)).isDirectory();
|
|
13213
13408
|
} catch {
|
|
13214
13409
|
}
|
|
13215
13410
|
try {
|
|
13216
|
-
bIsDir =
|
|
13411
|
+
bIsDir = statSync7(join18(d, b)).isDirectory();
|
|
13217
13412
|
} catch {
|
|
13218
13413
|
}
|
|
13219
13414
|
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
@@ -13226,7 +13421,7 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
13226
13421
|
const connector = isLast ? "+-- " : "|-- ";
|
|
13227
13422
|
let isDir;
|
|
13228
13423
|
try {
|
|
13229
|
-
isDir =
|
|
13424
|
+
isDir = statSync7(fullPath).isDirectory();
|
|
13230
13425
|
} catch {
|
|
13231
13426
|
continue;
|
|
13232
13427
|
}
|
|
@@ -13248,7 +13443,7 @@ function scanProject(cwd) {
|
|
|
13248
13443
|
configFiles: [],
|
|
13249
13444
|
directoryStructure: ""
|
|
13250
13445
|
};
|
|
13251
|
-
const check = (file) =>
|
|
13446
|
+
const check = (file) => existsSync24(join18(cwd, file));
|
|
13252
13447
|
const configCandidates = [
|
|
13253
13448
|
"package.json",
|
|
13254
13449
|
"tsconfig.json",
|
|
@@ -13275,7 +13470,7 @@ function scanProject(cwd) {
|
|
|
13275
13470
|
info.type = "node";
|
|
13276
13471
|
info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
|
|
13277
13472
|
try {
|
|
13278
|
-
const pkg = JSON.parse(
|
|
13473
|
+
const pkg = JSON.parse(readFileSync18(join18(cwd, "package.json"), "utf-8"));
|
|
13279
13474
|
const scripts = pkg.scripts ?? {};
|
|
13280
13475
|
info.buildCommand = scripts.build ? "npm run build" : void 0;
|
|
13281
13476
|
info.testCommand = scripts.test ? "npm test" : void 0;
|
|
@@ -14169,10 +14364,10 @@ var SessionHandler = class {
|
|
|
14169
14364
|
return;
|
|
14170
14365
|
}
|
|
14171
14366
|
case "ask_user_response": {
|
|
14172
|
-
const
|
|
14173
|
-
if (
|
|
14367
|
+
const resolve11 = this.pendingAskUser.get(msg.requestId);
|
|
14368
|
+
if (resolve11) {
|
|
14174
14369
|
this.pendingAskUser.delete(msg.requestId);
|
|
14175
|
-
|
|
14370
|
+
resolve11(msg.answer);
|
|
14176
14371
|
}
|
|
14177
14372
|
return;
|
|
14178
14373
|
}
|
|
@@ -14182,11 +14377,13 @@ var SessionHandler = class {
|
|
|
14182
14377
|
return this.handleMemoryStatus();
|
|
14183
14378
|
case "memory_rebuild":
|
|
14184
14379
|
return this.handleMemoryRebuild(Boolean(msg.full));
|
|
14380
|
+
case "memory_manage":
|
|
14381
|
+
return this.handleMemoryManage(msg.action, msg.id, msg.expiresAt);
|
|
14185
14382
|
case "auto_pause_response": {
|
|
14186
|
-
const
|
|
14187
|
-
if (
|
|
14383
|
+
const resolve11 = this.pendingAutoPause.get(msg.requestId);
|
|
14384
|
+
if (resolve11) {
|
|
14188
14385
|
this.pendingAutoPause.delete(msg.requestId);
|
|
14189
|
-
|
|
14386
|
+
resolve11({ action: msg.action, message: msg.message });
|
|
14190
14387
|
}
|
|
14191
14388
|
return;
|
|
14192
14389
|
}
|
|
@@ -14201,10 +14398,10 @@ var SessionHandler = class {
|
|
|
14201
14398
|
this.hubOrchestrator?.abort();
|
|
14202
14399
|
return;
|
|
14203
14400
|
case "hub_steer": {
|
|
14204
|
-
const
|
|
14205
|
-
if (
|
|
14401
|
+
const resolve11 = this.pendingHubReview.get(msg.requestId);
|
|
14402
|
+
if (resolve11) {
|
|
14206
14403
|
this.pendingHubReview.delete(msg.requestId);
|
|
14207
|
-
|
|
14404
|
+
resolve11({ action: msg.action, message: msg.message });
|
|
14208
14405
|
}
|
|
14209
14406
|
return;
|
|
14210
14407
|
}
|
|
@@ -14217,9 +14414,9 @@ var SessionHandler = class {
|
|
|
14217
14414
|
runLifecycleHooks(this.config.get("hooks") ?? void 0, "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
|
|
14218
14415
|
this.toolExecutor.cancelAll();
|
|
14219
14416
|
if (this.abortController) this.abortController.abort();
|
|
14220
|
-
for (const
|
|
14417
|
+
for (const resolve11 of this.pendingAskUser.values()) resolve11(null);
|
|
14221
14418
|
this.pendingAskUser.clear();
|
|
14222
|
-
for (const
|
|
14419
|
+
for (const resolve11 of this.pendingAutoPause.values()) resolve11({ action: "stop" });
|
|
14223
14420
|
this.pendingAutoPause.clear();
|
|
14224
14421
|
this.saveIfNeeded();
|
|
14225
14422
|
}
|
|
@@ -14353,9 +14550,9 @@ var SessionHandler = class {
|
|
|
14353
14550
|
this.hubOrchestrator = orchestrator;
|
|
14354
14551
|
orchestrator.onEvent = (event) => this.send({ type: "hub_event", event });
|
|
14355
14552
|
if (config.humanSteer) {
|
|
14356
|
-
orchestrator.onRoundReview = ({ round, maxRounds }) => new Promise((
|
|
14553
|
+
orchestrator.onRoundReview = ({ round, maxRounds }) => new Promise((resolve11) => {
|
|
14357
14554
|
const requestId = `hubrev_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
14358
|
-
this.pendingHubReview.set(requestId,
|
|
14555
|
+
this.pendingHubReview.set(requestId, resolve11);
|
|
14359
14556
|
this.send({ type: "hub_review", requestId, round, maxRounds });
|
|
14360
14557
|
});
|
|
14361
14558
|
}
|
|
@@ -14688,7 +14885,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
|
|
|
14688
14885
|
this.send({ type: "response_done", content, usage });
|
|
14689
14886
|
if (pendingTeeSave && isCleanDocumentBody(content)) {
|
|
14690
14887
|
try {
|
|
14691
|
-
mkdirSync11(
|
|
14888
|
+
mkdirSync11(dirname6(pendingTeeSave), { recursive: true });
|
|
14692
14889
|
const bodyToSave = stripOuterCodeFence(content);
|
|
14693
14890
|
atomicWriteFileSync(pendingTeeSave, bodyToSave);
|
|
14694
14891
|
undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
|
|
@@ -14713,8 +14910,8 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
|
|
|
14713
14910
|
onMcpToolUsed: (name) => this.usedMcpToolNames.add(name),
|
|
14714
14911
|
requestAutoPause: async ({ effectiveRound, maxToolRounds: totalRounds, toolSummary }) => {
|
|
14715
14912
|
const requestId = `pause_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
14716
|
-
const pauseResp = await new Promise((
|
|
14717
|
-
this.pendingAutoPause.set(requestId,
|
|
14913
|
+
const pauseResp = await new Promise((resolve11) => {
|
|
14914
|
+
this.pendingAutoPause.set(requestId, resolve11);
|
|
14718
14915
|
this.send({
|
|
14719
14916
|
type: "auto_pause_request",
|
|
14720
14917
|
requestId,
|
|
@@ -14864,7 +15061,7 @@ ${summaryContent}`,
|
|
|
14864
15061
|
let isError = false;
|
|
14865
15062
|
let summary;
|
|
14866
15063
|
try {
|
|
14867
|
-
mkdirSync11(
|
|
15064
|
+
mkdirSync11(dirname6(saveToFile), { recursive: true });
|
|
14868
15065
|
fileStream = createWriteStream(saveToFile);
|
|
14869
15066
|
const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
|
|
14870
15067
|
const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
|
|
@@ -14893,8 +15090,8 @@ ${summaryContent}`,
|
|
|
14893
15090
|
}
|
|
14894
15091
|
if (chunk.done) break;
|
|
14895
15092
|
}
|
|
14896
|
-
await new Promise((
|
|
14897
|
-
fileStream.end((err) => err ? reject(err) :
|
|
15093
|
+
await new Promise((resolve11, reject) => {
|
|
15094
|
+
fileStream.end((err) => err ? reject(err) : resolve11());
|
|
14898
15095
|
});
|
|
14899
15096
|
const verdict = evaluateTeeContent(fullContent, saveToFile, priorContent);
|
|
14900
15097
|
if (verdict.kind === "reject") {
|
|
@@ -14920,7 +15117,7 @@ ${summaryContent}`,
|
|
|
14920
15117
|
} catch (err) {
|
|
14921
15118
|
if (fileStream) {
|
|
14922
15119
|
try {
|
|
14923
|
-
await new Promise((
|
|
15120
|
+
await new Promise((resolve11) => fileStream.end(() => resolve11()));
|
|
14924
15121
|
} catch {
|
|
14925
15122
|
}
|
|
14926
15123
|
}
|
|
@@ -15552,9 +15749,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
15552
15749
|
let modifiedFiles = 0;
|
|
15553
15750
|
const diffLines2 = [];
|
|
15554
15751
|
for (const [filePath, { earliest }] of fileMap) {
|
|
15555
|
-
const currentContent =
|
|
15752
|
+
const currentContent = existsSync25(filePath) ? (() => {
|
|
15556
15753
|
try {
|
|
15557
|
-
return
|
|
15754
|
+
return readFileSync19(filePath, "utf-8");
|
|
15558
15755
|
} catch {
|
|
15559
15756
|
return null;
|
|
15560
15757
|
}
|
|
@@ -15655,7 +15852,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
15655
15852
|
break;
|
|
15656
15853
|
}
|
|
15657
15854
|
const sub = args[0]?.toLowerCase();
|
|
15658
|
-
const
|
|
15855
|
+
const resolve11 = (ref) => {
|
|
15659
15856
|
const r = session.resolveBranchRef(ref);
|
|
15660
15857
|
if (r.ok) return r.id;
|
|
15661
15858
|
if (r.reason === "ambiguous") {
|
|
@@ -15709,7 +15906,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
15709
15906
|
this.send({ type: "error", message: "Usage: /branch switch <id|title>" });
|
|
15710
15907
|
break;
|
|
15711
15908
|
}
|
|
15712
|
-
const id =
|
|
15909
|
+
const id = resolve11(ref);
|
|
15713
15910
|
if (!id) break;
|
|
15714
15911
|
const ok = session.switchBranch(id);
|
|
15715
15912
|
if (ok) {
|
|
@@ -15729,7 +15926,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
15729
15926
|
this.send({ type: "error", message: "Usage: /branch delete <id|title>" });
|
|
15730
15927
|
break;
|
|
15731
15928
|
}
|
|
15732
|
-
const id =
|
|
15929
|
+
const id = resolve11(ref);
|
|
15733
15930
|
if (!id) break;
|
|
15734
15931
|
const ok = session.deleteBranch(id);
|
|
15735
15932
|
if (ok) {
|
|
@@ -15748,7 +15945,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
15748
15945
|
this.send({ type: "error", message: "Usage: /branch rename <id|title> <new title>" });
|
|
15749
15946
|
break;
|
|
15750
15947
|
}
|
|
15751
|
-
const id =
|
|
15948
|
+
const id = resolve11(ref);
|
|
15752
15949
|
if (!id) break;
|
|
15753
15950
|
const ok = session.renameBranch(id, title);
|
|
15754
15951
|
if (ok) {
|
|
@@ -15766,7 +15963,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
15766
15963
|
this.send({ type: "error", message: "Usage: /branch diff <id|title>" });
|
|
15767
15964
|
break;
|
|
15768
15965
|
}
|
|
15769
|
-
const id =
|
|
15966
|
+
const id = resolve11(ref);
|
|
15770
15967
|
if (!id) break;
|
|
15771
15968
|
const d = session.diffBranches(id);
|
|
15772
15969
|
if (!d) {
|
|
@@ -15808,7 +16005,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
15808
16005
|
this.send({ type: "error", message: "Usage: /branch cherry-pick <source-id|title> <msg-index>" });
|
|
15809
16006
|
break;
|
|
15810
16007
|
}
|
|
15811
|
-
const id =
|
|
16008
|
+
const id = resolve11(ref);
|
|
15812
16009
|
if (!id) break;
|
|
15813
16010
|
const idx = parseInt(idxStr, 10);
|
|
15814
16011
|
if (Number.isNaN(idx)) {
|
|
@@ -16070,7 +16267,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16070
16267
|
case "test": {
|
|
16071
16268
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
16072
16269
|
try {
|
|
16073
|
-
const { executeTests } = await import("./run-tests-
|
|
16270
|
+
const { executeTests } = await import("./run-tests-K7QR5QN4.js");
|
|
16074
16271
|
const argStr = args.join(" ").trim();
|
|
16075
16272
|
let testArgs = {};
|
|
16076
16273
|
if (argStr) {
|
|
@@ -16089,7 +16286,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
16089
16286
|
const cwd = process.cwd();
|
|
16090
16287
|
const targetPath = join20(cwd, "AICLI.md");
|
|
16091
16288
|
const force = args.includes("--force");
|
|
16092
|
-
if (
|
|
16289
|
+
if (existsSync25(targetPath) && !force) {
|
|
16093
16290
|
this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
|
|
16094
16291
|
Use /init --force to overwrite.` });
|
|
16095
16292
|
break;
|
|
@@ -16122,11 +16319,11 @@ Use /context reload to load it.` });
|
|
|
16122
16319
|
lines.push("**Config Files:**");
|
|
16123
16320
|
lines.push(` Dir: ${configDir}`);
|
|
16124
16321
|
const checkFile = (label, filePath) => {
|
|
16125
|
-
const exists =
|
|
16322
|
+
const exists = existsSync25(filePath);
|
|
16126
16323
|
let extra = "";
|
|
16127
16324
|
if (exists) {
|
|
16128
16325
|
try {
|
|
16129
|
-
extra = ` (${
|
|
16326
|
+
extra = ` (${statSync8(filePath).size} bytes)`;
|
|
16130
16327
|
} catch {
|
|
16131
16328
|
}
|
|
16132
16329
|
}
|
|
@@ -16321,12 +16518,12 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
|
|
|
16321
16518
|
this.send({ type: "info", message: "\u2713 All added directories removed from context." });
|
|
16322
16519
|
break;
|
|
16323
16520
|
}
|
|
16324
|
-
const dirPath =
|
|
16325
|
-
if (!
|
|
16521
|
+
const dirPath = resolve9(sub);
|
|
16522
|
+
if (!existsSync25(dirPath)) {
|
|
16326
16523
|
this.send({ type: "error", message: `Directory not found: ${dirPath}` });
|
|
16327
16524
|
break;
|
|
16328
16525
|
}
|
|
16329
|
-
if (!
|
|
16526
|
+
if (!statSync8(dirPath).isDirectory()) {
|
|
16330
16527
|
this.send({ type: "error", message: `Not a directory: ${dirPath}` });
|
|
16331
16528
|
break;
|
|
16332
16529
|
}
|
|
@@ -16339,7 +16536,7 @@ It will be included in AI context for subsequent messages.` });
|
|
|
16339
16536
|
case "commands": {
|
|
16340
16537
|
const configDir = this.config.getConfigDir();
|
|
16341
16538
|
const commandsDir = join20(configDir, CUSTOM_COMMANDS_DIR_NAME);
|
|
16342
|
-
if (!
|
|
16539
|
+
if (!existsSync25(commandsDir)) {
|
|
16343
16540
|
this.send({ type: "info", message: `No custom commands directory.
|
|
16344
16541
|
Create: ${commandsDir}/ with .md files.` });
|
|
16345
16542
|
break;
|
|
@@ -16562,48 +16759,95 @@ Add .md files to create commands.` });
|
|
|
16562
16759
|
}
|
|
16563
16760
|
memoryShow() {
|
|
16564
16761
|
const configDir = this.config.getConfigDir();
|
|
16565
|
-
const
|
|
16566
|
-
|
|
16567
|
-
|
|
16568
|
-
|
|
16569
|
-
|
|
16570
|
-
|
|
16571
|
-
|
|
16572
|
-
|
|
16573
|
-
`);
|
|
16574
|
-
}
|
|
16762
|
+
const entries = listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
|
|
16763
|
+
const content = entries.length === 0 ? "(empty \u2014 no persistent memory entries yet)" : entries.map((entry) => {
|
|
16764
|
+
const status = isMemoryExpired(entry) ? "expired" : entry.approved ? "approved" : "pending";
|
|
16765
|
+
const source = entry.sourceSession ? `session ${entry.sourceSession.slice(0, 8)}` : entry.sourceProject ? `project ${entry.sourceProject}` : "manual/tool";
|
|
16766
|
+
return `## ${entry.id.slice(0, 8)} \xB7 ${status} \xB7 scope:${entry.scope} \xB7 sensitivity:${entry.sensitivity}
|
|
16767
|
+
Source: ${source}
|
|
16768
|
+
${entry.content}`;
|
|
16769
|
+
}).join("\n\n");
|
|
16575
16770
|
this.send({
|
|
16576
16771
|
type: "memory_content",
|
|
16577
|
-
content
|
|
16578
|
-
filePath:
|
|
16772
|
+
content,
|
|
16773
|
+
filePath: memoryStorePath(configDir)
|
|
16579
16774
|
});
|
|
16775
|
+
this.sendMemoryEntries();
|
|
16580
16776
|
}
|
|
16581
16777
|
memoryAdd(text) {
|
|
16582
|
-
const configDir = this.config.getConfigDir();
|
|
16583
|
-
const memPath = join20(configDir, MEMORY_FILE_NAME);
|
|
16584
16778
|
try {
|
|
16585
|
-
|
|
16586
|
-
|
|
16587
|
-
|
|
16588
|
-
|
|
16589
|
-
|
|
16590
|
-
|
|
16591
|
-
|
|
16592
|
-
this.
|
|
16779
|
+
const entry = addMemoryEntry(this.config.getConfigDir(), text, {
|
|
16780
|
+
scope: "personal",
|
|
16781
|
+
cwd: process.cwd(),
|
|
16782
|
+
redact: true
|
|
16783
|
+
});
|
|
16784
|
+
const status = entry.approved ? "approved" : "pending approval";
|
|
16785
|
+
this.send({ type: "info", message: `\u{1F4DD} Memory entry ${entry.id.slice(0, 8)} added (${status}).` });
|
|
16786
|
+
this.sendMemoryEntries();
|
|
16593
16787
|
} catch (err) {
|
|
16594
16788
|
this.send({ type: "error", message: `Failed to write memory: ${err.message}` });
|
|
16595
16789
|
}
|
|
16596
16790
|
}
|
|
16597
16791
|
memoryClear() {
|
|
16598
16792
|
const configDir = this.config.getConfigDir();
|
|
16599
|
-
const memPath = join20(configDir, MEMORY_FILE_NAME);
|
|
16600
16793
|
try {
|
|
16601
|
-
atomicWriteFileSync(
|
|
16794
|
+
atomicWriteFileSync(memoryStorePath(configDir), "");
|
|
16795
|
+
atomicWriteFileSync(join20(configDir, MEMORY_FILE_NAME), "");
|
|
16602
16796
|
this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
|
|
16797
|
+
this.sendMemoryEntries();
|
|
16603
16798
|
} catch (err) {
|
|
16604
16799
|
this.send({ type: "error", message: `Failed to clear memory: ${err.message}` });
|
|
16605
16800
|
}
|
|
16606
16801
|
}
|
|
16802
|
+
sendMemoryEntries() {
|
|
16803
|
+
const configDir = this.config.getConfigDir();
|
|
16804
|
+
const entries = listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
|
|
16805
|
+
this.send({
|
|
16806
|
+
type: "memory_entries",
|
|
16807
|
+
storePath: memoryStorePath(configDir),
|
|
16808
|
+
entries: entries.map((entry) => ({
|
|
16809
|
+
id: entry.id,
|
|
16810
|
+
content: entry.content,
|
|
16811
|
+
scope: entry.scope,
|
|
16812
|
+
sourceSession: entry.sourceSession,
|
|
16813
|
+
sourceProject: entry.sourceProject,
|
|
16814
|
+
createdAt: entry.createdAt,
|
|
16815
|
+
updatedAt: entry.updatedAt,
|
|
16816
|
+
expiresAt: entry.expiresAt,
|
|
16817
|
+
sensitivity: entry.sensitivity,
|
|
16818
|
+
approved: entry.approved,
|
|
16819
|
+
expired: isMemoryExpired(entry),
|
|
16820
|
+
redactedKinds: entry.redactedKinds
|
|
16821
|
+
}))
|
|
16822
|
+
});
|
|
16823
|
+
}
|
|
16824
|
+
handleMemoryManage(action, id, expiresAt) {
|
|
16825
|
+
const configDir = this.config.getConfigDir();
|
|
16826
|
+
try {
|
|
16827
|
+
if (action === "approve" || action === "reject") {
|
|
16828
|
+
if (!id) throw new Error(`${action} requires id`);
|
|
16829
|
+
updateMemoryApproval(configDir, id, action === "approve");
|
|
16830
|
+
} else if (action === "delete") {
|
|
16831
|
+
if (!id) throw new Error("delete requires id");
|
|
16832
|
+
deleteMemoryEntry(configDir, id);
|
|
16833
|
+
} else if (action === "expire") {
|
|
16834
|
+
if (!id) throw new Error("expire requires id");
|
|
16835
|
+
expireMemoryEntry(configDir, id, expiresAt);
|
|
16836
|
+
} else if (action === "export") {
|
|
16837
|
+
this.send({
|
|
16838
|
+
type: "export_data",
|
|
16839
|
+
format: "json",
|
|
16840
|
+
filename: `aicli-memory-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`,
|
|
16841
|
+
content: exportMemoryEntries(configDir, process.cwd())
|
|
16842
|
+
});
|
|
16843
|
+
} else if (action !== "list") {
|
|
16844
|
+
throw new Error(`unknown memory action: ${action}`);
|
|
16845
|
+
}
|
|
16846
|
+
this.sendMemoryEntries();
|
|
16847
|
+
} catch (err) {
|
|
16848
|
+
this.send({ type: "error", message: `Memory management failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
16849
|
+
}
|
|
16850
|
+
}
|
|
16607
16851
|
// ── B4 chat memory recall (v0.4.90+) ──────────────────────────────
|
|
16608
16852
|
// Lazy-imported so the 117 MB embedder stays out of the load path for
|
|
16609
16853
|
// clients that never open the Memory panel.
|
|
@@ -16614,7 +16858,7 @@ Add .md files to create commands.` });
|
|
|
16614
16858
|
return;
|
|
16615
16859
|
}
|
|
16616
16860
|
try {
|
|
16617
|
-
const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-
|
|
16861
|
+
const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-PS274XM7.js");
|
|
16618
16862
|
const loaded = loadChatIndex2();
|
|
16619
16863
|
if (!loaded || loaded.idx.chunks.length === 0) {
|
|
16620
16864
|
this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
|
|
@@ -16650,7 +16894,7 @@ Add .md files to create commands.` });
|
|
|
16650
16894
|
}
|
|
16651
16895
|
async handleMemoryStatus() {
|
|
16652
16896
|
try {
|
|
16653
|
-
const { getChatIndexStatus } = await import("./chat-index-
|
|
16897
|
+
const { getChatIndexStatus } = await import("./chat-index-PS274XM7.js");
|
|
16654
16898
|
const s = getChatIndexStatus();
|
|
16655
16899
|
this.send({
|
|
16656
16900
|
type: "memory_status",
|
|
@@ -16675,7 +16919,7 @@ Add .md files to create commands.` });
|
|
|
16675
16919
|
type: "info",
|
|
16676
16920
|
message: full ? "\u{1F9E0} Rebuilding chat memory index (this may take a while on first run \u2014 ~117 MB embedder)." : "\u{1F9E0} Refreshing chat memory index (incremental)\u2026"
|
|
16677
16921
|
});
|
|
16678
|
-
const { buildChatIndex } = await import("./chat-index-
|
|
16922
|
+
const { buildChatIndex } = await import("./chat-index-PS274XM7.js");
|
|
16679
16923
|
const stats = await buildChatIndex({
|
|
16680
16924
|
full,
|
|
16681
16925
|
onProgress: (p) => {
|
|
@@ -16830,7 +17074,7 @@ async function setupProxy(configProxy) {
|
|
|
16830
17074
|
}
|
|
16831
17075
|
|
|
16832
17076
|
// src/web/auth.ts
|
|
16833
|
-
import { existsSync as
|
|
17077
|
+
import { existsSync as existsSync26, readFileSync as readFileSync20, writeFileSync as writeFileSync3, mkdirSync as mkdirSync12, readdirSync as readdirSync12, copyFileSync } from "fs";
|
|
16834
17078
|
import { join as join21 } from "path";
|
|
16835
17079
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
16836
17080
|
var USERS_FILE = "users.json";
|
|
@@ -17029,23 +17273,23 @@ var AuthManager = class {
|
|
|
17029
17273
|
if (err) return err;
|
|
17030
17274
|
const userDir = this.getUserDataDir(username);
|
|
17031
17275
|
const globalConfig = join21(this.baseDir, "config.json");
|
|
17032
|
-
if (
|
|
17276
|
+
if (existsSync26(globalConfig)) {
|
|
17033
17277
|
try {
|
|
17034
|
-
const content =
|
|
17278
|
+
const content = readFileSync20(globalConfig, "utf-8");
|
|
17035
17279
|
writeFileSync3(join21(userDir, "config.json"), content, "utf-8");
|
|
17036
17280
|
} catch {
|
|
17037
17281
|
}
|
|
17038
17282
|
}
|
|
17039
17283
|
const globalMemory = join21(this.baseDir, "memory.md");
|
|
17040
|
-
if (
|
|
17284
|
+
if (existsSync26(globalMemory)) {
|
|
17041
17285
|
try {
|
|
17042
|
-
const content =
|
|
17286
|
+
const content = readFileSync20(globalMemory, "utf-8");
|
|
17043
17287
|
writeFileSync3(join21(userDir, "memory.md"), content, "utf-8");
|
|
17044
17288
|
} catch {
|
|
17045
17289
|
}
|
|
17046
17290
|
}
|
|
17047
17291
|
const globalHistory = join21(this.baseDir, "history");
|
|
17048
|
-
if (
|
|
17292
|
+
if (existsSync26(globalHistory)) {
|
|
17049
17293
|
try {
|
|
17050
17294
|
const files = readdirSync12(globalHistory).filter((f) => f.endsWith(".json"));
|
|
17051
17295
|
const userHistory = join21(userDir, "history");
|
|
@@ -17062,9 +17306,9 @@ var AuthManager = class {
|
|
|
17062
17306
|
}
|
|
17063
17307
|
// ── Private methods ────────────────────────────────────────────
|
|
17064
17308
|
loadOrCreate() {
|
|
17065
|
-
if (
|
|
17309
|
+
if (existsSync26(this.usersFile)) {
|
|
17066
17310
|
try {
|
|
17067
|
-
return JSON.parse(
|
|
17311
|
+
return JSON.parse(readFileSync20(this.usersFile, "utf-8"));
|
|
17068
17312
|
} catch {
|
|
17069
17313
|
}
|
|
17070
17314
|
}
|
|
@@ -17112,11 +17356,11 @@ function getModuleDir() {
|
|
|
17112
17356
|
if (typeof import.meta?.url === "string") {
|
|
17113
17357
|
const url = new URL(import.meta.url);
|
|
17114
17358
|
const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
|
|
17115
|
-
return
|
|
17359
|
+
return dirname7(filePath);
|
|
17116
17360
|
}
|
|
17117
17361
|
} catch {
|
|
17118
17362
|
}
|
|
17119
|
-
return
|
|
17363
|
+
return resolve10(".");
|
|
17120
17364
|
}
|
|
17121
17365
|
async function startWebServer(options = {}) {
|
|
17122
17366
|
const port = options.port ?? 3e3;
|
|
@@ -17182,7 +17426,7 @@ async function startWebServer(options = {}) {
|
|
|
17182
17426
|
}
|
|
17183
17427
|
let skillManager = null;
|
|
17184
17428
|
const skillsDir = join22(config.getConfigDir(), SKILLS_DIR_NAME);
|
|
17185
|
-
if (
|
|
17429
|
+
if (existsSync27(skillsDir)) {
|
|
17186
17430
|
skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
|
|
17187
17431
|
skillManager.loadSkills();
|
|
17188
17432
|
const count = skillManager.listSkills().length;
|
|
@@ -17254,13 +17498,13 @@ async function startWebServer(options = {}) {
|
|
|
17254
17498
|
};
|
|
17255
17499
|
const moduleDir = getModuleDir();
|
|
17256
17500
|
let clientDir = join22(moduleDir, "web", "client");
|
|
17257
|
-
if (!
|
|
17501
|
+
if (!existsSync27(clientDir)) {
|
|
17258
17502
|
clientDir = join22(moduleDir, "client");
|
|
17259
17503
|
}
|
|
17260
|
-
if (!
|
|
17504
|
+
if (!existsSync27(clientDir)) {
|
|
17261
17505
|
clientDir = join22(moduleDir, "..", "..", "src", "web", "client");
|
|
17262
17506
|
}
|
|
17263
|
-
if (!
|
|
17507
|
+
if (!existsSync27(clientDir)) {
|
|
17264
17508
|
clientDir = join22(process.cwd(), "src", "web", "client");
|
|
17265
17509
|
}
|
|
17266
17510
|
console.log(` Static files: ${clientDir}`);
|
|
@@ -17333,8 +17577,8 @@ async function startWebServer(options = {}) {
|
|
|
17333
17577
|
const prefix = req.query.prefix || "";
|
|
17334
17578
|
const targetDir = join22(cwd, prefix);
|
|
17335
17579
|
try {
|
|
17336
|
-
const canonicalTarget = realpathSync(
|
|
17337
|
-
const canonicalCwd = realpathSync(
|
|
17580
|
+
const canonicalTarget = realpathSync(resolve10(targetDir));
|
|
17581
|
+
const canonicalCwd = realpathSync(resolve10(cwd));
|
|
17338
17582
|
if (!canonicalTarget.startsWith(canonicalCwd + sep3) && canonicalTarget !== canonicalCwd) {
|
|
17339
17583
|
res.json({ files: [] });
|
|
17340
17584
|
return;
|
|
@@ -17385,7 +17629,7 @@ async function startWebServer(options = {}) {
|
|
|
17385
17629
|
const authUser = req._authUser;
|
|
17386
17630
|
const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
|
|
17387
17631
|
const filePath = join22(histDir, `${id}.json`);
|
|
17388
|
-
if (!
|
|
17632
|
+
if (!existsSync27(filePath)) {
|
|
17389
17633
|
res.status(404).json({ error: "Session not found" });
|
|
17390
17634
|
return;
|
|
17391
17635
|
}
|
|
@@ -17400,7 +17644,7 @@ async function startWebServer(options = {}) {
|
|
|
17400
17644
|
res.status(404).json({ error: "Session not found" });
|
|
17401
17645
|
return;
|
|
17402
17646
|
}
|
|
17403
|
-
const data = JSON.parse(
|
|
17647
|
+
const data = JSON.parse(readFileSync21(filePath, "utf-8"));
|
|
17404
17648
|
res.json({ session: data });
|
|
17405
17649
|
} catch (err) {
|
|
17406
17650
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
@@ -17413,10 +17657,10 @@ async function startWebServer(options = {}) {
|
|
|
17413
17657
|
return;
|
|
17414
17658
|
}
|
|
17415
17659
|
const cwd = process.cwd();
|
|
17416
|
-
const fullPath =
|
|
17660
|
+
const fullPath = resolve10(join22(cwd, filePath));
|
|
17417
17661
|
try {
|
|
17418
17662
|
const canonicalFull = realpathSync(fullPath);
|
|
17419
|
-
const canonicalCwd = realpathSync(
|
|
17663
|
+
const canonicalCwd = realpathSync(resolve10(cwd));
|
|
17420
17664
|
if (!canonicalFull.startsWith(canonicalCwd + sep3) && canonicalFull !== canonicalCwd) {
|
|
17421
17665
|
res.json({ error: "Access denied" });
|
|
17422
17666
|
return;
|
|
@@ -17426,12 +17670,12 @@ async function startWebServer(options = {}) {
|
|
|
17426
17670
|
return;
|
|
17427
17671
|
}
|
|
17428
17672
|
try {
|
|
17429
|
-
const stat =
|
|
17673
|
+
const stat = statSync9(fullPath);
|
|
17430
17674
|
if (stat.size > 512 * 1024) {
|
|
17431
17675
|
res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
|
|
17432
17676
|
return;
|
|
17433
17677
|
}
|
|
17434
|
-
const content =
|
|
17678
|
+
const content = readFileSync21(fullPath, "utf-8");
|
|
17435
17679
|
res.json({ content, size: stat.size });
|
|
17436
17680
|
} catch {
|
|
17437
17681
|
res.json({ error: "Cannot read file" });
|
|
@@ -17635,7 +17879,7 @@ async function startWebServer(options = {}) {
|
|
|
17635
17879
|
});
|
|
17636
17880
|
const MAX_PORT_ATTEMPTS = 10;
|
|
17637
17881
|
let actualPort = port;
|
|
17638
|
-
const result = await new Promise((
|
|
17882
|
+
const result = await new Promise((resolve11, reject) => {
|
|
17639
17883
|
const tryListen = (attempt) => {
|
|
17640
17884
|
server.once("error", (err) => {
|
|
17641
17885
|
if (err.code === "EADDRINUSE" && attempt < MAX_PORT_ATTEMPTS) {
|
|
@@ -17666,7 +17910,7 @@ async function startWebServer(options = {}) {
|
|
|
17666
17910
|
}
|
|
17667
17911
|
console.log(` Press Ctrl+C to stop
|
|
17668
17912
|
`);
|
|
17669
|
-
|
|
17913
|
+
resolve11({ port: actualPort, host, url });
|
|
17670
17914
|
});
|
|
17671
17915
|
};
|
|
17672
17916
|
tryListen(1);
|
|
@@ -17687,16 +17931,16 @@ function resolveProjectMcpPath() {
|
|
|
17687
17931
|
const gitRoot = getGitRoot(cwd);
|
|
17688
17932
|
const projectRoot = gitRoot ?? cwd;
|
|
17689
17933
|
const configPath = join22(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
17690
|
-
return
|
|
17934
|
+
return existsSync27(configPath) ? configPath : null;
|
|
17691
17935
|
}
|
|
17692
17936
|
function loadProjectMcpConfig() {
|
|
17693
17937
|
const cwd = process.cwd();
|
|
17694
17938
|
const gitRoot = getGitRoot(cwd);
|
|
17695
17939
|
const projectRoot = gitRoot ?? cwd;
|
|
17696
17940
|
const configPath = join22(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
17697
|
-
if (!
|
|
17941
|
+
if (!existsSync27(configPath)) return null;
|
|
17698
17942
|
try {
|
|
17699
|
-
const raw = JSON.parse(
|
|
17943
|
+
const raw = JSON.parse(readFileSync21(configPath, "utf-8"));
|
|
17700
17944
|
return raw.mcpServers ?? raw;
|
|
17701
17945
|
} catch {
|
|
17702
17946
|
return null;
|