@cocaxcode/ai-context-inspector 0.3.3 → 0.4.0
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 +124 -9
- package/dist/{chunk-CAQUPN6F.js → chunk-3NLUFQSB.js} +940 -1
- package/dist/index.js +657 -8
- package/dist/server-22Y7DPSM.js +395 -0
- package/package.json +7 -3
- package/dist/server-HS77RFVF.js +0 -179
|
@@ -3030,9 +3030,948 @@ function escHtml(str) {
|
|
|
3030
3030
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3031
3031
|
}
|
|
3032
3032
|
|
|
3033
|
+
// src/ecosystem/types.ts
|
|
3034
|
+
var ACI_DIR = ".aci";
|
|
3035
|
+
var ACI_BUNDLE = "bundle.json";
|
|
3036
|
+
var ACI_README = "README.md";
|
|
3037
|
+
|
|
3038
|
+
// src/ecosystem/secrets.ts
|
|
3039
|
+
var SENSITIVE_VAR_REGEX = /key|token|secret|password|credential|auth/i;
|
|
3040
|
+
var VAR_REFERENCE_REGEX = /^\$\{.+\}$/;
|
|
3041
|
+
var ARG_VAR_REGEX = /\$\{([^}]+)\}/g;
|
|
3042
|
+
function isSensitiveVar(varName) {
|
|
3043
|
+
return SENSITIVE_VAR_REGEX.test(varName);
|
|
3044
|
+
}
|
|
3045
|
+
function detectEnvVars(servers) {
|
|
3046
|
+
const vars = [];
|
|
3047
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3048
|
+
for (const server of servers) {
|
|
3049
|
+
if (server.config.env) {
|
|
3050
|
+
for (const [varName, value] of Object.entries(server.config.env)) {
|
|
3051
|
+
const key = `${server.name}:${varName}`;
|
|
3052
|
+
if (seen.has(key)) continue;
|
|
3053
|
+
seen.add(key);
|
|
3054
|
+
vars.push({
|
|
3055
|
+
serverName: server.name,
|
|
3056
|
+
varName,
|
|
3057
|
+
value,
|
|
3058
|
+
isSensitive: isSensitiveVar(varName)
|
|
3059
|
+
});
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
if (server.config.args) {
|
|
3063
|
+
for (const arg of server.config.args) {
|
|
3064
|
+
let match;
|
|
3065
|
+
while ((match = ARG_VAR_REGEX.exec(arg)) !== null) {
|
|
3066
|
+
const varName = match[1];
|
|
3067
|
+
const key = `${server.name}:${varName}`;
|
|
3068
|
+
if (seen.has(key)) continue;
|
|
3069
|
+
seen.add(key);
|
|
3070
|
+
vars.push({
|
|
3071
|
+
serverName: server.name,
|
|
3072
|
+
varName,
|
|
3073
|
+
value: `\${${varName}}`,
|
|
3074
|
+
isSensitive: isSensitiveVar(varName)
|
|
3075
|
+
});
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
return vars;
|
|
3081
|
+
}
|
|
3082
|
+
function redactValue(varName) {
|
|
3083
|
+
return `\${${varName}}`;
|
|
3084
|
+
}
|
|
3085
|
+
function applySecretsPolicy(env, mode, customDecisions) {
|
|
3086
|
+
if (!env || Object.keys(env).length === 0) {
|
|
3087
|
+
return { env: void 0, redacted: [], included: [] };
|
|
3088
|
+
}
|
|
3089
|
+
const processed = {};
|
|
3090
|
+
const redacted = [];
|
|
3091
|
+
const included = [];
|
|
3092
|
+
for (const [varName, value] of Object.entries(env)) {
|
|
3093
|
+
if (VAR_REFERENCE_REGEX.test(value)) {
|
|
3094
|
+
processed[varName] = value;
|
|
3095
|
+
included.push(varName);
|
|
3096
|
+
continue;
|
|
3097
|
+
}
|
|
3098
|
+
if (mode === "none") {
|
|
3099
|
+
processed[varName] = redactValue(varName);
|
|
3100
|
+
redacted.push(varName);
|
|
3101
|
+
} else if (mode === "all") {
|
|
3102
|
+
processed[varName] = value;
|
|
3103
|
+
included.push(varName);
|
|
3104
|
+
} else {
|
|
3105
|
+
const include = customDecisions?.[varName] ?? false;
|
|
3106
|
+
if (include) {
|
|
3107
|
+
processed[varName] = value;
|
|
3108
|
+
included.push(varName);
|
|
3109
|
+
} else {
|
|
3110
|
+
processed[varName] = redactValue(varName);
|
|
3111
|
+
redacted.push(varName);
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
return { env: processed, redacted, included };
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
// src/ecosystem/export.ts
|
|
3119
|
+
import { readFile as readFile7, writeFile, readdir as readdir5, stat as stat5, mkdir } from "fs/promises";
|
|
3120
|
+
import { join as join7, basename as basename3, dirname, relative as relative2 } from "path";
|
|
3121
|
+
import { createHash } from "crypto";
|
|
3122
|
+
async function exportEcosystem(options) {
|
|
3123
|
+
const scan = await runAllScanners({
|
|
3124
|
+
dir: options.dir,
|
|
3125
|
+
includeUser: options.includeUser,
|
|
3126
|
+
introspect: false,
|
|
3127
|
+
timeout: 5e3
|
|
3128
|
+
});
|
|
3129
|
+
const resources = await buildBundleResources(scan, options);
|
|
3130
|
+
const checksum = computeChecksum(resources);
|
|
3131
|
+
const warnings = [];
|
|
3132
|
+
if (options.secrets === "all") {
|
|
3133
|
+
warnings.push("Secretos incluidos sin redactar. No compartas este bundle publicamente.");
|
|
3134
|
+
}
|
|
3135
|
+
for (const w of scan.warnings) {
|
|
3136
|
+
warnings.push(`[${w.scanner}] ${w.message}`);
|
|
3137
|
+
}
|
|
3138
|
+
const bundle = {
|
|
3139
|
+
version: 1,
|
|
3140
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3141
|
+
sourceProject: scan.project.name,
|
|
3142
|
+
checksum,
|
|
3143
|
+
warnings,
|
|
3144
|
+
resources
|
|
3145
|
+
};
|
|
3146
|
+
const aciDir = join7(options.dir, ACI_DIR);
|
|
3147
|
+
await writeBundle(bundle, aciDir);
|
|
3148
|
+
const readme = generateReadme(bundle);
|
|
3149
|
+
await writeFile(join7(aciDir, ACI_README), readme, "utf-8");
|
|
3150
|
+
await ensureGitignore(options.dir);
|
|
3151
|
+
return bundle;
|
|
3152
|
+
}
|
|
3153
|
+
async function buildBundleResources(scan, options) {
|
|
3154
|
+
const categories = options.only ?? ["mcp", "skills", "agents", "memories", "context"];
|
|
3155
|
+
const projectDir = scan.project.path;
|
|
3156
|
+
const resources = {
|
|
3157
|
+
mcpServers: [],
|
|
3158
|
+
skills: [],
|
|
3159
|
+
agents: [],
|
|
3160
|
+
memories: [],
|
|
3161
|
+
contextFiles: []
|
|
3162
|
+
};
|
|
3163
|
+
if (categories.includes("mcp")) {
|
|
3164
|
+
resources.mcpServers = buildMcpServers(scan, options);
|
|
3165
|
+
}
|
|
3166
|
+
if (categories.includes("skills")) {
|
|
3167
|
+
resources.skills = await buildSkills(scan);
|
|
3168
|
+
}
|
|
3169
|
+
if (categories.includes("agents")) {
|
|
3170
|
+
resources.agents = await buildAgents(scan);
|
|
3171
|
+
}
|
|
3172
|
+
if (categories.includes("memories")) {
|
|
3173
|
+
resources.memories = await buildMemories(scan, projectDir);
|
|
3174
|
+
}
|
|
3175
|
+
if (categories.includes("context")) {
|
|
3176
|
+
resources.contextFiles = await buildContextFiles(scan);
|
|
3177
|
+
}
|
|
3178
|
+
return resources;
|
|
3179
|
+
}
|
|
3180
|
+
function buildMcpServers(scan, options) {
|
|
3181
|
+
const servers = [];
|
|
3182
|
+
for (const server of scan.mcpServers) {
|
|
3183
|
+
const { env, redacted, included } = applySecretsPolicy(
|
|
3184
|
+
server.config.env,
|
|
3185
|
+
options.secrets,
|
|
3186
|
+
options.secretDecisions
|
|
3187
|
+
);
|
|
3188
|
+
const scope = server.source === "user" || server.source === "desktop" ? "user" : "project";
|
|
3189
|
+
servers.push({
|
|
3190
|
+
name: server.name,
|
|
3191
|
+
scope,
|
|
3192
|
+
config: {
|
|
3193
|
+
transport: server.config.transport,
|
|
3194
|
+
command: server.config.command,
|
|
3195
|
+
args: server.config.args,
|
|
3196
|
+
env,
|
|
3197
|
+
url: server.config.url
|
|
3198
|
+
},
|
|
3199
|
+
envVarsRedacted: redacted,
|
|
3200
|
+
envVarsIncluded: included
|
|
3201
|
+
});
|
|
3202
|
+
}
|
|
3203
|
+
return servers;
|
|
3204
|
+
}
|
|
3205
|
+
async function buildSkills(scan) {
|
|
3206
|
+
const skills = [];
|
|
3207
|
+
for (const skill of scan.skills) {
|
|
3208
|
+
try {
|
|
3209
|
+
const content = await readFile7(skill.path, "utf-8");
|
|
3210
|
+
const dirName = basename3(dirname(skill.path));
|
|
3211
|
+
skills.push({
|
|
3212
|
+
name: skill.name,
|
|
3213
|
+
scope: skill.scope,
|
|
3214
|
+
dirName,
|
|
3215
|
+
content,
|
|
3216
|
+
description: skill.description,
|
|
3217
|
+
triggers: skill.triggers
|
|
3218
|
+
});
|
|
3219
|
+
} catch {
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
return skills;
|
|
3223
|
+
}
|
|
3224
|
+
async function buildAgents(scan) {
|
|
3225
|
+
const agents = [];
|
|
3226
|
+
for (const agent of scan.agents) {
|
|
3227
|
+
try {
|
|
3228
|
+
const content = await readFile7(agent.path, "utf-8");
|
|
3229
|
+
const fileName = basename3(agent.path);
|
|
3230
|
+
agents.push({
|
|
3231
|
+
name: agent.name,
|
|
3232
|
+
scope: agent.scope,
|
|
3233
|
+
fileName,
|
|
3234
|
+
content,
|
|
3235
|
+
description: agent.description,
|
|
3236
|
+
model: agent.model
|
|
3237
|
+
});
|
|
3238
|
+
} catch {
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
return agents;
|
|
3242
|
+
}
|
|
3243
|
+
async function buildMemories(scan, projectDir) {
|
|
3244
|
+
const memories = [];
|
|
3245
|
+
for (const memory of scan.memories) {
|
|
3246
|
+
if (memory.source !== "filesystem") continue;
|
|
3247
|
+
if (!memory.path) continue;
|
|
3248
|
+
const files = [];
|
|
3249
|
+
try {
|
|
3250
|
+
if (memory.type === "atl" || memory.type === "openspec") {
|
|
3251
|
+
const cleanPath = memory.path.endsWith("/") ? memory.path.slice(0, -1) : memory.path;
|
|
3252
|
+
const absDir = join7(projectDir, cleanPath);
|
|
3253
|
+
const dirFiles = await readDirRecursive(absDir, absDir);
|
|
3254
|
+
files.push(...dirFiles);
|
|
3255
|
+
} else if (memory.type === "claude-memory") {
|
|
3256
|
+
const content = await readFile7(memory.path, "utf-8");
|
|
3257
|
+
files.push({ relativePath: basename3(memory.path), content });
|
|
3258
|
+
} else if (memory.type === "agent-memory") {
|
|
3259
|
+
const s = await stat5(memory.path);
|
|
3260
|
+
if (s.isDirectory()) {
|
|
3261
|
+
const entries = await readdir5(memory.path);
|
|
3262
|
+
for (const entry of entries) {
|
|
3263
|
+
if (!entry.endsWith(".md")) continue;
|
|
3264
|
+
const filePath = join7(memory.path, entry);
|
|
3265
|
+
const content = await readFile7(filePath, "utf-8");
|
|
3266
|
+
files.push({ relativePath: entry, content });
|
|
3267
|
+
}
|
|
3268
|
+
} else {
|
|
3269
|
+
const content = await readFile7(memory.path, "utf-8");
|
|
3270
|
+
files.push({ relativePath: basename3(memory.path), content });
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
} catch {
|
|
3274
|
+
}
|
|
3275
|
+
const scope = memory.type === "claude-memory" || memory.type === "agent-memory" ? "user" : "project";
|
|
3276
|
+
if (files.length > 0) {
|
|
3277
|
+
memories.push({ type: memory.type, scope, files });
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
return memories;
|
|
3281
|
+
}
|
|
3282
|
+
async function buildContextFiles(scan) {
|
|
3283
|
+
const contextFiles = [];
|
|
3284
|
+
for (const cf of scan.contextFiles) {
|
|
3285
|
+
if (cf.type === "directory" && cf.children) {
|
|
3286
|
+
for (const child of cf.children) {
|
|
3287
|
+
if (child.type === "file") {
|
|
3288
|
+
try {
|
|
3289
|
+
const content = await readFile7(child.absolutePath, "utf-8");
|
|
3290
|
+
contextFiles.push({
|
|
3291
|
+
path: child.path,
|
|
3292
|
+
scope: child.scope,
|
|
3293
|
+
tool: child.tool,
|
|
3294
|
+
content
|
|
3295
|
+
});
|
|
3296
|
+
} catch {
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
} else if (cf.type === "file") {
|
|
3301
|
+
try {
|
|
3302
|
+
const content = await readFile7(cf.absolutePath, "utf-8");
|
|
3303
|
+
contextFiles.push({
|
|
3304
|
+
path: cf.path,
|
|
3305
|
+
scope: cf.scope,
|
|
3306
|
+
tool: cf.tool,
|
|
3307
|
+
content
|
|
3308
|
+
});
|
|
3309
|
+
} catch {
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
return contextFiles;
|
|
3314
|
+
}
|
|
3315
|
+
async function readDirRecursive(dirPath, basePath) {
|
|
3316
|
+
const files = [];
|
|
3317
|
+
try {
|
|
3318
|
+
const entries = await readdir5(dirPath, { withFileTypes: true });
|
|
3319
|
+
for (const entry of entries) {
|
|
3320
|
+
const fullPath = join7(dirPath, entry.name);
|
|
3321
|
+
if (entry.isDirectory()) {
|
|
3322
|
+
const subFiles = await readDirRecursive(fullPath, basePath);
|
|
3323
|
+
files.push(...subFiles);
|
|
3324
|
+
} else if (entry.isFile()) {
|
|
3325
|
+
try {
|
|
3326
|
+
const content = await readFile7(fullPath, "utf-8");
|
|
3327
|
+
files.push({
|
|
3328
|
+
relativePath: relative2(basePath, fullPath),
|
|
3329
|
+
content
|
|
3330
|
+
});
|
|
3331
|
+
} catch {
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
} catch {
|
|
3336
|
+
}
|
|
3337
|
+
return files;
|
|
3338
|
+
}
|
|
3339
|
+
function computeChecksum(resources) {
|
|
3340
|
+
const json = JSON.stringify(resources, Object.keys(resources).sort());
|
|
3341
|
+
return createHash("sha256").update(json).digest("hex");
|
|
3342
|
+
}
|
|
3343
|
+
function verifyChecksum(bundle) {
|
|
3344
|
+
const computed = computeChecksum(bundle.resources);
|
|
3345
|
+
return computed === bundle.checksum;
|
|
3346
|
+
}
|
|
3347
|
+
async function writeBundle(bundle, dir) {
|
|
3348
|
+
await mkdir(dir, { recursive: true });
|
|
3349
|
+
const filePath = join7(dir, ACI_BUNDLE);
|
|
3350
|
+
const json = JSON.stringify(bundle, null, 2);
|
|
3351
|
+
await writeFile(filePath, json, "utf-8");
|
|
3352
|
+
return filePath;
|
|
3353
|
+
}
|
|
3354
|
+
function generateReadme(bundle) {
|
|
3355
|
+
const r = bundle.resources;
|
|
3356
|
+
const lines = [
|
|
3357
|
+
"# ACI Export Bundle",
|
|
3358
|
+
"",
|
|
3359
|
+
`Proyecto: **${bundle.sourceProject}**`,
|
|
3360
|
+
`Fecha: ${bundle.createdAt}`,
|
|
3361
|
+
`Version: ${bundle.version}`,
|
|
3362
|
+
"",
|
|
3363
|
+
"## Contenido",
|
|
3364
|
+
"",
|
|
3365
|
+
`| Categoria | Cantidad |`,
|
|
3366
|
+
`|-----------|----------|`,
|
|
3367
|
+
`| MCP Servers | ${r.mcpServers.length} |`,
|
|
3368
|
+
`| Skills | ${r.skills.length} |`,
|
|
3369
|
+
`| Agents | ${r.agents.length} |`,
|
|
3370
|
+
`| Memories | ${r.memories.length} |`,
|
|
3371
|
+
`| Context Files | ${r.contextFiles.length} |`,
|
|
3372
|
+
""
|
|
3373
|
+
];
|
|
3374
|
+
if (r.mcpServers.length > 0) {
|
|
3375
|
+
lines.push("## MCP Servers", "");
|
|
3376
|
+
for (const s of r.mcpServers) {
|
|
3377
|
+
lines.push(`- **${s.name}** (${s.config.transport}, ${s.scope})`);
|
|
3378
|
+
if (s.envVarsRedacted.length > 0) {
|
|
3379
|
+
lines.push(` - Redactadas: ${s.envVarsRedacted.join(", ")}`);
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
lines.push("");
|
|
3383
|
+
}
|
|
3384
|
+
if (r.skills.length > 0) {
|
|
3385
|
+
lines.push("## Skills", "");
|
|
3386
|
+
for (const s of r.skills) {
|
|
3387
|
+
lines.push(`- **${s.name}** \u2014 ${s.description ?? "sin descripcion"}`);
|
|
3388
|
+
}
|
|
3389
|
+
lines.push("");
|
|
3390
|
+
}
|
|
3391
|
+
if (r.agents.length > 0) {
|
|
3392
|
+
lines.push("## Agents", "");
|
|
3393
|
+
for (const a of r.agents) {
|
|
3394
|
+
lines.push(`- **${a.name}** \u2014 ${a.description ?? "sin descripcion"}`);
|
|
3395
|
+
}
|
|
3396
|
+
lines.push("");
|
|
3397
|
+
}
|
|
3398
|
+
if (r.contextFiles.length > 0) {
|
|
3399
|
+
lines.push("## Context Files", "");
|
|
3400
|
+
for (const cf of r.contextFiles) {
|
|
3401
|
+
lines.push(`- \`${cf.path}\` (${cf.tool})`);
|
|
3402
|
+
}
|
|
3403
|
+
lines.push("");
|
|
3404
|
+
}
|
|
3405
|
+
if (bundle.warnings.length > 0) {
|
|
3406
|
+
lines.push("## Advertencias", "");
|
|
3407
|
+
for (const w of bundle.warnings) {
|
|
3408
|
+
lines.push(`- ${w}`);
|
|
3409
|
+
}
|
|
3410
|
+
lines.push("");
|
|
3411
|
+
}
|
|
3412
|
+
lines.push("---", "Generado por [@cocaxcode/ai-context-inspector](https://github.com/cocaxcode/ai-context-inspector)", "");
|
|
3413
|
+
return lines.join("\n");
|
|
3414
|
+
}
|
|
3415
|
+
async function ensureGitignore(dir) {
|
|
3416
|
+
const gitignorePath = join7(dir, ".gitignore");
|
|
3417
|
+
const entry = ".aci/";
|
|
3418
|
+
let content = "";
|
|
3419
|
+
try {
|
|
3420
|
+
content = await readFile7(gitignorePath, "utf-8");
|
|
3421
|
+
} catch {
|
|
3422
|
+
}
|
|
3423
|
+
const lines = content.split("\n");
|
|
3424
|
+
if (lines.some((line) => line.trim() === entry)) {
|
|
3425
|
+
return false;
|
|
3426
|
+
}
|
|
3427
|
+
const newContent = content.length > 0 && !content.endsWith("\n") ? content + "\n" + entry + "\n" : content + entry + "\n";
|
|
3428
|
+
await writeFile(gitignorePath, newContent, "utf-8");
|
|
3429
|
+
return true;
|
|
3430
|
+
}
|
|
3431
|
+
|
|
3432
|
+
// src/ecosystem/detect-target.ts
|
|
3433
|
+
import { access } from "fs/promises";
|
|
3434
|
+
import { join as join8 } from "path";
|
|
3435
|
+
|
|
3436
|
+
// src/ecosystem/target-map.ts
|
|
3437
|
+
var TARGET_CONFIGS = {
|
|
3438
|
+
claude: {
|
|
3439
|
+
mcpConfigPath: ".mcp.json",
|
|
3440
|
+
mcpConfigFormat: "flat",
|
|
3441
|
+
contextFilePath: "CLAUDE.md",
|
|
3442
|
+
rulesDir: null,
|
|
3443
|
+
skillsDir: ".claude/skills",
|
|
3444
|
+
agentsDir: ".claude/agents"
|
|
3445
|
+
},
|
|
3446
|
+
cursor: {
|
|
3447
|
+
mcpConfigPath: ".cursor/mcp.json",
|
|
3448
|
+
mcpConfigFormat: "flat",
|
|
3449
|
+
contextFilePath: ".cursorrules",
|
|
3450
|
+
rulesDir: ".cursor/rules",
|
|
3451
|
+
skillsDir: null,
|
|
3452
|
+
agentsDir: null
|
|
3453
|
+
},
|
|
3454
|
+
windsurf: {
|
|
3455
|
+
mcpConfigPath: ".mcp.json",
|
|
3456
|
+
mcpConfigFormat: "flat",
|
|
3457
|
+
contextFilePath: ".windsurfrules",
|
|
3458
|
+
rulesDir: ".windsurf/rules",
|
|
3459
|
+
skillsDir: null,
|
|
3460
|
+
agentsDir: null
|
|
3461
|
+
},
|
|
3462
|
+
copilot: {
|
|
3463
|
+
mcpConfigPath: ".vscode/mcp.json",
|
|
3464
|
+
mcpConfigFormat: "flat",
|
|
3465
|
+
contextFilePath: ".github/copilot-instructions.md",
|
|
3466
|
+
rulesDir: ".github/instructions",
|
|
3467
|
+
skillsDir: null,
|
|
3468
|
+
agentsDir: ".github/agents"
|
|
3469
|
+
},
|
|
3470
|
+
gemini: {
|
|
3471
|
+
mcpConfigPath: ".gemini/settings.json",
|
|
3472
|
+
mcpConfigFormat: "nested",
|
|
3473
|
+
contextFilePath: "GEMINI.md",
|
|
3474
|
+
rulesDir: ".gemini/rules",
|
|
3475
|
+
skillsDir: null,
|
|
3476
|
+
agentsDir: null
|
|
3477
|
+
},
|
|
3478
|
+
codex: {
|
|
3479
|
+
mcpConfigPath: ".mcp.json",
|
|
3480
|
+
mcpConfigFormat: "flat",
|
|
3481
|
+
contextFilePath: "AGENTS.md",
|
|
3482
|
+
rulesDir: ".codex/rules",
|
|
3483
|
+
skillsDir: null,
|
|
3484
|
+
agentsDir: null
|
|
3485
|
+
},
|
|
3486
|
+
opencode: {
|
|
3487
|
+
mcpConfigPath: "opencode.json",
|
|
3488
|
+
mcpConfigFormat: "nested",
|
|
3489
|
+
contextFilePath: "OPENCODE.md",
|
|
3490
|
+
rulesDir: ".opencode/rules",
|
|
3491
|
+
skillsDir: null,
|
|
3492
|
+
agentsDir: null
|
|
3493
|
+
}
|
|
3494
|
+
};
|
|
3495
|
+
var TOOL_MARKERS = {
|
|
3496
|
+
claude: ["CLAUDE.md", ".claude", ".mcp.json"],
|
|
3497
|
+
cursor: [".cursorrules", ".cursor"],
|
|
3498
|
+
windsurf: [".windsurfrules", ".windsurf"],
|
|
3499
|
+
copilot: [
|
|
3500
|
+
".github/copilot-instructions.md",
|
|
3501
|
+
".github/agents",
|
|
3502
|
+
".vscode/mcp.json"
|
|
3503
|
+
],
|
|
3504
|
+
gemini: ["GEMINI.md", ".gemini"],
|
|
3505
|
+
codex: ["AGENTS.md", ".codex"],
|
|
3506
|
+
opencode: ["OPENCODE.md", ".opencode", "opencode.json"]
|
|
3507
|
+
};
|
|
3508
|
+
var PRIMARY_CONTEXT_MAP = {
|
|
3509
|
+
"CLAUDE.md": "claude",
|
|
3510
|
+
".cursorrules": "cursor",
|
|
3511
|
+
".windsurfrules": "windsurf",
|
|
3512
|
+
".github/copilot-instructions.md": "copilot",
|
|
3513
|
+
"GEMINI.md": "gemini",
|
|
3514
|
+
"AGENTS.md": "codex",
|
|
3515
|
+
"OPENCODE.md": "opencode"
|
|
3516
|
+
};
|
|
3517
|
+
|
|
3518
|
+
// src/ecosystem/detect-target.ts
|
|
3519
|
+
async function detectTargetTools(dir) {
|
|
3520
|
+
const results = [];
|
|
3521
|
+
for (const [target, markers] of Object.entries(TOOL_MARKERS)) {
|
|
3522
|
+
let count = 0;
|
|
3523
|
+
for (const marker of markers) {
|
|
3524
|
+
try {
|
|
3525
|
+
await access(join8(dir, marker));
|
|
3526
|
+
count++;
|
|
3527
|
+
} catch {
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
if (count > 0) {
|
|
3531
|
+
results.push({ target, count });
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
results.sort((a, b) => b.count - a.count);
|
|
3535
|
+
return results.map((r) => r.target);
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3538
|
+
// src/ecosystem/import.ts
|
|
3539
|
+
import { readFile as readFile8, writeFile as writeFile2, mkdir as mkdir2, access as access2 } from "fs/promises";
|
|
3540
|
+
import { join as join9, dirname as dirname2 } from "path";
|
|
3541
|
+
import { homedir as homedir6 } from "os";
|
|
3542
|
+
async function loadBundle(filePath, dir) {
|
|
3543
|
+
const resolvedPath = filePath ?? join9(dir ?? ".", ACI_DIR, ACI_BUNDLE);
|
|
3544
|
+
let raw;
|
|
3545
|
+
try {
|
|
3546
|
+
raw = await readFile8(resolvedPath, "utf-8");
|
|
3547
|
+
} catch {
|
|
3548
|
+
throw new Error(`Bundle no encontrado: ${resolvedPath}`);
|
|
3549
|
+
}
|
|
3550
|
+
let bundle;
|
|
3551
|
+
try {
|
|
3552
|
+
bundle = JSON.parse(raw);
|
|
3553
|
+
} catch {
|
|
3554
|
+
throw new Error(`Bundle no es JSON valido: ${resolvedPath}`);
|
|
3555
|
+
}
|
|
3556
|
+
if (bundle.version !== 1) {
|
|
3557
|
+
throw new Error(`Version no soportada: ${bundle.version}. Se requiere version 1.`);
|
|
3558
|
+
}
|
|
3559
|
+
if (!verifyChecksum(bundle)) {
|
|
3560
|
+
throw new Error("Checksum invalido: el bundle puede estar corrupto o modificado.");
|
|
3561
|
+
}
|
|
3562
|
+
return bundle;
|
|
3563
|
+
}
|
|
3564
|
+
async function planImport(bundle, options) {
|
|
3565
|
+
let target;
|
|
3566
|
+
if (options.target) {
|
|
3567
|
+
target = options.target;
|
|
3568
|
+
} else {
|
|
3569
|
+
const detected = await detectTargetTools(options.dir);
|
|
3570
|
+
if (detected.length === 0) {
|
|
3571
|
+
throw new Error("No se detecto ninguna herramienta AI. Usa --target para especificar una.");
|
|
3572
|
+
}
|
|
3573
|
+
target = detected[0];
|
|
3574
|
+
}
|
|
3575
|
+
const config = TARGET_CONFIGS[target];
|
|
3576
|
+
const categories = options.only ?? ["mcp", "skills", "agents", "memories", "context"];
|
|
3577
|
+
const actions = [];
|
|
3578
|
+
const pendingEnvVars = [];
|
|
3579
|
+
if (categories.includes("mcp")) {
|
|
3580
|
+
for (const server of bundle.resources.mcpServers) {
|
|
3581
|
+
const scope = resolveScope(server.scope, options.scope);
|
|
3582
|
+
const basePath = getScopedBasePath(options.dir, scope);
|
|
3583
|
+
const mcpPath = join9(basePath, config.mcpConfigPath);
|
|
3584
|
+
const conflict = await checkMcpConflict(
|
|
3585
|
+
server.name,
|
|
3586
|
+
server.config,
|
|
3587
|
+
mcpPath,
|
|
3588
|
+
config.mcpConfigFormat
|
|
3589
|
+
);
|
|
3590
|
+
let action;
|
|
3591
|
+
let reason;
|
|
3592
|
+
if (conflict === "missing") {
|
|
3593
|
+
action = "install";
|
|
3594
|
+
} else if (conflict === "same") {
|
|
3595
|
+
action = "skip";
|
|
3596
|
+
reason = "Misma configuracion ya existe";
|
|
3597
|
+
} else {
|
|
3598
|
+
if (options.force) {
|
|
3599
|
+
action = "overwrite";
|
|
3600
|
+
reason = "Configuracion diferente (--force)";
|
|
3601
|
+
} else {
|
|
3602
|
+
action = "skip";
|
|
3603
|
+
reason = "Configuracion diferente (usa --force para sobreescribir)";
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
actions.push({
|
|
3607
|
+
category: "mcp",
|
|
3608
|
+
name: server.name,
|
|
3609
|
+
action,
|
|
3610
|
+
reason,
|
|
3611
|
+
targetPath: mcpPath
|
|
3612
|
+
});
|
|
3613
|
+
for (const varName of server.envVarsRedacted) {
|
|
3614
|
+
if (!pendingEnvVars.includes(varName)) {
|
|
3615
|
+
pendingEnvVars.push(varName);
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
if (categories.includes("skills")) {
|
|
3621
|
+
for (const skill of bundle.resources.skills) {
|
|
3622
|
+
const scope = resolveScope(skill.scope, options.scope);
|
|
3623
|
+
const basePath = getScopedBasePath(options.dir, scope);
|
|
3624
|
+
if (config.skillsDir) {
|
|
3625
|
+
const skillPath = join9(basePath, config.skillsDir, skill.dirName, "SKILL.md");
|
|
3626
|
+
const exists = await fileExists(skillPath);
|
|
3627
|
+
if (exists && !options.force) {
|
|
3628
|
+
actions.push({
|
|
3629
|
+
category: "skills",
|
|
3630
|
+
name: skill.name,
|
|
3631
|
+
action: "skip",
|
|
3632
|
+
reason: "Skill ya existe (usa --force para sobreescribir)",
|
|
3633
|
+
targetPath: skillPath
|
|
3634
|
+
});
|
|
3635
|
+
} else {
|
|
3636
|
+
actions.push({
|
|
3637
|
+
category: "skills",
|
|
3638
|
+
name: skill.name,
|
|
3639
|
+
action: exists ? "overwrite" : "install",
|
|
3640
|
+
reason: exists ? "Sobreescribiendo skill existente (--force)" : void 0,
|
|
3641
|
+
targetPath: skillPath
|
|
3642
|
+
});
|
|
3643
|
+
}
|
|
3644
|
+
} else if (config.rulesDir) {
|
|
3645
|
+
const rulePath = join9(basePath, config.rulesDir, `${skill.dirName}.md`);
|
|
3646
|
+
const exists = await fileExists(rulePath);
|
|
3647
|
+
if (exists && !options.force) {
|
|
3648
|
+
actions.push({
|
|
3649
|
+
category: "skills",
|
|
3650
|
+
name: skill.name,
|
|
3651
|
+
action: "skip",
|
|
3652
|
+
reason: "Regla ya existe (usa --force para sobreescribir)",
|
|
3653
|
+
targetPath: rulePath
|
|
3654
|
+
});
|
|
3655
|
+
} else {
|
|
3656
|
+
actions.push({
|
|
3657
|
+
category: "skills",
|
|
3658
|
+
name: skill.name,
|
|
3659
|
+
action: exists ? "overwrite" : "install",
|
|
3660
|
+
reason: exists ? "Sobreescribiendo como regla (--force)" : "Instalando como regla",
|
|
3661
|
+
targetPath: rulePath
|
|
3662
|
+
});
|
|
3663
|
+
}
|
|
3664
|
+
} else {
|
|
3665
|
+
actions.push({
|
|
3666
|
+
category: "skills",
|
|
3667
|
+
name: skill.name,
|
|
3668
|
+
action: "unsupported",
|
|
3669
|
+
reason: `${target} no soporta skills ni rules`,
|
|
3670
|
+
targetPath: ""
|
|
3671
|
+
});
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
if (categories.includes("agents")) {
|
|
3676
|
+
for (const agent of bundle.resources.agents) {
|
|
3677
|
+
const scope = resolveScope(agent.scope, options.scope);
|
|
3678
|
+
const basePath = getScopedBasePath(options.dir, scope);
|
|
3679
|
+
if (config.agentsDir) {
|
|
3680
|
+
const agentPath = join9(basePath, config.agentsDir, agent.fileName);
|
|
3681
|
+
const exists = await fileExists(agentPath);
|
|
3682
|
+
if (exists && !options.force) {
|
|
3683
|
+
actions.push({
|
|
3684
|
+
category: "agents",
|
|
3685
|
+
name: agent.name,
|
|
3686
|
+
action: "skip",
|
|
3687
|
+
reason: "Agent ya existe (usa --force para sobreescribir)",
|
|
3688
|
+
targetPath: agentPath
|
|
3689
|
+
});
|
|
3690
|
+
} else {
|
|
3691
|
+
actions.push({
|
|
3692
|
+
category: "agents",
|
|
3693
|
+
name: agent.name,
|
|
3694
|
+
action: exists ? "overwrite" : "install",
|
|
3695
|
+
reason: exists ? "Sobreescribiendo agent existente (--force)" : void 0,
|
|
3696
|
+
targetPath: agentPath
|
|
3697
|
+
});
|
|
3698
|
+
}
|
|
3699
|
+
} else {
|
|
3700
|
+
actions.push({
|
|
3701
|
+
category: "agents",
|
|
3702
|
+
name: agent.name,
|
|
3703
|
+
action: "unsupported",
|
|
3704
|
+
reason: `${target} no soporta agents`,
|
|
3705
|
+
targetPath: ""
|
|
3706
|
+
});
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
if (categories.includes("context")) {
|
|
3711
|
+
for (const cf of bundle.resources.contextFiles) {
|
|
3712
|
+
const mappedPath = mapContextFilePath(cf.path, target);
|
|
3713
|
+
if (mappedPath) {
|
|
3714
|
+
const scope = resolveScope(cf.scope, options.scope);
|
|
3715
|
+
const basePath = getScopedBasePath(options.dir, scope);
|
|
3716
|
+
const fullPath = join9(basePath, mappedPath);
|
|
3717
|
+
const exists = await fileExists(fullPath);
|
|
3718
|
+
if (exists && !options.force) {
|
|
3719
|
+
actions.push({
|
|
3720
|
+
category: "context",
|
|
3721
|
+
name: cf.path,
|
|
3722
|
+
action: "skip",
|
|
3723
|
+
reason: "Archivo de contexto ya existe (usa --force para sobreescribir)",
|
|
3724
|
+
targetPath: fullPath
|
|
3725
|
+
});
|
|
3726
|
+
} else {
|
|
3727
|
+
actions.push({
|
|
3728
|
+
category: "context",
|
|
3729
|
+
name: cf.path,
|
|
3730
|
+
action: exists ? "overwrite" : "install",
|
|
3731
|
+
reason: exists ? "Sobreescribiendo contexto (--force)" : void 0,
|
|
3732
|
+
targetPath: fullPath
|
|
3733
|
+
});
|
|
3734
|
+
}
|
|
3735
|
+
} else {
|
|
3736
|
+
actions.push({
|
|
3737
|
+
category: "context",
|
|
3738
|
+
name: cf.path,
|
|
3739
|
+
action: "unsupported",
|
|
3740
|
+
reason: `Sin equivalente en ${target}`,
|
|
3741
|
+
targetPath: ""
|
|
3742
|
+
});
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
if (categories.includes("memories")) {
|
|
3747
|
+
for (const memory of bundle.resources.memories) {
|
|
3748
|
+
const scope = resolveScope(memory.scope, options.scope);
|
|
3749
|
+
const basePath = getScopedBasePath(options.dir, scope);
|
|
3750
|
+
for (const file of memory.files) {
|
|
3751
|
+
const filePath = join9(basePath, file.relativePath);
|
|
3752
|
+
const exists = await fileExists(filePath);
|
|
3753
|
+
actions.push({
|
|
3754
|
+
category: "memories",
|
|
3755
|
+
name: `${memory.type}/${file.relativePath}`,
|
|
3756
|
+
action: exists && !options.force ? "skip" : exists ? "overwrite" : "install",
|
|
3757
|
+
reason: exists && !options.force ? "Archivo de memoria ya existe" : exists ? "Sobreescribiendo memoria (--force)" : void 0,
|
|
3758
|
+
targetPath: filePath
|
|
3759
|
+
});
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
const summary = {
|
|
3764
|
+
install: actions.filter((a) => a.action === "install").length,
|
|
3765
|
+
skip: actions.filter((a) => a.action === "skip").length,
|
|
3766
|
+
overwrite: actions.filter((a) => a.action === "overwrite").length,
|
|
3767
|
+
unsupported: actions.filter((a) => a.action === "unsupported").length
|
|
3768
|
+
};
|
|
3769
|
+
return { target, actions, pendingEnvVars, summary };
|
|
3770
|
+
}
|
|
3771
|
+
async function executeImport(plan, bundle, options) {
|
|
3772
|
+
const config = TARGET_CONFIGS[plan.target];
|
|
3773
|
+
const installed = [];
|
|
3774
|
+
const skipped = [];
|
|
3775
|
+
const unsupported = [];
|
|
3776
|
+
for (const action of plan.actions) {
|
|
3777
|
+
if (action.action === "unsupported") {
|
|
3778
|
+
unsupported.push(action);
|
|
3779
|
+
continue;
|
|
3780
|
+
}
|
|
3781
|
+
if (action.action === "skip") {
|
|
3782
|
+
skipped.push(action);
|
|
3783
|
+
continue;
|
|
3784
|
+
}
|
|
3785
|
+
try {
|
|
3786
|
+
switch (action.category) {
|
|
3787
|
+
case "mcp": {
|
|
3788
|
+
const server = bundle.resources.mcpServers.find((s) => s.name === action.name);
|
|
3789
|
+
if (server) {
|
|
3790
|
+
await installMcpServer(server, action.targetPath, config.mcpConfigFormat, options.secretValues);
|
|
3791
|
+
}
|
|
3792
|
+
break;
|
|
3793
|
+
}
|
|
3794
|
+
case "skills": {
|
|
3795
|
+
const skill = bundle.resources.skills.find((s) => s.name === action.name);
|
|
3796
|
+
if (skill) {
|
|
3797
|
+
await installSkill(skill, options.dir, config, options.scope, options.force);
|
|
3798
|
+
}
|
|
3799
|
+
break;
|
|
3800
|
+
}
|
|
3801
|
+
case "agents": {
|
|
3802
|
+
const agent = bundle.resources.agents.find((a) => a.name === action.name);
|
|
3803
|
+
if (agent) {
|
|
3804
|
+
await installAgent(agent, options.dir, config, options.scope, options.force);
|
|
3805
|
+
}
|
|
3806
|
+
break;
|
|
3807
|
+
}
|
|
3808
|
+
case "context": {
|
|
3809
|
+
const cf = bundle.resources.contextFiles.find((c) => c.path === action.name);
|
|
3810
|
+
if (cf) {
|
|
3811
|
+
const mappedPath = mapContextFilePath(cf.path, plan.target);
|
|
3812
|
+
if (mappedPath) {
|
|
3813
|
+
await installContextFile(cf, options.dir, mappedPath);
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
break;
|
|
3817
|
+
}
|
|
3818
|
+
case "memories": {
|
|
3819
|
+
const [memType, ...pathParts] = action.name.split("/");
|
|
3820
|
+
const relPath = pathParts.join("/");
|
|
3821
|
+
const memory = bundle.resources.memories.find((m) => m.type === memType);
|
|
3822
|
+
if (memory) {
|
|
3823
|
+
const file = memory.files.find((f) => f.relativePath === relPath);
|
|
3824
|
+
if (file) {
|
|
3825
|
+
await installMemory({ ...memory, files: [file] }, options.dir);
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
break;
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
installed.push(action);
|
|
3832
|
+
} catch {
|
|
3833
|
+
skipped.push({ ...action, reason: "Error al instalar" });
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
await ensureGitignore(options.dir);
|
|
3837
|
+
return {
|
|
3838
|
+
installed,
|
|
3839
|
+
skipped,
|
|
3840
|
+
unsupported,
|
|
3841
|
+
pendingEnvVars: plan.pendingEnvVars
|
|
3842
|
+
};
|
|
3843
|
+
}
|
|
3844
|
+
async function checkMcpConflict(serverName, serverConfig, mcpConfigPath, format) {
|
|
3845
|
+
let raw;
|
|
3846
|
+
try {
|
|
3847
|
+
raw = await readFile8(mcpConfigPath, "utf-8");
|
|
3848
|
+
} catch {
|
|
3849
|
+
return "missing";
|
|
3850
|
+
}
|
|
3851
|
+
let parsed;
|
|
3852
|
+
try {
|
|
3853
|
+
parsed = JSON.parse(raw);
|
|
3854
|
+
} catch {
|
|
3855
|
+
return "missing";
|
|
3856
|
+
}
|
|
3857
|
+
const servers = format === "flat" ? parsed.mcpServers : parsed.mcpServers;
|
|
3858
|
+
if (!servers || !(serverName in servers)) {
|
|
3859
|
+
return "missing";
|
|
3860
|
+
}
|
|
3861
|
+
const existing = servers[serverName];
|
|
3862
|
+
const keysToCompare = ["command", "args", "url", "transport"];
|
|
3863
|
+
for (const key of keysToCompare) {
|
|
3864
|
+
const a = serverConfig[key];
|
|
3865
|
+
const b = existing[key];
|
|
3866
|
+
if (JSON.stringify(a) !== JSON.stringify(b)) {
|
|
3867
|
+
return "different";
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
return "same";
|
|
3871
|
+
}
|
|
3872
|
+
async function installMcpServer(server, mcpConfigPath, format, secretValues) {
|
|
3873
|
+
let parsed = {};
|
|
3874
|
+
try {
|
|
3875
|
+
const raw = await readFile8(mcpConfigPath, "utf-8");
|
|
3876
|
+
parsed = JSON.parse(raw);
|
|
3877
|
+
} catch {
|
|
3878
|
+
}
|
|
3879
|
+
if (!parsed.mcpServers || typeof parsed.mcpServers !== "object") {
|
|
3880
|
+
parsed.mcpServers = {};
|
|
3881
|
+
}
|
|
3882
|
+
const entry = {};
|
|
3883
|
+
if (server.config.command) entry.command = server.config.command;
|
|
3884
|
+
if (server.config.args) entry.args = server.config.args;
|
|
3885
|
+
if (server.config.url) entry.url = server.config.url;
|
|
3886
|
+
if (server.config.transport && server.config.transport !== "stdio") {
|
|
3887
|
+
entry.transport = server.config.transport;
|
|
3888
|
+
}
|
|
3889
|
+
if (server.config.env) {
|
|
3890
|
+
const env = { ...server.config.env };
|
|
3891
|
+
if (secretValues) {
|
|
3892
|
+
for (const [varName, value] of Object.entries(secretValues)) {
|
|
3893
|
+
if (varName in env && value !== null) {
|
|
3894
|
+
env[varName] = value;
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
entry.env = env;
|
|
3899
|
+
}
|
|
3900
|
+
;
|
|
3901
|
+
parsed.mcpServers[server.name] = entry;
|
|
3902
|
+
await mkdir2(dirname2(mcpConfigPath), { recursive: true });
|
|
3903
|
+
await writeFile2(mcpConfigPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
3904
|
+
}
|
|
3905
|
+
async function installSkill(skill, dir, targetConfig, scope, force) {
|
|
3906
|
+
const effectiveScope = resolveScope(skill.scope, scope);
|
|
3907
|
+
const basePath = getScopedBasePath(dir, effectiveScope);
|
|
3908
|
+
if (targetConfig.skillsDir) {
|
|
3909
|
+
const skillDir = join9(basePath, targetConfig.skillsDir, skill.dirName);
|
|
3910
|
+
const skillPath = join9(skillDir, "SKILL.md");
|
|
3911
|
+
await mkdir2(skillDir, { recursive: true });
|
|
3912
|
+
await writeFile2(skillPath, skill.content, "utf-8");
|
|
3913
|
+
} else if (targetConfig.rulesDir) {
|
|
3914
|
+
const rulesDir = join9(basePath, targetConfig.rulesDir);
|
|
3915
|
+
const rulePath = join9(rulesDir, `${skill.dirName}.md`);
|
|
3916
|
+
await mkdir2(rulesDir, { recursive: true });
|
|
3917
|
+
await writeFile2(rulePath, skill.content, "utf-8");
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
async function installAgent(agent, dir, targetConfig, scope, force) {
|
|
3921
|
+
if (!targetConfig.agentsDir) return;
|
|
3922
|
+
const effectiveScope = resolveScope(agent.scope, scope);
|
|
3923
|
+
const basePath = getScopedBasePath(dir, effectiveScope);
|
|
3924
|
+
const agentsDir = join9(basePath, targetConfig.agentsDir);
|
|
3925
|
+
const agentPath = join9(agentsDir, agent.fileName);
|
|
3926
|
+
await mkdir2(agentsDir, { recursive: true });
|
|
3927
|
+
await writeFile2(agentPath, agent.content, "utf-8");
|
|
3928
|
+
}
|
|
3929
|
+
async function installContextFile(file, dir, targetPath) {
|
|
3930
|
+
const fullPath = join9(dir, targetPath);
|
|
3931
|
+
await mkdir2(dirname2(fullPath), { recursive: true });
|
|
3932
|
+
await writeFile2(fullPath, file.content, "utf-8");
|
|
3933
|
+
}
|
|
3934
|
+
async function installMemory(memory, dir) {
|
|
3935
|
+
for (const file of memory.files) {
|
|
3936
|
+
const filePath = join9(dir, file.relativePath);
|
|
3937
|
+
await mkdir2(dirname2(filePath), { recursive: true });
|
|
3938
|
+
await writeFile2(filePath, file.content, "utf-8");
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
function resolveScope(originalScope, overrideScope) {
|
|
3942
|
+
if (overrideScope) return overrideScope;
|
|
3943
|
+
return originalScope;
|
|
3944
|
+
}
|
|
3945
|
+
function mapContextFilePath(sourcePath, target) {
|
|
3946
|
+
if (sourcePath in PRIMARY_CONTEXT_MAP) {
|
|
3947
|
+
return TARGET_CONFIGS[target].contextFilePath;
|
|
3948
|
+
}
|
|
3949
|
+
return null;
|
|
3950
|
+
}
|
|
3951
|
+
function getScopedBasePath(dir, scope) {
|
|
3952
|
+
return scope === "project" ? dir : homedir6();
|
|
3953
|
+
}
|
|
3954
|
+
async function fileExists(path) {
|
|
3955
|
+
try {
|
|
3956
|
+
await access2(path);
|
|
3957
|
+
return true;
|
|
3958
|
+
} catch {
|
|
3959
|
+
return false;
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
|
|
3033
3963
|
export {
|
|
3034
3964
|
scanMcpConfigs,
|
|
3035
3965
|
introspectServers,
|
|
3036
3966
|
runAllScanners,
|
|
3037
|
-
generateHtml
|
|
3967
|
+
generateHtml,
|
|
3968
|
+
ACI_DIR,
|
|
3969
|
+
ACI_BUNDLE,
|
|
3970
|
+
isSensitiveVar,
|
|
3971
|
+
detectEnvVars,
|
|
3972
|
+
exportEcosystem,
|
|
3973
|
+
detectTargetTools,
|
|
3974
|
+
loadBundle,
|
|
3975
|
+
planImport,
|
|
3976
|
+
executeImport
|
|
3038
3977
|
};
|