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.
Files changed (28) hide show
  1. package/README.md +10 -8
  2. package/README.zh-CN.md +5 -5
  3. package/dist/{batch-3T44NFLG.js → batch-MFPYHOIL.js} +4 -4
  4. package/dist/{chunk-TD7JEHCR.js → chunk-7JWT3KXO.js} +390 -23
  5. package/dist/{chunk-SQB66GP6.js → chunk-BNERFXVO.js} +12 -9
  6. package/dist/{chunk-J22B3OSQ.js → chunk-DJ35G5VO.js} +1 -1
  7. package/dist/{chunk-VWYUB22Y.js → chunk-E2ZWWE6Q.js} +1 -1
  8. package/dist/{chunk-KV4GYNEA.js → chunk-GRJNQJA5.js} +5 -3
  9. package/dist/{chunk-QDY72ABW.js → chunk-HZQVX7VF.js} +5 -3
  10. package/dist/{chunk-O6UFCEUZ.js → chunk-K3CF65QH.js} +12 -9
  11. package/dist/{chunk-2OVMMSGQ.js → chunk-NEPFADHX.js} +10 -9
  12. package/dist/{chunk-P7JY3SWA.js → chunk-NWP7C6CC.js} +1 -1
  13. package/dist/{chunk-TM4MS63K.js → chunk-P6MTFCXB.js} +14 -6
  14. package/dist/{ci-6QWBVRJX.js → ci-QLJUDLMY.js} +3 -2
  15. package/dist/{constants-SZTQNN7K.js → constants-47OR72MV.js} +3 -1
  16. package/dist/{doctor-cli-I2Y2YR4V.js → doctor-cli-32COMA5K.js} +4 -4
  17. package/dist/electron-server.js +652 -457
  18. package/dist/{hub-XVPFBODB.js → hub-3NWJUCLK.js} +1 -1
  19. package/dist/index.js +128 -376
  20. package/dist/{indexer-AKWMYNJI.js → indexer-2AG4G6B5.js} +1 -1
  21. package/dist/{indexer-BMYUUDLH.js → indexer-4WWS3VIL.js} +1 -1
  22. package/dist/{run-tests-HZ2CRCTZ.js → run-tests-NT2UIUVB.js} +1 -1
  23. package/dist/{run-tests-D5SNUBYX.js → run-tests-UT7RZ7Y3.js} +2 -2
  24. package/dist/{server-PVKLPRUP.js → server-AIHVMLNU.js} +5 -5
  25. package/dist/{server-4EFTRFTN.js → server-E5D54DIZ.js} +87 -306
  26. package/dist/{task-orchestrator-2A5VCFLS.js → task-orchestrator-BFDSTSOH.js} +5 -5
  27. package/dist/{usage-Q24E5636.js → usage-P4B2RZKL.js} +2 -2
  28. package/package.json +2 -1
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  indexProject,
4
4
  updateFile
5
- } from "./chunk-O6UFCEUZ.js";
5
+ } from "./chunk-K3CF65QH.js";
6
6
  import "./chunk-CKH4KQ4E.js";
7
7
  import "./chunk-IW3Q7AE5.js";
8
8
  export {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  indexProject,
3
3
  updateFile
4
- } from "./chunk-SQB66GP6.js";
4
+ } from "./chunk-BNERFXVO.js";
5
5
  import "./chunk-RWM2GFRC.js";
6
6
  import "./chunk-6BUTA5VW.js";
7
7
  export {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-QDY72ABW.js";
4
+ } from "./chunk-HZQVX7VF.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-VWYUB22Y.js";
6
- import "./chunk-KV4GYNEA.js";
5
+ } from "./chunk-E2ZWWE6Q.js";
6
+ import "./chunk-GRJNQJA5.js";
7
7
  export {
8
8
  executeTests,
9
9
  runTestsTool
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ToolRegistry
4
- } from "./chunk-2OVMMSGQ.js";
4
+ } from "./chunk-NEPFADHX.js";
5
5
  import "./chunk-T2NL5ZIA.js";
6
6
  import "./chunk-BXP6YZ2P.js";
7
- import "./chunk-VWYUB22Y.js";
7
+ import "./chunk-E2ZWWE6Q.js";
8
8
  import {
9
9
  runTool
10
- } from "./chunk-J22B3OSQ.js";
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-KV4GYNEA.js";
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-O6UFCEUZ.js";
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-TD7JEHCR.js";
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-2OVMMSGQ.js";
50
+ } from "./chunk-NEPFADHX.js";
42
51
  import "./chunk-T2NL5ZIA.js";
43
52
  import "./chunk-BXP6YZ2P.js";
44
- import "./chunk-VWYUB22Y.js";
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-J22B3OSQ.js";
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-TM4MS63K.js";
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-KV4GYNEA.js";
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-O6UFCEUZ.js";
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, appendFileSync, writeFileSync, mkdirSync, readdirSync, statSync, createWriteStream } from "fs";
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 _SessionHandler {
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] \u2014 Show/reload context layers",
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-AKWMYNJI.js");
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
- const cmd = staged ? "git diff --staged" : "git diff";
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 = this.buildReviewPrompt(diff, formatGitContextForPrompt(gitCtx), detailed);
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
- const cmd = secStaged ? "git diff --staged" : "git diff";
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 = this.buildSecurityReviewPrompt(secDiff, formatGitContextForPrompt(gitCtx));
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-D5SNUBYX.js");
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 = this.scanProject(cwd);
2488
- const prompt = this.buildInitPrompt(projectInfo, cwd);
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 configDir = this.config.getConfigDir();
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
- const checkLayer = (label, dir) => {
2631
- for (const name2 of CONTEXT_FILE_CANDIDATES) {
2632
- const fullPath = join2(dir, name2);
2633
- if (existsSync3(fullPath)) {
2634
- try {
2635
- const size = statSync(fullPath).size;
2636
- layers.push(` \u2713 ${label}: ${fullPath} (${size} bytes)`);
2637
- return;
2638
- } catch {
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
- appendFileSync(memPath, `
2958
+ const previous = existsSync3(memPath) ? readFileSync3(memPath, "utf-8") : "";
2959
+ const entry = `
2958
2960
  - [${timestamp}] ${text}
2959
- `, "utf-8");
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
- writeFileSync(memPath, "", "utf-8");
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 += this.scanDirTree(dir, 2, 40) + "\n";
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 parts = [];
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 projectCtx = this.findContextFile(projectRoot);
3424
- if (projectCtx) parts.push(projectCtx);
3425
- if (resolve(cwd) !== resolve(projectRoot)) {
3426
- const localCtx = this.findContextFile(cwd);
3427
- if (localCtx) parts.push(localCtx);
3428
- }
3429
- return parts.length > 0 ? parts.join("\n\n---\n\n") : void 0;
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, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
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, { httpOnly: true, sameSite: "strict", maxAge: TOKEN_EXPIRY_MS });
3407
+ res.cookie("aicli_token", token, authCookieOptions(req));
3648
3408
  res.json({ success: true, username });
3649
3409
  });
3650
- app.post("/api/auth/logout", (_req, res) => {
3651
- res.clearCookie("aicli_token", { httpOnly: true, sameSite: "strict" });
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(";")) {