@knid/agentx 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/dist/index.js ADDED
@@ -0,0 +1,2142 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command as Command19 } from "commander";
5
+ import { readFileSync as readFileSync12 } from "fs";
6
+ import { fileURLToPath as fileURLToPath2 } from "url";
7
+ import { dirname as dirname2, join as join17 } from "path";
8
+
9
+ // src/commands/run.ts
10
+ import { Command } from "commander";
11
+
12
+ // src/runtime/runner.ts
13
+ import { execa } from "execa";
14
+ import { unlinkSync, existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
15
+
16
+ // src/config/agent-config.ts
17
+ import { readFileSync, existsSync } from "fs";
18
+ import { join as join2, resolve } from "path";
19
+ import { parse } from "yaml";
20
+
21
+ // src/schemas/agent-yaml.ts
22
+ import { z } from "zod";
23
+ var VALID_CATEGORIES = [
24
+ "productivity",
25
+ "devtools",
26
+ "communication",
27
+ "data",
28
+ "writing",
29
+ "research",
30
+ "automation",
31
+ "security",
32
+ "monitoring",
33
+ "other"
34
+ ];
35
+ var AGENT_NAME_REGEX = /^[a-z0-9-]+$/;
36
+ var SEMVER_REGEX = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$/;
37
+ var mcpServerSchema = z.object({
38
+ command: z.string(),
39
+ args: z.array(z.string()).optional(),
40
+ env: z.record(z.string()).optional()
41
+ });
42
+ var secretSchema = z.object({
43
+ name: z.string(),
44
+ description: z.string().optional(),
45
+ required: z.boolean().default(true)
46
+ });
47
+ var permissionsSchema = z.object({
48
+ filesystem: z.boolean().optional(),
49
+ network: z.boolean().optional(),
50
+ execute_commands: z.boolean().optional()
51
+ });
52
+ var configOptionSchema = z.object({
53
+ key: z.string(),
54
+ description: z.string().optional(),
55
+ default: z.string().optional()
56
+ });
57
+ var exampleSchema = z.object({
58
+ prompt: z.string(),
59
+ description: z.string().optional()
60
+ });
61
+ var requiresSchema = z.object({
62
+ claude_cli: z.string().optional(),
63
+ node: z.string().optional(),
64
+ os: z.array(z.string()).optional()
65
+ });
66
+ var agentYamlSchema = z.object({
67
+ name: z.string().min(1).max(100).regex(AGENT_NAME_REGEX, "Name must contain only lowercase letters, numbers, and hyphens"),
68
+ version: z.string().regex(SEMVER_REGEX, "Version must be valid semver (e.g. 1.0.0)"),
69
+ description: z.string().min(1).max(500),
70
+ author: z.string().refine((val) => val.startsWith("@"), {
71
+ message: "Author must start with @"
72
+ }),
73
+ license: z.string().default("MIT"),
74
+ tags: z.array(z.string()).max(10).optional(),
75
+ category: z.enum(VALID_CATEGORIES).optional(),
76
+ requires: requiresSchema.optional(),
77
+ mcp_servers: z.record(mcpServerSchema).optional(),
78
+ secrets: z.array(secretSchema).optional(),
79
+ permissions: permissionsSchema.optional(),
80
+ config: z.array(configOptionSchema).optional(),
81
+ examples: z.array(exampleSchema).optional()
82
+ });
83
+
84
+ // src/config/paths.ts
85
+ import { homedir } from "os";
86
+ import { join } from "path";
87
+ var AGENTX_HOME = join(homedir(), ".agentx");
88
+ var AGENTS_DIR = join(AGENTX_HOME, "agents");
89
+ var SECRETS_DIR = join(AGENTX_HOME, "secrets");
90
+ var CONFIG_PATH = join(AGENTX_HOME, "config.yaml");
91
+ var AUTH_PATH = join(AGENTX_HOME, "auth.json");
92
+ var CACHE_DIR = join(AGENTX_HOME, "cache");
93
+ var LOGS_DIR = join(AGENTX_HOME, "logs");
94
+
95
+ // src/utils/errors.ts
96
+ var AgentxError = class extends Error {
97
+ constructor(message) {
98
+ super(message);
99
+ this.name = "AgentxError";
100
+ }
101
+ };
102
+ var AgentNotFoundError = class extends AgentxError {
103
+ constructor(agentName) {
104
+ super(`Agent not found: ${agentName}`);
105
+ this.name = "AgentNotFoundError";
106
+ }
107
+ };
108
+ var ConfigError = class extends AgentxError {
109
+ constructor(message) {
110
+ super(message);
111
+ this.name = "ConfigError";
112
+ }
113
+ };
114
+ var AuthError = class extends AgentxError {
115
+ constructor(message) {
116
+ super(message);
117
+ this.name = "AuthError";
118
+ }
119
+ };
120
+ var RegistryError = class extends AgentxError {
121
+ constructor(message, statusCode) {
122
+ super(message);
123
+ this.statusCode = statusCode;
124
+ this.name = "RegistryError";
125
+ }
126
+ };
127
+ var ValidationError = class extends AgentxError {
128
+ constructor(message, details = []) {
129
+ super(message);
130
+ this.details = details;
131
+ this.name = "ValidationError";
132
+ }
133
+ };
134
+
135
+ // src/config/agent-config.ts
136
+ function getAgentDir(agentName) {
137
+ if (agentName === "." || agentName.startsWith("/") || agentName.startsWith("./")) {
138
+ return resolve(agentName);
139
+ }
140
+ return join2(AGENTS_DIR, agentName);
141
+ }
142
+ function loadAgentYaml(agentDir) {
143
+ const yamlPath = join2(agentDir, "agent.yaml");
144
+ if (!existsSync(yamlPath)) {
145
+ throw new AgentNotFoundError(agentDir);
146
+ }
147
+ try {
148
+ const raw = readFileSync(yamlPath, "utf-8");
149
+ const parsed = parse(raw);
150
+ return agentYamlSchema.parse(parsed);
151
+ } catch (error) {
152
+ if (error instanceof Error && error.name === "ZodError") {
153
+ throw new ValidationError(`Invalid agent.yaml in ${agentDir}`, [error.message]);
154
+ }
155
+ throw error;
156
+ }
157
+ }
158
+ function loadSystemPrompt(agentDir) {
159
+ const promptPath = join2(agentDir, "system-prompt.md");
160
+ if (!existsSync(promptPath)) {
161
+ throw new ValidationError(`Missing system-prompt.md in ${agentDir}`);
162
+ }
163
+ return readFileSync(promptPath, "utf-8");
164
+ }
165
+ function loadAgentConfig(agentName) {
166
+ const agentDir = getAgentDir(agentName);
167
+ const manifest = loadAgentYaml(agentDir);
168
+ const systemPrompt = loadSystemPrompt(agentDir);
169
+ return {
170
+ manifest,
171
+ systemPrompt,
172
+ agentDir
173
+ };
174
+ }
175
+
176
+ // src/config/global-config.ts
177
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
178
+ import { parse as parse2, stringify } from "yaml";
179
+
180
+ // src/schemas/config.ts
181
+ import { z as z2 } from "zod";
182
+ var globalConfigSchema = z2.object({
183
+ registry: z2.string().url().default("https://agentx-web.vercel.app"),
184
+ claude_path: z2.string().default("claude"),
185
+ default_output: z2.enum(["text", "json"]).default("text"),
186
+ telemetry: z2.boolean().default(true),
187
+ auto_update: z2.boolean().default(true),
188
+ claude_defaults: z2.object({
189
+ max_turns: z2.number().int().positive().default(10)
190
+ }).default({})
191
+ });
192
+ var DEFAULT_CONFIG = globalConfigSchema.parse({});
193
+
194
+ // src/utils/init-dirs.ts
195
+ import { mkdirSync, existsSync as existsSync2 } from "fs";
196
+ var REQUIRED_DIRS = [AGENTX_HOME, AGENTS_DIR, SECRETS_DIR, CACHE_DIR, LOGS_DIR];
197
+ function ensureDirectories() {
198
+ for (const dir of REQUIRED_DIRS) {
199
+ if (!existsSync2(dir)) {
200
+ mkdirSync(dir, { recursive: true });
201
+ }
202
+ }
203
+ }
204
+
205
+ // src/config/global-config.ts
206
+ function loadGlobalConfig() {
207
+ ensureDirectories();
208
+ if (!existsSync3(CONFIG_PATH)) {
209
+ return DEFAULT_CONFIG;
210
+ }
211
+ try {
212
+ const raw = readFileSync2(CONFIG_PATH, "utf-8");
213
+ const parsed = parse2(raw);
214
+ return globalConfigSchema.parse(parsed);
215
+ } catch (error) {
216
+ if (error instanceof Error && error.name === "ZodError") {
217
+ throw new ConfigError(`Invalid config at ${CONFIG_PATH}: ${error.message}`);
218
+ }
219
+ throw new ConfigError(`Failed to read config: ${error.message}`);
220
+ }
221
+ }
222
+ function saveGlobalConfig(config) {
223
+ ensureDirectories();
224
+ try {
225
+ const yaml = stringify(config);
226
+ writeFileSync(CONFIG_PATH, yaml, "utf-8");
227
+ } catch (error) {
228
+ throw new ConfigError(`Failed to save config: ${error.message}`);
229
+ }
230
+ }
231
+ function getConfigValue(key) {
232
+ const config = loadGlobalConfig();
233
+ return config[key];
234
+ }
235
+ function setConfigValue(key, value) {
236
+ const config = loadGlobalConfig();
237
+ config[key] = value;
238
+ saveGlobalConfig(config);
239
+ }
240
+
241
+ // src/runtime/prompt-processor.ts
242
+ function processSystemPrompt(template, configOptions, overrides) {
243
+ return template.replace(/\{\{config\.(\w+)\}\}/g, (_match, key) => {
244
+ if (key in overrides) {
245
+ return overrides[key];
246
+ }
247
+ const option = configOptions.find((opt) => opt.key === key);
248
+ if (option?.default !== void 0) {
249
+ return option.default;
250
+ }
251
+ return "";
252
+ });
253
+ }
254
+
255
+ // src/runtime/mcp-builder.ts
256
+ import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, chmodSync } from "fs";
257
+ import { join as join3 } from "path";
258
+ import { tmpdir } from "os";
259
+ import { randomUUID } from "crypto";
260
+ function resolveMCPConfig(servers, secrets) {
261
+ const resolved = {};
262
+ for (const [name, server] of Object.entries(servers)) {
263
+ const resolvedEnv = {};
264
+ if (server.env) {
265
+ for (const [envKey, envValue] of Object.entries(server.env)) {
266
+ resolvedEnv[envKey] = envValue.replace(
267
+ /\$\{secrets\.(\w+)\}/g,
268
+ (_match, secretKey) => secrets[secretKey] ?? ""
269
+ );
270
+ }
271
+ }
272
+ resolved[name] = {
273
+ command: server.command,
274
+ ...server.args ? { args: server.args } : {},
275
+ ...server.env ? { env: resolvedEnv } : {}
276
+ };
277
+ }
278
+ return resolved;
279
+ }
280
+ async function writeTempMCPConfig(servers) {
281
+ const tempDir = join3(tmpdir(), "agentx-mcp");
282
+ mkdirSync2(tempDir, { recursive: true });
283
+ const configPath = join3(tempDir, `${randomUUID()}.json`);
284
+ const config = { mcpServers: servers };
285
+ writeFileSync2(configPath, JSON.stringify(config, null, 2), "utf-8");
286
+ chmodSync(configPath, 384);
287
+ return configPath;
288
+ }
289
+
290
+ // src/runtime/pipe-handler.ts
291
+ async function detectPipedInput() {
292
+ return !process.stdin.isTTY;
293
+ }
294
+ async function readPipedInput() {
295
+ return new Promise((resolve6, reject) => {
296
+ let data = "";
297
+ process.stdin.setEncoding("utf-8");
298
+ process.stdin.on("data", (chunk) => {
299
+ data += chunk;
300
+ });
301
+ process.stdin.on("end", () => resolve6(data));
302
+ process.stdin.on("error", reject);
303
+ });
304
+ }
305
+ function buildPromptWithPipe(prompt, pipedContent) {
306
+ if (!pipedContent) {
307
+ return prompt;
308
+ }
309
+ if (!prompt) {
310
+ return pipedContent;
311
+ }
312
+ return `${pipedContent}
313
+
314
+ ${prompt}`;
315
+ }
316
+
317
+ // src/runtime/output-formatter.ts
318
+ function formatOutput(data, format) {
319
+ if (format === "json") {
320
+ try {
321
+ const parsed = JSON.parse(data);
322
+ return JSON.stringify(parsed, null, 2);
323
+ } catch {
324
+ return JSON.stringify({ output: data });
325
+ }
326
+ }
327
+ return data;
328
+ }
329
+
330
+ // src/telemetry/reporter.ts
331
+ function sendTelemetry(event) {
332
+ try {
333
+ const config = loadGlobalConfig();
334
+ if (!config.telemetry) {
335
+ return;
336
+ }
337
+ const url = `${config.registry}/api/v1/telemetry`;
338
+ fetch(url, {
339
+ method: "POST",
340
+ headers: { "Content-Type": "application/json" },
341
+ body: JSON.stringify(event),
342
+ signal: AbortSignal.timeout(5e3)
343
+ }).catch(() => {
344
+ });
345
+ } catch {
346
+ }
347
+ }
348
+
349
+ // src/runtime/runner.ts
350
+ function buildClaudeArgs(options) {
351
+ const args = [];
352
+ if (!options.interactive && options.prompt) {
353
+ args.push("-p", options.prompt);
354
+ }
355
+ args.push("--system-prompt", options.systemPrompt);
356
+ if (options.mcpConfigPath) {
357
+ args.push("--mcp-config", options.mcpConfigPath);
358
+ }
359
+ if (options.maxTurns !== void 0) {
360
+ args.push("--max-turns", String(options.maxTurns));
361
+ }
362
+ if (options.outputFormat) {
363
+ args.push("--output-format", options.outputFormat);
364
+ }
365
+ return args;
366
+ }
367
+ async function runAgent(agentName, options) {
368
+ const agentConfig = loadAgentConfig(agentName);
369
+ const globalConfig = loadGlobalConfig();
370
+ const { manifest, systemPrompt, agentDir } = agentConfig;
371
+ const processedPrompt = processSystemPrompt(
372
+ systemPrompt,
373
+ manifest.config ?? [],
374
+ options.configOverrides ?? {}
375
+ );
376
+ let mcpConfigPath;
377
+ if (manifest.mcp_servers && Object.keys(manifest.mcp_servers).length > 0) {
378
+ const secrets = {};
379
+ const resolved = resolveMCPConfig(manifest.mcp_servers, secrets);
380
+ mcpConfigPath = await writeTempMCPConfig(resolved);
381
+ }
382
+ const startTime = Date.now();
383
+ const agentFullName = `@${manifest.author?.replace(/^@/, "") ?? "local"}/${manifest.name}`;
384
+ try {
385
+ let finalPrompt = options.prompt ?? "";
386
+ if (!options.interactive) {
387
+ const isPiped = await detectPipedInput();
388
+ if (isPiped) {
389
+ const pipedContent = await readPipedInput();
390
+ finalPrompt = buildPromptWithPipe(finalPrompt, pipedContent);
391
+ }
392
+ if (options.file) {
393
+ const fileContent = readFileSync3(options.file, "utf-8");
394
+ finalPrompt = buildPromptWithPipe(finalPrompt, fileContent);
395
+ }
396
+ }
397
+ const claudeArgs = buildClaudeArgs({
398
+ prompt: options.interactive ? void 0 : finalPrompt,
399
+ systemPrompt: processedPrompt,
400
+ mcpConfigPath,
401
+ maxTurns: globalConfig.claude_defaults.max_turns,
402
+ outputFormat: options.outputFormat ?? globalConfig.default_output,
403
+ interactive: options.interactive
404
+ });
405
+ if (options.debug) {
406
+ console.error(`[debug] claude ${claudeArgs.join(" ")}`);
407
+ }
408
+ const claudePath = globalConfig.claude_path;
409
+ if (options.interactive) {
410
+ await execa(claudePath, claudeArgs, { stdio: "inherit" });
411
+ sendTelemetry({
412
+ agent: agentFullName,
413
+ version: manifest.version,
414
+ success: true,
415
+ duration_ms: Date.now() - startTime
416
+ });
417
+ return "";
418
+ }
419
+ const result = await execa(claudePath, claudeArgs);
420
+ const output = formatOutput(result.stdout, options.outputFormat ?? "text");
421
+ if (!options.quiet) {
422
+ process.stdout.write(output);
423
+ }
424
+ sendTelemetry({
425
+ agent: agentFullName,
426
+ version: manifest.version,
427
+ success: true,
428
+ duration_ms: Date.now() - startTime
429
+ });
430
+ return output;
431
+ } catch (error) {
432
+ sendTelemetry({
433
+ agent: agentFullName,
434
+ version: manifest.version,
435
+ success: false,
436
+ duration_ms: Date.now() - startTime,
437
+ error_type: error instanceof Error ? error.constructor.name : "UnknownError"
438
+ });
439
+ throw error;
440
+ } finally {
441
+ if (mcpConfigPath && existsSync4(mcpConfigPath)) {
442
+ try {
443
+ unlinkSync(mcpConfigPath);
444
+ } catch {
445
+ }
446
+ }
447
+ }
448
+ }
449
+
450
+ // src/ui/colors.ts
451
+ import chalk from "chalk";
452
+ var noColor = process.env.NO_COLOR !== void 0 || process.argv.includes("--no-color");
453
+ var identity = (s) => s;
454
+ var colors = {
455
+ success: noColor ? identity : chalk.green,
456
+ error: noColor ? identity : chalk.red,
457
+ warn: noColor ? identity : chalk.yellow,
458
+ info: noColor ? identity : chalk.blue,
459
+ dim: noColor ? identity : chalk.dim,
460
+ bold: noColor ? identity : chalk.bold,
461
+ cyan: noColor ? identity : chalk.cyan,
462
+ magenta: noColor ? identity : chalk.magenta
463
+ };
464
+
465
+ // src/commands/run.ts
466
+ var runCommand = new Command("run").description("Run a local or installed agent").argument("<agent>", 'Agent name, path, or "." for current directory').argument("[prompt...]", "Prompt to send to the agent").option("-i, --interactive", "Run in interactive mode (stdin/stdout)").option("--file <path>", "Include file content in the prompt").option("--json", "Output in JSON format").option("--quiet", "Suppress output (useful for scripting)").option("--debug", "Show debug information").option("--output-format <format>", "Output format: text or json", "text").addHelpText("after", `
467
+ Examples:
468
+ $ agentx run data-analyst "summarize this data" --file data.csv
469
+ $ agentx run . "test prompt"
470
+ $ cat report.csv | agentx run data-analyst "find anomalies"
471
+ $ agentx run gmail-agent -i`).action(async (agent, promptParts, options) => {
472
+ try {
473
+ const prompt = promptParts.join(" ");
474
+ const outputFormat = options.json ? "json" : options.outputFormat;
475
+ await runAgent(agent, {
476
+ prompt: prompt || void 0,
477
+ interactive: options.interactive ?? (!prompt && !options.file),
478
+ file: options.file,
479
+ outputFormat,
480
+ quiet: options.quiet,
481
+ debug: options.debug
482
+ });
483
+ } catch (error) {
484
+ if (error instanceof Error) {
485
+ console.error(colors.error(`Error: ${error.message}`));
486
+ if (options.debug && error.stack) {
487
+ console.error(colors.dim(error.stack));
488
+ }
489
+ }
490
+ process.exit(1);
491
+ }
492
+ });
493
+
494
+ // src/commands/configure.ts
495
+ import { Command as Command2 } from "commander";
496
+
497
+ // src/secrets/configure-flow.ts
498
+ import * as p from "@clack/prompts";
499
+
500
+ // src/secrets/store.ts
501
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3 } from "fs";
502
+ import { join as join4 } from "path";
503
+
504
+ // src/secrets/encrypt.ts
505
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
506
+ import { execFileSync } from "child_process";
507
+ import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
508
+ import { platform, hostname, homedir as homedir2 } from "os";
509
+ var ALGORITHM = "aes-256-gcm";
510
+ var IV_LENGTH = 12;
511
+ var KEY_LENGTH = 32;
512
+ var SALT = "agentx-salt";
513
+ function getMachineId() {
514
+ const os = platform();
515
+ if (os === "linux" && existsSync5("/etc/machine-id")) {
516
+ return readFileSync4("/etc/machine-id", "utf-8").trim();
517
+ }
518
+ if (os === "darwin") {
519
+ try {
520
+ const output = execFileSync(
521
+ "ioreg",
522
+ ["-rd1", "-c", "IOPlatformExpertDevice"],
523
+ { encoding: "utf-8" }
524
+ );
525
+ const match = output.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
526
+ if (match) return match[1];
527
+ } catch {
528
+ }
529
+ }
530
+ return `${hostname()}-${homedir2()}`;
531
+ }
532
+ function deriveKey() {
533
+ const machineId = getMachineId();
534
+ return scryptSync(machineId, SALT, KEY_LENGTH);
535
+ }
536
+ async function encrypt(secrets) {
537
+ const key = deriveKey();
538
+ const iv = randomBytes(IV_LENGTH);
539
+ const cipher = createCipheriv(ALGORITHM, key, iv);
540
+ const plaintext = JSON.stringify(secrets);
541
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
542
+ const tag = cipher.getAuthTag();
543
+ return {
544
+ iv: iv.toString("hex"),
545
+ tag: tag.toString("hex"),
546
+ data: encrypted.toString("hex")
547
+ };
548
+ }
549
+ async function decrypt(store) {
550
+ const key = deriveKey();
551
+ const iv = Buffer.from(store.iv, "hex");
552
+ const tag = Buffer.from(store.tag, "hex");
553
+ const data = Buffer.from(store.data, "hex");
554
+ const decipher = createDecipheriv(ALGORITHM, key, iv);
555
+ decipher.setAuthTag(tag);
556
+ const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
557
+ return JSON.parse(decrypted.toString("utf-8"));
558
+ }
559
+
560
+ // src/secrets/store.ts
561
+ function getSecretsFilePath(agentName, secretsDir) {
562
+ return join4(secretsDir, `${agentName}.enc.json`);
563
+ }
564
+ async function saveSecrets(agentName, secrets, secretsDir = SECRETS_DIR) {
565
+ mkdirSync3(secretsDir, { recursive: true });
566
+ const encrypted = await encrypt(secrets);
567
+ const filePath = getSecretsFilePath(agentName, secretsDir);
568
+ writeFileSync3(filePath, JSON.stringify(encrypted, null, 2), "utf-8");
569
+ }
570
+ async function loadSecrets(agentName, secretsDir = SECRETS_DIR) {
571
+ const filePath = getSecretsFilePath(agentName, secretsDir);
572
+ if (!existsSync6(filePath)) {
573
+ return {};
574
+ }
575
+ const raw = readFileSync5(filePath, "utf-8");
576
+ const store = JSON.parse(raw);
577
+ return decrypt(store);
578
+ }
579
+ async function deleteSecrets(agentName, secretsDir = SECRETS_DIR) {
580
+ const filePath = getSecretsFilePath(agentName, secretsDir);
581
+ if (existsSync6(filePath)) {
582
+ unlinkSync2(filePath);
583
+ }
584
+ }
585
+
586
+ // src/secrets/configure-flow.ts
587
+ async function runConfigureFlow(options) {
588
+ const { agentName, declarations, secretsDir } = options;
589
+ if (declarations.length === 0) {
590
+ p.log.info("This agent does not require any secrets.");
591
+ return;
592
+ }
593
+ p.intro(`Configure secrets for ${agentName}`);
594
+ const existing = await loadSecrets(agentName, secretsDir);
595
+ const secrets = { ...existing };
596
+ for (const decl of declarations) {
597
+ const currentValue = existing[decl.name];
598
+ const label = decl.description ? `${decl.name} (${decl.description})` : decl.name;
599
+ const hint = currentValue ? "Press enter to keep existing value" : void 0;
600
+ const value = await p.text({
601
+ message: label,
602
+ placeholder: hint,
603
+ validate(input) {
604
+ if (!input && !currentValue && decl.required !== false) {
605
+ return "This secret is required";
606
+ }
607
+ }
608
+ });
609
+ if (p.isCancel(value)) {
610
+ p.cancel("Configuration cancelled.");
611
+ process.exit(0);
612
+ }
613
+ if (value) {
614
+ secrets[decl.name] = value;
615
+ }
616
+ }
617
+ await saveSecrets(agentName, secrets, secretsDir);
618
+ p.outro(`Secrets saved for ${agentName}`);
619
+ }
620
+
621
+ // src/commands/configure.ts
622
+ var configureCommand = new Command2("configure").description("Configure secrets for an agent").argument("<agent>", "Agent name or path").addHelpText("after", `
623
+ Examples:
624
+ $ agentx configure gmail-agent
625
+ $ agentx configure ./my-agent`).action(async (agent) => {
626
+ try {
627
+ const agentDir = getAgentDir(agent);
628
+ const manifest = loadAgentYaml(agentDir);
629
+ if (!manifest.secrets || manifest.secrets.length === 0) {
630
+ console.log(colors.info(`Agent "${manifest.name}" does not declare any secrets.`));
631
+ return;
632
+ }
633
+ await runConfigureFlow({
634
+ agentName: manifest.name,
635
+ declarations: manifest.secrets
636
+ });
637
+ } catch (error) {
638
+ if (error instanceof Error) {
639
+ console.error(colors.error(`Error: ${error.message}`));
640
+ }
641
+ process.exit(1);
642
+ }
643
+ });
644
+
645
+ // src/commands/init.ts
646
+ import { Command as Command3 } from "commander";
647
+ import * as p2 from "@clack/prompts";
648
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync7 } from "fs";
649
+ import { join as join5, resolve as resolve2 } from "path";
650
+ function getTemplatesDir() {
651
+ const packageRoot = resolve2(new URL("..", import.meta.url).pathname);
652
+ const srcPath = join5(packageRoot, "src", "templates", "basic");
653
+ if (existsSync7(srcPath)) return srcPath;
654
+ return join5(packageRoot, "templates", "basic");
655
+ }
656
+ function loadTemplate(templatesDir, filename) {
657
+ return readFileSync6(join5(templatesDir, filename), "utf-8");
658
+ }
659
+ function replaceTemplateVars(content, vars) {
660
+ let result = content;
661
+ for (const [key, value] of Object.entries(vars)) {
662
+ result = result.replaceAll(`{{${key}}}`, value);
663
+ }
664
+ return result;
665
+ }
666
+ var initCommand = new Command3("init").description("Scaffold a new agent project").argument("[directory]", "Directory to create the agent in", ".").addHelpText("after", `
667
+ Examples:
668
+ $ agentx init
669
+ $ agentx init ./my-agent`).action(async (directory) => {
670
+ try {
671
+ p2.intro("Create a new agentx agent");
672
+ const name = await p2.text({
673
+ message: "Agent name",
674
+ placeholder: "my-agent",
675
+ validate(input) {
676
+ if (!input) return "Name is required";
677
+ if (!/^[a-z0-9-]+$/.test(input)) {
678
+ return "Name must contain only lowercase letters, numbers, and hyphens";
679
+ }
680
+ }
681
+ });
682
+ if (p2.isCancel(name)) {
683
+ p2.cancel("Init cancelled.");
684
+ process.exit(0);
685
+ }
686
+ const description = await p2.text({
687
+ message: "Description",
688
+ placeholder: "A helpful AI agent",
689
+ validate(input) {
690
+ if (!input) return "Description is required";
691
+ if (input.length > 500) return "Description must be 500 characters or less";
692
+ }
693
+ });
694
+ if (p2.isCancel(description)) {
695
+ p2.cancel("Init cancelled.");
696
+ process.exit(0);
697
+ }
698
+ const author = await p2.text({
699
+ message: "Author (e.g. @username)",
700
+ placeholder: "@username",
701
+ validate(input) {
702
+ if (!input) return "Author is required";
703
+ if (!input.startsWith("@")) return "Author must start with @";
704
+ }
705
+ });
706
+ if (p2.isCancel(author)) {
707
+ p2.cancel("Init cancelled.");
708
+ process.exit(0);
709
+ }
710
+ const category = await p2.select({
711
+ message: "Category",
712
+ options: VALID_CATEGORIES.map((c) => ({ value: c, label: c }))
713
+ });
714
+ if (p2.isCancel(category)) {
715
+ p2.cancel("Init cancelled.");
716
+ process.exit(0);
717
+ }
718
+ const license = await p2.text({
719
+ message: "License",
720
+ placeholder: "MIT",
721
+ initialValue: "MIT"
722
+ });
723
+ if (p2.isCancel(license)) {
724
+ p2.cancel("Init cancelled.");
725
+ process.exit(0);
726
+ }
727
+ const targetDir = directory === "." ? resolve2(name) : resolve2(directory);
728
+ if (existsSync7(targetDir)) {
729
+ const overwrite = await p2.confirm({
730
+ message: `Directory ${targetDir} already exists. Continue?`
731
+ });
732
+ if (p2.isCancel(overwrite) || !overwrite) {
733
+ p2.cancel("Init cancelled.");
734
+ process.exit(0);
735
+ }
736
+ }
737
+ mkdirSync4(targetDir, { recursive: true });
738
+ const vars = {
739
+ NAME: name,
740
+ DESCRIPTION: description,
741
+ AUTHOR: author,
742
+ CATEGORY: category,
743
+ LICENSE: license,
744
+ YEAR: (/* @__PURE__ */ new Date()).getFullYear().toString()
745
+ };
746
+ const templatesDir = getTemplatesDir();
747
+ const templates = ["agent.yaml", "system-prompt.md", "README.md", "LICENSE"];
748
+ for (const file of templates) {
749
+ const content = loadTemplate(templatesDir, file);
750
+ const populated = replaceTemplateVars(content, vars);
751
+ writeFileSync4(join5(targetDir, file), populated, "utf-8");
752
+ }
753
+ p2.outro(`Agent scaffolded at ${colors.cyan(targetDir)}`);
754
+ console.log();
755
+ console.log(` Next steps:`);
756
+ console.log(` ${colors.dim("1.")} cd ${name}`);
757
+ console.log(` ${colors.dim("2.")} Edit system-prompt.md with your agent's instructions`);
758
+ console.log(` ${colors.dim("3.")} agentx validate`);
759
+ console.log(` ${colors.dim("4.")} agentx run . "test prompt"`);
760
+ } catch (error) {
761
+ if (error instanceof Error) {
762
+ console.error(colors.error(`Error: ${error.message}`));
763
+ }
764
+ process.exit(1);
765
+ }
766
+ });
767
+
768
+ // src/commands/validate.ts
769
+ import { Command as Command4 } from "commander";
770
+ import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
771
+ import { join as join6, resolve as resolve3 } from "path";
772
+ import { parse as parse3 } from "yaml";
773
+ function validateAgentDir(agentDir) {
774
+ const errors = [];
775
+ const warnings = [];
776
+ const yamlPath = join6(agentDir, "agent.yaml");
777
+ if (!existsSync8(yamlPath)) {
778
+ errors.push("agent.yaml not found");
779
+ return { valid: false, errors, warnings };
780
+ }
781
+ try {
782
+ const raw = readFileSync7(yamlPath, "utf-8");
783
+ const parsed = parse3(raw);
784
+ agentYamlSchema.parse(parsed);
785
+ } catch (error) {
786
+ if (error instanceof Error && error.name === "ZodError") {
787
+ const zodError = error;
788
+ for (const issue of zodError.issues) {
789
+ errors.push(`agent.yaml: ${issue.path.join(".")}: ${issue.message}`);
790
+ }
791
+ } else if (error instanceof Error) {
792
+ errors.push(`agent.yaml: ${error.message}`);
793
+ }
794
+ }
795
+ const promptPath = join6(agentDir, "system-prompt.md");
796
+ if (!existsSync8(promptPath)) {
797
+ errors.push("system-prompt.md not found");
798
+ }
799
+ const readmePath = join6(agentDir, "README.md");
800
+ if (!existsSync8(readmePath)) {
801
+ warnings.push("README.md not found (recommended)");
802
+ }
803
+ return {
804
+ valid: errors.length === 0,
805
+ errors,
806
+ warnings
807
+ };
808
+ }
809
+ var validateCommand = new Command4("validate").description("Validate an agent project").argument("[directory]", "Agent directory to validate", ".").addHelpText("after", `
810
+ Examples:
811
+ $ agentx validate
812
+ $ agentx validate ./my-agent`).action(async (directory) => {
813
+ const agentDir = resolve3(directory);
814
+ const result = validateAgentDir(agentDir);
815
+ if (result.valid) {
816
+ console.log(colors.success(" Agent is valid!"));
817
+ } else {
818
+ console.log(colors.error(" Validation failed:"));
819
+ for (const error of result.errors) {
820
+ console.log(colors.error(` ${error}`));
821
+ }
822
+ }
823
+ for (const warning of result.warnings) {
824
+ console.log(colors.warn(` ${warning}`));
825
+ }
826
+ if (!result.valid) {
827
+ process.exit(1);
828
+ }
829
+ });
830
+
831
+ // src/commands/test.ts
832
+ import { Command as Command5 } from "commander";
833
+ import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
834
+ import { join as join7, resolve as resolve4 } from "path";
835
+ import { parse as parse4 } from "yaml";
836
+ import { execa as execa2 } from "execa";
837
+ async function checkMCPServerStartability(servers) {
838
+ const results = [];
839
+ for (const [name, server] of Object.entries(servers)) {
840
+ try {
841
+ await execa2("which", [server.command]);
842
+ results.push({ name, reachable: true });
843
+ } catch {
844
+ results.push({
845
+ name,
846
+ reachable: false,
847
+ error: `Command "${server.command}" not found in PATH`
848
+ });
849
+ }
850
+ }
851
+ return results;
852
+ }
853
+ var testCommand = new Command5("test").description("Validate and test an agent project").argument("[directory]", "Agent directory to test", ".").addHelpText("after", `
854
+ Examples:
855
+ $ agentx test
856
+ $ agentx test ./my-agent`).action(async (directory) => {
857
+ const agentDir = resolve4(directory);
858
+ console.log(colors.bold("Running validation..."));
859
+ const validation = validateAgentDir(agentDir);
860
+ if (validation.valid) {
861
+ console.log(colors.success(" Validation passed"));
862
+ } else {
863
+ console.log(colors.error(" Validation failed:"));
864
+ for (const error of validation.errors) {
865
+ console.log(colors.error(` ${error}`));
866
+ }
867
+ process.exit(1);
868
+ }
869
+ for (const warning of validation.warnings) {
870
+ console.log(colors.warn(` ${warning}`));
871
+ }
872
+ const yamlPath = join7(agentDir, "agent.yaml");
873
+ if (existsSync9(yamlPath)) {
874
+ try {
875
+ const raw = readFileSync8(yamlPath, "utf-8");
876
+ const parsed = parse4(raw);
877
+ const manifest = agentYamlSchema.parse(parsed);
878
+ if (manifest.mcp_servers && Object.keys(manifest.mcp_servers).length > 0) {
879
+ console.log(colors.bold("\nChecking MCP servers..."));
880
+ const mcpResults = await checkMCPServerStartability(manifest.mcp_servers);
881
+ for (const result of mcpResults) {
882
+ if (result.reachable) {
883
+ console.log(colors.success(` ${result.name}: command found`));
884
+ } else {
885
+ console.log(colors.warn(` ${result.name}: ${result.error}`));
886
+ }
887
+ }
888
+ }
889
+ } catch {
890
+ }
891
+ }
892
+ console.log(colors.success("\n All tests passed!"));
893
+ });
894
+
895
+ // src/commands/doctor.ts
896
+ import { Command as Command6 } from "commander";
897
+ import { execa as execa3 } from "execa";
898
+ import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
899
+ import { fileURLToPath } from "url";
900
+ import { dirname, join as join8 } from "path";
901
+ var __filename2 = fileURLToPath(import.meta.url);
902
+ var __dirname2 = dirname(__filename2);
903
+ async function checkClaudeCLI() {
904
+ try {
905
+ const whichResult = await execa3("which", ["claude"]);
906
+ if (!whichResult.stdout || whichResult.exitCode !== 0) {
907
+ return { found: false };
908
+ }
909
+ const claudePath = whichResult.stdout.trim();
910
+ try {
911
+ const versionResult = await execa3("claude", ["--version"]);
912
+ return {
913
+ found: true,
914
+ version: versionResult.stdout.trim(),
915
+ path: claudePath
916
+ };
917
+ } catch {
918
+ return { found: true, path: claudePath };
919
+ }
920
+ } catch {
921
+ return { found: false };
922
+ }
923
+ }
924
+ function checkNodeVersion() {
925
+ const version = process.version.replace(/^v/, "");
926
+ const major = parseInt(version.split(".")[0], 10);
927
+ return {
928
+ version,
929
+ supported: major >= 18
930
+ };
931
+ }
932
+ function checkAgentxVersion() {
933
+ const candidates = [
934
+ join8(__dirname2, "..", "..", "package.json"),
935
+ join8(__dirname2, "..", "package.json")
936
+ ];
937
+ for (const candidate of candidates) {
938
+ if (existsSync10(candidate)) {
939
+ const pkg2 = JSON.parse(readFileSync9(candidate, "utf-8"));
940
+ return { version: pkg2.version };
941
+ }
942
+ }
943
+ return { version: "unknown" };
944
+ }
945
+ async function runDoctor() {
946
+ const results = [];
947
+ const claudeCheck = await checkClaudeCLI();
948
+ results.push({
949
+ label: "Claude CLI",
950
+ pass: claudeCheck.found,
951
+ detail: claudeCheck.found ? `found (v${claudeCheck.version ?? "unknown"})` : "not found - install with: npm install -g @anthropic-ai/claude-code"
952
+ });
953
+ const nodeCheck = checkNodeVersion();
954
+ results.push({
955
+ label: "Node.js",
956
+ pass: nodeCheck.supported,
957
+ detail: nodeCheck.supported ? `v${nodeCheck.version}` : `v${nodeCheck.version} (requires >= 18)`
958
+ });
959
+ const agentxCheck = checkAgentxVersion();
960
+ results.push({
961
+ label: "agentx",
962
+ pass: true,
963
+ detail: `v${agentxCheck.version}`
964
+ });
965
+ return results;
966
+ }
967
+ var doctorCommand = new Command6("doctor").description("Check system requirements and configuration").addHelpText("after", `
968
+ Examples:
969
+ $ agentx doctor`).action(async () => {
970
+ const results = await runDoctor();
971
+ const allPassed = results.every((r) => r.pass);
972
+ for (const result of results) {
973
+ const icon = result.pass ? colors.success(" ") : colors.error(" ");
974
+ console.log(`${icon} ${result.label}: ${result.detail}`);
975
+ }
976
+ console.log();
977
+ if (allPassed) {
978
+ console.log(colors.success(" agentx ready!"));
979
+ } else {
980
+ console.log(colors.error(" Some checks failed. Please fix the issues above."));
981
+ process.exit(1);
982
+ }
983
+ });
984
+
985
+ // src/commands/login.ts
986
+ import { Command as Command7 } from "commander";
987
+
988
+ // src/auth/github-oauth.ts
989
+ import { createServer } from "http";
990
+ import { randomBytes as randomBytes2 } from "crypto";
991
+ import { execFileSync as execFileSync2 } from "child_process";
992
+ import { platform as platform2 } from "os";
993
+ import { ofetch } from "ofetch";
994
+ function openBrowser(url) {
995
+ const os = platform2();
996
+ try {
997
+ switch (os) {
998
+ case "darwin":
999
+ execFileSync2("open", [url]);
1000
+ break;
1001
+ case "linux":
1002
+ execFileSync2("xdg-open", [url]);
1003
+ break;
1004
+ case "win32":
1005
+ execFileSync2("cmd", ["/c", "start", url.replace(/&/g, "^&")]);
1006
+ break;
1007
+ default:
1008
+ throw new AuthError(`Unsupported platform: ${os}. Please open this URL manually: ${url}`);
1009
+ }
1010
+ } catch (error) {
1011
+ if (error instanceof AuthError) throw error;
1012
+ throw new AuthError(`Failed to open browser. Please open this URL manually: ${url}`);
1013
+ }
1014
+ }
1015
+ function parseQuery(url) {
1016
+ const qIndex = url.indexOf("?");
1017
+ if (qIndex === -1) return new URLSearchParams();
1018
+ return new URLSearchParams(url.slice(qIndex + 1));
1019
+ }
1020
+ function startOAuthFlow(registryUrl) {
1021
+ return new Promise((resolve6, reject) => {
1022
+ const state = randomBytes2(16).toString("hex");
1023
+ const server = createServer();
1024
+ const timeout = setTimeout(() => {
1025
+ server.close();
1026
+ reject(new AuthError("OAuth flow timed out after 5 minutes"));
1027
+ }, 5 * 60 * 1e3);
1028
+ server.on("request", async (req, res) => {
1029
+ const url = req.url ?? "";
1030
+ if (!url.startsWith("/callback")) {
1031
+ res.writeHead(404, { "Content-Type": "text/plain" });
1032
+ res.end("Not Found");
1033
+ return;
1034
+ }
1035
+ try {
1036
+ const params = parseQuery(url);
1037
+ const code = params.get("code");
1038
+ const returnedState = params.get("state");
1039
+ if (!code) {
1040
+ throw new AuthError("No authorization code received from GitHub");
1041
+ }
1042
+ if (returnedState !== state) {
1043
+ throw new AuthError("OAuth state mismatch - possible CSRF attack");
1044
+ }
1045
+ const authResponse = await ofetch(
1046
+ `${registryUrl}/api/v1/auth/callback`,
1047
+ {
1048
+ method: "GET",
1049
+ query: { code, state }
1050
+ }
1051
+ );
1052
+ const token = {
1053
+ token: authResponse.token,
1054
+ username: authResponse.user.username,
1055
+ github_id: authResponse.user.github_id,
1056
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
1057
+ };
1058
+ res.writeHead(200, { "Content-Type": "text/html" });
1059
+ res.end(`
1060
+ <html>
1061
+ <body style="font-family: system-ui, sans-serif; text-align: center; padding: 40px;">
1062
+ <h1>Authentication successful!</h1>
1063
+ <p>You can close this tab and return to the terminal.</p>
1064
+ </body>
1065
+ </html>
1066
+ `);
1067
+ clearTimeout(timeout);
1068
+ server.close();
1069
+ resolve6(token);
1070
+ } catch (error) {
1071
+ res.writeHead(500, { "Content-Type": "text/html" });
1072
+ res.end(`
1073
+ <html>
1074
+ <body style="font-family: system-ui, sans-serif; text-align: center; padding: 40px;">
1075
+ <h1>Authentication failed</h1>
1076
+ <p>Please return to the terminal for details.</p>
1077
+ </body>
1078
+ </html>
1079
+ `);
1080
+ clearTimeout(timeout);
1081
+ server.close();
1082
+ reject(
1083
+ error instanceof AuthError ? error : new AuthError(`OAuth callback failed: ${error.message}`)
1084
+ );
1085
+ }
1086
+ });
1087
+ server.listen(0, "127.0.0.1", async () => {
1088
+ try {
1089
+ const address = server.address();
1090
+ if (!address || typeof address === "string") {
1091
+ throw new AuthError("Failed to start local OAuth server");
1092
+ }
1093
+ const port = address.port;
1094
+ const redirectUri = `http://127.0.0.1:${port}/callback`;
1095
+ const { auth_url } = await ofetch(
1096
+ `${registryUrl}/api/v1/auth/github`,
1097
+ {
1098
+ method: "POST",
1099
+ body: { redirect_uri: redirectUri, state }
1100
+ }
1101
+ );
1102
+ openBrowser(auth_url);
1103
+ } catch (error) {
1104
+ clearTimeout(timeout);
1105
+ server.close();
1106
+ reject(
1107
+ error instanceof AuthError ? error : new AuthError(`Failed to start OAuth flow: ${error.message}`)
1108
+ );
1109
+ }
1110
+ });
1111
+ server.on("error", (error) => {
1112
+ clearTimeout(timeout);
1113
+ reject(new AuthError(`OAuth server error: ${error.message}`));
1114
+ });
1115
+ });
1116
+ }
1117
+
1118
+ // src/auth/token-store.ts
1119
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, existsSync as existsSync11, chmodSync as chmodSync2 } from "fs";
1120
+ function saveToken(token) {
1121
+ ensureDirectories();
1122
+ writeFileSync5(AUTH_PATH, JSON.stringify(token, null, 2), "utf-8");
1123
+ chmodSync2(AUTH_PATH, 384);
1124
+ }
1125
+ function loadToken() {
1126
+ if (!existsSync11(AUTH_PATH)) {
1127
+ return null;
1128
+ }
1129
+ try {
1130
+ const raw = readFileSync10(AUTH_PATH, "utf-8");
1131
+ return JSON.parse(raw);
1132
+ } catch {
1133
+ return null;
1134
+ }
1135
+ }
1136
+ function clearToken() {
1137
+ if (existsSync11(AUTH_PATH)) {
1138
+ unlinkSync3(AUTH_PATH);
1139
+ }
1140
+ }
1141
+ function isAuthenticated() {
1142
+ return existsSync11(AUTH_PATH);
1143
+ }
1144
+
1145
+ // src/ui/spinner.ts
1146
+ import { spinner } from "@clack/prompts";
1147
+ function createSpinner() {
1148
+ const s = spinner();
1149
+ return {
1150
+ start(message) {
1151
+ s.start(message);
1152
+ },
1153
+ stop(message) {
1154
+ s.stop(message);
1155
+ },
1156
+ message(message) {
1157
+ s.message(message);
1158
+ }
1159
+ };
1160
+ }
1161
+
1162
+ // src/commands/login.ts
1163
+ var loginCommand = new Command7("login").description("Authenticate with the agentx registry via GitHub").addHelpText("after", `
1164
+ Examples:
1165
+ $ agentx login`).action(async () => {
1166
+ try {
1167
+ if (isAuthenticated()) {
1168
+ const existing = loadToken();
1169
+ if (existing) {
1170
+ console.log(
1171
+ colors.info(`Already logged in as ${colors.bold(existing.username)}.`)
1172
+ );
1173
+ console.log(colors.dim('Run "agentx logout" first to switch accounts.'));
1174
+ return;
1175
+ }
1176
+ }
1177
+ const config = loadGlobalConfig();
1178
+ const spin = createSpinner();
1179
+ console.log(colors.info("Opening GitHub in your browser to authenticate..."));
1180
+ spin.start("Waiting for authentication...");
1181
+ const token = await startOAuthFlow(config.registry);
1182
+ saveToken(token);
1183
+ spin.stop(colors.success(`Logged in as ${colors.bold(token.username)}`));
1184
+ } catch (error) {
1185
+ if (error instanceof Error) {
1186
+ console.error(colors.error(`Error: ${error.message}`));
1187
+ }
1188
+ process.exit(1);
1189
+ }
1190
+ });
1191
+
1192
+ // src/commands/logout.ts
1193
+ import { Command as Command8 } from "commander";
1194
+ var logoutCommand = new Command8("logout").description("Log out of the agentx registry").addHelpText("after", `
1195
+ Examples:
1196
+ $ agentx logout`).action(() => {
1197
+ try {
1198
+ if (!isAuthenticated()) {
1199
+ console.log(colors.info("Not currently logged in."));
1200
+ return;
1201
+ }
1202
+ clearToken();
1203
+ console.log(colors.success("Logged out successfully."));
1204
+ } catch (error) {
1205
+ if (error instanceof Error) {
1206
+ console.error(colors.error(`Error: ${error.message}`));
1207
+ }
1208
+ process.exit(1);
1209
+ }
1210
+ });
1211
+
1212
+ // src/commands/whoami.ts
1213
+ import { Command as Command9 } from "commander";
1214
+ var whoamiCommand = new Command9("whoami").description("Display the currently authenticated user").addHelpText("after", `
1215
+ Examples:
1216
+ $ agentx whoami`).action(() => {
1217
+ try {
1218
+ const token = loadToken();
1219
+ if (!token) {
1220
+ console.log(colors.warn('Not logged in. Run "agentx login" to authenticate.'));
1221
+ return;
1222
+ }
1223
+ console.log(colors.bold(token.username));
1224
+ } catch (error) {
1225
+ if (error instanceof Error) {
1226
+ console.error(colors.error(`Error: ${error.message}`));
1227
+ }
1228
+ process.exit(1);
1229
+ }
1230
+ });
1231
+
1232
+ // src/commands/publish.ts
1233
+ import { Command as Command10 } from "commander";
1234
+ import { resolve as resolve5 } from "path";
1235
+ import { existsSync as existsSync14 } from "fs";
1236
+ import { join as join11 } from "path";
1237
+
1238
+ // src/registry/publish.ts
1239
+ import { readFileSync as readFileSync11, existsSync as existsSync13 } from "fs";
1240
+ import { join as join10 } from "path";
1241
+
1242
+ // src/registry/client.ts
1243
+ import { ofetch as ofetch2 } from "ofetch";
1244
+ function toRegistryError(error) {
1245
+ if (error && typeof error === "object" && "status" in error) {
1246
+ const status = error.status;
1247
+ const message = error.data?.message ?? `Registry request failed with status ${status}`;
1248
+ return new RegistryError(message, status);
1249
+ }
1250
+ return new RegistryError(
1251
+ `Registry request failed: ${error.message}`
1252
+ );
1253
+ }
1254
+ function createRegistryClient(options) {
1255
+ const config = loadGlobalConfig();
1256
+ const baseURL = `${config.registry}/api/v1`;
1257
+ const authHeaders = {};
1258
+ if (options?.token) {
1259
+ authHeaders["Authorization"] = `Bearer ${options.token}`;
1260
+ }
1261
+ return {
1262
+ async get(path) {
1263
+ try {
1264
+ return await ofetch2(path, {
1265
+ baseURL,
1266
+ method: "GET",
1267
+ headers: authHeaders
1268
+ });
1269
+ } catch (error) {
1270
+ throw toRegistryError(error);
1271
+ }
1272
+ },
1273
+ async post(path, body) {
1274
+ try {
1275
+ return await ofetch2(path, {
1276
+ baseURL,
1277
+ method: "POST",
1278
+ headers: authHeaders,
1279
+ body
1280
+ });
1281
+ } catch (error) {
1282
+ throw toRegistryError(error);
1283
+ }
1284
+ },
1285
+ async put(path, body) {
1286
+ try {
1287
+ return await ofetch2(path, {
1288
+ baseURL,
1289
+ method: "PUT",
1290
+ headers: authHeaders,
1291
+ body
1292
+ });
1293
+ } catch (error) {
1294
+ throw toRegistryError(error);
1295
+ }
1296
+ }
1297
+ };
1298
+ }
1299
+
1300
+ // src/utils/tar.ts
1301
+ import * as tar from "tar";
1302
+ import { existsSync as existsSync12 } from "fs";
1303
+ import { join as join9 } from "path";
1304
+
1305
+ // src/utils/hash.ts
1306
+ import { createHash } from "crypto";
1307
+ import { readFile } from "fs/promises";
1308
+ function hashBuffer(buffer) {
1309
+ return createHash("sha256").update(buffer).digest("hex");
1310
+ }
1311
+
1312
+ // src/utils/tar.ts
1313
+ var INCLUDE_ENTRIES = [
1314
+ "agent.yaml",
1315
+ "system-prompt.md",
1316
+ "README.md",
1317
+ "LICENSE",
1318
+ "prompts",
1319
+ "tools"
1320
+ ];
1321
+ async function createTarball(agentDir) {
1322
+ const entries = INCLUDE_ENTRIES.filter((entry) => {
1323
+ const fullPath = join9(agentDir, entry);
1324
+ return existsSync12(fullPath);
1325
+ });
1326
+ if (entries.length === 0) {
1327
+ throw new Error(`No packable files found in ${agentDir}`);
1328
+ }
1329
+ const chunks = [];
1330
+ const stream = tar.create(
1331
+ {
1332
+ gzip: true,
1333
+ cwd: agentDir,
1334
+ portable: true
1335
+ },
1336
+ entries
1337
+ );
1338
+ for await (const chunk of stream) {
1339
+ chunks.push(Buffer.from(chunk));
1340
+ }
1341
+ const buffer = Buffer.concat(chunks);
1342
+ const sha256 = hashBuffer(buffer);
1343
+ return { buffer, sha256 };
1344
+ }
1345
+
1346
+ // src/registry/publish.ts
1347
+ async function publishAgent(agentDir, token) {
1348
+ const manifest = loadAgentYaml(agentDir);
1349
+ if (!manifest.author.startsWith("@")) {
1350
+ throw new ValidationError("Agent author must start with @ (e.g. @username)");
1351
+ }
1352
+ const scope = manifest.author.slice(1);
1353
+ const { buffer, sha256 } = await createTarball(agentDir);
1354
+ const readmePath = join10(agentDir, "README.md");
1355
+ const readme = existsSync13(readmePath) ? readFileSync11(readmePath, "utf-8") : "";
1356
+ const formData = new FormData();
1357
+ formData.append(
1358
+ "tarball",
1359
+ new Blob([new Uint8Array(buffer)], { type: "application/gzip" }),
1360
+ `${manifest.name}-${manifest.version}.tar.gz`
1361
+ );
1362
+ formData.append("agent_yaml", JSON.stringify(manifest));
1363
+ formData.append("readme", readme);
1364
+ formData.append("sha256", sha256);
1365
+ const client = createRegistryClient({ token });
1366
+ return client.put(
1367
+ `/agents/${scope}/${manifest.name}`,
1368
+ formData
1369
+ );
1370
+ }
1371
+
1372
+ // src/commands/publish.ts
1373
+ var publishCommand = new Command10("publish").description("Publish an agent to the agentx registry").argument("[directory]", "Agent directory to publish", ".").addHelpText("after", `
1374
+ Examples:
1375
+ $ agentx publish
1376
+ $ agentx publish ./my-agent`).action(async (directory) => {
1377
+ try {
1378
+ const agentDir = resolve5(directory);
1379
+ const yamlPath = join11(agentDir, "agent.yaml");
1380
+ if (!existsSync14(yamlPath)) {
1381
+ console.error(
1382
+ colors.error(`No agent.yaml found in ${agentDir}. Run "agentx init" to create one.`)
1383
+ );
1384
+ process.exit(1);
1385
+ }
1386
+ const manifest = loadAgentYaml(agentDir);
1387
+ const token = loadToken();
1388
+ if (!token) {
1389
+ console.error(
1390
+ colors.error('Not logged in. Run "agentx login" to authenticate first.')
1391
+ );
1392
+ process.exit(1);
1393
+ }
1394
+ console.log(
1395
+ colors.info(
1396
+ `Publishing ${colors.bold(`${manifest.author}/${manifest.name}`)} v${manifest.version}...`
1397
+ )
1398
+ );
1399
+ const spin = createSpinner();
1400
+ spin.start("Packaging and uploading agent...");
1401
+ const result = await publishAgent(agentDir, token.token);
1402
+ spin.stop(
1403
+ colors.success(
1404
+ `Published ${colors.bold(result.full_name)} v${result.version}`
1405
+ )
1406
+ );
1407
+ console.log();
1408
+ console.log(` ${colors.dim("View at:")} ${colors.cyan(result.url)}`);
1409
+ } catch (error) {
1410
+ if (error instanceof Error) {
1411
+ console.error(colors.error(`Error: ${error.message}`));
1412
+ }
1413
+ process.exit(1);
1414
+ }
1415
+ });
1416
+
1417
+ // src/commands/install.ts
1418
+ import { Command as Command11 } from "commander";
1419
+ import { existsSync as existsSync16 } from "fs";
1420
+ import { join as join13 } from "path";
1421
+
1422
+ // src/registry/download.ts
1423
+ import { mkdirSync as mkdirSync5, existsSync as existsSync15, rmSync } from "fs";
1424
+ import { join as join12 } from "path";
1425
+ import { createHash as createHash2 } from "crypto";
1426
+ import { Readable } from "stream";
1427
+ import { pipeline } from "stream/promises";
1428
+ import * as tar2 from "tar";
1429
+ function verifyChecksum(buffer, expectedSha256) {
1430
+ const actual = createHash2("sha256").update(buffer).digest("hex");
1431
+ return actual === expectedSha256;
1432
+ }
1433
+ async function fetchTarball(url) {
1434
+ const { ofetch: ofetch3 } = await import("ofetch");
1435
+ const response = await ofetch3(url, { responseType: "arrayBuffer" });
1436
+ return Buffer.from(response);
1437
+ }
1438
+ async function downloadAndExtract(scope, name, version, tarballBuffer, expectedSha256) {
1439
+ if (!verifyChecksum(tarballBuffer, expectedSha256)) {
1440
+ throw new RegistryError(
1441
+ `SHA-256 checksum mismatch for ${scope}/${name}@${version}. The tarball may have been tampered with.`
1442
+ );
1443
+ }
1444
+ const agentDir = join12(AGENTS_DIR, name);
1445
+ if (existsSync15(agentDir)) {
1446
+ rmSync(agentDir, { recursive: true, force: true });
1447
+ }
1448
+ mkdirSync5(agentDir, { recursive: true });
1449
+ const stream = Readable.from(tarballBuffer);
1450
+ await pipeline(
1451
+ stream,
1452
+ tar2.extract({ cwd: agentDir, strip: 0 })
1453
+ );
1454
+ return { agentDir, version, name, scope };
1455
+ }
1456
+ async function fetchAgentInfo(scope, name) {
1457
+ const client = createRegistryClient();
1458
+ return client.get(`/agents/${scope}/${name}`);
1459
+ }
1460
+ async function fetchDownloadInfo(scope, name, version) {
1461
+ const client = createRegistryClient();
1462
+ return client.get(`/agents/${scope}/${name}/download/${version}`);
1463
+ }
1464
+ async function installAgent(scope, name, requestedVersion) {
1465
+ const info = await fetchAgentInfo(scope, name);
1466
+ const version = requestedVersion ?? info.latest_version;
1467
+ if (!version) {
1468
+ throw new RegistryError(`No version found for ${scope}/${name}`);
1469
+ }
1470
+ const downloadInfo = await fetchDownloadInfo(scope, name, version);
1471
+ const tarballBuffer = await fetchTarball(downloadInfo.tarball_url);
1472
+ return downloadAndExtract(scope, name, version, tarballBuffer, downloadInfo.tarball_sha256);
1473
+ }
1474
+
1475
+ // src/commands/install.ts
1476
+ function parseAgentSpecifier(spec) {
1477
+ const atVersionIndex = spec.lastIndexOf("@");
1478
+ if (atVersionIndex > 0) {
1479
+ const beforeVersion = spec.slice(0, atVersionIndex);
1480
+ const version = spec.slice(atVersionIndex + 1);
1481
+ const slashIndex2 = beforeVersion.indexOf("/");
1482
+ if (slashIndex2 > 0) {
1483
+ return {
1484
+ scope: beforeVersion.slice(0, slashIndex2),
1485
+ name: beforeVersion.slice(slashIndex2 + 1),
1486
+ version
1487
+ };
1488
+ }
1489
+ }
1490
+ const slashIndex = spec.indexOf("/");
1491
+ if (slashIndex > 0) {
1492
+ return {
1493
+ scope: spec.slice(0, slashIndex),
1494
+ name: spec.slice(slashIndex + 1)
1495
+ };
1496
+ }
1497
+ return { scope: "@agentx", name: spec };
1498
+ }
1499
+ var installCommand = new Command11("install").description("Install an agent from the registry").argument("<agent>", "Agent to install (e.g. @scope/name or @scope/name@version)").option("-f, --force", "Force reinstall even if already installed").addHelpText("after", `
1500
+ Examples:
1501
+ $ agentx install @agentx/data-analyst
1502
+ $ agentx install @agentx/gmail-agent@1.2.0
1503
+ $ agentx install @agentx/code-reviewer --force`).action(async (agentSpec, options) => {
1504
+ try {
1505
+ const { scope, name, version } = parseAgentSpecifier(agentSpec);
1506
+ const displayName = `${scope}/${name}`;
1507
+ const agentDir = join13(AGENTS_DIR, name);
1508
+ if (existsSync16(agentDir) && !options.force) {
1509
+ const existing = loadAgentYaml(agentDir);
1510
+ console.log(
1511
+ colors.warn(
1512
+ `${displayName} v${existing.version} is already installed. Use --force to reinstall.`
1513
+ )
1514
+ );
1515
+ return;
1516
+ }
1517
+ console.log(
1518
+ colors.info(
1519
+ `Installing ${colors.bold(displayName)}${version ? ` v${version}` : ""}...`
1520
+ )
1521
+ );
1522
+ const spin = createSpinner();
1523
+ spin.start("Downloading and extracting agent...");
1524
+ const result = await installAgent(scope, name, version);
1525
+ spin.stop(
1526
+ colors.success(
1527
+ `Installed ${colors.bold(`${result.scope}/${result.name}`)} v${result.version}`
1528
+ )
1529
+ );
1530
+ const manifest = loadAgentYaml(result.agentDir);
1531
+ if (manifest.secrets && manifest.secrets.length > 0) {
1532
+ console.log();
1533
+ console.log(
1534
+ colors.warn(
1535
+ `This agent requires secrets. Run ${colors.bold(`agentx configure ${name}`)} to set them up.`
1536
+ )
1537
+ );
1538
+ }
1539
+ console.log();
1540
+ console.log(
1541
+ ` ${colors.dim("Run with:")} ${colors.cyan(`agentx run ${name} "your prompt"`)}`
1542
+ );
1543
+ } catch (error) {
1544
+ if (error instanceof Error) {
1545
+ console.error(colors.error(`Error: ${error.message}`));
1546
+ }
1547
+ process.exit(1);
1548
+ }
1549
+ });
1550
+
1551
+ // src/commands/uninstall.ts
1552
+ import { Command as Command12 } from "commander";
1553
+ import { existsSync as existsSync17, rmSync as rmSync2 } from "fs";
1554
+ import { join as join14 } from "path";
1555
+ var uninstallCommand = new Command12("uninstall").description("Uninstall an agent").argument("<agent>", "Agent name to uninstall").option("--keep-secrets", "Keep secrets (do not delete encrypted secrets)").addHelpText("after", `
1556
+ Examples:
1557
+ $ agentx uninstall data-analyst
1558
+ $ agentx uninstall gmail-agent --keep-secrets`).action(async (agentName, options) => {
1559
+ try {
1560
+ const agentDir = join14(AGENTS_DIR, agentName);
1561
+ if (!existsSync17(agentDir)) {
1562
+ console.error(
1563
+ colors.error(`Agent "${agentName}" is not installed.`)
1564
+ );
1565
+ process.exit(1);
1566
+ }
1567
+ rmSync2(agentDir, { recursive: true, force: true });
1568
+ if (!options.keepSecrets) {
1569
+ await deleteSecrets(agentName);
1570
+ }
1571
+ console.log(
1572
+ colors.success(`Uninstalled ${colors.bold(agentName)}`)
1573
+ );
1574
+ } catch (error) {
1575
+ if (error instanceof Error) {
1576
+ console.error(colors.error(`Error: ${error.message}`));
1577
+ }
1578
+ process.exit(1);
1579
+ }
1580
+ });
1581
+
1582
+ // src/commands/list.ts
1583
+ import { Command as Command13 } from "commander";
1584
+ import { existsSync as existsSync18, readdirSync } from "fs";
1585
+ import { join as join15 } from "path";
1586
+
1587
+ // src/ui/table.ts
1588
+ function formatTable(columns, rows) {
1589
+ const widths = columns.map((col) => {
1590
+ const maxContent = Math.max(col.label.length, ...rows.map((r) => (r[col.key] || "").length));
1591
+ return col.width ?? Math.min(maxContent, 40);
1592
+ });
1593
+ const header = columns.map((col, i) => colors.bold(col.label.padEnd(widths[i]))).join(" ");
1594
+ const separator = widths.map((w) => "\u2500".repeat(w)).join("\u2500\u2500");
1595
+ const body = rows.map(
1596
+ (row) => columns.map((col, i) => {
1597
+ const value = row[col.key] || "";
1598
+ const truncated = value.length > widths[i] ? value.slice(0, widths[i] - 1) + "\u2026" : value;
1599
+ return col.align === "right" ? truncated.padStart(widths[i]) : truncated.padEnd(widths[i]);
1600
+ }).join(" ")
1601
+ ).join("\n");
1602
+ return `${header}
1603
+ ${separator}
1604
+ ${body}`;
1605
+ }
1606
+
1607
+ // src/commands/list.ts
1608
+ var listCommand = new Command13("list").alias("ls").description("List installed agents").option("--json", "Output as JSON").addHelpText("after", `
1609
+ Examples:
1610
+ $ agentx list
1611
+ $ agentx ls --json`).action((options) => {
1612
+ try {
1613
+ if (!existsSync18(AGENTS_DIR)) {
1614
+ if (options.json) {
1615
+ console.log(JSON.stringify([]));
1616
+ } else {
1617
+ console.log(colors.dim("No agents installed."));
1618
+ console.log(
1619
+ colors.dim(`Run ${colors.cyan("agentx install @scope/agent-name")} to install one.`)
1620
+ );
1621
+ }
1622
+ return;
1623
+ }
1624
+ const entries = readdirSync(AGENTS_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).filter((e) => existsSync18(join15(AGENTS_DIR, e.name, "agent.yaml")));
1625
+ if (entries.length === 0) {
1626
+ if (options.json) {
1627
+ console.log(JSON.stringify([]));
1628
+ } else {
1629
+ console.log(colors.dim("No agents installed."));
1630
+ console.log(
1631
+ colors.dim(`Run ${colors.cyan("agentx install @scope/agent-name")} to install one.`)
1632
+ );
1633
+ }
1634
+ return;
1635
+ }
1636
+ const agentData = entries.map((entry) => {
1637
+ const agentDir = join15(AGENTS_DIR, entry.name);
1638
+ try {
1639
+ const manifest = loadAgentYaml(agentDir);
1640
+ return {
1641
+ name: manifest.name,
1642
+ version: manifest.version,
1643
+ author: manifest.author,
1644
+ description: manifest.description,
1645
+ category: manifest.category ?? ""
1646
+ };
1647
+ } catch {
1648
+ return {
1649
+ name: entry.name,
1650
+ version: "?",
1651
+ author: "?",
1652
+ description: "Error reading agent.yaml",
1653
+ category: ""
1654
+ };
1655
+ }
1656
+ });
1657
+ if (options.json) {
1658
+ console.log(JSON.stringify(agentData, null, 2));
1659
+ return;
1660
+ }
1661
+ console.log(colors.bold(`Installed agents (${agentData.length}):
1662
+ `));
1663
+ const table = formatTable(
1664
+ [
1665
+ { key: "name", label: "Name", width: 25 },
1666
+ { key: "version", label: "Version", width: 10 },
1667
+ { key: "author", label: "Author", width: 20 },
1668
+ { key: "description", label: "Description", width: 40 }
1669
+ ],
1670
+ agentData
1671
+ );
1672
+ console.log(table);
1673
+ } catch (error) {
1674
+ if (error instanceof Error) {
1675
+ console.error(colors.error(`Error: ${error.message}`));
1676
+ }
1677
+ process.exit(1);
1678
+ }
1679
+ });
1680
+
1681
+ // src/commands/update.ts
1682
+ import { Command as Command14 } from "commander";
1683
+ import { existsSync as existsSync19, readdirSync as readdirSync2 } from "fs";
1684
+ import { join as join16 } from "path";
1685
+
1686
+ // src/utils/semver.ts
1687
+ var SEMVER_REGEX2 = /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.]+))?(?:\+[a-zA-Z0-9.]+)?$/;
1688
+ function parseVersion(version) {
1689
+ const match = version.match(SEMVER_REGEX2);
1690
+ if (!match) return null;
1691
+ return {
1692
+ major: Number(match[1]),
1693
+ minor: Number(match[2]),
1694
+ patch: Number(match[3]),
1695
+ prerelease: match[4] || void 0
1696
+ };
1697
+ }
1698
+ function compareVersions(a, b) {
1699
+ const va = parseVersion(a);
1700
+ const vb = parseVersion(b);
1701
+ if (!va || !vb) return 0;
1702
+ if (va.major !== vb.major) return va.major - vb.major;
1703
+ if (va.minor !== vb.minor) return va.minor - vb.minor;
1704
+ if (va.patch !== vb.patch) return va.patch - vb.patch;
1705
+ if (va.prerelease && !vb.prerelease) return -1;
1706
+ if (!va.prerelease && vb.prerelease) return 1;
1707
+ if (va.prerelease && vb.prerelease) {
1708
+ return va.prerelease < vb.prerelease ? -1 : va.prerelease > vb.prerelease ? 1 : 0;
1709
+ }
1710
+ return 0;
1711
+ }
1712
+ function isNewerThan(a, b) {
1713
+ return compareVersions(a, b) > 0;
1714
+ }
1715
+
1716
+ // src/commands/update.ts
1717
+ var updateCommand = new Command14("update").description("Update installed agents to the latest version").argument("[agent]", "Specific agent to update").option("--all", "Update all installed agents").addHelpText("after", `
1718
+ Examples:
1719
+ $ agentx update data-analyst
1720
+ $ agentx update --all`).action(async (agentName, options) => {
1721
+ try {
1722
+ if (!agentName && !options?.all) {
1723
+ console.error(
1724
+ colors.error("Specify an agent name or use --all to update all agents.")
1725
+ );
1726
+ process.exit(1);
1727
+ }
1728
+ const agentsToUpdate = [];
1729
+ if (options?.all) {
1730
+ if (!existsSync19(AGENTS_DIR)) {
1731
+ console.log(colors.dim("No agents installed."));
1732
+ return;
1733
+ }
1734
+ const entries = readdirSync2(AGENTS_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).filter((e) => existsSync19(join16(AGENTS_DIR, e.name, "agent.yaml")));
1735
+ agentsToUpdate.push(...entries.map((e) => e.name));
1736
+ } else if (agentName) {
1737
+ agentsToUpdate.push(agentName);
1738
+ }
1739
+ if (agentsToUpdate.length === 0) {
1740
+ console.log(colors.dim("No agents to update."));
1741
+ return;
1742
+ }
1743
+ let updatedCount = 0;
1744
+ for (const name of agentsToUpdate) {
1745
+ const agentDir = join16(AGENTS_DIR, name);
1746
+ if (!existsSync19(agentDir)) {
1747
+ console.log(colors.warn(`Agent "${name}" is not installed. Skipping.`));
1748
+ continue;
1749
+ }
1750
+ const manifest = loadAgentYaml(agentDir);
1751
+ const scope = manifest.author;
1752
+ const spin = createSpinner();
1753
+ spin.start(`Checking ${colors.bold(name)} for updates...`);
1754
+ try {
1755
+ const info = await fetchAgentInfo(scope, name);
1756
+ if (!info.latest_version || !isNewerThan(info.latest_version, manifest.version)) {
1757
+ spin.stop(colors.dim(`${name} v${manifest.version} is already up to date`));
1758
+ continue;
1759
+ }
1760
+ spin.message(`Updating ${name} from v${manifest.version} to v${info.latest_version}...`);
1761
+ await installAgent(scope, name, info.latest_version);
1762
+ spin.stop(
1763
+ colors.success(
1764
+ `Updated ${colors.bold(name)} from v${manifest.version} to v${info.latest_version}`
1765
+ )
1766
+ );
1767
+ updatedCount++;
1768
+ } catch (error) {
1769
+ spin.stop(
1770
+ colors.error(
1771
+ `Failed to update ${name}: ${error.message}`
1772
+ )
1773
+ );
1774
+ }
1775
+ }
1776
+ console.log();
1777
+ if (updatedCount > 0) {
1778
+ console.log(colors.success(`Updated ${updatedCount} agent${updatedCount !== 1 ? "s" : ""}.`));
1779
+ } else {
1780
+ console.log(colors.dim("All agents are up to date."));
1781
+ }
1782
+ } catch (error) {
1783
+ if (error instanceof Error) {
1784
+ console.error(colors.error(`Error: ${error.message}`));
1785
+ }
1786
+ process.exit(1);
1787
+ }
1788
+ });
1789
+
1790
+ // src/commands/info.ts
1791
+ import { Command as Command15 } from "commander";
1792
+ function parseInfoSpecifier(spec) {
1793
+ const slashIndex = spec.indexOf("/");
1794
+ if (slashIndex > 0) {
1795
+ return {
1796
+ scope: spec.slice(0, slashIndex),
1797
+ name: spec.slice(slashIndex + 1)
1798
+ };
1799
+ }
1800
+ return { scope: "@agentx", name: spec };
1801
+ }
1802
+ var infoCommand = new Command15("info").description("Display agent details from the registry").argument("<agent>", "Agent to look up (e.g. @scope/name)").option("--json", "Output as JSON").addHelpText("after", `
1803
+ Examples:
1804
+ $ agentx info @agentx/data-analyst
1805
+ $ agentx info @agentx/gmail-agent --json`).action(async (agentSpec, options) => {
1806
+ try {
1807
+ const { scope, name } = parseInfoSpecifier(agentSpec);
1808
+ const info = await fetchAgentInfo(scope, name);
1809
+ if (options.json) {
1810
+ console.log(JSON.stringify(info, null, 2));
1811
+ return;
1812
+ }
1813
+ console.log();
1814
+ console.log(` ${colors.bold(`${info.scope}/${info.name}`)} v${info.latest_version}`);
1815
+ console.log(` ${colors.dim(info.description ?? "")}`);
1816
+ console.log();
1817
+ if (info.author) {
1818
+ console.log(` ${colors.dim("Author:")} ${info.author.username}`);
1819
+ }
1820
+ if (info.category) {
1821
+ console.log(` ${colors.dim("Category:")} ${info.category}`);
1822
+ }
1823
+ if (info.license) {
1824
+ console.log(` ${colors.dim("License:")} ${info.license}`);
1825
+ }
1826
+ if (info.tags && info.tags.length > 0) {
1827
+ console.log(` ${colors.dim("Tags:")} ${info.tags.join(", ")}`);
1828
+ }
1829
+ console.log();
1830
+ console.log(` ${colors.dim("Downloads:")} ${info.download_count}`);
1831
+ console.log(` ${colors.dim("Stars:")} ${info.star_count}`);
1832
+ if (info.is_verified) {
1833
+ console.log(` ${colors.success("Verified")}`);
1834
+ }
1835
+ if (info.repository) {
1836
+ console.log(` ${colors.dim("Repository:")} ${colors.cyan(info.repository)}`);
1837
+ }
1838
+ if (info.homepage) {
1839
+ console.log(` ${colors.dim("Homepage:")} ${colors.cyan(info.homepage)}`);
1840
+ }
1841
+ console.log();
1842
+ console.log(
1843
+ ` ${colors.dim("Install:")} ${colors.cyan(`agentx install ${scope}/${name}`)}`
1844
+ );
1845
+ if (info.versions && Array.isArray(info.versions)) {
1846
+ console.log();
1847
+ console.log(` ${colors.dim("Versions:")}`);
1848
+ for (const v of info.versions) {
1849
+ console.log(` ${v.version} ${colors.dim(v.published_at ?? "")}`);
1850
+ }
1851
+ }
1852
+ } catch (error) {
1853
+ if (error instanceof Error) {
1854
+ console.error(colors.error(`Error: ${error.message}`));
1855
+ }
1856
+ process.exit(1);
1857
+ }
1858
+ });
1859
+
1860
+ // src/commands/search.ts
1861
+ import { Command as Command16 } from "commander";
1862
+
1863
+ // src/registry/search.ts
1864
+ async function searchAgents(query, options) {
1865
+ const client = createRegistryClient();
1866
+ const params = new URLSearchParams({ q: query });
1867
+ if (options?.limit) {
1868
+ params.set("limit", String(options.limit));
1869
+ }
1870
+ if (options?.category) {
1871
+ params.set("category", options.category);
1872
+ }
1873
+ if (options?.sort) {
1874
+ params.set("sort", options.sort);
1875
+ }
1876
+ return client.get(`/search?${params.toString()}`);
1877
+ }
1878
+ async function getTrending(options) {
1879
+ const client = createRegistryClient();
1880
+ const params = new URLSearchParams();
1881
+ if (options?.period) {
1882
+ params.set("period", options.period);
1883
+ }
1884
+ if (options?.limit) {
1885
+ params.set("limit", String(options.limit));
1886
+ }
1887
+ const qs = params.toString();
1888
+ return client.get(`/trending${qs ? `?${qs}` : ""}`);
1889
+ }
1890
+
1891
+ // src/commands/search.ts
1892
+ var searchCommand = new Command16("search").description("Search for agents in the registry").argument("<query>", "Search query").option("-l, --limit <n>", "Maximum results to display", "20").option("-c, --category <category>", "Filter by category").option("-s, --sort <sort>", "Sort order: downloads, stars, newest", "downloads").option("--json", "Output as JSON").addHelpText("after", `
1893
+ Examples:
1894
+ $ agentx search "data analysis"
1895
+ $ agentx search email --category productivity
1896
+ $ agentx search code --sort stars --limit 10`).action(
1897
+ async (query, options) => {
1898
+ try {
1899
+ const limit = parseInt(options.limit, 10) || 20;
1900
+ const response = await searchAgents(query, {
1901
+ limit,
1902
+ category: options.category,
1903
+ sort: options.sort
1904
+ });
1905
+ if (options.json) {
1906
+ console.log(JSON.stringify(response, null, 2));
1907
+ return;
1908
+ }
1909
+ if (response.agents.length === 0) {
1910
+ console.log(colors.dim(` No agents found for "${query}".`));
1911
+ return;
1912
+ }
1913
+ console.log();
1914
+ console.log(
1915
+ ` Found ${colors.bold(String(response.total))} agent${response.total === 1 ? "" : "s"} matching "${query}"`
1916
+ );
1917
+ console.log();
1918
+ const table = formatTable(
1919
+ [
1920
+ { key: "name", label: "Name", width: 30 },
1921
+ { key: "description", label: "Description", width: 40 },
1922
+ { key: "version", label: "Version", width: 10 },
1923
+ { key: "downloads", label: "DL", width: 8, align: "right" },
1924
+ { key: "stars", label: "Stars", width: 6, align: "right" }
1925
+ ],
1926
+ response.agents.map((a) => ({
1927
+ name: `${a.scope}/${a.name}`,
1928
+ description: a.description,
1929
+ version: a.latest_version,
1930
+ downloads: String(a.download_count),
1931
+ stars: String(a.star_count)
1932
+ }))
1933
+ );
1934
+ console.log(
1935
+ table.split("\n").map((line) => ` ${line}`).join("\n")
1936
+ );
1937
+ console.log();
1938
+ } catch (error) {
1939
+ if (error instanceof Error) {
1940
+ console.error(colors.error(`Error: ${error.message}`));
1941
+ }
1942
+ process.exit(1);
1943
+ }
1944
+ }
1945
+ );
1946
+
1947
+ // src/commands/trending.ts
1948
+ import { Command as Command17 } from "commander";
1949
+ var trendingCommand = new Command17("trending").description("Show trending agents").option("-p, --period <period>", "Time period: day, week, month", "week").option("-l, --limit <n>", "Maximum results to display", "20").option("--json", "Output as JSON").addHelpText("after", `
1950
+ Examples:
1951
+ $ agentx trending
1952
+ $ agentx trending --period month
1953
+ $ agentx trending --period day --limit 5`).action(
1954
+ async (options) => {
1955
+ try {
1956
+ const limit = parseInt(options.limit, 10) || 20;
1957
+ const response = await getTrending({
1958
+ period: options.period,
1959
+ limit
1960
+ });
1961
+ if (options.json) {
1962
+ console.log(JSON.stringify(response, null, 2));
1963
+ return;
1964
+ }
1965
+ if (response.agents.length === 0) {
1966
+ console.log(colors.dim(" No trending agents found."));
1967
+ return;
1968
+ }
1969
+ const periodLabel = options.period === "day" ? "today" : options.period === "month" ? "this month" : "this week";
1970
+ console.log();
1971
+ console.log(` ${colors.bold("Trending agents")} ${colors.dim(periodLabel)}`);
1972
+ console.log();
1973
+ const table = formatTable(
1974
+ [
1975
+ { key: "rank", label: "#", width: 4, align: "right" },
1976
+ { key: "name", label: "Name", width: 30 },
1977
+ { key: "description", label: "Description", width: 40 },
1978
+ { key: "downloads", label: "DL", width: 8, align: "right" },
1979
+ { key: "stars", label: "Stars", width: 6, align: "right" }
1980
+ ],
1981
+ response.agents.map((a, i) => ({
1982
+ rank: String(i + 1),
1983
+ name: `${a.scope}/${a.name}`,
1984
+ description: a.description,
1985
+ downloads: String(a.download_count),
1986
+ stars: String(a.star_count)
1987
+ }))
1988
+ );
1989
+ console.log(
1990
+ table.split("\n").map((line) => ` ${line}`).join("\n")
1991
+ );
1992
+ console.log();
1993
+ } catch (error) {
1994
+ if (error instanceof Error) {
1995
+ console.error(colors.error(`Error: ${error.message}`));
1996
+ }
1997
+ process.exit(1);
1998
+ }
1999
+ }
2000
+ );
2001
+
2002
+ // src/commands/config.ts
2003
+ import { Command as Command18 } from "commander";
2004
+ var VALID_KEYS = Object.keys(globalConfigSchema.shape);
2005
+ function formatValue(value) {
2006
+ if (typeof value === "object" && value !== null) {
2007
+ return JSON.stringify(value);
2008
+ }
2009
+ return String(value);
2010
+ }
2011
+ function parseValue(key, raw) {
2012
+ if (key === "telemetry" || key === "auto_update") {
2013
+ if (raw === "true") return true;
2014
+ if (raw === "false") return false;
2015
+ throw new Error(`Value for "${key}" must be "true" or "false"`);
2016
+ }
2017
+ if (key === "default_output") {
2018
+ if (raw === "text" || raw === "json") return raw;
2019
+ throw new Error(`Value for "${key}" must be "text" or "json"`);
2020
+ }
2021
+ if (key === "claude_defaults") {
2022
+ try {
2023
+ return JSON.parse(raw);
2024
+ } catch {
2025
+ throw new Error(`Value for "${key}" must be valid JSON`);
2026
+ }
2027
+ }
2028
+ return raw;
2029
+ }
2030
+ var configCommand = new Command18("config").description("Manage global agentx configuration").addHelpText("after", `
2031
+ Examples:
2032
+ $ agentx config list Show all config values
2033
+ $ agentx config get registry Get a specific value
2034
+ $ agentx config set telemetry false Set a config value
2035
+ $ agentx config path Show config file path
2036
+ $ agentx config reset Reset to defaults`);
2037
+ configCommand.command("list").description("Show all configuration values").option("--json", "Output as JSON").action((options) => {
2038
+ try {
2039
+ const config = loadGlobalConfig();
2040
+ if (options.json) {
2041
+ console.log(JSON.stringify(config, null, 2));
2042
+ } else {
2043
+ console.log(colors.bold("agentx configuration:\n"));
2044
+ for (const [key, value] of Object.entries(config)) {
2045
+ console.log(` ${colors.cyan(key)}: ${formatValue(value)}`);
2046
+ }
2047
+ console.log(`
2048
+ ${colors.dim(`Config file: ${CONFIG_PATH}`)}`);
2049
+ }
2050
+ } catch (error) {
2051
+ if (error instanceof Error) {
2052
+ console.error(colors.error(`Error: ${error.message}`));
2053
+ }
2054
+ process.exit(1);
2055
+ }
2056
+ });
2057
+ configCommand.command("get").description("Get a configuration value").argument("<key>", `Config key (${VALID_KEYS.join(", ")})`).action((key) => {
2058
+ try {
2059
+ if (!VALID_KEYS.includes(key)) {
2060
+ console.error(colors.error(`Unknown config key: "${key}"`));
2061
+ console.error(colors.dim(`Valid keys: ${VALID_KEYS.join(", ")}`));
2062
+ process.exit(1);
2063
+ }
2064
+ const value = getConfigValue(key);
2065
+ console.log(formatValue(value));
2066
+ } catch (error) {
2067
+ if (error instanceof Error) {
2068
+ console.error(colors.error(`Error: ${error.message}`));
2069
+ }
2070
+ process.exit(1);
2071
+ }
2072
+ });
2073
+ configCommand.command("set").description("Set a configuration value").argument("<key>", `Config key (${VALID_KEYS.join(", ")})`).argument("<value>", "Value to set").action((key, raw) => {
2074
+ try {
2075
+ if (!VALID_KEYS.includes(key)) {
2076
+ console.error(colors.error(`Unknown config key: "${key}"`));
2077
+ console.error(colors.dim(`Valid keys: ${VALID_KEYS.join(", ")}`));
2078
+ process.exit(1);
2079
+ }
2080
+ const value = parseValue(key, raw);
2081
+ setConfigValue(key, value);
2082
+ console.log(colors.success(`Set ${colors.cyan(key)} = ${formatValue(value)}`));
2083
+ } catch (error) {
2084
+ if (error instanceof Error) {
2085
+ console.error(colors.error(`Error: ${error.message}`));
2086
+ }
2087
+ process.exit(1);
2088
+ }
2089
+ });
2090
+ configCommand.command("path").description("Show the config file path").action(() => {
2091
+ console.log(CONFIG_PATH);
2092
+ });
2093
+ configCommand.command("reset").description("Reset configuration to defaults").action(() => {
2094
+ try {
2095
+ const defaults = globalConfigSchema.parse({});
2096
+ saveGlobalConfig(defaults);
2097
+ console.log(colors.success("Configuration reset to defaults."));
2098
+ } catch (error) {
2099
+ if (error instanceof Error) {
2100
+ console.error(colors.error(`Error: ${error.message}`));
2101
+ }
2102
+ process.exit(1);
2103
+ }
2104
+ });
2105
+
2106
+ // src/index.ts
2107
+ var __filename3 = fileURLToPath2(import.meta.url);
2108
+ var __dirname3 = dirname2(__filename3);
2109
+ var pkg = JSON.parse(readFileSync12(join17(__dirname3, "..", "package.json"), "utf-8"));
2110
+ var program = new Command19();
2111
+ program.name("agentx").description("The package manager for AI agents powered by Claude Code").version(pkg.version).option("--verbose", "Show verbose output").option("--debug", "Show debug output including stack traces").hook("preAction", (_thisCommand, actionCommand) => {
2112
+ const opts = program.opts();
2113
+ if (opts.verbose) {
2114
+ process.env.AGENTX_VERBOSE = "1";
2115
+ }
2116
+ if (opts.debug) {
2117
+ process.env.AGENTX_DEBUG = "1";
2118
+ process.env.AGENTX_VERBOSE = "1";
2119
+ }
2120
+ });
2121
+ program.addCommand(runCommand);
2122
+ program.addCommand(configureCommand);
2123
+ program.addCommand(initCommand);
2124
+ program.addCommand(validateCommand);
2125
+ program.addCommand(testCommand);
2126
+ program.addCommand(doctorCommand);
2127
+ program.addCommand(loginCommand);
2128
+ program.addCommand(logoutCommand);
2129
+ program.addCommand(whoamiCommand);
2130
+ program.addCommand(publishCommand);
2131
+ program.addCommand(installCommand);
2132
+ program.addCommand(uninstallCommand);
2133
+ program.addCommand(listCommand);
2134
+ program.addCommand(updateCommand);
2135
+ program.addCommand(infoCommand);
2136
+ program.addCommand(searchCommand);
2137
+ program.addCommand(trendingCommand);
2138
+ program.addCommand(configCommand);
2139
+ program.parse();
2140
+ export {
2141
+ program
2142
+ };