@tdsoft-tech/aikit 0.1.9 → 0.1.12
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 +27 -0
- package/dist/cli.js +1377 -118
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +134 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +134 -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();
|
|
@@ -1765,6 +1771,51 @@ For diagrams:
|
|
|
1765
1771
|
- Map to code structure
|
|
1766
1772
|
- Note data flow`,
|
|
1767
1773
|
delegatesTo: []
|
|
1774
|
+
},
|
|
1775
|
+
{
|
|
1776
|
+
name: "one-shot",
|
|
1777
|
+
displayName: "@one-shot",
|
|
1778
|
+
description: "End-to-end autonomous task execution (beta)",
|
|
1779
|
+
useWhen: "Complete tasks autonomously from start to finish with minimal intervention",
|
|
1780
|
+
capabilities: [
|
|
1781
|
+
"Gather requirements interactively",
|
|
1782
|
+
"Create detailed implementation plans",
|
|
1783
|
+
"Execute tasks with dynamic agent selection",
|
|
1784
|
+
"Run quality gates until all pass",
|
|
1785
|
+
"Multi-level verification",
|
|
1786
|
+
"Auto-recovery from failures",
|
|
1787
|
+
"Generate completion proof"
|
|
1788
|
+
],
|
|
1789
|
+
systemPrompt: `You are the one-shot agent. Your role is to execute tasks autonomously from start to finish.
|
|
1790
|
+
|
|
1791
|
+
## Workflow Phases
|
|
1792
|
+
|
|
1793
|
+
1. **REQUIREMENTS**: Gather task type, scope, dependencies, success criteria
|
|
1794
|
+
2. **PLANNING**: Create detailed plan with @planner, recommend skills/tools
|
|
1795
|
+
3. **COMPLEXITY**: Auto-split large tasks (>30min, >10 files, >500 lines)
|
|
1796
|
+
4. **EXECUTION**: Execute with parallel tasks, dynamic agent delegation
|
|
1797
|
+
5. **TESTING**: Run quality gates (typecheck, test, lint, build) until all pass
|
|
1798
|
+
6. **VERIFICATION**: Multi-level verification (gates + manual + deployment)
|
|
1799
|
+
7. **COMPLETION**: Generate proof, update tracking, collect feedback
|
|
1800
|
+
|
|
1801
|
+
## Quality Gates (Must ALL Pass)
|
|
1802
|
+
- npm run typecheck - No type errors
|
|
1803
|
+
- npm run test - All tests pass
|
|
1804
|
+
- npm run lint - No linting errors
|
|
1805
|
+
- npm run build - Build succeeds
|
|
1806
|
+
|
|
1807
|
+
## Error Recovery (3 Levels)
|
|
1808
|
+
- Level 1: Auto-fix (type errors, lint --fix)
|
|
1809
|
+
- Level 2: Alternative approach via @review
|
|
1810
|
+
- Level 3: User intervention + follow-up task creation
|
|
1811
|
+
|
|
1812
|
+
## Best Practices
|
|
1813
|
+
- Use for straightforward tasks first
|
|
1814
|
+
- Consider /plan + /implement for complex features
|
|
1815
|
+
- Review changes before final approval
|
|
1816
|
+
|
|
1817
|
+
\u26A0\uFE0F This mode is experimental. Start with simpler tasks.`,
|
|
1818
|
+
delegatesTo: ["planner", "build", "review", "scout", "explore", "vision"]
|
|
1768
1819
|
}
|
|
1769
1820
|
];
|
|
1770
1821
|
var AgentManager = class {
|
|
@@ -2037,6 +2088,95 @@ Before marking complete:
|
|
|
2037
2088
|
2. Display summary to user
|
|
2038
2089
|
3. Propose next actions
|
|
2039
2090
|
4. Continue from where left off`
|
|
2091
|
+
},
|
|
2092
|
+
{
|
|
2093
|
+
name: "one-shot",
|
|
2094
|
+
description: "End-to-end autonomous task execution (beta)",
|
|
2095
|
+
category: "core",
|
|
2096
|
+
usage: "/one-shot <task description>",
|
|
2097
|
+
examples: ["/one-shot Add user authentication", "/one-shot Fix navigation bug"],
|
|
2098
|
+
content: `One-Shot Mode (beta) - End-to-end autonomous task execution.
|
|
2099
|
+
|
|
2100
|
+
\u26A0\uFE0F This mode is experimental. Use for straightforward tasks first.
|
|
2101
|
+
|
|
2102
|
+
## Workflow
|
|
2103
|
+
|
|
2104
|
+
**Phase 1: Requirements Gathering**
|
|
2105
|
+
- Interactive selection of task type (Feature, Bug Fix, Refactoring, etc.)
|
|
2106
|
+
- Scope clarification
|
|
2107
|
+
- Dependencies identification
|
|
2108
|
+
- Success criteria definition
|
|
2109
|
+
- User selects progress level (Minimal/Moderate/Detailed/Quiet)
|
|
2110
|
+
|
|
2111
|
+
**Phase 2: Planning**
|
|
2112
|
+
- Delegate to @planner agent
|
|
2113
|
+
- Create detailed implementation plan
|
|
2114
|
+
- Recommend relevant skills and tools
|
|
2115
|
+
- Create Beads task for tracking
|
|
2116
|
+
|
|
2117
|
+
**Phase 3: Complexity Check & Auto-Split**
|
|
2118
|
+
- Analyze task complexity
|
|
2119
|
+
- Split into multiple beads if needed:
|
|
2120
|
+
* Time > 30 minutes
|
|
2121
|
+
* >10 files affected
|
|
2122
|
+
* >500 lines to change
|
|
2123
|
+
* Touches >2 sub-systems
|
|
2124
|
+
|
|
2125
|
+
**Phase 4: Execution**
|
|
2126
|
+
- Build dependency graph
|
|
2127
|
+
- Execute tasks in parallel (max 3 concurrent)
|
|
2128
|
+
- Dynamic agent selection (@build \u2192 @review \u2192 @scout \u2192 ...)
|
|
2129
|
+
- Integrate skills (TDD, debugging, etc.)
|
|
2130
|
+
- Smart terminal access (auto-allow/ask/forbid)
|
|
2131
|
+
|
|
2132
|
+
**Phase 5: Enhanced Testing & Validation**
|
|
2133
|
+
- Auto-generate test scripts for new functionality
|
|
2134
|
+
- Run quality gates: typecheck, test, lint, build
|
|
2135
|
+
- Execute sample commands (with user approval)
|
|
2136
|
+
- Validate logs semantically with historical comparison
|
|
2137
|
+
- Retry loop (max 3 attempts) with:
|
|
2138
|
+
* Auto-fix type errors, lint errors
|
|
2139
|
+
* Alternative approaches from @review
|
|
2140
|
+
* User intervention on final failure
|
|
2141
|
+
|
|
2142
|
+
**Phase 6: Multi-Level Verification**
|
|
2143
|
+
- All quality gates passed \u2713
|
|
2144
|
+
- Manual verification confirmation
|
|
2145
|
+
- Deployment approval (if needed)
|
|
2146
|
+
- Rollback confirmation (if verification fails)
|
|
2147
|
+
|
|
2148
|
+
**Phase 7: Completion**
|
|
2149
|
+
- Generate proof of completion
|
|
2150
|
+
* Files changed
|
|
2151
|
+
* Test results
|
|
2152
|
+
* Build output
|
|
2153
|
+
* Deployment status
|
|
2154
|
+
- Update Beads task \u2192 completed
|
|
2155
|
+
- Store proof in bead notes
|
|
2156
|
+
- Collect beta feedback
|
|
2157
|
+
|
|
2158
|
+
## Quality Gates (Must ALL Pass)
|
|
2159
|
+
- \`npm run typecheck\` - No type errors
|
|
2160
|
+
- \`npm run test\` - All tests pass
|
|
2161
|
+
- \`npm run lint\` - No linting errors
|
|
2162
|
+
- \`npm run build\` - Build succeeds
|
|
2163
|
+
|
|
2164
|
+
## Success Criteria
|
|
2165
|
+
- All tests passing
|
|
2166
|
+
- No regressions
|
|
2167
|
+
- Manual verification
|
|
2168
|
+
- Deployment complete (if applicable)
|
|
2169
|
+
- Beads task completed with proof
|
|
2170
|
+
|
|
2171
|
+
## Error Handling
|
|
2172
|
+
- **Level 1**: Auto-fix (type errors, lint --fix)
|
|
2173
|
+
- **Level 2**: Alternative approach (@review delegation)
|
|
2174
|
+
- **Level 3**: User intervention + follow-up bead creation
|
|
2175
|
+
|
|
2176
|
+
## Tips
|
|
2177
|
+
\u2713 Use for straightforward tasks first
|
|
2178
|
+
\u2713 Consider /plan + /implement for complex features
|
|
2179
|
+
\u2713 Review changes before final approval`
|
|
2040
2180
|
},
|
|
2041
2181
|
// Quick Actions
|
|
2042
2182
|
{
|
|
@@ -4221,11 +4361,1058 @@ var CliDetector = class {
|
|
|
4221
4361
|
// src/cli.ts
|
|
4222
4362
|
init_logger();
|
|
4223
4363
|
init_paths();
|
|
4364
|
+
|
|
4365
|
+
// src/core/sync-engine.ts
|
|
4366
|
+
init_esm_shims();
|
|
4367
|
+
import { readFile as readFile10, writeFile as writeFile11, copyFile, mkdir as mkdir9 } from "fs/promises";
|
|
4368
|
+
import { join as join16, dirname as dirname3 } from "path";
|
|
4369
|
+
import inquirer from "inquirer";
|
|
4370
|
+
import chalk2 from "chalk";
|
|
4371
|
+
|
|
4372
|
+
// src/core/version-manager.ts
|
|
4373
|
+
init_esm_shims();
|
|
4374
|
+
init_paths();
|
|
4375
|
+
init_logger();
|
|
4376
|
+
import { readFile as readFile7, readdir as readdir7, writeFile as writeFile8, stat } from "fs/promises";
|
|
4377
|
+
import { join as join13 } from "path";
|
|
4378
|
+
import { createHash } from "crypto";
|
|
4379
|
+
var VersionManager = class {
|
|
4380
|
+
config;
|
|
4381
|
+
constructor(config) {
|
|
4382
|
+
this.config = config;
|
|
4383
|
+
}
|
|
4384
|
+
/**
|
|
4385
|
+
* Get current installed version
|
|
4386
|
+
*/
|
|
4387
|
+
async getCurrentVersion() {
|
|
4388
|
+
const versionPath = join13(paths.globalConfig(), ".version.json");
|
|
4389
|
+
try {
|
|
4390
|
+
const content = await readFile7(versionPath, "utf-8");
|
|
4391
|
+
return JSON.parse(content);
|
|
4392
|
+
} catch {
|
|
4393
|
+
return null;
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
/**
|
|
4397
|
+
* Get package version from package.json
|
|
4398
|
+
*/
|
|
4399
|
+
getPackageVersion() {
|
|
4400
|
+
try {
|
|
4401
|
+
const packageJson = __require(join13(process.cwd(), "package.json"));
|
|
4402
|
+
return packageJson.version || "0.0.0";
|
|
4403
|
+
} catch {
|
|
4404
|
+
return "0.0.0";
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
/**
|
|
4408
|
+
* Check for updates
|
|
4409
|
+
*/
|
|
4410
|
+
async checkForUpdates() {
|
|
4411
|
+
const installed = await this.getCurrentVersion();
|
|
4412
|
+
const packageVersion = this.getPackageVersion();
|
|
4413
|
+
if (!installed) {
|
|
4414
|
+
return {
|
|
4415
|
+
hasUpdate: true,
|
|
4416
|
+
fromVersion: "none",
|
|
4417
|
+
toVersion: packageVersion,
|
|
4418
|
+
newSkills: [],
|
|
4419
|
+
modifiedSkills: [],
|
|
4420
|
+
removedSkills: [],
|
|
4421
|
+
conflicts: [],
|
|
4422
|
+
configChanges: ["Initial version tracking"]
|
|
4423
|
+
};
|
|
4424
|
+
}
|
|
4425
|
+
const hasUpdate = installed.installedVersion !== packageVersion;
|
|
4426
|
+
if (!hasUpdate) {
|
|
4427
|
+
return {
|
|
4428
|
+
hasUpdate: false,
|
|
4429
|
+
fromVersion: installed.installedVersion,
|
|
4430
|
+
toVersion: packageVersion,
|
|
4431
|
+
newSkills: [],
|
|
4432
|
+
modifiedSkills: [],
|
|
4433
|
+
removedSkills: [],
|
|
4434
|
+
conflicts: [],
|
|
4435
|
+
configChanges: []
|
|
4436
|
+
};
|
|
4437
|
+
}
|
|
4438
|
+
const changes = await this.detectChanges();
|
|
4439
|
+
return {
|
|
4440
|
+
hasUpdate: true,
|
|
4441
|
+
fromVersion: installed.installedVersion,
|
|
4442
|
+
toVersion: packageVersion,
|
|
4443
|
+
...changes
|
|
4444
|
+
};
|
|
4445
|
+
}
|
|
4446
|
+
/**
|
|
4447
|
+
* Detect changes between versions
|
|
4448
|
+
*/
|
|
4449
|
+
async detectChanges() {
|
|
4450
|
+
const globalSkillsPath = paths.skills(paths.globalConfig());
|
|
4451
|
+
const projectSkillsPath = paths.skills(this.config.configPath);
|
|
4452
|
+
const sourceSkills = await this.loadSkillHashes(globalSkillsPath);
|
|
4453
|
+
const userSkills = await this.loadSkillHashes(projectSkillsPath);
|
|
4454
|
+
const newSkills = [];
|
|
4455
|
+
const modifiedSkills = [];
|
|
4456
|
+
const removedSkills = [];
|
|
4457
|
+
const conflicts = [];
|
|
4458
|
+
const installedSkills = /* @__PURE__ */ new Map();
|
|
4459
|
+
const installedPath = join13(paths.globalConfig(), ".installed-skills.json");
|
|
4460
|
+
try {
|
|
4461
|
+
const installedData = await readFile7(installedPath, "utf-8");
|
|
4462
|
+
const installedList = JSON.parse(installedData);
|
|
4463
|
+
installedList.forEach((skill) => {
|
|
4464
|
+
installedSkills.set(skill.name, skill);
|
|
4465
|
+
});
|
|
4466
|
+
} catch {
|
|
4467
|
+
}
|
|
4468
|
+
for (const sourceSkill of sourceSkills) {
|
|
4469
|
+
const installed = installedSkills.get(sourceSkill.name);
|
|
4470
|
+
const user = userSkills.find((s) => s.name === sourceSkill.name);
|
|
4471
|
+
if (!installed) {
|
|
4472
|
+
newSkills.push(sourceSkill);
|
|
4473
|
+
} else if (installed.hash !== sourceSkill.hash) {
|
|
4474
|
+
modifiedSkills.push(sourceSkill);
|
|
4475
|
+
if (user && user.hash !== installed.hash) {
|
|
4476
|
+
conflicts.push({
|
|
4477
|
+
skillName: sourceSkill.name,
|
|
4478
|
+
userHash: user.hash,
|
|
4479
|
+
sourceHash: sourceSkill.hash,
|
|
4480
|
+
installedHash: installed.hash,
|
|
4481
|
+
userModified: user.hash !== installed.hash,
|
|
4482
|
+
sourceModified: sourceSkill.hash !== installed.hash
|
|
4483
|
+
});
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4487
|
+
for (const [name, installedSkill] of installedSkills.entries()) {
|
|
4488
|
+
const existsInSource = sourceSkills.find((s) => s.name === name);
|
|
4489
|
+
if (!existsInSource) {
|
|
4490
|
+
removedSkills.push(installedSkill);
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
return {
|
|
4494
|
+
newSkills,
|
|
4495
|
+
modifiedSkills,
|
|
4496
|
+
removedSkills,
|
|
4497
|
+
conflicts,
|
|
4498
|
+
configChanges: []
|
|
4499
|
+
// Will be detected separately
|
|
4500
|
+
};
|
|
4501
|
+
}
|
|
4502
|
+
/**
|
|
4503
|
+
* Load skill hashes from directory
|
|
4504
|
+
*/
|
|
4505
|
+
async loadSkillHashes(skillsPath) {
|
|
4506
|
+
const hashes = [];
|
|
4507
|
+
try {
|
|
4508
|
+
const loadFromDir = async (dir) => {
|
|
4509
|
+
const files = await readdir7(dir);
|
|
4510
|
+
for (const file of files) {
|
|
4511
|
+
const filePath = join13(dir, file);
|
|
4512
|
+
const stats = await stat(filePath);
|
|
4513
|
+
if (stats.isDirectory()) {
|
|
4514
|
+
await loadFromDir(filePath);
|
|
4515
|
+
} else if (file.endsWith(".md")) {
|
|
4516
|
+
const hash = await this.calculateSkillHash(filePath);
|
|
4517
|
+
hashes.push({
|
|
4518
|
+
path: filePath,
|
|
4519
|
+
name: file.replace(".md", ""),
|
|
4520
|
+
hash,
|
|
4521
|
+
category: this.extractCategory(dir, skillsPath)
|
|
4522
|
+
});
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
};
|
|
4526
|
+
await loadFromDir(skillsPath);
|
|
4527
|
+
} catch (error) {
|
|
4528
|
+
logger.debug(`Could not load skills from ${skillsPath}:`, error);
|
|
4529
|
+
}
|
|
4530
|
+
return hashes;
|
|
4531
|
+
}
|
|
4532
|
+
/**
|
|
4533
|
+
* Calculate hash for a skill file
|
|
4534
|
+
*/
|
|
4535
|
+
async calculateSkillHash(filePath) {
|
|
4536
|
+
try {
|
|
4537
|
+
const content = await readFile7(filePath, "utf-8");
|
|
4538
|
+
return createHash("sha256").update(content).digest("hex");
|
|
4539
|
+
} catch {
|
|
4540
|
+
return "";
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
/**
|
|
4544
|
+
* Extract category from path
|
|
4545
|
+
*/
|
|
4546
|
+
extractCategory(filePath, basePath) {
|
|
4547
|
+
const relative = filePath.replace(basePath + "/", "");
|
|
4548
|
+
const parts = relative.split("/");
|
|
4549
|
+
if (parts.length > 1) {
|
|
4550
|
+
return parts[0];
|
|
4551
|
+
}
|
|
4552
|
+
return "uncategorized";
|
|
4553
|
+
}
|
|
4554
|
+
/**
|
|
4555
|
+
* Save installed skills info
|
|
4556
|
+
*/
|
|
4557
|
+
async saveInstalledSkills(skills) {
|
|
4558
|
+
const installedPath = join13(paths.globalConfig(), ".installed-skills.json");
|
|
4559
|
+
try {
|
|
4560
|
+
await writeFile8(installedPath, JSON.stringify(skills, null, 2));
|
|
4561
|
+
} catch (error) {
|
|
4562
|
+
logger.error("Failed to save installed skills info:", error);
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4565
|
+
/**
|
|
4566
|
+
* Update version file
|
|
4567
|
+
*/
|
|
4568
|
+
async updateVersion(version, migration) {
|
|
4569
|
+
const current = await this.getCurrentVersion() || {
|
|
4570
|
+
installedVersion: "0.0.0",
|
|
4571
|
+
lastSynced: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4572
|
+
packageVersion: "0.0.0",
|
|
4573
|
+
migrationHistory: []
|
|
4574
|
+
};
|
|
4575
|
+
const updated = {
|
|
4576
|
+
installedVersion: version,
|
|
4577
|
+
lastSynced: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4578
|
+
packageVersion: this.getPackageVersion(),
|
|
4579
|
+
migrationHistory: migration ? [...current.migrationHistory, migration] : current.migrationHistory
|
|
4580
|
+
};
|
|
4581
|
+
const versionPath = join13(paths.globalConfig(), ".version.json");
|
|
4582
|
+
await writeFile8(versionPath, JSON.stringify(updated, null, 2));
|
|
4583
|
+
}
|
|
4584
|
+
/**
|
|
4585
|
+
* Check if migration is needed
|
|
4586
|
+
*/
|
|
4587
|
+
async needsMigration() {
|
|
4588
|
+
const current = await this.getCurrentVersion();
|
|
4589
|
+
const packageVersion = this.getPackageVersion();
|
|
4590
|
+
return current?.installedVersion !== packageVersion;
|
|
4591
|
+
}
|
|
4592
|
+
};
|
|
4593
|
+
|
|
4594
|
+
// src/core/backup-manager.ts
|
|
4595
|
+
init_esm_shims();
|
|
4596
|
+
init_logger();
|
|
4597
|
+
import { readFile as readFile8, writeFile as writeFile9, readdir as readdir8, stat as stat2, unlink, mkdir as mkdir8 } from "fs/promises";
|
|
4598
|
+
import { join as join14, dirname as dirname2 } from "path";
|
|
4599
|
+
import { createHash as createHash2 } from "crypto";
|
|
4600
|
+
var BackupManager = class {
|
|
4601
|
+
configPath;
|
|
4602
|
+
backupsDir;
|
|
4603
|
+
maxBackups;
|
|
4604
|
+
constructor(configPath, maxBackups = 5) {
|
|
4605
|
+
this.configPath = configPath;
|
|
4606
|
+
this.backupsDir = join14(configPath, ".backups");
|
|
4607
|
+
this.maxBackups = maxBackups;
|
|
4608
|
+
}
|
|
4609
|
+
/**
|
|
4610
|
+
* Create backup before update
|
|
4611
|
+
*/
|
|
4612
|
+
async createBackup(fromVersion, toVersion) {
|
|
4613
|
+
try {
|
|
4614
|
+
await mkdir8(this.backupsDir, { recursive: true });
|
|
4615
|
+
const backupId = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
4616
|
+
const backupPath = join14(this.backupsDir, `${backupId}-v${toVersion}`);
|
|
4617
|
+
await mkdir8(backupPath, { recursive: true });
|
|
4618
|
+
logger.info(`Creating backup: ${backupPath}`);
|
|
4619
|
+
const files = [];
|
|
4620
|
+
const backupItems = [
|
|
4621
|
+
"skills/",
|
|
4622
|
+
"aikit.json",
|
|
4623
|
+
"AGENTS.md",
|
|
4624
|
+
"config/"
|
|
4625
|
+
];
|
|
4626
|
+
for (const item of backupItems) {
|
|
4627
|
+
const files2 = await this.backupItem(this.configPath, item, backupPath);
|
|
4628
|
+
files2.push(...files2);
|
|
4629
|
+
}
|
|
4630
|
+
const manifest = {
|
|
4631
|
+
backupId,
|
|
4632
|
+
fromVersion,
|
|
4633
|
+
toVersion,
|
|
4634
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4635
|
+
files,
|
|
4636
|
+
success: true
|
|
4637
|
+
};
|
|
4638
|
+
const manifestPath = join14(backupPath, "backup-manifest.json");
|
|
4639
|
+
await writeFile9(manifestPath, JSON.stringify(manifest, null, 2));
|
|
4640
|
+
await this.cleanupOldBackups();
|
|
4641
|
+
logger.success(`\u2713 Backup created: ${backupId}`);
|
|
4642
|
+
return backupId;
|
|
4643
|
+
} catch (error) {
|
|
4644
|
+
logger.error("Failed to create backup:", error);
|
|
4645
|
+
return null;
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4648
|
+
/**
|
|
4649
|
+
* Backup a file or directory
|
|
4650
|
+
*/
|
|
4651
|
+
async backupItem(sourceDir, item, targetDir) {
|
|
4652
|
+
const sourcePath = join14(sourceDir, item);
|
|
4653
|
+
const targetPath = join14(targetDir, item);
|
|
4654
|
+
const files = [];
|
|
4655
|
+
try {
|
|
4656
|
+
const stats = await stat2(sourcePath);
|
|
4657
|
+
if (stats.isDirectory()) {
|
|
4658
|
+
await mkdir8(targetPath, { recursive: true });
|
|
4659
|
+
const entries = await readdir8(sourcePath);
|
|
4660
|
+
for (const entry of entries) {
|
|
4661
|
+
const entryFiles = await this.backupItem(sourcePath, entry, targetPath);
|
|
4662
|
+
files.push(...entryFiles);
|
|
4663
|
+
}
|
|
4664
|
+
} else if (stats.isFile()) {
|
|
4665
|
+
await mkdir8(dirname2(targetPath), { recursive: true });
|
|
4666
|
+
await this.copyFile(sourcePath, targetPath);
|
|
4667
|
+
const hash = await this.calculateHash(targetPath);
|
|
4668
|
+
files.push({
|
|
4669
|
+
path: item,
|
|
4670
|
+
hash,
|
|
4671
|
+
size: stats.size
|
|
4672
|
+
});
|
|
4673
|
+
}
|
|
4674
|
+
} catch (error) {
|
|
4675
|
+
logger.debug(`Could not backup ${item}:`, error);
|
|
4676
|
+
}
|
|
4677
|
+
return files;
|
|
4678
|
+
}
|
|
4679
|
+
/**
|
|
4680
|
+
* Copy file with hash calculation
|
|
4681
|
+
*/
|
|
4682
|
+
async copyFile(source, target) {
|
|
4683
|
+
const content = await readFile8(source);
|
|
4684
|
+
await writeFile9(target, content);
|
|
4685
|
+
}
|
|
4686
|
+
/**
|
|
4687
|
+
* Calculate file hash
|
|
4688
|
+
*/
|
|
4689
|
+
async calculateHash(filePath) {
|
|
4690
|
+
try {
|
|
4691
|
+
const content = await readFile8(filePath);
|
|
4692
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
4693
|
+
} catch {
|
|
4694
|
+
return "";
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
/**
|
|
4698
|
+
* List available backups
|
|
4699
|
+
*/
|
|
4700
|
+
async listBackups() {
|
|
4701
|
+
try {
|
|
4702
|
+
const entries = await readdir8(this.backupsDir);
|
|
4703
|
+
const backups = [];
|
|
4704
|
+
for (const entry of entries) {
|
|
4705
|
+
const backupPath = join14(this.backupsDir, entry);
|
|
4706
|
+
const manifestPath = join14(backupPath, "backup-manifest.json");
|
|
4707
|
+
try {
|
|
4708
|
+
const manifestContent = await readFile8(manifestPath, "utf-8");
|
|
4709
|
+
const manifest = JSON.parse(manifestContent);
|
|
4710
|
+
const size = await this.calculateBackupSize(backupPath);
|
|
4711
|
+
backups.push({
|
|
4712
|
+
manifest,
|
|
4713
|
+
path: backupPath,
|
|
4714
|
+
size
|
|
4715
|
+
});
|
|
4716
|
+
} catch {
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
backups.sort(
|
|
4720
|
+
(a, b) => new Date(b.manifest.timestamp).getTime() - new Date(a.manifest.timestamp).getTime()
|
|
4721
|
+
);
|
|
4722
|
+
return backups;
|
|
4723
|
+
} catch {
|
|
4724
|
+
return [];
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
/**
|
|
4728
|
+
* Calculate backup directory size
|
|
4729
|
+
*/
|
|
4730
|
+
async calculateBackupSize(backupPath) {
|
|
4731
|
+
let totalSize = 0;
|
|
4732
|
+
try {
|
|
4733
|
+
const calculate = async (dir) => {
|
|
4734
|
+
const entries = await readdir8(dir);
|
|
4735
|
+
for (const entry of entries) {
|
|
4736
|
+
const entryPath = join14(dir, entry);
|
|
4737
|
+
const stats = await stat2(entryPath);
|
|
4738
|
+
if (stats.isDirectory()) {
|
|
4739
|
+
await calculate(entryPath);
|
|
4740
|
+
} else {
|
|
4741
|
+
totalSize += stats.size;
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4744
|
+
};
|
|
4745
|
+
await calculate(backupPath);
|
|
4746
|
+
} catch {
|
|
4747
|
+
}
|
|
4748
|
+
return totalSize;
|
|
4749
|
+
}
|
|
4750
|
+
/**
|
|
4751
|
+
* Restore from backup
|
|
4752
|
+
*/
|
|
4753
|
+
async restoreBackup(backupId) {
|
|
4754
|
+
try {
|
|
4755
|
+
const backups = await this.listBackups();
|
|
4756
|
+
const backup = backups.find((b) => b.manifest.backupId === backupId);
|
|
4757
|
+
if (!backup) {
|
|
4758
|
+
logger.error(`Backup not found: ${backupId}`);
|
|
4759
|
+
return false;
|
|
4760
|
+
}
|
|
4761
|
+
logger.info(`Restoring from backup: ${backupId}`);
|
|
4762
|
+
const isValid = await this.validateBackup(backup);
|
|
4763
|
+
if (!isValid) {
|
|
4764
|
+
logger.error("Backup validation failed");
|
|
4765
|
+
return false;
|
|
4766
|
+
}
|
|
4767
|
+
for (const file of backup.manifest.files) {
|
|
4768
|
+
const sourcePath = join14(backup.path, file.path);
|
|
4769
|
+
const targetPath = join14(this.configPath, file.path);
|
|
4770
|
+
await mkdir8(dirname2(targetPath), { recursive: true });
|
|
4771
|
+
await this.copyFile(sourcePath, targetPath);
|
|
4772
|
+
}
|
|
4773
|
+
logger.success(`\u2713 Backup restored: ${backupId}`);
|
|
4774
|
+
return true;
|
|
4775
|
+
} catch (error) {
|
|
4776
|
+
logger.error("Failed to restore backup:", error);
|
|
4777
|
+
return false;
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
/**
|
|
4781
|
+
* Validate backup integrity
|
|
4782
|
+
*/
|
|
4783
|
+
async validateBackup(backup) {
|
|
4784
|
+
try {
|
|
4785
|
+
const manifestPath = join14(backup.path, "backup-manifest.json");
|
|
4786
|
+
await readFile8(manifestPath, "utf-8");
|
|
4787
|
+
for (const file of backup.manifest.files) {
|
|
4788
|
+
const filePath = join14(backup.path, file.path);
|
|
4789
|
+
await stat2(filePath);
|
|
4790
|
+
const currentHash = await this.calculateHash(filePath);
|
|
4791
|
+
if (currentHash !== file.hash) {
|
|
4792
|
+
logger.warn(`File hash mismatch: ${file.path}`);
|
|
4793
|
+
return false;
|
|
4794
|
+
}
|
|
4795
|
+
}
|
|
4796
|
+
return true;
|
|
4797
|
+
} catch (error) {
|
|
4798
|
+
logger.debug("Backup validation failed:", error);
|
|
4799
|
+
return false;
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
/**
|
|
4803
|
+
* Delete backup
|
|
4804
|
+
*/
|
|
4805
|
+
async deleteBackup(backupId) {
|
|
4806
|
+
try {
|
|
4807
|
+
const backups = await this.listBackups();
|
|
4808
|
+
const backup = backups.find((b) => b.manifest.backupId === backupId);
|
|
4809
|
+
if (!backup) {
|
|
4810
|
+
return false;
|
|
4811
|
+
}
|
|
4812
|
+
const entries = await readdir8(backup.path);
|
|
4813
|
+
for (const entry of entries) {
|
|
4814
|
+
const entryPath = join14(backup.path, entry);
|
|
4815
|
+
const stats = await stat2(entryPath);
|
|
4816
|
+
if (stats.isDirectory()) {
|
|
4817
|
+
await this.removeDirectory(entryPath);
|
|
4818
|
+
} else {
|
|
4819
|
+
await unlink(entryPath);
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
logger.success(`\u2713 Backup deleted: ${backupId}`);
|
|
4823
|
+
return true;
|
|
4824
|
+
} catch (error) {
|
|
4825
|
+
logger.error("Failed to delete backup:", error);
|
|
4826
|
+
return false;
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4829
|
+
/**
|
|
4830
|
+
* Remove directory recursively
|
|
4831
|
+
*/
|
|
4832
|
+
async removeDirectory(dirPath) {
|
|
4833
|
+
const entries = await readdir8(dirPath);
|
|
4834
|
+
for (const entry of entries) {
|
|
4835
|
+
const entryPath = join14(dirPath, entry);
|
|
4836
|
+
const stats = await stat2(entryPath);
|
|
4837
|
+
if (stats.isDirectory()) {
|
|
4838
|
+
await this.removeDirectory(entryPath);
|
|
4839
|
+
} else {
|
|
4840
|
+
await unlink(entryPath);
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
await unlink(dirPath);
|
|
4844
|
+
}
|
|
4845
|
+
/**
|
|
4846
|
+
* Cleanup old backups (keep only maxBackups)
|
|
4847
|
+
*/
|
|
4848
|
+
async cleanupOldBackups() {
|
|
4849
|
+
try {
|
|
4850
|
+
const backups = await this.listBackups();
|
|
4851
|
+
if (backups.length <= this.maxBackups) {
|
|
4852
|
+
return;
|
|
4853
|
+
}
|
|
4854
|
+
const toDelete = backups.slice(this.maxBackups);
|
|
4855
|
+
for (const backup of toDelete) {
|
|
4856
|
+
await this.deleteBackup(backup.manifest.backupId);
|
|
4857
|
+
}
|
|
4858
|
+
logger.info(`Cleaned up ${toDelete.length} old backup(s)`);
|
|
4859
|
+
} catch (error) {
|
|
4860
|
+
logger.error("Failed to cleanup old backups:", error);
|
|
4861
|
+
}
|
|
4862
|
+
}
|
|
4863
|
+
/**
|
|
4864
|
+
* Format backup size for display
|
|
4865
|
+
*/
|
|
4866
|
+
formatSize(bytes) {
|
|
4867
|
+
const units = ["B", "KB", "MB", "GB"];
|
|
4868
|
+
let size = bytes;
|
|
4869
|
+
let unitIndex = 0;
|
|
4870
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
4871
|
+
size /= 1024;
|
|
4872
|
+
unitIndex++;
|
|
4873
|
+
}
|
|
4874
|
+
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
|
4875
|
+
}
|
|
4876
|
+
};
|
|
4877
|
+
|
|
4878
|
+
// src/core/migration-manager.ts
|
|
4879
|
+
init_esm_shims();
|
|
4880
|
+
init_logger();
|
|
4881
|
+
import { readFile as readFile9, writeFile as writeFile10, readdir as readdir9 } from "fs/promises";
|
|
4882
|
+
import { join as join15 } from "path";
|
|
4883
|
+
var MigrationManager = class {
|
|
4884
|
+
configPath;
|
|
4885
|
+
migrationsDir;
|
|
4886
|
+
constructor(configPath) {
|
|
4887
|
+
this.configPath = configPath;
|
|
4888
|
+
this.migrationsDir = join15(process.cwd(), "src/core/migrations");
|
|
4889
|
+
}
|
|
4890
|
+
/**
|
|
4891
|
+
* Load all available migrations
|
|
4892
|
+
*/
|
|
4893
|
+
async loadMigrations() {
|
|
4894
|
+
const migrations = [];
|
|
4895
|
+
try {
|
|
4896
|
+
const files = await readdir9(this.migrationsDir);
|
|
4897
|
+
for (const file of files) {
|
|
4898
|
+
if (file.endsWith(".js") && file.startsWith("migrate-")) {
|
|
4899
|
+
try {
|
|
4900
|
+
const module = await import(join15(this.migrationsDir, file));
|
|
4901
|
+
const migration = module.default || module.migration;
|
|
4902
|
+
if (migration) {
|
|
4903
|
+
migrations.push(migration);
|
|
4904
|
+
}
|
|
4905
|
+
} catch (error) {
|
|
4906
|
+
logger.warn(`Failed to load migration ${file}:`, error);
|
|
4907
|
+
}
|
|
4908
|
+
}
|
|
4909
|
+
}
|
|
4910
|
+
} catch (error) {
|
|
4911
|
+
logger.debug("Could not load migrations:", error);
|
|
4912
|
+
}
|
|
4913
|
+
return migrations.sort((a, b) => a.version.localeCompare(b.version));
|
|
4914
|
+
}
|
|
4915
|
+
/**
|
|
4916
|
+
* Get applied migrations
|
|
4917
|
+
*/
|
|
4918
|
+
async getAppliedMigrations() {
|
|
4919
|
+
const migrationHistoryPath = join15(this.configPath, ".migration-history.json");
|
|
4920
|
+
try {
|
|
4921
|
+
const content = await readFile9(migrationHistoryPath, "utf-8");
|
|
4922
|
+
const history = JSON.parse(content);
|
|
4923
|
+
return history.filter((m) => m.status === "completed").map((m) => m.to);
|
|
4924
|
+
} catch {
|
|
4925
|
+
return [];
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
/**
|
|
4929
|
+
* Run pending migrations
|
|
4930
|
+
*/
|
|
4931
|
+
async runPendingMigrations() {
|
|
4932
|
+
const appliedMigrations = await this.getAppliedMigrations();
|
|
4933
|
+
const allMigrations = await this.loadMigrations();
|
|
4934
|
+
const pendingMigrations = allMigrations.filter(
|
|
4935
|
+
(m) => !appliedMigrations.includes(m.version)
|
|
4936
|
+
);
|
|
4937
|
+
if (pendingMigrations.length === 0) {
|
|
4938
|
+
logger.info("No pending migrations");
|
|
4939
|
+
return { success: true, applied: [], failed: [] };
|
|
4940
|
+
}
|
|
4941
|
+
logger.info(`Running ${pendingMigrations.length} pending migration(s)...`);
|
|
4942
|
+
const applied = [];
|
|
4943
|
+
const failed = [];
|
|
4944
|
+
const migrationHistory = [];
|
|
4945
|
+
for (const migration of pendingMigrations) {
|
|
4946
|
+
try {
|
|
4947
|
+
logger.info(`Running migration: ${migration.version}`);
|
|
4948
|
+
logger.info(` ${migration.description}`);
|
|
4949
|
+
await migration.up();
|
|
4950
|
+
applied.push(migration.version);
|
|
4951
|
+
migrationHistory.push({
|
|
4952
|
+
from: "previous",
|
|
4953
|
+
to: migration.version,
|
|
4954
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4955
|
+
status: "completed"
|
|
4956
|
+
});
|
|
4957
|
+
logger.success(`\u2713 Migration completed: ${migration.version}`);
|
|
4958
|
+
} catch (error) {
|
|
4959
|
+
logger.error(`\u2717 Migration failed: ${migration.version}`, error);
|
|
4960
|
+
failed.push(migration.version);
|
|
4961
|
+
migrationHistory.push({
|
|
4962
|
+
from: "previous",
|
|
4963
|
+
to: migration.version,
|
|
4964
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4965
|
+
status: "failed"
|
|
4966
|
+
});
|
|
4967
|
+
break;
|
|
4968
|
+
}
|
|
4969
|
+
}
|
|
4970
|
+
await this.updateMigrationHistory(migrationHistory);
|
|
4971
|
+
return {
|
|
4972
|
+
success: failed.length === 0,
|
|
4973
|
+
applied,
|
|
4974
|
+
failed
|
|
4975
|
+
};
|
|
4976
|
+
}
|
|
4977
|
+
/**
|
|
4978
|
+
* Rollback migration
|
|
4979
|
+
*/
|
|
4980
|
+
async rollbackMigration(version) {
|
|
4981
|
+
try {
|
|
4982
|
+
const applied = await this.getAppliedMigrations();
|
|
4983
|
+
if (!applied.includes(version)) {
|
|
4984
|
+
logger.error(`Migration not found in applied list: ${version}`);
|
|
4985
|
+
return false;
|
|
4986
|
+
}
|
|
4987
|
+
const allMigrations = await this.loadMigrations();
|
|
4988
|
+
const migration = allMigrations.find((m) => m.version === version);
|
|
4989
|
+
if (!migration) {
|
|
4990
|
+
logger.error(`Migration file not found: ${version}`);
|
|
4991
|
+
return false;
|
|
4992
|
+
}
|
|
4993
|
+
logger.info(`Rolling back migration: ${version}`);
|
|
4994
|
+
await migration.down();
|
|
4995
|
+
await this.updateMigrationHistoryStatus(version, "rolled-back");
|
|
4996
|
+
logger.success(`\u2713 Migration rolled back: ${version}`);
|
|
4997
|
+
return true;
|
|
4998
|
+
} catch (error) {
|
|
4999
|
+
logger.error("Failed to rollback migration:", error);
|
|
5000
|
+
return false;
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
/**
|
|
5004
|
+
* Update migration history
|
|
5005
|
+
*/
|
|
5006
|
+
async updateMigrationHistory(entries) {
|
|
5007
|
+
const historyPath = join15(this.configPath, ".migration-history.json");
|
|
5008
|
+
try {
|
|
5009
|
+
let history = [];
|
|
5010
|
+
try {
|
|
5011
|
+
const content = await readFile9(historyPath, "utf-8");
|
|
5012
|
+
history = JSON.parse(content);
|
|
5013
|
+
} catch {
|
|
5014
|
+
}
|
|
5015
|
+
history.push(...entries);
|
|
5016
|
+
await writeFile10(historyPath, JSON.stringify(history, null, 2));
|
|
5017
|
+
} catch (error) {
|
|
5018
|
+
logger.error("Failed to update migration history:", error);
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
5021
|
+
/**
|
|
5022
|
+
* Update migration history status
|
|
5023
|
+
*/
|
|
5024
|
+
async updateMigrationHistoryStatus(version, status) {
|
|
5025
|
+
const historyPath = join15(this.configPath, ".migration-history.json");
|
|
5026
|
+
try {
|
|
5027
|
+
const content = await readFile9(historyPath, "utf-8");
|
|
5028
|
+
const history = JSON.parse(content);
|
|
5029
|
+
const updated = history.map(
|
|
5030
|
+
(m) => m.to === version ? { ...m, status } : m
|
|
5031
|
+
);
|
|
5032
|
+
await writeFile10(historyPath, JSON.stringify(updated, null, 2));
|
|
5033
|
+
} catch (error) {
|
|
5034
|
+
logger.error("Failed to update migration history status:", error);
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
5037
|
+
/**
|
|
5038
|
+
* Get migration history
|
|
5039
|
+
*/
|
|
5040
|
+
async getMigrationHistory() {
|
|
5041
|
+
const historyPath = join15(this.configPath, ".migration-history.json");
|
|
5042
|
+
try {
|
|
5043
|
+
const content = await readFile9(historyPath, "utf-8");
|
|
5044
|
+
return JSON.parse(content);
|
|
5045
|
+
} catch {
|
|
5046
|
+
return [];
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
/**
|
|
5050
|
+
* Check if migration is needed for version
|
|
5051
|
+
*/
|
|
5052
|
+
async needsMigration(_fromVersion) {
|
|
5053
|
+
const applied = await this.getAppliedMigrations();
|
|
5054
|
+
const allMigrations = await this.loadMigrations();
|
|
5055
|
+
const pendingMigrations = allMigrations.filter(
|
|
5056
|
+
(m) => !applied.includes(m.version)
|
|
5057
|
+
);
|
|
5058
|
+
return pendingMigrations.length > 0;
|
|
5059
|
+
}
|
|
5060
|
+
};
|
|
5061
|
+
|
|
5062
|
+
// src/core/sync-engine.ts
|
|
5063
|
+
init_logger();
|
|
5064
|
+
init_paths();
|
|
5065
|
+
var SyncEngine = class {
|
|
5066
|
+
versionManager;
|
|
5067
|
+
backupManager;
|
|
5068
|
+
migrationManager;
|
|
5069
|
+
constructor(config) {
|
|
5070
|
+
this.versionManager = new VersionManager(config);
|
|
5071
|
+
this.backupManager = new BackupManager(config.configPath);
|
|
5072
|
+
this.migrationManager = new MigrationManager(config.configPath);
|
|
5073
|
+
}
|
|
5074
|
+
/**
|
|
5075
|
+
* Check for updates without applying
|
|
5076
|
+
*/
|
|
5077
|
+
async checkForUpdates() {
|
|
5078
|
+
try {
|
|
5079
|
+
const changes = await this.versionManager.checkForUpdates();
|
|
5080
|
+
if (changes.hasUpdate) {
|
|
5081
|
+
this.displayUpdateInfo(changes);
|
|
5082
|
+
} else {
|
|
5083
|
+
console.log(chalk2.green("\u2713 Your AIKit is up to date"));
|
|
5084
|
+
console.log(` Installed: ${changes.fromVersion}`);
|
|
5085
|
+
console.log(` Latest: ${changes.toVersion}`);
|
|
5086
|
+
}
|
|
5087
|
+
return changes;
|
|
5088
|
+
} catch (error) {
|
|
5089
|
+
logger.error("Failed to check for updates:", error);
|
|
5090
|
+
return null;
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
/**
|
|
5094
|
+
* Preview changes without applying
|
|
5095
|
+
*/
|
|
5096
|
+
async previewUpdate() {
|
|
5097
|
+
try {
|
|
5098
|
+
console.log(chalk2.bold("\n\u{1F50D} Previewing update...\n"));
|
|
5099
|
+
const changes = await this.versionManager.checkForUpdates();
|
|
5100
|
+
if (!changes.hasUpdate) {
|
|
5101
|
+
console.log(chalk2.green("\u2713 No updates available"));
|
|
5102
|
+
return false;
|
|
5103
|
+
}
|
|
5104
|
+
await this.displayChanges(changes);
|
|
5105
|
+
console.log(chalk2.yellow("\n\u26A0\uFE0F This is a preview - no changes will be made."));
|
|
5106
|
+
console.log(chalk2.gray("Use `aikit sync apply` to apply these changes."));
|
|
5107
|
+
return true;
|
|
5108
|
+
} catch (error) {
|
|
5109
|
+
logger.error("Failed to preview update:", error);
|
|
5110
|
+
return false;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
/**
|
|
5114
|
+
* Apply update
|
|
5115
|
+
*/
|
|
5116
|
+
async applyUpdate(options = {}) {
|
|
5117
|
+
try {
|
|
5118
|
+
const changes = await this.versionManager.checkForUpdates();
|
|
5119
|
+
if (!changes.hasUpdate) {
|
|
5120
|
+
console.log(chalk2.green("\u2713 Already up to date"));
|
|
5121
|
+
return {
|
|
5122
|
+
success: true,
|
|
5123
|
+
newSkills: [],
|
|
5124
|
+
updatedSkills: [],
|
|
5125
|
+
removedSkills: [],
|
|
5126
|
+
migrationsRun: []
|
|
5127
|
+
};
|
|
5128
|
+
}
|
|
5129
|
+
await this.displayChanges(changes);
|
|
5130
|
+
if (!options.force) {
|
|
5131
|
+
const { confirmed } = await inquirer.prompt([{
|
|
5132
|
+
type: "confirm",
|
|
5133
|
+
name: "confirmed",
|
|
5134
|
+
message: "Continue with update?",
|
|
5135
|
+
default: false
|
|
5136
|
+
}]);
|
|
5137
|
+
if (!confirmed) {
|
|
5138
|
+
console.log(chalk2.yellow("Update cancelled"));
|
|
5139
|
+
return {
|
|
5140
|
+
success: false,
|
|
5141
|
+
newSkills: [],
|
|
5142
|
+
updatedSkills: [],
|
|
5143
|
+
removedSkills: [],
|
|
5144
|
+
migrationsRun: []
|
|
5145
|
+
};
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
5148
|
+
let backupId = void 0;
|
|
5149
|
+
if (!options.dryRun && options.backup !== false) {
|
|
5150
|
+
console.log(chalk2.bold("\n\u{1F4E6} Creating backup..."));
|
|
5151
|
+
const backupResult = await this.backupManager.createBackup(
|
|
5152
|
+
changes.fromVersion,
|
|
5153
|
+
changes.toVersion
|
|
5154
|
+
);
|
|
5155
|
+
if (!backupResult) {
|
|
5156
|
+
throw new Error("Failed to create backup");
|
|
5157
|
+
}
|
|
5158
|
+
backupId = backupResult;
|
|
5159
|
+
}
|
|
5160
|
+
for (const conflict of changes.conflicts) {
|
|
5161
|
+
await this.resolveConflict(conflict);
|
|
5162
|
+
}
|
|
5163
|
+
console.log(chalk2.bold("\n\u{1F504} Running migrations..."));
|
|
5164
|
+
const migrationResult = await this.migrationManager.runPendingMigrations();
|
|
5165
|
+
if (!migrationResult.success) {
|
|
5166
|
+
throw new Error(`Migration failed: ${migrationResult.failed.join(", ")}`);
|
|
5167
|
+
}
|
|
5168
|
+
console.log(chalk2.bold("\n\u{1F4DD} Updating skills..."));
|
|
5169
|
+
const updateResult = await this.updateSkills(changes, options);
|
|
5170
|
+
await this.versionManager.updateVersion(changes.toVersion);
|
|
5171
|
+
if (backupId) {
|
|
5172
|
+
const allSkills = await this.versionManager.loadSkillHashes(paths.skills(paths.globalConfig()));
|
|
5173
|
+
await this.versionManager.saveInstalledSkills(allSkills);
|
|
5174
|
+
}
|
|
5175
|
+
console.log(chalk2.green("\n\u2705 Update complete!"));
|
|
5176
|
+
this.displaySummary({
|
|
5177
|
+
success: true,
|
|
5178
|
+
backupId,
|
|
5179
|
+
...updateResult,
|
|
5180
|
+
migrationsRun: migrationResult.applied
|
|
5181
|
+
});
|
|
5182
|
+
return {
|
|
5183
|
+
success: true,
|
|
5184
|
+
backupId,
|
|
5185
|
+
...updateResult,
|
|
5186
|
+
migrationsRun: migrationResult.applied
|
|
5187
|
+
};
|
|
5188
|
+
} catch (error) {
|
|
5189
|
+
logger.error("Update failed:", error);
|
|
5190
|
+
console.log(chalk2.red("\n\u274C Update failed"));
|
|
5191
|
+
return {
|
|
5192
|
+
success: false,
|
|
5193
|
+
newSkills: [],
|
|
5194
|
+
updatedSkills: [],
|
|
5195
|
+
removedSkills: [],
|
|
5196
|
+
migrationsRun: []
|
|
5197
|
+
};
|
|
5198
|
+
}
|
|
5199
|
+
}
|
|
5200
|
+
/**
|
|
5201
|
+
* Rollback to previous backup
|
|
5202
|
+
*/
|
|
5203
|
+
async rollback(backupId) {
|
|
5204
|
+
try {
|
|
5205
|
+
console.log(chalk2.bold("\n\u{1F504} Rollback...\n"));
|
|
5206
|
+
if (!backupId) {
|
|
5207
|
+
const backups = await this.backupManager.listBackups();
|
|
5208
|
+
if (backups.length === 0) {
|
|
5209
|
+
console.log(chalk2.yellow("No backups available"));
|
|
5210
|
+
return false;
|
|
5211
|
+
}
|
|
5212
|
+
const { selectedBackup } = await inquirer.prompt([{
|
|
5213
|
+
type: "list",
|
|
5214
|
+
name: "selectedBackup",
|
|
5215
|
+
message: "Select backup to restore:",
|
|
5216
|
+
choices: backups.map((b) => ({
|
|
5217
|
+
name: `${b.manifest.backupId} (${b.manifest.fromVersion} \u2192 ${b.manifest.toVersion})`,
|
|
5218
|
+
value: b.manifest.backupId
|
|
5219
|
+
}))
|
|
5220
|
+
}]);
|
|
5221
|
+
backupId = selectedBackup;
|
|
5222
|
+
}
|
|
5223
|
+
if (!backupId) {
|
|
5224
|
+
console.log(chalk2.yellow("No backup ID provided"));
|
|
5225
|
+
return false;
|
|
5226
|
+
}
|
|
5227
|
+
const success = await this.backupManager.restoreBackup(backupId);
|
|
5228
|
+
if (success) {
|
|
5229
|
+
console.log(chalk2.green("\u2713 Rollback complete"));
|
|
5230
|
+
return true;
|
|
5231
|
+
}
|
|
5232
|
+
return false;
|
|
5233
|
+
} catch (error) {
|
|
5234
|
+
logger.error("Rollback failed:", error);
|
|
5235
|
+
return false;
|
|
5236
|
+
}
|
|
5237
|
+
}
|
|
5238
|
+
/**
|
|
5239
|
+
* Display update information
|
|
5240
|
+
*/
|
|
5241
|
+
displayUpdateInfo(changes) {
|
|
5242
|
+
console.log(chalk2.bold("\n\u{1F4E2} New version available!\n"));
|
|
5243
|
+
console.log(` ${chalk2.cyan("Current:")} ${changes.fromVersion}`);
|
|
5244
|
+
console.log(` ${chalk2.cyan("Latest:")} ${changes.toVersion}
|
|
5245
|
+
`);
|
|
5246
|
+
}
|
|
5247
|
+
/**
|
|
5248
|
+
* Display changes summary
|
|
5249
|
+
*/
|
|
5250
|
+
async displayChanges(changes) {
|
|
5251
|
+
console.log(chalk2.bold("\u{1F4CA} Changes detected:\n"));
|
|
5252
|
+
if (changes.newSkills.length > 0) {
|
|
5253
|
+
console.log(chalk2.green(" New Skills:"));
|
|
5254
|
+
changes.newSkills.forEach((skill) => {
|
|
5255
|
+
console.log(` + ${skill.name} (${skill.category})`);
|
|
5256
|
+
});
|
|
5257
|
+
}
|
|
5258
|
+
if (changes.modifiedSkills.length > 0) {
|
|
5259
|
+
console.log(chalk2.yellow(" Updated Skills:"));
|
|
5260
|
+
changes.modifiedSkills.forEach((skill) => {
|
|
5261
|
+
console.log(` ~ ${skill.name}`);
|
|
5262
|
+
});
|
|
5263
|
+
}
|
|
5264
|
+
if (changes.removedSkills.length > 0) {
|
|
5265
|
+
console.log(chalk2.red(" Removed Skills:"));
|
|
5266
|
+
changes.removedSkills.forEach((skill) => {
|
|
5267
|
+
console.log(` - ${skill.name}`);
|
|
5268
|
+
});
|
|
5269
|
+
}
|
|
5270
|
+
if (changes.conflicts.length > 0) {
|
|
5271
|
+
console.log(chalk2.bold.red(" \u26A0\uFE0F Conflicts:"));
|
|
5272
|
+
changes.conflicts.forEach((conflict) => {
|
|
5273
|
+
console.log(` ! ${conflict.skillName} (user modified)`);
|
|
5274
|
+
});
|
|
5275
|
+
}
|
|
5276
|
+
}
|
|
5277
|
+
/**
|
|
5278
|
+
* Resolve a conflict
|
|
5279
|
+
*/
|
|
5280
|
+
async resolveConflict(conflict) {
|
|
5281
|
+
console.log(chalk2.bold.red(`
|
|
5282
|
+
\u26A0\uFE0F Conflict detected: ${conflict.skillName}
|
|
5283
|
+
`));
|
|
5284
|
+
console.log(chalk2.yellow("Your version differs from official version."));
|
|
5285
|
+
const { action } = await inquirer.prompt([{
|
|
5286
|
+
type: "list",
|
|
5287
|
+
name: "action",
|
|
5288
|
+
message: "Choose action:",
|
|
5289
|
+
choices: [
|
|
5290
|
+
{
|
|
5291
|
+
name: "Keep your version (will be renamed to -custom.md)",
|
|
5292
|
+
value: "preserve"
|
|
5293
|
+
},
|
|
5294
|
+
{
|
|
5295
|
+
name: "Overwrite with official version",
|
|
5296
|
+
value: "overwrite"
|
|
5297
|
+
},
|
|
5298
|
+
{
|
|
5299
|
+
name: "Skip this skill",
|
|
5300
|
+
value: "skip"
|
|
5301
|
+
}
|
|
5302
|
+
]
|
|
5303
|
+
}]);
|
|
5304
|
+
if (action === "skip") {
|
|
5305
|
+
return;
|
|
5306
|
+
}
|
|
5307
|
+
if (action === "overwrite") {
|
|
5308
|
+
return;
|
|
5309
|
+
}
|
|
5310
|
+
console.log(chalk2.yellow(" Your version will be preserved as -custom.md"));
|
|
5311
|
+
}
|
|
5312
|
+
/**
|
|
5313
|
+
* Update skills based on changes
|
|
5314
|
+
*/
|
|
5315
|
+
async updateSkills(changes, options) {
|
|
5316
|
+
const globalSkillsPath = paths.skills(paths.globalConfig());
|
|
5317
|
+
const projectSkillsPath = paths.skills(this.versionManager["config"].configPath);
|
|
5318
|
+
const newSkills = [];
|
|
5319
|
+
const updatedSkills = [];
|
|
5320
|
+
const removedSkills = [];
|
|
5321
|
+
for (const skill of changes.newSkills) {
|
|
5322
|
+
if (!options.dryRun) {
|
|
5323
|
+
await this.installSkill(globalSkillsPath, skill, projectSkillsPath);
|
|
5324
|
+
}
|
|
5325
|
+
newSkills.push(skill.name);
|
|
5326
|
+
console.log(chalk2.green(` + ${skill.name}`));
|
|
5327
|
+
}
|
|
5328
|
+
for (const skill of changes.modifiedSkills) {
|
|
5329
|
+
if (!options.dryRun) {
|
|
5330
|
+
await this.installSkill(globalSkillsPath, skill, projectSkillsPath);
|
|
5331
|
+
}
|
|
5332
|
+
updatedSkills.push(skill.name);
|
|
5333
|
+
console.log(chalk2.yellow(` ~ ${skill.name}`));
|
|
5334
|
+
}
|
|
5335
|
+
for (const skill of changes.removedSkills) {
|
|
5336
|
+
if (!options.dryRun) {
|
|
5337
|
+
await this.archiveSkill(projectSkillsPath, skill);
|
|
5338
|
+
}
|
|
5339
|
+
removedSkills.push(skill.name);
|
|
5340
|
+
console.log(chalk2.red(` - ${skill.name} (archived)`));
|
|
5341
|
+
}
|
|
5342
|
+
return {
|
|
5343
|
+
newSkills,
|
|
5344
|
+
updatedSkills,
|
|
5345
|
+
removedSkills
|
|
5346
|
+
};
|
|
5347
|
+
}
|
|
5348
|
+
/**
|
|
5349
|
+
* Install a skill
|
|
5350
|
+
*/
|
|
5351
|
+
async installSkill(sourceDir, skill, targetDir) {
|
|
5352
|
+
const sourcePath = join16(sourceDir, skill.category, `${skill.name}.md`);
|
|
5353
|
+
const targetPath = join16(targetDir, skill.category, `${skill.name}.md`);
|
|
5354
|
+
await mkdir9(dirname3(targetPath), { recursive: true });
|
|
5355
|
+
await copyFile(sourcePath, targetPath);
|
|
5356
|
+
}
|
|
5357
|
+
/**
|
|
5358
|
+
* Archive a removed skill
|
|
5359
|
+
*/
|
|
5360
|
+
async archiveSkill(targetDir, skill) {
|
|
5361
|
+
const sourcePath = join16(targetDir, skill.category, `${skill.name}.md`);
|
|
5362
|
+
const targetPath = join16(targetDir, skill.category, `${skill.name}-deprecated.md`);
|
|
5363
|
+
try {
|
|
5364
|
+
const content = await readFile10(sourcePath, "utf-8");
|
|
5365
|
+
const deprecatedNotice = `---
|
|
5366
|
+
\u26A0\uFE0F DEPRECATED: This skill has been removed
|
|
5367
|
+
|
|
5368
|
+
Deprecation date: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
5369
|
+
Reason: Check release notes for replacement
|
|
5370
|
+
---
|
|
5371
|
+
|
|
5372
|
+
${content}`;
|
|
5373
|
+
await mkdir9(dirname3(targetPath), { recursive: true });
|
|
5374
|
+
await writeFile11(targetPath, deprecatedNotice);
|
|
5375
|
+
} catch (error) {
|
|
5376
|
+
if (error.code === "ENOENT") {
|
|
5377
|
+
console.log(chalk2.yellow(` - ${skill.name} (not found, skipping)`));
|
|
5378
|
+
} else {
|
|
5379
|
+
throw error;
|
|
5380
|
+
}
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
/**
|
|
5384
|
+
* Display sync summary
|
|
5385
|
+
*/
|
|
5386
|
+
displaySummary(result) {
|
|
5387
|
+
console.log(chalk2.bold("\n\u{1F4CB} Summary:\n"));
|
|
5388
|
+
console.log(` Updated from: ${chalk2.cyan(result.backupId || "N/A")}`);
|
|
5389
|
+
console.log(` Updated to: ${chalk2.cyan("current")}`);
|
|
5390
|
+
console.log();
|
|
5391
|
+
if (result.newSkills.length > 0) {
|
|
5392
|
+
console.log(chalk2.green(` ${result.newSkills.length} new skills installed`));
|
|
5393
|
+
}
|
|
5394
|
+
if (result.updatedSkills.length > 0) {
|
|
5395
|
+
console.log(chalk2.yellow(` ${result.updatedSkills.length} skills updated`));
|
|
5396
|
+
}
|
|
5397
|
+
if (result.removedSkills.length > 0) {
|
|
5398
|
+
console.log(chalk2.red(` ${result.removedSkills.length} skills archived`));
|
|
5399
|
+
}
|
|
5400
|
+
if (result.migrationsRun.length > 0) {
|
|
5401
|
+
console.log(chalk2.blue(` ${result.migrationsRun.length} migrations run`));
|
|
5402
|
+
}
|
|
5403
|
+
if (result.backupId) {
|
|
5404
|
+
console.log(chalk2.gray(`
|
|
5405
|
+
Rollback available: aikit sync rollback ${result.backupId}`));
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5408
|
+
};
|
|
5409
|
+
|
|
5410
|
+
// src/cli.ts
|
|
4224
5411
|
var program = new Command();
|
|
4225
5412
|
program.name("aikit").description("Open-source AI coding agent toolkit for OpenCode").version(getVersion());
|
|
4226
5413
|
program.command("init").description("Initialize AIKit configuration").option("-g, --global", "Initialize global configuration").option("-p, --project", "Initialize project-level configuration").action(async (options) => {
|
|
4227
5414
|
const configDir = options.global ? paths.globalConfig() : paths.projectConfig();
|
|
4228
|
-
console.log(
|
|
5415
|
+
console.log(chalk3.bold("\n\u{1F680} AIKit Setup\n"));
|
|
4229
5416
|
logger.info(`Initializing AIKit in ${configDir}...`);
|
|
4230
5417
|
try {
|
|
4231
5418
|
await initializeConfig(configDir, options.global);
|
|
@@ -4237,17 +5424,17 @@ program.command("init").description("Initialize AIKit configuration").option("-g
|
|
|
4237
5424
|
if (result.count > 0) {
|
|
4238
5425
|
logger.success(`\u2713 Synced ${result.count} skills`);
|
|
4239
5426
|
}
|
|
4240
|
-
console.log(
|
|
5427
|
+
console.log(chalk3.bold("\n\u{1F50D} Checking CLI tools...\n"));
|
|
4241
5428
|
const cliTools = await CliDetector.checkAll();
|
|
4242
5429
|
const installableTools = CliDetector.filterInstallable(cliTools);
|
|
4243
5430
|
for (const tool of cliTools) {
|
|
4244
|
-
const status = tool.
|
|
4245
|
-
const version = tool.version ?
|
|
4246
|
-
console.log(` ${status} ${
|
|
5431
|
+
const status = tool.installed ? chalk3.green("\u2713 Installed") : chalk3.yellow("\u2717 Not installed");
|
|
5432
|
+
const version = tool.version ? chalk3.gray(` (${tool.version})`) : "";
|
|
5433
|
+
console.log(` ${status} ${chalk3.cyan(tool.displayName)}${version}`);
|
|
4247
5434
|
}
|
|
4248
5435
|
if (installableTools.length > 0) {
|
|
4249
5436
|
console.log();
|
|
4250
|
-
const { action } = await
|
|
5437
|
+
const { action } = await inquirer2.prompt([
|
|
4251
5438
|
{
|
|
4252
5439
|
type: "list",
|
|
4253
5440
|
name: "action",
|
|
@@ -4287,7 +5474,7 @@ program.command("init").description("Initialize AIKit configuration").option("-g
|
|
|
4287
5474
|
console.log();
|
|
4288
5475
|
logger.success("\u2713 CLI tools installed");
|
|
4289
5476
|
} else {
|
|
4290
|
-
const { installTools } = await
|
|
5477
|
+
const { installTools } = await inquirer2.prompt([
|
|
4291
5478
|
{
|
|
4292
5479
|
type: "checkbox",
|
|
4293
5480
|
name: "installTools",
|
|
@@ -4331,16 +5518,16 @@ program.command("init").description("Initialize AIKit configuration").option("-g
|
|
|
4331
5518
|
}
|
|
4332
5519
|
const opencodePath = paths.opencodeConfig();
|
|
4333
5520
|
await installToOpenCode(opencodePath);
|
|
4334
|
-
console.log(
|
|
5521
|
+
console.log(chalk3.bold("\n\u2728 AIKit is ready!\n"));
|
|
4335
5522
|
console.log("Usage in OpenCode:");
|
|
4336
|
-
console.log(
|
|
4337
|
-
console.log(
|
|
4338
|
-
console.log(
|
|
4339
|
-
console.log(
|
|
4340
|
-
console.log(
|
|
4341
|
-
console.log(
|
|
4342
|
-
console.log(
|
|
4343
|
-
console.log("\nPress " +
|
|
5523
|
+
console.log(chalk3.cyan(" /skills") + " - List all available skills");
|
|
5524
|
+
console.log(chalk3.cyan(" /plan") + " - Create implementation plan");
|
|
5525
|
+
console.log(chalk3.cyan(" /tdd") + " - Test-driven development");
|
|
5526
|
+
console.log(chalk3.cyan(" /debug") + " - Systematic debugging");
|
|
5527
|
+
console.log(chalk3.cyan(" /review") + " - Code review checklist");
|
|
5528
|
+
console.log(chalk3.cyan(" /git") + " - Git workflow");
|
|
5529
|
+
console.log(chalk3.cyan(" /frontend-aesthetics") + " - UI/UX guidelines");
|
|
5530
|
+
console.log("\nPress " + chalk3.bold("Ctrl+K") + " in OpenCode to see all commands.\n");
|
|
4344
5531
|
}
|
|
4345
5532
|
} catch (error) {
|
|
4346
5533
|
logger.error("Failed to initialize AIKit:", error);
|
|
@@ -4358,6 +5545,32 @@ program.command("install").description("Install AIKit to OpenCode configuration"
|
|
|
4358
5545
|
process.exit(1);
|
|
4359
5546
|
}
|
|
4360
5547
|
});
|
|
5548
|
+
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) => {
|
|
5549
|
+
const config = await loadConfig();
|
|
5550
|
+
const syncEngine = new SyncEngine(config);
|
|
5551
|
+
if (!subcommand) {
|
|
5552
|
+
await syncEngine.applyUpdate(options);
|
|
5553
|
+
} else {
|
|
5554
|
+
switch (subcommand) {
|
|
5555
|
+
case "check":
|
|
5556
|
+
await syncEngine.checkForUpdates();
|
|
5557
|
+
break;
|
|
5558
|
+
case "preview":
|
|
5559
|
+
await syncEngine.previewUpdate();
|
|
5560
|
+
break;
|
|
5561
|
+
case "apply":
|
|
5562
|
+
await syncEngine.applyUpdate(options);
|
|
5563
|
+
break;
|
|
5564
|
+
case "rollback":
|
|
5565
|
+
await syncEngine.rollback();
|
|
5566
|
+
break;
|
|
5567
|
+
default:
|
|
5568
|
+
logger.error(`Unknown subcommand: ${subcommand}`);
|
|
5569
|
+
console.log(chalk3.gray("Available subcommands: check, preview, apply, rollback"));
|
|
5570
|
+
process.exit(1);
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
});
|
|
4361
5574
|
var skillsCmd = program.command("skills").description("Manage skills");
|
|
4362
5575
|
skillsCmd.command("list").description("List available skills and tools with their configuration status").action(async () => {
|
|
4363
5576
|
const config = await loadConfig();
|
|
@@ -4366,31 +5579,31 @@ skillsCmd.command("list").description("List available skills and tools with thei
|
|
|
4366
5579
|
const { ToolConfigManager: ToolConfigManager2 } = await Promise.resolve().then(() => (init_tool_config(), tool_config_exports));
|
|
4367
5580
|
const toolConfigManager = new ToolConfigManager2(config);
|
|
4368
5581
|
const tools = await toolConfigManager.listTools();
|
|
4369
|
-
console.log(
|
|
5582
|
+
console.log(chalk3.bold("\n\u{1F4DA} Available Skills:\n"));
|
|
4370
5583
|
for (const skill of skills) {
|
|
4371
|
-
console.log(` ${
|
|
5584
|
+
console.log(` ${chalk3.cyan(skill.name)} - ${skill.description}`);
|
|
4372
5585
|
}
|
|
4373
|
-
console.log(
|
|
5586
|
+
console.log(chalk3.bold("\n\u{1F527} Available Tools:\n"));
|
|
4374
5587
|
for (const tool of tools) {
|
|
4375
5588
|
let statusIcon = " ";
|
|
4376
5589
|
let statusText = "";
|
|
4377
5590
|
if (tool.status === "ready") {
|
|
4378
|
-
statusIcon =
|
|
4379
|
-
statusText =
|
|
5591
|
+
statusIcon = chalk3.green("\u2713");
|
|
5592
|
+
statusText = chalk3.gray("(ready)");
|
|
4380
5593
|
} else if (tool.status === "needs_config") {
|
|
4381
|
-
statusIcon =
|
|
4382
|
-
statusText =
|
|
5594
|
+
statusIcon = chalk3.yellow("\u26A0");
|
|
5595
|
+
statusText = chalk3.yellow("(needs config)");
|
|
4383
5596
|
} else if (tool.status === "error") {
|
|
4384
|
-
statusIcon =
|
|
4385
|
-
statusText =
|
|
5597
|
+
statusIcon = chalk3.red("\u2717");
|
|
5598
|
+
statusText = chalk3.red("(error)");
|
|
4386
5599
|
}
|
|
4387
|
-
console.log(` ${statusIcon} ${
|
|
5600
|
+
console.log(` ${statusIcon} ${chalk3.cyan(tool.name)} - ${tool.description} ${statusText}`);
|
|
4388
5601
|
if (tool.errorMessage) {
|
|
4389
|
-
console.log(` ${
|
|
5602
|
+
console.log(` ${chalk3.red("Error:")} ${tool.errorMessage}`);
|
|
4390
5603
|
}
|
|
4391
5604
|
}
|
|
4392
5605
|
console.log();
|
|
4393
|
-
console.log(
|
|
5606
|
+
console.log(chalk3.gray('Tip: Use "aikit skills <tool-name> config" to configure a tool\n'));
|
|
4394
5607
|
});
|
|
4395
5608
|
skillsCmd.command("show <name>").description("Show skill details").action(async (name) => {
|
|
4396
5609
|
const config = await loadConfig();
|
|
@@ -4400,11 +5613,11 @@ skillsCmd.command("show <name>").description("Show skill details").action(async
|
|
|
4400
5613
|
logger.error(`Skill not found: ${name}`);
|
|
4401
5614
|
process.exit(1);
|
|
4402
5615
|
}
|
|
4403
|
-
console.log(
|
|
5616
|
+
console.log(chalk3.bold(`
|
|
4404
5617
|
\u{1F4D6} Skill: ${skill.name}
|
|
4405
5618
|
`));
|
|
4406
|
-
console.log(
|
|
4407
|
-
console.log(
|
|
5619
|
+
console.log(chalk3.gray(skill.description));
|
|
5620
|
+
console.log(chalk3.bold("\nWorkflow:"));
|
|
4408
5621
|
console.log(skill.content);
|
|
4409
5622
|
});
|
|
4410
5623
|
skillsCmd.command("create <name>").description("Create a new skill").action(async (name) => {
|
|
@@ -4420,11 +5633,11 @@ skillsCmd.command("sync").description("Sync global skills to project").action(as
|
|
|
4420
5633
|
if (result.count === 0) {
|
|
4421
5634
|
logger.info("Skills already in sync or no global skills to sync");
|
|
4422
5635
|
} else {
|
|
4423
|
-
console.log(
|
|
5636
|
+
console.log(chalk3.bold(`
|
|
4424
5637
|
\u2713 Synced ${result.count} skills to project:
|
|
4425
5638
|
`));
|
|
4426
5639
|
for (const skill of result.synced) {
|
|
4427
|
-
console.log(` ${
|
|
5640
|
+
console.log(` ${chalk3.cyan("\u2022")} ${skill}`);
|
|
4428
5641
|
}
|
|
4429
5642
|
console.log();
|
|
4430
5643
|
}
|
|
@@ -4436,18 +5649,18 @@ skillsCmd.command("config <tool-name>").description("Configure a tool (e.g., con
|
|
|
4436
5649
|
const tool = await toolConfigManager.getToolConfig(toolName);
|
|
4437
5650
|
if (!tool) {
|
|
4438
5651
|
logger.error(`Tool not found: ${toolName}`);
|
|
4439
|
-
console.log(
|
|
5652
|
+
console.log(chalk3.gray("\nAvailable tools:"));
|
|
4440
5653
|
const tools = await toolConfigManager.listTools();
|
|
4441
5654
|
for (const t of tools) {
|
|
4442
|
-
console.log(` - ${
|
|
5655
|
+
console.log(` - ${chalk3.cyan(t.name)}`);
|
|
4443
5656
|
}
|
|
4444
5657
|
console.log();
|
|
4445
5658
|
process.exit(1);
|
|
4446
5659
|
}
|
|
4447
|
-
console.log(
|
|
5660
|
+
console.log(chalk3.bold(`
|
|
4448
5661
|
\u{1F527} Configuring: ${tool.name}
|
|
4449
5662
|
`));
|
|
4450
|
-
console.log(
|
|
5663
|
+
console.log(chalk3.gray(tool.description));
|
|
4451
5664
|
console.log();
|
|
4452
5665
|
if (tool.configMethod === "oauth") {
|
|
4453
5666
|
if (toolName === "figma-analysis") {
|
|
@@ -4455,12 +5668,12 @@ skillsCmd.command("config <tool-name>").description("Configure a tool (e.g., con
|
|
|
4455
5668
|
const oauth = new FigmaOAuth2(toolConfigManager);
|
|
4456
5669
|
try {
|
|
4457
5670
|
const token = await oauth.authenticate();
|
|
4458
|
-
console.log(
|
|
5671
|
+
console.log(chalk3.gray("\nValidating token..."));
|
|
4459
5672
|
const isValid = await oauth.validateToken(token);
|
|
4460
5673
|
if (isValid) {
|
|
4461
5674
|
logger.success(`
|
|
4462
5675
|
\u2705 ${tool.name} configured successfully!`);
|
|
4463
|
-
console.log(
|
|
5676
|
+
console.log(chalk3.gray("\nYou can now use the /analyze-figma command in OpenCode.\n"));
|
|
4464
5677
|
} else {
|
|
4465
5678
|
await toolConfigManager.updateToolConfig(toolName, {
|
|
4466
5679
|
status: "error",
|
|
@@ -4499,20 +5712,20 @@ skillsCmd.command("*").description("Configure a tool (e.g., figma-analysis confi
|
|
|
4499
5712
|
const tool = await toolConfigManager.getToolConfig(toolName);
|
|
4500
5713
|
if (!tool) {
|
|
4501
5714
|
logger.error(`Tool not found: ${toolName}`);
|
|
4502
|
-
console.log(
|
|
5715
|
+
console.log(chalk3.gray("\nAvailable tools:"));
|
|
4503
5716
|
const tools = await toolConfigManager.listTools();
|
|
4504
5717
|
for (const t of tools) {
|
|
4505
|
-
console.log(` - ${
|
|
5718
|
+
console.log(` - ${chalk3.cyan(t.name)}`);
|
|
4506
5719
|
}
|
|
4507
5720
|
console.log();
|
|
4508
|
-
console.log(
|
|
5721
|
+
console.log(chalk3.gray("Tip: If you meant to show a skill, use: aikit skills show <name>"));
|
|
4509
5722
|
console.log();
|
|
4510
5723
|
process.exit(1);
|
|
4511
5724
|
}
|
|
4512
|
-
console.log(
|
|
5725
|
+
console.log(chalk3.bold(`
|
|
4513
5726
|
\u{1F527} Configuring: ${tool.name}
|
|
4514
5727
|
`));
|
|
4515
|
-
console.log(
|
|
5728
|
+
console.log(chalk3.gray(tool.description));
|
|
4516
5729
|
console.log();
|
|
4517
5730
|
if (tool.configMethod === "oauth") {
|
|
4518
5731
|
if (toolName === "figma-analysis") {
|
|
@@ -4520,12 +5733,12 @@ skillsCmd.command("*").description("Configure a tool (e.g., figma-analysis confi
|
|
|
4520
5733
|
const oauth = new FigmaOAuth2(toolConfigManager);
|
|
4521
5734
|
try {
|
|
4522
5735
|
const token = await oauth.authenticate();
|
|
4523
|
-
console.log(
|
|
5736
|
+
console.log(chalk3.gray("\nValidating token..."));
|
|
4524
5737
|
const isValid = await oauth.validateToken(token);
|
|
4525
5738
|
if (isValid) {
|
|
4526
5739
|
logger.success(`
|
|
4527
5740
|
\u2705 ${tool.name} configured successfully!`);
|
|
4528
|
-
console.log(
|
|
5741
|
+
console.log(chalk3.gray("\nYou can now use the /analyze-figma command in OpenCode.\n"));
|
|
4529
5742
|
} else {
|
|
4530
5743
|
await toolConfigManager.updateToolConfig(toolName, {
|
|
4531
5744
|
status: "error",
|
|
@@ -4554,7 +5767,7 @@ skillsCmd.command("*").description("Configure a tool (e.g., figma-analysis confi
|
|
|
4554
5767
|
}
|
|
4555
5768
|
} else {
|
|
4556
5769
|
logger.error(`Unknown command: ${toolName || "unknown"} ${action || ""}`);
|
|
4557
|
-
console.log(
|
|
5770
|
+
console.log(chalk3.gray("\nAvailable commands:"));
|
|
4558
5771
|
console.log(" aikit skills list - List all skills and tools");
|
|
4559
5772
|
console.log(" aikit skills show <name> - Show skill details");
|
|
4560
5773
|
console.log(" aikit skills config <tool-name> - Configure a tool");
|
|
@@ -4568,10 +5781,10 @@ agentsCmd.command("list").description("List available agents").action(async () =
|
|
|
4568
5781
|
const config = await loadConfig();
|
|
4569
5782
|
const manager = new AgentManager(config);
|
|
4570
5783
|
const agents = manager.listAgents();
|
|
4571
|
-
console.log(
|
|
5784
|
+
console.log(chalk3.bold("\n\u{1F916} Available Agents:\n"));
|
|
4572
5785
|
for (const agent of agents) {
|
|
4573
|
-
console.log(` ${
|
|
4574
|
-
console.log(
|
|
5786
|
+
console.log(` ${chalk3.cyan(`@${agent.name}`)} - ${agent.description}`);
|
|
5787
|
+
console.log(chalk3.gray(` Use when: ${agent.useWhen}`));
|
|
4575
5788
|
}
|
|
4576
5789
|
console.log();
|
|
4577
5790
|
});
|
|
@@ -4580,13 +5793,13 @@ commandsCmd.command("list").description("List available commands").action(async
|
|
|
4580
5793
|
const config = await loadConfig();
|
|
4581
5794
|
const runner = new CommandRunner(config);
|
|
4582
5795
|
const commands = await runner.listCommands();
|
|
4583
|
-
console.log(
|
|
5796
|
+
console.log(chalk3.bold("\n\u26A1 Available Commands:\n"));
|
|
4584
5797
|
const groups = groupBy(commands, (c) => c.category);
|
|
4585
5798
|
for (const [category, cmds] of Object.entries(groups)) {
|
|
4586
|
-
console.log(
|
|
5799
|
+
console.log(chalk3.bold.yellow(`
|
|
4587
5800
|
${category}:`));
|
|
4588
5801
|
for (const cmd of cmds) {
|
|
4589
|
-
console.log(` ${
|
|
5802
|
+
console.log(` ${chalk3.cyan(`/${cmd.name}`)} - ${cmd.description}`);
|
|
4590
5803
|
}
|
|
4591
5804
|
}
|
|
4592
5805
|
console.log();
|
|
@@ -4596,9 +5809,9 @@ toolsCmd.command("list").description("List available tools").action(async () =>
|
|
|
4596
5809
|
const config = await loadConfig();
|
|
4597
5810
|
const registry = new ToolRegistry(config);
|
|
4598
5811
|
const tools = await registry.listTools();
|
|
4599
|
-
console.log(
|
|
5812
|
+
console.log(chalk3.bold("\n\u{1F527} Available Tools:\n"));
|
|
4600
5813
|
for (const tool of tools) {
|
|
4601
|
-
console.log(` ${
|
|
5814
|
+
console.log(` ${chalk3.cyan(tool.name)} - ${tool.description}`);
|
|
4602
5815
|
}
|
|
4603
5816
|
console.log();
|
|
4604
5817
|
});
|
|
@@ -4607,10 +5820,10 @@ pluginsCmd.command("list").description("List available plugins").action(async ()
|
|
|
4607
5820
|
const config = await loadConfig();
|
|
4608
5821
|
const system = new PluginSystem(config);
|
|
4609
5822
|
const plugins = await system.listPlugins();
|
|
4610
|
-
console.log(
|
|
5823
|
+
console.log(chalk3.bold("\n\u{1F50C} Available Plugins:\n"));
|
|
4611
5824
|
for (const plugin of plugins) {
|
|
4612
|
-
const status = plugin.enabled ?
|
|
4613
|
-
console.log(` ${status} ${
|
|
5825
|
+
const status = plugin.enabled ? chalk3.green("\u2713") : chalk3.gray("\u25CB");
|
|
5826
|
+
console.log(` ${status} ${chalk3.cyan(plugin.name)} - ${plugin.description}`);
|
|
4614
5827
|
}
|
|
4615
5828
|
console.log();
|
|
4616
5829
|
});
|
|
@@ -4619,10 +5832,10 @@ memoryCmd.command("list").description("List memory entries").action(async () =>
|
|
|
4619
5832
|
const config = await loadConfig();
|
|
4620
5833
|
const memory = new MemoryManager(config);
|
|
4621
5834
|
const entries = await memory.list();
|
|
4622
|
-
console.log(
|
|
5835
|
+
console.log(chalk3.bold("\n\u{1F9E0} Memory Entries:\n"));
|
|
4623
5836
|
for (const entry of entries) {
|
|
4624
|
-
console.log(` ${
|
|
4625
|
-
console.log(
|
|
5837
|
+
console.log(` ${chalk3.cyan(entry.key)} - ${entry.summary}`);
|
|
5838
|
+
console.log(chalk3.gray(` Updated: ${entry.updatedAt}`));
|
|
4626
5839
|
}
|
|
4627
5840
|
console.log();
|
|
4628
5841
|
});
|
|
@@ -4640,19 +5853,19 @@ var beadsCmd = program.command("beads").description("Beads task management integ
|
|
|
4640
5853
|
beadsCmd.command("status").description("Show current Beads status").action(async () => {
|
|
4641
5854
|
const beads = new BeadsIntegration();
|
|
4642
5855
|
const status = await beads.getStatus();
|
|
4643
|
-
console.log(
|
|
5856
|
+
console.log(chalk3.bold("\n\u{1F4FF} Beads Status:\n"));
|
|
4644
5857
|
console.log(` Active tasks: ${status.activeTasks}`);
|
|
4645
5858
|
console.log(` Completed: ${status.completedTasks}`);
|
|
4646
5859
|
console.log(` Current: ${status.currentTask || "None"}`);
|
|
4647
5860
|
console.log();
|
|
4648
5861
|
});
|
|
4649
5862
|
program.command("status").description("Show AIKit status").action(async () => {
|
|
4650
|
-
console.log(
|
|
5863
|
+
console.log(chalk3.bold(`
|
|
4651
5864
|
\u{1F680} AIKit v${getVersion}
|
|
4652
5865
|
`));
|
|
4653
5866
|
try {
|
|
4654
5867
|
const config = await loadConfig();
|
|
4655
|
-
console.log(
|
|
5868
|
+
console.log(chalk3.green("\u2713 Configuration loaded"));
|
|
4656
5869
|
const skillEngine = new SkillEngine(config);
|
|
4657
5870
|
const skills = await skillEngine.listSkills();
|
|
4658
5871
|
console.log(` Skills: ${skills.length}`);
|
|
@@ -4667,15 +5880,15 @@ program.command("status").description("Show AIKit status").action(async () => {
|
|
|
4667
5880
|
console.log(` Tools: ${tools.length}`);
|
|
4668
5881
|
const beads = new BeadsIntegration();
|
|
4669
5882
|
const beadsStatus = await beads.isInstalled();
|
|
4670
|
-
console.log(` Beads: ${beadsStatus ?
|
|
5883
|
+
console.log(` Beads: ${beadsStatus ? chalk3.green("Installed") : chalk3.yellow("Not installed")}`);
|
|
4671
5884
|
} catch (error) {
|
|
4672
|
-
console.log(
|
|
5885
|
+
console.log(chalk3.yellow('\u26A0 AIKit not initialized. Run "aikit init" to get started.'));
|
|
4673
5886
|
}
|
|
4674
5887
|
console.log();
|
|
4675
5888
|
});
|
|
4676
5889
|
async function initializeConfig(configDir, _isGlobal) {
|
|
4677
|
-
const { mkdir:
|
|
4678
|
-
const { join:
|
|
5890
|
+
const { mkdir: mkdir11, writeFile: writeFile13 } = await import("fs/promises");
|
|
5891
|
+
const { join: join18 } = await import("path");
|
|
4679
5892
|
const dirs = [
|
|
4680
5893
|
"",
|
|
4681
5894
|
"skills",
|
|
@@ -4694,7 +5907,7 @@ async function initializeConfig(configDir, _isGlobal) {
|
|
|
4694
5907
|
"memory/research"
|
|
4695
5908
|
];
|
|
4696
5909
|
for (const dir of dirs) {
|
|
4697
|
-
await
|
|
5910
|
+
await mkdir11(join18(configDir, dir), { recursive: true });
|
|
4698
5911
|
}
|
|
4699
5912
|
const defaultConfig = {
|
|
4700
5913
|
version: getVersion,
|
|
@@ -4707,8 +5920,8 @@ async function initializeConfig(configDir, _isGlobal) {
|
|
|
4707
5920
|
beads: { enabled: true },
|
|
4708
5921
|
antiHallucination: { enabled: true }
|
|
4709
5922
|
};
|
|
4710
|
-
await
|
|
4711
|
-
|
|
5923
|
+
await writeFile13(
|
|
5924
|
+
join18(configDir, "aikit.json"),
|
|
4712
5925
|
JSON.stringify(defaultConfig, null, 2)
|
|
4713
5926
|
);
|
|
4714
5927
|
const agentsMd = `# AIKit Agent Rules
|
|
@@ -4731,26 +5944,26 @@ async function initializeConfig(configDir, _isGlobal) {
|
|
|
4731
5944
|
## Project-Specific Rules
|
|
4732
5945
|
Add your project-specific rules here.
|
|
4733
5946
|
`;
|
|
4734
|
-
await
|
|
5947
|
+
await writeFile13(join18(configDir, "AGENTS.md"), agentsMd);
|
|
4735
5948
|
}
|
|
4736
5949
|
async function configureMcpServer(projectPath) {
|
|
4737
|
-
const { mkdir:
|
|
4738
|
-
const { join:
|
|
5950
|
+
const { mkdir: mkdir11, writeFile: writeFile13, readFile: readFile12 } = await import("fs/promises");
|
|
5951
|
+
const { join: join18 } = await import("path");
|
|
4739
5952
|
const { existsSync: existsSync5 } = await import("fs");
|
|
4740
5953
|
const { homedir: homedir3 } = await import("os");
|
|
4741
5954
|
const { fileURLToPath: fileURLToPath3 } = await import("url");
|
|
4742
|
-
const { dirname:
|
|
5955
|
+
const { dirname: dirname4 } = await import("path");
|
|
4743
5956
|
const currentFile = fileURLToPath3(import.meta.url);
|
|
4744
|
-
const currentDir =
|
|
4745
|
-
const aikitPath =
|
|
4746
|
-
const mcpServerPath =
|
|
5957
|
+
const currentDir = dirname4(currentFile);
|
|
5958
|
+
const aikitPath = join18(currentDir, "..");
|
|
5959
|
+
const mcpServerPath = join18(aikitPath, "dist", "mcp-server.js");
|
|
4747
5960
|
const configLocations = [
|
|
4748
5961
|
// Global config (most common)
|
|
4749
|
-
|
|
5962
|
+
join18(homedir3(), ".config", "opencode", "opencode.json"),
|
|
4750
5963
|
// Project-level config
|
|
4751
|
-
|
|
5964
|
+
join18(projectPath, ".opencode", "opencode.json"),
|
|
4752
5965
|
// Alternative global location
|
|
4753
|
-
|
|
5966
|
+
join18(homedir3(), ".opencode", "opencode.json")
|
|
4754
5967
|
];
|
|
4755
5968
|
const mcpServerConfig = {
|
|
4756
5969
|
type: "local",
|
|
@@ -4759,12 +5972,12 @@ async function configureMcpServer(projectPath) {
|
|
|
4759
5972
|
};
|
|
4760
5973
|
for (const configPath of configLocations) {
|
|
4761
5974
|
try {
|
|
4762
|
-
const configDir =
|
|
4763
|
-
await
|
|
5975
|
+
const configDir = join18(configPath, "..");
|
|
5976
|
+
await mkdir11(configDir, { recursive: true });
|
|
4764
5977
|
let config = {};
|
|
4765
5978
|
if (existsSync5(configPath)) {
|
|
4766
5979
|
try {
|
|
4767
|
-
const existing = await
|
|
5980
|
+
const existing = await readFile12(configPath, "utf-8");
|
|
4768
5981
|
config = JSON.parse(existing);
|
|
4769
5982
|
} catch {
|
|
4770
5983
|
config = {};
|
|
@@ -4774,7 +5987,7 @@ async function configureMcpServer(projectPath) {
|
|
|
4774
5987
|
config.mcp = {};
|
|
4775
5988
|
}
|
|
4776
5989
|
config.mcp.aikit = mcpServerConfig;
|
|
4777
|
-
await
|
|
5990
|
+
await writeFile13(configPath, JSON.stringify(config, null, 2));
|
|
4778
5991
|
logger.success(`
|
|
4779
5992
|
\u2705 MCP server configured: ${configPath}`);
|
|
4780
5993
|
logger.info(` Server: node ${mcpServerPath}`);
|
|
@@ -4783,9 +5996,9 @@ async function configureMcpServer(projectPath) {
|
|
|
4783
5996
|
continue;
|
|
4784
5997
|
}
|
|
4785
5998
|
}
|
|
4786
|
-
const instructionsPath =
|
|
4787
|
-
await
|
|
4788
|
-
await
|
|
5999
|
+
const instructionsPath = join18(projectPath, ".opencode", "MCP_SETUP.md");
|
|
6000
|
+
await mkdir11(join18(projectPath, ".opencode"), { recursive: true });
|
|
6001
|
+
await writeFile13(instructionsPath, `# AIKit MCP Server Configuration
|
|
4789
6002
|
|
|
4790
6003
|
## Automatic Setup Failed
|
|
4791
6004
|
|
|
@@ -4848,15 +6061,15 @@ async function installCliTool(tool) {
|
|
|
4848
6061
|
}
|
|
4849
6062
|
}
|
|
4850
6063
|
async function installToOpenCode(_opencodePath) {
|
|
4851
|
-
const { mkdir:
|
|
4852
|
-
const { join:
|
|
6064
|
+
const { mkdir: mkdir11, writeFile: writeFile13, access: access5 } = await import("fs/promises");
|
|
6065
|
+
const { join: join18 } = await import("path");
|
|
4853
6066
|
const projectPath = process.cwd();
|
|
4854
|
-
const opencodeCommandDir =
|
|
4855
|
-
const aikitDir =
|
|
4856
|
-
const opencodeAgentDir =
|
|
4857
|
-
await
|
|
4858
|
-
await
|
|
4859
|
-
await
|
|
6067
|
+
const opencodeCommandDir = join18(projectPath, ".opencode", "command");
|
|
6068
|
+
const aikitDir = join18(projectPath, ".aikit");
|
|
6069
|
+
const opencodeAgentDir = join18(paths.opencodeConfig(), "agent");
|
|
6070
|
+
await mkdir11(opencodeCommandDir, { recursive: true });
|
|
6071
|
+
await mkdir11(join18(aikitDir, "skills"), { recursive: true });
|
|
6072
|
+
await mkdir11(opencodeAgentDir, { recursive: true });
|
|
4860
6073
|
const agentFiles = {
|
|
4861
6074
|
agent: `---
|
|
4862
6075
|
description: General-purpose default agent (OpenCode compatibility).
|
|
@@ -4925,14 +6138,60 @@ tools:
|
|
|
4925
6138
|
"*": true
|
|
4926
6139
|
---
|
|
4927
6140
|
|
|
4928
|
-
Use to interpret visual assets (components, layout, colors, typography) and translate to tasks
|
|
6141
|
+
Use to interpret visual assets (components, layout, colors, typography) and translate to tasks.`,
|
|
6142
|
+
"one-shot": `---
|
|
6143
|
+
description: End-to-end autonomous task execution (beta). Complete tasks from start to finish.
|
|
6144
|
+
mode: subagent
|
|
6145
|
+
tools:
|
|
6146
|
+
"*": true
|
|
6147
|
+
---
|
|
6148
|
+
|
|
6149
|
+
\u26A0\uFE0F BETA: This mode is experimental. Use for straightforward tasks first.
|
|
6150
|
+
|
|
6151
|
+
## One-Shot Mode - Autonomous Task Execution
|
|
6152
|
+
|
|
6153
|
+
Execute tasks end-to-end with minimal intervention:
|
|
6154
|
+
|
|
6155
|
+
### Workflow Phases
|
|
6156
|
+
1. **REQUIREMENTS** - Gather task type, scope, dependencies, success criteria
|
|
6157
|
+
2. **PLANNING** - Create detailed plan, recommend skills/tools, create tracking bead
|
|
6158
|
+
3. **COMPLEXITY** - Auto-split if: >30min, >10 files, >500 lines, >2 sub-systems
|
|
6159
|
+
4. **EXECUTION** - Parallel tasks (max 3), dynamic agent delegation
|
|
6160
|
+
5. **TESTING** - Run until pass: typecheck \u2192 test \u2192 lint \u2192 build (max 3 retries)
|
|
6161
|
+
6. **VERIFICATION** - Quality gates \u2713 \u2192 Manual verification \u2192 Deployment approval
|
|
6162
|
+
7. **COMPLETION** - Generate proof, update tracking, collect feedback
|
|
6163
|
+
|
|
6164
|
+
### Quality Gates (ALL must pass)
|
|
6165
|
+
- \`npm run typecheck\` - No type errors
|
|
6166
|
+
- \`npm run test\` - All tests pass
|
|
6167
|
+
- \`npm run lint\` - No lint errors
|
|
6168
|
+
- \`npm run build\` - Build succeeds
|
|
6169
|
+
|
|
6170
|
+
### Error Recovery (3 Levels)
|
|
6171
|
+
- **Level 1**: Auto-fix (type errors, lint --fix)
|
|
6172
|
+
- **Level 2**: Alternative approach via @review
|
|
6173
|
+
- **Level 3**: User intervention + follow-up task
|
|
6174
|
+
|
|
6175
|
+
### Delegates To
|
|
6176
|
+
@planner for planning, @build for implementation, @review for code review,
|
|
6177
|
+
@scout for research, @explore for navigation, @vision for visual analysis.
|
|
6178
|
+
|
|
6179
|
+
### Best Use Cases
|
|
6180
|
+
- Straightforward features with clear scope
|
|
6181
|
+
- Bug fixes with known reproduction steps
|
|
6182
|
+
- Refactoring with defined boundaries
|
|
6183
|
+
|
|
6184
|
+
### Consider Alternatives For
|
|
6185
|
+
- Complex multi-system features \u2192 Use /plan + /implement
|
|
6186
|
+
- Exploratory research \u2192 Use /research first
|
|
6187
|
+
- Critical production changes \u2192 Manual with /review`
|
|
4929
6188
|
};
|
|
4930
6189
|
for (const [name, content] of Object.entries(agentFiles)) {
|
|
4931
|
-
const filePath =
|
|
6190
|
+
const filePath = join18(opencodeAgentDir, `${name}.md`);
|
|
4932
6191
|
try {
|
|
4933
6192
|
await access5(filePath);
|
|
4934
6193
|
} catch {
|
|
4935
|
-
await
|
|
6194
|
+
await writeFile13(filePath, content, "utf8");
|
|
4936
6195
|
}
|
|
4937
6196
|
}
|
|
4938
6197
|
const config = await loadConfig();
|
|
@@ -5185,8 +6444,8 @@ ${cmd.content}
|
|
|
5185
6444
|
}
|
|
5186
6445
|
let count = 0;
|
|
5187
6446
|
for (const [name, content] of Object.entries(opencodeCommands)) {
|
|
5188
|
-
const filePath =
|
|
5189
|
-
await
|
|
6447
|
+
const filePath = join18(opencodeCommandDir, `${name}.md`);
|
|
6448
|
+
await writeFile13(filePath, content.trim());
|
|
5190
6449
|
logger.info(` \u2713 Created /${name} command`);
|
|
5191
6450
|
count++;
|
|
5192
6451
|
}
|