aigent-team 0.2.0 → 0.3.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/cli.js CHANGED
@@ -1,23 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'module'; const require = createRequire(import.meta.url);
3
3
  import {
4
+ GENERATE_SCOPES,
4
5
  PLATFORMS,
6
+ PLUGIN_BUNDLE_DIRS,
7
+ TEAM_ROLES,
5
8
  assembleReference,
6
9
  assembleSkill,
7
10
  assembleSkillIndex,
8
11
  configExists,
9
12
  loadAgents,
10
13
  loadConfig
11
- } from "./chunk-U3NGK2UZ.js";
14
+ } from "./chunk-RH4B2QFX.js";
12
15
 
13
16
  // bin/cli.ts
14
17
  import { Command } from "commander";
18
+ import chalk6 from "chalk";
15
19
 
16
20
  // src/cli/init.ts
17
- import { writeFileSync as writeFileSync2 } from "fs";
18
- import { resolve as resolve3 } from "path";
21
+ import { writeFileSync } from "fs";
22
+ import { resolve as resolve2 } from "path";
19
23
  import inquirer from "inquirer";
20
- import chalk2 from "chalk";
24
+ import chalk from "chalk";
21
25
 
22
26
  // src/detectors/platform-detector.ts
23
27
  import { execSync } from "child_process";
@@ -65,13 +69,132 @@ function detectPlatforms(cwd = process.cwd()) {
65
69
  return results;
66
70
  }
67
71
 
72
+ // src/cli/init.ts
73
+ async function runInit(cwd = process.cwd()) {
74
+ console.log(chalk.bold("\n\u{1F916} aigent-team init\n"));
75
+ if (configExists(cwd)) {
76
+ const { overwrite } = await inquirer.prompt([
77
+ {
78
+ type: "confirm",
79
+ name: "overwrite",
80
+ message: "aigent-team.config.json already exists. Overwrite?",
81
+ default: false
82
+ }
83
+ ]);
84
+ if (!overwrite) {
85
+ console.log(chalk.yellow("Aborted."));
86
+ return;
87
+ }
88
+ }
89
+ console.log(chalk.dim("Detecting installed AI tools..."));
90
+ const detections = detectPlatforms(cwd);
91
+ const detectedPlatforms = detections.filter((d) => d.detected);
92
+ if (detectedPlatforms.length > 0) {
93
+ console.log(chalk.green(`Found: ${detectedPlatforms.map((d) => d.platform).join(", ")}`));
94
+ } else {
95
+ console.log(chalk.dim("No AI tools detected. You can still select platforms manually."));
96
+ }
97
+ const answers = await inquirer.prompt([
98
+ {
99
+ type: "input",
100
+ name: "projectName",
101
+ message: "Project name:",
102
+ default: cwd.split("/").pop()
103
+ },
104
+ {
105
+ type: "checkbox",
106
+ name: "platforms",
107
+ message: "Target platforms:",
108
+ choices: PLATFORMS.map((p) => ({
109
+ name: p,
110
+ checked: detections.find((d) => d.platform === p)?.detected ?? false
111
+ })),
112
+ validate: (input) => input.length > 0 || "Select at least one platform"
113
+ },
114
+ {
115
+ type: "checkbox",
116
+ name: "teams",
117
+ message: "Team agents to enable:",
118
+ choices: [
119
+ { name: "Lead (Tech Lead / Orchestrator)", value: "lead", checked: true },
120
+ { name: "BA (Business Analyst)", value: "ba", checked: true },
121
+ { name: "FE (Frontend)", value: "fe", checked: true },
122
+ { name: "BE (Backend)", value: "be", checked: true },
123
+ { name: "QA (Testing)", value: "qa", checked: true },
124
+ { name: "DevOps (Infrastructure)", value: "devops", checked: true }
125
+ ],
126
+ validate: (input) => input.length > 0 || "Select at least one team"
127
+ }
128
+ ]);
129
+ const config = {
130
+ projectName: answers.projectName,
131
+ platforms: answers.platforms,
132
+ teams: answers.teams
133
+ };
134
+ const configPath = resolve2(cwd, "aigent-team.config.json");
135
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
136
+ console.log(chalk.green(`
137
+ \u2713 Created ${configPath}`));
138
+ console.log(chalk.bold.green("\n\u2705 aigent-team initialized successfully!\n"));
139
+ console.log(chalk.dim("Next steps:"));
140
+ console.log(chalk.dim(" - Review aigent-team.config.json and adjust if needed"));
141
+ console.log(chalk.dim(" - Run `aigent-team generate` to generate platform configs"));
142
+ console.log(chalk.dim(" - Use `aigent-team generate --scope agents,skills` to control what gets generated\n"));
143
+ }
144
+
68
145
  // src/cli/generate.ts
69
- import { writeFileSync, mkdirSync, existsSync as existsSync2, readFileSync } from "fs";
70
- import { resolve as resolve2, dirname } from "path";
71
- import chalk from "chalk";
146
+ import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2, readFileSync } from "fs";
147
+ import { resolve as resolve3, dirname } from "path";
148
+ import inquirer2 from "inquirer";
149
+ import chalk2 from "chalk";
72
150
 
73
151
  // src/compilers/base.compiler.ts
74
152
  var BaseCompiler = class {
153
+ /**
154
+ * Compile with scope filtering. Delegates to decomposed methods.
155
+ * 'all' = hub + agents + skills + references.
156
+ * 'plugin' is handled externally in generate.ts.
157
+ */
158
+ compileWithScope(agents, config, scopes) {
159
+ const normalized = /* @__PURE__ */ new Set();
160
+ for (const s of scopes) {
161
+ if (s === "all") {
162
+ normalized.add("hub");
163
+ normalized.add("agents");
164
+ normalized.add("skills");
165
+ normalized.add("references");
166
+ } else {
167
+ normalized.add(s);
168
+ }
169
+ }
170
+ const outputs = [];
171
+ if (normalized.has("agents")) {
172
+ outputs.push(...this.compileHubFile(agents, config));
173
+ outputs.push(...this.compileAgentIndexes(agents, config));
174
+ }
175
+ if (normalized.has("skills")) {
176
+ outputs.push(...this.compileAllSkills(agents));
177
+ }
178
+ if (normalized.has("references")) {
179
+ outputs.push(...this.compileAllReferences(agents));
180
+ }
181
+ return outputs;
182
+ }
183
+ /**
184
+ * Count files per artifact category from compiled outputs.
185
+ * Subclasses can override to customize category detection.
186
+ */
187
+ countArtifacts(outputs, rootDir) {
188
+ const counts = {};
189
+ const categories = ["rules", "skills", "agents", "kb", "ai"];
190
+ for (const cat of categories) {
191
+ const prefix = `${rootDir}/${cat}/`;
192
+ const count = outputs.filter((o) => o.filePath.startsWith(prefix)).length;
193
+ if (count > 0) counts[cat] = count;
194
+ }
195
+ return counts;
196
+ }
197
+ // ---- Shared helpers ----
75
198
  /**
76
199
  * Compile reference files for an agent into a given directory.
77
200
  * Returns CompiledOutput[] for each reference file.
@@ -118,6 +241,31 @@ var BaseCompiler = class {
118
241
  var ClaudeCodeCompiler = class extends BaseCompiler {
119
242
  platform = "claude-code";
120
243
  compile(agents, config) {
244
+ return this.compileWithScope(agents, config, ["all"]);
245
+ }
246
+ compileHubFile(agents, _config) {
247
+ const agentList = agents.map((a) => `- **${a.name}** (\`${a.id}\`): ${a.description.trim().split("\n")[0]}`).join("\n");
248
+ const claudeMd = [
249
+ `# CLAUDE.md`,
250
+ ``,
251
+ `This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.`,
252
+ ``,
253
+ `## Agent Team`,
254
+ ``,
255
+ `This project uses aigent-team. The following specialized agents are available:`,
256
+ ``,
257
+ agentList,
258
+ ``,
259
+ `Use the appropriate agent for your task by invoking it from the .claude/agents/ directory.`,
260
+ ``
261
+ ].join("\n");
262
+ return [{
263
+ filePath: "CLAUDE.md",
264
+ content: claudeMd,
265
+ overwriteStrategy: "skip-if-exists"
266
+ }];
267
+ }
268
+ compileAgentIndexes(agents, _config) {
121
269
  const outputs = [];
122
270
  for (const agent of agents) {
123
271
  const frontmatter = this.formatFrontmatter({
@@ -135,37 +283,94 @@ ${body}
135
283
  content,
136
284
  overwriteStrategy: "replace"
137
285
  });
138
- const refs = this.compileReferences(
139
- agent,
140
- `.claude/agents/${agent.id}-agent/references`
141
- );
142
- outputs.push(...refs);
143
- const skills = this.compileSkills(
286
+ }
287
+ return outputs;
288
+ }
289
+ compileAllSkills(agents) {
290
+ const outputs = [];
291
+ for (const agent of agents) {
292
+ outputs.push(...this.compileSkills(
144
293
  agent,
145
294
  `.claude/agents/${agent.id}-agent/skills`
146
- );
147
- outputs.push(...skills);
295
+ ));
296
+ }
297
+ return outputs;
298
+ }
299
+ compileAllReferences(agents) {
300
+ const outputs = [];
301
+ for (const agent of agents) {
302
+ outputs.push(...this.compileReferences(
303
+ agent,
304
+ `.claude/agents/${agent.id}-agent/references`
305
+ ));
148
306
  }
307
+ return outputs;
308
+ }
309
+ compilePluginBundle(agents, config, rootDir) {
310
+ const outputs = [];
149
311
  const agentList = agents.map((a) => `- **${a.name}** (\`${a.id}\`): ${a.description.trim().split("\n")[0]}`).join("\n");
150
- const claudeMd = [
151
- `# CLAUDE.md`,
152
- ``,
153
- `This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.`,
154
- ``,
155
- `## Agent Team`,
156
- ``,
157
- `This project uses aigent-team. The following specialized agents are available:`,
158
- ``,
159
- agentList,
160
- ``,
161
- `Use the appropriate agent for your task by invoking it from the .claude/agents/ directory.`,
162
- ``
163
- ].join("\n");
164
312
  outputs.push({
165
- filePath: "CLAUDE.md",
166
- content: claudeMd,
167
- overwriteStrategy: "skip-if-exists"
313
+ filePath: `${rootDir}/rules/CLAUDE.md`,
314
+ content: [
315
+ `# CLAUDE.md`,
316
+ ``,
317
+ `This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.`,
318
+ ``,
319
+ `## Agent Team`,
320
+ ``,
321
+ `This project uses aigent-team. The following specialized agents are available:`,
322
+ ``,
323
+ agentList,
324
+ ``,
325
+ `Use the appropriate agent for your task by invoking it from the agents/ directory.`,
326
+ ``
327
+ ].join("\n"),
328
+ overwriteStrategy: "replace"
168
329
  });
330
+ for (const agent of agents) {
331
+ const frontmatter = this.formatFrontmatter({
332
+ name: agent.name,
333
+ description: agent.description,
334
+ tools: agent.tools.allowed
335
+ });
336
+ const body = assembleSkillIndex(agent);
337
+ outputs.push({
338
+ filePath: `${rootDir}/agents/${agent.id}-agent.md`,
339
+ content: `${frontmatter}
340
+
341
+ ${body}
342
+ `,
343
+ overwriteStrategy: "replace"
344
+ });
345
+ }
346
+ for (const agent of agents) {
347
+ if (!agent.skills?.length) continue;
348
+ for (const skill of agent.skills) {
349
+ outputs.push({
350
+ filePath: `${rootDir}/skills/${agent.id}/${skill.id}.md`,
351
+ content: assembleSkill(skill) + "\n",
352
+ overwriteStrategy: "replace"
353
+ });
354
+ }
355
+ }
356
+ for (const agent of agents) {
357
+ if (!agent.references?.length) continue;
358
+ for (const ref of agent.references) {
359
+ outputs.push({
360
+ filePath: `${rootDir}/kb/${agent.id}/${ref.id}.md`,
361
+ content: assembleReference(ref) + "\n",
362
+ overwriteStrategy: "replace"
363
+ });
364
+ }
365
+ }
366
+ const sharedKnowledge = agents.flatMap((a) => a.sharedKnowledge).filter((v, i, arr) => arr.indexOf(v) === i && v);
367
+ for (let i = 0; i < sharedKnowledge.length; i++) {
368
+ outputs.push({
369
+ filePath: `${rootDir}/kb/shared/knowledge-${i + 1}.md`,
370
+ content: sharedKnowledge[i] + "\n",
371
+ overwriteStrategy: "replace"
372
+ });
373
+ }
169
374
  return outputs;
170
375
  }
171
376
  validate(outputs) {
@@ -184,10 +389,38 @@ ${body}
184
389
  }
185
390
  };
186
391
 
392
+ // src/compilers/cursor-ide-plugin.compiler.ts
393
+ function toKebabCursorPluginId(projectName) {
394
+ const s = projectName.toLowerCase().replace(/[^a-z0-9.-]+/g, "-").replace(/^-+|-+$/g, "").replace(/\.{2,}/g, ".");
395
+ if (!s || !/^[a-z0-9]/.test(s)) {
396
+ return `aigent-team-${s || "pack"}`.replace(/^-+/, "");
397
+ }
398
+ return s;
399
+ }
400
+
187
401
  // src/compilers/cursor.compiler.ts
188
402
  var CursorCompiler = class extends BaseCompiler {
189
403
  platform = "cursor";
190
404
  compile(agents, config) {
405
+ return this.compileWithScope(agents, config, ["all"]);
406
+ }
407
+ compileHubFile(agents, _config) {
408
+ const sharedKnowledge = agents.flatMap((a) => a.sharedKnowledge).filter((v, i, arr) => arr.indexOf(v) === i && v).join("\n\n---\n\n");
409
+ if (!sharedKnowledge) return [];
410
+ const frontmatter = this.formatFrontmatter({
411
+ description: "Shared project conventions and knowledge for all team agents",
412
+ alwaysApply: true
413
+ });
414
+ return [{
415
+ filePath: ".cursor/rules/shared-conventions.mdc",
416
+ content: `${frontmatter}
417
+
418
+ ${sharedKnowledge}
419
+ `,
420
+ overwriteStrategy: "replace"
421
+ }];
422
+ }
423
+ compileAgentIndexes(agents, _config) {
191
424
  const outputs = [];
192
425
  for (const agent of agents) {
193
426
  const globs = agent.globs?.length ? agent.globs.join(", ") : void 0;
@@ -209,41 +442,74 @@ ${body}
209
442
  content,
210
443
  overwriteStrategy: "replace"
211
444
  });
212
- if (agent.references?.length) {
213
- for (const ref of agent.references) {
214
- const refFrontmatter = this.formatFrontmatter({
215
- description: `${agent.name} reference: ${ref.title}`,
216
- alwaysApply: false,
217
- globs: globs || void 0
218
- });
219
- outputs.push({
220
- filePath: `.cursor/rules/${agent.id}-refs/${ref.id}.mdc`,
221
- content: `${refFrontmatter}
445
+ }
446
+ return outputs;
447
+ }
448
+ compileAllSkills(agents) {
449
+ const outputs = [];
450
+ for (const agent of agents) {
451
+ if (!agent.skills?.length) continue;
452
+ const globs = agent.globs?.length ? agent.globs.join(", ") : void 0;
453
+ for (const skill of agent.skills) {
454
+ const skillFrontmatter = this.formatFrontmatter({
455
+ description: `${agent.name} skill: ${skill.name}`,
456
+ alwaysApply: false,
457
+ globs: globs || void 0
458
+ });
459
+ outputs.push({
460
+ filePath: `.cursor/rules/${agent.id}-skills/${skill.id}.mdc`,
461
+ content: `${skillFrontmatter}
222
462
 
223
- ${ref.content}
463
+ ${skill.content}
224
464
  `,
225
- overwriteStrategy: "replace"
226
- });
227
- }
465
+ overwriteStrategy: "replace"
466
+ });
228
467
  }
229
- if (agent.skills?.length) {
230
- for (const skill of agent.skills) {
231
- const skillFrontmatter = this.formatFrontmatter({
232
- description: `${agent.name} skill: ${skill.name}`,
233
- alwaysApply: false,
234
- globs: globs || void 0
235
- });
236
- outputs.push({
237
- filePath: `.cursor/rules/${agent.id}-skills/${skill.id}.mdc`,
238
- content: `${skillFrontmatter}
468
+ }
469
+ return outputs;
470
+ }
471
+ compileAllReferences(agents) {
472
+ const outputs = [];
473
+ for (const agent of agents) {
474
+ if (!agent.references?.length) continue;
475
+ const globs = agent.globs?.length ? agent.globs.join(", ") : void 0;
476
+ for (const ref of agent.references) {
477
+ const refFrontmatter = this.formatFrontmatter({
478
+ description: `${agent.name} reference: ${ref.title}`,
479
+ alwaysApply: false,
480
+ globs: globs || void 0
481
+ });
482
+ outputs.push({
483
+ filePath: `.cursor/rules/${agent.id}-refs/${ref.id}.mdc`,
484
+ content: `${refFrontmatter}
239
485
 
240
- ${skill.content}
486
+ ${ref.content}
241
487
  `,
242
- overwriteStrategy: "replace"
243
- });
244
- }
488
+ overwriteStrategy: "replace"
489
+ });
245
490
  }
246
491
  }
492
+ return outputs;
493
+ }
494
+ compilePluginBundle(agents, config, rootDir) {
495
+ const outputs = [];
496
+ const pluginId = toKebabCursorPluginId(config.projectName);
497
+ const firstLine = (text) => text.trim().split("\n")[0] ?? "";
498
+ outputs.push({
499
+ filePath: `${rootDir}/.cursor-plugin/plugin.json`,
500
+ content: JSON.stringify(
501
+ {
502
+ name: pluginId,
503
+ version: "0.1.0",
504
+ description: `Agent team pack for ${config.projectName} (generated by aigent-team)`,
505
+ keywords: ["aigent-team", "agents"],
506
+ author: { name: "aigent-team" }
507
+ },
508
+ null,
509
+ 2
510
+ ) + "\n",
511
+ overwriteStrategy: "replace"
512
+ });
247
513
  const sharedKnowledge = agents.flatMap((a) => a.sharedKnowledge).filter((v, i, arr) => arr.indexOf(v) === i && v).join("\n\n---\n\n");
248
514
  if (sharedKnowledge) {
249
515
  const frontmatter = this.formatFrontmatter({
@@ -251,7 +517,7 @@ ${skill.content}
251
517
  alwaysApply: true
252
518
  });
253
519
  outputs.push({
254
- filePath: ".cursor/rules/shared-conventions.mdc",
520
+ filePath: `${rootDir}/rules/shared-conventions.mdc`,
255
521
  content: `${frontmatter}
256
522
 
257
523
  ${sharedKnowledge}
@@ -259,6 +525,62 @@ ${sharedKnowledge}
259
525
  overwriteStrategy: "replace"
260
526
  });
261
527
  }
528
+ for (const agent of agents) {
529
+ const globs = agent.globs?.length ? agent.globs.join(", ") : void 0;
530
+ const frontmatterData = {
531
+ description: firstLine(agent.description),
532
+ alwaysApply: !globs
533
+ };
534
+ if (globs) frontmatterData.globs = globs;
535
+ const frontmatter = this.formatFrontmatter(frontmatterData);
536
+ const body = assembleSkillIndex(agent);
537
+ outputs.push({
538
+ filePath: `${rootDir}/agents/${agent.id}-agent.mdc`,
539
+ content: `${frontmatter}
540
+
541
+ ${body}
542
+ `,
543
+ overwriteStrategy: "replace"
544
+ });
545
+ }
546
+ for (const agent of agents) {
547
+ if (!agent.skills?.length) continue;
548
+ for (const skill of agent.skills) {
549
+ const skillId = `${agent.id}-${skill.id}`;
550
+ const descSource = skill.description?.trim() || skill.name?.trim() || skill.trigger?.trim() || `Procedure for ${agent.name}`;
551
+ const skillFrontmatter = this.formatFrontmatter({
552
+ name: skillId,
553
+ description: firstLine(descSource)
554
+ });
555
+ outputs.push({
556
+ filePath: `${rootDir}/skills/${skillId}/SKILL.md`,
557
+ content: `${skillFrontmatter}
558
+
559
+ ${assembleSkill(skill)}
560
+ `,
561
+ overwriteStrategy: "replace"
562
+ });
563
+ }
564
+ }
565
+ for (const agent of agents) {
566
+ if (!agent.references?.length) continue;
567
+ const globs = agent.globs?.length ? agent.globs.join(", ") : void 0;
568
+ for (const ref of agent.references) {
569
+ const refFrontmatter = this.formatFrontmatter({
570
+ description: `${agent.name} reference: ${ref.title}`,
571
+ alwaysApply: false,
572
+ globs: globs || void 0
573
+ });
574
+ outputs.push({
575
+ filePath: `${rootDir}/kb/${agent.id}-refs/${ref.id}.mdc`,
576
+ content: `${refFrontmatter}
577
+
578
+ ${assembleReference(ref)}
579
+ `,
580
+ overwriteStrategy: "replace"
581
+ });
582
+ }
583
+ }
262
584
  return outputs;
263
585
  }
264
586
  validate(outputs) {
@@ -280,7 +602,9 @@ ${sharedKnowledge}
280
602
  var CodexCompiler = class extends BaseCompiler {
281
603
  platform = "codex";
282
604
  compile(agents, config) {
283
- const outputs = [];
605
+ return this.compileWithScope(agents, config, ["all"]);
606
+ }
607
+ compileHubFile(agents, _config) {
284
608
  const sections = agents.map((agent) => {
285
609
  const body = assembleSkillIndex(agent);
286
610
  return `## ${agent.name} (${agent.id})
@@ -299,11 +623,14 @@ ${body}`;
299
623
 
300
624
  ${sharedKnowledge}` : ""
301
625
  ].filter(Boolean).join("\n\n");
302
- outputs.push({
626
+ return [{
303
627
  filePath: "AGENTS.md",
304
628
  content: agentsMd + "\n",
305
629
  overwriteStrategy: "replace"
306
- });
630
+ }];
631
+ }
632
+ compileAgentIndexes(agents, _config) {
633
+ const outputs = [];
307
634
  for (const agent of agents) {
308
635
  const frontmatter = this.formatFrontmatter({
309
636
  nickname_candidates: [agent.id, agent.role],
@@ -319,32 +646,104 @@ ${body}
319
646
  content,
320
647
  overwriteStrategy: "replace"
321
648
  });
322
- const refs = this.compileReferences(
323
- agent,
324
- `.codex/agents/${agent.id}-agent/references`
325
- );
326
- outputs.push(...refs);
327
- const skills = this.compileSkills(
649
+ }
650
+ return outputs;
651
+ }
652
+ compileAllSkills(agents) {
653
+ const outputs = [];
654
+ for (const agent of agents) {
655
+ outputs.push(...this.compileSkills(
328
656
  agent,
329
657
  `.codex/agents/${agent.id}-agent/skills`
330
- );
331
- outputs.push(...skills);
658
+ ));
332
659
  }
333
660
  return outputs;
334
661
  }
335
- validate(outputs) {
336
- const errors = [];
337
- const warnings = [];
338
- const agentsMd = outputs.find((o) => o.filePath === "AGENTS.md");
339
- if (!agentsMd) {
340
- errors.push("AGENTS.md is missing from compiled output");
662
+ compileAllReferences(agents) {
663
+ const outputs = [];
664
+ for (const agent of agents) {
665
+ outputs.push(...this.compileReferences(
666
+ agent,
667
+ `.codex/agents/${agent.id}-agent/references`
668
+ ));
341
669
  }
342
- return { valid: errors.length === 0, errors, warnings };
670
+ return outputs;
343
671
  }
344
- };
672
+ compilePluginBundle(agents, _config, rootDir) {
673
+ const outputs = [];
674
+ const sections = agents.map((agent) => {
675
+ const body = assembleSkillIndex(agent);
676
+ return `## ${agent.name} (${agent.id})
345
677
 
346
- // src/compilers/antigravity.compiler.ts
347
- var TOOL_MAP = {
678
+ ${body}`;
679
+ });
680
+ const sharedKnowledge = agents.flatMap((a) => a.sharedKnowledge).filter((v, i, arr) => arr.indexOf(v) === i && v).join("\n\n");
681
+ const agentsMd = [
682
+ `# Project Agents`,
683
+ ``,
684
+ `This project uses specialized AI agents for different team roles.`,
685
+ ``,
686
+ ...sections,
687
+ ``,
688
+ sharedKnowledge ? `## Shared Knowledge
689
+
690
+ ${sharedKnowledge}` : ""
691
+ ].filter(Boolean).join("\n\n");
692
+ outputs.push({
693
+ filePath: `${rootDir}/rules/AGENTS.md`,
694
+ content: agentsMd + "\n",
695
+ overwriteStrategy: "replace"
696
+ });
697
+ for (const agent of agents) {
698
+ const frontmatter = this.formatFrontmatter({
699
+ nickname_candidates: [agent.id, agent.role],
700
+ model: "inherit"
701
+ });
702
+ const body = assembleSkillIndex(agent);
703
+ outputs.push({
704
+ filePath: `${rootDir}/agents/${agent.id}-agent.md`,
705
+ content: `${frontmatter}
706
+
707
+ ${body}
708
+ `,
709
+ overwriteStrategy: "replace"
710
+ });
711
+ }
712
+ for (const agent of agents) {
713
+ if (!agent.skills?.length) continue;
714
+ for (const skill of agent.skills) {
715
+ outputs.push({
716
+ filePath: `${rootDir}/skills/${agent.id}/${skill.id}.md`,
717
+ content: assembleSkill(skill) + "\n",
718
+ overwriteStrategy: "replace"
719
+ });
720
+ }
721
+ }
722
+ for (const agent of agents) {
723
+ if (!agent.references?.length) continue;
724
+ for (const ref of agent.references) {
725
+ outputs.push({
726
+ filePath: `${rootDir}/kb/${agent.id}/${ref.id}.md`,
727
+ content: assembleReference(ref) + "\n",
728
+ overwriteStrategy: "replace"
729
+ });
730
+ }
731
+ }
732
+ return outputs;
733
+ }
734
+ validate(outputs) {
735
+ const errors = [];
736
+ const warnings = [];
737
+ const agentsMd = outputs.find((o) => o.filePath === "AGENTS.md");
738
+ if (!agentsMd) {
739
+ errors.push("AGENTS.md is missing from compiled output");
740
+ }
741
+ return { valid: errors.length === 0, errors, warnings };
742
+ }
743
+ };
744
+
745
+ // src/compilers/antigravity.compiler.ts
746
+ var TOOL_MAP = {
348
747
  Read: "read_file",
349
748
  Write: "str_replace_editor",
350
749
  Edit: "str_replace_editor",
@@ -355,7 +754,9 @@ var TOOL_MAP = {
355
754
  var AntigravityCompiler = class extends BaseCompiler {
356
755
  platform = "antigravity";
357
756
  compile(agents, config) {
358
- const outputs = [];
757
+ return this.compileWithScope(agents, config, ["all"]);
758
+ }
759
+ compileHubFile(agents, _config) {
359
760
  const agentList = agents.map((a) => `- **${a.name}** (\`${a.id}\`): ${a.description.trim().split("\n")[0]}`).join("\n");
360
761
  const geminiMd = [
361
762
  `# Antigravity Configuration`,
@@ -368,11 +769,14 @@ var AntigravityCompiler = class extends BaseCompiler {
368
769
  `Use the appropriate agent skill for team-specific tasks.`,
369
770
  ``
370
771
  ].join("\n");
371
- outputs.push({
772
+ return [{
372
773
  filePath: "GEMINI.md",
373
774
  content: geminiMd,
374
775
  overwriteStrategy: "replace"
375
- });
776
+ }];
777
+ }
778
+ compileAgentIndexes(agents, _config) {
779
+ const outputs = [];
376
780
  for (const agent of agents) {
377
781
  const allowedTools = agent.tools.allowed.map((t) => TOOL_MAP[t] || t.toLowerCase()).filter((v, i, arr) => arr.indexOf(v) === i);
378
782
  const frontmatter = this.formatFrontmatter({
@@ -390,16 +794,91 @@ ${body}
390
794
  content,
391
795
  overwriteStrategy: "replace"
392
796
  });
393
- const refs = this.compileReferences(
394
- agent,
395
- `.agents/skills/${agent.id}-agent/references`
396
- );
397
- outputs.push(...refs);
398
- const skills = this.compileSkills(
797
+ }
798
+ return outputs;
799
+ }
800
+ compileAllSkills(agents) {
801
+ const outputs = [];
802
+ for (const agent of agents) {
803
+ outputs.push(...this.compileSkills(
399
804
  agent,
400
805
  `.agents/skills/${agent.id}-agent/skills`
401
- );
402
- outputs.push(...skills);
806
+ ));
807
+ }
808
+ return outputs;
809
+ }
810
+ compileAllReferences(agents) {
811
+ const outputs = [];
812
+ for (const agent of agents) {
813
+ outputs.push(...this.compileReferences(
814
+ agent,
815
+ `.agents/skills/${agent.id}-agent/references`
816
+ ));
817
+ }
818
+ return outputs;
819
+ }
820
+ compilePluginBundle(agents, _config, rootDir) {
821
+ const outputs = [];
822
+ const agentList = agents.map((a) => `- **${a.name}** (\`${a.id}\`): ${a.description.trim().split("\n")[0]}`).join("\n");
823
+ outputs.push({
824
+ filePath: `${rootDir}/rules/GEMINI.md`,
825
+ content: [
826
+ `# Antigravity Configuration`,
827
+ ``,
828
+ `## Available Agents`,
829
+ ``,
830
+ agentList,
831
+ ``,
832
+ `Agents are defined in the agents/ directory.`,
833
+ `Use the appropriate agent for team-specific tasks.`,
834
+ ``
835
+ ].join("\n"),
836
+ overwriteStrategy: "replace"
837
+ });
838
+ for (const agent of agents) {
839
+ const allowedTools = agent.tools.allowed.map((t) => TOOL_MAP[t] || t.toLowerCase()).filter((v, i, arr) => arr.indexOf(v) === i);
840
+ const frontmatter = this.formatFrontmatter({
841
+ name: `${agent.id}-agent`,
842
+ description: agent.description.trim().split("\n")[0],
843
+ "allowed-tools": allowedTools
844
+ });
845
+ const body = assembleSkillIndex(agent);
846
+ outputs.push({
847
+ filePath: `${rootDir}/agents/${agent.id}-agent/SKILL.md`,
848
+ content: `${frontmatter}
849
+
850
+ ${body}
851
+ `,
852
+ overwriteStrategy: "replace"
853
+ });
854
+ }
855
+ for (const agent of agents) {
856
+ if (!agent.skills?.length) continue;
857
+ for (const skill of agent.skills) {
858
+ outputs.push({
859
+ filePath: `${rootDir}/skills/${agent.id}/${skill.id}.md`,
860
+ content: assembleSkill(skill) + "\n",
861
+ overwriteStrategy: "replace"
862
+ });
863
+ }
864
+ }
865
+ for (const agent of agents) {
866
+ if (!agent.references?.length) continue;
867
+ for (const ref of agent.references) {
868
+ outputs.push({
869
+ filePath: `${rootDir}/kb/${agent.id}/${ref.id}.md`,
870
+ content: assembleReference(ref) + "\n",
871
+ overwriteStrategy: "replace"
872
+ });
873
+ }
874
+ }
875
+ const sharedKnowledge = agents.flatMap((a) => a.sharedKnowledge).filter((v, i, arr) => arr.indexOf(v) === i && v);
876
+ for (let i = 0; i < sharedKnowledge.length; i++) {
877
+ outputs.push({
878
+ filePath: `${rootDir}/kb/shared/knowledge-${i + 1}.md`,
879
+ content: sharedKnowledge[i] + "\n",
880
+ overwriteStrategy: "replace"
881
+ });
403
882
  }
404
883
  return outputs;
405
884
  }
@@ -428,13 +907,66 @@ var compilers = {
428
907
  codex: () => new CodexCompiler(),
429
908
  antigravity: () => new AntigravityCompiler()
430
909
  };
910
+ function getCompiler(platform) {
911
+ return compilers[platform]();
912
+ }
431
913
  function getAllCompilers(platforms) {
432
914
  return platforms.map((p) => compilers[p]());
433
915
  }
434
916
 
917
+ // src/compilers/plugin.compiler.ts
918
+ var PluginCompiler = class {
919
+ compilePlugin(agents, config, pluginDir = ".aigent-team-plugin") {
920
+ const outputs = [];
921
+ const bundles = [];
922
+ for (const platform of config.platforms) {
923
+ const bundleDir = PLUGIN_BUNDLE_DIRS[platform];
924
+ const rootDir = `${pluginDir}/${bundleDir}`;
925
+ const compiler = getCompiler(platform);
926
+ const bundleOutputs = compiler.compilePluginBundle(agents, config, rootDir);
927
+ outputs.push(...bundleOutputs);
928
+ bundles.push({
929
+ platform,
930
+ directory: bundleDir,
931
+ artifacts: compiler.countArtifacts(bundleOutputs, rootDir)
932
+ });
933
+ }
934
+ const agentsMeta = agents.map((a) => ({
935
+ id: a.id,
936
+ name: a.name,
937
+ description: a.description,
938
+ role: a.role,
939
+ tools: { allowed: [...a.tools.allowed], ...a.tools.denied ? { denied: [...a.tools.denied] } : {} },
940
+ ...a.globs?.length ? { globs: [...a.globs] } : {}
941
+ }));
942
+ const manifest = {
943
+ name: config.projectName,
944
+ version: "0.1.0",
945
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
946
+ generator: "aigent-team",
947
+ projectName: config.projectName,
948
+ roles: [...config.teams],
949
+ platforms: [...config.platforms],
950
+ agents: agentsMeta,
951
+ files: {
952
+ agents: agents.length,
953
+ skills: agents.reduce((sum, a) => sum + a.skills.length, 0),
954
+ references: agents.reduce((sum, a) => sum + a.references.length, 0)
955
+ },
956
+ bundles
957
+ };
958
+ outputs.push({
959
+ filePath: `${pluginDir}/manifest.json`,
960
+ content: JSON.stringify(manifest, null, 2) + "\n",
961
+ overwriteStrategy: "replace"
962
+ });
963
+ return outputs;
964
+ }
965
+ };
966
+
435
967
  // src/cli/generate.ts
436
968
  function writeOutput(output, cwd) {
437
- const fullPath = resolve2(cwd, output.filePath);
969
+ const fullPath = resolve3(cwd, output.filePath);
438
970
  const dir = dirname(fullPath);
439
971
  if (output.overwriteStrategy === "skip-if-exists" && existsSync2(fullPath)) {
440
972
  return false;
@@ -442,129 +974,129 @@ function writeOutput(output, cwd) {
442
974
  if (output.overwriteStrategy === "merge" && existsSync2(fullPath)) {
443
975
  const existing = readFileSync(fullPath, "utf-8");
444
976
  if (!existing.includes("## Agent Team")) {
445
- writeFileSync(fullPath, existing + "\n" + output.content);
977
+ writeFileSync2(fullPath, existing + "\n" + output.content);
446
978
  return true;
447
979
  }
448
980
  return false;
449
981
  }
450
- mkdirSync(dir, { recursive: true });
451
- writeFileSync(fullPath, output.content);
982
+ mkdirSync2(dir, { recursive: true });
983
+ writeFileSync2(fullPath, output.content);
452
984
  return true;
453
985
  }
986
+ function writeOutputs(outputs, cwd, label) {
987
+ let written = 0;
988
+ for (const output of outputs) {
989
+ if (writeOutput(output, cwd)) {
990
+ written++;
991
+ console.log(chalk2.dim(` ${output.filePath}`));
992
+ }
993
+ }
994
+ console.log(chalk2.green(`\u2713 ${label}: ${written} file(s) generated`));
995
+ return written;
996
+ }
997
+ async function promptGenerateOptions(configTeams, configPlatforms) {
998
+ const { mode } = await inquirer2.prompt([
999
+ {
1000
+ type: "list",
1001
+ name: "mode",
1002
+ message: "Generate mode:",
1003
+ choices: [
1004
+ { name: "Platform configs (agents, skills, references)", value: "platform" },
1005
+ { name: "Plugin bundle", value: "plugin" }
1006
+ ]
1007
+ }
1008
+ ]);
1009
+ if (mode === "plugin") {
1010
+ return { scopes: ["plugin"] };
1011
+ }
1012
+ const answers = await inquirer2.prompt([
1013
+ {
1014
+ type: "checkbox",
1015
+ name: "scopes",
1016
+ message: "What to generate:",
1017
+ choices: [
1018
+ { name: "Agents", value: "agents", checked: true },
1019
+ { name: "Skills", value: "skills", checked: true },
1020
+ { name: "References", value: "references", checked: true }
1021
+ ]
1022
+ },
1023
+ {
1024
+ type: "checkbox",
1025
+ name: "teams",
1026
+ message: "Team agents:",
1027
+ choices: TEAM_ROLES.map((role) => ({
1028
+ name: role,
1029
+ value: role,
1030
+ checked: configTeams.includes(role)
1031
+ })),
1032
+ validate: (input) => input.length > 0 || "Select at least one team"
1033
+ },
1034
+ {
1035
+ type: "checkbox",
1036
+ name: "platforms",
1037
+ message: "Target platforms:",
1038
+ choices: PLATFORMS.map((p) => ({
1039
+ name: p,
1040
+ value: p,
1041
+ checked: configPlatforms.includes(p)
1042
+ })),
1043
+ validate: (input) => input.length > 0 || "Select at least one platform"
1044
+ }
1045
+ ]);
1046
+ const scopes = answers.scopes.length > 0 ? answers.scopes : ["all"];
1047
+ return {
1048
+ scopes,
1049
+ teams: answers.teams,
1050
+ platforms: answers.platforms
1051
+ };
1052
+ }
454
1053
  async function runGenerate(cwd = process.cwd(), options = {}) {
455
1054
  const config = await loadConfig(cwd);
456
- const agents = loadAgents(config, cwd);
457
- const platforms = options.platform ? [options.platform] : config.platforms;
1055
+ const hasFlags = options.platform || options.platforms || options.teams || options.scopes && !(options.scopes.length === 1 && options.scopes[0] === "all");
1056
+ if (!hasFlags && process.stdin.isTTY) {
1057
+ const prompted = await promptGenerateOptions(config.teams, config.platforms);
1058
+ options = { ...options, ...prompted };
1059
+ }
1060
+ const effectiveConfig = options.teams ? { ...config, teams: options.teams } : config;
1061
+ const agents = loadAgents(effectiveConfig, cwd);
1062
+ const scopes = options.scopes ?? ["all"];
1063
+ if (scopes.includes("plugin")) {
1064
+ const pluginDir = effectiveConfig.output?.pluginDir ?? ".aigent-team-plugin";
1065
+ const pluginCompiler = new PluginCompiler();
1066
+ const outputs = pluginCompiler.compilePlugin(agents, effectiveConfig, pluginDir);
1067
+ const written = writeOutputs(outputs, cwd, "plugin");
1068
+ console.log(chalk2.bold(`
1069
+ Total: ${written} file(s) generated`));
1070
+ return;
1071
+ }
1072
+ const platforms = options.platform ? [options.platform] : options.platforms ?? effectiveConfig.platforms;
458
1073
  const compilers2 = getAllCompilers(platforms);
459
1074
  let totalFiles = 0;
460
1075
  let totalWarnings = 0;
461
1076
  for (const compiler of compilers2) {
462
- const outputs = compiler.compile(agents, config);
1077
+ const outputs = compiler.compileWithScope(agents, effectiveConfig, scopes);
463
1078
  const validation = compiler.validate(outputs);
464
1079
  if (!validation.valid) {
465
- console.log(chalk.red(`
1080
+ console.log(chalk2.red(`
466
1081
  \u2717 ${compiler.platform} validation errors:`));
467
1082
  for (const error of validation.errors) {
468
- console.log(chalk.red(` - ${error}`));
1083
+ console.log(chalk2.red(` - ${error}`));
469
1084
  }
470
1085
  continue;
471
1086
  }
472
1087
  for (const warning of validation.warnings) {
473
- console.log(chalk.yellow(` \u26A0 ${warning}`));
1088
+ console.log(chalk2.yellow(` \u26A0 ${warning}`));
474
1089
  totalWarnings++;
475
1090
  }
476
- let written = 0;
477
- for (const output of outputs) {
478
- if (writeOutput(output, cwd)) {
479
- written++;
480
- console.log(chalk.dim(` ${output.filePath}`));
481
- }
482
- }
483
- console.log(chalk.green(`\u2713 ${compiler.platform}: ${written} file(s) generated`));
484
- totalFiles += written;
1091
+ totalFiles += writeOutputs(outputs, cwd, compiler.platform);
485
1092
  }
486
- console.log(chalk.bold(`
1093
+ console.log(chalk2.bold(`
487
1094
  Total: ${totalFiles} file(s) generated`));
488
1095
  if (totalWarnings > 0) {
489
- console.log(chalk.yellow(`${totalWarnings} warning(s)`));
1096
+ console.log(chalk2.yellow(`${totalWarnings} warning(s)`));
490
1097
  }
491
1098
  }
492
1099
 
493
- // src/cli/init.ts
494
- async function runInit(cwd = process.cwd()) {
495
- console.log(chalk2.bold("\n\u{1F916} aigent-team init\n"));
496
- if (configExists(cwd)) {
497
- const { overwrite } = await inquirer.prompt([
498
- {
499
- type: "confirm",
500
- name: "overwrite",
501
- message: "aigent-team.config.json already exists. Overwrite?",
502
- default: false
503
- }
504
- ]);
505
- if (!overwrite) {
506
- console.log(chalk2.yellow("Aborted."));
507
- return;
508
- }
509
- }
510
- console.log(chalk2.dim("Detecting installed AI tools..."));
511
- const detections = detectPlatforms(cwd);
512
- const detectedPlatforms = detections.filter((d) => d.detected);
513
- if (detectedPlatforms.length > 0) {
514
- console.log(chalk2.green(`Found: ${detectedPlatforms.map((d) => d.platform).join(", ")}`));
515
- } else {
516
- console.log(chalk2.dim("No AI tools detected. You can still select platforms manually."));
517
- }
518
- const answers = await inquirer.prompt([
519
- {
520
- type: "input",
521
- name: "projectName",
522
- message: "Project name:",
523
- default: cwd.split("/").pop()
524
- },
525
- {
526
- type: "checkbox",
527
- name: "platforms",
528
- message: "Target platforms:",
529
- choices: PLATFORMS.map((p) => ({
530
- name: p,
531
- checked: detections.find((d) => d.platform === p)?.detected ?? false
532
- })),
533
- validate: (input) => input.length > 0 || "Select at least one platform"
534
- },
535
- {
536
- type: "checkbox",
537
- name: "teams",
538
- message: "Team agents to enable:",
539
- choices: [
540
- { name: "Lead (Tech Lead / Orchestrator)", value: "lead", checked: true },
541
- { name: "BA (Business Analyst)", value: "ba", checked: true },
542
- { name: "FE (Frontend)", value: "fe", checked: true },
543
- { name: "BE (Backend)", value: "be", checked: true },
544
- { name: "QA (Testing)", value: "qa", checked: true },
545
- { name: "DevOps (Infrastructure)", value: "devops", checked: true }
546
- ],
547
- validate: (input) => input.length > 0 || "Select at least one team"
548
- }
549
- ]);
550
- const config = {
551
- projectName: answers.projectName,
552
- platforms: answers.platforms,
553
- teams: answers.teams
554
- };
555
- const configPath = resolve3(cwd, "aigent-team.config.json");
556
- writeFileSync2(configPath, JSON.stringify(config, null, 2) + "\n");
557
- console.log(chalk2.green(`
558
- \u2713 Created ${configPath}`));
559
- console.log(chalk2.dim("\nGenerating platform configs..."));
560
- await runGenerate(cwd);
561
- console.log(chalk2.bold.green("\n\u2705 aigent-team initialized successfully!\n"));
562
- console.log(chalk2.dim("Next steps:"));
563
- console.log(chalk2.dim(" - Edit aigent-team.config.json to customize agents"));
564
- console.log(chalk2.dim(" - Run `aigent-team generate` after making changes"));
565
- console.log(chalk2.dim(" - Add .aigent-team/teams/<role>/ for local overrides\n"));
566
- }
567
-
568
1100
  // src/cli/validate.ts
569
1101
  import { existsSync as existsSync3 } from "fs";
570
1102
  import { resolve as resolve4 } from "path";
@@ -604,18 +1136,358 @@ async function runValidate(cwd = process.cwd()) {
604
1136
  }
605
1137
  }
606
1138
 
1139
+ // src/cli/install.ts
1140
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync5, readFileSync as readFileSync4, cpSync, rmSync } from "fs";
1141
+ import { homedir } from "os";
1142
+ import { resolve as resolve6, dirname as dirname2, join } from "path";
1143
+ import chalk4 from "chalk";
1144
+
1145
+ // src/core/plugin-loader.ts
1146
+ import { readFileSync as readFileSync3, existsSync as existsSync4, readdirSync } from "fs";
1147
+ import { resolve as resolve5, basename } from "path";
1148
+ function loadPlugin(pluginPath) {
1149
+ const absPath = resolve5(pluginPath);
1150
+ const manifestPath = resolve5(absPath, "manifest.json");
1151
+ if (!existsSync4(manifestPath)) {
1152
+ throw new Error(`Plugin manifest not found: ${manifestPath}`);
1153
+ }
1154
+ const manifest = JSON.parse(readFileSync3(manifestPath, "utf-8"));
1155
+ if (!manifest.agents?.length) {
1156
+ throw new Error("Plugin manifest has no agents metadata. Was it generated with aigent-team >= 0.3.0?");
1157
+ }
1158
+ const bundleDir = findBundleDir(absPath, manifest);
1159
+ const agents = [];
1160
+ for (const meta of manifest.agents) {
1161
+ const agentFilePath = resolve5(bundleDir, "agents", `${meta.id}-agent.md`);
1162
+ if (!existsSync4(agentFilePath)) {
1163
+ throw new Error(`Agent file not found: ${agentFilePath}`);
1164
+ }
1165
+ const skillContent = readFileSync3(agentFilePath, "utf-8").trim();
1166
+ const skills = loadSkillFiles(resolve5(bundleDir, "skills", meta.id));
1167
+ const references = loadReferenceFiles(resolve5(bundleDir, "kb", meta.id));
1168
+ const sharedKnowledge = loadSharedKnowledge(resolve5(bundleDir, "kb", "shared"));
1169
+ agents.push({
1170
+ id: meta.id,
1171
+ name: meta.name,
1172
+ description: meta.description,
1173
+ role: meta.role,
1174
+ tools: meta.tools,
1175
+ globs: meta.globs,
1176
+ // The skillContent already contains the assembled index (rules + skill content + catalog).
1177
+ // Set it as skillContent and leave rulesContent/skills empty so assembleSkillIndex()
1178
+ // returns it as-is without double-including rules or catalog.
1179
+ skillContent,
1180
+ rulesContent: "",
1181
+ skills,
1182
+ references,
1183
+ sharedKnowledge,
1184
+ // Unused during install — compilers only need the fields above
1185
+ systemPrompt: "",
1186
+ techStack: { languages: [], frameworks: [], libraries: [], buildTools: [] },
1187
+ conventions: "",
1188
+ reviewChecklist: "",
1189
+ workflows: []
1190
+ });
1191
+ }
1192
+ return { manifest, agents };
1193
+ }
1194
+ function findBundleDir(absPath, manifest) {
1195
+ const preferred = ["claude-code-plugin", "cursor-ide-plugin", "codex-plugin", "antigravity-plugin"];
1196
+ for (const dir of preferred) {
1197
+ const candidate = resolve5(absPath, dir);
1198
+ if (existsSync4(candidate)) return candidate;
1199
+ }
1200
+ for (const bundle of manifest.bundles ?? []) {
1201
+ const candidate = resolve5(absPath, bundle.directory);
1202
+ if (existsSync4(candidate)) return candidate;
1203
+ }
1204
+ throw new Error(`No platform bundle found in plugin: ${absPath}`);
1205
+ }
1206
+ function loadSkillFiles(dir) {
1207
+ if (!existsSync4(dir)) return [];
1208
+ return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => {
1209
+ const id = basename(f, ".md");
1210
+ const content = readFileSync3(resolve5(dir, f), "utf-8").trim();
1211
+ return {
1212
+ id,
1213
+ name: id.replace(/-/g, " "),
1214
+ description: "",
1215
+ trigger: "",
1216
+ content
1217
+ };
1218
+ });
1219
+ }
1220
+ function loadReferenceFiles(dir) {
1221
+ if (!existsSync4(dir)) return [];
1222
+ return readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => {
1223
+ const id = basename(f, ".md");
1224
+ const content = readFileSync3(resolve5(dir, f), "utf-8").trim();
1225
+ return {
1226
+ id,
1227
+ title: id.replace(/-/g, " "),
1228
+ description: "",
1229
+ whenToRead: "",
1230
+ content
1231
+ };
1232
+ });
1233
+ }
1234
+ function loadSharedKnowledge(dir) {
1235
+ if (!existsSync4(dir)) return [];
1236
+ return readdirSync(dir).filter((f) => f.endsWith(".md")).sort().map((f) => readFileSync3(resolve5(dir, f), "utf-8").trim()).filter(Boolean);
1237
+ }
1238
+
1239
+ // src/cli/install.ts
1240
+ function readCursorPluginId(cursorIdePluginRoot) {
1241
+ const manifestPath = resolve6(cursorIdePluginRoot, ".cursor-plugin", "plugin.json");
1242
+ if (!existsSync5(manifestPath)) {
1243
+ throw new Error(`Missing ${manifestPath}`);
1244
+ }
1245
+ const raw = JSON.parse(readFileSync4(manifestPath, "utf-8"));
1246
+ if (!raw.name || typeof raw.name !== "string") {
1247
+ throw new Error(`Invalid Cursor plugin manifest: missing "name" in ${manifestPath}`);
1248
+ }
1249
+ return raw.name;
1250
+ }
1251
+ function installCursorUserPlugin(cursorIdePluginSource, force) {
1252
+ const pluginId = readCursorPluginId(cursorIdePluginSource);
1253
+ const localRoot = join(homedir(), ".cursor", "plugins", "local");
1254
+ const dest = join(localRoot, pluginId);
1255
+ if (existsSync5(dest) && !force) {
1256
+ throw new Error(
1257
+ `Cursor user plugin already exists: ${dest}
1258
+ Use --force to replace, or remove that folder first.`
1259
+ );
1260
+ }
1261
+ mkdirSync3(localRoot, { recursive: true });
1262
+ if (existsSync5(dest)) {
1263
+ rmSync(dest, { recursive: true, force: true });
1264
+ }
1265
+ cpSync(cursorIdePluginSource, dest, { recursive: true });
1266
+ return dest;
1267
+ }
1268
+ function writeInstallOutput(output, cwd, force) {
1269
+ const fullPath = resolve6(cwd, output.filePath);
1270
+ const dir = dirname2(fullPath);
1271
+ if (!force && existsSync5(fullPath)) {
1272
+ return false;
1273
+ }
1274
+ mkdirSync3(dir, { recursive: true });
1275
+ writeFileSync3(fullPath, output.content);
1276
+ return true;
1277
+ }
1278
+ async function runInstall(pluginPath, cwd = process.cwd(), options = {}) {
1279
+ const absPluginPath = resolve6(cwd, pluginPath);
1280
+ console.log(chalk4.dim(`Loading plugin from ${absPluginPath}...`));
1281
+ const { manifest, agents } = loadPlugin(absPluginPath);
1282
+ console.log(chalk4.bold(`Plugin: ${manifest.name} v${manifest.version}`));
1283
+ console.log(chalk4.dim(` ${manifest.agents.length} agent(s), ${manifest.files.skills} skill(s), ${manifest.files.references} reference(s)`));
1284
+ let platforms;
1285
+ if (options.platform) {
1286
+ platforms = [options.platform];
1287
+ } else if (configExists(cwd)) {
1288
+ const config2 = await loadConfig(cwd);
1289
+ platforms = config2.platforms;
1290
+ } else {
1291
+ platforms = manifest.platforms;
1292
+ }
1293
+ const config = configExists(cwd) ? await loadConfig(cwd) : {
1294
+ projectName: manifest.projectName,
1295
+ platforms,
1296
+ teams: manifest.roles
1297
+ };
1298
+ const compilers2 = getAllCompilers(platforms);
1299
+ const allWrittenFiles = [];
1300
+ let totalFiles = 0;
1301
+ let skipped = 0;
1302
+ for (const compiler of compilers2) {
1303
+ if (options.cursorUserPlugin && compiler.platform === "cursor") {
1304
+ continue;
1305
+ }
1306
+ const outputs = compiler.compileWithScope(agents, config, ["all"]);
1307
+ const validation = compiler.validate(outputs);
1308
+ if (!validation.valid) {
1309
+ console.log(chalk4.red(`
1310
+ \u2717 ${compiler.platform} validation errors:`));
1311
+ for (const error of validation.errors) {
1312
+ console.log(chalk4.red(` - ${error}`));
1313
+ }
1314
+ continue;
1315
+ }
1316
+ for (const warning of validation.warnings) {
1317
+ console.log(chalk4.yellow(` \u26A0 ${warning}`));
1318
+ }
1319
+ let written = 0;
1320
+ for (const output of outputs) {
1321
+ if (writeInstallOutput(output, cwd, !!options.force)) {
1322
+ written++;
1323
+ allWrittenFiles.push(output.filePath);
1324
+ console.log(chalk4.dim(` ${output.filePath}`));
1325
+ } else {
1326
+ skipped++;
1327
+ }
1328
+ }
1329
+ console.log(chalk4.green(`\u2713 ${compiler.platform}: ${written} file(s) installed`));
1330
+ totalFiles += written;
1331
+ }
1332
+ let cursorUserPluginPath;
1333
+ const cursorIdeBundle = resolve6(absPluginPath, "cursor-ide-plugin");
1334
+ const shouldInstallCursorUser = !!options.cursorUserPlugin && (!options.platform || options.platform === "cursor") && platforms.includes("cursor");
1335
+ if (shouldInstallCursorUser) {
1336
+ if (!existsSync5(cursorIdeBundle)) {
1337
+ console.log(
1338
+ chalk4.red(
1339
+ `
1340
+ \u2717 cursor-ide-plugin/ not found under ${absPluginPath}.
1341
+ Run: npx aigent-team generate --scope plugin`
1342
+ )
1343
+ );
1344
+ } else {
1345
+ try {
1346
+ cursorUserPluginPath = installCursorUserPlugin(cursorIdeBundle, !!options.force);
1347
+ console.log(chalk4.green(`\u2713 cursor (user plugin): ${cursorUserPluginPath}`));
1348
+ } catch (e) {
1349
+ console.log(chalk4.red(`
1350
+ \u2717 Cursor user plugin install failed: ${e.message}`));
1351
+ }
1352
+ }
1353
+ }
1354
+ const recordDir = resolve6(cwd, ".aigent-team", "installed");
1355
+ mkdirSync3(recordDir, { recursive: true });
1356
+ const record = {
1357
+ name: manifest.name,
1358
+ version: manifest.version,
1359
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
1360
+ pluginPath: absPluginPath,
1361
+ files: allWrittenFiles,
1362
+ ...cursorUserPluginPath ? { cursorUserPluginPath } : {}
1363
+ };
1364
+ writeFileSync3(
1365
+ resolve6(recordDir, `${manifest.name}.json`),
1366
+ JSON.stringify(record, null, 2) + "\n"
1367
+ );
1368
+ console.log(chalk4.bold(`
1369
+ Total: ${totalFiles} file(s) installed`));
1370
+ if (skipped > 0) {
1371
+ console.log(chalk4.yellow(`${skipped} file(s) skipped (already exist, use --force to overwrite)`));
1372
+ }
1373
+ }
1374
+
1375
+ // src/cli/uninstall.ts
1376
+ import { unlinkSync, existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync2, rmdirSync, rmSync as rmSync2 } from "fs";
1377
+ import { resolve as resolve7, dirname as dirname3 } from "path";
1378
+ import chalk5 from "chalk";
1379
+ async function runUninstall(pluginName, cwd = process.cwd()) {
1380
+ const recordPath = resolve7(cwd, ".aigent-team", "installed", `${pluginName}.json`);
1381
+ if (!existsSync6(recordPath)) {
1382
+ const installedDir = resolve7(cwd, ".aigent-team", "installed");
1383
+ if (existsSync6(installedDir)) {
1384
+ const installed = readdirSync2(installedDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
1385
+ if (installed.length > 0) {
1386
+ console.log(chalk5.red(`Plugin "${pluginName}" is not installed.`));
1387
+ console.log(chalk5.dim(`Installed plugins: ${installed.join(", ")}`));
1388
+ return;
1389
+ }
1390
+ }
1391
+ console.log(chalk5.red(`No installed plugins found.`));
1392
+ return;
1393
+ }
1394
+ const record = JSON.parse(readFileSync5(recordPath, "utf-8"));
1395
+ console.log(chalk5.bold(`Uninstalling: ${record.name} v${record.version}`));
1396
+ if (record.cursorUserPluginPath && existsSync6(record.cursorUserPluginPath)) {
1397
+ rmSync2(record.cursorUserPluginPath, { recursive: true, force: true });
1398
+ console.log(chalk5.dim(` \u2717 ${record.cursorUserPluginPath} (Cursor user plugin)`));
1399
+ }
1400
+ let removed = 0;
1401
+ let missing = 0;
1402
+ for (const filePath of record.files) {
1403
+ const fullPath = resolve7(cwd, filePath);
1404
+ if (existsSync6(fullPath)) {
1405
+ unlinkSync(fullPath);
1406
+ removed++;
1407
+ console.log(chalk5.dim(` \u2717 ${filePath}`));
1408
+ tryRemoveEmptyDir(dirname3(fullPath));
1409
+ } else {
1410
+ missing++;
1411
+ }
1412
+ }
1413
+ unlinkSync(recordPath);
1414
+ console.log(chalk5.bold(`
1415
+ Removed: ${removed} file(s)`));
1416
+ if (missing > 0) {
1417
+ console.log(chalk5.yellow(`${missing} file(s) already missing`));
1418
+ }
1419
+ }
1420
+ function tryRemoveEmptyDir(dir) {
1421
+ try {
1422
+ const entries = readdirSync2(dir);
1423
+ if (entries.length === 0) {
1424
+ rmdirSync(dir);
1425
+ tryRemoveEmptyDir(dirname3(dir));
1426
+ }
1427
+ } catch {
1428
+ }
1429
+ }
1430
+
607
1431
  // bin/cli.ts
608
1432
  var program = new Command();
609
- program.name("aigent-team").description("Cross-platform AI agent team plugin for Claude Code, Cursor, Codex, and Antigravity").version("0.1.0");
1433
+ program.name("aigent-team").description("Cross-platform AI agent team plugin for Claude Code, Cursor, Codex, and Antigravity").version("0.2.0");
610
1434
  program.command("init").description("Initialize aigent-team in your project (interactive)").action(async () => {
611
1435
  await runInit(process.cwd());
612
1436
  });
613
- program.command("generate").description("Generate platform-specific config files from aigent-team config").option("-p, --platform <platform>", "Generate for a specific platform only").action(async (options) => {
1437
+ program.command("generate").description("Generate platform-specific config files from aigent-team config").option("-p, --platform <platform>", "Generate for a specific platform only").option("-s, --scope <scopes>", "Output scope(s), comma-separated: all | agents | skills | references | plugin", "all").option("-t, --team <teams>", "Team roles to generate, comma-separated (overrides config)").action(async (options) => {
1438
+ if (options.platform && !PLATFORMS.includes(options.platform)) {
1439
+ console.log(chalk6.red(`Unknown platform: ${options.platform}. Valid: ${PLATFORMS.join(", ")}`));
1440
+ process.exit(1);
1441
+ }
1442
+ const scopes = options.scope.split(",");
1443
+ for (const s of scopes) {
1444
+ if (!GENERATE_SCOPES.includes(s)) {
1445
+ console.log(chalk6.red(`Unknown scope: ${s}. Valid: ${GENERATE_SCOPES.join(", ")}`));
1446
+ process.exit(1);
1447
+ }
1448
+ }
1449
+ if (scopes.includes("plugin") && scopes.length > 1) {
1450
+ console.log(chalk6.red(`Scope 'plugin' is exclusive and cannot be combined with other scopes.`));
1451
+ process.exit(1);
1452
+ }
1453
+ if (scopes.includes("all") && scopes.length > 1) {
1454
+ console.log(chalk6.red(`Scope 'all' is exclusive and cannot be combined with other scopes.`));
1455
+ process.exit(1);
1456
+ }
1457
+ let teams;
1458
+ if (options.team) {
1459
+ teams = options.team.split(",");
1460
+ for (const t of teams) {
1461
+ if (!TEAM_ROLES.includes(t)) {
1462
+ console.log(chalk6.red(`Unknown team role: ${t}. Valid: ${TEAM_ROLES.join(", ")}`));
1463
+ process.exit(1);
1464
+ }
1465
+ }
1466
+ }
614
1467
  await runGenerate(process.cwd(), {
615
- platform: options.platform
1468
+ platform: options.platform,
1469
+ scopes,
1470
+ teams
616
1471
  });
617
1472
  });
618
1473
  program.command("validate").description("Validate generated config files against platform constraints").action(async () => {
619
1474
  await runValidate(process.cwd());
620
1475
  });
1476
+ program.command("install").description("Install a plugin bundle into the current project").argument("<plugin-path>", "Path to plugin directory (containing manifest.json)").option("-p, --platform <platform>", "Install for a specific platform only").option("-f, --force", "Overwrite existing files").option(
1477
+ "--cursor-user-plugin",
1478
+ "Install Cursor IDE bundle from plugin cursor-ide-plugin/ to ~/.cursor/plugins/local/ (see cursor.com/docs/plugins)"
1479
+ ).action(async (pluginPath, options) => {
1480
+ if (options.platform && !PLATFORMS.includes(options.platform)) {
1481
+ console.log(chalk6.red(`Unknown platform: ${options.platform}. Valid: ${PLATFORMS.join(", ")}`));
1482
+ process.exit(1);
1483
+ }
1484
+ await runInstall(pluginPath, process.cwd(), {
1485
+ platform: options.platform,
1486
+ force: options.force,
1487
+ cursorUserPlugin: options.cursorUserPlugin
1488
+ });
1489
+ });
1490
+ program.command("uninstall").description("Uninstall a previously installed plugin").argument("<plugin-name>", "Name of the plugin to uninstall").action(async (pluginName) => {
1491
+ await runUninstall(pluginName, process.cwd());
1492
+ });
621
1493
  program.parse();