@the-aico/cli 1.0.0 → 1.0.2

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 CHANGED
@@ -16,12 +16,12 @@ var defaultSuggestions = {
16
16
  REGISTRY_UNAVAILABLE: "The registry server is unavailable. Try again later.",
17
17
  // Resource errors
18
18
  NOT_FOUND: "The requested resource was not found.",
19
- SKILL_NOT_FOUND: "Use `npx aico list --skills` to see available skills.",
20
- EMPLOYEE_NOT_FOUND: "Use `npx aico list` to see available employees.",
19
+ SKILL_NOT_FOUND: "Use `aico list --skills` to see available skills.",
20
+ EMPLOYEE_NOT_FOUND: "Use `aico list` to see available employees.",
21
21
  // Configuration errors
22
- NOT_INITIALIZED: "Run `npx aico init` to initialize the project.",
23
- CONFIG_INVALID: "Check your aico.json for syntax errors or run `npx aico init --force`.",
24
- CONFIG_EXISTS: "Use `npx aico init --force` to overwrite existing config.",
22
+ NOT_INITIALIZED: "Run `aico init` to initialize the project.",
23
+ CONFIG_INVALID: "Check your aico.json for syntax errors or run `aico init --force`.",
24
+ CONFIG_EXISTS: "Use `aico init --force` to overwrite existing config.",
25
25
  CONFIG_PARSE_ERROR: "Your aico.json contains invalid JSON. Check for syntax errors.",
26
26
  // Dependency errors
27
27
  DEPENDENCY_MISSING: "Install the missing dependency first, or use `--no-deps` to skip.",
@@ -95,8 +95,8 @@ function handleError(error, options = {}) {
95
95
  console.error(kleur.red("\u2716 Validation Error:"));
96
96
  console.error("");
97
97
  for (const issue of error.issues) {
98
- const path9 = issue.path.length > 0 ? issue.path.join(".") : "value";
99
- console.error(` ${kleur.yellow(path9)}: ${issue.message}`);
98
+ const path10 = issue.path.length > 0 ? issue.path.join(".") : "value";
99
+ console.error(` ${kleur.yellow(path10)}: ${issue.message}`);
100
100
  }
101
101
  console.error("");
102
102
  console.error(kleur.cyan(" Suggestion:"));
@@ -238,15 +238,15 @@ var DEFAULT_PLATFORMS = {
238
238
  skills: ".claude/skills",
239
239
  commands: ".claude/commands"
240
240
  },
241
- // Codex 平台配置:
242
- // - skills: 项目目录 .codex/skills/(Codex 原生支持项目级 skills
243
- // - commands (prompts): 全局 ~/.codex/prompts/(一次安装所有项目可用)
244
- // 调用方式:/prompts:aico.{employee}.{command}
245
- // 用户可在 aico.json 中覆盖路径
241
+ // Codex platform configuration:
242
+ // - skills: project directory .codex/skills/ (Codex natively supports project-level skills)
243
+ // - commands (prompts): global ~/.codex/prompts/ (install once, available for all projects)
244
+ // Invocation: /prompts:aico.{employee}.{command}
245
+ // Users can override paths in aico.json
246
246
  codex: {
247
247
  skills: ".codex/skills",
248
248
  commands: "~/.codex/prompts"
249
- // Codex custom prompts (全局)
249
+ // Codex custom prompts (global)
250
250
  }
251
251
  };
252
252
  function createDefaultConfig(defaultPlatform = "claude-code") {
@@ -512,10 +512,8 @@ async function runInit(options) {
512
512
  logger.dim(`Default platform: ${platform}`);
513
513
  logger.break();
514
514
  logger.info("Next steps:");
515
- logger.log(` ${logger.highlight("npx aico add pm")} Add the PM employee`);
516
- logger.log(
517
- ` ${logger.highlight("npx aico list")} View available employees`
518
- );
515
+ logger.log(` ${logger.highlight("aico add pm")} Add the PM employee`);
516
+ logger.log(` ${logger.highlight("aico list")} View available employees`);
519
517
  }
520
518
  var init = new Command().name("init").description("Initialize aico in your project").option(
521
519
  "-p, --default-platform <platform>",
@@ -1226,9 +1224,9 @@ var DependencyResolver = class {
1226
1224
  /**
1227
1225
  * Recursively fetch a skill and all its dependencies
1228
1226
  */
1229
- async fetchWithDependencies(skillName, visited = /* @__PURE__ */ new Set(), path9 = []) {
1230
- if (path9.includes(skillName)) {
1231
- const cycle = [...path9, skillName].join(" \u2192 ");
1227
+ async fetchWithDependencies(skillName, visited = /* @__PURE__ */ new Set(), path10 = []) {
1228
+ if (path10.includes(skillName)) {
1229
+ const cycle = [...path10, skillName].join(" \u2192 ");
1232
1230
  throw new AicoError(
1233
1231
  `Circular dependency detected: ${cycle}`,
1234
1232
  "CIRCULAR_DEPENDENCY",
@@ -1249,7 +1247,7 @@ var DependencyResolver = class {
1249
1247
  const depSkills = await this.fetchWithDependencies(
1250
1248
  dep,
1251
1249
  visited,
1252
- [...path9, skillName]
1250
+ [...path10, skillName]
1253
1251
  );
1254
1252
  allSkills.push(...depSkills);
1255
1253
  }
@@ -2031,12 +2029,8 @@ var remove = new Command3().name("remove").description("Remove employees or skil
2031
2029
  "Please specify at least one employee or skill to remove."
2032
2030
  );
2033
2031
  logger.dim("Examples:");
2034
- logger.dim(
2035
- " npx aico remove pm # Remove employee"
2036
- );
2037
- logger.dim(
2038
- " npx aico remove @the-aico/pm/brainstorming # Remove skill"
2039
- );
2032
+ logger.dim(" aico remove pm # Remove employee");
2033
+ logger.dim(" aico remove @the-aico/pm/brainstorming # Remove skill");
2040
2034
  process.exit(1);
2041
2035
  }
2042
2036
  const options = removeOptionsSchema.parse({
@@ -2070,7 +2064,7 @@ async function runList(options) {
2070
2064
  const employees = Object.entries(config.employees);
2071
2065
  if (employees.length === 0) {
2072
2066
  logger.info("No employees installed.");
2073
- logger.dim("Run `npx aico add <employee>` to add an employee.");
2067
+ logger.dim("Run `aico add <employee>` to add an employee.");
2074
2068
  return;
2075
2069
  }
2076
2070
  logger.bold("Installed employees:");
@@ -2101,8 +2095,8 @@ async function runList(options) {
2101
2095
  );
2102
2096
  }
2103
2097
  logger.break();
2104
- logger.dim("Use `npx aico add <employee>` to add an employee.");
2105
- logger.dim("Use `npx aico list --installed` to see installed employees.");
2098
+ logger.dim("Use `aico add <employee>` to add an employee.");
2099
+ logger.dim("Use `aico list --installed` to see installed employees.");
2106
2100
  } catch {
2107
2101
  logger.warn(
2108
2102
  "Could not fetch registry. Showing installed employees only."
@@ -2135,14 +2129,370 @@ var list = new Command4().name("list").description("List available or installed
2135
2129
 
2136
2130
  // src/commands/build.ts
2137
2131
  import { Command as Command5 } from "commander";
2138
- import fs6 from "fs-extra";
2139
- import path7 from "path";
2132
+ import fs7 from "fs-extra";
2133
+ import path8 from "path";
2140
2134
  import glob from "fast-glob";
2141
2135
  import { z as z9 } from "zod";
2136
+
2137
+ // src/commands/build-schema.ts
2138
+ import fs6 from "fs-extra";
2139
+ import path7 from "path";
2140
+ var CONFIG_SCHEMA = {
2141
+ $schema: "http://json-schema.org/draft-07/schema#",
2142
+ $id: "https://the-aico.com/schema/config.json",
2143
+ title: "aico Configuration",
2144
+ description: "Configuration file schema for aico.json",
2145
+ type: "object",
2146
+ required: ["defaultPlatform", "platforms"],
2147
+ properties: {
2148
+ $schema: {
2149
+ type: "string",
2150
+ description: "JSON Schema reference"
2151
+ },
2152
+ language: {
2153
+ type: "string",
2154
+ description: "Default language for documentation",
2155
+ default: "en"
2156
+ },
2157
+ defaultPlatform: {
2158
+ type: "string",
2159
+ enum: ["claude-code", "codex"],
2160
+ description: "Default platform for installation"
2161
+ },
2162
+ platforms: {
2163
+ type: "object",
2164
+ description: "Platform-specific path configurations",
2165
+ additionalProperties: {
2166
+ type: "object",
2167
+ required: ["skills", "commands"],
2168
+ properties: {
2169
+ skills: {
2170
+ type: "string",
2171
+ description: "Path to skills directory"
2172
+ },
2173
+ commands: {
2174
+ type: "string",
2175
+ description: "Path to commands directory"
2176
+ }
2177
+ }
2178
+ }
2179
+ },
2180
+ employees: {
2181
+ type: "object",
2182
+ description: "Installed employees state",
2183
+ additionalProperties: {
2184
+ type: "object",
2185
+ required: ["platforms", "installedAt"],
2186
+ properties: {
2187
+ platforms: {
2188
+ type: "array",
2189
+ items: {
2190
+ type: "string",
2191
+ enum: ["claude-code", "codex"]
2192
+ },
2193
+ description: "Platforms this employee is installed on"
2194
+ },
2195
+ installedAt: {
2196
+ type: "string",
2197
+ format: "date-time",
2198
+ description: "Installation timestamp"
2199
+ },
2200
+ version: {
2201
+ type: "string",
2202
+ description: "Installed version"
2203
+ },
2204
+ skills: {
2205
+ type: "array",
2206
+ items: { type: "string" },
2207
+ description: "Installed skill names"
2208
+ },
2209
+ commands: {
2210
+ type: "array",
2211
+ items: { type: "string" },
2212
+ description: "Installed command names"
2213
+ }
2214
+ }
2215
+ }
2216
+ },
2217
+ skills: {
2218
+ type: "object",
2219
+ description: "Standalone installed skills",
2220
+ additionalProperties: {
2221
+ type: "object",
2222
+ required: ["version", "installedAt", "source", "platforms"],
2223
+ properties: {
2224
+ version: {
2225
+ type: "string",
2226
+ description: "Skill version"
2227
+ },
2228
+ installedAt: {
2229
+ type: "string",
2230
+ format: "date-time",
2231
+ description: "Installation timestamp"
2232
+ },
2233
+ source: {
2234
+ type: "string",
2235
+ enum: ["standalone", "employee"],
2236
+ description: "Installation source"
2237
+ },
2238
+ platforms: {
2239
+ type: "array",
2240
+ items: {
2241
+ type: "string",
2242
+ enum: ["claude-code", "codex"]
2243
+ }
2244
+ }
2245
+ }
2246
+ }
2247
+ },
2248
+ sharedSkills: {
2249
+ type: "object",
2250
+ description: "Shared skills with reference tracking",
2251
+ additionalProperties: {
2252
+ type: "object",
2253
+ required: ["version", "installedAt", "platforms", "usedBy"],
2254
+ properties: {
2255
+ version: {
2256
+ type: "string",
2257
+ description: "Skill version"
2258
+ },
2259
+ installedAt: {
2260
+ type: "string",
2261
+ format: "date-time",
2262
+ description: "Installation timestamp"
2263
+ },
2264
+ platforms: {
2265
+ type: "array",
2266
+ items: {
2267
+ type: "string",
2268
+ enum: ["claude-code", "codex"]
2269
+ }
2270
+ },
2271
+ usedBy: {
2272
+ type: "array",
2273
+ items: { type: "string" },
2274
+ description: "Employees that depend on this shared skill"
2275
+ }
2276
+ }
2277
+ }
2278
+ },
2279
+ registries: {
2280
+ type: "object",
2281
+ description: "Custom registry configurations",
2282
+ propertyNames: {
2283
+ pattern: "^@"
2284
+ },
2285
+ additionalProperties: {
2286
+ oneOf: [
2287
+ {
2288
+ type: "string",
2289
+ description: "Registry URL with {name} placeholder"
2290
+ },
2291
+ {
2292
+ type: "object",
2293
+ required: ["url"],
2294
+ properties: {
2295
+ url: {
2296
+ type: "string",
2297
+ description: "Registry URL with {name} placeholder"
2298
+ },
2299
+ headers: {
2300
+ type: "object",
2301
+ additionalProperties: { type: "string" },
2302
+ description: "Custom HTTP headers"
2303
+ }
2304
+ }
2305
+ }
2306
+ ]
2307
+ }
2308
+ }
2309
+ }
2310
+ };
2311
+ var EMPLOYEE_SCHEMA = {
2312
+ $schema: "http://json-schema.org/draft-07/schema#",
2313
+ $id: "https://the-aico.com/schema/employee.json",
2314
+ title: "aico Employee",
2315
+ description: "Employee definition schema for employee.json",
2316
+ type: "object",
2317
+ required: ["name", "role"],
2318
+ properties: {
2319
+ $schema: {
2320
+ type: "string",
2321
+ description: "JSON Schema reference"
2322
+ },
2323
+ name: {
2324
+ type: "string",
2325
+ pattern: "^[a-z0-9-]+$",
2326
+ description: "Employee name (hyphen-case)"
2327
+ },
2328
+ namespace: {
2329
+ type: "string",
2330
+ pattern: "^@[a-z0-9-]+$",
2331
+ description: "Registry namespace (@registry format)"
2332
+ },
2333
+ fullName: {
2334
+ type: "string",
2335
+ pattern: "^@[a-z0-9-]+/[a-z0-9-]+$",
2336
+ description: "Full name (@registry/employee format)"
2337
+ },
2338
+ role: {
2339
+ type: "string",
2340
+ minLength: 1,
2341
+ description: "Employee role title"
2342
+ },
2343
+ description: {
2344
+ type: "string",
2345
+ description: "Employee description"
2346
+ },
2347
+ version: {
2348
+ type: "string",
2349
+ pattern: "^\\d+\\.\\d+\\.\\d+$",
2350
+ description: "Semantic version"
2351
+ },
2352
+ category: {
2353
+ type: "string",
2354
+ enum: ["pm", "frontend", "backend", "devops", "general"],
2355
+ description: "Employee category"
2356
+ },
2357
+ skills: {
2358
+ type: "array",
2359
+ description: "Employee skills",
2360
+ items: {
2361
+ oneOf: [
2362
+ {
2363
+ type: "string",
2364
+ description: "Skill fullName reference"
2365
+ },
2366
+ {
2367
+ type: "object",
2368
+ required: ["name", "files"],
2369
+ properties: {
2370
+ name: {
2371
+ type: "string",
2372
+ pattern: "^[a-z0-9-]+$",
2373
+ description: "Skill name (hyphen-case)"
2374
+ },
2375
+ files: {
2376
+ type: "array",
2377
+ minItems: 1,
2378
+ items: {
2379
+ $ref: "#/definitions/fileSource"
2380
+ }
2381
+ }
2382
+ }
2383
+ }
2384
+ ]
2385
+ }
2386
+ },
2387
+ commands: {
2388
+ type: "array",
2389
+ description: "Employee commands",
2390
+ items: {
2391
+ type: "object",
2392
+ required: ["name", "files"],
2393
+ properties: {
2394
+ name: {
2395
+ type: "string",
2396
+ pattern: "^[a-z0-9-]+$",
2397
+ description: "Command name (hyphen-case)"
2398
+ },
2399
+ files: {
2400
+ type: "array",
2401
+ minItems: 1,
2402
+ items: {
2403
+ $ref: "#/definitions/file"
2404
+ }
2405
+ }
2406
+ }
2407
+ }
2408
+ },
2409
+ docs: {
2410
+ type: "array",
2411
+ description: "Employee documentation",
2412
+ items: {
2413
+ type: "object",
2414
+ required: ["name", "files"],
2415
+ properties: {
2416
+ name: {
2417
+ type: "string",
2418
+ description: "Document name"
2419
+ },
2420
+ files: {
2421
+ type: "array",
2422
+ minItems: 1,
2423
+ items: {
2424
+ $ref: "#/definitions/file"
2425
+ }
2426
+ }
2427
+ }
2428
+ }
2429
+ },
2430
+ dependencies: {
2431
+ type: "array",
2432
+ description: "Shared skill dependencies",
2433
+ items: {
2434
+ type: "string",
2435
+ description: "Dependency reference (@registry/_shared/skill)"
2436
+ }
2437
+ }
2438
+ },
2439
+ definitions: {
2440
+ fileSource: {
2441
+ type: "object",
2442
+ required: ["path", "type"],
2443
+ properties: {
2444
+ path: {
2445
+ type: "string",
2446
+ minLength: 1,
2447
+ description: "Relative file path"
2448
+ },
2449
+ type: {
2450
+ type: "string",
2451
+ enum: ["skill", "command", "doc"],
2452
+ description: "File type"
2453
+ }
2454
+ }
2455
+ },
2456
+ file: {
2457
+ type: "object",
2458
+ required: ["path", "type"],
2459
+ properties: {
2460
+ path: {
2461
+ type: "string",
2462
+ minLength: 1,
2463
+ description: "Relative file path"
2464
+ },
2465
+ type: {
2466
+ type: "string",
2467
+ enum: ["skill", "command", "doc"],
2468
+ description: "File type"
2469
+ },
2470
+ content: {
2471
+ type: "string",
2472
+ description: "File content (populated at build time)"
2473
+ }
2474
+ }
2475
+ }
2476
+ }
2477
+ };
2478
+ async function buildSchemas(outputDir) {
2479
+ const schemaDir = path7.join(outputDir, "schema");
2480
+ await fs6.ensureDir(schemaDir);
2481
+ await fs6.writeJson(path7.join(schemaDir, "config.json"), CONFIG_SCHEMA, {
2482
+ spaces: 2
2483
+ });
2484
+ await fs6.writeJson(path7.join(schemaDir, "employee.json"), EMPLOYEE_SCHEMA, {
2485
+ spaces: 2
2486
+ });
2487
+ logger.success("Built JSON schemas to schema/");
2488
+ }
2489
+
2490
+ // src/commands/build.ts
2142
2491
  var buildOptionsSchema = z9.object({
2143
2492
  cwd: z9.string(),
2144
2493
  employeesDir: z9.string(),
2145
2494
  outputDir: z9.string(),
2495
+ schemaDir: z9.string().optional(),
2146
2496
  registry: z9.string(),
2147
2497
  version: z9.string()
2148
2498
  });
@@ -2157,8 +2507,8 @@ function inferCategory(employeeName) {
2157
2507
  return categoryMap[employeeName] ?? "general";
2158
2508
  }
2159
2509
  async function buildSharedSkills(employeesPath, outputPath, registry, version, skillsIndex) {
2160
- const sharedDir = path7.join(employeesPath, "_shared", "skills");
2161
- if (!await fs6.pathExists(sharedDir)) {
2510
+ const sharedDir = path8.join(employeesPath, "_shared", "skills");
2511
+ if (!await fs7.pathExists(sharedDir)) {
2162
2512
  return 0;
2163
2513
  }
2164
2514
  const skillDirs = await glob("*/SKILL.md", {
@@ -2169,10 +2519,10 @@ async function buildSharedSkills(employeesPath, outputPath, registry, version, s
2169
2519
  }
2170
2520
  const namespace = `${registry}/_shared`;
2171
2521
  for (const skillPath of skillDirs) {
2172
- const skillName = path7.dirname(skillPath);
2522
+ const skillName = path8.dirname(skillPath);
2173
2523
  const skillFullName = `${namespace}/${skillName}`;
2174
- const skillFilePath = path7.join(sharedDir, skillPath);
2175
- const content = await fs6.readFile(skillFilePath, "utf-8");
2524
+ const skillFilePath = path8.join(sharedDir, skillPath);
2525
+ const content = await fs7.readFile(skillFilePath, "utf-8");
2176
2526
  const frontmatter = parseSkillFrontmatter(content);
2177
2527
  if (!frontmatter) {
2178
2528
  logger.warn(`Shared skill ${skillName} missing frontmatter, skipping`);
@@ -2195,15 +2545,15 @@ async function buildSharedSkills(employeesPath, outputPath, registry, version, s
2195
2545
  }
2196
2546
  ]
2197
2547
  };
2198
- const skillOutputDir = path7.join(
2548
+ const skillOutputDir = path8.join(
2199
2549
  outputPath,
2200
2550
  "skills",
2201
2551
  registry.replace("@", ""),
2202
2552
  "_shared"
2203
2553
  );
2204
- await fs6.ensureDir(skillOutputDir);
2205
- await fs6.writeJson(
2206
- path7.join(skillOutputDir, `${skillName}.json`),
2554
+ await fs7.ensureDir(skillOutputDir);
2555
+ await fs7.writeJson(
2556
+ path8.join(skillOutputDir, `${skillName}.json`),
2207
2557
  fullSkill,
2208
2558
  { spaces: 2 }
2209
2559
  );
@@ -2220,11 +2570,12 @@ async function buildSharedSkills(employeesPath, outputPath, registry, version, s
2220
2570
  return skillDirs.length;
2221
2571
  }
2222
2572
  async function runBuild(options) {
2223
- const { cwd, employeesDir, outputDir, registry, version } = options;
2224
- const employeesPath = path7.resolve(cwd, employeesDir);
2225
- const outputPath = path7.resolve(cwd, outputDir);
2226
- if (await fs6.pathExists(outputPath)) {
2227
- await fs6.emptyDir(outputPath);
2573
+ const { cwd, employeesDir, outputDir, schemaDir, registry, version } = options;
2574
+ const employeesPath = path8.resolve(cwd, employeesDir);
2575
+ const outputPath = path8.resolve(cwd, outputDir);
2576
+ const schemaPath = schemaDir ? path8.resolve(cwd, schemaDir) : outputPath;
2577
+ if (await fs7.pathExists(outputPath)) {
2578
+ await fs7.emptyDir(outputPath);
2228
2579
  }
2229
2580
  const employeeFiles = await glob("*/employee.json", {
2230
2581
  cwd: employeesPath,
@@ -2236,19 +2587,17 @@ async function runBuild(options) {
2236
2587
  }
2237
2588
  const s = spinner(`Building ${employeeFiles.length} employee(s)...`).start();
2238
2589
  const skillsIndex = [];
2239
- const employeesIndex = [];
2240
2590
  const legacyIndex = [];
2241
2591
  for (const employeeFile of employeeFiles) {
2242
- const employeeName = path7.dirname(employeeFile);
2243
- const employeeDir = path7.join(employeesPath, employeeName);
2592
+ const employeeName = path8.dirname(employeeFile);
2593
+ const employeeDir = path8.join(employeesPath, employeeName);
2244
2594
  try {
2245
- const employeeJson = await fs6.readJson(
2246
- path7.join(employeeDir, "employee.json")
2595
+ const employeeJson = await fs7.readJson(
2596
+ path8.join(employeeDir, "employee.json")
2247
2597
  );
2248
2598
  const employeeSource = employeeSourceSchema.parse(employeeJson);
2249
2599
  const category = inferCategory(employeeName);
2250
2600
  const namespace = `${registry}/${employeeName}`;
2251
- const employeeFullName = `${registry}/${employeeName}`;
2252
2601
  const employee = {
2253
2602
  ...employeeSource,
2254
2603
  skills: employeeSource.skills.map((sk) => ({
@@ -2264,16 +2613,14 @@ async function runBuild(options) {
2264
2613
  files: d.files.map((f) => ({ ...f, content: "" }))
2265
2614
  }))
2266
2615
  };
2267
- const skillFullNames = [];
2268
2616
  for (const skill of employee.skills) {
2269
2617
  const skillFullName = `${namespace}/${skill.name}`;
2270
- skillFullNames.push(skillFullName);
2271
2618
  const skillFiles = [];
2272
2619
  let skillDescription = "";
2273
2620
  for (const file of skill.files) {
2274
- const filePath = path7.join(employeeDir, file.path);
2275
- if (await fs6.pathExists(filePath)) {
2276
- const content = await fs6.readFile(filePath, "utf-8");
2621
+ const filePath = path8.join(employeeDir, file.path);
2622
+ if (await fs7.pathExists(filePath)) {
2623
+ const content = await fs7.readFile(filePath, "utf-8");
2277
2624
  file.content = content;
2278
2625
  if (file.path.endsWith("SKILL.md")) {
2279
2626
  const frontmatter = parseSkillFrontmatter(content);
@@ -2282,7 +2629,7 @@ async function runBuild(options) {
2282
2629
  }
2283
2630
  }
2284
2631
  skillFiles.push({
2285
- path: path7.basename(file.path),
2632
+ path: path8.basename(file.path),
2286
2633
  type: file.type === "skill" ? "skill" : "reference",
2287
2634
  content
2288
2635
  });
@@ -2301,15 +2648,15 @@ async function runBuild(options) {
2301
2648
  dependencies: [],
2302
2649
  files: skillFiles
2303
2650
  };
2304
- const skillOutputDir = path7.join(
2651
+ const skillOutputDir = path8.join(
2305
2652
  outputPath,
2306
2653
  "skills",
2307
2654
  registry.replace("@", ""),
2308
2655
  employeeName
2309
2656
  );
2310
- await fs6.ensureDir(skillOutputDir);
2311
- await fs6.writeJson(
2312
- path7.join(skillOutputDir, `${skill.name}.json`),
2657
+ await fs7.ensureDir(skillOutputDir);
2658
+ await fs7.writeJson(
2659
+ path8.join(skillOutputDir, `${skill.name}.json`),
2313
2660
  fullSkill,
2314
2661
  { spaces: 2 }
2315
2662
  );
@@ -2325,9 +2672,9 @@ async function runBuild(options) {
2325
2672
  }
2326
2673
  for (const command of employee.commands) {
2327
2674
  for (const file of command.files) {
2328
- const filePath = path7.join(employeeDir, file.path);
2329
- if (await fs6.pathExists(filePath)) {
2330
- file.content = await fs6.readFile(filePath, "utf-8");
2675
+ const filePath = path8.join(employeeDir, file.path);
2676
+ if (await fs7.pathExists(filePath)) {
2677
+ file.content = await fs7.readFile(filePath, "utf-8");
2331
2678
  } else {
2332
2679
  throw new Error(`Command file not found: ${file.path}`);
2333
2680
  }
@@ -2335,48 +2682,17 @@ async function runBuild(options) {
2335
2682
  }
2336
2683
  for (const doc of employee.docs) {
2337
2684
  for (const file of doc.files) {
2338
- const filePath = path7.join(employeeDir, file.path);
2339
- if (await fs6.pathExists(filePath)) {
2340
- file.content = await fs6.readFile(filePath, "utf-8");
2685
+ const filePath = path8.join(employeeDir, file.path);
2686
+ if (await fs7.pathExists(filePath)) {
2687
+ file.content = await fs7.readFile(filePath, "utf-8");
2341
2688
  } else {
2342
2689
  throw new Error(`Doc file not found: ${file.path}`);
2343
2690
  }
2344
2691
  }
2345
2692
  }
2346
- const legacyOutputPath = path7.join(outputPath, `${employee.name}.json`);
2347
- await fs6.ensureDir(outputPath);
2348
- await fs6.writeJson(legacyOutputPath, employee, { spaces: 2 });
2349
- const employeeExtended = {
2350
- $schema: "https://the-aico.com/schema/employee.json",
2351
- name: employee.name,
2352
- namespace: registry,
2353
- fullName: employeeFullName,
2354
- role: employee.role,
2355
- description: employee.description,
2356
- version,
2357
- category,
2358
- skills: skillFullNames,
2359
- commands: employee.commands,
2360
- docs: employee.docs
2361
- };
2362
- const employeesOutputDir = path7.join(outputPath, "employees");
2363
- await fs6.ensureDir(employeesOutputDir);
2364
- await fs6.writeJson(
2365
- path7.join(employeesOutputDir, `${employee.name}.json`),
2366
- employeeExtended,
2367
- { spaces: 2 }
2368
- );
2369
- employeesIndex.push({
2370
- name: employee.name,
2371
- namespace: registry,
2372
- fullName: employeeFullName,
2373
- role: employee.role,
2374
- description: employee.description,
2375
- version,
2376
- skillCount: employee.skills.length,
2377
- commandCount: employee.commands.length,
2378
- category
2379
- });
2693
+ const legacyOutputPath = path8.join(outputPath, `${employee.name}.json`);
2694
+ await fs7.ensureDir(outputPath);
2695
+ await fs7.writeJson(legacyOutputPath, employee, { spaces: 2 });
2380
2696
  legacyIndex.push({
2381
2697
  name: employee.name,
2382
2698
  role: employee.role,
@@ -2399,38 +2715,41 @@ async function runBuild(options) {
2399
2715
  if (sharedSkillCount > 0) {
2400
2716
  s.text = `Built ${sharedSkillCount} shared skill(s)`;
2401
2717
  }
2402
- const skillsIndexPath = path7.join(outputPath, "skills", "index.json");
2403
- await fs6.ensureDir(path7.dirname(skillsIndexPath));
2404
- await fs6.writeJson(skillsIndexPath, skillsIndex, { spaces: 2 });
2405
- const employeesIndexPath = path7.join(outputPath, "employees", "index.json");
2406
- await fs6.ensureDir(path7.dirname(employeesIndexPath));
2407
- await fs6.writeJson(employeesIndexPath, employeesIndex, { spaces: 2 });
2408
- const legacyIndexPath = path7.join(outputPath, "index.json");
2409
- await fs6.writeJson(
2718
+ const skillsIndexPath = path8.join(outputPath, "skills", "index.json");
2719
+ await fs7.ensureDir(path8.dirname(skillsIndexPath));
2720
+ await fs7.writeJson(skillsIndexPath, skillsIndex, { spaces: 2 });
2721
+ const legacyIndexPath = path8.join(outputPath, "index.json");
2722
+ await fs7.writeJson(
2410
2723
  legacyIndexPath,
2411
2724
  { employees: legacyIndex },
2412
2725
  { spaces: 2 }
2413
2726
  );
2727
+ s.text = "Building JSON schemas...";
2728
+ await buildSchemas(schemaPath);
2414
2729
  s.succeed(`Built ${employeeFiles.length} employee(s) to ${outputDir}/`);
2415
2730
  logger.break();
2416
2731
  logger.success("Registry build complete!");
2417
2732
  logger.dim(` Skills: ${skillsIndex.length} (${sharedSkillCount} shared)`);
2418
- logger.dim(` Employees: ${employeesIndex.length}`);
2733
+ logger.dim(` Employees: ${legacyIndex.length}`);
2419
2734
  logger.break();
2420
- for (const emp of employeesIndex) {
2421
- logger.dim(` - ${emp.name}: ${emp.role} (${emp.skillCount} skills)`);
2735
+ for (const emp of legacyIndex) {
2736
+ logger.dim(` - ${emp.name}: ${emp.role}`);
2422
2737
  }
2423
2738
  }
2424
2739
  var build = new Command5().name("build").description("Build registry from employees directory").option(
2425
2740
  "-e, --employees-dir <dir>",
2426
2741
  "Employees source directory",
2427
2742
  "employees"
2428
- ).option("-o, --output-dir <dir>", "Output directory", "registry").option("-r, --registry <name>", "Registry namespace", "@the-aico").option("-v, --version <version>", "Version number", "1.0.0").option("-c, --cwd <cwd>", "Working directory", process.cwd()).action(async (opts) => {
2743
+ ).option("-o, --output-dir <dir>", "Output directory", "registry").option(
2744
+ "-s, --schema-dir <dir>",
2745
+ "Schema output directory (defaults to output-dir)"
2746
+ ).option("-r, --registry <name>", "Registry namespace", "@the-aico").option("-v, --version <version>", "Version number", "1.0.0").option("-c, --cwd <cwd>", "Working directory", process.cwd()).action(async (opts) => {
2429
2747
  try {
2430
2748
  const options = buildOptionsSchema.parse({
2431
2749
  cwd: opts.cwd,
2432
2750
  employeesDir: opts.employeesDir,
2433
2751
  outputDir: opts.outputDir,
2752
+ schemaDir: opts.schemaDir,
2434
2753
  registry: opts.registry,
2435
2754
  version: opts.version
2436
2755
  });
@@ -2441,8 +2760,8 @@ var build = new Command5().name("build").description("Build registry from employ
2441
2760
  });
2442
2761
 
2443
2762
  // src/commands/diff.ts
2444
- import fs7 from "fs-extra";
2445
- import path8 from "path";
2763
+ import fs8 from "fs-extra";
2764
+ import path9 from "path";
2446
2765
  import { Command as Command6 } from "commander";
2447
2766
  import { diffLines } from "diff";
2448
2767
  import { z as z10 } from "zod";
@@ -2455,10 +2774,10 @@ var diffOptionsSchema = z10.object({
2455
2774
  cwd: z10.string()
2456
2775
  });
2457
2776
  async function compareFile(localPath, registryContent) {
2458
- if (!await fs7.pathExists(localPath)) {
2777
+ if (!await fs8.pathExists(localPath)) {
2459
2778
  return null;
2460
2779
  }
2461
- const localContent = await fs7.readFile(localPath, "utf-8");
2780
+ const localContent = await fs8.readFile(localPath, "utf-8");
2462
2781
  const patch = diffLines(localContent, registryContent);
2463
2782
  const hasChanges = patch.some((part) => part.added || part.removed);
2464
2783
  return hasChanges ? patch : null;
@@ -2502,10 +2821,10 @@ async function diffEmployee(employeeName, cwd, config) {
2502
2821
  for (const skill of registryEmployee.skills) {
2503
2822
  const skillDirName = adapter.getSkillDirName(employeeName, skill.name);
2504
2823
  for (const file of skill.files) {
2505
- const localPath = path8.join(
2824
+ const localPath = path9.join(
2506
2825
  skillsDir,
2507
2826
  skillDirName,
2508
- path8.basename(file.path)
2827
+ path9.basename(file.path)
2509
2828
  );
2510
2829
  let registryContent = file.content;
2511
2830
  if (file.path.endsWith("SKILL.md")) {
@@ -2514,7 +2833,7 @@ async function diffEmployee(employeeName, cwd, config) {
2514
2833
  `$1${skillDirName}`
2515
2834
  );
2516
2835
  }
2517
- if (!await fs7.pathExists(localPath)) {
2836
+ if (!await fs8.pathExists(localPath)) {
2518
2837
  if (!skillsAdded.includes(skill.name)) {
2519
2838
  fileChanges.push({
2520
2839
  filePath: localPath,
@@ -2539,8 +2858,8 @@ async function diffEmployee(employeeName, cwd, config) {
2539
2858
  command.name
2540
2859
  );
2541
2860
  for (const file of command.files) {
2542
- const localPath = path8.join(commandsDir, commandFileName);
2543
- if (!await fs7.pathExists(localPath)) {
2861
+ const localPath = path9.join(commandsDir, commandFileName);
2862
+ if (!await fs8.pathExists(localPath)) {
2544
2863
  if (!commandsAdded.includes(command.name)) {
2545
2864
  fileChanges.push({
2546
2865
  filePath: localPath,
@@ -2561,8 +2880,8 @@ async function diffEmployee(employeeName, cwd, config) {
2561
2880
  }
2562
2881
  for (const skillName of skillsRemoved) {
2563
2882
  const skillDirName = adapter.getSkillDirName(employeeName, skillName);
2564
- const skillPath = path8.join(skillsDir, skillDirName);
2565
- if (await fs7.pathExists(skillPath)) {
2883
+ const skillPath = path9.join(skillsDir, skillDirName);
2884
+ if (await fs8.pathExists(skillPath)) {
2566
2885
  fileChanges.push({
2567
2886
  filePath: skillPath,
2568
2887
  type: "removed"
@@ -2574,8 +2893,8 @@ async function diffEmployee(employeeName, cwd, config) {
2574
2893
  employeeName,
2575
2894
  commandName
2576
2895
  );
2577
- const commandPath = path8.join(commandsDir, commandFileName);
2578
- if (await fs7.pathExists(commandPath)) {
2896
+ const commandPath = path9.join(commandsDir, commandFileName);
2897
+ if (await fs8.pathExists(commandPath)) {
2579
2898
  fileChanges.push({
2580
2899
  filePath: commandPath,
2581
2900
  type: "removed"
@@ -2647,7 +2966,7 @@ async function runDiff(options) {
2647
2966
  logger.warn(` Removed commands: ${diff2.commandsRemoved.join(", ")}`);
2648
2967
  }
2649
2968
  for (const change of diff2.fileChanges) {
2650
- const relativePath = path8.relative(cwd, change.filePath);
2969
+ const relativePath = path9.relative(cwd, change.filePath);
2651
2970
  if (change.type === "modified") {
2652
2971
  logger.info(` Modified: ${relativePath}`);
2653
2972
  if (change.patch) {
@@ -2970,7 +3289,7 @@ function formatJSON(items) {
2970
3289
  category: item.category,
2971
3290
  description: item.description,
2972
3291
  tags: item.tags,
2973
- install: item.type === "employee" ? `npx aico add ${item.name}` : `npx aico add ${item.fullName}`
3292
+ install: item.type === "employee" ? `aico add ${item.name}` : `aico add ${item.fullName}`
2974
3293
  }))
2975
3294
  };
2976
3295
  return JSON.stringify(output, null, 2);
@@ -3016,7 +3335,7 @@ var search = new Command8().name("search").description("Search for skills and em
3016
3335
  logger.log(formatTable(results));
3017
3336
  logger.break();
3018
3337
  if (results.length > 0) {
3019
- logger.dim("Install: npx aico add <name>");
3338
+ logger.dim("Install: aico add <name>");
3020
3339
  }
3021
3340
  }
3022
3341
  } catch (error) {
@@ -3287,7 +3606,7 @@ var check = new Command9().name("check").description("Check environment and conf
3287
3606
  const hasWarn = results.some((r) => r.status === "warn");
3288
3607
  if (hasError) {
3289
3608
  logger.log(
3290
- kleur5.red("\u2716 Some checks failed. Run `npx aico init` to fix.")
3609
+ kleur5.red("\u2716 Some checks failed. Run `aico init` to fix.")
3291
3610
  );
3292
3611
  } else if (hasWarn) {
3293
3612
  logger.log(