@defai.digital/automatosx 12.6.1 → 12.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import * as path4 from 'path';
3
3
  import path4__default, { dirname, join, isAbsolute, basename, resolve, extname as extname$1, relative, sep, delimiter, normalize, parse as parse$1 } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import { mkdir, appendFile, access as access$1, readFile, stat, rm, readdir, copyFile, writeFile, rename, unlink, constants as constants$1, realpath as realpath$1 } from 'fs/promises';
5
+ import { mkdir, appendFile, access as access$1, stat, readFile, rm, readdir, copyFile, writeFile, rename, unlink, constants as constants$1, realpath as realpath$1 } from 'fs/promises';
6
6
  import * as fs4 from 'fs';
7
7
  import { access, realpath, existsSync, readFileSync, constants, writeFileSync, promises, mkdirSync, statSync, readdirSync, createWriteStream, unlinkSync } from 'fs';
8
8
  import Database2 from 'better-sqlite3';
@@ -20,15 +20,14 @@ import { promisify } from 'util';
20
20
  import yargs from 'yargs';
21
21
  import { hideBin } from 'yargs/helpers';
22
22
  import crypto4, { randomUUID, createHash, randomBytes } from 'crypto';
23
- import * as yaml4 from 'js-yaml';
24
- import yaml4__default, { load, dump } from 'js-yaml';
23
+ import * as yaml3 from 'js-yaml';
24
+ import yaml3__default, { load, dump } from 'js-yaml';
25
25
  import Table from 'cli-table3';
26
26
  import inquirer from 'inquirer';
27
27
  import Ajv from 'ajv';
28
28
  import addFormats from 'ajv-formats';
29
29
  import { EventEmitter } from 'events';
30
30
  import * as sqliteVec from 'sqlite-vec';
31
- import yaml from 'yaml';
32
31
  import { gzipSync, gunzipSync } from 'zlib';
33
32
  import { parse } from '@iarna/toml';
34
33
 
@@ -1418,6 +1417,119 @@ var init_path_resolver = __esm({
1418
1417
  }
1419
1418
  });
1420
1419
 
1420
+ // src/shared/utils/safe-timers.ts
1421
+ function createSafeInterval(callback, intervalMs, options = {}) {
1422
+ const { ref = false, name, signal, immediate = false } = options;
1423
+ if (signal?.aborted) {
1424
+ logger.debug("createSafeInterval: already aborted", { name });
1425
+ return () => {
1426
+ };
1427
+ }
1428
+ let cleared = false;
1429
+ let intervalId = null;
1430
+ const safeCallback = async () => {
1431
+ if (cleared) return;
1432
+ try {
1433
+ await callback();
1434
+ } catch (error) {
1435
+ logger.error("Safe interval callback error", {
1436
+ name,
1437
+ error: error.message
1438
+ });
1439
+ }
1440
+ };
1441
+ if (immediate) {
1442
+ setImmediate(() => {
1443
+ if (!cleared) {
1444
+ safeCallback();
1445
+ }
1446
+ });
1447
+ }
1448
+ intervalId = setInterval(safeCallback, intervalMs);
1449
+ if (!ref && intervalId.unref) {
1450
+ intervalId.unref();
1451
+ }
1452
+ const cleanup = () => {
1453
+ if (cleared) return;
1454
+ cleared = true;
1455
+ if (intervalId !== null) {
1456
+ clearInterval(intervalId);
1457
+ intervalId = null;
1458
+ logger.debug("Safe interval cleared", { name });
1459
+ }
1460
+ };
1461
+ if (signal) {
1462
+ signal.addEventListener("abort", cleanup, { once: true });
1463
+ }
1464
+ logger.debug("Safe interval created", {
1465
+ name,
1466
+ intervalMs,
1467
+ ref,
1468
+ immediate
1469
+ });
1470
+ return cleanup;
1471
+ }
1472
+ function createSafeTimeout(callback, delayMs, options = {}) {
1473
+ const { ref = false, name, signal } = options;
1474
+ if (signal?.aborted) {
1475
+ logger.debug("createSafeTimeout: already aborted", { name });
1476
+ return () => {
1477
+ };
1478
+ }
1479
+ let cleared = false;
1480
+ let timeoutId = null;
1481
+ const safeCallback = async () => {
1482
+ if (cleared) return;
1483
+ cleared = true;
1484
+ try {
1485
+ await callback();
1486
+ } catch (error) {
1487
+ logger.error("Safe timeout callback error", {
1488
+ name,
1489
+ error: error.message
1490
+ });
1491
+ }
1492
+ };
1493
+ timeoutId = setTimeout(safeCallback, delayMs);
1494
+ if (!ref && timeoutId.unref) {
1495
+ timeoutId.unref();
1496
+ }
1497
+ const cleanup = () => {
1498
+ if (cleared) return;
1499
+ cleared = true;
1500
+ if (timeoutId !== null) {
1501
+ clearTimeout(timeoutId);
1502
+ timeoutId = null;
1503
+ logger.debug("Safe timeout cleared", { name });
1504
+ }
1505
+ };
1506
+ if (signal) {
1507
+ signal.addEventListener("abort", cleanup, { once: true });
1508
+ }
1509
+ logger.debug("Safe timeout created", {
1510
+ name,
1511
+ delayMs,
1512
+ ref
1513
+ });
1514
+ return cleanup;
1515
+ }
1516
+ async function sleep(ms, signal) {
1517
+ return new Promise((resolve13, reject) => {
1518
+ const timeoutId = setTimeout(() => {
1519
+ resolve13();
1520
+ }, ms);
1521
+ if (timeoutId.unref) {
1522
+ timeoutId.unref();
1523
+ }
1524
+ });
1525
+ }
1526
+ var init_safe_timers = __esm({
1527
+ "src/shared/utils/safe-timers.ts"() {
1528
+ init_esm_shims();
1529
+ init_logger();
1530
+ }
1531
+ });
1532
+
1421
1533
  // src/core/workspace-indexer.ts
1422
1534
  var workspace_indexer_exports = {};
1423
1535
  __export(workspace_indexer_exports, {
@@ -2635,6 +2747,7 @@ function getRetryableErrors(provider) {
2635
2747
  return [...baseErrors, ...CODEX_RETRYABLE_ERRORS];
2636
2748
  case "glm":
2637
2749
  case "grok":
2750
+ case "qwen":
2638
2751
  return [...baseErrors, ...OPENAI_RETRYABLE_ERRORS];
2639
2752
  case "base":
2640
2753
  default:
@@ -2737,6 +2850,10 @@ var init_base_provider = __esm({
2737
2850
  // v12.0.0: Native Grok provider (xAI)
2738
2851
  "ax-grok",
2739
2852
  // v12.0.0: Alias for grok
2853
+ "qwen",
2854
+ // v12.7.0: Qwen Code provider (Alibaba Cloud)
2855
+ "qwen-code",
2856
+ // v12.7.0: Alias for qwen
2740
2857
  "test-provider"
2741
2858
  // For unit tests
2742
2859
  ];
@@ -3099,7 +3216,7 @@ var init_base_provider = __esm({
3099
3216
  if (readlineInterface) {
3100
3217
  try {
3101
3218
  readlineInterface.close();
3102
- } catch (error) {
3219
+ } catch {
3103
3220
  } finally {
3104
3221
  readlineInterface = null;
3105
3222
  }
@@ -3107,7 +3224,7 @@ var init_base_provider = __esm({
3107
3224
  if (stderrInterface) {
3108
3225
  try {
3109
3226
  stderrInterface.close();
3110
- } catch (error) {
3227
+ } catch {
3111
3228
  } finally {
3112
3229
  stderrInterface = null;
3113
3230
  }
@@ -3301,7 +3418,7 @@ ${fullPrompt}
3301
3418
  this.health.consecutiveSuccesses = 0;
3302
3419
  }
3303
3420
  return available;
3304
- } catch (error) {
3421
+ } catch {
3305
3422
  this.health.available = false;
3306
3423
  this.health.errorRate = 1;
3307
3424
  this.health.lastCheck = Date.now();
@@ -3477,6 +3594,8 @@ ${fullPrompt}
3477
3594
  retryableProvider = "glm";
3478
3595
  } else if (providerName === "grok" || providerName === "ax-grok") {
3479
3596
  retryableProvider = "grok";
3597
+ } else if (providerName === "qwen" || providerName === "qwen-code") {
3598
+ retryableProvider = "qwen";
3480
3599
  }
3481
3600
  return shouldRetryError(error, retryableProvider);
3482
3601
  }
@@ -5551,6 +5670,7 @@ var init_hybrid_adapter_base = __esm({
5551
5670
  init_flags();
5552
5671
  init_fallback_decision();
5553
5672
  init_provider_metrics();
5673
+ init_safe_timers();
5554
5674
  DEFAULT_CIRCUIT_BREAKER_CONFIG = {
5555
5675
  failureThreshold: 3,
5556
5676
  resetTimeout: 6e4,
@@ -5705,7 +5825,7 @@ var init_hybrid_adapter_base = __esm({
5705
5825
  });
5706
5826
  if (classification.decision === "retry_sdk" /* RETRY_SDK */ && attempt < this.maxRetries) {
5707
5827
  const delay = classification.retryDelayMs || 1e3;
5708
- await this.sleep(delay);
5828
+ await sleep(delay);
5709
5829
  continue;
5710
5830
  }
5711
5831
  if (classification.decision === "use_cli" /* USE_CLI */ || classification.decision === "retry_sdk" /* RETRY_SDK */ && attempt >= this.maxRetries) {
@@ -5880,12 +6000,6 @@ var init_hybrid_adapter_base = __esm({
5880
6000
  } catch {
5881
6001
  }
5882
6002
  }
5883
- /**
5884
- * Sleep utility
5885
- */
5886
- sleep(ms) {
5887
- return new Promise((resolve13) => setTimeout(resolve13, ms));
5888
- }
5889
6003
  /**
5890
6004
  * Get current active mode
5891
6005
  */
@@ -5936,13 +6050,21 @@ var init_types2 = __esm({
5936
6050
  "src/integrations/ax-glm/types.ts"() {
5937
6051
  init_esm_shims();
5938
6052
  GLM_MODEL_MAPPING = {
6053
+ // Convenience aliases (ax-cli v4.3.15)
6054
+ "glm-latest": "glm-4.6",
6055
+ "glm-vision": "glm-4.6v",
6056
+ "glm-fast": "glm-4-flash",
6057
+ "glm-image": "cogview-4",
6058
+ // Legacy aliases (deprecated)
5939
6059
  "glm-4-plus": "glm-4.6",
5940
- "glm-4v": "glm-4.5v",
6060
+ "glm-4.5v": "glm-4.6v",
6061
+ "glm-4v": "glm-4.6v",
6062
+ "glm-4": "glm-4.6",
5941
6063
  "glm-4-air": "glm-4-flash",
5942
6064
  "glm-4-airx": "glm-4-flash"
5943
6065
  };
5944
6066
  GLM_DEFAULT_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
5945
- GLM_DEFAULT_MODEL = "glm-4";
6067
+ GLM_DEFAULT_MODEL = "glm-4.6";
5946
6068
  GLM_DEFAULT_COMMAND = "ax-glm";
5947
6069
  }
5948
6070
  });
@@ -6098,14 +6220,14 @@ var init_sdk_adapter2 = __esm({
6098
6220
  };
6099
6221
  }
6100
6222
  });
6101
- var execAsync3, GLMCliWrapper;
6223
+ var execAsync4, GLMCliWrapper;
6102
6224
  var init_cli_wrapper2 = __esm({
6103
6225
  "src/integrations/ax-glm/cli-wrapper.ts"() {
6104
6226
  init_esm_shims();
6105
6227
  init_logger();
6106
6228
  init_validation_limits();
6107
6229
  init_types2();
6108
- execAsync3 = promisify(exec);
6230
+ execAsync4 = promisify(exec);
6109
6231
  GLMCliWrapper = class {
6110
6232
  config;
6111
6233
  cliPath = null;
@@ -6126,13 +6248,13 @@ var init_cli_wrapper2 = __esm({
6126
6248
  */
6127
6249
  async isAvailable() {
6128
6250
  try {
6129
- const { stdout } = await execAsync3(`which ${this.config.command}`, {
6251
+ const { stdout } = await execAsync4(`which ${this.config.command}`, {
6130
6252
  timeout: TIMEOUTS.PROVIDER_DETECTION
6131
6253
  });
6132
6254
  this.cliPath = stdout.trim();
6133
6255
  if (this.cliPath) {
6134
6256
  try {
6135
- const { stdout: versionOutput } = await execAsync3(
6257
+ const { stdout: versionOutput } = await execAsync4(
6136
6258
  `${this.config.command} --version`,
6137
6259
  { timeout: TIMEOUTS.PROVIDER_DETECTION }
6138
6260
  );
@@ -6481,6 +6603,7 @@ var init_sdk_only_adapter = __esm({
6481
6603
  "src/integrations/ax-glm/sdk-only-adapter.ts"() {
6482
6604
  init_esm_shims();
6483
6605
  init_logger();
6606
+ init_safe_timers();
6484
6607
  init_sdk_adapter2();
6485
6608
  init_types2();
6486
6609
  GLMSdkOnlyAdapter = class {
@@ -6560,7 +6683,7 @@ var init_sdk_only_adapter = __esm({
6560
6683
  attempt: attempt + 1,
6561
6684
  delayMs: delay
6562
6685
  });
6563
- await this.sleep(delay);
6686
+ await sleep(delay);
6564
6687
  continue;
6565
6688
  }
6566
6689
  break;
@@ -6596,12 +6719,6 @@ var init_sdk_only_adapter = __esm({
6596
6719
  }
6597
6720
  return false;
6598
6721
  }
6599
- /**
6600
- * Sleep utility
6601
- */
6602
- sleep(ms) {
6603
- return new Promise((resolve13) => setTimeout(resolve13, ms));
6604
- }
6605
6722
  /**
6606
6723
  * Get the configured model
6607
6724
  */
@@ -6896,10 +7013,18 @@ var init_types3 = __esm({
6896
7013
  "src/integrations/ax-grok/types.ts"() {
6897
7014
  init_esm_shims();
6898
7015
  GROK_MODEL_MAPPING = {
6899
- "grok-beta": "grok-3"
7016
+ // Convenience aliases (ax-cli v4.3.15)
7017
+ "grok-latest": "grok-4-0709",
7018
+ "grok-fast": "grok-4.1-fast",
7019
+ "grok-mini": "grok-3-mini",
7020
+ "grok-vision": "grok-2-vision-1212",
7021
+ "grok-image": "grok-2-image-1212",
7022
+ // Legacy aliases (deprecated)
7023
+ "grok-beta": "grok-3",
7024
+ "grok-2-vision": "grok-2-vision-1212"
6900
7025
  };
6901
7026
  GROK_DEFAULT_BASE_URL = "https://api.x.ai/v1";
6902
- GROK_DEFAULT_MODEL = "grok-3";
7027
+ GROK_DEFAULT_MODEL = "grok-4-0709";
6903
7028
  GROK_DEFAULT_COMMAND = "ax-grok";
6904
7029
  }
6905
7030
  });
@@ -7055,14 +7180,14 @@ var init_sdk_adapter3 = __esm({
7055
7180
  };
7056
7181
  }
7057
7182
  });
7058
- var execAsync4, GrokCliWrapper;
7183
+ var execAsync5, GrokCliWrapper;
7059
7184
  var init_cli_wrapper3 = __esm({
7060
7185
  "src/integrations/ax-grok/cli-wrapper.ts"() {
7061
7186
  init_esm_shims();
7062
7187
  init_logger();
7063
7188
  init_validation_limits();
7064
7189
  init_types3();
7065
- execAsync4 = promisify(exec);
7190
+ execAsync5 = promisify(exec);
7066
7191
  GrokCliWrapper = class {
7067
7192
  config;
7068
7193
  cliPath = null;
@@ -7083,13 +7208,13 @@ var init_cli_wrapper3 = __esm({
7083
7208
  */
7084
7209
  async isAvailable() {
7085
7210
  try {
7086
- const { stdout } = await execAsync4(`which ${this.config.command}`, {
7211
+ const { stdout } = await execAsync5(`which ${this.config.command}`, {
7087
7212
  timeout: TIMEOUTS.PROVIDER_DETECTION
7088
7213
  });
7089
7214
  this.cliPath = stdout.trim();
7090
7215
  if (this.cliPath) {
7091
7216
  try {
7092
- const { stdout: versionOutput } = await execAsync4(
7217
+ const { stdout: versionOutput } = await execAsync5(
7093
7218
  `${this.config.command} --version`,
7094
7219
  { timeout: TIMEOUTS.PROVIDER_DETECTION }
7095
7220
  );
@@ -7438,6 +7563,7 @@ var init_sdk_only_adapter2 = __esm({
7438
7563
  "src/integrations/ax-grok/sdk-only-adapter.ts"() {
7439
7564
  init_esm_shims();
7440
7565
  init_logger();
7566
+ init_safe_timers();
7441
7567
  init_sdk_adapter3();
7442
7568
  init_types3();
7443
7569
  GrokSdkOnlyAdapter = class {
@@ -7517,7 +7643,7 @@ var init_sdk_only_adapter2 = __esm({
7517
7643
  attempt: attempt + 1,
7518
7644
  delayMs: delay
7519
7645
  });
7520
- await this.sleep(delay);
7646
+ await sleep(delay);
7521
7647
  continue;
7522
7648
  }
7523
7649
  break;
@@ -7553,12 +7679,6 @@ var init_sdk_only_adapter2 = __esm({
7553
7679
  }
7554
7680
  return false;
7555
7681
  }
7556
- /**
7557
- * Sleep utility
7558
- */
7559
- sleep(ms) {
7560
- return new Promise((resolve13) => setTimeout(resolve13, ms));
7561
- }
7562
7682
  /**
7563
7683
  * Get the configured model
7564
7684
  */
@@ -7836,6 +7956,902 @@ Mode: ${this.grokConfig.mode || "auto"}`;
7836
7956
  }
7837
7957
  });
7838
7958
 
7959
+ // src/integrations/qwen-code/types.ts
7960
+ function normalizeQwenModel(model) {
7961
+ return QWEN_MODEL_MAPPING[model] || model;
7962
+ }
7963
+ function isVisionModel(model) {
7964
+ return model.includes("qwen3-coder") || model.includes("qwen-max");
7965
+ }
7966
+ function getModelContextWindow(model) {
7967
+ const normalizedModel = normalizeQwenModel(model);
7968
+ if (normalizedModel.includes("480b")) return 128e3;
7969
+ if (normalizedModel.includes("30b")) return 64e3;
7970
+ if (normalizedModel.includes("qwen-max")) return 128e3;
7971
+ if (normalizedModel.includes("qwen-plus")) return 128e3;
7972
+ if (normalizedModel.includes("qwen-turbo")) return 128e3;
7973
+ return 64e3;
7974
+ }
7975
+ var QWEN_DEFAULT_BASE_URL, QWEN_DEFAULT_MODEL, QWEN_DEFAULT_COMMAND, QWEN_MODEL_MAPPING;
7976
+ var init_types4 = __esm({
7977
+ "src/integrations/qwen-code/types.ts"() {
7978
+ init_esm_shims();
7979
+ QWEN_DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
7980
+ QWEN_DEFAULT_MODEL = "qwen-turbo";
7981
+ QWEN_DEFAULT_COMMAND = "qwen";
7982
+ QWEN_MODEL_MAPPING = {
7983
+ // Normalize common aliases
7984
+ "qwen-coder": "qwen3-coder-480b-a35b-instruct",
7985
+ "qwen-coder-480b": "qwen3-coder-480b-a35b-instruct",
7986
+ "qwen-coder-30b": "qwen3-coder-30b-a3b-instruct",
7987
+ "qwen2.5-coder": "qwen2.5-coder-32b-instruct"
7988
+ };
7989
+ }
7990
+ });
7991
+
7992
+ // src/integrations/qwen-code/sdk-adapter.ts
7993
+ var QwenSdkAdapter;
7994
+ var init_sdk_adapter4 = __esm({
7995
+ "src/integrations/qwen-code/sdk-adapter.ts"() {
7996
+ init_esm_shims();
7997
+ init_logger();
7998
+ init_validation_limits();
7999
+ init_types4();
8000
+ QwenSdkAdapter = class {
8001
+ client = null;
8002
+ config;
8003
+ initialized = false;
8004
+ constructor(config = {}) {
8005
+ const apiKey = config.apiKey || process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY || "";
8006
+ this.config = {
8007
+ apiKey,
8008
+ baseUrl: config.baseUrl || QWEN_DEFAULT_BASE_URL,
8009
+ model: config.model || QWEN_DEFAULT_MODEL,
8010
+ timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
8011
+ };
8012
+ logger.debug("[Qwen SDK] Adapter created", {
8013
+ model: this.config.model,
8014
+ baseUrl: this.config.baseUrl,
8015
+ hasApiKey: !!this.config.apiKey
8016
+ });
8017
+ }
8018
+ /**
8019
+ * Check if SDK is available (OpenAI package installed and API key configured)
8020
+ */
8021
+ async isAvailable() {
8022
+ try {
8023
+ if (!this.config.apiKey) {
8024
+ logger.debug("[Qwen SDK] No API key configured (set DASHSCOPE_API_KEY or QWEN_API_KEY)");
8025
+ return false;
8026
+ }
8027
+ await import('openai');
8028
+ return true;
8029
+ } catch (error) {
8030
+ logger.debug("[Qwen SDK] OpenAI SDK not available", {
8031
+ error: error instanceof Error ? error.message : String(error)
8032
+ });
8033
+ return false;
8034
+ }
8035
+ }
8036
+ /**
8037
+ * Initialize the SDK client
8038
+ */
8039
+ async initialize() {
8040
+ if (this.initialized) {
8041
+ return;
8042
+ }
8043
+ try {
8044
+ const OpenAI = (await import('openai')).default;
8045
+ this.client = new OpenAI({
8046
+ apiKey: this.config.apiKey,
8047
+ baseURL: this.config.baseUrl,
8048
+ timeout: this.config.timeout
8049
+ });
8050
+ this.initialized = true;
8051
+ logger.debug("[Qwen SDK] Client initialized", {
8052
+ model: this.config.model
8053
+ });
8054
+ } catch (error) {
8055
+ throw new Error(
8056
+ `Failed to initialize Qwen SDK: ${error instanceof Error ? error.message : String(error)}`
8057
+ );
8058
+ }
8059
+ }
8060
+ /**
8061
+ * Execute a request using the Qwen SDK
8062
+ */
8063
+ async execute(request) {
8064
+ if (!this.initialized) {
8065
+ await this.initialize();
8066
+ }
8067
+ const startTime = Date.now();
8068
+ try {
8069
+ const messages = [];
8070
+ if (request.systemPrompt) {
8071
+ messages.push({
8072
+ role: "system",
8073
+ content: request.systemPrompt
8074
+ });
8075
+ }
8076
+ messages.push({
8077
+ role: "user",
8078
+ content: request.prompt
8079
+ });
8080
+ const model = normalizeQwenModel(request.model || this.config.model);
8081
+ logger.debug("[Qwen SDK] Executing request", {
8082
+ model,
8083
+ messageCount: messages.length,
8084
+ promptLength: request.prompt.length
8085
+ });
8086
+ const openaiClient = this.client;
8087
+ const response = await openaiClient.chat.completions.create({
8088
+ model,
8089
+ messages,
8090
+ max_tokens: request.maxTokens,
8091
+ temperature: request.temperature,
8092
+ stream: false
8093
+ });
8094
+ const latencyMs = Date.now() - startTime;
8095
+ if (!response.choices || response.choices.length === 0) {
8096
+ throw new Error("Qwen API returned empty choices array");
8097
+ }
8098
+ const choice = response.choices[0];
8099
+ const content = choice?.message?.content || "";
8100
+ const finishReason = choice?.finish_reason || "unknown";
8101
+ logger.debug("[Qwen SDK] Request completed", {
8102
+ model: response.model,
8103
+ latencyMs,
8104
+ tokensUsed: response.usage?.total_tokens
8105
+ });
8106
+ return {
8107
+ content,
8108
+ model: response.model,
8109
+ tokensUsed: response.usage ? {
8110
+ prompt: response.usage.prompt_tokens,
8111
+ completion: response.usage.completion_tokens,
8112
+ total: response.usage.total_tokens
8113
+ } : { prompt: 0, completion: 0, total: 0 },
8114
+ latencyMs,
8115
+ finishReason,
8116
+ cached: false
8117
+ };
8118
+ } catch (error) {
8119
+ const latencyMs = Date.now() - startTime;
8120
+ logger.error("[Qwen SDK] Request failed", {
8121
+ error: error instanceof Error ? error.message : String(error),
8122
+ latencyMs
8123
+ });
8124
+ throw error;
8125
+ }
8126
+ }
8127
+ /**
8128
+ * Get the configured model
8129
+ */
8130
+ getModel() {
8131
+ return this.config.model;
8132
+ }
8133
+ /**
8134
+ * Clean up resources
8135
+ */
8136
+ async destroy() {
8137
+ this.client = null;
8138
+ this.initialized = false;
8139
+ logger.debug("[Qwen SDK] Adapter destroyed");
8140
+ }
8141
+ };
8142
+ }
8143
+ });
8144
+ var NON_INTERACTIVE_ENV, SIGKILL_ESCALATION_MS, QwenCliWrapper;
8145
+ var init_cli_wrapper4 = __esm({
8146
+ "src/integrations/qwen-code/cli-wrapper.ts"() {
8147
+ init_esm_shims();
8148
+ init_logger();
8149
+ init_validation_limits();
8150
+ init_cli_provider_detector();
8151
+ init_types4();
8152
+ NON_INTERACTIVE_ENV = {
8153
+ TERM: "dumb",
8154
+ NO_COLOR: "1",
8155
+ FORCE_COLOR: "0",
8156
+ CI: "true",
8157
+ NO_UPDATE_NOTIFIER: "1",
8158
+ DEBIAN_FRONTEND: "noninteractive"
8159
+ };
8160
+ SIGKILL_ESCALATION_MS = 5e3;
8161
+ QwenCliWrapper = class {
8162
+ config;
8163
+ constructor(config = {}) {
8164
+ this.config = {
8165
+ command: config.command || QWEN_DEFAULT_COMMAND,
8166
+ vlmSwitchMode: config.vlmSwitchMode || "once",
8167
+ yolo: config.yolo || false,
8168
+ timeout: config.timeout || TIMEOUTS.PROVIDER_DEFAULT
8169
+ };
8170
+ logger.debug("[Qwen CLI] Wrapper created", {
8171
+ command: this.config.command,
8172
+ timeout: this.config.timeout
8173
+ });
8174
+ }
8175
+ /**
8176
+ * Check if Qwen CLI is available on PATH
8177
+ */
8178
+ async isAvailable() {
8179
+ if (process.env.AX_MOCK_PROVIDERS === "true") {
8180
+ return true;
8181
+ }
8182
+ try {
8183
+ const result = findOnPath(this.config.command);
8184
+ logger.debug("[Qwen CLI] Availability check", {
8185
+ available: result.found,
8186
+ path: result.path
8187
+ });
8188
+ return result.found;
8189
+ } catch (error) {
8190
+ logger.debug("[Qwen CLI] Availability check failed", {
8191
+ error: error instanceof Error ? error.message : String(error)
8192
+ });
8193
+ return false;
8194
+ }
8195
+ }
8196
+ /**
8197
+ * Execute a prompt using the Qwen CLI
8198
+ *
8199
+ * Since Qwen CLI is interactive, we:
8200
+ * 1. Spawn the process
8201
+ * 2. Send the prompt via stdin
8202
+ * 3. Capture output until we detect completion
8203
+ * 4. Return the response
8204
+ */
8205
+ async execute(request) {
8206
+ if (process.env.AX_MOCK_PROVIDERS === "true") {
8207
+ return this.createMockResponse(request.prompt);
8208
+ }
8209
+ const startTime = Date.now();
8210
+ return new Promise((resolve13, reject) => {
8211
+ const args = [];
8212
+ if (this.config.vlmSwitchMode !== "once") {
8213
+ args.push("--vlm-switch-mode", this.config.vlmSwitchMode);
8214
+ }
8215
+ if (this.config.yolo) {
8216
+ args.push("--yolo");
8217
+ }
8218
+ logger.debug("[Qwen CLI] Spawning process", {
8219
+ command: this.config.command,
8220
+ args,
8221
+ promptLength: request.prompt.length
8222
+ });
8223
+ const child = spawn(this.config.command, args, {
8224
+ env: { ...process.env, ...NON_INTERACTIVE_ENV }
8225
+ });
8226
+ let stdout = "";
8227
+ let stderr = "";
8228
+ let timeoutId = null;
8229
+ let forceKillTimer = null;
8230
+ let readlineInterface = null;
8231
+ let responseStarted = false;
8232
+ const cleanup = () => {
8233
+ if (timeoutId) {
8234
+ clearTimeout(timeoutId);
8235
+ timeoutId = null;
8236
+ }
8237
+ if (forceKillTimer) {
8238
+ clearTimeout(forceKillTimer);
8239
+ forceKillTimer = null;
8240
+ }
8241
+ if (readlineInterface) {
8242
+ try {
8243
+ readlineInterface.close();
8244
+ } catch {
8245
+ }
8246
+ readlineInterface = null;
8247
+ }
8248
+ };
8249
+ if (child.stdout) {
8250
+ readlineInterface = readline2__default.createInterface({
8251
+ input: child.stdout,
8252
+ crlfDelay: Infinity
8253
+ });
8254
+ readlineInterface.on("line", (line) => {
8255
+ if (line.startsWith(">") && !responseStarted) {
8256
+ responseStarted = true;
8257
+ return;
8258
+ }
8259
+ if (responseStarted) {
8260
+ stdout += line + "\n";
8261
+ }
8262
+ });
8263
+ readlineInterface.on("error", (error) => {
8264
+ logger.debug("[Qwen CLI] Readline error", {
8265
+ error: error.message
8266
+ });
8267
+ });
8268
+ }
8269
+ if (child.stderr) {
8270
+ child.stderr.on("data", (data) => {
8271
+ stderr += data.toString();
8272
+ });
8273
+ }
8274
+ if (child.stdin) {
8275
+ try {
8276
+ let fullPrompt = request.prompt;
8277
+ if (request.systemPrompt) {
8278
+ fullPrompt = `${request.systemPrompt}
8279
+
8280
+ ${request.prompt}`;
8281
+ }
8282
+ child.stdin.write(fullPrompt);
8283
+ child.stdin.write("\n");
8284
+ child.stdin.end();
8285
+ } catch (error) {
8286
+ cleanup();
8287
+ child.kill("SIGTERM");
8288
+ reject(new Error(`Failed to write to Qwen stdin: ${error instanceof Error ? error.message : String(error)}`));
8289
+ return;
8290
+ }
8291
+ } else {
8292
+ cleanup();
8293
+ reject(new Error("Qwen CLI stdin not available"));
8294
+ return;
8295
+ }
8296
+ child.on("close", (code, signal) => {
8297
+ cleanup();
8298
+ const latencyMs = Date.now() - startTime;
8299
+ if (stderr) {
8300
+ logger.debug("[Qwen CLI] stderr output", { stderr: stderr.trim() });
8301
+ }
8302
+ if ((code === 0 || code === null) && !signal) {
8303
+ const content = this.parseResponse(stdout);
8304
+ resolve13({
8305
+ content: content.trim(),
8306
+ model: "qwen-code-cli",
8307
+ tokensUsed: {
8308
+ prompt: this.estimateTokens(request.prompt),
8309
+ completion: this.estimateTokens(content),
8310
+ total: this.estimateTokens(request.prompt) + this.estimateTokens(content)
8311
+ },
8312
+ latencyMs,
8313
+ finishReason: "stop",
8314
+ cached: false
8315
+ });
8316
+ } else if (signal) {
8317
+ reject(new Error(`Qwen CLI killed by signal ${signal}. stderr: ${stderr || "none"}`));
8318
+ } else {
8319
+ reject(new Error(`Qwen CLI exited with code ${code}. stderr: ${stderr || "none"}`));
8320
+ }
8321
+ });
8322
+ child.on("error", (error) => {
8323
+ cleanup();
8324
+ logger.error("[Qwen CLI] Process error", {
8325
+ error: error.message
8326
+ });
8327
+ reject(new Error(`Failed to spawn Qwen CLI: ${error.message}`));
8328
+ });
8329
+ timeoutId = setTimeout(() => {
8330
+ if (child.pid && !child.killed) {
8331
+ logger.warn("[Qwen CLI] Killing process due to timeout", {
8332
+ pid: child.pid,
8333
+ timeout: this.config.timeout
8334
+ });
8335
+ child.kill("SIGTERM");
8336
+ forceKillTimer = setTimeout(() => {
8337
+ if (child.pid) {
8338
+ try {
8339
+ process.kill(child.pid, 0);
8340
+ logger.warn("[Qwen CLI] Force killing process", { pid: child.pid });
8341
+ child.kill("SIGKILL");
8342
+ } catch {
8343
+ }
8344
+ }
8345
+ }, SIGKILL_ESCALATION_MS);
8346
+ }
8347
+ }, this.config.timeout);
8348
+ });
8349
+ }
8350
+ /**
8351
+ * Parse response from CLI output
8352
+ *
8353
+ * Qwen CLI outputs include prompts and formatting that we need to strip.
8354
+ */
8355
+ parseResponse(output) {
8356
+ let content = output.trim();
8357
+ content = content.replace(/\x1B\[[0-9;]*[mGKH]/g, "");
8358
+ content = content.replace(/^>\s*/gm, "").replace(/\n{3,}/g, "\n\n").trim();
8359
+ return content;
8360
+ }
8361
+ /**
8362
+ * Estimate token count
8363
+ */
8364
+ estimateTokens(text) {
8365
+ return Math.ceil(text.length / 4);
8366
+ }
8367
+ /**
8368
+ * Create mock response for testing
8369
+ */
8370
+ createMockResponse(prompt) {
8371
+ return {
8372
+ content: `[Mock Qwen Response]
8373
+
8374
+ This is a mock response from Qwen Code CLI.
8375
+ Prompt length: ${prompt.length} characters.`,
8376
+ model: "qwen-code-cli-mock",
8377
+ tokensUsed: {
8378
+ prompt: this.estimateTokens(prompt),
8379
+ completion: 50,
8380
+ total: this.estimateTokens(prompt) + 50
8381
+ },
8382
+ latencyMs: 10,
8383
+ finishReason: "stop",
8384
+ cached: false
8385
+ };
8386
+ }
8387
+ /**
8388
+ * Get CLI command
8389
+ */
8390
+ getCommand() {
8391
+ return this.config.command;
8392
+ }
8393
+ };
8394
+ }
8395
+ });
8396
+
8397
+ // src/integrations/qwen-code/hybrid-adapter.ts
8398
+ var QwenHybridAdapter;
8399
+ var init_hybrid_adapter4 = __esm({
8400
+ "src/integrations/qwen-code/hybrid-adapter.ts"() {
8401
+ init_esm_shims();
8402
+ init_logger();
8403
+ init_sdk_adapter4();
8404
+ init_cli_wrapper4();
8405
+ QwenHybridAdapter = class {
8406
+ sdkAdapter = null;
8407
+ cliWrapper = null;
8408
+ options;
8409
+ activeMode = null;
8410
+ // Circuit breakers for each mode
8411
+ sdkCircuitBreaker = {
8412
+ failures: 0,
8413
+ lastFailure: 0,
8414
+ isOpen: false
8415
+ };
8416
+ cliCircuitBreaker = {
8417
+ failures: 0,
8418
+ lastFailure: 0,
8419
+ isOpen: false
8420
+ };
8421
+ // Circuit breaker configuration
8422
+ FAILURE_THRESHOLD = 3;
8423
+ RECOVERY_TIMEOUT_MS = 6e4;
8424
+ // 1 minute
8425
+ constructor(options = {}) {
8426
+ this.options = {
8427
+ mode: options.mode || "auto",
8428
+ model: options.model || "qwen-turbo",
8429
+ apiKey: options.apiKey,
8430
+ baseUrl: options.baseUrl,
8431
+ command: options.command || "qwen",
8432
+ timeout: options.timeout
8433
+ };
8434
+ logger.debug("[Qwen Hybrid] Adapter created", {
8435
+ mode: this.options.mode,
8436
+ model: this.options.model
8437
+ });
8438
+ }
8439
+ /**
8440
+ * Get or create SDK adapter
8441
+ */
8442
+ getSdkAdapter() {
8443
+ if (!this.sdkAdapter) {
8444
+ const config = {
8445
+ apiKey: this.options.apiKey,
8446
+ baseUrl: this.options.baseUrl,
8447
+ model: this.options.model,
8448
+ timeout: this.options.timeout
8449
+ };
8450
+ this.sdkAdapter = new QwenSdkAdapter(config);
8451
+ }
8452
+ return this.sdkAdapter;
8453
+ }
8454
+ /**
8455
+ * Get or create CLI wrapper
8456
+ */
8457
+ getCliWrapper() {
8458
+ if (!this.cliWrapper) {
8459
+ const config = {
8460
+ command: this.options.command,
8461
+ timeout: this.options.timeout
8462
+ };
8463
+ this.cliWrapper = new QwenCliWrapper(config);
8464
+ }
8465
+ return this.cliWrapper;
8466
+ }
8467
+ /**
8468
+ * Check if a circuit breaker should allow requests
8469
+ */
8470
+ isCircuitClosed(breaker) {
8471
+ if (!breaker.isOpen) {
8472
+ return true;
8473
+ }
8474
+ if (Date.now() - breaker.lastFailure > this.RECOVERY_TIMEOUT_MS) {
8475
+ breaker.isOpen = false;
8476
+ breaker.failures = 0;
8477
+ return true;
8478
+ }
8479
+ return false;
8480
+ }
8481
+ /**
8482
+ * Record a failure on a circuit breaker
8483
+ */
8484
+ recordFailure(breaker) {
8485
+ breaker.failures++;
8486
+ breaker.lastFailure = Date.now();
8487
+ if (breaker.failures >= this.FAILURE_THRESHOLD) {
8488
+ breaker.isOpen = true;
8489
+ logger.warn("[Qwen Hybrid] Circuit breaker opened", {
8490
+ failures: breaker.failures
8491
+ });
8492
+ }
8493
+ }
8494
+ /**
8495
+ * Record a success on a circuit breaker
8496
+ */
8497
+ recordSuccess(breaker) {
8498
+ breaker.failures = 0;
8499
+ breaker.isOpen = false;
8500
+ }
8501
+ /**
8502
+ * Execute a request using the appropriate mode
8503
+ */
8504
+ async execute(request) {
8505
+ const mode = this.options.mode;
8506
+ if (mode === "sdk") {
8507
+ return this.executeWithSdk(request);
8508
+ }
8509
+ if (mode === "cli") {
8510
+ return this.executeWithCli(request);
8511
+ }
8512
+ return this.executeWithAuto(request);
8513
+ }
8514
+ /**
8515
+ * Execute with SDK only
8516
+ */
8517
+ async executeWithSdk(request) {
8518
+ const adapter = this.getSdkAdapter();
8519
+ if (!await adapter.isAvailable()) {
8520
+ throw new Error("Qwen SDK is not available. Check DASHSCOPE_API_KEY or QWEN_API_KEY.");
8521
+ }
8522
+ this.activeMode = "sdk";
8523
+ return adapter.execute(request);
8524
+ }
8525
+ /**
8526
+ * Execute with CLI only
8527
+ */
8528
+ async executeWithCli(request) {
8529
+ const wrapper = this.getCliWrapper();
8530
+ if (!await wrapper.isAvailable()) {
8531
+ throw new Error("Qwen CLI is not available. Run: npm install -g @qwen-code/qwen-code@latest");
8532
+ }
8533
+ this.activeMode = "cli";
8534
+ return wrapper.execute(request);
8535
+ }
8536
+ /**
8537
+ * Execute with automatic mode selection
8538
+ */
8539
+ async executeWithAuto(request) {
8540
+ if (this.isCircuitClosed(this.sdkCircuitBreaker)) {
8541
+ const adapter = this.getSdkAdapter();
8542
+ try {
8543
+ if (await adapter.isAvailable()) {
8544
+ logger.debug("[Qwen Hybrid] Using SDK mode");
8545
+ this.activeMode = "sdk";
8546
+ const response = await adapter.execute(request);
8547
+ this.recordSuccess(this.sdkCircuitBreaker);
8548
+ return response;
8549
+ }
8550
+ } catch (error) {
8551
+ logger.warn("[Qwen Hybrid] SDK execution failed, trying CLI", {
8552
+ error: error instanceof Error ? error.message : String(error)
8553
+ });
8554
+ this.recordFailure(this.sdkCircuitBreaker);
8555
+ }
8556
+ }
8557
+ if (this.isCircuitClosed(this.cliCircuitBreaker)) {
8558
+ const wrapper = this.getCliWrapper();
8559
+ try {
8560
+ if (await wrapper.isAvailable()) {
8561
+ logger.debug("[Qwen Hybrid] Using CLI mode (fallback)");
8562
+ this.activeMode = "cli";
8563
+ const response = await wrapper.execute(request);
8564
+ this.recordSuccess(this.cliCircuitBreaker);
8565
+ return response;
8566
+ }
8567
+ } catch (error) {
8568
+ logger.error("[Qwen Hybrid] CLI execution also failed", {
8569
+ error: error instanceof Error ? error.message : String(error)
8570
+ });
8571
+ this.recordFailure(this.cliCircuitBreaker);
8572
+ throw error;
8573
+ }
8574
+ }
8575
+ throw new Error(
8576
+ "Qwen execution failed: Both SDK and CLI modes are unavailable or have failed too many times. Ensure DASHSCOPE_API_KEY is set or Qwen CLI is installed."
8577
+ );
8578
+ }
8579
+ /**
8580
+ * Get the currently active execution mode
8581
+ */
8582
+ getActiveMode() {
8583
+ return this.activeMode;
8584
+ }
8585
+ /**
8586
+ * Reset circuit breakers
8587
+ */
8588
+ resetCircuitBreakers() {
8589
+ this.sdkCircuitBreaker = { failures: 0, lastFailure: 0, isOpen: false };
8590
+ this.cliCircuitBreaker = { failures: 0, lastFailure: 0, isOpen: false };
8591
+ logger.debug("[Qwen Hybrid] Circuit breakers reset");
8592
+ }
8593
+ /**
8594
+ * Clean up resources
8595
+ */
8596
+ async destroy() {
8597
+ if (this.sdkAdapter) {
8598
+ await this.sdkAdapter.destroy();
8599
+ this.sdkAdapter = null;
8600
+ }
8601
+ this.cliWrapper = null;
8602
+ this.activeMode = null;
8603
+ logger.debug("[Qwen Hybrid] Adapter destroyed");
8604
+ }
8605
+ };
8606
+ }
8607
+ });
8608
+
8609
+ // src/integrations/qwen-code/index.ts
8610
+ var init_qwen_code = __esm({
8611
+ "src/integrations/qwen-code/index.ts"() {
8612
+ init_esm_shims();
8613
+ init_hybrid_adapter4();
8614
+ init_sdk_adapter4();
8615
+ init_cli_wrapper4();
8616
+ init_types4();
8617
+ }
8618
+ });
8619
+
8620
+ // src/providers/qwen-provider.ts
8621
+ var qwen_provider_exports = {};
8622
+ __export(qwen_provider_exports, {
8623
+ QwenProvider: () => QwenProvider,
8624
+ default: () => QwenProvider
8625
+ });
8626
+ var SUPPORTED_MODELS, QwenProvider;
8627
+ var init_qwen_provider = __esm({
8628
+ "src/providers/qwen-provider.ts"() {
8629
+ init_esm_shims();
8630
+ init_base_provider();
8631
+ init_logger();
8632
+ init_qwen_code();
8633
+ SUPPORTED_MODELS = [
8634
+ "qwen3-coder-480b-a35b-instruct",
8635
+ "qwen3-coder-30b-a3b-instruct",
8636
+ "qwen2.5-coder-32b-instruct",
8637
+ "qwen-max",
8638
+ "qwen-plus",
8639
+ "qwen-turbo"
8640
+ ];
8641
+ QwenProvider = class extends BaseProvider {
8642
+ /** Selected model */
8643
+ model;
8644
+ /** SDK adapter for direct execution */
8645
+ sdkAdapter = null;
8646
+ /** Hybrid adapter for 'auto' mode */
8647
+ hybridAdapter = null;
8648
+ /** Provider configuration */
8649
+ qwenConfig;
8650
+ /** Supported models */
8651
+ static SUPPORTED_MODELS = SUPPORTED_MODELS;
8652
+ constructor(config) {
8653
+ super({
8654
+ ...config,
8655
+ command: "qwen"
8656
+ });
8657
+ this.qwenConfig = config;
8658
+ const requestedModel = config.model || QWEN_DEFAULT_MODEL;
8659
+ if (!SUPPORTED_MODELS.includes(requestedModel)) {
8660
+ logger.warn(`[Qwen] Unknown model: ${requestedModel}. Using ${QWEN_DEFAULT_MODEL}.`);
8661
+ this.model = QWEN_DEFAULT_MODEL;
8662
+ } else {
8663
+ this.model = requestedModel;
8664
+ }
8665
+ logger.debug("[Qwen Provider] Initialized", {
8666
+ model: this.model,
8667
+ mode: config.mode || "sdk"
8668
+ });
8669
+ }
8670
+ /**
8671
+ * Get the normalized model name
8672
+ */
8673
+ getNormalizedModel() {
8674
+ return normalizeQwenModel(this.model);
8675
+ }
8676
+ /**
8677
+ * Get or create SDK adapter
8678
+ */
8679
+ getSdkAdapter() {
8680
+ if (!this.sdkAdapter) {
8681
+ this.sdkAdapter = new QwenSdkAdapter({
8682
+ model: this.model,
8683
+ apiKey: this.qwenConfig.apiKey,
8684
+ baseUrl: this.qwenConfig.baseUrl,
8685
+ timeout: this.qwenConfig.timeout
8686
+ });
8687
+ }
8688
+ return this.sdkAdapter;
8689
+ }
8690
+ /**
8691
+ * Get or create hybrid adapter
8692
+ */
8693
+ getHybridAdapter() {
8694
+ if (!this.hybridAdapter) {
8695
+ const options = {
8696
+ mode: this.qwenConfig.mode || "auto",
8697
+ model: this.model,
8698
+ apiKey: this.qwenConfig.apiKey,
8699
+ baseUrl: this.qwenConfig.baseUrl,
8700
+ command: "qwen",
8701
+ timeout: this.qwenConfig.timeout
8702
+ };
8703
+ this.hybridAdapter = new QwenHybridAdapter(options);
8704
+ }
8705
+ return this.hybridAdapter;
8706
+ }
8707
+ /**
8708
+ * Execute a task using Qwen
8709
+ *
8710
+ * Execution flow:
8711
+ * 1. Mock mode → return mock response
8712
+ * 2. mode='sdk' (default) → use SDK adapter
8713
+ * 3. mode='auto' → use hybrid adapter (SDK with CLI fallback)
8714
+ * 4. mode='cli' → use CLI via BaseProvider
8715
+ */
8716
+ async execute(request) {
8717
+ if (process.env.AX_MOCK_PROVIDERS === "true") {
8718
+ return this.createMockResponse(request.prompt);
8719
+ }
8720
+ const effectiveMode = this.qwenConfig.mode || "sdk";
8721
+ if (effectiveMode === "cli") {
8722
+ logger.debug("[Qwen Provider] Executing via CLI", {
8723
+ model: this.model
8724
+ });
8725
+ return super.execute(request);
8726
+ }
8727
+ if (effectiveMode === "auto") {
8728
+ logger.debug("[Qwen Provider] Executing via hybrid adapter", {
8729
+ promptLength: request.prompt.length,
8730
+ model: this.model
8731
+ });
8732
+ const adapter2 = this.getHybridAdapter();
8733
+ return adapter2.execute(request);
8734
+ }
8735
+ logger.debug("[Qwen Provider] Executing via SDK adapter", {
8736
+ promptLength: request.prompt.length,
8737
+ model: this.model
8738
+ });
8739
+ const adapter = this.getSdkAdapter();
8740
+ if (!await adapter.isAvailable()) {
8741
+ throw new Error(
8742
+ 'Qwen SDK is not available. Set DASHSCOPE_API_KEY or QWEN_API_KEY environment variable, or use mode: "cli" to use Qwen Code CLI.'
8743
+ );
8744
+ }
8745
+ return adapter.execute(request);
8746
+ }
8747
+ /**
8748
+ * Get CLI command
8749
+ */
8750
+ getCLICommand() {
8751
+ const activeMode = this.hybridAdapter?.getActiveMode();
8752
+ if (activeMode === "sdk") {
8753
+ return "qwen-sdk";
8754
+ }
8755
+ return "qwen";
8756
+ }
8757
+ /**
8758
+ * Get CLI arguments for Qwen Code CLI
8759
+ *
8760
+ * Note: Qwen Code CLI is interactive by default.
8761
+ * We pass the prompt via stdin instead of command-line args.
8762
+ */
8763
+ getCLIArgs() {
8764
+ return [];
8765
+ }
8766
+ /**
8767
+ * Create mock response for testing
8768
+ */
8769
+ createMockResponse(prompt) {
8770
+ return {
8771
+ content: this.getMockResponse(),
8772
+ model: this.getNormalizedModel(),
8773
+ tokensUsed: {
8774
+ prompt: this.estimateTokens(prompt),
8775
+ completion: 50,
8776
+ total: this.estimateTokens(prompt) + 50
8777
+ },
8778
+ latencyMs: 10,
8779
+ finishReason: "stop",
8780
+ cached: false
8781
+ };
8782
+ }
8783
+ /**
8784
+ * Estimate token count
8785
+ */
8786
+ estimateTokens(text) {
8787
+ return Math.ceil(text.length / 4);
8788
+ }
8789
+ /**
8790
+ * Get mock response for testing
8791
+ */
8792
+ getMockResponse() {
8793
+ return `[Mock Qwen Response]
8794
+
8795
+ This is a mock response from the Qwen provider (${this.getNormalizedModel()}).
8796
+ In production, this would be a response from ${this.qwenConfig.mode === "sdk" ? "Qwen SDK" : "Qwen Code CLI"}.
8797
+
8798
+ Model: ${this.getNormalizedModel()}
8799
+ Provider: Qwen (Alibaba Cloud)
8800
+ Mode: ${this.qwenConfig.mode || "sdk"}`;
8801
+ }
8802
+ /**
8803
+ * Get provider capabilities
8804
+ */
8805
+ get capabilities() {
8806
+ const model = this.getNormalizedModel();
8807
+ const hasVision = isVisionModel(model);
8808
+ const maxContextTokens = getModelContextWindow(model);
8809
+ const activeMode = this.hybridAdapter?.getActiveMode();
8810
+ const integrationMode = activeMode === "sdk" ? "sdk" : "cli";
8811
+ return {
8812
+ ...super.capabilities,
8813
+ supportsStreaming: true,
8814
+ supportsVision: hasVision,
8815
+ maxContextTokens,
8816
+ supportedModels: SUPPORTED_MODELS,
8817
+ integrationMode
8818
+ };
8819
+ }
8820
+ /**
8821
+ * Get the active execution mode
8822
+ */
8823
+ getActiveMode() {
8824
+ return this.hybridAdapter?.getActiveMode() || null;
8825
+ }
8826
+ /**
8827
+ * Reset circuit breakers
8828
+ */
8829
+ resetCircuitBreakers() {
8830
+ this.hybridAdapter?.resetCircuitBreakers();
8831
+ }
8832
+ /**
8833
+ * Clean up resources
8834
+ */
8835
+ async destroy() {
8836
+ if (this.sdkAdapter) {
8837
+ await this.sdkAdapter.destroy();
8838
+ this.sdkAdapter = null;
8839
+ }
8840
+ if (this.hybridAdapter) {
8841
+ await this.hybridAdapter.destroy();
8842
+ this.hybridAdapter = null;
8843
+ }
8844
+ }
8845
+ /**
8846
+ * Get the list of supported models
8847
+ */
8848
+ static getSupportedModels() {
8849
+ return [...SUPPORTED_MODELS];
8850
+ }
8851
+ };
8852
+ }
8853
+ });
8854
+
7839
8855
  // src/cli/index.ts
7840
8856
  init_esm_shims();
7841
8857
  init_logger();
@@ -8736,12 +9752,16 @@ init_logger();
8736
9752
 
8737
9753
  // src/shared/helpers/deep-merge.ts
8738
9754
  init_esm_shims();
9755
+ function isPlainObject(value) {
9756
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
9757
+ }
8739
9758
  function deepMerge(defaults, user) {
8740
9759
  if (user === null || user === void 0) {
8741
- return defaults;
9760
+ return { ...defaults };
8742
9761
  }
8743
- const result = { ...defaults };
8744
- for (const key in user) {
9762
+ const result = Object.assign({}, defaults);
9763
+ const userKeys = Object.keys(user);
9764
+ for (const key of userKeys) {
8745
9765
  const userValue = user[key];
8746
9766
  if (userValue === null) {
8747
9767
  result[key] = void 0;
@@ -8751,8 +9771,11 @@ function deepMerge(defaults, user) {
8751
9771
  continue;
8752
9772
  }
8753
9773
  const defaultValue = defaults[key];
8754
- if (typeof userValue === "object" && typeof defaultValue === "object" && !Array.isArray(userValue) && !Array.isArray(defaultValue) && userValue !== null && defaultValue !== null) {
8755
- result[key] = deepMerge(defaultValue, userValue);
9774
+ if (isPlainObject(userValue) && isPlainObject(defaultValue)) {
9775
+ result[key] = deepMerge(
9776
+ defaultValue,
9777
+ userValue
9778
+ );
8756
9779
  } else {
8757
9780
  result[key] = userValue;
8758
9781
  }
@@ -9394,7 +10417,7 @@ var PRECOMPILED_CONFIG = {
9394
10417
  "enableFreeTierPrioritization": true,
9395
10418
  "enableWorkloadAwareRouting": true
9396
10419
  },
9397
- "version": "12.6.1"
10420
+ "version": "12.6.3"
9398
10421
  };
9399
10422
 
9400
10423
  // src/core/config/schemas.ts
@@ -12417,7 +13440,11 @@ var ProfileLoader = class {
12417
13440
  continue;
12418
13441
  }
12419
13442
  const data = load(content);
12420
- return data && typeof data === "object" && !Array.isArray(data) && "displayName" in data ? data.displayName || null : null;
13443
+ if (data && typeof data === "object" && !Array.isArray(data) && "displayName" in data) {
13444
+ const profile = data;
13445
+ return profile.displayName ?? null;
13446
+ }
13447
+ return null;
12421
13448
  } catch (error) {
12422
13449
  if (error.code === "ENOENT") {
12423
13450
  continue;
@@ -12446,7 +13473,7 @@ var ProfileLoader = class {
12446
13473
  });
12447
13474
  return identifier;
12448
13475
  } catch (error) {
12449
- if (error.name === "AgentNotFoundError") {
13476
+ if (error instanceof Error && error.name === "AgentNotFoundError") {
12450
13477
  logger.debug("Direct profile load failed, trying displayName lookup", { identifier });
12451
13478
  await this.buildDisplayNameMap();
12452
13479
  const resolved = this.displayNameMap.get(identifier.toLowerCase());
@@ -12775,7 +13802,7 @@ var ProfileLoader = class {
12775
13802
  if (typeof orch !== "object" || orch === null || Array.isArray(orch)) {
12776
13803
  throw new AgentValidationError("orchestration must be an object");
12777
13804
  }
12778
- if (orch.canDelegate !== void 0) {
13805
+ if ("canDelegate" in orch && orch.canDelegate !== void 0) {
12779
13806
  logger.warn("orchestration.canDelegate is deprecated and ignored (v4.9.0+). All agents can delegate by default.", {
12780
13807
  agent: profile.name
12781
13808
  });
@@ -12821,17 +13848,18 @@ var ProfileLoader = class {
12821
13848
  if (!data || typeof data !== "object" || Array.isArray(data)) {
12822
13849
  throw new AgentValidationError(`Invalid profile data for ${name}: expected object, got ${typeof data}`);
12823
13850
  }
12824
- if (data.name && typeof data.name === "string") {
12825
- if (!/^[a-zA-Z0-9_-]+$/.test(data.name)) {
12826
- if (!data.displayName) {
12827
- data.displayName = data.name;
13851
+ const profileData = data;
13852
+ if (profileData.name && typeof profileData.name === "string") {
13853
+ if (!/^[a-zA-Z0-9_-]+$/.test(profileData.name)) {
13854
+ if (!profileData.displayName) {
13855
+ profileData.displayName = profileData.name;
12828
13856
  }
12829
- data.name = name;
13857
+ profileData.name = name;
12830
13858
  }
12831
- } else if (!data.name) {
12832
- data.name = name;
13859
+ } else if (!profileData.name) {
13860
+ profileData.name = name;
12833
13861
  }
12834
- const validationResult = safeValidateAgentProfile(data);
13862
+ const validationResult = safeValidateAgentProfile(profileData);
12835
13863
  if (!validationResult.success) {
12836
13864
  const validationErrors = validationResult.error.issues.map(
12837
13865
  (e) => `${e.path.join(".")}: ${e.message}`
@@ -12841,22 +13869,22 @@ var ProfileLoader = class {
12841
13869
  );
12842
13870
  }
12843
13871
  let teamConfig;
12844
- if (data.team && this.teamManager) {
13872
+ if (profileData.team && this.teamManager) {
12845
13873
  try {
12846
- teamConfig = await this.teamManager.loadTeam(data.team);
13874
+ teamConfig = await this.teamManager.loadTeam(profileData.team);
12847
13875
  logger.debug("Team configuration loaded for agent", {
12848
13876
  agent: name,
12849
- team: data.team
13877
+ team: profileData.team
12850
13878
  });
12851
13879
  } catch (error) {
12852
13880
  logger.warn("Failed to load team configuration, using agent defaults", {
12853
13881
  agent: name,
12854
- team: data.team,
13882
+ team: profileData.team,
12855
13883
  error: error.message
12856
13884
  });
12857
13885
  }
12858
13886
  }
12859
- const abilities = data.abilities || [];
13887
+ const abilities = profileData.abilities || [];
12860
13888
  if (teamConfig?.sharedAbilities) {
12861
13889
  const allAbilities = [.../* @__PURE__ */ new Set([...teamConfig.sharedAbilities, ...abilities])];
12862
13890
  logger.debug("Merged abilities from team", {
@@ -12868,7 +13896,7 @@ var ProfileLoader = class {
12868
13896
  });
12869
13897
  abilities.splice(0, abilities.length, ...allAbilities);
12870
13898
  }
12871
- let orchestration = data.orchestration;
13899
+ let orchestration = profileData.orchestration;
12872
13900
  if (teamConfig?.orchestration && !orchestration) {
12873
13901
  orchestration = teamConfig.orchestration;
12874
13902
  logger.debug("Using team orchestration defaults", {
@@ -12877,32 +13905,32 @@ var ProfileLoader = class {
12877
13905
  });
12878
13906
  }
12879
13907
  const profile = {
12880
- name: data.name || name,
12881
- displayName: data.displayName,
12882
- role: data.role,
12883
- description: data.description,
13908
+ name: profileData.name || name,
13909
+ displayName: profileData.displayName,
13910
+ role: profileData.role,
13911
+ description: profileData.description,
12884
13912
  // v4.10.0+: Team field
12885
- team: data.team,
12886
- systemPrompt: data.systemPrompt,
13913
+ team: profileData.team,
13914
+ systemPrompt: profileData.systemPrompt,
12887
13915
  abilities,
12888
- dependencies: data.dependencies,
12889
- parallel: data.parallel,
13916
+ dependencies: profileData.dependencies,
13917
+ parallel: profileData.parallel,
12890
13918
  // Enhanced v4.1+ features
12891
- stages: data.stages,
12892
- personality: data.personality,
12893
- thinking_patterns: data.thinking_patterns,
12894
- abilitySelection: data.abilitySelection,
13919
+ stages: profileData.stages,
13920
+ personality: profileData.personality,
13921
+ thinking_patterns: profileData.thinking_patterns,
13922
+ abilitySelection: profileData.abilitySelection,
12895
13923
  // v5.7.0+: Agent Selection Metadata
12896
- selectionMetadata: data.selectionMetadata,
13924
+ selectionMetadata: profileData.selectionMetadata,
12897
13925
  // Provider preferences (deprecated, kept for backward compatibility)
12898
- provider: data.provider,
12899
- model: data.model,
12900
- temperature: data.temperature,
12901
- maxTokens: data.maxTokens,
13926
+ provider: profileData.provider,
13927
+ model: profileData.model,
13928
+ temperature: profileData.temperature,
13929
+ maxTokens: profileData.maxTokens,
12902
13930
  // Optional
12903
- tags: data.tags,
12904
- version: data.version,
12905
- metadata: data.metadata,
13931
+ tags: profileData.tags,
13932
+ version: profileData.version,
13933
+ metadata: profileData.metadata,
12906
13934
  // v4.7.0+ Orchestration (merged with team defaults)
12907
13935
  orchestration
12908
13936
  };
@@ -13160,14 +14188,17 @@ init_validation_limits();
13160
14188
  var execAsync2 = promisify(exec);
13161
14189
  var ProviderDetector = class _ProviderDetector {
13162
14190
  // v12.0.0: Removed ax-cli, added glm/grok (SDK-first providers)
14191
+ // v12.7.0: Added qwen (SDK-first with CLI fallback)
13163
14192
  static PROVIDER_COMMANDS = {
13164
14193
  "claude-code": "claude",
13165
14194
  "gemini-cli": "gemini",
13166
14195
  "codex": "codex",
13167
14196
  "glm": "glm",
13168
14197
  // v12.0.0: Native GLM provider (SDK-first)
13169
- "grok": "grok"
14198
+ "grok": "grok",
13170
14199
  // v12.0.0: Native Grok provider (SDK-first)
14200
+ "qwen": "qwen"
14201
+ // v12.7.0: Qwen Code provider (SDK-first with CLI fallback)
13171
14202
  };
13172
14203
  /**
13173
14204
  * Detect all supported AI provider CLIs
@@ -13192,16 +14223,22 @@ var ProviderDetector = class _ProviderDetector {
13192
14223
  const results = await Promise.all([
13193
14224
  this.isCommandAvailable("claude-code"),
13194
14225
  this.isCommandAvailable("gemini-cli"),
13195
- this.isCommandAvailable("codex")
14226
+ this.isCommandAvailable("codex"),
14227
+ this.isCommandAvailable("qwen")
13196
14228
  ]);
14229
+ const qwenApiKeySet = !!(process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY);
14230
+ const qwenCliAvailable = results[3];
14231
+ const qwenAvailable = qwenApiKeySet || qwenCliAvailable;
13197
14232
  const detected = {
13198
14233
  "claude-code": results[0],
13199
14234
  "gemini-cli": results[1],
13200
14235
  "codex": results[2],
13201
14236
  "glm": true,
13202
14237
  // SDK-first: always available via SDK
13203
- "grok": true
14238
+ "grok": true,
13204
14239
  // SDK-first: always available via SDK
14240
+ "qwen": qwenAvailable
14241
+ // v12.7.0: SDK-first with CLI fallback
13205
14242
  };
13206
14243
  const foundProviders = Object.entries(detected).filter(([_, isInstalled]) => isInstalled).map(([provider]) => provider);
13207
14244
  logger.info("Provider detection complete", {
@@ -13308,6 +14345,9 @@ var ProviderDetector = class _ProviderDetector {
13308
14345
  if (provider === "grok") {
13309
14346
  return this.getGrokVersion();
13310
14347
  }
14348
+ if (provider === "qwen") {
14349
+ return this.getQwenVersion();
14350
+ }
13311
14351
  const command = _ProviderDetector.PROVIDER_COMMANDS[provider];
13312
14352
  try {
13313
14353
  const { stdout } = await execAsync2(`${command} --version`, {
@@ -13348,6 +14388,19 @@ var ProviderDetector = class _ProviderDetector {
13348
14388
  }
13349
14389
  return "SDK v1 (grok-3, API key not set)";
13350
14390
  }
14391
+ /**
14392
+ * Get Qwen provider version info (SDK-first with CLI fallback)
14393
+ *
14394
+ * v12.7.0: Qwen is an SDK-first provider using OpenAI-compatible API (DashScope).
14395
+ * Returns SDK info with API key status or CLI availability.
14396
+ */
14397
+ getQwenVersion() {
14398
+ const apiKey = process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY;
14399
+ if (apiKey) {
14400
+ return "SDK v1 (qwen-turbo, API ready)";
14401
+ }
14402
+ return "SDK v1 (qwen-turbo, CLI fallback)";
14403
+ }
13351
14404
  /**
13352
14405
  * Get list of detected provider names
13353
14406
  *
@@ -13381,7 +14434,9 @@ var ProviderDetector = class _ProviderDetector {
13381
14434
  "gemini-cli": "Gemini CLI",
13382
14435
  "codex": "Codex CLI",
13383
14436
  "glm": "GLM (Zhipu AI)",
13384
- "grok": "Grok (xAI)"
14437
+ "grok": "Grok (xAI)",
14438
+ "qwen": "Qwen (Alibaba Cloud)"
14439
+ // v12.7.0
13385
14440
  };
13386
14441
  return nameMap[provider] || provider;
13387
14442
  }
@@ -13474,8 +14529,9 @@ var setupCommand = {
13474
14529
  console.log(chalk5.gray(" - Claude Code"));
13475
14530
  console.log(chalk5.gray(" - Gemini CLI"));
13476
14531
  console.log(chalk5.gray(" - Codex CLI"));
13477
- console.log(chalk5.gray(" - GLM (SDK-first, requires ZAI_API_KEY)"));
14532
+ console.log(chalk5.gray(" - GLM (SDK-first, requires GLM_API_KEY)"));
13478
14533
  console.log(chalk5.gray(" - Grok (SDK-first, requires XAI_API_KEY)"));
14534
+ console.log(chalk5.gray(" - Qwen (SDK-first, requires DASHSCOPE_API_KEY or qwen CLI)"));
13479
14535
  console.log("");
13480
14536
  console.log(chalk5.cyan(' \u{1F4A1} After installing, run "ax setup" again to configure.\n'));
13481
14537
  if (process.stdout.isTTY && process.stdin.isTTY) {
@@ -13552,14 +14608,14 @@ var setupCommand = {
13552
14608
  console.log(chalk5.cyan("\u{1F50C} Setting up Gemini CLI integration..."));
13553
14609
  const geminiDir = join(projectDir, ".gemini");
13554
14610
  const geminiDirExistedBefore = await checkExists2(geminiDir);
13555
- geminiMcpStatus = await setupGeminiIntegration(projectDir, packageRoot);
14611
+ geminiMcpStatus = await setupGeminiIntegration(projectDir, packageRoot, argv.force);
13556
14612
  if (!geminiDirExistedBefore) {
13557
14613
  createdResources.push(geminiDir);
13558
14614
  }
13559
14615
  if (geminiMcpStatus === "configured") {
13560
- console.log(chalk5.green(" \u2713 Gemini CLI MCP integration configured"));
14616
+ console.log(chalk5.green(" \u2713 Gemini CLI MCP integration configured (gemini mcp add)"));
13561
14617
  } else if (geminiMcpStatus === "skipped") {
13562
- console.log(chalk5.yellow(" \u26A0 Gemini CLI MCP skipped (MCP server not found)"));
14618
+ console.log(chalk5.yellow(" \u26A0 Gemini CLI not available, skipped MCP setup"));
13563
14619
  console.log(chalk5.gray(" CLI integration still works via subprocess"));
13564
14620
  } else {
13565
14621
  console.log(chalk5.yellow(" \u26A0 Gemini CLI MCP configuration failed"));
@@ -13581,34 +14637,26 @@ var setupCommand = {
13581
14637
  let codexMcpStatus = "skipped";
13582
14638
  if (providers["codex"]) {
13583
14639
  console.log(chalk5.cyan("\u{1F50C} Setting up Codex CLI MCP integration..."));
13584
- codexMcpStatus = await setupCodexGlobalMCPConfig();
14640
+ codexMcpStatus = await setupCodexGlobalMCPConfig(argv.force);
13585
14641
  if (codexMcpStatus === "configured") {
13586
- console.log(chalk5.green(" \u2713 Codex CLI MCP integration configured"));
14642
+ console.log(chalk5.green(" \u2713 Codex CLI MCP integration configured (codex mcp add)"));
13587
14643
  } else if (codexMcpStatus === "skipped") {
13588
- console.log(chalk5.yellow(" \u26A0 Codex CLI MCP skipped"));
14644
+ console.log(chalk5.yellow(" \u26A0 Codex CLI not available, skipped MCP setup"));
13589
14645
  } else {
13590
14646
  console.log(chalk5.yellow(" \u26A0 Codex CLI MCP configuration failed"));
13591
14647
  console.log(chalk5.gray(" CLI integration still works via subprocess"));
13592
14648
  }
13593
14649
  }
13594
- let glmMcpStatus = "skipped";
13595
- if (providers["glm"]) {
13596
- console.log(chalk5.cyan("\u{1F50C} Setting up GLM (ax-glm) MCP integration..."));
13597
- glmMcpStatus = await setupGlmMCPConfig(projectDir);
13598
- if (glmMcpStatus === "configured") {
13599
- console.log(chalk5.green(" \u2713 GLM MCP integration configured (.ax-glm/.mcp.json)"));
13600
- } else {
13601
- console.log(chalk5.yellow(" \u26A0 GLM MCP configuration failed"));
13602
- }
13603
- }
13604
- let grokMcpStatus = "skipped";
13605
- if (providers["grok"]) {
13606
- console.log(chalk5.cyan("\u{1F50C} Setting up Grok (ax-grok) MCP integration..."));
13607
- grokMcpStatus = await setupGrokMCPConfig(projectDir);
13608
- if (grokMcpStatus === "configured") {
13609
- console.log(chalk5.green(" \u2713 Grok MCP integration configured (.ax-grok/.mcp.json)"));
14650
+ let qwenMcpStatus = "skipped";
14651
+ if (providers["qwen"]) {
14652
+ console.log(chalk5.cyan("\u{1F50C} Setting up Qwen MCP integration..."));
14653
+ qwenMcpStatus = await setupQwenMCPConfig(projectDir, argv.force);
14654
+ if (qwenMcpStatus === "configured") {
14655
+ console.log(chalk5.green(" \u2713 Qwen MCP integration configured (qwen mcp add)"));
14656
+ } else if (qwenMcpStatus === "skipped") {
14657
+ console.log(chalk5.yellow(" \u26A0 Qwen CLI not available, skipped MCP setup"));
13610
14658
  } else {
13611
- console.log(chalk5.yellow(" \u26A0 Grok MCP configuration failed"));
14659
+ console.log(chalk5.yellow(" \u26A0 Qwen MCP configuration failed"));
13612
14660
  }
13613
14661
  }
13614
14662
  console.log(chalk5.cyan("\u{1F4DD} Updating .gitignore..."));
@@ -13639,9 +14687,11 @@ var setupCommand = {
13639
14687
  console.log(chalk5.gray(" This is fine - MCP discovery will work without manifests"));
13640
14688
  }
13641
14689
  }
13642
- console.log(chalk5.cyan("\u{1F50C} Creating MCP server configuration..."));
13643
- await createMcpConfig(projectDir);
13644
- console.log(chalk5.green(" \u2713 .mcp.json created for MCP discovery"));
14690
+ if (providers["claude-code"]) {
14691
+ console.log(chalk5.cyan("\u{1F50C} Setting up Claude Code MCP integration..."));
14692
+ await createMcpConfig(projectDir, argv.force);
14693
+ console.log(chalk5.green(" \u2713 Claude Code MCP integration configured (claude mcp add)"));
14694
+ }
13645
14695
  console.log(chalk5.green.bold("\n\u2705 AutomatosX set up successfully!\n"));
13646
14696
  if (foundProviders.length > 0) {
13647
14697
  console.log(chalk5.cyan(`Configured for: ${foundProviders.join(", ")}
@@ -13650,14 +14700,15 @@ var setupCommand = {
13650
14700
  console.log(chalk5.yellow("\u26A0\uFE0F No AI providers configured\n"));
13651
14701
  }
13652
14702
  console.log(chalk5.gray("Next steps:"));
13653
- console.log(chalk5.gray(" 1. Review ax.config.json"));
13654
- console.log(chalk5.gray(" 2. List agents: automatosx list agents"));
13655
- console.log(chalk5.gray(' 3. Run an agent: automatosx run backend "Hello!"\n'));
13656
- console.log(chalk5.cyan("MCP Integration (v13.0.0):"));
13657
- console.log(chalk5.gray(" \u2022 .mcp.json created for automatic MCP server discovery"));
13658
- console.log(chalk5.gray(" \u2022 Claude Code/Gemini CLI will auto-connect to AutomatosX"));
14703
+ console.log(chalk5.gray(' 1. Run "ax init" to create project context files (AX.md, CUSTOM.md)'));
14704
+ console.log(chalk5.gray(" 2. Review ax.config.json"));
14705
+ console.log(chalk5.gray(" 3. List agents: automatosx list agents"));
14706
+ console.log(chalk5.gray(' 4. Run an agent: automatosx run backend "Hello!"\n'));
14707
+ console.log(chalk5.cyan("MCP Integration (v12.8.0):"));
14708
+ console.log(chalk5.gray(" \u2022 Native CLI commands used: claude/gemini/codex/qwen mcp add"));
14709
+ console.log(chalk5.gray(" \u2022 View configured servers: <cli> mcp list"));
13659
14710
  console.log(chalk5.gray(" \u2022 Use get_capabilities tool to discover agents dynamically"));
13660
- console.log(chalk5.gray(" \u2022 No manual registration required\n"));
14711
+ console.log(chalk5.gray(" \u2022 GLM/Grok are SDK-only (no MCP CLI setup needed)\n"));
13661
14712
  if (claudeCodeSetupSucceeded) {
13662
14713
  console.log(chalk5.yellow("Legacy Claude Code Manifests (deprecated):"));
13663
14714
  console.log(chalk5.gray(" \u2022 Slash commands: /agent-<name> (will be removed in v14.0.0)"));
@@ -13707,7 +14758,7 @@ var setupCommand = {
13707
14758
  console.log(chalk5.gray(' \u2022 Example: "Use ax agent backend to create a REST API"'));
13708
14759
  console.log(chalk5.gray(" \u2022 No special commands needed - just ask naturally!\n"));
13709
14760
  }
13710
- if (providers["glm"] || providers["grok"]) {
14761
+ if (providers["glm"] || providers["grok"] || providers["qwen"]) {
13711
14762
  console.log(chalk5.cyan("SDK-First Providers (MCP Client Mode):"));
13712
14763
  if (providers["glm"]) {
13713
14764
  console.log(chalk5.gray(" \u2022 GLM (Zhipu AI) - API key: GLM_API_KEY"));
@@ -13717,8 +14768,12 @@ var setupCommand = {
13717
14768
  console.log(chalk5.gray(" \u2022 Grok (xAI) - API key: XAI_API_KEY"));
13718
14769
  console.log(chalk5.gray(" MCP: AxGrokWithMcp class connects to AutomatosX MCP server"));
13719
14770
  }
13720
- console.log(chalk5.gray(' \u2022 CLI: ax run <agent> "your task" --engine glm|grok'));
13721
- console.log(chalk5.gray(" \u2022 SDK: Import AxGlmWithMcp / AxGrokWithMcp for direct MCP integration\n"));
14771
+ if (providers["qwen"]) {
14772
+ console.log(chalk5.gray(" \u2022 Qwen (Alibaba Cloud) - API key: DASHSCOPE_API_KEY or QWEN_API_KEY"));
14773
+ console.log(chalk5.gray(" Or install Qwen Code CLI: npm install -g @qwen-code/qwen-code"));
14774
+ }
14775
+ console.log(chalk5.gray(' \u2022 CLI: ax run <agent> "your task" --engine glm|grok|qwen'));
14776
+ console.log(chalk5.gray(" \u2022 SDK: Import provider adapters for direct MCP integration\n"));
13722
14777
  }
13723
14778
  if (providers["codex"]) {
13724
14779
  console.log(chalk5.cyan("Codex CLI Integration:"));
@@ -14240,9 +15295,9 @@ async function initializeGitRepository(projectDir) {
14240
15295
  logger.info("Git repository already exists, skipping initialization");
14241
15296
  return true;
14242
15297
  }
14243
- const { spawn: spawn12 } = await import('child_process');
15298
+ const { spawn: spawn13 } = await import('child_process');
14244
15299
  await new Promise((resolve13, reject) => {
14245
- const child = spawn12("git", ["init"], {
15300
+ const child = spawn13("git", ["init"], {
14246
15301
  cwd: projectDir,
14247
15302
  stdio: "pipe",
14248
15303
  shell: false
@@ -14323,143 +15378,71 @@ async function updateGitignore(projectDir) {
14323
15378
  logger.warn("Failed to update .gitignore", { error: error.message });
14324
15379
  }
14325
15380
  }
14326
- async function setupGeminiIntegration(_projectDir, _packageRoot) {
14327
- const mcpStatus = await setupGeminiGlobalMCPConfig();
15381
+ async function setupGeminiIntegration(projectDir, _packageRoot, force = false) {
15382
+ const mcpStatus = await setupGeminiMCPViaCLI(projectDir, force);
14328
15383
  return mcpStatus;
14329
15384
  }
14330
- async function setupGeminiGlobalMCPConfig() {
14331
- const homeDir = process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME;
14332
- if (!homeDir) {
14333
- logger.warn("Could not determine home directory for Gemini CLI MCP configuration");
14334
- return "skipped";
14335
- }
14336
- const settingsPath = join(homeDir, ".gemini", "settings.json");
14337
- const geminiDir = join(homeDir, ".gemini");
15385
+ async function setupGeminiMCPViaCLI(projectDir, force = false) {
15386
+ const { exec: exec9 } = await import('child_process');
15387
+ const { promisify: promisify10 } = await import('util');
15388
+ const execAsync9 = promisify10(exec9);
14338
15389
  try {
14339
- await mkdir(geminiDir, { recursive: true });
14340
- let settings = {};
14341
- if (await checkExists2(settingsPath)) {
14342
- const existingContent = await readFile(settingsPath, "utf-8");
14343
- try {
14344
- settings = JSON.parse(existingContent);
14345
- } catch (error) {
14346
- logger.warn("Failed to parse existing Gemini settings, will merge carefully", {
14347
- error: error.message
15390
+ const { stdout: listOutput } = await execAsync9("gemini mcp list", { timeout: 1e4 });
15391
+ if (listOutput.includes("automatosx")) {
15392
+ if (force) {
15393
+ logger.info("Force mode: removing existing Gemini MCP server");
15394
+ await execAsync9("gemini mcp remove automatosx", { timeout: 1e4 }).catch(() => {
14348
15395
  });
15396
+ } else {
15397
+ logger.info("Gemini CLI MCP server already configured");
15398
+ return "configured";
14349
15399
  }
14350
15400
  }
14351
- let mcpServers = settings["mcpServers"];
14352
- if (!mcpServers || typeof mcpServers !== "object") {
14353
- mcpServers = {};
14354
- }
14355
- mcpServers["automatosx"] = {
14356
- command: "automatosx",
14357
- args: ["mcp", "server"]
14358
- };
14359
- settings["mcpServers"] = mcpServers;
14360
- await writeFile(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
14361
- logger.info("Configured Gemini CLI MCP in global settings", {
14362
- path: settingsPath
14363
- });
15401
+ const addCommand2 = `gemini mcp add automatosx automatosx mcp server -s project -e "AUTOMATOSX_PROJECT_DIR=${projectDir}"`;
15402
+ await execAsync9(addCommand2, { timeout: 3e4 });
15403
+ logger.info("Configured Gemini CLI MCP via gemini mcp add");
14364
15404
  return "configured";
14365
15405
  } catch (error) {
15406
+ const errorMessage = error.message;
15407
+ if (errorMessage.includes("command not found") || errorMessage.includes("not recognized")) {
15408
+ logger.debug("Gemini CLI not available, skipping MCP setup");
15409
+ return "skipped";
15410
+ }
14366
15411
  logger.warn("Failed to setup Gemini CLI MCP configuration", {
14367
- error: error.message,
14368
- path: settingsPath
15412
+ error: errorMessage
14369
15413
  });
14370
15414
  return "failed";
14371
15415
  }
14372
15416
  }
14373
- async function setupCodexGlobalMCPConfig() {
14374
- const homeDir = process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME;
14375
- if (!homeDir) {
14376
- logger.warn("Could not determine home directory for Codex CLI MCP configuration");
14377
- return "skipped";
14378
- }
14379
- const codexDir = join(homeDir, ".codex");
14380
- const configPath = join(codexDir, "config.toml");
15417
+ async function setupCodexGlobalMCPConfig(force = false) {
15418
+ const { exec: exec9 } = await import('child_process');
15419
+ const { promisify: promisify10 } = await import('util');
15420
+ const execAsync9 = promisify10(exec9);
14381
15421
  try {
14382
- await mkdir(codexDir, { recursive: true });
14383
- let existingContent = "";
14384
- if (await checkExists2(configPath)) {
14385
- existingContent = await readFile(configPath, "utf-8");
14386
- }
14387
- if (existingContent.includes("[mcp_servers.automatosx]")) {
14388
- logger.info("Codex CLI MCP already configured for automatosx");
14389
- return "configured";
14390
- }
14391
- const mcpConfig = `
14392
- # AutomatosX MCP Server - Added by ax setup v12.2.0
14393
- # Increased timeouts for lazy initialization (15-20s on first tool call)
14394
- [mcp_servers.automatosx]
14395
- command = "automatosx"
14396
- args = ["mcp", "server"]
14397
- startup_timeout_sec = 60
14398
- tool_timeout_sec = 120
14399
- `;
14400
- await writeFile(configPath, existingContent + mcpConfig, "utf-8");
14401
- logger.info("Configured Codex CLI MCP in global config", {
14402
- path: configPath
14403
- });
14404
- return "configured";
14405
- } catch (error) {
14406
- logger.warn("Failed to setup Codex CLI MCP configuration", {
14407
- error: error.message,
14408
- path: configPath
14409
- });
14410
- return "failed";
14411
- }
14412
- }
14413
- async function setupGlmMCPConfig(projectDir) {
14414
- const configDir = join(projectDir, ".ax-glm");
14415
- const configPath = join(configDir, ".mcp.json");
14416
- try {
14417
- await mkdir(configDir, { recursive: true });
14418
- const mcpConfig = {
14419
- mcpServers: {
14420
- automatosx: {
14421
- command: "automatosx",
14422
- args: ["mcp", "server"],
14423
- env: {
14424
- AUTOMATOSX_PROJECT_DIR: projectDir
14425
- }
14426
- }
14427
- }
14428
- };
14429
- await writeFile(configPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
14430
- logger.info("Configured GLM MCP integration", { path: configPath });
14431
- return "configured";
14432
- } catch (error) {
14433
- logger.warn("Failed to setup GLM MCP configuration", {
14434
- error: error.message,
14435
- path: configPath
14436
- });
14437
- return "failed";
14438
- }
14439
- }
14440
- async function setupGrokMCPConfig(projectDir) {
14441
- const configDir = join(projectDir, ".ax-grok");
14442
- const configPath = join(configDir, ".mcp.json");
14443
- try {
14444
- await mkdir(configDir, { recursive: true });
14445
- const mcpConfig = {
14446
- mcpServers: {
14447
- automatosx: {
14448
- command: "automatosx",
14449
- args: ["mcp", "server"],
14450
- env: {
14451
- AUTOMATOSX_PROJECT_DIR: projectDir
14452
- }
14453
- }
15422
+ const { stdout: listOutput } = await execAsync9("codex mcp list", { timeout: 1e4 });
15423
+ if (listOutput.includes("automatosx")) {
15424
+ if (force) {
15425
+ logger.info("Force mode: removing existing Codex MCP server");
15426
+ await execAsync9("codex mcp remove automatosx", { timeout: 1e4 }).catch(() => {
15427
+ });
15428
+ } else {
15429
+ logger.info("Codex CLI MCP server already configured");
15430
+ return "configured";
14454
15431
  }
14455
- };
14456
- await writeFile(configPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
14457
- logger.info("Configured Grok MCP integration", { path: configPath });
15432
+ }
15433
+ const cwd = process.cwd();
15434
+ const addCommand2 = `codex mcp add automatosx --env "AUTOMATOSX_PROJECT_DIR=${cwd}" -- automatosx mcp server`;
15435
+ await execAsync9(addCommand2, { timeout: 3e4 });
15436
+ logger.info("Configured Codex CLI MCP via codex mcp add");
14458
15437
  return "configured";
14459
15438
  } catch (error) {
14460
- logger.warn("Failed to setup Grok MCP configuration", {
14461
- error: error.message,
14462
- path: configPath
15439
+ const errorMessage = error.message;
15440
+ if (errorMessage.includes("command not found") || errorMessage.includes("not recognized")) {
15441
+ logger.debug("Codex CLI not available, skipping MCP setup");
15442
+ return "skipped";
15443
+ }
15444
+ logger.warn("Failed to setup Codex CLI MCP configuration", {
15445
+ error: errorMessage
14463
15446
  });
14464
15447
  return "failed";
14465
15448
  }
@@ -14508,41 +15491,66 @@ Created by AutomatosX for organized scratch work.
14508
15491
  });
14509
15492
  }
14510
15493
  }
14511
- async function createMcpConfig(projectDir) {
14512
- const mcpConfigPath = join(projectDir, ".mcp.json");
14513
- const mcpConfig = {
14514
- mcpServers: {
14515
- automatosx: {
14516
- command: "automatosx",
14517
- args: ["mcp", "server"],
14518
- env: {
14519
- AUTOMATOSX_PROJECT_DIR: projectDir
14520
- }
15494
+ async function createMcpConfig(projectDir, force = false) {
15495
+ const { exec: exec9 } = await import('child_process');
15496
+ const { promisify: promisify10 } = await import('util');
15497
+ const execAsync9 = promisify10(exec9);
15498
+ try {
15499
+ const { stdout: listOutput } = await execAsync9("claude mcp list", { timeout: 1e4 });
15500
+ if (listOutput.includes("automatosx")) {
15501
+ if (force) {
15502
+ logger.info("Force mode: removing existing Claude Code MCP server");
15503
+ await execAsync9("claude mcp remove automatosx", { timeout: 1e4 }).catch(() => {
15504
+ });
15505
+ } else {
15506
+ logger.info("Claude Code MCP server already configured");
15507
+ return;
14521
15508
  }
14522
15509
  }
14523
- };
15510
+ const addCommand2 = `claude mcp add automatosx -s local -e "AUTOMATOSX_PROJECT_DIR=${projectDir}" -- automatosx mcp server`;
15511
+ await execAsync9(addCommand2, { timeout: 3e4 });
15512
+ logger.info("Configured Claude Code MCP via claude mcp add");
15513
+ } catch (error) {
15514
+ const errorMessage = error.message;
15515
+ if (errorMessage.includes("command not found") || errorMessage.includes("not recognized")) {
15516
+ logger.debug("Claude Code not available, skipping MCP setup");
15517
+ return;
15518
+ }
15519
+ logger.warn("Failed to setup Claude Code MCP configuration", {
15520
+ error: errorMessage
15521
+ });
15522
+ }
15523
+ }
15524
+ async function setupQwenMCPConfig(projectDir, force = false) {
15525
+ const { exec: exec9 } = await import('child_process');
15526
+ const { promisify: promisify10 } = await import('util');
15527
+ const execAsync9 = promisify10(exec9);
14524
15528
  try {
14525
- if (await checkExists2(mcpConfigPath)) {
14526
- const existingContent = await readFile(mcpConfigPath, "utf-8");
14527
- try {
14528
- const existingConfig = JSON.parse(existingContent);
14529
- if (existingConfig.mcpServers) {
14530
- existingConfig.mcpServers["automatosx"] = mcpConfig.mcpServers.automatosx;
14531
- await writeFile(mcpConfigPath, JSON.stringify(existingConfig, null, 2), "utf-8");
14532
- logger.info("Updated existing .mcp.json with AutomatosX server", { path: mcpConfigPath });
14533
- return;
14534
- }
14535
- } catch {
14536
- logger.warn("Failed to parse existing .mcp.json, will overwrite", { path: mcpConfigPath });
15529
+ const { stdout: listOutput } = await execAsync9("qwen mcp list", { timeout: 1e4 });
15530
+ if (listOutput.includes("automatosx")) {
15531
+ if (force) {
15532
+ logger.info("Force mode: removing existing Qwen MCP server");
15533
+ await execAsync9("qwen mcp remove automatosx", { timeout: 1e4 }).catch(() => {
15534
+ });
15535
+ } else {
15536
+ logger.info("Qwen MCP server already configured");
15537
+ return "configured";
14537
15538
  }
14538
15539
  }
14539
- await writeFile(mcpConfigPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
14540
- logger.info("Created .mcp.json for MCP discovery", { path: mcpConfigPath });
15540
+ const addCommand2 = `qwen mcp add automatosx automatosx mcp server -s project -e "AUTOMATOSX_PROJECT_DIR=${projectDir}"`;
15541
+ await execAsync9(addCommand2, { timeout: 3e4 });
15542
+ logger.info("Configured Qwen MCP integration via qwen mcp add");
15543
+ return "configured";
14541
15544
  } catch (error) {
14542
- logger.warn("Failed to create .mcp.json", {
14543
- error: error.message,
14544
- path: mcpConfigPath
15545
+ const errorMessage = error.message;
15546
+ if (errorMessage.includes("command not found") || errorMessage.includes("not recognized")) {
15547
+ logger.debug("Qwen CLI not available, skipping MCP setup");
15548
+ return "skipped";
15549
+ }
15550
+ logger.warn("Failed to setup Qwen MCP configuration", {
15551
+ error: errorMessage
14545
15552
  });
15553
+ return "failed";
14546
15554
  }
14547
15555
  }
14548
15556
  async function cleanupForceMode(projectDir) {
@@ -14697,7 +15705,7 @@ async function parsePackageJson(projectDir, info) {
14697
15705
  if (packageJson.workspaces || devDeps.includes("lerna") || devDeps.includes("nx") || devDeps.includes("turborepo") || existsSync(join(projectDir, "lerna.json")) || existsSync(join(projectDir, "nx.json"))) {
14698
15706
  info.isMonorepo = true;
14699
15707
  }
14700
- } catch (error) {
15708
+ } catch {
14701
15709
  }
14702
15710
  }
14703
15711
  async function parseReadmeDescription(projectDir, info) {
@@ -14758,10 +15766,10 @@ async function parseReadmeDescription(projectDir, info) {
14758
15766
  info.teamSize = "Open source community";
14759
15767
  }
14760
15768
  }
14761
- } catch (error) {
15769
+ } catch {
14762
15770
  }
14763
15771
  }
14764
- function categorizeScripts(scripts, packageManager) {
15772
+ function categorizeScripts(scripts, _packageManager) {
14765
15773
  const categorized = {
14766
15774
  development: [],
14767
15775
  building: [],
@@ -14871,7 +15879,7 @@ async function analyzeFileStructure(projectDir) {
14871
15879
  if (name === "docs") {
14872
15880
  structure.docsDirectory = name;
14873
15881
  }
14874
- } catch (error) {
15882
+ } catch {
14875
15883
  }
14876
15884
  }
14877
15885
  }
@@ -14901,7 +15909,7 @@ async function countFilesRecursive(dirPath, depth = 0) {
14901
15909
  }
14902
15910
  return count;
14903
15911
  }
14904
- async function detectKeyComponents(projectDir, info) {
15912
+ async function detectKeyComponents(projectDir, _info) {
14905
15913
  const components = [];
14906
15914
  const srcDir = join(projectDir, "src");
14907
15915
  if (!existsSync(srcDir)) return components;
@@ -15200,7 +16208,7 @@ async function detectDatabaseSchema(projectDir, info) {
15200
16208
  }
15201
16209
  return void 0;
15202
16210
  }
15203
- async function detectPrismaSchema(projectDir, schemaPath, info) {
16211
+ async function detectPrismaSchema(projectDir, schemaPath, _info) {
15204
16212
  const models = [];
15205
16213
  const migrations = await detectMigrations(projectDir, "prisma/migrations");
15206
16214
  try {
@@ -15254,7 +16262,7 @@ async function detectPrismaSchema(projectDir, schemaPath, info) {
15254
16262
  relations
15255
16263
  });
15256
16264
  }
15257
- } catch (error) {
16265
+ } catch {
15258
16266
  }
15259
16267
  return {
15260
16268
  orm: "Prisma",
@@ -15342,347 +16350,66 @@ async function detectNextJSAPIRoutes(projectDir) {
15342
16350
  // Next.js API routes handle multiple methods
15343
16351
  path: `/api/${routePath}`,
15344
16352
  group: routePath.split("/")[0] || "api"
15345
- });
15346
- }
15347
- }
15348
- } catch {
15349
- }
15350
- }
15351
- return endpoints;
15352
- }
15353
- async function detectExpressRoutes(projectDir) {
15354
- const endpoints = [];
15355
- const srcDir = join(projectDir, "src");
15356
- if (!existsSync(srcDir)) return endpoints;
15357
- try {
15358
- const routeFiles = await findFilesRecursive(srcDir, /route|controller|api/i, 3);
15359
- for (const file of routeFiles.slice(0, 10)) {
15360
- try {
15361
- const content = await readFile(file, "utf-8");
15362
- const routeRegex = /(?:router|app)\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/g;
15363
- let match;
15364
- while ((match = routeRegex.exec(content)) !== null) {
15365
- if (match[1] && match[2]) {
15366
- const method = match[1].toUpperCase();
15367
- const path9 = match[2];
15368
- endpoints.push({
15369
- method,
15370
- path: path9,
15371
- group: path9.split("/")[1] || "api"
15372
- });
15373
- }
15374
- }
15375
- } catch {
15376
- }
15377
- }
15378
- } catch {
15379
- }
15380
- return endpoints;
15381
- }
15382
- async function findFilesRecursive(dir, pattern, maxDepth = 3, currentDepth = 0) {
15383
- if (currentDepth > maxDepth) return [];
15384
- const files = [];
15385
- try {
15386
- const entries = await readdir(dir, { withFileTypes: true });
15387
- for (const entry of entries) {
15388
- if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
15389
- const fullPath = join(dir, entry.name);
15390
- if (entry.isFile() && pattern.test(entry.name)) {
15391
- files.push(fullPath);
15392
- } else if (entry.isDirectory()) {
15393
- const subFiles = await findFilesRecursive(fullPath, pattern, maxDepth, currentDepth + 1);
15394
- files.push(...subFiles);
15395
- }
15396
- }
15397
- } catch {
15398
- }
15399
- return files;
15400
- }
15401
-
15402
- // src/cli/commands/init-parser.ts
15403
- init_esm_shims();
15404
- function parseAXMD(content) {
15405
- const result = {
15406
- dependencies: [],
15407
- scripts: {},
15408
- hasCustomContent: false,
15409
- customSections: []
15410
- };
15411
- const versionMatch = content.match(/Project:.*?\(v([\d.]+)\)/);
15412
- if (versionMatch) {
15413
- result.version = versionMatch[1];
15414
- }
15415
- const nameMatch = content.match(/# Project Context for (.+)/);
15416
- if (nameMatch) {
15417
- result.projectName = nameMatch[1];
15418
- }
15419
- const descMatch = content.match(/## Project Overview\n\n\*\*(.+?)\*\*/);
15420
- if (descMatch) {
15421
- result.description = descMatch[1];
15422
- }
15423
- const archMatch = content.match(/\*\*Architecture:\*\* (.+)/);
15424
- if (archMatch) {
15425
- result.architecture = archMatch[1];
15426
- }
15427
- const stackMatch = content.match(/\*\*Stack:\*\* (.+)/);
15428
- if (stackMatch && stackMatch[1]) {
15429
- result.stack = stackMatch[1];
15430
- result.dependencies = stackMatch[1].split(",").map((d) => d.trim().toLowerCase()).filter(Boolean);
15431
- }
15432
- const teamMatch = content.match(/\*\*Team:\*\* (.+)/);
15433
- if (teamMatch) {
15434
- result.team = teamMatch[1];
15435
- }
15436
- const testMatch = content.match(/- Framework: (.+)/);
15437
- if (testMatch) {
15438
- result.testFramework = testMatch[1];
15439
- }
15440
- const linterMatch = content.match(/- Linter: (.+)/);
15441
- if (linterMatch) {
15442
- result.linter = linterMatch[1];
15443
- }
15444
- const formatterMatch = content.match(/- Formatter: (.+)/);
15445
- if (formatterMatch) {
15446
- result.formatter = formatterMatch[1];
15447
- }
15448
- const commandsMatch = content.match(/## Canonical Commands\n\n```bash\n([\s\S]*?)```/);
15449
- if (commandsMatch && commandsMatch[1]) {
15450
- const commandsSection = commandsMatch[1];
15451
- const lines = commandsSection.split("\n");
15452
- for (const line of lines) {
15453
- const cmdMatch = line.match(/^([^\s#]+(?:\s+[^\s#]+)*)\s*#/);
15454
- if (cmdMatch && cmdMatch[1]) {
15455
- const cmd = cmdMatch[1].trim();
15456
- result.scripts[cmd] = cmd;
15457
- }
15458
- }
15459
- }
15460
- const customMarkers = [
15461
- /<!-- USER-EDITED -->/,
15462
- /## Our Custom/,
15463
- /## Notes/,
15464
- /## Team Guidelines/
15465
- ];
15466
- for (const marker of customMarkers) {
15467
- if (marker.test(content)) {
15468
- result.hasCustomContent = true;
15469
- break;
15470
- }
15471
- }
15472
- const sections = content.split(/^## /m);
15473
- const standardSections = [
15474
- "Project Overview",
15475
- "Agent Delegation Rules",
15476
- "Coding Conventions",
15477
- "Critical Guardrails",
15478
- "Canonical Commands",
15479
- "Useful Links"
15480
- ];
15481
- for (const section of sections) {
15482
- const sectionName = section.split("\n")[0] ?? "";
15483
- if (sectionName && !standardSections.includes(sectionName)) {
15484
- result.customSections.push(sectionName);
15485
- }
15486
- }
15487
- return result;
15488
- }
15489
- function detectDetailedChanges(parsed, current) {
15490
- const changes = [];
15491
- if (parsed.version !== current.version && current.version) {
15492
- changes.push({
15493
- type: "version",
15494
- field: "version",
15495
- oldValue: parsed.version,
15496
- newValue: current.version
15497
- });
15498
- }
15499
- const currentDeps = current.dependencies.map((d) => d.toLowerCase());
15500
- const addedDeps = currentDeps.filter((d) => !parsed.dependencies.includes(d));
15501
- if (addedDeps.length > 0) {
15502
- changes.push({
15503
- type: "dependency-added",
15504
- field: "dependencies",
15505
- items: addedDeps
15506
- });
15507
- }
15508
- const removedDeps = parsed.dependencies.filter((d) => !currentDeps.includes(d));
15509
- if (removedDeps.length > 0) {
15510
- changes.push({
15511
- type: "dependency-removed",
15512
- field: "dependencies",
15513
- items: removedDeps
15514
- });
15515
- }
15516
- const currentScriptKeys = Object.keys(current.scripts);
15517
- const parsedScriptKeys = Object.keys(parsed.scripts);
15518
- const addedScripts = currentScriptKeys.filter((s) => !parsedScriptKeys.includes(s));
15519
- if (addedScripts.length > 0) {
15520
- changes.push({
15521
- type: "script-added",
15522
- field: "scripts",
15523
- items: addedScripts
15524
- });
15525
- }
15526
- const removedScripts = parsedScriptKeys.filter((s) => !currentScriptKeys.includes(s));
15527
- if (removedScripts.length > 0) {
15528
- changes.push({
15529
- type: "script-removed",
15530
- field: "scripts",
15531
- items: removedScripts
15532
- });
15533
- }
15534
- if (current.linter && !parsed.linter) {
15535
- changes.push({
15536
- type: "tool-added",
15537
- field: "linter",
15538
- newValue: current.linter
15539
- });
15540
- }
15541
- if (current.formatter && !parsed.formatter) {
15542
- changes.push({
15543
- type: "tool-added",
15544
- field: "formatter",
15545
- newValue: current.formatter
15546
- });
15547
- }
15548
- if (current.testFramework && !parsed.testFramework) {
15549
- changes.push({
15550
- type: "tool-added",
15551
- field: "test framework",
15552
- newValue: current.testFramework
15553
- });
15554
- }
15555
- if (parsed.architecture && parsed.architecture !== current.architecture) {
15556
- changes.push({
15557
- type: "architecture",
15558
- field: "architecture",
15559
- oldValue: parsed.architecture,
15560
- newValue: current.architecture
15561
- });
15562
- }
15563
- return changes;
15564
- }
15565
- function formatChangeSummary(changes) {
15566
- if (changes.length === 0) return "";
15567
- const summaries = [];
15568
- for (const change of changes) {
15569
- switch (change.type) {
15570
- case "version":
15571
- summaries.push(`version ${change.oldValue} \u2192 ${change.newValue}`);
15572
- break;
15573
- case "dependency-added":
15574
- summaries.push(`+${change.items.length} dep${change.items.length > 1 ? "s" : ""}`);
15575
- break;
15576
- case "dependency-removed":
15577
- summaries.push(`-${change.items.length} dep${change.items.length > 1 ? "s" : ""}`);
15578
- break;
15579
- case "script-added":
15580
- summaries.push(`+${change.items.length} script${change.items.length > 1 ? "s" : ""}`);
15581
- break;
15582
- case "tool-added":
15583
- summaries.push(`+${change.field}`);
15584
- break;
15585
- case "architecture":
15586
- summaries.push("architecture");
15587
- break;
15588
- }
15589
- }
15590
- if (summaries.length === 0) return "";
15591
- if (summaries.length === 1) return `(${summaries[0]})`;
15592
- if (summaries.length === 2) return `(${summaries[0]}, ${summaries[1]})`;
15593
- return `(${summaries.length} changes)`;
15594
- }
15595
-
15596
- // src/cli/commands/init.ts
15597
- function generateAXMD(info) {
15598
- const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
15599
- return `# Project Context for ${info.name}
15600
-
15601
- > Last updated: ${today}
15602
- > Project: ${info.name}${info.version ? ` (v${info.version})` : ""}
15603
-
15604
- ## Project Overview
15605
-
15606
- ${generateProjectOverview(info)}
15607
-
15608
- ## Architecture
15609
-
15610
- ${generateArchitecture(info)}
15611
-
15612
- ## File Structure
15613
-
15614
- ${generateFileStructure(info)}
15615
-
15616
- ${generateGettingStarted(info)}
15617
-
15618
- ${generateTroubleshooting(info)}
15619
-
15620
- ${generateDevWorkflow(info)}
15621
-
15622
- ${generateDatabaseSchema(info)}
15623
-
15624
- ${generateAPIDocumentation(info)}
15625
-
15626
- ${generateAgentRules(info)}
15627
-
15628
- ${generateCodingConventions(info)}
15629
-
15630
- ${generateCriticalGuardrails(info)}
15631
-
15632
- ${generateCommands(info)}
15633
-
15634
- ${generateUsefulLinks(info)}
15635
-
15636
- ---
15637
-
15638
- **Generated by \`ax init\` \u2022 Run regularly to keep up-to-date**
15639
- `;
15640
- }
15641
- function generateProjectOverview(info) {
15642
- const sections = [];
15643
- if (info.detailedDescription) {
15644
- sections.push(info.detailedDescription);
15645
- } else if (info.description) {
15646
- sections.push(`**${info.description}**`);
15647
- } else {
15648
- sections.push(`**${info.framework || "Node.js"} project${info.hasTypeScript ? " with TypeScript" : ""}**`);
15649
- }
15650
- const keyInfo = [];
15651
- keyInfo.push(`**Version:** ${info.version || "Not specified"}`);
15652
- keyInfo.push(`**Language:** ${info.language}`);
15653
- if (info.framework) keyInfo.push(`**Framework:** ${info.framework}`);
15654
- if (info.buildTool) keyInfo.push(`**Build Tool:** ${info.buildTool}`);
15655
- if (info.testFramework) keyInfo.push(`**Test Framework:** ${info.testFramework}`);
15656
- if (info.isMonorepo) keyInfo.push(`**Type:** Monorepo`);
15657
- sections.push("\n" + keyInfo.join(" \n"));
15658
- sections.push(`
15659
- **Stack:** ${info.stack}`);
15660
- if (info.teamSize) {
15661
- sections.push(`
15662
- **Team:** ${info.teamSize}`);
16353
+ });
16354
+ }
16355
+ }
16356
+ } catch {
16357
+ }
15663
16358
  }
15664
- return sections.join("\n");
16359
+ return endpoints;
15665
16360
  }
15666
- function generateArchitecture(info) {
15667
- const sections = [];
15668
- sections.push(`**Type:** ${detectArchitectureType(info)}`);
15669
- if (info.architectureFlow) {
15670
- sections.push("\n**Flow:**\n```");
15671
- sections.push(info.architectureFlow.trim());
15672
- sections.push("```");
16361
+ async function detectExpressRoutes(projectDir) {
16362
+ const endpoints = [];
16363
+ const srcDir = join(projectDir, "src");
16364
+ if (!existsSync(srcDir)) return endpoints;
16365
+ try {
16366
+ const routeFiles = await findFilesRecursive(srcDir, /route|controller|api/i, 3);
16367
+ for (const file of routeFiles.slice(0, 10)) {
16368
+ try {
16369
+ const content = await readFile(file, "utf-8");
16370
+ const routeRegex = /(?:router|app)\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/g;
16371
+ let match;
16372
+ while ((match = routeRegex.exec(content)) !== null) {
16373
+ if (match[1] && match[2]) {
16374
+ const method = match[1].toUpperCase();
16375
+ const path9 = match[2];
16376
+ endpoints.push({
16377
+ method,
16378
+ path: path9,
16379
+ group: path9.split("/")[1] || "api"
16380
+ });
16381
+ }
16382
+ }
16383
+ } catch {
16384
+ }
16385
+ }
16386
+ } catch {
15673
16387
  }
15674
- if (info.keyComponents && info.keyComponents.length > 0) {
15675
- sections.push("\n**Key Components:**");
15676
- for (const comp of info.keyComponents) {
15677
- if (comp.purpose) {
15678
- sections.push(`- \`${comp.path}\` - ${comp.purpose}`);
15679
- } else {
15680
- sections.push(`- \`${comp.path}\``);
16388
+ return endpoints;
16389
+ }
16390
+ async function findFilesRecursive(dir, pattern, maxDepth = 3, currentDepth = 0) {
16391
+ if (currentDepth > maxDepth) return [];
16392
+ const files = [];
16393
+ try {
16394
+ const entries = await readdir(dir, { withFileTypes: true });
16395
+ for (const entry of entries) {
16396
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
16397
+ const fullPath = join(dir, entry.name);
16398
+ if (entry.isFile() && pattern.test(entry.name)) {
16399
+ files.push(fullPath);
16400
+ } else if (entry.isDirectory()) {
16401
+ const subFiles = await findFilesRecursive(fullPath, pattern, maxDepth, currentDepth + 1);
16402
+ files.push(...subFiles);
15681
16403
  }
15682
16404
  }
16405
+ } catch {
15683
16406
  }
15684
- return sections.join("\n");
16407
+ return files;
15685
16408
  }
16409
+
16410
+ // src/cli/commands/init.ts
16411
+ var execAsync3 = promisify(exec);
16412
+ var STALE_THRESHOLD_MS = 24 * 60 * 60 * 1e3;
15686
16413
  function detectArchitectureType(info) {
15687
16414
  if (info.dependencies.includes("next") || info.dependencies.includes("nuxt")) {
15688
16415
  return "Full-stack framework (SSR/SSG)";
@@ -15704,459 +16431,424 @@ function detectArchitectureType(info) {
15704
16431
  }
15705
16432
  return "Node.js application";
15706
16433
  }
15707
- function generateFileStructure(info) {
15708
- if (!info.fileStructure || info.fileStructure.directories.length === 0) {
15709
- return "";
15710
- }
15711
- const sections = [];
15712
- if (info.fileStructure.entryPoint) {
15713
- sections.push(`**Entry Point:** \`${info.fileStructure.entryPoint}\``);
15714
- }
15715
- sections.push("\n**Directories:**");
15716
- for (const dir of info.fileStructure.directories) {
15717
- if (dir.purpose) {
15718
- sections.push(`- \`${dir.path}/\` - ${dir.purpose} (${dir.fileCount} files)`);
15719
- } else {
15720
- sections.push(`- \`${dir.path}/\` - ${dir.fileCount} files`);
15721
- }
15722
- }
15723
- sections.push(`
15724
- **Total Files:** ${info.fileStructure.totalFiles}`);
15725
- return sections.join("\n");
15726
- }
15727
- function generateGettingStarted(info) {
15728
- if (!info.gettingStarted) {
15729
- return "";
15730
- }
15731
- const sections = ["## Getting Started", ""];
15732
- if (info.gettingStarted.prerequisites.length > 0) {
15733
- sections.push("### Prerequisites");
15734
- for (const prereq of info.gettingStarted.prerequisites) {
15735
- sections.push(`- ${prereq}`);
15736
- }
15737
- sections.push("");
15738
- }
15739
- if (info.gettingStarted.setupSteps.length > 0) {
15740
- sections.push("### First Time Setup");
15741
- for (const step of info.gettingStarted.setupSteps) {
15742
- sections.push(step);
16434
+ function generateAxIndex(info) {
16435
+ const now = (/* @__PURE__ */ new Date()).toISOString();
16436
+ const modules = [];
16437
+ if (info.fileStructure?.directories) {
16438
+ for (const dir of info.fileStructure.directories) {
16439
+ modules.push({
16440
+ path: dir.path,
16441
+ purpose: dir.purpose || `Contains ${dir.fileCount} files`,
16442
+ patterns: [],
16443
+ exports: []
16444
+ });
15743
16445
  }
15744
- sections.push("");
15745
16446
  }
15746
- if (info.gettingStarted.envVars.length > 0) {
15747
- sections.push("### Environment Variables");
15748
- sections.push("");
15749
- const required = info.gettingStarted.envVars.filter((v) => v.required);
15750
- const optional = info.gettingStarted.envVars.filter((v) => !v.required);
15751
- if (required.length > 0) {
15752
- sections.push("**Required:**");
15753
- for (const envVar of required) {
15754
- sections.push(`- \`${envVar.name}\`${envVar.description ? ` - ${envVar.description}` : ""}`);
15755
- if (envVar.example) {
15756
- sections.push(` - Example: \`${envVar.example}\``);
15757
- }
15758
- }
15759
- sections.push("");
15760
- }
15761
- if (optional.length > 0) {
15762
- sections.push("**Optional:**");
15763
- for (const envVar of optional) {
15764
- sections.push(`- \`${envVar.name}\`${envVar.description ? ` - ${envVar.description}` : ""}`);
15765
- if (envVar.example) {
15766
- sections.push(` - Example: \`${envVar.example}\``);
15767
- }
16447
+ const abstractions = [];
16448
+ if (info.keyComponents) {
16449
+ for (const comp of info.keyComponents) {
16450
+ abstractions.push({
16451
+ name: comp.path.split("/").pop() || comp.path,
16452
+ type: "pattern",
16453
+ location: comp.path,
16454
+ description: comp.purpose
16455
+ });
16456
+ }
16457
+ }
16458
+ const commands = {};
16459
+ if (info.categorizedScripts) {
16460
+ const categoryMap = {
16461
+ development: "development",
16462
+ testing: "testing",
16463
+ building: "building",
16464
+ quality: "quality",
16465
+ deployment: "deployment",
16466
+ other: "other"
16467
+ };
16468
+ for (const [category, scripts] of Object.entries(info.categorizedScripts)) {
16469
+ if (!scripts) continue;
16470
+ for (const script of scripts) {
16471
+ commands[script.name] = {
16472
+ script: `${info.packageManager} run ${script.name}`,
16473
+ description: script.description,
16474
+ category: categoryMap[category] || "other"
16475
+ };
15768
16476
  }
15769
- sections.push("");
15770
16477
  }
15771
16478
  }
15772
- return sections.join("\n");
16479
+ const prodDeps = info.dependencies.filter((d) => !d.startsWith("@types/"));
16480
+ const devDeps = info.dependencies.filter((d) => d.startsWith("@types/"));
16481
+ return {
16482
+ projectName: info.name,
16483
+ version: info.version || "0.0.0",
16484
+ projectType: detectArchitectureType(info),
16485
+ language: info.language,
16486
+ framework: info.framework,
16487
+ buildTool: info.buildTool,
16488
+ testFramework: info.testFramework,
16489
+ packageManager: info.packageManager,
16490
+ hasTypeScript: info.hasTypeScript,
16491
+ isMonorepo: info.isMonorepo || false,
16492
+ entryPoint: info.fileStructure?.entryPoint,
16493
+ sourceDirectory: info.fileStructure?.directories.find((d) => d.path === "src")?.path,
16494
+ testDirectory: info.fileStructure?.directories.find(
16495
+ (d) => d.path === "tests" || d.path === "test" || d.path === "__tests__"
16496
+ )?.path,
16497
+ modules,
16498
+ abstractions,
16499
+ commands,
16500
+ dependencies: {
16501
+ production: prodDeps.slice(0, 20),
16502
+ // Limit to top 20
16503
+ development: devDeps.slice(0, 10),
16504
+ // Limit to top 10
16505
+ total: info.dependencies.length
16506
+ },
16507
+ repository: info.repository,
16508
+ createdAt: now,
16509
+ updatedAt: now,
16510
+ analysisTier: 3
16511
+ // Default to comprehensive analysis
16512
+ };
15773
16513
  }
15774
- function generateTroubleshooting(info) {
15775
- const sections = ["## Troubleshooting", ""];
15776
- sections.push("### Common Issues");
16514
+ function generateCustomMD(info) {
16515
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
16516
+ const sections = [];
16517
+ sections.push(`# Custom Instructions for ${info.name}`);
15777
16518
  sections.push("");
15778
- sections.push("**Problem**: `npm install` fails with EACCES");
15779
- sections.push("**Solution**: Fix npm permissions: `sudo chown -R $USER ~/.npm`");
16519
+ sections.push(`> Generated: ${today}`);
16520
+ sections.push(`> Project: ${info.name}${info.version ? ` v${info.version}` : ""}`);
15780
16521
  sections.push("");
15781
- if (info.dependencies.includes("prisma") || info.dependencies.includes("typeorm") || info.dependencies.includes("pg") || info.dependencies.includes("mongodb")) {
15782
- sections.push("**Problem**: Database connection refused");
15783
- sections.push("**Solution**:");
15784
- sections.push("1. Check Docker is running: `docker ps`");
15785
- sections.push("2. Start services: `docker-compose up -d`");
15786
- if (info.scripts["db:ping"]) {
15787
- sections.push(`3. Verify connection: \`${info.packageManager} run db:ping\``);
15788
- }
15789
- sections.push("");
15790
- }
15791
- sections.push("**Problem**: Port already in use");
15792
- sections.push("**Solution**: Kill process: `lsof -ti:3000 | xargs kill`");
16522
+ sections.push("> **Note:** This file is protected from auto-rebuild. Edit freely to customize AI behavior.");
15793
16523
  sections.push("");
15794
- if (info.hasTypeScript) {
15795
- sections.push("**Problem**: TypeScript errors after `git pull`");
15796
- sections.push("**Solution**: Clean install: `rm -rf node_modules && npm install`");
15797
- sections.push("");
15798
- }
15799
- if (info.framework === "React" || info.framework === "Next.js" || info.framework === "Vue") {
15800
- sections.push("**Problem**: Hot reload not working");
15801
- sections.push("**Solution**: Check file watcher limits: `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf`");
15802
- sections.push("");
15803
- }
15804
- sections.push("### Debug Mode");
15805
- sections.push("Run with verbose logging:");
15806
- sections.push("```bash");
15807
- sections.push("DEBUG=* npm run dev");
15808
- if (info.scripts.test) {
15809
- sections.push("LOG_LEVEL=debug npm test");
16524
+ sections.push("## Project Overview");
16525
+ sections.push("");
16526
+ if (info.detailedDescription) {
16527
+ const firstParagraph = info.detailedDescription.split("\n\n")[0];
16528
+ sections.push(firstParagraph ?? "");
16529
+ } else if (info.description) {
16530
+ sections.push(info.description);
16531
+ } else {
16532
+ sections.push(`${info.framework || info.language} project${info.hasTypeScript ? " with TypeScript" : ""}`);
15810
16533
  }
15811
- sections.push("```");
15812
- return sections.join("\n");
15813
- }
15814
- function generateDevWorkflow(info) {
15815
- const sections = ["## Development Workflow", ""];
15816
- sections.push("### Daily Workflow");
15817
- sections.push("1. Pull latest: `git pull origin main`");
15818
- sections.push("2. Create feature branch: `git checkout -b feature/my-feature`");
15819
- sections.push("3. Make changes");
15820
- if (info.scripts.test) {
15821
- sections.push(`4. Run tests: \`${info.packageManager} test\``);
15822
- }
15823
- sections.push('5. Commit: `git commit -m "feat: add my feature"`');
15824
- sections.push("6. Push: `git push origin feature/my-feature`");
15825
- sections.push("7. Open PR on GitHub");
15826
- sections.push("8. Wait for CI + reviews");
15827
- sections.push("9. Merge to main (squash merge)");
15828
16534
  sections.push("");
15829
- sections.push("### Code Review Process");
15830
- sections.push("- Minimum 1 approval required");
15831
- sections.push("- CI must pass (tests + lint)");
15832
- sections.push("- No merge conflicts");
16535
+ sections.push("## Critical Rules");
15833
16536
  sections.push("");
15834
- sections.push("### Hot Reload");
15835
- if (info.framework === "React" || info.framework === "Vue") {
15836
- if (info.buildTool === "Vite") {
15837
- sections.push("- Frontend: Vite HMR (instant)");
15838
- } else {
15839
- sections.push("- Frontend: Hot module replacement enabled");
15840
- }
15841
- }
15842
- if (info.dependencies.includes("express") || info.dependencies.includes("fastify")) {
15843
- sections.push("- Backend: Nodemon (restart on save)");
15844
- }
15845
- if (info.framework === "Next.js") {
15846
- sections.push("- Next.js Fast Refresh (instant updates)");
15847
- }
16537
+ sections.push("### DO");
15848
16538
  sections.push("");
15849
- if (info.testFramework) {
15850
- sections.push("### Testing Strategy");
15851
- if (info.scripts["test:unit"]) {
15852
- sections.push(`- Unit tests: \`${info.packageManager} run test:unit\` (fast, no DB)`);
15853
- }
15854
- if (info.scripts["test:integration"]) {
15855
- sections.push(`- Integration tests: \`${info.packageManager} run test:integration\` (with test DB)`);
15856
- }
15857
- if (info.scripts["test:e2e"]) {
15858
- sections.push(`- E2E tests: \`${info.packageManager} run test:e2e\` (full stack)`);
15859
- }
15860
- if (info.scripts.test) {
15861
- sections.push(`- Run all: \`${info.packageManager} test\``);
15862
- }
16539
+ sections.push(`- Run \`${info.packageManager} test\` before pushing`);
16540
+ if (info.linter) {
16541
+ sections.push(`- Run \`${info.packageManager} run lint\` to check code style`);
15863
16542
  }
15864
- return sections.join("\n");
15865
- }
15866
- function generateDatabaseSchema(info) {
15867
- if (!info.databaseSchema || info.databaseSchema.models.length === 0) {
15868
- return "";
16543
+ if (info.hasTypeScript) {
16544
+ sections.push("- Use TypeScript strict mode conventions");
15869
16545
  }
15870
- const sections = ["## Database Schema", ""];
15871
- const schema = info.databaseSchema;
15872
- if (schema.orm) {
15873
- sections.push(`**ORM:** ${schema.orm}`);
15874
- sections.push("");
16546
+ sections.push("- Follow conventional commits (feat/fix/chore/docs)");
16547
+ sections.push("- Add tests for new features");
16548
+ sections.push("");
16549
+ sections.push("### DON'T");
16550
+ sections.push("");
16551
+ sections.push("- Commit directly to main/production branches");
16552
+ sections.push("- Skip tests before pushing");
16553
+ sections.push("- Expose API keys or credentials in code");
16554
+ if (info.dependencies.includes("prisma") || info.dependencies.includes("typeorm")) {
16555
+ sections.push("- Modify database migrations without approval");
15875
16556
  }
15876
- if (schema.models.length > 0) {
15877
- sections.push("### Models");
16557
+ sections.push("");
16558
+ if (info.categorizedScripts) {
16559
+ sections.push("## Key Commands");
15878
16560
  sections.push("");
15879
- for (const model of schema.models.slice(0, 10)) {
15880
- sections.push(`**${model.name}**`);
15881
- if (model.fields.length > 0) {
15882
- for (const field of model.fields) {
15883
- let fieldDesc = `- \`${field.name}\`: ${field.type}`;
15884
- const attrs = [];
15885
- if (field.isPrimaryKey) attrs.push("PK");
15886
- if (field.isUnique) attrs.push("unique");
15887
- if (field.isOptional) attrs.push("optional");
15888
- if (field.defaultValue) attrs.push(`default: ${field.defaultValue}`);
15889
- if (attrs.length > 0) {
15890
- fieldDesc += ` (${attrs.join(", ")})`;
15891
- }
15892
- sections.push(fieldDesc);
15893
- }
15894
- }
15895
- if (model.relations.length > 0) {
15896
- for (const rel of model.relations) {
15897
- sections.push(`- \`${rel.name}\`: ${rel.type} \u2192 ${rel.relatedModel}`);
15898
- }
15899
- }
15900
- sections.push("");
15901
- }
15902
- if (schema.models.length > 10) {
15903
- sections.push(`*... and ${schema.models.length - 10} more models*`);
15904
- sections.push("");
16561
+ sections.push("```bash");
16562
+ const allScripts = [
16563
+ ...info.categorizedScripts.development || [],
16564
+ ...info.categorizedScripts.testing || [],
16565
+ ...info.categorizedScripts.building || [],
16566
+ ...info.categorizedScripts.quality || []
16567
+ ].slice(0, 5);
16568
+ for (const script of allScripts) {
16569
+ sections.push(`${info.packageManager} run ${script.name} # ${script.description}`);
15905
16570
  }
16571
+ sections.push("```");
16572
+ sections.push("");
15906
16573
  }
15907
- if (schema.migrations.directory && schema.migrations.files.length > 0) {
15908
- sections.push("### Migrations");
15909
- sections.push(`- Location: \`${schema.migrations.directory}\``);
15910
- sections.push(`- Total: ${schema.migrations.files.length} migrations`);
15911
- if (info.scripts["db:migrate"] || info.scripts["migrate"] || info.scripts["prisma:migrate"]) {
15912
- const migrateCmd = info.scripts["db:migrate"] ? "db:migrate" : info.scripts["migrate"] ? "migrate" : "prisma:migrate";
15913
- sections.push(`- Run: \`${info.packageManager} run ${migrateCmd}\``);
15914
- }
16574
+ sections.push("## Troubleshooting");
16575
+ sections.push("");
16576
+ sections.push(`**Build fails**: Run \`rm -rf node_modules && ${info.packageManager} install\``);
16577
+ sections.push("");
16578
+ sections.push("**Tests fail**: Check for missing environment variables");
16579
+ sections.push("");
16580
+ if (info.hasTypeScript) {
16581
+ sections.push(`**Type errors**: Run \`${info.packageManager} run typecheck\` for details`);
15915
16582
  sections.push("");
15916
16583
  }
16584
+ sections.push("---");
16585
+ sections.push("");
16586
+ sections.push("*Generated by `ax init` \u2022 Edit this file to customize AI behavior*");
15917
16587
  return sections.join("\n");
15918
16588
  }
15919
- function generateAPIDocumentation(info) {
15920
- if (!info.apiDocumentation) {
15921
- return "";
16589
+ async function atomicWrite(filePath, content) {
16590
+ const tempPath = `${filePath}.tmp`;
16591
+ await writeFile(tempPath, content, "utf-8");
16592
+ await rename(tempPath, filePath);
16593
+ }
16594
+ function formatAge(ms) {
16595
+ const hours = Math.floor(ms / (1e3 * 60 * 60));
16596
+ if (hours < 24) return `${hours}h`;
16597
+ const days = Math.floor(hours / 24);
16598
+ return `${days}d`;
16599
+ }
16600
+ function getVersionCachePath() {
16601
+ return join(homedir(), ".automatosx", "version-cache.json");
16602
+ }
16603
+ async function readVersionCache() {
16604
+ try {
16605
+ const cachePath = getVersionCachePath();
16606
+ const content = await readFile(cachePath, "utf-8");
16607
+ return JSON.parse(content);
16608
+ } catch {
16609
+ return null;
15922
16610
  }
15923
- const sections = ["## API Documentation", ""];
15924
- const api = info.apiDocumentation;
15925
- if (api.framework) {
15926
- sections.push(`**Framework:** ${api.framework}`);
15927
- sections.push("");
16611
+ }
16612
+ async function writeVersionCache(cache) {
16613
+ const cachePath = getVersionCachePath();
16614
+ const cacheDir = join(homedir(), ".automatosx");
16615
+ await mkdir(cacheDir, { recursive: true });
16616
+ await writeFile(cachePath, JSON.stringify(cache, null, 2), "utf-8");
16617
+ }
16618
+ function isNewer(a, b) {
16619
+ const stripPrerelease = (v) => v.split("-")[0] || v;
16620
+ const parseVersion = (v) => stripPrerelease(v).split(".").map(Number);
16621
+ const [aMajor = 0, aMinor = 0, aPatch = 0] = parseVersion(a);
16622
+ const [bMajor = 0, bMinor = 0, bPatch = 0] = parseVersion(b);
16623
+ if (aMajor !== bMajor) return aMajor > bMajor;
16624
+ if (aMinor !== bMinor) return aMinor > bMinor;
16625
+ return aPatch > bPatch;
16626
+ }
16627
+ async function getCurrentVersion() {
16628
+ try {
16629
+ const { stdout } = await execAsync3("npm list -g @defai.digital/automatosx --depth=0 --json 2>/dev/null");
16630
+ const result = JSON.parse(stdout);
16631
+ const version = result.dependencies?.["@defai.digital/automatosx"]?.version;
16632
+ if (version) return version;
16633
+ } catch {
15928
16634
  }
15929
- if (api.hasOpenAPI && api.openAPIPath) {
15930
- sections.push(`**OpenAPI Spec:** \`${api.openAPIPath}\``);
15931
- sections.push("");
16635
+ try {
16636
+ const { dirname: dirname16 } = await import('path');
16637
+ const { fileURLToPath: fileURLToPath5 } = await import('url');
16638
+ const __filename3 = fileURLToPath5(import.meta.url);
16639
+ const __dirname4 = dirname16(__filename3);
16640
+ const pkgPath = join(__dirname4, "../../../package.json");
16641
+ const content = await readFile(pkgPath, "utf-8");
16642
+ const pkg = JSON.parse(content);
16643
+ return pkg.version || null;
16644
+ } catch {
16645
+ return null;
15932
16646
  }
15933
- if (api.endpoints.length > 0) {
15934
- sections.push("### Endpoints");
15935
- sections.push("");
15936
- const grouped = /* @__PURE__ */ new Map();
15937
- for (const endpoint of api.endpoints) {
15938
- const group = endpoint.group || "other";
15939
- if (!grouped.has(group)) {
15940
- grouped.set(group, []);
15941
- }
15942
- grouped.get(group)?.push(endpoint);
16647
+ }
16648
+ async function getLatestVersion() {
16649
+ try {
16650
+ const { stdout } = await execAsync3("npm view @defai.digital/automatosx version 2>/dev/null");
16651
+ return stdout.trim() || null;
16652
+ } catch {
16653
+ return null;
16654
+ }
16655
+ }
16656
+ async function updateAutomatosx(version) {
16657
+ try {
16658
+ console.log(chalk5.cyan(`\u{1F4E5} Updating AutomatosX to v${version}...`));
16659
+ await execAsync3(`npm install -g @defai.digital/automatosx@${version}`, {
16660
+ maxBuffer: 10 * 1024 * 1024
16661
+ });
16662
+ console.log(chalk5.green(`\u2713 AutomatosX updated to v${version}`));
16663
+ return true;
16664
+ } catch (error) {
16665
+ logger.warn("Failed to update AutomatosX", { error: error.message });
16666
+ console.log(chalk5.yellow(`\u26A0 Could not update AutomatosX: ${error.message}`));
16667
+ return false;
16668
+ }
16669
+ }
16670
+ async function checkAutomatosxUpdate() {
16671
+ try {
16672
+ const currentVersion = await getCurrentVersion();
16673
+ if (!currentVersion) {
16674
+ logger.debug("Could not determine current AutomatosX version");
16675
+ return false;
15943
16676
  }
15944
- for (const [group, endpoints] of grouped) {
15945
- if (group !== "other") {
15946
- sections.push(`**${group.charAt(0).toUpperCase() + group.slice(1)}**`);
15947
- }
15948
- for (const endpoint of endpoints.slice(0, 20)) {
15949
- let line = `- \`${endpoint.method} ${endpoint.path}\``;
15950
- if (endpoint.description) {
15951
- line += ` - ${endpoint.description}`;
16677
+ const cache = await readVersionCache();
16678
+ const now = Date.now();
16679
+ let latestVersion = null;
16680
+ let cacheIsValid = false;
16681
+ if (cache) {
16682
+ const lastCheckTime = new Date(cache.lastCheck).getTime();
16683
+ const age = now - lastCheckTime;
16684
+ if (age < STALE_THRESHOLD_MS && cache.currentVersion === currentVersion) {
16685
+ cacheIsValid = true;
16686
+ latestVersion = cache.latestVersion;
16687
+ logger.debug("AutomatosX version check using cache", {
16688
+ currentVersion,
16689
+ latestVersion,
16690
+ cacheAge: formatAge(age),
16691
+ updateAttempted: cache.updateAttempted
16692
+ });
16693
+ if (!isNewer(latestVersion, currentVersion)) {
16694
+ return false;
15952
16695
  }
15953
- if (endpoint.auth) {
15954
- line += " \u{1F512}";
16696
+ if (cache.updateAttempted) {
16697
+ console.log(chalk5.gray(`AutomatosX update available: ${currentVersion} \u2192 ${latestVersion} (run 'npm i -g @defai.digital/automatosx' to update)`));
16698
+ return false;
15955
16699
  }
15956
- sections.push(line);
15957
16700
  }
15958
- sections.push("");
15959
- }
15960
- if (api.endpoints.length > 20) {
15961
- sections.push(`*... and ${api.endpoints.length - 20} more endpoints*`);
15962
- sections.push("");
15963
16701
  }
15964
- }
15965
- return sections.join("\n");
15966
- }
15967
- function generateAgentRules(info) {
15968
- const rules = ["## Agent Delegation Rules", ""];
15969
- rules.push("### Development");
15970
- if (info.framework === "React" || info.framework === "Vue" || info.framework === "Next.js") {
15971
- rules.push(`- **Frontend/UI (${info.framework})** \u2192 @frontend (Frank)`);
15972
- }
15973
- if (info.dependencies.includes("express") || info.dependencies.includes("fastify") || info.dependencies.includes("koa")) {
15974
- rules.push("- **Backend/API** \u2192 @backend (Bob) or @fullstack (Felix)");
15975
- }
15976
- if (info.hasTypeScript) {
15977
- rules.push("- **TypeScript issues** \u2192 @fullstack (Felix)");
15978
- }
15979
- if (info.dependencies.some((d) => d.includes("react-native") || d.includes("expo"))) {
15980
- rules.push("- **Mobile** \u2192 @mobile (Maya)");
15981
- }
15982
- rules.push("- **Infrastructure/DevOps** \u2192 @devops (Oliver)");
15983
- rules.push("");
15984
- rules.push("### Quality & Architecture");
15985
- rules.push("- **Tests/QA** \u2192 @quality (Queenie)");
15986
- rules.push("- **Security audits** \u2192 @security (Steve) - mandatory for: auth, payments, PII");
15987
- rules.push("- **Architecture/ADR** \u2192 @architecture (Avery)");
15988
- rules.push("");
15989
- rules.push("### Documentation & Product");
15990
- rules.push("- **Technical writing** \u2192 @writer (Wendy)");
15991
- rules.push("- **Product management** \u2192 @product (Paris)");
15992
- return rules.join("\n");
15993
- }
15994
- function generateCodingConventions(info) {
15995
- const conventions = ["## Coding Conventions", ""];
15996
- conventions.push("### Testing");
15997
- if (info.testFramework) {
15998
- conventions.push(`- **Framework:** ${info.testFramework}`);
15999
- conventions.push("- **Coverage:** 80% minimum");
16000
- conventions.push(`- **Run:** \`${info.packageManager} test\``);
16001
- } else {
16002
- conventions.push("- Run tests before pushing");
16003
- }
16004
- conventions.push("");
16005
- conventions.push("### Code Style");
16006
- if (info.linter) {
16007
- conventions.push(`- **Linter:** ${info.linter}`);
16008
- }
16009
- if (info.formatter) {
16010
- conventions.push(`- **Formatter:** ${info.formatter}`);
16011
- }
16012
- if (info.hasTypeScript) {
16013
- conventions.push("- **TypeScript:** Strict mode enabled");
16014
- }
16015
- conventions.push("- **Indent:** 2 spaces");
16016
- conventions.push("- **Max line:** 100 chars");
16017
- conventions.push("");
16018
- conventions.push("### Git Workflow");
16019
- conventions.push("- **Branch naming:** `feature/description` or `fix/description`");
16020
- conventions.push("- **Commits:** Conventional commits format (feat/fix/chore/docs)");
16021
- conventions.push("- **PRs:** Review required before merge");
16022
- return conventions.join("\n");
16023
- }
16024
- function generateCriticalGuardrails(info) {
16025
- const guardrails = ["## Critical Guardrails", ""];
16026
- guardrails.push("\u26A0\uFE0F **NEVER:**");
16027
- guardrails.push("- Commit to main/production branches directly");
16028
- guardrails.push("- Skip tests before pushing");
16029
- guardrails.push("- Expose API keys or credentials in code");
16030
- if (info.dependencies.includes("prisma") || info.dependencies.includes("typeorm")) {
16031
- guardrails.push("- Modify database migrations without approval");
16032
- }
16033
- guardrails.push("");
16034
- guardrails.push("\u2705 **ALWAYS:**");
16035
- guardrails.push(`- Run \`${info.packageManager} test\` before pushing`);
16036
- if (info.linter) {
16037
- guardrails.push(`- Run \`${info.packageManager} run lint\` to check code style`);
16038
- }
16039
- if (info.formatter) {
16040
- guardrails.push(`- Format code with ${info.formatter} before committing`);
16041
- }
16042
- guardrails.push("- Document breaking changes");
16043
- guardrails.push("- Add tests for new features");
16044
- return guardrails.join("\n");
16045
- }
16046
- function generateCommands(info) {
16047
- const sections = ["## Canonical Commands", ""];
16048
- if (!info.categorizedScripts) {
16049
- return sections.join("\n") + "```bash\n# No scripts detected\n```";
16050
- }
16051
- const categories = [
16052
- { key: "development", title: "Development" },
16053
- { key: "building", title: "Building" },
16054
- { key: "testing", title: "Testing" },
16055
- { key: "quality", title: "Quality Checks" },
16056
- { key: "deployment", title: "Deployment" }
16057
- ];
16058
- sections.push("```bash");
16059
- for (const { key, title } of categories) {
16060
- const scripts = info.categorizedScripts[key];
16061
- if (scripts && scripts.length > 0) {
16062
- sections.push(`# ${title}`);
16063
- for (const script of scripts) {
16064
- const cmd = `${info.packageManager} run ${script.name}`;
16065
- const padding = " ".repeat(Math.max(35 - cmd.length, 1));
16066
- sections.push(`${cmd}${padding}# ${script.description}`);
16702
+ if (!cacheIsValid) {
16703
+ console.log(chalk5.gray("Checking for AutomatosX updates..."));
16704
+ latestVersion = await getLatestVersion();
16705
+ if (!latestVersion) {
16706
+ logger.debug("Could not fetch latest AutomatosX version");
16707
+ return false;
16708
+ }
16709
+ await writeVersionCache({
16710
+ lastCheck: (/* @__PURE__ */ new Date()).toISOString(),
16711
+ currentVersion,
16712
+ latestVersion,
16713
+ updateAttempted: false
16714
+ });
16715
+ logger.info("AutomatosX version check completed", {
16716
+ currentVersion,
16717
+ latestVersion,
16718
+ updateAvailable: isNewer(latestVersion, currentVersion)
16719
+ });
16720
+ if (!isNewer(latestVersion, currentVersion)) {
16721
+ console.log(chalk5.gray(`AutomatosX is up to date (v${currentVersion})`));
16722
+ return false;
16067
16723
  }
16068
- sections.push("");
16069
- }
16070
- }
16071
- const other = info.categorizedScripts.other;
16072
- if (other && other.length > 0) {
16073
- sections.push("# Other");
16074
- for (const script of other) {
16075
- const cmd = `${info.packageManager} run ${script.name}`;
16076
- const padding = " ".repeat(Math.max(35 - cmd.length, 1));
16077
- sections.push(`${cmd}${padding}# ${script.description}`);
16078
16724
  }
16725
+ console.log(chalk5.yellow(`\u{1F4E6} AutomatosX update available: ${currentVersion} \u2192 ${latestVersion}`));
16726
+ const updated = await updateAutomatosx(latestVersion);
16727
+ await writeVersionCache({
16728
+ lastCheck: cache?.lastCheck || (/* @__PURE__ */ new Date()).toISOString(),
16729
+ currentVersion: updated ? latestVersion : currentVersion,
16730
+ latestVersion,
16731
+ updateAttempted: true
16732
+ });
16733
+ return updated;
16734
+ } catch (error) {
16735
+ logger.debug("AutomatosX version check failed", { error: error.message });
16736
+ return false;
16079
16737
  }
16080
- sections.push("```");
16081
- return sections.join("\n");
16082
- }
16083
- function generateUsefulLinks(info) {
16084
- const links = ["## Useful Links", ""];
16085
- if (info.repository) {
16086
- links.push(`- [Repository](${info.repository})`);
16087
- }
16088
- if (info.fileStructure?.docsDirectory) {
16089
- links.push(`- [Documentation](${info.fileStructure.docsDirectory}/)`);
16090
- }
16091
- return links.join("\n");
16092
- }
16093
- async function atomicWrite(filePath, content) {
16094
- const tempPath = `${filePath}.tmp`;
16095
- await writeFile(tempPath, content, "utf-8");
16096
- await rename(tempPath, filePath);
16097
16738
  }
16098
16739
  var initCommand = {
16099
16740
  command: "init",
16100
- describe: "Initialize AX.md project context file",
16741
+ describe: "Initialize project index (ax.index.json) and custom instructions",
16101
16742
  builder: (yargs2) => {
16102
16743
  return yargs2.option("force", {
16103
16744
  alias: "f",
16104
- describe: "Regenerate AX.md from scratch",
16745
+ describe: "Regenerate all files including CUSTOM.md",
16746
+ type: "boolean",
16747
+ default: false
16748
+ }).option("skip-update", {
16749
+ describe: "Skip AutomatosX version check",
16105
16750
  type: "boolean",
16106
16751
  default: false
16107
16752
  }).example([
16108
- ["$0 init", "Create or update AX.md with current project info"],
16109
- ["$0 init --force", "Regenerate AX.md from scratch"]
16753
+ ["$0 init", "Create or update ax.index.json (auto-rebuilds if >24h old)"],
16754
+ ["$0 init --force", "Regenerate all files including CUSTOM.md"],
16755
+ ["$0 init --skip-update", "Skip AutomatosX version check"]
16110
16756
  ]);
16111
16757
  },
16112
16758
  handler: async (argv) => {
16113
16759
  const projectDir = process.cwd();
16114
- const axMdPath = join(projectDir, "AX.md");
16760
+ if (!argv.skipUpdate) {
16761
+ const wasUpdated = await checkAutomatosxUpdate();
16762
+ if (wasUpdated) {
16763
+ console.log(chalk5.cyan("\n\u{1F504} Re-running ax init --force with updated AutomatosX...\n"));
16764
+ try {
16765
+ const { stdout } = await execAsync3("automatosx init --force --skip-update");
16766
+ console.log(stdout);
16767
+ return;
16768
+ } catch (error) {
16769
+ try {
16770
+ const { stdout } = await execAsync3("ax init --force --skip-update");
16771
+ console.log(stdout);
16772
+ return;
16773
+ } catch {
16774
+ logger.warn("Could not re-run init with updated version, continuing with current process");
16775
+ argv.force = true;
16776
+ }
16777
+ }
16778
+ }
16779
+ }
16780
+ const indexPath = join(projectDir, "ax.index.json");
16781
+ const automatosxDir = join(projectDir, ".automatosx");
16782
+ const customMdPath = join(automatosxDir, "CUSTOM.md");
16115
16783
  try {
16784
+ const indexExists = await access$1(indexPath, constants.F_OK).then(() => true).catch(() => false);
16785
+ let indexAge = null;
16786
+ if (indexExists) {
16787
+ try {
16788
+ const stats = await stat(indexPath);
16789
+ indexAge = Date.now() - stats.mtime.getTime();
16790
+ } catch {
16791
+ }
16792
+ }
16793
+ const isStale = indexAge !== null && indexAge > STALE_THRESHOLD_MS;
16794
+ const shouldRebuildIndex = !indexExists || isStale || argv.force;
16795
+ if (!shouldRebuildIndex && !argv.force) {
16796
+ console.log(chalk5.green(`\u2713 ax.index.json is up to date (${indexAge ? formatAge(indexAge) : "0h"} old)`));
16797
+ console.log(chalk5.gray(" Use --force to regenerate"));
16798
+ return;
16799
+ }
16116
16800
  const projectInfo = await detectProjectInfo(projectDir);
16117
- const axMdExists = await access$1(axMdPath, constants.F_OK).then(() => true).catch(() => false);
16118
- if (axMdExists && !argv.force) {
16119
- const existingContent = await readFile(axMdPath, "utf-8");
16120
- const parsed = parseAXMD(existingContent);
16121
- const changes = detectDetailedChanges(parsed, {
16122
- version: projectInfo.version,
16123
- dependencies: projectInfo.dependencies,
16124
- scripts: projectInfo.scripts,
16125
- framework: projectInfo.framework,
16126
- architecture: detectArchitectureType(projectInfo),
16127
- linter: projectInfo.linter,
16128
- formatter: projectInfo.formatter,
16129
- testFramework: projectInfo.testFramework
16130
- });
16131
- if (changes.length === 0) {
16132
- console.log(chalk5.green("\u2713 AX.md is up to date"));
16133
- logger.info("AX.md unchanged", { projectName: projectInfo.name });
16134
- return;
16801
+ let indexContent;
16802
+ if (indexExists && !argv.force) {
16803
+ try {
16804
+ const existingIndex = JSON.parse(await readFile(indexPath, "utf-8"));
16805
+ indexContent = {
16806
+ ...generateAxIndex(projectInfo),
16807
+ createdAt: existingIndex.createdAt || (/* @__PURE__ */ new Date()).toISOString()
16808
+ };
16809
+ } catch {
16810
+ indexContent = generateAxIndex(projectInfo);
16135
16811
  }
16136
- const axMdContent = generateAXMD(projectInfo);
16137
- await atomicWrite(axMdPath, axMdContent);
16138
- const summary = formatChangeSummary(changes);
16139
- console.log(chalk5.green(`\u2713 Updated AX.md ${summary}`));
16140
- logger.info("AX.md updated", {
16141
- projectName: projectInfo.name,
16142
- changes: changes.length,
16143
- changeTypes: changes.map((c) => c.type)
16144
- });
16145
16812
  } else {
16146
- const axMdContent = generateAXMD(projectInfo);
16147
- await atomicWrite(axMdPath, axMdContent);
16813
+ indexContent = generateAxIndex(projectInfo);
16814
+ }
16815
+ await atomicWrite(indexPath, JSON.stringify(indexContent, null, 2));
16816
+ if (isStale) {
16817
+ console.log(chalk5.green(`\u2713 Rebuilt ax.index.json (was ${formatAge(indexAge)} old)`));
16818
+ } else if (indexExists) {
16819
+ console.log(chalk5.green("\u2713 Updated ax.index.json"));
16820
+ } else {
16148
16821
  const projectType = projectInfo.framework || projectInfo.language;
16149
- console.log(chalk5.green(`\u2713 Created AX.md (${projectType} project)`));
16150
- logger.info("AX.md created", {
16151
- projectName: projectInfo.name,
16152
- framework: projectInfo.framework,
16153
- language: projectInfo.language,
16154
- forced: argv.force
16155
- });
16822
+ console.log(chalk5.green(`\u2713 Created ax.index.json (${projectType} project)`));
16823
+ }
16824
+ await mkdir(automatosxDir, { recursive: true });
16825
+ const customMdExists = await access$1(customMdPath, constants.F_OK).then(() => true).catch(() => false);
16826
+ if (!customMdExists || argv.force) {
16827
+ const customMdContent = generateCustomMD(projectInfo);
16828
+ await atomicWrite(customMdPath, customMdContent);
16829
+ if (argv.force && customMdExists) {
16830
+ console.log(chalk5.yellow("\u2713 Regenerated .automatosx/CUSTOM.md (--force)"));
16831
+ } else {
16832
+ console.log(chalk5.green("\u2713 Created .automatosx/CUSTOM.md"));
16833
+ }
16834
+ } else {
16835
+ console.log(chalk5.gray(" .automatosx/CUSTOM.md preserved (use --force to regenerate)"));
16156
16836
  }
16837
+ console.log("");
16838
+ console.log(chalk5.cyan("Project initialized:"));
16839
+ console.log(chalk5.gray(" \u2022 ax.index.json - Shared project index (auto-rebuilds after 24h)"));
16840
+ console.log(chalk5.gray(" \u2022 CUSTOM.md - Custom AI instructions (edit to customize)"));
16841
+ console.log("");
16842
+ console.log(chalk5.gray("Agents will use ax.index.json to understand your project."));
16843
+ logger.info("Project initialized", {
16844
+ projectName: projectInfo.name,
16845
+ indexRebuilt: shouldRebuildIndex,
16846
+ wasStale: isStale,
16847
+ force: argv.force
16848
+ });
16157
16849
  } catch (error) {
16158
- console.log(chalk5.red("\u2717 Error with AX.md"));
16159
- logger.error("AX.md initialization failed", { error });
16850
+ console.log(chalk5.red("\u2717 Error initializing project"));
16851
+ logger.error("Project initialization failed", { error });
16160
16852
  if (process.env.DEBUG || process.env.AUTOMATOSX_DEBUG) {
16161
16853
  printError(error);
16162
16854
  }
@@ -16677,107 +17369,7 @@ init_logger();
16677
17369
  // src/shared/utils/disposable.ts
16678
17370
  init_esm_shims();
16679
17371
  init_logger();
16680
-
16681
- // src/shared/utils/safe-timers.ts
16682
- init_esm_shims();
16683
- init_logger();
16684
- function createSafeInterval(callback, intervalMs, options = {}) {
16685
- const { ref = false, name, signal, immediate = false } = options;
16686
- if (signal?.aborted) {
16687
- logger.debug("createSafeInterval: already aborted", { name });
16688
- return () => {
16689
- };
16690
- }
16691
- let cleared = false;
16692
- let intervalId = null;
16693
- const safeCallback = async () => {
16694
- if (cleared) return;
16695
- try {
16696
- await callback();
16697
- } catch (error) {
16698
- logger.error("Safe interval callback error", {
16699
- name,
16700
- error: error.message
16701
- });
16702
- }
16703
- };
16704
- if (immediate) {
16705
- setImmediate(() => {
16706
- if (!cleared) {
16707
- safeCallback();
16708
- }
16709
- });
16710
- }
16711
- intervalId = setInterval(safeCallback, intervalMs);
16712
- if (!ref && intervalId.unref) {
16713
- intervalId.unref();
16714
- }
16715
- const cleanup = () => {
16716
- if (cleared) return;
16717
- cleared = true;
16718
- if (intervalId !== null) {
16719
- clearInterval(intervalId);
16720
- intervalId = null;
16721
- logger.debug("Safe interval cleared", { name });
16722
- }
16723
- };
16724
- if (signal) {
16725
- signal.addEventListener("abort", cleanup, { once: true });
16726
- }
16727
- logger.debug("Safe interval created", {
16728
- name,
16729
- intervalMs,
16730
- ref,
16731
- immediate
16732
- });
16733
- return cleanup;
16734
- }
16735
- function createSafeTimeout(callback, delayMs, options = {}) {
16736
- const { ref = false, name, signal } = options;
16737
- if (signal?.aborted) {
16738
- logger.debug("createSafeTimeout: already aborted", { name });
16739
- return () => {
16740
- };
16741
- }
16742
- let cleared = false;
16743
- let timeoutId = null;
16744
- const safeCallback = async () => {
16745
- if (cleared) return;
16746
- cleared = true;
16747
- try {
16748
- await callback();
16749
- } catch (error) {
16750
- logger.error("Safe timeout callback error", {
16751
- name,
16752
- error: error.message
16753
- });
16754
- }
16755
- };
16756
- timeoutId = setTimeout(safeCallback, delayMs);
16757
- if (!ref && timeoutId.unref) {
16758
- timeoutId.unref();
16759
- }
16760
- const cleanup = () => {
16761
- if (cleared) return;
16762
- cleared = true;
16763
- if (timeoutId !== null) {
16764
- clearTimeout(timeoutId);
16765
- timeoutId = null;
16766
- logger.debug("Safe timeout cleared", { name });
16767
- }
16768
- };
16769
- if (signal) {
16770
- signal.addEventListener("abort", cleanup, { once: true });
16771
- }
16772
- logger.debug("Safe timeout created", {
16773
- name,
16774
- delayMs,
16775
- ref
16776
- });
16777
- return cleanup;
16778
- }
16779
-
16780
- // src/shared/utils/disposable.ts
17372
+ init_safe_timers();
16781
17373
  var DisposableEventEmitter = class extends EventEmitter {
16782
17374
  /** Registered cleanup tasks */
16783
17375
  cleanupTasks = [];
@@ -19968,7 +20560,7 @@ var MemoryManager = class _MemoryManager {
19968
20560
  if (this.db) {
19969
20561
  try {
19970
20562
  this.db.close();
19971
- } catch (closeError) {
20563
+ } catch {
19972
20564
  }
19973
20565
  this.initialized = false;
19974
20566
  this.entryCount = 0;
@@ -20722,9 +21314,9 @@ var MemoryManager = class _MemoryManager {
20722
21314
  throw new MemoryError("Memory manager not initialized", "DATABASE_ERROR");
20723
21315
  }
20724
21316
  try {
20725
- const { mkdir: mkdir14 } = await import('fs/promises');
21317
+ const { mkdir: mkdir15 } = await import('fs/promises');
20726
21318
  const destDir = dirname4(destPath);
20727
- await mkdir14(destDir, { recursive: true });
21319
+ await mkdir15(destDir, { recursive: true });
20728
21320
  await this.db.backup(destPath);
20729
21321
  logger.info("Database backup created", { destPath: normalizePath(destPath) });
20730
21322
  } catch (error) {
@@ -20809,9 +21401,9 @@ var MemoryManager = class _MemoryManager {
20809
21401
  }
20810
21402
  }
20811
21403
  const {
20812
- includeEmbeddings = false,
21404
+ includeEmbeddings: _includeEmbeddings = false,
20813
21405
  filters = {},
20814
- batchSize = 1e3,
21406
+ batchSize: _batchSize = 1e3,
20815
21407
  pretty = false
20816
21408
  } = options || {};
20817
21409
  try {
@@ -21969,7 +22561,7 @@ var SessionManager = class _SessionManager {
21969
22561
  if (this.pendingSave) {
21970
22562
  try {
21971
22563
  await this.pendingSave;
21972
- } catch (err) {
22564
+ } catch {
21973
22565
  }
21974
22566
  }
21975
22567
  this.pendingSave = this.doSave().finally(() => {
@@ -22037,7 +22629,7 @@ var SessionManager = class _SessionManager {
22037
22629
  }
22038
22630
  try {
22039
22631
  await unlink(tempPath);
22040
- } catch (unlinkError) {
22632
+ } catch {
22041
22633
  }
22042
22634
  throw renameError;
22043
22635
  }
@@ -22045,7 +22637,7 @@ var SessionManager = class _SessionManager {
22045
22637
  const tempPath = `${this.persistencePath}.tmp`;
22046
22638
  try {
22047
22639
  await unlink(tempPath);
22048
- } catch (unlinkError) {
22640
+ } catch {
22049
22641
  }
22050
22642
  logger.error("Failed to save sessions to persistence", {
22051
22643
  path: normalizePath(this.persistencePath),
@@ -22584,6 +23176,7 @@ init_esm_shims();
22584
23176
  init_logger();
22585
23177
  var MAX_CONTEXT_SIZE = 100 * 1024;
22586
23178
  var DEFAULT_CACHE_TTL = 3e5;
23179
+ var STALE_THRESHOLD_MS2 = 24 * 60 * 60 * 1e3;
22587
23180
  var ProjectContextLoader = class {
22588
23181
  constructor(projectRoot, options) {
22589
23182
  this.projectRoot = projectRoot;
@@ -22607,76 +23200,79 @@ var ProjectContextLoader = class {
22607
23200
  });
22608
23201
  const context = {};
22609
23202
  try {
22610
- const mdPath = path4__default.join(this.projectRoot, "AX.md");
22611
- const resolvedPath = await realpath$1(mdPath).catch(() => null);
23203
+ const indexPath = path4__default.join(this.projectRoot, "ax.index.json");
23204
+ const resolvedPath = await realpath$1(indexPath).catch(() => null);
22612
23205
  if (resolvedPath) {
22613
23206
  const rel = path4__default.relative(this.projectRoot, resolvedPath);
22614
23207
  if (!rel.startsWith("..") && !path4__default.isAbsolute(rel)) {
22615
23208
  const stats = await stat(resolvedPath);
22616
23209
  if (stats.size > MAX_CONTEXT_SIZE) {
22617
- logger.warn("AX.md too large, ignoring", {
23210
+ logger.warn("ax.index.json too large, ignoring", {
22618
23211
  size: stats.size,
22619
23212
  limit: MAX_CONTEXT_SIZE
22620
23213
  });
22621
23214
  } else {
22622
- context.markdown = await readFile(resolvedPath, "utf-8");
22623
- logger.info("Loaded AX.md", {
22624
- size: stats.size,
22625
- lines: context.markdown.split("\n").length
23215
+ const indexContent = await readFile(resolvedPath, "utf-8");
23216
+ context.index = JSON.parse(indexContent);
23217
+ context.lastUpdated = stats.mtime;
23218
+ const age = Date.now() - stats.mtime.getTime();
23219
+ context.isStale = age > STALE_THRESHOLD_MS2;
23220
+ logger.info("Loaded ax.index.json", {
23221
+ projectName: context.index.projectName,
23222
+ projectType: context.index.projectType,
23223
+ isStale: context.isStale,
23224
+ ageHours: Math.floor(age / (1e3 * 60 * 60))
22626
23225
  });
22627
- context.agentRules = this.parseAgentRules(context.markdown);
22628
- context.guardrails = this.parseGuardrails(context.markdown);
22629
- context.commands = this.parseCommands(context.markdown);
22630
- context.metadata = this.parseMetadata(context.markdown);
23226
+ if (context.index.commands) {
23227
+ context.commands = {};
23228
+ for (const [name, cmd] of Object.entries(context.index.commands)) {
23229
+ context.commands[name] = cmd.script;
23230
+ }
23231
+ }
22631
23232
  }
22632
23233
  }
22633
23234
  }
22634
23235
  } catch (error) {
22635
23236
  if (error && typeof error === "object" && "code" in error && error.code !== "ENOENT") {
22636
- logger.warn("Error loading AX.md", { error });
23237
+ logger.warn("Error loading ax.index.json", { error });
22637
23238
  }
22638
23239
  }
22639
23240
  try {
22640
- const ymlPath = path4__default.join(this.projectRoot, "ax.config.yml");
22641
- const resolvedPath = await realpath$1(ymlPath).catch(() => null);
23241
+ const customMdPath = path4__default.join(this.projectRoot, ".automatosx", "CUSTOM.md");
23242
+ const resolvedPath = await realpath$1(customMdPath).catch(() => null);
22642
23243
  if (resolvedPath) {
22643
23244
  const rel = path4__default.relative(this.projectRoot, resolvedPath);
22644
23245
  if (!rel.startsWith("..") && !path4__default.isAbsolute(rel)) {
22645
23246
  const stats = await stat(resolvedPath);
22646
23247
  if (stats.size > MAX_CONTEXT_SIZE) {
22647
- logger.warn("ax.config.yml too large, ignoring", {
23248
+ logger.warn("CUSTOM.md too large, ignoring", {
22648
23249
  size: stats.size,
22649
23250
  limit: MAX_CONTEXT_SIZE
22650
23251
  });
22651
23252
  } else {
22652
- const ymlContent = await readFile(resolvedPath, "utf-8");
22653
- context.config = yaml.parse(ymlContent);
22654
- logger.info("Loaded ax.config.yml", {
22655
- size: stats.size
23253
+ context.customInstructions = await readFile(resolvedPath, "utf-8");
23254
+ logger.info("Loaded CUSTOM.md", {
23255
+ size: stats.size,
23256
+ lines: context.customInstructions.split("\n").length
22656
23257
  });
22657
- if (context.config.commands) {
22658
- context.commands = { ...context.commands, ...context.config.commands };
22659
- }
22660
- if (context.config.project) {
22661
- context.metadata = { ...context.metadata, ...context.config.project };
22662
- }
23258
+ context.guardrails = this.parseGuardrails(context.customInstructions);
22663
23259
  }
22664
23260
  }
22665
23261
  }
22666
23262
  } catch (error) {
22667
23263
  if (error && typeof error === "object" && "code" in error && error.code !== "ENOENT") {
22668
- logger.warn("Error loading ax.config.yml", { error });
23264
+ logger.warn("Error loading CUSTOM.md", { error });
22669
23265
  }
22670
23266
  }
22671
23267
  context.contextPrompt = this.buildContextPrompt(context);
22672
23268
  this.cache = context;
22673
23269
  this.cacheExpiry = Date.now() + this.cacheTTL;
22674
23270
  logger.info("Project context loaded", {
22675
- hasMarkdown: !!context.markdown,
22676
- hasConfig: !!context.config,
22677
- agentRules: context.agentRules?.length ?? 0,
23271
+ hasIndex: !!context.index,
23272
+ hasCustomInstructions: !!context.customInstructions,
22678
23273
  guardrails: context.guardrails?.length ?? 0,
22679
- commands: Object.keys(context.commands ?? {}).length
23274
+ commands: Object.keys(context.commands ?? {}).length,
23275
+ isStale: context.isStale
22680
23276
  });
22681
23277
  return context;
22682
23278
  }
@@ -22692,8 +23288,8 @@ var ProjectContextLoader = class {
22692
23288
  * Check if context exists (without loading)
22693
23289
  */
22694
23290
  async exists() {
22695
- const mdPath = path4__default.join(this.projectRoot, "AX.md");
22696
- const ymlPath = path4__default.join(this.projectRoot, "ax.config.yml");
23291
+ const indexPath = path4__default.join(this.projectRoot, "ax.index.json");
23292
+ const customMdPath = path4__default.join(this.projectRoot, ".automatosx", "CUSTOM.md");
22697
23293
  const checkExists3 = async (filePath) => {
22698
23294
  try {
22699
23295
  await access$1(filePath, constants.F_OK);
@@ -22702,57 +23298,48 @@ var ProjectContextLoader = class {
22702
23298
  return false;
22703
23299
  }
22704
23300
  };
22705
- const [mdExists, ymlExists] = await Promise.all([
22706
- checkExists3(mdPath),
22707
- checkExists3(ymlPath)
23301
+ const [indexExists, customMdExists] = await Promise.all([
23302
+ checkExists3(indexPath),
23303
+ checkExists3(customMdPath)
22708
23304
  ]);
22709
- return mdExists || ymlExists;
23305
+ return indexExists || customMdExists;
22710
23306
  }
22711
23307
  /**
22712
- * Parse agent delegation rules from markdown
22713
- *
22714
- * Looks for patterns like:
22715
- * - Backend changes → @backend
22716
- * - API endpoints → @backend, @security
23308
+ * Check if ax.index.json is stale (older than 24 hours)
22717
23309
  */
22718
- parseAgentRules(markdown) {
22719
- const rules = [];
22720
- const sectionRegex = /##\s+Agent\s+Delegation\s+Rules[^\n]*\n([\s\S]*?)(?=\n##|$)/i;
22721
- const match = markdown.match(sectionRegex);
22722
- if (!match || !match[1]) {
22723
- return rules;
22724
- }
22725
- const section = match[1];
22726
- const lineRegex = /^[-*]\s+(.+?)\s+(?:→|->)+\s+(.+?)$/gm;
22727
- let lineMatch;
22728
- while ((lineMatch = lineRegex.exec(section)) !== null) {
22729
- const taskType = lineMatch[1]?.trim() ?? "";
22730
- const agentsText = lineMatch[2]?.trim() ?? "";
22731
- const agents = agentsText.split(",").map((a) => a.trim()).filter(Boolean);
22732
- const defaultAgent = agents[0];
22733
- if (defaultAgent) {
22734
- rules.push({
22735
- taskType,
22736
- patterns: [],
22737
- // TODO: Parse file patterns if specified
22738
- defaultAgent,
22739
- autoReview: agents.length > 1
22740
- });
22741
- }
23310
+ async isStale() {
23311
+ try {
23312
+ const indexPath = path4__default.join(this.projectRoot, "ax.index.json");
23313
+ const stats = await stat(indexPath);
23314
+ const age = Date.now() - stats.mtime.getTime();
23315
+ return age > STALE_THRESHOLD_MS2;
23316
+ } catch {
23317
+ return true;
22742
23318
  }
22743
- return rules;
22744
23319
  }
22745
23320
  /**
22746
- * Parse guardrails/prohibitions from markdown
23321
+ * Parse guardrails from CUSTOM.md
22747
23322
  *
22748
- * Looks for "## Critical Guardrails" or "## Critical Rules" section
22749
- * Extracts items marked with ⚠️ or under NEVER headings
23323
+ * Looks for DO/DON'T sections in ax-cli format
22750
23324
  */
22751
23325
  parseGuardrails(markdown) {
22752
23326
  const guardrails = [];
22753
- const sectionRegex = /##\s+(Critical\s+Guardrails|Critical\s+Rules|Never)[^\n]*\n([\s\S]*?)(?=\n##|$)/gi;
23327
+ const dontRegex = /###\s+DON'T[^\n]*\n([\s\S]*?)(?=\n###|$)/i;
23328
+ const dontMatch = markdown.match(dontRegex);
23329
+ if (dontMatch && dontMatch[1]) {
23330
+ const section = dontMatch[1];
23331
+ const bulletRegex = /^[-*]\s+(.+?)$/gm;
23332
+ let bulletMatch;
23333
+ while ((bulletMatch = bulletRegex.exec(section)) !== null) {
23334
+ const rule = bulletMatch[1]?.trim();
23335
+ if (rule) {
23336
+ guardrails.push(rule);
23337
+ }
23338
+ }
23339
+ }
23340
+ const criticalRegex = /##\s+(Critical\s+Guardrails|Critical\s+Rules|Never)[^\n]*\n([\s\S]*?)(?=\n##|$)/gi;
22754
23341
  let match;
22755
- while ((match = sectionRegex.exec(markdown)) !== null) {
23342
+ while ((match = criticalRegex.exec(markdown)) !== null) {
22756
23343
  const section = match[2];
22757
23344
  if (!section) continue;
22758
23345
  const bulletRegex = /^[-*]\s+(.+?)$/gm;
@@ -22762,106 +23349,58 @@ var ProjectContextLoader = class {
22762
23349
  rule = rule.replace(/^[⚠️❌✅✓⚡🔒]+\s*/, "");
22763
23350
  rule = rule.replace(/\*\*(.+?)\*\*/g, "$1");
22764
23351
  rule = rule.replace(/`(.+?)`/g, "$1");
22765
- if (rule.length > 0) {
23352
+ if (rule.length > 0 && !guardrails.includes(rule)) {
22766
23353
  guardrails.push(rule);
22767
23354
  }
22768
23355
  }
22769
23356
  }
22770
23357
  return guardrails;
22771
23358
  }
22772
- /**
22773
- * Parse commands from markdown code blocks
22774
- *
22775
- * Looks for "## Commands" or "## Canonical Commands" section
22776
- */
22777
- parseCommands(markdown) {
22778
- const commands = {};
22779
- const sectionRegex = /##\s+(Canonical\s+)?Commands[^\n]*\n([\s\S]*?)(?=\n##|$)/i;
22780
- const match = markdown.match(sectionRegex);
22781
- if (!match) {
22782
- return commands;
22783
- }
22784
- const section = match[2];
22785
- if (!section) {
22786
- return commands;
22787
- }
22788
- const codeBlockRegex = /```(?:bash|sh|shell)?\n([\s\S]*?)```/;
22789
- const codeMatch = section.match(codeBlockRegex);
22790
- if (codeMatch && codeMatch[1]) {
22791
- const lines = codeMatch[1].split("\n");
22792
- for (const line of lines) {
22793
- const cmdMatch = line.match(/^([^\s#]+(?:\s+[^\s#]+)*)\s*#?\s*(.*)$/);
22794
- if (cmdMatch && cmdMatch[1]) {
22795
- const cmd = cmdMatch[1].trim();
22796
- const desc = cmdMatch[2]?.trim() ?? "";
22797
- const key = desc || cmd;
22798
- commands[key] = cmd;
22799
- }
22800
- }
22801
- }
22802
- return commands;
22803
- }
22804
- /**
22805
- * Parse project metadata from markdown frontmatter or first section
22806
- */
22807
- parseMetadata(markdown) {
22808
- const metadata = {};
22809
- const lastUpdatedMatch = markdown.match(/>\s*Last\s+updated:\s*(.+?)$/im);
22810
- if (lastUpdatedMatch && lastUpdatedMatch[1]) {
22811
- metadata.lastUpdated = lastUpdatedMatch[1].trim();
22812
- }
22813
- const projectMatch = markdown.match(/>\s*Project:\s*(.+?)$/im);
22814
- if (projectMatch && projectMatch[1]) {
22815
- const parts = projectMatch[1].trim().split(/\s+v/);
22816
- metadata.name = parts[0] || "";
22817
- if (parts.length > 1 && parts[1]) {
22818
- metadata.version = parts[1];
22819
- }
22820
- }
22821
- const h1Match = markdown.match(/^#\s+(.+?)$/m);
22822
- if (h1Match && h1Match[1] && !metadata.name) {
22823
- metadata.name = h1Match[1].replace(/Project Context for AutomatosX/i, "").trim();
22824
- }
22825
- return metadata;
22826
- }
22827
23359
  /**
22828
23360
  * Build formatted context prompt for agent injection
22829
23361
  */
22830
23362
  buildContextPrompt(context) {
22831
- if (!context.markdown && !context.config) {
23363
+ if (!context.index && !context.customInstructions) {
22832
23364
  return "";
22833
23365
  }
22834
23366
  let prompt = "\n# PROJECT CONTEXT\n\n";
22835
- if (context.guardrails && context.guardrails.length > 0) {
22836
- prompt += "## CRITICAL RULES (NEVER VIOLATE):\n\n";
22837
- context.guardrails.forEach((rule) => {
22838
- prompt += `- ${rule}
23367
+ if (context.index) {
23368
+ prompt += `**Project:** ${context.index.projectName} v${context.index.version}
22839
23369
  `;
22840
- });
22841
- prompt += "\n";
22842
- }
22843
- if (context.agentRules && context.agentRules.length > 0) {
22844
- prompt += "## Agent Delegation Rules:\n\n";
22845
- context.agentRules.forEach((rule) => {
22846
- prompt += `- ${rule.taskType} \u2192 ${rule.defaultAgent}
23370
+ prompt += `**Type:** ${context.index.projectType}
22847
23371
  `;
22848
- });
22849
- prompt += "\n";
23372
+ prompt += `**Language:** ${context.index.language}`;
23373
+ if (context.index.framework) {
23374
+ prompt += ` + ${context.index.framework}`;
23375
+ }
23376
+ prompt += "\n\n";
23377
+ if (context.index.modules.length > 0) {
23378
+ prompt += "## Project Structure:\n\n";
23379
+ for (const mod of context.index.modules.slice(0, 10)) {
23380
+ prompt += `- \`${mod.path}/\` - ${mod.purpose}
23381
+ `;
23382
+ }
23383
+ prompt += "\n";
23384
+ }
23385
+ if (context.commands && Object.keys(context.commands).length > 0) {
23386
+ prompt += "## Available Commands:\n\n";
23387
+ for (const [name, script] of Object.entries(context.commands).slice(0, 10)) {
23388
+ prompt += `- ${name}: \`${script}\`
23389
+ `;
23390
+ }
23391
+ prompt += "\n";
23392
+ }
22850
23393
  }
22851
- if (context.commands && Object.keys(context.commands).length > 0) {
22852
- prompt += "## Available Commands:\n\n";
22853
- Object.entries(context.commands).forEach(([desc, cmd]) => {
22854
- prompt += `- ${desc}: \`${cmd}\`
23394
+ if (context.guardrails && context.guardrails.length > 0) {
23395
+ prompt += "## CRITICAL RULES (NEVER VIOLATE):\n\n";
23396
+ for (const rule of context.guardrails) {
23397
+ prompt += `- ${rule}
22855
23398
  `;
22856
- });
23399
+ }
22857
23400
  prompt += "\n";
22858
23401
  }
22859
- if (context.markdown) {
22860
- const MAX_PROMPT_LENGTH = 1e4;
22861
- const contextToAdd = context.markdown.length > MAX_PROMPT_LENGTH ? context.markdown.substring(0, MAX_PROMPT_LENGTH) + "\n\n[... context truncated for length ...]" : context.markdown;
22862
- prompt += "## Full Project Context:\n\n";
22863
- prompt += contextToAdd;
22864
- prompt += "\n";
23402
+ if (context.isStale) {
23403
+ prompt += "\u26A0\uFE0F **Note:** Project index is stale (>24h old). Run `ax init` to update.\n\n";
22865
23404
  }
22866
23405
  return prompt;
22867
23406
  }
@@ -23285,10 +23824,10 @@ var ContextManager = class {
23285
23824
  return provider;
23286
23825
  }
23287
23826
  /**
23288
- * Load project context from AX.md (v7.1.0+)
23827
+ * Load project context from ax.index.json (v12.9.0+)
23289
23828
  *
23290
- * Loads project-specific instructions from AX.md file if it exists.
23291
- * Falls back gracefully if file doesn't exist.
23829
+ * Loads project-specific context from ax.index.json and CUSTOM.md.
23830
+ * Falls back gracefully if files don't exist.
23292
23831
  *
23293
23832
  * @param projectDir - Project root directory
23294
23833
  * @returns Project context or null if not found
@@ -23301,15 +23840,15 @@ var ContextManager = class {
23301
23840
  const loader = this.projectContextLoader;
23302
23841
  const exists = await loader.exists();
23303
23842
  if (!exists) {
23304
- logger.debug("No AX.md found, skipping project context");
23843
+ logger.debug("No ax.index.json or CUSTOM.md found, skipping project context");
23305
23844
  return null;
23306
23845
  }
23307
23846
  const context = await loader.load();
23308
- logger.debug("Project context loaded from AX.md", {
23309
- hasMarkdown: !!context.markdown,
23310
- hasConfig: !!context.config,
23311
- agentRules: context.agentRules?.length ?? 0,
23312
- guardrails: context.guardrails?.length ?? 0
23847
+ logger.debug("Project context loaded", {
23848
+ hasIndex: !!context.index,
23849
+ hasCustomInstructions: !!context.customInstructions,
23850
+ guardrails: context.guardrails?.length ?? 0,
23851
+ isStale: context.isStale
23313
23852
  });
23314
23853
  return context;
23315
23854
  } catch (error) {
@@ -26239,6 +26778,24 @@ ${context.task}`;
26239
26778
  // src/agents/agent-selector.ts
26240
26779
  init_esm_shims();
26241
26780
  init_logger();
26781
+ var regexCache = /* @__PURE__ */ new Map();
26782
+ function getCachedRegex(pattern) {
26783
+ if (regexCache.has(pattern)) {
26784
+ return regexCache.get(pattern) ?? null;
26785
+ }
26786
+ try {
26787
+ const regex = new RegExp(pattern, "i");
26788
+ regexCache.set(pattern, regex);
26789
+ return regex;
26790
+ } catch (error) {
26791
+ logger.debug("Invalid regex pattern cached as null", {
26792
+ pattern,
26793
+ error: error instanceof Error ? error.message : String(error)
26794
+ });
26795
+ regexCache.set(pattern, null);
26796
+ return null;
26797
+ }
26798
+ }
26242
26799
  function scoreAgent(task, profile) {
26243
26800
  let score = 0;
26244
26801
  const taskLower = task.toLowerCase();
@@ -26276,16 +26833,9 @@ function scoreAgent(task, profile) {
26276
26833
  }
26277
26834
  if (profile.selectionMetadata?.redirectWhen) {
26278
26835
  for (const rule of profile.selectionMetadata.redirectWhen) {
26279
- try {
26280
- const regex = new RegExp(rule.phrase, "i");
26281
- if (regex.test(task)) {
26282
- score -= 15;
26283
- }
26284
- } catch (error) {
26285
- logger.debug("Invalid regex pattern in redirectWhen rule", {
26286
- pattern: rule.phrase,
26287
- error: error instanceof Error ? error.message : String(error)
26288
- });
26836
+ const regex = getCachedRegex(rule.phrase);
26837
+ if (regex && regex.test(task)) {
26838
+ score -= 15;
26289
26839
  }
26290
26840
  }
26291
26841
  }
@@ -28643,7 +29193,7 @@ var BugDetector = class {
28643
29193
  });
28644
29194
  let files;
28645
29195
  if (fileFilter && fileFilter.length > 0) {
28646
- files = fileFilter.map((f) => f.startsWith("/") ? f : join(rootDir, f)).filter((f) => {
29196
+ files = fileFilter.map((f) => isAbsolute(f) ? f : join(rootDir, f)).filter((f) => {
28647
29197
  const ext = extname$1(f);
28648
29198
  return [".ts", ".js", ".mts", ".mjs", ".tsx", ".jsx"].includes(ext);
28649
29199
  }).filter((f) => !this.isExcluded(relative(rootDir, f)));
@@ -28875,8 +29425,8 @@ var BugDetector = class {
28875
29425
  async loadRulesFromFile(filePath) {
28876
29426
  try {
28877
29427
  const content = await readFile(filePath, "utf-8");
28878
- const yaml5 = await import('js-yaml');
28879
- const parsed = yaml5.load(content);
29428
+ const yaml4 = await import('js-yaml');
29429
+ const parsed = yaml4.load(content);
28880
29430
  if (parsed.rules && Array.isArray(parsed.rules)) {
28881
29431
  for (const rule of parsed.rules) {
28882
29432
  this.addRule(rule);
@@ -29308,7 +29858,6 @@ var BugFixer = class {
29308
29858
  }
29309
29859
  const directSetIntervalPattern = /setInterval\s*\(/;
29310
29860
  if (directSetIntervalPattern.test(line)) {
29311
- line.match(/^(\s*)/)?.[1] || "";
29312
29861
  const newLine = line.replace(
29313
29862
  /(setInterval\s*\([^)]+\))/,
29314
29863
  "const _interval = $1; if (_interval.unref) _interval.unref()"
@@ -29335,7 +29884,6 @@ var BugFixer = class {
29335
29884
  const match = currentLine.match(classPattern);
29336
29885
  if (match && match[1]) {
29337
29886
  classStartLine = i;
29338
- match[1];
29339
29887
  break;
29340
29888
  }
29341
29889
  }
@@ -29387,7 +29935,7 @@ var BugFixer = class {
29387
29935
  /**
29388
29936
  * Apply use DisposableEventEmitter fix
29389
29937
  */
29390
- applyUseDisposableEventEmitterFix(finding, originalContent, lines) {
29938
+ applyUseDisposableEventEmitterFix(finding, originalContent, _lines) {
29391
29939
  let fixedContent = originalContent.replace(
29392
29940
  /extends\s+EventEmitter\b/g,
29393
29941
  "extends DisposableEventEmitter"
@@ -30309,7 +30857,7 @@ var DUPLICATION_RULES = [
30309
30857
  suggestion: "Extract to a named constant"
30310
30858
  }
30311
30859
  ];
30312
- function detectDuplication(filePath, content, lines, ignoreState, config) {
30860
+ function detectDuplication(filePath, content, lines, ignoreState, _config) {
30313
30861
  const findings = [];
30314
30862
  findings.push(...detectDuplicateBlocks(filePath, content, lines, ignoreState));
30315
30863
  findings.push(...detectRepeatedConditionals(filePath, content, lines, ignoreState));
@@ -30336,7 +30884,7 @@ function detectDuplicateBlocks(filePath, content, lines, ignoreState) {
30336
30884
  }
30337
30885
  blockHashes.get(blockHash).push({ start: i + 1, end: i + MIN_BLOCK_SIZE });
30338
30886
  }
30339
- for (const [hash, locations] of blockHashes) {
30887
+ for (const [_hash, locations] of blockHashes) {
30340
30888
  if (locations.length > 1) {
30341
30889
  for (let i = 1; i < locations.length; i++) {
30342
30890
  const loc = locations[i];
@@ -30385,7 +30933,7 @@ function detectRepeatedConditionals(filePath, content, lines, ignoreState) {
30385
30933
  }
30386
30934
  conditionPattern.lastIndex = 0;
30387
30935
  }
30388
- for (const [condition, lineNums] of conditionalCounts) {
30936
+ for (const [_condition, lineNums] of conditionalCounts) {
30389
30937
  if (lineNums.length >= 2) {
30390
30938
  const firstLine = lineNums[0];
30391
30939
  const secondLine = lineNums[1];
@@ -30548,7 +31096,7 @@ var THRESHOLDS = {
30548
31096
  maxCyclomaticComplexity: 10,
30549
31097
  maxChainedCalls: 4
30550
31098
  };
30551
- function detectReadability(filePath, content, lines, ignoreState, config) {
31099
+ function detectReadability(filePath, content, lines, ignoreState, _config) {
30552
31100
  const findings = [];
30553
31101
  findings.push(...detectLongFunctions(filePath, content, lines, ignoreState));
30554
31102
  findings.push(...detectDeepNesting(filePath, content, lines, ignoreState));
@@ -30924,7 +31472,7 @@ var PERFORMANCE_RULES = [
30924
31472
  suggestion: "Remove await - async functions automatically wrap return values in promises"
30925
31473
  }
30926
31474
  ];
30927
- function detectPerformance(filePath, content, lines, ignoreState, config) {
31475
+ function detectPerformance(filePath, content, lines, ignoreState, _config) {
30928
31476
  const findings = [];
30929
31477
  findings.push(...detectSyncInAsync(filePath, content, lines, ignoreState));
30930
31478
  findings.push(...detectNPlusOne(filePath, content, lines, ignoreState));
@@ -31339,7 +31887,7 @@ var HARDCODE_RULES = [
31339
31887
  suggestion: "Extract timeout to a named constant or config"
31340
31888
  }
31341
31889
  ];
31342
- function detectHardcode(filePath, content, lines, ignoreState, config) {
31890
+ function detectHardcode(filePath, content, lines, ignoreState, _config) {
31343
31891
  const findings = [];
31344
31892
  findings.push(...detectMagicNumbers(filePath, content, lines, ignoreState));
31345
31893
  findings.push(...detectHardcodedUrls(filePath, content, lines, ignoreState));
@@ -31695,7 +32243,7 @@ var NAMING_RULES = [
31695
32243
  suggestion: "Use is/has/can/should prefix for boolean variables"
31696
32244
  }
31697
32245
  ];
31698
- function detectNaming(filePath, content, lines, ignoreState, config) {
32246
+ function detectNaming(filePath, content, lines, ignoreState, _config) {
31699
32247
  const findings = [];
31700
32248
  findings.push(...detectSingleLetterVariables(filePath, content, lines, ignoreState));
31701
32249
  findings.push(...detectHungarianNotation(filePath, content, lines, ignoreState));
@@ -31978,7 +32526,7 @@ var CONDITIONAL_RULES = [
31978
32526
  suggestion: "Use the condition directly (or negate it)"
31979
32527
  }
31980
32528
  ];
31981
- function detectConditionals(filePath, content, lines, ignoreState, config) {
32529
+ function detectConditionals(filePath, content, lines, ignoreState, _config) {
31982
32530
  const findings = [];
31983
32531
  findings.push(...detectDeeplyNestedIf(filePath, content, lines, ignoreState));
31984
32532
  findings.push(...detectComplexConditions(filePath, content, lines, ignoreState));
@@ -32291,7 +32839,7 @@ var DEAD_CODE_RULES = [
32291
32839
  suggestion: "Implement function or add TODO comment"
32292
32840
  }
32293
32841
  ];
32294
- function detectDeadCode(filePath, content, lines, ignoreState, config) {
32842
+ function detectDeadCode(filePath, content, lines, ignoreState, _config) {
32295
32843
  const findings = [];
32296
32844
  findings.push(...detectUnusedImports(filePath, content, lines, ignoreState));
32297
32845
  findings.push(...detectUnusedVariables(filePath, content, lines, ignoreState));
@@ -32585,7 +33133,7 @@ var TYPE_SAFETY_RULES = [
32585
33133
  fileExtensions: [".ts", ".tsx"]
32586
33134
  }
32587
33135
  ];
32588
- function detectTypeSafety(filePath, content, lines, ignoreState, config) {
33136
+ function detectTypeSafety(filePath, content, lines, ignoreState, _config) {
32589
33137
  const findings = [];
32590
33138
  if (!filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) {
32591
33139
  return findings;
@@ -33191,6 +33739,9 @@ function countAnyTypes(code) {
33191
33739
  }
33192
33740
  return count;
33193
33741
  }
33742
+ function escapeRegex2(str) {
33743
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
33744
+ }
33194
33745
  function countUnusedImports(code) {
33195
33746
  const importMatches = code.match(/import\s+{([^}]+)}\s+from/g);
33196
33747
  if (!importMatches) return 0;
@@ -33200,10 +33751,17 @@ function countUnusedImports(code) {
33200
33751
  const namedImports = importMatch.match(/{([^}]+)}/);
33201
33752
  const namedImportContent = namedImports?.[1];
33202
33753
  if (namedImportContent) {
33203
- const names = namedImportContent.split(",").map((n) => n.trim().split(/\s+as\s+/).pop()?.trim() ?? "");
33754
+ const names = namedImportContent.split(",").map((n) => {
33755
+ const trimmed = n.trim();
33756
+ const withoutType = trimmed.replace(/^type\s+/, "");
33757
+ return withoutType.split(/\s+as\s+/).pop()?.trim() ?? "";
33758
+ }).filter((name) => name.length > 0 && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name));
33204
33759
  for (const name of names) {
33205
- if (name && !new RegExp(`\\b${name}\\b`).test(codeWithoutImports)) {
33206
- unusedCount++;
33760
+ try {
33761
+ if (!new RegExp(`\\b${escapeRegex2(name)}\\b`).test(codeWithoutImports)) {
33762
+ unusedCount++;
33763
+ }
33764
+ } catch {
33207
33765
  }
33208
33766
  }
33209
33767
  }
@@ -36659,6 +37217,7 @@ init_logger();
36659
37217
  // src/core/task-engine/task-queue.ts
36660
37218
  init_esm_shims();
36661
37219
  init_logger();
37220
+ init_safe_timers();
36662
37221
 
36663
37222
  // src/mcp/tools/task/create-task.ts
36664
37223
  init_logger();
@@ -36783,7 +37342,7 @@ var createTaskSchema = {
36783
37342
  // src/mcp/tools/task/run-task.ts
36784
37343
  init_esm_shims();
36785
37344
  init_logger();
36786
- function createRunTaskHandler(deps) {
37345
+ function createRunTaskHandler(_deps) {
36787
37346
  return async (input, context) => {
36788
37347
  const startTime = Date.now();
36789
37348
  if (context?.signal?.aborted) {
@@ -39333,6 +39892,17 @@ Use this tool first to understand what AutomatosX offers.`,
39333
39892
  mode: "sdk"
39334
39893
  }));
39335
39894
  }
39895
+ if (config.providers["qwen"]?.enabled) {
39896
+ const { QwenProvider: QwenProvider2 } = await Promise.resolve().then(() => (init_qwen_provider(), qwen_provider_exports));
39897
+ const qwenConfig = config.providers["qwen"];
39898
+ providers.push(new QwenProvider2({
39899
+ name: "qwen",
39900
+ enabled: true,
39901
+ priority: qwenConfig.priority,
39902
+ timeout: qwenConfig.timeout,
39903
+ mode: qwenConfig.mode || "sdk"
39904
+ }));
39905
+ }
39336
39906
  const healthCheckInterval = config.router?.healthCheckInterval;
39337
39907
  this.router = new Router({
39338
39908
  providers,
@@ -40894,6 +41464,7 @@ function createResourceEnforcer(config) {
40894
41464
 
40895
41465
  // src/mcp/unified-manager.ts
40896
41466
  init_validation_limits();
41467
+ init_safe_timers();
40897
41468
  var UnifiedMCPManager = class {
40898
41469
  constructor(config) {
40899
41470
  this.config = config;
@@ -41013,7 +41584,7 @@ var UnifiedMCPManager = class {
41013
41584
  };
41014
41585
  this.setupProcessHandlers(config.name, childProcess, serverProcess);
41015
41586
  this.servers.set(config.name, serverProcess);
41016
- await new Promise((resolve13) => setTimeout(resolve13, 100));
41587
+ await sleep(100);
41017
41588
  if (childProcess.killed || childProcess.exitCode !== null) {
41018
41589
  throw new Error(
41019
41590
  `Server process exited immediately with code ${childProcess.exitCode}`
@@ -41124,7 +41695,7 @@ var UnifiedMCPManager = class {
41124
41695
  async restartServer(serverName) {
41125
41696
  logger.info("UnifiedMCPManager: Restarting server", { name: serverName });
41126
41697
  await this.stopServer(serverName);
41127
- await new Promise((resolve13) => setTimeout(resolve13, 1e3));
41698
+ await sleep(1e3);
41128
41699
  const serverConfig = this.config.servers.find((s) => s.name === serverName);
41129
41700
  if (!serverConfig) {
41130
41701
  throw new Error(`Server configuration not found: ${serverName}`);
@@ -42895,7 +43466,7 @@ var CheckpointManager = class {
42895
43466
  if (checkpoint.schemaVersion === CURRENT_SCHEMA_VERSION) {
42896
43467
  return checkpoint;
42897
43468
  }
42898
- let migrated = { ...checkpoint };
43469
+ const migrated = { ...checkpoint };
42899
43470
  if (checkpointVersion.major === 1 && checkpointVersion.minor === 0) ;
42900
43471
  migrated.schemaVersion = CURRENT_SCHEMA_VERSION;
42901
43472
  migrated.checksum = this.calculateChecksum(migrated);
@@ -43001,7 +43572,7 @@ var CheckpointManager = class {
43001
43572
  * @returns Hex-encoded checksum
43002
43573
  */
43003
43574
  calculateChecksum(checkpoint) {
43004
- const { checksum, updatedAt, ...dataForChecksum } = checkpoint;
43575
+ const { checksum: _checksum, updatedAt: _updatedAt, ...dataForChecksum } = checkpoint;
43005
43576
  const hash = createHash("sha256");
43006
43577
  hash.update(JSON.stringify(dataForChecksum));
43007
43578
  return hash.digest("hex");
@@ -43650,6 +44221,7 @@ var ProgressRenderer = class {
43650
44221
 
43651
44222
  // src/core/stage-execution-controller.ts
43652
44223
  init_logger();
44224
+ init_safe_timers();
43653
44225
  var StageExecutionController = class {
43654
44226
  // Dependencies
43655
44227
  agentExecutor;
@@ -44316,9 +44888,9 @@ ${action.modifications}`;
44316
44888
  )
44317
44889
  );
44318
44890
  console.log(chalk5.gray(` Waiting ${delay}ms before retry...`));
44319
- await new Promise((resolve13) => setTimeout(resolve13, delay));
44891
+ await sleep(delay);
44320
44892
  } else {
44321
- await new Promise((resolve13) => setTimeout(resolve13, delay));
44893
+ await sleep(delay);
44322
44894
  }
44323
44895
  }
44324
44896
  const executionOptions = {
@@ -45744,7 +46316,7 @@ var IterateModeController = class {
45744
46316
  }
45745
46317
  });
45746
46318
  const responseContents = [];
45747
- let totalTokensUsed = { prompt: 0, completion: 0, total: 0 };
46319
+ const totalTokensUsed = { prompt: 0, completion: 0, total: 0 };
45748
46320
  let lastResponse;
45749
46321
  try {
45750
46322
  if (this.checkTimeBudget()) {
@@ -48023,7 +48595,7 @@ var runCommand = {
48023
48595
  }
48024
48596
  try {
48025
48597
  const workflowContent = readFileSync(workflowPath, "utf-8");
48026
- workflowConfig = yaml4__default.load(workflowContent);
48598
+ workflowConfig = yaml3__default.load(workflowContent);
48027
48599
  if (workflowConfig?.iterate?.enabled !== false) {
48028
48600
  argv.iterate = true;
48029
48601
  }
@@ -49244,7 +49816,7 @@ var statusCommand4 = {
49244
49816
  const agentCount = await countFiles(agentsDir, [".yaml", ".yml"]);
49245
49817
  const abilityCount = await countFiles(abilitiesDir, [".md"]);
49246
49818
  const projectInfo = await getProjectInfo(detectedProjectDir);
49247
- let limitData = {
49819
+ const limitData = {
49248
49820
  limits: [],
49249
49821
  manualOverride: null,
49250
49822
  envOverrides: []
@@ -49635,7 +50207,7 @@ function formatUptime2(seconds) {
49635
50207
  // src/cli/commands/update.ts
49636
50208
  init_esm_shims();
49637
50209
  init_logger();
49638
- var execAsync5 = promisify(exec);
50210
+ var execAsync6 = promisify(exec);
49639
50211
  var updateCommand = {
49640
50212
  command: "update",
49641
50213
  describe: "Check for updates and upgrade AutomatosX to the latest version",
@@ -49654,17 +50226,17 @@ var updateCommand = {
49654
50226
  handler: async (argv) => {
49655
50227
  console.log(chalk5.blue.bold("\n\u{1F504} AutomatosX Update Checker\n"));
49656
50228
  try {
49657
- const currentVersion = await getCurrentVersion();
50229
+ const currentVersion = await getCurrentVersion2();
49658
50230
  console.log(chalk5.gray(`Current version: ${currentVersion}`));
49659
50231
  console.log(chalk5.cyan("Checking for updates..."));
49660
- const latestVersion = await getLatestVersion();
50232
+ const latestVersion = await getLatestVersion2();
49661
50233
  console.log(chalk5.gray(`Latest version: ${latestVersion}
49662
50234
  `));
49663
50235
  if (currentVersion === latestVersion) {
49664
50236
  console.log(chalk5.green("\u2705 You are already running the latest version!\n"));
49665
50237
  return;
49666
50238
  }
49667
- if (isNewer(latestVersion, currentVersion)) {
50239
+ if (isNewer2(latestVersion, currentVersion)) {
49668
50240
  console.log(chalk5.yellow(`\u{1F4E6} New version available: ${currentVersion} \u2192 ${latestVersion}
49669
50241
  `));
49670
50242
  await showChangelog(currentVersion, latestVersion);
@@ -49709,28 +50281,28 @@ var updateCommand = {
49709
50281
  }
49710
50282
  }
49711
50283
  };
49712
- async function getCurrentVersion() {
50284
+ async function getCurrentVersion2() {
49713
50285
  try {
49714
- const { stdout } = await execAsync5("npm list -g @defai.digital/automatosx --depth=0 --json");
50286
+ const { stdout } = await execAsync6("npm list -g @defai.digital/automatosx --depth=0 --json");
49715
50287
  const result = JSON.parse(stdout);
49716
50288
  return result.dependencies["@defai.digital/automatosx"]?.version || "unknown";
49717
50289
  } catch (error) {
49718
50290
  const { readFile: readFile24 } = await import('fs/promises');
49719
- const { dirname: dirname18, join: join57 } = await import('path');
50291
+ const { dirname: dirname16, join: join57 } = await import('path');
49720
50292
  const { fileURLToPath: fileURLToPath5 } = await import('url');
49721
50293
  const __filename3 = fileURLToPath5(import.meta.url);
49722
- const __dirname4 = dirname18(__filename3);
50294
+ const __dirname4 = dirname16(__filename3);
49723
50295
  const pkgPath = join57(__dirname4, "../../../package.json");
49724
50296
  const content = await readFile24(pkgPath, "utf-8");
49725
50297
  const pkg = JSON.parse(content);
49726
50298
  return pkg.version;
49727
50299
  }
49728
50300
  }
49729
- async function getLatestVersion() {
49730
- const { stdout } = await execAsync5("npm view @defai.digital/automatosx version");
50301
+ async function getLatestVersion2() {
50302
+ const { stdout } = await execAsync6("npm view @defai.digital/automatosx version");
49731
50303
  return stdout.trim();
49732
50304
  }
49733
- function isNewer(a, b) {
50305
+ function isNewer2(a, b) {
49734
50306
  const stripPrerelease = (v) => v.split("-")[0] || v;
49735
50307
  const parseVersion = (v) => stripPrerelease(v).split(".").map(Number);
49736
50308
  const [aMajor = 0, aMinor = 0, aPatch = 0] = parseVersion(a);
@@ -49742,7 +50314,7 @@ function isNewer(a, b) {
49742
50314
  async function showChangelog(from, to) {
49743
50315
  try {
49744
50316
  console.log(chalk5.cyan("What's new:\n"));
49745
- const { stdout } = await execAsync5(
50317
+ const { stdout } = await execAsync6(
49746
50318
  `curl -s https://api.github.com/repos/defai-digital/automatosx/releases/tags/v${to}`
49747
50319
  );
49748
50320
  const release = JSON.parse(stdout);
@@ -49764,7 +50336,7 @@ async function showChangelog(from, to) {
49764
50336
  }
49765
50337
  async function installUpdate(version) {
49766
50338
  try {
49767
- const { stdout, stderr } = await execAsync5(
50339
+ const { stdout, stderr } = await execAsync6(
49768
50340
  `npm install -g @defai.digital/automatosx@${version}`,
49769
50341
  { maxBuffer: 10 * 1024 * 1024 }
49770
50342
  );
@@ -49922,6 +50494,7 @@ init_esm_shims();
49922
50494
  // src/cli/commands/agent/helpers.ts
49923
50495
  init_esm_shims();
49924
50496
  init_logger();
50497
+ var YAML_EXT_REGEX = /\.(yaml|yml)$/;
49925
50498
  var BUILTIN_TEMPLATE_METADATA = {
49926
50499
  "basic-agent": {
49927
50500
  name: "basic-agent",
@@ -49958,7 +50531,7 @@ async function listAvailableTemplates(baseDir) {
49958
50531
  const files = await readdir(projectTemplatesDir);
49959
50532
  for (const file of files) {
49960
50533
  if (extname$1(file) === ".yaml" || extname$1(file) === ".yml") {
49961
- const name = file.replace(/\.(yaml|yml)$/, "");
50534
+ const name = file.replace(YAML_EXT_REGEX, "");
49962
50535
  templates.push({
49963
50536
  name,
49964
50537
  path: join(projectTemplatesDir, file),
@@ -49977,7 +50550,7 @@ async function listAvailableTemplates(baseDir) {
49977
50550
  const files = await readdir(builtinTemplatesDir);
49978
50551
  for (const file of files) {
49979
50552
  if (extname$1(file) === ".yaml" || extname$1(file) === ".yml") {
49980
- const name = file.replace(/\.(yaml|yml)$/, "");
50553
+ const name = file.replace(YAML_EXT_REGEX, "");
49981
50554
  if (templates.find((t) => t.name === name)) {
49982
50555
  continue;
49983
50556
  }
@@ -52623,7 +53196,7 @@ var CommandTranslator = class {
52623
53196
  if (baseDir === void 0) {
52624
53197
  try {
52625
53198
  baseDir = await realpath$1(dir);
52626
- } catch (error) {
53199
+ } catch {
52627
53200
  return commands;
52628
53201
  }
52629
53202
  }
@@ -52641,7 +53214,7 @@ var CommandTranslator = class {
52641
53214
  let realPath;
52642
53215
  try {
52643
53216
  realPath = await realpath$1(fullPath);
52644
- } catch (error) {
53217
+ } catch {
52645
53218
  console.warn(`[Security] Cannot resolve path: ${fullPath}`);
52646
53219
  continue;
52647
53220
  }
@@ -52684,7 +53257,7 @@ var CommandTranslator = class {
52684
53257
  }
52685
53258
  }
52686
53259
  }
52687
- } catch (error) {
53260
+ } catch {
52688
53261
  }
52689
53262
  return commands;
52690
53263
  }
@@ -53997,7 +54570,7 @@ async function handleSpecExplain(workspacePath, options) {
53997
54570
  console.log(chalk5.blue.bold("\n\u{1F4D6} Spec-Kit: Explain Spec\n"));
53998
54571
  try {
53999
54572
  const specContent = readFileSync(options.file, "utf-8");
54000
- const spec = yaml4.load(specContent);
54573
+ const spec = yaml3.load(specContent);
54001
54574
  const validation = validateSpec(spec);
54002
54575
  if (!validation.valid) {
54003
54576
  console.error(chalk5.red("\n\u2717 Spec validation failed:\n"));
@@ -56099,7 +56672,7 @@ async function handleGenPlan(specFile, argv) {
56099
56672
  process.exit(1);
56100
56673
  }
56101
56674
  const specContent = readFileSync(specFile, "utf-8");
56102
- const spec = yaml4.load(specContent);
56675
+ const spec = yaml3.load(specContent);
56103
56676
  const validation = validateSpec(spec);
56104
56677
  if (!validation.valid) {
56105
56678
  spinner.fail("Spec validation failed");
@@ -56157,7 +56730,7 @@ async function handleGenDag(specFile, argv) {
56157
56730
  process.exit(1);
56158
56731
  }
56159
56732
  const specContent = readFileSync(specFile, "utf-8");
56160
- const spec = yaml4.load(specContent);
56733
+ const spec = yaml3.load(specContent);
56161
56734
  const validation = validateSpec(spec);
56162
56735
  if (!validation.valid) {
56163
56736
  spinner.fail("Spec validation failed");
@@ -56244,7 +56817,7 @@ async function handleGenScaffold(specFile, argv) {
56244
56817
  process.exit(1);
56245
56818
  }
56246
56819
  const specContent = readFileSync(specFile, "utf-8");
56247
- const spec = yaml4.load(specContent);
56820
+ const spec = yaml3.load(specContent);
56248
56821
  const validation = validateSpec(spec);
56249
56822
  if (!validation.valid) {
56250
56823
  spinner.fail("Spec validation failed");
@@ -56316,7 +56889,7 @@ async function handleGenTests(specFile, argv) {
56316
56889
  process.exit(1);
56317
56890
  }
56318
56891
  const specContent = readFileSync(specFile, "utf-8");
56319
- const spec = yaml4.load(specContent);
56892
+ const spec = yaml3.load(specContent);
56320
56893
  const validation = validateSpec(spec);
56321
56894
  if (!validation.valid) {
56322
56895
  spinner.fail("Spec validation failed");
@@ -56551,7 +57124,7 @@ async function handleList(config, argv) {
56551
57124
  });
56552
57125
  }
56553
57126
  }
56554
- let displayProviders2 = argv.available ? providers.filter((p) => p.enabled && !p.limitInfo?.isBlocked) : providers;
57127
+ const displayProviders2 = argv.available ? providers.filter((p) => p.enabled && !p.limitInfo?.isBlocked) : providers;
56555
57128
  switch (argv.sort) {
56556
57129
  case "cost":
56557
57130
  displayProviders2.sort((a, b) => {
@@ -58194,8 +58767,8 @@ async function runMcpDiagnostics(verbose) {
58194
58767
  let serverStarts = false;
58195
58768
  if (cliAvailable) {
58196
58769
  try {
58197
- const { spawn: spawn12 } = await import('child_process');
58198
- const proc = spawn12("automatosx", ["mcp", "server"], { stdio: ["pipe", "pipe", "pipe"] });
58770
+ const { spawn: spawn13 } = await import('child_process');
58771
+ const proc = spawn13("automatosx", ["mcp", "server"], { stdio: ["pipe", "pipe", "pipe"] });
58199
58772
  const initResult = await new Promise((resolve13) => {
58200
58773
  let output = "";
58201
58774
  proc.stderr?.on("data", (data) => {
@@ -58294,7 +58867,8 @@ async function runMcpDiagnostics(verbose) {
58294
58867
  // src/cli/commands/cleanup.ts
58295
58868
  init_esm_shims();
58296
58869
  init_logger();
58297
- var execAsync6 = promisify(exec);
58870
+ init_safe_timers();
58871
+ var execAsync7 = promisify(exec);
58298
58872
  var cleanupCommand2 = {
58299
58873
  command: "cleanup [provider]",
58300
58874
  describe: "Clean up orphaned provider processes (v6.0.7 Phase 3)",
@@ -58423,7 +58997,7 @@ async function findProcesses(processName, verbose) {
58423
58997
  throw new Error(`Invalid process name: ${processName}. Only alphanumeric characters, dashes, and underscores are allowed.`);
58424
58998
  }
58425
58999
  try {
58426
- const { stdout } = await execAsync6("ps -eo pid,comm,etime,rss");
59000
+ const { stdout } = await execAsync7("ps -eo pid,comm,etime,rss");
58427
59001
  if (!stdout.trim()) {
58428
59002
  return processes;
58429
59003
  }
@@ -58486,7 +59060,7 @@ async function killProcess(pid, verbose) {
58486
59060
  });
58487
59061
  proc.on("error", reject);
58488
59062
  });
58489
- await new Promise((resolve13) => setTimeout(resolve13, 1e3));
59063
+ await sleep(1e3);
58490
59064
  try {
58491
59065
  await new Promise((resolve13, reject) => {
58492
59066
  const proc = spawn("ps", ["-p", String(pid)], {
@@ -59737,6 +60311,7 @@ init_esm_shims();
59737
60311
  init_flags();
59738
60312
  init_logger();
59739
60313
  init_validation_limits();
60314
+ init_safe_timers();
59740
60315
  var flagsCommand = {
59741
60316
  command: "flags <command>",
59742
60317
  describe: "Manage feature flags",
@@ -59886,7 +60461,7 @@ async function handleKill(argv) {
59886
60461
  `);
59887
60462
  console.log(chalk5.yellow("This will immediately disable the feature for ALL users."));
59888
60463
  console.log(chalk5.gray("Press Ctrl+C to cancel, or wait 5 seconds to confirm...\n"));
59889
- await new Promise((resolve13) => setTimeout(resolve13, TIMEOUTS.KILL_SWITCH_DELAY));
60464
+ await sleep(TIMEOUTS.KILL_SWITCH_DELAY);
59890
60465
  try {
59891
60466
  await flagManager.killSwitch(flagName, reason);
59892
60467
  console.log(chalk5.red("\u2713 Kill switch activated"));
@@ -61896,42 +62471,20 @@ var AgentInstructionInjector = class {
61896
62471
  this.lastQualityCheckTurn = context.turnCount;
61897
62472
  }
61898
62473
  const taskText = this.extractTaskText(context);
61899
- if (this.config.delegationDetection) {
61900
- if (taskText !== this.recentTaskText) {
61901
- this.recentTaskText = taskText;
61902
- const delegationContent = this.checkAndFormatDelegation(template, taskText);
61903
- if (delegationContent) {
61904
- instructions.push({
61905
- type: "delegation",
61906
- priority: "high",
61907
- content: delegationContent,
61908
- source: INSTRUCTION_SOURCE,
61909
- createdAt: Date.now(),
61910
- createdAtTurn: context.turnCount,
61911
- expiresAfter: 3,
61912
- id: `agent-delegation-${Date.now()}`
61913
- });
61914
- }
61915
- }
62474
+ const delegationInstruction = this.tryCreateDelegationInstruction(
62475
+ template,
62476
+ taskText,
62477
+ context.turnCount
62478
+ );
62479
+ if (delegationInstruction) {
62480
+ instructions.push(delegationInstruction);
61916
62481
  }
61917
- if (this.config.contextAwareBoosting && taskText) {
61918
- const detectedCategories = this.detectContextCategories(taskText);
61919
- const contextBoostContent = this.formatContextBoost(detectedCategories);
61920
- if (contextBoostContent) {
61921
- instructions.push({
61922
- type: "context",
61923
- priority: "normal",
61924
- content: contextBoostContent,
61925
- source: INSTRUCTION_SOURCE,
61926
- createdAt: Date.now(),
61927
- createdAtTurn: context.turnCount,
61928
- expiresAfter: 5,
61929
- id: `agent-context-boost-${Date.now()}`
61930
- });
61931
- logger.debug("Context-aware boost applied", {
61932
- categories: detectedCategories.map((c) => c.name)
61933
- });
61934
- }
62482
+ const contextInstruction = this.tryCreateContextBoostInstruction(
62483
+ taskText,
62484
+ context.turnCount
62485
+ );
62486
+ if (contextInstruction) {
62487
+ instructions.push(contextInstruction);
61935
62488
  }
61936
62489
  logger.debug("Agent instructions generated", {
61937
62490
  domain,
@@ -62032,6 +62585,62 @@ var AgentInstructionInjector = class {
62032
62585
  lines.push("Use `DELEGATE TO @agent: task` or `@agent task` syntax to delegate.");
62033
62586
  return lines.join("\n");
62034
62587
  }
62588
+ /**
62589
+ * Try to create a delegation instruction
62590
+ * Returns null if delegation detection is disabled or no delegation is needed
62591
+ * @since v12.6.1 - Extracted to reduce nesting
62592
+ */
62593
+ tryCreateDelegationInstruction(template, taskText, turnCount) {
62594
+ if (!this.config.delegationDetection) {
62595
+ return null;
62596
+ }
62597
+ if (taskText === this.recentTaskText) {
62598
+ return null;
62599
+ }
62600
+ this.recentTaskText = taskText;
62601
+ const delegationContent = this.checkAndFormatDelegation(template, taskText);
62602
+ if (!delegationContent) {
62603
+ return null;
62604
+ }
62605
+ return {
62606
+ type: "delegation",
62607
+ priority: "high",
62608
+ content: delegationContent,
62609
+ source: INSTRUCTION_SOURCE,
62610
+ createdAt: Date.now(),
62611
+ createdAtTurn: turnCount,
62612
+ expiresAfter: 3,
62613
+ id: `agent-delegation-${Date.now()}`
62614
+ };
62615
+ }
62616
+ /**
62617
+ * Try to create a context boost instruction
62618
+ * Returns null if context boosting is disabled or no boost is needed
62619
+ * @since v12.6.1 - Extracted to reduce nesting
62620
+ */
62621
+ tryCreateContextBoostInstruction(taskText, turnCount) {
62622
+ if (!this.config.contextAwareBoosting || !taskText) {
62623
+ return null;
62624
+ }
62625
+ const detectedCategories = this.detectContextCategories(taskText);
62626
+ const contextBoostContent = this.formatContextBoost(detectedCategories);
62627
+ if (!contextBoostContent) {
62628
+ return null;
62629
+ }
62630
+ logger.debug("Context-aware boost applied", {
62631
+ categories: detectedCategories.map((c) => c.name)
62632
+ });
62633
+ return {
62634
+ type: "context",
62635
+ priority: "normal",
62636
+ content: contextBoostContent,
62637
+ source: INSTRUCTION_SOURCE,
62638
+ createdAt: Date.now(),
62639
+ createdAtTurn: turnCount,
62640
+ expiresAfter: 5,
62641
+ id: `agent-context-boost-${Date.now()}`
62642
+ };
62643
+ }
62035
62644
  /**
62036
62645
  * Detect context categories from task text
62037
62646
  * @since v11.3.1
@@ -62870,10 +63479,10 @@ init_esm_shims();
62870
63479
  // src/core/bugfix/git-utils.ts
62871
63480
  init_esm_shims();
62872
63481
  init_logger();
62873
- var execAsync7 = promisify(exec);
63482
+ var execAsync8 = promisify(exec);
62874
63483
  async function isGitRepo(cwd) {
62875
63484
  try {
62876
- await execAsync7("git rev-parse --is-inside-work-tree", { cwd });
63485
+ await execAsync8("git rev-parse --is-inside-work-tree", { cwd });
62877
63486
  return true;
62878
63487
  } catch {
62879
63488
  return false;
@@ -62891,14 +63500,14 @@ async function getChangedFiles(options) {
62891
63500
  command = "git diff --cached --name-only --diff-filter=ACMR";
62892
63501
  } else if (options.since) {
62893
63502
  const mergeBaseCmd = `git merge-base HEAD ${options.since}`;
62894
- const { stdout: mergeBase } = await execAsync7(mergeBaseCmd, { cwd });
63503
+ const { stdout: mergeBase } = await execAsync8(mergeBaseCmd, { cwd });
62895
63504
  command = `git diff --name-only ${mergeBase.trim()}..HEAD`;
62896
63505
  } else if (options.changed) {
62897
63506
  command = "git diff --name-only HEAD";
62898
63507
  } else {
62899
63508
  command = "git diff --name-only HEAD";
62900
63509
  }
62901
- const { stdout } = await execAsync7(command, { cwd });
63510
+ const { stdout } = await execAsync8(command, { cwd });
62902
63511
  const files = stdout.split("\n").map((f) => f.trim()).filter((f) => f.length > 0);
62903
63512
  logger.debug("Git changed files", {
62904
63513
  command,