@tdsoft-tech/aikit 0.1.8 → 0.1.11
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 +18 -0
- package/dist/cli.js +1292 -119
- package/dist/cli.js.map +1 -1
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +89 -0
- package/dist/mcp-server.js.map +1 -1
- package/package.json +4 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
4
10
|
var __esm = (fn, res) => function __init() {
|
|
5
11
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
12
|
};
|
|
@@ -735,8 +741,8 @@ var init_memory = __esm({
|
|
|
735
741
|
for (const subDir of subDirs) {
|
|
736
742
|
const dirPath = join8(memoryPath, subDir);
|
|
737
743
|
try {
|
|
738
|
-
const { readdir:
|
|
739
|
-
const files = await
|
|
744
|
+
const { readdir: readdir10 } = await import("fs/promises");
|
|
745
|
+
const files = await readdir10(dirPath);
|
|
740
746
|
for (const file of files) {
|
|
741
747
|
if (!file.endsWith(".md")) continue;
|
|
742
748
|
const content = await readFile5(join8(dirPath, file), "utf-8");
|
|
@@ -797,8 +803,8 @@ var init_memory = __esm({
|
|
|
797
803
|
const subDir = type === "observation" ? "observations" : type === "handoff" ? "handoffs" : type === "research" ? "research" : "";
|
|
798
804
|
filePath = join8(memoryPath, subDir, `${key}.md`);
|
|
799
805
|
}
|
|
800
|
-
const { dirname:
|
|
801
|
-
await mkdir4(
|
|
806
|
+
const { dirname: dirname4 } = await import("path");
|
|
807
|
+
await mkdir4(dirname4(filePath), { recursive: true });
|
|
802
808
|
if (options?.append) {
|
|
803
809
|
try {
|
|
804
810
|
const existing = await readFile5(filePath, "utf-8");
|
|
@@ -928,8 +934,8 @@ var tool_config_exports = {};
|
|
|
928
934
|
__export(tool_config_exports, {
|
|
929
935
|
ToolConfigManager: () => ToolConfigManager
|
|
930
936
|
});
|
|
931
|
-
import { readFile as
|
|
932
|
-
import { join as
|
|
937
|
+
import { readFile as readFile11, writeFile as writeFile12, mkdir as mkdir10, access as access4, constants as constants4 } from "fs/promises";
|
|
938
|
+
import { join as join17 } from "path";
|
|
933
939
|
import { z as z3 } from "zod";
|
|
934
940
|
var ToolConfigSchema, REGISTERED_TOOLS, ToolConfigManager;
|
|
935
941
|
var init_tool_config = __esm({
|
|
@@ -957,7 +963,7 @@ var init_tool_config = __esm({
|
|
|
957
963
|
toolsConfigPath;
|
|
958
964
|
constructor(config) {
|
|
959
965
|
this.config = config;
|
|
960
|
-
this.toolsConfigPath =
|
|
966
|
+
this.toolsConfigPath = join17(this.config.configPath, "config", "tools.json");
|
|
961
967
|
}
|
|
962
968
|
/**
|
|
963
969
|
* Get all registered tools with their current status
|
|
@@ -1041,7 +1047,7 @@ var init_tool_config = __esm({
|
|
|
1041
1047
|
async loadConfigs() {
|
|
1042
1048
|
try {
|
|
1043
1049
|
await access4(this.toolsConfigPath, constants4.R_OK);
|
|
1044
|
-
const content = await
|
|
1050
|
+
const content = await readFile11(this.toolsConfigPath, "utf-8");
|
|
1045
1051
|
return JSON.parse(content);
|
|
1046
1052
|
} catch {
|
|
1047
1053
|
return {};
|
|
@@ -1051,9 +1057,9 @@ var init_tool_config = __esm({
|
|
|
1051
1057
|
* Save configurations
|
|
1052
1058
|
*/
|
|
1053
1059
|
async saveConfigs(configs) {
|
|
1054
|
-
const configDir =
|
|
1055
|
-
await
|
|
1056
|
-
await
|
|
1060
|
+
const configDir = join17(this.config.configPath, "config");
|
|
1061
|
+
await mkdir10(configDir, { recursive: true });
|
|
1062
|
+
await writeFile12(this.toolsConfigPath, JSON.stringify(configs, null, 2));
|
|
1057
1063
|
}
|
|
1058
1064
|
};
|
|
1059
1065
|
}
|
|
@@ -1100,8 +1106,8 @@ var init_figma_oauth = __esm({
|
|
|
1100
1106
|
console.log('3. Give it a name (e.g., "AIKit")');
|
|
1101
1107
|
console.log("4. Copy the token (you won't see it again!)");
|
|
1102
1108
|
console.log("5. Paste it here when prompted\n");
|
|
1103
|
-
const { default:
|
|
1104
|
-
const { token } = await
|
|
1109
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
1110
|
+
const { token } = await inquirer3.prompt([
|
|
1105
1111
|
{
|
|
1106
1112
|
type: "password",
|
|
1107
1113
|
name: "token",
|
|
@@ -1131,14 +1137,14 @@ var init_figma_oauth = __esm({
|
|
|
1131
1137
|
* Alternative: Manual token input
|
|
1132
1138
|
*/
|
|
1133
1139
|
async authenticateManual() {
|
|
1134
|
-
const { default:
|
|
1140
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
1135
1141
|
console.log("\n\u{1F510} Figma Authentication (Manual)\n");
|
|
1136
1142
|
console.log("To get your Figma Personal Access Token:");
|
|
1137
1143
|
console.log("1. Visit: https://www.figma.com/developers/api#access-tokens");
|
|
1138
1144
|
console.log('2. Scroll to "Personal access tokens"');
|
|
1139
1145
|
console.log('3. Click "Create new token"');
|
|
1140
1146
|
console.log("4. Copy the token and paste it below\n");
|
|
1141
|
-
const { token } = await
|
|
1147
|
+
const { token } = await inquirer3.prompt([
|
|
1142
1148
|
{
|
|
1143
1149
|
type: "password",
|
|
1144
1150
|
name: "token",
|
|
@@ -1191,8 +1197,8 @@ var init_figma_oauth = __esm({
|
|
|
1191
1197
|
// src/cli.ts
|
|
1192
1198
|
init_esm_shims();
|
|
1193
1199
|
import { Command } from "commander";
|
|
1194
|
-
import
|
|
1195
|
-
import
|
|
1200
|
+
import chalk3 from "chalk";
|
|
1201
|
+
import inquirer2 from "inquirer";
|
|
1196
1202
|
|
|
1197
1203
|
// src/index.ts
|
|
1198
1204
|
init_esm_shims();
|
|
@@ -2037,6 +2043,95 @@ Before marking complete:
|
|
|
2037
2043
|
2. Display summary to user
|
|
2038
2044
|
3. Propose next actions
|
|
2039
2045
|
4. Continue from where left off`
|
|
2046
|
+
},
|
|
2047
|
+
{
|
|
2048
|
+
name: "one-shot",
|
|
2049
|
+
description: "End-to-end autonomous task execution (beta)",
|
|
2050
|
+
category: "core",
|
|
2051
|
+
usage: "/one-shot <task description>",
|
|
2052
|
+
examples: ["/one-shot Add user authentication", "/one-shot Fix navigation bug"],
|
|
2053
|
+
content: `One-Shot Mode (beta) - End-to-end autonomous task execution.
|
|
2054
|
+
|
|
2055
|
+
\u26A0\uFE0F This mode is experimental. Use for straightforward tasks first.
|
|
2056
|
+
|
|
2057
|
+
## Workflow
|
|
2058
|
+
|
|
2059
|
+
**Phase 1: Requirements Gathering**
|
|
2060
|
+
- Interactive selection of task type (Feature, Bug Fix, Refactoring, etc.)
|
|
2061
|
+
- Scope clarification
|
|
2062
|
+
- Dependencies identification
|
|
2063
|
+
- Success criteria definition
|
|
2064
|
+
- User selects progress level (Minimal/Moderate/Detailed/Quiet)
|
|
2065
|
+
|
|
2066
|
+
**Phase 2: Planning**
|
|
2067
|
+
- Delegate to @planner agent
|
|
2068
|
+
- Create detailed implementation plan
|
|
2069
|
+
- Recommend relevant skills and tools
|
|
2070
|
+
- Create Beads task for tracking
|
|
2071
|
+
|
|
2072
|
+
**Phase 3: Complexity Check & Auto-Split**
|
|
2073
|
+
- Analyze task complexity
|
|
2074
|
+
- Split into multiple beads if needed:
|
|
2075
|
+
* Time > 30 minutes
|
|
2076
|
+
* >10 files affected
|
|
2077
|
+
* >500 lines to change
|
|
2078
|
+
* Touches >2 sub-systems
|
|
2079
|
+
|
|
2080
|
+
**Phase 4: Execution**
|
|
2081
|
+
- Build dependency graph
|
|
2082
|
+
- Execute tasks in parallel (max 3 concurrent)
|
|
2083
|
+
- Dynamic agent selection (@build \u2192 @review \u2192 @scout \u2192 ...)
|
|
2084
|
+
- Integrate skills (TDD, debugging, etc.)
|
|
2085
|
+
- Smart terminal access (auto-allow/ask/forbid)
|
|
2086
|
+
|
|
2087
|
+
**Phase 5: Enhanced Testing & Validation**
|
|
2088
|
+
- Auto-generate test scripts for new functionality
|
|
2089
|
+
- Run quality gates: typecheck, test, lint, build
|
|
2090
|
+
- Execute sample commands (with user approval)
|
|
2091
|
+
- Validate logs semantically with historical comparison
|
|
2092
|
+
- Retry loop (max 3 attempts) with:
|
|
2093
|
+
* Auto-fix type errors, lint errors
|
|
2094
|
+
* Alternative approaches from @review
|
|
2095
|
+
* User intervention on final failure
|
|
2096
|
+
|
|
2097
|
+
**Phase 6: Multi-Level Verification**
|
|
2098
|
+
- All quality gates passed \u2713
|
|
2099
|
+
- Manual verification confirmation
|
|
2100
|
+
- Deployment approval (if needed)
|
|
2101
|
+
- Rollback confirmation (if verification fails)
|
|
2102
|
+
|
|
2103
|
+
**Phase 7: Completion**
|
|
2104
|
+
- Generate proof of completion
|
|
2105
|
+
* Files changed
|
|
2106
|
+
* Test results
|
|
2107
|
+
* Build output
|
|
2108
|
+
* Deployment status
|
|
2109
|
+
- Update Beads task \u2192 completed
|
|
2110
|
+
- Store proof in bead notes
|
|
2111
|
+
- Collect beta feedback
|
|
2112
|
+
|
|
2113
|
+
## Quality Gates (Must ALL Pass)
|
|
2114
|
+
- \`npm run typecheck\` - No type errors
|
|
2115
|
+
- \`npm run test\` - All tests pass
|
|
2116
|
+
- \`npm run lint\` - No linting errors
|
|
2117
|
+
- \`npm run build\` - Build succeeds
|
|
2118
|
+
|
|
2119
|
+
## Success Criteria
|
|
2120
|
+
- All tests passing
|
|
2121
|
+
- No regressions
|
|
2122
|
+
- Manual verification
|
|
2123
|
+
- Deployment complete (if applicable)
|
|
2124
|
+
- Beads task completed with proof
|
|
2125
|
+
|
|
2126
|
+
## Error Handling
|
|
2127
|
+
- **Level 1**: Auto-fix (type errors, lint --fix)
|
|
2128
|
+
- **Level 2**: Alternative approach (@review delegation)
|
|
2129
|
+
- **Level 3**: User intervention + follow-up bead creation
|
|
2130
|
+
|
|
2131
|
+
## Tips
|
|
2132
|
+
\u2713 Use for straightforward tasks first
|
|
2133
|
+
\u2713 Consider /plan + /implement for complex features
|
|
2134
|
+
\u2713 Review changes before final approval`
|
|
2040
2135
|
},
|
|
2041
2136
|
// Quick Actions
|
|
2042
2137
|
{
|
|
@@ -4117,13 +4212,18 @@ var CliDetector = class {
|
|
|
4117
4212
|
try {
|
|
4118
4213
|
const opencodePath = process.platform === "win32" ? process.env.APPDATA || join12(homedir2(), "AppData", "Roaming") : join12(homedir2(), ".config");
|
|
4119
4214
|
const opencodeConfig = join12(opencodePath, "opencode", "opencode.json");
|
|
4120
|
-
|
|
4215
|
+
let installed = existsSync4(opencodeConfig);
|
|
4121
4216
|
let version;
|
|
4122
4217
|
if (installed) {
|
|
4123
4218
|
try {
|
|
4124
4219
|
execSync("opencode --version", { stdio: "ignore" });
|
|
4125
4220
|
version = "installed";
|
|
4126
|
-
} catch {
|
|
4221
|
+
} catch (error) {
|
|
4222
|
+
if (existsSync4(opencodeConfig)) {
|
|
4223
|
+
version = "installed (config exists)";
|
|
4224
|
+
} else {
|
|
4225
|
+
installed = false;
|
|
4226
|
+
}
|
|
4127
4227
|
}
|
|
4128
4228
|
}
|
|
4129
4229
|
return {
|
|
@@ -4216,11 +4316,1058 @@ var CliDetector = class {
|
|
|
4216
4316
|
// src/cli.ts
|
|
4217
4317
|
init_logger();
|
|
4218
4318
|
init_paths();
|
|
4319
|
+
|
|
4320
|
+
// src/core/sync-engine.ts
|
|
4321
|
+
init_esm_shims();
|
|
4322
|
+
import { readFile as readFile10, writeFile as writeFile11, copyFile, mkdir as mkdir9 } from "fs/promises";
|
|
4323
|
+
import { join as join16, dirname as dirname3 } from "path";
|
|
4324
|
+
import inquirer from "inquirer";
|
|
4325
|
+
import chalk2 from "chalk";
|
|
4326
|
+
|
|
4327
|
+
// src/core/version-manager.ts
|
|
4328
|
+
init_esm_shims();
|
|
4329
|
+
init_paths();
|
|
4330
|
+
init_logger();
|
|
4331
|
+
import { readFile as readFile7, readdir as readdir7, writeFile as writeFile8, stat } from "fs/promises";
|
|
4332
|
+
import { join as join13 } from "path";
|
|
4333
|
+
import { createHash } from "crypto";
|
|
4334
|
+
var VersionManager = class {
|
|
4335
|
+
config;
|
|
4336
|
+
constructor(config) {
|
|
4337
|
+
this.config = config;
|
|
4338
|
+
}
|
|
4339
|
+
/**
|
|
4340
|
+
* Get current installed version
|
|
4341
|
+
*/
|
|
4342
|
+
async getCurrentVersion() {
|
|
4343
|
+
const versionPath = join13(paths.globalConfig(), ".version.json");
|
|
4344
|
+
try {
|
|
4345
|
+
const content = await readFile7(versionPath, "utf-8");
|
|
4346
|
+
return JSON.parse(content);
|
|
4347
|
+
} catch {
|
|
4348
|
+
return null;
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
/**
|
|
4352
|
+
* Get package version from package.json
|
|
4353
|
+
*/
|
|
4354
|
+
getPackageVersion() {
|
|
4355
|
+
try {
|
|
4356
|
+
const packageJson = __require(join13(process.cwd(), "package.json"));
|
|
4357
|
+
return packageJson.version || "0.0.0";
|
|
4358
|
+
} catch {
|
|
4359
|
+
return "0.0.0";
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
/**
|
|
4363
|
+
* Check for updates
|
|
4364
|
+
*/
|
|
4365
|
+
async checkForUpdates() {
|
|
4366
|
+
const installed = await this.getCurrentVersion();
|
|
4367
|
+
const packageVersion = this.getPackageVersion();
|
|
4368
|
+
if (!installed) {
|
|
4369
|
+
return {
|
|
4370
|
+
hasUpdate: true,
|
|
4371
|
+
fromVersion: "none",
|
|
4372
|
+
toVersion: packageVersion,
|
|
4373
|
+
newSkills: [],
|
|
4374
|
+
modifiedSkills: [],
|
|
4375
|
+
removedSkills: [],
|
|
4376
|
+
conflicts: [],
|
|
4377
|
+
configChanges: ["Initial version tracking"]
|
|
4378
|
+
};
|
|
4379
|
+
}
|
|
4380
|
+
const hasUpdate = installed.installedVersion !== packageVersion;
|
|
4381
|
+
if (!hasUpdate) {
|
|
4382
|
+
return {
|
|
4383
|
+
hasUpdate: false,
|
|
4384
|
+
fromVersion: installed.installedVersion,
|
|
4385
|
+
toVersion: packageVersion,
|
|
4386
|
+
newSkills: [],
|
|
4387
|
+
modifiedSkills: [],
|
|
4388
|
+
removedSkills: [],
|
|
4389
|
+
conflicts: [],
|
|
4390
|
+
configChanges: []
|
|
4391
|
+
};
|
|
4392
|
+
}
|
|
4393
|
+
const changes = await this.detectChanges();
|
|
4394
|
+
return {
|
|
4395
|
+
hasUpdate: true,
|
|
4396
|
+
fromVersion: installed.installedVersion,
|
|
4397
|
+
toVersion: packageVersion,
|
|
4398
|
+
...changes
|
|
4399
|
+
};
|
|
4400
|
+
}
|
|
4401
|
+
/**
|
|
4402
|
+
* Detect changes between versions
|
|
4403
|
+
*/
|
|
4404
|
+
async detectChanges() {
|
|
4405
|
+
const globalSkillsPath = paths.skills(paths.globalConfig());
|
|
4406
|
+
const projectSkillsPath = paths.skills(this.config.configPath);
|
|
4407
|
+
const sourceSkills = await this.loadSkillHashes(globalSkillsPath);
|
|
4408
|
+
const userSkills = await this.loadSkillHashes(projectSkillsPath);
|
|
4409
|
+
const newSkills = [];
|
|
4410
|
+
const modifiedSkills = [];
|
|
4411
|
+
const removedSkills = [];
|
|
4412
|
+
const conflicts = [];
|
|
4413
|
+
const installedSkills = /* @__PURE__ */ new Map();
|
|
4414
|
+
const installedPath = join13(paths.globalConfig(), ".installed-skills.json");
|
|
4415
|
+
try {
|
|
4416
|
+
const installedData = await readFile7(installedPath, "utf-8");
|
|
4417
|
+
const installedList = JSON.parse(installedData);
|
|
4418
|
+
installedList.forEach((skill) => {
|
|
4419
|
+
installedSkills.set(skill.name, skill);
|
|
4420
|
+
});
|
|
4421
|
+
} catch {
|
|
4422
|
+
}
|
|
4423
|
+
for (const sourceSkill of sourceSkills) {
|
|
4424
|
+
const installed = installedSkills.get(sourceSkill.name);
|
|
4425
|
+
const user = userSkills.find((s) => s.name === sourceSkill.name);
|
|
4426
|
+
if (!installed) {
|
|
4427
|
+
newSkills.push(sourceSkill);
|
|
4428
|
+
} else if (installed.hash !== sourceSkill.hash) {
|
|
4429
|
+
modifiedSkills.push(sourceSkill);
|
|
4430
|
+
if (user && user.hash !== installed.hash) {
|
|
4431
|
+
conflicts.push({
|
|
4432
|
+
skillName: sourceSkill.name,
|
|
4433
|
+
userHash: user.hash,
|
|
4434
|
+
sourceHash: sourceSkill.hash,
|
|
4435
|
+
installedHash: installed.hash,
|
|
4436
|
+
userModified: user.hash !== installed.hash,
|
|
4437
|
+
sourceModified: sourceSkill.hash !== installed.hash
|
|
4438
|
+
});
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
for (const [name, installedSkill] of installedSkills.entries()) {
|
|
4443
|
+
const existsInSource = sourceSkills.find((s) => s.name === name);
|
|
4444
|
+
if (!existsInSource) {
|
|
4445
|
+
removedSkills.push(installedSkill);
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4448
|
+
return {
|
|
4449
|
+
newSkills,
|
|
4450
|
+
modifiedSkills,
|
|
4451
|
+
removedSkills,
|
|
4452
|
+
conflicts,
|
|
4453
|
+
configChanges: []
|
|
4454
|
+
// Will be detected separately
|
|
4455
|
+
};
|
|
4456
|
+
}
|
|
4457
|
+
/**
|
|
4458
|
+
* Load skill hashes from directory
|
|
4459
|
+
*/
|
|
4460
|
+
async loadSkillHashes(skillsPath) {
|
|
4461
|
+
const hashes = [];
|
|
4462
|
+
try {
|
|
4463
|
+
const loadFromDir = async (dir) => {
|
|
4464
|
+
const files = await readdir7(dir);
|
|
4465
|
+
for (const file of files) {
|
|
4466
|
+
const filePath = join13(dir, file);
|
|
4467
|
+
const stats = await stat(filePath);
|
|
4468
|
+
if (stats.isDirectory()) {
|
|
4469
|
+
await loadFromDir(filePath);
|
|
4470
|
+
} else if (file.endsWith(".md")) {
|
|
4471
|
+
const hash = await this.calculateSkillHash(filePath);
|
|
4472
|
+
hashes.push({
|
|
4473
|
+
path: filePath,
|
|
4474
|
+
name: file.replace(".md", ""),
|
|
4475
|
+
hash,
|
|
4476
|
+
category: this.extractCategory(dir, skillsPath)
|
|
4477
|
+
});
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4480
|
+
};
|
|
4481
|
+
await loadFromDir(skillsPath);
|
|
4482
|
+
} catch (error) {
|
|
4483
|
+
logger.debug(`Could not load skills from ${skillsPath}:`, error);
|
|
4484
|
+
}
|
|
4485
|
+
return hashes;
|
|
4486
|
+
}
|
|
4487
|
+
/**
|
|
4488
|
+
* Calculate hash for a skill file
|
|
4489
|
+
*/
|
|
4490
|
+
async calculateSkillHash(filePath) {
|
|
4491
|
+
try {
|
|
4492
|
+
const content = await readFile7(filePath, "utf-8");
|
|
4493
|
+
return createHash("sha256").update(content).digest("hex");
|
|
4494
|
+
} catch {
|
|
4495
|
+
return "";
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
/**
|
|
4499
|
+
* Extract category from path
|
|
4500
|
+
*/
|
|
4501
|
+
extractCategory(filePath, basePath) {
|
|
4502
|
+
const relative = filePath.replace(basePath + "/", "");
|
|
4503
|
+
const parts = relative.split("/");
|
|
4504
|
+
if (parts.length > 1) {
|
|
4505
|
+
return parts[0];
|
|
4506
|
+
}
|
|
4507
|
+
return "uncategorized";
|
|
4508
|
+
}
|
|
4509
|
+
/**
|
|
4510
|
+
* Save installed skills info
|
|
4511
|
+
*/
|
|
4512
|
+
async saveInstalledSkills(skills) {
|
|
4513
|
+
const installedPath = join13(paths.globalConfig(), ".installed-skills.json");
|
|
4514
|
+
try {
|
|
4515
|
+
await writeFile8(installedPath, JSON.stringify(skills, null, 2));
|
|
4516
|
+
} catch (error) {
|
|
4517
|
+
logger.error("Failed to save installed skills info:", error);
|
|
4518
|
+
}
|
|
4519
|
+
}
|
|
4520
|
+
/**
|
|
4521
|
+
* Update version file
|
|
4522
|
+
*/
|
|
4523
|
+
async updateVersion(version, migration) {
|
|
4524
|
+
const current = await this.getCurrentVersion() || {
|
|
4525
|
+
installedVersion: "0.0.0",
|
|
4526
|
+
lastSynced: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4527
|
+
packageVersion: "0.0.0",
|
|
4528
|
+
migrationHistory: []
|
|
4529
|
+
};
|
|
4530
|
+
const updated = {
|
|
4531
|
+
installedVersion: version,
|
|
4532
|
+
lastSynced: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4533
|
+
packageVersion: this.getPackageVersion(),
|
|
4534
|
+
migrationHistory: migration ? [...current.migrationHistory, migration] : current.migrationHistory
|
|
4535
|
+
};
|
|
4536
|
+
const versionPath = join13(paths.globalConfig(), ".version.json");
|
|
4537
|
+
await writeFile8(versionPath, JSON.stringify(updated, null, 2));
|
|
4538
|
+
}
|
|
4539
|
+
/**
|
|
4540
|
+
* Check if migration is needed
|
|
4541
|
+
*/
|
|
4542
|
+
async needsMigration() {
|
|
4543
|
+
const current = await this.getCurrentVersion();
|
|
4544
|
+
const packageVersion = this.getPackageVersion();
|
|
4545
|
+
return current?.installedVersion !== packageVersion;
|
|
4546
|
+
}
|
|
4547
|
+
};
|
|
4548
|
+
|
|
4549
|
+
// src/core/backup-manager.ts
|
|
4550
|
+
init_esm_shims();
|
|
4551
|
+
init_logger();
|
|
4552
|
+
import { readFile as readFile8, writeFile as writeFile9, readdir as readdir8, stat as stat2, unlink, mkdir as mkdir8 } from "fs/promises";
|
|
4553
|
+
import { join as join14, dirname as dirname2 } from "path";
|
|
4554
|
+
import { createHash as createHash2 } from "crypto";
|
|
4555
|
+
var BackupManager = class {
|
|
4556
|
+
configPath;
|
|
4557
|
+
backupsDir;
|
|
4558
|
+
maxBackups;
|
|
4559
|
+
constructor(configPath, maxBackups = 5) {
|
|
4560
|
+
this.configPath = configPath;
|
|
4561
|
+
this.backupsDir = join14(configPath, ".backups");
|
|
4562
|
+
this.maxBackups = maxBackups;
|
|
4563
|
+
}
|
|
4564
|
+
/**
|
|
4565
|
+
* Create backup before update
|
|
4566
|
+
*/
|
|
4567
|
+
async createBackup(fromVersion, toVersion) {
|
|
4568
|
+
try {
|
|
4569
|
+
await mkdir8(this.backupsDir, { recursive: true });
|
|
4570
|
+
const backupId = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
4571
|
+
const backupPath = join14(this.backupsDir, `${backupId}-v${toVersion}`);
|
|
4572
|
+
await mkdir8(backupPath, { recursive: true });
|
|
4573
|
+
logger.info(`Creating backup: ${backupPath}`);
|
|
4574
|
+
const files = [];
|
|
4575
|
+
const backupItems = [
|
|
4576
|
+
"skills/",
|
|
4577
|
+
"aikit.json",
|
|
4578
|
+
"AGENTS.md",
|
|
4579
|
+
"config/"
|
|
4580
|
+
];
|
|
4581
|
+
for (const item of backupItems) {
|
|
4582
|
+
const files2 = await this.backupItem(this.configPath, item, backupPath);
|
|
4583
|
+
files2.push(...files2);
|
|
4584
|
+
}
|
|
4585
|
+
const manifest = {
|
|
4586
|
+
backupId,
|
|
4587
|
+
fromVersion,
|
|
4588
|
+
toVersion,
|
|
4589
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4590
|
+
files,
|
|
4591
|
+
success: true
|
|
4592
|
+
};
|
|
4593
|
+
const manifestPath = join14(backupPath, "backup-manifest.json");
|
|
4594
|
+
await writeFile9(manifestPath, JSON.stringify(manifest, null, 2));
|
|
4595
|
+
await this.cleanupOldBackups();
|
|
4596
|
+
logger.success(`\u2713 Backup created: ${backupId}`);
|
|
4597
|
+
return backupId;
|
|
4598
|
+
} catch (error) {
|
|
4599
|
+
logger.error("Failed to create backup:", error);
|
|
4600
|
+
return null;
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4603
|
+
/**
|
|
4604
|
+
* Backup a file or directory
|
|
4605
|
+
*/
|
|
4606
|
+
async backupItem(sourceDir, item, targetDir) {
|
|
4607
|
+
const sourcePath = join14(sourceDir, item);
|
|
4608
|
+
const targetPath = join14(targetDir, item);
|
|
4609
|
+
const files = [];
|
|
4610
|
+
try {
|
|
4611
|
+
const stats = await stat2(sourcePath);
|
|
4612
|
+
if (stats.isDirectory()) {
|
|
4613
|
+
await mkdir8(targetPath, { recursive: true });
|
|
4614
|
+
const entries = await readdir8(sourcePath);
|
|
4615
|
+
for (const entry of entries) {
|
|
4616
|
+
const entryFiles = await this.backupItem(sourcePath, entry, targetPath);
|
|
4617
|
+
files.push(...entryFiles);
|
|
4618
|
+
}
|
|
4619
|
+
} else if (stats.isFile()) {
|
|
4620
|
+
await mkdir8(dirname2(targetPath), { recursive: true });
|
|
4621
|
+
await this.copyFile(sourcePath, targetPath);
|
|
4622
|
+
const hash = await this.calculateHash(targetPath);
|
|
4623
|
+
files.push({
|
|
4624
|
+
path: item,
|
|
4625
|
+
hash,
|
|
4626
|
+
size: stats.size
|
|
4627
|
+
});
|
|
4628
|
+
}
|
|
4629
|
+
} catch (error) {
|
|
4630
|
+
logger.debug(`Could not backup ${item}:`, error);
|
|
4631
|
+
}
|
|
4632
|
+
return files;
|
|
4633
|
+
}
|
|
4634
|
+
/**
|
|
4635
|
+
* Copy file with hash calculation
|
|
4636
|
+
*/
|
|
4637
|
+
async copyFile(source, target) {
|
|
4638
|
+
const content = await readFile8(source);
|
|
4639
|
+
await writeFile9(target, content);
|
|
4640
|
+
}
|
|
4641
|
+
/**
|
|
4642
|
+
* Calculate file hash
|
|
4643
|
+
*/
|
|
4644
|
+
async calculateHash(filePath) {
|
|
4645
|
+
try {
|
|
4646
|
+
const content = await readFile8(filePath);
|
|
4647
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
4648
|
+
} catch {
|
|
4649
|
+
return "";
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
/**
|
|
4653
|
+
* List available backups
|
|
4654
|
+
*/
|
|
4655
|
+
async listBackups() {
|
|
4656
|
+
try {
|
|
4657
|
+
const entries = await readdir8(this.backupsDir);
|
|
4658
|
+
const backups = [];
|
|
4659
|
+
for (const entry of entries) {
|
|
4660
|
+
const backupPath = join14(this.backupsDir, entry);
|
|
4661
|
+
const manifestPath = join14(backupPath, "backup-manifest.json");
|
|
4662
|
+
try {
|
|
4663
|
+
const manifestContent = await readFile8(manifestPath, "utf-8");
|
|
4664
|
+
const manifest = JSON.parse(manifestContent);
|
|
4665
|
+
const size = await this.calculateBackupSize(backupPath);
|
|
4666
|
+
backups.push({
|
|
4667
|
+
manifest,
|
|
4668
|
+
path: backupPath,
|
|
4669
|
+
size
|
|
4670
|
+
});
|
|
4671
|
+
} catch {
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
backups.sort(
|
|
4675
|
+
(a, b) => new Date(b.manifest.timestamp).getTime() - new Date(a.manifest.timestamp).getTime()
|
|
4676
|
+
);
|
|
4677
|
+
return backups;
|
|
4678
|
+
} catch {
|
|
4679
|
+
return [];
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
/**
|
|
4683
|
+
* Calculate backup directory size
|
|
4684
|
+
*/
|
|
4685
|
+
async calculateBackupSize(backupPath) {
|
|
4686
|
+
let totalSize = 0;
|
|
4687
|
+
try {
|
|
4688
|
+
const calculate = async (dir) => {
|
|
4689
|
+
const entries = await readdir8(dir);
|
|
4690
|
+
for (const entry of entries) {
|
|
4691
|
+
const entryPath = join14(dir, entry);
|
|
4692
|
+
const stats = await stat2(entryPath);
|
|
4693
|
+
if (stats.isDirectory()) {
|
|
4694
|
+
await calculate(entryPath);
|
|
4695
|
+
} else {
|
|
4696
|
+
totalSize += stats.size;
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
};
|
|
4700
|
+
await calculate(backupPath);
|
|
4701
|
+
} catch {
|
|
4702
|
+
}
|
|
4703
|
+
return totalSize;
|
|
4704
|
+
}
|
|
4705
|
+
/**
|
|
4706
|
+
* Restore from backup
|
|
4707
|
+
*/
|
|
4708
|
+
async restoreBackup(backupId) {
|
|
4709
|
+
try {
|
|
4710
|
+
const backups = await this.listBackups();
|
|
4711
|
+
const backup = backups.find((b) => b.manifest.backupId === backupId);
|
|
4712
|
+
if (!backup) {
|
|
4713
|
+
logger.error(`Backup not found: ${backupId}`);
|
|
4714
|
+
return false;
|
|
4715
|
+
}
|
|
4716
|
+
logger.info(`Restoring from backup: ${backupId}`);
|
|
4717
|
+
const isValid = await this.validateBackup(backup);
|
|
4718
|
+
if (!isValid) {
|
|
4719
|
+
logger.error("Backup validation failed");
|
|
4720
|
+
return false;
|
|
4721
|
+
}
|
|
4722
|
+
for (const file of backup.manifest.files) {
|
|
4723
|
+
const sourcePath = join14(backup.path, file.path);
|
|
4724
|
+
const targetPath = join14(this.configPath, file.path);
|
|
4725
|
+
await mkdir8(dirname2(targetPath), { recursive: true });
|
|
4726
|
+
await this.copyFile(sourcePath, targetPath);
|
|
4727
|
+
}
|
|
4728
|
+
logger.success(`\u2713 Backup restored: ${backupId}`);
|
|
4729
|
+
return true;
|
|
4730
|
+
} catch (error) {
|
|
4731
|
+
logger.error("Failed to restore backup:", error);
|
|
4732
|
+
return false;
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
/**
|
|
4736
|
+
* Validate backup integrity
|
|
4737
|
+
*/
|
|
4738
|
+
async validateBackup(backup) {
|
|
4739
|
+
try {
|
|
4740
|
+
const manifestPath = join14(backup.path, "backup-manifest.json");
|
|
4741
|
+
await readFile8(manifestPath, "utf-8");
|
|
4742
|
+
for (const file of backup.manifest.files) {
|
|
4743
|
+
const filePath = join14(backup.path, file.path);
|
|
4744
|
+
await stat2(filePath);
|
|
4745
|
+
const currentHash = await this.calculateHash(filePath);
|
|
4746
|
+
if (currentHash !== file.hash) {
|
|
4747
|
+
logger.warn(`File hash mismatch: ${file.path}`);
|
|
4748
|
+
return false;
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
return true;
|
|
4752
|
+
} catch (error) {
|
|
4753
|
+
logger.debug("Backup validation failed:", error);
|
|
4754
|
+
return false;
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
/**
|
|
4758
|
+
* Delete backup
|
|
4759
|
+
*/
|
|
4760
|
+
async deleteBackup(backupId) {
|
|
4761
|
+
try {
|
|
4762
|
+
const backups = await this.listBackups();
|
|
4763
|
+
const backup = backups.find((b) => b.manifest.backupId === backupId);
|
|
4764
|
+
if (!backup) {
|
|
4765
|
+
return false;
|
|
4766
|
+
}
|
|
4767
|
+
const entries = await readdir8(backup.path);
|
|
4768
|
+
for (const entry of entries) {
|
|
4769
|
+
const entryPath = join14(backup.path, entry);
|
|
4770
|
+
const stats = await stat2(entryPath);
|
|
4771
|
+
if (stats.isDirectory()) {
|
|
4772
|
+
await this.removeDirectory(entryPath);
|
|
4773
|
+
} else {
|
|
4774
|
+
await unlink(entryPath);
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
logger.success(`\u2713 Backup deleted: ${backupId}`);
|
|
4778
|
+
return true;
|
|
4779
|
+
} catch (error) {
|
|
4780
|
+
logger.error("Failed to delete backup:", error);
|
|
4781
|
+
return false;
|
|
4782
|
+
}
|
|
4783
|
+
}
|
|
4784
|
+
/**
|
|
4785
|
+
* Remove directory recursively
|
|
4786
|
+
*/
|
|
4787
|
+
async removeDirectory(dirPath) {
|
|
4788
|
+
const entries = await readdir8(dirPath);
|
|
4789
|
+
for (const entry of entries) {
|
|
4790
|
+
const entryPath = join14(dirPath, entry);
|
|
4791
|
+
const stats = await stat2(entryPath);
|
|
4792
|
+
if (stats.isDirectory()) {
|
|
4793
|
+
await this.removeDirectory(entryPath);
|
|
4794
|
+
} else {
|
|
4795
|
+
await unlink(entryPath);
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
await unlink(dirPath);
|
|
4799
|
+
}
|
|
4800
|
+
/**
|
|
4801
|
+
* Cleanup old backups (keep only maxBackups)
|
|
4802
|
+
*/
|
|
4803
|
+
async cleanupOldBackups() {
|
|
4804
|
+
try {
|
|
4805
|
+
const backups = await this.listBackups();
|
|
4806
|
+
if (backups.length <= this.maxBackups) {
|
|
4807
|
+
return;
|
|
4808
|
+
}
|
|
4809
|
+
const toDelete = backups.slice(this.maxBackups);
|
|
4810
|
+
for (const backup of toDelete) {
|
|
4811
|
+
await this.deleteBackup(backup.manifest.backupId);
|
|
4812
|
+
}
|
|
4813
|
+
logger.info(`Cleaned up ${toDelete.length} old backup(s)`);
|
|
4814
|
+
} catch (error) {
|
|
4815
|
+
logger.error("Failed to cleanup old backups:", error);
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
/**
|
|
4819
|
+
* Format backup size for display
|
|
4820
|
+
*/
|
|
4821
|
+
formatSize(bytes) {
|
|
4822
|
+
const units = ["B", "KB", "MB", "GB"];
|
|
4823
|
+
let size = bytes;
|
|
4824
|
+
let unitIndex = 0;
|
|
4825
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
4826
|
+
size /= 1024;
|
|
4827
|
+
unitIndex++;
|
|
4828
|
+
}
|
|
4829
|
+
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
|
4830
|
+
}
|
|
4831
|
+
};
|
|
4832
|
+
|
|
4833
|
+
// src/core/migration-manager.ts
|
|
4834
|
+
init_esm_shims();
|
|
4835
|
+
init_logger();
|
|
4836
|
+
import { readFile as readFile9, writeFile as writeFile10, readdir as readdir9 } from "fs/promises";
|
|
4837
|
+
import { join as join15 } from "path";
|
|
4838
|
+
var MigrationManager = class {
|
|
4839
|
+
configPath;
|
|
4840
|
+
migrationsDir;
|
|
4841
|
+
constructor(configPath) {
|
|
4842
|
+
this.configPath = configPath;
|
|
4843
|
+
this.migrationsDir = join15(process.cwd(), "src/core/migrations");
|
|
4844
|
+
}
|
|
4845
|
+
/**
|
|
4846
|
+
* Load all available migrations
|
|
4847
|
+
*/
|
|
4848
|
+
async loadMigrations() {
|
|
4849
|
+
const migrations = [];
|
|
4850
|
+
try {
|
|
4851
|
+
const files = await readdir9(this.migrationsDir);
|
|
4852
|
+
for (const file of files) {
|
|
4853
|
+
if (file.endsWith(".js") && file.startsWith("migrate-")) {
|
|
4854
|
+
try {
|
|
4855
|
+
const module = await import(join15(this.migrationsDir, file));
|
|
4856
|
+
const migration = module.default || module.migration;
|
|
4857
|
+
if (migration) {
|
|
4858
|
+
migrations.push(migration);
|
|
4859
|
+
}
|
|
4860
|
+
} catch (error) {
|
|
4861
|
+
logger.warn(`Failed to load migration ${file}:`, error);
|
|
4862
|
+
}
|
|
4863
|
+
}
|
|
4864
|
+
}
|
|
4865
|
+
} catch (error) {
|
|
4866
|
+
logger.debug("Could not load migrations:", error);
|
|
4867
|
+
}
|
|
4868
|
+
return migrations.sort((a, b) => a.version.localeCompare(b.version));
|
|
4869
|
+
}
|
|
4870
|
+
/**
|
|
4871
|
+
* Get applied migrations
|
|
4872
|
+
*/
|
|
4873
|
+
async getAppliedMigrations() {
|
|
4874
|
+
const migrationHistoryPath = join15(this.configPath, ".migration-history.json");
|
|
4875
|
+
try {
|
|
4876
|
+
const content = await readFile9(migrationHistoryPath, "utf-8");
|
|
4877
|
+
const history = JSON.parse(content);
|
|
4878
|
+
return history.filter((m) => m.status === "completed").map((m) => m.to);
|
|
4879
|
+
} catch {
|
|
4880
|
+
return [];
|
|
4881
|
+
}
|
|
4882
|
+
}
|
|
4883
|
+
/**
|
|
4884
|
+
* Run pending migrations
|
|
4885
|
+
*/
|
|
4886
|
+
async runPendingMigrations() {
|
|
4887
|
+
const appliedMigrations = await this.getAppliedMigrations();
|
|
4888
|
+
const allMigrations = await this.loadMigrations();
|
|
4889
|
+
const pendingMigrations = allMigrations.filter(
|
|
4890
|
+
(m) => !appliedMigrations.includes(m.version)
|
|
4891
|
+
);
|
|
4892
|
+
if (pendingMigrations.length === 0) {
|
|
4893
|
+
logger.info("No pending migrations");
|
|
4894
|
+
return { success: true, applied: [], failed: [] };
|
|
4895
|
+
}
|
|
4896
|
+
logger.info(`Running ${pendingMigrations.length} pending migration(s)...`);
|
|
4897
|
+
const applied = [];
|
|
4898
|
+
const failed = [];
|
|
4899
|
+
const migrationHistory = [];
|
|
4900
|
+
for (const migration of pendingMigrations) {
|
|
4901
|
+
try {
|
|
4902
|
+
logger.info(`Running migration: ${migration.version}`);
|
|
4903
|
+
logger.info(` ${migration.description}`);
|
|
4904
|
+
await migration.up();
|
|
4905
|
+
applied.push(migration.version);
|
|
4906
|
+
migrationHistory.push({
|
|
4907
|
+
from: "previous",
|
|
4908
|
+
to: migration.version,
|
|
4909
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4910
|
+
status: "completed"
|
|
4911
|
+
});
|
|
4912
|
+
logger.success(`\u2713 Migration completed: ${migration.version}`);
|
|
4913
|
+
} catch (error) {
|
|
4914
|
+
logger.error(`\u2717 Migration failed: ${migration.version}`, error);
|
|
4915
|
+
failed.push(migration.version);
|
|
4916
|
+
migrationHistory.push({
|
|
4917
|
+
from: "previous",
|
|
4918
|
+
to: migration.version,
|
|
4919
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4920
|
+
status: "failed"
|
|
4921
|
+
});
|
|
4922
|
+
break;
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
await this.updateMigrationHistory(migrationHistory);
|
|
4926
|
+
return {
|
|
4927
|
+
success: failed.length === 0,
|
|
4928
|
+
applied,
|
|
4929
|
+
failed
|
|
4930
|
+
};
|
|
4931
|
+
}
|
|
4932
|
+
/**
|
|
4933
|
+
* Rollback migration
|
|
4934
|
+
*/
|
|
4935
|
+
async rollbackMigration(version) {
|
|
4936
|
+
try {
|
|
4937
|
+
const applied = await this.getAppliedMigrations();
|
|
4938
|
+
if (!applied.includes(version)) {
|
|
4939
|
+
logger.error(`Migration not found in applied list: ${version}`);
|
|
4940
|
+
return false;
|
|
4941
|
+
}
|
|
4942
|
+
const allMigrations = await this.loadMigrations();
|
|
4943
|
+
const migration = allMigrations.find((m) => m.version === version);
|
|
4944
|
+
if (!migration) {
|
|
4945
|
+
logger.error(`Migration file not found: ${version}`);
|
|
4946
|
+
return false;
|
|
4947
|
+
}
|
|
4948
|
+
logger.info(`Rolling back migration: ${version}`);
|
|
4949
|
+
await migration.down();
|
|
4950
|
+
await this.updateMigrationHistoryStatus(version, "rolled-back");
|
|
4951
|
+
logger.success(`\u2713 Migration rolled back: ${version}`);
|
|
4952
|
+
return true;
|
|
4953
|
+
} catch (error) {
|
|
4954
|
+
logger.error("Failed to rollback migration:", error);
|
|
4955
|
+
return false;
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
/**
|
|
4959
|
+
* Update migration history
|
|
4960
|
+
*/
|
|
4961
|
+
async updateMigrationHistory(entries) {
|
|
4962
|
+
const historyPath = join15(this.configPath, ".migration-history.json");
|
|
4963
|
+
try {
|
|
4964
|
+
let history = [];
|
|
4965
|
+
try {
|
|
4966
|
+
const content = await readFile9(historyPath, "utf-8");
|
|
4967
|
+
history = JSON.parse(content);
|
|
4968
|
+
} catch {
|
|
4969
|
+
}
|
|
4970
|
+
history.push(...entries);
|
|
4971
|
+
await writeFile10(historyPath, JSON.stringify(history, null, 2));
|
|
4972
|
+
} catch (error) {
|
|
4973
|
+
logger.error("Failed to update migration history:", error);
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
/**
|
|
4977
|
+
* Update migration history status
|
|
4978
|
+
*/
|
|
4979
|
+
async updateMigrationHistoryStatus(version, status) {
|
|
4980
|
+
const historyPath = join15(this.configPath, ".migration-history.json");
|
|
4981
|
+
try {
|
|
4982
|
+
const content = await readFile9(historyPath, "utf-8");
|
|
4983
|
+
const history = JSON.parse(content);
|
|
4984
|
+
const updated = history.map(
|
|
4985
|
+
(m) => m.to === version ? { ...m, status } : m
|
|
4986
|
+
);
|
|
4987
|
+
await writeFile10(historyPath, JSON.stringify(updated, null, 2));
|
|
4988
|
+
} catch (error) {
|
|
4989
|
+
logger.error("Failed to update migration history status:", error);
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
/**
|
|
4993
|
+
* Get migration history
|
|
4994
|
+
*/
|
|
4995
|
+
async getMigrationHistory() {
|
|
4996
|
+
const historyPath = join15(this.configPath, ".migration-history.json");
|
|
4997
|
+
try {
|
|
4998
|
+
const content = await readFile9(historyPath, "utf-8");
|
|
4999
|
+
return JSON.parse(content);
|
|
5000
|
+
} catch {
|
|
5001
|
+
return [];
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
/**
|
|
5005
|
+
* Check if migration is needed for version
|
|
5006
|
+
*/
|
|
5007
|
+
async needsMigration(_fromVersion) {
|
|
5008
|
+
const applied = await this.getAppliedMigrations();
|
|
5009
|
+
const allMigrations = await this.loadMigrations();
|
|
5010
|
+
const pendingMigrations = allMigrations.filter(
|
|
5011
|
+
(m) => !applied.includes(m.version)
|
|
5012
|
+
);
|
|
5013
|
+
return pendingMigrations.length > 0;
|
|
5014
|
+
}
|
|
5015
|
+
};
|
|
5016
|
+
|
|
5017
|
+
// src/core/sync-engine.ts
|
|
5018
|
+
init_logger();
|
|
5019
|
+
init_paths();
|
|
5020
|
+
var SyncEngine = class {
|
|
5021
|
+
versionManager;
|
|
5022
|
+
backupManager;
|
|
5023
|
+
migrationManager;
|
|
5024
|
+
constructor(config) {
|
|
5025
|
+
this.versionManager = new VersionManager(config);
|
|
5026
|
+
this.backupManager = new BackupManager(config.configPath);
|
|
5027
|
+
this.migrationManager = new MigrationManager(config.configPath);
|
|
5028
|
+
}
|
|
5029
|
+
/**
|
|
5030
|
+
* Check for updates without applying
|
|
5031
|
+
*/
|
|
5032
|
+
async checkForUpdates() {
|
|
5033
|
+
try {
|
|
5034
|
+
const changes = await this.versionManager.checkForUpdates();
|
|
5035
|
+
if (changes.hasUpdate) {
|
|
5036
|
+
this.displayUpdateInfo(changes);
|
|
5037
|
+
} else {
|
|
5038
|
+
console.log(chalk2.green("\u2713 Your AIKit is up to date"));
|
|
5039
|
+
console.log(` Installed: ${changes.fromVersion}`);
|
|
5040
|
+
console.log(` Latest: ${changes.toVersion}`);
|
|
5041
|
+
}
|
|
5042
|
+
return changes;
|
|
5043
|
+
} catch (error) {
|
|
5044
|
+
logger.error("Failed to check for updates:", error);
|
|
5045
|
+
return null;
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
/**
|
|
5049
|
+
* Preview changes without applying
|
|
5050
|
+
*/
|
|
5051
|
+
async previewUpdate() {
|
|
5052
|
+
try {
|
|
5053
|
+
console.log(chalk2.bold("\n\u{1F50D} Previewing update...\n"));
|
|
5054
|
+
const changes = await this.versionManager.checkForUpdates();
|
|
5055
|
+
if (!changes.hasUpdate) {
|
|
5056
|
+
console.log(chalk2.green("\u2713 No updates available"));
|
|
5057
|
+
return false;
|
|
5058
|
+
}
|
|
5059
|
+
await this.displayChanges(changes);
|
|
5060
|
+
console.log(chalk2.yellow("\n\u26A0\uFE0F This is a preview - no changes will be made."));
|
|
5061
|
+
console.log(chalk2.gray("Use `aikit sync apply` to apply these changes."));
|
|
5062
|
+
return true;
|
|
5063
|
+
} catch (error) {
|
|
5064
|
+
logger.error("Failed to preview update:", error);
|
|
5065
|
+
return false;
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5068
|
+
/**
|
|
5069
|
+
* Apply update
|
|
5070
|
+
*/
|
|
5071
|
+
async applyUpdate(options = {}) {
|
|
5072
|
+
try {
|
|
5073
|
+
const changes = await this.versionManager.checkForUpdates();
|
|
5074
|
+
if (!changes.hasUpdate) {
|
|
5075
|
+
console.log(chalk2.green("\u2713 Already up to date"));
|
|
5076
|
+
return {
|
|
5077
|
+
success: true,
|
|
5078
|
+
newSkills: [],
|
|
5079
|
+
updatedSkills: [],
|
|
5080
|
+
removedSkills: [],
|
|
5081
|
+
migrationsRun: []
|
|
5082
|
+
};
|
|
5083
|
+
}
|
|
5084
|
+
await this.displayChanges(changes);
|
|
5085
|
+
if (!options.force) {
|
|
5086
|
+
const { confirmed } = await inquirer.prompt([{
|
|
5087
|
+
type: "confirm",
|
|
5088
|
+
name: "confirmed",
|
|
5089
|
+
message: "Continue with update?",
|
|
5090
|
+
default: false
|
|
5091
|
+
}]);
|
|
5092
|
+
if (!confirmed) {
|
|
5093
|
+
console.log(chalk2.yellow("Update cancelled"));
|
|
5094
|
+
return {
|
|
5095
|
+
success: false,
|
|
5096
|
+
newSkills: [],
|
|
5097
|
+
updatedSkills: [],
|
|
5098
|
+
removedSkills: [],
|
|
5099
|
+
migrationsRun: []
|
|
5100
|
+
};
|
|
5101
|
+
}
|
|
5102
|
+
}
|
|
5103
|
+
let backupId = void 0;
|
|
5104
|
+
if (!options.dryRun && options.backup !== false) {
|
|
5105
|
+
console.log(chalk2.bold("\n\u{1F4E6} Creating backup..."));
|
|
5106
|
+
const backupResult = await this.backupManager.createBackup(
|
|
5107
|
+
changes.fromVersion,
|
|
5108
|
+
changes.toVersion
|
|
5109
|
+
);
|
|
5110
|
+
if (!backupResult) {
|
|
5111
|
+
throw new Error("Failed to create backup");
|
|
5112
|
+
}
|
|
5113
|
+
backupId = backupResult;
|
|
5114
|
+
}
|
|
5115
|
+
for (const conflict of changes.conflicts) {
|
|
5116
|
+
await this.resolveConflict(conflict);
|
|
5117
|
+
}
|
|
5118
|
+
console.log(chalk2.bold("\n\u{1F504} Running migrations..."));
|
|
5119
|
+
const migrationResult = await this.migrationManager.runPendingMigrations();
|
|
5120
|
+
if (!migrationResult.success) {
|
|
5121
|
+
throw new Error(`Migration failed: ${migrationResult.failed.join(", ")}`);
|
|
5122
|
+
}
|
|
5123
|
+
console.log(chalk2.bold("\n\u{1F4DD} Updating skills..."));
|
|
5124
|
+
const updateResult = await this.updateSkills(changes, options);
|
|
5125
|
+
await this.versionManager.updateVersion(changes.toVersion);
|
|
5126
|
+
if (backupId) {
|
|
5127
|
+
const allSkills = await this.versionManager.loadSkillHashes(paths.skills(paths.globalConfig()));
|
|
5128
|
+
await this.versionManager.saveInstalledSkills(allSkills);
|
|
5129
|
+
}
|
|
5130
|
+
console.log(chalk2.green("\n\u2705 Update complete!"));
|
|
5131
|
+
this.displaySummary({
|
|
5132
|
+
success: true,
|
|
5133
|
+
backupId,
|
|
5134
|
+
...updateResult,
|
|
5135
|
+
migrationsRun: migrationResult.applied
|
|
5136
|
+
});
|
|
5137
|
+
return {
|
|
5138
|
+
success: true,
|
|
5139
|
+
backupId,
|
|
5140
|
+
...updateResult,
|
|
5141
|
+
migrationsRun: migrationResult.applied
|
|
5142
|
+
};
|
|
5143
|
+
} catch (error) {
|
|
5144
|
+
logger.error("Update failed:", error);
|
|
5145
|
+
console.log(chalk2.red("\n\u274C Update failed"));
|
|
5146
|
+
return {
|
|
5147
|
+
success: false,
|
|
5148
|
+
newSkills: [],
|
|
5149
|
+
updatedSkills: [],
|
|
5150
|
+
removedSkills: [],
|
|
5151
|
+
migrationsRun: []
|
|
5152
|
+
};
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
5155
|
+
/**
|
|
5156
|
+
* Rollback to previous backup
|
|
5157
|
+
*/
|
|
5158
|
+
async rollback(backupId) {
|
|
5159
|
+
try {
|
|
5160
|
+
console.log(chalk2.bold("\n\u{1F504} Rollback...\n"));
|
|
5161
|
+
if (!backupId) {
|
|
5162
|
+
const backups = await this.backupManager.listBackups();
|
|
5163
|
+
if (backups.length === 0) {
|
|
5164
|
+
console.log(chalk2.yellow("No backups available"));
|
|
5165
|
+
return false;
|
|
5166
|
+
}
|
|
5167
|
+
const { selectedBackup } = await inquirer.prompt([{
|
|
5168
|
+
type: "list",
|
|
5169
|
+
name: "selectedBackup",
|
|
5170
|
+
message: "Select backup to restore:",
|
|
5171
|
+
choices: backups.map((b) => ({
|
|
5172
|
+
name: `${b.manifest.backupId} (${b.manifest.fromVersion} \u2192 ${b.manifest.toVersion})`,
|
|
5173
|
+
value: b.manifest.backupId
|
|
5174
|
+
}))
|
|
5175
|
+
}]);
|
|
5176
|
+
backupId = selectedBackup;
|
|
5177
|
+
}
|
|
5178
|
+
if (!backupId) {
|
|
5179
|
+
console.log(chalk2.yellow("No backup ID provided"));
|
|
5180
|
+
return false;
|
|
5181
|
+
}
|
|
5182
|
+
const success = await this.backupManager.restoreBackup(backupId);
|
|
5183
|
+
if (success) {
|
|
5184
|
+
console.log(chalk2.green("\u2713 Rollback complete"));
|
|
5185
|
+
return true;
|
|
5186
|
+
}
|
|
5187
|
+
return false;
|
|
5188
|
+
} catch (error) {
|
|
5189
|
+
logger.error("Rollback failed:", error);
|
|
5190
|
+
return false;
|
|
5191
|
+
}
|
|
5192
|
+
}
|
|
5193
|
+
/**
|
|
5194
|
+
* Display update information
|
|
5195
|
+
*/
|
|
5196
|
+
displayUpdateInfo(changes) {
|
|
5197
|
+
console.log(chalk2.bold("\n\u{1F4E2} New version available!\n"));
|
|
5198
|
+
console.log(` ${chalk2.cyan("Current:")} ${changes.fromVersion}`);
|
|
5199
|
+
console.log(` ${chalk2.cyan("Latest:")} ${changes.toVersion}
|
|
5200
|
+
`);
|
|
5201
|
+
}
|
|
5202
|
+
/**
|
|
5203
|
+
* Display changes summary
|
|
5204
|
+
*/
|
|
5205
|
+
async displayChanges(changes) {
|
|
5206
|
+
console.log(chalk2.bold("\u{1F4CA} Changes detected:\n"));
|
|
5207
|
+
if (changes.newSkills.length > 0) {
|
|
5208
|
+
console.log(chalk2.green(" New Skills:"));
|
|
5209
|
+
changes.newSkills.forEach((skill) => {
|
|
5210
|
+
console.log(` + ${skill.name} (${skill.category})`);
|
|
5211
|
+
});
|
|
5212
|
+
}
|
|
5213
|
+
if (changes.modifiedSkills.length > 0) {
|
|
5214
|
+
console.log(chalk2.yellow(" Updated Skills:"));
|
|
5215
|
+
changes.modifiedSkills.forEach((skill) => {
|
|
5216
|
+
console.log(` ~ ${skill.name}`);
|
|
5217
|
+
});
|
|
5218
|
+
}
|
|
5219
|
+
if (changes.removedSkills.length > 0) {
|
|
5220
|
+
console.log(chalk2.red(" Removed Skills:"));
|
|
5221
|
+
changes.removedSkills.forEach((skill) => {
|
|
5222
|
+
console.log(` - ${skill.name}`);
|
|
5223
|
+
});
|
|
5224
|
+
}
|
|
5225
|
+
if (changes.conflicts.length > 0) {
|
|
5226
|
+
console.log(chalk2.bold.red(" \u26A0\uFE0F Conflicts:"));
|
|
5227
|
+
changes.conflicts.forEach((conflict) => {
|
|
5228
|
+
console.log(` ! ${conflict.skillName} (user modified)`);
|
|
5229
|
+
});
|
|
5230
|
+
}
|
|
5231
|
+
}
|
|
5232
|
+
/**
|
|
5233
|
+
* Resolve a conflict
|
|
5234
|
+
*/
|
|
5235
|
+
async resolveConflict(conflict) {
|
|
5236
|
+
console.log(chalk2.bold.red(`
|
|
5237
|
+
\u26A0\uFE0F Conflict detected: ${conflict.skillName}
|
|
5238
|
+
`));
|
|
5239
|
+
console.log(chalk2.yellow("Your version differs from official version."));
|
|
5240
|
+
const { action } = await inquirer.prompt([{
|
|
5241
|
+
type: "list",
|
|
5242
|
+
name: "action",
|
|
5243
|
+
message: "Choose action:",
|
|
5244
|
+
choices: [
|
|
5245
|
+
{
|
|
5246
|
+
name: "Keep your version (will be renamed to -custom.md)",
|
|
5247
|
+
value: "preserve"
|
|
5248
|
+
},
|
|
5249
|
+
{
|
|
5250
|
+
name: "Overwrite with official version",
|
|
5251
|
+
value: "overwrite"
|
|
5252
|
+
},
|
|
5253
|
+
{
|
|
5254
|
+
name: "Skip this skill",
|
|
5255
|
+
value: "skip"
|
|
5256
|
+
}
|
|
5257
|
+
]
|
|
5258
|
+
}]);
|
|
5259
|
+
if (action === "skip") {
|
|
5260
|
+
return;
|
|
5261
|
+
}
|
|
5262
|
+
if (action === "overwrite") {
|
|
5263
|
+
return;
|
|
5264
|
+
}
|
|
5265
|
+
console.log(chalk2.yellow(" Your version will be preserved as -custom.md"));
|
|
5266
|
+
}
|
|
5267
|
+
/**
|
|
5268
|
+
* Update skills based on changes
|
|
5269
|
+
*/
|
|
5270
|
+
async updateSkills(changes, options) {
|
|
5271
|
+
const globalSkillsPath = paths.skills(paths.globalConfig());
|
|
5272
|
+
const projectSkillsPath = paths.skills(this.versionManager["config"].configPath);
|
|
5273
|
+
const newSkills = [];
|
|
5274
|
+
const updatedSkills = [];
|
|
5275
|
+
const removedSkills = [];
|
|
5276
|
+
for (const skill of changes.newSkills) {
|
|
5277
|
+
if (!options.dryRun) {
|
|
5278
|
+
await this.installSkill(globalSkillsPath, skill, projectSkillsPath);
|
|
5279
|
+
}
|
|
5280
|
+
newSkills.push(skill.name);
|
|
5281
|
+
console.log(chalk2.green(` + ${skill.name}`));
|
|
5282
|
+
}
|
|
5283
|
+
for (const skill of changes.modifiedSkills) {
|
|
5284
|
+
if (!options.dryRun) {
|
|
5285
|
+
await this.installSkill(globalSkillsPath, skill, projectSkillsPath);
|
|
5286
|
+
}
|
|
5287
|
+
updatedSkills.push(skill.name);
|
|
5288
|
+
console.log(chalk2.yellow(` ~ ${skill.name}`));
|
|
5289
|
+
}
|
|
5290
|
+
for (const skill of changes.removedSkills) {
|
|
5291
|
+
if (!options.dryRun) {
|
|
5292
|
+
await this.archiveSkill(projectSkillsPath, skill);
|
|
5293
|
+
}
|
|
5294
|
+
removedSkills.push(skill.name);
|
|
5295
|
+
console.log(chalk2.red(` - ${skill.name} (archived)`));
|
|
5296
|
+
}
|
|
5297
|
+
return {
|
|
5298
|
+
newSkills,
|
|
5299
|
+
updatedSkills,
|
|
5300
|
+
removedSkills
|
|
5301
|
+
};
|
|
5302
|
+
}
|
|
5303
|
+
/**
|
|
5304
|
+
* Install a skill
|
|
5305
|
+
*/
|
|
5306
|
+
async installSkill(sourceDir, skill, targetDir) {
|
|
5307
|
+
const sourcePath = join16(sourceDir, skill.category, `${skill.name}.md`);
|
|
5308
|
+
const targetPath = join16(targetDir, skill.category, `${skill.name}.md`);
|
|
5309
|
+
await mkdir9(dirname3(targetPath), { recursive: true });
|
|
5310
|
+
await copyFile(sourcePath, targetPath);
|
|
5311
|
+
}
|
|
5312
|
+
/**
|
|
5313
|
+
* Archive a removed skill
|
|
5314
|
+
*/
|
|
5315
|
+
async archiveSkill(targetDir, skill) {
|
|
5316
|
+
const sourcePath = join16(targetDir, skill.category, `${skill.name}.md`);
|
|
5317
|
+
const targetPath = join16(targetDir, skill.category, `${skill.name}-deprecated.md`);
|
|
5318
|
+
try {
|
|
5319
|
+
const content = await readFile10(sourcePath, "utf-8");
|
|
5320
|
+
const deprecatedNotice = `---
|
|
5321
|
+
\u26A0\uFE0F DEPRECATED: This skill has been removed
|
|
5322
|
+
|
|
5323
|
+
Deprecation date: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
5324
|
+
Reason: Check release notes for replacement
|
|
5325
|
+
---
|
|
5326
|
+
|
|
5327
|
+
${content}`;
|
|
5328
|
+
await mkdir9(dirname3(targetPath), { recursive: true });
|
|
5329
|
+
await writeFile11(targetPath, deprecatedNotice);
|
|
5330
|
+
} catch (error) {
|
|
5331
|
+
if (error.code === "ENOENT") {
|
|
5332
|
+
console.log(chalk2.yellow(` - ${skill.name} (not found, skipping)`));
|
|
5333
|
+
} else {
|
|
5334
|
+
throw error;
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5337
|
+
}
|
|
5338
|
+
/**
|
|
5339
|
+
* Display sync summary
|
|
5340
|
+
*/
|
|
5341
|
+
displaySummary(result) {
|
|
5342
|
+
console.log(chalk2.bold("\n\u{1F4CB} Summary:\n"));
|
|
5343
|
+
console.log(` Updated from: ${chalk2.cyan(result.backupId || "N/A")}`);
|
|
5344
|
+
console.log(` Updated to: ${chalk2.cyan("current")}`);
|
|
5345
|
+
console.log();
|
|
5346
|
+
if (result.newSkills.length > 0) {
|
|
5347
|
+
console.log(chalk2.green(` ${result.newSkills.length} new skills installed`));
|
|
5348
|
+
}
|
|
5349
|
+
if (result.updatedSkills.length > 0) {
|
|
5350
|
+
console.log(chalk2.yellow(` ${result.updatedSkills.length} skills updated`));
|
|
5351
|
+
}
|
|
5352
|
+
if (result.removedSkills.length > 0) {
|
|
5353
|
+
console.log(chalk2.red(` ${result.removedSkills.length} skills archived`));
|
|
5354
|
+
}
|
|
5355
|
+
if (result.migrationsRun.length > 0) {
|
|
5356
|
+
console.log(chalk2.blue(` ${result.migrationsRun.length} migrations run`));
|
|
5357
|
+
}
|
|
5358
|
+
if (result.backupId) {
|
|
5359
|
+
console.log(chalk2.gray(`
|
|
5360
|
+
Rollback available: aikit sync rollback ${result.backupId}`));
|
|
5361
|
+
}
|
|
5362
|
+
}
|
|
5363
|
+
};
|
|
5364
|
+
|
|
5365
|
+
// src/cli.ts
|
|
4219
5366
|
var program = new Command();
|
|
4220
5367
|
program.name("aikit").description("Open-source AI coding agent toolkit for OpenCode").version(getVersion());
|
|
4221
5368
|
program.command("init").description("Initialize AIKit configuration").option("-g, --global", "Initialize global configuration").option("-p, --project", "Initialize project-level configuration").action(async (options) => {
|
|
4222
5369
|
const configDir = options.global ? paths.globalConfig() : paths.projectConfig();
|
|
4223
|
-
console.log(
|
|
5370
|
+
console.log(chalk3.bold("\n\u{1F680} AIKit Setup\n"));
|
|
4224
5371
|
logger.info(`Initializing AIKit in ${configDir}...`);
|
|
4225
5372
|
try {
|
|
4226
5373
|
await initializeConfig(configDir, options.global);
|
|
@@ -4232,17 +5379,17 @@ program.command("init").description("Initialize AIKit configuration").option("-g
|
|
|
4232
5379
|
if (result.count > 0) {
|
|
4233
5380
|
logger.success(`\u2713 Synced ${result.count} skills`);
|
|
4234
5381
|
}
|
|
4235
|
-
console.log(
|
|
5382
|
+
console.log(chalk3.bold("\n\u{1F50D} Checking CLI tools...\n"));
|
|
4236
5383
|
const cliTools = await CliDetector.checkAll();
|
|
4237
5384
|
const installableTools = CliDetector.filterInstallable(cliTools);
|
|
4238
5385
|
for (const tool of cliTools) {
|
|
4239
|
-
const status = tool.installed ?
|
|
4240
|
-
const version = tool.version ?
|
|
4241
|
-
console.log(` ${status} ${
|
|
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}`);
|
|
4242
5389
|
}
|
|
4243
5390
|
if (installableTools.length > 0) {
|
|
4244
5391
|
console.log();
|
|
4245
|
-
const { action } = await
|
|
5392
|
+
const { action } = await inquirer2.prompt([
|
|
4246
5393
|
{
|
|
4247
5394
|
type: "list",
|
|
4248
5395
|
name: "action",
|
|
@@ -4282,7 +5429,7 @@ program.command("init").description("Initialize AIKit configuration").option("-g
|
|
|
4282
5429
|
console.log();
|
|
4283
5430
|
logger.success("\u2713 CLI tools installed");
|
|
4284
5431
|
} else {
|
|
4285
|
-
const { installTools } = await
|
|
5432
|
+
const { installTools } = await inquirer2.prompt([
|
|
4286
5433
|
{
|
|
4287
5434
|
type: "checkbox",
|
|
4288
5435
|
name: "installTools",
|
|
@@ -4326,16 +5473,16 @@ program.command("init").description("Initialize AIKit configuration").option("-g
|
|
|
4326
5473
|
}
|
|
4327
5474
|
const opencodePath = paths.opencodeConfig();
|
|
4328
5475
|
await installToOpenCode(opencodePath);
|
|
4329
|
-
console.log(
|
|
5476
|
+
console.log(chalk3.bold("\n\u2728 AIKit is ready!\n"));
|
|
4330
5477
|
console.log("Usage in OpenCode:");
|
|
4331
|
-
console.log(
|
|
4332
|
-
console.log(
|
|
4333
|
-
console.log(
|
|
4334
|
-
console.log(
|
|
4335
|
-
console.log(
|
|
4336
|
-
console.log(
|
|
4337
|
-
console.log(
|
|
4338
|
-
console.log("\nPress " +
|
|
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");
|
|
4339
5486
|
}
|
|
4340
5487
|
} catch (error) {
|
|
4341
5488
|
logger.error("Failed to initialize AIKit:", error);
|
|
@@ -4353,6 +5500,32 @@ program.command("install").description("Install AIKit to OpenCode configuration"
|
|
|
4353
5500
|
process.exit(1);
|
|
4354
5501
|
}
|
|
4355
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
|
+
});
|
|
4356
5529
|
var skillsCmd = program.command("skills").description("Manage skills");
|
|
4357
5530
|
skillsCmd.command("list").description("List available skills and tools with their configuration status").action(async () => {
|
|
4358
5531
|
const config = await loadConfig();
|
|
@@ -4361,31 +5534,31 @@ skillsCmd.command("list").description("List available skills and tools with thei
|
|
|
4361
5534
|
const { ToolConfigManager: ToolConfigManager2 } = await Promise.resolve().then(() => (init_tool_config(), tool_config_exports));
|
|
4362
5535
|
const toolConfigManager = new ToolConfigManager2(config);
|
|
4363
5536
|
const tools = await toolConfigManager.listTools();
|
|
4364
|
-
console.log(
|
|
5537
|
+
console.log(chalk3.bold("\n\u{1F4DA} Available Skills:\n"));
|
|
4365
5538
|
for (const skill of skills) {
|
|
4366
|
-
console.log(` ${
|
|
5539
|
+
console.log(` ${chalk3.cyan(skill.name)} - ${skill.description}`);
|
|
4367
5540
|
}
|
|
4368
|
-
console.log(
|
|
5541
|
+
console.log(chalk3.bold("\n\u{1F527} Available Tools:\n"));
|
|
4369
5542
|
for (const tool of tools) {
|
|
4370
5543
|
let statusIcon = " ";
|
|
4371
5544
|
let statusText = "";
|
|
4372
5545
|
if (tool.status === "ready") {
|
|
4373
|
-
statusIcon =
|
|
4374
|
-
statusText =
|
|
5546
|
+
statusIcon = chalk3.green("\u2713");
|
|
5547
|
+
statusText = chalk3.gray("(ready)");
|
|
4375
5548
|
} else if (tool.status === "needs_config") {
|
|
4376
|
-
statusIcon =
|
|
4377
|
-
statusText =
|
|
5549
|
+
statusIcon = chalk3.yellow("\u26A0");
|
|
5550
|
+
statusText = chalk3.yellow("(needs config)");
|
|
4378
5551
|
} else if (tool.status === "error") {
|
|
4379
|
-
statusIcon =
|
|
4380
|
-
statusText =
|
|
5552
|
+
statusIcon = chalk3.red("\u2717");
|
|
5553
|
+
statusText = chalk3.red("(error)");
|
|
4381
5554
|
}
|
|
4382
|
-
console.log(` ${statusIcon} ${
|
|
5555
|
+
console.log(` ${statusIcon} ${chalk3.cyan(tool.name)} - ${tool.description} ${statusText}`);
|
|
4383
5556
|
if (tool.errorMessage) {
|
|
4384
|
-
console.log(` ${
|
|
5557
|
+
console.log(` ${chalk3.red("Error:")} ${tool.errorMessage}`);
|
|
4385
5558
|
}
|
|
4386
5559
|
}
|
|
4387
5560
|
console.log();
|
|
4388
|
-
console.log(
|
|
5561
|
+
console.log(chalk3.gray('Tip: Use "aikit skills <tool-name> config" to configure a tool\n'));
|
|
4389
5562
|
});
|
|
4390
5563
|
skillsCmd.command("show <name>").description("Show skill details").action(async (name) => {
|
|
4391
5564
|
const config = await loadConfig();
|
|
@@ -4395,11 +5568,11 @@ skillsCmd.command("show <name>").description("Show skill details").action(async
|
|
|
4395
5568
|
logger.error(`Skill not found: ${name}`);
|
|
4396
5569
|
process.exit(1);
|
|
4397
5570
|
}
|
|
4398
|
-
console.log(
|
|
5571
|
+
console.log(chalk3.bold(`
|
|
4399
5572
|
\u{1F4D6} Skill: ${skill.name}
|
|
4400
5573
|
`));
|
|
4401
|
-
console.log(
|
|
4402
|
-
console.log(
|
|
5574
|
+
console.log(chalk3.gray(skill.description));
|
|
5575
|
+
console.log(chalk3.bold("\nWorkflow:"));
|
|
4403
5576
|
console.log(skill.content);
|
|
4404
5577
|
});
|
|
4405
5578
|
skillsCmd.command("create <name>").description("Create a new skill").action(async (name) => {
|
|
@@ -4415,11 +5588,11 @@ skillsCmd.command("sync").description("Sync global skills to project").action(as
|
|
|
4415
5588
|
if (result.count === 0) {
|
|
4416
5589
|
logger.info("Skills already in sync or no global skills to sync");
|
|
4417
5590
|
} else {
|
|
4418
|
-
console.log(
|
|
5591
|
+
console.log(chalk3.bold(`
|
|
4419
5592
|
\u2713 Synced ${result.count} skills to project:
|
|
4420
5593
|
`));
|
|
4421
5594
|
for (const skill of result.synced) {
|
|
4422
|
-
console.log(` ${
|
|
5595
|
+
console.log(` ${chalk3.cyan("\u2022")} ${skill}`);
|
|
4423
5596
|
}
|
|
4424
5597
|
console.log();
|
|
4425
5598
|
}
|
|
@@ -4431,18 +5604,18 @@ skillsCmd.command("config <tool-name>").description("Configure a tool (e.g., con
|
|
|
4431
5604
|
const tool = await toolConfigManager.getToolConfig(toolName);
|
|
4432
5605
|
if (!tool) {
|
|
4433
5606
|
logger.error(`Tool not found: ${toolName}`);
|
|
4434
|
-
console.log(
|
|
5607
|
+
console.log(chalk3.gray("\nAvailable tools:"));
|
|
4435
5608
|
const tools = await toolConfigManager.listTools();
|
|
4436
5609
|
for (const t of tools) {
|
|
4437
|
-
console.log(` - ${
|
|
5610
|
+
console.log(` - ${chalk3.cyan(t.name)}`);
|
|
4438
5611
|
}
|
|
4439
5612
|
console.log();
|
|
4440
5613
|
process.exit(1);
|
|
4441
5614
|
}
|
|
4442
|
-
console.log(
|
|
5615
|
+
console.log(chalk3.bold(`
|
|
4443
5616
|
\u{1F527} Configuring: ${tool.name}
|
|
4444
5617
|
`));
|
|
4445
|
-
console.log(
|
|
5618
|
+
console.log(chalk3.gray(tool.description));
|
|
4446
5619
|
console.log();
|
|
4447
5620
|
if (tool.configMethod === "oauth") {
|
|
4448
5621
|
if (toolName === "figma-analysis") {
|
|
@@ -4450,12 +5623,12 @@ skillsCmd.command("config <tool-name>").description("Configure a tool (e.g., con
|
|
|
4450
5623
|
const oauth = new FigmaOAuth2(toolConfigManager);
|
|
4451
5624
|
try {
|
|
4452
5625
|
const token = await oauth.authenticate();
|
|
4453
|
-
console.log(
|
|
5626
|
+
console.log(chalk3.gray("\nValidating token..."));
|
|
4454
5627
|
const isValid = await oauth.validateToken(token);
|
|
4455
5628
|
if (isValid) {
|
|
4456
5629
|
logger.success(`
|
|
4457
5630
|
\u2705 ${tool.name} configured successfully!`);
|
|
4458
|
-
console.log(
|
|
5631
|
+
console.log(chalk3.gray("\nYou can now use the /analyze-figma command in OpenCode.\n"));
|
|
4459
5632
|
} else {
|
|
4460
5633
|
await toolConfigManager.updateToolConfig(toolName, {
|
|
4461
5634
|
status: "error",
|
|
@@ -4494,20 +5667,20 @@ skillsCmd.command("*").description("Configure a tool (e.g., figma-analysis confi
|
|
|
4494
5667
|
const tool = await toolConfigManager.getToolConfig(toolName);
|
|
4495
5668
|
if (!tool) {
|
|
4496
5669
|
logger.error(`Tool not found: ${toolName}`);
|
|
4497
|
-
console.log(
|
|
5670
|
+
console.log(chalk3.gray("\nAvailable tools:"));
|
|
4498
5671
|
const tools = await toolConfigManager.listTools();
|
|
4499
5672
|
for (const t of tools) {
|
|
4500
|
-
console.log(` - ${
|
|
5673
|
+
console.log(` - ${chalk3.cyan(t.name)}`);
|
|
4501
5674
|
}
|
|
4502
5675
|
console.log();
|
|
4503
|
-
console.log(
|
|
5676
|
+
console.log(chalk3.gray("Tip: If you meant to show a skill, use: aikit skills show <name>"));
|
|
4504
5677
|
console.log();
|
|
4505
5678
|
process.exit(1);
|
|
4506
5679
|
}
|
|
4507
|
-
console.log(
|
|
5680
|
+
console.log(chalk3.bold(`
|
|
4508
5681
|
\u{1F527} Configuring: ${tool.name}
|
|
4509
5682
|
`));
|
|
4510
|
-
console.log(
|
|
5683
|
+
console.log(chalk3.gray(tool.description));
|
|
4511
5684
|
console.log();
|
|
4512
5685
|
if (tool.configMethod === "oauth") {
|
|
4513
5686
|
if (toolName === "figma-analysis") {
|
|
@@ -4515,12 +5688,12 @@ skillsCmd.command("*").description("Configure a tool (e.g., figma-analysis confi
|
|
|
4515
5688
|
const oauth = new FigmaOAuth2(toolConfigManager);
|
|
4516
5689
|
try {
|
|
4517
5690
|
const token = await oauth.authenticate();
|
|
4518
|
-
console.log(
|
|
5691
|
+
console.log(chalk3.gray("\nValidating token..."));
|
|
4519
5692
|
const isValid = await oauth.validateToken(token);
|
|
4520
5693
|
if (isValid) {
|
|
4521
5694
|
logger.success(`
|
|
4522
5695
|
\u2705 ${tool.name} configured successfully!`);
|
|
4523
|
-
console.log(
|
|
5696
|
+
console.log(chalk3.gray("\nYou can now use the /analyze-figma command in OpenCode.\n"));
|
|
4524
5697
|
} else {
|
|
4525
5698
|
await toolConfigManager.updateToolConfig(toolName, {
|
|
4526
5699
|
status: "error",
|
|
@@ -4549,7 +5722,7 @@ skillsCmd.command("*").description("Configure a tool (e.g., figma-analysis confi
|
|
|
4549
5722
|
}
|
|
4550
5723
|
} else {
|
|
4551
5724
|
logger.error(`Unknown command: ${toolName || "unknown"} ${action || ""}`);
|
|
4552
|
-
console.log(
|
|
5725
|
+
console.log(chalk3.gray("\nAvailable commands:"));
|
|
4553
5726
|
console.log(" aikit skills list - List all skills and tools");
|
|
4554
5727
|
console.log(" aikit skills show <name> - Show skill details");
|
|
4555
5728
|
console.log(" aikit skills config <tool-name> - Configure a tool");
|
|
@@ -4563,10 +5736,10 @@ agentsCmd.command("list").description("List available agents").action(async () =
|
|
|
4563
5736
|
const config = await loadConfig();
|
|
4564
5737
|
const manager = new AgentManager(config);
|
|
4565
5738
|
const agents = manager.listAgents();
|
|
4566
|
-
console.log(
|
|
5739
|
+
console.log(chalk3.bold("\n\u{1F916} Available Agents:\n"));
|
|
4567
5740
|
for (const agent of agents) {
|
|
4568
|
-
console.log(` ${
|
|
4569
|
-
console.log(
|
|
5741
|
+
console.log(` ${chalk3.cyan(`@${agent.name}`)} - ${agent.description}`);
|
|
5742
|
+
console.log(chalk3.gray(` Use when: ${agent.useWhen}`));
|
|
4570
5743
|
}
|
|
4571
5744
|
console.log();
|
|
4572
5745
|
});
|
|
@@ -4575,13 +5748,13 @@ commandsCmd.command("list").description("List available commands").action(async
|
|
|
4575
5748
|
const config = await loadConfig();
|
|
4576
5749
|
const runner = new CommandRunner(config);
|
|
4577
5750
|
const commands = await runner.listCommands();
|
|
4578
|
-
console.log(
|
|
5751
|
+
console.log(chalk3.bold("\n\u26A1 Available Commands:\n"));
|
|
4579
5752
|
const groups = groupBy(commands, (c) => c.category);
|
|
4580
5753
|
for (const [category, cmds] of Object.entries(groups)) {
|
|
4581
|
-
console.log(
|
|
5754
|
+
console.log(chalk3.bold.yellow(`
|
|
4582
5755
|
${category}:`));
|
|
4583
5756
|
for (const cmd of cmds) {
|
|
4584
|
-
console.log(` ${
|
|
5757
|
+
console.log(` ${chalk3.cyan(`/${cmd.name}`)} - ${cmd.description}`);
|
|
4585
5758
|
}
|
|
4586
5759
|
}
|
|
4587
5760
|
console.log();
|
|
@@ -4591,9 +5764,9 @@ toolsCmd.command("list").description("List available tools").action(async () =>
|
|
|
4591
5764
|
const config = await loadConfig();
|
|
4592
5765
|
const registry = new ToolRegistry(config);
|
|
4593
5766
|
const tools = await registry.listTools();
|
|
4594
|
-
console.log(
|
|
5767
|
+
console.log(chalk3.bold("\n\u{1F527} Available Tools:\n"));
|
|
4595
5768
|
for (const tool of tools) {
|
|
4596
|
-
console.log(` ${
|
|
5769
|
+
console.log(` ${chalk3.cyan(tool.name)} - ${tool.description}`);
|
|
4597
5770
|
}
|
|
4598
5771
|
console.log();
|
|
4599
5772
|
});
|
|
@@ -4602,10 +5775,10 @@ pluginsCmd.command("list").description("List available plugins").action(async ()
|
|
|
4602
5775
|
const config = await loadConfig();
|
|
4603
5776
|
const system = new PluginSystem(config);
|
|
4604
5777
|
const plugins = await system.listPlugins();
|
|
4605
|
-
console.log(
|
|
5778
|
+
console.log(chalk3.bold("\n\u{1F50C} Available Plugins:\n"));
|
|
4606
5779
|
for (const plugin of plugins) {
|
|
4607
|
-
const status = plugin.enabled ?
|
|
4608
|
-
console.log(` ${status} ${
|
|
5780
|
+
const status = plugin.enabled ? chalk3.green("\u2713") : chalk3.gray("\u25CB");
|
|
5781
|
+
console.log(` ${status} ${chalk3.cyan(plugin.name)} - ${plugin.description}`);
|
|
4609
5782
|
}
|
|
4610
5783
|
console.log();
|
|
4611
5784
|
});
|
|
@@ -4614,10 +5787,10 @@ memoryCmd.command("list").description("List memory entries").action(async () =>
|
|
|
4614
5787
|
const config = await loadConfig();
|
|
4615
5788
|
const memory = new MemoryManager(config);
|
|
4616
5789
|
const entries = await memory.list();
|
|
4617
|
-
console.log(
|
|
5790
|
+
console.log(chalk3.bold("\n\u{1F9E0} Memory Entries:\n"));
|
|
4618
5791
|
for (const entry of entries) {
|
|
4619
|
-
console.log(` ${
|
|
4620
|
-
console.log(
|
|
5792
|
+
console.log(` ${chalk3.cyan(entry.key)} - ${entry.summary}`);
|
|
5793
|
+
console.log(chalk3.gray(` Updated: ${entry.updatedAt}`));
|
|
4621
5794
|
}
|
|
4622
5795
|
console.log();
|
|
4623
5796
|
});
|
|
@@ -4635,19 +5808,19 @@ var beadsCmd = program.command("beads").description("Beads task management integ
|
|
|
4635
5808
|
beadsCmd.command("status").description("Show current Beads status").action(async () => {
|
|
4636
5809
|
const beads = new BeadsIntegration();
|
|
4637
5810
|
const status = await beads.getStatus();
|
|
4638
|
-
console.log(
|
|
5811
|
+
console.log(chalk3.bold("\n\u{1F4FF} Beads Status:\n"));
|
|
4639
5812
|
console.log(` Active tasks: ${status.activeTasks}`);
|
|
4640
5813
|
console.log(` Completed: ${status.completedTasks}`);
|
|
4641
5814
|
console.log(` Current: ${status.currentTask || "None"}`);
|
|
4642
5815
|
console.log();
|
|
4643
5816
|
});
|
|
4644
5817
|
program.command("status").description("Show AIKit status").action(async () => {
|
|
4645
|
-
console.log(
|
|
5818
|
+
console.log(chalk3.bold(`
|
|
4646
5819
|
\u{1F680} AIKit v${getVersion}
|
|
4647
5820
|
`));
|
|
4648
5821
|
try {
|
|
4649
5822
|
const config = await loadConfig();
|
|
4650
|
-
console.log(
|
|
5823
|
+
console.log(chalk3.green("\u2713 Configuration loaded"));
|
|
4651
5824
|
const skillEngine = new SkillEngine(config);
|
|
4652
5825
|
const skills = await skillEngine.listSkills();
|
|
4653
5826
|
console.log(` Skills: ${skills.length}`);
|
|
@@ -4662,15 +5835,15 @@ program.command("status").description("Show AIKit status").action(async () => {
|
|
|
4662
5835
|
console.log(` Tools: ${tools.length}`);
|
|
4663
5836
|
const beads = new BeadsIntegration();
|
|
4664
5837
|
const beadsStatus = await beads.isInstalled();
|
|
4665
|
-
console.log(` Beads: ${beadsStatus ?
|
|
5838
|
+
console.log(` Beads: ${beadsStatus ? chalk3.green("Installed") : chalk3.yellow("Not installed")}`);
|
|
4666
5839
|
} catch (error) {
|
|
4667
|
-
console.log(
|
|
5840
|
+
console.log(chalk3.yellow('\u26A0 AIKit not initialized. Run "aikit init" to get started.'));
|
|
4668
5841
|
}
|
|
4669
5842
|
console.log();
|
|
4670
5843
|
});
|
|
4671
5844
|
async function initializeConfig(configDir, _isGlobal) {
|
|
4672
|
-
const { mkdir:
|
|
4673
|
-
const { join:
|
|
5845
|
+
const { mkdir: mkdir11, writeFile: writeFile13 } = await import("fs/promises");
|
|
5846
|
+
const { join: join18 } = await import("path");
|
|
4674
5847
|
const dirs = [
|
|
4675
5848
|
"",
|
|
4676
5849
|
"skills",
|
|
@@ -4689,7 +5862,7 @@ async function initializeConfig(configDir, _isGlobal) {
|
|
|
4689
5862
|
"memory/research"
|
|
4690
5863
|
];
|
|
4691
5864
|
for (const dir of dirs) {
|
|
4692
|
-
await
|
|
5865
|
+
await mkdir11(join18(configDir, dir), { recursive: true });
|
|
4693
5866
|
}
|
|
4694
5867
|
const defaultConfig = {
|
|
4695
5868
|
version: getVersion,
|
|
@@ -4702,8 +5875,8 @@ async function initializeConfig(configDir, _isGlobal) {
|
|
|
4702
5875
|
beads: { enabled: true },
|
|
4703
5876
|
antiHallucination: { enabled: true }
|
|
4704
5877
|
};
|
|
4705
|
-
await
|
|
4706
|
-
|
|
5878
|
+
await writeFile13(
|
|
5879
|
+
join18(configDir, "aikit.json"),
|
|
4707
5880
|
JSON.stringify(defaultConfig, null, 2)
|
|
4708
5881
|
);
|
|
4709
5882
|
const agentsMd = `# AIKit Agent Rules
|
|
@@ -4726,26 +5899,26 @@ async function initializeConfig(configDir, _isGlobal) {
|
|
|
4726
5899
|
## Project-Specific Rules
|
|
4727
5900
|
Add your project-specific rules here.
|
|
4728
5901
|
`;
|
|
4729
|
-
await
|
|
5902
|
+
await writeFile13(join18(configDir, "AGENTS.md"), agentsMd);
|
|
4730
5903
|
}
|
|
4731
5904
|
async function configureMcpServer(projectPath) {
|
|
4732
|
-
const { mkdir:
|
|
4733
|
-
const { join:
|
|
5905
|
+
const { mkdir: mkdir11, writeFile: writeFile13, readFile: readFile12 } = await import("fs/promises");
|
|
5906
|
+
const { join: join18 } = await import("path");
|
|
4734
5907
|
const { existsSync: existsSync5 } = await import("fs");
|
|
4735
5908
|
const { homedir: homedir3 } = await import("os");
|
|
4736
5909
|
const { fileURLToPath: fileURLToPath3 } = await import("url");
|
|
4737
|
-
const { dirname:
|
|
5910
|
+
const { dirname: dirname4 } = await import("path");
|
|
4738
5911
|
const currentFile = fileURLToPath3(import.meta.url);
|
|
4739
|
-
const currentDir =
|
|
4740
|
-
const aikitPath =
|
|
4741
|
-
const mcpServerPath =
|
|
5912
|
+
const currentDir = dirname4(currentFile);
|
|
5913
|
+
const aikitPath = join18(currentDir, "..");
|
|
5914
|
+
const mcpServerPath = join18(aikitPath, "dist", "mcp-server.js");
|
|
4742
5915
|
const configLocations = [
|
|
4743
5916
|
// Global config (most common)
|
|
4744
|
-
|
|
5917
|
+
join18(homedir3(), ".config", "opencode", "opencode.json"),
|
|
4745
5918
|
// Project-level config
|
|
4746
|
-
|
|
5919
|
+
join18(projectPath, ".opencode", "opencode.json"),
|
|
4747
5920
|
// Alternative global location
|
|
4748
|
-
|
|
5921
|
+
join18(homedir3(), ".opencode", "opencode.json")
|
|
4749
5922
|
];
|
|
4750
5923
|
const mcpServerConfig = {
|
|
4751
5924
|
type: "local",
|
|
@@ -4754,12 +5927,12 @@ async function configureMcpServer(projectPath) {
|
|
|
4754
5927
|
};
|
|
4755
5928
|
for (const configPath of configLocations) {
|
|
4756
5929
|
try {
|
|
4757
|
-
const configDir =
|
|
4758
|
-
await
|
|
5930
|
+
const configDir = join18(configPath, "..");
|
|
5931
|
+
await mkdir11(configDir, { recursive: true });
|
|
4759
5932
|
let config = {};
|
|
4760
5933
|
if (existsSync5(configPath)) {
|
|
4761
5934
|
try {
|
|
4762
|
-
const existing = await
|
|
5935
|
+
const existing = await readFile12(configPath, "utf-8");
|
|
4763
5936
|
config = JSON.parse(existing);
|
|
4764
5937
|
} catch {
|
|
4765
5938
|
config = {};
|
|
@@ -4769,7 +5942,7 @@ async function configureMcpServer(projectPath) {
|
|
|
4769
5942
|
config.mcp = {};
|
|
4770
5943
|
}
|
|
4771
5944
|
config.mcp.aikit = mcpServerConfig;
|
|
4772
|
-
await
|
|
5945
|
+
await writeFile13(configPath, JSON.stringify(config, null, 2));
|
|
4773
5946
|
logger.success(`
|
|
4774
5947
|
\u2705 MCP server configured: ${configPath}`);
|
|
4775
5948
|
logger.info(` Server: node ${mcpServerPath}`);
|
|
@@ -4778,9 +5951,9 @@ async function configureMcpServer(projectPath) {
|
|
|
4778
5951
|
continue;
|
|
4779
5952
|
}
|
|
4780
5953
|
}
|
|
4781
|
-
const instructionsPath =
|
|
4782
|
-
await
|
|
4783
|
-
await
|
|
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
|
|
4784
5957
|
|
|
4785
5958
|
## Automatic Setup Failed
|
|
4786
5959
|
|
|
@@ -4843,15 +6016,15 @@ async function installCliTool(tool) {
|
|
|
4843
6016
|
}
|
|
4844
6017
|
}
|
|
4845
6018
|
async function installToOpenCode(_opencodePath) {
|
|
4846
|
-
const { mkdir:
|
|
4847
|
-
const { join:
|
|
6019
|
+
const { mkdir: mkdir11, writeFile: writeFile13, access: access5 } = await import("fs/promises");
|
|
6020
|
+
const { join: join18 } = await import("path");
|
|
4848
6021
|
const projectPath = process.cwd();
|
|
4849
|
-
const opencodeCommandDir =
|
|
4850
|
-
const aikitDir =
|
|
4851
|
-
const opencodeAgentDir =
|
|
4852
|
-
await
|
|
4853
|
-
await
|
|
4854
|
-
await
|
|
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 });
|
|
4855
6028
|
const agentFiles = {
|
|
4856
6029
|
agent: `---
|
|
4857
6030
|
description: General-purpose default agent (OpenCode compatibility).
|
|
@@ -4923,11 +6096,11 @@ tools:
|
|
|
4923
6096
|
Use to interpret visual assets (components, layout, colors, typography) and translate to tasks.`
|
|
4924
6097
|
};
|
|
4925
6098
|
for (const [name, content] of Object.entries(agentFiles)) {
|
|
4926
|
-
const filePath =
|
|
6099
|
+
const filePath = join18(opencodeAgentDir, `${name}.md`);
|
|
4927
6100
|
try {
|
|
4928
6101
|
await access5(filePath);
|
|
4929
6102
|
} catch {
|
|
4930
|
-
await
|
|
6103
|
+
await writeFile13(filePath, content, "utf8");
|
|
4931
6104
|
}
|
|
4932
6105
|
}
|
|
4933
6106
|
const config = await loadConfig();
|
|
@@ -5180,8 +6353,8 @@ ${cmd.content}
|
|
|
5180
6353
|
}
|
|
5181
6354
|
let count = 0;
|
|
5182
6355
|
for (const [name, content] of Object.entries(opencodeCommands)) {
|
|
5183
|
-
const filePath =
|
|
5184
|
-
await
|
|
6356
|
+
const filePath = join18(opencodeCommandDir, `${name}.md`);
|
|
6357
|
+
await writeFile13(filePath, content.trim());
|
|
5185
6358
|
logger.info(` \u2713 Created /${name} command`);
|
|
5186
6359
|
count++;
|
|
5187
6360
|
}
|