jinzd-ai-cli 0.4.208 → 0.4.210
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 +10 -8
- package/README.zh-CN.md +5 -5
- package/dist/{batch-3T44NFLG.js → batch-MFPYHOIL.js} +4 -4
- package/dist/{chunk-TD7JEHCR.js → chunk-7JWT3KXO.js} +390 -23
- package/dist/{chunk-SQB66GP6.js → chunk-BNERFXVO.js} +12 -9
- package/dist/{chunk-J22B3OSQ.js → chunk-DJ35G5VO.js} +1 -1
- package/dist/{chunk-VWYUB22Y.js → chunk-E2ZWWE6Q.js} +1 -1
- package/dist/{chunk-KV4GYNEA.js → chunk-GRJNQJA5.js} +5 -3
- package/dist/{chunk-QDY72ABW.js → chunk-HZQVX7VF.js} +5 -3
- package/dist/{chunk-O6UFCEUZ.js → chunk-K3CF65QH.js} +12 -9
- package/dist/{chunk-2OVMMSGQ.js → chunk-NEPFADHX.js} +10 -9
- package/dist/{chunk-P7JY3SWA.js → chunk-NWP7C6CC.js} +1 -1
- package/dist/{chunk-TM4MS63K.js → chunk-P6MTFCXB.js} +14 -6
- package/dist/{ci-6QWBVRJX.js → ci-QLJUDLMY.js} +3 -2
- package/dist/{constants-SZTQNN7K.js → constants-47OR72MV.js} +3 -1
- package/dist/{doctor-cli-I2Y2YR4V.js → doctor-cli-32COMA5K.js} +4 -4
- package/dist/electron-server.js +652 -457
- package/dist/{hub-XVPFBODB.js → hub-3NWJUCLK.js} +1 -1
- package/dist/index.js +128 -376
- package/dist/{indexer-AKWMYNJI.js → indexer-2AG4G6B5.js} +1 -1
- package/dist/{indexer-BMYUUDLH.js → indexer-4WWS3VIL.js} +1 -1
- package/dist/{run-tests-HZ2CRCTZ.js → run-tests-NT2UIUVB.js} +1 -1
- package/dist/{run-tests-D5SNUBYX.js → run-tests-UT7RZ7Y3.js} +2 -2
- package/dist/{server-PVKLPRUP.js → server-AIHVMLNU.js} +5 -5
- package/dist/{server-4EFTRFTN.js → server-E5D54DIZ.js} +87 -306
- package/dist/{task-orchestrator-2A5VCFLS.js → task-orchestrator-BFDSTSOH.js} +5 -5
- package/dist/{usage-Q24E5636.js → usage-P4B2RZKL.js} +2 -2
- package/package.json +2 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ToolRegistry
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-NEPFADHX.js";
|
|
5
5
|
import "./chunk-T2NL5ZIA.js";
|
|
6
6
|
import "./chunk-BXP6YZ2P.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-E2ZWWE6Q.js";
|
|
8
8
|
import {
|
|
9
9
|
runTool
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DJ35G5VO.js";
|
|
11
11
|
import {
|
|
12
12
|
getDangerLevel,
|
|
13
13
|
schemaToJsonSchema
|
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
import "./chunk-TZQHYZKT.js";
|
|
16
16
|
import {
|
|
17
17
|
VERSION
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-GRJNQJA5.js";
|
|
19
19
|
import "./chunk-4BKXL7SM.js";
|
|
20
20
|
import "./chunk-TB4W4Y4T.js";
|
|
21
21
|
import "./chunk-KHYD3WXE.js";
|
|
22
22
|
import "./chunk-SLSWPBK3.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-K3CF65QH.js";
|
|
24
24
|
import "./chunk-CKH4KQ4E.js";
|
|
25
25
|
import "./chunk-IW3Q7AE5.js";
|
|
26
26
|
|
|
@@ -16,10 +16,19 @@ import {
|
|
|
16
16
|
McpManager,
|
|
17
17
|
SkillManager,
|
|
18
18
|
autoTrimSessionIfNeeded,
|
|
19
|
+
buildInitPrompt,
|
|
20
|
+
loadContextFiles,
|
|
19
21
|
loadDevState,
|
|
20
22
|
persistToolRound,
|
|
23
|
+
readGitDiff,
|
|
24
|
+
scanDirTree,
|
|
25
|
+
scanProject,
|
|
21
26
|
setupProxy
|
|
22
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-7JWT3KXO.js";
|
|
28
|
+
import {
|
|
29
|
+
buildReviewPrompt,
|
|
30
|
+
buildSecurityReviewPrompt
|
|
31
|
+
} from "./chunk-HLWUDRBO.js";
|
|
23
32
|
import {
|
|
24
33
|
ToolExecutor,
|
|
25
34
|
ToolRegistry,
|
|
@@ -38,10 +47,10 @@ import {
|
|
|
38
47
|
spawnAgentContext,
|
|
39
48
|
truncateOutput,
|
|
40
49
|
undoStack
|
|
41
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-NEPFADHX.js";
|
|
42
51
|
import "./chunk-T2NL5ZIA.js";
|
|
43
52
|
import "./chunk-BXP6YZ2P.js";
|
|
44
|
-
import "./chunk-
|
|
53
|
+
import "./chunk-E2ZWWE6Q.js";
|
|
45
54
|
import {
|
|
46
55
|
SessionManager,
|
|
47
56
|
getContentText
|
|
@@ -55,7 +64,7 @@ import {
|
|
|
55
64
|
} from "./chunk-QMXC327F.js";
|
|
56
65
|
import {
|
|
57
66
|
runTool
|
|
58
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-DJ35G5VO.js";
|
|
59
68
|
import {
|
|
60
69
|
CONTENT_ONLY_STREAM_REMINDER,
|
|
61
70
|
TEE_FINAL_USER_NUDGE,
|
|
@@ -74,13 +83,12 @@ import {
|
|
|
74
83
|
} from "./chunk-XPBEJB27.js";
|
|
75
84
|
import {
|
|
76
85
|
ConfigManager
|
|
77
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-P6MTFCXB.js";
|
|
78
87
|
import "./chunk-TZQHYZKT.js";
|
|
79
88
|
import {
|
|
80
89
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
81
90
|
AUTHOR,
|
|
82
91
|
AUTHOR_EMAIL,
|
|
83
|
-
CONTEXT_FILE_CANDIDATES,
|
|
84
92
|
CUSTOM_COMMANDS_DIR_NAME,
|
|
85
93
|
DEFAULT_MAX_TOKENS,
|
|
86
94
|
DEFAULT_MAX_TOOL_ROUNDS,
|
|
@@ -94,7 +102,7 @@ import {
|
|
|
94
102
|
SKILLS_DIR_NAME,
|
|
95
103
|
VERSION,
|
|
96
104
|
buildUserIdentityPrompt
|
|
97
|
-
} from "./chunk-
|
|
105
|
+
} from "./chunk-GRJNQJA5.js";
|
|
98
106
|
import {
|
|
99
107
|
formatGitContextForPrompt,
|
|
100
108
|
getGitContext,
|
|
@@ -104,7 +112,7 @@ import "./chunk-4BKXL7SM.js";
|
|
|
104
112
|
import "./chunk-TB4W4Y4T.js";
|
|
105
113
|
import "./chunk-KHYD3WXE.js";
|
|
106
114
|
import "./chunk-SLSWPBK3.js";
|
|
107
|
-
import "./chunk-
|
|
115
|
+
import "./chunk-K3CF65QH.js";
|
|
108
116
|
import "./chunk-CKH4KQ4E.js";
|
|
109
117
|
import {
|
|
110
118
|
atomicWriteFileSync
|
|
@@ -519,9 +527,8 @@ function loadMemoryContent(configDir) {
|
|
|
519
527
|
}
|
|
520
528
|
|
|
521
529
|
// src/web/session-handler.ts
|
|
522
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3,
|
|
530
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync, mkdirSync, readdirSync, statSync, createWriteStream } from "fs";
|
|
523
531
|
import { join as join2, resolve, dirname } from "path";
|
|
524
|
-
import { execSync } from "child_process";
|
|
525
532
|
function lastAssistantText(messages) {
|
|
526
533
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
527
534
|
const m = messages[i];
|
|
@@ -536,7 +543,7 @@ function lastAssistantText(messages) {
|
|
|
536
543
|
}
|
|
537
544
|
return void 0;
|
|
538
545
|
}
|
|
539
|
-
var SessionHandler = class
|
|
546
|
+
var SessionHandler = class {
|
|
540
547
|
ws;
|
|
541
548
|
config;
|
|
542
549
|
providers;
|
|
@@ -573,6 +580,7 @@ var SessionHandler = class _SessionHandler {
|
|
|
573
580
|
pendingAutoPause = /* @__PURE__ */ new Map();
|
|
574
581
|
/** Active system prompt from context files */
|
|
575
582
|
activeSystemPrompt;
|
|
583
|
+
contextLoadResult = null;
|
|
576
584
|
/** Directories added via /add-dir */
|
|
577
585
|
addedDirs = /* @__PURE__ */ new Set();
|
|
578
586
|
/** Track MCP tool names used across this session (for MCP budget prioritization) */
|
|
@@ -1697,7 +1705,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
|
|
|
1697
1705
|
" /plan [enter|exit] \u2014 Toggle read-only planning mode",
|
|
1698
1706
|
" /session new|list|load|delete <id> \u2014 Session management",
|
|
1699
1707
|
" /system [prompt|clear] \u2014 Set/view/reset system prompt",
|
|
1700
|
-
" /context [reload]
|
|
1708
|
+
" /context [status|reload] \u2014 Show/reload context layers",
|
|
1701
1709
|
" /status \u2014 Show session info & token usage",
|
|
1702
1710
|
" /cost \u2014 Show cumulative token usage",
|
|
1703
1711
|
" /config [show|get|set] \u2014 View/modify configuration",
|
|
@@ -2221,7 +2229,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2221
2229
|
const sub = (args[0] ?? "status").toLowerCase();
|
|
2222
2230
|
const root = process.cwd();
|
|
2223
2231
|
const { loadIndex, clearIndex } = await import("./store-MWNHVGJT.js");
|
|
2224
|
-
const { indexProject } = await import("./indexer-
|
|
2232
|
+
const { indexProject } = await import("./indexer-2AG4G6B5.js");
|
|
2225
2233
|
const { loadVectorStore, clearVectorStore } = await import("./vector-store-BBDXB5IQ.js");
|
|
2226
2234
|
if (sub === "status") {
|
|
2227
2235
|
const idx = loadIndex(root);
|
|
@@ -2340,8 +2348,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2340
2348
|
const detailed = args.includes("--detailed");
|
|
2341
2349
|
let diff;
|
|
2342
2350
|
try {
|
|
2343
|
-
|
|
2344
|
-
diff = execSync(cmd, { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
2351
|
+
diff = readGitDiff({ staged });
|
|
2345
2352
|
} catch {
|
|
2346
2353
|
this.send({ type: "error", message: "Failed to run git diff." });
|
|
2347
2354
|
break;
|
|
@@ -2358,7 +2365,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2358
2365
|
diff = head + "\n\n... [diff truncated, " + diff.length + " chars total] ...\n\n" + tail;
|
|
2359
2366
|
truncated = true;
|
|
2360
2367
|
}
|
|
2361
|
-
const reviewPrompt =
|
|
2368
|
+
const reviewPrompt = buildReviewPrompt(diff, formatGitContextForPrompt(gitCtx), detailed);
|
|
2362
2369
|
this.send({ type: "info", message: "\u{1F50D} Analyzing changes..." });
|
|
2363
2370
|
try {
|
|
2364
2371
|
const review = await this.chatOnce(reviewPrompt, { temperature: 0.3, maxTokens: 8192 });
|
|
@@ -2379,8 +2386,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2379
2386
|
const secStaged = args.includes("--staged");
|
|
2380
2387
|
let secDiff;
|
|
2381
2388
|
try {
|
|
2382
|
-
|
|
2383
|
-
secDiff = execSync(cmd, { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
2389
|
+
secDiff = readGitDiff({ staged: secStaged });
|
|
2384
2390
|
} catch {
|
|
2385
2391
|
this.send({ type: "error", message: "Failed to run git diff." });
|
|
2386
2392
|
break;
|
|
@@ -2397,7 +2403,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2397
2403
|
secDiff = head + "\n\n... [diff truncated, " + secDiff.length + " chars total] ...\n\n" + tail;
|
|
2398
2404
|
secTruncated = true;
|
|
2399
2405
|
}
|
|
2400
|
-
const secPrompt =
|
|
2406
|
+
const secPrompt = buildSecurityReviewPrompt(secDiff, formatGitContextForPrompt(gitCtx));
|
|
2401
2407
|
this.send({ type: "info", message: "\u{1F512} Scanning for security vulnerabilities..." });
|
|
2402
2408
|
try {
|
|
2403
2409
|
const secReview = await this.chatOnce(secPrompt, { temperature: 0.2, maxTokens: 8192 });
|
|
@@ -2458,7 +2464,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2458
2464
|
case "test": {
|
|
2459
2465
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
2460
2466
|
try {
|
|
2461
|
-
const { executeTests } = await import("./run-tests-
|
|
2467
|
+
const { executeTests } = await import("./run-tests-UT7RZ7Y3.js");
|
|
2462
2468
|
const argStr = args.join(" ").trim();
|
|
2463
2469
|
let testArgs = {};
|
|
2464
2470
|
if (argStr) {
|
|
@@ -2484,8 +2490,8 @@ Use /init --force to overwrite.` });
|
|
|
2484
2490
|
}
|
|
2485
2491
|
this.send({ type: "info", message: "\u{1F4DD} Scanning project structure..." });
|
|
2486
2492
|
try {
|
|
2487
|
-
const projectInfo =
|
|
2488
|
-
const prompt =
|
|
2493
|
+
const projectInfo = scanProject(cwd);
|
|
2494
|
+
const prompt = buildInitPrompt(projectInfo, cwd);
|
|
2489
2495
|
const content = await this.chatOnce(prompt, { temperature: 0.3, maxTokens: 4096 });
|
|
2490
2496
|
writeFileSync(targetPath, content, "utf-8");
|
|
2491
2497
|
this.send({ type: "info", message: `\u2713 Generated: ${targetPath} (${content.length} chars)
|
|
@@ -2622,29 +2628,24 @@ ${this.config.toFormattedJSON()}
|
|
|
2622
2628
|
this.send({ type: "info", message: this.activeSystemPrompt ? `\u2713 Context reloaded (${this.activeSystemPrompt.length} chars).` : "\u2713 No context files found." });
|
|
2623
2629
|
break;
|
|
2624
2630
|
}
|
|
2625
|
-
const
|
|
2626
|
-
const cwd = process.cwd();
|
|
2627
|
-
const gitRoot = getGitRoot(cwd);
|
|
2628
|
-
const projectRoot = gitRoot ?? cwd;
|
|
2631
|
+
const result = this.contextLoadResult;
|
|
2629
2632
|
const layers = ["\u{1F4DA} **Context Layers:**", ""];
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2633
|
+
if (result && result.layers.length > 0) {
|
|
2634
|
+
const labels = { global: "Global ", project: "Project", local: "Local " };
|
|
2635
|
+
for (const layer of result.layers) {
|
|
2636
|
+
layers.push(` \u2713 ${labels[layer.level] ?? layer.level}: ${layer.displayPath} (${layer.charCount} chars${layer.truncated ? ", truncated" : ""})`);
|
|
2637
|
+
}
|
|
2638
|
+
layers.push(` Total: ${result.totalChars} chars (${result.layers.length} layer${result.layers.length > 1 ? "s" : ""})`);
|
|
2639
|
+
} else {
|
|
2640
|
+
layers.push(" No context files loaded.");
|
|
2641
|
+
layers.push(" Search order per layer: AICLI.override.md \u2192 AGENTS.override.md \u2192 AICLI.md \u2192 CLAUDE.md \u2192 AGENTS.md");
|
|
2642
|
+
}
|
|
2643
|
+
if (result && result.skipped.length > 0) {
|
|
2644
|
+
layers.push("");
|
|
2645
|
+
layers.push("Skipped:");
|
|
2646
|
+
for (const skipped of result.skipped.slice(0, 5)) {
|
|
2647
|
+
layers.push(` - ${skipped.displayPath}: ${skipped.reason}${skipped.message ? ` (${skipped.message})` : ""}`);
|
|
2641
2648
|
}
|
|
2642
|
-
layers.push(` \u2013 ${label}: (none)`);
|
|
2643
|
-
};
|
|
2644
|
-
checkLayer("Global", configDir);
|
|
2645
|
-
checkLayer("Project", projectRoot);
|
|
2646
|
-
if (resolve(cwd) !== resolve(projectRoot)) {
|
|
2647
|
-
checkLayer("Subdir", cwd);
|
|
2648
2649
|
}
|
|
2649
2650
|
layers.push("");
|
|
2650
2651
|
layers.push(`Total prompt length: ${this.activeSystemPrompt?.length ?? 0} chars`);
|
|
@@ -2954,9 +2955,11 @@ Add .md files to create commands.` });
|
|
|
2954
2955
|
try {
|
|
2955
2956
|
mkdirSync(configDir, { recursive: true });
|
|
2956
2957
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
2957
|
-
|
|
2958
|
+
const previous = existsSync3(memPath) ? readFileSync3(memPath, "utf-8") : "";
|
|
2959
|
+
const entry = `
|
|
2958
2960
|
- [${timestamp}] ${text}
|
|
2959
|
-
|
|
2961
|
+
`;
|
|
2962
|
+
atomicWriteFileSync(memPath, previous + entry);
|
|
2960
2963
|
this.send({ type: "info", message: `\u{1F4DD} Memory entry added: "${text}"` });
|
|
2961
2964
|
} catch (err) {
|
|
2962
2965
|
this.send({ type: "error", message: `Failed to write memory: ${err.message}` });
|
|
@@ -2966,7 +2969,7 @@ Add .md files to create commands.` });
|
|
|
2966
2969
|
const configDir = this.config.getConfigDir();
|
|
2967
2970
|
const memPath = join2(configDir, MEMORY_FILE_NAME);
|
|
2968
2971
|
try {
|
|
2969
|
-
|
|
2972
|
+
atomicWriteFileSync(memPath, "");
|
|
2970
2973
|
this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
|
|
2971
2974
|
} catch (err) {
|
|
2972
2975
|
this.send({ type: "error", message: `Failed to clear memory: ${err.message}` });
|
|
@@ -3129,7 +3132,7 @@ Add .md files to create commands.` });
|
|
|
3129
3132
|
dirContext += `
|
|
3130
3133
|
[Directory: ${dir}]
|
|
3131
3134
|
`;
|
|
3132
|
-
dirContext +=
|
|
3135
|
+
dirContext += scanDirTree(dir, 2, 40) + "\n";
|
|
3133
3136
|
totalLen = dirContext.length;
|
|
3134
3137
|
}
|
|
3135
3138
|
stable += dirContext;
|
|
@@ -3168,265 +3171,21 @@ Add .md files to create commands.` });
|
|
|
3168
3171
|
}
|
|
3169
3172
|
return { toolDefs: this.toolRegistry.getDefinitions(), mcpBudgetNote: null };
|
|
3170
3173
|
}
|
|
3171
|
-
/**
|
|
3172
|
-
* Find first matching context file in a directory.
|
|
3173
|
-
*/
|
|
3174
|
-
findContextFile(dir) {
|
|
3175
|
-
for (const name of CONTEXT_FILE_CANDIDATES) {
|
|
3176
|
-
const fullPath = join2(dir, name);
|
|
3177
|
-
try {
|
|
3178
|
-
if (existsSync3(fullPath)) {
|
|
3179
|
-
const content = readFileSync3(fullPath, "utf-8").trim();
|
|
3180
|
-
if (content) return content;
|
|
3181
|
-
}
|
|
3182
|
-
} catch {
|
|
3183
|
-
}
|
|
3184
|
-
}
|
|
3185
|
-
return null;
|
|
3186
|
-
}
|
|
3187
|
-
/**
|
|
3188
|
-
* Load hierarchical context files (same as CLI):
|
|
3189
|
-
* 1. Global: ~/.aicli/AICLI.md or CLAUDE.md
|
|
3190
|
-
* 2. Project: <git-root>/AICLI.md or CLAUDE.md
|
|
3191
|
-
* 3. Subdir: <cwd>/AICLI.md or CLAUDE.md (only if cwd ≠ project root)
|
|
3192
|
-
*/
|
|
3193
|
-
// ── /init helpers ─────────────────────────────────────────────────
|
|
3194
|
-
static SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
3195
|
-
"node_modules",
|
|
3196
|
-
".git",
|
|
3197
|
-
"dist",
|
|
3198
|
-
"build",
|
|
3199
|
-
"out",
|
|
3200
|
-
"target",
|
|
3201
|
-
".next",
|
|
3202
|
-
".nuxt",
|
|
3203
|
-
"__pycache__",
|
|
3204
|
-
".venv",
|
|
3205
|
-
"venv",
|
|
3206
|
-
".tox",
|
|
3207
|
-
".mypy_cache",
|
|
3208
|
-
".pytest_cache",
|
|
3209
|
-
".gradle",
|
|
3210
|
-
".idea",
|
|
3211
|
-
".vscode",
|
|
3212
|
-
".vs",
|
|
3213
|
-
"coverage",
|
|
3214
|
-
".cache",
|
|
3215
|
-
".parcel-cache",
|
|
3216
|
-
"dist-cjs",
|
|
3217
|
-
"release",
|
|
3218
|
-
".output",
|
|
3219
|
-
".turbo",
|
|
3220
|
-
"vendor"
|
|
3221
|
-
]);
|
|
3222
|
-
scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
3223
|
-
const lines = [];
|
|
3224
|
-
let count = 0;
|
|
3225
|
-
const walk = (d, prefix, depth) => {
|
|
3226
|
-
if (depth > maxDepth || count >= maxEntries) return;
|
|
3227
|
-
let entries;
|
|
3228
|
-
try {
|
|
3229
|
-
entries = readdirSync(d);
|
|
3230
|
-
} catch {
|
|
3231
|
-
return;
|
|
3232
|
-
}
|
|
3233
|
-
const filtered = entries.filter((e) => !e.startsWith(".") && !_SessionHandler.SCAN_SKIP_DIRS.has(e));
|
|
3234
|
-
const sorted = filtered.sort((a, b) => {
|
|
3235
|
-
let aIsDir = false, bIsDir = false;
|
|
3236
|
-
try {
|
|
3237
|
-
aIsDir = statSync(join2(d, a)).isDirectory();
|
|
3238
|
-
} catch {
|
|
3239
|
-
}
|
|
3240
|
-
try {
|
|
3241
|
-
bIsDir = statSync(join2(d, b)).isDirectory();
|
|
3242
|
-
} catch {
|
|
3243
|
-
}
|
|
3244
|
-
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
3245
|
-
return a.localeCompare(b);
|
|
3246
|
-
});
|
|
3247
|
-
for (let i = 0; i < sorted.length && count < maxEntries; i++) {
|
|
3248
|
-
const name = sorted[i];
|
|
3249
|
-
const fullPath = join2(d, name);
|
|
3250
|
-
const isLast = i === sorted.length - 1;
|
|
3251
|
-
let isDir;
|
|
3252
|
-
try {
|
|
3253
|
-
isDir = statSync(fullPath).isDirectory();
|
|
3254
|
-
} catch {
|
|
3255
|
-
continue;
|
|
3256
|
-
}
|
|
3257
|
-
lines.push(prefix + (isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ") + name + (isDir ? "/" : ""));
|
|
3258
|
-
count++;
|
|
3259
|
-
if (isDir) walk(fullPath, prefix + (isLast ? " " : "\u2502 "), depth + 1);
|
|
3260
|
-
}
|
|
3261
|
-
};
|
|
3262
|
-
walk(dir, "", 0);
|
|
3263
|
-
if (count >= maxEntries) lines.push("... (truncated)");
|
|
3264
|
-
return lines.join("\n");
|
|
3265
|
-
}
|
|
3266
|
-
scanProject(cwd) {
|
|
3267
|
-
const info = { type: "unknown", language: "unknown", configFiles: [], directoryStructure: "" };
|
|
3268
|
-
const check = (file) => existsSync3(join2(cwd, file));
|
|
3269
|
-
const configCandidates = [
|
|
3270
|
-
"package.json",
|
|
3271
|
-
"tsconfig.json",
|
|
3272
|
-
"Cargo.toml",
|
|
3273
|
-
"pyproject.toml",
|
|
3274
|
-
"setup.py",
|
|
3275
|
-
"requirements.txt",
|
|
3276
|
-
"go.mod",
|
|
3277
|
-
"pom.xml",
|
|
3278
|
-
"build.gradle",
|
|
3279
|
-
"build.gradle.kts",
|
|
3280
|
-
"CMakeLists.txt",
|
|
3281
|
-
"Makefile",
|
|
3282
|
-
".csproj",
|
|
3283
|
-
".sln",
|
|
3284
|
-
"composer.json",
|
|
3285
|
-
"Gemfile",
|
|
3286
|
-
"mix.exs",
|
|
3287
|
-
"deno.json",
|
|
3288
|
-
"bun.lockb"
|
|
3289
|
-
];
|
|
3290
|
-
info.configFiles = configCandidates.filter(check);
|
|
3291
|
-
if (check("package.json")) {
|
|
3292
|
-
info.type = "node";
|
|
3293
|
-
info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
|
|
3294
|
-
try {
|
|
3295
|
-
const pkg = JSON.parse(readFileSync3(join2(cwd, "package.json"), "utf-8"));
|
|
3296
|
-
const scripts = pkg.scripts ?? {};
|
|
3297
|
-
info.buildCommand = scripts.build ? "npm run build" : void 0;
|
|
3298
|
-
info.testCommand = scripts.test ? "npm test" : void 0;
|
|
3299
|
-
info.devCommand = scripts.dev ? "npm run dev" : void 0;
|
|
3300
|
-
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3301
|
-
if (allDeps["react"]) info.framework = "React";
|
|
3302
|
-
else if (allDeps["vue"]) info.framework = "Vue";
|
|
3303
|
-
else if (allDeps["@angular/core"]) info.framework = "Angular";
|
|
3304
|
-
else if (allDeps["next"]) info.framework = "Next.js";
|
|
3305
|
-
else if (allDeps["express"]) info.framework = "Express";
|
|
3306
|
-
} catch {
|
|
3307
|
-
}
|
|
3308
|
-
} else if (check("Cargo.toml")) {
|
|
3309
|
-
info.type = "rust";
|
|
3310
|
-
info.language = "Rust";
|
|
3311
|
-
info.buildCommand = "cargo build";
|
|
3312
|
-
info.testCommand = "cargo test";
|
|
3313
|
-
} else if (check("pyproject.toml") || check("setup.py") || check("requirements.txt")) {
|
|
3314
|
-
info.type = "python";
|
|
3315
|
-
info.language = "Python";
|
|
3316
|
-
info.testCommand = "pytest";
|
|
3317
|
-
} else if (check("go.mod")) {
|
|
3318
|
-
info.type = "go";
|
|
3319
|
-
info.language = "Go";
|
|
3320
|
-
info.buildCommand = "go build ./...";
|
|
3321
|
-
info.testCommand = "go test ./...";
|
|
3322
|
-
} else if (check("pom.xml")) {
|
|
3323
|
-
info.type = "java";
|
|
3324
|
-
info.language = "Java";
|
|
3325
|
-
info.buildCommand = "mvn package";
|
|
3326
|
-
info.testCommand = "mvn test";
|
|
3327
|
-
} else if (check("build.gradle") || check("build.gradle.kts")) {
|
|
3328
|
-
info.type = "java";
|
|
3329
|
-
info.language = "Java/Kotlin";
|
|
3330
|
-
info.buildCommand = "./gradlew build";
|
|
3331
|
-
info.testCommand = "./gradlew test";
|
|
3332
|
-
}
|
|
3333
|
-
info.directoryStructure = this.scanDirTree(cwd);
|
|
3334
|
-
return info;
|
|
3335
|
-
}
|
|
3336
|
-
buildInitPrompt(info, cwd) {
|
|
3337
|
-
const parts = [
|
|
3338
|
-
"Please generate an AICLI.md context file (Markdown format) for the following project.",
|
|
3339
|
-
"\n## Project Info\n",
|
|
3340
|
-
`- Working directory: ${cwd}`,
|
|
3341
|
-
`- Type: ${info.type}`,
|
|
3342
|
-
`- Language: ${info.language}`
|
|
3343
|
-
];
|
|
3344
|
-
if (info.framework) parts.push(`- Framework: ${info.framework}`);
|
|
3345
|
-
if (info.buildCommand) parts.push(`- Build command: ${info.buildCommand}`);
|
|
3346
|
-
if (info.testCommand) parts.push(`- Test command: ${info.testCommand}`);
|
|
3347
|
-
if (info.devCommand) parts.push(`- Dev command: ${info.devCommand}`);
|
|
3348
|
-
parts.push(`
|
|
3349
|
-
## Detected Config Files
|
|
3350
|
-
${info.configFiles.map((f) => `- ${f}`).join("\n")}`);
|
|
3351
|
-
parts.push(`
|
|
3352
|
-
## Directory Structure
|
|
3353
|
-
\`\`\`
|
|
3354
|
-
${info.directoryStructure}
|
|
3355
|
-
\`\`\``);
|
|
3356
|
-
parts.push(`
|
|
3357
|
-
## Requirements
|
|
3358
|
-
Generate a structured Markdown file with: project overview, tech stack, project structure, common commands, code style. Keep it concise, within 200 lines.`);
|
|
3359
|
-
return parts.join("\n");
|
|
3360
|
-
}
|
|
3361
|
-
// ── /review helper ──────────────────────────────────────────────────
|
|
3362
|
-
buildReviewPrompt(diff, gitContextStr, detailed) {
|
|
3363
|
-
const level = detailed ? "Please perform a detailed in-depth review covering: security, performance, maintainability, error handling, naming conventions, and code duplication." : "Please perform a concise code review focusing on bugs, security issues, and key improvement suggestions.";
|
|
3364
|
-
return `# Code Review Request
|
|
3365
|
-
|
|
3366
|
-
${level}
|
|
3367
|
-
|
|
3368
|
-
## Git Status
|
|
3369
|
-
${gitContextStr}
|
|
3370
|
-
|
|
3371
|
-
## Code Changes (diff)
|
|
3372
|
-
\`\`\`diff
|
|
3373
|
-
${diff}
|
|
3374
|
-
\`\`\`
|
|
3375
|
-
|
|
3376
|
-
## Output Format
|
|
3377
|
-
1. **Overall Assessment**: One-sentence summary
|
|
3378
|
-
2. **Issues** (if any): [Severity] file:line \u2014 description + fix
|
|
3379
|
-
3. **Improvement Suggestions** (if any)
|
|
3380
|
-
4. **Highlights** (if any)
|
|
3381
|
-
|
|
3382
|
-
Severity: \u{1F534} Critical / \u{1F7E1} Warning / \u{1F535} Info`;
|
|
3383
|
-
}
|
|
3384
|
-
buildSecurityReviewPrompt(diff, gitContextStr) {
|
|
3385
|
-
return `# Security Vulnerability Review
|
|
3386
|
-
|
|
3387
|
-
Analyze the following code changes **exclusively for security vulnerabilities**.
|
|
3388
|
-
|
|
3389
|
-
## Categories
|
|
3390
|
-
1. Injection (SQL, command, path traversal, XSS)
|
|
3391
|
-
2. Auth & Authorization (hardcoded creds, missing checks)
|
|
3392
|
-
3. Secrets & Sensitive Data (API keys, tokens in code)
|
|
3393
|
-
4. Input Validation (missing validation, unsafe deserialization)
|
|
3394
|
-
5. Cryptography (weak algorithms, hardcoded IVs)
|
|
3395
|
-
6. File System (path traversal, symlink attacks)
|
|
3396
|
-
7. Network (SSRF, insecure protocols)
|
|
3397
|
-
|
|
3398
|
-
## Git Status
|
|
3399
|
-
${gitContextStr}
|
|
3400
|
-
|
|
3401
|
-
## Code Changes
|
|
3402
|
-
\`\`\`diff
|
|
3403
|
-
${diff}
|
|
3404
|
-
\`\`\`
|
|
3405
|
-
|
|
3406
|
-
## Output Format
|
|
3407
|
-
For each finding:
|
|
3408
|
-
- **Severity**: \u{1F534} CRITICAL / \u{1F7E0} HIGH / \u{1F7E1} MEDIUM / \u{1F535} LOW
|
|
3409
|
-
- **Category** + **File:line**
|
|
3410
|
-
- **Description** + exploitation scenario
|
|
3411
|
-
- **Recommended fix**
|
|
3412
|
-
|
|
3413
|
-
If none found: "\u2705 No security vulnerabilities detected"`;
|
|
3414
|
-
}
|
|
3415
3174
|
loadContextFiles() {
|
|
3416
|
-
const
|
|
3175
|
+
const contextConfig = this.config.get("context");
|
|
3417
3176
|
const cwd = process.cwd();
|
|
3418
|
-
const configDir = this.config.getConfigDir();
|
|
3419
|
-
const globalCtx = this.findContextFile(configDir);
|
|
3420
|
-
if (globalCtx) parts.push(globalCtx);
|
|
3421
3177
|
const gitRoot = getGitRoot(cwd);
|
|
3422
3178
|
const projectRoot = gitRoot ?? cwd;
|
|
3423
|
-
const
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3179
|
+
const result = loadContextFiles({
|
|
3180
|
+
cwd,
|
|
3181
|
+
configDir: this.config.getConfigDir(),
|
|
3182
|
+
projectRoot,
|
|
3183
|
+
setting: this.config.get("contextFile"),
|
|
3184
|
+
fallbackFilenames: contextConfig.projectDocFallbackFilenames,
|
|
3185
|
+
maxBytes: contextConfig.projectDocMaxBytes
|
|
3186
|
+
});
|
|
3187
|
+
this.contextLoadResult = result;
|
|
3188
|
+
return result.mergedContent || void 0;
|
|
3430
3189
|
}
|
|
3431
3190
|
};
|
|
3432
3191
|
|
|
@@ -3588,6 +3347,7 @@ async function startWebServer(options = {}) {
|
|
|
3588
3347
|
clientDir = join3(process.cwd(), "src", "web", "client");
|
|
3589
3348
|
}
|
|
3590
3349
|
console.log(` Static files: ${clientDir}`);
|
|
3350
|
+
app.use("/vendor", express.static(join3(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
|
|
3591
3351
|
app.use(express.static(clientDir));
|
|
3592
3352
|
app.get("/api/status", (_req, res) => {
|
|
3593
3353
|
res.json({
|
|
@@ -3630,7 +3390,7 @@ async function startWebServer(options = {}) {
|
|
|
3630
3390
|
}
|
|
3631
3391
|
const token = authManager.login(username, password);
|
|
3632
3392
|
console.log(` \u2713 User registered via API: ${username}${firstRun ? " (first-run)" : ""}`);
|
|
3633
|
-
res.cookie("aicli_token", token,
|
|
3393
|
+
res.cookie("aicli_token", token, authCookieOptions(req));
|
|
3634
3394
|
res.json({ success: true, username });
|
|
3635
3395
|
});
|
|
3636
3396
|
app.post("/api/auth/login", (req, res) => {
|
|
@@ -3644,11 +3404,11 @@ async function startWebServer(options = {}) {
|
|
|
3644
3404
|
res.status(401).json({ error: "Invalid username or password" });
|
|
3645
3405
|
return;
|
|
3646
3406
|
}
|
|
3647
|
-
res.cookie("aicli_token", token,
|
|
3407
|
+
res.cookie("aicli_token", token, authCookieOptions(req));
|
|
3648
3408
|
res.json({ success: true, username });
|
|
3649
3409
|
});
|
|
3650
|
-
app.post("/api/auth/logout", (
|
|
3651
|
-
res.clearCookie("aicli_token",
|
|
3410
|
+
app.post("/api/auth/logout", (req, res) => {
|
|
3411
|
+
res.clearCookie("aicli_token", clearAuthCookieOptions(req));
|
|
3652
3412
|
res.json({ success: true });
|
|
3653
3413
|
});
|
|
3654
3414
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
@@ -4025,6 +3785,27 @@ function loadProjectMcpConfig() {
|
|
|
4025
3785
|
return null;
|
|
4026
3786
|
}
|
|
4027
3787
|
}
|
|
3788
|
+
function isHttpsRequest(req) {
|
|
3789
|
+
if (req.secure) return true;
|
|
3790
|
+
const forwardedProto = req.headers["x-forwarded-proto"];
|
|
3791
|
+
const proto = Array.isArray(forwardedProto) ? forwardedProto[0] : forwardedProto;
|
|
3792
|
+
return proto?.split(",")[0]?.trim().toLowerCase() === "https";
|
|
3793
|
+
}
|
|
3794
|
+
function authCookieOptions(req) {
|
|
3795
|
+
return {
|
|
3796
|
+
httpOnly: true,
|
|
3797
|
+
sameSite: "strict",
|
|
3798
|
+
maxAge: TOKEN_EXPIRY_MS,
|
|
3799
|
+
secure: isHttpsRequest(req)
|
|
3800
|
+
};
|
|
3801
|
+
}
|
|
3802
|
+
function clearAuthCookieOptions(req) {
|
|
3803
|
+
return {
|
|
3804
|
+
httpOnly: true,
|
|
3805
|
+
sameSite: "strict",
|
|
3806
|
+
secure: isHttpsRequest(req)
|
|
3807
|
+
};
|
|
3808
|
+
}
|
|
4028
3809
|
function parseCookie(cookie) {
|
|
4029
3810
|
const result = {};
|
|
4030
3811
|
for (const part of cookie.split(";")) {
|