@defai.digital/automatosx 12.6.2 → 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/README.md +1 -1
- package/dist/index.js +1694 -1137
- package/dist/mcp/index.js +1100 -208
- package/package.json +1 -1
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,
|
|
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
|
|
24
|
-
import
|
|
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
|
|
|
@@ -2748,6 +2747,7 @@ function getRetryableErrors(provider) {
|
|
|
2748
2747
|
return [...baseErrors, ...CODEX_RETRYABLE_ERRORS];
|
|
2749
2748
|
case "glm":
|
|
2750
2749
|
case "grok":
|
|
2750
|
+
case "qwen":
|
|
2751
2751
|
return [...baseErrors, ...OPENAI_RETRYABLE_ERRORS];
|
|
2752
2752
|
case "base":
|
|
2753
2753
|
default:
|
|
@@ -2850,6 +2850,10 @@ var init_base_provider = __esm({
|
|
|
2850
2850
|
// v12.0.0: Native Grok provider (xAI)
|
|
2851
2851
|
"ax-grok",
|
|
2852
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
|
|
2853
2857
|
"test-provider"
|
|
2854
2858
|
// For unit tests
|
|
2855
2859
|
];
|
|
@@ -3212,7 +3216,7 @@ var init_base_provider = __esm({
|
|
|
3212
3216
|
if (readlineInterface) {
|
|
3213
3217
|
try {
|
|
3214
3218
|
readlineInterface.close();
|
|
3215
|
-
} catch
|
|
3219
|
+
} catch {
|
|
3216
3220
|
} finally {
|
|
3217
3221
|
readlineInterface = null;
|
|
3218
3222
|
}
|
|
@@ -3220,7 +3224,7 @@ var init_base_provider = __esm({
|
|
|
3220
3224
|
if (stderrInterface) {
|
|
3221
3225
|
try {
|
|
3222
3226
|
stderrInterface.close();
|
|
3223
|
-
} catch
|
|
3227
|
+
} catch {
|
|
3224
3228
|
} finally {
|
|
3225
3229
|
stderrInterface = null;
|
|
3226
3230
|
}
|
|
@@ -3414,7 +3418,7 @@ ${fullPrompt}
|
|
|
3414
3418
|
this.health.consecutiveSuccesses = 0;
|
|
3415
3419
|
}
|
|
3416
3420
|
return available;
|
|
3417
|
-
} catch
|
|
3421
|
+
} catch {
|
|
3418
3422
|
this.health.available = false;
|
|
3419
3423
|
this.health.errorRate = 1;
|
|
3420
3424
|
this.health.lastCheck = Date.now();
|
|
@@ -3590,6 +3594,8 @@ ${fullPrompt}
|
|
|
3590
3594
|
retryableProvider = "glm";
|
|
3591
3595
|
} else if (providerName === "grok" || providerName === "ax-grok") {
|
|
3592
3596
|
retryableProvider = "grok";
|
|
3597
|
+
} else if (providerName === "qwen" || providerName === "qwen-code") {
|
|
3598
|
+
retryableProvider = "qwen";
|
|
3593
3599
|
}
|
|
3594
3600
|
return shouldRetryError(error, retryableProvider);
|
|
3595
3601
|
}
|
|
@@ -6044,13 +6050,21 @@ var init_types2 = __esm({
|
|
|
6044
6050
|
"src/integrations/ax-glm/types.ts"() {
|
|
6045
6051
|
init_esm_shims();
|
|
6046
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)
|
|
6047
6059
|
"glm-4-plus": "glm-4.6",
|
|
6048
|
-
"glm-
|
|
6060
|
+
"glm-4.5v": "glm-4.6v",
|
|
6061
|
+
"glm-4v": "glm-4.6v",
|
|
6062
|
+
"glm-4": "glm-4.6",
|
|
6049
6063
|
"glm-4-air": "glm-4-flash",
|
|
6050
6064
|
"glm-4-airx": "glm-4-flash"
|
|
6051
6065
|
};
|
|
6052
6066
|
GLM_DEFAULT_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
|
|
6053
|
-
GLM_DEFAULT_MODEL = "glm-4";
|
|
6067
|
+
GLM_DEFAULT_MODEL = "glm-4.6";
|
|
6054
6068
|
GLM_DEFAULT_COMMAND = "ax-glm";
|
|
6055
6069
|
}
|
|
6056
6070
|
});
|
|
@@ -6206,14 +6220,14 @@ var init_sdk_adapter2 = __esm({
|
|
|
6206
6220
|
};
|
|
6207
6221
|
}
|
|
6208
6222
|
});
|
|
6209
|
-
var
|
|
6223
|
+
var execAsync4, GLMCliWrapper;
|
|
6210
6224
|
var init_cli_wrapper2 = __esm({
|
|
6211
6225
|
"src/integrations/ax-glm/cli-wrapper.ts"() {
|
|
6212
6226
|
init_esm_shims();
|
|
6213
6227
|
init_logger();
|
|
6214
6228
|
init_validation_limits();
|
|
6215
6229
|
init_types2();
|
|
6216
|
-
|
|
6230
|
+
execAsync4 = promisify(exec);
|
|
6217
6231
|
GLMCliWrapper = class {
|
|
6218
6232
|
config;
|
|
6219
6233
|
cliPath = null;
|
|
@@ -6234,13 +6248,13 @@ var init_cli_wrapper2 = __esm({
|
|
|
6234
6248
|
*/
|
|
6235
6249
|
async isAvailable() {
|
|
6236
6250
|
try {
|
|
6237
|
-
const { stdout } = await
|
|
6251
|
+
const { stdout } = await execAsync4(`which ${this.config.command}`, {
|
|
6238
6252
|
timeout: TIMEOUTS.PROVIDER_DETECTION
|
|
6239
6253
|
});
|
|
6240
6254
|
this.cliPath = stdout.trim();
|
|
6241
6255
|
if (this.cliPath) {
|
|
6242
6256
|
try {
|
|
6243
|
-
const { stdout: versionOutput } = await
|
|
6257
|
+
const { stdout: versionOutput } = await execAsync4(
|
|
6244
6258
|
`${this.config.command} --version`,
|
|
6245
6259
|
{ timeout: TIMEOUTS.PROVIDER_DETECTION }
|
|
6246
6260
|
);
|
|
@@ -6999,10 +7013,18 @@ var init_types3 = __esm({
|
|
|
6999
7013
|
"src/integrations/ax-grok/types.ts"() {
|
|
7000
7014
|
init_esm_shims();
|
|
7001
7015
|
GROK_MODEL_MAPPING = {
|
|
7002
|
-
|
|
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"
|
|
7003
7025
|
};
|
|
7004
7026
|
GROK_DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
7005
|
-
GROK_DEFAULT_MODEL = "grok-
|
|
7027
|
+
GROK_DEFAULT_MODEL = "grok-4-0709";
|
|
7006
7028
|
GROK_DEFAULT_COMMAND = "ax-grok";
|
|
7007
7029
|
}
|
|
7008
7030
|
});
|
|
@@ -7158,14 +7180,14 @@ var init_sdk_adapter3 = __esm({
|
|
|
7158
7180
|
};
|
|
7159
7181
|
}
|
|
7160
7182
|
});
|
|
7161
|
-
var
|
|
7183
|
+
var execAsync5, GrokCliWrapper;
|
|
7162
7184
|
var init_cli_wrapper3 = __esm({
|
|
7163
7185
|
"src/integrations/ax-grok/cli-wrapper.ts"() {
|
|
7164
7186
|
init_esm_shims();
|
|
7165
7187
|
init_logger();
|
|
7166
7188
|
init_validation_limits();
|
|
7167
7189
|
init_types3();
|
|
7168
|
-
|
|
7190
|
+
execAsync5 = promisify(exec);
|
|
7169
7191
|
GrokCliWrapper = class {
|
|
7170
7192
|
config;
|
|
7171
7193
|
cliPath = null;
|
|
@@ -7186,13 +7208,13 @@ var init_cli_wrapper3 = __esm({
|
|
|
7186
7208
|
*/
|
|
7187
7209
|
async isAvailable() {
|
|
7188
7210
|
try {
|
|
7189
|
-
const { stdout } = await
|
|
7211
|
+
const { stdout } = await execAsync5(`which ${this.config.command}`, {
|
|
7190
7212
|
timeout: TIMEOUTS.PROVIDER_DETECTION
|
|
7191
7213
|
});
|
|
7192
7214
|
this.cliPath = stdout.trim();
|
|
7193
7215
|
if (this.cliPath) {
|
|
7194
7216
|
try {
|
|
7195
|
-
const { stdout: versionOutput } = await
|
|
7217
|
+
const { stdout: versionOutput } = await execAsync5(
|
|
7196
7218
|
`${this.config.command} --version`,
|
|
7197
7219
|
{ timeout: TIMEOUTS.PROVIDER_DETECTION }
|
|
7198
7220
|
);
|
|
@@ -7934,6 +7956,902 @@ Mode: ${this.grokConfig.mode || "auto"}`;
|
|
|
7934
7956
|
}
|
|
7935
7957
|
});
|
|
7936
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
|
+
|
|
7937
8855
|
// src/cli/index.ts
|
|
7938
8856
|
init_esm_shims();
|
|
7939
8857
|
init_logger();
|
|
@@ -8834,12 +9752,16 @@ init_logger();
|
|
|
8834
9752
|
|
|
8835
9753
|
// src/shared/helpers/deep-merge.ts
|
|
8836
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
|
+
}
|
|
8837
9758
|
function deepMerge(defaults, user) {
|
|
8838
9759
|
if (user === null || user === void 0) {
|
|
8839
|
-
return defaults;
|
|
9760
|
+
return { ...defaults };
|
|
8840
9761
|
}
|
|
8841
|
-
const result = {
|
|
8842
|
-
|
|
9762
|
+
const result = Object.assign({}, defaults);
|
|
9763
|
+
const userKeys = Object.keys(user);
|
|
9764
|
+
for (const key of userKeys) {
|
|
8843
9765
|
const userValue = user[key];
|
|
8844
9766
|
if (userValue === null) {
|
|
8845
9767
|
result[key] = void 0;
|
|
@@ -8849,8 +9771,11 @@ function deepMerge(defaults, user) {
|
|
|
8849
9771
|
continue;
|
|
8850
9772
|
}
|
|
8851
9773
|
const defaultValue = defaults[key];
|
|
8852
|
-
if (
|
|
8853
|
-
result[key] = deepMerge(
|
|
9774
|
+
if (isPlainObject(userValue) && isPlainObject(defaultValue)) {
|
|
9775
|
+
result[key] = deepMerge(
|
|
9776
|
+
defaultValue,
|
|
9777
|
+
userValue
|
|
9778
|
+
);
|
|
8854
9779
|
} else {
|
|
8855
9780
|
result[key] = userValue;
|
|
8856
9781
|
}
|
|
@@ -9492,7 +10417,7 @@ var PRECOMPILED_CONFIG = {
|
|
|
9492
10417
|
"enableFreeTierPrioritization": true,
|
|
9493
10418
|
"enableWorkloadAwareRouting": true
|
|
9494
10419
|
},
|
|
9495
|
-
"version": "12.6.
|
|
10420
|
+
"version": "12.6.3"
|
|
9496
10421
|
};
|
|
9497
10422
|
|
|
9498
10423
|
// src/core/config/schemas.ts
|
|
@@ -13263,14 +14188,17 @@ init_validation_limits();
|
|
|
13263
14188
|
var execAsync2 = promisify(exec);
|
|
13264
14189
|
var ProviderDetector = class _ProviderDetector {
|
|
13265
14190
|
// v12.0.0: Removed ax-cli, added glm/grok (SDK-first providers)
|
|
14191
|
+
// v12.7.0: Added qwen (SDK-first with CLI fallback)
|
|
13266
14192
|
static PROVIDER_COMMANDS = {
|
|
13267
14193
|
"claude-code": "claude",
|
|
13268
14194
|
"gemini-cli": "gemini",
|
|
13269
14195
|
"codex": "codex",
|
|
13270
14196
|
"glm": "glm",
|
|
13271
14197
|
// v12.0.0: Native GLM provider (SDK-first)
|
|
13272
|
-
"grok": "grok"
|
|
14198
|
+
"grok": "grok",
|
|
13273
14199
|
// v12.0.0: Native Grok provider (SDK-first)
|
|
14200
|
+
"qwen": "qwen"
|
|
14201
|
+
// v12.7.0: Qwen Code provider (SDK-first with CLI fallback)
|
|
13274
14202
|
};
|
|
13275
14203
|
/**
|
|
13276
14204
|
* Detect all supported AI provider CLIs
|
|
@@ -13295,16 +14223,22 @@ var ProviderDetector = class _ProviderDetector {
|
|
|
13295
14223
|
const results = await Promise.all([
|
|
13296
14224
|
this.isCommandAvailable("claude-code"),
|
|
13297
14225
|
this.isCommandAvailable("gemini-cli"),
|
|
13298
|
-
this.isCommandAvailable("codex")
|
|
14226
|
+
this.isCommandAvailable("codex"),
|
|
14227
|
+
this.isCommandAvailable("qwen")
|
|
13299
14228
|
]);
|
|
14229
|
+
const qwenApiKeySet = !!(process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY);
|
|
14230
|
+
const qwenCliAvailable = results[3];
|
|
14231
|
+
const qwenAvailable = qwenApiKeySet || qwenCliAvailable;
|
|
13300
14232
|
const detected = {
|
|
13301
14233
|
"claude-code": results[0],
|
|
13302
14234
|
"gemini-cli": results[1],
|
|
13303
14235
|
"codex": results[2],
|
|
13304
14236
|
"glm": true,
|
|
13305
14237
|
// SDK-first: always available via SDK
|
|
13306
|
-
"grok": true
|
|
14238
|
+
"grok": true,
|
|
13307
14239
|
// SDK-first: always available via SDK
|
|
14240
|
+
"qwen": qwenAvailable
|
|
14241
|
+
// v12.7.0: SDK-first with CLI fallback
|
|
13308
14242
|
};
|
|
13309
14243
|
const foundProviders = Object.entries(detected).filter(([_, isInstalled]) => isInstalled).map(([provider]) => provider);
|
|
13310
14244
|
logger.info("Provider detection complete", {
|
|
@@ -13411,6 +14345,9 @@ var ProviderDetector = class _ProviderDetector {
|
|
|
13411
14345
|
if (provider === "grok") {
|
|
13412
14346
|
return this.getGrokVersion();
|
|
13413
14347
|
}
|
|
14348
|
+
if (provider === "qwen") {
|
|
14349
|
+
return this.getQwenVersion();
|
|
14350
|
+
}
|
|
13414
14351
|
const command = _ProviderDetector.PROVIDER_COMMANDS[provider];
|
|
13415
14352
|
try {
|
|
13416
14353
|
const { stdout } = await execAsync2(`${command} --version`, {
|
|
@@ -13451,6 +14388,19 @@ var ProviderDetector = class _ProviderDetector {
|
|
|
13451
14388
|
}
|
|
13452
14389
|
return "SDK v1 (grok-3, API key not set)";
|
|
13453
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
|
+
}
|
|
13454
14404
|
/**
|
|
13455
14405
|
* Get list of detected provider names
|
|
13456
14406
|
*
|
|
@@ -13484,7 +14434,9 @@ var ProviderDetector = class _ProviderDetector {
|
|
|
13484
14434
|
"gemini-cli": "Gemini CLI",
|
|
13485
14435
|
"codex": "Codex CLI",
|
|
13486
14436
|
"glm": "GLM (Zhipu AI)",
|
|
13487
|
-
"grok": "Grok (xAI)"
|
|
14437
|
+
"grok": "Grok (xAI)",
|
|
14438
|
+
"qwen": "Qwen (Alibaba Cloud)"
|
|
14439
|
+
// v12.7.0
|
|
13488
14440
|
};
|
|
13489
14441
|
return nameMap[provider] || provider;
|
|
13490
14442
|
}
|
|
@@ -13577,8 +14529,9 @@ var setupCommand = {
|
|
|
13577
14529
|
console.log(chalk5.gray(" - Claude Code"));
|
|
13578
14530
|
console.log(chalk5.gray(" - Gemini CLI"));
|
|
13579
14531
|
console.log(chalk5.gray(" - Codex CLI"));
|
|
13580
|
-
console.log(chalk5.gray(" - GLM (SDK-first, requires
|
|
14532
|
+
console.log(chalk5.gray(" - GLM (SDK-first, requires GLM_API_KEY)"));
|
|
13581
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)"));
|
|
13582
14535
|
console.log("");
|
|
13583
14536
|
console.log(chalk5.cyan(' \u{1F4A1} After installing, run "ax setup" again to configure.\n'));
|
|
13584
14537
|
if (process.stdout.isTTY && process.stdin.isTTY) {
|
|
@@ -13655,14 +14608,14 @@ var setupCommand = {
|
|
|
13655
14608
|
console.log(chalk5.cyan("\u{1F50C} Setting up Gemini CLI integration..."));
|
|
13656
14609
|
const geminiDir = join(projectDir, ".gemini");
|
|
13657
14610
|
const geminiDirExistedBefore = await checkExists2(geminiDir);
|
|
13658
|
-
geminiMcpStatus = await setupGeminiIntegration(projectDir, packageRoot);
|
|
14611
|
+
geminiMcpStatus = await setupGeminiIntegration(projectDir, packageRoot, argv.force);
|
|
13659
14612
|
if (!geminiDirExistedBefore) {
|
|
13660
14613
|
createdResources.push(geminiDir);
|
|
13661
14614
|
}
|
|
13662
14615
|
if (geminiMcpStatus === "configured") {
|
|
13663
|
-
console.log(chalk5.green(" \u2713 Gemini CLI MCP integration configured"));
|
|
14616
|
+
console.log(chalk5.green(" \u2713 Gemini CLI MCP integration configured (gemini mcp add)"));
|
|
13664
14617
|
} else if (geminiMcpStatus === "skipped") {
|
|
13665
|
-
console.log(chalk5.yellow(" \u26A0 Gemini CLI
|
|
14618
|
+
console.log(chalk5.yellow(" \u26A0 Gemini CLI not available, skipped MCP setup"));
|
|
13666
14619
|
console.log(chalk5.gray(" CLI integration still works via subprocess"));
|
|
13667
14620
|
} else {
|
|
13668
14621
|
console.log(chalk5.yellow(" \u26A0 Gemini CLI MCP configuration failed"));
|
|
@@ -13684,34 +14637,26 @@ var setupCommand = {
|
|
|
13684
14637
|
let codexMcpStatus = "skipped";
|
|
13685
14638
|
if (providers["codex"]) {
|
|
13686
14639
|
console.log(chalk5.cyan("\u{1F50C} Setting up Codex CLI MCP integration..."));
|
|
13687
|
-
codexMcpStatus = await setupCodexGlobalMCPConfig();
|
|
14640
|
+
codexMcpStatus = await setupCodexGlobalMCPConfig(argv.force);
|
|
13688
14641
|
if (codexMcpStatus === "configured") {
|
|
13689
|
-
console.log(chalk5.green(" \u2713 Codex CLI MCP integration configured"));
|
|
14642
|
+
console.log(chalk5.green(" \u2713 Codex CLI MCP integration configured (codex mcp add)"));
|
|
13690
14643
|
} else if (codexMcpStatus === "skipped") {
|
|
13691
|
-
console.log(chalk5.yellow(" \u26A0 Codex CLI MCP
|
|
14644
|
+
console.log(chalk5.yellow(" \u26A0 Codex CLI not available, skipped MCP setup"));
|
|
13692
14645
|
} else {
|
|
13693
14646
|
console.log(chalk5.yellow(" \u26A0 Codex CLI MCP configuration failed"));
|
|
13694
14647
|
console.log(chalk5.gray(" CLI integration still works via subprocess"));
|
|
13695
14648
|
}
|
|
13696
14649
|
}
|
|
13697
|
-
let
|
|
13698
|
-
if (providers["
|
|
13699
|
-
console.log(chalk5.cyan("\u{1F50C} Setting up
|
|
13700
|
-
|
|
13701
|
-
if (
|
|
13702
|
-
console.log(chalk5.green(" \u2713
|
|
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"));
|
|
13703
14658
|
} else {
|
|
13704
|
-
console.log(chalk5.yellow(" \u26A0
|
|
13705
|
-
}
|
|
13706
|
-
}
|
|
13707
|
-
let grokMcpStatus = "skipped";
|
|
13708
|
-
if (providers["grok"]) {
|
|
13709
|
-
console.log(chalk5.cyan("\u{1F50C} Setting up Grok (ax-grok) MCP integration..."));
|
|
13710
|
-
grokMcpStatus = await setupGrokMCPConfig(projectDir);
|
|
13711
|
-
if (grokMcpStatus === "configured") {
|
|
13712
|
-
console.log(chalk5.green(" \u2713 Grok MCP integration configured (.ax-grok/.mcp.json)"));
|
|
13713
|
-
} else {
|
|
13714
|
-
console.log(chalk5.yellow(" \u26A0 Grok MCP configuration failed"));
|
|
14659
|
+
console.log(chalk5.yellow(" \u26A0 Qwen MCP configuration failed"));
|
|
13715
14660
|
}
|
|
13716
14661
|
}
|
|
13717
14662
|
console.log(chalk5.cyan("\u{1F4DD} Updating .gitignore..."));
|
|
@@ -13742,9 +14687,11 @@ var setupCommand = {
|
|
|
13742
14687
|
console.log(chalk5.gray(" This is fine - MCP discovery will work without manifests"));
|
|
13743
14688
|
}
|
|
13744
14689
|
}
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
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
|
+
}
|
|
13748
14695
|
console.log(chalk5.green.bold("\n\u2705 AutomatosX set up successfully!\n"));
|
|
13749
14696
|
if (foundProviders.length > 0) {
|
|
13750
14697
|
console.log(chalk5.cyan(`Configured for: ${foundProviders.join(", ")}
|
|
@@ -13753,14 +14700,15 @@ var setupCommand = {
|
|
|
13753
14700
|
console.log(chalk5.yellow("\u26A0\uFE0F No AI providers configured\n"));
|
|
13754
14701
|
}
|
|
13755
14702
|
console.log(chalk5.gray("Next steps:"));
|
|
13756
|
-
console.log(chalk5.gray(
|
|
13757
|
-
console.log(chalk5.gray(" 2.
|
|
13758
|
-
console.log(chalk5.gray(
|
|
13759
|
-
console.log(chalk5.
|
|
13760
|
-
console.log(chalk5.
|
|
13761
|
-
console.log(chalk5.gray(" \u2022
|
|
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"));
|
|
13762
14710
|
console.log(chalk5.gray(" \u2022 Use get_capabilities tool to discover agents dynamically"));
|
|
13763
|
-
console.log(chalk5.gray(" \u2022
|
|
14711
|
+
console.log(chalk5.gray(" \u2022 GLM/Grok are SDK-only (no MCP CLI setup needed)\n"));
|
|
13764
14712
|
if (claudeCodeSetupSucceeded) {
|
|
13765
14713
|
console.log(chalk5.yellow("Legacy Claude Code Manifests (deprecated):"));
|
|
13766
14714
|
console.log(chalk5.gray(" \u2022 Slash commands: /agent-<name> (will be removed in v14.0.0)"));
|
|
@@ -13810,7 +14758,7 @@ var setupCommand = {
|
|
|
13810
14758
|
console.log(chalk5.gray(' \u2022 Example: "Use ax agent backend to create a REST API"'));
|
|
13811
14759
|
console.log(chalk5.gray(" \u2022 No special commands needed - just ask naturally!\n"));
|
|
13812
14760
|
}
|
|
13813
|
-
if (providers["glm"] || providers["grok"]) {
|
|
14761
|
+
if (providers["glm"] || providers["grok"] || providers["qwen"]) {
|
|
13814
14762
|
console.log(chalk5.cyan("SDK-First Providers (MCP Client Mode):"));
|
|
13815
14763
|
if (providers["glm"]) {
|
|
13816
14764
|
console.log(chalk5.gray(" \u2022 GLM (Zhipu AI) - API key: GLM_API_KEY"));
|
|
@@ -13820,8 +14768,12 @@ var setupCommand = {
|
|
|
13820
14768
|
console.log(chalk5.gray(" \u2022 Grok (xAI) - API key: XAI_API_KEY"));
|
|
13821
14769
|
console.log(chalk5.gray(" MCP: AxGrokWithMcp class connects to AutomatosX MCP server"));
|
|
13822
14770
|
}
|
|
13823
|
-
|
|
13824
|
-
|
|
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"));
|
|
13825
14777
|
}
|
|
13826
14778
|
if (providers["codex"]) {
|
|
13827
14779
|
console.log(chalk5.cyan("Codex CLI Integration:"));
|
|
@@ -14343,9 +15295,9 @@ async function initializeGitRepository(projectDir) {
|
|
|
14343
15295
|
logger.info("Git repository already exists, skipping initialization");
|
|
14344
15296
|
return true;
|
|
14345
15297
|
}
|
|
14346
|
-
const { spawn:
|
|
15298
|
+
const { spawn: spawn13 } = await import('child_process');
|
|
14347
15299
|
await new Promise((resolve13, reject) => {
|
|
14348
|
-
const child =
|
|
15300
|
+
const child = spawn13("git", ["init"], {
|
|
14349
15301
|
cwd: projectDir,
|
|
14350
15302
|
stdio: "pipe",
|
|
14351
15303
|
shell: false
|
|
@@ -14426,143 +15378,71 @@ async function updateGitignore(projectDir) {
|
|
|
14426
15378
|
logger.warn("Failed to update .gitignore", { error: error.message });
|
|
14427
15379
|
}
|
|
14428
15380
|
}
|
|
14429
|
-
async function setupGeminiIntegration(
|
|
14430
|
-
const mcpStatus = await
|
|
15381
|
+
async function setupGeminiIntegration(projectDir, _packageRoot, force = false) {
|
|
15382
|
+
const mcpStatus = await setupGeminiMCPViaCLI(projectDir, force);
|
|
14431
15383
|
return mcpStatus;
|
|
14432
15384
|
}
|
|
14433
|
-
async function
|
|
14434
|
-
const
|
|
14435
|
-
|
|
14436
|
-
|
|
14437
|
-
return "skipped";
|
|
14438
|
-
}
|
|
14439
|
-
const settingsPath = join(homeDir, ".gemini", "settings.json");
|
|
14440
|
-
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);
|
|
14441
15389
|
try {
|
|
14442
|
-
await
|
|
14443
|
-
|
|
14444
|
-
|
|
14445
|
-
|
|
14446
|
-
|
|
14447
|
-
settings = JSON.parse(existingContent);
|
|
14448
|
-
} catch (error) {
|
|
14449
|
-
logger.warn("Failed to parse existing Gemini settings, will merge carefully", {
|
|
14450
|
-
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(() => {
|
|
14451
15395
|
});
|
|
15396
|
+
} else {
|
|
15397
|
+
logger.info("Gemini CLI MCP server already configured");
|
|
15398
|
+
return "configured";
|
|
14452
15399
|
}
|
|
14453
15400
|
}
|
|
14454
|
-
|
|
14455
|
-
|
|
14456
|
-
|
|
14457
|
-
}
|
|
14458
|
-
mcpServers["automatosx"] = {
|
|
14459
|
-
command: "automatosx",
|
|
14460
|
-
args: ["mcp", "server"]
|
|
14461
|
-
};
|
|
14462
|
-
settings["mcpServers"] = mcpServers;
|
|
14463
|
-
await writeFile(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
14464
|
-
logger.info("Configured Gemini CLI MCP in global settings", {
|
|
14465
|
-
path: settingsPath
|
|
14466
|
-
});
|
|
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");
|
|
14467
15404
|
return "configured";
|
|
14468
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
|
+
}
|
|
14469
15411
|
logger.warn("Failed to setup Gemini CLI MCP configuration", {
|
|
14470
|
-
error:
|
|
14471
|
-
path: settingsPath
|
|
15412
|
+
error: errorMessage
|
|
14472
15413
|
});
|
|
14473
15414
|
return "failed";
|
|
14474
15415
|
}
|
|
14475
15416
|
}
|
|
14476
|
-
async function setupCodexGlobalMCPConfig() {
|
|
14477
|
-
const
|
|
14478
|
-
|
|
14479
|
-
|
|
14480
|
-
return "skipped";
|
|
14481
|
-
}
|
|
14482
|
-
const codexDir = join(homeDir, ".codex");
|
|
14483
|
-
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);
|
|
14484
15421
|
try {
|
|
14485
|
-
await
|
|
14486
|
-
|
|
14487
|
-
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
|
|
14491
|
-
|
|
14492
|
-
|
|
14493
|
-
|
|
14494
|
-
const mcpConfig = `
|
|
14495
|
-
# AutomatosX MCP Server - Added by ax setup v12.2.0
|
|
14496
|
-
# Increased timeouts for lazy initialization (15-20s on first tool call)
|
|
14497
|
-
[mcp_servers.automatosx]
|
|
14498
|
-
command = "automatosx"
|
|
14499
|
-
args = ["mcp", "server"]
|
|
14500
|
-
startup_timeout_sec = 60
|
|
14501
|
-
tool_timeout_sec = 120
|
|
14502
|
-
`;
|
|
14503
|
-
await writeFile(configPath, existingContent + mcpConfig, "utf-8");
|
|
14504
|
-
logger.info("Configured Codex CLI MCP in global config", {
|
|
14505
|
-
path: configPath
|
|
14506
|
-
});
|
|
14507
|
-
return "configured";
|
|
14508
|
-
} catch (error) {
|
|
14509
|
-
logger.warn("Failed to setup Codex CLI MCP configuration", {
|
|
14510
|
-
error: error.message,
|
|
14511
|
-
path: configPath
|
|
14512
|
-
});
|
|
14513
|
-
return "failed";
|
|
14514
|
-
}
|
|
14515
|
-
}
|
|
14516
|
-
async function setupGlmMCPConfig(projectDir) {
|
|
14517
|
-
const configDir = join(projectDir, ".ax-glm");
|
|
14518
|
-
const configPath = join(configDir, ".mcp.json");
|
|
14519
|
-
try {
|
|
14520
|
-
await mkdir(configDir, { recursive: true });
|
|
14521
|
-
const mcpConfig = {
|
|
14522
|
-
mcpServers: {
|
|
14523
|
-
automatosx: {
|
|
14524
|
-
command: "automatosx",
|
|
14525
|
-
args: ["mcp", "server"],
|
|
14526
|
-
env: {
|
|
14527
|
-
AUTOMATOSX_PROJECT_DIR: projectDir
|
|
14528
|
-
}
|
|
14529
|
-
}
|
|
14530
|
-
}
|
|
14531
|
-
};
|
|
14532
|
-
await writeFile(configPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
|
|
14533
|
-
logger.info("Configured GLM MCP integration", { path: configPath });
|
|
14534
|
-
return "configured";
|
|
14535
|
-
} catch (error) {
|
|
14536
|
-
logger.warn("Failed to setup GLM MCP configuration", {
|
|
14537
|
-
error: error.message,
|
|
14538
|
-
path: configPath
|
|
14539
|
-
});
|
|
14540
|
-
return "failed";
|
|
14541
|
-
}
|
|
14542
|
-
}
|
|
14543
|
-
async function setupGrokMCPConfig(projectDir) {
|
|
14544
|
-
const configDir = join(projectDir, ".ax-grok");
|
|
14545
|
-
const configPath = join(configDir, ".mcp.json");
|
|
14546
|
-
try {
|
|
14547
|
-
await mkdir(configDir, { recursive: true });
|
|
14548
|
-
const mcpConfig = {
|
|
14549
|
-
mcpServers: {
|
|
14550
|
-
automatosx: {
|
|
14551
|
-
command: "automatosx",
|
|
14552
|
-
args: ["mcp", "server"],
|
|
14553
|
-
env: {
|
|
14554
|
-
AUTOMATOSX_PROJECT_DIR: projectDir
|
|
14555
|
-
}
|
|
14556
|
-
}
|
|
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";
|
|
14557
15431
|
}
|
|
14558
|
-
}
|
|
14559
|
-
|
|
14560
|
-
|
|
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");
|
|
14561
15437
|
return "configured";
|
|
14562
15438
|
} catch (error) {
|
|
14563
|
-
|
|
14564
|
-
|
|
14565
|
-
|
|
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
|
|
14566
15446
|
});
|
|
14567
15447
|
return "failed";
|
|
14568
15448
|
}
|
|
@@ -14611,41 +15491,66 @@ Created by AutomatosX for organized scratch work.
|
|
|
14611
15491
|
});
|
|
14612
15492
|
}
|
|
14613
15493
|
}
|
|
14614
|
-
async function createMcpConfig(projectDir) {
|
|
14615
|
-
const
|
|
14616
|
-
const
|
|
14617
|
-
|
|
14618
|
-
|
|
14619
|
-
|
|
14620
|
-
|
|
14621
|
-
|
|
14622
|
-
|
|
14623
|
-
}
|
|
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;
|
|
14624
15508
|
}
|
|
14625
15509
|
}
|
|
14626
|
-
|
|
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);
|
|
14627
15528
|
try {
|
|
14628
|
-
|
|
14629
|
-
|
|
14630
|
-
|
|
14631
|
-
|
|
14632
|
-
|
|
14633
|
-
|
|
14634
|
-
|
|
14635
|
-
|
|
14636
|
-
|
|
14637
|
-
}
|
|
14638
|
-
} catch {
|
|
14639
|
-
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";
|
|
14640
15538
|
}
|
|
14641
15539
|
}
|
|
14642
|
-
|
|
14643
|
-
|
|
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";
|
|
14644
15544
|
} catch (error) {
|
|
14645
|
-
|
|
14646
|
-
|
|
14647
|
-
|
|
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
|
|
14648
15552
|
});
|
|
15553
|
+
return "failed";
|
|
14649
15554
|
}
|
|
14650
15555
|
}
|
|
14651
15556
|
async function cleanupForceMode(projectDir) {
|
|
@@ -14800,7 +15705,7 @@ async function parsePackageJson(projectDir, info) {
|
|
|
14800
15705
|
if (packageJson.workspaces || devDeps.includes("lerna") || devDeps.includes("nx") || devDeps.includes("turborepo") || existsSync(join(projectDir, "lerna.json")) || existsSync(join(projectDir, "nx.json"))) {
|
|
14801
15706
|
info.isMonorepo = true;
|
|
14802
15707
|
}
|
|
14803
|
-
} catch
|
|
15708
|
+
} catch {
|
|
14804
15709
|
}
|
|
14805
15710
|
}
|
|
14806
15711
|
async function parseReadmeDescription(projectDir, info) {
|
|
@@ -14861,10 +15766,10 @@ async function parseReadmeDescription(projectDir, info) {
|
|
|
14861
15766
|
info.teamSize = "Open source community";
|
|
14862
15767
|
}
|
|
14863
15768
|
}
|
|
14864
|
-
} catch
|
|
15769
|
+
} catch {
|
|
14865
15770
|
}
|
|
14866
15771
|
}
|
|
14867
|
-
function categorizeScripts(scripts,
|
|
15772
|
+
function categorizeScripts(scripts, _packageManager) {
|
|
14868
15773
|
const categorized = {
|
|
14869
15774
|
development: [],
|
|
14870
15775
|
building: [],
|
|
@@ -14974,7 +15879,7 @@ async function analyzeFileStructure(projectDir) {
|
|
|
14974
15879
|
if (name === "docs") {
|
|
14975
15880
|
structure.docsDirectory = name;
|
|
14976
15881
|
}
|
|
14977
|
-
} catch
|
|
15882
|
+
} catch {
|
|
14978
15883
|
}
|
|
14979
15884
|
}
|
|
14980
15885
|
}
|
|
@@ -15004,7 +15909,7 @@ async function countFilesRecursive(dirPath, depth = 0) {
|
|
|
15004
15909
|
}
|
|
15005
15910
|
return count;
|
|
15006
15911
|
}
|
|
15007
|
-
async function detectKeyComponents(projectDir,
|
|
15912
|
+
async function detectKeyComponents(projectDir, _info) {
|
|
15008
15913
|
const components = [];
|
|
15009
15914
|
const srcDir = join(projectDir, "src");
|
|
15010
15915
|
if (!existsSync(srcDir)) return components;
|
|
@@ -15303,7 +16208,7 @@ async function detectDatabaseSchema(projectDir, info) {
|
|
|
15303
16208
|
}
|
|
15304
16209
|
return void 0;
|
|
15305
16210
|
}
|
|
15306
|
-
async function detectPrismaSchema(projectDir, schemaPath,
|
|
16211
|
+
async function detectPrismaSchema(projectDir, schemaPath, _info) {
|
|
15307
16212
|
const models = [];
|
|
15308
16213
|
const migrations = await detectMigrations(projectDir, "prisma/migrations");
|
|
15309
16214
|
try {
|
|
@@ -15357,7 +16262,7 @@ async function detectPrismaSchema(projectDir, schemaPath, info) {
|
|
|
15357
16262
|
relations
|
|
15358
16263
|
});
|
|
15359
16264
|
}
|
|
15360
|
-
} catch
|
|
16265
|
+
} catch {
|
|
15361
16266
|
}
|
|
15362
16267
|
return {
|
|
15363
16268
|
orm: "Prisma",
|
|
@@ -15502,290 +16407,9 @@ async function findFilesRecursive(dir, pattern, maxDepth = 3, currentDepth = 0)
|
|
|
15502
16407
|
return files;
|
|
15503
16408
|
}
|
|
15504
16409
|
|
|
15505
|
-
// src/cli/commands/init-parser.ts
|
|
15506
|
-
init_esm_shims();
|
|
15507
|
-
function parseAXMD(content) {
|
|
15508
|
-
const result = {
|
|
15509
|
-
dependencies: [],
|
|
15510
|
-
scripts: {},
|
|
15511
|
-
hasCustomContent: false,
|
|
15512
|
-
customSections: []
|
|
15513
|
-
};
|
|
15514
|
-
const versionMatch = content.match(/Project:.*?\(v([\d.]+)\)/);
|
|
15515
|
-
if (versionMatch) {
|
|
15516
|
-
result.version = versionMatch[1];
|
|
15517
|
-
}
|
|
15518
|
-
const nameMatch = content.match(/# Project Context for (.+)/);
|
|
15519
|
-
if (nameMatch) {
|
|
15520
|
-
result.projectName = nameMatch[1];
|
|
15521
|
-
}
|
|
15522
|
-
const descMatch = content.match(/## Project Overview\n\n\*\*(.+?)\*\*/);
|
|
15523
|
-
if (descMatch) {
|
|
15524
|
-
result.description = descMatch[1];
|
|
15525
|
-
}
|
|
15526
|
-
const archMatch = content.match(/\*\*Architecture:\*\* (.+)/);
|
|
15527
|
-
if (archMatch) {
|
|
15528
|
-
result.architecture = archMatch[1];
|
|
15529
|
-
}
|
|
15530
|
-
const stackMatch = content.match(/\*\*Stack:\*\* (.+)/);
|
|
15531
|
-
if (stackMatch && stackMatch[1]) {
|
|
15532
|
-
result.stack = stackMatch[1];
|
|
15533
|
-
result.dependencies = stackMatch[1].split(",").map((d) => d.trim().toLowerCase()).filter(Boolean);
|
|
15534
|
-
}
|
|
15535
|
-
const teamMatch = content.match(/\*\*Team:\*\* (.+)/);
|
|
15536
|
-
if (teamMatch) {
|
|
15537
|
-
result.team = teamMatch[1];
|
|
15538
|
-
}
|
|
15539
|
-
const testMatch = content.match(/- Framework: (.+)/);
|
|
15540
|
-
if (testMatch) {
|
|
15541
|
-
result.testFramework = testMatch[1];
|
|
15542
|
-
}
|
|
15543
|
-
const linterMatch = content.match(/- Linter: (.+)/);
|
|
15544
|
-
if (linterMatch) {
|
|
15545
|
-
result.linter = linterMatch[1];
|
|
15546
|
-
}
|
|
15547
|
-
const formatterMatch = content.match(/- Formatter: (.+)/);
|
|
15548
|
-
if (formatterMatch) {
|
|
15549
|
-
result.formatter = formatterMatch[1];
|
|
15550
|
-
}
|
|
15551
|
-
const commandsMatch = content.match(/## Canonical Commands\n\n```bash\n([\s\S]*?)```/);
|
|
15552
|
-
if (commandsMatch && commandsMatch[1]) {
|
|
15553
|
-
const commandsSection = commandsMatch[1];
|
|
15554
|
-
const lines = commandsSection.split("\n");
|
|
15555
|
-
for (const line of lines) {
|
|
15556
|
-
const cmdMatch = line.match(/^([^\s#]+(?:\s+[^\s#]+)*)\s*#/);
|
|
15557
|
-
if (cmdMatch && cmdMatch[1]) {
|
|
15558
|
-
const cmd = cmdMatch[1].trim();
|
|
15559
|
-
result.scripts[cmd] = cmd;
|
|
15560
|
-
}
|
|
15561
|
-
}
|
|
15562
|
-
}
|
|
15563
|
-
const customMarkers = [
|
|
15564
|
-
/<!-- USER-EDITED -->/,
|
|
15565
|
-
/## Our Custom/,
|
|
15566
|
-
/## Notes/,
|
|
15567
|
-
/## Team Guidelines/
|
|
15568
|
-
];
|
|
15569
|
-
for (const marker of customMarkers) {
|
|
15570
|
-
if (marker.test(content)) {
|
|
15571
|
-
result.hasCustomContent = true;
|
|
15572
|
-
break;
|
|
15573
|
-
}
|
|
15574
|
-
}
|
|
15575
|
-
const sections = content.split(/^## /m);
|
|
15576
|
-
const standardSections = [
|
|
15577
|
-
"Project Overview",
|
|
15578
|
-
"Agent Delegation Rules",
|
|
15579
|
-
"Coding Conventions",
|
|
15580
|
-
"Critical Guardrails",
|
|
15581
|
-
"Canonical Commands",
|
|
15582
|
-
"Useful Links"
|
|
15583
|
-
];
|
|
15584
|
-
for (const section of sections) {
|
|
15585
|
-
const sectionName = section.split("\n")[0] ?? "";
|
|
15586
|
-
if (sectionName && !standardSections.includes(sectionName)) {
|
|
15587
|
-
result.customSections.push(sectionName);
|
|
15588
|
-
}
|
|
15589
|
-
}
|
|
15590
|
-
return result;
|
|
15591
|
-
}
|
|
15592
|
-
function detectDetailedChanges(parsed, current) {
|
|
15593
|
-
const changes = [];
|
|
15594
|
-
if (parsed.version !== current.version && current.version) {
|
|
15595
|
-
changes.push({
|
|
15596
|
-
type: "version",
|
|
15597
|
-
field: "version",
|
|
15598
|
-
oldValue: parsed.version,
|
|
15599
|
-
newValue: current.version
|
|
15600
|
-
});
|
|
15601
|
-
}
|
|
15602
|
-
const currentDeps = current.dependencies.map((d) => d.toLowerCase());
|
|
15603
|
-
const addedDeps = currentDeps.filter((d) => !parsed.dependencies.includes(d));
|
|
15604
|
-
if (addedDeps.length > 0) {
|
|
15605
|
-
changes.push({
|
|
15606
|
-
type: "dependency-added",
|
|
15607
|
-
field: "dependencies",
|
|
15608
|
-
items: addedDeps
|
|
15609
|
-
});
|
|
15610
|
-
}
|
|
15611
|
-
const removedDeps = parsed.dependencies.filter((d) => !currentDeps.includes(d));
|
|
15612
|
-
if (removedDeps.length > 0) {
|
|
15613
|
-
changes.push({
|
|
15614
|
-
type: "dependency-removed",
|
|
15615
|
-
field: "dependencies",
|
|
15616
|
-
items: removedDeps
|
|
15617
|
-
});
|
|
15618
|
-
}
|
|
15619
|
-
const currentScriptKeys = Object.keys(current.scripts);
|
|
15620
|
-
const parsedScriptKeys = Object.keys(parsed.scripts);
|
|
15621
|
-
const addedScripts = currentScriptKeys.filter((s) => !parsedScriptKeys.includes(s));
|
|
15622
|
-
if (addedScripts.length > 0) {
|
|
15623
|
-
changes.push({
|
|
15624
|
-
type: "script-added",
|
|
15625
|
-
field: "scripts",
|
|
15626
|
-
items: addedScripts
|
|
15627
|
-
});
|
|
15628
|
-
}
|
|
15629
|
-
const removedScripts = parsedScriptKeys.filter((s) => !currentScriptKeys.includes(s));
|
|
15630
|
-
if (removedScripts.length > 0) {
|
|
15631
|
-
changes.push({
|
|
15632
|
-
type: "script-removed",
|
|
15633
|
-
field: "scripts",
|
|
15634
|
-
items: removedScripts
|
|
15635
|
-
});
|
|
15636
|
-
}
|
|
15637
|
-
if (current.linter && !parsed.linter) {
|
|
15638
|
-
changes.push({
|
|
15639
|
-
type: "tool-added",
|
|
15640
|
-
field: "linter",
|
|
15641
|
-
newValue: current.linter
|
|
15642
|
-
});
|
|
15643
|
-
}
|
|
15644
|
-
if (current.formatter && !parsed.formatter) {
|
|
15645
|
-
changes.push({
|
|
15646
|
-
type: "tool-added",
|
|
15647
|
-
field: "formatter",
|
|
15648
|
-
newValue: current.formatter
|
|
15649
|
-
});
|
|
15650
|
-
}
|
|
15651
|
-
if (current.testFramework && !parsed.testFramework) {
|
|
15652
|
-
changes.push({
|
|
15653
|
-
type: "tool-added",
|
|
15654
|
-
field: "test framework",
|
|
15655
|
-
newValue: current.testFramework
|
|
15656
|
-
});
|
|
15657
|
-
}
|
|
15658
|
-
if (parsed.architecture && parsed.architecture !== current.architecture) {
|
|
15659
|
-
changes.push({
|
|
15660
|
-
type: "architecture",
|
|
15661
|
-
field: "architecture",
|
|
15662
|
-
oldValue: parsed.architecture,
|
|
15663
|
-
newValue: current.architecture
|
|
15664
|
-
});
|
|
15665
|
-
}
|
|
15666
|
-
return changes;
|
|
15667
|
-
}
|
|
15668
|
-
function formatChangeSummary(changes) {
|
|
15669
|
-
if (changes.length === 0) return "";
|
|
15670
|
-
const summaries = [];
|
|
15671
|
-
for (const change of changes) {
|
|
15672
|
-
switch (change.type) {
|
|
15673
|
-
case "version":
|
|
15674
|
-
summaries.push(`version ${change.oldValue} \u2192 ${change.newValue}`);
|
|
15675
|
-
break;
|
|
15676
|
-
case "dependency-added":
|
|
15677
|
-
summaries.push(`+${change.items.length} dep${change.items.length > 1 ? "s" : ""}`);
|
|
15678
|
-
break;
|
|
15679
|
-
case "dependency-removed":
|
|
15680
|
-
summaries.push(`-${change.items.length} dep${change.items.length > 1 ? "s" : ""}`);
|
|
15681
|
-
break;
|
|
15682
|
-
case "script-added":
|
|
15683
|
-
summaries.push(`+${change.items.length} script${change.items.length > 1 ? "s" : ""}`);
|
|
15684
|
-
break;
|
|
15685
|
-
case "tool-added":
|
|
15686
|
-
summaries.push(`+${change.field}`);
|
|
15687
|
-
break;
|
|
15688
|
-
case "architecture":
|
|
15689
|
-
summaries.push("architecture");
|
|
15690
|
-
break;
|
|
15691
|
-
}
|
|
15692
|
-
}
|
|
15693
|
-
if (summaries.length === 0) return "";
|
|
15694
|
-
if (summaries.length === 1) return `(${summaries[0]})`;
|
|
15695
|
-
if (summaries.length === 2) return `(${summaries[0]}, ${summaries[1]})`;
|
|
15696
|
-
return `(${summaries.length} changes)`;
|
|
15697
|
-
}
|
|
15698
|
-
|
|
15699
16410
|
// src/cli/commands/init.ts
|
|
15700
|
-
|
|
15701
|
-
|
|
15702
|
-
return `# Project Context for ${info.name}
|
|
15703
|
-
|
|
15704
|
-
> Last updated: ${today}
|
|
15705
|
-
> Project: ${info.name}${info.version ? ` (v${info.version})` : ""}
|
|
15706
|
-
|
|
15707
|
-
## Project Overview
|
|
15708
|
-
|
|
15709
|
-
${generateProjectOverview(info)}
|
|
15710
|
-
|
|
15711
|
-
## Architecture
|
|
15712
|
-
|
|
15713
|
-
${generateArchitecture(info)}
|
|
15714
|
-
|
|
15715
|
-
## File Structure
|
|
15716
|
-
|
|
15717
|
-
${generateFileStructure(info)}
|
|
15718
|
-
|
|
15719
|
-
${generateGettingStarted(info)}
|
|
15720
|
-
|
|
15721
|
-
${generateTroubleshooting(info)}
|
|
15722
|
-
|
|
15723
|
-
${generateDevWorkflow(info)}
|
|
15724
|
-
|
|
15725
|
-
${generateDatabaseSchema(info)}
|
|
15726
|
-
|
|
15727
|
-
${generateAPIDocumentation(info)}
|
|
15728
|
-
|
|
15729
|
-
${generateAgentRules(info)}
|
|
15730
|
-
|
|
15731
|
-
${generateCodingConventions(info)}
|
|
15732
|
-
|
|
15733
|
-
${generateCriticalGuardrails(info)}
|
|
15734
|
-
|
|
15735
|
-
${generateCommands(info)}
|
|
15736
|
-
|
|
15737
|
-
${generateUsefulLinks(info)}
|
|
15738
|
-
|
|
15739
|
-
---
|
|
15740
|
-
|
|
15741
|
-
**Generated by \`ax init\` \u2022 Run regularly to keep up-to-date**
|
|
15742
|
-
`;
|
|
15743
|
-
}
|
|
15744
|
-
function generateProjectOverview(info) {
|
|
15745
|
-
const sections = [];
|
|
15746
|
-
if (info.detailedDescription) {
|
|
15747
|
-
sections.push(info.detailedDescription);
|
|
15748
|
-
} else if (info.description) {
|
|
15749
|
-
sections.push(`**${info.description}**`);
|
|
15750
|
-
} else {
|
|
15751
|
-
sections.push(`**${info.framework || "Node.js"} project${info.hasTypeScript ? " with TypeScript" : ""}**`);
|
|
15752
|
-
}
|
|
15753
|
-
const keyInfo = [];
|
|
15754
|
-
keyInfo.push(`**Version:** ${info.version || "Not specified"}`);
|
|
15755
|
-
keyInfo.push(`**Language:** ${info.language}`);
|
|
15756
|
-
if (info.framework) keyInfo.push(`**Framework:** ${info.framework}`);
|
|
15757
|
-
if (info.buildTool) keyInfo.push(`**Build Tool:** ${info.buildTool}`);
|
|
15758
|
-
if (info.testFramework) keyInfo.push(`**Test Framework:** ${info.testFramework}`);
|
|
15759
|
-
if (info.isMonorepo) keyInfo.push(`**Type:** Monorepo`);
|
|
15760
|
-
sections.push("\n" + keyInfo.join(" \n"));
|
|
15761
|
-
sections.push(`
|
|
15762
|
-
**Stack:** ${info.stack}`);
|
|
15763
|
-
if (info.teamSize) {
|
|
15764
|
-
sections.push(`
|
|
15765
|
-
**Team:** ${info.teamSize}`);
|
|
15766
|
-
}
|
|
15767
|
-
return sections.join("\n");
|
|
15768
|
-
}
|
|
15769
|
-
function generateArchitecture(info) {
|
|
15770
|
-
const sections = [];
|
|
15771
|
-
sections.push(`**Type:** ${detectArchitectureType(info)}`);
|
|
15772
|
-
if (info.architectureFlow) {
|
|
15773
|
-
sections.push("\n**Flow:**\n```");
|
|
15774
|
-
sections.push(info.architectureFlow.trim());
|
|
15775
|
-
sections.push("```");
|
|
15776
|
-
}
|
|
15777
|
-
if (info.keyComponents && info.keyComponents.length > 0) {
|
|
15778
|
-
sections.push("\n**Key Components:**");
|
|
15779
|
-
for (const comp of info.keyComponents) {
|
|
15780
|
-
if (comp.purpose) {
|
|
15781
|
-
sections.push(`- \`${comp.path}\` - ${comp.purpose}`);
|
|
15782
|
-
} else {
|
|
15783
|
-
sections.push(`- \`${comp.path}\``);
|
|
15784
|
-
}
|
|
15785
|
-
}
|
|
15786
|
-
}
|
|
15787
|
-
return sections.join("\n");
|
|
15788
|
-
}
|
|
16411
|
+
var execAsync3 = promisify(exec);
|
|
16412
|
+
var STALE_THRESHOLD_MS = 24 * 60 * 60 * 1e3;
|
|
15789
16413
|
function detectArchitectureType(info) {
|
|
15790
16414
|
if (info.dependencies.includes("next") || info.dependencies.includes("nuxt")) {
|
|
15791
16415
|
return "Full-stack framework (SSR/SSG)";
|
|
@@ -15807,459 +16431,424 @@ function detectArchitectureType(info) {
|
|
|
15807
16431
|
}
|
|
15808
16432
|
return "Node.js application";
|
|
15809
16433
|
}
|
|
15810
|
-
function
|
|
15811
|
-
|
|
15812
|
-
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15819
|
-
|
|
15820
|
-
|
|
15821
|
-
sections.push(`- \`${dir.path}/\` - ${dir.purpose} (${dir.fileCount} files)`);
|
|
15822
|
-
} else {
|
|
15823
|
-
sections.push(`- \`${dir.path}/\` - ${dir.fileCount} files`);
|
|
15824
|
-
}
|
|
15825
|
-
}
|
|
15826
|
-
sections.push(`
|
|
15827
|
-
**Total Files:** ${info.fileStructure.totalFiles}`);
|
|
15828
|
-
return sections.join("\n");
|
|
15829
|
-
}
|
|
15830
|
-
function generateGettingStarted(info) {
|
|
15831
|
-
if (!info.gettingStarted) {
|
|
15832
|
-
return "";
|
|
15833
|
-
}
|
|
15834
|
-
const sections = ["## Getting Started", ""];
|
|
15835
|
-
if (info.gettingStarted.prerequisites.length > 0) {
|
|
15836
|
-
sections.push("### Prerequisites");
|
|
15837
|
-
for (const prereq of info.gettingStarted.prerequisites) {
|
|
15838
|
-
sections.push(`- ${prereq}`);
|
|
15839
|
-
}
|
|
15840
|
-
sections.push("");
|
|
15841
|
-
}
|
|
15842
|
-
if (info.gettingStarted.setupSteps.length > 0) {
|
|
15843
|
-
sections.push("### First Time Setup");
|
|
15844
|
-
for (const step of info.gettingStarted.setupSteps) {
|
|
15845
|
-
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
|
+
});
|
|
15846
16445
|
}
|
|
15847
|
-
sections.push("");
|
|
15848
16446
|
}
|
|
15849
|
-
|
|
15850
|
-
|
|
15851
|
-
|
|
15852
|
-
|
|
15853
|
-
|
|
15854
|
-
|
|
15855
|
-
|
|
15856
|
-
|
|
15857
|
-
|
|
15858
|
-
|
|
15859
|
-
|
|
15860
|
-
|
|
15861
|
-
|
|
15862
|
-
|
|
15863
|
-
|
|
15864
|
-
|
|
15865
|
-
|
|
15866
|
-
|
|
15867
|
-
|
|
15868
|
-
|
|
15869
|
-
|
|
15870
|
-
|
|
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
|
+
};
|
|
15871
16476
|
}
|
|
15872
|
-
sections.push("");
|
|
15873
16477
|
}
|
|
15874
16478
|
}
|
|
15875
|
-
|
|
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
|
+
};
|
|
15876
16513
|
}
|
|
15877
|
-
function
|
|
15878
|
-
const
|
|
15879
|
-
sections
|
|
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}`);
|
|
15880
16518
|
sections.push("");
|
|
15881
|
-
sections.push(
|
|
15882
|
-
sections.push(
|
|
16519
|
+
sections.push(`> Generated: ${today}`);
|
|
16520
|
+
sections.push(`> Project: ${info.name}${info.version ? ` v${info.version}` : ""}`);
|
|
15883
16521
|
sections.push("");
|
|
15884
|
-
|
|
15885
|
-
sections.push("**Problem**: Database connection refused");
|
|
15886
|
-
sections.push("**Solution**:");
|
|
15887
|
-
sections.push("1. Check Docker is running: `docker ps`");
|
|
15888
|
-
sections.push("2. Start services: `docker-compose up -d`");
|
|
15889
|
-
if (info.scripts["db:ping"]) {
|
|
15890
|
-
sections.push(`3. Verify connection: \`${info.packageManager} run db:ping\``);
|
|
15891
|
-
}
|
|
15892
|
-
sections.push("");
|
|
15893
|
-
}
|
|
15894
|
-
sections.push("**Problem**: Port already in use");
|
|
15895
|
-
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.");
|
|
15896
16523
|
sections.push("");
|
|
15897
|
-
|
|
15898
|
-
|
|
15899
|
-
|
|
15900
|
-
|
|
15901
|
-
|
|
15902
|
-
if (info.
|
|
15903
|
-
sections.push(
|
|
15904
|
-
|
|
15905
|
-
sections.push("");
|
|
15906
|
-
}
|
|
15907
|
-
sections.push("### Debug Mode");
|
|
15908
|
-
sections.push("Run with verbose logging:");
|
|
15909
|
-
sections.push("```bash");
|
|
15910
|
-
sections.push("DEBUG=* npm run dev");
|
|
15911
|
-
if (info.scripts.test) {
|
|
15912
|
-
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" : ""}`);
|
|
15913
16533
|
}
|
|
15914
|
-
sections.push("```");
|
|
15915
|
-
return sections.join("\n");
|
|
15916
|
-
}
|
|
15917
|
-
function generateDevWorkflow(info) {
|
|
15918
|
-
const sections = ["## Development Workflow", ""];
|
|
15919
|
-
sections.push("### Daily Workflow");
|
|
15920
|
-
sections.push("1. Pull latest: `git pull origin main`");
|
|
15921
|
-
sections.push("2. Create feature branch: `git checkout -b feature/my-feature`");
|
|
15922
|
-
sections.push("3. Make changes");
|
|
15923
|
-
if (info.scripts.test) {
|
|
15924
|
-
sections.push(`4. Run tests: \`${info.packageManager} test\``);
|
|
15925
|
-
}
|
|
15926
|
-
sections.push('5. Commit: `git commit -m "feat: add my feature"`');
|
|
15927
|
-
sections.push("6. Push: `git push origin feature/my-feature`");
|
|
15928
|
-
sections.push("7. Open PR on GitHub");
|
|
15929
|
-
sections.push("8. Wait for CI + reviews");
|
|
15930
|
-
sections.push("9. Merge to main (squash merge)");
|
|
15931
16534
|
sections.push("");
|
|
15932
|
-
sections.push("
|
|
15933
|
-
sections.push("- Minimum 1 approval required");
|
|
15934
|
-
sections.push("- CI must pass (tests + lint)");
|
|
15935
|
-
sections.push("- No merge conflicts");
|
|
16535
|
+
sections.push("## Critical Rules");
|
|
15936
16536
|
sections.push("");
|
|
15937
|
-
sections.push("###
|
|
15938
|
-
if (info.framework === "React" || info.framework === "Vue") {
|
|
15939
|
-
if (info.buildTool === "Vite") {
|
|
15940
|
-
sections.push("- Frontend: Vite HMR (instant)");
|
|
15941
|
-
} else {
|
|
15942
|
-
sections.push("- Frontend: Hot module replacement enabled");
|
|
15943
|
-
}
|
|
15944
|
-
}
|
|
15945
|
-
if (info.dependencies.includes("express") || info.dependencies.includes("fastify")) {
|
|
15946
|
-
sections.push("- Backend: Nodemon (restart on save)");
|
|
15947
|
-
}
|
|
15948
|
-
if (info.framework === "Next.js") {
|
|
15949
|
-
sections.push("- Next.js Fast Refresh (instant updates)");
|
|
15950
|
-
}
|
|
16537
|
+
sections.push("### DO");
|
|
15951
16538
|
sections.push("");
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
sections.push(`- Unit tests: \`${info.packageManager} run test:unit\` (fast, no DB)`);
|
|
15956
|
-
}
|
|
15957
|
-
if (info.scripts["test:integration"]) {
|
|
15958
|
-
sections.push(`- Integration tests: \`${info.packageManager} run test:integration\` (with test DB)`);
|
|
15959
|
-
}
|
|
15960
|
-
if (info.scripts["test:e2e"]) {
|
|
15961
|
-
sections.push(`- E2E tests: \`${info.packageManager} run test:e2e\` (full stack)`);
|
|
15962
|
-
}
|
|
15963
|
-
if (info.scripts.test) {
|
|
15964
|
-
sections.push(`- Run all: \`${info.packageManager} test\``);
|
|
15965
|
-
}
|
|
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`);
|
|
15966
16542
|
}
|
|
15967
|
-
|
|
15968
|
-
|
|
15969
|
-
function generateDatabaseSchema(info) {
|
|
15970
|
-
if (!info.databaseSchema || info.databaseSchema.models.length === 0) {
|
|
15971
|
-
return "";
|
|
16543
|
+
if (info.hasTypeScript) {
|
|
16544
|
+
sections.push("- Use TypeScript strict mode conventions");
|
|
15972
16545
|
}
|
|
15973
|
-
|
|
15974
|
-
|
|
15975
|
-
|
|
15976
|
-
|
|
15977
|
-
|
|
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");
|
|
15978
16556
|
}
|
|
15979
|
-
|
|
15980
|
-
|
|
16557
|
+
sections.push("");
|
|
16558
|
+
if (info.categorizedScripts) {
|
|
16559
|
+
sections.push("## Key Commands");
|
|
15981
16560
|
sections.push("");
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
if (field.defaultValue) attrs.push(`default: ${field.defaultValue}`);
|
|
15992
|
-
if (attrs.length > 0) {
|
|
15993
|
-
fieldDesc += ` (${attrs.join(", ")})`;
|
|
15994
|
-
}
|
|
15995
|
-
sections.push(fieldDesc);
|
|
15996
|
-
}
|
|
15997
|
-
}
|
|
15998
|
-
if (model.relations.length > 0) {
|
|
15999
|
-
for (const rel of model.relations) {
|
|
16000
|
-
sections.push(`- \`${rel.name}\`: ${rel.type} \u2192 ${rel.relatedModel}`);
|
|
16001
|
-
}
|
|
16002
|
-
}
|
|
16003
|
-
sections.push("");
|
|
16004
|
-
}
|
|
16005
|
-
if (schema.models.length > 10) {
|
|
16006
|
-
sections.push(`*... and ${schema.models.length - 10} more models*`);
|
|
16007
|
-
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}`);
|
|
16008
16570
|
}
|
|
16571
|
+
sections.push("```");
|
|
16572
|
+
sections.push("");
|
|
16009
16573
|
}
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16013
|
-
|
|
16014
|
-
|
|
16015
|
-
|
|
16016
|
-
|
|
16017
|
-
}
|
|
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`);
|
|
16018
16582
|
sections.push("");
|
|
16019
16583
|
}
|
|
16584
|
+
sections.push("---");
|
|
16585
|
+
sections.push("");
|
|
16586
|
+
sections.push("*Generated by `ax init` \u2022 Edit this file to customize AI behavior*");
|
|
16020
16587
|
return sections.join("\n");
|
|
16021
16588
|
}
|
|
16022
|
-
function
|
|
16023
|
-
|
|
16024
|
-
|
|
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;
|
|
16025
16610
|
}
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
|
|
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 {
|
|
16031
16634
|
}
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
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;
|
|
16035
16646
|
}
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
const
|
|
16040
|
-
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
|
|
16044
|
-
|
|
16045
|
-
|
|
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;
|
|
16046
16676
|
}
|
|
16047
|
-
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
|
|
16052
|
-
|
|
16053
|
-
|
|
16054
|
-
|
|
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;
|
|
16055
16695
|
}
|
|
16056
|
-
if (
|
|
16057
|
-
|
|
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;
|
|
16058
16699
|
}
|
|
16059
|
-
sections.push(line);
|
|
16060
16700
|
}
|
|
16061
|
-
sections.push("");
|
|
16062
16701
|
}
|
|
16063
|
-
if (
|
|
16064
|
-
|
|
16065
|
-
|
|
16066
|
-
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
}
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
|
|
16076
|
-
|
|
16077
|
-
|
|
16078
|
-
|
|
16079
|
-
|
|
16080
|
-
|
|
16081
|
-
|
|
16082
|
-
|
|
16083
|
-
|
|
16084
|
-
}
|
|
16085
|
-
rules.push("- **Infrastructure/DevOps** \u2192 @devops (Oliver)");
|
|
16086
|
-
rules.push("");
|
|
16087
|
-
rules.push("### Quality & Architecture");
|
|
16088
|
-
rules.push("- **Tests/QA** \u2192 @quality (Queenie)");
|
|
16089
|
-
rules.push("- **Security audits** \u2192 @security (Steve) - mandatory for: auth, payments, PII");
|
|
16090
|
-
rules.push("- **Architecture/ADR** \u2192 @architecture (Avery)");
|
|
16091
|
-
rules.push("");
|
|
16092
|
-
rules.push("### Documentation & Product");
|
|
16093
|
-
rules.push("- **Technical writing** \u2192 @writer (Wendy)");
|
|
16094
|
-
rules.push("- **Product management** \u2192 @product (Paris)");
|
|
16095
|
-
return rules.join("\n");
|
|
16096
|
-
}
|
|
16097
|
-
function generateCodingConventions(info) {
|
|
16098
|
-
const conventions = ["## Coding Conventions", ""];
|
|
16099
|
-
conventions.push("### Testing");
|
|
16100
|
-
if (info.testFramework) {
|
|
16101
|
-
conventions.push(`- **Framework:** ${info.testFramework}`);
|
|
16102
|
-
conventions.push("- **Coverage:** 80% minimum");
|
|
16103
|
-
conventions.push(`- **Run:** \`${info.packageManager} test\``);
|
|
16104
|
-
} else {
|
|
16105
|
-
conventions.push("- Run tests before pushing");
|
|
16106
|
-
}
|
|
16107
|
-
conventions.push("");
|
|
16108
|
-
conventions.push("### Code Style");
|
|
16109
|
-
if (info.linter) {
|
|
16110
|
-
conventions.push(`- **Linter:** ${info.linter}`);
|
|
16111
|
-
}
|
|
16112
|
-
if (info.formatter) {
|
|
16113
|
-
conventions.push(`- **Formatter:** ${info.formatter}`);
|
|
16114
|
-
}
|
|
16115
|
-
if (info.hasTypeScript) {
|
|
16116
|
-
conventions.push("- **TypeScript:** Strict mode enabled");
|
|
16117
|
-
}
|
|
16118
|
-
conventions.push("- **Indent:** 2 spaces");
|
|
16119
|
-
conventions.push("- **Max line:** 100 chars");
|
|
16120
|
-
conventions.push("");
|
|
16121
|
-
conventions.push("### Git Workflow");
|
|
16122
|
-
conventions.push("- **Branch naming:** `feature/description` or `fix/description`");
|
|
16123
|
-
conventions.push("- **Commits:** Conventional commits format (feat/fix/chore/docs)");
|
|
16124
|
-
conventions.push("- **PRs:** Review required before merge");
|
|
16125
|
-
return conventions.join("\n");
|
|
16126
|
-
}
|
|
16127
|
-
function generateCriticalGuardrails(info) {
|
|
16128
|
-
const guardrails = ["## Critical Guardrails", ""];
|
|
16129
|
-
guardrails.push("\u26A0\uFE0F **NEVER:**");
|
|
16130
|
-
guardrails.push("- Commit to main/production branches directly");
|
|
16131
|
-
guardrails.push("- Skip tests before pushing");
|
|
16132
|
-
guardrails.push("- Expose API keys or credentials in code");
|
|
16133
|
-
if (info.dependencies.includes("prisma") || info.dependencies.includes("typeorm")) {
|
|
16134
|
-
guardrails.push("- Modify database migrations without approval");
|
|
16135
|
-
}
|
|
16136
|
-
guardrails.push("");
|
|
16137
|
-
guardrails.push("\u2705 **ALWAYS:**");
|
|
16138
|
-
guardrails.push(`- Run \`${info.packageManager} test\` before pushing`);
|
|
16139
|
-
if (info.linter) {
|
|
16140
|
-
guardrails.push(`- Run \`${info.packageManager} run lint\` to check code style`);
|
|
16141
|
-
}
|
|
16142
|
-
if (info.formatter) {
|
|
16143
|
-
guardrails.push(`- Format code with ${info.formatter} before committing`);
|
|
16144
|
-
}
|
|
16145
|
-
guardrails.push("- Document breaking changes");
|
|
16146
|
-
guardrails.push("- Add tests for new features");
|
|
16147
|
-
return guardrails.join("\n");
|
|
16148
|
-
}
|
|
16149
|
-
function generateCommands(info) {
|
|
16150
|
-
const sections = ["## Canonical Commands", ""];
|
|
16151
|
-
if (!info.categorizedScripts) {
|
|
16152
|
-
return sections.join("\n") + "```bash\n# No scripts detected\n```";
|
|
16153
|
-
}
|
|
16154
|
-
const categories = [
|
|
16155
|
-
{ key: "development", title: "Development" },
|
|
16156
|
-
{ key: "building", title: "Building" },
|
|
16157
|
-
{ key: "testing", title: "Testing" },
|
|
16158
|
-
{ key: "quality", title: "Quality Checks" },
|
|
16159
|
-
{ key: "deployment", title: "Deployment" }
|
|
16160
|
-
];
|
|
16161
|
-
sections.push("```bash");
|
|
16162
|
-
for (const { key, title } of categories) {
|
|
16163
|
-
const scripts = info.categorizedScripts[key];
|
|
16164
|
-
if (scripts && scripts.length > 0) {
|
|
16165
|
-
sections.push(`# ${title}`);
|
|
16166
|
-
for (const script of scripts) {
|
|
16167
|
-
const cmd = `${info.packageManager} run ${script.name}`;
|
|
16168
|
-
const padding = " ".repeat(Math.max(35 - cmd.length, 1));
|
|
16169
|
-
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;
|
|
16170
16723
|
}
|
|
16171
|
-
sections.push("");
|
|
16172
|
-
}
|
|
16173
|
-
}
|
|
16174
|
-
const other = info.categorizedScripts.other;
|
|
16175
|
-
if (other && other.length > 0) {
|
|
16176
|
-
sections.push("# Other");
|
|
16177
|
-
for (const script of other) {
|
|
16178
|
-
const cmd = `${info.packageManager} run ${script.name}`;
|
|
16179
|
-
const padding = " ".repeat(Math.max(35 - cmd.length, 1));
|
|
16180
|
-
sections.push(`${cmd}${padding}# ${script.description}`);
|
|
16181
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;
|
|
16182
16737
|
}
|
|
16183
|
-
sections.push("```");
|
|
16184
|
-
return sections.join("\n");
|
|
16185
|
-
}
|
|
16186
|
-
function generateUsefulLinks(info) {
|
|
16187
|
-
const links = ["## Useful Links", ""];
|
|
16188
|
-
if (info.repository) {
|
|
16189
|
-
links.push(`- [Repository](${info.repository})`);
|
|
16190
|
-
}
|
|
16191
|
-
if (info.fileStructure?.docsDirectory) {
|
|
16192
|
-
links.push(`- [Documentation](${info.fileStructure.docsDirectory}/)`);
|
|
16193
|
-
}
|
|
16194
|
-
return links.join("\n");
|
|
16195
|
-
}
|
|
16196
|
-
async function atomicWrite(filePath, content) {
|
|
16197
|
-
const tempPath = `${filePath}.tmp`;
|
|
16198
|
-
await writeFile(tempPath, content, "utf-8");
|
|
16199
|
-
await rename(tempPath, filePath);
|
|
16200
16738
|
}
|
|
16201
16739
|
var initCommand = {
|
|
16202
16740
|
command: "init",
|
|
16203
|
-
describe: "Initialize
|
|
16741
|
+
describe: "Initialize project index (ax.index.json) and custom instructions",
|
|
16204
16742
|
builder: (yargs2) => {
|
|
16205
16743
|
return yargs2.option("force", {
|
|
16206
16744
|
alias: "f",
|
|
16207
|
-
describe: "Regenerate
|
|
16745
|
+
describe: "Regenerate all files including CUSTOM.md",
|
|
16746
|
+
type: "boolean",
|
|
16747
|
+
default: false
|
|
16748
|
+
}).option("skip-update", {
|
|
16749
|
+
describe: "Skip AutomatosX version check",
|
|
16208
16750
|
type: "boolean",
|
|
16209
16751
|
default: false
|
|
16210
16752
|
}).example([
|
|
16211
|
-
["$0 init", "Create or update
|
|
16212
|
-
["$0 init --force", "Regenerate
|
|
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"]
|
|
16213
16756
|
]);
|
|
16214
16757
|
},
|
|
16215
16758
|
handler: async (argv) => {
|
|
16216
16759
|
const projectDir = process.cwd();
|
|
16217
|
-
|
|
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");
|
|
16218
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
|
+
}
|
|
16219
16800
|
const projectInfo = await detectProjectInfo(projectDir);
|
|
16220
|
-
|
|
16221
|
-
if (
|
|
16222
|
-
|
|
16223
|
-
|
|
16224
|
-
|
|
16225
|
-
|
|
16226
|
-
|
|
16227
|
-
|
|
16228
|
-
|
|
16229
|
-
|
|
16230
|
-
linter: projectInfo.linter,
|
|
16231
|
-
formatter: projectInfo.formatter,
|
|
16232
|
-
testFramework: projectInfo.testFramework
|
|
16233
|
-
});
|
|
16234
|
-
if (changes.length === 0) {
|
|
16235
|
-
console.log(chalk5.green("\u2713 AX.md is up to date"));
|
|
16236
|
-
logger.info("AX.md unchanged", { projectName: projectInfo.name });
|
|
16237
|
-
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);
|
|
16238
16811
|
}
|
|
16239
|
-
const axMdContent = generateAXMD(projectInfo);
|
|
16240
|
-
await atomicWrite(axMdPath, axMdContent);
|
|
16241
|
-
const summary = formatChangeSummary(changes);
|
|
16242
|
-
console.log(chalk5.green(`\u2713 Updated AX.md ${summary}`));
|
|
16243
|
-
logger.info("AX.md updated", {
|
|
16244
|
-
projectName: projectInfo.name,
|
|
16245
|
-
changes: changes.length,
|
|
16246
|
-
changeTypes: changes.map((c) => c.type)
|
|
16247
|
-
});
|
|
16248
16812
|
} else {
|
|
16249
|
-
|
|
16250
|
-
|
|
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 {
|
|
16251
16821
|
const projectType = projectInfo.framework || projectInfo.language;
|
|
16252
|
-
console.log(chalk5.green(`\u2713 Created
|
|
16253
|
-
|
|
16254
|
-
|
|
16255
|
-
|
|
16256
|
-
|
|
16257
|
-
|
|
16258
|
-
|
|
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)"));
|
|
16259
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
|
+
});
|
|
16260
16849
|
} catch (error) {
|
|
16261
|
-
console.log(chalk5.red("\u2717 Error
|
|
16262
|
-
logger.error("
|
|
16850
|
+
console.log(chalk5.red("\u2717 Error initializing project"));
|
|
16851
|
+
logger.error("Project initialization failed", { error });
|
|
16263
16852
|
if (process.env.DEBUG || process.env.AUTOMATOSX_DEBUG) {
|
|
16264
16853
|
printError(error);
|
|
16265
16854
|
}
|
|
@@ -19971,7 +20560,7 @@ var MemoryManager = class _MemoryManager {
|
|
|
19971
20560
|
if (this.db) {
|
|
19972
20561
|
try {
|
|
19973
20562
|
this.db.close();
|
|
19974
|
-
} catch
|
|
20563
|
+
} catch {
|
|
19975
20564
|
}
|
|
19976
20565
|
this.initialized = false;
|
|
19977
20566
|
this.entryCount = 0;
|
|
@@ -20725,9 +21314,9 @@ var MemoryManager = class _MemoryManager {
|
|
|
20725
21314
|
throw new MemoryError("Memory manager not initialized", "DATABASE_ERROR");
|
|
20726
21315
|
}
|
|
20727
21316
|
try {
|
|
20728
|
-
const { mkdir:
|
|
21317
|
+
const { mkdir: mkdir15 } = await import('fs/promises');
|
|
20729
21318
|
const destDir = dirname4(destPath);
|
|
20730
|
-
await
|
|
21319
|
+
await mkdir15(destDir, { recursive: true });
|
|
20731
21320
|
await this.db.backup(destPath);
|
|
20732
21321
|
logger.info("Database backup created", { destPath: normalizePath(destPath) });
|
|
20733
21322
|
} catch (error) {
|
|
@@ -20812,9 +21401,9 @@ var MemoryManager = class _MemoryManager {
|
|
|
20812
21401
|
}
|
|
20813
21402
|
}
|
|
20814
21403
|
const {
|
|
20815
|
-
includeEmbeddings = false,
|
|
21404
|
+
includeEmbeddings: _includeEmbeddings = false,
|
|
20816
21405
|
filters = {},
|
|
20817
|
-
batchSize = 1e3,
|
|
21406
|
+
batchSize: _batchSize = 1e3,
|
|
20818
21407
|
pretty = false
|
|
20819
21408
|
} = options || {};
|
|
20820
21409
|
try {
|
|
@@ -21972,7 +22561,7 @@ var SessionManager = class _SessionManager {
|
|
|
21972
22561
|
if (this.pendingSave) {
|
|
21973
22562
|
try {
|
|
21974
22563
|
await this.pendingSave;
|
|
21975
|
-
} catch
|
|
22564
|
+
} catch {
|
|
21976
22565
|
}
|
|
21977
22566
|
}
|
|
21978
22567
|
this.pendingSave = this.doSave().finally(() => {
|
|
@@ -22040,7 +22629,7 @@ var SessionManager = class _SessionManager {
|
|
|
22040
22629
|
}
|
|
22041
22630
|
try {
|
|
22042
22631
|
await unlink(tempPath);
|
|
22043
|
-
} catch
|
|
22632
|
+
} catch {
|
|
22044
22633
|
}
|
|
22045
22634
|
throw renameError;
|
|
22046
22635
|
}
|
|
@@ -22048,7 +22637,7 @@ var SessionManager = class _SessionManager {
|
|
|
22048
22637
|
const tempPath = `${this.persistencePath}.tmp`;
|
|
22049
22638
|
try {
|
|
22050
22639
|
await unlink(tempPath);
|
|
22051
|
-
} catch
|
|
22640
|
+
} catch {
|
|
22052
22641
|
}
|
|
22053
22642
|
logger.error("Failed to save sessions to persistence", {
|
|
22054
22643
|
path: normalizePath(this.persistencePath),
|
|
@@ -22587,6 +23176,7 @@ init_esm_shims();
|
|
|
22587
23176
|
init_logger();
|
|
22588
23177
|
var MAX_CONTEXT_SIZE = 100 * 1024;
|
|
22589
23178
|
var DEFAULT_CACHE_TTL = 3e5;
|
|
23179
|
+
var STALE_THRESHOLD_MS2 = 24 * 60 * 60 * 1e3;
|
|
22590
23180
|
var ProjectContextLoader = class {
|
|
22591
23181
|
constructor(projectRoot, options) {
|
|
22592
23182
|
this.projectRoot = projectRoot;
|
|
@@ -22610,76 +23200,79 @@ var ProjectContextLoader = class {
|
|
|
22610
23200
|
});
|
|
22611
23201
|
const context = {};
|
|
22612
23202
|
try {
|
|
22613
|
-
const
|
|
22614
|
-
const resolvedPath = await realpath$1(
|
|
23203
|
+
const indexPath = path4__default.join(this.projectRoot, "ax.index.json");
|
|
23204
|
+
const resolvedPath = await realpath$1(indexPath).catch(() => null);
|
|
22615
23205
|
if (resolvedPath) {
|
|
22616
23206
|
const rel = path4__default.relative(this.projectRoot, resolvedPath);
|
|
22617
23207
|
if (!rel.startsWith("..") && !path4__default.isAbsolute(rel)) {
|
|
22618
23208
|
const stats = await stat(resolvedPath);
|
|
22619
23209
|
if (stats.size > MAX_CONTEXT_SIZE) {
|
|
22620
|
-
logger.warn("
|
|
23210
|
+
logger.warn("ax.index.json too large, ignoring", {
|
|
22621
23211
|
size: stats.size,
|
|
22622
23212
|
limit: MAX_CONTEXT_SIZE
|
|
22623
23213
|
});
|
|
22624
23214
|
} else {
|
|
22625
|
-
|
|
22626
|
-
|
|
22627
|
-
|
|
22628
|
-
|
|
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))
|
|
22629
23225
|
});
|
|
22630
|
-
|
|
22631
|
-
|
|
22632
|
-
|
|
22633
|
-
|
|
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
|
+
}
|
|
22634
23232
|
}
|
|
22635
23233
|
}
|
|
22636
23234
|
}
|
|
22637
23235
|
} catch (error) {
|
|
22638
23236
|
if (error && typeof error === "object" && "code" in error && error.code !== "ENOENT") {
|
|
22639
|
-
logger.warn("Error loading
|
|
23237
|
+
logger.warn("Error loading ax.index.json", { error });
|
|
22640
23238
|
}
|
|
22641
23239
|
}
|
|
22642
23240
|
try {
|
|
22643
|
-
const
|
|
22644
|
-
const resolvedPath = await realpath$1(
|
|
23241
|
+
const customMdPath = path4__default.join(this.projectRoot, ".automatosx", "CUSTOM.md");
|
|
23242
|
+
const resolvedPath = await realpath$1(customMdPath).catch(() => null);
|
|
22645
23243
|
if (resolvedPath) {
|
|
22646
23244
|
const rel = path4__default.relative(this.projectRoot, resolvedPath);
|
|
22647
23245
|
if (!rel.startsWith("..") && !path4__default.isAbsolute(rel)) {
|
|
22648
23246
|
const stats = await stat(resolvedPath);
|
|
22649
23247
|
if (stats.size > MAX_CONTEXT_SIZE) {
|
|
22650
|
-
logger.warn("
|
|
23248
|
+
logger.warn("CUSTOM.md too large, ignoring", {
|
|
22651
23249
|
size: stats.size,
|
|
22652
23250
|
limit: MAX_CONTEXT_SIZE
|
|
22653
23251
|
});
|
|
22654
23252
|
} else {
|
|
22655
|
-
|
|
22656
|
-
|
|
22657
|
-
|
|
22658
|
-
|
|
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
|
|
22659
23257
|
});
|
|
22660
|
-
|
|
22661
|
-
context.commands = { ...context.commands, ...context.config.commands };
|
|
22662
|
-
}
|
|
22663
|
-
if (context.config.project) {
|
|
22664
|
-
context.metadata = { ...context.metadata, ...context.config.project };
|
|
22665
|
-
}
|
|
23258
|
+
context.guardrails = this.parseGuardrails(context.customInstructions);
|
|
22666
23259
|
}
|
|
22667
23260
|
}
|
|
22668
23261
|
}
|
|
22669
23262
|
} catch (error) {
|
|
22670
23263
|
if (error && typeof error === "object" && "code" in error && error.code !== "ENOENT") {
|
|
22671
|
-
logger.warn("Error loading
|
|
23264
|
+
logger.warn("Error loading CUSTOM.md", { error });
|
|
22672
23265
|
}
|
|
22673
23266
|
}
|
|
22674
23267
|
context.contextPrompt = this.buildContextPrompt(context);
|
|
22675
23268
|
this.cache = context;
|
|
22676
23269
|
this.cacheExpiry = Date.now() + this.cacheTTL;
|
|
22677
23270
|
logger.info("Project context loaded", {
|
|
22678
|
-
|
|
22679
|
-
|
|
22680
|
-
agentRules: context.agentRules?.length ?? 0,
|
|
23271
|
+
hasIndex: !!context.index,
|
|
23272
|
+
hasCustomInstructions: !!context.customInstructions,
|
|
22681
23273
|
guardrails: context.guardrails?.length ?? 0,
|
|
22682
|
-
commands: Object.keys(context.commands ?? {}).length
|
|
23274
|
+
commands: Object.keys(context.commands ?? {}).length,
|
|
23275
|
+
isStale: context.isStale
|
|
22683
23276
|
});
|
|
22684
23277
|
return context;
|
|
22685
23278
|
}
|
|
@@ -22695,8 +23288,8 @@ var ProjectContextLoader = class {
|
|
|
22695
23288
|
* Check if context exists (without loading)
|
|
22696
23289
|
*/
|
|
22697
23290
|
async exists() {
|
|
22698
|
-
const
|
|
22699
|
-
const
|
|
23291
|
+
const indexPath = path4__default.join(this.projectRoot, "ax.index.json");
|
|
23292
|
+
const customMdPath = path4__default.join(this.projectRoot, ".automatosx", "CUSTOM.md");
|
|
22700
23293
|
const checkExists3 = async (filePath) => {
|
|
22701
23294
|
try {
|
|
22702
23295
|
await access$1(filePath, constants.F_OK);
|
|
@@ -22705,57 +23298,48 @@ var ProjectContextLoader = class {
|
|
|
22705
23298
|
return false;
|
|
22706
23299
|
}
|
|
22707
23300
|
};
|
|
22708
|
-
const [
|
|
22709
|
-
checkExists3(
|
|
22710
|
-
checkExists3(
|
|
23301
|
+
const [indexExists, customMdExists] = await Promise.all([
|
|
23302
|
+
checkExists3(indexPath),
|
|
23303
|
+
checkExists3(customMdPath)
|
|
22711
23304
|
]);
|
|
22712
|
-
return
|
|
23305
|
+
return indexExists || customMdExists;
|
|
22713
23306
|
}
|
|
22714
23307
|
/**
|
|
22715
|
-
*
|
|
22716
|
-
*
|
|
22717
|
-
* Looks for patterns like:
|
|
22718
|
-
* - Backend changes → @backend
|
|
22719
|
-
* - API endpoints → @backend, @security
|
|
23308
|
+
* Check if ax.index.json is stale (older than 24 hours)
|
|
22720
23309
|
*/
|
|
22721
|
-
|
|
22722
|
-
|
|
22723
|
-
|
|
22724
|
-
|
|
22725
|
-
|
|
22726
|
-
return
|
|
22727
|
-
}
|
|
22728
|
-
|
|
22729
|
-
const lineRegex = /^[-*]\s+(.+?)\s+(?:→|->)+\s+(.+?)$/gm;
|
|
22730
|
-
let lineMatch;
|
|
22731
|
-
while ((lineMatch = lineRegex.exec(section)) !== null) {
|
|
22732
|
-
const taskType = lineMatch[1]?.trim() ?? "";
|
|
22733
|
-
const agentsText = lineMatch[2]?.trim() ?? "";
|
|
22734
|
-
const agents = agentsText.split(",").map((a) => a.trim()).filter(Boolean);
|
|
22735
|
-
const defaultAgent = agents[0];
|
|
22736
|
-
if (defaultAgent) {
|
|
22737
|
-
rules.push({
|
|
22738
|
-
taskType,
|
|
22739
|
-
patterns: [],
|
|
22740
|
-
// TODO: Parse file patterns if specified
|
|
22741
|
-
defaultAgent,
|
|
22742
|
-
autoReview: agents.length > 1
|
|
22743
|
-
});
|
|
22744
|
-
}
|
|
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;
|
|
22745
23318
|
}
|
|
22746
|
-
return rules;
|
|
22747
23319
|
}
|
|
22748
23320
|
/**
|
|
22749
|
-
* Parse guardrails
|
|
23321
|
+
* Parse guardrails from CUSTOM.md
|
|
22750
23322
|
*
|
|
22751
|
-
* Looks for
|
|
22752
|
-
* Extracts items marked with ⚠️ or under NEVER headings
|
|
23323
|
+
* Looks for DO/DON'T sections in ax-cli format
|
|
22753
23324
|
*/
|
|
22754
23325
|
parseGuardrails(markdown) {
|
|
22755
23326
|
const guardrails = [];
|
|
22756
|
-
const
|
|
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;
|
|
22757
23341
|
let match;
|
|
22758
|
-
while ((match =
|
|
23342
|
+
while ((match = criticalRegex.exec(markdown)) !== null) {
|
|
22759
23343
|
const section = match[2];
|
|
22760
23344
|
if (!section) continue;
|
|
22761
23345
|
const bulletRegex = /^[-*]\s+(.+?)$/gm;
|
|
@@ -22765,106 +23349,58 @@ var ProjectContextLoader = class {
|
|
|
22765
23349
|
rule = rule.replace(/^[⚠️❌✅✓⚡🔒]+\s*/, "");
|
|
22766
23350
|
rule = rule.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
22767
23351
|
rule = rule.replace(/`(.+?)`/g, "$1");
|
|
22768
|
-
if (rule.length > 0) {
|
|
23352
|
+
if (rule.length > 0 && !guardrails.includes(rule)) {
|
|
22769
23353
|
guardrails.push(rule);
|
|
22770
23354
|
}
|
|
22771
23355
|
}
|
|
22772
23356
|
}
|
|
22773
23357
|
return guardrails;
|
|
22774
23358
|
}
|
|
22775
|
-
/**
|
|
22776
|
-
* Parse commands from markdown code blocks
|
|
22777
|
-
*
|
|
22778
|
-
* Looks for "## Commands" or "## Canonical Commands" section
|
|
22779
|
-
*/
|
|
22780
|
-
parseCommands(markdown) {
|
|
22781
|
-
const commands = {};
|
|
22782
|
-
const sectionRegex = /##\s+(Canonical\s+)?Commands[^\n]*\n([\s\S]*?)(?=\n##|$)/i;
|
|
22783
|
-
const match = markdown.match(sectionRegex);
|
|
22784
|
-
if (!match) {
|
|
22785
|
-
return commands;
|
|
22786
|
-
}
|
|
22787
|
-
const section = match[2];
|
|
22788
|
-
if (!section) {
|
|
22789
|
-
return commands;
|
|
22790
|
-
}
|
|
22791
|
-
const codeBlockRegex = /```(?:bash|sh|shell)?\n([\s\S]*?)```/;
|
|
22792
|
-
const codeMatch = section.match(codeBlockRegex);
|
|
22793
|
-
if (codeMatch && codeMatch[1]) {
|
|
22794
|
-
const lines = codeMatch[1].split("\n");
|
|
22795
|
-
for (const line of lines) {
|
|
22796
|
-
const cmdMatch = line.match(/^([^\s#]+(?:\s+[^\s#]+)*)\s*#?\s*(.*)$/);
|
|
22797
|
-
if (cmdMatch && cmdMatch[1]) {
|
|
22798
|
-
const cmd = cmdMatch[1].trim();
|
|
22799
|
-
const desc = cmdMatch[2]?.trim() ?? "";
|
|
22800
|
-
const key = desc || cmd;
|
|
22801
|
-
commands[key] = cmd;
|
|
22802
|
-
}
|
|
22803
|
-
}
|
|
22804
|
-
}
|
|
22805
|
-
return commands;
|
|
22806
|
-
}
|
|
22807
|
-
/**
|
|
22808
|
-
* Parse project metadata from markdown frontmatter or first section
|
|
22809
|
-
*/
|
|
22810
|
-
parseMetadata(markdown) {
|
|
22811
|
-
const metadata = {};
|
|
22812
|
-
const lastUpdatedMatch = markdown.match(/>\s*Last\s+updated:\s*(.+?)$/im);
|
|
22813
|
-
if (lastUpdatedMatch && lastUpdatedMatch[1]) {
|
|
22814
|
-
metadata.lastUpdated = lastUpdatedMatch[1].trim();
|
|
22815
|
-
}
|
|
22816
|
-
const projectMatch = markdown.match(/>\s*Project:\s*(.+?)$/im);
|
|
22817
|
-
if (projectMatch && projectMatch[1]) {
|
|
22818
|
-
const parts = projectMatch[1].trim().split(/\s+v/);
|
|
22819
|
-
metadata.name = parts[0] || "";
|
|
22820
|
-
if (parts.length > 1 && parts[1]) {
|
|
22821
|
-
metadata.version = parts[1];
|
|
22822
|
-
}
|
|
22823
|
-
}
|
|
22824
|
-
const h1Match = markdown.match(/^#\s+(.+?)$/m);
|
|
22825
|
-
if (h1Match && h1Match[1] && !metadata.name) {
|
|
22826
|
-
metadata.name = h1Match[1].replace(/Project Context for AutomatosX/i, "").trim();
|
|
22827
|
-
}
|
|
22828
|
-
return metadata;
|
|
22829
|
-
}
|
|
22830
23359
|
/**
|
|
22831
23360
|
* Build formatted context prompt for agent injection
|
|
22832
23361
|
*/
|
|
22833
23362
|
buildContextPrompt(context) {
|
|
22834
|
-
if (!context.
|
|
23363
|
+
if (!context.index && !context.customInstructions) {
|
|
22835
23364
|
return "";
|
|
22836
23365
|
}
|
|
22837
23366
|
let prompt = "\n# PROJECT CONTEXT\n\n";
|
|
22838
|
-
if (context.
|
|
22839
|
-
prompt +=
|
|
22840
|
-
context.guardrails.forEach((rule) => {
|
|
22841
|
-
prompt += `- ${rule}
|
|
23367
|
+
if (context.index) {
|
|
23368
|
+
prompt += `**Project:** ${context.index.projectName} v${context.index.version}
|
|
22842
23369
|
`;
|
|
22843
|
-
}
|
|
22844
|
-
prompt += "\n";
|
|
22845
|
-
}
|
|
22846
|
-
if (context.agentRules && context.agentRules.length > 0) {
|
|
22847
|
-
prompt += "## Agent Delegation Rules:\n\n";
|
|
22848
|
-
context.agentRules.forEach((rule) => {
|
|
22849
|
-
prompt += `- ${rule.taskType} \u2192 ${rule.defaultAgent}
|
|
23370
|
+
prompt += `**Type:** ${context.index.projectType}
|
|
22850
23371
|
`;
|
|
22851
|
-
}
|
|
22852
|
-
|
|
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
|
+
}
|
|
22853
23393
|
}
|
|
22854
|
-
if (context.
|
|
22855
|
-
prompt += "##
|
|
22856
|
-
|
|
22857
|
-
prompt += `- ${
|
|
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}
|
|
22858
23398
|
`;
|
|
22859
|
-
}
|
|
23399
|
+
}
|
|
22860
23400
|
prompt += "\n";
|
|
22861
23401
|
}
|
|
22862
|
-
if (context.
|
|
22863
|
-
|
|
22864
|
-
const contextToAdd = context.markdown.length > MAX_PROMPT_LENGTH ? context.markdown.substring(0, MAX_PROMPT_LENGTH) + "\n\n[... context truncated for length ...]" : context.markdown;
|
|
22865
|
-
prompt += "## Full Project Context:\n\n";
|
|
22866
|
-
prompt += contextToAdd;
|
|
22867
|
-
prompt += "\n";
|
|
23402
|
+
if (context.isStale) {
|
|
23403
|
+
prompt += "\u26A0\uFE0F **Note:** Project index is stale (>24h old). Run `ax init` to update.\n\n";
|
|
22868
23404
|
}
|
|
22869
23405
|
return prompt;
|
|
22870
23406
|
}
|
|
@@ -23288,10 +23824,10 @@ var ContextManager = class {
|
|
|
23288
23824
|
return provider;
|
|
23289
23825
|
}
|
|
23290
23826
|
/**
|
|
23291
|
-
* Load project context from
|
|
23827
|
+
* Load project context from ax.index.json (v12.9.0+)
|
|
23292
23828
|
*
|
|
23293
|
-
* Loads project-specific
|
|
23294
|
-
* Falls back gracefully if
|
|
23829
|
+
* Loads project-specific context from ax.index.json and CUSTOM.md.
|
|
23830
|
+
* Falls back gracefully if files don't exist.
|
|
23295
23831
|
*
|
|
23296
23832
|
* @param projectDir - Project root directory
|
|
23297
23833
|
* @returns Project context or null if not found
|
|
@@ -23304,15 +23840,15 @@ var ContextManager = class {
|
|
|
23304
23840
|
const loader = this.projectContextLoader;
|
|
23305
23841
|
const exists = await loader.exists();
|
|
23306
23842
|
if (!exists) {
|
|
23307
|
-
logger.debug("No
|
|
23843
|
+
logger.debug("No ax.index.json or CUSTOM.md found, skipping project context");
|
|
23308
23844
|
return null;
|
|
23309
23845
|
}
|
|
23310
23846
|
const context = await loader.load();
|
|
23311
|
-
logger.debug("Project context loaded
|
|
23312
|
-
|
|
23313
|
-
|
|
23314
|
-
|
|
23315
|
-
|
|
23847
|
+
logger.debug("Project context loaded", {
|
|
23848
|
+
hasIndex: !!context.index,
|
|
23849
|
+
hasCustomInstructions: !!context.customInstructions,
|
|
23850
|
+
guardrails: context.guardrails?.length ?? 0,
|
|
23851
|
+
isStale: context.isStale
|
|
23316
23852
|
});
|
|
23317
23853
|
return context;
|
|
23318
23854
|
} catch (error) {
|
|
@@ -26242,6 +26778,24 @@ ${context.task}`;
|
|
|
26242
26778
|
// src/agents/agent-selector.ts
|
|
26243
26779
|
init_esm_shims();
|
|
26244
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
|
+
}
|
|
26245
26799
|
function scoreAgent(task, profile) {
|
|
26246
26800
|
let score = 0;
|
|
26247
26801
|
const taskLower = task.toLowerCase();
|
|
@@ -26279,16 +26833,9 @@ function scoreAgent(task, profile) {
|
|
|
26279
26833
|
}
|
|
26280
26834
|
if (profile.selectionMetadata?.redirectWhen) {
|
|
26281
26835
|
for (const rule of profile.selectionMetadata.redirectWhen) {
|
|
26282
|
-
|
|
26283
|
-
|
|
26284
|
-
|
|
26285
|
-
score -= 15;
|
|
26286
|
-
}
|
|
26287
|
-
} catch (error) {
|
|
26288
|
-
logger.debug("Invalid regex pattern in redirectWhen rule", {
|
|
26289
|
-
pattern: rule.phrase,
|
|
26290
|
-
error: error instanceof Error ? error.message : String(error)
|
|
26291
|
-
});
|
|
26836
|
+
const regex = getCachedRegex(rule.phrase);
|
|
26837
|
+
if (regex && regex.test(task)) {
|
|
26838
|
+
score -= 15;
|
|
26292
26839
|
}
|
|
26293
26840
|
}
|
|
26294
26841
|
}
|
|
@@ -28878,8 +29425,8 @@ var BugDetector = class {
|
|
|
28878
29425
|
async loadRulesFromFile(filePath) {
|
|
28879
29426
|
try {
|
|
28880
29427
|
const content = await readFile(filePath, "utf-8");
|
|
28881
|
-
const
|
|
28882
|
-
const parsed =
|
|
29428
|
+
const yaml4 = await import('js-yaml');
|
|
29429
|
+
const parsed = yaml4.load(content);
|
|
28883
29430
|
if (parsed.rules && Array.isArray(parsed.rules)) {
|
|
28884
29431
|
for (const rule of parsed.rules) {
|
|
28885
29432
|
this.addRule(rule);
|
|
@@ -29311,7 +29858,6 @@ var BugFixer = class {
|
|
|
29311
29858
|
}
|
|
29312
29859
|
const directSetIntervalPattern = /setInterval\s*\(/;
|
|
29313
29860
|
if (directSetIntervalPattern.test(line)) {
|
|
29314
|
-
line.match(/^(\s*)/)?.[1] || "";
|
|
29315
29861
|
const newLine = line.replace(
|
|
29316
29862
|
/(setInterval\s*\([^)]+\))/,
|
|
29317
29863
|
"const _interval = $1; if (_interval.unref) _interval.unref()"
|
|
@@ -29338,7 +29884,6 @@ var BugFixer = class {
|
|
|
29338
29884
|
const match = currentLine.match(classPattern);
|
|
29339
29885
|
if (match && match[1]) {
|
|
29340
29886
|
classStartLine = i;
|
|
29341
|
-
match[1];
|
|
29342
29887
|
break;
|
|
29343
29888
|
}
|
|
29344
29889
|
}
|
|
@@ -29390,7 +29935,7 @@ var BugFixer = class {
|
|
|
29390
29935
|
/**
|
|
29391
29936
|
* Apply use DisposableEventEmitter fix
|
|
29392
29937
|
*/
|
|
29393
|
-
applyUseDisposableEventEmitterFix(finding, originalContent,
|
|
29938
|
+
applyUseDisposableEventEmitterFix(finding, originalContent, _lines) {
|
|
29394
29939
|
let fixedContent = originalContent.replace(
|
|
29395
29940
|
/extends\s+EventEmitter\b/g,
|
|
29396
29941
|
"extends DisposableEventEmitter"
|
|
@@ -30312,7 +30857,7 @@ var DUPLICATION_RULES = [
|
|
|
30312
30857
|
suggestion: "Extract to a named constant"
|
|
30313
30858
|
}
|
|
30314
30859
|
];
|
|
30315
|
-
function detectDuplication(filePath, content, lines, ignoreState,
|
|
30860
|
+
function detectDuplication(filePath, content, lines, ignoreState, _config) {
|
|
30316
30861
|
const findings = [];
|
|
30317
30862
|
findings.push(...detectDuplicateBlocks(filePath, content, lines, ignoreState));
|
|
30318
30863
|
findings.push(...detectRepeatedConditionals(filePath, content, lines, ignoreState));
|
|
@@ -30339,7 +30884,7 @@ function detectDuplicateBlocks(filePath, content, lines, ignoreState) {
|
|
|
30339
30884
|
}
|
|
30340
30885
|
blockHashes.get(blockHash).push({ start: i + 1, end: i + MIN_BLOCK_SIZE });
|
|
30341
30886
|
}
|
|
30342
|
-
for (const [
|
|
30887
|
+
for (const [_hash, locations] of blockHashes) {
|
|
30343
30888
|
if (locations.length > 1) {
|
|
30344
30889
|
for (let i = 1; i < locations.length; i++) {
|
|
30345
30890
|
const loc = locations[i];
|
|
@@ -30388,7 +30933,7 @@ function detectRepeatedConditionals(filePath, content, lines, ignoreState) {
|
|
|
30388
30933
|
}
|
|
30389
30934
|
conditionPattern.lastIndex = 0;
|
|
30390
30935
|
}
|
|
30391
|
-
for (const [
|
|
30936
|
+
for (const [_condition, lineNums] of conditionalCounts) {
|
|
30392
30937
|
if (lineNums.length >= 2) {
|
|
30393
30938
|
const firstLine = lineNums[0];
|
|
30394
30939
|
const secondLine = lineNums[1];
|
|
@@ -30551,7 +31096,7 @@ var THRESHOLDS = {
|
|
|
30551
31096
|
maxCyclomaticComplexity: 10,
|
|
30552
31097
|
maxChainedCalls: 4
|
|
30553
31098
|
};
|
|
30554
|
-
function detectReadability(filePath, content, lines, ignoreState,
|
|
31099
|
+
function detectReadability(filePath, content, lines, ignoreState, _config) {
|
|
30555
31100
|
const findings = [];
|
|
30556
31101
|
findings.push(...detectLongFunctions(filePath, content, lines, ignoreState));
|
|
30557
31102
|
findings.push(...detectDeepNesting(filePath, content, lines, ignoreState));
|
|
@@ -30927,7 +31472,7 @@ var PERFORMANCE_RULES = [
|
|
|
30927
31472
|
suggestion: "Remove await - async functions automatically wrap return values in promises"
|
|
30928
31473
|
}
|
|
30929
31474
|
];
|
|
30930
|
-
function detectPerformance(filePath, content, lines, ignoreState,
|
|
31475
|
+
function detectPerformance(filePath, content, lines, ignoreState, _config) {
|
|
30931
31476
|
const findings = [];
|
|
30932
31477
|
findings.push(...detectSyncInAsync(filePath, content, lines, ignoreState));
|
|
30933
31478
|
findings.push(...detectNPlusOne(filePath, content, lines, ignoreState));
|
|
@@ -31342,7 +31887,7 @@ var HARDCODE_RULES = [
|
|
|
31342
31887
|
suggestion: "Extract timeout to a named constant or config"
|
|
31343
31888
|
}
|
|
31344
31889
|
];
|
|
31345
|
-
function detectHardcode(filePath, content, lines, ignoreState,
|
|
31890
|
+
function detectHardcode(filePath, content, lines, ignoreState, _config) {
|
|
31346
31891
|
const findings = [];
|
|
31347
31892
|
findings.push(...detectMagicNumbers(filePath, content, lines, ignoreState));
|
|
31348
31893
|
findings.push(...detectHardcodedUrls(filePath, content, lines, ignoreState));
|
|
@@ -31698,7 +32243,7 @@ var NAMING_RULES = [
|
|
|
31698
32243
|
suggestion: "Use is/has/can/should prefix for boolean variables"
|
|
31699
32244
|
}
|
|
31700
32245
|
];
|
|
31701
|
-
function detectNaming(filePath, content, lines, ignoreState,
|
|
32246
|
+
function detectNaming(filePath, content, lines, ignoreState, _config) {
|
|
31702
32247
|
const findings = [];
|
|
31703
32248
|
findings.push(...detectSingleLetterVariables(filePath, content, lines, ignoreState));
|
|
31704
32249
|
findings.push(...detectHungarianNotation(filePath, content, lines, ignoreState));
|
|
@@ -31981,7 +32526,7 @@ var CONDITIONAL_RULES = [
|
|
|
31981
32526
|
suggestion: "Use the condition directly (or negate it)"
|
|
31982
32527
|
}
|
|
31983
32528
|
];
|
|
31984
|
-
function detectConditionals(filePath, content, lines, ignoreState,
|
|
32529
|
+
function detectConditionals(filePath, content, lines, ignoreState, _config) {
|
|
31985
32530
|
const findings = [];
|
|
31986
32531
|
findings.push(...detectDeeplyNestedIf(filePath, content, lines, ignoreState));
|
|
31987
32532
|
findings.push(...detectComplexConditions(filePath, content, lines, ignoreState));
|
|
@@ -32294,7 +32839,7 @@ var DEAD_CODE_RULES = [
|
|
|
32294
32839
|
suggestion: "Implement function or add TODO comment"
|
|
32295
32840
|
}
|
|
32296
32841
|
];
|
|
32297
|
-
function detectDeadCode(filePath, content, lines, ignoreState,
|
|
32842
|
+
function detectDeadCode(filePath, content, lines, ignoreState, _config) {
|
|
32298
32843
|
const findings = [];
|
|
32299
32844
|
findings.push(...detectUnusedImports(filePath, content, lines, ignoreState));
|
|
32300
32845
|
findings.push(...detectUnusedVariables(filePath, content, lines, ignoreState));
|
|
@@ -32588,7 +33133,7 @@ var TYPE_SAFETY_RULES = [
|
|
|
32588
33133
|
fileExtensions: [".ts", ".tsx"]
|
|
32589
33134
|
}
|
|
32590
33135
|
];
|
|
32591
|
-
function detectTypeSafety(filePath, content, lines, ignoreState,
|
|
33136
|
+
function detectTypeSafety(filePath, content, lines, ignoreState, _config) {
|
|
32592
33137
|
const findings = [];
|
|
32593
33138
|
if (!filePath.endsWith(".ts") && !filePath.endsWith(".tsx")) {
|
|
32594
33139
|
return findings;
|
|
@@ -36797,7 +37342,7 @@ var createTaskSchema = {
|
|
|
36797
37342
|
// src/mcp/tools/task/run-task.ts
|
|
36798
37343
|
init_esm_shims();
|
|
36799
37344
|
init_logger();
|
|
36800
|
-
function createRunTaskHandler(
|
|
37345
|
+
function createRunTaskHandler(_deps) {
|
|
36801
37346
|
return async (input, context) => {
|
|
36802
37347
|
const startTime = Date.now();
|
|
36803
37348
|
if (context?.signal?.aborted) {
|
|
@@ -39347,6 +39892,17 @@ Use this tool first to understand what AutomatosX offers.`,
|
|
|
39347
39892
|
mode: "sdk"
|
|
39348
39893
|
}));
|
|
39349
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
|
+
}
|
|
39350
39906
|
const healthCheckInterval = config.router?.healthCheckInterval;
|
|
39351
39907
|
this.router = new Router({
|
|
39352
39908
|
providers,
|
|
@@ -42910,7 +43466,7 @@ var CheckpointManager = class {
|
|
|
42910
43466
|
if (checkpoint.schemaVersion === CURRENT_SCHEMA_VERSION) {
|
|
42911
43467
|
return checkpoint;
|
|
42912
43468
|
}
|
|
42913
|
-
|
|
43469
|
+
const migrated = { ...checkpoint };
|
|
42914
43470
|
if (checkpointVersion.major === 1 && checkpointVersion.minor === 0) ;
|
|
42915
43471
|
migrated.schemaVersion = CURRENT_SCHEMA_VERSION;
|
|
42916
43472
|
migrated.checksum = this.calculateChecksum(migrated);
|
|
@@ -43016,7 +43572,7 @@ var CheckpointManager = class {
|
|
|
43016
43572
|
* @returns Hex-encoded checksum
|
|
43017
43573
|
*/
|
|
43018
43574
|
calculateChecksum(checkpoint) {
|
|
43019
|
-
const { checksum, updatedAt, ...dataForChecksum } = checkpoint;
|
|
43575
|
+
const { checksum: _checksum, updatedAt: _updatedAt, ...dataForChecksum } = checkpoint;
|
|
43020
43576
|
const hash = createHash("sha256");
|
|
43021
43577
|
hash.update(JSON.stringify(dataForChecksum));
|
|
43022
43578
|
return hash.digest("hex");
|
|
@@ -45760,7 +46316,7 @@ var IterateModeController = class {
|
|
|
45760
46316
|
}
|
|
45761
46317
|
});
|
|
45762
46318
|
const responseContents = [];
|
|
45763
|
-
|
|
46319
|
+
const totalTokensUsed = { prompt: 0, completion: 0, total: 0 };
|
|
45764
46320
|
let lastResponse;
|
|
45765
46321
|
try {
|
|
45766
46322
|
if (this.checkTimeBudget()) {
|
|
@@ -48039,7 +48595,7 @@ var runCommand = {
|
|
|
48039
48595
|
}
|
|
48040
48596
|
try {
|
|
48041
48597
|
const workflowContent = readFileSync(workflowPath, "utf-8");
|
|
48042
|
-
workflowConfig =
|
|
48598
|
+
workflowConfig = yaml3__default.load(workflowContent);
|
|
48043
48599
|
if (workflowConfig?.iterate?.enabled !== false) {
|
|
48044
48600
|
argv.iterate = true;
|
|
48045
48601
|
}
|
|
@@ -49260,7 +49816,7 @@ var statusCommand4 = {
|
|
|
49260
49816
|
const agentCount = await countFiles(agentsDir, [".yaml", ".yml"]);
|
|
49261
49817
|
const abilityCount = await countFiles(abilitiesDir, [".md"]);
|
|
49262
49818
|
const projectInfo = await getProjectInfo(detectedProjectDir);
|
|
49263
|
-
|
|
49819
|
+
const limitData = {
|
|
49264
49820
|
limits: [],
|
|
49265
49821
|
manualOverride: null,
|
|
49266
49822
|
envOverrides: []
|
|
@@ -49651,7 +50207,7 @@ function formatUptime2(seconds) {
|
|
|
49651
50207
|
// src/cli/commands/update.ts
|
|
49652
50208
|
init_esm_shims();
|
|
49653
50209
|
init_logger();
|
|
49654
|
-
var
|
|
50210
|
+
var execAsync6 = promisify(exec);
|
|
49655
50211
|
var updateCommand = {
|
|
49656
50212
|
command: "update",
|
|
49657
50213
|
describe: "Check for updates and upgrade AutomatosX to the latest version",
|
|
@@ -49670,17 +50226,17 @@ var updateCommand = {
|
|
|
49670
50226
|
handler: async (argv) => {
|
|
49671
50227
|
console.log(chalk5.blue.bold("\n\u{1F504} AutomatosX Update Checker\n"));
|
|
49672
50228
|
try {
|
|
49673
|
-
const currentVersion = await
|
|
50229
|
+
const currentVersion = await getCurrentVersion2();
|
|
49674
50230
|
console.log(chalk5.gray(`Current version: ${currentVersion}`));
|
|
49675
50231
|
console.log(chalk5.cyan("Checking for updates..."));
|
|
49676
|
-
const latestVersion = await
|
|
50232
|
+
const latestVersion = await getLatestVersion2();
|
|
49677
50233
|
console.log(chalk5.gray(`Latest version: ${latestVersion}
|
|
49678
50234
|
`));
|
|
49679
50235
|
if (currentVersion === latestVersion) {
|
|
49680
50236
|
console.log(chalk5.green("\u2705 You are already running the latest version!\n"));
|
|
49681
50237
|
return;
|
|
49682
50238
|
}
|
|
49683
|
-
if (
|
|
50239
|
+
if (isNewer2(latestVersion, currentVersion)) {
|
|
49684
50240
|
console.log(chalk5.yellow(`\u{1F4E6} New version available: ${currentVersion} \u2192 ${latestVersion}
|
|
49685
50241
|
`));
|
|
49686
50242
|
await showChangelog(currentVersion, latestVersion);
|
|
@@ -49725,28 +50281,28 @@ var updateCommand = {
|
|
|
49725
50281
|
}
|
|
49726
50282
|
}
|
|
49727
50283
|
};
|
|
49728
|
-
async function
|
|
50284
|
+
async function getCurrentVersion2() {
|
|
49729
50285
|
try {
|
|
49730
|
-
const { stdout } = await
|
|
50286
|
+
const { stdout } = await execAsync6("npm list -g @defai.digital/automatosx --depth=0 --json");
|
|
49731
50287
|
const result = JSON.parse(stdout);
|
|
49732
50288
|
return result.dependencies["@defai.digital/automatosx"]?.version || "unknown";
|
|
49733
50289
|
} catch (error) {
|
|
49734
50290
|
const { readFile: readFile24 } = await import('fs/promises');
|
|
49735
|
-
const { dirname:
|
|
50291
|
+
const { dirname: dirname16, join: join57 } = await import('path');
|
|
49736
50292
|
const { fileURLToPath: fileURLToPath5 } = await import('url');
|
|
49737
50293
|
const __filename3 = fileURLToPath5(import.meta.url);
|
|
49738
|
-
const __dirname4 =
|
|
50294
|
+
const __dirname4 = dirname16(__filename3);
|
|
49739
50295
|
const pkgPath = join57(__dirname4, "../../../package.json");
|
|
49740
50296
|
const content = await readFile24(pkgPath, "utf-8");
|
|
49741
50297
|
const pkg = JSON.parse(content);
|
|
49742
50298
|
return pkg.version;
|
|
49743
50299
|
}
|
|
49744
50300
|
}
|
|
49745
|
-
async function
|
|
49746
|
-
const { stdout } = await
|
|
50301
|
+
async function getLatestVersion2() {
|
|
50302
|
+
const { stdout } = await execAsync6("npm view @defai.digital/automatosx version");
|
|
49747
50303
|
return stdout.trim();
|
|
49748
50304
|
}
|
|
49749
|
-
function
|
|
50305
|
+
function isNewer2(a, b) {
|
|
49750
50306
|
const stripPrerelease = (v) => v.split("-")[0] || v;
|
|
49751
50307
|
const parseVersion = (v) => stripPrerelease(v).split(".").map(Number);
|
|
49752
50308
|
const [aMajor = 0, aMinor = 0, aPatch = 0] = parseVersion(a);
|
|
@@ -49758,7 +50314,7 @@ function isNewer(a, b) {
|
|
|
49758
50314
|
async function showChangelog(from, to) {
|
|
49759
50315
|
try {
|
|
49760
50316
|
console.log(chalk5.cyan("What's new:\n"));
|
|
49761
|
-
const { stdout } = await
|
|
50317
|
+
const { stdout } = await execAsync6(
|
|
49762
50318
|
`curl -s https://api.github.com/repos/defai-digital/automatosx/releases/tags/v${to}`
|
|
49763
50319
|
);
|
|
49764
50320
|
const release = JSON.parse(stdout);
|
|
@@ -49780,7 +50336,7 @@ async function showChangelog(from, to) {
|
|
|
49780
50336
|
}
|
|
49781
50337
|
async function installUpdate(version) {
|
|
49782
50338
|
try {
|
|
49783
|
-
const { stdout, stderr } = await
|
|
50339
|
+
const { stdout, stderr } = await execAsync6(
|
|
49784
50340
|
`npm install -g @defai.digital/automatosx@${version}`,
|
|
49785
50341
|
{ maxBuffer: 10 * 1024 * 1024 }
|
|
49786
50342
|
);
|
|
@@ -49938,6 +50494,7 @@ init_esm_shims();
|
|
|
49938
50494
|
// src/cli/commands/agent/helpers.ts
|
|
49939
50495
|
init_esm_shims();
|
|
49940
50496
|
init_logger();
|
|
50497
|
+
var YAML_EXT_REGEX = /\.(yaml|yml)$/;
|
|
49941
50498
|
var BUILTIN_TEMPLATE_METADATA = {
|
|
49942
50499
|
"basic-agent": {
|
|
49943
50500
|
name: "basic-agent",
|
|
@@ -49974,7 +50531,7 @@ async function listAvailableTemplates(baseDir) {
|
|
|
49974
50531
|
const files = await readdir(projectTemplatesDir);
|
|
49975
50532
|
for (const file of files) {
|
|
49976
50533
|
if (extname$1(file) === ".yaml" || extname$1(file) === ".yml") {
|
|
49977
|
-
const name = file.replace(
|
|
50534
|
+
const name = file.replace(YAML_EXT_REGEX, "");
|
|
49978
50535
|
templates.push({
|
|
49979
50536
|
name,
|
|
49980
50537
|
path: join(projectTemplatesDir, file),
|
|
@@ -49993,7 +50550,7 @@ async function listAvailableTemplates(baseDir) {
|
|
|
49993
50550
|
const files = await readdir(builtinTemplatesDir);
|
|
49994
50551
|
for (const file of files) {
|
|
49995
50552
|
if (extname$1(file) === ".yaml" || extname$1(file) === ".yml") {
|
|
49996
|
-
const name = file.replace(
|
|
50553
|
+
const name = file.replace(YAML_EXT_REGEX, "");
|
|
49997
50554
|
if (templates.find((t) => t.name === name)) {
|
|
49998
50555
|
continue;
|
|
49999
50556
|
}
|
|
@@ -52639,7 +53196,7 @@ var CommandTranslator = class {
|
|
|
52639
53196
|
if (baseDir === void 0) {
|
|
52640
53197
|
try {
|
|
52641
53198
|
baseDir = await realpath$1(dir);
|
|
52642
|
-
} catch
|
|
53199
|
+
} catch {
|
|
52643
53200
|
return commands;
|
|
52644
53201
|
}
|
|
52645
53202
|
}
|
|
@@ -52657,7 +53214,7 @@ var CommandTranslator = class {
|
|
|
52657
53214
|
let realPath;
|
|
52658
53215
|
try {
|
|
52659
53216
|
realPath = await realpath$1(fullPath);
|
|
52660
|
-
} catch
|
|
53217
|
+
} catch {
|
|
52661
53218
|
console.warn(`[Security] Cannot resolve path: ${fullPath}`);
|
|
52662
53219
|
continue;
|
|
52663
53220
|
}
|
|
@@ -52700,7 +53257,7 @@ var CommandTranslator = class {
|
|
|
52700
53257
|
}
|
|
52701
53258
|
}
|
|
52702
53259
|
}
|
|
52703
|
-
} catch
|
|
53260
|
+
} catch {
|
|
52704
53261
|
}
|
|
52705
53262
|
return commands;
|
|
52706
53263
|
}
|
|
@@ -54013,7 +54570,7 @@ async function handleSpecExplain(workspacePath, options) {
|
|
|
54013
54570
|
console.log(chalk5.blue.bold("\n\u{1F4D6} Spec-Kit: Explain Spec\n"));
|
|
54014
54571
|
try {
|
|
54015
54572
|
const specContent = readFileSync(options.file, "utf-8");
|
|
54016
|
-
const spec =
|
|
54573
|
+
const spec = yaml3.load(specContent);
|
|
54017
54574
|
const validation = validateSpec(spec);
|
|
54018
54575
|
if (!validation.valid) {
|
|
54019
54576
|
console.error(chalk5.red("\n\u2717 Spec validation failed:\n"));
|
|
@@ -56115,7 +56672,7 @@ async function handleGenPlan(specFile, argv) {
|
|
|
56115
56672
|
process.exit(1);
|
|
56116
56673
|
}
|
|
56117
56674
|
const specContent = readFileSync(specFile, "utf-8");
|
|
56118
|
-
const spec =
|
|
56675
|
+
const spec = yaml3.load(specContent);
|
|
56119
56676
|
const validation = validateSpec(spec);
|
|
56120
56677
|
if (!validation.valid) {
|
|
56121
56678
|
spinner.fail("Spec validation failed");
|
|
@@ -56173,7 +56730,7 @@ async function handleGenDag(specFile, argv) {
|
|
|
56173
56730
|
process.exit(1);
|
|
56174
56731
|
}
|
|
56175
56732
|
const specContent = readFileSync(specFile, "utf-8");
|
|
56176
|
-
const spec =
|
|
56733
|
+
const spec = yaml3.load(specContent);
|
|
56177
56734
|
const validation = validateSpec(spec);
|
|
56178
56735
|
if (!validation.valid) {
|
|
56179
56736
|
spinner.fail("Spec validation failed");
|
|
@@ -56260,7 +56817,7 @@ async function handleGenScaffold(specFile, argv) {
|
|
|
56260
56817
|
process.exit(1);
|
|
56261
56818
|
}
|
|
56262
56819
|
const specContent = readFileSync(specFile, "utf-8");
|
|
56263
|
-
const spec =
|
|
56820
|
+
const spec = yaml3.load(specContent);
|
|
56264
56821
|
const validation = validateSpec(spec);
|
|
56265
56822
|
if (!validation.valid) {
|
|
56266
56823
|
spinner.fail("Spec validation failed");
|
|
@@ -56332,7 +56889,7 @@ async function handleGenTests(specFile, argv) {
|
|
|
56332
56889
|
process.exit(1);
|
|
56333
56890
|
}
|
|
56334
56891
|
const specContent = readFileSync(specFile, "utf-8");
|
|
56335
|
-
const spec =
|
|
56892
|
+
const spec = yaml3.load(specContent);
|
|
56336
56893
|
const validation = validateSpec(spec);
|
|
56337
56894
|
if (!validation.valid) {
|
|
56338
56895
|
spinner.fail("Spec validation failed");
|
|
@@ -56567,7 +57124,7 @@ async function handleList(config, argv) {
|
|
|
56567
57124
|
});
|
|
56568
57125
|
}
|
|
56569
57126
|
}
|
|
56570
|
-
|
|
57127
|
+
const displayProviders2 = argv.available ? providers.filter((p) => p.enabled && !p.limitInfo?.isBlocked) : providers;
|
|
56571
57128
|
switch (argv.sort) {
|
|
56572
57129
|
case "cost":
|
|
56573
57130
|
displayProviders2.sort((a, b) => {
|
|
@@ -58210,8 +58767,8 @@ async function runMcpDiagnostics(verbose) {
|
|
|
58210
58767
|
let serverStarts = false;
|
|
58211
58768
|
if (cliAvailable) {
|
|
58212
58769
|
try {
|
|
58213
|
-
const { spawn:
|
|
58214
|
-
const proc =
|
|
58770
|
+
const { spawn: spawn13 } = await import('child_process');
|
|
58771
|
+
const proc = spawn13("automatosx", ["mcp", "server"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
58215
58772
|
const initResult = await new Promise((resolve13) => {
|
|
58216
58773
|
let output = "";
|
|
58217
58774
|
proc.stderr?.on("data", (data) => {
|
|
@@ -58311,7 +58868,7 @@ async function runMcpDiagnostics(verbose) {
|
|
|
58311
58868
|
init_esm_shims();
|
|
58312
58869
|
init_logger();
|
|
58313
58870
|
init_safe_timers();
|
|
58314
|
-
var
|
|
58871
|
+
var execAsync7 = promisify(exec);
|
|
58315
58872
|
var cleanupCommand2 = {
|
|
58316
58873
|
command: "cleanup [provider]",
|
|
58317
58874
|
describe: "Clean up orphaned provider processes (v6.0.7 Phase 3)",
|
|
@@ -58440,7 +58997,7 @@ async function findProcesses(processName, verbose) {
|
|
|
58440
58997
|
throw new Error(`Invalid process name: ${processName}. Only alphanumeric characters, dashes, and underscores are allowed.`);
|
|
58441
58998
|
}
|
|
58442
58999
|
try {
|
|
58443
|
-
const { stdout } = await
|
|
59000
|
+
const { stdout } = await execAsync7("ps -eo pid,comm,etime,rss");
|
|
58444
59001
|
if (!stdout.trim()) {
|
|
58445
59002
|
return processes;
|
|
58446
59003
|
}
|
|
@@ -62922,10 +63479,10 @@ init_esm_shims();
|
|
|
62922
63479
|
// src/core/bugfix/git-utils.ts
|
|
62923
63480
|
init_esm_shims();
|
|
62924
63481
|
init_logger();
|
|
62925
|
-
var
|
|
63482
|
+
var execAsync8 = promisify(exec);
|
|
62926
63483
|
async function isGitRepo(cwd) {
|
|
62927
63484
|
try {
|
|
62928
|
-
await
|
|
63485
|
+
await execAsync8("git rev-parse --is-inside-work-tree", { cwd });
|
|
62929
63486
|
return true;
|
|
62930
63487
|
} catch {
|
|
62931
63488
|
return false;
|
|
@@ -62943,14 +63500,14 @@ async function getChangedFiles(options) {
|
|
|
62943
63500
|
command = "git diff --cached --name-only --diff-filter=ACMR";
|
|
62944
63501
|
} else if (options.since) {
|
|
62945
63502
|
const mergeBaseCmd = `git merge-base HEAD ${options.since}`;
|
|
62946
|
-
const { stdout: mergeBase } = await
|
|
63503
|
+
const { stdout: mergeBase } = await execAsync8(mergeBaseCmd, { cwd });
|
|
62947
63504
|
command = `git diff --name-only ${mergeBase.trim()}..HEAD`;
|
|
62948
63505
|
} else if (options.changed) {
|
|
62949
63506
|
command = "git diff --name-only HEAD";
|
|
62950
63507
|
} else {
|
|
62951
63508
|
command = "git diff --name-only HEAD";
|
|
62952
63509
|
}
|
|
62953
|
-
const { stdout } = await
|
|
63510
|
+
const { stdout } = await execAsync8(command, { cwd });
|
|
62954
63511
|
const files = stdout.split("\n").map((f) => f.trim()).filter((f) => f.length > 0);
|
|
62955
63512
|
logger.debug("Git changed files", {
|
|
62956
63513
|
command,
|