jinzd-ai-cli 0.4.213 → 0.4.214
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/README.zh-CN.md +5 -2
- package/dist/{batch-PKY63TME.js → batch-NPUUDETK.js} +2 -2
- package/dist/{chunk-FGUKBIYO.js → chunk-5FG7PSVM.js} +2 -2
- package/dist/{chunk-IWLVH32D.js → chunk-DBYMX2ZC.js} +1 -1
- package/dist/{chunk-J35V3IAH.js → chunk-JOJA35RK.js} +1 -1
- package/dist/{chunk-G3PW3AOB.js → chunk-JQC7IATA.js} +1 -1
- package/dist/{chunk-THNECAAY.js → chunk-R5CJD2AB.js} +380 -74
- package/dist/{chunk-UDMU4FUF.js → chunk-TR6MSPSS.js} +1 -1
- package/dist/{chunk-MRGWPCFQ.js → chunk-YFHF3PVB.js} +1 -1
- package/dist/{chunk-7NTFMLJJ.js → chunk-YMFGGCUP.js} +1 -1
- package/dist/{ci-HBZ6CIW5.js → ci-3TU7WCY4.js} +2 -2
- package/dist/{constants-CAUBTSFW.js → constants-BKR7T3QH.js} +1 -1
- package/dist/{doctor-cli-5Z5UWYW6.js → doctor-cli-TGVSWKDB.js} +4 -4
- package/dist/electron-server.js +558 -184
- package/dist/{hub-ZRADBR76.js → hub-GURYEW6F.js} +1 -1
- package/dist/index.js +17 -16
- package/dist/{run-tests-DSOJBJWA.js → run-tests-F44JA2YW.js} +2 -2
- package/dist/{run-tests-7J3PNWVS.js → run-tests-R6FL6CQ3.js} +1 -1
- package/dist/{server-U4I7RI42.js → server-FXWCSCDZ.js} +88 -8
- package/dist/{server-PWBWHVYB.js → server-GQXC46R5.js} +4 -4
- package/dist/{task-orchestrator-RUXWRMQV.js → task-orchestrator-6AZD5WSR.js} +4 -4
- package/dist/{usage-2P6TIBTN.js → usage-W7J2QA3G.js} +2 -2
- package/dist/web/client/app.js +4 -1
- package/package.json +1 -1
package/dist/electron-server.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
VERSION,
|
|
38
38
|
buildUserIdentityPrompt,
|
|
39
39
|
runTestsTool
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-JOJA35RK.js";
|
|
41
41
|
import {
|
|
42
42
|
hasSemanticIndex,
|
|
43
43
|
semanticSearch
|
|
@@ -61,8 +61,8 @@ import {
|
|
|
61
61
|
import express from "express";
|
|
62
62
|
import { createServer } from "http";
|
|
63
63
|
import { WebSocketServer } from "ws";
|
|
64
|
-
import { join as
|
|
65
|
-
import { existsSync as
|
|
64
|
+
import { join as join22, dirname as dirname6, resolve as resolve9, relative as relative4, sep as sep3 } from "path";
|
|
65
|
+
import { existsSync as existsSync28, readFileSync as readFileSync22, readdirSync as readdirSync13, statSync as statSync10, realpathSync } from "fs";
|
|
66
66
|
import { networkInterfaces } from "os";
|
|
67
67
|
|
|
68
68
|
// src/config/config-manager.ts
|
|
@@ -9663,6 +9663,218 @@ function formatResults2(query, data, _requested) {
|
|
|
9663
9663
|
return header + "\n" + results.join("\n\n");
|
|
9664
9664
|
}
|
|
9665
9665
|
|
|
9666
|
+
// src/agents/agent-config.ts
|
|
9667
|
+
import { existsSync as existsSync15, readdirSync as readdirSync7, readFileSync as readFileSync12 } from "fs";
|
|
9668
|
+
import { join as join10 } from "path";
|
|
9669
|
+
import { homedir as homedir6 } from "os";
|
|
9670
|
+
var READ_ONLY_TOOLS2 = /* @__PURE__ */ new Set([
|
|
9671
|
+
"read_file",
|
|
9672
|
+
"list_dir",
|
|
9673
|
+
"grep_files",
|
|
9674
|
+
"glob_files",
|
|
9675
|
+
"web_fetch",
|
|
9676
|
+
"google_search",
|
|
9677
|
+
"write_todos",
|
|
9678
|
+
"find_symbol",
|
|
9679
|
+
"get_outline",
|
|
9680
|
+
"find_references",
|
|
9681
|
+
"search_code"
|
|
9682
|
+
]);
|
|
9683
|
+
var BUILTIN_AGENTS = [
|
|
9684
|
+
{
|
|
9685
|
+
name: "explorer",
|
|
9686
|
+
description: "Read-only code exploration and evidence gathering.",
|
|
9687
|
+
permissionProfile: "read-only",
|
|
9688
|
+
allowedTools: [...READ_ONLY_TOOLS2],
|
|
9689
|
+
maxToolRounds: 10,
|
|
9690
|
+
contextPolicy: "task-only",
|
|
9691
|
+
system: "Act as a read-only explorer. Inspect code, collect evidence, and do not modify files.",
|
|
9692
|
+
source: "builtin"
|
|
9693
|
+
},
|
|
9694
|
+
{
|
|
9695
|
+
name: "worker",
|
|
9696
|
+
description: "Implementation and focused fixes within sub-agent safety limits.",
|
|
9697
|
+
permissionProfile: "workspace-write",
|
|
9698
|
+
maxToolRounds: 15,
|
|
9699
|
+
contextPolicy: "task-only",
|
|
9700
|
+
system: "Act as a focused implementation worker. Keep edits scoped and report changed files.",
|
|
9701
|
+
source: "builtin"
|
|
9702
|
+
},
|
|
9703
|
+
{
|
|
9704
|
+
name: "reviewer",
|
|
9705
|
+
description: "Code review focused on bugs, regressions, and missing tests.",
|
|
9706
|
+
permissionProfile: "read-only",
|
|
9707
|
+
allowedTools: [...READ_ONLY_TOOLS2],
|
|
9708
|
+
maxToolRounds: 12,
|
|
9709
|
+
contextPolicy: "task-only",
|
|
9710
|
+
system: "Act as a code reviewer. Lead with concrete findings, file evidence, and residual risks.",
|
|
9711
|
+
source: "builtin"
|
|
9712
|
+
},
|
|
9713
|
+
{
|
|
9714
|
+
name: "security",
|
|
9715
|
+
description: "Security audit with read-only evidence collection.",
|
|
9716
|
+
permissionProfile: "read-only",
|
|
9717
|
+
allowedTools: [...READ_ONLY_TOOLS2],
|
|
9718
|
+
maxToolRounds: 12,
|
|
9719
|
+
contextPolicy: "task-only",
|
|
9720
|
+
system: "Act as a security reviewer. Look for trust-boundary, injection, filesystem, network, and secret-handling issues.",
|
|
9721
|
+
source: "builtin"
|
|
9722
|
+
},
|
|
9723
|
+
{
|
|
9724
|
+
name: "tester",
|
|
9725
|
+
description: "Test planning and regression analysis without direct shell execution.",
|
|
9726
|
+
permissionProfile: "read-only",
|
|
9727
|
+
allowedTools: [...READ_ONLY_TOOLS2],
|
|
9728
|
+
maxToolRounds: 12,
|
|
9729
|
+
contextPolicy: "task-only",
|
|
9730
|
+
system: "Act as a tester. Inspect tests, identify coverage gaps, and propose precise regression checks.",
|
|
9731
|
+
source: "builtin"
|
|
9732
|
+
}
|
|
9733
|
+
];
|
|
9734
|
+
function normalizeName(name) {
|
|
9735
|
+
return name.trim().toLowerCase();
|
|
9736
|
+
}
|
|
9737
|
+
function isStringArray(value) {
|
|
9738
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
9739
|
+
}
|
|
9740
|
+
function parseAgentConfig(raw, source, path3) {
|
|
9741
|
+
if (!raw || typeof raw !== "object") return null;
|
|
9742
|
+
const obj = raw;
|
|
9743
|
+
if (typeof obj.name !== "string" || obj.name.trim() === "") return null;
|
|
9744
|
+
const cfg = {
|
|
9745
|
+
name: obj.name.trim(),
|
|
9746
|
+
source,
|
|
9747
|
+
path: path3
|
|
9748
|
+
};
|
|
9749
|
+
if (typeof obj.description === "string") cfg.description = obj.description;
|
|
9750
|
+
if (typeof obj.provider === "string") cfg.provider = obj.provider;
|
|
9751
|
+
if (typeof obj.model === "string") cfg.model = obj.model;
|
|
9752
|
+
if (typeof obj.system === "string") cfg.system = obj.system;
|
|
9753
|
+
if (typeof obj.instructions === "string") cfg.instructions = obj.instructions;
|
|
9754
|
+
if (typeof obj.systemInstructions === "string") cfg.systemInstructions = obj.systemInstructions;
|
|
9755
|
+
if (isStringArray(obj.allowedTools)) cfg.allowedTools = obj.allowedTools;
|
|
9756
|
+
if (isStringArray(obj.blockedTools)) cfg.blockedTools = obj.blockedTools;
|
|
9757
|
+
if (typeof obj.permissionProfile === "string") cfg.permissionProfile = obj.permissionProfile;
|
|
9758
|
+
if (typeof obj.maxToolRounds === "number" && Number.isFinite(obj.maxToolRounds)) cfg.maxToolRounds = obj.maxToolRounds;
|
|
9759
|
+
if (typeof obj.contextPolicy === "string") cfg.contextPolicy = obj.contextPolicy;
|
|
9760
|
+
return cfg;
|
|
9761
|
+
}
|
|
9762
|
+
function loadAgentDir(dir, source) {
|
|
9763
|
+
if (!existsSync15(dir)) return [];
|
|
9764
|
+
const out = [];
|
|
9765
|
+
for (const file of readdirSync7(dir)) {
|
|
9766
|
+
if (!file.endsWith(".json")) continue;
|
|
9767
|
+
const path3 = join10(dir, file);
|
|
9768
|
+
try {
|
|
9769
|
+
const parsed = JSON.parse(readFileSync12(path3, "utf-8"));
|
|
9770
|
+
const cfg = parseAgentConfig(parsed, source, path3);
|
|
9771
|
+
if (cfg) out.push(cfg);
|
|
9772
|
+
} catch {
|
|
9773
|
+
}
|
|
9774
|
+
}
|
|
9775
|
+
return out;
|
|
9776
|
+
}
|
|
9777
|
+
function getAgentDirs(configDir, cwd = process.cwd()) {
|
|
9778
|
+
return {
|
|
9779
|
+
user: join10(configDir ?? join10(homedir6(), CONFIG_DIR_NAME), "agents"),
|
|
9780
|
+
project: join10(cwd, CONFIG_DIR_NAME, "agents")
|
|
9781
|
+
};
|
|
9782
|
+
}
|
|
9783
|
+
function listAgentConfigs(configDir, cwd = process.cwd()) {
|
|
9784
|
+
const dirs = getAgentDirs(configDir, cwd);
|
|
9785
|
+
const byName = /* @__PURE__ */ new Map();
|
|
9786
|
+
for (const cfg of BUILTIN_AGENTS) byName.set(normalizeName(cfg.name), { ...cfg });
|
|
9787
|
+
for (const cfg of loadAgentDir(dirs.user, "user")) byName.set(normalizeName(cfg.name), cfg);
|
|
9788
|
+
for (const cfg of loadAgentDir(dirs.project, "project")) byName.set(normalizeName(cfg.name), cfg);
|
|
9789
|
+
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
9790
|
+
}
|
|
9791
|
+
function resolveAgentConfig(name, configDir, cwd = process.cwd()) {
|
|
9792
|
+
const target = normalizeName(name);
|
|
9793
|
+
return listAgentConfigs(configDir, cwd).find((cfg) => normalizeName(cfg.name) === target);
|
|
9794
|
+
}
|
|
9795
|
+
function getAgentInstructions(agent) {
|
|
9796
|
+
return agent.system ?? agent.systemInstructions ?? agent.instructions;
|
|
9797
|
+
}
|
|
9798
|
+
function getEffectiveAgentTools(agent) {
|
|
9799
|
+
let allowed = new Set(SUBAGENT_ALLOWED_TOOLS);
|
|
9800
|
+
if (agent.permissionProfile === "read-only") {
|
|
9801
|
+
allowed = new Set([...allowed].filter((name) => READ_ONLY_TOOLS2.has(name)));
|
|
9802
|
+
}
|
|
9803
|
+
if (agent.allowedTools && agent.allowedTools.length > 0) {
|
|
9804
|
+
const requested = new Set(agent.allowedTools);
|
|
9805
|
+
allowed = new Set([...allowed].filter((name) => requested.has(name)));
|
|
9806
|
+
}
|
|
9807
|
+
if (agent.blockedTools && agent.blockedTools.length > 0) {
|
|
9808
|
+
for (const name of agent.blockedTools) allowed.delete(name);
|
|
9809
|
+
}
|
|
9810
|
+
return allowed;
|
|
9811
|
+
}
|
|
9812
|
+
function getEffectiveMaxRounds(requestedMaxRounds, agent) {
|
|
9813
|
+
const agentMax = agent.maxToolRounds && agent.maxToolRounds > 0 ? Math.round(agent.maxToolRounds) : SUBAGENT_MAX_ROUNDS_LIMIT;
|
|
9814
|
+
return Math.min(requestedMaxRounds, agentMax, SUBAGENT_MAX_ROUNDS_LIMIT);
|
|
9815
|
+
}
|
|
9816
|
+
|
|
9817
|
+
// src/agents/agent-runtime.ts
|
|
9818
|
+
var nextId = 1;
|
|
9819
|
+
var preferredAgentName = "worker";
|
|
9820
|
+
var runs = [];
|
|
9821
|
+
var MAX_RUNS = 50;
|
|
9822
|
+
function nowIso() {
|
|
9823
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
9824
|
+
}
|
|
9825
|
+
function startAgentRun(input) {
|
|
9826
|
+
const run = {
|
|
9827
|
+
id: `agent-${nextId++}`,
|
|
9828
|
+
agentName: input.agentName,
|
|
9829
|
+
task: input.task,
|
|
9830
|
+
status: "running",
|
|
9831
|
+
startedAt: nowIso(),
|
|
9832
|
+
toolNames: [],
|
|
9833
|
+
agentIndex: input.agentIndex,
|
|
9834
|
+
stopRequested: false
|
|
9835
|
+
};
|
|
9836
|
+
runs.unshift(run);
|
|
9837
|
+
if (runs.length > MAX_RUNS) runs.splice(MAX_RUNS);
|
|
9838
|
+
return run;
|
|
9839
|
+
}
|
|
9840
|
+
function recordAgentTool(runId, toolName) {
|
|
9841
|
+
const run = runs.find((r) => r.id === runId);
|
|
9842
|
+
if (!run) return;
|
|
9843
|
+
if (!run.toolNames.includes(toolName)) run.toolNames.push(toolName);
|
|
9844
|
+
}
|
|
9845
|
+
function finishAgentRun(runId, status, summary, error) {
|
|
9846
|
+
const run = runs.find((r) => r.id === runId);
|
|
9847
|
+
if (!run) return;
|
|
9848
|
+
run.status = status;
|
|
9849
|
+
run.finishedAt = nowIso();
|
|
9850
|
+
if (summary) run.summary = summary;
|
|
9851
|
+
if (error) run.error = error;
|
|
9852
|
+
}
|
|
9853
|
+
function listAgentRuns() {
|
|
9854
|
+
return runs.map((run) => ({ ...run, toolNames: [...run.toolNames] }));
|
|
9855
|
+
}
|
|
9856
|
+
function requestAgentStop(idOrAll) {
|
|
9857
|
+
const target = idOrAll.trim().toLowerCase();
|
|
9858
|
+
let count = 0;
|
|
9859
|
+
for (const run of runs) {
|
|
9860
|
+
if (run.status !== "running") continue;
|
|
9861
|
+
if (target === "all" || run.id.toLowerCase() === target) {
|
|
9862
|
+
run.stopRequested = true;
|
|
9863
|
+
count++;
|
|
9864
|
+
}
|
|
9865
|
+
}
|
|
9866
|
+
return count;
|
|
9867
|
+
}
|
|
9868
|
+
function isAgentStopRequested(runId) {
|
|
9869
|
+
return runs.find((r) => r.id === runId)?.stopRequested ?? false;
|
|
9870
|
+
}
|
|
9871
|
+
function setPreferredAgentName(name) {
|
|
9872
|
+
preferredAgentName = name.trim() || "worker";
|
|
9873
|
+
}
|
|
9874
|
+
function getPreferredAgentName() {
|
|
9875
|
+
return preferredAgentName;
|
|
9876
|
+
}
|
|
9877
|
+
|
|
9666
9878
|
// src/tools/builtin/spawn-agent.ts
|
|
9667
9879
|
var spawnAgentContext = {
|
|
9668
9880
|
provider: null,
|
|
@@ -9677,18 +9889,27 @@ function agentPrefix(agentIndex) {
|
|
|
9677
9889
|
return theme.dim(` \u2503${agentIndex} `);
|
|
9678
9890
|
}
|
|
9679
9891
|
var SubAgentExecutor = class {
|
|
9680
|
-
constructor(registry, agentIndex = null) {
|
|
9892
|
+
constructor(registry, agentIndex = null, runId) {
|
|
9681
9893
|
this.registry = registry;
|
|
9894
|
+
this.runId = runId;
|
|
9682
9895
|
this.prefix = agentPrefix(agentIndex);
|
|
9683
9896
|
}
|
|
9684
9897
|
round = 0;
|
|
9685
9898
|
totalRounds = 0;
|
|
9686
9899
|
prefix = PREFIX;
|
|
9900
|
+
toolNames = [];
|
|
9687
9901
|
setRoundInfo(current, total) {
|
|
9688
9902
|
this.round = current;
|
|
9689
9903
|
this.totalRounds = total;
|
|
9690
9904
|
}
|
|
9691
9905
|
async execute(call) {
|
|
9906
|
+
if (this.runId && isAgentStopRequested(this.runId)) {
|
|
9907
|
+
return {
|
|
9908
|
+
callId: call.id,
|
|
9909
|
+
content: "Sub-agent stop requested.",
|
|
9910
|
+
isError: true
|
|
9911
|
+
};
|
|
9912
|
+
}
|
|
9692
9913
|
const tool = this.registry.get(call.name);
|
|
9693
9914
|
if (!tool) {
|
|
9694
9915
|
return {
|
|
@@ -9709,6 +9930,8 @@ var SubAgentExecutor = class {
|
|
|
9709
9930
|
};
|
|
9710
9931
|
}
|
|
9711
9932
|
this.printToolCall(call, dangerLevel);
|
|
9933
|
+
if (!this.toolNames.includes(call.name)) this.toolNames.push(call.name);
|
|
9934
|
+
if (this.runId) recordAgentTool(this.runId, call.name);
|
|
9712
9935
|
try {
|
|
9713
9936
|
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
9714
9937
|
const content = truncateOutput(rawContent, call.name);
|
|
@@ -9728,7 +9951,6 @@ var SubAgentExecutor = class {
|
|
|
9728
9951
|
}
|
|
9729
9952
|
return results;
|
|
9730
9953
|
}
|
|
9731
|
-
// ── 带前缀的终端输出 ──
|
|
9732
9954
|
printPrefixed(text) {
|
|
9733
9955
|
for (const line of text.split("\n")) {
|
|
9734
9956
|
console.log(this.prefix + line);
|
|
@@ -9769,7 +9991,7 @@ var SubAgentExecutor = class {
|
|
|
9769
9991
|
}
|
|
9770
9992
|
}
|
|
9771
9993
|
};
|
|
9772
|
-
function buildSubAgentSystemPrompt(task, parentSystemPrompt) {
|
|
9994
|
+
function buildSubAgentSystemPrompt(task, agent, parentSystemPrompt) {
|
|
9773
9995
|
const parts = [];
|
|
9774
9996
|
if (parentSystemPrompt) {
|
|
9775
9997
|
parts.push(parentSystemPrompt);
|
|
@@ -9777,6 +9999,8 @@ function buildSubAgentSystemPrompt(task, parentSystemPrompt) {
|
|
|
9777
9999
|
parts.push(
|
|
9778
10000
|
`# Sub-Agent Mode
|
|
9779
10001
|
|
|
10002
|
+
**Agent Role**: ${agent.name}${agent.description ? ` \u2014 ${agent.description}` : ""}
|
|
10003
|
+
|
|
9780
10004
|
You are a focused sub-agent delegated by the main agent to complete a specific task.
|
|
9781
10005
|
|
|
9782
10006
|
**Your Task**:
|
|
@@ -9790,13 +10014,19 @@ ${task}
|
|
|
9790
10014
|
5. Destructive operations (rm -rf etc.) will be automatically blocked
|
|
9791
10015
|
6. Be efficient, minimize unnecessary tool call rounds`
|
|
9792
10016
|
);
|
|
10017
|
+
const agentInstructions = getAgentInstructions(agent);
|
|
10018
|
+
if (agentInstructions) {
|
|
10019
|
+
parts.push(`# Agent Instructions
|
|
10020
|
+
|
|
10021
|
+
${agentInstructions}`);
|
|
10022
|
+
}
|
|
9793
10023
|
return parts.join("\n\n---\n\n");
|
|
9794
10024
|
}
|
|
9795
|
-
function printSubAgentHeader(task, maxRounds, agentIndex = null) {
|
|
10025
|
+
function printSubAgentHeader(task, maxRounds, agentName, agentIndex = null) {
|
|
9796
10026
|
const prefix = agentPrefix(agentIndex);
|
|
9797
10027
|
console.log();
|
|
9798
10028
|
console.log(theme.dim(" \u250F\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
9799
|
-
const label = agentIndex !== null ? `\u{1F916} Sub-Agent #${agentIndex} Spawned` :
|
|
10029
|
+
const label = agentIndex !== null ? `\u{1F916} Sub-Agent #${agentIndex} Spawned (${agentName})` : `\u{1F916} Sub-Agent Spawned (${agentName})`;
|
|
9800
10030
|
console.log(prefix + theme.toolCall(label));
|
|
9801
10031
|
console.log(
|
|
9802
10032
|
prefix + theme.dim("Task: ") + (task.slice(0, 120) + (task.length > 120 ? "..." : ""))
|
|
@@ -9819,58 +10049,110 @@ function printSubAgentFooter(usage, agentIndex = null) {
|
|
|
9819
10049
|
console.log(theme.dim(" \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
9820
10050
|
console.log();
|
|
9821
10051
|
}
|
|
9822
|
-
|
|
10052
|
+
function summarizeContent(content) {
|
|
10053
|
+
const flat = content.replace(/\s+/g, " ").trim();
|
|
10054
|
+
return flat.length > 500 ? flat.slice(0, 497) + "..." : flat;
|
|
10055
|
+
}
|
|
10056
|
+
function extractEvidence(content, toolNames) {
|
|
10057
|
+
const evidence = /* @__PURE__ */ new Set();
|
|
10058
|
+
if (toolNames.length > 0) evidence.add(`Tools: ${toolNames.join(", ")}`);
|
|
10059
|
+
const matches = content.match(/(?:[\w.-]+[\\/])+[\w.-]+\.(?:ts|tsx|js|jsx|json|md|css|html|py|go|rs|yml|yaml)/g) ?? [];
|
|
10060
|
+
for (const match of matches.slice(0, 8)) evidence.add(match);
|
|
10061
|
+
return [...evidence].slice(0, 10);
|
|
10062
|
+
}
|
|
10063
|
+
function formatAgentResult(input) {
|
|
10064
|
+
const lines = [input.title, "", `**Agent**: ${input.agentName}`];
|
|
10065
|
+
if (input.task) lines.push(`**Task**: ${input.task}`);
|
|
10066
|
+
lines.push("", "### Summary", input.summary || "(no summary)", "", "### Key Evidence");
|
|
10067
|
+
if (input.evidence.length > 0) {
|
|
10068
|
+
for (const item of input.evidence) lines.push(`- ${item}`);
|
|
10069
|
+
} else {
|
|
10070
|
+
lines.push("- No structured evidence extracted.");
|
|
10071
|
+
}
|
|
10072
|
+
lines.push("", "### Tools Used");
|
|
10073
|
+
lines.push(input.toolNames.length > 0 ? input.toolNames.join(", ") : "No tools used.");
|
|
10074
|
+
lines.push("", "### Full Result", input.content, "");
|
|
10075
|
+
lines.push(`*Tokens: ${input.usage.inputTokens} in / ${input.usage.outputTokens} out*`);
|
|
10076
|
+
return lines;
|
|
10077
|
+
}
|
|
10078
|
+
async function runSubAgent(task, maxRounds, agentIndex, ctx, agent) {
|
|
9823
10079
|
if (!ctx.provider) {
|
|
9824
10080
|
throw new ToolError("spawn_agent", "provider not initialized (context not injected)");
|
|
9825
10081
|
}
|
|
10082
|
+
const providerFromConfig = agent.provider && ctx.providers?.has(agent.provider) ? ctx.providers.get(agent.provider) : void 0;
|
|
10083
|
+
const provider = providerFromConfig ?? ctx.provider;
|
|
10084
|
+
const model = agent.model ?? (providerFromConfig?.info?.defaultModel ?? ctx.model);
|
|
10085
|
+
const run = startAgentRun({
|
|
10086
|
+
agentName: agent.name,
|
|
10087
|
+
task,
|
|
10088
|
+
agentIndex: agentIndex ?? void 0
|
|
10089
|
+
});
|
|
10090
|
+
const effectiveTools = getEffectiveAgentTools(agent);
|
|
9826
10091
|
const subRegistry = new ToolRegistry();
|
|
9827
10092
|
for (const tool of subRegistry.listAll()) {
|
|
9828
|
-
if (!SUBAGENT_ALLOWED_TOOLS.has(tool.definition.name)) {
|
|
10093
|
+
if (!SUBAGENT_ALLOWED_TOOLS.has(tool.definition.name) || !effectiveTools.has(tool.definition.name)) {
|
|
9829
10094
|
subRegistry.unregister(tool.definition.name);
|
|
9830
10095
|
}
|
|
9831
10096
|
}
|
|
9832
|
-
const subExecutor = new SubAgentExecutor(subRegistry, agentIndex);
|
|
10097
|
+
const subExecutor = new SubAgentExecutor(subRegistry, agentIndex, run.id);
|
|
9833
10098
|
const subMessages = [
|
|
9834
10099
|
{ role: "user", content: task, timestamp: /* @__PURE__ */ new Date() }
|
|
9835
10100
|
];
|
|
9836
|
-
const subSystemPrompt = buildSubAgentSystemPrompt(task, ctx.systemPrompt);
|
|
10101
|
+
const subSystemPrompt = buildSubAgentSystemPrompt(task, agent, ctx.systemPrompt);
|
|
9837
10102
|
const toolDefs = subRegistry.getDefinitions();
|
|
9838
|
-
printSubAgentHeader(task, maxRounds, agentIndex);
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
10103
|
+
printSubAgentHeader(task, maxRounds, agent.name, agentIndex);
|
|
10104
|
+
try {
|
|
10105
|
+
const loop = await runLeanAgentLoop({
|
|
10106
|
+
provider,
|
|
10107
|
+
messages: subMessages,
|
|
10108
|
+
model,
|
|
10109
|
+
maxRounds,
|
|
10110
|
+
chatParams: {
|
|
10111
|
+
temperature: ctx.modelParams.temperature,
|
|
10112
|
+
maxTokens: ctx.modelParams.maxTokens,
|
|
10113
|
+
timeout: ctx.modelParams.timeout,
|
|
10114
|
+
thinking: ctx.modelParams.thinking
|
|
10115
|
+
},
|
|
10116
|
+
executeTools: (calls) => subExecutor.executeAll(calls),
|
|
10117
|
+
systemPromptForRound: () => subSystemPrompt,
|
|
10118
|
+
toolDefsForRound: () => toolDefs,
|
|
10119
|
+
onRoundStart: (round) => {
|
|
10120
|
+
subExecutor.setRoundInfo(round + 1, maxRounds);
|
|
10121
|
+
if (ctx.configManager) {
|
|
10122
|
+
googleSearchContext.configManager = ctx.configManager;
|
|
10123
|
+
}
|
|
10124
|
+
},
|
|
10125
|
+
onRoundsExhausted: () => `(Sub-agent reached maximum rounds (${maxRounds}) without producing a final response)`,
|
|
10126
|
+
onError: (errMsg) => {
|
|
10127
|
+
process.stderr.write(`
|
|
9862
10128
|
[spawn_agent] Error in sub-agent loop: ${errMsg}
|
|
9863
10129
|
`);
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
10130
|
+
return `(Sub-agent error: ${errMsg})`;
|
|
10131
|
+
}
|
|
10132
|
+
});
|
|
10133
|
+
printSubAgentFooter(loop.usage, agentIndex);
|
|
10134
|
+
const summary = summarizeContent(loop.content);
|
|
10135
|
+
const evidence = extractEvidence(loop.content, subExecutor.toolNames);
|
|
10136
|
+
const stopped = isAgentStopRequested(run.id);
|
|
10137
|
+
finishAgentRun(run.id, stopped ? "stopped" : "ok", summary);
|
|
10138
|
+
return {
|
|
10139
|
+
content: loop.content,
|
|
10140
|
+
usage: loop.usage,
|
|
10141
|
+
toolNames: subExecutor.toolNames,
|
|
10142
|
+
summary,
|
|
10143
|
+
evidence,
|
|
10144
|
+
agentName: agent.name
|
|
10145
|
+
};
|
|
10146
|
+
} catch (err) {
|
|
10147
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10148
|
+
finishAgentRun(run.id, "error", void 0, message);
|
|
10149
|
+
throw err;
|
|
10150
|
+
}
|
|
9869
10151
|
}
|
|
9870
10152
|
var spawnAgentTool = {
|
|
9871
10153
|
definition: {
|
|
9872
10154
|
name: "spawn_agent",
|
|
9873
|
-
description: "Delegate subtask(s) to independent
|
|
10155
|
+
description: "Delegate subtask(s) to independent named agents. Each sub-agent has its own conversation context and agentic tool-call loop, inherits the parent safety boundary, and can only narrow the sub-agent tool whitelist. Pass `agent` to select a built-in or configured role. Built-ins: explorer, worker, reviewer, security, tester. Pass `task` for a single sub-agent, or `tasks` (array) to run multiple sub-agents IN PARALLEL. Returns summary, key evidence, and tools used for each sub-agent result.",
|
|
9874
10156
|
parameters: {
|
|
9875
10157
|
task: {
|
|
9876
10158
|
type: "string",
|
|
@@ -9888,7 +10170,12 @@ var spawnAgentTool = {
|
|
|
9888
10170
|
},
|
|
9889
10171
|
max_rounds: {
|
|
9890
10172
|
type: "number",
|
|
9891
|
-
description: "Max tool-call rounds per sub-agent (1-
|
|
10173
|
+
description: "Max tool-call rounds per sub-agent (1-30, default 15). Agent config can only reduce this value.",
|
|
10174
|
+
required: false
|
|
10175
|
+
},
|
|
10176
|
+
agent: {
|
|
10177
|
+
type: "string",
|
|
10178
|
+
description: "Agent role name from built-ins or ~/.aicli/agents / .aicli/agents JSON configs. Built-ins: explorer, worker, reviewer, security, tester.",
|
|
9892
10179
|
required: false
|
|
9893
10180
|
}
|
|
9894
10181
|
},
|
|
@@ -9908,10 +10195,17 @@ var spawnAgentTool = {
|
|
|
9908
10195
|
Math.max(Math.round(rawMaxRounds), 1),
|
|
9909
10196
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
9910
10197
|
);
|
|
10198
|
+
const requestedAgentName = typeof args["agent"] === "string" && args["agent"].trim() ? args["agent"].trim() : getPreferredAgentName();
|
|
9911
10199
|
const ctx = spawnAgentContext;
|
|
9912
10200
|
if (!ctx.provider) {
|
|
9913
10201
|
throw new ToolError("spawn_agent", "provider not initialized (context not injected)");
|
|
9914
10202
|
}
|
|
10203
|
+
const agent = resolveAgentConfig(requestedAgentName, ctx.configManager?.getConfigDir());
|
|
10204
|
+
if (!agent) {
|
|
10205
|
+
const available = listAgentConfigs(ctx.configManager?.getConfigDir()).map((cfg) => cfg.name).join(", ");
|
|
10206
|
+
throw new ToolError("spawn_agent", `unknown agent "${requestedAgentName}". Available agents: ${available}`);
|
|
10207
|
+
}
|
|
10208
|
+
const effectiveMaxRounds = getEffectiveMaxRounds(maxRounds, agent);
|
|
9915
10209
|
const hookConfig = ctx.configManager?.get("hooks") ?? void 0;
|
|
9916
10210
|
const hookConfigDir = ctx.configManager?.getConfigDir() ?? process.cwd();
|
|
9917
10211
|
const taskCount = singleTask ? 1 : tasksArr.length;
|
|
@@ -9919,7 +10213,8 @@ var spawnAgentTool = {
|
|
|
9919
10213
|
task: singleTask || void 0,
|
|
9920
10214
|
tasks: tasksArr.length > 0 ? tasksArr : void 0,
|
|
9921
10215
|
taskCount,
|
|
9922
|
-
maxRounds
|
|
10216
|
+
maxRounds: effectiveMaxRounds,
|
|
10217
|
+
agent: agent.name
|
|
9923
10218
|
};
|
|
9924
10219
|
const startDecisions = runLifecycleHooks(hookConfig, "SubagentStart", hookPayload, hookConfigDir);
|
|
9925
10220
|
const startDeny = startDecisions.find((d) => d.action === "deny");
|
|
@@ -9931,32 +10226,37 @@ var spawnAgentTool = {
|
|
|
9931
10226
|
subAgentGuard.active = true;
|
|
9932
10227
|
try {
|
|
9933
10228
|
if (singleTask) {
|
|
9934
|
-
const
|
|
10229
|
+
const result = await runSubAgent(singleTask, effectiveMaxRounds, null, ctx, agent);
|
|
9935
10230
|
subagentStatus = "ok";
|
|
9936
|
-
return
|
|
9937
|
-
"## Sub-Agent Result",
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
9943
|
-
|
|
10231
|
+
return formatAgentResult({
|
|
10232
|
+
title: "## Sub-Agent Result",
|
|
10233
|
+
content: result.content,
|
|
10234
|
+
usage: result.usage,
|
|
10235
|
+
toolNames: result.toolNames,
|
|
10236
|
+
summary: result.summary,
|
|
10237
|
+
evidence: result.evidence,
|
|
10238
|
+
agentName: result.agentName
|
|
10239
|
+
}).join("\n");
|
|
9944
10240
|
}
|
|
9945
10241
|
console.log();
|
|
9946
|
-
console.log(theme.toolCall(`\u{1F916} Spawning ${tasksArr.length} sub-agents in parallel...`));
|
|
10242
|
+
console.log(theme.toolCall(`\u{1F916} Spawning ${tasksArr.length} sub-agents in parallel as ${agent.name}...`));
|
|
9947
10243
|
const results = await Promise.all(
|
|
9948
|
-
tasksArr.map((t, i) => runSubAgent(t,
|
|
10244
|
+
tasksArr.map((t, i) => runSubAgent(t, effectiveMaxRounds, i + 1, ctx, agent))
|
|
9949
10245
|
);
|
|
9950
10246
|
const totalIn = results.reduce((s, r) => s + r.usage.inputTokens, 0);
|
|
9951
10247
|
const totalOut = results.reduce((s, r) => s + r.usage.outputTokens, 0);
|
|
9952
10248
|
const lines = [`## Parallel Sub-Agent Results (${results.length} agents)`, ""];
|
|
9953
10249
|
results.forEach((r, i) => {
|
|
9954
|
-
lines.push(
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
10250
|
+
lines.push(...formatAgentResult({
|
|
10251
|
+
title: `### Sub-Agent #${i + 1}`,
|
|
10252
|
+
task: `${tasksArr[i].slice(0, 200)}${tasksArr[i].length > 200 ? "..." : ""}`,
|
|
10253
|
+
content: r.content,
|
|
10254
|
+
usage: r.usage,
|
|
10255
|
+
toolNames: r.toolNames,
|
|
10256
|
+
summary: r.summary,
|
|
10257
|
+
evidence: r.evidence,
|
|
10258
|
+
agentName: r.agentName
|
|
10259
|
+
}));
|
|
9960
10260
|
lines.push("");
|
|
9961
10261
|
});
|
|
9962
10262
|
lines.push("---");
|
|
@@ -10187,14 +10487,14 @@ var taskStopTool = {
|
|
|
10187
10487
|
|
|
10188
10488
|
// src/tools/builtin/git-tools.ts
|
|
10189
10489
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
10190
|
-
import { existsSync as
|
|
10191
|
-
import { join as
|
|
10490
|
+
import { existsSync as existsSync16 } from "fs";
|
|
10491
|
+
import { join as join11 } from "path";
|
|
10192
10492
|
function assertGitRepo(cwd) {
|
|
10193
10493
|
let dir = cwd;
|
|
10194
10494
|
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
10195
10495
|
while (dir && dir !== root) {
|
|
10196
|
-
if (
|
|
10197
|
-
const parent =
|
|
10496
|
+
if (existsSync16(join11(dir, ".git"))) return;
|
|
10497
|
+
const parent = join11(dir, "..");
|
|
10198
10498
|
if (parent === dir) break;
|
|
10199
10499
|
dir = parent;
|
|
10200
10500
|
}
|
|
@@ -10459,7 +10759,7 @@ ${commitOutput.trim()}`;
|
|
|
10459
10759
|
};
|
|
10460
10760
|
|
|
10461
10761
|
// src/tools/builtin/notebook-edit.ts
|
|
10462
|
-
import { readFileSync as
|
|
10762
|
+
import { readFileSync as readFileSync13, existsSync as existsSync17 } from "fs";
|
|
10463
10763
|
import { writeFile } from "fs/promises";
|
|
10464
10764
|
import { resolve as resolve6, extname as extname2 } from "path";
|
|
10465
10765
|
var notebookEditTool = {
|
|
@@ -10514,10 +10814,10 @@ var notebookEditTool = {
|
|
|
10514
10814
|
if (extname2(absPath).toLowerCase() !== ".ipynb") {
|
|
10515
10815
|
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
10516
10816
|
}
|
|
10517
|
-
if (!
|
|
10817
|
+
if (!existsSync17(absPath)) {
|
|
10518
10818
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
10519
10819
|
}
|
|
10520
|
-
const raw =
|
|
10820
|
+
const raw = readFileSync13(absPath, "utf-8");
|
|
10521
10821
|
const nb = parseNotebook(raw);
|
|
10522
10822
|
const cellIdx0 = cellIndexRaw - 1;
|
|
10523
10823
|
undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
|
|
@@ -10934,8 +11234,8 @@ function estimateToolDefinitionTokens(def) {
|
|
|
10934
11234
|
|
|
10935
11235
|
// src/tools/registry.ts
|
|
10936
11236
|
import { pathToFileURL } from "url";
|
|
10937
|
-
import { existsSync as
|
|
10938
|
-
import { join as
|
|
11237
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync8, readdirSync as readdirSync8 } from "fs";
|
|
11238
|
+
import { join as join12 } from "path";
|
|
10939
11239
|
var ToolRegistry = class {
|
|
10940
11240
|
tools = /* @__PURE__ */ new Map();
|
|
10941
11241
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -11096,7 +11396,7 @@ var ToolRegistry = class {
|
|
|
11096
11396
|
* Returns the number of successfully loaded plugins.
|
|
11097
11397
|
*/
|
|
11098
11398
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
11099
|
-
if (!
|
|
11399
|
+
if (!existsSync18(pluginsDir)) {
|
|
11100
11400
|
try {
|
|
11101
11401
|
mkdirSync8(pluginsDir, { recursive: true });
|
|
11102
11402
|
} catch {
|
|
@@ -11105,7 +11405,7 @@ var ToolRegistry = class {
|
|
|
11105
11405
|
}
|
|
11106
11406
|
let files;
|
|
11107
11407
|
try {
|
|
11108
|
-
files =
|
|
11408
|
+
files = readdirSync8(pluginsDir).filter((f) => f.endsWith(".js"));
|
|
11109
11409
|
} catch {
|
|
11110
11410
|
return 0;
|
|
11111
11411
|
}
|
|
@@ -11122,12 +11422,12 @@ var ToolRegistry = class {
|
|
|
11122
11422
|
process.stderr.write(
|
|
11123
11423
|
`
|
|
11124
11424
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
11125
|
-
` + files.map((f) => ` + ${
|
|
11425
|
+
` + files.map((f) => ` + ${join12(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
11126
11426
|
);
|
|
11127
11427
|
let loaded = 0;
|
|
11128
11428
|
for (const file of files) {
|
|
11129
11429
|
try {
|
|
11130
|
-
const fileUrl = pathToFileURL(
|
|
11430
|
+
const fileUrl = pathToFileURL(join12(pluginsDir, file)).href;
|
|
11131
11431
|
const mod = await import(fileUrl);
|
|
11132
11432
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
11133
11433
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -11673,11 +11973,11 @@ var McpManager = class {
|
|
|
11673
11973
|
};
|
|
11674
11974
|
|
|
11675
11975
|
// src/skills/manager.ts
|
|
11676
|
-
import { existsSync as
|
|
11677
|
-
import { join as
|
|
11976
|
+
import { existsSync as existsSync19, readdirSync as readdirSync9, mkdirSync as mkdirSync9, statSync as statSync7 } from "fs";
|
|
11977
|
+
import { join as join13 } from "path";
|
|
11678
11978
|
|
|
11679
11979
|
// src/skills/types.ts
|
|
11680
|
-
import { readFileSync as
|
|
11980
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
11681
11981
|
import { basename as basename5 } from "path";
|
|
11682
11982
|
function parseSimpleYaml(yaml) {
|
|
11683
11983
|
const result = {};
|
|
@@ -11699,7 +11999,7 @@ function parseYamlArray(value) {
|
|
|
11699
11999
|
function parseSkillFile(filePath) {
|
|
11700
12000
|
let raw;
|
|
11701
12001
|
try {
|
|
11702
|
-
raw =
|
|
12002
|
+
raw = readFileSync14(filePath, "utf-8");
|
|
11703
12003
|
} catch {
|
|
11704
12004
|
return null;
|
|
11705
12005
|
}
|
|
@@ -11742,7 +12042,7 @@ var SkillManager = class {
|
|
|
11742
12042
|
/** 发现并加载 skillsDir 下所有 .md 文件,返回加载数量 */
|
|
11743
12043
|
loadSkills() {
|
|
11744
12044
|
this.skills.clear();
|
|
11745
|
-
if (!
|
|
12045
|
+
if (!existsSync19(this.skillsDir)) {
|
|
11746
12046
|
try {
|
|
11747
12047
|
mkdirSync9(this.skillsDir, { recursive: true });
|
|
11748
12048
|
} catch {
|
|
@@ -11751,20 +12051,20 @@ var SkillManager = class {
|
|
|
11751
12051
|
}
|
|
11752
12052
|
let entries;
|
|
11753
12053
|
try {
|
|
11754
|
-
entries =
|
|
12054
|
+
entries = readdirSync9(this.skillsDir);
|
|
11755
12055
|
} catch {
|
|
11756
12056
|
return 0;
|
|
11757
12057
|
}
|
|
11758
12058
|
for (const entry of entries) {
|
|
11759
12059
|
let filePath;
|
|
11760
|
-
const fullPath =
|
|
12060
|
+
const fullPath = join13(this.skillsDir, entry);
|
|
11761
12061
|
if (entry.endsWith(".md")) {
|
|
11762
12062
|
filePath = fullPath;
|
|
11763
12063
|
} else {
|
|
11764
12064
|
try {
|
|
11765
12065
|
if (statSync7(fullPath).isDirectory()) {
|
|
11766
|
-
const skillMd =
|
|
11767
|
-
if (
|
|
12066
|
+
const skillMd = join13(fullPath, "SKILL.md");
|
|
12067
|
+
if (existsSync19(skillMd)) {
|
|
11768
12068
|
filePath = skillMd;
|
|
11769
12069
|
} else {
|
|
11770
12070
|
continue;
|
|
@@ -11829,7 +12129,7 @@ var SkillManager = class {
|
|
|
11829
12129
|
// src/web/tool-executor-web.ts
|
|
11830
12130
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
11831
12131
|
import { tmpdir as tmpdir2 } from "os";
|
|
11832
|
-
import { existsSync as
|
|
12132
|
+
import { existsSync as existsSync20, readFileSync as readFileSync15 } from "fs";
|
|
11833
12133
|
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
11834
12134
|
constructor(registry, ws) {
|
|
11835
12135
|
this.registry = registry;
|
|
@@ -11968,9 +12268,9 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
11968
12268
|
if (call.name === "write_file") {
|
|
11969
12269
|
const filePath = String(call.arguments["path"] ?? "");
|
|
11970
12270
|
const newContent = String(call.arguments["content"] ?? "");
|
|
11971
|
-
if (filePath &&
|
|
12271
|
+
if (filePath && existsSync20(filePath)) {
|
|
11972
12272
|
try {
|
|
11973
|
-
const old =
|
|
12273
|
+
const old = readFileSync15(filePath, "utf-8");
|
|
11974
12274
|
return renderDiff(old, newContent, { filePath });
|
|
11975
12275
|
} catch {
|
|
11976
12276
|
}
|
|
@@ -12273,20 +12573,20 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
12273
12573
|
};
|
|
12274
12574
|
|
|
12275
12575
|
// src/core/system-prompt-builder.ts
|
|
12276
|
-
import { existsSync as
|
|
12277
|
-
import { join as
|
|
12576
|
+
import { existsSync as existsSync22, readFileSync as readFileSync17 } from "fs";
|
|
12577
|
+
import { join as join15 } from "path";
|
|
12278
12578
|
|
|
12279
12579
|
// src/repl/dev-state.ts
|
|
12280
|
-
import { existsSync as
|
|
12281
|
-
import { join as
|
|
12282
|
-
import { homedir as
|
|
12580
|
+
import { existsSync as existsSync21, readFileSync as readFileSync16, unlinkSync as unlinkSync4, mkdirSync as mkdirSync10 } from "fs";
|
|
12581
|
+
import { join as join14 } from "path";
|
|
12582
|
+
import { homedir as homedir7 } from "os";
|
|
12283
12583
|
function getDevStatePath() {
|
|
12284
|
-
return
|
|
12584
|
+
return join14(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
|
|
12285
12585
|
}
|
|
12286
12586
|
function loadDevState() {
|
|
12287
12587
|
const path3 = getDevStatePath();
|
|
12288
|
-
if (!
|
|
12289
|
-
const content =
|
|
12588
|
+
if (!existsSync21(path3)) return null;
|
|
12589
|
+
const content = readFileSync16(path3, "utf-8").trim();
|
|
12290
12590
|
return content || null;
|
|
12291
12591
|
}
|
|
12292
12592
|
|
|
@@ -12344,9 +12644,9 @@ ${ctx.activeSkill.content}`);
|
|
|
12344
12644
|
return { stable: stableParts.join("\n\n---\n\n"), volatile };
|
|
12345
12645
|
}
|
|
12346
12646
|
function loadMemoryContent(configDir) {
|
|
12347
|
-
const memoryPath =
|
|
12348
|
-
if (!
|
|
12349
|
-
let content =
|
|
12647
|
+
const memoryPath = join15(configDir, MEMORY_FILE_NAME);
|
|
12648
|
+
if (!existsSync22(memoryPath)) return null;
|
|
12649
|
+
let content = readFileSync17(memoryPath, "utf-8").trim();
|
|
12350
12650
|
if (!content) return null;
|
|
12351
12651
|
if (content.length > MEMORY_MAX_CHARS) {
|
|
12352
12652
|
content = content.slice(-MEMORY_MAX_CHARS);
|
|
@@ -12555,8 +12855,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
12555
12855
|
}
|
|
12556
12856
|
|
|
12557
12857
|
// src/core/context-files.ts
|
|
12558
|
-
import { existsSync as
|
|
12559
|
-
import { join as
|
|
12858
|
+
import { existsSync as existsSync23, readFileSync as readFileSync18 } from "fs";
|
|
12859
|
+
import { join as join16, relative as relative3, resolve as resolve7 } from "path";
|
|
12560
12860
|
function uniqueNonEmpty(values) {
|
|
12561
12861
|
const seen = /* @__PURE__ */ new Set();
|
|
12562
12862
|
const out = [];
|
|
@@ -12586,7 +12886,7 @@ function readContextFile(level, filePath, cwd, maxBytes) {
|
|
|
12586
12886
|
const name = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
12587
12887
|
const shown = displayPath(filePath, cwd);
|
|
12588
12888
|
try {
|
|
12589
|
-
const raw =
|
|
12889
|
+
const raw = readFileSync18(filePath);
|
|
12590
12890
|
const byteCount = raw.byteLength;
|
|
12591
12891
|
if (byteCount === 0) {
|
|
12592
12892
|
return {
|
|
@@ -12633,8 +12933,8 @@ function readContextFile(level, filePath, cwd, maxBytes) {
|
|
|
12633
12933
|
function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
|
|
12634
12934
|
const skipped = [];
|
|
12635
12935
|
for (const candidate of candidates) {
|
|
12636
|
-
const filePath =
|
|
12637
|
-
if (!
|
|
12936
|
+
const filePath = join16(dir, candidate);
|
|
12937
|
+
if (!existsSync23(filePath)) continue;
|
|
12638
12938
|
const result = readContextFile(level, filePath, cwd, maxBytes);
|
|
12639
12939
|
if (result.skipped) skipped.push(result.skipped);
|
|
12640
12940
|
if (result.layer) return { layer: result.layer, skipped };
|
|
@@ -12661,7 +12961,7 @@ function loadContextFiles(options) {
|
|
|
12661
12961
|
});
|
|
12662
12962
|
return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
|
|
12663
12963
|
}
|
|
12664
|
-
if (!
|
|
12964
|
+
if (!existsSync23(filePath)) {
|
|
12665
12965
|
skipped.push({
|
|
12666
12966
|
level: "single",
|
|
12667
12967
|
filePath,
|
|
@@ -12712,13 +13012,13 @@ function loadContextFiles(options) {
|
|
|
12712
13012
|
}
|
|
12713
13013
|
|
|
12714
13014
|
// src/web/session-handler.ts
|
|
12715
|
-
import { existsSync as
|
|
12716
|
-
import { join as
|
|
13015
|
+
import { existsSync as existsSync26, readFileSync as readFileSync20, writeFileSync as writeFileSync2, mkdirSync as mkdirSync11, readdirSync as readdirSync11, statSync as statSync9, createWriteStream } from "fs";
|
|
13016
|
+
import { join as join20, resolve as resolve8, dirname as dirname5 } from "path";
|
|
12717
13017
|
|
|
12718
13018
|
// src/tools/git-context.ts
|
|
12719
13019
|
import { execSync as execSync2 } from "child_process";
|
|
12720
|
-
import { existsSync as
|
|
12721
|
-
import { join as
|
|
13020
|
+
import { existsSync as existsSync24 } from "fs";
|
|
13021
|
+
import { join as join17 } from "path";
|
|
12722
13022
|
function runGit2(cmd, cwd) {
|
|
12723
13023
|
try {
|
|
12724
13024
|
return execSync2(`git ${cmd}`, {
|
|
@@ -12735,7 +13035,7 @@ function getGitRoot(cwd = process.cwd()) {
|
|
|
12735
13035
|
return runGit2("rev-parse --show-toplevel", cwd);
|
|
12736
13036
|
}
|
|
12737
13037
|
function getGitContext(cwd = process.cwd()) {
|
|
12738
|
-
if (!
|
|
13038
|
+
if (!existsSync24(join17(cwd, ".git"))) {
|
|
12739
13039
|
const result = runGit2("rev-parse --git-dir", cwd);
|
|
12740
13040
|
if (!result) return null;
|
|
12741
13041
|
}
|
|
@@ -12864,8 +13164,8 @@ If no security issues found, state "\u2705 No security vulnerabilities detected"
|
|
|
12864
13164
|
}
|
|
12865
13165
|
|
|
12866
13166
|
// src/repl/commands/project-init.ts
|
|
12867
|
-
import { existsSync as
|
|
12868
|
-
import { join as
|
|
13167
|
+
import { existsSync as existsSync25, readFileSync as readFileSync19, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
|
|
13168
|
+
import { join as join18 } from "path";
|
|
12869
13169
|
var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
12870
13170
|
"node_modules",
|
|
12871
13171
|
".git",
|
|
@@ -12901,7 +13201,7 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
12901
13201
|
if (depth > maxDepth || count >= maxEntries) return;
|
|
12902
13202
|
let entries;
|
|
12903
13203
|
try {
|
|
12904
|
-
entries =
|
|
13204
|
+
entries = readdirSync10(d);
|
|
12905
13205
|
} catch {
|
|
12906
13206
|
return;
|
|
12907
13207
|
}
|
|
@@ -12909,11 +13209,11 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
12909
13209
|
const sorted = filtered.sort((a, b) => {
|
|
12910
13210
|
let aIsDir = false, bIsDir = false;
|
|
12911
13211
|
try {
|
|
12912
|
-
aIsDir = statSync8(
|
|
13212
|
+
aIsDir = statSync8(join18(d, a)).isDirectory();
|
|
12913
13213
|
} catch {
|
|
12914
13214
|
}
|
|
12915
13215
|
try {
|
|
12916
|
-
bIsDir = statSync8(
|
|
13216
|
+
bIsDir = statSync8(join18(d, b)).isDirectory();
|
|
12917
13217
|
} catch {
|
|
12918
13218
|
}
|
|
12919
13219
|
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
@@ -12921,7 +13221,7 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
12921
13221
|
});
|
|
12922
13222
|
for (let i = 0; i < sorted.length && count < maxEntries; i++) {
|
|
12923
13223
|
const name = sorted[i];
|
|
12924
|
-
const fullPath =
|
|
13224
|
+
const fullPath = join18(d, name);
|
|
12925
13225
|
const isLast = i === sorted.length - 1;
|
|
12926
13226
|
const connector = isLast ? "+-- " : "|-- ";
|
|
12927
13227
|
let isDir;
|
|
@@ -12948,7 +13248,7 @@ function scanProject(cwd) {
|
|
|
12948
13248
|
configFiles: [],
|
|
12949
13249
|
directoryStructure: ""
|
|
12950
13250
|
};
|
|
12951
|
-
const check = (file) =>
|
|
13251
|
+
const check = (file) => existsSync25(join18(cwd, file));
|
|
12952
13252
|
const configCandidates = [
|
|
12953
13253
|
"package.json",
|
|
12954
13254
|
"tsconfig.json",
|
|
@@ -12975,7 +13275,7 @@ function scanProject(cwd) {
|
|
|
12975
13275
|
info.type = "node";
|
|
12976
13276
|
info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
|
|
12977
13277
|
try {
|
|
12978
|
-
const pkg = JSON.parse(
|
|
13278
|
+
const pkg = JSON.parse(readFileSync19(join18(cwd, "package.json"), "utf-8"));
|
|
12979
13279
|
const scripts = pkg.scripts ?? {};
|
|
12980
13280
|
info.buildCommand = scripts.build ? "npm run build" : void 0;
|
|
12981
13281
|
info.testCommand = scripts.test ? "npm test" : void 0;
|
|
@@ -13635,7 +13935,7 @@ function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, avai
|
|
|
13635
13935
|
}
|
|
13636
13936
|
|
|
13637
13937
|
// src/hub/persist.ts
|
|
13638
|
-
import { join as
|
|
13938
|
+
import { join as join19 } from "path";
|
|
13639
13939
|
function discussionToMessages(state2) {
|
|
13640
13940
|
const out = [];
|
|
13641
13941
|
const t0 = state2.messages[0]?.timestamp ?? /* @__PURE__ */ new Date();
|
|
@@ -13673,7 +13973,7 @@ async function persistDiscussion(state2, config, defaultProvider, defaultModel)
|
|
|
13673
13973
|
session.title = `[Hub] ${state2.topic.slice(0, 48)}`.replace(/\n/g, " ");
|
|
13674
13974
|
session.titleAiGenerated = true;
|
|
13675
13975
|
await sm.save();
|
|
13676
|
-
return { id: session.id, path:
|
|
13976
|
+
return { id: session.id, path: join19(config.getHistoryDir(), `${session.id}.json`) };
|
|
13677
13977
|
}
|
|
13678
13978
|
|
|
13679
13979
|
// src/web/session-handler.ts
|
|
@@ -13833,7 +14133,19 @@ var SessionHandler = class {
|
|
|
13833
14133
|
costUsd,
|
|
13834
14134
|
providers: providerList,
|
|
13835
14135
|
branches,
|
|
13836
|
-
activeBranchId: sess?.activeBranchId ?? "main"
|
|
14136
|
+
activeBranchId: sess?.activeBranchId ?? "main",
|
|
14137
|
+
agents: listAgentRuns().slice(0, 20).map((run) => ({
|
|
14138
|
+
id: run.id,
|
|
14139
|
+
agentName: run.agentName,
|
|
14140
|
+
task: run.task,
|
|
14141
|
+
status: run.status,
|
|
14142
|
+
startedAt: run.startedAt,
|
|
14143
|
+
finishedAt: run.finishedAt,
|
|
14144
|
+
summary: run.summary,
|
|
14145
|
+
error: run.error,
|
|
14146
|
+
toolNames: run.toolNames,
|
|
14147
|
+
agentIndex: run.agentIndex
|
|
14148
|
+
}))
|
|
13837
14149
|
});
|
|
13838
14150
|
}
|
|
13839
14151
|
async handleMessage(raw) {
|
|
@@ -14310,6 +14622,7 @@ Details: ${errMsg.split("\n")[0]}
|
|
|
14310
14622
|
${systemPromptVolatile}` : systemPrompt;
|
|
14311
14623
|
spawnAgentContext.modelParams = modelParams;
|
|
14312
14624
|
spawnAgentContext.configManager = this.config;
|
|
14625
|
+
spawnAgentContext.providers = this.providers;
|
|
14313
14626
|
ToolExecutor.currentMessageIndex = this.sessions.current?.messages.length ?? 0;
|
|
14314
14627
|
return this.toolExecutor.executeAll(toolCalls);
|
|
14315
14628
|
},
|
|
@@ -14904,6 +15217,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
|
|
|
14904
15217
|
" /export [md|json] \u2014 Export conversation",
|
|
14905
15218
|
" /skill [name|off|list|reload] \u2014 Manage agent skills",
|
|
14906
15219
|
" /memory [show|add|clear] \u2014 Persistent memory management",
|
|
15220
|
+
" /agent list|switch|stop|summary \u2014 Manage named sub-agents",
|
|
14907
15221
|
" /yolo [on|off] \u2014 Toggle auto-approve (skip confirmations)",
|
|
14908
15222
|
" /search <keyword> \u2014 Search across all session histories",
|
|
14909
15223
|
" /undo [list|<n>] \u2014 Undo file operations",
|
|
@@ -15036,6 +15350,66 @@ ${activated.meta.description || ""}` });
|
|
|
15036
15350
|
}
|
|
15037
15351
|
break;
|
|
15038
15352
|
}
|
|
15353
|
+
case "agent": {
|
|
15354
|
+
const sub = args[0]?.toLowerCase() ?? "summary";
|
|
15355
|
+
if (sub === "list") {
|
|
15356
|
+
const agents = listAgentConfigs(this.config.getConfigDir());
|
|
15357
|
+
const lines = [`Agents (${agents.length})`, ""];
|
|
15358
|
+
for (const agent of agents) {
|
|
15359
|
+
const active = agent.name === getPreferredAgentName() ? " *" : " ";
|
|
15360
|
+
const model = [agent.provider, agent.model].filter(Boolean).join("/") || "inherit";
|
|
15361
|
+
lines.push(`${active} ${agent.name} [${agent.source ?? "builtin"}] \u2014 ${agent.description ?? ""}`);
|
|
15362
|
+
lines.push(` model: ${model} \xB7 permission: ${agent.permissionProfile ?? "workspace-write"} \xB7 maxRounds: ${agent.maxToolRounds ?? "inherit"}`);
|
|
15363
|
+
}
|
|
15364
|
+
lines.push("", "Config dirs: ~/.aicli/agents/ and .aicli/agents/");
|
|
15365
|
+
this.send({ type: "info", message: lines.join("\n") });
|
|
15366
|
+
break;
|
|
15367
|
+
}
|
|
15368
|
+
if (sub === "switch") {
|
|
15369
|
+
const name2 = args[1];
|
|
15370
|
+
if (!name2) {
|
|
15371
|
+
this.send({ type: "error", message: "Usage: /agent switch <name>" });
|
|
15372
|
+
break;
|
|
15373
|
+
}
|
|
15374
|
+
const agent = resolveAgentConfig(name2, this.config.getConfigDir());
|
|
15375
|
+
if (!agent) {
|
|
15376
|
+
const available = listAgentConfigs(this.config.getConfigDir()).map((a) => a.name).join(", ");
|
|
15377
|
+
this.send({ type: "error", message: `Unknown agent: ${name2}
|
|
15378
|
+
Available: ${available}` });
|
|
15379
|
+
break;
|
|
15380
|
+
}
|
|
15381
|
+
setPreferredAgentName(agent.name);
|
|
15382
|
+
this.send({ type: "info", message: `Preferred spawn_agent role: ${agent.name}` });
|
|
15383
|
+
this.sendStatus();
|
|
15384
|
+
break;
|
|
15385
|
+
}
|
|
15386
|
+
if (sub === "stop") {
|
|
15387
|
+
const target = args[1] ?? "all";
|
|
15388
|
+
const count = requestAgentStop(target);
|
|
15389
|
+
this.send({ type: "info", message: count > 0 ? `Stop requested for ${count} running agent(s).` : "No matching running agents." });
|
|
15390
|
+
this.sendStatus();
|
|
15391
|
+
break;
|
|
15392
|
+
}
|
|
15393
|
+
if (sub === "summary") {
|
|
15394
|
+
const runs2 = listAgentRuns();
|
|
15395
|
+
if (runs2.length === 0) {
|
|
15396
|
+
this.send({ type: "info", message: "No sub-agent runs yet." });
|
|
15397
|
+
break;
|
|
15398
|
+
}
|
|
15399
|
+
const lines = [`Recent agent runs (${runs2.length})`, ""];
|
|
15400
|
+
for (const run of runs2.slice(0, 20)) {
|
|
15401
|
+
lines.push(`${run.id} ${run.status} ${run.agentName} ${run.startedAt}`);
|
|
15402
|
+
lines.push(` task: ${run.task.slice(0, 120)}${run.task.length > 120 ? "..." : ""}`);
|
|
15403
|
+
if (run.toolNames.length > 0) lines.push(` tools: ${run.toolNames.join(", ")}`);
|
|
15404
|
+
if (run.summary) lines.push(` summary: ${run.summary.slice(0, 180)}${run.summary.length > 180 ? "..." : ""}`);
|
|
15405
|
+
if (run.error) lines.push(` error: ${run.error}`);
|
|
15406
|
+
}
|
|
15407
|
+
this.send({ type: "info", message: lines.join("\n") });
|
|
15408
|
+
break;
|
|
15409
|
+
}
|
|
15410
|
+
this.send({ type: "error", message: "Usage: /agent list|switch <name>|stop <id|all>|summary" });
|
|
15411
|
+
break;
|
|
15412
|
+
}
|
|
15039
15413
|
// ── /yolo ──────────────────────────────────────────────────────
|
|
15040
15414
|
case "auto": {
|
|
15041
15415
|
const sub = args[0]?.toLowerCase() ?? "status";
|
|
@@ -15178,9 +15552,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
15178
15552
|
let modifiedFiles = 0;
|
|
15179
15553
|
const diffLines2 = [];
|
|
15180
15554
|
for (const [filePath, { earliest }] of fileMap) {
|
|
15181
|
-
const currentContent =
|
|
15555
|
+
const currentContent = existsSync26(filePath) ? (() => {
|
|
15182
15556
|
try {
|
|
15183
|
-
return
|
|
15557
|
+
return readFileSync20(filePath, "utf-8");
|
|
15184
15558
|
} catch {
|
|
15185
15559
|
return null;
|
|
15186
15560
|
}
|
|
@@ -15696,7 +16070,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
15696
16070
|
case "test": {
|
|
15697
16071
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
15698
16072
|
try {
|
|
15699
|
-
const { executeTests } = await import("./run-tests-
|
|
16073
|
+
const { executeTests } = await import("./run-tests-R6FL6CQ3.js");
|
|
15700
16074
|
const argStr = args.join(" ").trim();
|
|
15701
16075
|
let testArgs = {};
|
|
15702
16076
|
if (argStr) {
|
|
@@ -15713,9 +16087,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
15713
16087
|
// ── /init ───────────────────────────────────────────────────────
|
|
15714
16088
|
case "init": {
|
|
15715
16089
|
const cwd = process.cwd();
|
|
15716
|
-
const targetPath =
|
|
16090
|
+
const targetPath = join20(cwd, "AICLI.md");
|
|
15717
16091
|
const force = args.includes("--force");
|
|
15718
|
-
if (
|
|
16092
|
+
if (existsSync26(targetPath) && !force) {
|
|
15719
16093
|
this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
|
|
15720
16094
|
Use /init --force to overwrite.` });
|
|
15721
16095
|
break;
|
|
@@ -15748,7 +16122,7 @@ Use /context reload to load it.` });
|
|
|
15748
16122
|
lines.push("**Config Files:**");
|
|
15749
16123
|
lines.push(` Dir: ${configDir}`);
|
|
15750
16124
|
const checkFile = (label, filePath) => {
|
|
15751
|
-
const exists =
|
|
16125
|
+
const exists = existsSync26(filePath);
|
|
15752
16126
|
let extra = "";
|
|
15753
16127
|
if (exists) {
|
|
15754
16128
|
try {
|
|
@@ -15758,9 +16132,9 @@ Use /context reload to load it.` });
|
|
|
15758
16132
|
}
|
|
15759
16133
|
lines.push(` ${exists ? "\u2713" : "\u2013"} ${label.padEnd(14)} ${exists ? filePath + extra : "(not found)"}`);
|
|
15760
16134
|
};
|
|
15761
|
-
checkFile("config.json",
|
|
15762
|
-
checkFile("memory.md",
|
|
15763
|
-
checkFile("dev-state.md",
|
|
16135
|
+
checkFile("config.json", join20(configDir, "config.json"));
|
|
16136
|
+
checkFile("memory.md", join20(configDir, MEMORY_FILE_NAME));
|
|
16137
|
+
checkFile("dev-state.md", join20(configDir, "dev-state.md"));
|
|
15764
16138
|
lines.push("");
|
|
15765
16139
|
if (this.mcpManager) {
|
|
15766
16140
|
lines.push("**MCP Servers:**");
|
|
@@ -15948,7 +16322,7 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
|
|
|
15948
16322
|
break;
|
|
15949
16323
|
}
|
|
15950
16324
|
const dirPath = resolve8(sub);
|
|
15951
|
-
if (!
|
|
16325
|
+
if (!existsSync26(dirPath)) {
|
|
15952
16326
|
this.send({ type: "error", message: `Directory not found: ${dirPath}` });
|
|
15953
16327
|
break;
|
|
15954
16328
|
}
|
|
@@ -15964,14 +16338,14 @@ It will be included in AI context for subsequent messages.` });
|
|
|
15964
16338
|
// ── /commands ───────────────────────────────────────────────────
|
|
15965
16339
|
case "commands": {
|
|
15966
16340
|
const configDir = this.config.getConfigDir();
|
|
15967
|
-
const commandsDir =
|
|
15968
|
-
if (!
|
|
16341
|
+
const commandsDir = join20(configDir, CUSTOM_COMMANDS_DIR_NAME);
|
|
16342
|
+
if (!existsSync26(commandsDir)) {
|
|
15969
16343
|
this.send({ type: "info", message: `No custom commands directory.
|
|
15970
16344
|
Create: ${commandsDir}/ with .md files.` });
|
|
15971
16345
|
break;
|
|
15972
16346
|
}
|
|
15973
16347
|
try {
|
|
15974
|
-
const files =
|
|
16348
|
+
const files = readdirSync11(commandsDir).filter((f) => f.endsWith(".md"));
|
|
15975
16349
|
if (files.length === 0) {
|
|
15976
16350
|
this.send({ type: "info", message: `No custom commands found in ${commandsDir}
|
|
15977
16351
|
Add .md files to create commands.` });
|
|
@@ -15991,7 +16365,7 @@ Add .md files to create commands.` });
|
|
|
15991
16365
|
// ── /plugins ────────────────────────────────────────────────────
|
|
15992
16366
|
case "plugins": {
|
|
15993
16367
|
const configDir = this.config.getConfigDir();
|
|
15994
|
-
const pluginsDir =
|
|
16368
|
+
const pluginsDir = join20(configDir, PLUGINS_DIR_NAME);
|
|
15995
16369
|
const pluginTools = this.toolRegistry.listPluginTools();
|
|
15996
16370
|
const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
|
|
15997
16371
|
if (pluginTools.length === 0) {
|
|
@@ -16188,11 +16562,11 @@ Add .md files to create commands.` });
|
|
|
16188
16562
|
}
|
|
16189
16563
|
memoryShow() {
|
|
16190
16564
|
const configDir = this.config.getConfigDir();
|
|
16191
|
-
const memPath =
|
|
16565
|
+
const memPath = join20(configDir, MEMORY_FILE_NAME);
|
|
16192
16566
|
let content = "";
|
|
16193
16567
|
try {
|
|
16194
|
-
if (
|
|
16195
|
-
content =
|
|
16568
|
+
if (existsSync26(memPath)) {
|
|
16569
|
+
content = readFileSync20(memPath, "utf-8");
|
|
16196
16570
|
}
|
|
16197
16571
|
} catch (err) {
|
|
16198
16572
|
process.stderr.write(`[web] Failed to read memory file: ${err instanceof Error ? err.message : err}
|
|
@@ -16206,11 +16580,11 @@ Add .md files to create commands.` });
|
|
|
16206
16580
|
}
|
|
16207
16581
|
memoryAdd(text) {
|
|
16208
16582
|
const configDir = this.config.getConfigDir();
|
|
16209
|
-
const memPath =
|
|
16583
|
+
const memPath = join20(configDir, MEMORY_FILE_NAME);
|
|
16210
16584
|
try {
|
|
16211
16585
|
mkdirSync11(configDir, { recursive: true });
|
|
16212
16586
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
16213
|
-
const previous =
|
|
16587
|
+
const previous = existsSync26(memPath) ? readFileSync20(memPath, "utf-8") : "";
|
|
16214
16588
|
const entry = `
|
|
16215
16589
|
- [${timestamp}] ${text}
|
|
16216
16590
|
`;
|
|
@@ -16222,7 +16596,7 @@ Add .md files to create commands.` });
|
|
|
16222
16596
|
}
|
|
16223
16597
|
memoryClear() {
|
|
16224
16598
|
const configDir = this.config.getConfigDir();
|
|
16225
|
-
const memPath =
|
|
16599
|
+
const memPath = join20(configDir, MEMORY_FILE_NAME);
|
|
16226
16600
|
try {
|
|
16227
16601
|
atomicWriteFileSync(memPath, "");
|
|
16228
16602
|
this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
|
|
@@ -16456,8 +16830,8 @@ async function setupProxy(configProxy) {
|
|
|
16456
16830
|
}
|
|
16457
16831
|
|
|
16458
16832
|
// src/web/auth.ts
|
|
16459
|
-
import { existsSync as
|
|
16460
|
-
import { join as
|
|
16833
|
+
import { existsSync as existsSync27, readFileSync as readFileSync21, writeFileSync as writeFileSync3, mkdirSync as mkdirSync12, readdirSync as readdirSync12, copyFileSync } from "fs";
|
|
16834
|
+
import { join as join21 } from "path";
|
|
16461
16835
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
16462
16836
|
var USERS_FILE = "users.json";
|
|
16463
16837
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
@@ -16472,7 +16846,7 @@ var AuthManager = class {
|
|
|
16472
16846
|
db;
|
|
16473
16847
|
constructor(baseDir) {
|
|
16474
16848
|
this.baseDir = baseDir;
|
|
16475
|
-
this.usersFile =
|
|
16849
|
+
this.usersFile = join21(baseDir, USERS_FILE);
|
|
16476
16850
|
this.db = this.loadOrCreate();
|
|
16477
16851
|
}
|
|
16478
16852
|
// ── Public API ─────────────────────────────────────────────────
|
|
@@ -16501,9 +16875,9 @@ var AuthManager = class {
|
|
|
16501
16875
|
}
|
|
16502
16876
|
const salt = randomBytes(16).toString("hex");
|
|
16503
16877
|
const passwordHash = this.hashPassword(password, salt);
|
|
16504
|
-
const dataDir =
|
|
16505
|
-
const fullDataDir =
|
|
16506
|
-
mkdirSync12(
|
|
16878
|
+
const dataDir = join21(USERS_DIR, username);
|
|
16879
|
+
const fullDataDir = join21(this.baseDir, dataDir);
|
|
16880
|
+
mkdirSync12(join21(fullDataDir, "history"), { recursive: true });
|
|
16507
16881
|
const user = {
|
|
16508
16882
|
username,
|
|
16509
16883
|
passwordHash,
|
|
@@ -16618,7 +16992,7 @@ var AuthManager = class {
|
|
|
16618
16992
|
getUserDataDir(username) {
|
|
16619
16993
|
const user = this.db.users.find((u) => u.username === username);
|
|
16620
16994
|
if (!user) throw new Error(`User not found: ${username}`);
|
|
16621
|
-
return
|
|
16995
|
+
return join21(this.baseDir, user.dataDir);
|
|
16622
16996
|
}
|
|
16623
16997
|
/** List all usernames */
|
|
16624
16998
|
listUsers() {
|
|
@@ -16654,30 +17028,30 @@ var AuthManager = class {
|
|
|
16654
17028
|
const err = this.register(username, password);
|
|
16655
17029
|
if (err) return err;
|
|
16656
17030
|
const userDir = this.getUserDataDir(username);
|
|
16657
|
-
const globalConfig =
|
|
16658
|
-
if (
|
|
17031
|
+
const globalConfig = join21(this.baseDir, "config.json");
|
|
17032
|
+
if (existsSync27(globalConfig)) {
|
|
16659
17033
|
try {
|
|
16660
|
-
const content =
|
|
16661
|
-
writeFileSync3(
|
|
17034
|
+
const content = readFileSync21(globalConfig, "utf-8");
|
|
17035
|
+
writeFileSync3(join21(userDir, "config.json"), content, "utf-8");
|
|
16662
17036
|
} catch {
|
|
16663
17037
|
}
|
|
16664
17038
|
}
|
|
16665
|
-
const globalMemory =
|
|
16666
|
-
if (
|
|
17039
|
+
const globalMemory = join21(this.baseDir, "memory.md");
|
|
17040
|
+
if (existsSync27(globalMemory)) {
|
|
16667
17041
|
try {
|
|
16668
|
-
const content =
|
|
16669
|
-
writeFileSync3(
|
|
17042
|
+
const content = readFileSync21(globalMemory, "utf-8");
|
|
17043
|
+
writeFileSync3(join21(userDir, "memory.md"), content, "utf-8");
|
|
16670
17044
|
} catch {
|
|
16671
17045
|
}
|
|
16672
17046
|
}
|
|
16673
|
-
const globalHistory =
|
|
16674
|
-
if (
|
|
17047
|
+
const globalHistory = join21(this.baseDir, "history");
|
|
17048
|
+
if (existsSync27(globalHistory)) {
|
|
16675
17049
|
try {
|
|
16676
|
-
const files =
|
|
16677
|
-
const userHistory =
|
|
17050
|
+
const files = readdirSync12(globalHistory).filter((f) => f.endsWith(".json"));
|
|
17051
|
+
const userHistory = join21(userDir, "history");
|
|
16678
17052
|
for (const f of files) {
|
|
16679
17053
|
try {
|
|
16680
|
-
copyFileSync(
|
|
17054
|
+
copyFileSync(join21(globalHistory, f), join21(userHistory, f));
|
|
16681
17055
|
} catch {
|
|
16682
17056
|
}
|
|
16683
17057
|
}
|
|
@@ -16688,9 +17062,9 @@ var AuthManager = class {
|
|
|
16688
17062
|
}
|
|
16689
17063
|
// ── Private methods ────────────────────────────────────────────
|
|
16690
17064
|
loadOrCreate() {
|
|
16691
|
-
if (
|
|
17065
|
+
if (existsSync27(this.usersFile)) {
|
|
16692
17066
|
try {
|
|
16693
|
-
return JSON.parse(
|
|
17067
|
+
return JSON.parse(readFileSync21(this.usersFile, "utf-8"));
|
|
16694
17068
|
} catch {
|
|
16695
17069
|
}
|
|
16696
17070
|
}
|
|
@@ -16807,8 +17181,8 @@ async function startWebServer(options = {}) {
|
|
|
16807
17181
|
}
|
|
16808
17182
|
}
|
|
16809
17183
|
let skillManager = null;
|
|
16810
|
-
const skillsDir =
|
|
16811
|
-
if (
|
|
17184
|
+
const skillsDir = join22(config.getConfigDir(), SKILLS_DIR_NAME);
|
|
17185
|
+
if (existsSync28(skillsDir)) {
|
|
16812
17186
|
skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
|
|
16813
17187
|
skillManager.loadSkills();
|
|
16814
17188
|
const count = skillManager.listSkills().length;
|
|
@@ -16879,18 +17253,18 @@ async function startWebServer(options = {}) {
|
|
|
16879
17253
|
next();
|
|
16880
17254
|
};
|
|
16881
17255
|
const moduleDir = getModuleDir();
|
|
16882
|
-
let clientDir =
|
|
16883
|
-
if (!
|
|
16884
|
-
clientDir =
|
|
17256
|
+
let clientDir = join22(moduleDir, "web", "client");
|
|
17257
|
+
if (!existsSync28(clientDir)) {
|
|
17258
|
+
clientDir = join22(moduleDir, "client");
|
|
16885
17259
|
}
|
|
16886
|
-
if (!
|
|
16887
|
-
clientDir =
|
|
17260
|
+
if (!existsSync28(clientDir)) {
|
|
17261
|
+
clientDir = join22(moduleDir, "..", "..", "src", "web", "client");
|
|
16888
17262
|
}
|
|
16889
|
-
if (!
|
|
16890
|
-
clientDir =
|
|
17263
|
+
if (!existsSync28(clientDir)) {
|
|
17264
|
+
clientDir = join22(process.cwd(), "src", "web", "client");
|
|
16891
17265
|
}
|
|
16892
17266
|
console.log(` Static files: ${clientDir}`);
|
|
16893
|
-
app.use("/vendor", express.static(
|
|
17267
|
+
app.use("/vendor", express.static(join22(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
|
|
16894
17268
|
app.use(express.static(clientDir));
|
|
16895
17269
|
app.get("/api/status", (_req, res) => {
|
|
16896
17270
|
res.json({
|
|
@@ -16957,7 +17331,7 @@ async function startWebServer(options = {}) {
|
|
|
16957
17331
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
16958
17332
|
const cwd = process.cwd();
|
|
16959
17333
|
const prefix = req.query.prefix || "";
|
|
16960
|
-
const targetDir =
|
|
17334
|
+
const targetDir = join22(cwd, prefix);
|
|
16961
17335
|
try {
|
|
16962
17336
|
const canonicalTarget = realpathSync(resolve9(targetDir));
|
|
16963
17337
|
const canonicalCwd = realpathSync(resolve9(cwd));
|
|
@@ -16971,10 +17345,10 @@ async function startWebServer(options = {}) {
|
|
|
16971
17345
|
}
|
|
16972
17346
|
try {
|
|
16973
17347
|
const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "dist-cjs", "release", "__pycache__", ".next", ".nuxt", "coverage", ".cache"]);
|
|
16974
|
-
const entries =
|
|
17348
|
+
const entries = readdirSync13(targetDir, { withFileTypes: true });
|
|
16975
17349
|
const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
|
|
16976
17350
|
name: e.name,
|
|
16977
|
-
path: relative4(cwd,
|
|
17351
|
+
path: relative4(cwd, join22(targetDir, e.name)).replace(/\\/g, "/"),
|
|
16978
17352
|
isDir: e.isDirectory()
|
|
16979
17353
|
}));
|
|
16980
17354
|
res.json({ files });
|
|
@@ -17010,8 +17384,8 @@ async function startWebServer(options = {}) {
|
|
|
17010
17384
|
try {
|
|
17011
17385
|
const authUser = req._authUser;
|
|
17012
17386
|
const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
|
|
17013
|
-
const filePath =
|
|
17014
|
-
if (!
|
|
17387
|
+
const filePath = join22(histDir, `${id}.json`);
|
|
17388
|
+
if (!existsSync28(filePath)) {
|
|
17015
17389
|
res.status(404).json({ error: "Session not found" });
|
|
17016
17390
|
return;
|
|
17017
17391
|
}
|
|
@@ -17026,7 +17400,7 @@ async function startWebServer(options = {}) {
|
|
|
17026
17400
|
res.status(404).json({ error: "Session not found" });
|
|
17027
17401
|
return;
|
|
17028
17402
|
}
|
|
17029
|
-
const data = JSON.parse(
|
|
17403
|
+
const data = JSON.parse(readFileSync22(filePath, "utf-8"));
|
|
17030
17404
|
res.json({ session: data });
|
|
17031
17405
|
} catch (err) {
|
|
17032
17406
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
@@ -17039,7 +17413,7 @@ async function startWebServer(options = {}) {
|
|
|
17039
17413
|
return;
|
|
17040
17414
|
}
|
|
17041
17415
|
const cwd = process.cwd();
|
|
17042
|
-
const fullPath = resolve9(
|
|
17416
|
+
const fullPath = resolve9(join22(cwd, filePath));
|
|
17043
17417
|
try {
|
|
17044
17418
|
const canonicalFull = realpathSync(fullPath);
|
|
17045
17419
|
const canonicalCwd = realpathSync(resolve9(cwd));
|
|
@@ -17057,7 +17431,7 @@ async function startWebServer(options = {}) {
|
|
|
17057
17431
|
res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
|
|
17058
17432
|
return;
|
|
17059
17433
|
}
|
|
17060
|
-
const content =
|
|
17434
|
+
const content = readFileSync22(fullPath, "utf-8");
|
|
17061
17435
|
res.json({ content, size: stat.size });
|
|
17062
17436
|
} catch {
|
|
17063
17437
|
res.json({ error: "Cannot read file" });
|
|
@@ -17312,17 +17686,17 @@ function resolveProjectMcpPath() {
|
|
|
17312
17686
|
const cwd = process.cwd();
|
|
17313
17687
|
const gitRoot = getGitRoot(cwd);
|
|
17314
17688
|
const projectRoot = gitRoot ?? cwd;
|
|
17315
|
-
const configPath =
|
|
17316
|
-
return
|
|
17689
|
+
const configPath = join22(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
17690
|
+
return existsSync28(configPath) ? configPath : null;
|
|
17317
17691
|
}
|
|
17318
17692
|
function loadProjectMcpConfig() {
|
|
17319
17693
|
const cwd = process.cwd();
|
|
17320
17694
|
const gitRoot = getGitRoot(cwd);
|
|
17321
17695
|
const projectRoot = gitRoot ?? cwd;
|
|
17322
|
-
const configPath =
|
|
17323
|
-
if (!
|
|
17696
|
+
const configPath = join22(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
17697
|
+
if (!existsSync28(configPath)) return null;
|
|
17324
17698
|
try {
|
|
17325
|
-
const raw = JSON.parse(
|
|
17699
|
+
const raw = JSON.parse(readFileSync22(configPath, "utf-8"));
|
|
17326
17700
|
return raw.mcpServers ?? raw;
|
|
17327
17701
|
} catch {
|
|
17328
17702
|
return null;
|