@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/CHANGELOG.md +9 -0
- package/dist/cli.js +1389 -1352
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +11 -7
- package/dist/index.js +50 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +122 -1
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
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:
|
|
807
|
-
await mkdir4(
|
|
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
|
|
938
|
-
import { join as
|
|
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 =
|
|
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
|
|
1050
|
-
const content = await
|
|
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 =
|
|
1061
|
-
await
|
|
1062
|
-
await
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
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
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
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
|
|
4346
|
-
|
|
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
|
-
|
|
4484
|
+
continue;
|
|
4349
4485
|
}
|
|
4350
4486
|
}
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
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
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
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
|
-
|
|
4394
|
-
return
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
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
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
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
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
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
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
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
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
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 =
|
|
5184
|
+
const installedPath = join14(paths.globalConfig(), ".installed-skills.json");
|
|
4514
5185
|
try {
|
|
4515
|
-
await
|
|
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 =
|
|
4537
|
-
await
|
|
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
|
|
4553
|
-
import { join as
|
|
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 =
|
|
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
|
|
5240
|
+
await mkdir9(this.backupsDir, { recursive: true });
|
|
4570
5241
|
const backupId = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
4571
|
-
const backupPath =
|
|
4572
|
-
await
|
|
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 =
|
|
4594
|
-
await
|
|
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 =
|
|
4608
|
-
const targetPath =
|
|
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
|
|
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
|
|
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
|
|
4639
|
-
await
|
|
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
|
|
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 =
|
|
4661
|
-
const manifestPath =
|
|
5331
|
+
const backupPath = join15(this.backupsDir, entry);
|
|
5332
|
+
const manifestPath = join15(backupPath, "backup-manifest.json");
|
|
4662
5333
|
try {
|
|
4663
|
-
const manifestContent = await
|
|
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 =
|
|
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 =
|
|
4724
|
-
const targetPath =
|
|
4725
|
-
await
|
|
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 =
|
|
4741
|
-
await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
4837
|
-
import { join as
|
|
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 =
|
|
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(
|
|
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 =
|
|
5545
|
+
const migrationHistoryPath = join16(this.configPath, ".migration-history.json");
|
|
4875
5546
|
try {
|
|
4876
|
-
const content = await
|
|
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 =
|
|
5633
|
+
const historyPath = join16(this.configPath, ".migration-history.json");
|
|
4963
5634
|
try {
|
|
4964
5635
|
let history = [];
|
|
4965
5636
|
try {
|
|
4966
|
-
const content = await
|
|
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
|
|
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 =
|
|
5651
|
+
const historyPath = join16(this.configPath, ".migration-history.json");
|
|
4981
5652
|
try {
|
|
4982
|
-
const content = await
|
|
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
|
|
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 =
|
|
5667
|
+
const historyPath = join16(this.configPath, ".migration-history.json");
|
|
4997
5668
|
try {
|
|
4998
|
-
const content = await
|
|
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(
|
|
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(
|
|
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(
|
|
5727
|
+
console.log(chalk3.green("\u2713 No updates available"));
|
|
5057
5728
|
return false;
|
|
5058
5729
|
}
|
|
5059
5730
|
await this.displayChanges(changes);
|
|
5060
|
-
console.log(
|
|
5061
|
-
console.log(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
5835
|
+
console.log(chalk3.yellow("No backups available"));
|
|
5165
5836
|
return false;
|
|
5166
5837
|
}
|
|
5167
|
-
const { selectedBackup } = await
|
|
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(
|
|
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(
|
|
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(
|
|
5198
|
-
console.log(` ${
|
|
5199
|
-
console.log(` ${
|
|
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(
|
|
5877
|
+
console.log(chalk3.bold("\u{1F4CA} Changes detected:\n"));
|
|
5207
5878
|
if (changes.newSkills.length > 0) {
|
|
5208
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
5907
|
+
console.log(chalk3.bold.red(`
|
|
5237
5908
|
\u26A0\uFE0F Conflict detected: ${conflict.skillName}
|
|
5238
5909
|
`));
|
|
5239
|
-
console.log(
|
|
5240
|
-
const { action } = await
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
5308
|
-
const targetPath =
|
|
5309
|
-
await
|
|
5310
|
-
await copyFile(sourcePath, targetPath);
|
|
5311
|
-
}
|
|
5312
|
-
/**
|
|
5313
|
-
* Archive a removed skill
|
|
5314
|
-
*/
|
|
5315
|
-
async archiveSkill(targetDir, skill) {
|
|
5316
|
-
const sourcePath =
|
|
5317
|
-
const targetPath =
|
|
5318
|
-
try {
|
|
5319
|
-
const content = await
|
|
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
|
-
|
|
5994
|
+
Deprecation date: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
5995
|
+
Reason: Check release notes for replacement
|
|
5996
|
+
---
|
|
6328
5997
|
|
|
6329
|
-
|
|
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
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
-
|
|
6335
|
-
|
|
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
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
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
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
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
|