@tdsoft-tech/aikit 0.1.11 → 0.1.13

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
@@ -803,8 +803,8 @@ var init_memory = __esm({
803
803
  const subDir = type === "observation" ? "observations" : type === "handoff" ? "handoffs" : type === "research" ? "research" : "";
804
804
  filePath = join8(memoryPath, subDir, `${key}.md`);
805
805
  }
806
- const { dirname: dirname4 } = await import("path");
807
- await mkdir4(dirname4(filePath), { recursive: true });
806
+ const { dirname: dirname5 } = await import("path");
807
+ await mkdir4(dirname5(filePath), { recursive: true });
808
808
  if (options?.append) {
809
809
  try {
810
810
  const existing = await readFile5(filePath, "utf-8");
@@ -934,8 +934,8 @@ var tool_config_exports = {};
934
934
  __export(tool_config_exports, {
935
935
  ToolConfigManager: () => ToolConfigManager
936
936
  });
937
- import { readFile as readFile11, writeFile as writeFile12, mkdir as mkdir10, access as access4, constants as constants4 } from "fs/promises";
938
- import { join as join17 } from "path";
937
+ import { readFile as readFile12, writeFile as writeFile13, mkdir as mkdir11, access as access5, constants as constants4 } from "fs/promises";
938
+ import { join as join18 } from "path";
939
939
  import { z as z3 } from "zod";
940
940
  var ToolConfigSchema, REGISTERED_TOOLS, ToolConfigManager;
941
941
  var init_tool_config = __esm({
@@ -963,7 +963,7 @@ var init_tool_config = __esm({
963
963
  toolsConfigPath;
964
964
  constructor(config) {
965
965
  this.config = config;
966
- this.toolsConfigPath = join17(this.config.configPath, "config", "tools.json");
966
+ this.toolsConfigPath = join18(this.config.configPath, "config", "tools.json");
967
967
  }
968
968
  /**
969
969
  * Get all registered tools with their current status
@@ -1046,8 +1046,8 @@ var init_tool_config = __esm({
1046
1046
  */
1047
1047
  async loadConfigs() {
1048
1048
  try {
1049
- await access4(this.toolsConfigPath, constants4.R_OK);
1050
- const content = await readFile11(this.toolsConfigPath, "utf-8");
1049
+ await access5(this.toolsConfigPath, constants4.R_OK);
1050
+ const content = await readFile12(this.toolsConfigPath, "utf-8");
1051
1051
  return JSON.parse(content);
1052
1052
  } catch {
1053
1053
  return {};
@@ -1057,9 +1057,9 @@ var init_tool_config = __esm({
1057
1057
  * Save configurations
1058
1058
  */
1059
1059
  async saveConfigs(configs) {
1060
- const configDir = join17(this.config.configPath, "config");
1061
- await mkdir10(configDir, { recursive: true });
1062
- await writeFile12(this.toolsConfigPath, JSON.stringify(configs, null, 2));
1060
+ const configDir = join18(this.config.configPath, "config");
1061
+ await mkdir11(configDir, { recursive: true });
1062
+ await writeFile13(this.toolsConfigPath, JSON.stringify(configs, null, 2));
1063
1063
  }
1064
1064
  };
1065
1065
  }
@@ -1196,9 +1196,10 @@ var init_figma_oauth = __esm({
1196
1196
 
1197
1197
  // src/cli.ts
1198
1198
  init_esm_shims();
1199
+
1200
+ // src/cli/index.ts
1201
+ init_esm_shims();
1199
1202
  import { Command } from "commander";
1200
- import chalk3 from "chalk";
1201
- import inquirer2 from "inquirer";
1202
1203
 
1203
1204
  // src/index.ts
1204
1205
  init_esm_shims();
@@ -1266,7 +1267,8 @@ var ConfigSchema = z.object({
1266
1267
  context7: z.boolean().default(false),
1267
1268
  githubGrep: z.boolean().default(false),
1268
1269
  gkg: z.boolean().default(false)
1269
- }).optional()
1270
+ }).optional(),
1271
+ mode: z.string().default("build").optional()
1270
1272
  });
1271
1273
  var Config = class {
1272
1274
  config;
@@ -1300,6 +1302,9 @@ var Config = class {
1300
1302
  get antiHallucination() {
1301
1303
  return this.config.antiHallucination;
1302
1304
  }
1305
+ get mode() {
1306
+ return this.config.mode;
1307
+ }
1303
1308
  get configPath() {
1304
1309
  return this.config.configPath;
1305
1310
  }
@@ -1771,6 +1776,51 @@ For diagrams:
1771
1776
  - Map to code structure
1772
1777
  - Note data flow`,
1773
1778
  delegatesTo: []
1779
+ },
1780
+ {
1781
+ name: "one-shot",
1782
+ displayName: "@one-shot",
1783
+ description: "End-to-end autonomous task execution (beta)",
1784
+ useWhen: "Complete tasks autonomously from start to finish with minimal intervention",
1785
+ capabilities: [
1786
+ "Gather requirements interactively",
1787
+ "Create detailed implementation plans",
1788
+ "Execute tasks with dynamic agent selection",
1789
+ "Run quality gates until all pass",
1790
+ "Multi-level verification",
1791
+ "Auto-recovery from failures",
1792
+ "Generate completion proof"
1793
+ ],
1794
+ systemPrompt: `You are the one-shot agent. Your role is to execute tasks autonomously from start to finish.
1795
+
1796
+ ## Workflow Phases
1797
+
1798
+ 1. **REQUIREMENTS**: Gather task type, scope, dependencies, success criteria
1799
+ 2. **PLANNING**: Create detailed plan with @planner, recommend skills/tools
1800
+ 3. **COMPLEXITY**: Auto-split large tasks (>30min, >10 files, >500 lines)
1801
+ 4. **EXECUTION**: Execute with parallel tasks, dynamic agent delegation
1802
+ 5. **TESTING**: Run quality gates (typecheck, test, lint, build) until all pass
1803
+ 6. **VERIFICATION**: Multi-level verification (gates + manual + deployment)
1804
+ 7. **COMPLETION**: Generate proof, update tracking, collect feedback
1805
+
1806
+ ## Quality Gates (Must ALL Pass)
1807
+ - npm run typecheck - No type errors
1808
+ - npm run test - All tests pass
1809
+ - npm run lint - No linting errors
1810
+ - npm run build - Build succeeds
1811
+
1812
+ ## Error Recovery (3 Levels)
1813
+ - Level 1: Auto-fix (type errors, lint --fix)
1814
+ - Level 2: Alternative approach via @review
1815
+ - Level 3: User intervention + follow-up task creation
1816
+
1817
+ ## Best Practices
1818
+ - Use for straightforward tasks first
1819
+ - Consider /plan + /implement for complex features
1820
+ - Review changes before final approval
1821
+
1822
+ \u26A0\uFE0F This mode is experimental. Start with simpler tasks.`,
1823
+ delegatesTo: ["planner", "build", "review", "scout", "explore", "vision"]
1774
1824
  }
1775
1825
  ];
1776
1826
  var AgentManager = class {
@@ -4195,8 +4245,13 @@ init_logger();
4195
4245
  init_logger();
4196
4246
  init_paths();
4197
4247
 
4198
- // src/cli.ts
4199
- init_memory();
4248
+ // src/cli/commands/index.ts
4249
+ init_esm_shims();
4250
+
4251
+ // src/cli/commands/init.ts
4252
+ init_esm_shims();
4253
+ import chalk2 from "chalk";
4254
+ import inquirer from "inquirer";
4200
4255
 
4201
4256
  // src/utils/cli-detector.ts
4202
4257
  init_esm_shims();
@@ -4313,192 +4368,808 @@ var CliDetector = class {
4313
4368
  }
4314
4369
  };
4315
4370
 
4316
- // src/cli.ts
4371
+ // src/cli/commands/init.ts
4317
4372
  init_logger();
4318
4373
  init_paths();
4319
4374
 
4320
- // src/core/sync-engine.ts
4321
- init_esm_shims();
4322
- import { readFile as readFile10, writeFile as writeFile11, copyFile, mkdir as mkdir9 } from "fs/promises";
4323
- import { join as join16, dirname as dirname3 } from "path";
4324
- import inquirer from "inquirer";
4325
- import chalk2 from "chalk";
4326
-
4327
- // src/core/version-manager.ts
4375
+ // src/cli/helpers.ts
4328
4376
  init_esm_shims();
4329
- init_paths();
4377
+ import { existsSync as existsSync5 } from "fs";
4378
+ import { mkdir as mkdir8, writeFile as writeFile8, readFile as readFile7, access as access4 } from "fs/promises";
4379
+ import { join as join13, dirname as dirname2 } from "path";
4380
+ import { homedir as homedir3 } from "os";
4381
+ import { fileURLToPath as fileURLToPath3 } from "url";
4382
+ import { execSync as execSync2 } from "child_process";
4330
4383
  init_logger();
4331
- import { readFile as readFile7, readdir as readdir7, writeFile as writeFile8, stat } from "fs/promises";
4332
- import { join as join13 } from "path";
4333
- import { createHash } from "crypto";
4334
- var VersionManager = class {
4335
- config;
4336
- constructor(config) {
4337
- this.config = config;
4384
+ init_paths();
4385
+ async function initializeConfig(configDir, _isGlobal) {
4386
+ const dirs = [
4387
+ "",
4388
+ "skills",
4389
+ "agents",
4390
+ "commands",
4391
+ "commands/build",
4392
+ "commands/git",
4393
+ "commands/plan",
4394
+ "commands/research",
4395
+ "tools",
4396
+ "plugins",
4397
+ "memory",
4398
+ "memory/_templates",
4399
+ "memory/handoffs",
4400
+ "memory/observations",
4401
+ "memory/research"
4402
+ ];
4403
+ for (const dir of dirs) {
4404
+ await mkdir8(join13(configDir, dir), { recursive: true });
4338
4405
  }
4339
- /**
4340
- * Get current installed version
4341
- */
4342
- async getCurrentVersion() {
4343
- const versionPath = join13(paths.globalConfig(), ".version.json");
4406
+ const defaultConfig = {
4407
+ version: getVersion(),
4408
+ skills: { enabled: true },
4409
+ agents: { enabled: true, default: "build" },
4410
+ commands: { enabled: true },
4411
+ tools: { enabled: true },
4412
+ plugins: { enabled: true },
4413
+ memory: { enabled: true },
4414
+ beads: { enabled: true },
4415
+ antiHallucination: { enabled: true }
4416
+ };
4417
+ await writeFile8(
4418
+ join13(configDir, "aikit.json"),
4419
+ JSON.stringify(defaultConfig, null, 2)
4420
+ );
4421
+ const agentsMd = `# AIKit Agent Rules
4422
+
4423
+ ## Build Commands
4424
+ - \`npm run build\` - Build the project
4425
+ - \`npm run test\` - Run tests
4426
+ - \`npm run lint\` - Run linting
4427
+
4428
+ ## Code Style
4429
+ - Use 2 spaces for indentation
4430
+ - Use single quotes for strings
4431
+ - Add trailing commas
4432
+
4433
+ ## Naming Conventions
4434
+ - Variables: camelCase
4435
+ - Components: PascalCase
4436
+ - Files: kebab-case
4437
+
4438
+ ## Project-Specific Rules
4439
+ Add your project-specific rules here.
4440
+ `;
4441
+ await writeFile8(join13(configDir, "AGENTS.md"), agentsMd);
4442
+ }
4443
+ async function configureMcpServer(projectPath) {
4444
+ const currentFile = fileURLToPath3(import.meta.url);
4445
+ const currentDir = dirname2(currentFile);
4446
+ const aikitPath = join13(currentDir, "..", "..");
4447
+ const mcpServerPath = join13(aikitPath, "dist", "mcp-server.js");
4448
+ const configLocations = [
4449
+ // Global config (most common)
4450
+ join13(homedir3(), ".config", "opencode", "opencode.json"),
4451
+ // Project-level config
4452
+ join13(projectPath, ".opencode", "opencode.json"),
4453
+ // Alternative global location
4454
+ join13(homedir3(), ".opencode", "opencode.json")
4455
+ ];
4456
+ const mcpServerConfig = {
4457
+ type: "local",
4458
+ command: ["node", mcpServerPath],
4459
+ environment: {}
4460
+ };
4461
+ for (const configPath of configLocations) {
4344
4462
  try {
4345
- const content = await readFile7(versionPath, "utf-8");
4346
- return JSON.parse(content);
4463
+ const configDir = join13(configPath, "..");
4464
+ await mkdir8(configDir, { recursive: true });
4465
+ let config = {};
4466
+ if (existsSync5(configPath)) {
4467
+ try {
4468
+ const existing = await readFile7(configPath, "utf-8");
4469
+ config = JSON.parse(existing);
4470
+ } catch {
4471
+ config = {};
4472
+ }
4473
+ }
4474
+ if (!config.mcp) {
4475
+ config.mcp = {};
4476
+ }
4477
+ config.mcp.aikit = mcpServerConfig;
4478
+ await writeFile8(configPath, JSON.stringify(config, null, 2));
4479
+ logger.success(`
4480
+ \u2705 MCP server configured: ${configPath}`);
4481
+ logger.info(` Server: node ${mcpServerPath}`);
4482
+ return;
4347
4483
  } catch {
4348
- return null;
4484
+ continue;
4349
4485
  }
4350
4486
  }
4351
- /**
4352
- * Get package version from package.json
4353
- */
4354
- getPackageVersion() {
4355
- try {
4356
- const packageJson = __require(join13(process.cwd(), "package.json"));
4357
- return packageJson.version || "0.0.0";
4358
- } catch {
4359
- return "0.0.0";
4487
+ const instructionsPath = join13(projectPath, ".opencode", "MCP_SETUP.md");
4488
+ await mkdir8(join13(projectPath, ".opencode"), { recursive: true });
4489
+ await writeFile8(instructionsPath, `# AIKit MCP Server Configuration
4490
+
4491
+ ## Automatic Setup Failed
4492
+
4493
+ Please manually configure the MCP server in OpenCode.
4494
+
4495
+ ## Configuration
4496
+
4497
+ Add this to your OpenCode configuration file (\`~/.config/opencode/opencode.json\`):
4498
+
4499
+ \`\`\`json
4500
+ {
4501
+ "mcpServers": {
4502
+ "aikit": {
4503
+ "command": "node",
4504
+ "args": ["${mcpServerPath}"],
4505
+ "env": {}
4360
4506
  }
4361
4507
  }
4362
- /**
4363
- * Check for updates
4364
- */
4365
- async checkForUpdates() {
4366
- const installed = await this.getCurrentVersion();
4367
- const packageVersion = this.getPackageVersion();
4368
- if (!installed) {
4369
- return {
4370
- hasUpdate: true,
4371
- fromVersion: "none",
4372
- toVersion: packageVersion,
4373
- newSkills: [],
4374
- modifiedSkills: [],
4375
- removedSkills: [],
4376
- conflicts: [],
4377
- configChanges: ["Initial version tracking"]
4378
- };
4379
- }
4380
- const hasUpdate = installed.installedVersion !== packageVersion;
4381
- if (!hasUpdate) {
4382
- return {
4383
- hasUpdate: false,
4384
- fromVersion: installed.installedVersion,
4385
- toVersion: packageVersion,
4386
- newSkills: [],
4387
- modifiedSkills: [],
4388
- removedSkills: [],
4389
- conflicts: [],
4390
- configChanges: []
4391
- };
4508
+ }
4509
+ \`\`\`
4510
+
4511
+ ## After Configuration
4512
+
4513
+ 1. Restart OpenCode completely
4514
+ 2. OpenCode will automatically start the MCP server
4515
+ 3. Tools will be available via MCP protocol
4516
+ 4. You can use tools like \`tool_read_figma_design\` directly
4517
+
4518
+ ## Verify
4519
+
4520
+ After restarting OpenCode, check:
4521
+ - MCP server is running (check OpenCode settings)
4522
+ - Tools are discoverable (OpenCode should list them)
4523
+ - You can call tools via MCP protocol
4524
+ `);
4525
+ logger.warn(`
4526
+ \u26A0\uFE0F Could not auto-configure MCP server. See: ${instructionsPath}`);
4527
+ }
4528
+ async function installCliTool(tool) {
4529
+ try {
4530
+ logger.info(`Installing ${tool.displayName}...`);
4531
+ switch (tool.name) {
4532
+ case "opencode" /* OPENCODE */:
4533
+ await installToOpenCode(paths.opencodeConfig());
4534
+ break;
4535
+ case "claude" /* CLAUDE */:
4536
+ execSync2("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
4537
+ break;
4538
+ case "github" /* GITHUB */:
4539
+ execSync2("npm install -g gh", { stdio: "inherit" });
4540
+ break;
4392
4541
  }
4393
- const changes = await this.detectChanges();
4394
- return {
4395
- hasUpdate: true,
4396
- fromVersion: installed.installedVersion,
4397
- toVersion: packageVersion,
4398
- ...changes
4399
- };
4542
+ logger.success(`\u2713 ${tool.displayName} installed`);
4543
+ return true;
4544
+ } catch (error) {
4545
+ logger.error(`Failed to install ${tool.displayName}:`, error);
4546
+ return false;
4400
4547
  }
4401
- /**
4402
- * Detect changes between versions
4403
- */
4404
- async detectChanges() {
4405
- const globalSkillsPath = paths.skills(paths.globalConfig());
4406
- const projectSkillsPath = paths.skills(this.config.configPath);
4407
- const sourceSkills = await this.loadSkillHashes(globalSkillsPath);
4408
- const userSkills = await this.loadSkillHashes(projectSkillsPath);
4409
- const newSkills = [];
4410
- const modifiedSkills = [];
4411
- const removedSkills = [];
4412
- const conflicts = [];
4413
- const installedSkills = /* @__PURE__ */ new Map();
4414
- const installedPath = join13(paths.globalConfig(), ".installed-skills.json");
4415
- try {
4416
- const installedData = await readFile7(installedPath, "utf-8");
4417
- const installedList = JSON.parse(installedData);
4418
- installedList.forEach((skill) => {
4419
- installedSkills.set(skill.name, skill);
4420
- });
4421
- } catch {
4422
- }
4423
- for (const sourceSkill of sourceSkills) {
4424
- const installed = installedSkills.get(sourceSkill.name);
4425
- const user = userSkills.find((s) => s.name === sourceSkill.name);
4426
- if (!installed) {
4427
- newSkills.push(sourceSkill);
4428
- } else if (installed.hash !== sourceSkill.hash) {
4429
- modifiedSkills.push(sourceSkill);
4430
- if (user && user.hash !== installed.hash) {
4431
- conflicts.push({
4432
- skillName: sourceSkill.name,
4433
- userHash: user.hash,
4434
- sourceHash: sourceSkill.hash,
4435
- installedHash: installed.hash,
4436
- userModified: user.hash !== installed.hash,
4437
- sourceModified: sourceSkill.hash !== installed.hash
4438
- });
4439
- }
4440
- }
4441
- }
4442
- for (const [name, installedSkill] of installedSkills.entries()) {
4443
- const existsInSource = sourceSkills.find((s) => s.name === name);
4444
- if (!existsInSource) {
4445
- removedSkills.push(installedSkill);
4548
+ }
4549
+ var AGENT_FILES = {
4550
+ rush: `---
4551
+ description: Fast execution for small/urgent changes with minimal planning.
4552
+ mode: subagent
4553
+ tools:
4554
+ "*": true
4555
+ ---
4556
+
4557
+ Use for quick fixes, hotfixes, or tiny edits. Keep scope minimal and verify quickly.`,
4558
+ review: `---
4559
+ description: Code review and quality/security auditing agent.
4560
+ mode: subagent
4561
+ tools:
4562
+ "*": true
4563
+ ---
4564
+
4565
+ Use to review correctness, security, performance, maintainability, and tests. Be specific
4566
+ about issues and suggest concrete fixes.`,
4567
+ scout: `---
4568
+ description: Research agent for external docs, patterns, and references.
4569
+ mode: subagent
4570
+ tools:
4571
+ "*": true
4572
+ ---
4573
+
4574
+ Use to look up docs, examples, best practices. Summarize findings concisely and cite sources.`,
4575
+ explore: `---
4576
+ description: Codebase navigation agent (search, grep, structure understanding).
4577
+ mode: subagent
4578
+ tools:
4579
+ "*": true
4580
+ ---
4581
+
4582
+ Use to locate files, patterns, dependencies, and gather quick context in the repo.`,
4583
+ vision: `---
4584
+ description: Visual analysis agent for mockups, screenshots, PDFs, diagrams.
4585
+ mode: subagent
4586
+ tools:
4587
+ "*": true
4588
+ ---
4589
+
4590
+ Use to interpret visual assets (components, layout, colors, typography) and translate to tasks.`,
4591
+ "one-shot": `---
4592
+ description: End-to-end autonomous task execution (beta). Complete tasks from start to finish.
4593
+ mode: subagent
4594
+ tools:
4595
+ "*": true
4596
+ ---
4597
+
4598
+ \u26A0\uFE0F BETA: This mode is experimental. Use for straightforward tasks first.
4599
+
4600
+ ## One-Shot Mode - Autonomous Task Execution
4601
+
4602
+ Execute tasks end-to-end with minimal intervention:
4603
+
4604
+ ### Workflow Phases
4605
+ 1. **REQUIREMENTS** - Gather task type, scope, dependencies, success criteria
4606
+ 2. **PLANNING** - Create detailed plan, recommend skills/tools, create tracking bead
4607
+ 3. **COMPLEXITY** - Auto-split if: >30min, >10 files, >500 lines, >2 sub-systems
4608
+ 4. **EXECUTION** - Parallel tasks (max 3), dynamic agent delegation
4609
+ 5. **TESTING** - Run until pass: typecheck \u2192 test \u2192 lint \u2192 build (max 3 retries)
4610
+ 6. **VERIFICATION** - Quality gates \u2713 \u2192 Manual verification \u2192 Deployment approval
4611
+ 7. **COMPLETION** - Generate proof, update tracking, collect feedback
4612
+
4613
+ ### Quality Gates (ALL must pass)
4614
+ - \`npm run typecheck\` - No type errors
4615
+ - \`npm run test\` - All tests pass
4616
+ - \`npm run lint\` - No lint errors
4617
+ - \`npm run build\` - Build succeeds
4618
+
4619
+ ### Error Recovery (3 Levels)
4620
+ - **Level 1**: Auto-fix (type errors, lint --fix)
4621
+ - **Level 2**: Alternative approach via @review
4622
+ - **Level 3**: User intervention + follow-up task
4623
+
4624
+ ### Delegates To
4625
+ @planner for planning, @build for implementation, @review for code review,
4626
+ @scout for research, @explore for navigation, @vision for visual analysis.
4627
+
4628
+ ### Best Use Cases
4629
+ - Straightforward features with clear scope
4630
+ - Bug fixes with known reproduction steps
4631
+ - Refactoring with defined boundaries
4632
+
4633
+ ### Consider Alternatives For
4634
+ - Complex multi-system features \u2192 Use /plan + /implement
4635
+ - Exploratory research \u2192 Use /research first
4636
+ - Critical production changes \u2192 Manual with /review`
4637
+ };
4638
+ function generateAnalyzeFigmaCommand() {
4639
+ return `# Command: /analyze-figma
4640
+
4641
+ ## Description
4642
+ Analyze a Figma design and extract design tokens
4643
+
4644
+ ## Usage
4645
+ \`/analyze-figma <figma-url>\`
4646
+
4647
+ ## Examples
4648
+ - \`/analyze-figma https://www.figma.com/design/...\`
4649
+
4650
+ ## \u26A0\uFE0F CRITICAL: Extract URL FIRST!
4651
+
4652
+ **BEFORE ANYTHING ELSE**: Look at the user's FULL input message (all lines) and find the Figma URL. It's ALWAYS there - never ask for it!
4653
+
4654
+ **The URL pattern**: Look for text containing \`figma.com/design/\` anywhere in the user's message.
4655
+
4656
+ **Example of what user input looks like**:
4657
+ \`\`\`
4658
+ /analyze-figma https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/
4659
+ Online-Education-Website-Free-Template--Community-?t=7G5yzTiEtJlIZBtY-0
4660
+ \`\`\`
4661
+
4662
+ **Extract the complete URL** (combine if split):
4663
+ \`https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/Online-Education-Website-Free-Template--Community-?t=7G5yzTiEtJlIZBtY-0\`
4664
+
4665
+ ## Workflow
4666
+
4667
+ **IMPORTANT**: When user provides a Figma URL, you MUST immediately:
4668
+
4669
+ **Step 1: Extract URL from User Input**
4670
+
4671
+ **CRITICAL**: The URL is ALWAYS in the user's input message! DO NOT ask for it - just extract it!
4672
+
4673
+ **MANDATORY**: You MUST extract the URL before proceeding. This is not optional!
4674
+
4675
+ **How to Extract**:
4676
+ 1. **Read the ENTIRE user input message** - look at ALL lines, not just the first line
4677
+ 2. **Search for ANY text containing** \`figma.com/design/\` - this is the URL
4678
+ 3. **URL may appear in different formats**:
4679
+ - On same line: \`/analyze-figma https://www.figma.com/design/...\`
4680
+ - Split across lines
4681
+ 4. **Extract the COMPLETE URL**:
4682
+ - Start from \`https://\` or \`http://\`
4683
+ - Include everything until the end of the line or next whitespace
4684
+ - If URL is split, combine ALL parts into one complete URL
4685
+ 5. **Include ALL query parameters**: \`?node-id=...\`, \`&t=...\`, etc.
4686
+
4687
+ **CRITICAL RULES**:
4688
+ - \u2705 DO: Read the ENTIRE user message (all lines)
4689
+ - \u2705 DO: Look for \`figma.com/design/\` anywhere in the message
4690
+ - \u2705 DO: Combine split lines into one URL
4691
+ - \u274C DO NOT: Ask user for URL - it's ALWAYS in the input
4692
+ - \u274C DO NOT: Skip this step - URL extraction is MANDATORY
4693
+ - \u274C DO NOT: Proceed without extracting URL first
4694
+
4695
+ **Step 2: Check Tool Configuration**
4696
+
4697
+ Before calling the tool, verify that Figma tool is configured:
4698
+ - If not configured, inform user to run: \`aikit skills figma-analysis config\`
4699
+ - The tool requires a Figma Personal Access Token
4700
+
4701
+ **Step 3: Call MCP Tool read_figma_design**
4702
+
4703
+ Use the MCP tool \`read_figma_design\` with the extracted URL:
4704
+ \`\`\`
4705
+ Use tool: read_figma_design
4706
+ Arguments: { "url": "[extracted URL]" }
4707
+ \`\`\`
4708
+
4709
+ **Step 4: Format and Save**
4710
+
4711
+ Format extracted tokens as structured markdown and save using memory-update tool.
4712
+
4713
+ **Step 5: Report Results**
4714
+
4715
+ Report what was extracted:
4716
+ - Number of screens found
4717
+ - Number of colors in palette
4718
+ - Typography styles found
4719
+ - Components identified
4720
+
4721
+ ## Critical Instructions
4722
+
4723
+ - **DO NOT** ask user to "share the Figma URL" - they already provided it in the command
4724
+ - **DO NOT** wait for confirmation - just start analyzing immediately
4725
+ - **DO** extract URL from full user input message
4726
+ - **DO** call MCP tool \`read_figma_design\` immediately
4727
+ - **DO** save to memory automatically`;
4728
+ }
4729
+ async function installToOpenCode(_opencodePath) {
4730
+ const projectPath = process.cwd();
4731
+ const opencodeCommandDir = join13(projectPath, ".opencode", "command");
4732
+ const aikitDir = join13(projectPath, ".aikit");
4733
+ const opencodeAgentDir = join13(paths.opencodeConfig(), "agent");
4734
+ await mkdir8(opencodeCommandDir, { recursive: true });
4735
+ await mkdir8(join13(aikitDir, "skills"), { recursive: true });
4736
+ await mkdir8(opencodeAgentDir, { recursive: true });
4737
+ for (const [name, content] of Object.entries(AGENT_FILES)) {
4738
+ const filePath = join13(opencodeAgentDir, `${name}.md`);
4739
+ try {
4740
+ await access4(filePath);
4741
+ const existingContent = await readFile7(filePath, "utf8");
4742
+ if (!existingContent.includes("mode: subagent")) {
4743
+ const matter3 = await import("gray-matter");
4744
+ const { data: frontmatter, content: body } = matter3.default(existingContent);
4745
+ frontmatter.mode = "subagent";
4746
+ const updatedContent = matter3.default.stringify(body, frontmatter);
4747
+ await writeFile8(filePath, updatedContent, "utf8");
4446
4748
  }
4749
+ } catch {
4750
+ await writeFile8(filePath, content, "utf8");
4447
4751
  }
4448
- return {
4449
- newSkills,
4450
- modifiedSkills,
4451
- removedSkills,
4452
- conflicts,
4453
- configChanges: []
4454
- // Will be detected separately
4455
- };
4456
4752
  }
4457
- /**
4458
- * Load skill hashes from directory
4459
- */
4460
- async loadSkillHashes(skillsPath) {
4461
- const hashes = [];
4462
- try {
4463
- const loadFromDir = async (dir) => {
4464
- const files = await readdir7(dir);
4465
- for (const file of files) {
4466
- const filePath = join13(dir, file);
4467
- const stats = await stat(filePath);
4468
- if (stats.isDirectory()) {
4469
- await loadFromDir(filePath);
4470
- } else if (file.endsWith(".md")) {
4471
- const hash = await this.calculateSkillHash(filePath);
4472
- hashes.push({
4473
- path: filePath,
4474
- name: file.replace(".md", ""),
4475
- hash,
4476
- category: this.extractCategory(dir, skillsPath)
4477
- });
4478
- }
4479
- }
4480
- };
4481
- await loadFromDir(skillsPath);
4482
- } catch (error) {
4483
- logger.debug(`Could not load skills from ${skillsPath}:`, error);
4484
- }
4485
- return hashes;
4753
+ const config = await loadConfig();
4754
+ const skillEngine = new SkillEngine(config);
4755
+ const commandRunner = new CommandRunner(config);
4756
+ const skills = await skillEngine.listSkills();
4757
+ const commands = await commandRunner.listCommands();
4758
+ const opencodeCommands = {};
4759
+ const skillsList = skills.map((s) => `| \`/${s.name.replace(/\s+/g, "-")}\` | ${s.description} |`).join("\n");
4760
+ opencodeCommands["skills"] = `List all available AIKit skills and how to use them.
4761
+
4762
+ READ .aikit/AGENTS.md
4763
+
4764
+ ## Available Skills
4765
+
4766
+ | Command | Description |
4767
+ |---------|-------------|
4768
+ ${skillsList}
4769
+
4770
+ Type any command to use that skill. For example: \`/test-driven-development\` or \`/tdd\`.`;
4771
+ for (const skill of skills) {
4772
+ const commandName = skill.name.replace(/\s+/g, "-").toLowerCase();
4773
+ const skillPath = skill.filePath;
4774
+ const relativePath = skillPath.startsWith(projectPath) ? skillPath.replace(projectPath, "").replace(/\\/g, "/").replace(/^\//, "") : `.aikit/skills/${skill.name.replace(/\s+/g, "-").toLowerCase()}.md`;
4775
+ const useWhen = skill.useWhen || `The user asks you to ${skill.name}`;
4776
+ opencodeCommands[commandName] = `Use the **${skill.name} skill** ${useWhen.toLowerCase()}.
4777
+
4778
+ READ ${relativePath}
4779
+
4780
+ ## Description
4781
+ ${skill.description}
4782
+
4783
+ ## When to Use
4784
+ ${useWhen}
4785
+
4786
+ ## Workflow
4787
+ ${skill.content.split("\n").slice(0, 20).join("\n")}${skill.content.split("\n").length > 20 ? "\n\n... (see full skill file for complete workflow)" : ""}
4788
+
4789
+ **IMPORTANT**: Follow this skill's workflow step by step. Do not skip steps.
4790
+ Complete the checklist at the end of the skill.`;
4486
4791
  }
4487
- /**
4488
- * Calculate hash for a skill file
4489
- */
4490
- async calculateSkillHash(filePath) {
4491
- try {
4492
- const content = await readFile7(filePath, "utf-8");
4493
- return createHash("sha256").update(content).digest("hex");
4494
- } catch {
4495
- return "";
4792
+ for (const cmd of commands) {
4793
+ if (opencodeCommands[cmd.name]) continue;
4794
+ const commandName = cmd.name.replace(/\//g, "").replace(/\s+/g, "-");
4795
+ const examples = cmd.examples.map((e) => `- \`${e}\``).join("\n");
4796
+ if (cmd.name === "analyze-figma") {
4797
+ opencodeCommands[commandName] = generateAnalyzeFigmaCommand();
4798
+ } else {
4799
+ opencodeCommands[commandName] = `# Command: /${cmd.name}
4800
+
4801
+ ## Description
4802
+ ${cmd.description}
4803
+
4804
+ ## Usage
4805
+ \`${cmd.usage}\`
4806
+
4807
+ ## Examples
4808
+ ${examples}
4809
+
4810
+ ## Workflow
4811
+ ${cmd.content}
4812
+
4813
+ **Category**: ${cmd.category}`;
4496
4814
  }
4497
4815
  }
4498
- /**
4499
- * Extract category from path
4500
- */
4501
- extractCategory(filePath, basePath) {
4816
+ let count = 0;
4817
+ for (const [name, content] of Object.entries(opencodeCommands)) {
4818
+ const filePath = join13(opencodeCommandDir, `${name}.md`);
4819
+ await writeFile8(filePath, content.trim());
4820
+ logger.info(` \u2713 Created /${name} command`);
4821
+ count++;
4822
+ }
4823
+ logger.success(`
4824
+ Created ${count} OpenCode commands in .opencode/command/`);
4825
+ await configureMcpServer(projectPath);
4826
+ logger.info("\nUsage in OpenCode:");
4827
+ logger.info(" Press Ctrl+K to open command picker");
4828
+ logger.info(" Or type /skills to see all available skills");
4829
+ logger.info(` Available: ${skills.length} skills, ${commands.length} commands`);
4830
+ logger.info(" MCP server configured - tools available via MCP protocol");
4831
+ }
4832
+ function groupBy(array, keyFn) {
4833
+ return array.reduce((acc, item) => {
4834
+ const key = keyFn(item);
4835
+ if (!acc[key]) acc[key] = [];
4836
+ acc[key].push(item);
4837
+ return acc;
4838
+ }, {});
4839
+ }
4840
+
4841
+ // src/cli/commands/init.ts
4842
+ function registerInitCommand(program2) {
4843
+ program2.command("init").description("Initialize AIKit configuration").option("-g, --global", "Initialize global configuration").option("-p, --project", "Initialize project-level configuration").action(async (options) => {
4844
+ const configDir = options.global ? paths.globalConfig() : paths.projectConfig();
4845
+ console.log(chalk2.bold("\n\u{1F680} AIKit Setup\n"));
4846
+ logger.info(`Initializing AIKit in ${configDir}...`);
4847
+ try {
4848
+ await initializeConfig(configDir, options.global);
4849
+ logger.success("\u2713 Configuration created");
4850
+ if (!options.global) {
4851
+ const config = await loadConfig();
4852
+ const engine = new SkillEngine(config);
4853
+ const result = await engine.syncSkillsToProject();
4854
+ if (result.count > 0) {
4855
+ logger.success(`\u2713 Synced ${result.count} skills`);
4856
+ }
4857
+ console.log(chalk2.bold("\n\u{1F50D} Checking CLI tools...\n"));
4858
+ const cliTools = await CliDetector.checkAll();
4859
+ const installableTools = CliDetector.filterInstallable(cliTools);
4860
+ for (const tool of cliTools) {
4861
+ const status = tool.installed ? chalk2.green("\u2713 Installed") : chalk2.yellow("\u2717 Not installed");
4862
+ const version = tool.version ? chalk2.gray(` (${tool.version})`) : "";
4863
+ console.log(` ${status} ${chalk2.cyan(tool.displayName)}${version}`);
4864
+ }
4865
+ if (installableTools.length > 0) {
4866
+ console.log();
4867
+ const { action } = await inquirer.prompt([
4868
+ {
4869
+ type: "list",
4870
+ name: "action",
4871
+ message: "How would you like to proceed?",
4872
+ choices: [
4873
+ {
4874
+ name: "all",
4875
+ value: "all",
4876
+ short: "a",
4877
+ message: "Install all missing CLI tools"
4878
+ },
4879
+ {
4880
+ name: "select",
4881
+ value: "select",
4882
+ short: "s",
4883
+ message: "Select specific tools to install (use space to select, Enter to confirm)"
4884
+ },
4885
+ {
4886
+ name: "skip",
4887
+ value: "skip",
4888
+ short: "n",
4889
+ message: "Skip CLI tool installation"
4890
+ }
4891
+ ],
4892
+ default: "all"
4893
+ }
4894
+ ]);
4895
+ if (action === "skip") {
4896
+ console.log();
4897
+ logger.info("Skipping CLI tool installation");
4898
+ } else if (action === "all") {
4899
+ console.log();
4900
+ logger.info(`Installing ${installableTools.length} CLI tool(s)...`);
4901
+ for (const tool of installableTools) {
4902
+ await installCliTool(tool);
4903
+ }
4904
+ console.log();
4905
+ logger.success("\u2713 CLI tools installed");
4906
+ } else {
4907
+ const { installTools } = await inquirer.prompt([
4908
+ {
4909
+ type: "checkbox",
4910
+ name: "installTools",
4911
+ message: "Select CLI tools to install (press Enter to skip):",
4912
+ choices: installableTools.map((tool) => ({
4913
+ name: tool.name,
4914
+ value: tool,
4915
+ checked: true
4916
+ // Default to install all
4917
+ }))
4918
+ }
4919
+ ]);
4920
+ if (installTools.length > 0) {
4921
+ console.log();
4922
+ logger.info(`Installing ${installTools.length} CLI tool(s)...`);
4923
+ for (const tool of installTools) {
4924
+ await installCliTool(tool);
4925
+ }
4926
+ console.log();
4927
+ logger.success("\u2713 CLI tools installed");
4928
+ } else {
4929
+ console.log();
4930
+ logger.info("Skipping CLI tool installation");
4931
+ }
4932
+ }
4933
+ } else {
4934
+ console.log();
4935
+ logger.success("\u2713 All CLI tools already installed");
4936
+ }
4937
+ const beads = new BeadsIntegration();
4938
+ const beadsStatus = await beads.getStatus();
4939
+ if (!beadsStatus.initialized) {
4940
+ logger.info("Initializing .beads directory...");
4941
+ await beads.initLocal();
4942
+ logger.success("\u2713 .beads directory created");
4943
+ if (!beadsStatus.installed) {
4944
+ logger.info("Tip: Install Beads CLI globally for full functionality: npm install -g beads");
4945
+ }
4946
+ } else {
4947
+ logger.info("Beads already initialized");
4948
+ }
4949
+ const opencodePath = paths.opencodeConfig();
4950
+ await installToOpenCode(opencodePath);
4951
+ console.log(chalk2.bold("\n\u2728 AIKit is ready!\n"));
4952
+ console.log("Usage in OpenCode:");
4953
+ console.log(chalk2.cyan(" /skills") + " - List all available skills");
4954
+ console.log(chalk2.cyan(" /plan") + " - Create implementation plan");
4955
+ console.log(chalk2.cyan(" /tdd") + " - Test-driven development");
4956
+ console.log(chalk2.cyan(" /debug") + " - Systematic debugging");
4957
+ console.log(chalk2.cyan(" /review") + " - Code review checklist");
4958
+ console.log(chalk2.cyan(" /git") + " - Git workflow");
4959
+ console.log(chalk2.cyan(" /frontend-aesthetics") + " - UI/UX guidelines");
4960
+ console.log("\nPress " + chalk2.bold("Ctrl+K") + " in OpenCode to see all commands.\n");
4961
+ }
4962
+ } catch (error) {
4963
+ logger.error("Failed to initialize AIKit:", error);
4964
+ process.exit(1);
4965
+ }
4966
+ });
4967
+ }
4968
+
4969
+ // src/cli/commands/install.ts
4970
+ init_esm_shims();
4971
+ init_logger();
4972
+ init_paths();
4973
+ function registerInstallCommand(program2) {
4974
+ program2.command("install").description("Install AIKit to OpenCode configuration").action(async () => {
4975
+ logger.info("Installing AIKit to OpenCode...");
4976
+ try {
4977
+ const opencodePath = paths.opencodeConfig();
4978
+ await installToOpenCode(opencodePath);
4979
+ logger.success("AIKit installed to OpenCode!");
4980
+ } catch (error) {
4981
+ logger.error("Failed to install:", error);
4982
+ process.exit(1);
4983
+ }
4984
+ });
4985
+ }
4986
+
4987
+ // src/cli/commands/sync.ts
4988
+ init_esm_shims();
4989
+ import chalk4 from "chalk";
4990
+
4991
+ // src/core/sync-engine.ts
4992
+ init_esm_shims();
4993
+ import { readFile as readFile11, writeFile as writeFile12, copyFile, mkdir as mkdir10 } from "fs/promises";
4994
+ import { join as join17, dirname as dirname4 } from "path";
4995
+ import inquirer2 from "inquirer";
4996
+ import chalk3 from "chalk";
4997
+
4998
+ // src/core/version-manager.ts
4999
+ init_esm_shims();
5000
+ init_paths();
5001
+ init_logger();
5002
+ import { readFile as readFile8, readdir as readdir7, writeFile as writeFile9, stat } from "fs/promises";
5003
+ import { join as join14 } from "path";
5004
+ import { createHash } from "crypto";
5005
+ var VersionManager = class {
5006
+ config;
5007
+ constructor(config) {
5008
+ this.config = config;
5009
+ }
5010
+ /**
5011
+ * Get current installed version
5012
+ */
5013
+ async getCurrentVersion() {
5014
+ const versionPath = join14(paths.globalConfig(), ".version.json");
5015
+ try {
5016
+ const content = await readFile8(versionPath, "utf-8");
5017
+ return JSON.parse(content);
5018
+ } catch {
5019
+ return null;
5020
+ }
5021
+ }
5022
+ /**
5023
+ * Get package version from package.json
5024
+ */
5025
+ getPackageVersion() {
5026
+ try {
5027
+ const packageJson = __require(join14(process.cwd(), "package.json"));
5028
+ return packageJson.version || "0.0.0";
5029
+ } catch {
5030
+ return "0.0.0";
5031
+ }
5032
+ }
5033
+ /**
5034
+ * Check for updates
5035
+ */
5036
+ async checkForUpdates() {
5037
+ const installed = await this.getCurrentVersion();
5038
+ const packageVersion = this.getPackageVersion();
5039
+ if (!installed) {
5040
+ return {
5041
+ hasUpdate: true,
5042
+ fromVersion: "none",
5043
+ toVersion: packageVersion,
5044
+ newSkills: [],
5045
+ modifiedSkills: [],
5046
+ removedSkills: [],
5047
+ conflicts: [],
5048
+ configChanges: ["Initial version tracking"]
5049
+ };
5050
+ }
5051
+ const hasUpdate = installed.installedVersion !== packageVersion;
5052
+ if (!hasUpdate) {
5053
+ return {
5054
+ hasUpdate: false,
5055
+ fromVersion: installed.installedVersion,
5056
+ toVersion: packageVersion,
5057
+ newSkills: [],
5058
+ modifiedSkills: [],
5059
+ removedSkills: [],
5060
+ conflicts: [],
5061
+ configChanges: []
5062
+ };
5063
+ }
5064
+ const changes = await this.detectChanges();
5065
+ return {
5066
+ hasUpdate: true,
5067
+ fromVersion: installed.installedVersion,
5068
+ toVersion: packageVersion,
5069
+ ...changes
5070
+ };
5071
+ }
5072
+ /**
5073
+ * Detect changes between versions
5074
+ */
5075
+ async detectChanges() {
5076
+ const globalSkillsPath = paths.skills(paths.globalConfig());
5077
+ const projectSkillsPath = paths.skills(this.config.configPath);
5078
+ const sourceSkills = await this.loadSkillHashes(globalSkillsPath);
5079
+ const userSkills = await this.loadSkillHashes(projectSkillsPath);
5080
+ const newSkills = [];
5081
+ const modifiedSkills = [];
5082
+ const removedSkills = [];
5083
+ const conflicts = [];
5084
+ const installedSkills = /* @__PURE__ */ new Map();
5085
+ const installedPath = join14(paths.globalConfig(), ".installed-skills.json");
5086
+ try {
5087
+ const installedData = await readFile8(installedPath, "utf-8");
5088
+ const installedList = JSON.parse(installedData);
5089
+ installedList.forEach((skill) => {
5090
+ installedSkills.set(skill.name, skill);
5091
+ });
5092
+ } catch {
5093
+ }
5094
+ for (const sourceSkill of sourceSkills) {
5095
+ const installed = installedSkills.get(sourceSkill.name);
5096
+ const user = userSkills.find((s) => s.name === sourceSkill.name);
5097
+ if (!installed) {
5098
+ newSkills.push(sourceSkill);
5099
+ } else if (installed.hash !== sourceSkill.hash) {
5100
+ modifiedSkills.push(sourceSkill);
5101
+ if (user && user.hash !== installed.hash) {
5102
+ conflicts.push({
5103
+ skillName: sourceSkill.name,
5104
+ userHash: user.hash,
5105
+ sourceHash: sourceSkill.hash,
5106
+ installedHash: installed.hash,
5107
+ userModified: user.hash !== installed.hash,
5108
+ sourceModified: sourceSkill.hash !== installed.hash
5109
+ });
5110
+ }
5111
+ }
5112
+ }
5113
+ for (const [name, installedSkill] of installedSkills.entries()) {
5114
+ const existsInSource = sourceSkills.find((s) => s.name === name);
5115
+ if (!existsInSource) {
5116
+ removedSkills.push(installedSkill);
5117
+ }
5118
+ }
5119
+ return {
5120
+ newSkills,
5121
+ modifiedSkills,
5122
+ removedSkills,
5123
+ conflicts,
5124
+ configChanges: []
5125
+ // Will be detected separately
5126
+ };
5127
+ }
5128
+ /**
5129
+ * Load skill hashes from directory
5130
+ */
5131
+ async loadSkillHashes(skillsPath) {
5132
+ const hashes = [];
5133
+ try {
5134
+ const loadFromDir = async (dir) => {
5135
+ const files = await readdir7(dir);
5136
+ for (const file of files) {
5137
+ const filePath = join14(dir, file);
5138
+ const stats = await stat(filePath);
5139
+ if (stats.isDirectory()) {
5140
+ await loadFromDir(filePath);
5141
+ } else if (file.endsWith(".md")) {
5142
+ const hash = await this.calculateSkillHash(filePath);
5143
+ hashes.push({
5144
+ path: filePath,
5145
+ name: file.replace(".md", ""),
5146
+ hash,
5147
+ category: this.extractCategory(dir, skillsPath)
5148
+ });
5149
+ }
5150
+ }
5151
+ };
5152
+ await loadFromDir(skillsPath);
5153
+ } catch (error) {
5154
+ logger.debug(`Could not load skills from ${skillsPath}:`, error);
5155
+ }
5156
+ return hashes;
5157
+ }
5158
+ /**
5159
+ * Calculate hash for a skill file
5160
+ */
5161
+ async calculateSkillHash(filePath) {
5162
+ try {
5163
+ const content = await readFile8(filePath, "utf-8");
5164
+ return createHash("sha256").update(content).digest("hex");
5165
+ } catch {
5166
+ return "";
5167
+ }
5168
+ }
5169
+ /**
5170
+ * Extract category from path
5171
+ */
5172
+ extractCategory(filePath, basePath) {
4502
5173
  const relative = filePath.replace(basePath + "/", "");
4503
5174
  const parts = relative.split("/");
4504
5175
  if (parts.length > 1) {
@@ -4510,9 +5181,9 @@ var VersionManager = class {
4510
5181
  * Save installed skills info
4511
5182
  */
4512
5183
  async saveInstalledSkills(skills) {
4513
- const installedPath = join13(paths.globalConfig(), ".installed-skills.json");
5184
+ const installedPath = join14(paths.globalConfig(), ".installed-skills.json");
4514
5185
  try {
4515
- await writeFile8(installedPath, JSON.stringify(skills, null, 2));
5186
+ await writeFile9(installedPath, JSON.stringify(skills, null, 2));
4516
5187
  } catch (error) {
4517
5188
  logger.error("Failed to save installed skills info:", error);
4518
5189
  }
@@ -4533,8 +5204,8 @@ var VersionManager = class {
4533
5204
  packageVersion: this.getPackageVersion(),
4534
5205
  migrationHistory: migration ? [...current.migrationHistory, migration] : current.migrationHistory
4535
5206
  };
4536
- const versionPath = join13(paths.globalConfig(), ".version.json");
4537
- await writeFile8(versionPath, JSON.stringify(updated, null, 2));
5207
+ const versionPath = join14(paths.globalConfig(), ".version.json");
5208
+ await writeFile9(versionPath, JSON.stringify(updated, null, 2));
4538
5209
  }
4539
5210
  /**
4540
5211
  * Check if migration is needed
@@ -4549,8 +5220,8 @@ var VersionManager = class {
4549
5220
  // src/core/backup-manager.ts
4550
5221
  init_esm_shims();
4551
5222
  init_logger();
4552
- import { readFile as readFile8, writeFile as writeFile9, readdir as readdir8, stat as stat2, unlink, mkdir as mkdir8 } from "fs/promises";
4553
- import { join as join14, dirname as dirname2 } from "path";
5223
+ import { readFile as readFile9, writeFile as writeFile10, readdir as readdir8, stat as stat2, unlink, mkdir as mkdir9 } from "fs/promises";
5224
+ import { join as join15, dirname as dirname3 } from "path";
4554
5225
  import { createHash as createHash2 } from "crypto";
4555
5226
  var BackupManager = class {
4556
5227
  configPath;
@@ -4558,7 +5229,7 @@ var BackupManager = class {
4558
5229
  maxBackups;
4559
5230
  constructor(configPath, maxBackups = 5) {
4560
5231
  this.configPath = configPath;
4561
- this.backupsDir = join14(configPath, ".backups");
5232
+ this.backupsDir = join15(configPath, ".backups");
4562
5233
  this.maxBackups = maxBackups;
4563
5234
  }
4564
5235
  /**
@@ -4566,10 +5237,10 @@ var BackupManager = class {
4566
5237
  */
4567
5238
  async createBackup(fromVersion, toVersion) {
4568
5239
  try {
4569
- await mkdir8(this.backupsDir, { recursive: true });
5240
+ await mkdir9(this.backupsDir, { recursive: true });
4570
5241
  const backupId = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
4571
- const backupPath = join14(this.backupsDir, `${backupId}-v${toVersion}`);
4572
- await mkdir8(backupPath, { recursive: true });
5242
+ const backupPath = join15(this.backupsDir, `${backupId}-v${toVersion}`);
5243
+ await mkdir9(backupPath, { recursive: true });
4573
5244
  logger.info(`Creating backup: ${backupPath}`);
4574
5245
  const files = [];
4575
5246
  const backupItems = [
@@ -4590,8 +5261,8 @@ var BackupManager = class {
4590
5261
  files,
4591
5262
  success: true
4592
5263
  };
4593
- const manifestPath = join14(backupPath, "backup-manifest.json");
4594
- await writeFile9(manifestPath, JSON.stringify(manifest, null, 2));
5264
+ const manifestPath = join15(backupPath, "backup-manifest.json");
5265
+ await writeFile10(manifestPath, JSON.stringify(manifest, null, 2));
4595
5266
  await this.cleanupOldBackups();
4596
5267
  logger.success(`\u2713 Backup created: ${backupId}`);
4597
5268
  return backupId;
@@ -4604,20 +5275,20 @@ var BackupManager = class {
4604
5275
  * Backup a file or directory
4605
5276
  */
4606
5277
  async backupItem(sourceDir, item, targetDir) {
4607
- const sourcePath = join14(sourceDir, item);
4608
- const targetPath = join14(targetDir, item);
5278
+ const sourcePath = join15(sourceDir, item);
5279
+ const targetPath = join15(targetDir, item);
4609
5280
  const files = [];
4610
5281
  try {
4611
5282
  const stats = await stat2(sourcePath);
4612
5283
  if (stats.isDirectory()) {
4613
- await mkdir8(targetPath, { recursive: true });
5284
+ await mkdir9(targetPath, { recursive: true });
4614
5285
  const entries = await readdir8(sourcePath);
4615
5286
  for (const entry of entries) {
4616
5287
  const entryFiles = await this.backupItem(sourcePath, entry, targetPath);
4617
5288
  files.push(...entryFiles);
4618
5289
  }
4619
5290
  } else if (stats.isFile()) {
4620
- await mkdir8(dirname2(targetPath), { recursive: true });
5291
+ await mkdir9(dirname3(targetPath), { recursive: true });
4621
5292
  await this.copyFile(sourcePath, targetPath);
4622
5293
  const hash = await this.calculateHash(targetPath);
4623
5294
  files.push({
@@ -4635,15 +5306,15 @@ var BackupManager = class {
4635
5306
  * Copy file with hash calculation
4636
5307
  */
4637
5308
  async copyFile(source, target) {
4638
- const content = await readFile8(source);
4639
- await writeFile9(target, content);
5309
+ const content = await readFile9(source);
5310
+ await writeFile10(target, content);
4640
5311
  }
4641
5312
  /**
4642
5313
  * Calculate file hash
4643
5314
  */
4644
5315
  async calculateHash(filePath) {
4645
5316
  try {
4646
- const content = await readFile8(filePath);
5317
+ const content = await readFile9(filePath);
4647
5318
  return createHash2("sha256").update(content).digest("hex");
4648
5319
  } catch {
4649
5320
  return "";
@@ -4657,10 +5328,10 @@ var BackupManager = class {
4657
5328
  const entries = await readdir8(this.backupsDir);
4658
5329
  const backups = [];
4659
5330
  for (const entry of entries) {
4660
- const backupPath = join14(this.backupsDir, entry);
4661
- const manifestPath = join14(backupPath, "backup-manifest.json");
5331
+ const backupPath = join15(this.backupsDir, entry);
5332
+ const manifestPath = join15(backupPath, "backup-manifest.json");
4662
5333
  try {
4663
- const manifestContent = await readFile8(manifestPath, "utf-8");
5334
+ const manifestContent = await readFile9(manifestPath, "utf-8");
4664
5335
  const manifest = JSON.parse(manifestContent);
4665
5336
  const size = await this.calculateBackupSize(backupPath);
4666
5337
  backups.push({
@@ -4688,7 +5359,7 @@ var BackupManager = class {
4688
5359
  const calculate = async (dir) => {
4689
5360
  const entries = await readdir8(dir);
4690
5361
  for (const entry of entries) {
4691
- const entryPath = join14(dir, entry);
5362
+ const entryPath = join15(dir, entry);
4692
5363
  const stats = await stat2(entryPath);
4693
5364
  if (stats.isDirectory()) {
4694
5365
  await calculate(entryPath);
@@ -4720,9 +5391,9 @@ var BackupManager = class {
4720
5391
  return false;
4721
5392
  }
4722
5393
  for (const file of backup.manifest.files) {
4723
- const sourcePath = join14(backup.path, file.path);
4724
- const targetPath = join14(this.configPath, file.path);
4725
- await mkdir8(dirname2(targetPath), { recursive: true });
5394
+ const sourcePath = join15(backup.path, file.path);
5395
+ const targetPath = join15(this.configPath, file.path);
5396
+ await mkdir9(dirname3(targetPath), { recursive: true });
4726
5397
  await this.copyFile(sourcePath, targetPath);
4727
5398
  }
4728
5399
  logger.success(`\u2713 Backup restored: ${backupId}`);
@@ -4737,10 +5408,10 @@ var BackupManager = class {
4737
5408
  */
4738
5409
  async validateBackup(backup) {
4739
5410
  try {
4740
- const manifestPath = join14(backup.path, "backup-manifest.json");
4741
- await readFile8(manifestPath, "utf-8");
5411
+ const manifestPath = join15(backup.path, "backup-manifest.json");
5412
+ await readFile9(manifestPath, "utf-8");
4742
5413
  for (const file of backup.manifest.files) {
4743
- const filePath = join14(backup.path, file.path);
5414
+ const filePath = join15(backup.path, file.path);
4744
5415
  await stat2(filePath);
4745
5416
  const currentHash = await this.calculateHash(filePath);
4746
5417
  if (currentHash !== file.hash) {
@@ -4766,7 +5437,7 @@ var BackupManager = class {
4766
5437
  }
4767
5438
  const entries = await readdir8(backup.path);
4768
5439
  for (const entry of entries) {
4769
- const entryPath = join14(backup.path, entry);
5440
+ const entryPath = join15(backup.path, entry);
4770
5441
  const stats = await stat2(entryPath);
4771
5442
  if (stats.isDirectory()) {
4772
5443
  await this.removeDirectory(entryPath);
@@ -4787,7 +5458,7 @@ var BackupManager = class {
4787
5458
  async removeDirectory(dirPath) {
4788
5459
  const entries = await readdir8(dirPath);
4789
5460
  for (const entry of entries) {
4790
- const entryPath = join14(dirPath, entry);
5461
+ const entryPath = join15(dirPath, entry);
4791
5462
  const stats = await stat2(entryPath);
4792
5463
  if (stats.isDirectory()) {
4793
5464
  await this.removeDirectory(entryPath);
@@ -4833,14 +5504,14 @@ var BackupManager = class {
4833
5504
  // src/core/migration-manager.ts
4834
5505
  init_esm_shims();
4835
5506
  init_logger();
4836
- import { readFile as readFile9, writeFile as writeFile10, readdir as readdir9 } from "fs/promises";
4837
- import { join as join15 } from "path";
5507
+ import { readFile as readFile10, writeFile as writeFile11, readdir as readdir9 } from "fs/promises";
5508
+ import { join as join16 } from "path";
4838
5509
  var MigrationManager = class {
4839
5510
  configPath;
4840
5511
  migrationsDir;
4841
5512
  constructor(configPath) {
4842
5513
  this.configPath = configPath;
4843
- this.migrationsDir = join15(process.cwd(), "src/core/migrations");
5514
+ this.migrationsDir = join16(process.cwd(), "src/core/migrations");
4844
5515
  }
4845
5516
  /**
4846
5517
  * Load all available migrations
@@ -4852,7 +5523,7 @@ var MigrationManager = class {
4852
5523
  for (const file of files) {
4853
5524
  if (file.endsWith(".js") && file.startsWith("migrate-")) {
4854
5525
  try {
4855
- const module = await import(join15(this.migrationsDir, file));
5526
+ const module = await import(join16(this.migrationsDir, file));
4856
5527
  const migration = module.default || module.migration;
4857
5528
  if (migration) {
4858
5529
  migrations.push(migration);
@@ -4871,9 +5542,9 @@ var MigrationManager = class {
4871
5542
  * Get applied migrations
4872
5543
  */
4873
5544
  async getAppliedMigrations() {
4874
- const migrationHistoryPath = join15(this.configPath, ".migration-history.json");
5545
+ const migrationHistoryPath = join16(this.configPath, ".migration-history.json");
4875
5546
  try {
4876
- const content = await readFile9(migrationHistoryPath, "utf-8");
5547
+ const content = await readFile10(migrationHistoryPath, "utf-8");
4877
5548
  const history = JSON.parse(content);
4878
5549
  return history.filter((m) => m.status === "completed").map((m) => m.to);
4879
5550
  } catch {
@@ -4959,16 +5630,16 @@ var MigrationManager = class {
4959
5630
  * Update migration history
4960
5631
  */
4961
5632
  async updateMigrationHistory(entries) {
4962
- const historyPath = join15(this.configPath, ".migration-history.json");
5633
+ const historyPath = join16(this.configPath, ".migration-history.json");
4963
5634
  try {
4964
5635
  let history = [];
4965
5636
  try {
4966
- const content = await readFile9(historyPath, "utf-8");
5637
+ const content = await readFile10(historyPath, "utf-8");
4967
5638
  history = JSON.parse(content);
4968
5639
  } catch {
4969
5640
  }
4970
5641
  history.push(...entries);
4971
- await writeFile10(historyPath, JSON.stringify(history, null, 2));
5642
+ await writeFile11(historyPath, JSON.stringify(history, null, 2));
4972
5643
  } catch (error) {
4973
5644
  logger.error("Failed to update migration history:", error);
4974
5645
  }
@@ -4977,14 +5648,14 @@ var MigrationManager = class {
4977
5648
  * Update migration history status
4978
5649
  */
4979
5650
  async updateMigrationHistoryStatus(version, status) {
4980
- const historyPath = join15(this.configPath, ".migration-history.json");
5651
+ const historyPath = join16(this.configPath, ".migration-history.json");
4981
5652
  try {
4982
- const content = await readFile9(historyPath, "utf-8");
5653
+ const content = await readFile10(historyPath, "utf-8");
4983
5654
  const history = JSON.parse(content);
4984
5655
  const updated = history.map(
4985
5656
  (m) => m.to === version ? { ...m, status } : m
4986
5657
  );
4987
- await writeFile10(historyPath, JSON.stringify(updated, null, 2));
5658
+ await writeFile11(historyPath, JSON.stringify(updated, null, 2));
4988
5659
  } catch (error) {
4989
5660
  logger.error("Failed to update migration history status:", error);
4990
5661
  }
@@ -4993,9 +5664,9 @@ var MigrationManager = class {
4993
5664
  * Get migration history
4994
5665
  */
4995
5666
  async getMigrationHistory() {
4996
- const historyPath = join15(this.configPath, ".migration-history.json");
5667
+ const historyPath = join16(this.configPath, ".migration-history.json");
4997
5668
  try {
4998
- const content = await readFile9(historyPath, "utf-8");
5669
+ const content = await readFile10(historyPath, "utf-8");
4999
5670
  return JSON.parse(content);
5000
5671
  } catch {
5001
5672
  return [];
@@ -5035,7 +5706,7 @@ var SyncEngine = class {
5035
5706
  if (changes.hasUpdate) {
5036
5707
  this.displayUpdateInfo(changes);
5037
5708
  } else {
5038
- console.log(chalk2.green("\u2713 Your AIKit is up to date"));
5709
+ console.log(chalk3.green("\u2713 Your AIKit is up to date"));
5039
5710
  console.log(` Installed: ${changes.fromVersion}`);
5040
5711
  console.log(` Latest: ${changes.toVersion}`);
5041
5712
  }
@@ -5050,15 +5721,15 @@ var SyncEngine = class {
5050
5721
  */
5051
5722
  async previewUpdate() {
5052
5723
  try {
5053
- console.log(chalk2.bold("\n\u{1F50D} Previewing update...\n"));
5724
+ console.log(chalk3.bold("\n\u{1F50D} Previewing update...\n"));
5054
5725
  const changes = await this.versionManager.checkForUpdates();
5055
5726
  if (!changes.hasUpdate) {
5056
- console.log(chalk2.green("\u2713 No updates available"));
5727
+ console.log(chalk3.green("\u2713 No updates available"));
5057
5728
  return false;
5058
5729
  }
5059
5730
  await this.displayChanges(changes);
5060
- console.log(chalk2.yellow("\n\u26A0\uFE0F This is a preview - no changes will be made."));
5061
- console.log(chalk2.gray("Use `aikit sync apply` to apply these changes."));
5731
+ console.log(chalk3.yellow("\n\u26A0\uFE0F This is a preview - no changes will be made."));
5732
+ console.log(chalk3.gray("Use `aikit sync apply` to apply these changes."));
5062
5733
  return true;
5063
5734
  } catch (error) {
5064
5735
  logger.error("Failed to preview update:", error);
@@ -5072,7 +5743,7 @@ var SyncEngine = class {
5072
5743
  try {
5073
5744
  const changes = await this.versionManager.checkForUpdates();
5074
5745
  if (!changes.hasUpdate) {
5075
- console.log(chalk2.green("\u2713 Already up to date"));
5746
+ console.log(chalk3.green("\u2713 Already up to date"));
5076
5747
  return {
5077
5748
  success: true,
5078
5749
  newSkills: [],
@@ -5083,14 +5754,14 @@ var SyncEngine = class {
5083
5754
  }
5084
5755
  await this.displayChanges(changes);
5085
5756
  if (!options.force) {
5086
- const { confirmed } = await inquirer.prompt([{
5757
+ const { confirmed } = await inquirer2.prompt([{
5087
5758
  type: "confirm",
5088
5759
  name: "confirmed",
5089
5760
  message: "Continue with update?",
5090
5761
  default: false
5091
5762
  }]);
5092
5763
  if (!confirmed) {
5093
- console.log(chalk2.yellow("Update cancelled"));
5764
+ console.log(chalk3.yellow("Update cancelled"));
5094
5765
  return {
5095
5766
  success: false,
5096
5767
  newSkills: [],
@@ -5102,7 +5773,7 @@ var SyncEngine = class {
5102
5773
  }
5103
5774
  let backupId = void 0;
5104
5775
  if (!options.dryRun && options.backup !== false) {
5105
- console.log(chalk2.bold("\n\u{1F4E6} Creating backup..."));
5776
+ console.log(chalk3.bold("\n\u{1F4E6} Creating backup..."));
5106
5777
  const backupResult = await this.backupManager.createBackup(
5107
5778
  changes.fromVersion,
5108
5779
  changes.toVersion
@@ -5115,19 +5786,19 @@ var SyncEngine = class {
5115
5786
  for (const conflict of changes.conflicts) {
5116
5787
  await this.resolveConflict(conflict);
5117
5788
  }
5118
- console.log(chalk2.bold("\n\u{1F504} Running migrations..."));
5789
+ console.log(chalk3.bold("\n\u{1F504} Running migrations..."));
5119
5790
  const migrationResult = await this.migrationManager.runPendingMigrations();
5120
5791
  if (!migrationResult.success) {
5121
5792
  throw new Error(`Migration failed: ${migrationResult.failed.join(", ")}`);
5122
5793
  }
5123
- console.log(chalk2.bold("\n\u{1F4DD} Updating skills..."));
5794
+ console.log(chalk3.bold("\n\u{1F4DD} Updating skills..."));
5124
5795
  const updateResult = await this.updateSkills(changes, options);
5125
5796
  await this.versionManager.updateVersion(changes.toVersion);
5126
5797
  if (backupId) {
5127
5798
  const allSkills = await this.versionManager.loadSkillHashes(paths.skills(paths.globalConfig()));
5128
5799
  await this.versionManager.saveInstalledSkills(allSkills);
5129
5800
  }
5130
- console.log(chalk2.green("\n\u2705 Update complete!"));
5801
+ console.log(chalk3.green("\n\u2705 Update complete!"));
5131
5802
  this.displaySummary({
5132
5803
  success: true,
5133
5804
  backupId,
@@ -5142,7 +5813,7 @@ var SyncEngine = class {
5142
5813
  };
5143
5814
  } catch (error) {
5144
5815
  logger.error("Update failed:", error);
5145
- console.log(chalk2.red("\n\u274C Update failed"));
5816
+ console.log(chalk3.red("\n\u274C Update failed"));
5146
5817
  return {
5147
5818
  success: false,
5148
5819
  newSkills: [],
@@ -5157,14 +5828,14 @@ var SyncEngine = class {
5157
5828
  */
5158
5829
  async rollback(backupId) {
5159
5830
  try {
5160
- console.log(chalk2.bold("\n\u{1F504} Rollback...\n"));
5831
+ console.log(chalk3.bold("\n\u{1F504} Rollback...\n"));
5161
5832
  if (!backupId) {
5162
5833
  const backups = await this.backupManager.listBackups();
5163
5834
  if (backups.length === 0) {
5164
- console.log(chalk2.yellow("No backups available"));
5835
+ console.log(chalk3.yellow("No backups available"));
5165
5836
  return false;
5166
5837
  }
5167
- const { selectedBackup } = await inquirer.prompt([{
5838
+ const { selectedBackup } = await inquirer2.prompt([{
5168
5839
  type: "list",
5169
5840
  name: "selectedBackup",
5170
5841
  message: "Select backup to restore:",
@@ -5176,12 +5847,12 @@ var SyncEngine = class {
5176
5847
  backupId = selectedBackup;
5177
5848
  }
5178
5849
  if (!backupId) {
5179
- console.log(chalk2.yellow("No backup ID provided"));
5850
+ console.log(chalk3.yellow("No backup ID provided"));
5180
5851
  return false;
5181
5852
  }
5182
5853
  const success = await this.backupManager.restoreBackup(backupId);
5183
5854
  if (success) {
5184
- console.log(chalk2.green("\u2713 Rollback complete"));
5855
+ console.log(chalk3.green("\u2713 Rollback complete"));
5185
5856
  return true;
5186
5857
  }
5187
5858
  return false;
@@ -5194,36 +5865,36 @@ var SyncEngine = class {
5194
5865
  * Display update information
5195
5866
  */
5196
5867
  displayUpdateInfo(changes) {
5197
- console.log(chalk2.bold("\n\u{1F4E2} New version available!\n"));
5198
- console.log(` ${chalk2.cyan("Current:")} ${changes.fromVersion}`);
5199
- console.log(` ${chalk2.cyan("Latest:")} ${changes.toVersion}
5868
+ console.log(chalk3.bold("\n\u{1F4E2} New version available!\n"));
5869
+ console.log(` ${chalk3.cyan("Current:")} ${changes.fromVersion}`);
5870
+ console.log(` ${chalk3.cyan("Latest:")} ${changes.toVersion}
5200
5871
  `);
5201
5872
  }
5202
5873
  /**
5203
5874
  * Display changes summary
5204
5875
  */
5205
5876
  async displayChanges(changes) {
5206
- console.log(chalk2.bold("\u{1F4CA} Changes detected:\n"));
5877
+ console.log(chalk3.bold("\u{1F4CA} Changes detected:\n"));
5207
5878
  if (changes.newSkills.length > 0) {
5208
- console.log(chalk2.green(" New Skills:"));
5879
+ console.log(chalk3.green(" New Skills:"));
5209
5880
  changes.newSkills.forEach((skill) => {
5210
5881
  console.log(` + ${skill.name} (${skill.category})`);
5211
5882
  });
5212
5883
  }
5213
5884
  if (changes.modifiedSkills.length > 0) {
5214
- console.log(chalk2.yellow(" Updated Skills:"));
5885
+ console.log(chalk3.yellow(" Updated Skills:"));
5215
5886
  changes.modifiedSkills.forEach((skill) => {
5216
5887
  console.log(` ~ ${skill.name}`);
5217
5888
  });
5218
5889
  }
5219
5890
  if (changes.removedSkills.length > 0) {
5220
- console.log(chalk2.red(" Removed Skills:"));
5891
+ console.log(chalk3.red(" Removed Skills:"));
5221
5892
  changes.removedSkills.forEach((skill) => {
5222
5893
  console.log(` - ${skill.name}`);
5223
5894
  });
5224
5895
  }
5225
5896
  if (changes.conflicts.length > 0) {
5226
- console.log(chalk2.bold.red(" \u26A0\uFE0F Conflicts:"));
5897
+ console.log(chalk3.bold.red(" \u26A0\uFE0F Conflicts:"));
5227
5898
  changes.conflicts.forEach((conflict) => {
5228
5899
  console.log(` ! ${conflict.skillName} (user modified)`);
5229
5900
  });
@@ -5233,11 +5904,11 @@ var SyncEngine = class {
5233
5904
  * Resolve a conflict
5234
5905
  */
5235
5906
  async resolveConflict(conflict) {
5236
- console.log(chalk2.bold.red(`
5907
+ console.log(chalk3.bold.red(`
5237
5908
  \u26A0\uFE0F Conflict detected: ${conflict.skillName}
5238
5909
  `));
5239
- console.log(chalk2.yellow("Your version differs from official version."));
5240
- const { action } = await inquirer.prompt([{
5910
+ console.log(chalk3.yellow("Your version differs from official version."));
5911
+ const { action } = await inquirer2.prompt([{
5241
5912
  type: "list",
5242
5913
  name: "action",
5243
5914
  message: "Choose action:",
@@ -5262,1118 +5933,484 @@ var SyncEngine = class {
5262
5933
  if (action === "overwrite") {
5263
5934
  return;
5264
5935
  }
5265
- console.log(chalk2.yellow(" Your version will be preserved as -custom.md"));
5936
+ console.log(chalk3.yellow(" Your version will be preserved as -custom.md"));
5266
5937
  }
5267
5938
  /**
5268
5939
  * Update skills based on changes
5269
5940
  */
5270
5941
  async updateSkills(changes, options) {
5271
5942
  const globalSkillsPath = paths.skills(paths.globalConfig());
5272
- const projectSkillsPath = paths.skills(this.versionManager["config"].configPath);
5273
- const newSkills = [];
5274
- const updatedSkills = [];
5275
- const removedSkills = [];
5276
- for (const skill of changes.newSkills) {
5277
- if (!options.dryRun) {
5278
- await this.installSkill(globalSkillsPath, skill, projectSkillsPath);
5279
- }
5280
- newSkills.push(skill.name);
5281
- console.log(chalk2.green(` + ${skill.name}`));
5282
- }
5283
- for (const skill of changes.modifiedSkills) {
5284
- if (!options.dryRun) {
5285
- await this.installSkill(globalSkillsPath, skill, projectSkillsPath);
5286
- }
5287
- updatedSkills.push(skill.name);
5288
- console.log(chalk2.yellow(` ~ ${skill.name}`));
5289
- }
5290
- for (const skill of changes.removedSkills) {
5291
- if (!options.dryRun) {
5292
- await this.archiveSkill(projectSkillsPath, skill);
5293
- }
5294
- removedSkills.push(skill.name);
5295
- console.log(chalk2.red(` - ${skill.name} (archived)`));
5296
- }
5297
- return {
5298
- newSkills,
5299
- updatedSkills,
5300
- removedSkills
5301
- };
5302
- }
5303
- /**
5304
- * Install a skill
5305
- */
5306
- async installSkill(sourceDir, skill, targetDir) {
5307
- const sourcePath = join16(sourceDir, skill.category, `${skill.name}.md`);
5308
- const targetPath = join16(targetDir, skill.category, `${skill.name}.md`);
5309
- await mkdir9(dirname3(targetPath), { recursive: true });
5310
- await copyFile(sourcePath, targetPath);
5311
- }
5312
- /**
5313
- * Archive a removed skill
5314
- */
5315
- async archiveSkill(targetDir, skill) {
5316
- const sourcePath = join16(targetDir, skill.category, `${skill.name}.md`);
5317
- const targetPath = join16(targetDir, skill.category, `${skill.name}-deprecated.md`);
5318
- try {
5319
- const content = await readFile10(sourcePath, "utf-8");
5320
- const deprecatedNotice = `---
5321
- \u26A0\uFE0F DEPRECATED: This skill has been removed
5322
-
5323
- Deprecation date: ${(/* @__PURE__ */ new Date()).toISOString()}
5324
- Reason: Check release notes for replacement
5325
- ---
5326
-
5327
- ${content}`;
5328
- await mkdir9(dirname3(targetPath), { recursive: true });
5329
- await writeFile11(targetPath, deprecatedNotice);
5330
- } catch (error) {
5331
- if (error.code === "ENOENT") {
5332
- console.log(chalk2.yellow(` - ${skill.name} (not found, skipping)`));
5333
- } else {
5334
- throw error;
5335
- }
5336
- }
5337
- }
5338
- /**
5339
- * Display sync summary
5340
- */
5341
- displaySummary(result) {
5342
- console.log(chalk2.bold("\n\u{1F4CB} Summary:\n"));
5343
- console.log(` Updated from: ${chalk2.cyan(result.backupId || "N/A")}`);
5344
- console.log(` Updated to: ${chalk2.cyan("current")}`);
5345
- console.log();
5346
- if (result.newSkills.length > 0) {
5347
- console.log(chalk2.green(` ${result.newSkills.length} new skills installed`));
5348
- }
5349
- if (result.updatedSkills.length > 0) {
5350
- console.log(chalk2.yellow(` ${result.updatedSkills.length} skills updated`));
5351
- }
5352
- if (result.removedSkills.length > 0) {
5353
- console.log(chalk2.red(` ${result.removedSkills.length} skills archived`));
5354
- }
5355
- if (result.migrationsRun.length > 0) {
5356
- console.log(chalk2.blue(` ${result.migrationsRun.length} migrations run`));
5357
- }
5358
- if (result.backupId) {
5359
- console.log(chalk2.gray(`
5360
- Rollback available: aikit sync rollback ${result.backupId}`));
5361
- }
5362
- }
5363
- };
5364
-
5365
- // src/cli.ts
5366
- var program = new Command();
5367
- program.name("aikit").description("Open-source AI coding agent toolkit for OpenCode").version(getVersion());
5368
- program.command("init").description("Initialize AIKit configuration").option("-g, --global", "Initialize global configuration").option("-p, --project", "Initialize project-level configuration").action(async (options) => {
5369
- const configDir = options.global ? paths.globalConfig() : paths.projectConfig();
5370
- console.log(chalk3.bold("\n\u{1F680} AIKit Setup\n"));
5371
- logger.info(`Initializing AIKit in ${configDir}...`);
5372
- try {
5373
- await initializeConfig(configDir, options.global);
5374
- logger.success("\u2713 Configuration created");
5375
- if (!options.global) {
5376
- const config = await loadConfig();
5377
- const engine = new SkillEngine(config);
5378
- const result = await engine.syncSkillsToProject();
5379
- if (result.count > 0) {
5380
- logger.success(`\u2713 Synced ${result.count} skills`);
5381
- }
5382
- console.log(chalk3.bold("\n\u{1F50D} Checking CLI tools...\n"));
5383
- const cliTools = await CliDetector.checkAll();
5384
- const installableTools = CliDetector.filterInstallable(cliTools);
5385
- for (const tool of cliTools) {
5386
- const status = tool.installed ? chalk3.green("\u2713 Installed") : chalk3.yellow("\u2717 Not installed");
5387
- const version = tool.version ? chalk3.gray(` (${tool.version})`) : "";
5388
- console.log(` ${status} ${chalk3.cyan(tool.displayName)}${version}`);
5389
- }
5390
- if (installableTools.length > 0) {
5391
- console.log();
5392
- const { action } = await inquirer2.prompt([
5393
- {
5394
- type: "list",
5395
- name: "action",
5396
- message: "How would you like to proceed?",
5397
- choices: [
5398
- {
5399
- name: "all",
5400
- value: "all",
5401
- short: "a",
5402
- message: "Install all missing CLI tools"
5403
- },
5404
- {
5405
- name: "select",
5406
- value: "select",
5407
- short: "s",
5408
- message: "Select specific tools to install (use space to select, Enter to confirm)"
5409
- },
5410
- {
5411
- name: "skip",
5412
- value: "skip",
5413
- short: "n",
5414
- message: "Skip CLI tool installation"
5415
- }
5416
- ],
5417
- default: "all"
5418
- }
5419
- ]);
5420
- if (action === "skip") {
5421
- console.log();
5422
- logger.info("Skipping CLI tool installation");
5423
- } else if (action === "all") {
5424
- console.log();
5425
- logger.info(`Installing ${installableTools.length} CLI tool(s)...`);
5426
- for (const tool of installableTools) {
5427
- await installCliTool(tool);
5428
- }
5429
- console.log();
5430
- logger.success("\u2713 CLI tools installed");
5431
- } else {
5432
- const { installTools } = await inquirer2.prompt([
5433
- {
5434
- type: "checkbox",
5435
- name: "installTools",
5436
- message: "Select CLI tools to install (press Enter to skip):",
5437
- choices: installableTools.map((tool) => ({
5438
- name: tool.name,
5439
- value: tool,
5440
- checked: true
5441
- // Default to install all
5442
- }))
5443
- }
5444
- ]);
5445
- if (installTools.length > 0) {
5446
- console.log();
5447
- logger.info(`Installing ${installTools.length} CLI tool(s)...`);
5448
- for (const tool of installTools) {
5449
- await installCliTool(tool);
5450
- }
5451
- console.log();
5452
- logger.success("\u2713 CLI tools installed");
5453
- } else {
5454
- console.log();
5455
- logger.info("Skipping CLI tool installation");
5456
- }
5457
- }
5458
- } else {
5459
- console.log();
5460
- logger.success("\u2713 All CLI tools already installed");
5461
- }
5462
- const beads = new BeadsIntegration();
5463
- const beadsStatus = await beads.getStatus();
5464
- if (!beadsStatus.initialized) {
5465
- logger.info("Initializing .beads directory...");
5466
- await beads.initLocal();
5467
- logger.success("\u2713 .beads directory created");
5468
- if (!beadsStatus.installed) {
5469
- logger.info("Tip: Install Beads CLI globally for full functionality: npm install -g beads");
5470
- }
5471
- } else {
5472
- logger.info("Beads already initialized");
5473
- }
5474
- const opencodePath = paths.opencodeConfig();
5475
- await installToOpenCode(opencodePath);
5476
- console.log(chalk3.bold("\n\u2728 AIKit is ready!\n"));
5477
- console.log("Usage in OpenCode:");
5478
- console.log(chalk3.cyan(" /skills") + " - List all available skills");
5479
- console.log(chalk3.cyan(" /plan") + " - Create implementation plan");
5480
- console.log(chalk3.cyan(" /tdd") + " - Test-driven development");
5481
- console.log(chalk3.cyan(" /debug") + " - Systematic debugging");
5482
- console.log(chalk3.cyan(" /review") + " - Code review checklist");
5483
- console.log(chalk3.cyan(" /git") + " - Git workflow");
5484
- console.log(chalk3.cyan(" /frontend-aesthetics") + " - UI/UX guidelines");
5485
- console.log("\nPress " + chalk3.bold("Ctrl+K") + " in OpenCode to see all commands.\n");
5486
- }
5487
- } catch (error) {
5488
- logger.error("Failed to initialize AIKit:", error);
5489
- process.exit(1);
5490
- }
5491
- });
5492
- program.command("install").description("Install AIKit to OpenCode configuration").action(async () => {
5493
- logger.info("Installing AIKit to OpenCode...");
5494
- try {
5495
- const opencodePath = paths.opencodeConfig();
5496
- await installToOpenCode(opencodePath);
5497
- logger.success("AIKit installed to OpenCode!");
5498
- } catch (error) {
5499
- logger.error("Failed to install:", error);
5500
- process.exit(1);
5501
- }
5502
- });
5503
- program.command("sync [subcommand]").description("Update AIKit to latest version").option("--dry-run", "Preview changes without applying").option("-f, --force", "Skip confirmation prompts").option("--no-backup", "Skip creating backup").action(async (subcommand, options) => {
5504
- const config = await loadConfig();
5505
- const syncEngine = new SyncEngine(config);
5506
- if (!subcommand) {
5507
- await syncEngine.applyUpdate(options);
5508
- } else {
5509
- switch (subcommand) {
5510
- case "check":
5511
- await syncEngine.checkForUpdates();
5512
- break;
5513
- case "preview":
5514
- await syncEngine.previewUpdate();
5515
- break;
5516
- case "apply":
5517
- await syncEngine.applyUpdate(options);
5518
- break;
5519
- case "rollback":
5520
- await syncEngine.rollback();
5521
- break;
5522
- default:
5523
- logger.error(`Unknown subcommand: ${subcommand}`);
5524
- console.log(chalk3.gray("Available subcommands: check, preview, apply, rollback"));
5525
- process.exit(1);
5526
- }
5527
- }
5528
- });
5529
- var skillsCmd = program.command("skills").description("Manage skills");
5530
- skillsCmd.command("list").description("List available skills and tools with their configuration status").action(async () => {
5531
- const config = await loadConfig();
5532
- const engine = new SkillEngine(config);
5533
- const skills = await engine.listSkills();
5534
- const { ToolConfigManager: ToolConfigManager2 } = await Promise.resolve().then(() => (init_tool_config(), tool_config_exports));
5535
- const toolConfigManager = new ToolConfigManager2(config);
5536
- const tools = await toolConfigManager.listTools();
5537
- console.log(chalk3.bold("\n\u{1F4DA} Available Skills:\n"));
5538
- for (const skill of skills) {
5539
- console.log(` ${chalk3.cyan(skill.name)} - ${skill.description}`);
5540
- }
5541
- console.log(chalk3.bold("\n\u{1F527} Available Tools:\n"));
5542
- for (const tool of tools) {
5543
- let statusIcon = " ";
5544
- let statusText = "";
5545
- if (tool.status === "ready") {
5546
- statusIcon = chalk3.green("\u2713");
5547
- statusText = chalk3.gray("(ready)");
5548
- } else if (tool.status === "needs_config") {
5549
- statusIcon = chalk3.yellow("\u26A0");
5550
- statusText = chalk3.yellow("(needs config)");
5551
- } else if (tool.status === "error") {
5552
- statusIcon = chalk3.red("\u2717");
5553
- statusText = chalk3.red("(error)");
5554
- }
5555
- console.log(` ${statusIcon} ${chalk3.cyan(tool.name)} - ${tool.description} ${statusText}`);
5556
- if (tool.errorMessage) {
5557
- console.log(` ${chalk3.red("Error:")} ${tool.errorMessage}`);
5558
- }
5559
- }
5560
- console.log();
5561
- console.log(chalk3.gray('Tip: Use "aikit skills <tool-name> config" to configure a tool\n'));
5562
- });
5563
- skillsCmd.command("show <name>").description("Show skill details").action(async (name) => {
5564
- const config = await loadConfig();
5565
- const engine = new SkillEngine(config);
5566
- const skill = await engine.getSkill(name);
5567
- if (!skill) {
5568
- logger.error(`Skill not found: ${name}`);
5569
- process.exit(1);
5570
- }
5571
- console.log(chalk3.bold(`
5572
- \u{1F4D6} Skill: ${skill.name}
5573
- `));
5574
- console.log(chalk3.gray(skill.description));
5575
- console.log(chalk3.bold("\nWorkflow:"));
5576
- console.log(skill.content);
5577
- });
5578
- skillsCmd.command("create <name>").description("Create a new skill").action(async (name) => {
5579
- const config = await loadConfig();
5580
- const engine = new SkillEngine(config);
5581
- await engine.createSkill(name);
5582
- logger.success(`Skill created: ${name}`);
5583
- });
5584
- skillsCmd.command("sync").description("Sync global skills to project").action(async () => {
5585
- const config = await loadConfig();
5586
- const engine = new SkillEngine(config);
5587
- const result = await engine.syncSkillsToProject();
5588
- if (result.count === 0) {
5589
- logger.info("Skills already in sync or no global skills to sync");
5590
- } else {
5591
- console.log(chalk3.bold(`
5592
- \u2713 Synced ${result.count} skills to project:
5593
- `));
5594
- for (const skill of result.synced) {
5595
- console.log(` ${chalk3.cyan("\u2022")} ${skill}`);
5596
- }
5597
- console.log();
5598
- }
5599
- });
5600
- skillsCmd.command("config <tool-name>").description("Configure a tool (e.g., config figma-analysis)").action(async (toolName) => {
5601
- const config = await loadConfig();
5602
- const { ToolConfigManager: ToolConfigManager2 } = await Promise.resolve().then(() => (init_tool_config(), tool_config_exports));
5603
- const toolConfigManager = new ToolConfigManager2(config);
5604
- const tool = await toolConfigManager.getToolConfig(toolName);
5605
- if (!tool) {
5606
- logger.error(`Tool not found: ${toolName}`);
5607
- console.log(chalk3.gray("\nAvailable tools:"));
5608
- const tools = await toolConfigManager.listTools();
5609
- for (const t of tools) {
5610
- console.log(` - ${chalk3.cyan(t.name)}`);
5611
- }
5612
- console.log();
5613
- process.exit(1);
5614
- }
5615
- console.log(chalk3.bold(`
5616
- \u{1F527} Configuring: ${tool.name}
5617
- `));
5618
- console.log(chalk3.gray(tool.description));
5619
- console.log();
5620
- if (tool.configMethod === "oauth") {
5621
- if (toolName === "figma-analysis") {
5622
- const { FigmaOAuth: FigmaOAuth2 } = await Promise.resolve().then(() => (init_figma_oauth(), figma_oauth_exports));
5623
- const oauth = new FigmaOAuth2(toolConfigManager);
5624
- try {
5625
- const token = await oauth.authenticate();
5626
- console.log(chalk3.gray("\nValidating token..."));
5627
- const isValid = await oauth.validateToken(token);
5628
- if (isValid) {
5629
- logger.success(`
5630
- \u2705 ${tool.name} configured successfully!`);
5631
- console.log(chalk3.gray("\nYou can now use the /analyze-figma command in OpenCode.\n"));
5632
- } else {
5633
- await toolConfigManager.updateToolConfig(toolName, {
5634
- status: "error",
5635
- errorMessage: "Token validation failed"
5636
- });
5637
- logger.error("Token validation failed. Please try again.");
5638
- process.exit(1);
5639
- }
5640
- } catch (error) {
5641
- await toolConfigManager.updateToolConfig(toolName, {
5642
- status: "error",
5643
- errorMessage: error instanceof Error ? error.message : String(error)
5644
- });
5645
- logger.error(`Configuration failed: ${error instanceof Error ? error.message : String(error)}`);
5646
- process.exit(1);
5647
- }
5648
- } else {
5649
- logger.error(`OAuth flow not implemented for tool: ${toolName}`);
5650
- process.exit(1);
5651
- }
5652
- } else if (tool.configMethod === "manual") {
5653
- logger.info("Manual configuration not yet implemented");
5654
- process.exit(1);
5655
- } else {
5656
- logger.info(`Tool ${tool.name} does not require configuration`);
5657
- }
5658
- });
5659
- skillsCmd.command("*").description("Configure a tool (e.g., figma-analysis config)").allowUnknownOption().action(async () => {
5660
- const args = process.argv.slice(process.argv.indexOf("skills") + 1);
5661
- const toolName = args[0];
5662
- const action = args[1];
5663
- if (action === "config" && toolName) {
5664
- const config = await loadConfig();
5665
- const { ToolConfigManager: ToolConfigManager2 } = await Promise.resolve().then(() => (init_tool_config(), tool_config_exports));
5666
- const toolConfigManager = new ToolConfigManager2(config);
5667
- const tool = await toolConfigManager.getToolConfig(toolName);
5668
- if (!tool) {
5669
- logger.error(`Tool not found: ${toolName}`);
5670
- console.log(chalk3.gray("\nAvailable tools:"));
5671
- const tools = await toolConfigManager.listTools();
5672
- for (const t of tools) {
5673
- console.log(` - ${chalk3.cyan(t.name)}`);
5674
- }
5675
- console.log();
5676
- console.log(chalk3.gray("Tip: If you meant to show a skill, use: aikit skills show <name>"));
5677
- console.log();
5678
- process.exit(1);
5679
- }
5680
- console.log(chalk3.bold(`
5681
- \u{1F527} Configuring: ${tool.name}
5682
- `));
5683
- console.log(chalk3.gray(tool.description));
5684
- console.log();
5685
- if (tool.configMethod === "oauth") {
5686
- if (toolName === "figma-analysis") {
5687
- const { FigmaOAuth: FigmaOAuth2 } = await Promise.resolve().then(() => (init_figma_oauth(), figma_oauth_exports));
5688
- const oauth = new FigmaOAuth2(toolConfigManager);
5689
- try {
5690
- const token = await oauth.authenticate();
5691
- console.log(chalk3.gray("\nValidating token..."));
5692
- const isValid = await oauth.validateToken(token);
5693
- if (isValid) {
5694
- logger.success(`
5695
- \u2705 ${tool.name} configured successfully!`);
5696
- console.log(chalk3.gray("\nYou can now use the /analyze-figma command in OpenCode.\n"));
5697
- } else {
5698
- await toolConfigManager.updateToolConfig(toolName, {
5699
- status: "error",
5700
- errorMessage: "Token validation failed"
5701
- });
5702
- logger.error("Token validation failed. Please try again.");
5703
- process.exit(1);
5704
- }
5705
- } catch (error) {
5706
- await toolConfigManager.updateToolConfig(toolName, {
5707
- status: "error",
5708
- errorMessage: error instanceof Error ? error.message : String(error)
5709
- });
5710
- logger.error(`Configuration failed: ${error instanceof Error ? error.message : String(error)}`);
5711
- process.exit(1);
5712
- }
5713
- } else {
5714
- logger.error(`OAuth flow not implemented for tool: ${toolName}`);
5715
- process.exit(1);
5716
- }
5717
- } else if (tool.configMethod === "manual") {
5718
- logger.info("Manual configuration not yet implemented");
5719
- process.exit(1);
5720
- } else {
5721
- logger.info(`Tool ${tool.name} does not require configuration`);
5722
- }
5723
- } else {
5724
- logger.error(`Unknown command: ${toolName || "unknown"} ${action || ""}`);
5725
- console.log(chalk3.gray("\nAvailable commands:"));
5726
- console.log(" aikit skills list - List all skills and tools");
5727
- console.log(" aikit skills show <name> - Show skill details");
5728
- console.log(" aikit skills config <tool-name> - Configure a tool");
5729
- console.log(" aikit skills <tool-name> config - Configure a tool (alternative syntax)");
5730
- console.log();
5731
- process.exit(1);
5732
- }
5733
- });
5734
- var agentsCmd = program.command("agents").description("Manage agents");
5735
- agentsCmd.command("list").description("List available agents").action(async () => {
5736
- const config = await loadConfig();
5737
- const manager = new AgentManager(config);
5738
- const agents = manager.listAgents();
5739
- console.log(chalk3.bold("\n\u{1F916} Available Agents:\n"));
5740
- for (const agent of agents) {
5741
- console.log(` ${chalk3.cyan(`@${agent.name}`)} - ${agent.description}`);
5742
- console.log(chalk3.gray(` Use when: ${agent.useWhen}`));
5743
- }
5744
- console.log();
5745
- });
5746
- var commandsCmd = program.command("commands").description("Manage commands");
5747
- commandsCmd.command("list").description("List available commands").action(async () => {
5748
- const config = await loadConfig();
5749
- const runner = new CommandRunner(config);
5750
- const commands = await runner.listCommands();
5751
- console.log(chalk3.bold("\n\u26A1 Available Commands:\n"));
5752
- const groups = groupBy(commands, (c) => c.category);
5753
- for (const [category, cmds] of Object.entries(groups)) {
5754
- console.log(chalk3.bold.yellow(`
5755
- ${category}:`));
5756
- for (const cmd of cmds) {
5757
- console.log(` ${chalk3.cyan(`/${cmd.name}`)} - ${cmd.description}`);
5758
- }
5759
- }
5760
- console.log();
5761
- });
5762
- var toolsCmd = program.command("tools").description("Manage custom tools");
5763
- toolsCmd.command("list").description("List available tools").action(async () => {
5764
- const config = await loadConfig();
5765
- const registry = new ToolRegistry(config);
5766
- const tools = await registry.listTools();
5767
- console.log(chalk3.bold("\n\u{1F527} Available Tools:\n"));
5768
- for (const tool of tools) {
5769
- console.log(` ${chalk3.cyan(tool.name)} - ${tool.description}`);
5770
- }
5771
- console.log();
5772
- });
5773
- var pluginsCmd = program.command("plugins").description("Manage plugins");
5774
- pluginsCmd.command("list").description("List available plugins").action(async () => {
5775
- const config = await loadConfig();
5776
- const system = new PluginSystem(config);
5777
- const plugins = await system.listPlugins();
5778
- console.log(chalk3.bold("\n\u{1F50C} Available Plugins:\n"));
5779
- for (const plugin of plugins) {
5780
- const status = plugin.enabled ? chalk3.green("\u2713") : chalk3.gray("\u25CB");
5781
- console.log(` ${status} ${chalk3.cyan(plugin.name)} - ${plugin.description}`);
5782
- }
5783
- console.log();
5784
- });
5785
- var memoryCmd = program.command("memory").description("Manage persistent memory");
5786
- memoryCmd.command("list").description("List memory entries").action(async () => {
5787
- const config = await loadConfig();
5788
- const memory = new MemoryManager(config);
5789
- const entries = await memory.list();
5790
- console.log(chalk3.bold("\n\u{1F9E0} Memory Entries:\n"));
5791
- for (const entry of entries) {
5792
- console.log(` ${chalk3.cyan(entry.key)} - ${entry.summary}`);
5793
- console.log(chalk3.gray(` Updated: ${entry.updatedAt}`));
5794
- }
5795
- console.log();
5796
- });
5797
- memoryCmd.command("read <key>").description("Read a memory entry").action(async (key) => {
5798
- const config = await loadConfig();
5799
- const memory = new MemoryManager(config);
5800
- const content = await memory.read(key);
5801
- if (!content) {
5802
- logger.error(`Memory entry not found: ${key}`);
5803
- process.exit(1);
5804
- }
5805
- console.log(content);
5806
- });
5807
- var beadsCmd = program.command("beads").description("Beads task management integration");
5808
- beadsCmd.command("status").description("Show current Beads status").action(async () => {
5809
- const beads = new BeadsIntegration();
5810
- const status = await beads.getStatus();
5811
- console.log(chalk3.bold("\n\u{1F4FF} Beads Status:\n"));
5812
- console.log(` Active tasks: ${status.activeTasks}`);
5813
- console.log(` Completed: ${status.completedTasks}`);
5814
- console.log(` Current: ${status.currentTask || "None"}`);
5815
- console.log();
5816
- });
5817
- program.command("status").description("Show AIKit status").action(async () => {
5818
- console.log(chalk3.bold(`
5819
- \u{1F680} AIKit v${getVersion}
5820
- `));
5821
- try {
5822
- const config = await loadConfig();
5823
- console.log(chalk3.green("\u2713 Configuration loaded"));
5824
- const skillEngine = new SkillEngine(config);
5825
- const skills = await skillEngine.listSkills();
5826
- console.log(` Skills: ${skills.length}`);
5827
- const agentManager = new AgentManager(config);
5828
- const agents = agentManager.listAgents();
5829
- console.log(` Agents: ${agents.length}`);
5830
- const commandRunner = new CommandRunner(config);
5831
- const commands = await commandRunner.listCommands();
5832
- console.log(` Commands: ${commands.length}`);
5833
- const toolRegistry = new ToolRegistry(config);
5834
- const tools = await toolRegistry.listTools();
5835
- console.log(` Tools: ${tools.length}`);
5836
- const beads = new BeadsIntegration();
5837
- const beadsStatus = await beads.isInstalled();
5838
- console.log(` Beads: ${beadsStatus ? chalk3.green("Installed") : chalk3.yellow("Not installed")}`);
5839
- } catch (error) {
5840
- console.log(chalk3.yellow('\u26A0 AIKit not initialized. Run "aikit init" to get started.'));
5841
- }
5842
- console.log();
5843
- });
5844
- async function initializeConfig(configDir, _isGlobal) {
5845
- const { mkdir: mkdir11, writeFile: writeFile13 } = await import("fs/promises");
5846
- const { join: join18 } = await import("path");
5847
- const dirs = [
5848
- "",
5849
- "skills",
5850
- "agents",
5851
- "commands",
5852
- "commands/build",
5853
- "commands/git",
5854
- "commands/plan",
5855
- "commands/research",
5856
- "tools",
5857
- "plugins",
5858
- "memory",
5859
- "memory/_templates",
5860
- "memory/handoffs",
5861
- "memory/observations",
5862
- "memory/research"
5863
- ];
5864
- for (const dir of dirs) {
5865
- await mkdir11(join18(configDir, dir), { recursive: true });
5866
- }
5867
- const defaultConfig = {
5868
- version: getVersion,
5869
- skills: { enabled: true },
5870
- agents: { enabled: true, default: "build" },
5871
- commands: { enabled: true },
5872
- tools: { enabled: true },
5873
- plugins: { enabled: true },
5874
- memory: { enabled: true },
5875
- beads: { enabled: true },
5876
- antiHallucination: { enabled: true }
5877
- };
5878
- await writeFile13(
5879
- join18(configDir, "aikit.json"),
5880
- JSON.stringify(defaultConfig, null, 2)
5881
- );
5882
- const agentsMd = `# AIKit Agent Rules
5883
-
5884
- ## Build Commands
5885
- - \`npm run build\` - Build the project
5886
- - \`npm run test\` - Run tests
5887
- - \`npm run lint\` - Run linting
5888
-
5889
- ## Code Style
5890
- - Use 2 spaces for indentation
5891
- - Use single quotes for strings
5892
- - Add trailing commas
5893
-
5894
- ## Naming Conventions
5895
- - Variables: camelCase
5896
- - Components: PascalCase
5897
- - Files: kebab-case
5898
-
5899
- ## Project-Specific Rules
5900
- Add your project-specific rules here.
5901
- `;
5902
- await writeFile13(join18(configDir, "AGENTS.md"), agentsMd);
5903
- }
5904
- async function configureMcpServer(projectPath) {
5905
- const { mkdir: mkdir11, writeFile: writeFile13, readFile: readFile12 } = await import("fs/promises");
5906
- const { join: join18 } = await import("path");
5907
- const { existsSync: existsSync5 } = await import("fs");
5908
- const { homedir: homedir3 } = await import("os");
5909
- const { fileURLToPath: fileURLToPath3 } = await import("url");
5910
- const { dirname: dirname4 } = await import("path");
5911
- const currentFile = fileURLToPath3(import.meta.url);
5912
- const currentDir = dirname4(currentFile);
5913
- const aikitPath = join18(currentDir, "..");
5914
- const mcpServerPath = join18(aikitPath, "dist", "mcp-server.js");
5915
- const configLocations = [
5916
- // Global config (most common)
5917
- join18(homedir3(), ".config", "opencode", "opencode.json"),
5918
- // Project-level config
5919
- join18(projectPath, ".opencode", "opencode.json"),
5920
- // Alternative global location
5921
- join18(homedir3(), ".opencode", "opencode.json")
5922
- ];
5923
- const mcpServerConfig = {
5924
- type: "local",
5925
- command: ["node", mcpServerPath],
5926
- environment: {}
5927
- };
5928
- for (const configPath of configLocations) {
5929
- try {
5930
- const configDir = join18(configPath, "..");
5931
- await mkdir11(configDir, { recursive: true });
5932
- let config = {};
5933
- if (existsSync5(configPath)) {
5934
- try {
5935
- const existing = await readFile12(configPath, "utf-8");
5936
- config = JSON.parse(existing);
5937
- } catch {
5938
- config = {};
5939
- }
5940
- }
5941
- if (!config.mcp) {
5942
- config.mcp = {};
5943
- }
5944
- config.mcp.aikit = mcpServerConfig;
5945
- await writeFile13(configPath, JSON.stringify(config, null, 2));
5946
- logger.success(`
5947
- \u2705 MCP server configured: ${configPath}`);
5948
- logger.info(` Server: node ${mcpServerPath}`);
5949
- return;
5950
- } catch (error) {
5951
- continue;
5952
- }
5953
- }
5954
- const instructionsPath = join18(projectPath, ".opencode", "MCP_SETUP.md");
5955
- await mkdir11(join18(projectPath, ".opencode"), { recursive: true });
5956
- await writeFile13(instructionsPath, `# AIKit MCP Server Configuration
5957
-
5958
- ## Automatic Setup Failed
5959
-
5960
- Please manually configure the MCP server in OpenCode.
5961
-
5962
- ## Configuration
5963
-
5964
- Add this to your OpenCode configuration file (\`~/.config/opencode/opencode.json\`):
5965
-
5966
- \`\`\`json
5967
- {
5968
- "mcpServers": {
5969
- "aikit": {
5970
- "command": "node",
5971
- "args": ["${mcpServerPath}"],
5972
- "env": {}
5973
- }
5974
- }
5975
- }
5976
- \`\`\`
5977
-
5978
- ## After Configuration
5979
-
5980
- 1. Restart OpenCode completely
5981
- 2. OpenCode will automatically start the MCP server
5982
- 3. Tools will be available via MCP protocol
5983
- 4. You can use tools like \`tool_read_figma_design\` directly
5984
-
5985
- ## Verify
5986
-
5987
- After restarting OpenCode, check:
5988
- - MCP server is running (check OpenCode settings)
5989
- - Tools are discoverable (OpenCode should list them)
5990
- - You can call tools via MCP protocol
5991
- `);
5992
- logger.warn(`
5993
- \u26A0\uFE0F Could not auto-configure MCP server. See: ${instructionsPath}`);
5994
- }
5995
- async function installCliTool(tool) {
5996
- try {
5997
- logger.info(`Installing ${tool.displayName}...`);
5998
- switch (tool.name) {
5999
- case "opencode" /* OPENCODE */:
6000
- await installToOpenCode(paths.opencodeConfig());
6001
- break;
6002
- case "claude" /* CLAUDE */:
6003
- const { execSync: execSync2 } = await import("child_process");
6004
- execSync2("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
6005
- break;
6006
- case "github" /* GITHUB */:
6007
- const { execSync: execGh } = await import("child_process");
6008
- execGh("npm install -g gh", { stdio: "inherit" });
6009
- break;
6010
- }
6011
- logger.success(`\u2713 ${tool.displayName} installed`);
6012
- return true;
6013
- } catch (error) {
6014
- logger.error(`Failed to install ${tool.displayName}:`, error);
6015
- return false;
6016
- }
6017
- }
6018
- async function installToOpenCode(_opencodePath) {
6019
- const { mkdir: mkdir11, writeFile: writeFile13, access: access5 } = await import("fs/promises");
6020
- const { join: join18 } = await import("path");
6021
- const projectPath = process.cwd();
6022
- const opencodeCommandDir = join18(projectPath, ".opencode", "command");
6023
- const aikitDir = join18(projectPath, ".aikit");
6024
- const opencodeAgentDir = join18(paths.opencodeConfig(), "agent");
6025
- await mkdir11(opencodeCommandDir, { recursive: true });
6026
- await mkdir11(join18(aikitDir, "skills"), { recursive: true });
6027
- await mkdir11(opencodeAgentDir, { recursive: true });
6028
- const agentFiles = {
6029
- agent: `---
6030
- description: General-purpose default agent (OpenCode compatibility).
6031
- mode: subagent
6032
- tools:
6033
- "*": true
6034
- ---
6035
-
6036
- Use for quick tasks when no specialized agent is needed.`,
6037
- planner: `---
6038
- description: Strategic planner; breaks down work and coordinates specialist agents.
6039
- mode: subagent
6040
- tools:
6041
- "*": true
6042
- ---
6043
-
6044
- Use when the task is complex or multi-step. Delegates to @build for implementation,
6045
- @scout for research, @review for code review/security, @explore for codebase navigation,
6046
- and @vision for visual analysis.`,
6047
- build: `---
6048
- description: Primary builder; writes code, tests, and implements features.
6049
- mode: subagent
6050
- tools:
6051
- "*": true
6052
- ---
6053
-
6054
- Use for feature implementation, refactors, and bug fixes. Prefer TDD, small steps,
6055
- and run checks after changes. Delegates to @review for audits and @explore for context.`,
6056
- rush: `---
6057
- description: Fast execution for small/urgent changes with minimal planning.
6058
- mode: subagent
6059
- tools:
6060
- "*": true
6061
- ---
6062
-
6063
- Use for quick fixes, hotfixes, or tiny edits. Keep scope minimal and verify quickly.`,
6064
- review: `---
6065
- description: Code review and quality/security auditing agent.
6066
- mode: subagent
6067
- tools:
6068
- "*": true
6069
- ---
6070
-
6071
- Use to review correctness, security, performance, maintainability, and tests. Be specific
6072
- about issues and suggest concrete fixes.`,
6073
- scout: `---
6074
- description: Research agent for external docs, patterns, and references.
6075
- mode: subagent
6076
- tools:
6077
- "*": true
6078
- ---
6079
-
6080
- Use to look up docs, examples, best practices. Summarize findings concisely and cite sources.`,
6081
- explore: `---
6082
- description: Codebase navigation agent (search, grep, structure understanding).
6083
- mode: subagent
6084
- tools:
6085
- "*": true
6086
- ---
6087
-
6088
- Use to locate files, patterns, dependencies, and gather quick context in the repo.`,
6089
- vision: `---
6090
- description: Visual analysis agent for mockups, screenshots, PDFs, diagrams.
6091
- mode: subagent
6092
- tools:
6093
- "*": true
6094
- ---
6095
-
6096
- Use to interpret visual assets (components, layout, colors, typography) and translate to tasks.`
6097
- };
6098
- for (const [name, content] of Object.entries(agentFiles)) {
6099
- const filePath = join18(opencodeAgentDir, `${name}.md`);
6100
- try {
6101
- await access5(filePath);
6102
- } catch {
6103
- await writeFile13(filePath, content, "utf8");
6104
- }
6105
- }
6106
- const config = await loadConfig();
6107
- const skillEngine = new SkillEngine(config);
6108
- const commandRunner = new CommandRunner(config);
6109
- const skills = await skillEngine.listSkills();
6110
- const commands = await commandRunner.listCommands();
6111
- const opencodeCommands = {};
6112
- const skillsList = skills.map((s) => `| \`/${s.name.replace(/\s+/g, "-")}\` | ${s.description} |`).join("\n");
6113
- opencodeCommands["skills"] = `List all available AIKit skills and how to use them.
6114
-
6115
- READ .aikit/AGENTS.md
6116
-
6117
- ## Available Skills
6118
-
6119
- | Command | Description |
6120
- |---------|-------------|
6121
- ${skillsList}
6122
-
6123
- Type any command to use that skill. For example: \`/test-driven-development\` or \`/tdd\`.`;
6124
- for (const skill of skills) {
6125
- const commandName = skill.name.replace(/\s+/g, "-").toLowerCase();
6126
- const skillPath = skill.filePath;
6127
- const relativePath = skillPath.startsWith(projectPath) ? skillPath.replace(projectPath, "").replace(/\\/g, "/").replace(/^\//, "") : `.aikit/skills/${skill.name.replace(/\s+/g, "-").toLowerCase()}.md`;
6128
- const useWhen = skill.useWhen || `The user asks you to ${skill.name}`;
6129
- opencodeCommands[commandName] = `Use the **${skill.name} skill** ${useWhen.toLowerCase()}.
6130
-
6131
- READ ${relativePath}
6132
-
6133
- ## Description
6134
- ${skill.description}
6135
-
6136
- ## When to Use
6137
- ${useWhen}
6138
-
6139
- ## Workflow
6140
- ${skill.content.split("\n").slice(0, 20).join("\n")}${skill.content.split("\n").length > 20 ? "\n\n... (see full skill file for complete workflow)" : ""}
6141
-
6142
- **IMPORTANT**: Follow this skill's workflow step by step. Do not skip steps.
6143
- Complete the checklist at the end of the skill.`;
6144
- }
6145
- for (const cmd of commands) {
6146
- if (opencodeCommands[cmd.name]) continue;
6147
- const commandName = cmd.name.replace(/\//g, "").replace(/\s+/g, "-");
6148
- const examples = cmd.examples.map((e) => `- \`${e}\``).join("\n");
6149
- if (cmd.name === "analyze-figma") {
6150
- opencodeCommands[commandName] = `# Command: /analyze-figma
6151
-
6152
- ## Description
6153
- ${cmd.description}
6154
-
6155
- ## Usage
6156
- \`${cmd.usage}\`
6157
-
6158
- ## Examples
6159
- ${examples}
6160
-
6161
- ## \u26A0\uFE0F CRITICAL: Extract URL FIRST!
6162
-
6163
- **BEFORE ANYTHING ELSE**: Look at the user's FULL input message (all lines) and find the Figma URL. It's ALWAYS there - never ask for it!
6164
-
6165
- **The URL pattern**: Look for text containing \`figma.com/design/\` anywhere in the user's message.
6166
-
6167
- **Example of what user input looks like**:
6168
- \`\`\`
6169
- /analyze-figma https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/
6170
- Online-Education-Website-Free-Template--Community-?t=7G5yzTiEtJlIZBtY-0
6171
- \`\`\`
6172
-
6173
- **Extract the complete URL** (combine if split):
6174
- \`https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/Online-Education-Website-Free-Template--Community-?t=7G5yzTiEtJlIZBtY-0\`
6175
-
6176
- ## Workflow
6177
-
6178
- **IMPORTANT**: When user provides a Figma URL, you MUST immediately:
6179
-
6180
- **Step 1: Extract URL from User Input**
6181
-
6182
- **CRITICAL**: The URL is ALWAYS in the user's input message! DO NOT ask for it - just extract it!
6183
-
6184
- **MANDATORY**: You MUST extract the URL before proceeding. This is not optional!
6185
-
6186
- **How to Extract**:
6187
- 1. **Read the ENTIRE user input message** - look at ALL lines, not just the first line
6188
- 2. **Search for ANY text containing** \`figma.com/design/\` - this is the URL
6189
- 3. **URL may appear in different formats**:
6190
- - On same line: \`/analyze-figma https://www.figma.com/design/...\`
6191
- - Split across lines:
6192
- \`\`\`
6193
- /analyze-figma https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/
6194
- Online-Education-Website-Free-Template--Community-?t=7G5yzTiEtJlIZBtY-0
6195
- \`\`\`
6196
- - Just the URL: \`https://www.figma.com/design/...\`
6197
- 4. **Extract the COMPLETE URL**:
6198
- - Start from \`https://\` or \`http://\`
6199
- - Include everything until the end of the line or next whitespace
6200
- - If URL is split, combine ALL parts into one complete URL
6201
- 5. **Include ALL query parameters**: \`?node-id=...\`, \`&t=...\`, etc.
6202
-
6203
- **REAL EXAMPLE**:
6204
- \`\`\`
6205
- User input:
6206
- /analyze-figma https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/
6207
- Online-Education-Website-Free-Template--Community-?t=7G5yzTiEtJlIZBtY-0
6208
- \`\`\`
6209
-
6210
- **Extract as**:
6211
- \`https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/Online-Education-Website-Free-Template--Community-?t=7G5yzTiEtJlIZBtY-0\`
6212
-
6213
- **CRITICAL RULES**:
6214
- - \u2705 DO: Read the ENTIRE user message (all lines)
6215
- - \u2705 DO: Look for \`figma.com/design/\` anywhere in the message
6216
- - \u2705 DO: Combine split lines into one URL
6217
- - \u274C DO NOT: Ask user for URL - it's ALWAYS in the input
6218
- - \u274C DO NOT: Skip this step - URL extraction is MANDATORY
6219
- - \u274C DO NOT: Proceed without extracting URL first
6220
-
6221
- **If you think URL is not found**:
6222
- 1. Re-read the user's message line by line
6223
- 2. Look for ANY mention of "figma.com"
6224
- 3. Check if URL is split across multiple lines
6225
- 4. The URL is definitely there - find it!
6226
- - If URL not found in current message, check previous messages
6227
-
6228
- **Step 2: Check Tool Configuration**
6229
-
6230
- Before calling the tool, verify that Figma tool is configured:
6231
- - If not configured, inform user to run: \`aikit skills figma-analysis config\`
6232
- - The tool requires a Figma Personal Access Token
6233
-
6234
- **Step 3: Call MCP Tool read_figma_design**
6235
-
6236
- Use the MCP tool \`read_figma_design\` (or \`tool_read_figma_design\` via MCP) with the extracted URL:
6237
- \`\`\`
6238
- Use tool: read_figma_design
6239
- Arguments: { "url": "[extracted URL]" }
6240
- \`\`\`
6241
-
6242
- **This tool will automatically**:
6243
- 1. Validate the Figma URL format
6244
- 2. Check if Figma tool is configured
6245
- 3. Call Figma API to fetch design data
6246
- 4. Extract design tokens:
6247
- - Colors (from fills and strokes, converted to hex)
6248
- - Typography (font families, sizes, weights, line heights)
6249
- - Spacing system (8px grid detection)
6250
- - Components (from Figma components)
6251
- - Screens/Frames (dimensions and names)
6252
- - Breakpoints (common responsive breakpoints)
6253
- 5. Return formatted markdown with all extracted tokens
6254
-
6255
- **Step 4: Format and Save**
6256
-
6257
- Format extracted tokens as structured markdown and save using memory-update tool:
6258
- \`\`\`
6259
- Use tool: memory-update
6260
- Arguments: {
6261
- "key": "research/figma-analysis",
6262
- "content": "[formatted markdown with all tokens]"
6263
- }
6264
- \`\`\`
6265
-
6266
- **Step 5: Report Results**
6267
-
6268
- Report what was extracted:
6269
- - Number of screens found
6270
- - Number of colors in palette
6271
- - Typography styles found
6272
- - Components identified
6273
- - Confirm save location: \`memory/research/figma-analysis.md\`
6274
-
6275
- ## Critical Instructions
6276
-
6277
- - **DO NOT** ask user to "share the Figma URL" - they already provided it in the command
6278
- - **DO NOT** wait for confirmation - just start analyzing immediately
6279
- - **DO** extract URL from full user input message
6280
- - **DO** call MCP tool \`read_figma_design\` immediately
6281
- - **DO** use browser MCP to navigate and snapshot
6282
- - **DO** extract everything automatically without asking
6283
- - **DO** save to memory automatically
6284
-
6285
- ## Error Handling
6286
-
6287
- If the tool returns an error:
6288
- 1. **If "needs config"**: Guide user to run \`aikit skills figma-analysis config\`
6289
- 2. **If API error**:
6290
- - Verify the Figma URL is correct and accessible
6291
- - Ensure your API token has access to the file
6292
- - Check if the file is public or you have permission to access it
6293
- 3. **If URL invalid**: Re-check the extracted URL format
6294
-
6295
- ## How to Parse URL from Command
6296
-
6297
- **CRITICAL**: The Figma URL is provided in the SAME message as the command!
6298
-
6299
- Example:
6300
- - User input: \`/analyze-figma https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/Online-Education-Website-Free-Template--Community-?node-id=0-1&t=70yZa7w5wSyjDhYj-1\`
6301
- - The URL is: \`https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/Online-Education-Website-Free-Template--Community-?node-id=0-1&t=70yZa7w5wSyjDhYj-1\`
6302
-
6303
- **Extract the URL** from the command input:
6304
- - Everything after \`/analyze-figma \` (note the space) is the URL
6305
- - The URL starts with \`https://\` or \`http://\`
6306
- - Extract the ENTIRE URL including all query parameters
6307
- - Check the FULL user message, not just command name
6308
-
6309
- ## Example Usage
6310
-
6311
- User input: \`/analyze-figma https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/Online-Education-Website-Free-Template--Community-?node-id=0-1&t=70yZa7w5wSyjDhYj-1\`
6312
-
6313
- **Step 1: Extract URL**
6314
- - Check full user input message
6315
- - Extract: \`https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/Online-Education-Website-Free-Template--Community-?node-id=0-1&t=70yZa7w5wSyjDhYj-1\`
6316
-
6317
- **Step 2: Call MCP Tool**
6318
- \`\`\`
6319
- Use tool: read_figma_design
6320
- Arguments: { "url": "https://www.figma.com/design/lC34qpTSy2MYalTIOsj8S2/Online-Education-Website-Free-Template--Community-?node-id=0-1&t=70yZa7w5wSyjDhYj-1" }
6321
- \`\`\`
6322
-
6323
- **Step 3: Tool automatically extracts tokens via Figma API**
6324
-
6325
- **Step 4: Save to memory using memory-update tool**
5943
+ const projectSkillsPath = paths.skills(this.versionManager["config"].configPath);
5944
+ const newSkills = [];
5945
+ const updatedSkills = [];
5946
+ const removedSkills = [];
5947
+ for (const skill of changes.newSkills) {
5948
+ if (!options.dryRun) {
5949
+ await this.installSkill(globalSkillsPath, skill, projectSkillsPath);
5950
+ }
5951
+ newSkills.push(skill.name);
5952
+ console.log(chalk3.green(` + ${skill.name}`));
5953
+ }
5954
+ for (const skill of changes.modifiedSkills) {
5955
+ if (!options.dryRun) {
5956
+ await this.installSkill(globalSkillsPath, skill, projectSkillsPath);
5957
+ }
5958
+ updatedSkills.push(skill.name);
5959
+ console.log(chalk3.yellow(` ~ ${skill.name}`));
5960
+ }
5961
+ for (const skill of changes.removedSkills) {
5962
+ if (!options.dryRun) {
5963
+ await this.archiveSkill(projectSkillsPath, skill);
5964
+ }
5965
+ removedSkills.push(skill.name);
5966
+ console.log(chalk3.red(` - ${skill.name} (archived)`));
5967
+ }
5968
+ return {
5969
+ newSkills,
5970
+ updatedSkills,
5971
+ removedSkills
5972
+ };
5973
+ }
5974
+ /**
5975
+ * Install a skill
5976
+ */
5977
+ async installSkill(sourceDir, skill, targetDir) {
5978
+ const sourcePath = join17(sourceDir, skill.category, `${skill.name}.md`);
5979
+ const targetPath = join17(targetDir, skill.category, `${skill.name}.md`);
5980
+ await mkdir10(dirname4(targetPath), { recursive: true });
5981
+ await copyFile(sourcePath, targetPath);
5982
+ }
5983
+ /**
5984
+ * Archive a removed skill
5985
+ */
5986
+ async archiveSkill(targetDir, skill) {
5987
+ const sourcePath = join17(targetDir, skill.category, `${skill.name}.md`);
5988
+ const targetPath = join17(targetDir, skill.category, `${skill.name}-deprecated.md`);
5989
+ try {
5990
+ const content = await readFile11(sourcePath, "utf-8");
5991
+ const deprecatedNotice = `---
5992
+ \u26A0\uFE0F DEPRECATED: This skill has been removed
6326
5993
 
6327
- **Step 5: Report results to user**
5994
+ Deprecation date: ${(/* @__PURE__ */ new Date()).toISOString()}
5995
+ Reason: Check release notes for replacement
5996
+ ---
6328
5997
 
6329
- ## Important Reminders
5998
+ ${content}`;
5999
+ await mkdir10(dirname4(targetPath), { recursive: true });
6000
+ await writeFile12(targetPath, deprecatedNotice);
6001
+ } catch (error) {
6002
+ if (error.code === "ENOENT") {
6003
+ console.log(chalk3.yellow(` - ${skill.name} (not found, skipping)`));
6004
+ } else {
6005
+ throw error;
6006
+ }
6007
+ }
6008
+ }
6009
+ /**
6010
+ * Display sync summary
6011
+ */
6012
+ displaySummary(result) {
6013
+ console.log(chalk3.bold("\n\u{1F4CB} Summary:\n"));
6014
+ console.log(` Updated from: ${chalk3.cyan(result.backupId || "N/A")}`);
6015
+ console.log(` Updated to: ${chalk3.cyan("current")}`);
6016
+ console.log();
6017
+ if (result.newSkills.length > 0) {
6018
+ console.log(chalk3.green(` ${result.newSkills.length} new skills installed`));
6019
+ }
6020
+ if (result.updatedSkills.length > 0) {
6021
+ console.log(chalk3.yellow(` ${result.updatedSkills.length} skills updated`));
6022
+ }
6023
+ if (result.removedSkills.length > 0) {
6024
+ console.log(chalk3.red(` ${result.removedSkills.length} skills archived`));
6025
+ }
6026
+ if (result.migrationsRun.length > 0) {
6027
+ console.log(chalk3.blue(` ${result.migrationsRun.length} migrations run`));
6028
+ }
6029
+ if (result.backupId) {
6030
+ console.log(chalk3.gray(`
6031
+ Rollback available: aikit sync rollback ${result.backupId}`));
6032
+ }
6033
+ }
6034
+ };
6330
6035
 
6331
- - The URL is ALREADY in the command input - extract it from full message!
6332
- - URL may be split across multiple lines - combine them into one complete URL
6333
- - Do NOT ask for URL again - it's always in the input
6334
- - Do NOT wait - start immediately
6335
- - Use MCP tool \`read_figma_design\` which uses Figma API directly`;
6036
+ // src/cli/commands/sync.ts
6037
+ init_logger();
6038
+ function registerSyncCommand(program2) {
6039
+ program2.command("sync [subcommand]").description("Update AIKit to latest version").option("--dry-run", "Preview changes without applying").option("-f, --force", "Skip confirmation prompts").option("--no-backup", "Skip creating backup").action(async (subcommand, options) => {
6040
+ const config = await loadConfig();
6041
+ const syncEngine = new SyncEngine(config);
6042
+ if (!subcommand) {
6043
+ await syncEngine.applyUpdate(options);
6336
6044
  } else {
6337
- opencodeCommands[commandName] = `# Command: /${cmd.name}
6338
-
6339
- ## Description
6340
- ${cmd.description}
6341
-
6342
- ## Usage
6343
- \`${cmd.usage}\`
6344
-
6345
- ## Examples
6346
- ${examples}
6347
-
6348
- ## Workflow
6349
- ${cmd.content}
6045
+ switch (subcommand) {
6046
+ case "check":
6047
+ await syncEngine.checkForUpdates();
6048
+ break;
6049
+ case "preview":
6050
+ await syncEngine.previewUpdate();
6051
+ break;
6052
+ case "apply":
6053
+ await syncEngine.applyUpdate(options);
6054
+ break;
6055
+ case "rollback":
6056
+ await syncEngine.rollback();
6057
+ break;
6058
+ default:
6059
+ logger.error(`Unknown subcommand: ${subcommand}`);
6060
+ console.log(chalk4.gray("Available subcommands: check, preview, apply, rollback"));
6061
+ process.exit(1);
6062
+ }
6063
+ }
6064
+ });
6065
+ }
6350
6066
 
6351
- **Category**: ${cmd.category}`;
6067
+ // src/cli/commands/skills.ts
6068
+ init_esm_shims();
6069
+ import chalk5 from "chalk";
6070
+ init_logger();
6071
+ function registerSkillsCommand(program2) {
6072
+ const skillsCmd = program2.command("skills").description("Manage skills");
6073
+ skillsCmd.command("list").description("List available skills and tools with their configuration status").action(async () => {
6074
+ const config = await loadConfig();
6075
+ const engine = new SkillEngine(config);
6076
+ const skills = await engine.listSkills();
6077
+ const { ToolConfigManager: ToolConfigManager2 } = await Promise.resolve().then(() => (init_tool_config(), tool_config_exports));
6078
+ const toolConfigManager = new ToolConfigManager2(config);
6079
+ const tools = await toolConfigManager.listTools();
6080
+ console.log(chalk5.bold("\n\u{1F4DA} Available Skills:\n"));
6081
+ for (const skill of skills) {
6082
+ console.log(` ${chalk5.cyan(skill.name)} - ${skill.description}`);
6083
+ }
6084
+ console.log(chalk5.bold("\n\u{1F527} Available Tools:\n"));
6085
+ for (const tool of tools) {
6086
+ let statusIcon = " ";
6087
+ let statusText = "";
6088
+ if (tool.status === "ready") {
6089
+ statusIcon = chalk5.green("\u2713");
6090
+ statusText = chalk5.gray("(ready)");
6091
+ } else if (tool.status === "needs_config") {
6092
+ statusIcon = chalk5.yellow("\u26A0");
6093
+ statusText = chalk5.yellow("(needs config)");
6094
+ } else if (tool.status === "error") {
6095
+ statusIcon = chalk5.red("\u2717");
6096
+ statusText = chalk5.red("(error)");
6097
+ }
6098
+ console.log(` ${statusIcon} ${chalk5.cyan(tool.name)} - ${tool.description} ${statusText}`);
6099
+ if (tool.errorMessage) {
6100
+ console.log(` ${chalk5.red("Error:")} ${tool.errorMessage}`);
6101
+ }
6102
+ }
6103
+ console.log();
6104
+ console.log(chalk5.gray('Tip: Use "aikit skills <tool-name> config" to configure a tool\n'));
6105
+ });
6106
+ skillsCmd.command("show <name>").description("Show skill details").action(async (name) => {
6107
+ const config = await loadConfig();
6108
+ const engine = new SkillEngine(config);
6109
+ const skill = await engine.getSkill(name);
6110
+ if (!skill) {
6111
+ logger.error(`Skill not found: ${name}`);
6112
+ process.exit(1);
6113
+ }
6114
+ console.log(chalk5.bold(`
6115
+ \u{1F4D6} Skill: ${skill.name}
6116
+ `));
6117
+ console.log(chalk5.gray(skill.description));
6118
+ console.log(chalk5.bold("\nWorkflow:"));
6119
+ console.log(skill.content);
6120
+ });
6121
+ skillsCmd.command("create <name>").description("Create a new skill").action(async (name) => {
6122
+ const config = await loadConfig();
6123
+ const engine = new SkillEngine(config);
6124
+ await engine.createSkill(name);
6125
+ logger.success(`Skill created: ${name}`);
6126
+ });
6127
+ skillsCmd.command("sync").description("Sync global skills to project").action(async () => {
6128
+ const config = await loadConfig();
6129
+ const engine = new SkillEngine(config);
6130
+ const result = await engine.syncSkillsToProject();
6131
+ if (result.count === 0) {
6132
+ logger.info("Skills already in sync or no global skills to sync");
6133
+ } else {
6134
+ console.log(chalk5.bold(`
6135
+ \u2713 Synced ${result.count} skills to project:
6136
+ `));
6137
+ for (const skill of result.synced) {
6138
+ console.log(` ${chalk5.cyan("\u2022")} ${skill}`);
6139
+ }
6140
+ console.log();
6141
+ }
6142
+ });
6143
+ skillsCmd.command("config <tool-name>").description("Configure a tool (e.g., config figma-analysis)").action(async (toolName) => {
6144
+ await configureToolAction(toolName);
6145
+ });
6146
+ skillsCmd.command("*").description("Configure a tool (e.g., figma-analysis config)").allowUnknownOption().action(async () => {
6147
+ const args = process.argv.slice(process.argv.indexOf("skills") + 1);
6148
+ const toolName = args[0];
6149
+ const action = args[1];
6150
+ if (action === "config" && toolName) {
6151
+ await configureToolAction(toolName);
6152
+ } else {
6153
+ logger.error(`Unknown command: ${toolName || "unknown"} ${action || ""}`);
6154
+ console.log(chalk5.gray("\nAvailable commands:"));
6155
+ console.log(" aikit skills list - List all skills and tools");
6156
+ console.log(" aikit skills show <name> - Show skill details");
6157
+ console.log(" aikit skills config <tool-name> - Configure a tool");
6158
+ console.log(" aikit skills <tool-name> config - Configure a tool (alternative syntax)");
6159
+ console.log();
6160
+ process.exit(1);
6161
+ }
6162
+ });
6163
+ return skillsCmd;
6164
+ }
6165
+ async function configureToolAction(toolName) {
6166
+ const config = await loadConfig();
6167
+ const { ToolConfigManager: ToolConfigManager2 } = await Promise.resolve().then(() => (init_tool_config(), tool_config_exports));
6168
+ const toolConfigManager = new ToolConfigManager2(config);
6169
+ const tool = await toolConfigManager.getToolConfig(toolName);
6170
+ if (!tool) {
6171
+ logger.error(`Tool not found: ${toolName}`);
6172
+ console.log(chalk5.gray("\nAvailable tools:"));
6173
+ const tools = await toolConfigManager.listTools();
6174
+ for (const t of tools) {
6175
+ console.log(` - ${chalk5.cyan(t.name)}`);
6352
6176
  }
6177
+ console.log();
6178
+ console.log(chalk5.gray("Tip: If you meant to show a skill, use: aikit skills show <name>"));
6179
+ console.log();
6180
+ process.exit(1);
6353
6181
  }
6354
- let count = 0;
6355
- for (const [name, content] of Object.entries(opencodeCommands)) {
6356
- const filePath = join18(opencodeCommandDir, `${name}.md`);
6357
- await writeFile13(filePath, content.trim());
6358
- logger.info(` \u2713 Created /${name} command`);
6359
- count++;
6182
+ console.log(chalk5.bold(`
6183
+ \u{1F527} Configuring: ${tool.name}
6184
+ `));
6185
+ console.log(chalk5.gray(tool.description));
6186
+ console.log();
6187
+ if (tool.configMethod === "oauth") {
6188
+ if (toolName === "figma-analysis") {
6189
+ const { FigmaOAuth: FigmaOAuth2 } = await Promise.resolve().then(() => (init_figma_oauth(), figma_oauth_exports));
6190
+ const oauth = new FigmaOAuth2(toolConfigManager);
6191
+ try {
6192
+ const token = await oauth.authenticate();
6193
+ console.log(chalk5.gray("\nValidating token..."));
6194
+ const isValid = await oauth.validateToken(token);
6195
+ if (isValid) {
6196
+ logger.success(`
6197
+ \u2705 ${tool.name} configured successfully!`);
6198
+ console.log(chalk5.gray("\nYou can now use the /analyze-figma command in OpenCode.\n"));
6199
+ } else {
6200
+ await toolConfigManager.updateToolConfig(toolName, {
6201
+ status: "error",
6202
+ errorMessage: "Token validation failed"
6203
+ });
6204
+ logger.error("Token validation failed. Please try again.");
6205
+ process.exit(1);
6206
+ }
6207
+ } catch (error) {
6208
+ await toolConfigManager.updateToolConfig(toolName, {
6209
+ status: "error",
6210
+ errorMessage: error instanceof Error ? error.message : String(error)
6211
+ });
6212
+ logger.error(`Configuration failed: ${error instanceof Error ? error.message : String(error)}`);
6213
+ process.exit(1);
6214
+ }
6215
+ } else {
6216
+ logger.error(`OAuth flow not implemented for tool: ${toolName}`);
6217
+ process.exit(1);
6218
+ }
6219
+ } else if (tool.configMethod === "manual") {
6220
+ logger.info("Manual configuration not yet implemented");
6221
+ process.exit(1);
6222
+ } else {
6223
+ logger.info(`Tool ${tool.name} does not require configuration`);
6360
6224
  }
6361
- logger.success(`
6362
- Created ${count} OpenCode commands in .opencode/command/`);
6363
- await configureMcpServer(projectPath);
6364
- logger.info("\nUsage in OpenCode:");
6365
- logger.info(" Press Ctrl+K to open command picker");
6366
- logger.info(" Or type /skills to see all available skills");
6367
- logger.info(` Available: ${skills.length} skills, ${commands.length} commands`);
6368
- logger.info(" MCP server configured - tools available via MCP protocol");
6369
6225
  }
6370
- function groupBy(array, keyFn) {
6371
- return array.reduce((acc, item) => {
6372
- const key = keyFn(item);
6373
- if (!acc[key]) acc[key] = [];
6374
- acc[key].push(item);
6375
- return acc;
6376
- }, {});
6226
+
6227
+ // src/cli/commands/misc.ts
6228
+ init_esm_shims();
6229
+ import chalk6 from "chalk";
6230
+ import { readFile as readFile13, writeFile as writeFile14 } from "fs/promises";
6231
+ import { join as join19 } from "path";
6232
+ init_memory();
6233
+ init_logger();
6234
+ function registerAgentsCommand(program2) {
6235
+ const agentsCmd = program2.command("agents").description("Manage agents");
6236
+ agentsCmd.command("list").description("List available agents").action(async () => {
6237
+ const config = await loadConfig();
6238
+ const manager = new AgentManager(config);
6239
+ const agents = manager.listAgents();
6240
+ console.log(chalk6.bold("\n\u{1F916} Available Agents:\n"));
6241
+ for (const agent of agents) {
6242
+ console.log(` ${chalk6.cyan(`@${agent.name}`)} - ${agent.description}`);
6243
+ console.log(chalk6.gray(` Use when: ${agent.useWhen}`));
6244
+ }
6245
+ console.log();
6246
+ });
6247
+ return agentsCmd;
6248
+ }
6249
+ function registerCommandsCommand(program2) {
6250
+ const commandsCmd = program2.command("commands").description("Manage commands");
6251
+ commandsCmd.command("list").description("List available commands").action(async () => {
6252
+ const config = await loadConfig();
6253
+ const runner = new CommandRunner(config);
6254
+ const commands = await runner.listCommands();
6255
+ console.log(chalk6.bold("\n\u26A1 Available Commands:\n"));
6256
+ const groups = groupBy(commands, (c) => c.category);
6257
+ for (const [category, cmds] of Object.entries(groups)) {
6258
+ console.log(chalk6.bold.yellow(`
6259
+ ${category}:`));
6260
+ for (const cmd of cmds) {
6261
+ console.log(` ${chalk6.cyan(`/${cmd.name}`)} - ${cmd.description}`);
6262
+ }
6263
+ }
6264
+ console.log();
6265
+ });
6266
+ return commandsCmd;
6267
+ }
6268
+ function registerModeCommand(program2) {
6269
+ const modeCmd = program2.command("mode").description("Manage AIKit mode");
6270
+ modeCmd.command("get").description("Get current AIKit mode").action(async () => {
6271
+ const config = await loadConfig();
6272
+ const { mode } = config;
6273
+ console.log(chalk6.bold("\n\u{1F4CB} Current Mode:\n"));
6274
+ console.log(` ${chalk6.cyan(mode || "build")}`);
6275
+ console.log();
6276
+ console.log(chalk6.bold("Available Modes:\n"));
6277
+ console.log(` ${chalk6.cyan("plan")} - Create detailed implementation plans`);
6278
+ console.log(` ${chalk6.cyan("build")} - Direct execution mode`);
6279
+ console.log(` ${chalk6.cyan("one-shot")} - End-to-end autonomous execution`);
6280
+ console.log();
6281
+ console.log(chalk6.gray('Use "aikit mode set <mode>" to change mode.'));
6282
+ });
6283
+ modeCmd.command("set <mode>").description("Set AIKit mode (plan, build, one-shot)").action(async (mode) => {
6284
+ const config = await loadConfig();
6285
+ const configPath = config.configPath;
6286
+ try {
6287
+ const validModes = ["plan", "build", "one-shot"];
6288
+ if (!validModes.includes(mode)) {
6289
+ console.log(chalk6.red(`Invalid mode. Available modes: ${validModes.join(", ")}`));
6290
+ return;
6291
+ }
6292
+ const configData = JSON.parse(await readFile13(join19(configPath, "aikit.json"), "utf-8"));
6293
+ configData.mode = mode;
6294
+ await writeFile14(join19(configPath, "aikit.json"), JSON.stringify(configData, null, 2));
6295
+ console.log(chalk6.green(`\u2713 Mode set to: ${mode}`));
6296
+ console.log(chalk6.gray(`Configuration updated at: ${configPath}/aikit.json`));
6297
+ } catch (error) {
6298
+ console.log(chalk6.red(`Failed to set mode: ${error instanceof Error ? error.message : String(error)}`));
6299
+ }
6300
+ });
6301
+ return modeCmd;
6302
+ }
6303
+ function registerToolsCommand(program2) {
6304
+ const toolsCmd = program2.command("tools").description("Manage custom tools");
6305
+ toolsCmd.command("list").description("List available tools").action(async () => {
6306
+ const config = await loadConfig();
6307
+ const registry = new ToolRegistry(config);
6308
+ const tools = await registry.listTools();
6309
+ console.log(chalk6.bold("\n\u{1F527} Available Tools:\n"));
6310
+ for (const tool of tools) {
6311
+ console.log(` ${chalk6.cyan(tool.name)} - ${tool.description}`);
6312
+ }
6313
+ console.log();
6314
+ });
6315
+ return toolsCmd;
6316
+ }
6317
+ function registerPluginsCommand(program2) {
6318
+ const pluginsCmd = program2.command("plugins").description("Manage plugins");
6319
+ pluginsCmd.command("list").description("List available plugins").action(async () => {
6320
+ const config = await loadConfig();
6321
+ const system = new PluginSystem(config);
6322
+ const plugins = await system.listPlugins();
6323
+ console.log(chalk6.bold("\n\u{1F50C} Available Plugins:\n"));
6324
+ for (const plugin of plugins) {
6325
+ const status = plugin.enabled ? chalk6.green("\u2713") : chalk6.gray("\u25CB");
6326
+ console.log(` ${status} ${chalk6.cyan(plugin.name)} - ${plugin.description}`);
6327
+ }
6328
+ console.log();
6329
+ });
6330
+ return pluginsCmd;
6331
+ }
6332
+ function registerMemoryCommand(program2) {
6333
+ const memoryCmd = program2.command("memory").description("Manage persistent memory");
6334
+ memoryCmd.command("list").description("List memory entries").action(async () => {
6335
+ const config = await loadConfig();
6336
+ const memory = new MemoryManager(config);
6337
+ const entries = await memory.list();
6338
+ console.log(chalk6.bold("\n\u{1F9E0} Memory Entries:\n"));
6339
+ for (const entry of entries) {
6340
+ console.log(` ${chalk6.cyan(entry.key)} - ${entry.summary}`);
6341
+ console.log(chalk6.gray(` Updated: ${entry.updatedAt}`));
6342
+ }
6343
+ console.log();
6344
+ });
6345
+ memoryCmd.command("read <key>").description("Read a memory entry").action(async (key) => {
6346
+ const config = await loadConfig();
6347
+ const memory = new MemoryManager(config);
6348
+ const content = await memory.read(key);
6349
+ if (!content) {
6350
+ logger.error(`Memory entry not found: ${key}`);
6351
+ process.exit(1);
6352
+ }
6353
+ console.log(content);
6354
+ });
6355
+ return memoryCmd;
6377
6356
  }
6357
+ function registerBeadsCommand(program2) {
6358
+ const beadsCmd = program2.command("beads").description("Beads task management integration");
6359
+ beadsCmd.command("status").description("Show current Beads status").action(async () => {
6360
+ const beads = new BeadsIntegration();
6361
+ const status = await beads.getStatus();
6362
+ console.log(chalk6.bold("\n\u{1F4FF} Beads Status:\n"));
6363
+ console.log(` Active tasks: ${status.activeTasks}`);
6364
+ console.log(` Completed: ${status.completedTasks}`);
6365
+ console.log(` Current: ${status.currentTask || "None"}`);
6366
+ console.log();
6367
+ });
6368
+ return beadsCmd;
6369
+ }
6370
+ function registerStatusCommand(program2) {
6371
+ program2.command("status").description("Show AIKit status").action(async () => {
6372
+ console.log(chalk6.bold(`
6373
+ \u{1F680} AIKit v${getVersion()}
6374
+ `));
6375
+ try {
6376
+ const config = await loadConfig();
6377
+ console.log(chalk6.green("\u2713 Configuration loaded"));
6378
+ const skillEngine = new SkillEngine(config);
6379
+ const skills = await skillEngine.listSkills();
6380
+ console.log(` Skills: ${skills.length}`);
6381
+ const agentManager = new AgentManager(config);
6382
+ const agents = agentManager.listAgents();
6383
+ console.log(` Agents: ${agents.length}`);
6384
+ const commandRunner = new CommandRunner(config);
6385
+ const commands = await commandRunner.listCommands();
6386
+ console.log(` Commands: ${commands.length}`);
6387
+ const toolRegistry = new ToolRegistry(config);
6388
+ const tools = await toolRegistry.listTools();
6389
+ console.log(` Tools: ${tools.length}`);
6390
+ const beads = new BeadsIntegration();
6391
+ const beadsStatus = await beads.isInstalled();
6392
+ console.log(` Beads: ${beadsStatus ? chalk6.green("Installed") : chalk6.yellow("Not installed")}`);
6393
+ } catch (error) {
6394
+ console.log(chalk6.yellow('\u26A0 AIKit not initialized. Run "aikit init" to get started.'));
6395
+ }
6396
+ console.log();
6397
+ });
6398
+ }
6399
+
6400
+ // src/cli/index.ts
6401
+ var program = new Command();
6402
+ program.name("aikit").description("Open-source AI coding agent toolkit for OpenCode").version(getVersion());
6403
+ registerInitCommand(program);
6404
+ registerInstallCommand(program);
6405
+ registerSyncCommand(program);
6406
+ registerSkillsCommand(program);
6407
+ registerAgentsCommand(program);
6408
+ registerCommandsCommand(program);
6409
+ registerModeCommand(program);
6410
+ registerToolsCommand(program);
6411
+ registerPluginsCommand(program);
6412
+ registerMemoryCommand(program);
6413
+ registerBeadsCommand(program);
6414
+ registerStatusCommand(program);
6378
6415
  program.parse();
6379
6416
  //# sourceMappingURL=cli.js.map