jinzd-ai-cli 0.4.219 → 0.4.221

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 (33) hide show
  1. package/README.md +101 -99
  2. package/README.zh-CN.md +40 -38
  3. package/dist/{batch-CHAPYRVM.js → batch-LSWWSINO.js} +3 -3
  4. package/dist/{chunk-GEN4O5QH.js → chunk-4AX2MEJA.js} +4 -4
  5. package/dist/{chunk-64NUW3WL.js → chunk-4MOBTDS6.js} +1 -1
  6. package/dist/{chunk-4ZN6L6K5.js → chunk-5BY4XICU.js} +6 -5
  7. package/dist/{chunk-MC34ISJU.js → chunk-5GTWZ4QF.js} +30 -6
  8. package/dist/{chunk-TZQHYZKT.js → chunk-5ULLIOVC.js} +1 -0
  9. package/dist/{chunk-3I4RZYYO.js → chunk-6SKMGONY.js} +6 -5
  10. package/dist/{chunk-QUFYBR6Q.js → chunk-BYQEWWA4.js} +2 -2
  11. package/dist/{chunk-VADHBW7T.js → chunk-OULVIHCT.js} +1 -1
  12. package/dist/{chunk-KIEZBTVD.js → chunk-PMTEZTXP.js} +2 -2
  13. package/dist/{chunk-QMXC327F.js → chunk-PUBCJF7E.js} +213 -175
  14. package/dist/{chunk-U7KVU25H.js → chunk-QDN5G7JN.js} +1 -1
  15. package/dist/{chunk-GGKQHPB3.js → chunk-QKJ3MAP7.js} +1 -1
  16. package/dist/{chunk-A4GUGWEY.js → chunk-QT5FA2ZY.js} +1 -1
  17. package/dist/{chunk-EGMORH5W.js → chunk-S5EEKS4P.js} +1 -1
  18. package/dist/{ci-QPP66T53.js → ci-OIFTAJKR.js} +6 -6
  19. package/dist/{ci-format-5S3EEYRK.js → ci-format-H6BLMWQ5.js} +2 -2
  20. package/dist/{constants-FJOLBABC.js → constants-EJRLT7DM.js} +1 -1
  21. package/dist/{doctor-cli-KB2LUDF3.js → doctor-cli-EXC7YU5D.js} +6 -6
  22. package/dist/electron-server.js +558 -466
  23. package/dist/{hub-DIM7SKKY.js → hub-HQDRQBNM.js} +2 -2
  24. package/dist/index.js +376 -45
  25. package/dist/{persist-L54DPLI7.js → persist-GTBEKVFL.js} +2 -2
  26. package/dist/{pr-YQGH72N6.js → pr-VSZNVKBS.js} +8 -7
  27. package/dist/{run-tests-O76JIBCW.js → run-tests-55JGEZYL.js} +1 -1
  28. package/dist/{run-tests-GEZSSNJM.js → run-tests-OYXK27XH.js} +2 -2
  29. package/dist/{server-5E2AIXVX.js → server-JKNIZ5CB.js} +54 -22
  30. package/dist/{server-Z6O3G2LY.js → server-XX6UM4ZQ.js} +5 -5
  31. package/dist/{task-orchestrator-5HBW4O64.js → task-orchestrator-F3DUIACA.js} +5 -5
  32. package/dist/{usage-BMK6M5U3.js → usage-4KFX3HTK.js} +3 -3
  33. package/package.json +1 -1
@@ -38,7 +38,7 @@ import {
38
38
  VERSION,
39
39
  buildUserIdentityPrompt,
40
40
  runTestsTool
41
- } from "./chunk-EGMORH5W.js";
41
+ } from "./chunk-S5EEKS4P.js";
42
42
  import {
43
43
  hasSemanticIndex,
44
44
  semanticSearch
@@ -64,8 +64,8 @@ import {
64
64
  import express from "express";
65
65
  import { createServer } from "http";
66
66
  import { WebSocketServer } from "ws";
67
- import { join as join25, dirname as dirname8, resolve as resolve11, relative as relative4, sep as sep3 } from "path";
68
- import { existsSync as existsSync30, readFileSync as readFileSync23, readdirSync as readdirSync15, statSync as statSync12, realpathSync } from "fs";
67
+ import { join as join26, dirname as dirname9, resolve as resolve11, relative as relative4, sep as sep3 } from "path";
68
+ import { existsSync as existsSync31, readFileSync as readFileSync24, readdirSync as readdirSync15, statSync as statSync13, realpathSync } from "fs";
69
69
  import { networkInterfaces } from "os";
70
70
 
71
71
  // src/config/config-manager.ts
@@ -386,6 +386,7 @@ var ENV_KEY_MAP = {
386
386
  zhipu: "AICLI_API_KEY_ZHIPU",
387
387
  kimi: "AICLI_API_KEY_KIMI",
388
388
  minimax: "AICLI_API_KEY_MINIMAX",
389
+ qwen: "AICLI_API_KEY_QWEN",
389
390
  openai: "AICLI_API_KEY_OPENAI",
390
391
  openrouter: "AICLI_API_KEY_OPENROUTER",
391
392
  "google-search": "AICLI_API_KEY_GOOGLESEARCH",
@@ -1297,6 +1298,23 @@ var ClaudeProvider = class extends BaseProvider {
1297
1298
  async listModels() {
1298
1299
  return this.info.models;
1299
1300
  }
1301
+ async refreshModels() {
1302
+ try {
1303
+ const response = await this.client.models.list();
1304
+ const data = response.data ?? [];
1305
+ return data.map((model) => ({
1306
+ id: model.id,
1307
+ displayName: model.id,
1308
+ contextWindow: 2e5,
1309
+ supportsStreaming: true,
1310
+ source: "live",
1311
+ available: true,
1312
+ metadataUnknown: true
1313
+ }));
1314
+ } catch (err) {
1315
+ throw this.wrapError(err);
1316
+ }
1317
+ }
1300
1318
  wrapError(err) {
1301
1319
  if (err instanceof Anthropic.AuthenticationError) {
1302
1320
  return new AuthError("claude");
@@ -3088,6 +3106,22 @@ var OpenAICompatibleProvider = class extends BaseProvider {
3088
3106
  async listModels() {
3089
3107
  return this.info.models;
3090
3108
  }
3109
+ async refreshModels() {
3110
+ try {
3111
+ const response = await this.client.models.list();
3112
+ return response.data.map((model) => ({
3113
+ id: model.id,
3114
+ displayName: model.id,
3115
+ contextWindow: 128e3,
3116
+ supportsStreaming: true,
3117
+ source: "live",
3118
+ available: true,
3119
+ metadataUnknown: true
3120
+ }));
3121
+ } catch (err) {
3122
+ throw this.wrapError(err);
3123
+ }
3124
+ }
3091
3125
  wrapError(err) {
3092
3126
  if (err instanceof OpenAI.AuthenticationError) {
3093
3127
  return new AuthError(this.info.id);
@@ -3278,54 +3312,6 @@ var ZhipuProvider = class extends OpenAICompatibleProvider {
3278
3312
  displayName: "GLM-4.6 (200K, \u4E2D\u6587\u5199\u4F5C\u63A8\u8350)",
3279
3313
  contextWindow: 204800,
3280
3314
  supportsStreaming: true
3281
- },
3282
- {
3283
- id: "glm-4.6v",
3284
- displayName: "GLM-4.6V (Vision + Thinking)",
3285
- contextWindow: 131072,
3286
- supportsStreaming: true,
3287
- supportsThinking: true
3288
- },
3289
- // ── GLM-Z1 推理系列 ──
3290
- {
3291
- id: "glm-z1",
3292
- displayName: "GLM-Z1 (Reasoning Flagship)",
3293
- contextWindow: 131072,
3294
- supportsStreaming: true,
3295
- supportsThinking: true
3296
- },
3297
- {
3298
- id: "glm-z1-air",
3299
- displayName: "GLM-Z1 Air (Lightweight Reasoning)",
3300
- contextWindow: 131072,
3301
- supportsStreaming: true,
3302
- supportsThinking: true
3303
- },
3304
- {
3305
- id: "glm-z1-flash",
3306
- displayName: "GLM-Z1 Flash (Free Reasoning)",
3307
- contextWindow: 128e3,
3308
- supportsStreaming: true,
3309
- supportsThinking: true
3310
- },
3311
- // ── GLM-4 系列(稳定,价格低) ──
3312
- {
3313
- id: "glm-4-plus",
3314
- displayName: "GLM-4 Plus",
3315
- contextWindow: 128e3,
3316
- supportsStreaming: true
3317
- },
3318
- {
3319
- id: "glm-4-air",
3320
- displayName: "GLM-4 Air",
3321
- contextWindow: 128e3,
3322
- supportsStreaming: true
3323
- },
3324
- {
3325
- id: "glm-4-flash",
3326
- displayName: "GLM-4 Flash (Free)",
3327
- contextWindow: 128e3,
3328
- supportsStreaming: true
3329
3315
  }
3330
3316
  ]
3331
3317
  };
@@ -3362,8 +3348,7 @@ var KimiProvider = class _KimiProvider extends OpenAICompatibleProvider {
3362
3348
  "kimi-k2.6",
3363
3349
  "kimi-k2.5",
3364
3350
  "kimi-k2-thinking",
3365
- "kimi-k2-thinking-turbo",
3366
- "kimi-k2-0905-preview"
3351
+ "kimi-k2-thinking-turbo"
3367
3352
  ]);
3368
3353
  /** 对需要固定 temperature=1 的模型自动修正请求。 */
3369
3354
  fixTemp(req) {
@@ -3411,51 +3396,6 @@ var KimiProvider = class _KimiProvider extends OpenAICompatibleProvider {
3411
3396
  displayName: "Kimi K2 Thinking Turbo",
3412
3397
  contextWindow: 262144,
3413
3398
  supportsStreaming: true
3414
- },
3415
- // Kimi K2 Preview 系列
3416
- {
3417
- id: "kimi-k2-0905-preview",
3418
- displayName: "Kimi K2 0905 Preview",
3419
- contextWindow: 262144,
3420
- supportsStreaming: true
3421
- },
3422
- // moonshot-v1 系列(老一代,稳定)
3423
- {
3424
- id: "moonshot-v1-8k",
3425
- displayName: "Moonshot V1 8K",
3426
- contextWindow: 8192,
3427
- supportsStreaming: true
3428
- },
3429
- {
3430
- id: "moonshot-v1-32k",
3431
- displayName: "Moonshot V1 32K",
3432
- contextWindow: 32768,
3433
- supportsStreaming: true
3434
- },
3435
- {
3436
- id: "moonshot-v1-128k",
3437
- displayName: "Moonshot V1 128K",
3438
- contextWindow: 131072,
3439
- supportsStreaming: true
3440
- },
3441
- // moonshot-v1 Vision 系列(支持图片输入)
3442
- {
3443
- id: "moonshot-v1-8k-vision-preview",
3444
- displayName: "Moonshot V1 8K Vision",
3445
- contextWindow: 8192,
3446
- supportsStreaming: true
3447
- },
3448
- {
3449
- id: "moonshot-v1-32k-vision-preview",
3450
- displayName: "Moonshot V1 32K Vision",
3451
- contextWindow: 32768,
3452
- supportsStreaming: true
3453
- },
3454
- {
3455
- id: "moonshot-v1-128k-vision-preview",
3456
- displayName: "Moonshot V1 128K Vision",
3457
- contextWindow: 131072,
3458
- supportsStreaming: true
3459
3399
  }
3460
3400
  ]
3461
3401
  };
@@ -3581,23 +3521,44 @@ var MiniMaxProvider = class extends OpenAICompatibleProvider {
3581
3521
  displayName: "MiniMax M2.5 Highspeed",
3582
3522
  contextWindow: 1e6,
3583
3523
  supportsStreaming: true
3524
+ }
3525
+ ]
3526
+ };
3527
+ };
3528
+
3529
+ // src/providers/qwen.ts
3530
+ var QwenProvider = class extends OpenAICompatibleProvider {
3531
+ defaultBaseUrl = "https://dashscope.aliyuncs.com/compatible-mode/v1";
3532
+ info = {
3533
+ id: "qwen",
3534
+ displayName: "Qwen (Alibaba Cloud)",
3535
+ defaultModel: "qwen-plus",
3536
+ apiKeyEnvVar: "AICLI_API_KEY_QWEN",
3537
+ requiresApiKey: true,
3538
+ baseUrl: this.defaultBaseUrl,
3539
+ models: [
3540
+ {
3541
+ id: "qwen-plus",
3542
+ displayName: "Qwen Plus",
3543
+ contextWindow: 131072,
3544
+ supportsStreaming: true
3584
3545
  },
3585
3546
  {
3586
- id: "MiniMax-M2.1",
3587
- displayName: "MiniMax M2.1",
3588
- contextWindow: 1e6,
3547
+ id: "qwen-max",
3548
+ displayName: "Qwen Max",
3549
+ contextWindow: 131072,
3589
3550
  supportsStreaming: true
3590
3551
  },
3591
3552
  {
3592
- id: "MiniMax-M2.1-highspeed",
3593
- displayName: "MiniMax M2.1 Highspeed",
3594
- contextWindow: 1e6,
3553
+ id: "qwen-turbo",
3554
+ displayName: "Qwen Turbo",
3555
+ contextWindow: 131072,
3595
3556
  supportsStreaming: true
3596
3557
  },
3597
3558
  {
3598
- id: "MiniMax-M2",
3599
- displayName: "MiniMax M2",
3600
- contextWindow: 1e6,
3559
+ id: "qwen-coder-plus",
3560
+ displayName: "Qwen Coder Plus",
3561
+ contextWindow: 131072,
3601
3562
  supportsStreaming: true
3602
3563
  }
3603
3564
  ]
@@ -3610,7 +3571,7 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
3610
3571
  info = {
3611
3572
  id: "openai",
3612
3573
  displayName: "OpenAI",
3613
- defaultModel: "gpt-4o",
3574
+ defaultModel: "gpt-5.4",
3614
3575
  apiKeyEnvVar: "AICLI_API_KEY_OPENAI",
3615
3576
  requiresApiKey: true,
3616
3577
  baseUrl: this.defaultBaseUrl,
@@ -3666,19 +3627,6 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
3666
3627
  contextWindow: 1048576,
3667
3628
  supportsStreaming: true
3668
3629
  },
3669
- // GPT-4o 系列(128K 上下文)
3670
- {
3671
- id: "gpt-4o",
3672
- displayName: "GPT-4o",
3673
- contextWindow: 128e3,
3674
- supportsStreaming: true
3675
- },
3676
- {
3677
- id: "gpt-4o-mini",
3678
- displayName: "GPT-4o Mini",
3679
- contextWindow: 128e3,
3680
- supportsStreaming: true
3681
- },
3682
3630
  // 推理模型(200K 上下文)
3683
3631
  {
3684
3632
  id: "o3",
@@ -3686,12 +3634,6 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
3686
3634
  contextWindow: 2e5,
3687
3635
  supportsStreaming: true
3688
3636
  },
3689
- {
3690
- id: "o3-mini",
3691
- displayName: "o3 Mini",
3692
- contextWindow: 2e5,
3693
- supportsStreaming: true
3694
- },
3695
3637
  {
3696
3638
  id: "o4-mini",
3697
3639
  displayName: "o4 Mini",
@@ -3713,7 +3655,6 @@ var OpenRouterProvider = class extends OpenAICompatibleProvider {
3713
3655
  requiresApiKey: true,
3714
3656
  baseUrl: this.defaultBaseUrl,
3715
3657
  models: [
3716
- // Anthropic Claude 系列
3717
3658
  {
3718
3659
  id: "anthropic/claude-opus-4",
3719
3660
  displayName: "Claude Opus 4",
@@ -3732,26 +3673,12 @@ var OpenRouterProvider = class extends OpenAICompatibleProvider {
3732
3673
  contextWindow: 2e5,
3733
3674
  supportsStreaming: true
3734
3675
  },
3735
- // OpenAI 系列
3736
3676
  {
3737
3677
  id: "openai/gpt-5.4",
3738
3678
  displayName: "GPT-5.4",
3739
3679
  contextWindow: 105e4,
3740
3680
  supportsStreaming: true
3741
3681
  },
3742
- {
3743
- id: "openai/gpt-4o",
3744
- displayName: "GPT-4o",
3745
- contextWindow: 128e3,
3746
- supportsStreaming: true
3747
- },
3748
- {
3749
- id: "openai/gpt-4o-mini",
3750
- displayName: "GPT-4o Mini",
3751
- contextWindow: 128e3,
3752
- supportsStreaming: true
3753
- },
3754
- // Google Gemini 系列
3755
3682
  {
3756
3683
  id: "google/gemini-2.5-pro",
3757
3684
  displayName: "Gemini 2.5 Pro",
@@ -3764,7 +3691,6 @@ var OpenRouterProvider = class extends OpenAICompatibleProvider {
3764
3691
  contextWindow: 1048576,
3765
3692
  supportsStreaming: true
3766
3693
  },
3767
- // Meta Llama 系列
3768
3694
  {
3769
3695
  id: "meta-llama/llama-4-maverick",
3770
3696
  displayName: "Llama 4 Maverick",
@@ -3777,44 +3703,17 @@ var OpenRouterProvider = class extends OpenAICompatibleProvider {
3777
3703
  contextWindow: 512e3,
3778
3704
  supportsStreaming: true
3779
3705
  },
3780
- // DeepSeek 系列
3781
- {
3782
- id: "deepseek/deepseek-chat-v3-0324",
3783
- displayName: "DeepSeek V3 0324",
3784
- contextWindow: 131072,
3785
- supportsStreaming: true
3786
- },
3787
- {
3788
- id: "deepseek/deepseek-r1",
3789
- displayName: "DeepSeek R1",
3790
- contextWindow: 163840,
3791
- supportsStreaming: true
3792
- },
3793
- // Qwen 系列
3794
3706
  {
3795
3707
  id: "qwen/qwen3-coder",
3796
3708
  displayName: "Qwen3 Coder",
3797
3709
  contextWindow: 262144,
3798
3710
  supportsStreaming: true
3799
3711
  },
3800
- {
3801
- id: "qwen/qwen-2.5-72b-instruct",
3802
- displayName: "Qwen 2.5 72B",
3803
- contextWindow: 131072,
3804
- supportsStreaming: true
3805
- },
3806
- // Mistral 系列
3807
3712
  {
3808
3713
  id: "mistralai/mistral-large",
3809
3714
  displayName: "Mistral Large",
3810
3715
  contextWindow: 131072,
3811
3716
  supportsStreaming: true
3812
- },
3813
- {
3814
- id: "mistralai/mistral-small-3.1-24b-instruct",
3815
- displayName: "Mistral Small 3.1 24B",
3816
- contextWindow: 128e3,
3817
- supportsStreaming: true
3818
3717
  }
3819
3718
  ]
3820
3719
  };
@@ -3899,6 +3798,10 @@ var OllamaProvider = class extends OpenAICompatibleProvider {
3899
3798
  await this.tryConnect();
3900
3799
  return this.dynamicModels.length > 0 ? this.dynamicModels : this.info.models;
3901
3800
  }
3801
+ async refreshModels() {
3802
+ await this.tryConnect(false);
3803
+ return this.dynamicModels.length > 0 ? this.dynamicModels : this.info.models;
3804
+ }
3902
3805
  /** Ollama 无需验证 API Key,只检查服务是否可达 */
3903
3806
  async validateApiKey() {
3904
3807
  return this.tryConnect();
@@ -3939,6 +3842,111 @@ var CustomProvider = class extends OpenAICompatibleProvider {
3939
3842
  }
3940
3843
  };
3941
3844
 
3845
+ // src/providers/model-cache.ts
3846
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, unlinkSync } from "fs";
3847
+ import { dirname, join as join2 } from "path";
3848
+ var CACHE_FILE_NAME = "model-cache.json";
3849
+ var LIVE_DEFAULT_CONTEXT = 128e3;
3850
+ function modelCachePath(configDir) {
3851
+ return join2(configDir, CACHE_FILE_NAME);
3852
+ }
3853
+ function loadModelCache(configDir) {
3854
+ const path3 = modelCachePath(configDir);
3855
+ if (!existsSync2(path3)) return { version: 1, providers: {} };
3856
+ try {
3857
+ const parsed = JSON.parse(readFileSync2(path3, "utf-8"));
3858
+ if (parsed.version !== 1 || !parsed.providers || typeof parsed.providers !== "object") {
3859
+ return { version: 1, providers: {} };
3860
+ }
3861
+ return { version: 1, providers: parsed.providers };
3862
+ } catch {
3863
+ return { version: 1, providers: {} };
3864
+ }
3865
+ }
3866
+ function saveModelCache(configDir, cache) {
3867
+ const path3 = modelCachePath(configDir);
3868
+ mkdirSync2(dirname(path3), { recursive: true });
3869
+ atomicWriteFileSync(path3, `${JSON.stringify(cache, null, 2)}
3870
+ `);
3871
+ }
3872
+ function getCachedModels(configDir, providerId) {
3873
+ const entry = loadModelCache(configDir).providers[providerId];
3874
+ if (!entry) return [];
3875
+ return normalizeLiveModels(entry.models, entry.fetchedAt, "cache");
3876
+ }
3877
+ function getModelCacheSummary(configDir) {
3878
+ const cache = loadModelCache(configDir);
3879
+ return Object.entries(cache.providers).map(([providerId, entry]) => ({
3880
+ providerId,
3881
+ count: entry.models.length,
3882
+ fetchedAt: entry.fetchedAt,
3883
+ baseUrl: entry.baseUrl
3884
+ }));
3885
+ }
3886
+ function setCachedModels(configDir, providerId, models, baseUrl) {
3887
+ const cache = loadModelCache(configDir);
3888
+ const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
3889
+ const entry = {
3890
+ fetchedAt,
3891
+ ...baseUrl ? { baseUrl } : {},
3892
+ models: normalizeLiveModels(models, fetchedAt, "live"),
3893
+ lastError: null
3894
+ };
3895
+ cache.providers[providerId] = entry;
3896
+ saveModelCache(configDir, cache);
3897
+ return entry;
3898
+ }
3899
+ function clearModelCache(configDir, providerId) {
3900
+ const path3 = modelCachePath(configDir);
3901
+ if (!providerId) {
3902
+ if (existsSync2(path3)) unlinkSync(path3);
3903
+ return;
3904
+ }
3905
+ const cache = loadModelCache(configDir);
3906
+ delete cache.providers[providerId];
3907
+ saveModelCache(configDir, cache);
3908
+ }
3909
+ function normalizeLiveModels(models, fetchedAt, source = "live") {
3910
+ const seen = /* @__PURE__ */ new Set();
3911
+ const normalized = [];
3912
+ for (const model of models) {
3913
+ if (!model.id || seen.has(model.id)) continue;
3914
+ seen.add(model.id);
3915
+ normalized.push({
3916
+ id: model.id,
3917
+ displayName: model.displayName || model.id,
3918
+ contextWindow: model.contextWindow || LIVE_DEFAULT_CONTEXT,
3919
+ supportsStreaming: model.supportsStreaming ?? true,
3920
+ ...model.supportsThinking !== void 0 ? { supportsThinking: model.supportsThinking } : {},
3921
+ source,
3922
+ available: true,
3923
+ metadataUnknown: model.metadataUnknown ?? !model.contextWindow,
3924
+ ...fetchedAt ? { fetchedAt } : {}
3925
+ });
3926
+ }
3927
+ return normalized;
3928
+ }
3929
+ function mergeModelLists(curatedModels, liveModels) {
3930
+ const liveById = new Map(liveModels.map((model) => [model.id, model]));
3931
+ const merged = curatedModels.map((model) => ({
3932
+ ...model,
3933
+ source: liveById.has(model.id) ? "curated+live" : model.source ?? "curated",
3934
+ available: liveById.has(model.id) ? true : model.available ?? "unknown",
3935
+ fetchedAt: liveById.get(model.id)?.fetchedAt
3936
+ }));
3937
+ const curatedIds = new Set(curatedModels.map((model) => model.id));
3938
+ for (const model of liveModels) {
3939
+ if (curatedIds.has(model.id)) continue;
3940
+ merged.push({
3941
+ ...model,
3942
+ source: model.source ?? "live",
3943
+ available: true,
3944
+ metadataUnknown: model.metadataUnknown ?? true
3945
+ });
3946
+ }
3947
+ return merged;
3948
+ }
3949
+
3942
3950
  // src/providers/registry.ts
3943
3951
  var BUILT_IN_PROVIDERS = [
3944
3952
  ClaudeProvider,
@@ -3947,15 +3955,17 @@ var BUILT_IN_PROVIDERS = [
3947
3955
  ZhipuProvider,
3948
3956
  KimiProvider,
3949
3957
  MiniMaxProvider,
3958
+ QwenProvider,
3950
3959
  OpenAIProvider,
3951
3960
  OpenRouterProvider,
3952
3961
  OllamaProvider
3953
3962
  ];
3954
3963
  var ProviderRegistry = class {
3955
3964
  providers = /* @__PURE__ */ new Map();
3956
- // 记录自定义 provider 的状态(用于 listAll 展示)
3957
3965
  customConfigs = [];
3958
- async initialize(getApiKey, getOptions, customProviderConfigs) {
3966
+ configDir;
3967
+ async initialize(getApiKey, getOptions, customProviderConfigs, configDir) {
3968
+ this.configDir = configDir;
3959
3969
  for (const ProviderClass of BUILT_IN_PROVIDERS) {
3960
3970
  const provider = new ProviderClass();
3961
3971
  const apiKey = getApiKey(provider.info.id);
@@ -3982,11 +3992,6 @@ var ProviderRegistry = class {
3982
3992
  this.providers.set(cfg.id, provider);
3983
3993
  }
3984
3994
  }
3985
- /**
3986
- * Register a provider instance under the given id (overwrites any existing).
3987
- * Primary uses: integration tests injecting MockProvider, and future hot-swap
3988
- * scenarios. The provider is assumed already initialized.
3989
- */
3990
3995
  register(id, provider) {
3991
3996
  this.providers.set(id, provider);
3992
3997
  }
@@ -4000,6 +4005,37 @@ var ProviderRegistry = class {
4000
4005
  has(id) {
4001
4006
  return this.providers.has(id);
4002
4007
  }
4008
+ async listModels(providerId) {
4009
+ const provider = this.get(providerId);
4010
+ const curated = await provider.listModels();
4011
+ if (!this.configDir) return curated;
4012
+ const cached = getCachedModels(this.configDir, providerId);
4013
+ return cached.length > 0 ? mergeModelLists(curated, cached) : curated;
4014
+ }
4015
+ async refreshModels(providerId) {
4016
+ const provider = this.get(providerId);
4017
+ if (!provider.refreshModels) {
4018
+ throw new Error(`Provider ${providerId} does not support model refresh`);
4019
+ }
4020
+ const liveModels = await provider.refreshModels();
4021
+ if (!this.configDir) {
4022
+ const merged2 = mergeModelLists(await provider.listModels(), liveModels);
4023
+ provider.info.models = merged2;
4024
+ return merged2;
4025
+ }
4026
+ setCachedModels(this.configDir, providerId, liveModels, provider.info.baseUrl);
4027
+ const merged = await this.listModels(providerId);
4028
+ provider.info.models = merged;
4029
+ return merged;
4030
+ }
4031
+ getModelCacheStatus() {
4032
+ if (!this.configDir) return [];
4033
+ return getModelCacheSummary(this.configDir);
4034
+ }
4035
+ clearModelCache(providerId) {
4036
+ if (!this.configDir) return;
4037
+ clearModelCache(this.configDir, providerId);
4038
+ }
4003
4039
  listAvailable() {
4004
4040
  return [...this.providers.values()];
4005
4041
  }
@@ -4029,8 +4065,8 @@ var ProviderRegistry = class {
4029
4065
  };
4030
4066
 
4031
4067
  // src/session/session-manager.ts
4032
- import { readFileSync as readFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, unlinkSync, openSync, readSync, closeSync } from "fs";
4033
- import { join as join2 } from "path";
4068
+ import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, unlinkSync as unlinkSync2, openSync, readSync, closeSync, statSync } from "fs";
4069
+ import { join as join3 } from "path";
4034
4070
  import { v4 as uuidv4 } from "uuid";
4035
4071
 
4036
4072
  // src/core/types.ts
@@ -4609,8 +4645,8 @@ var SessionManager = class {
4609
4645
  }
4610
4646
  async save() {
4611
4647
  if (!this._current) return;
4612
- mkdirSync2(this.historyDir, { recursive: true });
4613
- const filePath = join2(this.historyDir, `${this._current.id}.json`);
4648
+ mkdirSync3(this.historyDir, { recursive: true });
4649
+ const filePath = join3(this.historyDir, `${this._current.id}.json`);
4614
4650
  const raw = this._current.toJSON();
4615
4651
  const opts = this.redactOptionsForSave();
4616
4652
  const { value: payload, hits } = redactJson(raw, opts);
@@ -4618,13 +4654,13 @@ var SessionManager = class {
4618
4654
  atomicWriteFileSync(filePath, JSON.stringify(payload, null, 2));
4619
4655
  }
4620
4656
  loadSession(id) {
4621
- const filePath = join2(this.historyDir, `${id}.json`);
4622
- if (!existsSync2(filePath)) {
4657
+ const filePath = join3(this.historyDir, `${id}.json`);
4658
+ if (!existsSync3(filePath)) {
4623
4659
  throw new Error(`Session ${id} not found`);
4624
4660
  }
4625
4661
  let data;
4626
4662
  try {
4627
- data = JSON.parse(readFileSync2(filePath, "utf-8"));
4663
+ data = JSON.parse(readFileSync3(filePath, "utf-8"));
4628
4664
  } catch (err) {
4629
4665
  throw new Error(`Session ${id} is corrupted: ${err instanceof Error ? err.message : String(err)}`);
4630
4666
  }
@@ -4632,14 +4668,22 @@ var SessionManager = class {
4632
4668
  this._current = session;
4633
4669
  return session;
4634
4670
  }
4635
- listSessions() {
4636
- if (!existsSync2(this.historyDir)) return [];
4671
+ listSessions(opts) {
4672
+ if (!existsSync3(this.historyDir)) return [];
4637
4673
  const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json"));
4638
4674
  const metas = [];
4639
4675
  for (const file of files) {
4640
4676
  try {
4641
- const meta = this.readSessionMeta(join2(this.historyDir, file));
4642
- if (meta) metas.push(meta);
4677
+ const filePath = join3(this.historyDir, file);
4678
+ const meta = this.readSessionMeta(filePath);
4679
+ if (!meta) continue;
4680
+ if (opts?.includeFileSize) {
4681
+ try {
4682
+ meta.fileSize = statSync(filePath).size;
4683
+ } catch {
4684
+ }
4685
+ }
4686
+ metas.push(meta);
4643
4687
  } catch (err) {
4644
4688
  process.stderr.write(
4645
4689
  `[Warning] Skipping corrupted session file "${file}": ${err instanceof Error ? err.message : String(err)}
@@ -4678,12 +4722,24 @@ var SessionManager = class {
4678
4722
  if (messageCount === void 0) {
4679
4723
  messageCount = 0;
4680
4724
  try {
4681
- const full = readFileSync2(filePath, "utf-8");
4725
+ const full = readFileSync3(filePath, "utf-8");
4682
4726
  const matches = full.match(/"role"\s*:/g);
4683
4727
  messageCount = matches ? matches.length : 0;
4684
4728
  } catch {
4685
4729
  }
4686
4730
  }
4731
+ let branchCount;
4732
+ let activeBranch;
4733
+ try {
4734
+ const brMatch = header.match(/"branches"\s*:\s*\[/);
4735
+ if (brMatch) {
4736
+ const brIds = header.match(/"id"\s*:\s*"[^"]*"/g);
4737
+ const parentMatches = header.match(/"parentBranchId"/g);
4738
+ branchCount = parentMatches ? parentMatches.length + 1 : 1;
4739
+ }
4740
+ activeBranch = extractJsonField(header, "activeBranchId");
4741
+ } catch {
4742
+ }
4687
4743
  return {
4688
4744
  id,
4689
4745
  provider,
@@ -4691,10 +4747,12 @@ var SessionManager = class {
4691
4747
  messageCount,
4692
4748
  created: safeDate(created),
4693
4749
  updated: safeDate(updated),
4694
- title: title || void 0
4750
+ title: title || void 0,
4751
+ branchCount,
4752
+ activeBranch
4695
4753
  };
4696
4754
  }
4697
- const data = JSON.parse(readFileSync2(filePath, "utf-8"));
4755
+ const data = JSON.parse(readFileSync3(filePath, "utf-8"));
4698
4756
  return {
4699
4757
  id: data.id,
4700
4758
  provider: data.provider,
@@ -4702,14 +4760,16 @@ var SessionManager = class {
4702
4760
  messageCount: data.messages?.length ?? 0,
4703
4761
  created: safeDate(data.created),
4704
4762
  updated: safeDate(data.updated),
4705
- title: data.title
4763
+ title: data.title,
4764
+ branchCount: data.branches?.length ?? 1,
4765
+ activeBranch: data.activeBranchId
4706
4766
  };
4707
4767
  }
4708
4768
  deleteSession(id) {
4709
- const filePath = join2(this.historyDir, `${id}.json`);
4710
- if (!existsSync2(filePath)) return false;
4769
+ const filePath = join3(this.historyDir, `${id}.json`);
4770
+ if (!existsSync3(filePath)) return false;
4711
4771
  try {
4712
- unlinkSync(filePath);
4772
+ unlinkSync2(filePath);
4713
4773
  if (this._current && this._current.id === id) {
4714
4774
  this._current = null;
4715
4775
  }
@@ -4744,14 +4804,14 @@ var SessionManager = class {
4744
4804
  * 每个 session 最多返回 3 条匹配片段,全局最多 maxResults 个 session。
4745
4805
  */
4746
4806
  searchMessages(query, maxResults = 20) {
4747
- if (!existsSync2(this.historyDir)) return [];
4807
+ if (!existsSync3(this.historyDir)) return [];
4748
4808
  const q = query.toLowerCase();
4749
- const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) => join2(this.historyDir, f));
4809
+ const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) => join3(this.historyDir, f));
4750
4810
  const results = [];
4751
4811
  for (const filePath of files) {
4752
4812
  if (results.length >= maxResults) break;
4753
4813
  try {
4754
- const data = JSON.parse(readFileSync2(filePath, "utf-8"));
4814
+ const data = JSON.parse(readFileSync3(filePath, "utf-8"));
4755
4815
  const messages = data.messages ?? [];
4756
4816
  const matches = [];
4757
4817
  for (const msg of messages) {
@@ -4798,12 +4858,12 @@ var SessionManager = class {
4798
4858
 
4799
4859
  // src/tools/builtin/bash.ts
4800
4860
  import { spawn } from "child_process";
4801
- import { existsSync as existsSync4, readdirSync as readdirSync2, statSync } from "fs";
4861
+ import { existsSync as existsSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
4802
4862
  import { platform as platform2 } from "os";
4803
4863
  import { resolve } from "path";
4804
4864
 
4805
4865
  // src/tools/undo-stack.ts
4806
- import { readFileSync as readFileSync3, writeFileSync, unlinkSync as unlinkSync2, rmdirSync, existsSync as existsSync3 } from "fs";
4866
+ import { readFileSync as readFileSync4, writeFileSync, unlinkSync as unlinkSync3, rmdirSync, existsSync as existsSync4 } from "fs";
4807
4867
  var MAX_UNDO_DEPTH = 20;
4808
4868
  var UndoStack = class {
4809
4869
  stack = [];
@@ -4814,9 +4874,9 @@ var UndoStack = class {
4814
4874
  */
4815
4875
  push(filePath, description) {
4816
4876
  let previousContent = null;
4817
- if (existsSync3(filePath)) {
4877
+ if (existsSync4(filePath)) {
4818
4878
  try {
4819
- previousContent = readFileSync3(filePath, "utf-8");
4879
+ previousContent = readFileSync4(filePath, "utf-8");
4820
4880
  } catch {
4821
4881
  return;
4822
4882
  }
@@ -4870,7 +4930,7 @@ var UndoStack = class {
4870
4930
  try {
4871
4931
  if (entry.previousContent === null) {
4872
4932
  if (entry.isDirectory) {
4873
- if (existsSync3(entry.filePath)) {
4933
+ if (existsSync4(entry.filePath)) {
4874
4934
  try {
4875
4935
  rmdirSync(entry.filePath);
4876
4936
  return { entry, result: `Removed newly created directory: ${entry.filePath}` };
@@ -4880,8 +4940,8 @@ var UndoStack = class {
4880
4940
  }
4881
4941
  return { entry, result: `Directory already removed: ${entry.filePath}` };
4882
4942
  } else {
4883
- if (existsSync3(entry.filePath)) {
4884
- unlinkSync2(entry.filePath);
4943
+ if (existsSync4(entry.filePath)) {
4944
+ unlinkSync3(entry.filePath);
4885
4945
  }
4886
4946
  return { entry, result: `Deleted newly created file: ${entry.filePath}` };
4887
4947
  }
@@ -5025,7 +5085,7 @@ Important rules:
5025
5085
  throw new ToolError("bash", blockingHint);
5026
5086
  }
5027
5087
  let currentCwd = getCwd();
5028
- if (!existsSync4(currentCwd)) {
5088
+ if (!existsSync5(currentCwd)) {
5029
5089
  const fallback = process.cwd();
5030
5090
  process.stderr.write(
5031
5091
  `[bash] Previous cwd "${currentCwd}" no longer exists, reset to "${fallback}"
@@ -5037,7 +5097,7 @@ Important rules:
5037
5097
  let effectiveCwd = currentCwd;
5038
5098
  if (cwdArg) {
5039
5099
  const resolved = resolve(currentCwd, cwdArg);
5040
- if (!existsSync4(resolved)) {
5100
+ if (!existsSync5(resolved)) {
5041
5101
  throw new ToolError(
5042
5102
  "bash",
5043
5103
  `cwd directory does not exist: "${resolved}". Create it first (e.g. mkdir -p "${resolved}") before specifying it as cwd.`
@@ -5057,7 +5117,7 @@ Important rules:
5057
5117
  const parsedTargets = parseCreationTargets(command, effectiveCwd);
5058
5118
  const parsedTargetsBefore = /* @__PURE__ */ new Map();
5059
5119
  for (const t of parsedTargets) {
5060
- parsedTargetsBefore.set(t, existsSync4(t));
5120
+ parsedTargetsBefore.set(t, existsSync5(t));
5061
5121
  }
5062
5122
  try {
5063
5123
  const { stdout, stderr, status, signal, timedOut, aborted } = await runShell(
@@ -5273,7 +5333,7 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
5273
5333
  for (const absPath of afterSnapshot) {
5274
5334
  if (!beforeSnapshot.has(absPath)) {
5275
5335
  try {
5276
- const st = statSync(absPath);
5336
+ const st = statSync2(absPath);
5277
5337
  if (st.isDirectory()) {
5278
5338
  undoStack.pushNewDir(absPath, `bash (new dir): ${absPath}`);
5279
5339
  } else {
@@ -5285,9 +5345,9 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
5285
5345
  }
5286
5346
  }
5287
5347
  for (const [target, existedBefore] of parsedTargetsBefore) {
5288
- if (!existedBefore && !tracked.has(target) && existsSync4(target)) {
5348
+ if (!existedBefore && !tracked.has(target) && existsSync5(target)) {
5289
5349
  try {
5290
- const st = statSync(target);
5350
+ const st = statSync2(target);
5291
5351
  if (st.isDirectory()) {
5292
5352
  undoStack.pushNewDir(target, `bash (new dir): ${target}`);
5293
5353
  } else {
@@ -5398,7 +5458,7 @@ function updateCwdFromCommand(command, baseCwd) {
5398
5458
  if (!target || target.startsWith("$") || target === "~") return;
5399
5459
  try {
5400
5460
  const newDir = resolve(baseCwd, target);
5401
- if (existsSync4(newDir)) {
5461
+ if (existsSync5(newDir)) {
5402
5462
  setCwd(newDir);
5403
5463
  }
5404
5464
  } catch {
@@ -5406,9 +5466,9 @@ function updateCwdFromCommand(command, baseCwd) {
5406
5466
  }
5407
5467
 
5408
5468
  // src/tools/builtin/read-file.ts
5409
- import { readFileSync as readFileSync4, existsSync as existsSync5, statSync as statSync2, readdirSync as readdirSync3 } from "fs";
5469
+ import { readFileSync as readFileSync5, existsSync as existsSync6, statSync as statSync3, readdirSync as readdirSync3 } from "fs";
5410
5470
  import { execFileSync } from "child_process";
5411
- import { extname, resolve as resolve2, basename, sep, dirname } from "path";
5471
+ import { extname, resolve as resolve2, basename, sep, dirname as dirname2 } from "path";
5412
5472
  import { homedir as homedir2 } from "os";
5413
5473
 
5414
5474
  // src/tools/builtin/notebook-utils.ts
@@ -5683,7 +5743,7 @@ var readFileTool = {
5683
5743
  const normalizedPath = resolve2(filePath);
5684
5744
  const refusal = getHardRefusal(normalizedPath);
5685
5745
  if (refusal) return refusal;
5686
- if (!existsSync5(normalizedPath)) {
5746
+ if (!existsSync6(normalizedPath)) {
5687
5747
  const suggestions = findSimilarFiles(filePath);
5688
5748
  if (suggestions.length > 0) {
5689
5749
  throw new ToolError(
@@ -5702,7 +5762,7 @@ Current working directory: ${process.cwd()}
5702
5762
  Please use list_dir to verify the file path and retry.`
5703
5763
  );
5704
5764
  }
5705
- const { size } = statSync2(normalizedPath);
5765
+ const { size } = statSync3(normalizedPath);
5706
5766
  if (size > MAX_FILE_BYTES) {
5707
5767
  const mb = (size / 1024 / 1024).toFixed(1);
5708
5768
  return `[File too large: ${filePath} (${mb} MB)]
@@ -5722,9 +5782,9 @@ Use the bash tool to read in segments, e.g.:
5722
5782
 
5723
5783
  ${pdfText}`;
5724
5784
  }
5725
- const dir = dirname(normalizedPath);
5785
+ const dir = dirname2(normalizedPath);
5726
5786
  const nameNoExt = basename(normalizedPath, ext);
5727
- const textAlts = [".md", ".txt", ".html"].map((e) => resolve2(dir, nameNoExt + e)).filter(existsSync5);
5787
+ const textAlts = [".md", ".txt", ".html"].map((e) => resolve2(dir, nameNoExt + e)).filter(existsSync6);
5728
5788
  if (textAlts.length > 0) {
5729
5789
  return `[PDF file: ${filePath}]
5730
5790
  Cannot extract text from this PDF, but found alternative text versions:
@@ -5737,7 +5797,7 @@ Suggestion: read existing text versions (.md / .txt) in the project, or install
5737
5797
  }
5738
5798
  if (ext === ".ipynb") {
5739
5799
  try {
5740
- const raw = readFileSync4(normalizedPath, "utf-8");
5800
+ const raw = readFileSync5(normalizedPath, "utf-8");
5741
5801
  const nb = parseNotebook(raw);
5742
5802
  return renderNotebookAsText(nb, filePath);
5743
5803
  } catch (err) {
@@ -5750,7 +5810,7 @@ ${err.message}`;
5750
5810
  This is a binary file and cannot be read as text. If there is a text version (.md / .txt) in the project, please read that instead.`;
5751
5811
  }
5752
5812
  const { readFile: readFile2 } = await import("fs/promises");
5753
- const buf = size > 1048576 ? await readFile2(normalizedPath) : readFileSync4(normalizedPath);
5813
+ const buf = size > 1048576 ? await readFile2(normalizedPath) : readFileSync5(normalizedPath);
5754
5814
  if (encoding === "base64") {
5755
5815
  return `[File: ${filePath} | base64]
5756
5816
 
@@ -5788,12 +5848,12 @@ ${content}`;
5788
5848
  };
5789
5849
 
5790
5850
  // src/tools/builtin/write-file.ts
5791
- import { appendFileSync, mkdirSync as mkdirSync5 } from "fs";
5792
- import { dirname as dirname3 } from "path";
5851
+ import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
5852
+ import { dirname as dirname4 } from "path";
5793
5853
 
5794
5854
  // src/tools/executor.ts
5795
5855
  import chalk3 from "chalk";
5796
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
5856
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
5797
5857
  import { tmpdir } from "os";
5798
5858
 
5799
5859
  // src/core/readline-internal.ts
@@ -6002,8 +6062,8 @@ function simpleDiff(oldLines, newLines) {
6002
6062
  // src/tools/hooks.ts
6003
6063
  import { execSync } from "child_process";
6004
6064
  import { createHash } from "crypto";
6005
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "fs";
6006
- import { join as join3 } from "path";
6065
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6 } from "fs";
6066
+ import { join as join4 } from "path";
6007
6067
  var TRUST_FILE = "hooks-trust.json";
6008
6068
  var DEFAULT_TIMEOUT_MS = 5e3;
6009
6069
  function asArray(entry) {
@@ -6012,13 +6072,13 @@ function asArray(entry) {
6012
6072
  return raw.map((item) => typeof item === "string" ? { command: item } : item);
6013
6073
  }
6014
6074
  function hookTrustPath(configDir) {
6015
- return join3(configDir, TRUST_FILE);
6075
+ return join4(configDir, TRUST_FILE);
6016
6076
  }
6017
6077
  function loadHookTrustStore(configDir) {
6018
6078
  const file = hookTrustPath(configDir);
6019
- if (!existsSync6(file)) return { records: [] };
6079
+ if (!existsSync7(file)) return { records: [] };
6020
6080
  try {
6021
- const parsed = JSON.parse(readFileSync5(file, "utf-8"));
6081
+ const parsed = JSON.parse(readFileSync6(file, "utf-8"));
6022
6082
  return { records: Array.isArray(parsed.records) ? parsed.records : [] };
6023
6083
  } catch {
6024
6084
  return { records: [] };
@@ -6544,8 +6604,8 @@ var theme = new Proxy(DARK_THEME, {
6544
6604
  });
6545
6605
 
6546
6606
  // src/diagnostics/tool-stats.ts
6547
- import { existsSync as existsSync7, readFileSync as readFileSync6, mkdirSync as mkdirSync4 } from "fs";
6548
- import { join as join4, dirname as dirname2 } from "path";
6607
+ import { existsSync as existsSync8, readFileSync as readFileSync7, mkdirSync as mkdirSync5 } from "fs";
6608
+ import { join as join5, dirname as dirname3 } from "path";
6549
6609
  import { homedir as homedir3 } from "os";
6550
6610
  var STATS_FILE_NAME = "tool-stats.json";
6551
6611
  var STATS_VERSION = 1;
@@ -6555,16 +6615,16 @@ var dirty = false;
6555
6615
  var pendingWrites = 0;
6556
6616
  var configDirOverride;
6557
6617
  function statsFilePath() {
6558
- const base = configDirOverride ?? join4(homedir3(), CONFIG_DIR_NAME);
6559
- return join4(base, STATS_FILE_NAME);
6618
+ const base = configDirOverride ?? join5(homedir3(), CONFIG_DIR_NAME);
6619
+ return join5(base, STATS_FILE_NAME);
6560
6620
  }
6561
6621
  function load() {
6562
6622
  const path3 = statsFilePath();
6563
- if (!existsSync7(path3)) {
6623
+ if (!existsSync8(path3)) {
6564
6624
  return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
6565
6625
  }
6566
6626
  try {
6567
- const raw = JSON.parse(readFileSync6(path3, "utf-8"));
6627
+ const raw = JSON.parse(readFileSync7(path3, "utf-8"));
6568
6628
  if (raw.version !== STATS_VERSION || !raw.entries) {
6569
6629
  return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
6570
6630
  }
@@ -6622,7 +6682,7 @@ function flush() {
6622
6682
  if (!dirty || state === null) return;
6623
6683
  const path3 = statsFilePath();
6624
6684
  try {
6625
- mkdirSync4(dirname2(path3), { recursive: true });
6685
+ mkdirSync5(dirname3(path3), { recursive: true });
6626
6686
  atomicWriteFileSync(path3, JSON.stringify(state, null, 2));
6627
6687
  dirty = false;
6628
6688
  pendingWrites = 0;
@@ -6646,8 +6706,8 @@ function getTopUsedTools(limit = 5) {
6646
6706
  }
6647
6707
 
6648
6708
  // src/tools/action-classifier.ts
6649
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
6650
- import { isAbsolute as isAbsolute2, join as join5, resolve as resolve4 } from "path";
6709
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
6710
+ import { isAbsolute as isAbsolute2, join as join6, resolve as resolve4 } from "path";
6651
6711
  var RECENT_DENIED_LIMIT = 50;
6652
6712
  var recentlyDenied = [];
6653
6713
  function recordRecentlyDeniedAutoAction(call, classification) {
@@ -6688,10 +6748,10 @@ function splitShellWords(command) {
6688
6748
  return out.filter(Boolean);
6689
6749
  }
6690
6750
  function readPackageManifest(root) {
6691
- const p = join5(root, "package.json");
6692
- if (!existsSync8(p)) return null;
6751
+ const p = join6(root, "package.json");
6752
+ if (!existsSync9(p)) return null;
6693
6753
  try {
6694
- return JSON.parse(readFileSync7(p, "utf-8"));
6754
+ return JSON.parse(readFileSync8(p, "utf-8"));
6695
6755
  } catch {
6696
6756
  return null;
6697
6757
  }
@@ -6707,10 +6767,10 @@ function manifestHasDependency(root, name) {
6707
6767
  }
6708
6768
  function lockfileMentions(root, name) {
6709
6769
  for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
6710
- const p = join5(root, file);
6711
- if (!existsSync8(p)) continue;
6770
+ const p = join6(root, file);
6771
+ if (!existsSync9(p)) continue;
6712
6772
  try {
6713
- if (readFileSync7(p, "utf-8").includes(name)) return true;
6773
+ if (readFileSync8(p, "utf-8").includes(name)) return true;
6714
6774
  } catch {
6715
6775
  }
6716
6776
  }
@@ -7274,10 +7334,10 @@ var ToolExecutor = class {
7274
7334
  const filePath = String(call.arguments["path"] ?? "");
7275
7335
  const newContent = String(call.arguments["content"] ?? "");
7276
7336
  if (!filePath) return;
7277
- if (existsSync9(filePath)) {
7337
+ if (existsSync10(filePath)) {
7278
7338
  let oldContent;
7279
7339
  try {
7280
- oldContent = readFileSync8(filePath, "utf-8");
7340
+ oldContent = readFileSync9(filePath, "utf-8");
7281
7341
  } catch {
7282
7342
  return;
7283
7343
  }
@@ -7300,7 +7360,7 @@ var ToolExecutor = class {
7300
7360
  }
7301
7361
  } else if (call.name === "edit_file") {
7302
7362
  const filePath = String(call.arguments["path"] ?? "");
7303
- if (!filePath || !existsSync9(filePath)) return;
7363
+ if (!filePath || !existsSync10(filePath)) return;
7304
7364
  const oldStr = call.arguments["old_str"];
7305
7365
  const newStr = call.arguments["new_str"];
7306
7366
  if (oldStr !== void 0) {
@@ -7327,7 +7387,7 @@ var ToolExecutor = class {
7327
7387
  const to = Number(call.arguments["delete_to_line"] ?? from);
7328
7388
  let fileContent;
7329
7389
  try {
7330
- fileContent = readFileSync8(filePath, "utf-8");
7390
+ fileContent = readFileSync9(filePath, "utf-8");
7331
7391
  } catch {
7332
7392
  return;
7333
7393
  }
@@ -7562,7 +7622,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
7562
7622
  }
7563
7623
  undoStack.push(filePath, `write_file${appendMode ? " (append)" : ""}: ${filePath}`);
7564
7624
  fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
7565
- mkdirSync5(dirname3(filePath), { recursive: true });
7625
+ mkdirSync6(dirname4(filePath), { recursive: true });
7566
7626
  if (appendMode) {
7567
7627
  appendFileSync(filePath, content, encoding);
7568
7628
  } else {
@@ -7582,7 +7642,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
7582
7642
  };
7583
7643
 
7584
7644
  // src/tools/builtin/edit-file.ts
7585
- import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
7645
+ import { readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
7586
7646
 
7587
7647
  // src/tools/builtin/patch-apply.ts
7588
7648
  function parseUnifiedDiff(patch) {
@@ -7943,7 +8003,7 @@ Note: Path can be absolute or relative to cwd.`,
7943
8003
  const filePath = String(args["path"] ?? "");
7944
8004
  const encoding = args["encoding"] ?? "utf-8";
7945
8005
  if (!filePath) throw new ToolError("edit_file", "path is required");
7946
- if (!existsSync10(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
8006
+ if (!existsSync11(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
7947
8007
  const verdict = classifyWritePath(filePath);
7948
8008
  if (verdict.sensitive && subAgentGuard.active) {
7949
8009
  throw new ToolError(
@@ -7951,7 +8011,7 @@ Note: Path can be absolute or relative to cwd.`,
7951
8011
  `Refused: sub-agents cannot edit sensitive paths \u2014 ${verdict.reason}. If this is genuinely needed, ask the parent agent to perform the edit so the user can approve it.`
7952
8012
  );
7953
8013
  }
7954
- const original = readFileSync9(filePath, encoding);
8014
+ const original = readFileSync10(filePath, encoding);
7955
8015
  if (args["patch"] !== void 0) {
7956
8016
  const patchText = String(args["patch"] ?? "");
7957
8017
  const stopOnError = args["stop_on_error"] !== false;
@@ -8109,8 +8169,8 @@ function truncatePreview(str, maxLen = 80) {
8109
8169
  }
8110
8170
 
8111
8171
  // src/tools/builtin/list-dir.ts
8112
- import { readdirSync as readdirSync4, statSync as statSync3, existsSync as existsSync11 } from "fs";
8113
- import { join as join6, basename as basename3 } from "path";
8172
+ import { readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync12 } from "fs";
8173
+ import { join as join7, basename as basename3 } from "path";
8114
8174
  var listDirTool = {
8115
8175
  definition: {
8116
8176
  name: "list_dir",
@@ -8132,7 +8192,7 @@ var listDirTool = {
8132
8192
  async execute(args) {
8133
8193
  const dirPath = String(args["path"] ?? process.cwd());
8134
8194
  const recursive = Boolean(args["recursive"] ?? false);
8135
- if (!existsSync11(dirPath)) {
8195
+ if (!existsSync12(dirPath)) {
8136
8196
  const targetName = basename3(dirPath).toLowerCase();
8137
8197
  const cwd = process.cwd();
8138
8198
  const suggestions = [];
@@ -8190,11 +8250,11 @@ function listRecursive(basePath, indent, recursive, lines) {
8190
8250
  if (entry.isDirectory()) {
8191
8251
  lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
8192
8252
  if (recursive) {
8193
- listRecursive(join6(basePath, entry.name), indent + " ", true, lines);
8253
+ listRecursive(join7(basePath, entry.name), indent + " ", true, lines);
8194
8254
  }
8195
8255
  } else {
8196
8256
  try {
8197
- const stat = statSync3(join6(basePath, entry.name));
8257
+ const stat = statSync4(join7(basePath, entry.name));
8198
8258
  const size = formatSize(stat.size);
8199
8259
  lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
8200
8260
  } catch {
@@ -8210,9 +8270,9 @@ function formatSize(bytes) {
8210
8270
  }
8211
8271
 
8212
8272
  // src/tools/builtin/grep-files.ts
8213
- import { readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync4, existsSync as existsSync12 } from "fs";
8273
+ import { readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync5, existsSync as existsSync13 } from "fs";
8214
8274
  import { readFile } from "fs/promises";
8215
- import { join as join7, relative } from "path";
8275
+ import { join as join8, relative } from "path";
8216
8276
  var grepFilesTool = {
8217
8277
  definition: {
8218
8278
  name: "grep_files",
@@ -8263,7 +8323,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
8263
8323
  const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
8264
8324
  const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
8265
8325
  if (!pattern) throw new ToolError("grep_files", "pattern is required");
8266
- if (!existsSync12(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
8326
+ if (!existsSync13(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
8267
8327
  const MAX_PATTERN_LENGTH = 1e3;
8268
8328
  if (pattern.length > MAX_PATTERN_LENGTH) {
8269
8329
  throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
@@ -8276,7 +8336,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
8276
8336
  regex = new RegExp(escaped, ignoreCase ? "gi" : "g");
8277
8337
  }
8278
8338
  const results = [];
8279
- const stat = statSync4(rootPath);
8339
+ const stat = statSync5(rootPath);
8280
8340
  if (stat.isFile()) {
8281
8341
  searchInFile(rootPath, rootPath, regex, contextLines, maxResults, results);
8282
8342
  } else {
@@ -8357,13 +8417,13 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
8357
8417
  if (paths.length >= maxFiles) return;
8358
8418
  if (entry.isDirectory()) {
8359
8419
  if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
8360
- walk(join7(dirPath, entry.name));
8420
+ walk(join8(dirPath, entry.name));
8361
8421
  } else if (entry.isFile()) {
8362
8422
  if (isBinary(entry.name)) continue;
8363
8423
  if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
8364
- const fullPath = join7(dirPath, entry.name);
8424
+ const fullPath = join8(dirPath, entry.name);
8365
8425
  try {
8366
- if (statSync4(fullPath).size > 1e6) continue;
8426
+ if (statSync5(fullPath).size > 1e6) continue;
8367
8427
  } catch {
8368
8428
  continue;
8369
8429
  }
@@ -8409,14 +8469,14 @@ async function searchInFileAsync(fullPath, displayPath2, regex, contextLines, ma
8409
8469
  }
8410
8470
  function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, results) {
8411
8471
  try {
8412
- const stat = statSync4(fullPath);
8472
+ const stat = statSync5(fullPath);
8413
8473
  if (stat.size > 1e6) return;
8414
8474
  } catch {
8415
8475
  return;
8416
8476
  }
8417
8477
  let content;
8418
8478
  try {
8419
- content = readFileSync10(fullPath, "utf-8");
8479
+ content = readFileSync11(fullPath, "utf-8");
8420
8480
  } catch {
8421
8481
  return;
8422
8482
  }
@@ -8447,8 +8507,8 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
8447
8507
  }
8448
8508
 
8449
8509
  // src/tools/builtin/glob-files.ts
8450
- import { readdirSync as readdirSync6, statSync as statSync5, existsSync as existsSync13 } from "fs";
8451
- import { join as join8, relative as relative2, basename as basename4 } from "path";
8510
+ import { readdirSync as readdirSync6, statSync as statSync6, existsSync as existsSync14 } from "fs";
8511
+ import { join as join9, relative as relative2, basename as basename4 } from "path";
8452
8512
  var globFilesTool = {
8453
8513
  definition: {
8454
8514
  name: "glob_files",
@@ -8481,7 +8541,7 @@ Results sorted by most recent modification time. Automatically skips node_module
8481
8541
  const rootPath = String(args["path"] ?? process.cwd());
8482
8542
  const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
8483
8543
  if (!pattern) throw new ToolError("glob_files", "pattern is required");
8484
- if (!existsSync13(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
8544
+ if (!existsSync14(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
8485
8545
  const regex = globToRegex(pattern);
8486
8546
  const matches = [];
8487
8547
  collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
@@ -8558,7 +8618,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
8558
8618
  }
8559
8619
  for (const entry of entries) {
8560
8620
  if (results.length >= maxResults) break;
8561
- const fullPath = join8(dirPath, entry.name);
8621
+ const fullPath = join9(dirPath, entry.name);
8562
8622
  if (entry.isDirectory()) {
8563
8623
  if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
8564
8624
  collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
@@ -8566,7 +8626,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
8566
8626
  const relPath = relative2(rootPath, fullPath).replace(/\\/g, "/");
8567
8627
  if (regex.test(relPath) || regex.test(basename4(relPath))) {
8568
8628
  try {
8569
- const stat = statSync5(fullPath);
8629
+ const stat = statSync6(fullPath);
8570
8630
  results.push({ relPath, absPath: fullPath, mtime: stat.mtimeMs });
8571
8631
  } catch {
8572
8632
  results.push({ relPath, absPath: fullPath, mtime: 0 });
@@ -9298,16 +9358,16 @@ ${preamble}`;
9298
9358
  }
9299
9359
 
9300
9360
  // src/tools/builtin/save-last-response.ts
9301
- import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync3, rmdirSync as rmdirSync2 } from "fs";
9302
- import { dirname as dirname4 } from "path";
9361
+ import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync4, rmdirSync as rmdirSync2 } from "fs";
9362
+ import { dirname as dirname5 } from "path";
9303
9363
  var lastResponseStore = { content: "" };
9304
9364
  function cleanupRejectedTeeFile(filePath) {
9305
9365
  try {
9306
- unlinkSync3(filePath);
9366
+ unlinkSync4(filePath);
9307
9367
  } catch {
9308
9368
  }
9309
9369
  try {
9310
- rmdirSync2(dirname4(filePath));
9370
+ rmdirSync2(dirname5(filePath));
9311
9371
  } catch {
9312
9372
  }
9313
9373
  }
@@ -9348,7 +9408,7 @@ Any of these triggers means use save_last_response, NOT write_file:
9348
9408
  throw new ToolError("save_last_response", "No content to save: AI has not produced any response yet, or the last response was empty.");
9349
9409
  }
9350
9410
  undoStack.push(filePath, `save_last_response: ${filePath}`);
9351
- mkdirSync6(dirname4(filePath), { recursive: true });
9411
+ mkdirSync7(dirname5(filePath), { recursive: true });
9352
9412
  atomicWriteFileSync(filePath, content);
9353
9413
  const lines = content.split("\n").length;
9354
9414
  return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
@@ -9356,18 +9416,18 @@ Any of these triggers means use save_last_response, NOT write_file:
9356
9416
  };
9357
9417
 
9358
9418
  // src/tools/builtin/save-memory.ts
9359
- import { join as join11 } from "path";
9419
+ import { join as join12 } from "path";
9360
9420
  import { homedir as homedir5 } from "os";
9361
9421
 
9362
9422
  // src/memory/persistent-memory.ts
9363
- import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync11 } from "fs";
9423
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync12 } from "fs";
9364
9424
  import { randomUUID, createHash as createHash2 } from "crypto";
9365
- import { dirname as dirname5, join as join10, resolve as resolve6 } from "path";
9425
+ import { dirname as dirname6, join as join11, resolve as resolve6 } from "path";
9366
9426
 
9367
9427
  // src/tools/git-context.ts
9368
9428
  import { execSync as execSync2 } from "child_process";
9369
- import { existsSync as existsSync14 } from "fs";
9370
- import { join as join9 } from "path";
9429
+ import { existsSync as existsSync15 } from "fs";
9430
+ import { join as join10 } from "path";
9371
9431
  function runGit(cmd, cwd) {
9372
9432
  try {
9373
9433
  return execSync2(`git ${cmd}`, {
@@ -9384,7 +9444,7 @@ function getGitRoot(cwd = process.cwd()) {
9384
9444
  return runGit("rev-parse --show-toplevel", cwd);
9385
9445
  }
9386
9446
  function getGitContext(cwd = process.cwd()) {
9387
- if (!existsSync14(join9(cwd, ".git"))) {
9447
+ if (!existsSync15(join10(cwd, ".git"))) {
9388
9448
  const result = runGit("rev-parse --git-dir", cwd);
9389
9449
  if (!result) return null;
9390
9450
  }
@@ -9456,10 +9516,10 @@ function formatGitContextForPrompt(ctx) {
9456
9516
 
9457
9517
  // src/memory/persistent-memory.ts
9458
9518
  function memoryStorePath(configDir) {
9459
- return join10(configDir, MEMORY_STORE_FILE_NAME);
9519
+ return join11(configDir, MEMORY_STORE_FILE_NAME);
9460
9520
  }
9461
9521
  function memoryMarkdownPath(configDir) {
9462
- return join10(configDir, MEMORY_FILE_NAME);
9522
+ return join11(configDir, MEMORY_FILE_NAME);
9463
9523
  }
9464
9524
  function getProjectKey(cwd = process.cwd()) {
9465
9525
  return resolve6(getGitRoot(cwd) ?? cwd);
@@ -9495,8 +9555,8 @@ function normalizeEntry(value) {
9495
9555
  }
9496
9556
  function legacyEntriesFromMarkdown(configDir) {
9497
9557
  const file = memoryMarkdownPath(configDir);
9498
- if (!existsSync15(file)) return [];
9499
- const content = readFileSync11(file, "utf-8").trim();
9558
+ if (!existsSync16(file)) return [];
9559
+ const content = readFileSync12(file, "utf-8").trim();
9500
9560
  if (!content) return [];
9501
9561
  const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
9502
9562
  const chunks = parts.length > 1 ? parts : [content];
@@ -9512,8 +9572,8 @@ ${chunk}`).digest("hex").slice(0, 12);
9512
9572
  }
9513
9573
  function loadMemoryEntries(configDir) {
9514
9574
  const file = memoryStorePath(configDir);
9515
- if (!existsSync15(file)) return legacyEntriesFromMarkdown(configDir);
9516
- const text = readFileSync11(file, "utf-8").trim();
9575
+ if (!existsSync16(file)) return legacyEntriesFromMarkdown(configDir);
9576
+ const text = readFileSync12(file, "utf-8").trim();
9517
9577
  if (!text) return [];
9518
9578
  const entries = [];
9519
9579
  for (const line of text.split("\n")) {
@@ -9528,7 +9588,7 @@ function loadMemoryEntries(configDir) {
9528
9588
  return entries;
9529
9589
  }
9530
9590
  function saveMemoryEntries(configDir, entries) {
9531
- mkdirSync7(configDir, { recursive: true });
9591
+ mkdirSync8(configDir, { recursive: true });
9532
9592
  const jsonl = entries.map((entry) => JSON.stringify(entry)).join("\n");
9533
9593
  atomicWriteFileSync(memoryStorePath(configDir), jsonl ? jsonl + "\n" : "");
9534
9594
  syncLegacyMarkdown(configDir, entries);
@@ -9603,7 +9663,7 @@ function findUnique(entries, idPrefix) {
9603
9663
  return matches[0];
9604
9664
  }
9605
9665
  function syncLegacyMarkdown(configDir, entries = loadMemoryEntries(configDir)) {
9606
- mkdirSync7(dirname5(memoryMarkdownPath(configDir)), { recursive: true });
9666
+ mkdirSync8(dirname6(memoryMarkdownPath(configDir)), { recursive: true });
9607
9667
  const active = entries.filter((entry) => entry.approved && !isMemoryExpired(entry));
9608
9668
  const markdown = active.map((entry) => {
9609
9669
  const date = entry.createdAt.replace("T", " ").slice(0, 19);
@@ -9646,7 +9706,7 @@ ${entry.content}`;
9646
9706
 
9647
9707
  // src/tools/builtin/save-memory.ts
9648
9708
  function getConfigDir() {
9649
- return join11(homedir5(), CONFIG_DIR_NAME);
9709
+ return join12(homedir5(), CONFIG_DIR_NAME);
9650
9710
  }
9651
9711
  function parseScope(value) {
9652
9712
  return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
@@ -9980,13 +10040,13 @@ function formatResults2(query, data, _requested) {
9980
10040
  }
9981
10041
 
9982
10042
  // src/agents/agent-config.ts
9983
- import { existsSync as existsSync17, readdirSync as readdirSync8, readFileSync as readFileSync13 } from "fs";
9984
- import { join as join13 } from "path";
10043
+ import { existsSync as existsSync18, readdirSync as readdirSync8, readFileSync as readFileSync14 } from "fs";
10044
+ import { join as join14 } from "path";
9985
10045
  import { homedir as homedir6 } from "os";
9986
10046
 
9987
10047
  // src/plugins/plugin-manager.ts
9988
- import { existsSync as existsSync16, mkdirSync as mkdirSync8, readdirSync as readdirSync7, readFileSync as readFileSync12, cpSync, rmSync, statSync as statSync6 } from "fs";
9989
- import { basename as basename5, dirname as dirname6, join as join12, resolve as resolve7 } from "path";
10048
+ import { existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync13, cpSync, rmSync, statSync as statSync7 } from "fs";
10049
+ import { basename as basename5, dirname as dirname7, join as join13, resolve as resolve7 } from "path";
9990
10050
  import { createHash as createHash3 } from "crypto";
9991
10051
  import { z as z2 } from "zod";
9992
10052
  var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
@@ -10046,48 +10106,48 @@ var PluginManifestSchema = z2.object({
10046
10106
  permissionHints: z2.array(z2.string()).default([])
10047
10107
  }).strict();
10048
10108
  function pluginRoot(configDir) {
10049
- return join12(configDir, PLUGINS_DIR_NAME);
10109
+ return join13(configDir, PLUGINS_DIR_NAME);
10050
10110
  }
10051
10111
  function pluginManifestPath(pluginDir) {
10052
- return join12(pluginDir, MANIFEST_RELATIVE);
10112
+ return join13(pluginDir, MANIFEST_RELATIVE);
10053
10113
  }
10054
10114
  function statePath(configDir) {
10055
- return join12(pluginRoot(configDir), STATE_FILE);
10115
+ return join13(pluginRoot(configDir), STATE_FILE);
10056
10116
  }
10057
10117
  function loadState(configDir) {
10058
10118
  const file = statePath(configDir);
10059
- if (!existsSync16(file)) return { version: 1, plugins: [] };
10119
+ if (!existsSync17(file)) return { version: 1, plugins: [] };
10060
10120
  try {
10061
- const parsed = JSON.parse(readFileSync12(file, "utf-8"));
10121
+ const parsed = JSON.parse(readFileSync13(file, "utf-8"));
10062
10122
  return { version: 1, plugins: Array.isArray(parsed.plugins) ? parsed.plugins : [] };
10063
10123
  } catch {
10064
10124
  return { version: 1, plugins: [] };
10065
10125
  }
10066
10126
  }
10067
10127
  function saveState(configDir, state2) {
10068
- mkdirSync8(pluginRoot(configDir), { recursive: true });
10128
+ mkdirSync9(pluginRoot(configDir), { recursive: true });
10069
10129
  atomicWriteFileSync(statePath(configDir), JSON.stringify(state2, null, 2));
10070
10130
  }
10071
10131
  function hashPluginManifest(manifestPath) {
10072
- return createHash3("sha256").update(readFileSync12(manifestPath)).digest("hex");
10132
+ return createHash3("sha256").update(readFileSync13(manifestPath)).digest("hex");
10073
10133
  }
10074
10134
  function readPluginManifest(manifestPath) {
10075
- return PluginManifestSchema.parse(JSON.parse(readFileSync12(manifestPath, "utf-8")));
10135
+ return PluginManifestSchema.parse(JSON.parse(readFileSync13(manifestPath, "utf-8")));
10076
10136
  }
10077
10137
  function findPluginSourceDir(inputPath) {
10078
10138
  const abs = resolve7(inputPath);
10079
- const direct = statSync6(abs).isDirectory() ? abs : dirname6(abs);
10080
- if (existsSync16(pluginManifestPath(direct))) return direct;
10081
- const nested = join12(direct, ".aicli-plugin");
10082
- if (existsSync16(join12(nested, "plugin.json"))) return direct;
10139
+ const direct = statSync7(abs).isDirectory() ? abs : dirname7(abs);
10140
+ if (existsSync17(pluginManifestPath(direct))) return direct;
10141
+ const nested = join13(direct, ".aicli-plugin");
10142
+ if (existsSync17(join13(nested, "plugin.json"))) return direct;
10083
10143
  throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
10084
10144
  }
10085
10145
  function installPlugin(configDir, inputPath) {
10086
10146
  const sourceDir = findPluginSourceDir(inputPath);
10087
10147
  const manifest = readPluginManifest(pluginManifestPath(sourceDir));
10088
- const targetDir = join12(pluginRoot(configDir), manifest.name);
10089
- mkdirSync8(pluginRoot(configDir), { recursive: true });
10090
- if (existsSync16(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10148
+ const targetDir = join13(pluginRoot(configDir), manifest.name);
10149
+ mkdirSync9(pluginRoot(configDir), { recursive: true });
10150
+ if (existsSync17(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10091
10151
  cpSync(sourceDir, targetDir, { recursive: true });
10092
10152
  const manifestPath = pluginManifestPath(targetDir);
10093
10153
  const hash = hashPluginManifest(manifestPath);
@@ -10145,18 +10205,18 @@ function getInstalledPlugin(configDir, name) {
10145
10205
  }
10146
10206
  function listInstalledPlugins(configDir) {
10147
10207
  const root = pluginRoot(configDir);
10148
- if (!existsSync16(root)) return [];
10208
+ if (!existsSync17(root)) return [];
10149
10209
  const state2 = loadState(configDir);
10150
10210
  const out = [];
10151
10211
  for (const entry of readdirSync7(root)) {
10152
- const dir = join12(root, entry);
10212
+ const dir = join13(root, entry);
10153
10213
  try {
10154
- if (!statSync6(dir).isDirectory()) continue;
10214
+ if (!statSync7(dir).isDirectory()) continue;
10155
10215
  } catch {
10156
10216
  continue;
10157
10217
  }
10158
10218
  const manifestPath = pluginManifestPath(dir);
10159
- if (!existsSync16(manifestPath)) continue;
10219
+ if (!existsSync17(manifestPath)) continue;
10160
10220
  try {
10161
10221
  const manifest = readPluginManifest(manifestPath);
10162
10222
  const hash = hashPluginManifest(manifestPath);
@@ -10196,7 +10256,7 @@ function uniqueDirs(plugin, rels) {
10196
10256
  for (const rel of rels) {
10197
10257
  const abs = resolve7(plugin.dir, rel);
10198
10258
  if (!abs.startsWith(resolve7(plugin.dir))) continue;
10199
- if (existsSync16(abs)) dirs.add(statSync6(abs).isDirectory() ? abs : dirname6(abs));
10259
+ if (existsSync17(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname7(abs));
10200
10260
  }
10201
10261
  return [...dirs];
10202
10262
  }
@@ -10359,13 +10419,13 @@ function parseAgentConfig(raw, source, path3) {
10359
10419
  return cfg;
10360
10420
  }
10361
10421
  function loadAgentDir(dir, source) {
10362
- if (!existsSync17(dir)) return [];
10422
+ if (!existsSync18(dir)) return [];
10363
10423
  const out = [];
10364
10424
  for (const file of readdirSync8(dir)) {
10365
10425
  if (!file.endsWith(".json")) continue;
10366
- const path3 = join13(dir, file);
10426
+ const path3 = join14(dir, file);
10367
10427
  try {
10368
- const parsed = JSON.parse(readFileSync13(path3, "utf-8"));
10428
+ const parsed = JSON.parse(readFileSync14(path3, "utf-8"));
10369
10429
  const cfg = parseAgentConfig(parsed, source, path3);
10370
10430
  if (cfg) out.push(cfg);
10371
10431
  } catch {
@@ -10375,8 +10435,8 @@ function loadAgentDir(dir, source) {
10375
10435
  }
10376
10436
  function getAgentDirs(configDir, cwd = process.cwd()) {
10377
10437
  return {
10378
- user: join13(configDir ?? join13(homedir6(), CONFIG_DIR_NAME), "agents"),
10379
- project: join13(cwd, CONFIG_DIR_NAME, "agents")
10438
+ user: join14(configDir ?? join14(homedir6(), CONFIG_DIR_NAME), "agents"),
10439
+ project: join14(cwd, CONFIG_DIR_NAME, "agents")
10380
10440
  };
10381
10441
  }
10382
10442
  function listAgentConfigs(configDir, cwd = process.cwd()) {
@@ -11091,14 +11151,14 @@ var taskStopTool = {
11091
11151
 
11092
11152
  // src/tools/builtin/git-tools.ts
11093
11153
  import { execFileSync as execFileSync2 } from "child_process";
11094
- import { existsSync as existsSync18 } from "fs";
11095
- import { join as join14 } from "path";
11154
+ import { existsSync as existsSync19 } from "fs";
11155
+ import { join as join15 } from "path";
11096
11156
  function assertGitRepo(cwd) {
11097
11157
  let dir = cwd;
11098
11158
  const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
11099
11159
  while (dir && dir !== root) {
11100
- if (existsSync18(join14(dir, ".git"))) return;
11101
- const parent = join14(dir, "..");
11160
+ if (existsSync19(join15(dir, ".git"))) return;
11161
+ const parent = join15(dir, "..");
11102
11162
  if (parent === dir) break;
11103
11163
  dir = parent;
11104
11164
  }
@@ -11363,7 +11423,7 @@ ${commitOutput.trim()}`;
11363
11423
  };
11364
11424
 
11365
11425
  // src/tools/builtin/notebook-edit.ts
11366
- import { readFileSync as readFileSync14, existsSync as existsSync19 } from "fs";
11426
+ import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
11367
11427
  import { writeFile } from "fs/promises";
11368
11428
  import { resolve as resolve8, extname as extname2 } from "path";
11369
11429
  var notebookEditTool = {
@@ -11418,10 +11478,10 @@ var notebookEditTool = {
11418
11478
  if (extname2(absPath).toLowerCase() !== ".ipynb") {
11419
11479
  throw new ToolError("notebook_edit", "path must point to a .ipynb file");
11420
11480
  }
11421
- if (!existsSync19(absPath)) {
11481
+ if (!existsSync20(absPath)) {
11422
11482
  throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
11423
11483
  }
11424
- const raw = readFileSync14(absPath, "utf-8");
11484
+ const raw = readFileSync15(absPath, "utf-8");
11425
11485
  const nb = parseNotebook(raw);
11426
11486
  const cellIdx0 = cellIndexRaw - 1;
11427
11487
  undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
@@ -11838,8 +11898,8 @@ function estimateToolDefinitionTokens(def) {
11838
11898
 
11839
11899
  // src/tools/registry.ts
11840
11900
  import { pathToFileURL } from "url";
11841
- import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync9 } from "fs";
11842
- import { join as join15 } from "path";
11901
+ import { existsSync as existsSync21, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
11902
+ import { join as join16 } from "path";
11843
11903
  var ToolRegistry = class {
11844
11904
  tools = /* @__PURE__ */ new Map();
11845
11905
  pluginToolNames = /* @__PURE__ */ new Set();
@@ -12000,9 +12060,9 @@ var ToolRegistry = class {
12000
12060
  * Returns the number of successfully loaded plugins.
12001
12061
  */
12002
12062
  async loadPlugins(pluginsDir, allowPlugins = false) {
12003
- if (!existsSync20(pluginsDir)) {
12063
+ if (!existsSync21(pluginsDir)) {
12004
12064
  try {
12005
- mkdirSync9(pluginsDir, { recursive: true });
12065
+ mkdirSync10(pluginsDir, { recursive: true });
12006
12066
  } catch {
12007
12067
  }
12008
12068
  return 0;
@@ -12026,12 +12086,12 @@ var ToolRegistry = class {
12026
12086
  process.stderr.write(
12027
12087
  `
12028
12088
  [plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
12029
- ` + files.map((f) => ` + ${join15(pluginsDir, f)}`).join("\n") + "\n\n"
12089
+ ` + files.map((f) => ` + ${join16(pluginsDir, f)}`).join("\n") + "\n\n"
12030
12090
  );
12031
12091
  let loaded = 0;
12032
12092
  for (const file of files) {
12033
12093
  try {
12034
- const fileUrl = pathToFileURL(join15(pluginsDir, file)).href;
12094
+ const fileUrl = pathToFileURL(join16(pluginsDir, file)).href;
12035
12095
  const mod = await import(fileUrl);
12036
12096
  const tool = mod.tool ?? mod.default?.tool ?? mod.default;
12037
12097
  if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
@@ -12577,11 +12637,11 @@ var McpManager = class {
12577
12637
  };
12578
12638
 
12579
12639
  // src/skills/manager.ts
12580
- import { existsSync as existsSync21, readdirSync as readdirSync10, mkdirSync as mkdirSync10, statSync as statSync7 } from "fs";
12581
- import { join as join16 } from "path";
12640
+ import { existsSync as existsSync22, readdirSync as readdirSync10, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
12641
+ import { join as join17 } from "path";
12582
12642
 
12583
12643
  // src/skills/types.ts
12584
- import { readFileSync as readFileSync15 } from "fs";
12644
+ import { readFileSync as readFileSync16 } from "fs";
12585
12645
  import { basename as basename6 } from "path";
12586
12646
  function parseSimpleYaml(yaml) {
12587
12647
  const result = {};
@@ -12603,7 +12663,7 @@ function parseYamlArray(value) {
12603
12663
  function parseSkillFile(filePath) {
12604
12664
  let raw;
12605
12665
  try {
12606
- raw = readFileSync15(filePath, "utf-8");
12666
+ raw = readFileSync16(filePath, "utf-8");
12607
12667
  } catch {
12608
12668
  return null;
12609
12669
  }
@@ -12651,10 +12711,10 @@ var SkillManager = class {
12651
12711
  return this.skills.size;
12652
12712
  }
12653
12713
  loadSkillDir(dir, createIfMissing) {
12654
- if (!existsSync21(dir)) {
12714
+ if (!existsSync22(dir)) {
12655
12715
  if (createIfMissing) {
12656
12716
  try {
12657
- mkdirSync10(dir, { recursive: true });
12717
+ mkdirSync11(dir, { recursive: true });
12658
12718
  } catch {
12659
12719
  }
12660
12720
  }
@@ -12668,14 +12728,14 @@ var SkillManager = class {
12668
12728
  }
12669
12729
  for (const entry of entries) {
12670
12730
  let filePath;
12671
- const fullPath = join16(dir, entry);
12731
+ const fullPath = join17(dir, entry);
12672
12732
  if (entry.endsWith(".md")) {
12673
12733
  filePath = fullPath;
12674
12734
  } else {
12675
12735
  try {
12676
- if (statSync7(fullPath).isDirectory()) {
12677
- const skillMd = join16(fullPath, "SKILL.md");
12678
- if (existsSync21(skillMd)) filePath = skillMd;
12736
+ if (statSync8(fullPath).isDirectory()) {
12737
+ const skillMd = join17(fullPath, "SKILL.md");
12738
+ if (existsSync22(skillMd)) filePath = skillMd;
12679
12739
  else continue;
12680
12740
  } else {
12681
12741
  continue;
@@ -12734,7 +12794,7 @@ var SkillManager = class {
12734
12794
  // src/web/tool-executor-web.ts
12735
12795
  import { randomUUID as randomUUID3 } from "crypto";
12736
12796
  import { tmpdir as tmpdir2 } from "os";
12737
- import { existsSync as existsSync22, readFileSync as readFileSync16 } from "fs";
12797
+ import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
12738
12798
  var ToolExecutorWeb = class _ToolExecutorWeb {
12739
12799
  constructor(registry, ws) {
12740
12800
  this.registry = registry;
@@ -12873,9 +12933,9 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
12873
12933
  if (call.name === "write_file") {
12874
12934
  const filePath = String(call.arguments["path"] ?? "");
12875
12935
  const newContent = String(call.arguments["content"] ?? "");
12876
- if (filePath && existsSync22(filePath)) {
12936
+ if (filePath && existsSync23(filePath)) {
12877
12937
  try {
12878
- const old = readFileSync16(filePath, "utf-8");
12938
+ const old = readFileSync17(filePath, "utf-8");
12879
12939
  return renderDiff(old, newContent, { filePath });
12880
12940
  } catch {
12881
12941
  }
@@ -13178,16 +13238,16 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13178
13238
  };
13179
13239
 
13180
13240
  // src/repl/dev-state.ts
13181
- import { existsSync as existsSync23, readFileSync as readFileSync17, unlinkSync as unlinkSync4, mkdirSync as mkdirSync11 } from "fs";
13182
- import { join as join17 } from "path";
13241
+ import { existsSync as existsSync24, readFileSync as readFileSync18, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "fs";
13242
+ import { join as join18 } from "path";
13183
13243
  import { homedir as homedir7 } from "os";
13184
13244
  function getDevStatePath() {
13185
- return join17(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
13245
+ return join18(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
13186
13246
  }
13187
13247
  function loadDevState() {
13188
13248
  const path3 = getDevStatePath();
13189
- if (!existsSync23(path3)) return null;
13190
- const content = readFileSync17(path3, "utf-8").trim();
13249
+ if (!existsSync24(path3)) return null;
13250
+ const content = readFileSync18(path3, "utf-8").trim();
13191
13251
  return content || null;
13192
13252
  }
13193
13253
 
@@ -13444,8 +13504,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
13444
13504
  }
13445
13505
 
13446
13506
  // src/core/context-files.ts
13447
- import { existsSync as existsSync24, readFileSync as readFileSync18 } from "fs";
13448
- import { join as join18, relative as relative3, resolve as resolve9 } from "path";
13507
+ import { existsSync as existsSync25, readFileSync as readFileSync19 } from "fs";
13508
+ import { join as join19, relative as relative3, resolve as resolve9 } from "path";
13449
13509
  function uniqueNonEmpty(values) {
13450
13510
  const seen = /* @__PURE__ */ new Set();
13451
13511
  const out = [];
@@ -13475,7 +13535,7 @@ function readContextFile(level, filePath, cwd, maxBytes) {
13475
13535
  const name = filePath.split(/[\\/]/).pop() ?? filePath;
13476
13536
  const shown = displayPath(filePath, cwd);
13477
13537
  try {
13478
- const raw = readFileSync18(filePath);
13538
+ const raw = readFileSync19(filePath);
13479
13539
  const byteCount = raw.byteLength;
13480
13540
  if (byteCount === 0) {
13481
13541
  return {
@@ -13522,8 +13582,8 @@ function readContextFile(level, filePath, cwd, maxBytes) {
13522
13582
  function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
13523
13583
  const skipped = [];
13524
13584
  for (const candidate of candidates) {
13525
- const filePath = join18(dir, candidate);
13526
- if (!existsSync24(filePath)) continue;
13585
+ const filePath = join19(dir, candidate);
13586
+ if (!existsSync25(filePath)) continue;
13527
13587
  const result = readContextFile(level, filePath, cwd, maxBytes);
13528
13588
  if (result.skipped) skipped.push(result.skipped);
13529
13589
  if (result.layer) return { layer: result.layer, skipped };
@@ -13550,7 +13610,7 @@ function loadContextFiles(options) {
13550
13610
  });
13551
13611
  return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
13552
13612
  }
13553
- if (!existsSync24(filePath)) {
13613
+ if (!existsSync25(filePath)) {
13554
13614
  skipped.push({
13555
13615
  level: "single",
13556
13616
  filePath,
@@ -13601,8 +13661,8 @@ function loadContextFiles(options) {
13601
13661
  }
13602
13662
 
13603
13663
  // src/web/session-handler.ts
13604
- import { existsSync as existsSync28, readFileSync as readFileSync21, writeFileSync as writeFileSync3, mkdirSync as mkdirSync13, readdirSync as readdirSync13, statSync as statSync11, createWriteStream } from "fs";
13605
- import { join as join23, resolve as resolve10, dirname as dirname7 } from "path";
13664
+ import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync3, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync12, createWriteStream } from "fs";
13665
+ import { join as join24, resolve as resolve10, dirname as dirname8 } from "path";
13606
13666
 
13607
13667
  // src/cli/review-prompts.ts
13608
13668
  function buildReviewPrompt(diff, gitContextStr, detailed) {
@@ -13663,8 +13723,8 @@ If no security issues found, state "\u2705 No security vulnerabilities detected"
13663
13723
  }
13664
13724
 
13665
13725
  // src/repl/commands/project-init.ts
13666
- import { existsSync as existsSync25, readFileSync as readFileSync19, readdirSync as readdirSync11, statSync as statSync8 } from "fs";
13667
- import { join as join19 } from "path";
13726
+ import { existsSync as existsSync26, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
13727
+ import { join as join20 } from "path";
13668
13728
  var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
13669
13729
  "node_modules",
13670
13730
  ".git",
@@ -13708,11 +13768,11 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
13708
13768
  const sorted = filtered.sort((a, b) => {
13709
13769
  let aIsDir = false, bIsDir = false;
13710
13770
  try {
13711
- aIsDir = statSync8(join19(d, a)).isDirectory();
13771
+ aIsDir = statSync9(join20(d, a)).isDirectory();
13712
13772
  } catch {
13713
13773
  }
13714
13774
  try {
13715
- bIsDir = statSync8(join19(d, b)).isDirectory();
13775
+ bIsDir = statSync9(join20(d, b)).isDirectory();
13716
13776
  } catch {
13717
13777
  }
13718
13778
  if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
@@ -13720,12 +13780,12 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
13720
13780
  });
13721
13781
  for (let i = 0; i < sorted.length && count < maxEntries; i++) {
13722
13782
  const name = sorted[i];
13723
- const fullPath = join19(d, name);
13783
+ const fullPath = join20(d, name);
13724
13784
  const isLast = i === sorted.length - 1;
13725
13785
  const connector = isLast ? "+-- " : "|-- ";
13726
13786
  let isDir;
13727
13787
  try {
13728
- isDir = statSync8(fullPath).isDirectory();
13788
+ isDir = statSync9(fullPath).isDirectory();
13729
13789
  } catch {
13730
13790
  continue;
13731
13791
  }
@@ -13747,7 +13807,7 @@ function scanProject(cwd) {
13747
13807
  configFiles: [],
13748
13808
  directoryStructure: ""
13749
13809
  };
13750
- const check = (file) => existsSync25(join19(cwd, file));
13810
+ const check = (file) => existsSync26(join20(cwd, file));
13751
13811
  const configCandidates = [
13752
13812
  "package.json",
13753
13813
  "tsconfig.json",
@@ -13774,7 +13834,7 @@ function scanProject(cwd) {
13774
13834
  info.type = "node";
13775
13835
  info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
13776
13836
  try {
13777
- const pkg = JSON.parse(readFileSync19(join19(cwd, "package.json"), "utf-8"));
13837
+ const pkg = JSON.parse(readFileSync20(join20(cwd, "package.json"), "utf-8"));
13778
13838
  const scripts = pkg.scripts ?? {};
13779
13839
  info.buildCommand = scripts.build ? "npm run build" : void 0;
13780
13840
  info.testCommand = scripts.test ? "npm test" : void 0;
@@ -14248,34 +14308,34 @@ var DiscussionOrchestrator = class {
14248
14308
  };
14249
14309
 
14250
14310
  // src/diagnostics/doctor-report.ts
14251
- import { existsSync as existsSync27, statSync as statSync10 } from "fs";
14252
- import { join as join21 } from "path";
14311
+ import { existsSync as existsSync28, statSync as statSync11 } from "fs";
14312
+ import { join as join22 } from "path";
14253
14313
  import { arch as arch2, platform as platform6, release as release2 } from "os";
14254
14314
 
14255
14315
  // src/diagnostics/crash-log.ts
14256
14316
  import {
14257
- existsSync as existsSync26,
14258
- mkdirSync as mkdirSync12,
14317
+ existsSync as existsSync27,
14318
+ mkdirSync as mkdirSync13,
14259
14319
  readdirSync as readdirSync12,
14260
- readFileSync as readFileSync20,
14261
- statSync as statSync9,
14262
- unlinkSync as unlinkSync5,
14320
+ readFileSync as readFileSync21,
14321
+ statSync as statSync10,
14322
+ unlinkSync as unlinkSync6,
14263
14323
  writeFileSync as writeFileSync2
14264
14324
  } from "fs";
14265
- import { join as join20 } from "path";
14325
+ import { join as join21 } from "path";
14266
14326
  import { homedir as homedir8, platform as platform5, release, arch } from "os";
14267
14327
  var LOGS_DIR_NAME = "logs";
14268
14328
  var CRASH_LOG_PREFIX = "crash-";
14269
14329
  function getLogsDir(configDir) {
14270
- const base = configDir ?? join20(homedir8(), CONFIG_DIR_NAME);
14271
- return join20(base, LOGS_DIR_NAME);
14330
+ const base = configDir ?? join21(homedir8(), CONFIG_DIR_NAME);
14331
+ return join21(base, LOGS_DIR_NAME);
14272
14332
  }
14273
14333
  function truncate(s, n) {
14274
14334
  return s.length <= n ? s : s.slice(0, n) + "\u2026";
14275
14335
  }
14276
14336
  function listRecentCrashes(limit = 10, configDir) {
14277
14337
  const dir = getLogsDir(configDir);
14278
- if (!existsSync26(dir)) return [];
14338
+ if (!existsSync27(dir)) return [];
14279
14339
  const files = [];
14280
14340
  let names;
14281
14341
  try {
@@ -14285,10 +14345,10 @@ function listRecentCrashes(limit = 10, configDir) {
14285
14345
  }
14286
14346
  for (const name of names) {
14287
14347
  if (!name.startsWith(CRASH_LOG_PREFIX) || !name.endsWith(".log")) continue;
14288
- const full = join20(dir, name);
14348
+ const full = join21(dir, name);
14289
14349
  try {
14290
- const st = statSync9(full);
14291
- const head = readFileSync20(full, "utf-8").split("\n").slice(0, 20);
14350
+ const st = statSync10(full);
14351
+ const head = readFileSync21(full, "utf-8").split("\n").slice(0, 20);
14292
14352
  const tsLine = head.find((l) => l.startsWith("timestamp:"));
14293
14353
  const kindLine = head.find((l) => l.startsWith("kind:"));
14294
14354
  const msgIdx = head.findIndex((l) => l.trim() === "## message");
@@ -14307,8 +14367,8 @@ function listRecentCrashes(limit = 10, configDir) {
14307
14367
  return files.slice(0, limit);
14308
14368
  }
14309
14369
  function getConfigDirUsage(configDir) {
14310
- const base = configDir ?? join20(homedir8(), CONFIG_DIR_NAME);
14311
- if (!existsSync26(base)) return { totalBytes: 0, entries: [] };
14370
+ const base = configDir ?? join21(homedir8(), CONFIG_DIR_NAME);
14371
+ if (!existsSync27(base)) return { totalBytes: 0, entries: [] };
14312
14372
  const entries = [];
14313
14373
  let total = 0;
14314
14374
  let names;
@@ -14318,7 +14378,7 @@ function getConfigDirUsage(configDir) {
14318
14378
  return { totalBytes: 0, entries: [] };
14319
14379
  }
14320
14380
  for (const name of names) {
14321
- const full = join20(base, name);
14381
+ const full = join21(base, name);
14322
14382
  const bytes = dirSize(full);
14323
14383
  entries.push({ name, bytes });
14324
14384
  total += bytes;
@@ -14328,12 +14388,12 @@ function getConfigDirUsage(configDir) {
14328
14388
  }
14329
14389
  function dirSize(path3) {
14330
14390
  try {
14331
- const st = statSync9(path3);
14391
+ const st = statSync10(path3);
14332
14392
  if (st.isFile()) return st.size;
14333
14393
  if (!st.isDirectory()) return 0;
14334
14394
  let sum = 0;
14335
14395
  for (const name of readdirSync12(path3)) {
14336
- sum += dirSize(join20(path3, name));
14396
+ sum += dirSize(join21(path3, name));
14337
14397
  }
14338
14398
  return sum;
14339
14399
  } catch {
@@ -14343,11 +14403,11 @@ function dirSize(path3) {
14343
14403
 
14344
14404
  // src/diagnostics/doctor-report.ts
14345
14405
  function checkFile(label, path3) {
14346
- const exists = existsSync27(path3);
14406
+ const exists = existsSync28(path3);
14347
14407
  let sizeBytes = null;
14348
14408
  if (exists) {
14349
14409
  try {
14350
- sizeBytes = statSync10(path3).size;
14410
+ sizeBytes = statSync11(path3).size;
14351
14411
  } catch {
14352
14412
  }
14353
14413
  }
@@ -14386,12 +14446,12 @@ function buildDoctorReport(options) {
14386
14446
  configDir,
14387
14447
  providers: options.providers.listAll().map((p) => ({ id: p.id, displayName: p.displayName, configured: p.configured })),
14388
14448
  files: [
14389
- checkFile("config.json", join21(configDir, "config.json")),
14390
- checkFile("memory.md", join21(configDir, MEMORY_FILE_NAME)),
14391
- checkFile("memory.jsonl", join21(configDir, "memory.jsonl")),
14392
- checkFile("dev-state.md", join21(configDir, DEV_STATE_FILE_NAME)),
14393
- checkFile("hooks-trust.json", join21(configDir, "hooks-trust.json")),
14394
- checkFile("plugin-state.json", join21(configDir, "plugins", "plugin-state.json"))
14449
+ checkFile("config.json", join22(configDir, "config.json")),
14450
+ checkFile("memory.md", join22(configDir, MEMORY_FILE_NAME)),
14451
+ checkFile("memory.jsonl", join22(configDir, "memory.jsonl")),
14452
+ checkFile("dev-state.md", join22(configDir, DEV_STATE_FILE_NAME)),
14453
+ checkFile("hooks-trust.json", join22(configDir, "hooks-trust.json")),
14454
+ checkFile("plugin-state.json", join22(configDir, "plugins", "plugin-state.json"))
14395
14455
  ],
14396
14456
  context: {
14397
14457
  enabled: contextSetting !== false,
@@ -14426,7 +14486,7 @@ function buildDoctorReport(options) {
14426
14486
  disabled: hooks.filter((h) => h.disabled).length
14427
14487
  },
14428
14488
  plugins: {
14429
- root: join21(configDir, "plugins"),
14489
+ root: join22(configDir, "plugins"),
14430
14490
  installed: plugins.length,
14431
14491
  active: activeAssets.plugins.length,
14432
14492
  invalid: plugins.filter((p) => !p.valid).length,
@@ -14441,7 +14501,7 @@ function buildDoctorReport(options) {
14441
14501
  mcp: {
14442
14502
  enabled: config.get("mcpEnabled"),
14443
14503
  configured: Object.keys(mcpServers).length,
14444
- projectConfigExists: existsSync27(join21(projectRoot, MCP_PROJECT_CONFIG_NAME)),
14504
+ projectConfigExists: existsSync28(join22(projectRoot, MCP_PROJECT_CONFIG_NAME)),
14445
14505
  statuses: options.mcpStatuses ?? []
14446
14506
  },
14447
14507
  recentCrashes: listRecentCrashes(5, configDir),
@@ -14762,7 +14822,7 @@ function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, avai
14762
14822
  }
14763
14823
 
14764
14824
  // src/hub/persist.ts
14765
- import { join as join22 } from "path";
14825
+ import { join as join23 } from "path";
14766
14826
  function discussionToMessages(state2) {
14767
14827
  const out = [];
14768
14828
  const t0 = state2.messages[0]?.timestamp ?? /* @__PURE__ */ new Date();
@@ -14800,7 +14860,7 @@ async function persistDiscussion(state2, config, defaultProvider, defaultModel)
14800
14860
  session.title = `[Hub] ${state2.topic.slice(0, 48)}`.replace(/\n/g, " ");
14801
14861
  session.titleAiGenerated = true;
14802
14862
  await sm.save();
14803
- return { id: session.id, path: join22(config.getHistoryDir(), `${session.id}.json`) };
14863
+ return { id: session.id, path: join23(config.getHistoryDir(), `${session.id}.json`) };
14804
14864
  }
14805
14865
 
14806
14866
  // src/web/session-handler.ts
@@ -15517,7 +15577,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
15517
15577
  this.send({ type: "response_done", content, usage });
15518
15578
  if (pendingTeeSave && isCleanDocumentBody(content)) {
15519
15579
  try {
15520
- mkdirSync13(dirname7(pendingTeeSave), { recursive: true });
15580
+ mkdirSync14(dirname8(pendingTeeSave), { recursive: true });
15521
15581
  const bodyToSave = stripOuterCodeFence(content);
15522
15582
  atomicWriteFileSync(pendingTeeSave, bodyToSave);
15523
15583
  undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
@@ -15693,7 +15753,7 @@ ${summaryContent}`,
15693
15753
  let isError = false;
15694
15754
  let summary;
15695
15755
  try {
15696
- mkdirSync13(dirname7(saveToFile), { recursive: true });
15756
+ mkdirSync14(dirname8(saveToFile), { recursive: true });
15697
15757
  fileStream = createWriteStream(saveToFile);
15698
15758
  const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
15699
15759
  const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
@@ -15817,13 +15877,39 @@ ${summaryContent}`,
15817
15877
  break;
15818
15878
  }
15819
15879
  case "model": {
15820
- const modelId = args[0];
15821
- if (!modelId) {
15822
- this.send({ type: "error", message: "Usage: /model <id>" });
15880
+ const sub = args[0];
15881
+ if (!sub) {
15882
+ this.send({ type: "error", message: "Usage: /model <id>|refresh [provider]|cache [clear [provider]]" });
15883
+ return;
15884
+ }
15885
+ if (sub === "refresh") {
15886
+ const providerId = args[1] ?? this.currentProvider;
15887
+ try {
15888
+ const models2 = await this.providers.refreshModels(providerId);
15889
+ this.send({ type: "info", message: `Refreshed ${models2.length} model(s) for ${providerId}.` });
15890
+ this.sendStatus();
15891
+ } catch (err) {
15892
+ this.send({ type: "error", message: `Model refresh failed: ${err instanceof Error ? err.message : String(err)}` });
15893
+ }
15823
15894
  return;
15824
15895
  }
15825
- const provider = this.providers.get(this.currentProvider);
15826
- const found = provider?.info.models.find((m) => m.id === modelId);
15896
+ if (sub === "cache") {
15897
+ if ((args[1] ?? "").toLowerCase() === "clear") {
15898
+ const providerId = args[2];
15899
+ this.providers.clearModelCache(providerId);
15900
+ this.send({ type: "info", message: providerId ? `Cleared model cache for ${providerId}.` : "Cleared model cache." });
15901
+ return;
15902
+ }
15903
+ const rows = this.providers.getModelCacheStatus();
15904
+ this.send({
15905
+ type: "info",
15906
+ message: rows.length === 0 ? "Model cache is empty. Use /model refresh to populate it." : rows.map((row) => `${row.providerId}: ${row.count} model(s), fetched ${row.fetchedAt}`).join("\n")
15907
+ });
15908
+ return;
15909
+ }
15910
+ const modelId = sub;
15911
+ const models = await this.providers.listModels(this.currentProvider);
15912
+ const found = models.find((m) => m.id === modelId);
15827
15913
  if (!found) {
15828
15914
  this.send({ type: "error", message: `Model "${modelId}" not found` });
15829
15915
  return;
@@ -16382,9 +16468,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
16382
16468
  let modifiedFiles = 0;
16383
16469
  const diffLines2 = [];
16384
16470
  for (const [filePath, { earliest }] of fileMap) {
16385
- const currentContent = existsSync28(filePath) ? (() => {
16471
+ const currentContent = existsSync29(filePath) ? (() => {
16386
16472
  try {
16387
- return readFileSync21(filePath, "utf-8");
16473
+ return readFileSync22(filePath, "utf-8");
16388
16474
  } catch {
16389
16475
  return null;
16390
16476
  }
@@ -16900,7 +16986,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
16900
16986
  case "test": {
16901
16987
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
16902
16988
  try {
16903
- const { executeTests } = await import("./run-tests-O76JIBCW.js");
16989
+ const { executeTests } = await import("./run-tests-55JGEZYL.js");
16904
16990
  const argStr = args.join(" ").trim();
16905
16991
  let testArgs = {};
16906
16992
  if (argStr) {
@@ -16917,9 +17003,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
16917
17003
  // ── /init ───────────────────────────────────────────────────────
16918
17004
  case "init": {
16919
17005
  const cwd = process.cwd();
16920
- const targetPath = join23(cwd, "AICLI.md");
17006
+ const targetPath = join24(cwd, "AICLI.md");
16921
17007
  const force = args.includes("--force");
16922
- if (existsSync28(targetPath) && !force) {
17008
+ if (existsSync29(targetPath) && !force) {
16923
17009
  this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
16924
17010
  Use /init --force to overwrite.` });
16925
17011
  break;
@@ -17112,11 +17198,11 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
17112
17198
  break;
17113
17199
  }
17114
17200
  const dirPath = resolve10(sub);
17115
- if (!existsSync28(dirPath)) {
17201
+ if (!existsSync29(dirPath)) {
17116
17202
  this.send({ type: "error", message: `Directory not found: ${dirPath}` });
17117
17203
  break;
17118
17204
  }
17119
- if (!statSync11(dirPath).isDirectory()) {
17205
+ if (!statSync12(dirPath).isDirectory()) {
17120
17206
  this.send({ type: "error", message: `Not a directory: ${dirPath}` });
17121
17207
  break;
17122
17208
  }
@@ -17128,8 +17214,8 @@ It will be included in AI context for subsequent messages.` });
17128
17214
  // ── /commands ───────────────────────────────────────────────────
17129
17215
  case "commands": {
17130
17216
  const configDir = this.config.getConfigDir();
17131
- const commandsDir = join23(configDir, CUSTOM_COMMANDS_DIR_NAME);
17132
- if (!existsSync28(commandsDir)) {
17217
+ const commandsDir = join24(configDir, CUSTOM_COMMANDS_DIR_NAME);
17218
+ if (!existsSync29(commandsDir)) {
17133
17219
  this.send({ type: "info", message: `No custom commands directory.
17134
17220
  Create: ${commandsDir}/ with .md files.` });
17135
17221
  break;
@@ -17155,7 +17241,7 @@ Add .md files to create commands.` });
17155
17241
  // ── /plugins / /plugin ──────────────────────────────────────────
17156
17242
  case "plugins": {
17157
17243
  const configDir = this.config.getConfigDir();
17158
- const pluginsDir = join23(configDir, PLUGINS_DIR_NAME);
17244
+ const pluginsDir = join24(configDir, PLUGINS_DIR_NAME);
17159
17245
  const pluginTools = this.toolRegistry.listPluginTools();
17160
17246
  const pluginPackages = listInstalledPlugins(configDir);
17161
17247
  const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
@@ -17323,7 +17409,8 @@ Add .md files to create commands.` });
17323
17409
  messageCount: s.messageCount,
17324
17410
  updated: s.updated instanceof Date ? s.updated.toISOString() : String(s.updated),
17325
17411
  isCurrent: s.id === this.sessions.current?.id
17326
- }))
17412
+ })),
17413
+ totalCount: list.length
17327
17414
  });
17328
17415
  }
17329
17416
  sendToolsList() {
@@ -17437,7 +17524,7 @@ ${entry.content}`;
17437
17524
  const configDir = this.config.getConfigDir();
17438
17525
  try {
17439
17526
  atomicWriteFileSync(memoryStorePath(configDir), "");
17440
- atomicWriteFileSync(join23(configDir, MEMORY_FILE_NAME), "");
17527
+ atomicWriteFileSync(join24(configDir, MEMORY_FILE_NAME), "");
17441
17528
  this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
17442
17529
  this.sendMemoryEntries();
17443
17530
  } catch (err) {
@@ -17719,8 +17806,8 @@ async function setupProxy(configProxy) {
17719
17806
  }
17720
17807
 
17721
17808
  // src/web/auth.ts
17722
- import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync4, mkdirSync as mkdirSync14, readdirSync as readdirSync14, copyFileSync } from "fs";
17723
- import { join as join24 } from "path";
17809
+ import { existsSync as existsSync30, readFileSync as readFileSync23, writeFileSync as writeFileSync4, mkdirSync as mkdirSync15, readdirSync as readdirSync14, copyFileSync } from "fs";
17810
+ import { join as join25 } from "path";
17724
17811
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
17725
17812
  var USERS_FILE = "users.json";
17726
17813
  var TOKEN_EXPIRY_HOURS = 24;
@@ -17735,7 +17822,7 @@ var AuthManager = class {
17735
17822
  db;
17736
17823
  constructor(baseDir) {
17737
17824
  this.baseDir = baseDir;
17738
- this.usersFile = join24(baseDir, USERS_FILE);
17825
+ this.usersFile = join25(baseDir, USERS_FILE);
17739
17826
  this.db = this.loadOrCreate();
17740
17827
  }
17741
17828
  // ── Public API ─────────────────────────────────────────────────
@@ -17764,9 +17851,9 @@ var AuthManager = class {
17764
17851
  }
17765
17852
  const salt = randomBytes(16).toString("hex");
17766
17853
  const passwordHash = this.hashPassword(password, salt);
17767
- const dataDir = join24(USERS_DIR, username);
17768
- const fullDataDir = join24(this.baseDir, dataDir);
17769
- mkdirSync14(join24(fullDataDir, "history"), { recursive: true });
17854
+ const dataDir = join25(USERS_DIR, username);
17855
+ const fullDataDir = join25(this.baseDir, dataDir);
17856
+ mkdirSync15(join25(fullDataDir, "history"), { recursive: true });
17770
17857
  const user = {
17771
17858
  username,
17772
17859
  passwordHash,
@@ -17881,7 +17968,7 @@ var AuthManager = class {
17881
17968
  getUserDataDir(username) {
17882
17969
  const user = this.db.users.find((u) => u.username === username);
17883
17970
  if (!user) throw new Error(`User not found: ${username}`);
17884
- return join24(this.baseDir, user.dataDir);
17971
+ return join25(this.baseDir, user.dataDir);
17885
17972
  }
17886
17973
  /** List all usernames */
17887
17974
  listUsers() {
@@ -17917,30 +18004,30 @@ var AuthManager = class {
17917
18004
  const err = this.register(username, password);
17918
18005
  if (err) return err;
17919
18006
  const userDir = this.getUserDataDir(username);
17920
- const globalConfig = join24(this.baseDir, "config.json");
17921
- if (existsSync29(globalConfig)) {
18007
+ const globalConfig = join25(this.baseDir, "config.json");
18008
+ if (existsSync30(globalConfig)) {
17922
18009
  try {
17923
- const content = readFileSync22(globalConfig, "utf-8");
17924
- writeFileSync4(join24(userDir, "config.json"), content, "utf-8");
18010
+ const content = readFileSync23(globalConfig, "utf-8");
18011
+ writeFileSync4(join25(userDir, "config.json"), content, "utf-8");
17925
18012
  } catch {
17926
18013
  }
17927
18014
  }
17928
- const globalMemory = join24(this.baseDir, "memory.md");
17929
- if (existsSync29(globalMemory)) {
18015
+ const globalMemory = join25(this.baseDir, "memory.md");
18016
+ if (existsSync30(globalMemory)) {
17930
18017
  try {
17931
- const content = readFileSync22(globalMemory, "utf-8");
17932
- writeFileSync4(join24(userDir, "memory.md"), content, "utf-8");
18018
+ const content = readFileSync23(globalMemory, "utf-8");
18019
+ writeFileSync4(join25(userDir, "memory.md"), content, "utf-8");
17933
18020
  } catch {
17934
18021
  }
17935
18022
  }
17936
- const globalHistory = join24(this.baseDir, "history");
17937
- if (existsSync29(globalHistory)) {
18023
+ const globalHistory = join25(this.baseDir, "history");
18024
+ if (existsSync30(globalHistory)) {
17938
18025
  try {
17939
18026
  const files = readdirSync14(globalHistory).filter((f) => f.endsWith(".json"));
17940
- const userHistory = join24(userDir, "history");
18027
+ const userHistory = join25(userDir, "history");
17941
18028
  for (const f of files) {
17942
18029
  try {
17943
- copyFileSync(join24(globalHistory, f), join24(userHistory, f));
18030
+ copyFileSync(join25(globalHistory, f), join25(userHistory, f));
17944
18031
  } catch {
17945
18032
  }
17946
18033
  }
@@ -17951,9 +18038,9 @@ var AuthManager = class {
17951
18038
  }
17952
18039
  // ── Private methods ────────────────────────────────────────────
17953
18040
  loadOrCreate() {
17954
- if (existsSync29(this.usersFile)) {
18041
+ if (existsSync30(this.usersFile)) {
17955
18042
  try {
17956
- return JSON.parse(readFileSync22(this.usersFile, "utf-8"));
18043
+ return JSON.parse(readFileSync23(this.usersFile, "utf-8"));
17957
18044
  } catch {
17958
18045
  }
17959
18046
  }
@@ -17969,7 +18056,7 @@ var AuthManager = class {
17969
18056
  this.saveDB(this.db);
17970
18057
  }
17971
18058
  saveDB(db) {
17972
- mkdirSync14(this.baseDir, { recursive: true });
18059
+ mkdirSync15(this.baseDir, { recursive: true });
17973
18060
  atomicWriteFileSync(this.usersFile, JSON.stringify(db, null, 2));
17974
18061
  }
17975
18062
  /** Legacy hash — kept only for migrating old users (v0.2.x) */
@@ -18001,7 +18088,7 @@ function getModuleDir() {
18001
18088
  if (typeof import.meta?.url === "string") {
18002
18089
  const url = new URL(import.meta.url);
18003
18090
  const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
18004
- return dirname8(filePath);
18091
+ return dirname9(filePath);
18005
18092
  }
18006
18093
  } catch {
18007
18094
  }
@@ -18024,7 +18111,8 @@ async function startWebServer(options = {}) {
18024
18111
  timeout: config.get("timeouts")[id],
18025
18112
  proxy: config.get("proxy")
18026
18113
  }),
18027
- config.get("customProviders")
18114
+ config.get("customProviders"),
18115
+ config.getConfigDir()
18028
18116
  );
18029
18117
  if (options._extraProviders) {
18030
18118
  for (const p of options._extraProviders) {
@@ -18072,8 +18160,8 @@ async function startWebServer(options = {}) {
18072
18160
  }
18073
18161
  }
18074
18162
  let skillManager = null;
18075
- const skillsDir = join25(config.getConfigDir(), SKILLS_DIR_NAME);
18076
- if (existsSync30(skillsDir)) {
18163
+ const skillsDir = join26(config.getConfigDir(), SKILLS_DIR_NAME);
18164
+ if (existsSync31(skillsDir)) {
18077
18165
  skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
18078
18166
  skillManager.loadSkills(pluginAssets.skillDirs);
18079
18167
  const count = skillManager.listSkills().length;
@@ -18144,18 +18232,18 @@ async function startWebServer(options = {}) {
18144
18232
  next();
18145
18233
  };
18146
18234
  const moduleDir = getModuleDir();
18147
- let clientDir = join25(moduleDir, "web", "client");
18148
- if (!existsSync30(clientDir)) {
18149
- clientDir = join25(moduleDir, "client");
18235
+ let clientDir = join26(moduleDir, "web", "client");
18236
+ if (!existsSync31(clientDir)) {
18237
+ clientDir = join26(moduleDir, "client");
18150
18238
  }
18151
- if (!existsSync30(clientDir)) {
18152
- clientDir = join25(moduleDir, "..", "..", "src", "web", "client");
18239
+ if (!existsSync31(clientDir)) {
18240
+ clientDir = join26(moduleDir, "..", "..", "src", "web", "client");
18153
18241
  }
18154
- if (!existsSync30(clientDir)) {
18155
- clientDir = join25(process.cwd(), "src", "web", "client");
18242
+ if (!existsSync31(clientDir)) {
18243
+ clientDir = join26(process.cwd(), "src", "web", "client");
18156
18244
  }
18157
18245
  console.log(` Static files: ${clientDir}`);
18158
- app.use("/vendor", express.static(join25(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
18246
+ app.use("/vendor", express.static(join26(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
18159
18247
  app.use(express.static(clientDir));
18160
18248
  app.get("/api/status", (_req, res) => {
18161
18249
  res.json({
@@ -18222,7 +18310,7 @@ async function startWebServer(options = {}) {
18222
18310
  app.get("/api/files", requireAuth, (req, res) => {
18223
18311
  const cwd = process.cwd();
18224
18312
  const prefix = req.query.prefix || "";
18225
- const targetDir = join25(cwd, prefix);
18313
+ const targetDir = join26(cwd, prefix);
18226
18314
  try {
18227
18315
  const canonicalTarget = realpathSync(resolve11(targetDir));
18228
18316
  const canonicalCwd = realpathSync(resolve11(cwd));
@@ -18239,7 +18327,7 @@ async function startWebServer(options = {}) {
18239
18327
  const entries = readdirSync15(targetDir, { withFileTypes: true });
18240
18328
  const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
18241
18329
  name: e.name,
18242
- path: relative4(cwd, join25(targetDir, e.name)).replace(/\\/g, "/"),
18330
+ path: relative4(cwd, join26(targetDir, e.name)).replace(/\\/g, "/"),
18243
18331
  isDir: e.isDirectory()
18244
18332
  }));
18245
18333
  res.json({ files });
@@ -18252,18 +18340,22 @@ async function startWebServer(options = {}) {
18252
18340
  const authUser = req._authUser;
18253
18341
  const sm = authUser ? getUserShared(authUser).sessions : sessions;
18254
18342
  const list = sm.listSessions();
18343
+ const all = req.query.all === "true";
18344
+ const limit = all ? -1 : Number(req.query.limit) || 50;
18345
+ const sliced = limit > 0 ? list.slice(0, limit) : list;
18255
18346
  res.json({
18256
- sessions: list.slice(0, 50).map((s) => ({
18347
+ sessions: sliced.map((s) => ({
18257
18348
  id: s.id,
18258
18349
  title: s.title,
18259
18350
  provider: s.provider,
18260
18351
  model: s.model,
18261
18352
  messageCount: s.messageCount,
18262
18353
  updated: s.updated
18263
- }))
18354
+ })),
18355
+ totalCount: list.length
18264
18356
  });
18265
18357
  } catch {
18266
- res.json({ sessions: [] });
18358
+ res.json({ sessions: [], totalCount: 0 });
18267
18359
  }
18268
18360
  });
18269
18361
  app.get("/api/sessions/:id/replay", requireAuth, (req, res) => {
@@ -18275,8 +18367,8 @@ async function startWebServer(options = {}) {
18275
18367
  try {
18276
18368
  const authUser = req._authUser;
18277
18369
  const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
18278
- const filePath = join25(histDir, `${id}.json`);
18279
- if (!existsSync30(filePath)) {
18370
+ const filePath = join26(histDir, `${id}.json`);
18371
+ if (!existsSync31(filePath)) {
18280
18372
  res.status(404).json({ error: "Session not found" });
18281
18373
  return;
18282
18374
  }
@@ -18291,7 +18383,7 @@ async function startWebServer(options = {}) {
18291
18383
  res.status(404).json({ error: "Session not found" });
18292
18384
  return;
18293
18385
  }
18294
- const data = JSON.parse(readFileSync23(filePath, "utf-8"));
18386
+ const data = JSON.parse(readFileSync24(filePath, "utf-8"));
18295
18387
  res.json({ session: data });
18296
18388
  } catch (err) {
18297
18389
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
@@ -18304,7 +18396,7 @@ async function startWebServer(options = {}) {
18304
18396
  return;
18305
18397
  }
18306
18398
  const cwd = process.cwd();
18307
- const fullPath = resolve11(join25(cwd, filePath));
18399
+ const fullPath = resolve11(join26(cwd, filePath));
18308
18400
  try {
18309
18401
  const canonicalFull = realpathSync(fullPath);
18310
18402
  const canonicalCwd = realpathSync(resolve11(cwd));
@@ -18317,12 +18409,12 @@ async function startWebServer(options = {}) {
18317
18409
  return;
18318
18410
  }
18319
18411
  try {
18320
- const stat = statSync12(fullPath);
18412
+ const stat = statSync13(fullPath);
18321
18413
  if (stat.size > 512 * 1024) {
18322
18414
  res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
18323
18415
  return;
18324
18416
  }
18325
- const content = readFileSync23(fullPath, "utf-8");
18417
+ const content = readFileSync24(fullPath, "utf-8");
18326
18418
  res.json({ content, size: stat.size });
18327
18419
  } catch {
18328
18420
  res.json({ error: "Cannot read file" });
@@ -18577,17 +18669,17 @@ function resolveProjectMcpPath() {
18577
18669
  const cwd = process.cwd();
18578
18670
  const gitRoot = getGitRoot(cwd);
18579
18671
  const projectRoot = gitRoot ?? cwd;
18580
- const configPath = join25(projectRoot, MCP_PROJECT_CONFIG_NAME);
18581
- return existsSync30(configPath) ? configPath : null;
18672
+ const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
18673
+ return existsSync31(configPath) ? configPath : null;
18582
18674
  }
18583
18675
  function loadProjectMcpConfig() {
18584
18676
  const cwd = process.cwd();
18585
18677
  const gitRoot = getGitRoot(cwd);
18586
18678
  const projectRoot = gitRoot ?? cwd;
18587
- const configPath = join25(projectRoot, MCP_PROJECT_CONFIG_NAME);
18588
- if (!existsSync30(configPath)) return null;
18679
+ const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
18680
+ if (!existsSync31(configPath)) return null;
18589
18681
  try {
18590
- const raw = JSON.parse(readFileSync23(configPath, "utf-8"));
18682
+ const raw = JSON.parse(readFileSync24(configPath, "utf-8"));
18591
18683
  return raw.mcpServers ?? raw;
18592
18684
  } catch {
18593
18685
  return null;