jinzd-ai-cli 0.4.216 → 0.4.218
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 +6 -3
- package/dist/{batch-C6HCXZIG.js → batch-7V7OTMUP.js} +2 -2
- package/dist/{chat-index-R2E27VXN.js → chat-index-PS274XM7.js} +1 -1
- package/dist/{chunk-TJ6GGN4B.js → chunk-5CA2TJ5F.js} +1 -1
- package/dist/{chunk-OLN7VUZA.js → chunk-C2Z42DI5.js} +3 -1
- package/dist/{chunk-TCOC4AUI.js → chunk-GX3HSGJX.js} +690 -169
- package/dist/{chunk-PNTLA3MN.js → chunk-H2UIHGHH.js} +23 -22
- package/dist/{chunk-GY3C3C2Y.js → chunk-L4UREAID.js} +3 -3
- package/dist/{chunk-77HDCGTN.js → chunk-MWKE2TNS.js} +1 -1
- package/dist/{chunk-7YWS3JLU.js → chunk-NTCB7CMT.js} +3 -1
- package/dist/{chunk-6HLKCSB3.js → chunk-OUC75QCF.js} +1 -1
- package/dist/{chunk-WAI3WPV2.js → chunk-P4VBLXKS.js} +1 -1
- package/dist/{chunk-VW7Y27WW.js → chunk-VGFTM3XT.js} +1 -1
- package/dist/{chunk-OQGVGPEK.js → chunk-VHY6NVMQ.js} +2 -0
- package/dist/{ci-6ZTFO3LX.js → ci-L6GH2WVC.js} +4 -4
- package/dist/{ci-format-CLZ6QJRL.js → ci-format-WW7454AY.js} +2 -2
- package/dist/{constants-APSORFOH.js → constants-NCTFSHDU.js} +3 -1
- package/dist/{doctor-cli-GUIX4X5F.js → doctor-cli-EWMFBP5Q.js} +4 -4
- package/dist/electron-server.js +965 -377
- package/dist/{hub-ZGHQWNWE.js → hub-CDL6T7CP.js} +1 -1
- package/dist/index.js +259 -97
- package/dist/{pr-JOL3IAGV.js → pr-D6PEKEGK.js} +4 -4
- package/dist/{run-tests-4PKSIVK5.js → run-tests-NXVVKAK2.js} +1 -1
- package/dist/{run-tests-ZRK4TQUN.js → run-tests-SWU2XEV7.js} +2 -2
- package/dist/{server-FXUF5P64.js → server-LHYSS6CK.js} +207 -100
- package/dist/{server-O3XHT56X.js → server-WUT7VYTD.js} +5 -4
- package/dist/{task-orchestrator-TUMDTOAX.js → task-orchestrator-C5AA2BI5.js} +5 -4
- package/dist/{usage-X7MJX4YD.js → usage-6ZUUJBI2.js} +2 -2
- package/dist/web/client/app.js +53 -2
- package/dist/web/client/index.html +11 -1
- package/package.json +1 -1
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
scanDirTree,
|
|
25
25
|
scanProject,
|
|
26
26
|
setupProxy
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-H2UIHGHH.js";
|
|
28
28
|
import {
|
|
29
29
|
buildReviewPrompt,
|
|
30
30
|
buildSecurityReviewPrompt
|
|
@@ -32,21 +32,36 @@ import {
|
|
|
32
32
|
import {
|
|
33
33
|
ToolExecutor,
|
|
34
34
|
ToolRegistry,
|
|
35
|
+
addMemoryEntry,
|
|
35
36
|
askUserContext,
|
|
36
37
|
checkNetworkPolicy,
|
|
37
38
|
checkPermissionWithProfile,
|
|
38
39
|
cleanupRejectedTeeFile,
|
|
39
40
|
clearRecentlyDeniedAutoActions,
|
|
40
41
|
defaultActionClassifier,
|
|
42
|
+
deleteMemoryEntry,
|
|
43
|
+
describePlugin,
|
|
41
44
|
estimateTokens,
|
|
45
|
+
expireMemoryEntry,
|
|
46
|
+
exportMemoryEntries,
|
|
47
|
+
formatMemoryForPrompt,
|
|
42
48
|
formatPermissionProfileWarning,
|
|
49
|
+
getActivePluginAssets,
|
|
50
|
+
getInstalledPlugin,
|
|
43
51
|
getPendingHookTrust,
|
|
44
52
|
getPreferredAgentName,
|
|
45
53
|
getRecentlyDeniedAutoActions,
|
|
46
54
|
googleSearchContext,
|
|
47
55
|
groupCallsByPhase,
|
|
56
|
+
installPlugin,
|
|
57
|
+
isMemoryExpired,
|
|
48
58
|
listAgentConfigs,
|
|
49
59
|
listAgentRuns,
|
|
60
|
+
listInstalledPlugins,
|
|
61
|
+
listMemoryEntries,
|
|
62
|
+
memoryStorePath,
|
|
63
|
+
mergePluginHooks,
|
|
64
|
+
pluginRoot,
|
|
50
65
|
recordRecentlyDeniedAutoAction,
|
|
51
66
|
renderDiff,
|
|
52
67
|
requestAgentStop,
|
|
@@ -57,14 +72,17 @@ import {
|
|
|
57
72
|
runWithSessionKey,
|
|
58
73
|
setContextWindow,
|
|
59
74
|
setMaxOutputCap,
|
|
75
|
+
setPluginEnabled,
|
|
60
76
|
setPreferredAgentName,
|
|
61
77
|
spawnAgentContext,
|
|
62
78
|
truncateOutput,
|
|
63
|
-
|
|
64
|
-
|
|
79
|
+
trustPlugin,
|
|
80
|
+
undoStack,
|
|
81
|
+
updateMemoryApproval
|
|
82
|
+
} from "./chunk-GX3HSGJX.js";
|
|
65
83
|
import "./chunk-T2NL5ZIA.js";
|
|
66
84
|
import "./chunk-BXP6YZ2P.js";
|
|
67
|
-
import "./chunk-
|
|
85
|
+
import "./chunk-VGFTM3XT.js";
|
|
68
86
|
import {
|
|
69
87
|
SessionManager,
|
|
70
88
|
getContentText
|
|
@@ -78,7 +96,7 @@ import {
|
|
|
78
96
|
} from "./chunk-QMXC327F.js";
|
|
79
97
|
import {
|
|
80
98
|
runTool
|
|
81
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-MWKE2TNS.js";
|
|
82
100
|
import {
|
|
83
101
|
CONTENT_ONLY_STREAM_REMINDER,
|
|
84
102
|
TEE_FINAL_USER_NUDGE,
|
|
@@ -97,7 +115,7 @@ import {
|
|
|
97
115
|
} from "./chunk-XPBEJB27.js";
|
|
98
116
|
import {
|
|
99
117
|
ConfigManager
|
|
100
|
-
} from "./chunk-
|
|
118
|
+
} from "./chunk-P4VBLXKS.js";
|
|
101
119
|
import "./chunk-TZQHYZKT.js";
|
|
102
120
|
import {
|
|
103
121
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -109,14 +127,13 @@ import {
|
|
|
109
127
|
DESCRIPTION,
|
|
110
128
|
MCP_PROJECT_CONFIG_NAME,
|
|
111
129
|
MEMORY_FILE_NAME,
|
|
112
|
-
MEMORY_MAX_CHARS,
|
|
113
130
|
PLAN_MODE_READONLY_TOOLS,
|
|
114
131
|
PLAN_MODE_SYSTEM_ADDON,
|
|
115
132
|
PLUGINS_DIR_NAME,
|
|
116
133
|
SKILLS_DIR_NAME,
|
|
117
134
|
VERSION,
|
|
118
135
|
buildUserIdentityPrompt
|
|
119
|
-
} from "./chunk-
|
|
136
|
+
} from "./chunk-NTCB7CMT.js";
|
|
120
137
|
import {
|
|
121
138
|
formatGitContextForPrompt,
|
|
122
139
|
getGitContext,
|
|
@@ -138,8 +155,8 @@ import {
|
|
|
138
155
|
import express from "express";
|
|
139
156
|
import { createServer } from "http";
|
|
140
157
|
import { WebSocketServer } from "ws";
|
|
141
|
-
import { join as
|
|
142
|
-
import { existsSync as
|
|
158
|
+
import { join as join2, dirname as dirname2, resolve as resolve2, relative, sep } from "path";
|
|
159
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, realpathSync } from "fs";
|
|
143
160
|
import { networkInterfaces } from "os";
|
|
144
161
|
|
|
145
162
|
// src/web/tool-executor-web.ts
|
|
@@ -589,8 +606,6 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
589
606
|
};
|
|
590
607
|
|
|
591
608
|
// src/core/system-prompt-builder.ts
|
|
592
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
593
|
-
import { join } from "path";
|
|
594
609
|
function buildSystemPromptParts(ctx) {
|
|
595
610
|
const now = /* @__PURE__ */ new Date();
|
|
596
611
|
const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
@@ -644,24 +659,12 @@ ${ctx.activeSkill.content}`);
|
|
|
644
659
|
return { stable: stableParts.join("\n\n---\n\n"), volatile };
|
|
645
660
|
}
|
|
646
661
|
function loadMemoryContent(configDir) {
|
|
647
|
-
|
|
648
|
-
if (!existsSync2(memoryPath)) return null;
|
|
649
|
-
let content = readFileSync2(memoryPath, "utf-8").trim();
|
|
650
|
-
if (!content) return null;
|
|
651
|
-
if (content.length > MEMORY_MAX_CHARS) {
|
|
652
|
-
content = content.slice(-MEMORY_MAX_CHARS);
|
|
653
|
-
const firstEntry = content.indexOf("\n## ");
|
|
654
|
-
if (firstEntry !== -1) {
|
|
655
|
-
content = content.slice(firstEntry + 1);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
const entryCount = (content.match(/^## \d{4}-\d{2}-\d{2}/gm) ?? []).length;
|
|
659
|
-
return { content, entryCount };
|
|
662
|
+
return formatMemoryForPrompt(configDir, process.cwd());
|
|
660
663
|
}
|
|
661
664
|
|
|
662
665
|
// src/web/session-handler.ts
|
|
663
|
-
import { existsSync as
|
|
664
|
-
import { join
|
|
666
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync, readdirSync, statSync, createWriteStream } from "fs";
|
|
667
|
+
import { join, resolve, dirname } from "path";
|
|
665
668
|
function lastAssistantText(messages) {
|
|
666
669
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
667
670
|
const m = messages[i];
|
|
@@ -754,7 +757,7 @@ var SessionHandler = class {
|
|
|
754
757
|
}
|
|
755
758
|
this.updateContextWindow();
|
|
756
759
|
this.activeSystemPrompt = this.loadContextFiles();
|
|
757
|
-
const hooks = this.config.get("hooks");
|
|
760
|
+
const hooks = mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir());
|
|
758
761
|
const permissionRules = this.config.get("permissionRules");
|
|
759
762
|
const defaultPermission = this.config.get("defaultPermission");
|
|
760
763
|
const permissionProfileName = this.config.get("defaultPermissionProfile") ?? "legacy";
|
|
@@ -867,6 +870,8 @@ var SessionHandler = class {
|
|
|
867
870
|
return this.handleMemoryStatus();
|
|
868
871
|
case "memory_rebuild":
|
|
869
872
|
return this.handleMemoryRebuild(Boolean(msg.full));
|
|
873
|
+
case "memory_manage":
|
|
874
|
+
return this.handleMemoryManage(msg.action, msg.id, msg.expiresAt);
|
|
870
875
|
case "auto_pause_response": {
|
|
871
876
|
const resolve3 = this.pendingAutoPause.get(msg.requestId);
|
|
872
877
|
if (resolve3) {
|
|
@@ -899,7 +904,7 @@ var SessionHandler = class {
|
|
|
899
904
|
}
|
|
900
905
|
}
|
|
901
906
|
onDisconnect() {
|
|
902
|
-
runLifecycleHooks(this.config.get("hooks") ?? void 0, "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
|
|
907
|
+
runLifecycleHooks(mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()), "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
|
|
903
908
|
this.toolExecutor.cancelAll();
|
|
904
909
|
if (this.abortController) this.abortController.abort();
|
|
905
910
|
for (const resolve3 of this.pendingAskUser.values()) resolve3(null);
|
|
@@ -1078,7 +1083,7 @@ var SessionHandler = class {
|
|
|
1078
1083
|
this.ensureSession();
|
|
1079
1084
|
const session = this.sessions.current;
|
|
1080
1085
|
const promptDecisions = runLifecycleHooks(
|
|
1081
|
-
this.config.get("hooks") ?? void 0,
|
|
1086
|
+
mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()),
|
|
1082
1087
|
"UserPromptSubmit",
|
|
1083
1088
|
{ sessionId: session.id, prompt: content },
|
|
1084
1089
|
{ configDir: this.config.getConfigDir(), onSummary: (message) => this.send({ type: "info", message: "[hook] " + message }) }
|
|
@@ -1920,6 +1925,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
|
|
|
1920
1925
|
" /add-dir [path|remove] \u2014 Add/remove directory from AI context",
|
|
1921
1926
|
" /mcp [reconnect] \u2014 Show/manage MCP servers",
|
|
1922
1927
|
" /plugins \u2014 Show loaded plugins",
|
|
1928
|
+
" /plugin <cmd> \u2014 Manage package plugins",
|
|
1923
1929
|
" /commands \u2014 List custom commands",
|
|
1924
1930
|
" /doctor \u2014 Health check (API keys, config, MCP)",
|
|
1925
1931
|
" /bug \u2014 Generate bug report template",
|
|
@@ -2018,7 +2024,7 @@ ${lines.join("\n")}` });
|
|
|
2018
2024
|
this.sendToolsList();
|
|
2019
2025
|
this.sendStatus();
|
|
2020
2026
|
} else if (sub === "reload") {
|
|
2021
|
-
this.skillManager.loadSkills();
|
|
2027
|
+
this.skillManager.loadSkills(getActivePluginAssets(this.config.getConfigDir()).skillDirs);
|
|
2022
2028
|
this.send({ type: "info", message: `\u{1F504} Reloaded ${this.skillManager.listSkills().length} skill(s).` });
|
|
2023
2029
|
this.sendToolsList();
|
|
2024
2030
|
} else {
|
|
@@ -2237,9 +2243,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2237
2243
|
let modifiedFiles = 0;
|
|
2238
2244
|
const diffLines = [];
|
|
2239
2245
|
for (const [filePath, { earliest }] of fileMap) {
|
|
2240
|
-
const currentContent =
|
|
2246
|
+
const currentContent = existsSync2(filePath) ? (() => {
|
|
2241
2247
|
try {
|
|
2242
|
-
return
|
|
2248
|
+
return readFileSync2(filePath, "utf-8");
|
|
2243
2249
|
} catch {
|
|
2244
2250
|
return null;
|
|
2245
2251
|
}
|
|
@@ -2755,7 +2761,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2755
2761
|
case "test": {
|
|
2756
2762
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
2757
2763
|
try {
|
|
2758
|
-
const { executeTests } = await import("./run-tests-
|
|
2764
|
+
const { executeTests } = await import("./run-tests-SWU2XEV7.js");
|
|
2759
2765
|
const argStr = args.join(" ").trim();
|
|
2760
2766
|
let testArgs = {};
|
|
2761
2767
|
if (argStr) {
|
|
@@ -2772,9 +2778,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2772
2778
|
// ── /init ───────────────────────────────────────────────────────
|
|
2773
2779
|
case "init": {
|
|
2774
2780
|
const cwd = process.cwd();
|
|
2775
|
-
const targetPath =
|
|
2781
|
+
const targetPath = join(cwd, "AICLI.md");
|
|
2776
2782
|
const force = args.includes("--force");
|
|
2777
|
-
if (
|
|
2783
|
+
if (existsSync2(targetPath) && !force) {
|
|
2778
2784
|
this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
|
|
2779
2785
|
Use /init --force to overwrite.` });
|
|
2780
2786
|
break;
|
|
@@ -2807,7 +2813,7 @@ Use /context reload to load it.` });
|
|
|
2807
2813
|
lines.push("**Config Files:**");
|
|
2808
2814
|
lines.push(` Dir: ${configDir}`);
|
|
2809
2815
|
const checkFile = (label, filePath) => {
|
|
2810
|
-
const exists =
|
|
2816
|
+
const exists = existsSync2(filePath);
|
|
2811
2817
|
let extra = "";
|
|
2812
2818
|
if (exists) {
|
|
2813
2819
|
try {
|
|
@@ -2817,9 +2823,9 @@ Use /context reload to load it.` });
|
|
|
2817
2823
|
}
|
|
2818
2824
|
lines.push(` ${exists ? "\u2713" : "\u2013"} ${label.padEnd(14)} ${exists ? filePath + extra : "(not found)"}`);
|
|
2819
2825
|
};
|
|
2820
|
-
checkFile("config.json",
|
|
2821
|
-
checkFile("memory.md",
|
|
2822
|
-
checkFile("dev-state.md",
|
|
2826
|
+
checkFile("config.json", join(configDir, "config.json"));
|
|
2827
|
+
checkFile("memory.md", join(configDir, MEMORY_FILE_NAME));
|
|
2828
|
+
checkFile("dev-state.md", join(configDir, "dev-state.md"));
|
|
2823
2829
|
lines.push("");
|
|
2824
2830
|
if (this.mcpManager) {
|
|
2825
2831
|
lines.push("**MCP Servers:**");
|
|
@@ -2922,7 +2928,7 @@ ${this.config.toFormattedJSON()}
|
|
|
2922
2928
|
const result = this.contextLoadResult;
|
|
2923
2929
|
const layers = ["\u{1F4DA} **Context Layers:**", ""];
|
|
2924
2930
|
if (result && result.layers.length > 0) {
|
|
2925
|
-
const labels = { global: "Global ", project: "Project", local: "Local " };
|
|
2931
|
+
const labels = { global: "Global ", project: "Project", plugin: "Plugin ", local: "Local " };
|
|
2926
2932
|
for (const layer of result.layers) {
|
|
2927
2933
|
layers.push(` \u2713 ${labels[layer.level] ?? layer.level}: ${layer.displayPath} (${layer.charCount} chars${layer.truncated ? ", truncated" : ""})`);
|
|
2928
2934
|
}
|
|
@@ -3007,7 +3013,7 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
|
|
|
3007
3013
|
break;
|
|
3008
3014
|
}
|
|
3009
3015
|
const dirPath = resolve(sub);
|
|
3010
|
-
if (!
|
|
3016
|
+
if (!existsSync2(dirPath)) {
|
|
3011
3017
|
this.send({ type: "error", message: `Directory not found: ${dirPath}` });
|
|
3012
3018
|
break;
|
|
3013
3019
|
}
|
|
@@ -3023,8 +3029,8 @@ It will be included in AI context for subsequent messages.` });
|
|
|
3023
3029
|
// ── /commands ───────────────────────────────────────────────────
|
|
3024
3030
|
case "commands": {
|
|
3025
3031
|
const configDir = this.config.getConfigDir();
|
|
3026
|
-
const commandsDir =
|
|
3027
|
-
if (!
|
|
3032
|
+
const commandsDir = join(configDir, CUSTOM_COMMANDS_DIR_NAME);
|
|
3033
|
+
if (!existsSync2(commandsDir)) {
|
|
3028
3034
|
this.send({ type: "info", message: `No custom commands directory.
|
|
3029
3035
|
Create: ${commandsDir}/ with .md files.` });
|
|
3030
3036
|
break;
|
|
@@ -3047,23 +3053,75 @@ Add .md files to create commands.` });
|
|
|
3047
3053
|
}
|
|
3048
3054
|
break;
|
|
3049
3055
|
}
|
|
3050
|
-
// ── /plugins
|
|
3056
|
+
// ── /plugins / /plugin ──────────────────────────────────────────
|
|
3051
3057
|
case "plugins": {
|
|
3052
3058
|
const configDir = this.config.getConfigDir();
|
|
3053
|
-
const pluginsDir =
|
|
3059
|
+
const pluginsDir = join(configDir, PLUGINS_DIR_NAME);
|
|
3054
3060
|
const pluginTools = this.toolRegistry.listPluginTools();
|
|
3061
|
+
const pluginPackages = listInstalledPlugins(configDir);
|
|
3055
3062
|
const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
|
|
3056
|
-
if (pluginTools.length === 0) {
|
|
3063
|
+
if (pluginTools.length === 0 && pluginPackages.length === 0) {
|
|
3057
3064
|
lines.push("No plugins loaded.");
|
|
3058
|
-
lines.push("
|
|
3065
|
+
lines.push("Use /plugin install <path>, /plugin trust <name>, then /plugin enable <name>.");
|
|
3059
3066
|
} else {
|
|
3060
|
-
|
|
3061
|
-
lines.push(`
|
|
3067
|
+
if (pluginPackages.length > 0) {
|
|
3068
|
+
lines.push(`Package plugins (${pluginPackages.length}):`);
|
|
3069
|
+
for (const p of pluginPackages) lines.push(` \u2022 ${p.name}@${p.manifest.version} \u2014 ${p.enabled ? "enabled" : "disabled"} / ${p.trusted ? "trusted" : "untrusted"}`);
|
|
3070
|
+
lines.push("");
|
|
3071
|
+
}
|
|
3072
|
+
if (pluginTools.length > 0) {
|
|
3073
|
+
lines.push(`Legacy JS plugin tools (${pluginTools.length}):`);
|
|
3074
|
+
for (const t of pluginTools) lines.push(` \u2022 ${t.definition.name} \u2014 ${t.definition.description}`);
|
|
3062
3075
|
}
|
|
3063
3076
|
}
|
|
3064
3077
|
this.send({ type: "info", message: lines.join("\n") });
|
|
3065
3078
|
break;
|
|
3066
3079
|
}
|
|
3080
|
+
case "plugin": {
|
|
3081
|
+
const configDir = this.config.getConfigDir();
|
|
3082
|
+
const sub = args[0] ?? "list";
|
|
3083
|
+
try {
|
|
3084
|
+
if (sub === "list") {
|
|
3085
|
+
const plugins = listInstalledPlugins(configDir);
|
|
3086
|
+
const lines = [`\u{1F50C} **Package Plugins**`, `Dir: ${pluginRoot(configDir)}`, ""];
|
|
3087
|
+
if (plugins.length === 0) lines.push("No package plugins installed.");
|
|
3088
|
+
for (const p of plugins) lines.push(` \u2022 ${p.name}@${p.manifest.version} \u2014 ${p.enabled ? "enabled" : "disabled"} / ${p.trusted ? "trusted" : "untrusted"}`);
|
|
3089
|
+
this.send({ type: "info", message: lines.join("\n") });
|
|
3090
|
+
break;
|
|
3091
|
+
}
|
|
3092
|
+
if (sub === "install") {
|
|
3093
|
+
const input = args[1];
|
|
3094
|
+
if (!input) throw new Error("Usage: /plugin install <path>");
|
|
3095
|
+
const plugin = installPlugin(configDir, input);
|
|
3096
|
+
this.send({ type: "info", message: `Installed plugin ${plugin.name}@${plugin.manifest.version}. Run /plugin trust ${plugin.name}, then /plugin enable ${plugin.name}.` });
|
|
3097
|
+
break;
|
|
3098
|
+
}
|
|
3099
|
+
if (sub === "trust") {
|
|
3100
|
+
const name2 = args[1];
|
|
3101
|
+
if (!name2) throw new Error("Usage: /plugin trust <name>");
|
|
3102
|
+
const state = trustPlugin(configDir, name2);
|
|
3103
|
+
this.send({ type: "info", message: `Trusted plugin ${state.name}.` });
|
|
3104
|
+
break;
|
|
3105
|
+
}
|
|
3106
|
+
if (sub === "enable" || sub === "disable") {
|
|
3107
|
+
const name2 = args[1];
|
|
3108
|
+
if (!name2) throw new Error(`Usage: /plugin ${sub} <name>`);
|
|
3109
|
+
const state = setPluginEnabled(configDir, name2, sub === "enable");
|
|
3110
|
+
this.send({ type: "info", message: `${sub === "enable" ? "Enabled" : "Disabled"} plugin ${state.name}. Restart Web UI for changes to take effect.` });
|
|
3111
|
+
break;
|
|
3112
|
+
}
|
|
3113
|
+
if (sub === "inspect") {
|
|
3114
|
+
const name2 = args[1];
|
|
3115
|
+
if (!name2) throw new Error("Usage: /plugin inspect <name>");
|
|
3116
|
+
this.send({ type: "info", message: describePlugin(getInstalledPlugin(configDir, name2)).join("\n") });
|
|
3117
|
+
break;
|
|
3118
|
+
}
|
|
3119
|
+
throw new Error("Usage: /plugin [list|install|enable|disable|inspect|trust]");
|
|
3120
|
+
} catch (err) {
|
|
3121
|
+
this.send({ type: "error", message: err instanceof Error ? err.message : String(err) });
|
|
3122
|
+
}
|
|
3123
|
+
break;
|
|
3124
|
+
}
|
|
3067
3125
|
// ── /bug ────────────────────────────────────────────────────────
|
|
3068
3126
|
case "bug": {
|
|
3069
3127
|
const session = this.sessions.current;
|
|
@@ -3106,7 +3164,7 @@ Add .md files to create commands.` });
|
|
|
3106
3164
|
return;
|
|
3107
3165
|
}
|
|
3108
3166
|
const preDecisions = runLifecycleHooks(
|
|
3109
|
-
this.config.get("hooks") ?? void 0,
|
|
3167
|
+
mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()),
|
|
3110
3168
|
"PreCompact",
|
|
3111
3169
|
{ sessionId: session.id, messageCount: session.messages.length, instruction },
|
|
3112
3170
|
{ configDir: this.config.getConfigDir(), onSummary: (message) => this.send({ type: "info", message: "[hook] " + message }) }
|
|
@@ -3143,7 +3201,7 @@ Add .md files to create commands.` });
|
|
|
3143
3201
|
const removedCount = session.compact(summaryMsg, ackMsg, keepLast);
|
|
3144
3202
|
const afterCount = session.messages.length;
|
|
3145
3203
|
runLifecycleHooks(
|
|
3146
|
-
this.config.get("hooks") ?? void 0,
|
|
3204
|
+
mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()),
|
|
3147
3205
|
"PostCompact",
|
|
3148
3206
|
{ sessionId: session.id, beforeCount, afterCount, removedCount, keepLast, instruction },
|
|
3149
3207
|
{ configDir: this.config.getConfigDir(), onSummary: (message) => this.send({ type: "info", message: "[hook] " + message }) }
|
|
@@ -3247,48 +3305,95 @@ Add .md files to create commands.` });
|
|
|
3247
3305
|
}
|
|
3248
3306
|
memoryShow() {
|
|
3249
3307
|
const configDir = this.config.getConfigDir();
|
|
3250
|
-
const
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
`);
|
|
3259
|
-
}
|
|
3308
|
+
const entries = listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
|
|
3309
|
+
const content = entries.length === 0 ? "(empty \u2014 no persistent memory entries yet)" : entries.map((entry) => {
|
|
3310
|
+
const status = isMemoryExpired(entry) ? "expired" : entry.approved ? "approved" : "pending";
|
|
3311
|
+
const source = entry.sourceSession ? `session ${entry.sourceSession.slice(0, 8)}` : entry.sourceProject ? `project ${entry.sourceProject}` : "manual/tool";
|
|
3312
|
+
return `## ${entry.id.slice(0, 8)} \xB7 ${status} \xB7 scope:${entry.scope} \xB7 sensitivity:${entry.sensitivity}
|
|
3313
|
+
Source: ${source}
|
|
3314
|
+
${entry.content}`;
|
|
3315
|
+
}).join("\n\n");
|
|
3260
3316
|
this.send({
|
|
3261
3317
|
type: "memory_content",
|
|
3262
|
-
content
|
|
3263
|
-
filePath:
|
|
3318
|
+
content,
|
|
3319
|
+
filePath: memoryStorePath(configDir)
|
|
3264
3320
|
});
|
|
3321
|
+
this.sendMemoryEntries();
|
|
3265
3322
|
}
|
|
3266
3323
|
memoryAdd(text) {
|
|
3267
|
-
const configDir = this.config.getConfigDir();
|
|
3268
|
-
const memPath = join2(configDir, MEMORY_FILE_NAME);
|
|
3269
3324
|
try {
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
this.
|
|
3325
|
+
const entry = addMemoryEntry(this.config.getConfigDir(), text, {
|
|
3326
|
+
scope: "personal",
|
|
3327
|
+
cwd: process.cwd(),
|
|
3328
|
+
redact: true
|
|
3329
|
+
});
|
|
3330
|
+
const status = entry.approved ? "approved" : "pending approval";
|
|
3331
|
+
this.send({ type: "info", message: `\u{1F4DD} Memory entry ${entry.id.slice(0, 8)} added (${status}).` });
|
|
3332
|
+
this.sendMemoryEntries();
|
|
3278
3333
|
} catch (err) {
|
|
3279
3334
|
this.send({ type: "error", message: `Failed to write memory: ${err.message}` });
|
|
3280
3335
|
}
|
|
3281
3336
|
}
|
|
3282
3337
|
memoryClear() {
|
|
3283
3338
|
const configDir = this.config.getConfigDir();
|
|
3284
|
-
const memPath = join2(configDir, MEMORY_FILE_NAME);
|
|
3285
3339
|
try {
|
|
3286
|
-
atomicWriteFileSync(
|
|
3340
|
+
atomicWriteFileSync(memoryStorePath(configDir), "");
|
|
3341
|
+
atomicWriteFileSync(join(configDir, MEMORY_FILE_NAME), "");
|
|
3287
3342
|
this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
|
|
3343
|
+
this.sendMemoryEntries();
|
|
3288
3344
|
} catch (err) {
|
|
3289
3345
|
this.send({ type: "error", message: `Failed to clear memory: ${err.message}` });
|
|
3290
3346
|
}
|
|
3291
3347
|
}
|
|
3348
|
+
sendMemoryEntries() {
|
|
3349
|
+
const configDir = this.config.getConfigDir();
|
|
3350
|
+
const entries = listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
|
|
3351
|
+
this.send({
|
|
3352
|
+
type: "memory_entries",
|
|
3353
|
+
storePath: memoryStorePath(configDir),
|
|
3354
|
+
entries: entries.map((entry) => ({
|
|
3355
|
+
id: entry.id,
|
|
3356
|
+
content: entry.content,
|
|
3357
|
+
scope: entry.scope,
|
|
3358
|
+
sourceSession: entry.sourceSession,
|
|
3359
|
+
sourceProject: entry.sourceProject,
|
|
3360
|
+
createdAt: entry.createdAt,
|
|
3361
|
+
updatedAt: entry.updatedAt,
|
|
3362
|
+
expiresAt: entry.expiresAt,
|
|
3363
|
+
sensitivity: entry.sensitivity,
|
|
3364
|
+
approved: entry.approved,
|
|
3365
|
+
expired: isMemoryExpired(entry),
|
|
3366
|
+
redactedKinds: entry.redactedKinds
|
|
3367
|
+
}))
|
|
3368
|
+
});
|
|
3369
|
+
}
|
|
3370
|
+
handleMemoryManage(action, id, expiresAt) {
|
|
3371
|
+
const configDir = this.config.getConfigDir();
|
|
3372
|
+
try {
|
|
3373
|
+
if (action === "approve" || action === "reject") {
|
|
3374
|
+
if (!id) throw new Error(`${action} requires id`);
|
|
3375
|
+
updateMemoryApproval(configDir, id, action === "approve");
|
|
3376
|
+
} else if (action === "delete") {
|
|
3377
|
+
if (!id) throw new Error("delete requires id");
|
|
3378
|
+
deleteMemoryEntry(configDir, id);
|
|
3379
|
+
} else if (action === "expire") {
|
|
3380
|
+
if (!id) throw new Error("expire requires id");
|
|
3381
|
+
expireMemoryEntry(configDir, id, expiresAt);
|
|
3382
|
+
} else if (action === "export") {
|
|
3383
|
+
this.send({
|
|
3384
|
+
type: "export_data",
|
|
3385
|
+
format: "json",
|
|
3386
|
+
filename: `aicli-memory-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`,
|
|
3387
|
+
content: exportMemoryEntries(configDir, process.cwd())
|
|
3388
|
+
});
|
|
3389
|
+
} else if (action !== "list") {
|
|
3390
|
+
throw new Error(`unknown memory action: ${action}`);
|
|
3391
|
+
}
|
|
3392
|
+
this.sendMemoryEntries();
|
|
3393
|
+
} catch (err) {
|
|
3394
|
+
this.send({ type: "error", message: `Memory management failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3292
3397
|
// ── B4 chat memory recall (v0.4.90+) ──────────────────────────────
|
|
3293
3398
|
// Lazy-imported so the 117 MB embedder stays out of the load path for
|
|
3294
3399
|
// clients that never open the Memory panel.
|
|
@@ -3548,6 +3653,8 @@ async function startWebServer(options = {}) {
|
|
|
3548
3653
|
let mcpManager = null;
|
|
3549
3654
|
const mcpEnabled = config.get("mcpEnabled") !== false;
|
|
3550
3655
|
const globalMcpServers = mcpEnabled ? config.get("mcpServers") ?? {} : {};
|
|
3656
|
+
const pluginAssets = getActivePluginAssets(config.getConfigDir());
|
|
3657
|
+
const pluginMcpServers = mcpEnabled ? pluginAssets.mcpServers : {};
|
|
3551
3658
|
const projectMcpResolved = mcpEnabled ? resolveProjectMcpPath() : null;
|
|
3552
3659
|
let projectMcpServers = {};
|
|
3553
3660
|
if (projectMcpResolved) {
|
|
@@ -3560,7 +3667,7 @@ async function startWebServer(options = {}) {
|
|
|
3560
3667
|
console.log(` Skipped to prevent unintended RCE. Run "aicli" then "/mcp trust-project" to approve.`);
|
|
3561
3668
|
}
|
|
3562
3669
|
}
|
|
3563
|
-
const mergedMcpServers = { ...globalMcpServers, ...projectMcpServers };
|
|
3670
|
+
const mergedMcpServers = { ...globalMcpServers, ...pluginMcpServers, ...projectMcpServers };
|
|
3564
3671
|
if (!mcpEnabled) {
|
|
3565
3672
|
console.log(" \u{1F50C} MCP: disabled (config.mcpEnabled=false)");
|
|
3566
3673
|
}
|
|
@@ -3578,10 +3685,10 @@ async function startWebServer(options = {}) {
|
|
|
3578
3685
|
}
|
|
3579
3686
|
}
|
|
3580
3687
|
let skillManager = null;
|
|
3581
|
-
const skillsDir =
|
|
3582
|
-
if (
|
|
3688
|
+
const skillsDir = join2(config.getConfigDir(), SKILLS_DIR_NAME);
|
|
3689
|
+
if (existsSync3(skillsDir)) {
|
|
3583
3690
|
skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
|
|
3584
|
-
skillManager.loadSkills();
|
|
3691
|
+
skillManager.loadSkills(pluginAssets.skillDirs);
|
|
3585
3692
|
const count = skillManager.listSkills().length;
|
|
3586
3693
|
if (count > 0) {
|
|
3587
3694
|
console.log(` Skills: ${count} available`);
|
|
@@ -3650,18 +3757,18 @@ async function startWebServer(options = {}) {
|
|
|
3650
3757
|
next();
|
|
3651
3758
|
};
|
|
3652
3759
|
const moduleDir = getModuleDir();
|
|
3653
|
-
let clientDir =
|
|
3654
|
-
if (!
|
|
3655
|
-
clientDir =
|
|
3760
|
+
let clientDir = join2(moduleDir, "web", "client");
|
|
3761
|
+
if (!existsSync3(clientDir)) {
|
|
3762
|
+
clientDir = join2(moduleDir, "client");
|
|
3656
3763
|
}
|
|
3657
|
-
if (!
|
|
3658
|
-
clientDir =
|
|
3764
|
+
if (!existsSync3(clientDir)) {
|
|
3765
|
+
clientDir = join2(moduleDir, "..", "..", "src", "web", "client");
|
|
3659
3766
|
}
|
|
3660
|
-
if (!
|
|
3661
|
-
clientDir =
|
|
3767
|
+
if (!existsSync3(clientDir)) {
|
|
3768
|
+
clientDir = join2(process.cwd(), "src", "web", "client");
|
|
3662
3769
|
}
|
|
3663
3770
|
console.log(` Static files: ${clientDir}`);
|
|
3664
|
-
app.use("/vendor", express.static(
|
|
3771
|
+
app.use("/vendor", express.static(join2(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
|
|
3665
3772
|
app.use(express.static(clientDir));
|
|
3666
3773
|
app.get("/api/status", (_req, res) => {
|
|
3667
3774
|
res.json({
|
|
@@ -3728,7 +3835,7 @@ async function startWebServer(options = {}) {
|
|
|
3728
3835
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
3729
3836
|
const cwd = process.cwd();
|
|
3730
3837
|
const prefix = req.query.prefix || "";
|
|
3731
|
-
const targetDir =
|
|
3838
|
+
const targetDir = join2(cwd, prefix);
|
|
3732
3839
|
try {
|
|
3733
3840
|
const canonicalTarget = realpathSync(resolve2(targetDir));
|
|
3734
3841
|
const canonicalCwd = realpathSync(resolve2(cwd));
|
|
@@ -3745,7 +3852,7 @@ async function startWebServer(options = {}) {
|
|
|
3745
3852
|
const entries = readdirSync2(targetDir, { withFileTypes: true });
|
|
3746
3853
|
const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
|
|
3747
3854
|
name: e.name,
|
|
3748
|
-
path: relative(cwd,
|
|
3855
|
+
path: relative(cwd, join2(targetDir, e.name)).replace(/\\/g, "/"),
|
|
3749
3856
|
isDir: e.isDirectory()
|
|
3750
3857
|
}));
|
|
3751
3858
|
res.json({ files });
|
|
@@ -3781,8 +3888,8 @@ async function startWebServer(options = {}) {
|
|
|
3781
3888
|
try {
|
|
3782
3889
|
const authUser = req._authUser;
|
|
3783
3890
|
const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
|
|
3784
|
-
const filePath =
|
|
3785
|
-
if (!
|
|
3891
|
+
const filePath = join2(histDir, `${id}.json`);
|
|
3892
|
+
if (!existsSync3(filePath)) {
|
|
3786
3893
|
res.status(404).json({ error: "Session not found" });
|
|
3787
3894
|
return;
|
|
3788
3895
|
}
|
|
@@ -3797,7 +3904,7 @@ async function startWebServer(options = {}) {
|
|
|
3797
3904
|
res.status(404).json({ error: "Session not found" });
|
|
3798
3905
|
return;
|
|
3799
3906
|
}
|
|
3800
|
-
const data = JSON.parse(
|
|
3907
|
+
const data = JSON.parse(readFileSync3(filePath, "utf-8"));
|
|
3801
3908
|
res.json({ session: data });
|
|
3802
3909
|
} catch (err) {
|
|
3803
3910
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
@@ -3810,7 +3917,7 @@ async function startWebServer(options = {}) {
|
|
|
3810
3917
|
return;
|
|
3811
3918
|
}
|
|
3812
3919
|
const cwd = process.cwd();
|
|
3813
|
-
const fullPath = resolve2(
|
|
3920
|
+
const fullPath = resolve2(join2(cwd, filePath));
|
|
3814
3921
|
try {
|
|
3815
3922
|
const canonicalFull = realpathSync(fullPath);
|
|
3816
3923
|
const canonicalCwd = realpathSync(resolve2(cwd));
|
|
@@ -3828,7 +3935,7 @@ async function startWebServer(options = {}) {
|
|
|
3828
3935
|
res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
|
|
3829
3936
|
return;
|
|
3830
3937
|
}
|
|
3831
|
-
const content =
|
|
3938
|
+
const content = readFileSync3(fullPath, "utf-8");
|
|
3832
3939
|
res.json({ content, size: stat.size });
|
|
3833
3940
|
} catch {
|
|
3834
3941
|
res.json({ error: "Cannot read file" });
|
|
@@ -4083,17 +4190,17 @@ function resolveProjectMcpPath() {
|
|
|
4083
4190
|
const cwd = process.cwd();
|
|
4084
4191
|
const gitRoot = getGitRoot(cwd);
|
|
4085
4192
|
const projectRoot = gitRoot ?? cwd;
|
|
4086
|
-
const configPath =
|
|
4087
|
-
return
|
|
4193
|
+
const configPath = join2(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
4194
|
+
return existsSync3(configPath) ? configPath : null;
|
|
4088
4195
|
}
|
|
4089
4196
|
function loadProjectMcpConfig() {
|
|
4090
4197
|
const cwd = process.cwd();
|
|
4091
4198
|
const gitRoot = getGitRoot(cwd);
|
|
4092
4199
|
const projectRoot = gitRoot ?? cwd;
|
|
4093
|
-
const configPath =
|
|
4094
|
-
if (!
|
|
4200
|
+
const configPath = join2(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
4201
|
+
if (!existsSync3(configPath)) return null;
|
|
4095
4202
|
try {
|
|
4096
|
-
const raw = JSON.parse(
|
|
4203
|
+
const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
4097
4204
|
return raw.mcpServers ?? raw;
|
|
4098
4205
|
} catch {
|
|
4099
4206
|
return null;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ToolRegistry
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GX3HSGJX.js";
|
|
5
5
|
import "./chunk-T2NL5ZIA.js";
|
|
6
6
|
import "./chunk-BXP6YZ2P.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-VGFTM3XT.js";
|
|
8
8
|
import {
|
|
9
9
|
runTool
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-MWKE2TNS.js";
|
|
11
11
|
import {
|
|
12
12
|
getDangerLevel,
|
|
13
13
|
schemaToJsonSchema
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
import "./chunk-TZQHYZKT.js";
|
|
16
16
|
import {
|
|
17
17
|
VERSION
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-NTCB7CMT.js";
|
|
19
|
+
import "./chunk-HOSJZMQS.js";
|
|
19
20
|
import "./chunk-4BKXL7SM.js";
|
|
20
21
|
import "./chunk-TB4W4Y4T.js";
|
|
21
22
|
import "./chunk-KHYD3WXE.js";
|