jinzd-ai-cli 0.4.209 → 0.4.211

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/dist/index.js CHANGED
@@ -1,28 +1,32 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- buildReviewPrompt,
4
- buildSecurityReviewPrompt
5
- } from "./chunk-HLWUDRBO.js";
6
2
  import {
7
3
  McpManager,
8
4
  SNAPSHOT_PROMPT,
9
5
  SkillManager,
10
6
  autoTrimSessionIfNeeded,
7
+ buildInitPrompt,
11
8
  clearDevState,
9
+ loadContextFiles,
12
10
  loadDevState,
13
11
  parseSimpleYaml,
14
12
  persistToolRound,
15
13
  readGitDiff,
16
14
  saveDevState,
15
+ scanProject,
17
16
  sessionHasMeaningfulContent,
18
17
  setupProxy
19
- } from "./chunk-ATNYI6JL.js";
18
+ } from "./chunk-C3OU2OPF.js";
19
+ import {
20
+ buildReviewPrompt,
21
+ buildSecurityReviewPrompt
22
+ } from "./chunk-HLWUDRBO.js";
20
23
  import {
21
24
  ToolExecutor,
22
25
  ToolRegistry,
23
26
  askUserContext,
24
27
  cleanupRejectedTeeFile,
25
28
  estimateTokens,
29
+ formatPermissionProfileWarning,
26
30
  googleSearchContext,
27
31
  initTheme,
28
32
  isInterrupted,
@@ -36,10 +40,10 @@ import {
36
40
  spawnAgentContext,
37
41
  theme,
38
42
  undoStack
39
- } from "./chunk-JVLRMIHA.js";
43
+ } from "./chunk-ZOPYREL5.js";
40
44
  import "./chunk-T2NL5ZIA.js";
41
45
  import "./chunk-BXP6YZ2P.js";
42
- import "./chunk-EN63JA7N.js";
46
+ import "./chunk-6NS6643Y.js";
43
47
  import {
44
48
  SessionManager,
45
49
  getContentText
@@ -56,7 +60,7 @@ import {
56
60
  getConfigDirUsage,
57
61
  listRecentCrashes,
58
62
  writeCrashLog
59
- } from "./chunk-D6M4E7SH.js";
63
+ } from "./chunk-BE6ERF7M.js";
60
64
  import {
61
65
  ProviderRegistry
62
66
  } from "./chunk-QMXC327F.js";
@@ -65,7 +69,7 @@ import {
65
69
  getTopFailingTools,
66
70
  getTopUsedTools,
67
71
  installFlushOnExit
68
- } from "./chunk-STIEOOOL.js";
72
+ } from "./chunk-JBWA73GK.js";
69
73
  import {
70
74
  CONTENT_ONLY_STREAM_REMINDER,
71
75
  TEE_FINAL_USER_NUDGE,
@@ -87,7 +91,7 @@ import {
87
91
  } from "./chunk-XPBEJB27.js";
88
92
  import {
89
93
  ConfigManager
90
- } from "./chunk-DUZRQXIP.js";
94
+ } from "./chunk-UOROWTGG.js";
91
95
  import {
92
96
  AuthError,
93
97
  ProviderError,
@@ -98,7 +102,6 @@ import {
98
102
  AUTHOR,
99
103
  AUTHOR_EMAIL,
100
104
  CONFIG_DIR_NAME,
101
- CONTEXT_FILE_CANDIDATES,
102
105
  CONTEXT_PRESSURE_THRESHOLD,
103
106
  CUSTOM_COMMANDS_DIR_NAME,
104
107
  DEFAULT_MAX_TOKENS,
@@ -114,7 +117,7 @@ import {
114
117
  SKILLS_DIR_NAME,
115
118
  VERSION,
116
119
  buildUserIdentityPrompt
117
- } from "./chunk-UPMBIS4T.js";
120
+ } from "./chunk-E5XCM4A6.js";
118
121
  import {
119
122
  formatGitContextForPrompt,
120
123
  getGitContext,
@@ -145,8 +148,8 @@ import { program } from "commander";
145
148
 
146
149
  // src/repl/repl.ts
147
150
  import * as readline from "readline";
148
- import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync4, mkdirSync as mkdirSync4 } from "fs";
149
- import { join as join5, resolve as resolve2, extname as extname2, dirname as dirname3, basename as basename2 } from "path";
151
+ import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync3, mkdirSync as mkdirSync4 } from "fs";
152
+ import { join as join4, resolve as resolve2, extname as extname2, dirname as dirname3, basename as basename2 } from "path";
150
153
  import chalk4 from "chalk";
151
154
 
152
155
  // src/session/title-generator.ts
@@ -638,10 +641,10 @@ Error${typeName}: ${lines.join("\n")}
638
641
  };
639
642
 
640
643
  // src/repl/commands/index.ts
641
- import { writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync3, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
644
+ import { writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync2, readFileSync, statSync as statSync2 } from "fs";
642
645
  import { execSync as execSync2 } from "child_process";
643
646
  import { platform } from "os";
644
- import { resolve, dirname as dirname2, join as join3, basename } from "path";
647
+ import { resolve, dirname as dirname2, join as join2, basename } from "path";
645
648
  import chalk2 from "chalk";
646
649
 
647
650
  // src/repl/clipboard.ts
@@ -731,195 +734,6 @@ function getClipboardHint() {
731
734
  return "";
732
735
  }
733
736
 
734
- // src/repl/commands/project-init.ts
735
- import { existsSync as existsSync2, readFileSync, readdirSync, statSync as statSync2 } from "fs";
736
- import { join as join2 } from "path";
737
- var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
738
- "node_modules",
739
- ".git",
740
- "dist",
741
- "build",
742
- "out",
743
- "target",
744
- ".next",
745
- ".nuxt",
746
- "__pycache__",
747
- ".venv",
748
- "venv",
749
- ".tox",
750
- ".mypy_cache",
751
- ".pytest_cache",
752
- ".gradle",
753
- ".idea",
754
- ".vscode",
755
- ".vs",
756
- "coverage",
757
- ".cache",
758
- ".parcel-cache",
759
- "dist-cjs",
760
- "release",
761
- ".output",
762
- ".turbo",
763
- "vendor"
764
- ]);
765
- function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
766
- const lines = [];
767
- let count = 0;
768
- const walk = (d, prefix, depth) => {
769
- if (depth > maxDepth || count >= maxEntries) return;
770
- let entries;
771
- try {
772
- entries = readdirSync(d);
773
- } catch {
774
- return;
775
- }
776
- const filtered = entries.filter((e) => !e.startsWith(".") && !SCAN_SKIP_DIRS.has(e));
777
- const sorted = filtered.sort((a, b) => {
778
- let aIsDir = false, bIsDir = false;
779
- try {
780
- aIsDir = statSync2(join2(d, a)).isDirectory();
781
- } catch {
782
- }
783
- try {
784
- bIsDir = statSync2(join2(d, b)).isDirectory();
785
- } catch {
786
- }
787
- if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
788
- return a.localeCompare(b);
789
- });
790
- for (let i = 0; i < sorted.length && count < maxEntries; i++) {
791
- const name = sorted[i];
792
- const fullPath = join2(d, name);
793
- const isLast = i === sorted.length - 1;
794
- const connector = isLast ? "+-- " : "|-- ";
795
- let isDir;
796
- try {
797
- isDir = statSync2(fullPath).isDirectory();
798
- } catch {
799
- continue;
800
- }
801
- lines.push(prefix + connector + name + (isDir ? "/" : ""));
802
- count++;
803
- if (isDir) {
804
- walk(fullPath, prefix + (isLast ? " " : "| "), depth + 1);
805
- }
806
- }
807
- };
808
- walk(dir, "", 0);
809
- if (count >= maxEntries) lines.push("... (truncated)");
810
- return lines.join("\n");
811
- }
812
- function scanProject(cwd) {
813
- const info = {
814
- type: "unknown",
815
- language: "unknown",
816
- configFiles: [],
817
- directoryStructure: ""
818
- };
819
- const check = (file) => existsSync2(join2(cwd, file));
820
- const configCandidates = [
821
- "package.json",
822
- "tsconfig.json",
823
- "Cargo.toml",
824
- "pyproject.toml",
825
- "setup.py",
826
- "requirements.txt",
827
- "go.mod",
828
- "pom.xml",
829
- "build.gradle",
830
- "build.gradle.kts",
831
- "CMakeLists.txt",
832
- "Makefile",
833
- ".csproj",
834
- ".sln",
835
- "composer.json",
836
- "Gemfile",
837
- "mix.exs",
838
- "deno.json",
839
- "bun.lockb"
840
- ];
841
- info.configFiles = configCandidates.filter(check);
842
- if (check("package.json")) {
843
- info.type = "node";
844
- info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
845
- try {
846
- const pkg = JSON.parse(readFileSync(join2(cwd, "package.json"), "utf-8"));
847
- const scripts = pkg.scripts ?? {};
848
- info.buildCommand = scripts.build ? "npm run build" : void 0;
849
- info.testCommand = scripts.test ? "npm test" : void 0;
850
- info.devCommand = scripts.dev ? "npm run dev" : void 0;
851
- const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
852
- if (allDeps["react"]) info.framework = "React";
853
- else if (allDeps["vue"]) info.framework = "Vue";
854
- else if (allDeps["@angular/core"]) info.framework = "Angular";
855
- else if (allDeps["next"]) info.framework = "Next.js";
856
- else if (allDeps["nuxt"]) info.framework = "Nuxt";
857
- else if (allDeps["express"]) info.framework = "Express";
858
- else if (allDeps["fastify"]) info.framework = "Fastify";
859
- else if (allDeps["svelte"]) info.framework = "Svelte";
860
- } catch {
861
- }
862
- } else if (check("Cargo.toml")) {
863
- info.type = "rust";
864
- info.language = "Rust";
865
- info.buildCommand = "cargo build";
866
- info.testCommand = "cargo test";
867
- } else if (check("pyproject.toml") || check("setup.py") || check("requirements.txt")) {
868
- info.type = "python";
869
- info.language = "Python";
870
- info.testCommand = check("pytest.ini") || check("pyproject.toml") ? "pytest" : "python -m unittest";
871
- } else if (check("go.mod")) {
872
- info.type = "go";
873
- info.language = "Go";
874
- info.buildCommand = "go build ./...";
875
- info.testCommand = "go test ./...";
876
- } else if (check("pom.xml")) {
877
- info.type = "java";
878
- info.language = "Java";
879
- info.buildCommand = "mvn package";
880
- info.testCommand = "mvn test";
881
- } else if (check("build.gradle") || check("build.gradle.kts")) {
882
- info.type = "java";
883
- info.language = "Java/Kotlin";
884
- info.buildCommand = "./gradlew build";
885
- info.testCommand = "./gradlew test";
886
- }
887
- info.directoryStructure = scanDirTree(cwd);
888
- return info;
889
- }
890
- function buildInitPrompt(info, cwd) {
891
- const parts = [
892
- "Please generate an AICLI.md context file (Markdown format) for the following project. This file will be injected into the AI conversation system prompt to help the AI understand the project structure and coding conventions.",
893
- "\n## Project Info\n",
894
- `- Working directory: ${cwd}`,
895
- `- Type: ${info.type}`,
896
- `- Language: ${info.language}`
897
- ];
898
- if (info.framework) parts.push(`- Framework: ${info.framework}`);
899
- if (info.buildCommand) parts.push(`- Build command: ${info.buildCommand}`);
900
- if (info.testCommand) parts.push(`- Test command: ${info.testCommand}`);
901
- if (info.devCommand) parts.push(`- Dev command: ${info.devCommand}`);
902
- parts.push(`
903
- ## Detected Config Files
904
- ${info.configFiles.map((f) => `- ${f}`).join("\n")}`);
905
- parts.push(`
906
- ## Directory Structure
907
- \`\`\`
908
- ${info.directoryStructure}
909
- \`\`\``);
910
- parts.push(`
911
- ## Requirements
912
- Please generate a structured Markdown file containing:
913
- 1. Project overview (one-sentence summary)
914
- 2. Tech stack
915
- 3. Project structure description (based on the directory structure above)
916
- 4. Common commands (build, test, dev, etc.)
917
- 5. Code style and conventions (inferred from config files)
918
-
919
- Output the Markdown content directly, do not wrap the entire file in a code block. Keep it concise, within 200 lines.`);
920
- return parts.join("\n");
921
- }
922
-
923
737
  // src/repl/commands/index.ts
924
738
  function fmtCtx(tokens) {
925
739
  if (tokens >= 1e6) return `${Math.round(tokens / 1e5) / 10}M`;
@@ -1489,7 +1303,7 @@ No commands match "${filter}".
1489
1303
  {
1490
1304
  name: "context",
1491
1305
  description: "Show or reload hierarchical context files",
1492
- usage: "/context [reload]",
1306
+ usage: "/context [status|reload]",
1493
1307
  execute(args, ctx) {
1494
1308
  const sub = args[0];
1495
1309
  if (sub === "reload") {
@@ -1499,7 +1313,7 @@ No commands match "${filter}".
1499
1313
  const summary = layers2.map((l) => `${l.level}:${l.charCount}`).join(", ");
1500
1314
  ctx.renderer.printSuccess(`Context reloaded: ${layers2.length} layer(s) (${summary} chars)`);
1501
1315
  } else {
1502
- ctx.renderer.printInfo("No context file found. Place AICLI.md or CLAUDE.md in your project root or ~/.aicli/.");
1316
+ ctx.renderer.printInfo("No context file found. Place AICLI.md, CLAUDE.md, or AGENTS.md in your project root or ~/.aicli/.");
1503
1317
  }
1504
1318
  return;
1505
1319
  }
@@ -1509,16 +1323,16 @@ No commands match "${filter}".
1509
1323
  console.log(" Context layers:");
1510
1324
  const levelLabels = { global: "Global ", project: "Project", local: "Local " };
1511
1325
  for (const l of layers) {
1512
- console.log(` ${levelLabels[l.level] ?? l.level} : ${l.filePath} (${l.charCount} chars)`);
1326
+ console.log(` ${levelLabels[l.level] ?? l.level} : ${l.displayPath ?? l.filePath} (${l.charCount} chars${l.truncated ? ", truncated" : ""})`);
1513
1327
  }
1514
1328
  const totalChars = layers.reduce((s, l) => s + l.charCount, 0);
1515
1329
  console.log(` Total : ${totalChars} chars (${layers.length} layer${layers.length > 1 ? "s" : ""})`);
1516
1330
  } else {
1517
1331
  console.log(" No context files loaded.");
1518
1332
  console.log(" Hierarchical context search order:");
1519
- console.log(" 1. Global : ~/.aicli/AICLI.md (or CLAUDE.md)");
1520
- console.log(" 2. Project : <git-root>/AICLI.md");
1521
- console.log(" 3. Local : <cwd>/AICLI.md (when cwd \u2260 project root)");
1333
+ console.log(" 1. Global : ~/.aicli/AICLI.override.md, AGENTS.override.md, AICLI.md, CLAUDE.md, or AGENTS.md");
1334
+ console.log(" 2. Project : <git-root>/same candidate order");
1335
+ console.log(" 3. Local : <cwd>/same candidate order (when cwd \u2260 project root)");
1522
1336
  }
1523
1337
  console.log();
1524
1338
  }
@@ -1540,6 +1354,8 @@ No commands match "${filter}".
1540
1354
  console.log(` Provider : ${ctx.getCurrentProvider()}`);
1541
1355
  console.log(` Model : ${currentModel}`);
1542
1356
  console.log(` Ctx Win : ${ctxStr}`);
1357
+ console.log(" PermProf : " + (ctx.config.get("defaultPermissionProfile") ?? "legacy"));
1358
+ console.log(" Network : " + (ctx.config.get("networkPolicy")?.enabled ?? false ? "on" : "off"));
1543
1359
  console.log(` Messages : ${session?.messages.length ?? 0}`);
1544
1360
  console.log(` Session : ${session?.id.slice(0, 8) ?? "none"}`);
1545
1361
  if (layers.length > 0) {
@@ -1830,16 +1646,16 @@ No tools match "${filter}".
1830
1646
  usage: "/mcp [reconnect [serverId] | trust-project]",
1831
1647
  async execute(args, ctx) {
1832
1648
  if (args[0] === "trust-project") {
1833
- const { join: join6 } = await import("path");
1834
- const { existsSync: existsSync6 } = await import("fs");
1649
+ const { join: join5 } = await import("path");
1650
+ const { existsSync: existsSync5 } = await import("fs");
1835
1651
  const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
1836
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-VTZLKDAG.js");
1652
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-AWTIQIWG.js");
1837
1653
  const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
1838
1654
  const cwd = process.cwd();
1839
1655
  const projectRoot = getGitRoot2(cwd) ?? cwd;
1840
- const mcpPath = join6(projectRoot, MCP_PROJECT_CONFIG_NAME2);
1656
+ const mcpPath = join5(projectRoot, MCP_PROJECT_CONFIG_NAME2);
1841
1657
  console.log();
1842
- if (!existsSync6(mcpPath)) {
1658
+ if (!existsSync5(mcpPath)) {
1843
1659
  console.log(theme.dim(` No .mcp.json in ${projectRoot}.`));
1844
1660
  console.log();
1845
1661
  return;
@@ -2172,9 +1988,9 @@ No tools match "${filter}".
2172
1988
  usage: "/init [--force]",
2173
1989
  async execute(args, ctx) {
2174
1990
  const cwd = process.cwd();
2175
- const targetPath = join3(cwd, "AICLI.md");
1991
+ const targetPath = join2(cwd, "AICLI.md");
2176
1992
  const force = args.includes("--force");
2177
- if (existsSync3(targetPath) && !force) {
1993
+ if (existsSync2(targetPath) && !force) {
2178
1994
  ctx.renderer.printInfo(`AICLI.md already exists at ${targetPath}`);
2179
1995
  ctx.renderer.printInfo("Use /init --force to overwrite, or edit it manually.");
2180
1996
  return;
@@ -2892,7 +2708,7 @@ ${hint}` : "")
2892
2708
  usage: "/test [command|filter]",
2893
2709
  async execute(args, ctx) {
2894
2710
  try {
2895
- const { executeTests } = await import("./run-tests-NDS2IEMK.js");
2711
+ const { executeTests } = await import("./run-tests-3MHWUF43.js");
2896
2712
  const argStr = args.join(" ").trim();
2897
2713
  let testArgs = {};
2898
2714
  if (argStr) {
@@ -3050,7 +2866,7 @@ ${hint}` : "")
3050
2866
  description: "Persistent memory (memory.md) + chat memory recall index (v0.4.89+)",
3051
2867
  usage: "/memory [show|add <text>|clear|path|rebuild|refresh|status|recall <query>|index-clear]",
3052
2868
  async execute(args, ctx) {
3053
- const memoryFile = join3(ctx.config.getConfigDir(), MEMORY_FILE_NAME);
2869
+ const memoryFile = join2(ctx.config.getConfigDir(), MEMORY_FILE_NAME);
3054
2870
  const sub = args[0] ?? "show";
3055
2871
  if (sub === "rebuild" || sub === "refresh") {
3056
2872
  const full = sub === "rebuild";
@@ -3134,11 +2950,11 @@ ${hint}` : "")
3134
2950
  return;
3135
2951
  }
3136
2952
  if (sub === "show" || sub === "view") {
3137
- if (!existsSync3(memoryFile)) {
2953
+ if (!existsSync2(memoryFile)) {
3138
2954
  ctx.renderer.printInfo("Memory is empty (memory.md not found)");
3139
2955
  return;
3140
2956
  }
3141
- const content = readFileSync2(memoryFile, "utf-8").trim();
2957
+ const content = readFileSync(memoryFile, "utf-8").trim();
3142
2958
  if (!content) {
3143
2959
  ctx.renderer.printInfo("Memory is empty");
3144
2960
  return;
@@ -3163,14 +2979,14 @@ ${text}
3163
2979
  `;
3164
2980
  try {
3165
2981
  mkdirSync2(dirname2(memoryFile), { recursive: true });
3166
- const previous = existsSync3(memoryFile) ? readFileSync2(memoryFile, "utf-8") : "";
2982
+ const previous = existsSync2(memoryFile) ? readFileSync(memoryFile, "utf-8") : "";
3167
2983
  atomicWriteFileSync(memoryFile, previous + entry);
3168
2984
  ctx.renderer.printSuccess(`Added to memory: "${text.slice(0, 60)}${text.length > 60 ? "\u2026" : ""}"`);
3169
2985
  } catch (err) {
3170
2986
  ctx.renderer.renderError(`Failed to write memory: ${err instanceof Error ? err.message : String(err)}`);
3171
2987
  }
3172
2988
  } else if (sub === "clear") {
3173
- if (!existsSync3(memoryFile)) {
2989
+ if (!existsSync2(memoryFile)) {
3174
2990
  ctx.renderer.printInfo("Memory is already empty");
3175
2991
  return;
3176
2992
  }
@@ -3206,20 +3022,20 @@ ${text}
3206
3022
  console.log(theme.heading("Config Files:"));
3207
3023
  console.log(` Dir: ${theme.accent(configDir)}`);
3208
3024
  const checkFile = (label, filePath) => {
3209
- const exists = existsSync3(filePath);
3025
+ const exists = existsSync2(filePath);
3210
3026
  const icon = exists ? theme.success("\u2713") : theme.dim("\u2013");
3211
3027
  let extra = "";
3212
3028
  if (exists) {
3213
3029
  try {
3214
- extra = theme.dim(` (${statSync3(filePath).size} bytes)`);
3030
+ extra = theme.dim(` (${statSync2(filePath).size} bytes)`);
3215
3031
  } catch {
3216
3032
  }
3217
3033
  }
3218
3034
  console.log(` ${icon} ${label.padEnd(14)} ${exists ? filePath : theme.dim("(not found)")}${extra}`);
3219
3035
  };
3220
- checkFile("config.json", join3(configDir, "config.json"));
3221
- checkFile("memory.md", join3(configDir, MEMORY_FILE_NAME));
3222
- checkFile("dev-state.md", join3(configDir, "dev-state.md"));
3036
+ checkFile("config.json", join2(configDir, "config.json"));
3037
+ checkFile("memory.md", join2(configDir, MEMORY_FILE_NAME));
3038
+ checkFile("dev-state.md", join2(configDir, "dev-state.md"));
3223
3039
  console.log();
3224
3040
  const mcpManager = ctx.getMcpManager();
3225
3041
  if (mcpManager) {
@@ -3383,9 +3199,9 @@ ${text}
3383
3199
  let newFiles = 0;
3384
3200
  let modifiedFiles = 0;
3385
3201
  for (const [filePath, { earliest }] of fileMap) {
3386
- const currentContent = existsSync3(filePath) ? (() => {
3202
+ const currentContent = existsSync2(filePath) ? (() => {
3387
3203
  try {
3388
- return readFileSync2(filePath, "utf-8");
3204
+ return readFileSync(filePath, "utf-8");
3389
3205
  } catch {
3390
3206
  return null;
3391
3207
  }
@@ -3488,6 +3304,8 @@ Summary: ${fileMap.size} file(s) \u2014 ${newFiles} new, ${modifiedFiles} modifi
3488
3304
  console.log(` redactOnSave : ${security.redactOnSave ? theme.success("on") : theme.warning("off")}`);
3489
3305
  console.log(` redactOnSend : ${security.redactOnSend ? theme.success("on") : theme.dim("off")}`);
3490
3306
  console.log(` mode : ${security.mode}`);
3307
+ console.log(" profile : " + (ctx.config.get("defaultPermissionProfile") ?? "legacy"));
3308
+ console.log(" network : " + (ctx.config.get("networkPolicy")?.enabled ?? false ? "on" : "off"));
3491
3309
  console.log(` patterns : ${DEFAULT_PATTERNS.length} built-in + ${security.customPatterns?.length ?? 0} custom`);
3492
3310
  console.log(` last save : ${ctx.sessions.lastRedactionHits} hit(s) redacted`);
3493
3311
  console.log();
@@ -3511,12 +3329,12 @@ Summary: ${fileMap.size} file(s) \u2014 ${newFiles} new, ${modifiedFiles} modifi
3511
3329
  if (scanAll) {
3512
3330
  const metas = ctx.sessions.listSessions();
3513
3331
  console.log(theme.info(` Scanning ${metas.length} session(s)\u2026`));
3514
- const { readFileSync: readFileSync5 } = await import("fs");
3515
- const { join: join6 } = await import("path");
3332
+ const { readFileSync: readFileSync4 } = await import("fs");
3333
+ const { join: join5 } = await import("path");
3516
3334
  const historyDir = ctx.config.getHistoryDir();
3517
3335
  for (const m of metas) {
3518
3336
  try {
3519
- const content = readFileSync5(join6(historyDir, `${m.id}.json`), "utf-8");
3337
+ const content = readFileSync4(join5(historyDir, `${m.id}.json`), "utf-8");
3520
3338
  const hits2 = scanString(content, opts);
3521
3339
  if (hits2.length) {
3522
3340
  filesWithHits++;
@@ -4201,12 +4019,12 @@ Managing ${displayName} API Key`);
4201
4019
  };
4202
4020
 
4203
4021
  // src/repl/custom-commands.ts
4204
- import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync2, mkdirSync as mkdirSync3 } from "fs";
4205
- import { join as join4, extname } from "path";
4022
+ import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync, mkdirSync as mkdirSync3 } from "fs";
4023
+ import { join as join3, extname } from "path";
4206
4024
  function parseCommandFile(filePath) {
4207
4025
  let content;
4208
4026
  try {
4209
- content = readFileSync3(filePath, "utf-8");
4027
+ content = readFileSync2(filePath, "utf-8");
4210
4028
  } catch {
4211
4029
  return null;
4212
4030
  }
@@ -4230,7 +4048,7 @@ function expandTemplate(template, args) {
4230
4048
  result = result.replace(/\{\{input\}\}/g, args.join(" "));
4231
4049
  result = result.replace(/\{\{file:([^}]+)\}\}/g, (_m, p) => {
4232
4050
  try {
4233
- return readFileSync3(p.trim(), "utf-8");
4051
+ return readFileSync2(p.trim(), "utf-8");
4234
4052
  } catch {
4235
4053
  return `[Error: cannot read ${p.trim()}]`;
4236
4054
  }
@@ -4255,14 +4073,14 @@ var CustomCommandManager = class {
4255
4073
  commands = /* @__PURE__ */ new Map();
4256
4074
  loadCommands() {
4257
4075
  this.commands.clear();
4258
- if (!existsSync4(this.commandsDir)) {
4076
+ if (!existsSync3(this.commandsDir)) {
4259
4077
  mkdirSync3(this.commandsDir, { recursive: true });
4260
4078
  return 0;
4261
4079
  }
4262
4080
  let count = 0;
4263
- for (const file of readdirSync2(this.commandsDir)) {
4081
+ for (const file of readdirSync(this.commandsDir)) {
4264
4082
  if (extname(file) !== ".md") continue;
4265
- const cmd = parseCommandFile(join4(this.commandsDir, file));
4083
+ const cmd = parseCommandFile(join3(this.commandsDir, file));
4266
4084
  if (cmd) {
4267
4085
  this.commands.set(cmd.meta.name, cmd);
4268
4086
  count++;
@@ -4567,17 +4385,17 @@ function parseAtReferences(input2, cwd) {
4567
4385
  const absPath = resolve2(cwd, rawPath);
4568
4386
  const ext = extname2(rawPath).toLowerCase();
4569
4387
  const mime = IMAGE_MIME[ext];
4570
- if (!existsSync5(absPath)) {
4388
+ if (!existsSync4(absPath)) {
4571
4389
  refs.push({ path: rawPath, type: "notfound" });
4572
4390
  continue;
4573
4391
  }
4574
4392
  if (mime) {
4575
- const fileSize = statSync4(absPath).size;
4393
+ const fileSize = statSync3(absPath).size;
4576
4394
  if (fileSize > MAX_IMAGE_BYTES) {
4577
4395
  refs.push({ path: rawPath, type: "toolarge" });
4578
4396
  continue;
4579
4397
  }
4580
- const data = readFileSync4(absPath).toString("base64");
4398
+ const data = readFileSync3(absPath).toString("base64");
4581
4399
  imageParts.push({
4582
4400
  type: "image_url",
4583
4401
  image_url: { url: `data:${mime};base64,${data}` }
@@ -4585,7 +4403,7 @@ function parseAtReferences(input2, cwd) {
4585
4403
  refs.push({ path: rawPath, type: "image" });
4586
4404
  textBody = textBody.replace(match[0], "").trim();
4587
4405
  } else {
4588
- const content = readFileSync4(absPath, "utf-8");
4406
+ const content = readFileSync3(absPath, "utf-8");
4589
4407
  const inlined = `
4590
4408
 
4591
4409
  [File: ${rawPath}]
@@ -4643,10 +4461,17 @@ var Repl = class {
4643
4461
  this.toolRegistry = new ToolRegistry();
4644
4462
  this.toolExecutor = new ToolExecutor(this.toolRegistry);
4645
4463
  this.toolExecutor.setReadline(this.rl);
4464
+ const permissionProfileName = this.config.get("defaultPermissionProfile") ?? "legacy";
4465
+ const permissionProfiles = this.config.get("permissionProfiles") ?? {};
4646
4466
  this.toolExecutor.setConfig({
4647
4467
  hookConfig: this.config.get("hooks") ?? void 0,
4648
4468
  permissionRules: this.config.get("permissionRules") ?? [],
4649
- defaultPermission: this.config.get("defaultPermission") ?? "confirm"
4469
+ defaultPermission: this.config.get("defaultPermission") ?? "confirm",
4470
+ permissionProfileName,
4471
+ permissionProfile: permissionProfiles[permissionProfileName],
4472
+ allowedPermissionProfiles: this.config.get("allowedPermissionProfiles") ?? [],
4473
+ workspaceRoot: process.cwd(),
4474
+ networkPolicy: this.config.get("networkPolicy")
4650
4475
  });
4651
4476
  if (this.config.get("alwaysYolo") === true) {
4652
4477
  this.toolExecutor.sessionAutoApprove = true;
@@ -4677,6 +4502,7 @@ var Repl = class {
4677
4502
  };
4678
4503
  /** 当前加载的层级上下文(全局/项目/子目录) */
4679
4504
  contextLayers = [];
4505
+ contextLoadResult = null;
4680
4506
  /** 本次会话累计 token 用量 */
4681
4507
  sessionTokenUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
4682
4508
  /** Fold a single-request TokenUsage (with optional cache fields) into sessionTokenUsage + cost tracker.
@@ -4803,19 +4629,19 @@ var Repl = class {
4803
4629
  if (depth > 2 || entryCount >= MAX_TREE_ENTRIES) return;
4804
4630
  let entries;
4805
4631
  try {
4806
- entries = readdirSync3(dir);
4632
+ entries = readdirSync2(dir);
4807
4633
  } catch {
4808
4634
  return;
4809
4635
  }
4810
4636
  const filtered = entries.filter((e) => !SKIP_DIRS_SET.has(e));
4811
4637
  for (let i = 0; i < filtered.length && entryCount < MAX_TREE_ENTRIES; i++) {
4812
4638
  const name = filtered[i];
4813
- const fullPath = join5(dir, name);
4639
+ const fullPath = join4(dir, name);
4814
4640
  const isLast = i === filtered.length - 1;
4815
4641
  const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
4816
4642
  let isDir;
4817
4643
  try {
4818
- isDir = statSync4(fullPath).isDirectory();
4644
+ isDir = statSync3(fullPath).isDirectory();
4819
4645
  } catch {
4820
4646
  continue;
4821
4647
  }
@@ -4837,17 +4663,17 @@ ${treeLines.join("\n")}`
4837
4663
  if (totalChars >= MAX_TOTAL_CHARS) return;
4838
4664
  let entries;
4839
4665
  try {
4840
- entries = readdirSync3(dir);
4666
+ entries = readdirSync2(dir);
4841
4667
  } catch {
4842
4668
  return;
4843
4669
  }
4844
4670
  for (const name of entries) {
4845
4671
  if (totalChars >= MAX_TOTAL_CHARS) break;
4846
4672
  if (SKIP_DIRS_SET.has(name)) continue;
4847
- const fullPath = join5(dir, name);
4673
+ const fullPath = join4(dir, name);
4848
4674
  let st;
4849
4675
  try {
4850
- st = statSync4(fullPath);
4676
+ st = statSync3(fullPath);
4851
4677
  } catch {
4852
4678
  continue;
4853
4679
  }
@@ -4859,7 +4685,7 @@ ${treeLines.join("\n")}`
4859
4685
  if (!TEXT_EXTS.has(ext) && !isSpecial) continue;
4860
4686
  if (st.size > MAX_FILE_CHARS * 3) continue;
4861
4687
  try {
4862
- let content = readFileSync4(fullPath, "utf-8");
4688
+ let content = readFileSync3(fullPath, "utf-8");
4863
4689
  if (content.length > MAX_FILE_CHARS) {
4864
4690
  content = content.slice(0, MAX_FILE_CHARS) + `
4865
4691
  ... (truncated, ${content.length} chars total)`;
@@ -4889,12 +4715,12 @@ ${content}
4889
4715
  */
4890
4716
  addExtraContextDir(dirPath) {
4891
4717
  const absPath = resolve2(dirPath);
4892
- if (!existsSync5(absPath)) {
4718
+ if (!existsSync4(absPath)) {
4893
4719
  return { success: false, charCount: 0, added: false, error: `Directory not found: ${dirPath}` };
4894
4720
  }
4895
4721
  let isDir;
4896
4722
  try {
4897
- isDir = statSync4(absPath).isDirectory();
4723
+ isDir = statSync3(absPath).isDirectory();
4898
4724
  } catch {
4899
4725
  return { success: false, charCount: 0, added: false, error: `Cannot access: ${dirPath}` };
4900
4726
  }
@@ -4917,33 +4743,6 @@ ${content}
4917
4743
  this.extraContextDirs.splice(idx, 1);
4918
4744
  return true;
4919
4745
  }
4920
- /**
4921
- * 在指定目录中按候选文件名列表查找第一个存在且非空的上下文文件。
4922
- * 返回 { filePath, content } 或 null。
4923
- */
4924
- findContextFile(dir, candidates = CONTEXT_FILE_CANDIDATES) {
4925
- for (const candidate of candidates) {
4926
- const fullPath = join5(dir, candidate);
4927
- if (existsSync5(fullPath)) {
4928
- const content = readFileSync4(fullPath, "utf-8").trim();
4929
- if (content) return { filePath: fullPath, content };
4930
- }
4931
- }
4932
- return null;
4933
- }
4934
- /**
4935
- * 加载层级上下文文件系统。
4936
- *
4937
- * 三层级结构(按顺序拼接):
4938
- * 1. 全局层 ~/.aicli/AICLI.md — 所有项目通用的个人偏好
4939
- * 2. 项目层 <git-root>/AICLI.md — 项目级规则(提交到 git 供团队共享)
4940
- * 3. 子目录层 <cwd>/AICLI.md — 当前工作子目录的特定指令
4941
- *
4942
- * contextFile 配置语义:
4943
- * - 'auto'(默认):启用三层级自动发现
4944
- * - false:禁用所有上下文加载
4945
- * - 具体文件名:仅加载 cwd 下的指定文件(回退到旧的单文件行为)
4946
- */
4947
4746
  /**
4948
4747
  * 加载项目级 .mcp.json 文件(位于 git 根目录或 cwd)。
4949
4748
  * 格式兼容 Claude Desktop:`{ "mcpServers": { ... } }`
@@ -4953,10 +4752,10 @@ ${content}
4953
4752
  const cwd = process.cwd();
4954
4753
  const gitRoot = getGitRoot(cwd);
4955
4754
  const projectRoot = gitRoot ?? cwd;
4956
- const mcpPath = join5(projectRoot, MCP_PROJECT_CONFIG_NAME);
4957
- if (!existsSync5(mcpPath)) return null;
4755
+ const mcpPath = join4(projectRoot, MCP_PROJECT_CONFIG_NAME);
4756
+ if (!existsSync4(mcpPath)) return null;
4958
4757
  try {
4959
- const raw = JSON.parse(readFileSync4(mcpPath, "utf-8"));
4758
+ const raw = JSON.parse(readFileSync3(mcpPath, "utf-8"));
4960
4759
  const servers = raw?.mcpServers;
4961
4760
  if (!servers || typeof servers !== "object") {
4962
4761
  process.stderr.write(
@@ -4990,79 +4789,25 @@ ${content}
4990
4789
  }
4991
4790
  }
4992
4791
  loadHierarchicalContext() {
4993
- const setting = this.config.get("contextFile");
4994
- if (setting === false) return { layers: [], mergedContent: "" };
4792
+ const contextConfig = this.config.get("context");
4995
4793
  const cwd = process.cwd();
4996
- if (setting !== "auto") {
4997
- const fullPath = resolve2(cwd, String(setting));
4998
- if (!fullPath.startsWith(resolve2(cwd))) {
4999
- process.stderr.write(
5000
- `[Warning] contextFile path "${setting}" is outside current directory, ignoring.
5001
- `
5002
- );
5003
- return { layers: [], mergedContent: "" };
5004
- }
5005
- if (existsSync5(fullPath)) {
5006
- const content = readFileSync4(fullPath, "utf-8").trim();
5007
- if (content) {
5008
- const layer = {
5009
- level: "project",
5010
- filePath: setting,
5011
- content,
5012
- charCount: content.length
5013
- };
5014
- return { layers: [layer], mergedContent: content };
5015
- }
5016
- }
5017
- return { layers: [], mergedContent: "" };
5018
- }
5019
- const layers = [];
5020
- const configDir = this.config.getConfigDir();
5021
- const globalCtx = this.findContextFile(configDir);
5022
- if (globalCtx) {
5023
- layers.push({
5024
- level: "global",
5025
- filePath: globalCtx.filePath,
5026
- content: globalCtx.content,
5027
- charCount: globalCtx.content.length
5028
- });
5029
- }
5030
4794
  const gitRoot = getGitRoot(cwd);
5031
4795
  const projectRoot = gitRoot ?? cwd;
5032
- const projectCtx = this.findContextFile(projectRoot);
5033
- if (projectCtx) {
5034
- layers.push({
5035
- level: "project",
5036
- filePath: projectCtx.filePath,
5037
- content: projectCtx.content,
5038
- charCount: projectCtx.content.length
5039
- });
5040
- }
5041
- const normalizedCwd = resolve2(cwd);
5042
- const normalizedRoot = resolve2(projectRoot);
5043
- if (normalizedCwd !== normalizedRoot) {
5044
- const localCtx = this.findContextFile(cwd);
5045
- if (localCtx) {
5046
- layers.push({
5047
- level: "local",
5048
- filePath: localCtx.filePath,
5049
- content: localCtx.content,
5050
- charCount: localCtx.content.length
5051
- });
5052
- }
5053
- }
5054
- const mergedContent = layers.map((l) => l.content).join("\n\n---\n\n");
5055
- return { layers, mergedContent };
4796
+ const result = loadContextFiles({
4797
+ cwd,
4798
+ configDir: this.config.getConfigDir(),
4799
+ projectRoot,
4800
+ setting: this.config.get("contextFile"),
4801
+ fallbackFilenames: contextConfig.projectDocFallbackFilenames,
4802
+ maxBytes: contextConfig.projectDocMaxBytes
4803
+ });
4804
+ this.contextLoadResult = result;
4805
+ return { layers: result.layers, mergedContent: result.mergedContent };
5056
4806
  }
5057
- /**
5058
- * 加载持久记忆内容(~/.aicli/memory.md)。
5059
- * 不存在或为空时返回 null。
5060
- * 超过 MEMORY_MAX_CHARS 时只取末尾最新部分。
5061
- */
5062
4807
  loadMemoryContent() {
5063
- const memoryPath = join5(this.config.getConfigDir(), MEMORY_FILE_NAME);
5064
- if (!existsSync5(memoryPath)) return null;
5065
- let content = readFileSync4(memoryPath, "utf-8").trim();
4808
+ const memoryPath = join4(this.config.getConfigDir(), MEMORY_FILE_NAME);
4809
+ if (!existsSync4(memoryPath)) return null;
4810
+ let content = readFileSync3(memoryPath, "utf-8").trim();
5066
4811
  if (!content) return null;
5067
4812
  if (content.length > MEMORY_MAX_CHARS) {
5068
4813
  content = content.slice(-MEMORY_MAX_CHARS);
@@ -5397,6 +5142,11 @@ Session '${this.resumeSessionId}' not found.
5397
5142
  theme.warning(" \u26A1 YOLO mode ON (alwaysYolo=true in config) \u2014 write tools auto-approved; destructive tools still require confirmation.\n")
5398
5143
  );
5399
5144
  }
5145
+ const permissionProfileWarning = formatPermissionProfileWarning(this.config.get("defaultPermissionProfile") ?? "legacy");
5146
+ if (permissionProfileWarning) {
5147
+ process.stdout.write(theme.warning(` ${permissionProfileWarning}
5148
+ `));
5149
+ }
5400
5150
  const cfgOutputCap = this.config.get("maxToolOutputChars");
5401
5151
  if (typeof cfgOutputCap === "number" && cfgOutputCap > 0) setMaxOutputCap(cfgOutputCap);
5402
5152
  if (welcomeModelInfo?.contextWindow) setContextWindow(welcomeModelInfo.contextWindow);
@@ -5450,14 +5200,14 @@ Session '${this.resumeSessionId}' not found.
5450
5200
  process.stdout.write(theme.dim(` \u{1F50C} Plugins loaded: ${pluginCount} tool(s) from plugins/
5451
5201
  `));
5452
5202
  }
5453
- const skillsDir = join5(this.config.getConfigDir(), SKILLS_DIR_NAME);
5203
+ const skillsDir = join4(this.config.getConfigDir(), SKILLS_DIR_NAME);
5454
5204
  this.skillManager = new SkillManager(skillsDir, this.config.get("ui").skillSizeWarn);
5455
5205
  const skillCount = this.skillManager.loadSkills();
5456
5206
  if (skillCount > 0) {
5457
5207
  process.stdout.write(theme.dim(` \u{1F3AF} Skills: ${skillCount} available (use /skill to manage)
5458
5208
  `));
5459
5209
  }
5460
- const commandsDir = join5(this.config.getConfigDir(), CUSTOM_COMMANDS_DIR_NAME);
5210
+ const commandsDir = join4(this.config.getConfigDir(), CUSTOM_COMMANDS_DIR_NAME);
5461
5211
  this.customCommandManager = new CustomCommandManager(commandsDir);
5462
5212
  const customCmdCount = this.customCommandManager.loadCommands();
5463
5213
  if (customCmdCount > 0) {
@@ -6093,15 +5843,15 @@ Session '${this.resumeSessionId}' not found.
6093
5843
  const dir = normalized.includes("/") ? dirname3(normalized) : ".";
6094
5844
  const prefix = normalized.includes("/") ? basename2(normalized) : normalized;
6095
5845
  const absDir = resolve2(process.cwd(), dir);
6096
- if (!existsSync5(absDir)) return [];
6097
- const entries = readdirSync3(absDir);
5846
+ if (!existsSync4(absDir)) return [];
5847
+ const entries = readdirSync2(absDir);
6098
5848
  const results = [];
6099
5849
  for (const entry of entries) {
6100
5850
  if (entry.startsWith(".")) continue;
6101
5851
  if (!entry.toLowerCase().startsWith(prefix.toLowerCase())) continue;
6102
5852
  try {
6103
- const fullPath = join5(absDir, entry);
6104
- const stat = statSync4(fullPath);
5853
+ const fullPath = join4(absDir, entry);
5854
+ const stat = statSync3(fullPath);
6105
5855
  const rel = dir === "." ? entry : `${dir}/${entry}`;
6106
5856
  results.push(stat.isDirectory() ? `${rel}/` : rel);
6107
5857
  } catch {
@@ -7270,7 +7020,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7270
7020
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7271
7021
  process.exit(1);
7272
7022
  }
7273
- const { startWebServer } = await import("./server-4URLNC7P.js");
7023
+ const { startWebServer } = await import("./server-BTSKOPQI.js");
7274
7024
  await startWebServer({ port, host: options.host });
7275
7025
  });
7276
7026
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
@@ -7437,16 +7187,16 @@ program.command("sessions").description("List recent conversation sessions").opt
7437
7187
  console.log(footer + "\n");
7438
7188
  });
7439
7189
  program.command("usage").description("Show token + cost usage grouped by provider/model (cross-session)").option("--days <n>", "Only the last N days (inclusive of today)").option("--month <ym>", "Only a specific month, format YYYY-MM (e.g. 2026-06)").option("--json", "Output as JSON (for scripting)").action(async (options) => {
7440
- const { runUsageCli } = await import("./usage-ZNCKGF6U.js");
7190
+ const { runUsageCli } = await import("./usage-T2P6FTE7.js");
7441
7191
  await runUsageCli(options);
7442
7192
  });
7443
7193
  program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
7444
- const { runDoctorCli } = await import("./doctor-cli-IU6FSVLF.js");
7194
+ const { runDoctorCli } = await import("./doctor-cli-SSI6ETFT.js");
7445
7195
  await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
7446
7196
  });
7447
7197
  program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
7448
7198
  try {
7449
- const batch = await import("./batch-45WLJUVP.js");
7199
+ const batch = await import("./batch-DE4RXKZD.js");
7450
7200
  switch (action) {
7451
7201
  case "submit":
7452
7202
  if (!arg) {
@@ -7489,7 +7239,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7489
7239
  }
7490
7240
  });
7491
7241
  program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
7492
- const { startMcpServer } = await import("./server-72SAPLPS.js");
7242
+ const { startMcpServer } = await import("./server-7USZJJAH.js");
7493
7243
  await startMcpServer({
7494
7244
  allowDestructive: !!options.allowDestructive,
7495
7245
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7498,7 +7248,7 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
7498
7248
  });
7499
7249
  });
7500
7250
  program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number; diff fetched via `gh pr diff <num>`", (v) => parseInt(v, 10)).option("--base <ref>", "Base ref for `git diff <ref>...HEAD` (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (options) => {
7501
- const { runCi } = await import("./ci-PUE3MQ7J.js");
7251
+ const { runCi } = await import("./ci-XMUEX526.js");
7502
7252
  const result = await runCi({
7503
7253
  pr: options.pr,
7504
7254
  base: options.base,
@@ -7644,7 +7394,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7644
7394
  }),
7645
7395
  config.get("customProviders")
7646
7396
  );
7647
- const { startHub } = await import("./hub-4ETK4R2J.js");
7397
+ const { startHub } = await import("./hub-INUJND2G.js");
7648
7398
  await startHub(
7649
7399
  {
7650
7400
  topic: topic ?? "",
@@ -7744,7 +7494,27 @@ ${options.prompt}` : options.prompt;
7744
7494
  const maxTokens = modelParams.maxTokens ?? DEFAULT_MAX_TOKENS;
7745
7495
  const timeout = modelParams.timeout ?? 6e4;
7746
7496
  const temperature = modelParams.temperature;
7747
- const systemPrompt = options.system ?? config.get("session").systemPrompt ?? void 0;
7497
+ let systemPrompt = options.system;
7498
+ if (!systemPrompt) {
7499
+ const contextConfig = config.get("context");
7500
+ const cwd = process.cwd();
7501
+ const gitRoot = getGitRoot(cwd);
7502
+ const loaded = loadContextFiles({
7503
+ cwd,
7504
+ configDir: config.getConfigDir(),
7505
+ projectRoot: gitRoot ?? cwd,
7506
+ setting: config.get("contextFile"),
7507
+ fallbackFilenames: contextConfig.projectDocFallbackFilenames,
7508
+ maxBytes: contextConfig.projectDocMaxBytes
7509
+ });
7510
+ const parts = [
7511
+ config.get("session").systemPrompt,
7512
+ loaded.mergedContent ? `# Project Context
7513
+
7514
+ ${loaded.mergedContent}` : void 0
7515
+ ].filter(Boolean);
7516
+ systemPrompt = parts.length > 0 ? parts.join("\n\n---\n\n") : void 0;
7517
+ }
7748
7518
  const isStreamingJson = options.outputFormat === "streaming-json";
7749
7519
  const useStream = options.stream !== false && !options.json && !isStreamingJson;
7750
7520
  try {