jinzd-ai-cli 0.4.219 → 0.4.220

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 (30) hide show
  1. package/README.md +101 -99
  2. package/README.zh-CN.md +40 -38
  3. package/dist/{batch-CHAPYRVM.js → batch-QBSKTLTO.js} +3 -3
  4. package/dist/{chunk-TZQHYZKT.js → chunk-5ULLIOVC.js} +1 -0
  5. package/dist/{chunk-KIEZBTVD.js → chunk-5WBNXYDJ.js} +2 -2
  6. package/dist/{chunk-GGKQHPB3.js → chunk-ALQN7VUJ.js} +1 -1
  7. package/dist/{chunk-EGMORH5W.js → chunk-CAUHLHDR.js} +1 -1
  8. package/dist/{chunk-GEN4O5QH.js → chunk-IZW5LMI6.js} +4 -4
  9. package/dist/{chunk-3I4RZYYO.js → chunk-P3LKFA54.js} +6 -5
  10. package/dist/{chunk-VADHBW7T.js → chunk-P4DYAGUI.js} +1 -1
  11. package/dist/{chunk-4ZN6L6K5.js → chunk-P4PTXCHY.js} +6 -5
  12. package/dist/{chunk-QMXC327F.js → chunk-PUBCJF7E.js} +213 -175
  13. package/dist/{chunk-U7KVU25H.js → chunk-QZYNRCPX.js} +1 -1
  14. package/dist/{chunk-QUFYBR6Q.js → chunk-VHX7KZDY.js} +2 -2
  15. package/dist/{chunk-64NUW3WL.js → chunk-Y25MSSUP.js} +1 -1
  16. package/dist/{ci-QPP66T53.js → ci-5SNXGM3M.js} +6 -6
  17. package/dist/{ci-format-5S3EEYRK.js → ci-format-57QQTHQE.js} +2 -2
  18. package/dist/{constants-FJOLBABC.js → constants-ML5KFEOH.js} +1 -1
  19. package/dist/{doctor-cli-KB2LUDF3.js → doctor-cli-ZRH3XZ3R.js} +6 -6
  20. package/dist/electron-server.js +505 -442
  21. package/dist/{hub-DIM7SKKY.js → hub-IPLMJ6TJ.js} +1 -1
  22. package/dist/index.js +72 -31
  23. package/dist/{pr-YQGH72N6.js → pr-GJYDAPK4.js} +8 -7
  24. package/dist/{run-tests-O76JIBCW.js → run-tests-GK5AIAV2.js} +1 -1
  25. package/dist/{run-tests-GEZSSNJM.js → run-tests-KLQAI5CX.js} +2 -2
  26. package/dist/{server-Z6O3G2LY.js → server-5FZDALHH.js} +5 -5
  27. package/dist/{server-5E2AIXVX.js → server-RU36PRSH.js} +43 -16
  28. package/dist/{task-orchestrator-5HBW4O64.js → task-orchestrator-JMHPVNUO.js} +5 -5
  29. package/dist/{usage-BMK6M5U3.js → usage-IRMRCCKN.js} +3 -3
  30. 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-CAUHLHDR.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 statSync12, 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 } 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
  }
@@ -4633,12 +4669,12 @@ var SessionManager = class {
4633
4669
  return session;
4634
4670
  }
4635
4671
  listSessions() {
4636
- if (!existsSync2(this.historyDir)) return [];
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));
4677
+ const meta = this.readSessionMeta(join3(this.historyDir, file));
4642
4678
  if (meta) metas.push(meta);
4643
4679
  } catch (err) {
4644
4680
  process.stderr.write(
@@ -4678,7 +4714,7 @@ var SessionManager = class {
4678
4714
  if (messageCount === void 0) {
4679
4715
  messageCount = 0;
4680
4716
  try {
4681
- const full = readFileSync2(filePath, "utf-8");
4717
+ const full = readFileSync3(filePath, "utf-8");
4682
4718
  const matches = full.match(/"role"\s*:/g);
4683
4719
  messageCount = matches ? matches.length : 0;
4684
4720
  } catch {
@@ -4694,7 +4730,7 @@ var SessionManager = class {
4694
4730
  title: title || void 0
4695
4731
  };
4696
4732
  }
4697
- const data = JSON.parse(readFileSync2(filePath, "utf-8"));
4733
+ const data = JSON.parse(readFileSync3(filePath, "utf-8"));
4698
4734
  return {
4699
4735
  id: data.id,
4700
4736
  provider: data.provider,
@@ -4706,10 +4742,10 @@ var SessionManager = class {
4706
4742
  };
4707
4743
  }
4708
4744
  deleteSession(id) {
4709
- const filePath = join2(this.historyDir, `${id}.json`);
4710
- if (!existsSync2(filePath)) return false;
4745
+ const filePath = join3(this.historyDir, `${id}.json`);
4746
+ if (!existsSync3(filePath)) return false;
4711
4747
  try {
4712
- unlinkSync(filePath);
4748
+ unlinkSync2(filePath);
4713
4749
  if (this._current && this._current.id === id) {
4714
4750
  this._current = null;
4715
4751
  }
@@ -4744,14 +4780,14 @@ var SessionManager = class {
4744
4780
  * 每个 session 最多返回 3 条匹配片段,全局最多 maxResults 个 session。
4745
4781
  */
4746
4782
  searchMessages(query, maxResults = 20) {
4747
- if (!existsSync2(this.historyDir)) return [];
4783
+ if (!existsSync3(this.historyDir)) return [];
4748
4784
  const q = query.toLowerCase();
4749
- const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) => join2(this.historyDir, f));
4785
+ const files = readdirSync(this.historyDir).filter((f) => f.endsWith(".json")).map((f) => join3(this.historyDir, f));
4750
4786
  const results = [];
4751
4787
  for (const filePath of files) {
4752
4788
  if (results.length >= maxResults) break;
4753
4789
  try {
4754
- const data = JSON.parse(readFileSync2(filePath, "utf-8"));
4790
+ const data = JSON.parse(readFileSync3(filePath, "utf-8"));
4755
4791
  const messages = data.messages ?? [];
4756
4792
  const matches = [];
4757
4793
  for (const msg of messages) {
@@ -4798,12 +4834,12 @@ var SessionManager = class {
4798
4834
 
4799
4835
  // src/tools/builtin/bash.ts
4800
4836
  import { spawn } from "child_process";
4801
- import { existsSync as existsSync4, readdirSync as readdirSync2, statSync } from "fs";
4837
+ import { existsSync as existsSync5, readdirSync as readdirSync2, statSync } from "fs";
4802
4838
  import { platform as platform2 } from "os";
4803
4839
  import { resolve } from "path";
4804
4840
 
4805
4841
  // src/tools/undo-stack.ts
4806
- import { readFileSync as readFileSync3, writeFileSync, unlinkSync as unlinkSync2, rmdirSync, existsSync as existsSync3 } from "fs";
4842
+ import { readFileSync as readFileSync4, writeFileSync, unlinkSync as unlinkSync3, rmdirSync, existsSync as existsSync4 } from "fs";
4807
4843
  var MAX_UNDO_DEPTH = 20;
4808
4844
  var UndoStack = class {
4809
4845
  stack = [];
@@ -4814,9 +4850,9 @@ var UndoStack = class {
4814
4850
  */
4815
4851
  push(filePath, description) {
4816
4852
  let previousContent = null;
4817
- if (existsSync3(filePath)) {
4853
+ if (existsSync4(filePath)) {
4818
4854
  try {
4819
- previousContent = readFileSync3(filePath, "utf-8");
4855
+ previousContent = readFileSync4(filePath, "utf-8");
4820
4856
  } catch {
4821
4857
  return;
4822
4858
  }
@@ -4870,7 +4906,7 @@ var UndoStack = class {
4870
4906
  try {
4871
4907
  if (entry.previousContent === null) {
4872
4908
  if (entry.isDirectory) {
4873
- if (existsSync3(entry.filePath)) {
4909
+ if (existsSync4(entry.filePath)) {
4874
4910
  try {
4875
4911
  rmdirSync(entry.filePath);
4876
4912
  return { entry, result: `Removed newly created directory: ${entry.filePath}` };
@@ -4880,8 +4916,8 @@ var UndoStack = class {
4880
4916
  }
4881
4917
  return { entry, result: `Directory already removed: ${entry.filePath}` };
4882
4918
  } else {
4883
- if (existsSync3(entry.filePath)) {
4884
- unlinkSync2(entry.filePath);
4919
+ if (existsSync4(entry.filePath)) {
4920
+ unlinkSync3(entry.filePath);
4885
4921
  }
4886
4922
  return { entry, result: `Deleted newly created file: ${entry.filePath}` };
4887
4923
  }
@@ -5025,7 +5061,7 @@ Important rules:
5025
5061
  throw new ToolError("bash", blockingHint);
5026
5062
  }
5027
5063
  let currentCwd = getCwd();
5028
- if (!existsSync4(currentCwd)) {
5064
+ if (!existsSync5(currentCwd)) {
5029
5065
  const fallback = process.cwd();
5030
5066
  process.stderr.write(
5031
5067
  `[bash] Previous cwd "${currentCwd}" no longer exists, reset to "${fallback}"
@@ -5037,7 +5073,7 @@ Important rules:
5037
5073
  let effectiveCwd = currentCwd;
5038
5074
  if (cwdArg) {
5039
5075
  const resolved = resolve(currentCwd, cwdArg);
5040
- if (!existsSync4(resolved)) {
5076
+ if (!existsSync5(resolved)) {
5041
5077
  throw new ToolError(
5042
5078
  "bash",
5043
5079
  `cwd directory does not exist: "${resolved}". Create it first (e.g. mkdir -p "${resolved}") before specifying it as cwd.`
@@ -5057,7 +5093,7 @@ Important rules:
5057
5093
  const parsedTargets = parseCreationTargets(command, effectiveCwd);
5058
5094
  const parsedTargetsBefore = /* @__PURE__ */ new Map();
5059
5095
  for (const t of parsedTargets) {
5060
- parsedTargetsBefore.set(t, existsSync4(t));
5096
+ parsedTargetsBefore.set(t, existsSync5(t));
5061
5097
  }
5062
5098
  try {
5063
5099
  const { stdout, stderr, status, signal, timedOut, aborted } = await runShell(
@@ -5285,7 +5321,7 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
5285
5321
  }
5286
5322
  }
5287
5323
  for (const [target, existedBefore] of parsedTargetsBefore) {
5288
- if (!existedBefore && !tracked.has(target) && existsSync4(target)) {
5324
+ if (!existedBefore && !tracked.has(target) && existsSync5(target)) {
5289
5325
  try {
5290
5326
  const st = statSync(target);
5291
5327
  if (st.isDirectory()) {
@@ -5398,7 +5434,7 @@ function updateCwdFromCommand(command, baseCwd) {
5398
5434
  if (!target || target.startsWith("$") || target === "~") return;
5399
5435
  try {
5400
5436
  const newDir = resolve(baseCwd, target);
5401
- if (existsSync4(newDir)) {
5437
+ if (existsSync5(newDir)) {
5402
5438
  setCwd(newDir);
5403
5439
  }
5404
5440
  } catch {
@@ -5406,9 +5442,9 @@ function updateCwdFromCommand(command, baseCwd) {
5406
5442
  }
5407
5443
 
5408
5444
  // src/tools/builtin/read-file.ts
5409
- import { readFileSync as readFileSync4, existsSync as existsSync5, statSync as statSync2, readdirSync as readdirSync3 } from "fs";
5445
+ import { readFileSync as readFileSync5, existsSync as existsSync6, statSync as statSync2, readdirSync as readdirSync3 } from "fs";
5410
5446
  import { execFileSync } from "child_process";
5411
- import { extname, resolve as resolve2, basename, sep, dirname } from "path";
5447
+ import { extname, resolve as resolve2, basename, sep, dirname as dirname2 } from "path";
5412
5448
  import { homedir as homedir2 } from "os";
5413
5449
 
5414
5450
  // src/tools/builtin/notebook-utils.ts
@@ -5683,7 +5719,7 @@ var readFileTool = {
5683
5719
  const normalizedPath = resolve2(filePath);
5684
5720
  const refusal = getHardRefusal(normalizedPath);
5685
5721
  if (refusal) return refusal;
5686
- if (!existsSync5(normalizedPath)) {
5722
+ if (!existsSync6(normalizedPath)) {
5687
5723
  const suggestions = findSimilarFiles(filePath);
5688
5724
  if (suggestions.length > 0) {
5689
5725
  throw new ToolError(
@@ -5722,9 +5758,9 @@ Use the bash tool to read in segments, e.g.:
5722
5758
 
5723
5759
  ${pdfText}`;
5724
5760
  }
5725
- const dir = dirname(normalizedPath);
5761
+ const dir = dirname2(normalizedPath);
5726
5762
  const nameNoExt = basename(normalizedPath, ext);
5727
- const textAlts = [".md", ".txt", ".html"].map((e) => resolve2(dir, nameNoExt + e)).filter(existsSync5);
5763
+ const textAlts = [".md", ".txt", ".html"].map((e) => resolve2(dir, nameNoExt + e)).filter(existsSync6);
5728
5764
  if (textAlts.length > 0) {
5729
5765
  return `[PDF file: ${filePath}]
5730
5766
  Cannot extract text from this PDF, but found alternative text versions:
@@ -5737,7 +5773,7 @@ Suggestion: read existing text versions (.md / .txt) in the project, or install
5737
5773
  }
5738
5774
  if (ext === ".ipynb") {
5739
5775
  try {
5740
- const raw = readFileSync4(normalizedPath, "utf-8");
5776
+ const raw = readFileSync5(normalizedPath, "utf-8");
5741
5777
  const nb = parseNotebook(raw);
5742
5778
  return renderNotebookAsText(nb, filePath);
5743
5779
  } catch (err) {
@@ -5750,7 +5786,7 @@ ${err.message}`;
5750
5786
  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
5787
  }
5752
5788
  const { readFile: readFile2 } = await import("fs/promises");
5753
- const buf = size > 1048576 ? await readFile2(normalizedPath) : readFileSync4(normalizedPath);
5789
+ const buf = size > 1048576 ? await readFile2(normalizedPath) : readFileSync5(normalizedPath);
5754
5790
  if (encoding === "base64") {
5755
5791
  return `[File: ${filePath} | base64]
5756
5792
 
@@ -5788,12 +5824,12 @@ ${content}`;
5788
5824
  };
5789
5825
 
5790
5826
  // src/tools/builtin/write-file.ts
5791
- import { appendFileSync, mkdirSync as mkdirSync5 } from "fs";
5792
- import { dirname as dirname3 } from "path";
5827
+ import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
5828
+ import { dirname as dirname4 } from "path";
5793
5829
 
5794
5830
  // src/tools/executor.ts
5795
5831
  import chalk3 from "chalk";
5796
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
5832
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
5797
5833
  import { tmpdir } from "os";
5798
5834
 
5799
5835
  // src/core/readline-internal.ts
@@ -6002,8 +6038,8 @@ function simpleDiff(oldLines, newLines) {
6002
6038
  // src/tools/hooks.ts
6003
6039
  import { execSync } from "child_process";
6004
6040
  import { createHash } from "crypto";
6005
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5 } from "fs";
6006
- import { join as join3 } from "path";
6041
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6 } from "fs";
6042
+ import { join as join4 } from "path";
6007
6043
  var TRUST_FILE = "hooks-trust.json";
6008
6044
  var DEFAULT_TIMEOUT_MS = 5e3;
6009
6045
  function asArray(entry) {
@@ -6012,13 +6048,13 @@ function asArray(entry) {
6012
6048
  return raw.map((item) => typeof item === "string" ? { command: item } : item);
6013
6049
  }
6014
6050
  function hookTrustPath(configDir) {
6015
- return join3(configDir, TRUST_FILE);
6051
+ return join4(configDir, TRUST_FILE);
6016
6052
  }
6017
6053
  function loadHookTrustStore(configDir) {
6018
6054
  const file = hookTrustPath(configDir);
6019
- if (!existsSync6(file)) return { records: [] };
6055
+ if (!existsSync7(file)) return { records: [] };
6020
6056
  try {
6021
- const parsed = JSON.parse(readFileSync5(file, "utf-8"));
6057
+ const parsed = JSON.parse(readFileSync6(file, "utf-8"));
6022
6058
  return { records: Array.isArray(parsed.records) ? parsed.records : [] };
6023
6059
  } catch {
6024
6060
  return { records: [] };
@@ -6544,8 +6580,8 @@ var theme = new Proxy(DARK_THEME, {
6544
6580
  });
6545
6581
 
6546
6582
  // 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";
6583
+ import { existsSync as existsSync8, readFileSync as readFileSync7, mkdirSync as mkdirSync5 } from "fs";
6584
+ import { join as join5, dirname as dirname3 } from "path";
6549
6585
  import { homedir as homedir3 } from "os";
6550
6586
  var STATS_FILE_NAME = "tool-stats.json";
6551
6587
  var STATS_VERSION = 1;
@@ -6555,16 +6591,16 @@ var dirty = false;
6555
6591
  var pendingWrites = 0;
6556
6592
  var configDirOverride;
6557
6593
  function statsFilePath() {
6558
- const base = configDirOverride ?? join4(homedir3(), CONFIG_DIR_NAME);
6559
- return join4(base, STATS_FILE_NAME);
6594
+ const base = configDirOverride ?? join5(homedir3(), CONFIG_DIR_NAME);
6595
+ return join5(base, STATS_FILE_NAME);
6560
6596
  }
6561
6597
  function load() {
6562
6598
  const path3 = statsFilePath();
6563
- if (!existsSync7(path3)) {
6599
+ if (!existsSync8(path3)) {
6564
6600
  return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
6565
6601
  }
6566
6602
  try {
6567
- const raw = JSON.parse(readFileSync6(path3, "utf-8"));
6603
+ const raw = JSON.parse(readFileSync7(path3, "utf-8"));
6568
6604
  if (raw.version !== STATS_VERSION || !raw.entries) {
6569
6605
  return { version: STATS_VERSION, startedAt: (/* @__PURE__ */ new Date()).toISOString(), entries: {} };
6570
6606
  }
@@ -6622,7 +6658,7 @@ function flush() {
6622
6658
  if (!dirty || state === null) return;
6623
6659
  const path3 = statsFilePath();
6624
6660
  try {
6625
- mkdirSync4(dirname2(path3), { recursive: true });
6661
+ mkdirSync5(dirname3(path3), { recursive: true });
6626
6662
  atomicWriteFileSync(path3, JSON.stringify(state, null, 2));
6627
6663
  dirty = false;
6628
6664
  pendingWrites = 0;
@@ -6646,8 +6682,8 @@ function getTopUsedTools(limit = 5) {
6646
6682
  }
6647
6683
 
6648
6684
  // 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";
6685
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
6686
+ import { isAbsolute as isAbsolute2, join as join6, resolve as resolve4 } from "path";
6651
6687
  var RECENT_DENIED_LIMIT = 50;
6652
6688
  var recentlyDenied = [];
6653
6689
  function recordRecentlyDeniedAutoAction(call, classification) {
@@ -6688,10 +6724,10 @@ function splitShellWords(command) {
6688
6724
  return out.filter(Boolean);
6689
6725
  }
6690
6726
  function readPackageManifest(root) {
6691
- const p = join5(root, "package.json");
6692
- if (!existsSync8(p)) return null;
6727
+ const p = join6(root, "package.json");
6728
+ if (!existsSync9(p)) return null;
6693
6729
  try {
6694
- return JSON.parse(readFileSync7(p, "utf-8"));
6730
+ return JSON.parse(readFileSync8(p, "utf-8"));
6695
6731
  } catch {
6696
6732
  return null;
6697
6733
  }
@@ -6707,10 +6743,10 @@ function manifestHasDependency(root, name) {
6707
6743
  }
6708
6744
  function lockfileMentions(root, name) {
6709
6745
  for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
6710
- const p = join5(root, file);
6711
- if (!existsSync8(p)) continue;
6746
+ const p = join6(root, file);
6747
+ if (!existsSync9(p)) continue;
6712
6748
  try {
6713
- if (readFileSync7(p, "utf-8").includes(name)) return true;
6749
+ if (readFileSync8(p, "utf-8").includes(name)) return true;
6714
6750
  } catch {
6715
6751
  }
6716
6752
  }
@@ -7274,10 +7310,10 @@ var ToolExecutor = class {
7274
7310
  const filePath = String(call.arguments["path"] ?? "");
7275
7311
  const newContent = String(call.arguments["content"] ?? "");
7276
7312
  if (!filePath) return;
7277
- if (existsSync9(filePath)) {
7313
+ if (existsSync10(filePath)) {
7278
7314
  let oldContent;
7279
7315
  try {
7280
- oldContent = readFileSync8(filePath, "utf-8");
7316
+ oldContent = readFileSync9(filePath, "utf-8");
7281
7317
  } catch {
7282
7318
  return;
7283
7319
  }
@@ -7300,7 +7336,7 @@ var ToolExecutor = class {
7300
7336
  }
7301
7337
  } else if (call.name === "edit_file") {
7302
7338
  const filePath = String(call.arguments["path"] ?? "");
7303
- if (!filePath || !existsSync9(filePath)) return;
7339
+ if (!filePath || !existsSync10(filePath)) return;
7304
7340
  const oldStr = call.arguments["old_str"];
7305
7341
  const newStr = call.arguments["new_str"];
7306
7342
  if (oldStr !== void 0) {
@@ -7327,7 +7363,7 @@ var ToolExecutor = class {
7327
7363
  const to = Number(call.arguments["delete_to_line"] ?? from);
7328
7364
  let fileContent;
7329
7365
  try {
7330
- fileContent = readFileSync8(filePath, "utf-8");
7366
+ fileContent = readFileSync9(filePath, "utf-8");
7331
7367
  } catch {
7332
7368
  return;
7333
7369
  }
@@ -7562,7 +7598,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
7562
7598
  }
7563
7599
  undoStack.push(filePath, `write_file${appendMode ? " (append)" : ""}: ${filePath}`);
7564
7600
  fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
7565
- mkdirSync5(dirname3(filePath), { recursive: true });
7601
+ mkdirSync6(dirname4(filePath), { recursive: true });
7566
7602
  if (appendMode) {
7567
7603
  appendFileSync(filePath, content, encoding);
7568
7604
  } else {
@@ -7582,7 +7618,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
7582
7618
  };
7583
7619
 
7584
7620
  // src/tools/builtin/edit-file.ts
7585
- import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
7621
+ import { readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
7586
7622
 
7587
7623
  // src/tools/builtin/patch-apply.ts
7588
7624
  function parseUnifiedDiff(patch) {
@@ -7943,7 +7979,7 @@ Note: Path can be absolute or relative to cwd.`,
7943
7979
  const filePath = String(args["path"] ?? "");
7944
7980
  const encoding = args["encoding"] ?? "utf-8";
7945
7981
  if (!filePath) throw new ToolError("edit_file", "path is required");
7946
- if (!existsSync10(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
7982
+ if (!existsSync11(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
7947
7983
  const verdict = classifyWritePath(filePath);
7948
7984
  if (verdict.sensitive && subAgentGuard.active) {
7949
7985
  throw new ToolError(
@@ -7951,7 +7987,7 @@ Note: Path can be absolute or relative to cwd.`,
7951
7987
  `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
7988
  );
7953
7989
  }
7954
- const original = readFileSync9(filePath, encoding);
7990
+ const original = readFileSync10(filePath, encoding);
7955
7991
  if (args["patch"] !== void 0) {
7956
7992
  const patchText = String(args["patch"] ?? "");
7957
7993
  const stopOnError = args["stop_on_error"] !== false;
@@ -8109,8 +8145,8 @@ function truncatePreview(str, maxLen = 80) {
8109
8145
  }
8110
8146
 
8111
8147
  // 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";
8148
+ import { readdirSync as readdirSync4, statSync as statSync3, existsSync as existsSync12 } from "fs";
8149
+ import { join as join7, basename as basename3 } from "path";
8114
8150
  var listDirTool = {
8115
8151
  definition: {
8116
8152
  name: "list_dir",
@@ -8132,7 +8168,7 @@ var listDirTool = {
8132
8168
  async execute(args) {
8133
8169
  const dirPath = String(args["path"] ?? process.cwd());
8134
8170
  const recursive = Boolean(args["recursive"] ?? false);
8135
- if (!existsSync11(dirPath)) {
8171
+ if (!existsSync12(dirPath)) {
8136
8172
  const targetName = basename3(dirPath).toLowerCase();
8137
8173
  const cwd = process.cwd();
8138
8174
  const suggestions = [];
@@ -8190,11 +8226,11 @@ function listRecursive(basePath, indent, recursive, lines) {
8190
8226
  if (entry.isDirectory()) {
8191
8227
  lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
8192
8228
  if (recursive) {
8193
- listRecursive(join6(basePath, entry.name), indent + " ", true, lines);
8229
+ listRecursive(join7(basePath, entry.name), indent + " ", true, lines);
8194
8230
  }
8195
8231
  } else {
8196
8232
  try {
8197
- const stat = statSync3(join6(basePath, entry.name));
8233
+ const stat = statSync3(join7(basePath, entry.name));
8198
8234
  const size = formatSize(stat.size);
8199
8235
  lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
8200
8236
  } catch {
@@ -8210,9 +8246,9 @@ function formatSize(bytes) {
8210
8246
  }
8211
8247
 
8212
8248
  // src/tools/builtin/grep-files.ts
8213
- import { readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync4, existsSync as existsSync12 } from "fs";
8249
+ import { readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync4, existsSync as existsSync13 } from "fs";
8214
8250
  import { readFile } from "fs/promises";
8215
- import { join as join7, relative } from "path";
8251
+ import { join as join8, relative } from "path";
8216
8252
  var grepFilesTool = {
8217
8253
  definition: {
8218
8254
  name: "grep_files",
@@ -8263,7 +8299,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
8263
8299
  const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
8264
8300
  const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
8265
8301
  if (!pattern) throw new ToolError("grep_files", "pattern is required");
8266
- if (!existsSync12(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
8302
+ if (!existsSync13(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
8267
8303
  const MAX_PATTERN_LENGTH = 1e3;
8268
8304
  if (pattern.length > MAX_PATTERN_LENGTH) {
8269
8305
  throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
@@ -8357,11 +8393,11 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
8357
8393
  if (paths.length >= maxFiles) return;
8358
8394
  if (entry.isDirectory()) {
8359
8395
  if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
8360
- walk(join7(dirPath, entry.name));
8396
+ walk(join8(dirPath, entry.name));
8361
8397
  } else if (entry.isFile()) {
8362
8398
  if (isBinary(entry.name)) continue;
8363
8399
  if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
8364
- const fullPath = join7(dirPath, entry.name);
8400
+ const fullPath = join8(dirPath, entry.name);
8365
8401
  try {
8366
8402
  if (statSync4(fullPath).size > 1e6) continue;
8367
8403
  } catch {
@@ -8416,7 +8452,7 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
8416
8452
  }
8417
8453
  let content;
8418
8454
  try {
8419
- content = readFileSync10(fullPath, "utf-8");
8455
+ content = readFileSync11(fullPath, "utf-8");
8420
8456
  } catch {
8421
8457
  return;
8422
8458
  }
@@ -8447,8 +8483,8 @@ function searchInFile(fullPath, displayPath2, regex, contextLines, maxResults, r
8447
8483
  }
8448
8484
 
8449
8485
  // 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";
8486
+ import { readdirSync as readdirSync6, statSync as statSync5, existsSync as existsSync14 } from "fs";
8487
+ import { join as join9, relative as relative2, basename as basename4 } from "path";
8452
8488
  var globFilesTool = {
8453
8489
  definition: {
8454
8490
  name: "glob_files",
@@ -8481,7 +8517,7 @@ Results sorted by most recent modification time. Automatically skips node_module
8481
8517
  const rootPath = String(args["path"] ?? process.cwd());
8482
8518
  const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
8483
8519
  if (!pattern) throw new ToolError("glob_files", "pattern is required");
8484
- if (!existsSync13(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
8520
+ if (!existsSync14(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
8485
8521
  const regex = globToRegex(pattern);
8486
8522
  const matches = [];
8487
8523
  collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
@@ -8558,7 +8594,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
8558
8594
  }
8559
8595
  for (const entry of entries) {
8560
8596
  if (results.length >= maxResults) break;
8561
- const fullPath = join8(dirPath, entry.name);
8597
+ const fullPath = join9(dirPath, entry.name);
8562
8598
  if (entry.isDirectory()) {
8563
8599
  if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
8564
8600
  collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
@@ -9298,16 +9334,16 @@ ${preamble}`;
9298
9334
  }
9299
9335
 
9300
9336
  // 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";
9337
+ import { mkdirSync as mkdirSync7, unlinkSync as unlinkSync4, rmdirSync as rmdirSync2 } from "fs";
9338
+ import { dirname as dirname5 } from "path";
9303
9339
  var lastResponseStore = { content: "" };
9304
9340
  function cleanupRejectedTeeFile(filePath) {
9305
9341
  try {
9306
- unlinkSync3(filePath);
9342
+ unlinkSync4(filePath);
9307
9343
  } catch {
9308
9344
  }
9309
9345
  try {
9310
- rmdirSync2(dirname4(filePath));
9346
+ rmdirSync2(dirname5(filePath));
9311
9347
  } catch {
9312
9348
  }
9313
9349
  }
@@ -9348,7 +9384,7 @@ Any of these triggers means use save_last_response, NOT write_file:
9348
9384
  throw new ToolError("save_last_response", "No content to save: AI has not produced any response yet, or the last response was empty.");
9349
9385
  }
9350
9386
  undoStack.push(filePath, `save_last_response: ${filePath}`);
9351
- mkdirSync6(dirname4(filePath), { recursive: true });
9387
+ mkdirSync7(dirname5(filePath), { recursive: true });
9352
9388
  atomicWriteFileSync(filePath, content);
9353
9389
  const lines = content.split("\n").length;
9354
9390
  return `File saved: ${filePath} (${lines} lines, ${content.length} bytes)`;
@@ -9356,18 +9392,18 @@ Any of these triggers means use save_last_response, NOT write_file:
9356
9392
  };
9357
9393
 
9358
9394
  // src/tools/builtin/save-memory.ts
9359
- import { join as join11 } from "path";
9395
+ import { join as join12 } from "path";
9360
9396
  import { homedir as homedir5 } from "os";
9361
9397
 
9362
9398
  // src/memory/persistent-memory.ts
9363
- import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync11 } from "fs";
9399
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync12 } from "fs";
9364
9400
  import { randomUUID, createHash as createHash2 } from "crypto";
9365
- import { dirname as dirname5, join as join10, resolve as resolve6 } from "path";
9401
+ import { dirname as dirname6, join as join11, resolve as resolve6 } from "path";
9366
9402
 
9367
9403
  // src/tools/git-context.ts
9368
9404
  import { execSync as execSync2 } from "child_process";
9369
- import { existsSync as existsSync14 } from "fs";
9370
- import { join as join9 } from "path";
9405
+ import { existsSync as existsSync15 } from "fs";
9406
+ import { join as join10 } from "path";
9371
9407
  function runGit(cmd, cwd) {
9372
9408
  try {
9373
9409
  return execSync2(`git ${cmd}`, {
@@ -9384,7 +9420,7 @@ function getGitRoot(cwd = process.cwd()) {
9384
9420
  return runGit("rev-parse --show-toplevel", cwd);
9385
9421
  }
9386
9422
  function getGitContext(cwd = process.cwd()) {
9387
- if (!existsSync14(join9(cwd, ".git"))) {
9423
+ if (!existsSync15(join10(cwd, ".git"))) {
9388
9424
  const result = runGit("rev-parse --git-dir", cwd);
9389
9425
  if (!result) return null;
9390
9426
  }
@@ -9456,10 +9492,10 @@ function formatGitContextForPrompt(ctx) {
9456
9492
 
9457
9493
  // src/memory/persistent-memory.ts
9458
9494
  function memoryStorePath(configDir) {
9459
- return join10(configDir, MEMORY_STORE_FILE_NAME);
9495
+ return join11(configDir, MEMORY_STORE_FILE_NAME);
9460
9496
  }
9461
9497
  function memoryMarkdownPath(configDir) {
9462
- return join10(configDir, MEMORY_FILE_NAME);
9498
+ return join11(configDir, MEMORY_FILE_NAME);
9463
9499
  }
9464
9500
  function getProjectKey(cwd = process.cwd()) {
9465
9501
  return resolve6(getGitRoot(cwd) ?? cwd);
@@ -9495,8 +9531,8 @@ function normalizeEntry(value) {
9495
9531
  }
9496
9532
  function legacyEntriesFromMarkdown(configDir) {
9497
9533
  const file = memoryMarkdownPath(configDir);
9498
- if (!existsSync15(file)) return [];
9499
- const content = readFileSync11(file, "utf-8").trim();
9534
+ if (!existsSync16(file)) return [];
9535
+ const content = readFileSync12(file, "utf-8").trim();
9500
9536
  if (!content) return [];
9501
9537
  const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
9502
9538
  const chunks = parts.length > 1 ? parts : [content];
@@ -9512,8 +9548,8 @@ ${chunk}`).digest("hex").slice(0, 12);
9512
9548
  }
9513
9549
  function loadMemoryEntries(configDir) {
9514
9550
  const file = memoryStorePath(configDir);
9515
- if (!existsSync15(file)) return legacyEntriesFromMarkdown(configDir);
9516
- const text = readFileSync11(file, "utf-8").trim();
9551
+ if (!existsSync16(file)) return legacyEntriesFromMarkdown(configDir);
9552
+ const text = readFileSync12(file, "utf-8").trim();
9517
9553
  if (!text) return [];
9518
9554
  const entries = [];
9519
9555
  for (const line of text.split("\n")) {
@@ -9528,7 +9564,7 @@ function loadMemoryEntries(configDir) {
9528
9564
  return entries;
9529
9565
  }
9530
9566
  function saveMemoryEntries(configDir, entries) {
9531
- mkdirSync7(configDir, { recursive: true });
9567
+ mkdirSync8(configDir, { recursive: true });
9532
9568
  const jsonl = entries.map((entry) => JSON.stringify(entry)).join("\n");
9533
9569
  atomicWriteFileSync(memoryStorePath(configDir), jsonl ? jsonl + "\n" : "");
9534
9570
  syncLegacyMarkdown(configDir, entries);
@@ -9603,7 +9639,7 @@ function findUnique(entries, idPrefix) {
9603
9639
  return matches[0];
9604
9640
  }
9605
9641
  function syncLegacyMarkdown(configDir, entries = loadMemoryEntries(configDir)) {
9606
- mkdirSync7(dirname5(memoryMarkdownPath(configDir)), { recursive: true });
9642
+ mkdirSync8(dirname6(memoryMarkdownPath(configDir)), { recursive: true });
9607
9643
  const active = entries.filter((entry) => entry.approved && !isMemoryExpired(entry));
9608
9644
  const markdown = active.map((entry) => {
9609
9645
  const date = entry.createdAt.replace("T", " ").slice(0, 19);
@@ -9646,7 +9682,7 @@ ${entry.content}`;
9646
9682
 
9647
9683
  // src/tools/builtin/save-memory.ts
9648
9684
  function getConfigDir() {
9649
- return join11(homedir5(), CONFIG_DIR_NAME);
9685
+ return join12(homedir5(), CONFIG_DIR_NAME);
9650
9686
  }
9651
9687
  function parseScope(value) {
9652
9688
  return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
@@ -9980,13 +10016,13 @@ function formatResults2(query, data, _requested) {
9980
10016
  }
9981
10017
 
9982
10018
  // 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";
10019
+ import { existsSync as existsSync18, readdirSync as readdirSync8, readFileSync as readFileSync14 } from "fs";
10020
+ import { join as join14 } from "path";
9985
10021
  import { homedir as homedir6 } from "os";
9986
10022
 
9987
10023
  // 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";
10024
+ import { existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync13, cpSync, rmSync, statSync as statSync6 } from "fs";
10025
+ import { basename as basename5, dirname as dirname7, join as join13, resolve as resolve7 } from "path";
9990
10026
  import { createHash as createHash3 } from "crypto";
9991
10027
  import { z as z2 } from "zod";
9992
10028
  var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
@@ -10046,48 +10082,48 @@ var PluginManifestSchema = z2.object({
10046
10082
  permissionHints: z2.array(z2.string()).default([])
10047
10083
  }).strict();
10048
10084
  function pluginRoot(configDir) {
10049
- return join12(configDir, PLUGINS_DIR_NAME);
10085
+ return join13(configDir, PLUGINS_DIR_NAME);
10050
10086
  }
10051
10087
  function pluginManifestPath(pluginDir) {
10052
- return join12(pluginDir, MANIFEST_RELATIVE);
10088
+ return join13(pluginDir, MANIFEST_RELATIVE);
10053
10089
  }
10054
10090
  function statePath(configDir) {
10055
- return join12(pluginRoot(configDir), STATE_FILE);
10091
+ return join13(pluginRoot(configDir), STATE_FILE);
10056
10092
  }
10057
10093
  function loadState(configDir) {
10058
10094
  const file = statePath(configDir);
10059
- if (!existsSync16(file)) return { version: 1, plugins: [] };
10095
+ if (!existsSync17(file)) return { version: 1, plugins: [] };
10060
10096
  try {
10061
- const parsed = JSON.parse(readFileSync12(file, "utf-8"));
10097
+ const parsed = JSON.parse(readFileSync13(file, "utf-8"));
10062
10098
  return { version: 1, plugins: Array.isArray(parsed.plugins) ? parsed.plugins : [] };
10063
10099
  } catch {
10064
10100
  return { version: 1, plugins: [] };
10065
10101
  }
10066
10102
  }
10067
10103
  function saveState(configDir, state2) {
10068
- mkdirSync8(pluginRoot(configDir), { recursive: true });
10104
+ mkdirSync9(pluginRoot(configDir), { recursive: true });
10069
10105
  atomicWriteFileSync(statePath(configDir), JSON.stringify(state2, null, 2));
10070
10106
  }
10071
10107
  function hashPluginManifest(manifestPath) {
10072
- return createHash3("sha256").update(readFileSync12(manifestPath)).digest("hex");
10108
+ return createHash3("sha256").update(readFileSync13(manifestPath)).digest("hex");
10073
10109
  }
10074
10110
  function readPluginManifest(manifestPath) {
10075
- return PluginManifestSchema.parse(JSON.parse(readFileSync12(manifestPath, "utf-8")));
10111
+ return PluginManifestSchema.parse(JSON.parse(readFileSync13(manifestPath, "utf-8")));
10076
10112
  }
10077
10113
  function findPluginSourceDir(inputPath) {
10078
10114
  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;
10115
+ const direct = statSync6(abs).isDirectory() ? abs : dirname7(abs);
10116
+ if (existsSync17(pluginManifestPath(direct))) return direct;
10117
+ const nested = join13(direct, ".aicli-plugin");
10118
+ if (existsSync17(join13(nested, "plugin.json"))) return direct;
10083
10119
  throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
10084
10120
  }
10085
10121
  function installPlugin(configDir, inputPath) {
10086
10122
  const sourceDir = findPluginSourceDir(inputPath);
10087
10123
  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 });
10124
+ const targetDir = join13(pluginRoot(configDir), manifest.name);
10125
+ mkdirSync9(pluginRoot(configDir), { recursive: true });
10126
+ if (existsSync17(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10091
10127
  cpSync(sourceDir, targetDir, { recursive: true });
10092
10128
  const manifestPath = pluginManifestPath(targetDir);
10093
10129
  const hash = hashPluginManifest(manifestPath);
@@ -10145,18 +10181,18 @@ function getInstalledPlugin(configDir, name) {
10145
10181
  }
10146
10182
  function listInstalledPlugins(configDir) {
10147
10183
  const root = pluginRoot(configDir);
10148
- if (!existsSync16(root)) return [];
10184
+ if (!existsSync17(root)) return [];
10149
10185
  const state2 = loadState(configDir);
10150
10186
  const out = [];
10151
10187
  for (const entry of readdirSync7(root)) {
10152
- const dir = join12(root, entry);
10188
+ const dir = join13(root, entry);
10153
10189
  try {
10154
10190
  if (!statSync6(dir).isDirectory()) continue;
10155
10191
  } catch {
10156
10192
  continue;
10157
10193
  }
10158
10194
  const manifestPath = pluginManifestPath(dir);
10159
- if (!existsSync16(manifestPath)) continue;
10195
+ if (!existsSync17(manifestPath)) continue;
10160
10196
  try {
10161
10197
  const manifest = readPluginManifest(manifestPath);
10162
10198
  const hash = hashPluginManifest(manifestPath);
@@ -10196,7 +10232,7 @@ function uniqueDirs(plugin, rels) {
10196
10232
  for (const rel of rels) {
10197
10233
  const abs = resolve7(plugin.dir, rel);
10198
10234
  if (!abs.startsWith(resolve7(plugin.dir))) continue;
10199
- if (existsSync16(abs)) dirs.add(statSync6(abs).isDirectory() ? abs : dirname6(abs));
10235
+ if (existsSync17(abs)) dirs.add(statSync6(abs).isDirectory() ? abs : dirname7(abs));
10200
10236
  }
10201
10237
  return [...dirs];
10202
10238
  }
@@ -10359,13 +10395,13 @@ function parseAgentConfig(raw, source, path3) {
10359
10395
  return cfg;
10360
10396
  }
10361
10397
  function loadAgentDir(dir, source) {
10362
- if (!existsSync17(dir)) return [];
10398
+ if (!existsSync18(dir)) return [];
10363
10399
  const out = [];
10364
10400
  for (const file of readdirSync8(dir)) {
10365
10401
  if (!file.endsWith(".json")) continue;
10366
- const path3 = join13(dir, file);
10402
+ const path3 = join14(dir, file);
10367
10403
  try {
10368
- const parsed = JSON.parse(readFileSync13(path3, "utf-8"));
10404
+ const parsed = JSON.parse(readFileSync14(path3, "utf-8"));
10369
10405
  const cfg = parseAgentConfig(parsed, source, path3);
10370
10406
  if (cfg) out.push(cfg);
10371
10407
  } catch {
@@ -10375,8 +10411,8 @@ function loadAgentDir(dir, source) {
10375
10411
  }
10376
10412
  function getAgentDirs(configDir, cwd = process.cwd()) {
10377
10413
  return {
10378
- user: join13(configDir ?? join13(homedir6(), CONFIG_DIR_NAME), "agents"),
10379
- project: join13(cwd, CONFIG_DIR_NAME, "agents")
10414
+ user: join14(configDir ?? join14(homedir6(), CONFIG_DIR_NAME), "agents"),
10415
+ project: join14(cwd, CONFIG_DIR_NAME, "agents")
10380
10416
  };
10381
10417
  }
10382
10418
  function listAgentConfigs(configDir, cwd = process.cwd()) {
@@ -11091,14 +11127,14 @@ var taskStopTool = {
11091
11127
 
11092
11128
  // src/tools/builtin/git-tools.ts
11093
11129
  import { execFileSync as execFileSync2 } from "child_process";
11094
- import { existsSync as existsSync18 } from "fs";
11095
- import { join as join14 } from "path";
11130
+ import { existsSync as existsSync19 } from "fs";
11131
+ import { join as join15 } from "path";
11096
11132
  function assertGitRepo(cwd) {
11097
11133
  let dir = cwd;
11098
11134
  const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
11099
11135
  while (dir && dir !== root) {
11100
- if (existsSync18(join14(dir, ".git"))) return;
11101
- const parent = join14(dir, "..");
11136
+ if (existsSync19(join15(dir, ".git"))) return;
11137
+ const parent = join15(dir, "..");
11102
11138
  if (parent === dir) break;
11103
11139
  dir = parent;
11104
11140
  }
@@ -11363,7 +11399,7 @@ ${commitOutput.trim()}`;
11363
11399
  };
11364
11400
 
11365
11401
  // src/tools/builtin/notebook-edit.ts
11366
- import { readFileSync as readFileSync14, existsSync as existsSync19 } from "fs";
11402
+ import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
11367
11403
  import { writeFile } from "fs/promises";
11368
11404
  import { resolve as resolve8, extname as extname2 } from "path";
11369
11405
  var notebookEditTool = {
@@ -11418,10 +11454,10 @@ var notebookEditTool = {
11418
11454
  if (extname2(absPath).toLowerCase() !== ".ipynb") {
11419
11455
  throw new ToolError("notebook_edit", "path must point to a .ipynb file");
11420
11456
  }
11421
- if (!existsSync19(absPath)) {
11457
+ if (!existsSync20(absPath)) {
11422
11458
  throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
11423
11459
  }
11424
- const raw = readFileSync14(absPath, "utf-8");
11460
+ const raw = readFileSync15(absPath, "utf-8");
11425
11461
  const nb = parseNotebook(raw);
11426
11462
  const cellIdx0 = cellIndexRaw - 1;
11427
11463
  undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
@@ -11838,8 +11874,8 @@ function estimateToolDefinitionTokens(def) {
11838
11874
 
11839
11875
  // src/tools/registry.ts
11840
11876
  import { pathToFileURL } from "url";
11841
- import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync9 } from "fs";
11842
- import { join as join15 } from "path";
11877
+ import { existsSync as existsSync21, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
11878
+ import { join as join16 } from "path";
11843
11879
  var ToolRegistry = class {
11844
11880
  tools = /* @__PURE__ */ new Map();
11845
11881
  pluginToolNames = /* @__PURE__ */ new Set();
@@ -12000,9 +12036,9 @@ var ToolRegistry = class {
12000
12036
  * Returns the number of successfully loaded plugins.
12001
12037
  */
12002
12038
  async loadPlugins(pluginsDir, allowPlugins = false) {
12003
- if (!existsSync20(pluginsDir)) {
12039
+ if (!existsSync21(pluginsDir)) {
12004
12040
  try {
12005
- mkdirSync9(pluginsDir, { recursive: true });
12041
+ mkdirSync10(pluginsDir, { recursive: true });
12006
12042
  } catch {
12007
12043
  }
12008
12044
  return 0;
@@ -12026,12 +12062,12 @@ var ToolRegistry = class {
12026
12062
  process.stderr.write(
12027
12063
  `
12028
12064
  [plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
12029
- ` + files.map((f) => ` + ${join15(pluginsDir, f)}`).join("\n") + "\n\n"
12065
+ ` + files.map((f) => ` + ${join16(pluginsDir, f)}`).join("\n") + "\n\n"
12030
12066
  );
12031
12067
  let loaded = 0;
12032
12068
  for (const file of files) {
12033
12069
  try {
12034
- const fileUrl = pathToFileURL(join15(pluginsDir, file)).href;
12070
+ const fileUrl = pathToFileURL(join16(pluginsDir, file)).href;
12035
12071
  const mod = await import(fileUrl);
12036
12072
  const tool = mod.tool ?? mod.default?.tool ?? mod.default;
12037
12073
  if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
@@ -12577,11 +12613,11 @@ var McpManager = class {
12577
12613
  };
12578
12614
 
12579
12615
  // 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";
12616
+ import { existsSync as existsSync22, readdirSync as readdirSync10, mkdirSync as mkdirSync11, statSync as statSync7 } from "fs";
12617
+ import { join as join17 } from "path";
12582
12618
 
12583
12619
  // src/skills/types.ts
12584
- import { readFileSync as readFileSync15 } from "fs";
12620
+ import { readFileSync as readFileSync16 } from "fs";
12585
12621
  import { basename as basename6 } from "path";
12586
12622
  function parseSimpleYaml(yaml) {
12587
12623
  const result = {};
@@ -12603,7 +12639,7 @@ function parseYamlArray(value) {
12603
12639
  function parseSkillFile(filePath) {
12604
12640
  let raw;
12605
12641
  try {
12606
- raw = readFileSync15(filePath, "utf-8");
12642
+ raw = readFileSync16(filePath, "utf-8");
12607
12643
  } catch {
12608
12644
  return null;
12609
12645
  }
@@ -12651,10 +12687,10 @@ var SkillManager = class {
12651
12687
  return this.skills.size;
12652
12688
  }
12653
12689
  loadSkillDir(dir, createIfMissing) {
12654
- if (!existsSync21(dir)) {
12690
+ if (!existsSync22(dir)) {
12655
12691
  if (createIfMissing) {
12656
12692
  try {
12657
- mkdirSync10(dir, { recursive: true });
12693
+ mkdirSync11(dir, { recursive: true });
12658
12694
  } catch {
12659
12695
  }
12660
12696
  }
@@ -12668,14 +12704,14 @@ var SkillManager = class {
12668
12704
  }
12669
12705
  for (const entry of entries) {
12670
12706
  let filePath;
12671
- const fullPath = join16(dir, entry);
12707
+ const fullPath = join17(dir, entry);
12672
12708
  if (entry.endsWith(".md")) {
12673
12709
  filePath = fullPath;
12674
12710
  } else {
12675
12711
  try {
12676
12712
  if (statSync7(fullPath).isDirectory()) {
12677
- const skillMd = join16(fullPath, "SKILL.md");
12678
- if (existsSync21(skillMd)) filePath = skillMd;
12713
+ const skillMd = join17(fullPath, "SKILL.md");
12714
+ if (existsSync22(skillMd)) filePath = skillMd;
12679
12715
  else continue;
12680
12716
  } else {
12681
12717
  continue;
@@ -12734,7 +12770,7 @@ var SkillManager = class {
12734
12770
  // src/web/tool-executor-web.ts
12735
12771
  import { randomUUID as randomUUID3 } from "crypto";
12736
12772
  import { tmpdir as tmpdir2 } from "os";
12737
- import { existsSync as existsSync22, readFileSync as readFileSync16 } from "fs";
12773
+ import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
12738
12774
  var ToolExecutorWeb = class _ToolExecutorWeb {
12739
12775
  constructor(registry, ws) {
12740
12776
  this.registry = registry;
@@ -12873,9 +12909,9 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
12873
12909
  if (call.name === "write_file") {
12874
12910
  const filePath = String(call.arguments["path"] ?? "");
12875
12911
  const newContent = String(call.arguments["content"] ?? "");
12876
- if (filePath && existsSync22(filePath)) {
12912
+ if (filePath && existsSync23(filePath)) {
12877
12913
  try {
12878
- const old = readFileSync16(filePath, "utf-8");
12914
+ const old = readFileSync17(filePath, "utf-8");
12879
12915
  return renderDiff(old, newContent, { filePath });
12880
12916
  } catch {
12881
12917
  }
@@ -13178,16 +13214,16 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13178
13214
  };
13179
13215
 
13180
13216
  // 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";
13217
+ import { existsSync as existsSync24, readFileSync as readFileSync18, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "fs";
13218
+ import { join as join18 } from "path";
13183
13219
  import { homedir as homedir7 } from "os";
13184
13220
  function getDevStatePath() {
13185
- return join17(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
13221
+ return join18(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
13186
13222
  }
13187
13223
  function loadDevState() {
13188
13224
  const path3 = getDevStatePath();
13189
- if (!existsSync23(path3)) return null;
13190
- const content = readFileSync17(path3, "utf-8").trim();
13225
+ if (!existsSync24(path3)) return null;
13226
+ const content = readFileSync18(path3, "utf-8").trim();
13191
13227
  return content || null;
13192
13228
  }
13193
13229
 
@@ -13444,8 +13480,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
13444
13480
  }
13445
13481
 
13446
13482
  // 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";
13483
+ import { existsSync as existsSync25, readFileSync as readFileSync19 } from "fs";
13484
+ import { join as join19, relative as relative3, resolve as resolve9 } from "path";
13449
13485
  function uniqueNonEmpty(values) {
13450
13486
  const seen = /* @__PURE__ */ new Set();
13451
13487
  const out = [];
@@ -13475,7 +13511,7 @@ function readContextFile(level, filePath, cwd, maxBytes) {
13475
13511
  const name = filePath.split(/[\\/]/).pop() ?? filePath;
13476
13512
  const shown = displayPath(filePath, cwd);
13477
13513
  try {
13478
- const raw = readFileSync18(filePath);
13514
+ const raw = readFileSync19(filePath);
13479
13515
  const byteCount = raw.byteLength;
13480
13516
  if (byteCount === 0) {
13481
13517
  return {
@@ -13522,8 +13558,8 @@ function readContextFile(level, filePath, cwd, maxBytes) {
13522
13558
  function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
13523
13559
  const skipped = [];
13524
13560
  for (const candidate of candidates) {
13525
- const filePath = join18(dir, candidate);
13526
- if (!existsSync24(filePath)) continue;
13561
+ const filePath = join19(dir, candidate);
13562
+ if (!existsSync25(filePath)) continue;
13527
13563
  const result = readContextFile(level, filePath, cwd, maxBytes);
13528
13564
  if (result.skipped) skipped.push(result.skipped);
13529
13565
  if (result.layer) return { layer: result.layer, skipped };
@@ -13550,7 +13586,7 @@ function loadContextFiles(options) {
13550
13586
  });
13551
13587
  return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
13552
13588
  }
13553
- if (!existsSync24(filePath)) {
13589
+ if (!existsSync25(filePath)) {
13554
13590
  skipped.push({
13555
13591
  level: "single",
13556
13592
  filePath,
@@ -13601,8 +13637,8 @@ function loadContextFiles(options) {
13601
13637
  }
13602
13638
 
13603
13639
  // 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";
13640
+ import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync3, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync11, createWriteStream } from "fs";
13641
+ import { join as join24, resolve as resolve10, dirname as dirname8 } from "path";
13606
13642
 
13607
13643
  // src/cli/review-prompts.ts
13608
13644
  function buildReviewPrompt(diff, gitContextStr, detailed) {
@@ -13663,8 +13699,8 @@ If no security issues found, state "\u2705 No security vulnerabilities detected"
13663
13699
  }
13664
13700
 
13665
13701
  // 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";
13702
+ import { existsSync as existsSync26, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync8 } from "fs";
13703
+ import { join as join20 } from "path";
13668
13704
  var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
13669
13705
  "node_modules",
13670
13706
  ".git",
@@ -13708,11 +13744,11 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
13708
13744
  const sorted = filtered.sort((a, b) => {
13709
13745
  let aIsDir = false, bIsDir = false;
13710
13746
  try {
13711
- aIsDir = statSync8(join19(d, a)).isDirectory();
13747
+ aIsDir = statSync8(join20(d, a)).isDirectory();
13712
13748
  } catch {
13713
13749
  }
13714
13750
  try {
13715
- bIsDir = statSync8(join19(d, b)).isDirectory();
13751
+ bIsDir = statSync8(join20(d, b)).isDirectory();
13716
13752
  } catch {
13717
13753
  }
13718
13754
  if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
@@ -13720,7 +13756,7 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
13720
13756
  });
13721
13757
  for (let i = 0; i < sorted.length && count < maxEntries; i++) {
13722
13758
  const name = sorted[i];
13723
- const fullPath = join19(d, name);
13759
+ const fullPath = join20(d, name);
13724
13760
  const isLast = i === sorted.length - 1;
13725
13761
  const connector = isLast ? "+-- " : "|-- ";
13726
13762
  let isDir;
@@ -13747,7 +13783,7 @@ function scanProject(cwd) {
13747
13783
  configFiles: [],
13748
13784
  directoryStructure: ""
13749
13785
  };
13750
- const check = (file) => existsSync25(join19(cwd, file));
13786
+ const check = (file) => existsSync26(join20(cwd, file));
13751
13787
  const configCandidates = [
13752
13788
  "package.json",
13753
13789
  "tsconfig.json",
@@ -13774,7 +13810,7 @@ function scanProject(cwd) {
13774
13810
  info.type = "node";
13775
13811
  info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
13776
13812
  try {
13777
- const pkg = JSON.parse(readFileSync19(join19(cwd, "package.json"), "utf-8"));
13813
+ const pkg = JSON.parse(readFileSync20(join20(cwd, "package.json"), "utf-8"));
13778
13814
  const scripts = pkg.scripts ?? {};
13779
13815
  info.buildCommand = scripts.build ? "npm run build" : void 0;
13780
13816
  info.testCommand = scripts.test ? "npm test" : void 0;
@@ -14248,34 +14284,34 @@ var DiscussionOrchestrator = class {
14248
14284
  };
14249
14285
 
14250
14286
  // src/diagnostics/doctor-report.ts
14251
- import { existsSync as existsSync27, statSync as statSync10 } from "fs";
14252
- import { join as join21 } from "path";
14287
+ import { existsSync as existsSync28, statSync as statSync10 } from "fs";
14288
+ import { join as join22 } from "path";
14253
14289
  import { arch as arch2, platform as platform6, release as release2 } from "os";
14254
14290
 
14255
14291
  // src/diagnostics/crash-log.ts
14256
14292
  import {
14257
- existsSync as existsSync26,
14258
- mkdirSync as mkdirSync12,
14293
+ existsSync as existsSync27,
14294
+ mkdirSync as mkdirSync13,
14259
14295
  readdirSync as readdirSync12,
14260
- readFileSync as readFileSync20,
14296
+ readFileSync as readFileSync21,
14261
14297
  statSync as statSync9,
14262
- unlinkSync as unlinkSync5,
14298
+ unlinkSync as unlinkSync6,
14263
14299
  writeFileSync as writeFileSync2
14264
14300
  } from "fs";
14265
- import { join as join20 } from "path";
14301
+ import { join as join21 } from "path";
14266
14302
  import { homedir as homedir8, platform as platform5, release, arch } from "os";
14267
14303
  var LOGS_DIR_NAME = "logs";
14268
14304
  var CRASH_LOG_PREFIX = "crash-";
14269
14305
  function getLogsDir(configDir) {
14270
- const base = configDir ?? join20(homedir8(), CONFIG_DIR_NAME);
14271
- return join20(base, LOGS_DIR_NAME);
14306
+ const base = configDir ?? join21(homedir8(), CONFIG_DIR_NAME);
14307
+ return join21(base, LOGS_DIR_NAME);
14272
14308
  }
14273
14309
  function truncate(s, n) {
14274
14310
  return s.length <= n ? s : s.slice(0, n) + "\u2026";
14275
14311
  }
14276
14312
  function listRecentCrashes(limit = 10, configDir) {
14277
14313
  const dir = getLogsDir(configDir);
14278
- if (!existsSync26(dir)) return [];
14314
+ if (!existsSync27(dir)) return [];
14279
14315
  const files = [];
14280
14316
  let names;
14281
14317
  try {
@@ -14285,10 +14321,10 @@ function listRecentCrashes(limit = 10, configDir) {
14285
14321
  }
14286
14322
  for (const name of names) {
14287
14323
  if (!name.startsWith(CRASH_LOG_PREFIX) || !name.endsWith(".log")) continue;
14288
- const full = join20(dir, name);
14324
+ const full = join21(dir, name);
14289
14325
  try {
14290
14326
  const st = statSync9(full);
14291
- const head = readFileSync20(full, "utf-8").split("\n").slice(0, 20);
14327
+ const head = readFileSync21(full, "utf-8").split("\n").slice(0, 20);
14292
14328
  const tsLine = head.find((l) => l.startsWith("timestamp:"));
14293
14329
  const kindLine = head.find((l) => l.startsWith("kind:"));
14294
14330
  const msgIdx = head.findIndex((l) => l.trim() === "## message");
@@ -14307,8 +14343,8 @@ function listRecentCrashes(limit = 10, configDir) {
14307
14343
  return files.slice(0, limit);
14308
14344
  }
14309
14345
  function getConfigDirUsage(configDir) {
14310
- const base = configDir ?? join20(homedir8(), CONFIG_DIR_NAME);
14311
- if (!existsSync26(base)) return { totalBytes: 0, entries: [] };
14346
+ const base = configDir ?? join21(homedir8(), CONFIG_DIR_NAME);
14347
+ if (!existsSync27(base)) return { totalBytes: 0, entries: [] };
14312
14348
  const entries = [];
14313
14349
  let total = 0;
14314
14350
  let names;
@@ -14318,7 +14354,7 @@ function getConfigDirUsage(configDir) {
14318
14354
  return { totalBytes: 0, entries: [] };
14319
14355
  }
14320
14356
  for (const name of names) {
14321
- const full = join20(base, name);
14357
+ const full = join21(base, name);
14322
14358
  const bytes = dirSize(full);
14323
14359
  entries.push({ name, bytes });
14324
14360
  total += bytes;
@@ -14333,7 +14369,7 @@ function dirSize(path3) {
14333
14369
  if (!st.isDirectory()) return 0;
14334
14370
  let sum = 0;
14335
14371
  for (const name of readdirSync12(path3)) {
14336
- sum += dirSize(join20(path3, name));
14372
+ sum += dirSize(join21(path3, name));
14337
14373
  }
14338
14374
  return sum;
14339
14375
  } catch {
@@ -14343,7 +14379,7 @@ function dirSize(path3) {
14343
14379
 
14344
14380
  // src/diagnostics/doctor-report.ts
14345
14381
  function checkFile(label, path3) {
14346
- const exists = existsSync27(path3);
14382
+ const exists = existsSync28(path3);
14347
14383
  let sizeBytes = null;
14348
14384
  if (exists) {
14349
14385
  try {
@@ -14386,12 +14422,12 @@ function buildDoctorReport(options) {
14386
14422
  configDir,
14387
14423
  providers: options.providers.listAll().map((p) => ({ id: p.id, displayName: p.displayName, configured: p.configured })),
14388
14424
  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"))
14425
+ checkFile("config.json", join22(configDir, "config.json")),
14426
+ checkFile("memory.md", join22(configDir, MEMORY_FILE_NAME)),
14427
+ checkFile("memory.jsonl", join22(configDir, "memory.jsonl")),
14428
+ checkFile("dev-state.md", join22(configDir, DEV_STATE_FILE_NAME)),
14429
+ checkFile("hooks-trust.json", join22(configDir, "hooks-trust.json")),
14430
+ checkFile("plugin-state.json", join22(configDir, "plugins", "plugin-state.json"))
14395
14431
  ],
14396
14432
  context: {
14397
14433
  enabled: contextSetting !== false,
@@ -14426,7 +14462,7 @@ function buildDoctorReport(options) {
14426
14462
  disabled: hooks.filter((h) => h.disabled).length
14427
14463
  },
14428
14464
  plugins: {
14429
- root: join21(configDir, "plugins"),
14465
+ root: join22(configDir, "plugins"),
14430
14466
  installed: plugins.length,
14431
14467
  active: activeAssets.plugins.length,
14432
14468
  invalid: plugins.filter((p) => !p.valid).length,
@@ -14441,7 +14477,7 @@ function buildDoctorReport(options) {
14441
14477
  mcp: {
14442
14478
  enabled: config.get("mcpEnabled"),
14443
14479
  configured: Object.keys(mcpServers).length,
14444
- projectConfigExists: existsSync27(join21(projectRoot, MCP_PROJECT_CONFIG_NAME)),
14480
+ projectConfigExists: existsSync28(join22(projectRoot, MCP_PROJECT_CONFIG_NAME)),
14445
14481
  statuses: options.mcpStatuses ?? []
14446
14482
  },
14447
14483
  recentCrashes: listRecentCrashes(5, configDir),
@@ -14762,7 +14798,7 @@ function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, avai
14762
14798
  }
14763
14799
 
14764
14800
  // src/hub/persist.ts
14765
- import { join as join22 } from "path";
14801
+ import { join as join23 } from "path";
14766
14802
  function discussionToMessages(state2) {
14767
14803
  const out = [];
14768
14804
  const t0 = state2.messages[0]?.timestamp ?? /* @__PURE__ */ new Date();
@@ -14800,7 +14836,7 @@ async function persistDiscussion(state2, config, defaultProvider, defaultModel)
14800
14836
  session.title = `[Hub] ${state2.topic.slice(0, 48)}`.replace(/\n/g, " ");
14801
14837
  session.titleAiGenerated = true;
14802
14838
  await sm.save();
14803
- return { id: session.id, path: join22(config.getHistoryDir(), `${session.id}.json`) };
14839
+ return { id: session.id, path: join23(config.getHistoryDir(), `${session.id}.json`) };
14804
14840
  }
14805
14841
 
14806
14842
  // src/web/session-handler.ts
@@ -15517,7 +15553,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
15517
15553
  this.send({ type: "response_done", content, usage });
15518
15554
  if (pendingTeeSave && isCleanDocumentBody(content)) {
15519
15555
  try {
15520
- mkdirSync13(dirname7(pendingTeeSave), { recursive: true });
15556
+ mkdirSync14(dirname8(pendingTeeSave), { recursive: true });
15521
15557
  const bodyToSave = stripOuterCodeFence(content);
15522
15558
  atomicWriteFileSync(pendingTeeSave, bodyToSave);
15523
15559
  undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
@@ -15693,7 +15729,7 @@ ${summaryContent}`,
15693
15729
  let isError = false;
15694
15730
  let summary;
15695
15731
  try {
15696
- mkdirSync13(dirname7(saveToFile), { recursive: true });
15732
+ mkdirSync14(dirname8(saveToFile), { recursive: true });
15697
15733
  fileStream = createWriteStream(saveToFile);
15698
15734
  const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
15699
15735
  const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
@@ -15817,13 +15853,39 @@ ${summaryContent}`,
15817
15853
  break;
15818
15854
  }
15819
15855
  case "model": {
15820
- const modelId = args[0];
15821
- if (!modelId) {
15822
- this.send({ type: "error", message: "Usage: /model <id>" });
15856
+ const sub = args[0];
15857
+ if (!sub) {
15858
+ this.send({ type: "error", message: "Usage: /model <id>|refresh [provider]|cache [clear [provider]]" });
15859
+ return;
15860
+ }
15861
+ if (sub === "refresh") {
15862
+ const providerId = args[1] ?? this.currentProvider;
15863
+ try {
15864
+ const models2 = await this.providers.refreshModels(providerId);
15865
+ this.send({ type: "info", message: `Refreshed ${models2.length} model(s) for ${providerId}.` });
15866
+ this.sendStatus();
15867
+ } catch (err) {
15868
+ this.send({ type: "error", message: `Model refresh failed: ${err instanceof Error ? err.message : String(err)}` });
15869
+ }
15870
+ return;
15871
+ }
15872
+ if (sub === "cache") {
15873
+ if ((args[1] ?? "").toLowerCase() === "clear") {
15874
+ const providerId = args[2];
15875
+ this.providers.clearModelCache(providerId);
15876
+ this.send({ type: "info", message: providerId ? `Cleared model cache for ${providerId}.` : "Cleared model cache." });
15877
+ return;
15878
+ }
15879
+ const rows = this.providers.getModelCacheStatus();
15880
+ this.send({
15881
+ type: "info",
15882
+ 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")
15883
+ });
15823
15884
  return;
15824
15885
  }
15825
- const provider = this.providers.get(this.currentProvider);
15826
- const found = provider?.info.models.find((m) => m.id === modelId);
15886
+ const modelId = sub;
15887
+ const models = await this.providers.listModels(this.currentProvider);
15888
+ const found = models.find((m) => m.id === modelId);
15827
15889
  if (!found) {
15828
15890
  this.send({ type: "error", message: `Model "${modelId}" not found` });
15829
15891
  return;
@@ -16382,9 +16444,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
16382
16444
  let modifiedFiles = 0;
16383
16445
  const diffLines2 = [];
16384
16446
  for (const [filePath, { earliest }] of fileMap) {
16385
- const currentContent = existsSync28(filePath) ? (() => {
16447
+ const currentContent = existsSync29(filePath) ? (() => {
16386
16448
  try {
16387
- return readFileSync21(filePath, "utf-8");
16449
+ return readFileSync22(filePath, "utf-8");
16388
16450
  } catch {
16389
16451
  return null;
16390
16452
  }
@@ -16900,7 +16962,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
16900
16962
  case "test": {
16901
16963
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
16902
16964
  try {
16903
- const { executeTests } = await import("./run-tests-O76JIBCW.js");
16965
+ const { executeTests } = await import("./run-tests-GK5AIAV2.js");
16904
16966
  const argStr = args.join(" ").trim();
16905
16967
  let testArgs = {};
16906
16968
  if (argStr) {
@@ -16917,9 +16979,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
16917
16979
  // ── /init ───────────────────────────────────────────────────────
16918
16980
  case "init": {
16919
16981
  const cwd = process.cwd();
16920
- const targetPath = join23(cwd, "AICLI.md");
16982
+ const targetPath = join24(cwd, "AICLI.md");
16921
16983
  const force = args.includes("--force");
16922
- if (existsSync28(targetPath) && !force) {
16984
+ if (existsSync29(targetPath) && !force) {
16923
16985
  this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
16924
16986
  Use /init --force to overwrite.` });
16925
16987
  break;
@@ -17112,7 +17174,7 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
17112
17174
  break;
17113
17175
  }
17114
17176
  const dirPath = resolve10(sub);
17115
- if (!existsSync28(dirPath)) {
17177
+ if (!existsSync29(dirPath)) {
17116
17178
  this.send({ type: "error", message: `Directory not found: ${dirPath}` });
17117
17179
  break;
17118
17180
  }
@@ -17128,8 +17190,8 @@ It will be included in AI context for subsequent messages.` });
17128
17190
  // ── /commands ───────────────────────────────────────────────────
17129
17191
  case "commands": {
17130
17192
  const configDir = this.config.getConfigDir();
17131
- const commandsDir = join23(configDir, CUSTOM_COMMANDS_DIR_NAME);
17132
- if (!existsSync28(commandsDir)) {
17193
+ const commandsDir = join24(configDir, CUSTOM_COMMANDS_DIR_NAME);
17194
+ if (!existsSync29(commandsDir)) {
17133
17195
  this.send({ type: "info", message: `No custom commands directory.
17134
17196
  Create: ${commandsDir}/ with .md files.` });
17135
17197
  break;
@@ -17155,7 +17217,7 @@ Add .md files to create commands.` });
17155
17217
  // ── /plugins / /plugin ──────────────────────────────────────────
17156
17218
  case "plugins": {
17157
17219
  const configDir = this.config.getConfigDir();
17158
- const pluginsDir = join23(configDir, PLUGINS_DIR_NAME);
17220
+ const pluginsDir = join24(configDir, PLUGINS_DIR_NAME);
17159
17221
  const pluginTools = this.toolRegistry.listPluginTools();
17160
17222
  const pluginPackages = listInstalledPlugins(configDir);
17161
17223
  const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
@@ -17437,7 +17499,7 @@ ${entry.content}`;
17437
17499
  const configDir = this.config.getConfigDir();
17438
17500
  try {
17439
17501
  atomicWriteFileSync(memoryStorePath(configDir), "");
17440
- atomicWriteFileSync(join23(configDir, MEMORY_FILE_NAME), "");
17502
+ atomicWriteFileSync(join24(configDir, MEMORY_FILE_NAME), "");
17441
17503
  this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
17442
17504
  this.sendMemoryEntries();
17443
17505
  } catch (err) {
@@ -17719,8 +17781,8 @@ async function setupProxy(configProxy) {
17719
17781
  }
17720
17782
 
17721
17783
  // 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";
17784
+ import { existsSync as existsSync30, readFileSync as readFileSync23, writeFileSync as writeFileSync4, mkdirSync as mkdirSync15, readdirSync as readdirSync14, copyFileSync } from "fs";
17785
+ import { join as join25 } from "path";
17724
17786
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
17725
17787
  var USERS_FILE = "users.json";
17726
17788
  var TOKEN_EXPIRY_HOURS = 24;
@@ -17735,7 +17797,7 @@ var AuthManager = class {
17735
17797
  db;
17736
17798
  constructor(baseDir) {
17737
17799
  this.baseDir = baseDir;
17738
- this.usersFile = join24(baseDir, USERS_FILE);
17800
+ this.usersFile = join25(baseDir, USERS_FILE);
17739
17801
  this.db = this.loadOrCreate();
17740
17802
  }
17741
17803
  // ── Public API ─────────────────────────────────────────────────
@@ -17764,9 +17826,9 @@ var AuthManager = class {
17764
17826
  }
17765
17827
  const salt = randomBytes(16).toString("hex");
17766
17828
  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 });
17829
+ const dataDir = join25(USERS_DIR, username);
17830
+ const fullDataDir = join25(this.baseDir, dataDir);
17831
+ mkdirSync15(join25(fullDataDir, "history"), { recursive: true });
17770
17832
  const user = {
17771
17833
  username,
17772
17834
  passwordHash,
@@ -17881,7 +17943,7 @@ var AuthManager = class {
17881
17943
  getUserDataDir(username) {
17882
17944
  const user = this.db.users.find((u) => u.username === username);
17883
17945
  if (!user) throw new Error(`User not found: ${username}`);
17884
- return join24(this.baseDir, user.dataDir);
17946
+ return join25(this.baseDir, user.dataDir);
17885
17947
  }
17886
17948
  /** List all usernames */
17887
17949
  listUsers() {
@@ -17917,30 +17979,30 @@ var AuthManager = class {
17917
17979
  const err = this.register(username, password);
17918
17980
  if (err) return err;
17919
17981
  const userDir = this.getUserDataDir(username);
17920
- const globalConfig = join24(this.baseDir, "config.json");
17921
- if (existsSync29(globalConfig)) {
17982
+ const globalConfig = join25(this.baseDir, "config.json");
17983
+ if (existsSync30(globalConfig)) {
17922
17984
  try {
17923
- const content = readFileSync22(globalConfig, "utf-8");
17924
- writeFileSync4(join24(userDir, "config.json"), content, "utf-8");
17985
+ const content = readFileSync23(globalConfig, "utf-8");
17986
+ writeFileSync4(join25(userDir, "config.json"), content, "utf-8");
17925
17987
  } catch {
17926
17988
  }
17927
17989
  }
17928
- const globalMemory = join24(this.baseDir, "memory.md");
17929
- if (existsSync29(globalMemory)) {
17990
+ const globalMemory = join25(this.baseDir, "memory.md");
17991
+ if (existsSync30(globalMemory)) {
17930
17992
  try {
17931
- const content = readFileSync22(globalMemory, "utf-8");
17932
- writeFileSync4(join24(userDir, "memory.md"), content, "utf-8");
17993
+ const content = readFileSync23(globalMemory, "utf-8");
17994
+ writeFileSync4(join25(userDir, "memory.md"), content, "utf-8");
17933
17995
  } catch {
17934
17996
  }
17935
17997
  }
17936
- const globalHistory = join24(this.baseDir, "history");
17937
- if (existsSync29(globalHistory)) {
17998
+ const globalHistory = join25(this.baseDir, "history");
17999
+ if (existsSync30(globalHistory)) {
17938
18000
  try {
17939
18001
  const files = readdirSync14(globalHistory).filter((f) => f.endsWith(".json"));
17940
- const userHistory = join24(userDir, "history");
18002
+ const userHistory = join25(userDir, "history");
17941
18003
  for (const f of files) {
17942
18004
  try {
17943
- copyFileSync(join24(globalHistory, f), join24(userHistory, f));
18005
+ copyFileSync(join25(globalHistory, f), join25(userHistory, f));
17944
18006
  } catch {
17945
18007
  }
17946
18008
  }
@@ -17951,9 +18013,9 @@ var AuthManager = class {
17951
18013
  }
17952
18014
  // ── Private methods ────────────────────────────────────────────
17953
18015
  loadOrCreate() {
17954
- if (existsSync29(this.usersFile)) {
18016
+ if (existsSync30(this.usersFile)) {
17955
18017
  try {
17956
- return JSON.parse(readFileSync22(this.usersFile, "utf-8"));
18018
+ return JSON.parse(readFileSync23(this.usersFile, "utf-8"));
17957
18019
  } catch {
17958
18020
  }
17959
18021
  }
@@ -17969,7 +18031,7 @@ var AuthManager = class {
17969
18031
  this.saveDB(this.db);
17970
18032
  }
17971
18033
  saveDB(db) {
17972
- mkdirSync14(this.baseDir, { recursive: true });
18034
+ mkdirSync15(this.baseDir, { recursive: true });
17973
18035
  atomicWriteFileSync(this.usersFile, JSON.stringify(db, null, 2));
17974
18036
  }
17975
18037
  /** Legacy hash — kept only for migrating old users (v0.2.x) */
@@ -18001,7 +18063,7 @@ function getModuleDir() {
18001
18063
  if (typeof import.meta?.url === "string") {
18002
18064
  const url = new URL(import.meta.url);
18003
18065
  const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
18004
- return dirname8(filePath);
18066
+ return dirname9(filePath);
18005
18067
  }
18006
18068
  } catch {
18007
18069
  }
@@ -18024,7 +18086,8 @@ async function startWebServer(options = {}) {
18024
18086
  timeout: config.get("timeouts")[id],
18025
18087
  proxy: config.get("proxy")
18026
18088
  }),
18027
- config.get("customProviders")
18089
+ config.get("customProviders"),
18090
+ config.getConfigDir()
18028
18091
  );
18029
18092
  if (options._extraProviders) {
18030
18093
  for (const p of options._extraProviders) {
@@ -18072,8 +18135,8 @@ async function startWebServer(options = {}) {
18072
18135
  }
18073
18136
  }
18074
18137
  let skillManager = null;
18075
- const skillsDir = join25(config.getConfigDir(), SKILLS_DIR_NAME);
18076
- if (existsSync30(skillsDir)) {
18138
+ const skillsDir = join26(config.getConfigDir(), SKILLS_DIR_NAME);
18139
+ if (existsSync31(skillsDir)) {
18077
18140
  skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
18078
18141
  skillManager.loadSkills(pluginAssets.skillDirs);
18079
18142
  const count = skillManager.listSkills().length;
@@ -18144,18 +18207,18 @@ async function startWebServer(options = {}) {
18144
18207
  next();
18145
18208
  };
18146
18209
  const moduleDir = getModuleDir();
18147
- let clientDir = join25(moduleDir, "web", "client");
18148
- if (!existsSync30(clientDir)) {
18149
- clientDir = join25(moduleDir, "client");
18210
+ let clientDir = join26(moduleDir, "web", "client");
18211
+ if (!existsSync31(clientDir)) {
18212
+ clientDir = join26(moduleDir, "client");
18150
18213
  }
18151
- if (!existsSync30(clientDir)) {
18152
- clientDir = join25(moduleDir, "..", "..", "src", "web", "client");
18214
+ if (!existsSync31(clientDir)) {
18215
+ clientDir = join26(moduleDir, "..", "..", "src", "web", "client");
18153
18216
  }
18154
- if (!existsSync30(clientDir)) {
18155
- clientDir = join25(process.cwd(), "src", "web", "client");
18217
+ if (!existsSync31(clientDir)) {
18218
+ clientDir = join26(process.cwd(), "src", "web", "client");
18156
18219
  }
18157
18220
  console.log(` Static files: ${clientDir}`);
18158
- app.use("/vendor", express.static(join25(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
18221
+ app.use("/vendor", express.static(join26(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
18159
18222
  app.use(express.static(clientDir));
18160
18223
  app.get("/api/status", (_req, res) => {
18161
18224
  res.json({
@@ -18222,7 +18285,7 @@ async function startWebServer(options = {}) {
18222
18285
  app.get("/api/files", requireAuth, (req, res) => {
18223
18286
  const cwd = process.cwd();
18224
18287
  const prefix = req.query.prefix || "";
18225
- const targetDir = join25(cwd, prefix);
18288
+ const targetDir = join26(cwd, prefix);
18226
18289
  try {
18227
18290
  const canonicalTarget = realpathSync(resolve11(targetDir));
18228
18291
  const canonicalCwd = realpathSync(resolve11(cwd));
@@ -18239,7 +18302,7 @@ async function startWebServer(options = {}) {
18239
18302
  const entries = readdirSync15(targetDir, { withFileTypes: true });
18240
18303
  const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
18241
18304
  name: e.name,
18242
- path: relative4(cwd, join25(targetDir, e.name)).replace(/\\/g, "/"),
18305
+ path: relative4(cwd, join26(targetDir, e.name)).replace(/\\/g, "/"),
18243
18306
  isDir: e.isDirectory()
18244
18307
  }));
18245
18308
  res.json({ files });
@@ -18275,8 +18338,8 @@ async function startWebServer(options = {}) {
18275
18338
  try {
18276
18339
  const authUser = req._authUser;
18277
18340
  const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
18278
- const filePath = join25(histDir, `${id}.json`);
18279
- if (!existsSync30(filePath)) {
18341
+ const filePath = join26(histDir, `${id}.json`);
18342
+ if (!existsSync31(filePath)) {
18280
18343
  res.status(404).json({ error: "Session not found" });
18281
18344
  return;
18282
18345
  }
@@ -18291,7 +18354,7 @@ async function startWebServer(options = {}) {
18291
18354
  res.status(404).json({ error: "Session not found" });
18292
18355
  return;
18293
18356
  }
18294
- const data = JSON.parse(readFileSync23(filePath, "utf-8"));
18357
+ const data = JSON.parse(readFileSync24(filePath, "utf-8"));
18295
18358
  res.json({ session: data });
18296
18359
  } catch (err) {
18297
18360
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
@@ -18304,7 +18367,7 @@ async function startWebServer(options = {}) {
18304
18367
  return;
18305
18368
  }
18306
18369
  const cwd = process.cwd();
18307
- const fullPath = resolve11(join25(cwd, filePath));
18370
+ const fullPath = resolve11(join26(cwd, filePath));
18308
18371
  try {
18309
18372
  const canonicalFull = realpathSync(fullPath);
18310
18373
  const canonicalCwd = realpathSync(resolve11(cwd));
@@ -18322,7 +18385,7 @@ async function startWebServer(options = {}) {
18322
18385
  res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
18323
18386
  return;
18324
18387
  }
18325
- const content = readFileSync23(fullPath, "utf-8");
18388
+ const content = readFileSync24(fullPath, "utf-8");
18326
18389
  res.json({ content, size: stat.size });
18327
18390
  } catch {
18328
18391
  res.json({ error: "Cannot read file" });
@@ -18577,17 +18640,17 @@ function resolveProjectMcpPath() {
18577
18640
  const cwd = process.cwd();
18578
18641
  const gitRoot = getGitRoot(cwd);
18579
18642
  const projectRoot = gitRoot ?? cwd;
18580
- const configPath = join25(projectRoot, MCP_PROJECT_CONFIG_NAME);
18581
- return existsSync30(configPath) ? configPath : null;
18643
+ const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
18644
+ return existsSync31(configPath) ? configPath : null;
18582
18645
  }
18583
18646
  function loadProjectMcpConfig() {
18584
18647
  const cwd = process.cwd();
18585
18648
  const gitRoot = getGitRoot(cwd);
18586
18649
  const projectRoot = gitRoot ?? cwd;
18587
- const configPath = join25(projectRoot, MCP_PROJECT_CONFIG_NAME);
18588
- if (!existsSync30(configPath)) return null;
18650
+ const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
18651
+ if (!existsSync31(configPath)) return null;
18589
18652
  try {
18590
- const raw = JSON.parse(readFileSync23(configPath, "utf-8"));
18653
+ const raw = JSON.parse(readFileSync24(configPath, "utf-8"));
18591
18654
  return raw.mcpServers ?? raw;
18592
18655
  } catch {
18593
18656
  return null;