codex-subagent-kit 0.1.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 +123 -0
- package/builtin_catalog/categories/01-core-development/README.md +18 -0
- package/builtin_catalog/categories/01-core-development/api-designer.toml +43 -0
- package/builtin_catalog/categories/01-core-development/backend-developer.toml +42 -0
- package/builtin_catalog/categories/01-core-development/code-mapper.toml +35 -0
- package/builtin_catalog/categories/01-core-development/electron-pro.toml +40 -0
- package/builtin_catalog/categories/01-core-development/frontend-developer.toml +41 -0
- package/builtin_catalog/categories/01-core-development/fullstack-developer.toml +39 -0
- package/builtin_catalog/categories/01-core-development/graphql-architect.toml +46 -0
- package/builtin_catalog/categories/01-core-development/microservices-architect.toml +41 -0
- package/builtin_catalog/categories/01-core-development/mobile-developer.toml +35 -0
- package/builtin_catalog/categories/01-core-development/ui-designer.toml +35 -0
- package/builtin_catalog/categories/01-core-development/ui-fixer.toml +33 -0
- package/builtin_catalog/categories/01-core-development/websocket-engineer.toml +35 -0
- package/builtin_catalog/categories/02-language-specialists/README.md +33 -0
- package/builtin_catalog/categories/02-language-specialists/angular-architect.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/cpp-pro.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/csharp-developer.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/django-developer.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/dotnet-core-expert.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/dotnet-framework-4.8-expert.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/elixir-expert.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/erlang-expert.toml +49 -0
- package/builtin_catalog/categories/02-language-specialists/flutter-expert.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/golang-pro.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/java-architect.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/javascript-pro.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/kotlin-specialist.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/laravel-specialist.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/nextjs-developer.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/php-pro.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/powershell-5.1-expert.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/powershell-7-expert.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/python-pro.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/rails-expert.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/react-specialist.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/rust-engineer.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/spring-boot-engineer.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/sql-pro.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/swift-expert.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/typescript-pro.toml +41 -0
- package/builtin_catalog/categories/02-language-specialists/vue-expert.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/README.md +22 -0
- package/builtin_catalog/categories/03-infrastructure/azure-infra-engineer.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/cloud-architect.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/database-administrator.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/deployment-engineer.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/devops-engineer.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/devops-incident-responder.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/docker-expert.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/incident-responder.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/kubernetes-specialist.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/network-engineer.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/platform-engineer.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/security-engineer.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/sre-engineer.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/terraform-engineer.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/terragrunt-expert.toml +41 -0
- package/builtin_catalog/categories/03-infrastructure/windows-infra-admin.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/README.md +22 -0
- package/builtin_catalog/categories/04-quality-security/accessibility-tester.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/ad-security-reviewer.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/architect-reviewer.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/browser-debugger.toml +45 -0
- package/builtin_catalog/categories/04-quality-security/chaos-engineer.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/code-reviewer.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/compliance-auditor.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/debugger.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/error-detective.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/penetration-tester.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/performance-engineer.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/powershell-security-hardening.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/qa-expert.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/reviewer.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/security-auditor.toml +41 -0
- package/builtin_catalog/categories/04-quality-security/test-automator.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/README.md +18 -0
- package/builtin_catalog/categories/05-data-ai/ai-engineer.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/data-analyst.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/data-engineer.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/data-scientist.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/database-optimizer.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/llm-architect.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/machine-learning-engineer.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/ml-engineer.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/mlops-engineer.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/nlp-engineer.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/postgres-pro.toml +41 -0
- package/builtin_catalog/categories/05-data-ai/prompt-engineer.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/README.md +19 -0
- package/builtin_catalog/categories/06-developer-experience/build-engineer.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/cli-developer.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/dependency-manager.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/documentation-engineer.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/dx-optimizer.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/git-workflow-manager.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/legacy-modernizer.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/mcp-developer.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/powershell-module-architect.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/powershell-ui-architect.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/refactoring-specialist.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/slack-expert.toml +41 -0
- package/builtin_catalog/categories/06-developer-experience/tooling-engineer.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/README.md +18 -0
- package/builtin_catalog/categories/07-specialized-domains/api-documenter.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/blockchain-developer.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/embedded-systems.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/fintech-engineer.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/game-developer.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/iot-engineer.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/m365-admin.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/mobile-app-developer.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/payment-integration.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/quant-analyst.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/risk-manager.toml +41 -0
- package/builtin_catalog/categories/07-specialized-domains/seo-specialist.toml +41 -0
- package/builtin_catalog/categories/08-business-product/README.md +17 -0
- package/builtin_catalog/categories/08-business-product/business-analyst.toml +41 -0
- package/builtin_catalog/categories/08-business-product/content-marketer.toml +41 -0
- package/builtin_catalog/categories/08-business-product/customer-success-manager.toml +41 -0
- package/builtin_catalog/categories/08-business-product/legal-advisor.toml +41 -0
- package/builtin_catalog/categories/08-business-product/product-manager.toml +41 -0
- package/builtin_catalog/categories/08-business-product/project-manager.toml +41 -0
- package/builtin_catalog/categories/08-business-product/sales-engineer.toml +41 -0
- package/builtin_catalog/categories/08-business-product/scrum-master.toml +41 -0
- package/builtin_catalog/categories/08-business-product/technical-writer.toml +41 -0
- package/builtin_catalog/categories/08-business-product/ux-researcher.toml +41 -0
- package/builtin_catalog/categories/08-business-product/wordpress-master.toml +41 -0
- package/builtin_catalog/categories/09-meta-orchestration/README.md +16 -0
- package/builtin_catalog/categories/09-meta-orchestration/agent-installer.toml +41 -0
- package/builtin_catalog/categories/09-meta-orchestration/agent-organizer.toml +41 -0
- package/builtin_catalog/categories/09-meta-orchestration/context-manager.toml +41 -0
- package/builtin_catalog/categories/09-meta-orchestration/error-coordinator.toml +41 -0
- package/builtin_catalog/categories/09-meta-orchestration/it-ops-orchestrator.toml +41 -0
- package/builtin_catalog/categories/09-meta-orchestration/knowledge-synthesizer.toml +41 -0
- package/builtin_catalog/categories/09-meta-orchestration/multi-agent-coordinator.toml +41 -0
- package/builtin_catalog/categories/09-meta-orchestration/performance-monitor.toml +41 -0
- package/builtin_catalog/categories/09-meta-orchestration/task-distributor.toml +41 -0
- package/builtin_catalog/categories/09-meta-orchestration/workflow-orchestrator.toml +41 -0
- package/builtin_catalog/categories/10-research-analysis/README.md +13 -0
- package/builtin_catalog/categories/10-research-analysis/competitive-analyst.toml +41 -0
- package/builtin_catalog/categories/10-research-analysis/data-researcher.toml +41 -0
- package/builtin_catalog/categories/10-research-analysis/docs-researcher.toml +44 -0
- package/builtin_catalog/categories/10-research-analysis/market-researcher.toml +41 -0
- package/builtin_catalog/categories/10-research-analysis/research-analyst.toml +41 -0
- package/builtin_catalog/categories/10-research-analysis/search-specialist.toml +41 -0
- package/builtin_catalog/categories/10-research-analysis/trend-analyst.toml +41 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +1550 -0
- package/dist/index.d.ts +218 -0
- package/dist/index.js +1665 -0
- package/package.json +52 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1550 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/cli.ts
|
|
32
|
+
var cli_exports = {};
|
|
33
|
+
__export(cli_exports, {
|
|
34
|
+
buildProgram: () => buildProgram,
|
|
35
|
+
main: () => main
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(cli_exports);
|
|
38
|
+
var import_node_path8 = require("path");
|
|
39
|
+
var import_commander = require("commander");
|
|
40
|
+
|
|
41
|
+
// src/catalog.ts
|
|
42
|
+
var import_node_fs2 = require("fs");
|
|
43
|
+
var import_node_path2 = require("path");
|
|
44
|
+
var TOML = __toESM(require("@iarna/toml"));
|
|
45
|
+
|
|
46
|
+
// src/paths.ts
|
|
47
|
+
var import_node_fs = require("fs");
|
|
48
|
+
var import_node_os = require("os");
|
|
49
|
+
var import_node_path = require("path");
|
|
50
|
+
var TOOL_DIR_NAME = "subagent-kit";
|
|
51
|
+
var CATEGORY_OVERRIDE_KEY = "codex_subagent_kit_category";
|
|
52
|
+
var DEFAULT_SYNC_SOURCE_NAME = "voltagent";
|
|
53
|
+
function resolveProjectToolDir(projectRoot) {
|
|
54
|
+
return (0, import_node_path.resolve)(projectRoot, ".codex", TOOL_DIR_NAME);
|
|
55
|
+
}
|
|
56
|
+
function resolveGlobalToolDir(home = (0, import_node_os.homedir)()) {
|
|
57
|
+
return (0, import_node_path.resolve)(home, ".codex", TOOL_DIR_NAME);
|
|
58
|
+
}
|
|
59
|
+
function resolveProjectCatalogDir(projectRoot) {
|
|
60
|
+
return (0, import_node_path.resolve)(resolveProjectToolDir(projectRoot), "catalog", "categories");
|
|
61
|
+
}
|
|
62
|
+
function resolveGlobalCatalogDir(home) {
|
|
63
|
+
return (0, import_node_path.resolve)(resolveGlobalToolDir(home), "catalog", "categories");
|
|
64
|
+
}
|
|
65
|
+
function resolveProjectSourcesDir(projectRoot) {
|
|
66
|
+
return (0, import_node_path.resolve)(resolveProjectToolDir(projectRoot), "sources");
|
|
67
|
+
}
|
|
68
|
+
function resolveGlobalSourcesDir(home) {
|
|
69
|
+
return (0, import_node_path.resolve)(resolveGlobalToolDir(home), "sources");
|
|
70
|
+
}
|
|
71
|
+
function resolveProjectSourceCategoriesDir(projectRoot, sourceName = DEFAULT_SYNC_SOURCE_NAME) {
|
|
72
|
+
return (0, import_node_path.resolve)(resolveProjectSourcesDir(projectRoot), sourceName, "categories");
|
|
73
|
+
}
|
|
74
|
+
function resolveGlobalSourceCategoriesDir(sourceName = DEFAULT_SYNC_SOURCE_NAME, home) {
|
|
75
|
+
return (0, import_node_path.resolve)(resolveGlobalSourcesDir(home), sourceName, "categories");
|
|
76
|
+
}
|
|
77
|
+
function resolveSourceCategoryDirs(root) {
|
|
78
|
+
try {
|
|
79
|
+
return normalizeCatalogRoots(
|
|
80
|
+
(0, import_node_fs.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path.resolve)(root, entry.name, "categories")).filter((categoriesRoot) => (0, import_node_fs.existsSync)(categoriesRoot))
|
|
81
|
+
);
|
|
82
|
+
} catch {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function resolveProjectSourceCategoriesDirs(projectRoot) {
|
|
87
|
+
return resolveSourceCategoryDirs(resolveProjectSourcesDir(projectRoot));
|
|
88
|
+
}
|
|
89
|
+
function resolveGlobalSourceCategoriesDirs(home) {
|
|
90
|
+
return resolveSourceCategoryDirs(resolveGlobalSourcesDir(home));
|
|
91
|
+
}
|
|
92
|
+
function resolveProjectAgentsDir(projectRoot) {
|
|
93
|
+
return (0, import_node_path.resolve)(projectRoot, ".codex", "agents");
|
|
94
|
+
}
|
|
95
|
+
function resolveGlobalAgentsDir(home = (0, import_node_os.homedir)()) {
|
|
96
|
+
return (0, import_node_path.resolve)(home, ".codex", "agents");
|
|
97
|
+
}
|
|
98
|
+
function normalizeCatalogRoots(roots) {
|
|
99
|
+
if (!roots) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
return roots.filter((root) => root.trim()).map((root) => (0, import_node_path.resolve)(root));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/catalog.ts
|
|
106
|
+
var BUILTIN_CATEGORIES_DIR = (0, import_node_path2.resolve)(__dirname, "..", "builtin_catalog", "categories");
|
|
107
|
+
var IMPORTED_AGENTS_CATEGORY = {
|
|
108
|
+
key: "imported-agents",
|
|
109
|
+
title: "Imported & External",
|
|
110
|
+
description: "Portable TOML agent definitions discovered from project/global agent directories."
|
|
111
|
+
};
|
|
112
|
+
var builtinCatalogCache = null;
|
|
113
|
+
function readToml(path) {
|
|
114
|
+
return TOML.parse((0, import_node_fs2.readFileSync)(path, "utf8"));
|
|
115
|
+
}
|
|
116
|
+
function listDirectories(path) {
|
|
117
|
+
if (!(0, import_node_fs2.existsSync)(path)) {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
return (0, import_node_fs2.readdirSync)(path, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path2.resolve)(path, entry.name)).sort();
|
|
121
|
+
}
|
|
122
|
+
function listTomlFiles(path) {
|
|
123
|
+
if (!(0, import_node_fs2.existsSync)(path)) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
return (0, import_node_fs2.readdirSync)(path, { withFileTypes: true }).filter((entry) => entry.isFile() && (0, import_node_path2.extname)(entry.name) === ".toml").map((entry) => (0, import_node_path2.resolve)(path, entry.name)).sort();
|
|
127
|
+
}
|
|
128
|
+
function categoryKeyFromDir(directoryName) {
|
|
129
|
+
const match = directoryName.match(/^(\d+)-(.*)$/);
|
|
130
|
+
return match ? match[2] : directoryName;
|
|
131
|
+
}
|
|
132
|
+
function fallbackTitleFromKey(key) {
|
|
133
|
+
return key.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
134
|
+
}
|
|
135
|
+
function parseCategoryDir(categoryDir) {
|
|
136
|
+
const key = categoryKeyFromDir((0, import_node_path2.basename)(categoryDir));
|
|
137
|
+
let title = fallbackTitleFromKey(key);
|
|
138
|
+
let description = title;
|
|
139
|
+
const readmePath = (0, import_node_path2.resolve)(categoryDir, "README.md");
|
|
140
|
+
if (!(0, import_node_fs2.existsSync)(readmePath)) {
|
|
141
|
+
return { key, title, description };
|
|
142
|
+
}
|
|
143
|
+
const lines = (0, import_node_fs2.readFileSync)(readmePath, "utf8").split(/\r?\n/).map((line) => line.trim());
|
|
144
|
+
for (const line of lines) {
|
|
145
|
+
if (!line.startsWith("#")) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const parsedTitle = line.replace(/^#+\s*/, "").trim();
|
|
149
|
+
const numericTitle = parsedTitle.match(/^\d+\.\s+(.*)$/);
|
|
150
|
+
title = numericTitle ? numericTitle[1].trim() : parsedTitle || title;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
for (const line of lines) {
|
|
154
|
+
if (!line || line.startsWith("#") || line === "Included agents:") {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
description = line;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
return { key, title, description };
|
|
161
|
+
}
|
|
162
|
+
function readInstructions(data) {
|
|
163
|
+
const developerInstructions = data.developer_instructions;
|
|
164
|
+
if (typeof developerInstructions === "string" && developerInstructions.trim()) {
|
|
165
|
+
return developerInstructions.trimEnd();
|
|
166
|
+
}
|
|
167
|
+
const instructions = data.instructions;
|
|
168
|
+
if (typeof instructions === "string" && instructions.trim()) {
|
|
169
|
+
return instructions.trimEnd();
|
|
170
|
+
}
|
|
171
|
+
if (instructions && typeof instructions === "object" && "text" in instructions && typeof instructions.text === "string" && instructions.text.trim()) {
|
|
172
|
+
return instructions.text.trimEnd();
|
|
173
|
+
}
|
|
174
|
+
throw new Error("missing instructions text");
|
|
175
|
+
}
|
|
176
|
+
function requiredString(data, key) {
|
|
177
|
+
const value = data[key];
|
|
178
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
179
|
+
throw new Error(`missing required string field: ${key}`);
|
|
180
|
+
}
|
|
181
|
+
return value.trim();
|
|
182
|
+
}
|
|
183
|
+
function parseAgentFile(path, source, inheritedCategory) {
|
|
184
|
+
const data = readToml(path);
|
|
185
|
+
const explicitCategory = data[CATEGORY_OVERRIDE_KEY];
|
|
186
|
+
if (explicitCategory !== void 0 && (typeof explicitCategory !== "string" || !explicitCategory.trim())) {
|
|
187
|
+
throw new Error(`invalid ${CATEGORY_OVERRIDE_KEY}`);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
key: (0, import_node_path2.basename)(path, ".toml"),
|
|
191
|
+
category: typeof explicitCategory === "string" && explicitCategory.trim() ? explicitCategory.trim() : inheritedCategory ?? IMPORTED_AGENTS_CATEGORY.key,
|
|
192
|
+
name: requiredString(data, "name"),
|
|
193
|
+
description: requiredString(data, "description"),
|
|
194
|
+
model: requiredString(data, "model"),
|
|
195
|
+
reasoningEffort: requiredString(data, "model_reasoning_effort"),
|
|
196
|
+
sandboxMode: requiredString(data, "sandbox_mode"),
|
|
197
|
+
developerInstructions: readInstructions(data),
|
|
198
|
+
source,
|
|
199
|
+
definitionPath: path
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function loadCatalogRoot(root, source) {
|
|
203
|
+
const categories = /* @__PURE__ */ new Map();
|
|
204
|
+
const agents = /* @__PURE__ */ new Map();
|
|
205
|
+
if (!(0, import_node_fs2.existsSync)(root)) {
|
|
206
|
+
return { categories, agents };
|
|
207
|
+
}
|
|
208
|
+
for (const categoryDir of listDirectories(root)) {
|
|
209
|
+
const category = parseCategoryDir(categoryDir);
|
|
210
|
+
categories.set(category.key, category);
|
|
211
|
+
for (const file of listTomlFiles(categoryDir)) {
|
|
212
|
+
const agent = parseAgentFile(file, source, category.key);
|
|
213
|
+
agents.set(agent.key, agent);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return { categories, agents };
|
|
217
|
+
}
|
|
218
|
+
function getBuiltinCatalog() {
|
|
219
|
+
if (builtinCatalogCache) {
|
|
220
|
+
return builtinCatalogCache;
|
|
221
|
+
}
|
|
222
|
+
const builtin = loadCatalogRoot(BUILTIN_CATEGORIES_DIR, "builtin");
|
|
223
|
+
if (builtin.categories.size === 0) {
|
|
224
|
+
throw new Error(`builtin catalog is empty: ${BUILTIN_CATEGORIES_DIR}`);
|
|
225
|
+
}
|
|
226
|
+
builtinCatalogCache = builtin;
|
|
227
|
+
return builtin;
|
|
228
|
+
}
|
|
229
|
+
function mergeCatalogInto(target, incoming) {
|
|
230
|
+
for (const [key, category] of incoming.categories) {
|
|
231
|
+
target.categories.set(key, category);
|
|
232
|
+
}
|
|
233
|
+
for (const [key, agent] of incoming.agents) {
|
|
234
|
+
target.agents.set(key, agent);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function loadExternalAgents(directory, source, inheritedAgents) {
|
|
238
|
+
if (!(0, import_node_fs2.existsSync)(directory)) {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
const loaded = [];
|
|
242
|
+
for (const file of listTomlFiles(directory)) {
|
|
243
|
+
try {
|
|
244
|
+
const inheritedCategory = inheritedAgents.get((0, import_node_path2.basename)(file, ".toml"))?.category;
|
|
245
|
+
loaded.push(parseAgentFile(file, source, inheritedCategory));
|
|
246
|
+
} catch {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return loaded;
|
|
251
|
+
}
|
|
252
|
+
function buildCatalogState(options = {}) {
|
|
253
|
+
const includeProject = options.includeProject ?? false;
|
|
254
|
+
const includeGlobal = options.includeGlobal ?? false;
|
|
255
|
+
const homeDir = options.homeDir;
|
|
256
|
+
const catalogRoots = normalizeCatalogRoots(options.catalogRoots);
|
|
257
|
+
const builtin = getBuiltinCatalog();
|
|
258
|
+
const state = {
|
|
259
|
+
categories: new Map(builtin.categories),
|
|
260
|
+
agents: new Map(builtin.agents)
|
|
261
|
+
};
|
|
262
|
+
if (includeGlobal) {
|
|
263
|
+
for (const root of resolveGlobalSourceCategoriesDirs(homeDir)) {
|
|
264
|
+
mergeCatalogInto(state, loadCatalogRoot(root, `global-source:${(0, import_node_path2.basename)((0, import_node_path2.resolve)(root, ".."))}`));
|
|
265
|
+
}
|
|
266
|
+
mergeCatalogInto(state, loadCatalogRoot(resolveGlobalCatalogDir(homeDir), "global-catalog"));
|
|
267
|
+
}
|
|
268
|
+
if (includeProject && options.projectRoot) {
|
|
269
|
+
for (const root of resolveProjectSourceCategoriesDirs(options.projectRoot)) {
|
|
270
|
+
mergeCatalogInto(state, loadCatalogRoot(root, `project-source:${(0, import_node_path2.basename)((0, import_node_path2.resolve)(root, ".."))}`));
|
|
271
|
+
}
|
|
272
|
+
mergeCatalogInto(
|
|
273
|
+
state,
|
|
274
|
+
loadCatalogRoot(resolveProjectCatalogDir(options.projectRoot), "project-catalog")
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
for (const root of catalogRoots) {
|
|
278
|
+
mergeCatalogInto(state, loadCatalogRoot(root, "catalog-root"));
|
|
279
|
+
}
|
|
280
|
+
state.categories.set(IMPORTED_AGENTS_CATEGORY.key, IMPORTED_AGENTS_CATEGORY);
|
|
281
|
+
if (includeGlobal) {
|
|
282
|
+
for (const agent of loadExternalAgents(
|
|
283
|
+
resolveGlobalAgentsDir(homeDir),
|
|
284
|
+
"global",
|
|
285
|
+
state.agents
|
|
286
|
+
)) {
|
|
287
|
+
state.agents.set(agent.key, agent);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (includeProject && options.projectRoot) {
|
|
291
|
+
for (const agent of loadExternalAgents(
|
|
292
|
+
resolveProjectAgentsDir(options.projectRoot),
|
|
293
|
+
"project",
|
|
294
|
+
state.agents
|
|
295
|
+
)) {
|
|
296
|
+
state.agents.set(agent.key, agent);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return state;
|
|
300
|
+
}
|
|
301
|
+
function getAgentMap(options = {}) {
|
|
302
|
+
return buildCatalogState(options).agents;
|
|
303
|
+
}
|
|
304
|
+
function getCategories(options = {}) {
|
|
305
|
+
const state = buildCatalogState(options);
|
|
306
|
+
const usedCategories = new Set([...state.agents.values()].map((agent) => agent.category));
|
|
307
|
+
return [...state.categories.values()].filter((category) => usedCategories.has(category.key));
|
|
308
|
+
}
|
|
309
|
+
function getAgents(options = {}) {
|
|
310
|
+
const categories = getCategories(options);
|
|
311
|
+
const agents = [...getAgentMap(options).values()];
|
|
312
|
+
const categoryOrder = new Map(categories.map((category, index) => [category.key, index]));
|
|
313
|
+
return agents.sort((left, right) => {
|
|
314
|
+
const leftOrder = categoryOrder.get(left.category) ?? categoryOrder.size;
|
|
315
|
+
const rightOrder = categoryOrder.get(right.category) ?? categoryOrder.size;
|
|
316
|
+
if (leftOrder !== rightOrder) {
|
|
317
|
+
return leftOrder - rightOrder;
|
|
318
|
+
}
|
|
319
|
+
const byName = left.name.localeCompare(right.name, void 0, { sensitivity: "base" });
|
|
320
|
+
if (byName !== 0) {
|
|
321
|
+
return byName;
|
|
322
|
+
}
|
|
323
|
+
return left.key.localeCompare(right.key);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
function getAgentsByCategory(categoryKeys, options = {}) {
|
|
327
|
+
const agents = getAgents(options);
|
|
328
|
+
if (!categoryKeys || categoryKeys.size === 0) {
|
|
329
|
+
return agents;
|
|
330
|
+
}
|
|
331
|
+
return agents.filter((agent) => categoryKeys.has(agent.category));
|
|
332
|
+
}
|
|
333
|
+
function renderCatalogOutput(options = {}) {
|
|
334
|
+
const categories = getCategories(options);
|
|
335
|
+
const agents = getAgents(options);
|
|
336
|
+
const lines = [];
|
|
337
|
+
for (const category of categories) {
|
|
338
|
+
lines.push(`[${category.title}]`);
|
|
339
|
+
lines.push(` key: ${category.key}`);
|
|
340
|
+
lines.push(` description: ${category.description}`);
|
|
341
|
+
for (const agent of agents) {
|
|
342
|
+
if (agent.category !== category.key) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
const sourceSuffix = agent.source === "builtin" ? "" : ` [${agent.source}]`;
|
|
346
|
+
lines.push(` - ${agent.key}: ${agent.description}${sourceSuffix}`);
|
|
347
|
+
}
|
|
348
|
+
lines.push("");
|
|
349
|
+
}
|
|
350
|
+
return lines.join("\n").trimEnd();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// src/catalog-import.ts
|
|
354
|
+
var import_node_fs3 = require("fs");
|
|
355
|
+
var import_node_path3 = require("path");
|
|
356
|
+
var CatalogImportError = class extends Error {
|
|
357
|
+
};
|
|
358
|
+
function resolveTargetRoot(projectRoot, scope, homeDir) {
|
|
359
|
+
if (scope === "project") {
|
|
360
|
+
return resolveProjectCatalogDir(projectRoot);
|
|
361
|
+
}
|
|
362
|
+
if (scope === "global") {
|
|
363
|
+
return resolveGlobalCatalogDir(homeDir);
|
|
364
|
+
}
|
|
365
|
+
throw new CatalogImportError(`unsupported scope: ${scope}`);
|
|
366
|
+
}
|
|
367
|
+
function categoryKeyFromDir2(directoryName) {
|
|
368
|
+
const match = directoryName.match(/^(\d+)-(.*)$/);
|
|
369
|
+
return match ? match[2] : directoryName;
|
|
370
|
+
}
|
|
371
|
+
function resolveTargetCategoryDir(targetRoot, sourceCategoryDir) {
|
|
372
|
+
const sourceKey = categoryKeyFromDir2((0, import_node_path3.basename)(sourceCategoryDir));
|
|
373
|
+
if ((0, import_node_fs3.existsSync)(targetRoot)) {
|
|
374
|
+
for (const entry of (0, import_node_fs3.readdirSync)(targetRoot, { withFileTypes: true })) {
|
|
375
|
+
if (!entry.isDirectory()) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (categoryKeyFromDir2(entry.name) === sourceKey) {
|
|
379
|
+
return (0, import_node_path3.resolve)(targetRoot, entry.name);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return (0, import_node_path3.resolve)(targetRoot, (0, import_node_path3.basename)(sourceCategoryDir));
|
|
384
|
+
}
|
|
385
|
+
function ensureDirectory(path, createdPaths, preservedPaths) {
|
|
386
|
+
if ((0, import_node_fs3.existsSync)(path)) {
|
|
387
|
+
if (!(0, import_node_fs3.statSync)(path).isDirectory()) {
|
|
388
|
+
throw new CatalogImportError(`expected directory but found file: ${path}`);
|
|
389
|
+
}
|
|
390
|
+
preservedPaths.push(path);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
(0, import_node_fs3.mkdirSync)(path, { recursive: true });
|
|
394
|
+
createdPaths.push(path);
|
|
395
|
+
}
|
|
396
|
+
function copyFile(source, destination, overwrite, createdPaths, preservedPaths) {
|
|
397
|
+
if ((0, import_node_fs3.existsSync)(destination) && !overwrite) {
|
|
398
|
+
if ((0, import_node_fs3.statSync)(destination).isDirectory()) {
|
|
399
|
+
throw new CatalogImportError(`expected file but found directory: ${destination}`);
|
|
400
|
+
}
|
|
401
|
+
preservedPaths.push(destination);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
(0, import_node_fs3.mkdirSync)((0, import_node_path3.dirname)(destination), { recursive: true });
|
|
405
|
+
(0, import_node_fs3.copyFileSync)(source, destination);
|
|
406
|
+
createdPaths.push(destination);
|
|
407
|
+
}
|
|
408
|
+
function scanSourceRoots(catalogRoots) {
|
|
409
|
+
const normalizedRoots = normalizeCatalogRoots(catalogRoots);
|
|
410
|
+
if (normalizedRoots.length === 0) {
|
|
411
|
+
throw new CatalogImportError("catalog import requires at least one --catalog-root");
|
|
412
|
+
}
|
|
413
|
+
const categories = /* @__PURE__ */ new Map();
|
|
414
|
+
const agents = /* @__PURE__ */ new Map();
|
|
415
|
+
const issues = [];
|
|
416
|
+
for (const root of normalizedRoots) {
|
|
417
|
+
if (!(0, import_node_fs3.existsSync)(root)) {
|
|
418
|
+
throw new CatalogImportError(`catalog root does not exist: ${root}`);
|
|
419
|
+
}
|
|
420
|
+
for (const entry of (0, import_node_fs3.readdirSync)(root, { withFileTypes: true })) {
|
|
421
|
+
if (!entry.isDirectory()) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const categoryDir = (0, import_node_path3.resolve)(root, entry.name);
|
|
425
|
+
const category = parseCategoryDir(categoryDir);
|
|
426
|
+
const readmePath = (0, import_node_path3.resolve)(categoryDir, "README.md");
|
|
427
|
+
categories.set(category.key, {
|
|
428
|
+
key: category.key,
|
|
429
|
+
directory: categoryDir,
|
|
430
|
+
readmePath: (0, import_node_fs3.existsSync)(readmePath) ? readmePath : void 0
|
|
431
|
+
});
|
|
432
|
+
for (const file of (0, import_node_fs3.readdirSync)(categoryDir, { withFileTypes: true })) {
|
|
433
|
+
if (!file.isFile() || !file.name.endsWith(".toml")) {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
const path = (0, import_node_path3.resolve)(categoryDir, file.name);
|
|
437
|
+
try {
|
|
438
|
+
const agent = parseAgentFile(path, "catalog-root", category.key);
|
|
439
|
+
agents.set(agent.key, {
|
|
440
|
+
key: agent.key,
|
|
441
|
+
path,
|
|
442
|
+
categoryKey: category.key,
|
|
443
|
+
categoryDirectory: categoryDir,
|
|
444
|
+
readmePath: (0, import_node_fs3.existsSync)(readmePath) ? readmePath : void 0
|
|
445
|
+
});
|
|
446
|
+
} catch (error) {
|
|
447
|
+
issues.push(`${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (issues.length > 0) {
|
|
453
|
+
throw new CatalogImportError(
|
|
454
|
+
`source catalog contains malformed templates:
|
|
455
|
+
${issues.map((issue) => `- ${issue}`).join("\n")}`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
return { categories, agents };
|
|
459
|
+
}
|
|
460
|
+
function importCatalog(options) {
|
|
461
|
+
if (options.agentKeys.length === 0 && options.categoryKeys.length === 0) {
|
|
462
|
+
throw new CatalogImportError("catalog import requires --agents, --categories, or both");
|
|
463
|
+
}
|
|
464
|
+
const { categories, agents } = scanSourceRoots(options.catalogRoots);
|
|
465
|
+
const missingCategories = options.categoryKeys.filter((key) => !categories.has(key));
|
|
466
|
+
if (missingCategories.length > 0) {
|
|
467
|
+
throw new CatalogImportError(`unknown category keys: ${missingCategories.join(", ")}`);
|
|
468
|
+
}
|
|
469
|
+
const missingAgents = options.agentKeys.filter((key) => !agents.has(key));
|
|
470
|
+
if (missingAgents.length > 0) {
|
|
471
|
+
throw new CatalogImportError(`unknown agent keys: ${missingAgents.join(", ")}`);
|
|
472
|
+
}
|
|
473
|
+
const targetRoot = resolveTargetRoot(options.projectRoot, options.scope, options.homeDir);
|
|
474
|
+
const createdPaths = [];
|
|
475
|
+
const preservedPaths = [];
|
|
476
|
+
ensureDirectory(targetRoot, createdPaths, preservedPaths);
|
|
477
|
+
const selectedCategories = new Set(options.categoryKeys);
|
|
478
|
+
const selectedAgents = new Set(options.agentKeys);
|
|
479
|
+
const copyPlan = /* @__PURE__ */ new Map();
|
|
480
|
+
for (const categoryKey of options.categoryKeys) {
|
|
481
|
+
const categorySource = categories.get(categoryKey);
|
|
482
|
+
if (!categorySource) {
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
const targetCategoryDir = resolveTargetCategoryDir(targetRoot, categorySource.directory);
|
|
486
|
+
ensureDirectory(targetCategoryDir, createdPaths, preservedPaths);
|
|
487
|
+
if (categorySource.readmePath) {
|
|
488
|
+
copyPlan.set(categorySource.readmePath, (0, import_node_path3.resolve)(targetCategoryDir, "README.md"));
|
|
489
|
+
}
|
|
490
|
+
for (const agentSource of agents.values()) {
|
|
491
|
+
if (agentSource.categoryDirectory !== categorySource.directory) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
selectedAgents.add(agentSource.key);
|
|
495
|
+
copyPlan.set(agentSource.path, (0, import_node_path3.resolve)(targetCategoryDir, (0, import_node_path3.basename)(agentSource.path)));
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
for (const agentKey of options.agentKeys) {
|
|
499
|
+
const agentSource = agents.get(agentKey);
|
|
500
|
+
if (!agentSource) {
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
selectedCategories.add(agentSource.categoryKey);
|
|
504
|
+
const targetCategoryDir = resolveTargetCategoryDir(targetRoot, agentSource.categoryDirectory);
|
|
505
|
+
ensureDirectory(targetCategoryDir, createdPaths, preservedPaths);
|
|
506
|
+
if (agentSource.readmePath) {
|
|
507
|
+
copyPlan.set(agentSource.readmePath, (0, import_node_path3.resolve)(targetCategoryDir, "README.md"));
|
|
508
|
+
}
|
|
509
|
+
copyPlan.set(agentSource.path, (0, import_node_path3.resolve)(targetCategoryDir, (0, import_node_path3.basename)(agentSource.path)));
|
|
510
|
+
}
|
|
511
|
+
for (const [source, destination] of [...copyPlan.entries()].sort(
|
|
512
|
+
(left, right) => left[1].localeCompare(right[1])
|
|
513
|
+
)) {
|
|
514
|
+
copyFile(source, destination, options.overwrite ?? false, createdPaths, preservedPaths);
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
targetRoot,
|
|
518
|
+
importedCategoryKeys: [...selectedCategories].sort(),
|
|
519
|
+
importedAgentKeys: [...selectedAgents].sort(),
|
|
520
|
+
createdPaths,
|
|
521
|
+
preservedPaths
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/catalog-sync.ts
|
|
526
|
+
var import_node_fs4 = require("fs");
|
|
527
|
+
var import_node_path4 = require("path");
|
|
528
|
+
var VOLTAGENT_TREE_API_URL = "https://api.github.com/repos/VoltAgent/awesome-codex-subagents/git/trees/main?recursive=1";
|
|
529
|
+
var VOLTAGENT_RAW_BASE_URL = "https://raw.githubusercontent.com/VoltAgent/awesome-codex-subagents/main/";
|
|
530
|
+
var CatalogSyncError = class extends Error {
|
|
531
|
+
};
|
|
532
|
+
function normalizeSourceName(sourceName) {
|
|
533
|
+
const candidate = sourceName.trim().toLowerCase();
|
|
534
|
+
if (!candidate) {
|
|
535
|
+
throw new CatalogSyncError("catalog sync requires a non-empty source name");
|
|
536
|
+
}
|
|
537
|
+
if (!/^[a-z0-9][a-z0-9_-]*$/.test(candidate)) {
|
|
538
|
+
throw new CatalogSyncError(
|
|
539
|
+
"source names may contain only lowercase letters, digits, hyphens, and underscores"
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
return candidate;
|
|
543
|
+
}
|
|
544
|
+
function resolveCategoriesRoot(sourceRoot) {
|
|
545
|
+
const root = (0, import_node_path4.resolve)(sourceRoot);
|
|
546
|
+
if ((0, import_node_fs4.existsSync)(root) && (0, import_node_path4.basename)(root) === "categories") {
|
|
547
|
+
return root;
|
|
548
|
+
}
|
|
549
|
+
const nested = (0, import_node_path4.resolve)(root, "categories");
|
|
550
|
+
if ((0, import_node_fs4.existsSync)(nested)) {
|
|
551
|
+
return nested;
|
|
552
|
+
}
|
|
553
|
+
throw new CatalogSyncError(`expected a categories/ directory but found: ${sourceRoot}`);
|
|
554
|
+
}
|
|
555
|
+
function prepareTargetRoot(targetRoot) {
|
|
556
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path4.resolve)(targetRoot, ".."), { recursive: true });
|
|
557
|
+
if ((0, import_node_fs4.existsSync)(targetRoot)) {
|
|
558
|
+
(0, import_node_fs4.rmSync)(targetRoot, { recursive: true, force: true });
|
|
559
|
+
}
|
|
560
|
+
(0, import_node_fs4.mkdirSync)(targetRoot, { recursive: true });
|
|
561
|
+
}
|
|
562
|
+
function listRelativeCatalogFiles(root) {
|
|
563
|
+
const discovered = [];
|
|
564
|
+
function walk(current) {
|
|
565
|
+
for (const entry of (0, import_node_fs4.readdirSync)(current, { withFileTypes: true })) {
|
|
566
|
+
const absolutePath = (0, import_node_path4.resolve)(current, entry.name);
|
|
567
|
+
if (entry.isDirectory()) {
|
|
568
|
+
walk(absolutePath);
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
if (!entry.isFile()) {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
if (entry.name === "README.md" || entry.name.endsWith(".toml")) {
|
|
575
|
+
discovered.push(absolutePath.slice(root.length + 1));
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
walk(root);
|
|
580
|
+
return discovered.sort();
|
|
581
|
+
}
|
|
582
|
+
function copyLocalSource(sourceRoot, targetRoot) {
|
|
583
|
+
const categoriesRoot = resolveCategoriesRoot(sourceRoot);
|
|
584
|
+
const copiedPaths = [];
|
|
585
|
+
for (const relativePath of listRelativeCatalogFiles(categoriesRoot)) {
|
|
586
|
+
const sourcePath = (0, import_node_path4.resolve)(categoriesRoot, relativePath);
|
|
587
|
+
const destination = (0, import_node_path4.resolve)(targetRoot, relativePath);
|
|
588
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path4.resolve)(destination, ".."), { recursive: true });
|
|
589
|
+
(0, import_node_fs4.cpSync)(sourcePath, destination);
|
|
590
|
+
copiedPaths.push(destination);
|
|
591
|
+
}
|
|
592
|
+
return copiedPaths;
|
|
593
|
+
}
|
|
594
|
+
async function fetchJson(url) {
|
|
595
|
+
const response = await fetch(url, {
|
|
596
|
+
headers: {
|
|
597
|
+
Accept: "application/vnd.github+json",
|
|
598
|
+
"User-Agent": "codex-subagent-kit"
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
if (!response.ok) {
|
|
602
|
+
throw new CatalogSyncError(`failed to fetch upstream catalog metadata: ${response.status}`);
|
|
603
|
+
}
|
|
604
|
+
return await response.json();
|
|
605
|
+
}
|
|
606
|
+
async function fetchText(url) {
|
|
607
|
+
const response = await fetch(url, {
|
|
608
|
+
headers: { "User-Agent": "codex-subagent-kit" }
|
|
609
|
+
});
|
|
610
|
+
if (!response.ok) {
|
|
611
|
+
throw new CatalogSyncError(`failed to fetch upstream catalog file: ${url}`);
|
|
612
|
+
}
|
|
613
|
+
return response.text();
|
|
614
|
+
}
|
|
615
|
+
async function downloadVoltAgentSource(targetRoot) {
|
|
616
|
+
const payload = await fetchJson(VOLTAGENT_TREE_API_URL);
|
|
617
|
+
const tree = payload.tree;
|
|
618
|
+
if (!Array.isArray(tree)) {
|
|
619
|
+
throw new CatalogSyncError("unexpected upstream catalog metadata payload");
|
|
620
|
+
}
|
|
621
|
+
const upstreamPaths = tree.map((entry) => entry && typeof entry === "object" ? entry.path : void 0).filter((path) => typeof path === "string" && path.startsWith("categories/")).filter((path) => path.endsWith(".toml") || path.endsWith("README.md")).sort();
|
|
622
|
+
if (upstreamPaths.length === 0) {
|
|
623
|
+
throw new CatalogSyncError("upstream catalog did not expose any categories files");
|
|
624
|
+
}
|
|
625
|
+
const copiedPaths = [];
|
|
626
|
+
for (const upstreamPath of upstreamPaths) {
|
|
627
|
+
const relativePath = upstreamPath.replace(/^categories\//, "");
|
|
628
|
+
const destination = (0, import_node_path4.resolve)(targetRoot, relativePath);
|
|
629
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path4.resolve)(destination, ".."), { recursive: true });
|
|
630
|
+
(0, import_node_fs4.writeFileSync)(destination, await fetchText(`${VOLTAGENT_RAW_BASE_URL}${upstreamPath}`), "utf8");
|
|
631
|
+
copiedPaths.push(destination);
|
|
632
|
+
}
|
|
633
|
+
return copiedPaths;
|
|
634
|
+
}
|
|
635
|
+
async function syncCatalog(options) {
|
|
636
|
+
const sourceName = normalizeSourceName(options.sourceName ?? DEFAULT_SYNC_SOURCE_NAME);
|
|
637
|
+
const targetRoot = options.scope === "project" ? resolveProjectSourceCategoriesDir(options.projectRoot, sourceName) : resolveGlobalSourceCategoriesDir(sourceName, options.homeDir);
|
|
638
|
+
prepareTargetRoot(targetRoot);
|
|
639
|
+
let copiedPaths;
|
|
640
|
+
let sourceLabel;
|
|
641
|
+
if (options.sourceRoot) {
|
|
642
|
+
copiedPaths = copyLocalSource(options.sourceRoot, targetRoot);
|
|
643
|
+
sourceLabel = resolveCategoriesRoot(options.sourceRoot);
|
|
644
|
+
} else {
|
|
645
|
+
if (sourceName !== DEFAULT_SYNC_SOURCE_NAME) {
|
|
646
|
+
throw new CatalogSyncError(
|
|
647
|
+
"remote sync is only supported for the default voltagent source without --source-root"
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
copiedPaths = await downloadVoltAgentSource(targetRoot);
|
|
651
|
+
sourceLabel = "VoltAgent/awesome-codex-subagents@main";
|
|
652
|
+
}
|
|
653
|
+
if (copiedPaths.length === 0) {
|
|
654
|
+
throw new CatalogSyncError("catalog sync did not copy any README or TOML files");
|
|
655
|
+
}
|
|
656
|
+
return {
|
|
657
|
+
sourceName,
|
|
658
|
+
targetRoot,
|
|
659
|
+
sourceLabel,
|
|
660
|
+
copiedPaths
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// src/doctor.ts
|
|
665
|
+
var import_node_fs6 = require("fs");
|
|
666
|
+
var import_node_path6 = require("path");
|
|
667
|
+
|
|
668
|
+
// src/generator.ts
|
|
669
|
+
var import_node_fs5 = require("fs");
|
|
670
|
+
var import_node_path5 = require("path");
|
|
671
|
+
var GenerationError = class extends Error {
|
|
672
|
+
};
|
|
673
|
+
var ORCHESTRATOR_CATEGORY = "meta-orchestration";
|
|
674
|
+
var DEFAULT_ORCHESTRATOR_KEY = "multi-agent-coordinator";
|
|
675
|
+
function renderAgentFile(agent) {
|
|
676
|
+
const lines = [
|
|
677
|
+
`name = ${JSON.stringify(agent.name)}`,
|
|
678
|
+
`description = ${JSON.stringify(agent.description)}`,
|
|
679
|
+
`model = ${JSON.stringify(agent.model)}`,
|
|
680
|
+
`model_reasoning_effort = ${JSON.stringify(agent.reasoningEffort)}`,
|
|
681
|
+
`sandbox_mode = ${JSON.stringify(agent.sandboxMode)}`,
|
|
682
|
+
'developer_instructions = """',
|
|
683
|
+
agent.developerInstructions.trimEnd(),
|
|
684
|
+
'"""'
|
|
685
|
+
];
|
|
686
|
+
if (agent.categoryOverride) {
|
|
687
|
+
lines.push(`codex_subagent_kit_category = ${JSON.stringify(agent.categoryOverride)}`);
|
|
688
|
+
}
|
|
689
|
+
return `${lines.join("\n")}
|
|
690
|
+
`;
|
|
691
|
+
}
|
|
692
|
+
function resolveTargetDir(scope, projectRoot, homeDir) {
|
|
693
|
+
if (scope === "project") {
|
|
694
|
+
return resolveProjectAgentsDir(projectRoot);
|
|
695
|
+
}
|
|
696
|
+
if (scope === "global") {
|
|
697
|
+
return resolveGlobalAgentsDir(homeDir);
|
|
698
|
+
}
|
|
699
|
+
throw new GenerationError(`unsupported scope: ${scope}`);
|
|
700
|
+
}
|
|
701
|
+
function installAgents(options) {
|
|
702
|
+
const agentMap = getAgentMap({
|
|
703
|
+
projectRoot: options.projectRoot,
|
|
704
|
+
homeDir: options.homeDir,
|
|
705
|
+
includeProject: options.scope === "project",
|
|
706
|
+
includeGlobal: true,
|
|
707
|
+
catalogRoots: options.catalogRoots
|
|
708
|
+
});
|
|
709
|
+
const missing = options.agentKeys.filter((key) => !agentMap.has(key));
|
|
710
|
+
if (missing.length > 0) {
|
|
711
|
+
throw new GenerationError(`unknown agent keys: ${missing.join(", ")}`);
|
|
712
|
+
}
|
|
713
|
+
const targetDir = resolveTargetDir(options.scope, options.projectRoot, options.homeDir);
|
|
714
|
+
(0, import_node_fs5.mkdirSync)(targetDir, { recursive: true });
|
|
715
|
+
const agentPaths = [];
|
|
716
|
+
const agentPreservedPaths = [];
|
|
717
|
+
for (const key of options.agentKeys) {
|
|
718
|
+
const agent = agentMap.get(key);
|
|
719
|
+
if (!agent) {
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
const filePath = (0, import_node_path5.resolve)(targetDir, `${agent.key}.toml`);
|
|
723
|
+
if ((0, import_node_fs5.existsSync)(filePath) && !options.overwrite) {
|
|
724
|
+
agentPreservedPaths.push(filePath);
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
(0, import_node_fs5.writeFileSync)(
|
|
728
|
+
filePath,
|
|
729
|
+
renderAgentFile({
|
|
730
|
+
name: agent.name,
|
|
731
|
+
description: agent.description,
|
|
732
|
+
model: agent.model,
|
|
733
|
+
reasoningEffort: agent.reasoningEffort,
|
|
734
|
+
sandboxMode: agent.sandboxMode,
|
|
735
|
+
developerInstructions: agent.developerInstructions
|
|
736
|
+
}),
|
|
737
|
+
"utf8"
|
|
738
|
+
);
|
|
739
|
+
agentPaths.push(filePath);
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
agentPaths,
|
|
743
|
+
agentPreservedPaths
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// src/doctor.ts
|
|
748
|
+
function scanCatalogRoot(root, source, missingIsIssue = false) {
|
|
749
|
+
const categories = /* @__PURE__ */ new Map();
|
|
750
|
+
const agents = /* @__PURE__ */ new Map();
|
|
751
|
+
const issues = [];
|
|
752
|
+
let checkedTemplates = 0;
|
|
753
|
+
if (!(0, import_node_fs6.existsSync)(root)) {
|
|
754
|
+
if (missingIsIssue) {
|
|
755
|
+
issues.push({ path: root, message: "catalog root does not exist" });
|
|
756
|
+
}
|
|
757
|
+
return { categories, agents, issues, checkedTemplates };
|
|
758
|
+
}
|
|
759
|
+
for (const entry of (0, import_node_fs6.readdirSync)(root, { withFileTypes: true })) {
|
|
760
|
+
if (!entry.isDirectory()) {
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
const categoryDir = (0, import_node_path6.resolve)(root, entry.name);
|
|
764
|
+
const category = parseCategoryDir(categoryDir);
|
|
765
|
+
categories.set(category.key, category);
|
|
766
|
+
for (const file of (0, import_node_fs6.readdirSync)(categoryDir, { withFileTypes: true })) {
|
|
767
|
+
if (!file.isFile() || !file.name.endsWith(".toml")) {
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
const path = (0, import_node_path6.resolve)(categoryDir, file.name);
|
|
771
|
+
checkedTemplates += 1;
|
|
772
|
+
try {
|
|
773
|
+
const agent = parseAgentFile(path, source, category.key);
|
|
774
|
+
agents.set(agent.key, agent);
|
|
775
|
+
} catch (error) {
|
|
776
|
+
issues.push({ path, message: error instanceof Error ? error.message : String(error) });
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return { categories, agents, issues, checkedTemplates };
|
|
781
|
+
}
|
|
782
|
+
function scanInstalledAgents(directory, source, inheritedAgents) {
|
|
783
|
+
const agents = [];
|
|
784
|
+
const issues = [];
|
|
785
|
+
let checkedFiles = 0;
|
|
786
|
+
if (!(0, import_node_fs6.existsSync)(directory)) {
|
|
787
|
+
return { agents, issues, checkedFiles };
|
|
788
|
+
}
|
|
789
|
+
for (const file of (0, import_node_fs6.readdirSync)(directory, { withFileTypes: true })) {
|
|
790
|
+
if (!file.isFile() || !file.name.endsWith(".toml")) {
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
const path = (0, import_node_path6.resolve)(directory, file.name);
|
|
794
|
+
checkedFiles += 1;
|
|
795
|
+
try {
|
|
796
|
+
const inheritedCategory = inheritedAgents.get(file.name.replace(/\.toml$/, ""))?.category;
|
|
797
|
+
agents.push(parseAgentFile(path, source, inheritedCategory));
|
|
798
|
+
} catch (error) {
|
|
799
|
+
issues.push({ path, message: error instanceof Error ? error.message : String(error) });
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return { agents, issues, checkedFiles };
|
|
803
|
+
}
|
|
804
|
+
function runDoctor(options) {
|
|
805
|
+
const catalogCounts = [];
|
|
806
|
+
const installedCounts = [];
|
|
807
|
+
const issues = [];
|
|
808
|
+
const builtin = scanCatalogRoot(BUILTIN_CATEGORIES_DIR, "builtin");
|
|
809
|
+
catalogCounts.push(["built-in", builtin.checkedTemplates]);
|
|
810
|
+
issues.push(...builtin.issues);
|
|
811
|
+
const categories = new Map(builtin.categories);
|
|
812
|
+
const agentMap = new Map(builtin.agents);
|
|
813
|
+
for (const root of resolveGlobalSourceCategoriesDirs(options.homeDir)) {
|
|
814
|
+
const sourceName = (0, import_node_path6.basename)((0, import_node_path6.dirname)(root)) || "unknown";
|
|
815
|
+
const synced = scanCatalogRoot(root, `global-source:${sourceName}`);
|
|
816
|
+
catalogCounts.push([`global source: ${sourceName}`, synced.checkedTemplates]);
|
|
817
|
+
issues.push(...synced.issues);
|
|
818
|
+
for (const [key, category] of synced.categories) categories.set(key, category);
|
|
819
|
+
for (const [key, agent] of synced.agents) agentMap.set(key, agent);
|
|
820
|
+
}
|
|
821
|
+
const globalCatalog = scanCatalogRoot(resolveGlobalCatalogDir(options.homeDir), "global-catalog");
|
|
822
|
+
catalogCounts.push(["global catalog", globalCatalog.checkedTemplates]);
|
|
823
|
+
issues.push(...globalCatalog.issues);
|
|
824
|
+
for (const [key, category] of globalCatalog.categories) categories.set(key, category);
|
|
825
|
+
for (const [key, agent] of globalCatalog.agents) agentMap.set(key, agent);
|
|
826
|
+
if (options.scope === "project") {
|
|
827
|
+
for (const root of resolveProjectSourceCategoriesDirs(options.projectRoot)) {
|
|
828
|
+
const sourceName = (0, import_node_path6.basename)((0, import_node_path6.dirname)(root)) || "unknown";
|
|
829
|
+
const synced = scanCatalogRoot(root, `project-source:${sourceName}`);
|
|
830
|
+
catalogCounts.push([`project source: ${sourceName}`, synced.checkedTemplates]);
|
|
831
|
+
issues.push(...synced.issues);
|
|
832
|
+
for (const [key, category] of synced.categories) categories.set(key, category);
|
|
833
|
+
for (const [key, agent] of synced.agents) agentMap.set(key, agent);
|
|
834
|
+
}
|
|
835
|
+
const projectCatalog = scanCatalogRoot(
|
|
836
|
+
resolveProjectCatalogDir(options.projectRoot),
|
|
837
|
+
"project-catalog"
|
|
838
|
+
);
|
|
839
|
+
catalogCounts.push(["project catalog", projectCatalog.checkedTemplates]);
|
|
840
|
+
issues.push(...projectCatalog.issues);
|
|
841
|
+
for (const [key, category] of projectCatalog.categories) categories.set(key, category);
|
|
842
|
+
for (const [key, agent] of projectCatalog.agents) agentMap.set(key, agent);
|
|
843
|
+
}
|
|
844
|
+
for (const root of normalizeCatalogRoots(options.catalogRoots)) {
|
|
845
|
+
const extra = scanCatalogRoot(root, "catalog-root", true);
|
|
846
|
+
catalogCounts.push([`catalog root: ${root}`, extra.checkedTemplates]);
|
|
847
|
+
issues.push(...extra.issues);
|
|
848
|
+
for (const [key, category] of extra.categories) categories.set(key, category);
|
|
849
|
+
for (const [key, agent] of extra.agents) agentMap.set(key, agent);
|
|
850
|
+
}
|
|
851
|
+
categories.set(IMPORTED_AGENTS_CATEGORY.key, IMPORTED_AGENTS_CATEGORY);
|
|
852
|
+
const globalInstalled = scanInstalledAgents(
|
|
853
|
+
resolveGlobalAgentsDir(options.homeDir),
|
|
854
|
+
"global",
|
|
855
|
+
agentMap
|
|
856
|
+
);
|
|
857
|
+
installedCounts.push(["global agents", globalInstalled.checkedFiles]);
|
|
858
|
+
issues.push(...globalInstalled.issues);
|
|
859
|
+
for (const agent of globalInstalled.agents) {
|
|
860
|
+
agentMap.set(agent.key, agent);
|
|
861
|
+
}
|
|
862
|
+
let targetDir;
|
|
863
|
+
if (options.scope === "project") {
|
|
864
|
+
const projectInstalledDir = resolveTargetDir("project", options.projectRoot);
|
|
865
|
+
const projectInstalled = scanInstalledAgents(projectInstalledDir, "project", agentMap);
|
|
866
|
+
installedCounts.push(["project agents", projectInstalled.checkedFiles]);
|
|
867
|
+
issues.push(...projectInstalled.issues);
|
|
868
|
+
targetDir = projectInstalledDir;
|
|
869
|
+
if (projectInstalled.checkedFiles === 0) {
|
|
870
|
+
issues.push({ path: targetDir, message: "no installed agent definitions found in target scope" });
|
|
871
|
+
}
|
|
872
|
+
} else {
|
|
873
|
+
targetDir = resolveTargetDir("global", options.projectRoot, options.homeDir);
|
|
874
|
+
if (globalInstalled.checkedFiles === 0) {
|
|
875
|
+
issues.push({ path: targetDir, message: "no installed agent definitions found in target scope" });
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return {
|
|
879
|
+
scope: options.scope,
|
|
880
|
+
targetDir,
|
|
881
|
+
catalogCounts,
|
|
882
|
+
installedCounts,
|
|
883
|
+
issues
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
function renderDoctorReport(report) {
|
|
887
|
+
const lines = [
|
|
888
|
+
`status: ${report.issues.length === 0 ? "ok" : "issues found"}`,
|
|
889
|
+
`scope: ${report.scope}`,
|
|
890
|
+
`target: ${report.targetDir}`,
|
|
891
|
+
"",
|
|
892
|
+
"Catalog templates checked:"
|
|
893
|
+
];
|
|
894
|
+
for (const [label, count] of report.catalogCounts) {
|
|
895
|
+
lines.push(`- ${label}: ${count}`);
|
|
896
|
+
}
|
|
897
|
+
lines.push("");
|
|
898
|
+
lines.push("Installed agent files checked:");
|
|
899
|
+
for (const [label, count] of report.installedCounts) {
|
|
900
|
+
lines.push(`- ${label}: ${count}`);
|
|
901
|
+
}
|
|
902
|
+
lines.push("");
|
|
903
|
+
if (report.issues.length === 0) {
|
|
904
|
+
lines.push("Issues: none");
|
|
905
|
+
} else {
|
|
906
|
+
lines.push("Issues:");
|
|
907
|
+
for (const issue of report.issues) {
|
|
908
|
+
lines.push(issue.path ? `- ${issue.path}: ${issue.message}` : `- ${issue.message}`);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return lines.join("\n");
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// src/meta.ts
|
|
915
|
+
var STABLE_COMMANDS = [
|
|
916
|
+
"catalog",
|
|
917
|
+
"catalog import",
|
|
918
|
+
"catalog sync",
|
|
919
|
+
"install",
|
|
920
|
+
"doctor",
|
|
921
|
+
"usage",
|
|
922
|
+
"template init",
|
|
923
|
+
"tui"
|
|
924
|
+
];
|
|
925
|
+
|
|
926
|
+
// src/templates.ts
|
|
927
|
+
var import_node_fs7 = require("fs");
|
|
928
|
+
var import_node_path7 = require("path");
|
|
929
|
+
var DEFAULT_MODEL = "gpt-5.4";
|
|
930
|
+
var DEFAULT_REASONING_EFFORT = "medium";
|
|
931
|
+
var DEFAULT_SANDBOX_MODE = "read-only";
|
|
932
|
+
var ORCHESTRATOR_CATEGORY2 = "meta-orchestration";
|
|
933
|
+
var SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
934
|
+
var TemplateError = class extends Error {
|
|
935
|
+
};
|
|
936
|
+
function validateSlug(value, fieldName) {
|
|
937
|
+
const normalized = value.trim().toLowerCase();
|
|
938
|
+
if (!SLUG_PATTERN.test(normalized)) {
|
|
939
|
+
throw new TemplateError(
|
|
940
|
+
`${fieldName} must be a lowercase slug using letters, numbers, and hyphens`
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
return normalized;
|
|
944
|
+
}
|
|
945
|
+
function titleFromSlug(value) {
|
|
946
|
+
return value.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
947
|
+
}
|
|
948
|
+
function categoryKeyFromDir3(directoryName) {
|
|
949
|
+
const match = directoryName.match(/^(\d+)-(.*)$/);
|
|
950
|
+
return match ? match[2] : directoryName;
|
|
951
|
+
}
|
|
952
|
+
function normalizePrefix(prefix) {
|
|
953
|
+
if (prefix === void 0) {
|
|
954
|
+
return void 0;
|
|
955
|
+
}
|
|
956
|
+
const stripped = prefix.trim();
|
|
957
|
+
if (!stripped) {
|
|
958
|
+
return void 0;
|
|
959
|
+
}
|
|
960
|
+
if (!/^\d+$/.test(stripped)) {
|
|
961
|
+
throw new TemplateError("category prefix must be numeric");
|
|
962
|
+
}
|
|
963
|
+
return stripped.padStart(2, "0");
|
|
964
|
+
}
|
|
965
|
+
function nextPrefix(root) {
|
|
966
|
+
if (!existsDirectory(root)) {
|
|
967
|
+
return "11";
|
|
968
|
+
}
|
|
969
|
+
const numericPrefixes = (0, import_node_fs7.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name.match(/^(\d+)-/)).filter((match) => Boolean(match)).map((match) => Number.parseInt(match[1], 10));
|
|
970
|
+
return String(Math.max(10, ...numericPrefixes) + 1).padStart(2, "0");
|
|
971
|
+
}
|
|
972
|
+
function existsDirectory(path) {
|
|
973
|
+
try {
|
|
974
|
+
return (0, import_node_fs7.statSync)(path).isDirectory();
|
|
975
|
+
} catch {
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
function ensureDirectory2(path, createdPaths, preservedPaths) {
|
|
980
|
+
if (existsDirectory(path)) {
|
|
981
|
+
preservedPaths.push(path);
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
try {
|
|
985
|
+
(0, import_node_fs7.mkdirSync)(path, { recursive: false });
|
|
986
|
+
} catch (error) {
|
|
987
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
988
|
+
(0, import_node_fs7.mkdirSync)(path, { recursive: true });
|
|
989
|
+
} else {
|
|
990
|
+
throw error;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
createdPaths.push(path);
|
|
994
|
+
}
|
|
995
|
+
function writeFile(path, content, overwrite, createdPaths, preservedPaths) {
|
|
996
|
+
try {
|
|
997
|
+
const stats = (0, import_node_fs7.statSync)(path);
|
|
998
|
+
if (stats.isDirectory()) {
|
|
999
|
+
throw new TemplateError(`expected file but found directory: ${path}`);
|
|
1000
|
+
}
|
|
1001
|
+
if (!overwrite) {
|
|
1002
|
+
preservedPaths.push(path);
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
1007
|
+
if (error instanceof TemplateError) {
|
|
1008
|
+
throw error;
|
|
1009
|
+
}
|
|
1010
|
+
throw error;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
(0, import_node_fs7.writeFileSync)(path, content, "utf8");
|
|
1014
|
+
createdPaths.push(path);
|
|
1015
|
+
}
|
|
1016
|
+
function resolveTargetRoot2(options) {
|
|
1017
|
+
if (options.catalogRoot) {
|
|
1018
|
+
return (0, import_node_path7.resolve)(options.catalogRoot);
|
|
1019
|
+
}
|
|
1020
|
+
if (options.scope === "project") {
|
|
1021
|
+
return resolveProjectCatalogDir(options.projectRoot);
|
|
1022
|
+
}
|
|
1023
|
+
if (options.scope === "global") {
|
|
1024
|
+
return resolveGlobalCatalogDir();
|
|
1025
|
+
}
|
|
1026
|
+
throw new TemplateError(`unsupported scope: ${options.scope}`);
|
|
1027
|
+
}
|
|
1028
|
+
function resolveCategoryDir(root, categoryKey, categoryPrefix) {
|
|
1029
|
+
if (existsDirectory(root)) {
|
|
1030
|
+
for (const entry of (0, import_node_fs7.readdirSync)(root, { withFileTypes: true })) {
|
|
1031
|
+
if (!entry.isDirectory()) {
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
if (categoryKeyFromDir3(entry.name) === categoryKey) {
|
|
1035
|
+
const match = entry.name.match(/^(\d+)-/);
|
|
1036
|
+
return {
|
|
1037
|
+
categoryDir: (0, import_node_path7.resolve)(root, entry.name),
|
|
1038
|
+
resolvedPrefix: match ? match[1] : ""
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
const effectivePrefix = categoryPrefix ?? nextPrefix(root);
|
|
1044
|
+
return {
|
|
1045
|
+
categoryDir: (0, import_node_path7.resolve)(root, `${effectivePrefix}-${categoryKey}`),
|
|
1046
|
+
resolvedPrefix: effectivePrefix
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
function renderCategoryReadme(options) {
|
|
1050
|
+
const headingNumber = options.categoryPrefix ? `${Number.parseInt(options.categoryPrefix, 10)}. ` : "";
|
|
1051
|
+
return `# ${headingNumber}${options.categoryTitle}
|
|
1052
|
+
|
|
1053
|
+
${options.categoryDescription}
|
|
1054
|
+
`;
|
|
1055
|
+
}
|
|
1056
|
+
function renderAgentTemplate(options) {
|
|
1057
|
+
return renderAgentFile({
|
|
1058
|
+
name: options.agentName,
|
|
1059
|
+
description: options.agentDescription,
|
|
1060
|
+
model: options.model,
|
|
1061
|
+
reasoningEffort: options.reasoningEffort,
|
|
1062
|
+
sandboxMode: options.sandboxMode,
|
|
1063
|
+
developerInstructions: [
|
|
1064
|
+
`You are \`${options.agentKey}\`.`,
|
|
1065
|
+
"",
|
|
1066
|
+
"Responsibilities:",
|
|
1067
|
+
"- TODO: define the primary ownership boundary.",
|
|
1068
|
+
"- TODO: define what inputs to expect.",
|
|
1069
|
+
"- TODO: define what outputs to return.",
|
|
1070
|
+
"",
|
|
1071
|
+
"Constraints:",
|
|
1072
|
+
"- Stay within the assigned scope.",
|
|
1073
|
+
"- Escalate when requirements are ambiguous or cross ownership boundaries."
|
|
1074
|
+
].join("\n"),
|
|
1075
|
+
categoryOverride: options.categoryOverride
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
function initTemplate(options) {
|
|
1079
|
+
const normalizedCategoryKey = validateSlug(options.categoryKey, "category key");
|
|
1080
|
+
const normalizedAgentKey = validateSlug(options.agentKey, "agent key");
|
|
1081
|
+
const normalizedPrefix = normalizePrefix(options.categoryPrefix);
|
|
1082
|
+
const targetRoot = resolveTargetRoot2(options);
|
|
1083
|
+
const createdPaths = [];
|
|
1084
|
+
const preservedPaths = [];
|
|
1085
|
+
ensureDirectory2(targetRoot, createdPaths, preservedPaths);
|
|
1086
|
+
const { categoryDir, resolvedPrefix } = resolveCategoryDir(
|
|
1087
|
+
targetRoot,
|
|
1088
|
+
normalizedCategoryKey,
|
|
1089
|
+
normalizedPrefix
|
|
1090
|
+
);
|
|
1091
|
+
ensureDirectory2(categoryDir, createdPaths, preservedPaths);
|
|
1092
|
+
const resolvedCategoryTitle = (options.categoryTitle ?? titleFromSlug(normalizedCategoryKey)).trim();
|
|
1093
|
+
const resolvedCategoryDescription = (options.categoryDescription ?? `Custom templates for the ${resolvedCategoryTitle.toLowerCase()} workflow.`).trim();
|
|
1094
|
+
const resolvedAgentName = (options.agentName ?? normalizedAgentKey).trim();
|
|
1095
|
+
const resolvedAgentDescription = (options.agentDescription ?? `TODO: describe when to use ${normalizedAgentKey}.`).trim();
|
|
1096
|
+
const readmePath = (0, import_node_path7.resolve)(categoryDir, "README.md");
|
|
1097
|
+
writeFile(
|
|
1098
|
+
readmePath,
|
|
1099
|
+
renderCategoryReadme({
|
|
1100
|
+
categoryTitle: resolvedCategoryTitle,
|
|
1101
|
+
categoryDescription: resolvedCategoryDescription,
|
|
1102
|
+
categoryPrefix: resolvedPrefix
|
|
1103
|
+
}),
|
|
1104
|
+
options.overwrite ?? false,
|
|
1105
|
+
createdPaths,
|
|
1106
|
+
preservedPaths
|
|
1107
|
+
);
|
|
1108
|
+
let categoryOverride = options.orchestrator ? ORCHESTRATOR_CATEGORY2 : void 0;
|
|
1109
|
+
if (normalizedCategoryKey === ORCHESTRATOR_CATEGORY2) {
|
|
1110
|
+
categoryOverride = void 0;
|
|
1111
|
+
}
|
|
1112
|
+
const agentPath = (0, import_node_path7.resolve)(categoryDir, `${normalizedAgentKey}.toml`);
|
|
1113
|
+
writeFile(
|
|
1114
|
+
agentPath,
|
|
1115
|
+
renderAgentTemplate({
|
|
1116
|
+
agentKey: normalizedAgentKey,
|
|
1117
|
+
agentName: resolvedAgentName,
|
|
1118
|
+
agentDescription: resolvedAgentDescription,
|
|
1119
|
+
model: (options.model ?? DEFAULT_MODEL).trim(),
|
|
1120
|
+
reasoningEffort: (options.reasoningEffort ?? DEFAULT_REASONING_EFFORT).trim(),
|
|
1121
|
+
sandboxMode: (options.sandboxMode ?? DEFAULT_SANDBOX_MODE).trim(),
|
|
1122
|
+
categoryOverride
|
|
1123
|
+
}),
|
|
1124
|
+
options.overwrite ?? false,
|
|
1125
|
+
createdPaths,
|
|
1126
|
+
preservedPaths
|
|
1127
|
+
);
|
|
1128
|
+
return {
|
|
1129
|
+
targetRoot,
|
|
1130
|
+
categoryDir,
|
|
1131
|
+
readmePath,
|
|
1132
|
+
agentPath,
|
|
1133
|
+
createdPaths,
|
|
1134
|
+
preservedPaths
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// src/tui.ts
|
|
1139
|
+
var import_prompts = require("@inquirer/prompts");
|
|
1140
|
+
var defaultPromptAdapter = {
|
|
1141
|
+
select: import_prompts.select,
|
|
1142
|
+
checkbox: import_prompts.checkbox,
|
|
1143
|
+
confirm: import_prompts.confirm
|
|
1144
|
+
};
|
|
1145
|
+
var defaultDeps = {
|
|
1146
|
+
installAgentsImpl: installAgents,
|
|
1147
|
+
runDoctorImpl: runDoctor
|
|
1148
|
+
};
|
|
1149
|
+
function defaultAgentSelection(scope, agentSpecs) {
|
|
1150
|
+
void scope;
|
|
1151
|
+
void agentSpecs;
|
|
1152
|
+
return /* @__PURE__ */ new Set();
|
|
1153
|
+
}
|
|
1154
|
+
function validateAgentSelection(scope, agentSpecs, selectedAgents) {
|
|
1155
|
+
void scope;
|
|
1156
|
+
void agentSpecs;
|
|
1157
|
+
if (selectedAgents.size === 0) {
|
|
1158
|
+
return "Select at least one subagent.";
|
|
1159
|
+
}
|
|
1160
|
+
return void 0;
|
|
1161
|
+
}
|
|
1162
|
+
function formatResultSummary(result, validationReport) {
|
|
1163
|
+
const lines = [
|
|
1164
|
+
"Install complete",
|
|
1165
|
+
`validation: ${validationReport.issues.length === 0 ? "ok" : "issues found"}`
|
|
1166
|
+
];
|
|
1167
|
+
for (const path of result.agentPaths.slice(0, 8)) {
|
|
1168
|
+
lines.push(path);
|
|
1169
|
+
}
|
|
1170
|
+
for (const path of result.agentPreservedPaths.slice(0, 4)) {
|
|
1171
|
+
lines.push(`agent preserved: ${path}`);
|
|
1172
|
+
}
|
|
1173
|
+
if (validationReport.issues.length > 0) {
|
|
1174
|
+
lines.push("issues:");
|
|
1175
|
+
for (const issue of validationReport.issues.slice(0, 4)) {
|
|
1176
|
+
lines.push(issue.path ? `- ${issue.path}: ${issue.message}` : `- ${issue.message}`);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
return lines.join("\n");
|
|
1180
|
+
}
|
|
1181
|
+
async function runTui(projectRoot, options = {}) {
|
|
1182
|
+
const prompt = options.promptAdapter ?? defaultPromptAdapter;
|
|
1183
|
+
const deps = { ...defaultDeps, ...options.deps };
|
|
1184
|
+
try {
|
|
1185
|
+
while (true) {
|
|
1186
|
+
const scope = await prompt.select({
|
|
1187
|
+
message: `Choose an install target for ${projectRoot}`,
|
|
1188
|
+
choices: [
|
|
1189
|
+
{ value: "project", name: "Project (.codex/agents in current project)" },
|
|
1190
|
+
{ value: "global", name: "Global (~/.codex/agents)" }
|
|
1191
|
+
]
|
|
1192
|
+
});
|
|
1193
|
+
const categories = getCategories({
|
|
1194
|
+
projectRoot,
|
|
1195
|
+
includeProject: scope === "project",
|
|
1196
|
+
includeGlobal: true,
|
|
1197
|
+
catalogRoots: options.catalogRoots
|
|
1198
|
+
});
|
|
1199
|
+
const selectedCategoryKeys = await prompt.checkbox({
|
|
1200
|
+
message: "Select categories. Leave empty to browse all agents.",
|
|
1201
|
+
choices: categories.map((category) => ({
|
|
1202
|
+
value: category.key,
|
|
1203
|
+
name: `${category.title} - ${category.description}`
|
|
1204
|
+
}))
|
|
1205
|
+
});
|
|
1206
|
+
const categorySet = new Set(selectedCategoryKeys);
|
|
1207
|
+
const agentSpecs = getAgentsByCategory(categorySet, {
|
|
1208
|
+
projectRoot,
|
|
1209
|
+
includeProject: scope === "project",
|
|
1210
|
+
includeGlobal: true,
|
|
1211
|
+
catalogRoots: options.catalogRoots
|
|
1212
|
+
});
|
|
1213
|
+
const defaultSelected = defaultAgentSelection(scope, agentSpecs);
|
|
1214
|
+
const selectedAgentsList = await prompt.checkbox({
|
|
1215
|
+
message: "Select subagents.",
|
|
1216
|
+
choices: agentSpecs.map((agent) => ({
|
|
1217
|
+
value: agent.key,
|
|
1218
|
+
name: `${agent.name} - ${agent.description}`,
|
|
1219
|
+
checked: defaultSelected.has(agent.key) ? true : void 0
|
|
1220
|
+
}))
|
|
1221
|
+
});
|
|
1222
|
+
const selectedAgents = new Set(selectedAgentsList);
|
|
1223
|
+
const validationError = validateAgentSelection(scope, agentSpecs, selectedAgents);
|
|
1224
|
+
if (validationError) {
|
|
1225
|
+
console.error(validationError);
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
const confirmed = await prompt.confirm({
|
|
1229
|
+
message: `Install ${selectedAgents.size} agent(s) into ${resolveTargetDir(scope, projectRoot)}?`,
|
|
1230
|
+
default: true
|
|
1231
|
+
});
|
|
1232
|
+
if (!confirmed) {
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1235
|
+
const result = deps.installAgentsImpl({
|
|
1236
|
+
scope,
|
|
1237
|
+
projectRoot,
|
|
1238
|
+
agentKeys: [...selectedAgents].sort(),
|
|
1239
|
+
catalogRoots: options.catalogRoots
|
|
1240
|
+
});
|
|
1241
|
+
const validationReport = deps.runDoctorImpl({
|
|
1242
|
+
projectRoot,
|
|
1243
|
+
scope,
|
|
1244
|
+
catalogRoots: options.catalogRoots
|
|
1245
|
+
});
|
|
1246
|
+
console.log(formatResultSummary(result, validationReport));
|
|
1247
|
+
return validationReport.issues.length === 0 ? 0 : 1;
|
|
1248
|
+
}
|
|
1249
|
+
} catch (error) {
|
|
1250
|
+
if (error instanceof GenerationError) {
|
|
1251
|
+
console.error(`error: ${error.message}`);
|
|
1252
|
+
return 1;
|
|
1253
|
+
}
|
|
1254
|
+
const errorName = error instanceof Error ? error.name : "";
|
|
1255
|
+
if (errorName === "ExitPromptError") {
|
|
1256
|
+
return 130;
|
|
1257
|
+
}
|
|
1258
|
+
throw error;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// src/usage.ts
|
|
1263
|
+
var UsageError = class extends Error {
|
|
1264
|
+
};
|
|
1265
|
+
function visibleInstalledAgents(options) {
|
|
1266
|
+
const agents = getAgents({
|
|
1267
|
+
projectRoot: options.projectRoot,
|
|
1268
|
+
homeDir: options.homeDir,
|
|
1269
|
+
includeProject: options.scope === "project",
|
|
1270
|
+
includeGlobal: true
|
|
1271
|
+
});
|
|
1272
|
+
const visible = agents.filter((agent) => agent.source === "project" || agent.source === "global");
|
|
1273
|
+
if (visible.length === 0) {
|
|
1274
|
+
throw new UsageError("no installed agents were found for the selected scope");
|
|
1275
|
+
}
|
|
1276
|
+
return visible;
|
|
1277
|
+
}
|
|
1278
|
+
function starterPrompt(task, orchestratorKey, workerKeys) {
|
|
1279
|
+
if (orchestratorKey) {
|
|
1280
|
+
if (workerKeys.length > 0) {
|
|
1281
|
+
return `Use ${orchestratorKey} to coordinate this task: "${task}". Delegate to ${workerKeys.join(", ")} when appropriate.`;
|
|
1282
|
+
}
|
|
1283
|
+
return `Use ${orchestratorKey} for this task: "${task}".`;
|
|
1284
|
+
}
|
|
1285
|
+
if (workerKeys.length > 0) {
|
|
1286
|
+
return `For this task: "${task}", use these installed agents as needed: ${workerKeys.join(", ")}.`;
|
|
1287
|
+
}
|
|
1288
|
+
throw new UsageError("no installed agents were found for the selected scope");
|
|
1289
|
+
}
|
|
1290
|
+
function renderUsageGuide(options) {
|
|
1291
|
+
const visibleAgents = visibleInstalledAgents(options);
|
|
1292
|
+
const taskText = (options.task ?? "<describe the task here>").trim();
|
|
1293
|
+
const orchestrators = visibleAgents.filter((agent) => agent.category === ORCHESTRATOR_CATEGORY);
|
|
1294
|
+
let orchestratorKey;
|
|
1295
|
+
if (orchestrators.length > 0) {
|
|
1296
|
+
orchestratorKey = orchestrators.find((agent) => agent.key === DEFAULT_ORCHESTRATOR_KEY)?.key ?? orchestrators[0]?.key;
|
|
1297
|
+
}
|
|
1298
|
+
const workerKeys = visibleAgents.filter((agent) => agent.key !== orchestratorKey).map((agent) => agent.key);
|
|
1299
|
+
const lines = [
|
|
1300
|
+
`scope: ${options.scope}`,
|
|
1301
|
+
"visible installed agents:",
|
|
1302
|
+
...visibleAgents.map((agent) => `- ${agent.key} [${agent.source}] - ${agent.description}`),
|
|
1303
|
+
"",
|
|
1304
|
+
"starter prompt:",
|
|
1305
|
+
starterPrompt(taskText, orchestratorKey, workerKeys),
|
|
1306
|
+
"",
|
|
1307
|
+
"direct prompt ideas:",
|
|
1308
|
+
...visibleAgents.slice(0, 6).map(
|
|
1309
|
+
(agent) => `- Use ${agent.key} for this task: "${taskText}". Focus on: ${agent.description}`
|
|
1310
|
+
),
|
|
1311
|
+
"",
|
|
1312
|
+
"session tip:",
|
|
1313
|
+
"- If Codex spawns a subagent thread, use /agent to inspect or continue that thread."
|
|
1314
|
+
];
|
|
1315
|
+
return lines.join("\n");
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
// src/cli.ts
|
|
1319
|
+
function collectRepeatedOption(value, previous = []) {
|
|
1320
|
+
return [...previous, value];
|
|
1321
|
+
}
|
|
1322
|
+
function buildCatalogCommand() {
|
|
1323
|
+
const catalog = new import_commander.Command("catalog").description("Browse the stable subagent catalog.").argument("[operation]", "Optional operation. Use 'import' for persistent catalog imports.").option("--project-root <path>", "Project root used for project-scope catalog discovery.", ".").option("--scope <scope>", "Catalog visibility scope: project or global.", "project").option(
|
|
1324
|
+
"--catalog-root <path>",
|
|
1325
|
+
"One external awesome-style categories root. Repeat to provide more than one.",
|
|
1326
|
+
collectRepeatedOption,
|
|
1327
|
+
[]
|
|
1328
|
+
).option("--source-name <name>", "Synced source name for catalog sync.", "voltagent").option("--source-root <path>", "Local awesome-style categories root used for catalog sync.").option("--agents <keys>", "Comma-separated agent keys to import.").option("--categories <keys>", "Comma-separated category keys to import.").option("--overwrite", "Overwrite existing imported templates.").action(async (operation, options) => {
|
|
1329
|
+
if (!operation) {
|
|
1330
|
+
console.log(
|
|
1331
|
+
renderCatalogOutput({
|
|
1332
|
+
projectRoot: options.projectRoot,
|
|
1333
|
+
includeProject: options.scope === "project",
|
|
1334
|
+
includeGlobal: true,
|
|
1335
|
+
catalogRoots: options.catalogRoot ?? []
|
|
1336
|
+
})
|
|
1337
|
+
);
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
if (operation === "sync") {
|
|
1341
|
+
try {
|
|
1342
|
+
const result = await syncCatalog({
|
|
1343
|
+
projectRoot: options.projectRoot,
|
|
1344
|
+
scope: options.scope,
|
|
1345
|
+
sourceName: options.sourceName,
|
|
1346
|
+
sourceRoot: options.sourceRoot
|
|
1347
|
+
});
|
|
1348
|
+
console.log(`source: ${result.sourceName}`);
|
|
1349
|
+
console.log(`origin: ${result.sourceLabel}`);
|
|
1350
|
+
console.log(`target: ${result.targetRoot}`);
|
|
1351
|
+
console.log(`files: ${result.copiedPaths.length}`);
|
|
1352
|
+
for (const path of result.copiedPaths.slice(0, 10)) {
|
|
1353
|
+
console.log(`synced: ${path}`);
|
|
1354
|
+
}
|
|
1355
|
+
if (result.copiedPaths.length > 10) {
|
|
1356
|
+
console.log(`... ${result.copiedPaths.length - 10} more`);
|
|
1357
|
+
}
|
|
1358
|
+
} catch (error) {
|
|
1359
|
+
const message = error instanceof CatalogSyncError ? error.message : String(error);
|
|
1360
|
+
console.error(`error: ${message}`);
|
|
1361
|
+
process.exitCode = 1;
|
|
1362
|
+
}
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
if (operation !== "import") {
|
|
1366
|
+
console.error(`error: unknown catalog operation: ${operation}`);
|
|
1367
|
+
process.exitCode = 1;
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
try {
|
|
1371
|
+
const result = importCatalog({
|
|
1372
|
+
projectRoot: options.projectRoot,
|
|
1373
|
+
scope: options.scope,
|
|
1374
|
+
catalogRoots: options.catalogRoot ?? [],
|
|
1375
|
+
agentKeys: (options.agents ?? "").split(",").map((item) => item.trim()).filter(Boolean),
|
|
1376
|
+
categoryKeys: (options.categories ?? "").split(",").map((item) => item.trim()).filter(Boolean),
|
|
1377
|
+
overwrite: options.overwrite
|
|
1378
|
+
});
|
|
1379
|
+
console.log(`target: ${result.targetRoot}`);
|
|
1380
|
+
if (result.importedCategoryKeys.length > 0) {
|
|
1381
|
+
console.log(`categories: ${result.importedCategoryKeys.join(", ")}`);
|
|
1382
|
+
}
|
|
1383
|
+
if (result.importedAgentKeys.length > 0) {
|
|
1384
|
+
console.log(`agents: ${result.importedAgentKeys.join(", ")}`);
|
|
1385
|
+
}
|
|
1386
|
+
for (const path of result.createdPaths) {
|
|
1387
|
+
console.log(`created: ${path}`);
|
|
1388
|
+
}
|
|
1389
|
+
for (const path of result.preservedPaths) {
|
|
1390
|
+
console.log(`preserved: ${path}`);
|
|
1391
|
+
}
|
|
1392
|
+
} catch (error) {
|
|
1393
|
+
const message = error instanceof CatalogImportError ? error.message : String(error);
|
|
1394
|
+
console.error(`error: ${message}`);
|
|
1395
|
+
process.exitCode = 1;
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1398
|
+
return catalog;
|
|
1399
|
+
}
|
|
1400
|
+
function buildTemplateCommand() {
|
|
1401
|
+
const template = new import_commander.Command("template").description("Scaffold custom catalog templates.");
|
|
1402
|
+
template.command("init").description("Create a category README and agent TOML skeleton.").option("--project-root <path>", "Project root used for project-scope templates.", ".").option("--scope <scope>", "Template target scope: project or global.", "project").option("--catalog-root <path>", "Explicit external categories root.").requiredOption("--category <key>", "Category key, for example custom-ops.").option("--category-prefix <prefix>", "Numeric category prefix, for example 11.").option("--category-title <title>", "Display title for the category.").option("--category-description <description>", "Short category description.").requiredOption("--agent <key>", "Agent key, for example custom-coordinator.").option("--agent-name <name>", "Human-friendly agent name.").option("--agent-description <description>", "Short agent description.").option("--model <model>", "Model name used in the generated TOML.", "gpt-5.4").option("--reasoning-effort <level>", "Model reasoning effort.", "medium").option("--sandbox-mode <mode>", "Sandbox mode stored in the generated TOML.", "read-only").option("--orchestrator", "Mark the generated agent as a root orchestrator template.").option("--overwrite", "Overwrite existing template files.").action((options) => {
|
|
1403
|
+
try {
|
|
1404
|
+
const result = initTemplate({
|
|
1405
|
+
projectRoot: options.projectRoot,
|
|
1406
|
+
scope: options.scope,
|
|
1407
|
+
catalogRoot: options.catalogRoot,
|
|
1408
|
+
categoryKey: options.category,
|
|
1409
|
+
categoryPrefix: options.categoryPrefix,
|
|
1410
|
+
categoryTitle: options.categoryTitle,
|
|
1411
|
+
categoryDescription: options.categoryDescription,
|
|
1412
|
+
agentKey: options.agent,
|
|
1413
|
+
agentName: options.agentName,
|
|
1414
|
+
agentDescription: options.agentDescription,
|
|
1415
|
+
model: options.model,
|
|
1416
|
+
reasoningEffort: options.reasoningEffort,
|
|
1417
|
+
sandboxMode: options.sandboxMode,
|
|
1418
|
+
orchestrator: options.orchestrator,
|
|
1419
|
+
overwrite: options.overwrite
|
|
1420
|
+
});
|
|
1421
|
+
console.log(`target: ${result.targetRoot}`);
|
|
1422
|
+
console.log(`category: ${result.categoryDir}`);
|
|
1423
|
+
console.log(`agent: ${result.agentPath}`);
|
|
1424
|
+
for (const path of result.createdPaths) {
|
|
1425
|
+
console.log(`created: ${path}`);
|
|
1426
|
+
}
|
|
1427
|
+
for (const path of result.preservedPaths) {
|
|
1428
|
+
console.log(`preserved: ${path}`);
|
|
1429
|
+
}
|
|
1430
|
+
} catch (error) {
|
|
1431
|
+
const message = error instanceof TemplateError ? error.message : String(error);
|
|
1432
|
+
console.error(`error: ${message}`);
|
|
1433
|
+
process.exitCode = 1;
|
|
1434
|
+
}
|
|
1435
|
+
});
|
|
1436
|
+
return template;
|
|
1437
|
+
}
|
|
1438
|
+
function buildProgram() {
|
|
1439
|
+
const program = new import_commander.Command();
|
|
1440
|
+
program.name("codex-subagent-kit").description("Install and manage Codex subagent catalogs, templates, and TOML definitions.").addHelpText(
|
|
1441
|
+
"after",
|
|
1442
|
+
[
|
|
1443
|
+
"",
|
|
1444
|
+
`Stable commands available: ${STABLE_COMMANDS.join(", ")}`
|
|
1445
|
+
].join("\n")
|
|
1446
|
+
).action(async () => {
|
|
1447
|
+
process.exitCode = await runTui((0, import_node_path8.resolve)(process.cwd()), {
|
|
1448
|
+
catalogRoots: []
|
|
1449
|
+
});
|
|
1450
|
+
});
|
|
1451
|
+
program.command("install").description("Install selected subagents without the TUI.").requiredOption("--scope <scope>", "Install target scope: project or global.").requiredOption("--agents <keys>", "Comma-separated agent keys.").option("--project-root <path>", "Project root used for project-scope installs.", ".").option(
|
|
1452
|
+
"--catalog-root <path>",
|
|
1453
|
+
"One external awesome-style categories root. Repeat to provide more than one.",
|
|
1454
|
+
collectRepeatedOption,
|
|
1455
|
+
[]
|
|
1456
|
+
).option("--overwrite", "Overwrite existing generated agent files.").option("--validate", "Run doctor immediately after install.").action((options) => {
|
|
1457
|
+
try {
|
|
1458
|
+
const result = installAgents({
|
|
1459
|
+
scope: options.scope,
|
|
1460
|
+
projectRoot: options.projectRoot,
|
|
1461
|
+
agentKeys: options.agents.split(",").map((item) => item.trim()).filter(Boolean),
|
|
1462
|
+
catalogRoots: options.catalogRoot ?? [],
|
|
1463
|
+
overwrite: options.overwrite
|
|
1464
|
+
});
|
|
1465
|
+
console.log(`target: ${resolveTargetDir(options.scope, options.projectRoot)}`);
|
|
1466
|
+
for (const path of result.agentPaths) {
|
|
1467
|
+
console.log(path);
|
|
1468
|
+
}
|
|
1469
|
+
for (const path of result.agentPreservedPaths) {
|
|
1470
|
+
console.log(`agent preserved: ${path}`);
|
|
1471
|
+
}
|
|
1472
|
+
if (!options.validate) {
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
const report = runDoctor({
|
|
1476
|
+
projectRoot: options.projectRoot,
|
|
1477
|
+
scope: options.scope,
|
|
1478
|
+
catalogRoots: options.catalogRoot ?? []
|
|
1479
|
+
});
|
|
1480
|
+
console.log("");
|
|
1481
|
+
console.log(renderDoctorReport(report));
|
|
1482
|
+
if (report.issues.length > 0) {
|
|
1483
|
+
process.exitCode = 1;
|
|
1484
|
+
}
|
|
1485
|
+
} catch (error) {
|
|
1486
|
+
const message = error instanceof GenerationError ? error.message : String(error);
|
|
1487
|
+
console.error(`error: ${message}`);
|
|
1488
|
+
process.exitCode = 1;
|
|
1489
|
+
}
|
|
1490
|
+
});
|
|
1491
|
+
program.command("doctor").description("Validate installed agent definitions and injected catalog roots.").option("--project-root <path>", "Project root used for project-scope validation.", ".").option("--scope <scope>", "Validation scope: project or global.", "project").option(
|
|
1492
|
+
"--catalog-root <path>",
|
|
1493
|
+
"One external awesome-style categories root. Repeat to provide more than one.",
|
|
1494
|
+
collectRepeatedOption,
|
|
1495
|
+
[]
|
|
1496
|
+
).action((options) => {
|
|
1497
|
+
const report = runDoctor({
|
|
1498
|
+
projectRoot: options.projectRoot,
|
|
1499
|
+
scope: options.scope,
|
|
1500
|
+
catalogRoots: options.catalogRoot ?? []
|
|
1501
|
+
});
|
|
1502
|
+
console.log(renderDoctorReport(report));
|
|
1503
|
+
if (report.issues.length > 0) {
|
|
1504
|
+
process.exitCode = 1;
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
program.command("usage").description("Render starter prompts for the installed agents visible in the selected scope.").option("--project-root <path>", "Project root used for project-scope usage output.", ".").option("--scope <scope>", "Usage scope: project or global.", "project").option("--task <description>", "Task description to embed in the starter prompt.").action((options) => {
|
|
1508
|
+
try {
|
|
1509
|
+
console.log(
|
|
1510
|
+
renderUsageGuide({
|
|
1511
|
+
projectRoot: options.projectRoot,
|
|
1512
|
+
scope: options.scope,
|
|
1513
|
+
task: options.task
|
|
1514
|
+
})
|
|
1515
|
+
);
|
|
1516
|
+
} catch (error) {
|
|
1517
|
+
const message = error instanceof UsageError ? error.message : String(error);
|
|
1518
|
+
console.error(`error: ${message}`);
|
|
1519
|
+
process.exitCode = 1;
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
program.command("tui").description("Run the interactive install-first TUI.").option("--project-root <path>", "Project root used for the install session.", ".").option(
|
|
1523
|
+
"--catalog-root <path>",
|
|
1524
|
+
"One external awesome-style categories root. Repeat to provide more than one.",
|
|
1525
|
+
collectRepeatedOption,
|
|
1526
|
+
[]
|
|
1527
|
+
).action(async (options) => {
|
|
1528
|
+
process.exitCode = await runTui((0, import_node_path8.resolve)(options.projectRoot), {
|
|
1529
|
+
catalogRoots: options.catalogRoot ?? []
|
|
1530
|
+
});
|
|
1531
|
+
});
|
|
1532
|
+
program.addCommand(buildCatalogCommand());
|
|
1533
|
+
program.addCommand(buildTemplateCommand());
|
|
1534
|
+
return program;
|
|
1535
|
+
}
|
|
1536
|
+
async function main(argv = process.argv) {
|
|
1537
|
+
const program = buildProgram();
|
|
1538
|
+
await program.parseAsync(argv);
|
|
1539
|
+
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
1540
|
+
}
|
|
1541
|
+
if (require.main === module) {
|
|
1542
|
+
void main().then((exitCode) => {
|
|
1543
|
+
process.exitCode = exitCode;
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1547
|
+
0 && (module.exports = {
|
|
1548
|
+
buildProgram,
|
|
1549
|
+
main
|
|
1550
|
+
});
|