@corbat-tech/coco 2.24.1 → 2.25.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 +92 -8
- package/dist/cli/index.js +1056 -188
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.js +63 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -81,6 +81,33 @@ var init_version = __esm({
|
|
|
81
81
|
VERSION = findPackageJson().version;
|
|
82
82
|
}
|
|
83
83
|
});
|
|
84
|
+
|
|
85
|
+
// src/config/schema.ts
|
|
86
|
+
var schema_exports = {};
|
|
87
|
+
__export(schema_exports, {
|
|
88
|
+
CocoConfigSchema: () => CocoConfigSchema,
|
|
89
|
+
GitHubConfigSchema: () => GitHubConfigSchema,
|
|
90
|
+
IntegrationsConfigSchema: () => IntegrationsConfigSchema,
|
|
91
|
+
MCPConfigSchema: () => MCPConfigSchema,
|
|
92
|
+
MCPServerConfigEntrySchema: () => MCPServerConfigEntrySchema,
|
|
93
|
+
PersistenceConfigSchema: () => PersistenceConfigSchema,
|
|
94
|
+
ProjectConfigSchema: () => ProjectConfigSchema,
|
|
95
|
+
ProviderConfigSchema: () => ProviderConfigSchema,
|
|
96
|
+
QualityConfigSchema: () => QualityConfigSchema,
|
|
97
|
+
ShipConfigSchema: () => ShipConfigSchema,
|
|
98
|
+
SkillsConfigSchema: () => SkillsConfigSchema,
|
|
99
|
+
StackConfigSchema: () => StackConfigSchema,
|
|
100
|
+
ToolsConfigSchema: () => ToolsConfigSchema,
|
|
101
|
+
createDefaultConfigObject: () => createDefaultConfigObject,
|
|
102
|
+
validateConfig: () => validateConfig
|
|
103
|
+
});
|
|
104
|
+
function validateConfig(config) {
|
|
105
|
+
const result = CocoConfigSchema.safeParse(config);
|
|
106
|
+
if (result.success) {
|
|
107
|
+
return { success: true, data: result.data };
|
|
108
|
+
}
|
|
109
|
+
return { success: false, error: result.error };
|
|
110
|
+
}
|
|
84
111
|
function createDefaultConfigObject(projectName, language = "typescript") {
|
|
85
112
|
return {
|
|
86
113
|
project: {
|
|
@@ -200,7 +227,15 @@ var init_schema = __esm({
|
|
|
200
227
|
enabled: z.boolean().default(true),
|
|
201
228
|
configFile: z.string().optional(),
|
|
202
229
|
// Path to external MCP config file
|
|
203
|
-
servers: z.array(MCPServerConfigEntrySchema).default([])
|
|
230
|
+
servers: z.array(MCPServerConfigEntrySchema).default([]),
|
|
231
|
+
/** Default timeout for MCP requests in milliseconds */
|
|
232
|
+
defaultTimeout: z.number().min(1e3).default(6e4).optional(),
|
|
233
|
+
/** Auto-discover MCP servers from well-known locations */
|
|
234
|
+
autoDiscover: z.boolean().default(true).optional(),
|
|
235
|
+
/** Log level for MCP operations */
|
|
236
|
+
logLevel: z.enum(["debug", "info", "warn", "error"]).default("info").optional(),
|
|
237
|
+
/** Path to custom servers directory */
|
|
238
|
+
customServersPath: z.string().optional()
|
|
204
239
|
});
|
|
205
240
|
ToolsConfigSchema = z.object({
|
|
206
241
|
webSearch: z.object({
|
|
@@ -243,8 +278,12 @@ var init_schema = __esm({
|
|
|
243
278
|
enabled: z.boolean().default(true),
|
|
244
279
|
/** Override global skills directory */
|
|
245
280
|
globalDir: z.string().optional(),
|
|
281
|
+
/** Override global skills directories (preferred over globalDir) */
|
|
282
|
+
globalDirs: z.array(z.string()).optional(),
|
|
246
283
|
/** Override project skills directory */
|
|
247
284
|
projectDir: z.string().optional(),
|
|
285
|
+
/** Override project skills directories (preferred over projectDir) */
|
|
286
|
+
projectDirs: z.array(z.string()).optional(),
|
|
248
287
|
/** Auto-activate skills based on user messages */
|
|
249
288
|
autoActivate: z.boolean().default(true),
|
|
250
289
|
/** Maximum concurrent active markdown skills */
|
|
@@ -445,7 +484,7 @@ var init_errors = __esm({
|
|
|
445
484
|
};
|
|
446
485
|
}
|
|
447
486
|
});
|
|
448
|
-
var COCO_HOME, CONFIG_PATHS;
|
|
487
|
+
var COCO_HOME, CONFIG_PATHS, LEGACY_PATHS;
|
|
449
488
|
var init_paths = __esm({
|
|
450
489
|
"src/config/paths.ts"() {
|
|
451
490
|
COCO_HOME = join(homedir(), ".coco");
|
|
@@ -475,12 +514,16 @@ var init_paths = __esm({
|
|
|
475
514
|
/** Search index directory: ~/.coco/search-index/ */
|
|
476
515
|
searchIndex: join(COCO_HOME, "search-index"),
|
|
477
516
|
/** Global skills directory: ~/.coco/skills/ */
|
|
478
|
-
skills: join(COCO_HOME, "skills")
|
|
517
|
+
skills: join(COCO_HOME, "skills"),
|
|
518
|
+
/** MCP server registry: ~/.coco/mcp.json */
|
|
519
|
+
mcpRegistry: join(COCO_HOME, "mcp.json")
|
|
479
520
|
};
|
|
480
|
-
|
|
521
|
+
LEGACY_PATHS = {
|
|
481
522
|
/** Old config location */
|
|
482
|
-
oldConfig: join(homedir(), ".config", "corbat-coco")
|
|
483
|
-
|
|
523
|
+
oldConfig: join(homedir(), ".config", "corbat-coco"),
|
|
524
|
+
/** Old MCP config directory (pre-unification) */
|
|
525
|
+
oldMcpDir: join(homedir(), ".config", "coco", "mcp")
|
|
526
|
+
};
|
|
484
527
|
}
|
|
485
528
|
});
|
|
486
529
|
|
|
@@ -658,8 +701,8 @@ async function configExists(configPath, scope = "any") {
|
|
|
658
701
|
}
|
|
659
702
|
return false;
|
|
660
703
|
}
|
|
661
|
-
function getConfigValue(config,
|
|
662
|
-
const keys =
|
|
704
|
+
function getConfigValue(config, path59) {
|
|
705
|
+
const keys = path59.split(".");
|
|
663
706
|
let current = config;
|
|
664
707
|
for (const key of keys) {
|
|
665
708
|
if (current === null || current === void 0 || typeof current !== "object") {
|
|
@@ -4650,7 +4693,9 @@ var init_openai = __esm({
|
|
|
4650
4693
|
break;
|
|
4651
4694
|
case "response.completed":
|
|
4652
4695
|
{
|
|
4696
|
+
const emittedCallIds = /* @__PURE__ */ new Set();
|
|
4653
4697
|
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
4698
|
+
if (toolCall.id) emittedCallIds.add(toolCall.id);
|
|
4654
4699
|
yield {
|
|
4655
4700
|
type: "tool_use_end",
|
|
4656
4701
|
toolCall: {
|
|
@@ -4660,9 +4705,20 @@ var init_openai = __esm({
|
|
|
4660
4705
|
}
|
|
4661
4706
|
};
|
|
4662
4707
|
}
|
|
4663
|
-
const
|
|
4664
|
-
|
|
4665
|
-
|
|
4708
|
+
const outputItems = event.response?.output ?? [];
|
|
4709
|
+
for (const item of outputItems) {
|
|
4710
|
+
if (item.type !== "function_call" || !item.call_id || !item.name) continue;
|
|
4711
|
+
if (emittedCallIds.has(item.call_id)) continue;
|
|
4712
|
+
yield {
|
|
4713
|
+
type: "tool_use_end",
|
|
4714
|
+
toolCall: {
|
|
4715
|
+
id: item.call_id,
|
|
4716
|
+
name: item.name,
|
|
4717
|
+
input: parseToolCallArguments(item.arguments ?? "{}", this.name)
|
|
4718
|
+
}
|
|
4719
|
+
};
|
|
4720
|
+
}
|
|
4721
|
+
const hasToolCalls = outputItems.some((i) => i.type === "function_call");
|
|
4666
4722
|
yield {
|
|
4667
4723
|
type: "done",
|
|
4668
4724
|
stopReason: hasToolCalls ? "tool_use" : "end_turn"
|
|
@@ -5277,6 +5333,8 @@ var init_codex = __esm({
|
|
|
5277
5333
|
const decoder = new TextDecoder();
|
|
5278
5334
|
let buffer = "";
|
|
5279
5335
|
const toolCallAssembler = new ResponsesToolCallAssembler();
|
|
5336
|
+
const emittedToolCallIds = /* @__PURE__ */ new Set();
|
|
5337
|
+
const emittedToolCallSignatures = /* @__PURE__ */ new Set();
|
|
5280
5338
|
let lastActivityTime = Date.now();
|
|
5281
5339
|
const timeoutController = new AbortController();
|
|
5282
5340
|
const timeoutInterval = setInterval(() => {
|
|
@@ -5339,6 +5397,9 @@ var init_codex = __esm({
|
|
|
5339
5397
|
this.name
|
|
5340
5398
|
);
|
|
5341
5399
|
if (toolCall) {
|
|
5400
|
+
if (toolCall.id) emittedToolCallIds.add(toolCall.id);
|
|
5401
|
+
const signature = `${toolCall.name}:${JSON.stringify(toolCall.input ?? {})}`;
|
|
5402
|
+
emittedToolCallSignatures.add(signature);
|
|
5342
5403
|
yield {
|
|
5343
5404
|
type: "tool_use_end",
|
|
5344
5405
|
toolCall: {
|
|
@@ -5352,6 +5413,9 @@ var init_codex = __esm({
|
|
|
5352
5413
|
}
|
|
5353
5414
|
case "response.completed": {
|
|
5354
5415
|
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
5416
|
+
if (toolCall.id) emittedToolCallIds.add(toolCall.id);
|
|
5417
|
+
const signature = `${toolCall.name}:${JSON.stringify(toolCall.input ?? {})}`;
|
|
5418
|
+
emittedToolCallSignatures.add(signature);
|
|
5355
5419
|
yield {
|
|
5356
5420
|
type: "tool_use_end",
|
|
5357
5421
|
toolCall: {
|
|
@@ -5361,8 +5425,26 @@ var init_codex = __esm({
|
|
|
5361
5425
|
}
|
|
5362
5426
|
};
|
|
5363
5427
|
}
|
|
5364
|
-
const
|
|
5365
|
-
const output =
|
|
5428
|
+
const responsePayload = event.response;
|
|
5429
|
+
const output = responsePayload?.output ?? [];
|
|
5430
|
+
for (const item of output) {
|
|
5431
|
+
if (item.type !== "function_call" || !item.call_id || !item.name) continue;
|
|
5432
|
+
const parsedInput = parseToolCallArguments(item.arguments ?? "{}", this.name);
|
|
5433
|
+
const signature = `${item.name}:${JSON.stringify(parsedInput ?? {})}`;
|
|
5434
|
+
if (emittedToolCallIds.has(item.call_id) || emittedToolCallSignatures.has(signature)) {
|
|
5435
|
+
continue;
|
|
5436
|
+
}
|
|
5437
|
+
emittedToolCallIds.add(item.call_id);
|
|
5438
|
+
emittedToolCallSignatures.add(signature);
|
|
5439
|
+
yield {
|
|
5440
|
+
type: "tool_use_end",
|
|
5441
|
+
toolCall: {
|
|
5442
|
+
id: item.call_id,
|
|
5443
|
+
name: item.name,
|
|
5444
|
+
input: parsedInput
|
|
5445
|
+
}
|
|
5446
|
+
};
|
|
5447
|
+
}
|
|
5366
5448
|
const hasToolCalls = output.some((i) => i.type === "function_call");
|
|
5367
5449
|
yield {
|
|
5368
5450
|
type: "done",
|
|
@@ -6953,6 +7035,9 @@ var init_errors2 = __esm({
|
|
|
6953
7035
|
};
|
|
6954
7036
|
}
|
|
6955
7037
|
});
|
|
7038
|
+
function getDefaultRegistryPath() {
|
|
7039
|
+
return CONFIG_PATHS.mcpRegistry;
|
|
7040
|
+
}
|
|
6956
7041
|
function validateServerConfig(config) {
|
|
6957
7042
|
if (!config || typeof config !== "object") {
|
|
6958
7043
|
throw new MCPError(-32602 /* INVALID_PARAMS */, "Server config must be an object");
|
|
@@ -7003,9 +7088,6 @@ function validateServerConfig(config) {
|
|
|
7003
7088
|
}
|
|
7004
7089
|
}
|
|
7005
7090
|
}
|
|
7006
|
-
function getDefaultRegistryPath() {
|
|
7007
|
-
return join(DEFAULT_MCP_CONFIG_DIR, DEFAULT_REGISTRY_FILE);
|
|
7008
|
-
}
|
|
7009
7091
|
function parseRegistry(json2) {
|
|
7010
7092
|
try {
|
|
7011
7093
|
const parsed = JSON.parse(json2);
|
|
@@ -7020,13 +7102,82 @@ function parseRegistry(json2) {
|
|
|
7020
7102
|
function serializeRegistry(servers) {
|
|
7021
7103
|
return JSON.stringify({ servers, version: "1.0" }, null, 2);
|
|
7022
7104
|
}
|
|
7023
|
-
|
|
7105
|
+
async function migrateMCPData(opts) {
|
|
7106
|
+
const oldDir = LEGACY_PATHS.oldMcpDir;
|
|
7107
|
+
const newRegistry = CONFIG_PATHS.mcpRegistry;
|
|
7108
|
+
const newConfig = CONFIG_PATHS.config;
|
|
7109
|
+
try {
|
|
7110
|
+
await migrateRegistry(oldDir, newRegistry);
|
|
7111
|
+
await migrateGlobalConfig(oldDir, newConfig);
|
|
7112
|
+
} catch (error) {
|
|
7113
|
+
getLogger().warn(
|
|
7114
|
+
`[MCP] Migration failed unexpectedly: ${error instanceof Error ? error.message : String(error)}`
|
|
7115
|
+
);
|
|
7116
|
+
}
|
|
7117
|
+
}
|
|
7118
|
+
async function migrateRegistry(oldDir, newRegistry) {
|
|
7119
|
+
const oldFile = join(oldDir, "registry.json");
|
|
7120
|
+
if (await fileExists(newRegistry)) return;
|
|
7121
|
+
if (!await fileExists(oldFile)) return;
|
|
7122
|
+
try {
|
|
7123
|
+
const content = await readFile(oldFile, "utf-8");
|
|
7124
|
+
const servers = parseRegistry(content);
|
|
7125
|
+
await mkdir(dirname(newRegistry), { recursive: true });
|
|
7126
|
+
await writeFile(newRegistry, serializeRegistry(servers), "utf-8");
|
|
7127
|
+
getLogger().info(
|
|
7128
|
+
`[MCP] Migrated registry from ${oldFile} to ${newRegistry}. The old file can be safely deleted.`
|
|
7129
|
+
);
|
|
7130
|
+
} catch (error) {
|
|
7131
|
+
getLogger().warn(
|
|
7132
|
+
`[MCP] Could not migrate registry: ${error instanceof Error ? error.message : String(error)}`
|
|
7133
|
+
);
|
|
7134
|
+
}
|
|
7135
|
+
}
|
|
7136
|
+
async function migrateGlobalConfig(oldDir, newConfigPath) {
|
|
7137
|
+
const oldFile = join(oldDir, "config.json");
|
|
7138
|
+
if (!await fileExists(oldFile)) return;
|
|
7139
|
+
try {
|
|
7140
|
+
const oldContent = await readFile(oldFile, "utf-8");
|
|
7141
|
+
const oldMcpConfig = JSON.parse(oldContent);
|
|
7142
|
+
let cocoConfig = {};
|
|
7143
|
+
if (await fileExists(newConfigPath)) {
|
|
7144
|
+
const existing = await readFile(newConfigPath, "utf-8");
|
|
7145
|
+
cocoConfig = JSON.parse(existing);
|
|
7146
|
+
}
|
|
7147
|
+
const existingMcp = cocoConfig.mcp ?? {};
|
|
7148
|
+
const fieldsToMigrate = ["defaultTimeout", "autoDiscover", "logLevel", "customServersPath"];
|
|
7149
|
+
let didMerge = false;
|
|
7150
|
+
for (const field of fieldsToMigrate) {
|
|
7151
|
+
if (oldMcpConfig[field] !== void 0 && existingMcp[field] === void 0) {
|
|
7152
|
+
existingMcp[field] = oldMcpConfig[field];
|
|
7153
|
+
didMerge = true;
|
|
7154
|
+
}
|
|
7155
|
+
}
|
|
7156
|
+
if (!didMerge) return;
|
|
7157
|
+
cocoConfig.mcp = existingMcp;
|
|
7158
|
+
await mkdir(dirname(newConfigPath), { recursive: true });
|
|
7159
|
+
await writeFile(newConfigPath, JSON.stringify(cocoConfig, null, 2), "utf-8");
|
|
7160
|
+
getLogger().info(`[MCP] Migrated global MCP settings from ${oldFile} into ${newConfigPath}.`);
|
|
7161
|
+
} catch (error) {
|
|
7162
|
+
getLogger().warn(
|
|
7163
|
+
`[MCP] Could not migrate global MCP config: ${error instanceof Error ? error.message : String(error)}`
|
|
7164
|
+
);
|
|
7165
|
+
}
|
|
7166
|
+
}
|
|
7167
|
+
async function fileExists(path59) {
|
|
7168
|
+
try {
|
|
7169
|
+
await access(path59);
|
|
7170
|
+
return true;
|
|
7171
|
+
} catch {
|
|
7172
|
+
return false;
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7024
7175
|
var init_config = __esm({
|
|
7025
7176
|
"src/mcp/config.ts"() {
|
|
7177
|
+
init_paths();
|
|
7026
7178
|
init_types();
|
|
7027
7179
|
init_errors2();
|
|
7028
|
-
|
|
7029
|
-
DEFAULT_REGISTRY_FILE = "registry.json";
|
|
7180
|
+
init_logger();
|
|
7030
7181
|
}
|
|
7031
7182
|
});
|
|
7032
7183
|
|
|
@@ -7124,6 +7275,9 @@ var init_registry = __esm({
|
|
|
7124
7275
|
* Load registry from disk
|
|
7125
7276
|
*/
|
|
7126
7277
|
async load() {
|
|
7278
|
+
if (this.registryPath === getDefaultRegistryPath()) {
|
|
7279
|
+
await migrateMCPData();
|
|
7280
|
+
}
|
|
7127
7281
|
try {
|
|
7128
7282
|
await access(this.registryPath);
|
|
7129
7283
|
const content = await readFile(this.registryPath, "utf-8");
|
|
@@ -7143,8 +7297,8 @@ var init_registry = __esm({
|
|
|
7143
7297
|
/**
|
|
7144
7298
|
* Ensure directory exists
|
|
7145
7299
|
*/
|
|
7146
|
-
async ensureDir(
|
|
7147
|
-
await mkdir(dirname(
|
|
7300
|
+
async ensureDir(path59) {
|
|
7301
|
+
await mkdir(dirname(path59), { recursive: true });
|
|
7148
7302
|
}
|
|
7149
7303
|
};
|
|
7150
7304
|
}
|
|
@@ -7378,8 +7532,8 @@ async function loadSkillFromDirectory(skillDir, scope) {
|
|
|
7378
7532
|
if (await isMarkdownSkill(skillDir)) {
|
|
7379
7533
|
return loadMarkdownMetadata(skillDir, scope);
|
|
7380
7534
|
}
|
|
7381
|
-
const hasTs = await
|
|
7382
|
-
const hasJs = await
|
|
7535
|
+
const hasTs = await fileExists2(path37__default.join(skillDir, "index.ts"));
|
|
7536
|
+
const hasJs = await fileExists2(path37__default.join(skillDir, "index.js"));
|
|
7383
7537
|
if (hasTs || hasJs) {
|
|
7384
7538
|
return null;
|
|
7385
7539
|
}
|
|
@@ -7397,7 +7551,7 @@ async function loadFullSkill(metadata) {
|
|
|
7397
7551
|
);
|
|
7398
7552
|
return null;
|
|
7399
7553
|
}
|
|
7400
|
-
async function
|
|
7554
|
+
async function fileExists2(filePath) {
|
|
7401
7555
|
try {
|
|
7402
7556
|
await fs34__default.access(filePath);
|
|
7403
7557
|
return true;
|
|
@@ -7412,19 +7566,48 @@ var init_loader2 = __esm({
|
|
|
7412
7566
|
init_typescript_loader();
|
|
7413
7567
|
}
|
|
7414
7568
|
});
|
|
7569
|
+
function parseDiscoveryOptions(options) {
|
|
7570
|
+
return typeof options === "string" ? { globalDir: options } : options ?? {};
|
|
7571
|
+
}
|
|
7572
|
+
function normalizeDirectories(dirs, relativeBaseDir) {
|
|
7573
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7574
|
+
const normalized = [];
|
|
7575
|
+
const home = homedir();
|
|
7576
|
+
for (const dir of dirs) {
|
|
7577
|
+
const trimmed = dir.trim();
|
|
7578
|
+
if (!trimmed) continue;
|
|
7579
|
+
const expanded = trimmed === "~" ? home : trimmed.startsWith("~/") ? path37__default.join(home, trimmed.slice(2)) : trimmed;
|
|
7580
|
+
const resolved = path37__default.isAbsolute(expanded) ? path37__default.resolve(expanded) : path37__default.resolve(relativeBaseDir ?? process.cwd(), expanded);
|
|
7581
|
+
if (seen.has(resolved)) continue;
|
|
7582
|
+
seen.add(resolved);
|
|
7583
|
+
normalized.push(resolved);
|
|
7584
|
+
}
|
|
7585
|
+
return normalized;
|
|
7586
|
+
}
|
|
7587
|
+
function resolveDiscoveryDirs(projectPath, options) {
|
|
7588
|
+
const opts = parseDiscoveryOptions(options);
|
|
7589
|
+
const globalDirs = normalizeDirectories(
|
|
7590
|
+
opts.globalDirs && opts.globalDirs.length > 0 ? opts.globalDirs : opts.globalDir ? [opts.globalDir] : GLOBAL_SKILLS_DIRS
|
|
7591
|
+
);
|
|
7592
|
+
const projectDirs = normalizeDirectories(
|
|
7593
|
+
opts.projectDirs && opts.projectDirs.length > 0 ? opts.projectDirs : opts.projectDir ? [opts.projectDir] : PROJECT_SKILLS_DIRNAMES.map((d) => path37__default.join(projectPath, d)),
|
|
7594
|
+
projectPath
|
|
7595
|
+
);
|
|
7596
|
+
return { globalDirs, projectDirs };
|
|
7597
|
+
}
|
|
7415
7598
|
async function discoverAllSkills(projectPath, builtinSkills = [], options) {
|
|
7416
|
-
const opts = typeof options === "string" ? { globalDir: options } : options ?? {};
|
|
7417
7599
|
const allSkills = /* @__PURE__ */ new Map();
|
|
7600
|
+
const { globalDirs, projectDirs } = resolveDiscoveryDirs(projectPath, options);
|
|
7418
7601
|
for (const skill of builtinSkills) {
|
|
7419
7602
|
const meta = nativeSkillToMetadata(skill, "builtin");
|
|
7420
7603
|
allSkills.set(meta.id, meta);
|
|
7421
7604
|
}
|
|
7422
|
-
const
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7605
|
+
for (const dir of globalDirs) {
|
|
7606
|
+
const globalSkills = await scanSkillsDirectory(dir, "global");
|
|
7607
|
+
for (const meta of globalSkills) {
|
|
7608
|
+
applyWithPriority(allSkills, meta);
|
|
7609
|
+
}
|
|
7426
7610
|
}
|
|
7427
|
-
const projectDirs = opts.projectDir ? [opts.projectDir] : PROJECT_SKILLS_DIRNAMES.map((d) => path37__default.join(projectPath, d));
|
|
7428
7611
|
for (const dir of projectDirs) {
|
|
7429
7612
|
const projectSkills = await scanSkillsDirectory(dir, "project");
|
|
7430
7613
|
for (const meta of projectSkills) {
|
|
@@ -7490,11 +7673,11 @@ async function scanNestedSkills(dir, scope, depth) {
|
|
|
7490
7673
|
}
|
|
7491
7674
|
function applyWithPriority(map, meta) {
|
|
7492
7675
|
const existing = map.get(meta.id);
|
|
7493
|
-
if (!existing || SCOPE_PRIORITY[meta.scope]
|
|
7676
|
+
if (!existing || SCOPE_PRIORITY[meta.scope] >= SCOPE_PRIORITY[existing.scope]) {
|
|
7494
7677
|
map.set(meta.id, meta);
|
|
7495
7678
|
}
|
|
7496
7679
|
}
|
|
7497
|
-
var
|
|
7680
|
+
var GLOBAL_SKILLS_DIRS, PROJECT_SKILLS_DIRNAMES, MAX_NESTING_DEPTH;
|
|
7498
7681
|
var init_discovery = __esm({
|
|
7499
7682
|
"src/skills/discovery.ts"() {
|
|
7500
7683
|
init_types2();
|
|
@@ -7502,14 +7685,31 @@ var init_discovery = __esm({
|
|
|
7502
7685
|
init_typescript_loader();
|
|
7503
7686
|
init_paths();
|
|
7504
7687
|
init_logger();
|
|
7505
|
-
|
|
7688
|
+
GLOBAL_SKILLS_DIRS = [
|
|
7689
|
+
path37__default.join(homedir(), ".codex", "skills"),
|
|
7690
|
+
// Codex CLI legacy compat
|
|
7691
|
+
path37__default.join(homedir(), ".gemini", "skills"),
|
|
7692
|
+
// Gemini CLI compat
|
|
7693
|
+
path37__default.join(homedir(), ".opencode", "skills"),
|
|
7694
|
+
// OpenCode compat
|
|
7695
|
+
path37__default.join(homedir(), ".claude", "skills"),
|
|
7696
|
+
// Claude Code compat
|
|
7697
|
+
path37__default.join(homedir(), ".agents", "skills"),
|
|
7698
|
+
// shared cross-agent standard
|
|
7699
|
+
path37__default.join(COCO_HOME, "skills")
|
|
7700
|
+
// Coco native global directory (authoritative for Coco)
|
|
7701
|
+
];
|
|
7506
7702
|
PROJECT_SKILLS_DIRNAMES = [
|
|
7507
7703
|
".claude/skills",
|
|
7508
|
-
// Claude compat — read for migration/interop
|
|
7509
|
-
".
|
|
7510
|
-
//
|
|
7511
|
-
".
|
|
7512
|
-
//
|
|
7704
|
+
// Claude Code compat — read for migration/interop
|
|
7705
|
+
".codex/skills",
|
|
7706
|
+
// Codex CLI (OpenAI) compat
|
|
7707
|
+
".gemini/skills",
|
|
7708
|
+
// Gemini CLI (Google) compat
|
|
7709
|
+
".opencode/skills",
|
|
7710
|
+
// OpenCode compat
|
|
7711
|
+
".agents/skills"
|
|
7712
|
+
// Native — cross-agent standard, authoritative (highest priority)
|
|
7513
7713
|
];
|
|
7514
7714
|
MAX_NESTING_DEPTH = 1;
|
|
7515
7715
|
}
|
|
@@ -7811,7 +8011,9 @@ var init_registry2 = __esm({
|
|
|
7811
8011
|
}
|
|
7812
8012
|
const discoveryOptions = {
|
|
7813
8013
|
globalDir: globalDir ?? this._config.globalDir,
|
|
7814
|
-
|
|
8014
|
+
globalDirs: this._config.globalDirs,
|
|
8015
|
+
projectDir: this._config.projectDir,
|
|
8016
|
+
projectDirs: this._config.projectDirs
|
|
7815
8017
|
};
|
|
7816
8018
|
const discovered = await discoverAllSkills(projectPath, builtinSkills, discoveryOptions);
|
|
7817
8019
|
const disabledSet = new Set(this._config.disabled ?? []);
|
|
@@ -8032,6 +8234,7 @@ __export(skills_exports, {
|
|
|
8032
8234
|
nativeSkillToLoaded: () => nativeSkillToLoaded,
|
|
8033
8235
|
nativeSkillToMetadata: () => nativeSkillToMetadata,
|
|
8034
8236
|
resolveCategory: () => resolveCategory,
|
|
8237
|
+
resolveDiscoveryDirs: () => resolveDiscoveryDirs,
|
|
8035
8238
|
scanSkillsDirectory: () => scanSkillsDirectory,
|
|
8036
8239
|
stem: () => stem,
|
|
8037
8240
|
tokenize: () => tokenize
|
|
@@ -8732,9 +8935,9 @@ function createEmptyMemoryContext() {
|
|
|
8732
8935
|
errors: []
|
|
8733
8936
|
};
|
|
8734
8937
|
}
|
|
8735
|
-
function createMissingMemoryFile(
|
|
8938
|
+
function createMissingMemoryFile(path59, level) {
|
|
8736
8939
|
return {
|
|
8737
|
-
path:
|
|
8940
|
+
path: path59,
|
|
8738
8941
|
level,
|
|
8739
8942
|
content: "",
|
|
8740
8943
|
sections: [],
|
|
@@ -9339,7 +9542,8 @@ async function createDefaultReplConfig() {
|
|
|
9339
9542
|
agent: {
|
|
9340
9543
|
systemPrompt: COCO_SYSTEM_PROMPT,
|
|
9341
9544
|
maxToolIterations: 25,
|
|
9342
|
-
confirmDestructive: true
|
|
9545
|
+
confirmDestructive: true,
|
|
9546
|
+
enableAutoSwitchProvider: false
|
|
9343
9547
|
}
|
|
9344
9548
|
};
|
|
9345
9549
|
}
|
|
@@ -10179,8 +10383,8 @@ __export(trust_store_exports, {
|
|
|
10179
10383
|
saveTrustStore: () => saveTrustStore,
|
|
10180
10384
|
updateLastAccessed: () => updateLastAccessed
|
|
10181
10385
|
});
|
|
10182
|
-
async function ensureDir(
|
|
10183
|
-
await mkdir(dirname(
|
|
10386
|
+
async function ensureDir(path59) {
|
|
10387
|
+
await mkdir(dirname(path59), { recursive: true });
|
|
10184
10388
|
}
|
|
10185
10389
|
async function loadTrustStore(storePath = TRUST_STORE_PATH) {
|
|
10186
10390
|
try {
|
|
@@ -10265,8 +10469,8 @@ function canPerformOperation(store, projectPath, operation) {
|
|
|
10265
10469
|
};
|
|
10266
10470
|
return permissions[level]?.includes(operation) ?? false;
|
|
10267
10471
|
}
|
|
10268
|
-
function normalizePath(
|
|
10269
|
-
return join(
|
|
10472
|
+
function normalizePath(path59) {
|
|
10473
|
+
return join(path59);
|
|
10270
10474
|
}
|
|
10271
10475
|
function createTrustStore(storePath = TRUST_STORE_PATH) {
|
|
10272
10476
|
let store = null;
|
|
@@ -10622,12 +10826,12 @@ function humanizeError(message, toolName) {
|
|
|
10622
10826
|
return msg;
|
|
10623
10827
|
}
|
|
10624
10828
|
if (/ENOENT/i.test(msg)) {
|
|
10625
|
-
const
|
|
10626
|
-
return
|
|
10829
|
+
const path59 = extractQuotedPath(msg);
|
|
10830
|
+
return path59 ? `File or directory not found: ${path59}` : "File or directory not found";
|
|
10627
10831
|
}
|
|
10628
10832
|
if (/EACCES/i.test(msg)) {
|
|
10629
|
-
const
|
|
10630
|
-
return
|
|
10833
|
+
const path59 = extractQuotedPath(msg);
|
|
10834
|
+
return path59 ? `Permission denied: ${path59}` : "Permission denied \u2014 check file permissions";
|
|
10631
10835
|
}
|
|
10632
10836
|
if (/EISDIR/i.test(msg)) {
|
|
10633
10837
|
return "Expected a file but found a directory at the specified path";
|
|
@@ -10899,7 +11103,7 @@ var MAX_CONSECUTIVE_ERRORS, _safetyNetInstalled;
|
|
|
10899
11103
|
var init_error_resilience = __esm({
|
|
10900
11104
|
"src/cli/repl/error-resilience.ts"() {
|
|
10901
11105
|
init_errors();
|
|
10902
|
-
MAX_CONSECUTIVE_ERRORS =
|
|
11106
|
+
MAX_CONSECUTIVE_ERRORS = 5;
|
|
10903
11107
|
_safetyNetInstalled = false;
|
|
10904
11108
|
}
|
|
10905
11109
|
});
|
|
@@ -11100,7 +11304,7 @@ Suggestion: ${error.suggestion}`;
|
|
|
11100
11304
|
};
|
|
11101
11305
|
}
|
|
11102
11306
|
});
|
|
11103
|
-
async function
|
|
11307
|
+
async function fileExists3(filePath) {
|
|
11104
11308
|
try {
|
|
11105
11309
|
await fs34__default.access(filePath);
|
|
11106
11310
|
return true;
|
|
@@ -11561,9 +11765,9 @@ var init_diff_renderer = __esm({
|
|
|
11561
11765
|
getTerminalWidth = () => process.stdout.columns || 80;
|
|
11562
11766
|
}
|
|
11563
11767
|
});
|
|
11564
|
-
async function
|
|
11768
|
+
async function fileExists4(path59) {
|
|
11565
11769
|
try {
|
|
11566
|
-
await access(
|
|
11770
|
+
await access(path59);
|
|
11567
11771
|
return true;
|
|
11568
11772
|
} catch {
|
|
11569
11773
|
return false;
|
|
@@ -11578,7 +11782,7 @@ async function dirHasFiles(dir) {
|
|
|
11578
11782
|
}
|
|
11579
11783
|
}
|
|
11580
11784
|
async function detectMaturity(cwd) {
|
|
11581
|
-
const hasPackageJson = await
|
|
11785
|
+
const hasPackageJson = await fileExists4(join(cwd, "package.json"));
|
|
11582
11786
|
if (!hasPackageJson) {
|
|
11583
11787
|
const otherManifests = [
|
|
11584
11788
|
"go.mod",
|
|
@@ -11591,7 +11795,7 @@ async function detectMaturity(cwd) {
|
|
|
11591
11795
|
];
|
|
11592
11796
|
let hasAnyManifest = false;
|
|
11593
11797
|
for (const m of otherManifests) {
|
|
11594
|
-
if (await
|
|
11798
|
+
if (await fileExists4(join(cwd, m))) {
|
|
11595
11799
|
hasAnyManifest = true;
|
|
11596
11800
|
break;
|
|
11597
11801
|
}
|
|
@@ -11632,7 +11836,7 @@ async function detectMaturity(cwd) {
|
|
|
11632
11836
|
cwd,
|
|
11633
11837
|
ignore: ["node_modules/**", "dist/**", "build/**"]
|
|
11634
11838
|
});
|
|
11635
|
-
const hasCI = await
|
|
11839
|
+
const hasCI = await fileExists4(join(cwd, ".github/workflows")) && await dirHasFiles(join(cwd, ".github/workflows"));
|
|
11636
11840
|
const lintConfigs = [
|
|
11637
11841
|
".eslintrc.js",
|
|
11638
11842
|
".eslintrc.json",
|
|
@@ -11645,7 +11849,7 @@ async function detectMaturity(cwd) {
|
|
|
11645
11849
|
];
|
|
11646
11850
|
let hasLintConfig = false;
|
|
11647
11851
|
for (const config of lintConfigs) {
|
|
11648
|
-
if (await
|
|
11852
|
+
if (await fileExists4(join(cwd, config))) {
|
|
11649
11853
|
hasLintConfig = true;
|
|
11650
11854
|
break;
|
|
11651
11855
|
}
|
|
@@ -15943,16 +16147,16 @@ var init_evaluator = __esm({
|
|
|
15943
16147
|
* Find source files in project, adapting to the detected language stack.
|
|
15944
16148
|
*/
|
|
15945
16149
|
async findSourceFiles() {
|
|
15946
|
-
const { access:
|
|
16150
|
+
const { access: access17 } = await import('fs/promises');
|
|
15947
16151
|
const { join: join26 } = await import('path');
|
|
15948
16152
|
let isJava = false;
|
|
15949
16153
|
try {
|
|
15950
|
-
await
|
|
16154
|
+
await access17(join26(this.projectPath, "pom.xml"));
|
|
15951
16155
|
isJava = true;
|
|
15952
16156
|
} catch {
|
|
15953
16157
|
for (const f of ["build.gradle", "build.gradle.kts"]) {
|
|
15954
16158
|
try {
|
|
15955
|
-
await
|
|
16159
|
+
await access17(join26(this.projectPath, f));
|
|
15956
16160
|
isJava = true;
|
|
15957
16161
|
break;
|
|
15958
16162
|
} catch {
|
|
@@ -16444,7 +16648,7 @@ async function checkTestCoverage(diff, cwd) {
|
|
|
16444
16648
|
);
|
|
16445
16649
|
if (!hasTestChange) {
|
|
16446
16650
|
const ext = src.path.match(/\.(ts|tsx|js|jsx)$/)?.[0] ?? ".ts";
|
|
16447
|
-
const testExists = await
|
|
16651
|
+
const testExists = await fileExists3(path37__default.join(cwd, `${baseName}.test${ext}`)) || await fileExists3(path37__default.join(cwd, `${baseName}.spec${ext}`));
|
|
16448
16652
|
if (testExists) {
|
|
16449
16653
|
if (src.additions >= TEST_COVERAGE_LARGE_CHANGE_THRESHOLD) {
|
|
16450
16654
|
findings.push({
|
|
@@ -18126,7 +18330,7 @@ var init_github = __esm({
|
|
|
18126
18330
|
async function detectVersionFile(cwd) {
|
|
18127
18331
|
for (const { file, stack, field } of VERSION_FILES) {
|
|
18128
18332
|
const fullPath = path37__default.join(cwd, file);
|
|
18129
|
-
if (await
|
|
18333
|
+
if (await fileExists3(fullPath)) {
|
|
18130
18334
|
const version = await readVersionFromFile(fullPath, stack, field);
|
|
18131
18335
|
if (version) {
|
|
18132
18336
|
return { path: file, stack, currentVersion: version, field };
|
|
@@ -18231,7 +18435,7 @@ var init_version_detector = __esm({
|
|
|
18231
18435
|
async function detectChangelog(cwd) {
|
|
18232
18436
|
for (const name of CHANGELOG_NAMES) {
|
|
18233
18437
|
const fullPath = path37__default.join(cwd, name);
|
|
18234
|
-
if (await
|
|
18438
|
+
if (await fileExists3(fullPath)) {
|
|
18235
18439
|
const content = await readFile(fullPath, "utf-8");
|
|
18236
18440
|
const format = detectFormat(content);
|
|
18237
18441
|
return { path: name, format };
|
|
@@ -18313,11 +18517,11 @@ var init_changelog = __esm({
|
|
|
18313
18517
|
}
|
|
18314
18518
|
});
|
|
18315
18519
|
async function detectStack(cwd) {
|
|
18316
|
-
if (await
|
|
18317
|
-
if (await
|
|
18318
|
-
if (await
|
|
18319
|
-
if (await
|
|
18320
|
-
if (await
|
|
18520
|
+
if (await fileExists3(path37__default.join(cwd, "package.json"))) return "node";
|
|
18521
|
+
if (await fileExists3(path37__default.join(cwd, "Cargo.toml"))) return "rust";
|
|
18522
|
+
if (await fileExists3(path37__default.join(cwd, "pyproject.toml"))) return "python";
|
|
18523
|
+
if (await fileExists3(path37__default.join(cwd, "go.mod"))) return "go";
|
|
18524
|
+
if (await fileExists3(path37__default.join(cwd, "pom.xml"))) return "java";
|
|
18321
18525
|
return "unknown";
|
|
18322
18526
|
}
|
|
18323
18527
|
async function detectPackageManager(cwd, stack) {
|
|
@@ -18325,16 +18529,16 @@ async function detectPackageManager(cwd, stack) {
|
|
|
18325
18529
|
if (stack === "python") return "pip";
|
|
18326
18530
|
if (stack === "go") return "go";
|
|
18327
18531
|
if (stack === "node") {
|
|
18328
|
-
if (await
|
|
18329
|
-
if (await
|
|
18330
|
-
if (await
|
|
18532
|
+
if (await fileExists3(path37__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
18533
|
+
if (await fileExists3(path37__default.join(cwd, "yarn.lock"))) return "yarn";
|
|
18534
|
+
if (await fileExists3(path37__default.join(cwd, "bun.lockb"))) return "bun";
|
|
18331
18535
|
return "npm";
|
|
18332
18536
|
}
|
|
18333
18537
|
return null;
|
|
18334
18538
|
}
|
|
18335
18539
|
async function detectCI(cwd) {
|
|
18336
18540
|
const ghDir = path37__default.join(cwd, ".github", "workflows");
|
|
18337
|
-
if (await
|
|
18541
|
+
if (await fileExists3(ghDir)) {
|
|
18338
18542
|
let workflowFiles = [];
|
|
18339
18543
|
let hasCodeQL = false;
|
|
18340
18544
|
let hasLinting = false;
|
|
@@ -18357,7 +18561,7 @@ async function detectCI(cwd) {
|
|
|
18357
18561
|
}
|
|
18358
18562
|
return { type: "github-actions", workflowFiles, hasCodeQL, hasLinting };
|
|
18359
18563
|
}
|
|
18360
|
-
if (await
|
|
18564
|
+
if (await fileExists3(path37__default.join(cwd, ".gitlab-ci.yml"))) {
|
|
18361
18565
|
return {
|
|
18362
18566
|
type: "gitlab-ci",
|
|
18363
18567
|
workflowFiles: [".gitlab-ci.yml"],
|
|
@@ -18365,7 +18569,7 @@ async function detectCI(cwd) {
|
|
|
18365
18569
|
hasLinting: false
|
|
18366
18570
|
};
|
|
18367
18571
|
}
|
|
18368
|
-
if (await
|
|
18572
|
+
if (await fileExists3(path37__default.join(cwd, ".circleci"))) {
|
|
18369
18573
|
return { type: "circle-ci", workflowFiles: [], hasCodeQL: false, hasLinting: false };
|
|
18370
18574
|
}
|
|
18371
18575
|
return { type: "none", workflowFiles: [], hasCodeQL: false, hasLinting: false };
|
|
@@ -21639,13 +21843,13 @@ __export(stack_detector_exports, {
|
|
|
21639
21843
|
detectProjectStack: () => detectProjectStack
|
|
21640
21844
|
});
|
|
21641
21845
|
async function detectStack2(cwd) {
|
|
21642
|
-
if (await
|
|
21643
|
-
if (await
|
|
21644
|
-
if (await
|
|
21645
|
-
if (await
|
|
21646
|
-
if (await
|
|
21647
|
-
if (await
|
|
21648
|
-
if (await
|
|
21846
|
+
if (await fileExists3(path37__default.join(cwd, "package.json"))) return "node";
|
|
21847
|
+
if (await fileExists3(path37__default.join(cwd, "Cargo.toml"))) return "rust";
|
|
21848
|
+
if (await fileExists3(path37__default.join(cwd, "pyproject.toml"))) return "python";
|
|
21849
|
+
if (await fileExists3(path37__default.join(cwd, "go.mod"))) return "go";
|
|
21850
|
+
if (await fileExists3(path37__default.join(cwd, "pom.xml"))) return "java";
|
|
21851
|
+
if (await fileExists3(path37__default.join(cwd, "build.gradle"))) return "java";
|
|
21852
|
+
if (await fileExists3(path37__default.join(cwd, "build.gradle.kts"))) return "java";
|
|
21649
21853
|
return "unknown";
|
|
21650
21854
|
}
|
|
21651
21855
|
async function detectPackageManager3(cwd, stack) {
|
|
@@ -21653,17 +21857,17 @@ async function detectPackageManager3(cwd, stack) {
|
|
|
21653
21857
|
if (stack === "python") return "pip";
|
|
21654
21858
|
if (stack === "go") return "go";
|
|
21655
21859
|
if (stack === "java") {
|
|
21656
|
-
if (await
|
|
21860
|
+
if (await fileExists3(path37__default.join(cwd, "build.gradle")) || await fileExists3(path37__default.join(cwd, "build.gradle.kts"))) {
|
|
21657
21861
|
return "gradle";
|
|
21658
21862
|
}
|
|
21659
|
-
if (await
|
|
21863
|
+
if (await fileExists3(path37__default.join(cwd, "pom.xml"))) {
|
|
21660
21864
|
return "maven";
|
|
21661
21865
|
}
|
|
21662
21866
|
}
|
|
21663
21867
|
if (stack === "node") {
|
|
21664
|
-
if (await
|
|
21665
|
-
if (await
|
|
21666
|
-
if (await
|
|
21868
|
+
if (await fileExists3(path37__default.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
21869
|
+
if (await fileExists3(path37__default.join(cwd, "yarn.lock"))) return "yarn";
|
|
21870
|
+
if (await fileExists3(path37__default.join(cwd, "bun.lockb"))) return "bun";
|
|
21667
21871
|
return "npm";
|
|
21668
21872
|
}
|
|
21669
21873
|
return null;
|
|
@@ -21701,7 +21905,7 @@ async function parsePackageJson(cwd) {
|
|
|
21701
21905
|
if (allDeps["@playwright/test"]) testingFrameworks.push("playwright");
|
|
21702
21906
|
if (allDeps.cypress) testingFrameworks.push("cypress");
|
|
21703
21907
|
const languages = ["JavaScript"];
|
|
21704
|
-
if (allDeps.typescript || await
|
|
21908
|
+
if (allDeps.typescript || await fileExists3(path37__default.join(cwd, "tsconfig.json"))) {
|
|
21705
21909
|
languages.push("TypeScript");
|
|
21706
21910
|
}
|
|
21707
21911
|
return {
|
|
@@ -21802,7 +22006,7 @@ async function detectProjectStack(cwd) {
|
|
|
21802
22006
|
testingFrameworks = parsed.testingFrameworks;
|
|
21803
22007
|
languages = parsed.languages;
|
|
21804
22008
|
} else if (stack === "java") {
|
|
21805
|
-
const isGradle = await
|
|
22009
|
+
const isGradle = await fileExists3(path37__default.join(cwd, "build.gradle")) || await fileExists3(path37__default.join(cwd, "build.gradle.kts"));
|
|
21806
22010
|
const parsed = isGradle ? { dependencies: {}, frameworks: [], buildTools: ["gradle"], testingFrameworks: ["JUnit"] } : await parsePomXml(cwd);
|
|
21807
22011
|
dependencies = parsed.dependencies;
|
|
21808
22012
|
frameworks = parsed.frameworks;
|
|
@@ -22088,6 +22292,251 @@ var init_tools = __esm({
|
|
|
22088
22292
|
}
|
|
22089
22293
|
});
|
|
22090
22294
|
|
|
22295
|
+
// src/mcp/config-loader.ts
|
|
22296
|
+
var config_loader_exports = {};
|
|
22297
|
+
__export(config_loader_exports, {
|
|
22298
|
+
loadMCPConfigFile: () => loadMCPConfigFile,
|
|
22299
|
+
loadMCPServersFromCOCOConfig: () => loadMCPServersFromCOCOConfig,
|
|
22300
|
+
loadProjectMCPFile: () => loadProjectMCPFile,
|
|
22301
|
+
mergeMCPConfigs: () => mergeMCPConfigs
|
|
22302
|
+
});
|
|
22303
|
+
function expandEnvVar(value) {
|
|
22304
|
+
return value.replace(/\$\{([^}]+)\}/g, (match, name) => process.env[name] ?? match);
|
|
22305
|
+
}
|
|
22306
|
+
function expandEnvObject(env2) {
|
|
22307
|
+
const result = {};
|
|
22308
|
+
for (const [k, v] of Object.entries(env2)) {
|
|
22309
|
+
result[k] = expandEnvVar(v);
|
|
22310
|
+
}
|
|
22311
|
+
return result;
|
|
22312
|
+
}
|
|
22313
|
+
function expandHeaders(headers) {
|
|
22314
|
+
const result = {};
|
|
22315
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
22316
|
+
result[k] = expandEnvVar(v);
|
|
22317
|
+
}
|
|
22318
|
+
return result;
|
|
22319
|
+
}
|
|
22320
|
+
function convertStandardEntry(name, entry) {
|
|
22321
|
+
if (entry.command) {
|
|
22322
|
+
return {
|
|
22323
|
+
name,
|
|
22324
|
+
transport: "stdio",
|
|
22325
|
+
enabled: entry.enabled ?? true,
|
|
22326
|
+
stdio: {
|
|
22327
|
+
command: entry.command,
|
|
22328
|
+
args: entry.args,
|
|
22329
|
+
env: entry.env ? expandEnvObject(entry.env) : void 0
|
|
22330
|
+
}
|
|
22331
|
+
};
|
|
22332
|
+
}
|
|
22333
|
+
if (entry.url) {
|
|
22334
|
+
const headers = entry.headers ? expandHeaders(entry.headers) : void 0;
|
|
22335
|
+
const authHeader = headers?.["Authorization"] ?? headers?.["authorization"];
|
|
22336
|
+
let auth;
|
|
22337
|
+
if (authHeader) {
|
|
22338
|
+
if (authHeader.startsWith("Bearer ")) {
|
|
22339
|
+
auth = { type: "bearer", token: authHeader.slice(7) };
|
|
22340
|
+
} else {
|
|
22341
|
+
auth = { type: "apikey", token: authHeader };
|
|
22342
|
+
}
|
|
22343
|
+
}
|
|
22344
|
+
return {
|
|
22345
|
+
name,
|
|
22346
|
+
transport: "http",
|
|
22347
|
+
enabled: entry.enabled ?? true,
|
|
22348
|
+
http: {
|
|
22349
|
+
url: entry.url,
|
|
22350
|
+
...headers && Object.keys(headers).length > 0 ? { headers } : {},
|
|
22351
|
+
...auth ? { auth } : {}
|
|
22352
|
+
}
|
|
22353
|
+
};
|
|
22354
|
+
}
|
|
22355
|
+
throw new Error(`Server "${name}" must have either "command" (stdio) or "url" (http) defined`);
|
|
22356
|
+
}
|
|
22357
|
+
async function loadMCPConfigFile(configPath) {
|
|
22358
|
+
try {
|
|
22359
|
+
await access(configPath);
|
|
22360
|
+
} catch {
|
|
22361
|
+
throw new MCPError(-32003 /* CONNECTION_ERROR */, `Config file not found: ${configPath}`);
|
|
22362
|
+
}
|
|
22363
|
+
let content;
|
|
22364
|
+
try {
|
|
22365
|
+
content = await readFile(configPath, "utf-8");
|
|
22366
|
+
} catch (error) {
|
|
22367
|
+
throw new MCPError(
|
|
22368
|
+
-32003 /* CONNECTION_ERROR */,
|
|
22369
|
+
`Failed to read config file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
22370
|
+
);
|
|
22371
|
+
}
|
|
22372
|
+
let parsed;
|
|
22373
|
+
try {
|
|
22374
|
+
parsed = JSON.parse(content);
|
|
22375
|
+
} catch {
|
|
22376
|
+
throw new MCPError(-32700 /* PARSE_ERROR */, "Invalid JSON in config file");
|
|
22377
|
+
}
|
|
22378
|
+
const obj = parsed;
|
|
22379
|
+
if (obj.mcpServers && typeof obj.mcpServers === "object" && !Array.isArray(obj.mcpServers)) {
|
|
22380
|
+
return loadStandardFormat(obj, configPath);
|
|
22381
|
+
}
|
|
22382
|
+
if (obj.servers && Array.isArray(obj.servers)) {
|
|
22383
|
+
return loadCocoFormat(obj, configPath);
|
|
22384
|
+
}
|
|
22385
|
+
throw new MCPError(
|
|
22386
|
+
-32602 /* INVALID_PARAMS */,
|
|
22387
|
+
'Config file must have either a "mcpServers" object (standard) or a "servers" array (Coco format)'
|
|
22388
|
+
);
|
|
22389
|
+
}
|
|
22390
|
+
function loadStandardFormat(config, configPath) {
|
|
22391
|
+
const validServers = [];
|
|
22392
|
+
const errors = [];
|
|
22393
|
+
for (const [name, entry] of Object.entries(config.mcpServers)) {
|
|
22394
|
+
if (name.startsWith("_")) continue;
|
|
22395
|
+
try {
|
|
22396
|
+
const converted = convertStandardEntry(name, entry);
|
|
22397
|
+
validateServerConfig(converted);
|
|
22398
|
+
validServers.push(converted);
|
|
22399
|
+
} catch (error) {
|
|
22400
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
22401
|
+
errors.push(`Server '${name}': ${message}`);
|
|
22402
|
+
}
|
|
22403
|
+
}
|
|
22404
|
+
if (errors.length > 0) {
|
|
22405
|
+
getLogger().warn(`[MCP] Some servers in ${configPath} failed to load: ${errors.join("; ")}`);
|
|
22406
|
+
}
|
|
22407
|
+
return validServers;
|
|
22408
|
+
}
|
|
22409
|
+
async function loadProjectMCPFile(projectPath) {
|
|
22410
|
+
const mcpJsonPath = path37__default.join(projectPath, ".mcp.json");
|
|
22411
|
+
try {
|
|
22412
|
+
await access(mcpJsonPath);
|
|
22413
|
+
} catch {
|
|
22414
|
+
return [];
|
|
22415
|
+
}
|
|
22416
|
+
try {
|
|
22417
|
+
return await loadMCPConfigFile(mcpJsonPath);
|
|
22418
|
+
} catch (error) {
|
|
22419
|
+
getLogger().warn(
|
|
22420
|
+
`[MCP] Failed to load .mcp.json: ${error instanceof Error ? error.message : String(error)}`
|
|
22421
|
+
);
|
|
22422
|
+
return [];
|
|
22423
|
+
}
|
|
22424
|
+
}
|
|
22425
|
+
function loadCocoFormat(config, configPath) {
|
|
22426
|
+
const validServers = [];
|
|
22427
|
+
const errors = [];
|
|
22428
|
+
for (const server of config.servers) {
|
|
22429
|
+
try {
|
|
22430
|
+
const converted = convertCocoServerEntry(server);
|
|
22431
|
+
validateServerConfig(converted);
|
|
22432
|
+
validServers.push(converted);
|
|
22433
|
+
} catch (error) {
|
|
22434
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
22435
|
+
errors.push(`Server '${server.name || "unknown"}': ${message}`);
|
|
22436
|
+
}
|
|
22437
|
+
}
|
|
22438
|
+
if (errors.length > 0) {
|
|
22439
|
+
getLogger().warn(`[MCP] Some servers in ${configPath} failed to load: ${errors.join("; ")}`);
|
|
22440
|
+
}
|
|
22441
|
+
return validServers;
|
|
22442
|
+
}
|
|
22443
|
+
function convertCocoServerEntry(server) {
|
|
22444
|
+
const base = {
|
|
22445
|
+
name: server.name,
|
|
22446
|
+
description: server.description,
|
|
22447
|
+
transport: server.transport,
|
|
22448
|
+
enabled: server.enabled ?? true,
|
|
22449
|
+
metadata: server.metadata
|
|
22450
|
+
};
|
|
22451
|
+
if (server.transport === "stdio" && server.stdio) {
|
|
22452
|
+
return {
|
|
22453
|
+
...base,
|
|
22454
|
+
stdio: {
|
|
22455
|
+
command: server.stdio.command,
|
|
22456
|
+
args: server.stdio.args,
|
|
22457
|
+
env: server.stdio.env ? expandEnvObject(server.stdio.env) : void 0,
|
|
22458
|
+
cwd: server.stdio.cwd
|
|
22459
|
+
}
|
|
22460
|
+
};
|
|
22461
|
+
}
|
|
22462
|
+
if (server.transport === "http" && server.http) {
|
|
22463
|
+
return {
|
|
22464
|
+
...base,
|
|
22465
|
+
http: {
|
|
22466
|
+
url: server.http.url,
|
|
22467
|
+
...server.http.headers ? { headers: expandHeaders(server.http.headers) } : {},
|
|
22468
|
+
...server.http.auth ? { auth: server.http.auth } : {},
|
|
22469
|
+
...server.http.timeout !== void 0 ? { timeout: server.http.timeout } : {}
|
|
22470
|
+
}
|
|
22471
|
+
};
|
|
22472
|
+
}
|
|
22473
|
+
throw new Error(`Missing configuration for transport: ${server.transport}`);
|
|
22474
|
+
}
|
|
22475
|
+
function mergeMCPConfigs(base, ...overrides) {
|
|
22476
|
+
const merged = /* @__PURE__ */ new Map();
|
|
22477
|
+
for (const server of base) {
|
|
22478
|
+
merged.set(server.name, server);
|
|
22479
|
+
}
|
|
22480
|
+
for (const override of overrides) {
|
|
22481
|
+
for (const server of override) {
|
|
22482
|
+
const existing = merged.get(server.name);
|
|
22483
|
+
if (existing) {
|
|
22484
|
+
merged.set(server.name, { ...existing, ...server });
|
|
22485
|
+
} else {
|
|
22486
|
+
merged.set(server.name, server);
|
|
22487
|
+
}
|
|
22488
|
+
}
|
|
22489
|
+
}
|
|
22490
|
+
return Array.from(merged.values());
|
|
22491
|
+
}
|
|
22492
|
+
async function loadMCPServersFromCOCOConfig(configPath) {
|
|
22493
|
+
const { loadConfig: loadConfig3 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
|
|
22494
|
+
const { MCPServerConfigEntrySchema: MCPServerConfigEntrySchema2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
22495
|
+
const config = await loadConfig3(configPath);
|
|
22496
|
+
if (!config.mcp?.servers || config.mcp.servers.length === 0) {
|
|
22497
|
+
return [];
|
|
22498
|
+
}
|
|
22499
|
+
const servers = [];
|
|
22500
|
+
for (const entry of config.mcp.servers) {
|
|
22501
|
+
try {
|
|
22502
|
+
const parsed = MCPServerConfigEntrySchema2.parse(entry);
|
|
22503
|
+
const serverConfig = {
|
|
22504
|
+
name: parsed.name,
|
|
22505
|
+
description: parsed.description,
|
|
22506
|
+
transport: parsed.transport,
|
|
22507
|
+
enabled: parsed.enabled,
|
|
22508
|
+
...parsed.transport === "stdio" && parsed.command && {
|
|
22509
|
+
stdio: {
|
|
22510
|
+
command: parsed.command,
|
|
22511
|
+
args: parsed.args,
|
|
22512
|
+
env: parsed.env ? expandEnvObject(parsed.env) : void 0
|
|
22513
|
+
}
|
|
22514
|
+
},
|
|
22515
|
+
...parsed.transport === "http" && parsed.url && {
|
|
22516
|
+
http: {
|
|
22517
|
+
url: parsed.url,
|
|
22518
|
+
auth: parsed.auth
|
|
22519
|
+
}
|
|
22520
|
+
}
|
|
22521
|
+
};
|
|
22522
|
+
validateServerConfig(serverConfig);
|
|
22523
|
+
servers.push(serverConfig);
|
|
22524
|
+
} catch (error) {
|
|
22525
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
22526
|
+
getLogger().warn(`[MCP] Failed to load server '${entry.name}': ${message}`);
|
|
22527
|
+
}
|
|
22528
|
+
}
|
|
22529
|
+
return servers;
|
|
22530
|
+
}
|
|
22531
|
+
var init_config_loader = __esm({
|
|
22532
|
+
"src/mcp/config-loader.ts"() {
|
|
22533
|
+
init_config();
|
|
22534
|
+
init_types();
|
|
22535
|
+
init_errors2();
|
|
22536
|
+
init_logger();
|
|
22537
|
+
}
|
|
22538
|
+
});
|
|
22539
|
+
|
|
22091
22540
|
// src/cli/repl/hooks/types.ts
|
|
22092
22541
|
function isHookEvent(value) {
|
|
22093
22542
|
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
@@ -23415,8 +23864,8 @@ Generated by Corbat-Coco v0.1.0
|
|
|
23415
23864
|
|
|
23416
23865
|
// src/cli/commands/init.ts
|
|
23417
23866
|
function registerInitCommand(program2) {
|
|
23418
|
-
program2.command("init").description("Initialize a new Corbat-Coco project").argument("[path]", "Project directory path", ".").option("-t, --template <template>", "Project template to use").option("-y, --yes", "Skip prompts and use defaults").option("--skip-discovery", "Skip the discovery phase (use existing spec)").action(async (
|
|
23419
|
-
await runInit(
|
|
23867
|
+
program2.command("init").description("Initialize a new Corbat-Coco project").argument("[path]", "Project directory path", ".").option("-t, --template <template>", "Project template to use").option("-y, --yes", "Skip prompts and use defaults").option("--skip-discovery", "Skip the discovery phase (use existing spec)").action(async (path59, options) => {
|
|
23868
|
+
await runInit(path59, options);
|
|
23420
23869
|
});
|
|
23421
23870
|
}
|
|
23422
23871
|
async function runInit(projectPath, options) {
|
|
@@ -23495,18 +23944,18 @@ async function gatherProjectInfo() {
|
|
|
23495
23944
|
language
|
|
23496
23945
|
};
|
|
23497
23946
|
}
|
|
23498
|
-
function getDefaultProjectInfo(
|
|
23499
|
-
const name =
|
|
23947
|
+
function getDefaultProjectInfo(path59) {
|
|
23948
|
+
const name = path59 === "." ? "my-project" : path59.split("/").pop() || "my-project";
|
|
23500
23949
|
return {
|
|
23501
23950
|
name,
|
|
23502
23951
|
description: "",
|
|
23503
23952
|
language: "typescript"
|
|
23504
23953
|
};
|
|
23505
23954
|
}
|
|
23506
|
-
async function checkExistingProject(
|
|
23955
|
+
async function checkExistingProject(path59) {
|
|
23507
23956
|
try {
|
|
23508
23957
|
const fs55 = await import('fs/promises');
|
|
23509
|
-
await fs55.access(`${
|
|
23958
|
+
await fs55.access(`${path59}/.coco`);
|
|
23510
23959
|
return true;
|
|
23511
23960
|
} catch {
|
|
23512
23961
|
return false;
|
|
@@ -27007,20 +27456,20 @@ async function createCliPhaseContext(projectPath, _onUserInput) {
|
|
|
27007
27456
|
},
|
|
27008
27457
|
tools: {
|
|
27009
27458
|
file: {
|
|
27010
|
-
async read(
|
|
27459
|
+
async read(path59) {
|
|
27011
27460
|
const fs55 = await import('fs/promises');
|
|
27012
|
-
return fs55.readFile(
|
|
27461
|
+
return fs55.readFile(path59, "utf-8");
|
|
27013
27462
|
},
|
|
27014
|
-
async write(
|
|
27463
|
+
async write(path59, content) {
|
|
27015
27464
|
const fs55 = await import('fs/promises');
|
|
27016
27465
|
const nodePath = await import('path');
|
|
27017
|
-
await fs55.mkdir(nodePath.dirname(
|
|
27018
|
-
await fs55.writeFile(
|
|
27466
|
+
await fs55.mkdir(nodePath.dirname(path59), { recursive: true });
|
|
27467
|
+
await fs55.writeFile(path59, content, "utf-8");
|
|
27019
27468
|
},
|
|
27020
|
-
async exists(
|
|
27469
|
+
async exists(path59) {
|
|
27021
27470
|
const fs55 = await import('fs/promises');
|
|
27022
27471
|
try {
|
|
27023
|
-
await fs55.access(
|
|
27472
|
+
await fs55.access(path59);
|
|
27024
27473
|
return true;
|
|
27025
27474
|
} catch {
|
|
27026
27475
|
return false;
|
|
@@ -27376,10 +27825,10 @@ function getPhaseStatusForPhase(phase) {
|
|
|
27376
27825
|
}
|
|
27377
27826
|
async function loadProjectState(cwd, config) {
|
|
27378
27827
|
const fs55 = await import('fs/promises');
|
|
27379
|
-
const
|
|
27380
|
-
const statePath =
|
|
27381
|
-
const backlogPath =
|
|
27382
|
-
const checkpointDir =
|
|
27828
|
+
const path59 = await import('path');
|
|
27829
|
+
const statePath = path59.join(cwd, ".coco", "state.json");
|
|
27830
|
+
const backlogPath = path59.join(cwd, ".coco", "planning", "backlog.json");
|
|
27831
|
+
const checkpointDir = path59.join(cwd, ".coco", "checkpoints");
|
|
27383
27832
|
let currentPhase = "idle";
|
|
27384
27833
|
let metrics;
|
|
27385
27834
|
let sprint;
|
|
@@ -28781,8 +29230,8 @@ async function saveConfig2(config) {
|
|
|
28781
29230
|
await fs55.mkdir(dir, { recursive: true });
|
|
28782
29231
|
await fs55.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
28783
29232
|
}
|
|
28784
|
-
function getNestedValue(obj,
|
|
28785
|
-
const keys =
|
|
29233
|
+
function getNestedValue(obj, path59) {
|
|
29234
|
+
const keys = path59.split(".");
|
|
28786
29235
|
let current = obj;
|
|
28787
29236
|
for (const key of keys) {
|
|
28788
29237
|
if (current === null || current === void 0 || typeof current !== "object") {
|
|
@@ -28792,8 +29241,8 @@ function getNestedValue(obj, path58) {
|
|
|
28792
29241
|
}
|
|
28793
29242
|
return current;
|
|
28794
29243
|
}
|
|
28795
|
-
function setNestedValue(obj,
|
|
28796
|
-
const keys =
|
|
29244
|
+
function setNestedValue(obj, path59, value) {
|
|
29245
|
+
const keys = path59.split(".");
|
|
28797
29246
|
let current = obj;
|
|
28798
29247
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
28799
29248
|
const key = keys[i];
|
|
@@ -29068,6 +29517,21 @@ async function runDisableServer(name) {
|
|
|
29068
29517
|
init_skills();
|
|
29069
29518
|
init_skills2();
|
|
29070
29519
|
init_paths();
|
|
29520
|
+
async function loadSkillsSettings() {
|
|
29521
|
+
try {
|
|
29522
|
+
const { loadConfig: loadConfig3 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
|
|
29523
|
+
const config = await loadConfig3();
|
|
29524
|
+
return {
|
|
29525
|
+
globalDir: config.skills?.globalDir,
|
|
29526
|
+
globalDirs: config.skills?.globalDirs,
|
|
29527
|
+
projectDir: config.skills?.projectDir,
|
|
29528
|
+
projectDirs: config.skills?.projectDirs,
|
|
29529
|
+
disabled: config.skills?.disabled ?? []
|
|
29530
|
+
};
|
|
29531
|
+
} catch {
|
|
29532
|
+
return {};
|
|
29533
|
+
}
|
|
29534
|
+
}
|
|
29071
29535
|
function registerSkillsCommand(program2) {
|
|
29072
29536
|
const skillsCommand = program2.command("skills").description("Manage agent skills (SKILL.md and native)");
|
|
29073
29537
|
skillsCommand.command("list").description("List all discovered skills").option("-s, --scope <scope>", "Filter by scope (builtin|global|project)").option("-k, --kind <kind>", "Filter by kind (markdown|native)").action(runList);
|
|
@@ -29075,6 +29539,16 @@ function registerSkillsCommand(program2) {
|
|
|
29075
29539
|
skillsCommand.command("remove").description("Remove an installed skill").argument("<name>", "Skill name to remove").option("-g, --global", "Remove from global directory").option("-y, --yes", "Skip confirmation").action(runRemove);
|
|
29076
29540
|
skillsCommand.command("info").description("Show details about a skill").argument("<name>", "Skill name").action(runInfo);
|
|
29077
29541
|
skillsCommand.command("create").description("Create a new skill from template").argument("<name>", "Skill name").option("-g, --global", "Create in global directory").action(runCreate);
|
|
29542
|
+
skillsCommand.command("doctor").description("Explain skill discovery paths, conflicts, and winners").action(runDoctor);
|
|
29543
|
+
}
|
|
29544
|
+
async function loadSkillsDiscoveryOptions() {
|
|
29545
|
+
const settings = await loadSkillsSettings();
|
|
29546
|
+
return {
|
|
29547
|
+
globalDir: settings.globalDir,
|
|
29548
|
+
globalDirs: settings.globalDirs,
|
|
29549
|
+
projectDir: settings.projectDir,
|
|
29550
|
+
projectDirs: settings.projectDirs
|
|
29551
|
+
};
|
|
29078
29552
|
}
|
|
29079
29553
|
async function runList(options) {
|
|
29080
29554
|
p26.intro(chalk.magenta("Skills"));
|
|
@@ -29082,7 +29556,8 @@ async function runList(options) {
|
|
|
29082
29556
|
let allSkills;
|
|
29083
29557
|
try {
|
|
29084
29558
|
const builtins = getBuiltinSkillsForDiscovery();
|
|
29085
|
-
|
|
29559
|
+
const discoveryOptions = await loadSkillsDiscoveryOptions();
|
|
29560
|
+
allSkills = await discoverAllSkills(projectPath, builtins, discoveryOptions);
|
|
29086
29561
|
} catch (error) {
|
|
29087
29562
|
p26.log.error(
|
|
29088
29563
|
`Failed to discover skills: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -29110,8 +29585,8 @@ async function runList(options) {
|
|
|
29110
29585
|
}
|
|
29111
29586
|
const scopeLabels = {
|
|
29112
29587
|
builtin: "Builtin",
|
|
29113
|
-
global: `Global (${CONFIG_PATHS.skills})`,
|
|
29114
|
-
project: `Project (
|
|
29588
|
+
global: `Global (multi-dir, default includes ${CONFIG_PATHS.skills})`,
|
|
29589
|
+
project: `Project (.agents/skills/, .claude/skills/, ...)`
|
|
29115
29590
|
};
|
|
29116
29591
|
for (const [scope, skills] of byScope) {
|
|
29117
29592
|
const label = scopeLabels[scope] ?? scope;
|
|
@@ -29133,7 +29608,7 @@ async function runAdd(source, options) {
|
|
|
29133
29608
|
const isGithubShorthand = source.includes("/") && !isGitUrl;
|
|
29134
29609
|
const isLocalPath = source.startsWith(".") || source.startsWith("/");
|
|
29135
29610
|
if (isLocalPath) {
|
|
29136
|
-
const targetDir = options.global ? CONFIG_PATHS.skills : path37__default.join(process.cwd(), ".
|
|
29611
|
+
const targetDir = options.global ? CONFIG_PATHS.skills : path37__default.join(process.cwd(), ".agents", "skills");
|
|
29137
29612
|
const sourcePath = path37__default.resolve(source);
|
|
29138
29613
|
const skillName = path37__default.basename(sourcePath);
|
|
29139
29614
|
const destPath = path37__default.join(targetDir, skillName);
|
|
@@ -29163,13 +29638,14 @@ async function runAdd(source, options) {
|
|
|
29163
29638
|
spinner18.stop("Skill installed successfully");
|
|
29164
29639
|
} catch (error) {
|
|
29165
29640
|
spinner18.stop("Installation failed");
|
|
29166
|
-
const
|
|
29641
|
+
const spawnError = error;
|
|
29642
|
+
const stderr = spawnError.stderr?.toString().trim();
|
|
29167
29643
|
const msg = stderr || (error instanceof Error ? error.message : String(error));
|
|
29168
29644
|
p26.log.error(`Failed to install skill: ${msg}`);
|
|
29169
|
-
p26.log.info("Try installing manually: git clone the repo into .
|
|
29645
|
+
p26.log.info("Try installing manually: git clone the repo into .agents/skills/");
|
|
29170
29646
|
}
|
|
29171
29647
|
} else if (isGitUrl) {
|
|
29172
|
-
const targetDir = options.global ? CONFIG_PATHS.skills : path37__default.join(process.cwd(), ".
|
|
29648
|
+
const targetDir = options.global ? CONFIG_PATHS.skills : path37__default.join(process.cwd(), ".agents", "skills");
|
|
29173
29649
|
await fs34__default.mkdir(targetDir, { recursive: true });
|
|
29174
29650
|
const skillName = source.split("/").pop()?.replace(".git", "") ?? "skill";
|
|
29175
29651
|
const skillDir = path37__default.join(targetDir, skillName);
|
|
@@ -29184,7 +29660,8 @@ async function runAdd(source, options) {
|
|
|
29184
29660
|
spinner18.stop(`Skill cloned to ${skillDir}`);
|
|
29185
29661
|
} catch (error) {
|
|
29186
29662
|
spinner18.stop("Clone failed");
|
|
29187
|
-
const
|
|
29663
|
+
const spawnError = error;
|
|
29664
|
+
const stderr = spawnError.stderr?.toString().trim();
|
|
29188
29665
|
const msg = stderr || (error instanceof Error ? error.message : String(error));
|
|
29189
29666
|
p26.log.error(`Failed to clone skill: ${msg}`);
|
|
29190
29667
|
}
|
|
@@ -29197,8 +29674,13 @@ async function runAdd(source, options) {
|
|
|
29197
29674
|
}
|
|
29198
29675
|
async function runRemove(name, options) {
|
|
29199
29676
|
p26.intro(chalk.magenta("Remove Skill"));
|
|
29200
|
-
const targetDir = options.global ? CONFIG_PATHS.skills : path37__default.join(process.cwd(), ".
|
|
29201
|
-
const skillPath = path37__default.
|
|
29677
|
+
const targetDir = options.global ? CONFIG_PATHS.skills : path37__default.join(process.cwd(), ".agents", "skills");
|
|
29678
|
+
const skillPath = path37__default.resolve(targetDir, name);
|
|
29679
|
+
if (!skillPath.startsWith(path37__default.resolve(targetDir) + path37__default.sep)) {
|
|
29680
|
+
p26.log.error(`Invalid skill name: "${name}"`);
|
|
29681
|
+
p26.outro("");
|
|
29682
|
+
return;
|
|
29683
|
+
}
|
|
29202
29684
|
try {
|
|
29203
29685
|
await fs34__default.access(skillPath);
|
|
29204
29686
|
} catch {
|
|
@@ -29226,7 +29708,8 @@ async function runInfo(name) {
|
|
|
29226
29708
|
let allSkills;
|
|
29227
29709
|
try {
|
|
29228
29710
|
const builtins = getBuiltinSkillsForDiscovery();
|
|
29229
|
-
|
|
29711
|
+
const discoveryOptions = await loadSkillsDiscoveryOptions();
|
|
29712
|
+
allSkills = await discoverAllSkills(projectPath, builtins, discoveryOptions);
|
|
29230
29713
|
} catch (error) {
|
|
29231
29714
|
p26.log.error(
|
|
29232
29715
|
`Failed to discover skills: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -29277,7 +29760,7 @@ async function runInfo(name) {
|
|
|
29277
29760
|
}
|
|
29278
29761
|
async function runCreate(name, options) {
|
|
29279
29762
|
p26.intro(chalk.magenta("Create Skill"));
|
|
29280
|
-
const targetDir = options.global ? CONFIG_PATHS.skills : path37__default.join(process.cwd(), ".
|
|
29763
|
+
const targetDir = options.global ? CONFIG_PATHS.skills : path37__default.join(process.cwd(), ".agents", "skills");
|
|
29281
29764
|
const skillDir = path37__default.join(targetDir, name);
|
|
29282
29765
|
try {
|
|
29283
29766
|
await fs34__default.access(skillDir);
|
|
@@ -29337,6 +29820,98 @@ when this skill is activated (automatically via matching or manually via /${name
|
|
|
29337
29820
|
p26.log.info(`Edit ${path37__default.join(skillDir, "SKILL.md")} to add instructions.`);
|
|
29338
29821
|
p26.outro("");
|
|
29339
29822
|
}
|
|
29823
|
+
function registerWinner(winners, candidate, candidateScanOrder, scanOrderById) {
|
|
29824
|
+
const existing = winners.get(candidate.id);
|
|
29825
|
+
const existingOrder = scanOrderById.get(candidate.id) ?? -1;
|
|
29826
|
+
const scopePriority = { builtin: 1, global: 2, project: 3 };
|
|
29827
|
+
if (!existing || scopePriority[candidate.scope] > scopePriority[existing.scope] || scopePriority[candidate.scope] === scopePriority[existing.scope] && candidateScanOrder >= existingOrder) {
|
|
29828
|
+
winners.set(candidate.id, candidate);
|
|
29829
|
+
scanOrderById.set(candidate.id, candidateScanOrder);
|
|
29830
|
+
}
|
|
29831
|
+
}
|
|
29832
|
+
async function runDoctor() {
|
|
29833
|
+
p26.intro(chalk.magenta("Skills Doctor"));
|
|
29834
|
+
const projectPath = process.cwd();
|
|
29835
|
+
const settings = await loadSkillsSettings();
|
|
29836
|
+
const discoveryOptions = {
|
|
29837
|
+
globalDir: settings.globalDir,
|
|
29838
|
+
globalDirs: settings.globalDirs,
|
|
29839
|
+
projectDir: settings.projectDir,
|
|
29840
|
+
projectDirs: settings.projectDirs
|
|
29841
|
+
};
|
|
29842
|
+
const disabled = new Set(settings.disabled ?? []);
|
|
29843
|
+
const builtins = getBuiltinSkillsForDiscovery();
|
|
29844
|
+
const { globalDirs, projectDirs } = resolveDiscoveryDirs(projectPath, discoveryOptions);
|
|
29845
|
+
p26.log.step("Discovery paths (scan order, later wins within same scope)");
|
|
29846
|
+
console.log(chalk.dim(" Global:"));
|
|
29847
|
+
for (const dir of globalDirs) {
|
|
29848
|
+
console.log(chalk.dim(` - ${dir}`));
|
|
29849
|
+
}
|
|
29850
|
+
console.log(chalk.dim(" Project:"));
|
|
29851
|
+
for (const dir of projectDirs) {
|
|
29852
|
+
console.log(chalk.dim(` - ${dir}`));
|
|
29853
|
+
}
|
|
29854
|
+
console.log();
|
|
29855
|
+
const winners = /* @__PURE__ */ new Map();
|
|
29856
|
+
const winnerScanOrderById = /* @__PURE__ */ new Map();
|
|
29857
|
+
const candidatesById = /* @__PURE__ */ new Map();
|
|
29858
|
+
let scanOrder = 0;
|
|
29859
|
+
for (const skill of builtins) {
|
|
29860
|
+
const meta = nativeSkillToMetadata(skill, "builtin");
|
|
29861
|
+
meta.path = "<builtin>";
|
|
29862
|
+
registerWinner(winners, meta, scanOrder, winnerScanOrderById);
|
|
29863
|
+
candidatesById.set(meta.id, [meta]);
|
|
29864
|
+
scanOrder += 1;
|
|
29865
|
+
}
|
|
29866
|
+
for (const dir of globalDirs) {
|
|
29867
|
+
const metas = await scanSkillsDirectory(dir, "global");
|
|
29868
|
+
const names = metas.map((m) => m.name).sort().join(", ");
|
|
29869
|
+
p26.log.info(`Global ${dir}: ${metas.length} skills${names ? ` (${names})` : ""}`);
|
|
29870
|
+
for (const meta of metas) {
|
|
29871
|
+
registerWinner(winners, meta, scanOrder, winnerScanOrderById);
|
|
29872
|
+
const list = candidatesById.get(meta.id) ?? [];
|
|
29873
|
+
list.push(meta);
|
|
29874
|
+
candidatesById.set(meta.id, list);
|
|
29875
|
+
scanOrder += 1;
|
|
29876
|
+
}
|
|
29877
|
+
}
|
|
29878
|
+
for (const dir of projectDirs) {
|
|
29879
|
+
const metas = await scanSkillsDirectory(dir, "project");
|
|
29880
|
+
const names = metas.map((m) => m.name).sort().join(", ");
|
|
29881
|
+
p26.log.info(`Project ${dir}: ${metas.length} skills${names ? ` (${names})` : ""}`);
|
|
29882
|
+
for (const meta of metas) {
|
|
29883
|
+
registerWinner(winners, meta, scanOrder, winnerScanOrderById);
|
|
29884
|
+
const list = candidatesById.get(meta.id) ?? [];
|
|
29885
|
+
list.push(meta);
|
|
29886
|
+
candidatesById.set(meta.id, list);
|
|
29887
|
+
scanOrder += 1;
|
|
29888
|
+
}
|
|
29889
|
+
}
|
|
29890
|
+
const conflicts = Array.from(candidatesById.entries()).filter(([, list]) => list.length > 1).sort(([a], [b]) => a.localeCompare(b));
|
|
29891
|
+
const activeWinners = Array.from(winners.values()).filter((meta) => !disabled.has(meta.id));
|
|
29892
|
+
console.log();
|
|
29893
|
+
p26.log.step(`Final active skills: ${activeWinners.length}`);
|
|
29894
|
+
if (disabled.size > 0) {
|
|
29895
|
+
p26.log.info(`Disabled by config: ${Array.from(disabled).sort().join(", ")}`);
|
|
29896
|
+
}
|
|
29897
|
+
if (conflicts.length === 0) {
|
|
29898
|
+
p26.log.success("No naming conflicts detected.");
|
|
29899
|
+
p26.outro("");
|
|
29900
|
+
return;
|
|
29901
|
+
}
|
|
29902
|
+
p26.log.step(`Conflicts detected: ${conflicts.length}`);
|
|
29903
|
+
for (const [id, list] of conflicts) {
|
|
29904
|
+
const winner = winners.get(id);
|
|
29905
|
+
if (!winner) continue;
|
|
29906
|
+
const disabledTag = disabled.has(winner.id) ? chalk.yellow(" [DISABLED]") : "";
|
|
29907
|
+
console.log(` ${chalk.bold(id)} -> winner: ${winner.path} [${winner.scope}]${disabledTag}`);
|
|
29908
|
+
for (const candidate of list) {
|
|
29909
|
+
const marker = candidate.path === winner.path ? chalk.green("WIN") : chalk.dim("LOSE");
|
|
29910
|
+
console.log(` - ${marker} ${candidate.path} [${candidate.scope}]`);
|
|
29911
|
+
}
|
|
29912
|
+
}
|
|
29913
|
+
p26.outro("");
|
|
29914
|
+
}
|
|
29340
29915
|
|
|
29341
29916
|
// src/cli/commands/check.ts
|
|
29342
29917
|
init_evaluator();
|
|
@@ -29699,9 +30274,9 @@ function registerCheckCommand(program2) {
|
|
|
29699
30274
|
// src/swarm/spec-parser.ts
|
|
29700
30275
|
async function parseSwarmSpec(filePath) {
|
|
29701
30276
|
const fs55 = await import('fs/promises');
|
|
29702
|
-
const
|
|
30277
|
+
const path59 = await import('path');
|
|
29703
30278
|
const rawContent = await fs55.readFile(filePath, "utf-8");
|
|
29704
|
-
const ext =
|
|
30279
|
+
const ext = path59.extname(filePath).toLowerCase();
|
|
29705
30280
|
if (ext === ".yaml" || ext === ".yml") {
|
|
29706
30281
|
return parseYamlSpec(rawContent);
|
|
29707
30282
|
}
|
|
@@ -30031,8 +30606,8 @@ var DEFAULT_AGENT_CONFIG = {
|
|
|
30031
30606
|
};
|
|
30032
30607
|
async function loadAgentConfig(projectPath) {
|
|
30033
30608
|
const fs55 = await import('fs/promises');
|
|
30034
|
-
const
|
|
30035
|
-
const configPath =
|
|
30609
|
+
const path59 = await import('path');
|
|
30610
|
+
const configPath = path59.join(projectPath, ".coco", "swarm", "agents.json");
|
|
30036
30611
|
try {
|
|
30037
30612
|
const raw = await fs55.readFile(configPath, "utf-8");
|
|
30038
30613
|
const parsed = JSON.parse(raw);
|
|
@@ -30223,16 +30798,16 @@ async function createBoard(projectPath, spec) {
|
|
|
30223
30798
|
}
|
|
30224
30799
|
async function loadBoard(projectPath) {
|
|
30225
30800
|
const fs55 = await import('fs/promises');
|
|
30226
|
-
const
|
|
30227
|
-
const boardPath =
|
|
30801
|
+
const path59 = await import('path');
|
|
30802
|
+
const boardPath = path59.join(projectPath, ".coco", "swarm", "task-board.json");
|
|
30228
30803
|
const raw = await fs55.readFile(boardPath, "utf-8");
|
|
30229
30804
|
return JSON.parse(raw);
|
|
30230
30805
|
}
|
|
30231
30806
|
async function saveBoard(projectPath, board) {
|
|
30232
30807
|
const fs55 = await import('fs/promises');
|
|
30233
|
-
const
|
|
30234
|
-
const boardDir =
|
|
30235
|
-
const boardPath =
|
|
30808
|
+
const path59 = await import('path');
|
|
30809
|
+
const boardDir = path59.join(projectPath, ".coco", "swarm");
|
|
30810
|
+
const boardPath = path59.join(boardDir, "task-board.json");
|
|
30236
30811
|
await fs55.mkdir(boardDir, { recursive: true });
|
|
30237
30812
|
await fs55.writeFile(boardPath, JSON.stringify(board, null, 2), "utf-8");
|
|
30238
30813
|
}
|
|
@@ -30400,9 +30975,9 @@ async function defaultPromptHandler(q) {
|
|
|
30400
30975
|
}
|
|
30401
30976
|
async function writeAssumptionsFile(projectPath, projectName, questions, assumptions) {
|
|
30402
30977
|
const fs55 = await import('fs/promises');
|
|
30403
|
-
const
|
|
30404
|
-
const swarmDir =
|
|
30405
|
-
const assumptionsPath =
|
|
30978
|
+
const path59 = await import('path');
|
|
30979
|
+
const swarmDir = path59.join(projectPath, ".coco", "swarm");
|
|
30980
|
+
const assumptionsPath = path59.join(swarmDir, "assumptions.md");
|
|
30406
30981
|
await fs55.mkdir(swarmDir, { recursive: true });
|
|
30407
30982
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
30408
30983
|
const content = [
|
|
@@ -30426,9 +31001,9 @@ async function writeAssumptionsFile(projectPath, projectName, questions, assumpt
|
|
|
30426
31001
|
// src/swarm/events.ts
|
|
30427
31002
|
async function appendSwarmEvent(projectPath, event) {
|
|
30428
31003
|
const fs55 = await import('fs/promises');
|
|
30429
|
-
const
|
|
30430
|
-
const eventsDir =
|
|
30431
|
-
const eventsFile =
|
|
31004
|
+
const path59 = await import('path');
|
|
31005
|
+
const eventsDir = path59.join(projectPath, ".coco", "swarm");
|
|
31006
|
+
const eventsFile = path59.join(eventsDir, "events.jsonl");
|
|
30432
31007
|
await fs55.mkdir(eventsDir, { recursive: true });
|
|
30433
31008
|
await fs55.appendFile(eventsFile, JSON.stringify(event) + "\n", "utf-8");
|
|
30434
31009
|
}
|
|
@@ -30439,9 +31014,9 @@ function createEventId() {
|
|
|
30439
31014
|
// src/swarm/knowledge.ts
|
|
30440
31015
|
async function appendKnowledge(projectPath, entry) {
|
|
30441
31016
|
const fs55 = await import('fs/promises');
|
|
30442
|
-
const
|
|
30443
|
-
const knowledgeDir =
|
|
30444
|
-
const knowledgeFile =
|
|
31017
|
+
const path59 = await import('path');
|
|
31018
|
+
const knowledgeDir = path59.join(projectPath, ".coco", "swarm");
|
|
31019
|
+
const knowledgeFile = path59.join(knowledgeDir, "knowledge.jsonl");
|
|
30445
31020
|
await fs55.mkdir(knowledgeDir, { recursive: true });
|
|
30446
31021
|
await fs55.appendFile(knowledgeFile, JSON.stringify(entry) + "\n", "utf-8");
|
|
30447
31022
|
}
|
|
@@ -30748,10 +31323,10 @@ async function runSwarmLifecycle(options) {
|
|
|
30748
31323
|
async function stageInit(ctx) {
|
|
30749
31324
|
const { projectPath, spec } = ctx.options;
|
|
30750
31325
|
const fs55 = await import('fs/promises');
|
|
30751
|
-
const
|
|
30752
|
-
await fs55.mkdir(
|
|
31326
|
+
const path59 = await import('path');
|
|
31327
|
+
await fs55.mkdir(path59.join(projectPath, ".coco", "swarm"), { recursive: true });
|
|
30753
31328
|
await fs55.mkdir(ctx.options.outputPath, { recursive: true });
|
|
30754
|
-
const specSummaryPath =
|
|
31329
|
+
const specSummaryPath = path59.join(projectPath, ".coco", "swarm", "spec-summary.json");
|
|
30755
31330
|
const specSummary = {
|
|
30756
31331
|
projectName: spec.projectName,
|
|
30757
31332
|
description: spec.description,
|
|
@@ -30823,8 +31398,8 @@ async function stagePlan(ctx) {
|
|
|
30823
31398
|
]);
|
|
30824
31399
|
await createBoard(projectPath, spec);
|
|
30825
31400
|
const fs55 = await import('fs/promises');
|
|
30826
|
-
const
|
|
30827
|
-
const planPath =
|
|
31401
|
+
const path59 = await import('path');
|
|
31402
|
+
const planPath = path59.join(projectPath, ".coco", "swarm", "plan.json");
|
|
30828
31403
|
await fs55.writeFile(
|
|
30829
31404
|
planPath,
|
|
30830
31405
|
JSON.stringify({ pm: pmResult, architect: archResult, bestPractices: bpResult }, null, 2),
|
|
@@ -31041,7 +31616,7 @@ async function stageIntegrate(ctx) {
|
|
|
31041
31616
|
async function stageOutput(ctx) {
|
|
31042
31617
|
const { projectPath, outputPath } = ctx.options;
|
|
31043
31618
|
const fs55 = await import('fs/promises');
|
|
31044
|
-
const
|
|
31619
|
+
const path59 = await import('path');
|
|
31045
31620
|
const board = await loadBoard(projectPath);
|
|
31046
31621
|
const featureResults = Array.from(ctx.featureResults.values());
|
|
31047
31622
|
const summary = {
|
|
@@ -31056,7 +31631,7 @@ async function stageOutput(ctx) {
|
|
|
31056
31631
|
globalScore: computeGlobalScore(featureResults)
|
|
31057
31632
|
};
|
|
31058
31633
|
await fs55.mkdir(outputPath, { recursive: true });
|
|
31059
|
-
const summaryPath =
|
|
31634
|
+
const summaryPath = path59.join(outputPath, "swarm-summary.json");
|
|
31060
31635
|
await fs55.writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
|
|
31061
31636
|
const passed = summary.globalScore >= ctx.options.minScore;
|
|
31062
31637
|
await emitGate(projectPath, "global-score", passed, `Global score: ${summary.globalScore}`);
|
|
@@ -31403,8 +31978,8 @@ var SwarmOrchestrator = class {
|
|
|
31403
31978
|
noQuestions = false,
|
|
31404
31979
|
onProgress
|
|
31405
31980
|
} = options;
|
|
31406
|
-
const
|
|
31407
|
-
const projectPath =
|
|
31981
|
+
const path59 = await import('path');
|
|
31982
|
+
const projectPath = path59.dirname(path59.resolve(specFile));
|
|
31408
31983
|
onProgress?.("init", `Parsing spec file: ${specFile}`);
|
|
31409
31984
|
const spec = await parseSwarmSpec(specFile);
|
|
31410
31985
|
onProgress?.("init", `Initializing provider: ${providerType}`);
|
|
@@ -31415,7 +31990,7 @@ var SwarmOrchestrator = class {
|
|
|
31415
31990
|
await runSwarmLifecycle({
|
|
31416
31991
|
spec,
|
|
31417
31992
|
projectPath,
|
|
31418
|
-
outputPath:
|
|
31993
|
+
outputPath: path59.resolve(outputPath),
|
|
31419
31994
|
provider,
|
|
31420
31995
|
agentConfig,
|
|
31421
31996
|
minScore,
|
|
@@ -33092,7 +33667,13 @@ async function ensureConfiguredV2(config) {
|
|
|
33092
33667
|
try {
|
|
33093
33668
|
const recommended = getRecommendedModel(prov.id);
|
|
33094
33669
|
const model = recommended?.id || prov.models[0]?.id || "";
|
|
33095
|
-
|
|
33670
|
+
let providerId = prov.id;
|
|
33671
|
+
if (prov.id === "openai" && hasOpenAIOAuthTokens && !process.env[prov.envVar]) {
|
|
33672
|
+
const tokenResult = await getOrRefreshOAuthToken("openai");
|
|
33673
|
+
if (!tokenResult) continue;
|
|
33674
|
+
process.env["OPENAI_CODEX_TOKEN"] = tokenResult.accessToken;
|
|
33675
|
+
providerId = "codex";
|
|
33676
|
+
}
|
|
33096
33677
|
const provider = await createProvider(providerId, { model });
|
|
33097
33678
|
if (await provider.isAvailable()) {
|
|
33098
33679
|
return {
|
|
@@ -34330,8 +34911,8 @@ async function listTrustedProjects2(trustStore) {
|
|
|
34330
34911
|
p26.log.message("");
|
|
34331
34912
|
for (const project of projects) {
|
|
34332
34913
|
const level = project.approvalLevel.toUpperCase().padEnd(5);
|
|
34333
|
-
const
|
|
34334
|
-
p26.log.message(` [${level}] ${
|
|
34914
|
+
const path59 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
|
|
34915
|
+
p26.log.message(` [${level}] ${path59}`);
|
|
34335
34916
|
p26.log.message(` Last accessed: ${new Date(project.lastAccessed).toLocaleString()}`);
|
|
34336
34917
|
}
|
|
34337
34918
|
p26.log.message("");
|
|
@@ -35562,8 +36143,8 @@ function displayRewindResult(result) {
|
|
|
35562
36143
|
const fileName = filePath.split("/").pop() ?? filePath;
|
|
35563
36144
|
console.log(`${chalk.green(String.fromCodePoint(10003))} Restored: ${fileName}`);
|
|
35564
36145
|
}
|
|
35565
|
-
for (const { path:
|
|
35566
|
-
const fileName =
|
|
36146
|
+
for (const { path: path59, error } of result.filesFailed) {
|
|
36147
|
+
const fileName = path59.split("/").pop() ?? path59;
|
|
35567
36148
|
console.log(`${chalk.red(String.fromCodePoint(10007))} Failed: ${fileName} (${error})`);
|
|
35568
36149
|
}
|
|
35569
36150
|
if (result.conversationRestored) {
|
|
@@ -48250,8 +48831,8 @@ function formatToolSummary(toolName, input) {
|
|
|
48250
48831
|
case "grep":
|
|
48251
48832
|
case "search_files": {
|
|
48252
48833
|
const pattern = String(input.pattern || "");
|
|
48253
|
-
const
|
|
48254
|
-
return `"${pattern}"${
|
|
48834
|
+
const path59 = input.path ? ` in ${input.path}` : "";
|
|
48835
|
+
return `"${pattern}"${path59}`;
|
|
48255
48836
|
}
|
|
48256
48837
|
case "bash_exec": {
|
|
48257
48838
|
const cmd = String(input.command || "");
|
|
@@ -48272,8 +48853,8 @@ function formatToolSummary(toolName, input) {
|
|
|
48272
48853
|
function formatUrl(url) {
|
|
48273
48854
|
try {
|
|
48274
48855
|
const u = new URL(url);
|
|
48275
|
-
const
|
|
48276
|
-
const display =
|
|
48856
|
+
const path59 = u.pathname.replace(/\/$/, "");
|
|
48857
|
+
const display = path59 ? `${u.hostname} \u203A ${path59.slice(1)}` : u.hostname;
|
|
48277
48858
|
const max = Math.max(getTerminalWidth2() - 20, 50);
|
|
48278
48859
|
return display.length > max ? display.slice(0, max - 1) + "\u2026" : display;
|
|
48279
48860
|
} catch {
|
|
@@ -50334,6 +50915,7 @@ var ParallelToolExecutor = class {
|
|
|
50334
50915
|
if (signal?.aborted) {
|
|
50335
50916
|
return { executed: null, skipped: true, reason: "Operation cancelled" };
|
|
50336
50917
|
}
|
|
50918
|
+
let hookWarning = null;
|
|
50337
50919
|
if (hookRegistry && hookExecutor) {
|
|
50338
50920
|
const preContext = {
|
|
50339
50921
|
event: "PreToolUse",
|
|
@@ -50343,7 +50925,23 @@ var ParallelToolExecutor = class {
|
|
|
50343
50925
|
projectPath: projectPath ?? process.cwd(),
|
|
50344
50926
|
timestamp: /* @__PURE__ */ new Date()
|
|
50345
50927
|
};
|
|
50346
|
-
|
|
50928
|
+
let preResult;
|
|
50929
|
+
try {
|
|
50930
|
+
preResult = await hookExecutor.executeHooks(hookRegistry, preContext);
|
|
50931
|
+
} catch (error) {
|
|
50932
|
+
if (isAbortError(error, signal)) {
|
|
50933
|
+
return { executed: null, skipped: true, reason: "Operation cancelled" };
|
|
50934
|
+
}
|
|
50935
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
50936
|
+
hookWarning = `PreToolUse hook failed: ${msg}`;
|
|
50937
|
+
preResult = {
|
|
50938
|
+
event: "PreToolUse",
|
|
50939
|
+
results: [],
|
|
50940
|
+
allSucceeded: false,
|
|
50941
|
+
shouldContinue: true,
|
|
50942
|
+
duration: 0
|
|
50943
|
+
};
|
|
50944
|
+
}
|
|
50347
50945
|
onHookExecuted?.("PreToolUse", preResult);
|
|
50348
50946
|
if (!preResult.shouldContinue) {
|
|
50349
50947
|
return {
|
|
@@ -50361,9 +50959,27 @@ var ParallelToolExecutor = class {
|
|
|
50361
50959
|
}
|
|
50362
50960
|
onToolStart?.(toolCall, index, total);
|
|
50363
50961
|
const startTime = performance.now();
|
|
50364
|
-
|
|
50962
|
+
let result;
|
|
50963
|
+
try {
|
|
50964
|
+
result = await registry.execute(toolCall.name, toolCall.input, { signal });
|
|
50965
|
+
} catch (error) {
|
|
50966
|
+
if (isAbortError(error, signal)) {
|
|
50967
|
+
return { executed: null, skipped: true, reason: "Operation cancelled" };
|
|
50968
|
+
}
|
|
50969
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
50970
|
+
result = {
|
|
50971
|
+
success: false,
|
|
50972
|
+
error: `Unexpected error in ${toolCall.name}: ${msg}`,
|
|
50973
|
+
duration: 0
|
|
50974
|
+
};
|
|
50975
|
+
}
|
|
50365
50976
|
const duration = performance.now() - startTime;
|
|
50366
|
-
const
|
|
50977
|
+
const outputParts = [];
|
|
50978
|
+
if (hookWarning) outputParts.push(hookWarning);
|
|
50979
|
+
outputParts.push(
|
|
50980
|
+
result.success ? JSON.stringify(result.data, null, 2) : result.error ?? "Unknown error"
|
|
50981
|
+
);
|
|
50982
|
+
const output = outputParts.filter(Boolean).join("\n");
|
|
50367
50983
|
const executedCall = {
|
|
50368
50984
|
id: toolCall.id,
|
|
50369
50985
|
name: toolCall.name,
|
|
@@ -50391,8 +51007,25 @@ var ParallelToolExecutor = class {
|
|
|
50391
51007
|
projectPath: projectPath ?? process.cwd(),
|
|
50392
51008
|
timestamp: /* @__PURE__ */ new Date()
|
|
50393
51009
|
};
|
|
50394
|
-
|
|
50395
|
-
|
|
51010
|
+
try {
|
|
51011
|
+
const postResult = await hookExecutor.executeHooks(hookRegistry, postContext);
|
|
51012
|
+
onHookExecuted?.("PostToolUse", postResult);
|
|
51013
|
+
} catch (error) {
|
|
51014
|
+
if (isAbortError(error, signal)) {
|
|
51015
|
+
return { executed: null, skipped: true, reason: "Operation cancelled" };
|
|
51016
|
+
}
|
|
51017
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
51018
|
+
const warningResult = {
|
|
51019
|
+
event: "PostToolUse",
|
|
51020
|
+
results: [],
|
|
51021
|
+
allSucceeded: false,
|
|
51022
|
+
shouldContinue: true,
|
|
51023
|
+
duration: 0
|
|
51024
|
+
};
|
|
51025
|
+
onHookExecuted?.("PostToolUse", warningResult);
|
|
51026
|
+
executedCall.result.output = `${executedCall.result.output}
|
|
51027
|
+
PostToolUse hook failed: ${msg}`;
|
|
51028
|
+
}
|
|
50396
51029
|
}
|
|
50397
51030
|
onToolEnd?.(executedCall);
|
|
50398
51031
|
return { executed: executedCall, skipped: false };
|
|
@@ -50493,9 +51126,20 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
50493
51126
|
const allTools = toolRegistry.getToolDefinitionsForLLM();
|
|
50494
51127
|
const tools = session.planMode ? filterReadOnlyTools(allTools) : allTools;
|
|
50495
51128
|
let iteration = 0;
|
|
50496
|
-
|
|
51129
|
+
let maxIterations = session.config.agent.maxToolIterations;
|
|
51130
|
+
const HARD_MAX_ITERATIONS = 100;
|
|
51131
|
+
const MAX_AUTO_ITERATION_EXTENSIONS = 2;
|
|
51132
|
+
const AUTO_ITERATION_EXTENSION_SIZE = Math.max(
|
|
51133
|
+
5,
|
|
51134
|
+
Math.ceil(session.config.agent.maxToolIterations * 0.4)
|
|
51135
|
+
);
|
|
51136
|
+
let autoIterationExtensionsUsed = 0;
|
|
50497
51137
|
const toolErrorCounts = /* @__PURE__ */ new Map();
|
|
50498
51138
|
const MAX_CONSECUTIVE_TOOL_ERRORS = 3;
|
|
51139
|
+
const MAX_NO_TOOL_RECOVERY_ATTEMPTS = 3;
|
|
51140
|
+
let noToolRecoveryAttempts = 0;
|
|
51141
|
+
let lastSuccessIterationSignature = "";
|
|
51142
|
+
let repeatedSuccessIterationCount = 0;
|
|
50499
51143
|
const ITERATION_LIMIT_WARNING_RATIO = 0.75;
|
|
50500
51144
|
const ITERATION_LIMIT_SUMMARY_PROMPT = `[System: You have now used all allowed iterations and tool calls are no longer available. Write your final response immediately: (1) briefly state what was completed, (2) describe what still needs to be done, (3) give specific next steps so the user can continue. Be concise and direct.]`;
|
|
50501
51145
|
const INLINE_RESULT_MAX_CHARS = 8e3;
|
|
@@ -50510,6 +51154,38 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
50510
51154
|
[... ${omitted.toLocaleString()} characters omitted \u2014 use read_file with offset/limit to retrieve more of '${toolName}' output ...]
|
|
50511
51155
|
${tail}`;
|
|
50512
51156
|
}
|
|
51157
|
+
function shouldRecoverNoToolTurn(stopReason, content) {
|
|
51158
|
+
const trimmed = content.trim();
|
|
51159
|
+
if (stopReason === "tool_use") {
|
|
51160
|
+
return {
|
|
51161
|
+
recover: true,
|
|
51162
|
+
reason: "The previous response indicated tool use, but no tool calls were received. Re-emit the tool call(s) now."
|
|
51163
|
+
};
|
|
51164
|
+
}
|
|
51165
|
+
if (stopReason === "max_tokens" && trimmed.length === 0) {
|
|
51166
|
+
return {
|
|
51167
|
+
recover: true,
|
|
51168
|
+
reason: "The previous response was cut off before producing any usable output. Continue immediately."
|
|
51169
|
+
};
|
|
51170
|
+
}
|
|
51171
|
+
const planningOnly = trimmed.length > 0 && trimmed.length < 320 && /^(voy a|ahora voy|i('| )?ll|i will|let me|starting|preparing|activating|continuo|contin[uú]o|de acuerdo[, ]+voy)\b/i.test(
|
|
51172
|
+
trimmed
|
|
51173
|
+
);
|
|
51174
|
+
if (planningOnly) {
|
|
51175
|
+
return {
|
|
51176
|
+
recover: true,
|
|
51177
|
+
reason: "Do not only describe the next step. Execute it now with concrete tool calls."
|
|
51178
|
+
};
|
|
51179
|
+
}
|
|
51180
|
+
return { recover: false, reason: "" };
|
|
51181
|
+
}
|
|
51182
|
+
function shouldAutoExtendIterationBudget(latestExecuted, latestToolResults, isInErrorLoop) {
|
|
51183
|
+
if (isInErrorLoop) return false;
|
|
51184
|
+
if (latestExecuted.length === 0) return false;
|
|
51185
|
+
if (latestToolResults.length === 0) return false;
|
|
51186
|
+
if (repeatedSuccessIterationCount >= 2) return false;
|
|
51187
|
+
return latestExecuted.some((c) => c.result.success);
|
|
51188
|
+
}
|
|
50513
51189
|
while (iteration < maxIterations) {
|
|
50514
51190
|
iteration++;
|
|
50515
51191
|
const isLastIteration = iteration === maxIterations;
|
|
@@ -50655,9 +51331,24 @@ ${tail}`;
|
|
|
50655
51331
|
});
|
|
50656
51332
|
continue;
|
|
50657
51333
|
}
|
|
51334
|
+
const noToolRecovery = shouldRecoverNoToolTurn(lastStopReason, responseContent);
|
|
51335
|
+
if (noToolRecovery.recover && noToolRecoveryAttempts < MAX_NO_TOOL_RECOVERY_ATTEMPTS && iteration < maxIterations) {
|
|
51336
|
+
noToolRecoveryAttempts++;
|
|
51337
|
+
addMessage(session, {
|
|
51338
|
+
role: "assistant",
|
|
51339
|
+
content: responseContent || "[No output returned in previous step.]"
|
|
51340
|
+
});
|
|
51341
|
+
addMessage(session, {
|
|
51342
|
+
role: "user",
|
|
51343
|
+
content: `[System: ${noToolRecovery.reason}]`
|
|
51344
|
+
});
|
|
51345
|
+
continue;
|
|
51346
|
+
}
|
|
51347
|
+
noToolRecoveryAttempts = 0;
|
|
50658
51348
|
addMessage(session, { role: "assistant", content: responseContent });
|
|
50659
51349
|
break;
|
|
50660
51350
|
}
|
|
51351
|
+
noToolRecoveryAttempts = 0;
|
|
50661
51352
|
const response = {
|
|
50662
51353
|
content: responseContent,
|
|
50663
51354
|
toolCalls: collectedToolCalls
|
|
@@ -50665,6 +51356,7 @@ ${tail}`;
|
|
|
50665
51356
|
const toolResults = [];
|
|
50666
51357
|
const toolUses = [];
|
|
50667
51358
|
let turnAborted = false;
|
|
51359
|
+
let currentIterationExecuted = [];
|
|
50668
51360
|
const totalTools = response.toolCalls.length;
|
|
50669
51361
|
const confirmedTools = [];
|
|
50670
51362
|
const declinedTools = /* @__PURE__ */ new Map();
|
|
@@ -50749,6 +51441,7 @@ ${tail}`;
|
|
|
50749
51441
|
});
|
|
50750
51442
|
for (const executed of parallelResult.executed) {
|
|
50751
51443
|
executedTools.push(executed);
|
|
51444
|
+
currentIterationExecuted.push(executed);
|
|
50752
51445
|
if (executed.name === "manage_permissions" && executed.result.success) {
|
|
50753
51446
|
const action = executed.input.action;
|
|
50754
51447
|
const patterns = executed.input.patterns;
|
|
@@ -50876,11 +51569,24 @@ ${tail}`;
|
|
|
50876
51569
|
}
|
|
50877
51570
|
}
|
|
50878
51571
|
}
|
|
51572
|
+
if (currentIterationExecuted.length > 0 && currentIterationExecuted.every((c) => c.result.success)) {
|
|
51573
|
+
const iterationSignature = currentIterationExecuted.map((c) => `${c.name}:${JSON.stringify(c.input)}`).join("|");
|
|
51574
|
+
if (iterationSignature === lastSuccessIterationSignature) {
|
|
51575
|
+
repeatedSuccessIterationCount++;
|
|
51576
|
+
} else {
|
|
51577
|
+
lastSuccessIterationSignature = iterationSignature;
|
|
51578
|
+
repeatedSuccessIterationCount = 0;
|
|
51579
|
+
}
|
|
51580
|
+
} else {
|
|
51581
|
+
lastSuccessIterationSignature = "";
|
|
51582
|
+
repeatedSuccessIterationCount = 0;
|
|
51583
|
+
}
|
|
51584
|
+
const canAutoExtendNow = isLastIteration && toolResults.length > 0 && autoIterationExtensionsUsed < MAX_AUTO_ITERATION_EXTENSIONS && maxIterations < HARD_MAX_ITERATIONS && shouldAutoExtendIterationBudget(executedTools, toolResults, stuckInErrorLoop);
|
|
50879
51585
|
if (toolResults.length > 0) {
|
|
50880
51586
|
const warningThreshold = Math.ceil(maxIterations * ITERATION_LIMIT_WARNING_RATIO);
|
|
50881
51587
|
const lastIdx = toolResults.length - 1;
|
|
50882
51588
|
const last = toolResults[lastIdx];
|
|
50883
|
-
if (isLastIteration && !stuckInErrorLoop) {
|
|
51589
|
+
if (isLastIteration && !stuckInErrorLoop && !canAutoExtendNow) {
|
|
50884
51590
|
toolResults[lastIdx] = {
|
|
50885
51591
|
...last,
|
|
50886
51592
|
content: typeof last.content === "string" ? last.content + `
|
|
@@ -50942,6 +51648,16 @@ ${ITERATION_LIMIT_SUMMARY_PROMPT}` : ITERATION_LIMIT_SUMMARY_PROMPT
|
|
|
50942
51648
|
}
|
|
50943
51649
|
break;
|
|
50944
51650
|
}
|
|
51651
|
+
if (canAutoExtendNow) {
|
|
51652
|
+
autoIterationExtensionsUsed++;
|
|
51653
|
+
const oldMax = maxIterations;
|
|
51654
|
+
maxIterations = Math.min(maxIterations + AUTO_ITERATION_EXTENSION_SIZE, HARD_MAX_ITERATIONS);
|
|
51655
|
+
addMessage(session, {
|
|
51656
|
+
role: "user",
|
|
51657
|
+
content: `[System: Iteration budget auto-extended from ${oldMax} to ${maxIterations} because progress is being made. Continue execution and prioritize completing the remaining critical work.]`
|
|
51658
|
+
});
|
|
51659
|
+
continue;
|
|
51660
|
+
}
|
|
50945
51661
|
if (isLastIteration && toolResults.length > 0) {
|
|
50946
51662
|
let summaryThinkingEnded = false;
|
|
50947
51663
|
options.onThinkingStart?.();
|
|
@@ -51606,7 +52322,7 @@ async function startRepl(options = {}) {
|
|
|
51606
52322
|
}
|
|
51607
52323
|
initializeContextManager(session, provider);
|
|
51608
52324
|
const { createLLMClassifier: createLLMClassifier2 } = await Promise.resolve().then(() => (init_llm_classifier(), llm_classifier_exports));
|
|
51609
|
-
|
|
52325
|
+
let llmClassifier = createLLMClassifier2(provider);
|
|
51610
52326
|
const { detectProjectStack: detectProjectStack2 } = await Promise.resolve().then(() => (init_stack_detector(), stack_detector_exports));
|
|
51611
52327
|
const [projectContext] = await Promise.all([
|
|
51612
52328
|
detectProjectStack2(projectPath),
|
|
@@ -51658,7 +52374,13 @@ async function startRepl(options = {}) {
|
|
|
51658
52374
|
const { registerMCPTools: registerMCPTools2 } = await Promise.resolve().then(() => (init_tools(), tools_exports));
|
|
51659
52375
|
const mcpRegistry = new MCPRegistryImpl2();
|
|
51660
52376
|
await mcpRegistry.load();
|
|
51661
|
-
const
|
|
52377
|
+
const registryServers = mcpRegistry.listEnabledServers();
|
|
52378
|
+
const { loadProjectMCPFile: loadProjectMCPFile2, mergeMCPConfigs: mergeMCPConfigs2 } = await Promise.resolve().then(() => (init_config_loader(), config_loader_exports));
|
|
52379
|
+
const projectServers = await loadProjectMCPFile2(process.cwd());
|
|
52380
|
+
const enabledServers = mergeMCPConfigs2(
|
|
52381
|
+
registryServers,
|
|
52382
|
+
projectServers.filter((s) => s.enabled !== false)
|
|
52383
|
+
);
|
|
51662
52384
|
if (enabledServers.length > 0) {
|
|
51663
52385
|
mcpManager = getMCPServerManager2();
|
|
51664
52386
|
let connections;
|
|
@@ -51748,6 +52470,127 @@ async function startRepl(options = {}) {
|
|
|
51748
52470
|
let warned75 = false;
|
|
51749
52471
|
let warned90 = false;
|
|
51750
52472
|
let consecutiveErrors = 0;
|
|
52473
|
+
const AUTO_SWITCH_THRESHOLD = 2;
|
|
52474
|
+
const autoSwitchHistory = /* @__PURE__ */ new Set();
|
|
52475
|
+
const enableAutoSwitchProvider = session.config.agent.enableAutoSwitchProvider === true;
|
|
52476
|
+
const buildReplayMessage = (message) => {
|
|
52477
|
+
if (typeof message === "string") {
|
|
52478
|
+
const trimmed = message.trim();
|
|
52479
|
+
return trimmed.length > 0 ? message : null;
|
|
52480
|
+
}
|
|
52481
|
+
const textParts = [];
|
|
52482
|
+
let imageCount = 0;
|
|
52483
|
+
for (const block of message) {
|
|
52484
|
+
if (block.type === "text" && typeof block.text === "string" && block.text.trim().length > 0) {
|
|
52485
|
+
textParts.push(block.text.trim());
|
|
52486
|
+
} else if (block.type === "image") {
|
|
52487
|
+
imageCount++;
|
|
52488
|
+
}
|
|
52489
|
+
}
|
|
52490
|
+
const text13 = textParts.join("\n\n").trim();
|
|
52491
|
+
if (text13.length > 0) {
|
|
52492
|
+
if (imageCount > 0) {
|
|
52493
|
+
return `${text13}
|
|
52494
|
+
|
|
52495
|
+
[System: The original request included ${imageCount} image(s). Use the image context already provided in this conversation.]`;
|
|
52496
|
+
}
|
|
52497
|
+
return text13;
|
|
52498
|
+
}
|
|
52499
|
+
if (imageCount > 0) {
|
|
52500
|
+
return `[System: Retry the previous image-based user request (${imageCount} image(s)). Use the existing image context in the conversation and do not repeat the same failed action.]`;
|
|
52501
|
+
}
|
|
52502
|
+
return null;
|
|
52503
|
+
};
|
|
52504
|
+
const showRecoveryAlternatives = () => {
|
|
52505
|
+
console.log(chalk.yellow(" Choose how to continue:"));
|
|
52506
|
+
console.log(chalk.dim(" 1. /provider \u2192 switch provider"));
|
|
52507
|
+
console.log(chalk.dim(" 2. /model \u2192 switch model"));
|
|
52508
|
+
console.log(chalk.dim(" 3. Retry with a narrower scope/task"));
|
|
52509
|
+
console.log(chalk.dim(" 4. If needed, share constraints so Coco can adapt strategy"));
|
|
52510
|
+
if (!enableAutoSwitchProvider) {
|
|
52511
|
+
console.log(chalk.dim(" 5. (Optional) enable `agent.enableAutoSwitchProvider` in config"));
|
|
52512
|
+
}
|
|
52513
|
+
};
|
|
52514
|
+
const getAutoSwitchCandidates = (current) => {
|
|
52515
|
+
const ordered = [];
|
|
52516
|
+
const push = (p45) => {
|
|
52517
|
+
if (p45 !== current && !ordered.includes(p45)) ordered.push(p45);
|
|
52518
|
+
};
|
|
52519
|
+
if (current === "openai") {
|
|
52520
|
+
push("codex");
|
|
52521
|
+
push("kimi");
|
|
52522
|
+
push("openrouter");
|
|
52523
|
+
} else if (current === "codex") {
|
|
52524
|
+
push("openai");
|
|
52525
|
+
push("openrouter");
|
|
52526
|
+
push("anthropic");
|
|
52527
|
+
} else if (current === "anthropic") {
|
|
52528
|
+
push("openai");
|
|
52529
|
+
push("codex");
|
|
52530
|
+
push("gemini");
|
|
52531
|
+
} else if (current === "gemini") {
|
|
52532
|
+
push("openai");
|
|
52533
|
+
push("codex");
|
|
52534
|
+
push("anthropic");
|
|
52535
|
+
} else if (current === "kimi") {
|
|
52536
|
+
push("openai");
|
|
52537
|
+
push("codex");
|
|
52538
|
+
push("openrouter");
|
|
52539
|
+
}
|
|
52540
|
+
const genericOrder = [
|
|
52541
|
+
"codex",
|
|
52542
|
+
"openai",
|
|
52543
|
+
"anthropic",
|
|
52544
|
+
"gemini",
|
|
52545
|
+
"kimi",
|
|
52546
|
+
"openrouter",
|
|
52547
|
+
"deepseek",
|
|
52548
|
+
"groq",
|
|
52549
|
+
"mistral",
|
|
52550
|
+
"together",
|
|
52551
|
+
"qwen",
|
|
52552
|
+
"lmstudio",
|
|
52553
|
+
"ollama"
|
|
52554
|
+
];
|
|
52555
|
+
for (const p45 of genericOrder) push(p45);
|
|
52556
|
+
return ordered;
|
|
52557
|
+
};
|
|
52558
|
+
const attemptAutoProviderSwitch = async (reason, originalMessage) => {
|
|
52559
|
+
if (!originalMessage) return false;
|
|
52560
|
+
const currentType = session.config.provider.type;
|
|
52561
|
+
const candidates = getAutoSwitchCandidates(currentType);
|
|
52562
|
+
for (const candidate of candidates) {
|
|
52563
|
+
const edge = `${currentType}->${candidate}`;
|
|
52564
|
+
if (autoSwitchHistory.has(edge)) continue;
|
|
52565
|
+
try {
|
|
52566
|
+
const nextInternalId = getInternalProviderId(candidate);
|
|
52567
|
+
const nextProvider = await createProvider(nextInternalId, {
|
|
52568
|
+
maxTokens: session.config.provider.maxTokens
|
|
52569
|
+
});
|
|
52570
|
+
const ok = await nextProvider.isAvailable();
|
|
52571
|
+
if (!ok) {
|
|
52572
|
+
autoSwitchHistory.add(edge);
|
|
52573
|
+
continue;
|
|
52574
|
+
}
|
|
52575
|
+
provider = nextProvider;
|
|
52576
|
+
session.config.provider.type = candidate;
|
|
52577
|
+
session.config.provider.model = getDefaultModel(candidate);
|
|
52578
|
+
setAgentProvider(provider);
|
|
52579
|
+
initializeContextManager(session, provider);
|
|
52580
|
+
llmClassifier = createLLMClassifier2(provider);
|
|
52581
|
+
autoSwitchHistory.add(edge);
|
|
52582
|
+
console.log(
|
|
52583
|
+
chalk.cyan(
|
|
52584
|
+
` \u21BA Auto-switched provider: ${currentType} \u2192 ${candidate} (${reason.slice(0, 80)})`
|
|
52585
|
+
)
|
|
52586
|
+
);
|
|
52587
|
+
return true;
|
|
52588
|
+
} catch {
|
|
52589
|
+
autoSwitchHistory.add(edge);
|
|
52590
|
+
}
|
|
52591
|
+
}
|
|
52592
|
+
return false;
|
|
52593
|
+
};
|
|
51751
52594
|
while (true) {
|
|
51752
52595
|
let autoInput = null;
|
|
51753
52596
|
if (pendingQueuedMessages.length > 0) {
|
|
@@ -51867,6 +52710,7 @@ ${imagePrompts}`.trim() : imagePrompts;
|
|
|
51867
52710
|
agentMessage = input ?? "";
|
|
51868
52711
|
}
|
|
51869
52712
|
const originalUserMessage = typeof agentMessage === "string" ? agentMessage : null;
|
|
52713
|
+
const replayUserMessage = buildReplayMessage(agentMessage);
|
|
51870
52714
|
if (session.skillRegistry && session.skillRegistry.config.autoActivate !== false && typeof agentMessage === "string" && agentMessage.length > 0) {
|
|
51871
52715
|
const matches = session.skillRegistry.findRelevantSkills(agentMessage, 3, 0.4);
|
|
51872
52716
|
const mdMatches = matches.filter((m) => m.skill.kind === "markdown");
|
|
@@ -52141,12 +52985,12 @@ ${imagePrompts}`.trim() : imagePrompts;
|
|
|
52141
52985
|
"\n\n## The user interrupted and modified the task:",
|
|
52142
52986
|
`- ${modParts}`,
|
|
52143
52987
|
toolSummary,
|
|
52144
|
-
`Apply the user's modification to the original task: "${
|
|
52988
|
+
`Apply the user's modification to the original task: "${replayUserMessage || ""}"`
|
|
52145
52989
|
].join("\n");
|
|
52146
52990
|
pendingInterruptionContext = ctx;
|
|
52147
52991
|
pendingModificationPreview = modParts;
|
|
52148
|
-
if (
|
|
52149
|
-
pendingQueuedMessages = [
|
|
52992
|
+
if (replayUserMessage) {
|
|
52993
|
+
pendingQueuedMessages = [replayUserMessage, ...turnQueuedMessages];
|
|
52150
52994
|
} else {
|
|
52151
52995
|
pendingQueuedMessages = turnQueuedMessages;
|
|
52152
52996
|
}
|
|
@@ -52181,27 +53025,35 @@ ${imagePrompts}`.trim() : imagePrompts;
|
|
|
52181
53025
|
}
|
|
52182
53026
|
if (result.error) {
|
|
52183
53027
|
session.messages.length = preCallMessageLength;
|
|
52184
|
-
if (
|
|
53028
|
+
if (replayUserMessage !== null && consecutiveErrors < MAX_CONSECUTIVE_ERRORS && !isNonRetryableProviderError(new Error(result.error))) {
|
|
52185
53029
|
consecutiveErrors++;
|
|
52186
53030
|
const humanized = humanizeProviderError(new Error(result.error));
|
|
52187
53031
|
renderError(humanized);
|
|
53032
|
+
let switched = false;
|
|
53033
|
+
if (enableAutoSwitchProvider && consecutiveErrors >= AUTO_SWITCH_THRESHOLD) {
|
|
53034
|
+
switched = await attemptAutoProviderSwitch(humanized, replayUserMessage);
|
|
53035
|
+
} else if (!enableAutoSwitchProvider && consecutiveErrors >= AUTO_SWITCH_THRESHOLD) {
|
|
53036
|
+
console.log(
|
|
53037
|
+
chalk.dim(
|
|
53038
|
+
" Tip: repeated provider errors detected. Use /provider, or enable `agent.enableAutoSwitchProvider`."
|
|
53039
|
+
)
|
|
53040
|
+
);
|
|
53041
|
+
}
|
|
52188
53042
|
console.log(
|
|
52189
53043
|
chalk.dim(
|
|
52190
53044
|
` \u21BB Retrying automatically (attempt ${consecutiveErrors}/${MAX_CONSECUTIVE_ERRORS})\u2026`
|
|
52191
53045
|
)
|
|
52192
53046
|
);
|
|
52193
|
-
const recoveryPrefix = `[System:
|
|
53047
|
+
const recoveryPrefix = (switched ? `[System: Provider auto-switched to "${session.config.provider.type}" after repeated failures. Adapt your strategy to this provider and continue.]
|
|
53048
|
+
|
|
53049
|
+
` : "") + `[System: The previous attempt failed with: "${humanized}". Please try a different approach, tool, or method to complete the task. Do NOT repeat the exact same action that caused the error.]
|
|
52194
53050
|
|
|
52195
53051
|
`;
|
|
52196
|
-
pendingQueuedMessages = [recoveryPrefix +
|
|
53052
|
+
pendingQueuedMessages = [recoveryPrefix + replayUserMessage];
|
|
52197
53053
|
} else {
|
|
52198
53054
|
renderError(result.error);
|
|
52199
|
-
console.log(
|
|
52200
|
-
|
|
52201
|
-
);
|
|
52202
|
-
console.log(
|
|
52203
|
-
chalk.dim(" Tip: Try /provider or /model to switch, then rephrase and retry.")
|
|
52204
|
-
);
|
|
53055
|
+
console.log(chalk.dim(" Automatic recovery stopped for this turn."));
|
|
53056
|
+
showRecoveryAlternatives();
|
|
52205
53057
|
consecutiveErrors = 0;
|
|
52206
53058
|
}
|
|
52207
53059
|
console.log();
|
|
@@ -52365,39 +53217,55 @@ ${imagePrompts}`.trim() : imagePrompts;
|
|
|
52365
53217
|
console.log(chalk.dim(" \u2022 Check your subscription status and billing"));
|
|
52366
53218
|
console.log(chalk.dim(" \u2022 Try a different provider: /provider"));
|
|
52367
53219
|
console.log(chalk.dim(" \u2022 Switch to a different model: /model"));
|
|
53220
|
+
if (!enableAutoSwitchProvider) {
|
|
53221
|
+
console.log(
|
|
53222
|
+
chalk.dim(" \u2022 Optional: enable `agent.enableAutoSwitchProvider` for auto-failover")
|
|
53223
|
+
);
|
|
53224
|
+
}
|
|
53225
|
+
showRecoveryAlternatives();
|
|
52368
53226
|
console.log();
|
|
52369
53227
|
continue;
|
|
52370
53228
|
}
|
|
52371
|
-
if (
|
|
53229
|
+
if (replayUserMessage !== null && consecutiveErrors < MAX_CONSECUTIVE_ERRORS && !isNonRetryableProviderError(error)) {
|
|
52372
53230
|
consecutiveErrors++;
|
|
52373
53231
|
session.messages.length = preCallMessageLength;
|
|
52374
53232
|
const humanized = humanizeProviderError(error);
|
|
52375
53233
|
renderError(humanized);
|
|
53234
|
+
let switched = false;
|
|
53235
|
+
if (enableAutoSwitchProvider && consecutiveErrors >= AUTO_SWITCH_THRESHOLD) {
|
|
53236
|
+
switched = await attemptAutoProviderSwitch(humanized, replayUserMessage);
|
|
53237
|
+
} else if (!enableAutoSwitchProvider && consecutiveErrors >= AUTO_SWITCH_THRESHOLD) {
|
|
53238
|
+
console.log(
|
|
53239
|
+
chalk.dim(
|
|
53240
|
+
" Tip: repeated provider errors detected. Use /provider, or enable `agent.enableAutoSwitchProvider`."
|
|
53241
|
+
)
|
|
53242
|
+
);
|
|
53243
|
+
}
|
|
52376
53244
|
console.log(
|
|
52377
53245
|
chalk.dim(
|
|
52378
53246
|
` \u21BB Retrying automatically (attempt ${consecutiveErrors}/${MAX_CONSECUTIVE_ERRORS})\u2026`
|
|
52379
53247
|
)
|
|
52380
53248
|
);
|
|
52381
|
-
const recoveryPrefix = `[System:
|
|
53249
|
+
const recoveryPrefix = (switched ? `[System: Provider auto-switched to "${session.config.provider.type}" after repeated failures. Adapt your strategy to this provider and continue.]
|
|
53250
|
+
|
|
53251
|
+
` : "") + `[System: The previous attempt failed with the following error: "${humanized}". Please try a different approach, tool, or method to complete the task. Do NOT repeat the exact same action that caused the error.]
|
|
52382
53252
|
|
|
52383
53253
|
`;
|
|
52384
|
-
pendingQueuedMessages = [recoveryPrefix +
|
|
53254
|
+
pendingQueuedMessages = [recoveryPrefix + replayUserMessage];
|
|
52385
53255
|
continue;
|
|
52386
53256
|
}
|
|
52387
53257
|
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
52388
53258
|
consecutiveErrors = 0;
|
|
52389
53259
|
session.messages.length = preCallMessageLength;
|
|
52390
53260
|
renderError(errorMsg);
|
|
52391
|
-
console.log(chalk.dim(" Recovery
|
|
52392
|
-
|
|
52393
|
-
chalk.dim(" Tip: Try /provider or /model to switch, then rephrase and retry.")
|
|
52394
|
-
);
|
|
53261
|
+
console.log(chalk.dim(" Recovery exhausted after multiple attempts."));
|
|
53262
|
+
showRecoveryAlternatives();
|
|
52395
53263
|
continue;
|
|
52396
53264
|
}
|
|
52397
53265
|
session.messages.length = preCallMessageLength;
|
|
52398
53266
|
consecutiveErrors = 0;
|
|
52399
53267
|
renderError(errorMsg);
|
|
52400
|
-
|
|
53268
|
+
showRecoveryAlternatives();
|
|
52401
53269
|
} finally {
|
|
52402
53270
|
clearSpinner();
|
|
52403
53271
|
if (originalSystemPrompt !== void 0) {
|