@orderful/droid 0.27.4 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +15 -0
- package/CHANGELOG.md +34 -0
- package/dist/bin/droid.js +92 -76
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/tui/components/ToolDetails.d.ts.map +1 -1
- package/dist/index.js +80 -39
- package/dist/lib/migrations.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/commands/setup.ts +4 -52
- package/src/commands/tui/components/ToolDetails.tsx +37 -10
- package/src/lib/migrations.ts +117 -52
- package/src/lib/platforms.ts +1 -1
- package/src/lib/skills.test.ts +1 -1
package/AGENTS.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
Project instructions for AI coding assistants.
|
|
4
4
|
|
|
5
|
+
## ⚠️ CRITICAL: Git Workflow
|
|
6
|
+
|
|
7
|
+
**NEVER PUSH DIRECTLY TO MAIN!**
|
|
8
|
+
|
|
9
|
+
All changes MUST go through Pull Requests:
|
|
10
|
+
|
|
11
|
+
1. Create a feature branch: `git checkout -b fix/description` or `feat/description`
|
|
12
|
+
2. Make your changes and commit
|
|
13
|
+
3. Push the branch: `git push origin <branch-name>`
|
|
14
|
+
4. Create a PR using `gh pr create` or the GitHub UI
|
|
15
|
+
5. Wait for CI checks and review before merging
|
|
16
|
+
|
|
17
|
+
**No exceptions.** Even "small fixes" require PRs.
|
|
18
|
+
|
|
5
19
|
## Overview
|
|
6
20
|
|
|
7
21
|
Droid is a TUI dashboard for managing AI tools. Each tool bundles related skills, commands, and agents. Installs to Claude Code (`~/.claude/`) and OpenCode (`~/.config/opencode/`).
|
|
@@ -71,6 +85,7 @@ Don't consolidate them. Using Ink for simple prompts would be overkill.
|
|
|
71
85
|
## Code Style
|
|
72
86
|
|
|
73
87
|
- TypeScript strict mode
|
|
88
|
+
- **Use single quotes** for strings (enforced by ESLint)
|
|
74
89
|
- Prefer `const` over `let`
|
|
75
90
|
- Use Canadian/British spelling (behaviour, colour, favourite)
|
|
76
91
|
- Use "allow list/deny list" not "whitelist/blacklist"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @orderful/droid
|
|
2
2
|
|
|
3
|
+
## 0.28.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#160](https://github.com/Orderful/droid/pull/160) [`235c520`](https://github.com/Orderful/droid/commit/235c520de056f286fd2af04d6ec2606c51c7bbb2) Thanks [@frytyler](https://github.com/frytyler)! - Support OpenCode's native skills implementation
|
|
8
|
+
|
|
9
|
+
OpenCode now has native skills support and no longer requires the deprecated opencode-skills plugin. This update removes the plugin requirement and updates paths to match OpenCode's native directory structure.
|
|
10
|
+
|
|
11
|
+
**Breaking changes for OpenCode users:**
|
|
12
|
+
- Skills directory changed from `~/.config/opencode/skills/` to `~/.config/opencode/skill/` (singular)
|
|
13
|
+
- Automatic migration moves existing skills to the new location
|
|
14
|
+
|
|
15
|
+
**Changes:**
|
|
16
|
+
- Remove opencode-skills plugin requirement from setup
|
|
17
|
+
- Update OpenCode skills path to use singular `skill/` directory
|
|
18
|
+
- Add migration to move existing skills from old to new directory
|
|
19
|
+
- Update setup messaging to reflect native skills support
|
|
20
|
+
- Fix test expectations for new OpenCode paths
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- [#159](https://github.com/Orderful/droid/pull/159) [`4275602`](https://github.com/Orderful/droid/commit/4275602e1c05f6205e6442a2a6c121a508116bd8) Thanks [@frytyler](https://github.com/frytyler)! - Add migration to remove non-alias commands from Claude Code
|
|
25
|
+
|
|
26
|
+
Claude Code v2.1.3+ can invoke skills directly via /{skillName}, making non-alias commands redundant. This migration removes old non-alias commands from ~/.claude/commands/ while preserving aliases (e.g., /scratchpad for /brain).
|
|
27
|
+
- New package migration (v0.27.3): `createClaudeCodeCommandCleanupMigration`
|
|
28
|
+
- Only affects Claude Code platform
|
|
29
|
+
- Keeps alias commands, removes primary commands that are now redundant
|
|
30
|
+
|
|
31
|
+
## 0.27.5
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- [`dd0e5f1`](https://github.com/Orderful/droid/commit/dd0e5f114f11ab2ff34c6e329265ea84117792e9) Thanks [@frytyler](https://github.com/frytyler)! - Fix TUI displaying commands as `/[object Object]` instead of actual command names. Commands are now displayed correctly (e.g., `/brain, /scratchpad`) after handling both string and object formats in TOOL.yaml.
|
|
36
|
+
|
|
3
37
|
## 0.27.4
|
|
4
38
|
|
|
5
39
|
### Patch Changes
|
package/dist/bin/droid.js
CHANGED
|
@@ -208,7 +208,7 @@ var PLATFORM_PATHS = {
|
|
|
208
208
|
config: join2(homedir2(), ".claude", "CLAUDE.md")
|
|
209
209
|
},
|
|
210
210
|
["opencode" /* OpenCode */]: {
|
|
211
|
-
skills: join2(homedir2(), ".config", "opencode", "
|
|
211
|
+
skills: join2(homedir2(), ".config", "opencode", "skill"),
|
|
212
212
|
commands: join2(homedir2(), ".config", "opencode", "command"),
|
|
213
213
|
agents: join2(homedir2(), ".config", "opencode", "agent"),
|
|
214
214
|
config: join2(homedir2(), ".config", "opencode", "AGENTS.md")
|
|
@@ -642,52 +642,92 @@ function createConfigSkillNameMigration(version2) {
|
|
|
642
642
|
}
|
|
643
643
|
};
|
|
644
644
|
}
|
|
645
|
-
function
|
|
645
|
+
function createOpenCodeSkillsPathMigration(version2) {
|
|
646
646
|
return {
|
|
647
647
|
version: version2,
|
|
648
|
-
description: "
|
|
648
|
+
description: "Move OpenCode skills from skills/ to skill/ directory",
|
|
649
649
|
up: () => {
|
|
650
650
|
const config = loadConfig();
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
);
|
|
666
|
-
const installedDirs = readdirSync3(skillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
667
|
-
for (const dirName of installedDirs) {
|
|
668
|
-
if (!allToolNames.has(dirName)) continue;
|
|
669
|
-
if (trackedTools[dirName]) continue;
|
|
670
|
-
let version3 = "0.0.0";
|
|
671
|
-
const matchingTool = bundledTools.find(
|
|
672
|
-
(tool) => tool.includes.skills.some((s) => s.name === dirName && s.required)
|
|
651
|
+
if (config.platform !== "opencode" /* OpenCode */) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
const oldSkillsPath = join6(getSkillsPath("opencode" /* OpenCode */), "..", "skills");
|
|
655
|
+
const newSkillsPath = getSkillsPath("opencode" /* OpenCode */);
|
|
656
|
+
if (!existsSync4(oldSkillsPath)) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
if (!existsSync4(newSkillsPath)) {
|
|
660
|
+
try {
|
|
661
|
+
renameSync(oldSkillsPath, newSkillsPath);
|
|
662
|
+
} catch (error) {
|
|
663
|
+
console.warn(
|
|
664
|
+
`Warning: Could not rename skills directory ${oldSkillsPath}: ${error}`
|
|
673
665
|
);
|
|
674
|
-
|
|
675
|
-
|
|
666
|
+
}
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const skillDirs = readdirSync3(oldSkillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
670
|
+
for (const skillName of skillDirs) {
|
|
671
|
+
const oldSkillDir = join6(oldSkillsPath, skillName);
|
|
672
|
+
const newSkillDir = join6(newSkillsPath, skillName);
|
|
673
|
+
if (!existsSync4(newSkillDir)) {
|
|
674
|
+
try {
|
|
675
|
+
renameSync(oldSkillDir, newSkillDir);
|
|
676
|
+
} catch (error) {
|
|
677
|
+
console.warn(
|
|
678
|
+
`Warning: Could not move skill ${skillName}: ${error}`
|
|
679
|
+
);
|
|
676
680
|
}
|
|
677
|
-
trackedTools[dirName] = {
|
|
678
|
-
version: version3,
|
|
679
|
-
installed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
680
|
-
};
|
|
681
|
-
platformChanged = true;
|
|
682
|
-
configChanged = true;
|
|
683
681
|
}
|
|
684
|
-
|
|
685
|
-
|
|
682
|
+
}
|
|
683
|
+
try {
|
|
684
|
+
const remaining = readdirSync3(oldSkillsPath);
|
|
685
|
+
if (remaining.length === 0) {
|
|
686
|
+
rmSync(oldSkillsPath, { recursive: true });
|
|
686
687
|
}
|
|
688
|
+
} catch (error) {
|
|
689
|
+
console.warn(
|
|
690
|
+
`Warning: Could not remove old skills directory ${oldSkillsPath}: ${error}`
|
|
691
|
+
);
|
|
687
692
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
function createClaudeCodeCommandCleanupMigration(version2) {
|
|
697
|
+
return {
|
|
698
|
+
version: version2,
|
|
699
|
+
description: "Remove non-alias commands from Claude Code",
|
|
700
|
+
up: () => {
|
|
701
|
+
const config = loadConfig();
|
|
702
|
+
if (config.platform !== "claude-code" /* ClaudeCode */) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const commandsPath = getCommandsPath("claude-code" /* ClaudeCode */);
|
|
706
|
+
if (!existsSync4(commandsPath)) {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
const bundledTools = getBundledTools();
|
|
710
|
+
const aliasCommands = /* @__PURE__ */ new Set();
|
|
711
|
+
for (const tool of bundledTools) {
|
|
712
|
+
for (const cmd of tool.includes.commands) {
|
|
713
|
+
if (typeof cmd === "object" && cmd.is_alias) {
|
|
714
|
+
aliasCommands.add(cmd.name);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
const commandFiles = readdirSync3(commandsPath, { withFileTypes: true }).filter((dirent) => dirent.isFile() && dirent.name.endsWith(".md")).map((dirent) => dirent.name);
|
|
719
|
+
for (const file of commandFiles) {
|
|
720
|
+
const commandName = file.replace(".md", "");
|
|
721
|
+
if (!aliasCommands.has(commandName)) {
|
|
722
|
+
const commandFilePath = join6(commandsPath, file);
|
|
723
|
+
try {
|
|
724
|
+
rmSync(commandFilePath);
|
|
725
|
+
} catch (error) {
|
|
726
|
+
console.warn(
|
|
727
|
+
`Warning: Could not remove command ${commandFilePath}: ${error}`
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
691
731
|
}
|
|
692
732
|
}
|
|
693
733
|
};
|
|
@@ -695,7 +735,8 @@ function createFilesystemSyncMigration(version2) {
|
|
|
695
735
|
var PACKAGE_MIGRATIONS = [
|
|
696
736
|
createPlatformSyncMigration("0.25.0"),
|
|
697
737
|
createConfigSkillNameMigration("0.27.2"),
|
|
698
|
-
|
|
738
|
+
createOpenCodeSkillsPathMigration("0.28.0"),
|
|
739
|
+
createClaudeCodeCommandCleanupMigration("0.28.0")
|
|
699
740
|
];
|
|
700
741
|
var TOOL_MIGRATIONS = {
|
|
701
742
|
brain: [createConfigDirMigration("droid-brain", "0.2.3")],
|
|
@@ -1251,7 +1292,6 @@ function detectGitUsername() {
|
|
|
1251
1292
|
return "";
|
|
1252
1293
|
}
|
|
1253
1294
|
}
|
|
1254
|
-
var OPENCODE_SKILLS_PLUGIN = "opencode-skills";
|
|
1255
1295
|
function configurePlatformPermissions(platform) {
|
|
1256
1296
|
const added = [];
|
|
1257
1297
|
if (platform === "claude-code" /* ClaudeCode */) {
|
|
@@ -1293,35 +1333,10 @@ function configurePlatformPermissions(platform) {
|
|
|
1293
1333
|
}
|
|
1294
1334
|
if (platform === "opencode" /* OpenCode */) {
|
|
1295
1335
|
const globalConfigDir = join8(homedir3(), ".config", "opencode");
|
|
1296
|
-
const globalConfigPath = join8(globalConfigDir, "opencode.json");
|
|
1297
1336
|
if (!existsSync6(globalConfigDir)) {
|
|
1298
1337
|
mkdirSync5(globalConfigDir, { recursive: true });
|
|
1299
1338
|
}
|
|
1300
|
-
|
|
1301
|
-
if (existsSync6(globalConfigPath)) {
|
|
1302
|
-
try {
|
|
1303
|
-
config = JSON.parse(readFileSync6(globalConfigPath, "utf-8"));
|
|
1304
|
-
} catch {
|
|
1305
|
-
console.warn(chalk2.yellow("\u26A0 OpenCode config appears corrupted, resetting"));
|
|
1306
|
-
config = {};
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
if (!Array.isArray(config.plugin)) {
|
|
1310
|
-
config.plugin = [];
|
|
1311
|
-
}
|
|
1312
|
-
if (!config.plugin.includes(OPENCODE_SKILLS_PLUGIN)) {
|
|
1313
|
-
config.plugin.push(OPENCODE_SKILLS_PLUGIN);
|
|
1314
|
-
added.push(OPENCODE_SKILLS_PLUGIN);
|
|
1315
|
-
}
|
|
1316
|
-
if (added.length > 0) {
|
|
1317
|
-
try {
|
|
1318
|
-
writeFileSync4(globalConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1319
|
-
} catch (e) {
|
|
1320
|
-
const message = e instanceof Error ? e.message : "Unknown error";
|
|
1321
|
-
return { added: [], alreadyPresent: false, error: `Failed to update OpenCode config: ${message}` };
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
return { added, alreadyPresent: added.length === 0 };
|
|
1339
|
+
return { added: [], alreadyPresent: true };
|
|
1325
1340
|
}
|
|
1326
1341
|
return { added: [], alreadyPresent: true };
|
|
1327
1342
|
}
|
|
@@ -1422,13 +1437,7 @@ async function setupCommand() {
|
|
|
1422
1437
|
console.log(chalk2.gray(" Droid permissions already configured in Claude Code"));
|
|
1423
1438
|
}
|
|
1424
1439
|
} else if (answers.platform === "opencode" /* OpenCode */) {
|
|
1425
|
-
|
|
1426
|
-
console.log(chalk2.green("\u2713 Added opencode-skills plugin to OpenCode config"));
|
|
1427
|
-
console.log(chalk2.gray(" This enables Claude Code-style skills in OpenCode"));
|
|
1428
|
-
console.log(chalk2.gray(" Restart OpenCode to activate the plugin"));
|
|
1429
|
-
} else if (alreadyPresent) {
|
|
1430
|
-
console.log(chalk2.gray(" opencode-skills plugin already configured in OpenCode"));
|
|
1431
|
-
}
|
|
1440
|
+
console.log(chalk2.gray(" OpenCode has native skills support - no configuration needed"));
|
|
1432
1441
|
}
|
|
1433
1442
|
console.log(chalk2.gray("\nRun `droid skills` to browse and install skills."));
|
|
1434
1443
|
}
|
|
@@ -1974,7 +1983,13 @@ function ToolDetails({
|
|
|
1974
1983
|
const isSystemTool = tool.system === true;
|
|
1975
1984
|
const actions = installed ? [
|
|
1976
1985
|
{ id: "explore", label: "Explore", variant: "default" },
|
|
1977
|
-
...updateStatus.hasUpdate ? [
|
|
1986
|
+
...updateStatus.hasUpdate ? [
|
|
1987
|
+
{
|
|
1988
|
+
id: "update",
|
|
1989
|
+
label: `Update (${updateStatus.bundledVersion})`,
|
|
1990
|
+
variant: "primary"
|
|
1991
|
+
}
|
|
1992
|
+
] : [],
|
|
1978
1993
|
{ id: "configure", label: "Configure", variant: "default" },
|
|
1979
1994
|
// System tools can't be uninstalled
|
|
1980
1995
|
...!isSystemTool ? [{ id: "uninstall", label: "Uninstall", variant: "danger" }] : []
|
|
@@ -1990,7 +2005,8 @@ function ToolDetails({
|
|
|
1990
2005
|
tool.status && ` \xB7 ${tool.status}`,
|
|
1991
2006
|
installed && /* @__PURE__ */ jsx4(Text4, { color: colors.success, children: " \xB7 installed" }),
|
|
1992
2007
|
updateStatus.hasUpdate && /* @__PURE__ */ jsxs4(Text4, { color: colors.primary, children: [
|
|
1993
|
-
"
|
|
2008
|
+
" ",
|
|
2009
|
+
"\xB7 update (",
|
|
1994
2010
|
installedVersion,
|
|
1995
2011
|
" \u2192 ",
|
|
1996
2012
|
updateStatus.bundledVersion,
|
|
@@ -2008,7 +2024,7 @@ function ToolDetails({
|
|
|
2008
2024
|
] }),
|
|
2009
2025
|
tool.includes.commands.length > 0 && /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
2010
2026
|
/* @__PURE__ */ jsx4(Text4, { color: colors.command, children: "Commands: " }),
|
|
2011
|
-
/* @__PURE__ */ jsx4(Text4, { color: colors.textMuted, children: tool.includes.commands.map((c) => `/${c}`).join(", ") })
|
|
2027
|
+
/* @__PURE__ */ jsx4(Text4, { color: colors.textMuted, children: tool.includes.commands.map((c) => typeof c === "string" ? `/${c}` : `/${c.name}`).join(", ") })
|
|
2012
2028
|
] }),
|
|
2013
2029
|
tool.includes.agents.length > 0 && /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
2014
2030
|
/* @__PURE__ */ jsx4(Text4, { color: colors.agent, children: "Agents: " }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAA0D,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAA0D,MAAM,cAAc,CAAC;AA6ChG;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAgE7H;AAyBD,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAwGlD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolDetails.d.ts","sourceRoot":"","sources":["../../../../src/commands/tui/components/ToolDetails.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ToolDetails.d.ts","sourceRoot":"","sources":["../../../../src/commands/tui/components/ToolDetails.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,SAAS,EACT,cAAc,GACf,EAAE,gBAAgB,2CA2HlB"}
|
package/dist/index.js
CHANGED
|
@@ -216,7 +216,7 @@ var PLATFORM_PATHS = {
|
|
|
216
216
|
config: join2(homedir2(), ".claude", "CLAUDE.md")
|
|
217
217
|
},
|
|
218
218
|
["opencode" /* OpenCode */]: {
|
|
219
|
-
skills: join2(homedir2(), ".config", "opencode", "
|
|
219
|
+
skills: join2(homedir2(), ".config", "opencode", "skill"),
|
|
220
220
|
commands: join2(homedir2(), ".config", "opencode", "command"),
|
|
221
221
|
agents: join2(homedir2(), ".config", "opencode", "agent"),
|
|
222
222
|
config: join2(homedir2(), ".config", "opencode", "AGENTS.md")
|
|
@@ -615,52 +615,92 @@ function createConfigSkillNameMigration(version) {
|
|
|
615
615
|
}
|
|
616
616
|
};
|
|
617
617
|
}
|
|
618
|
-
function
|
|
618
|
+
function createOpenCodeSkillsPathMigration(version) {
|
|
619
619
|
return {
|
|
620
620
|
version,
|
|
621
|
-
description: "
|
|
621
|
+
description: "Move OpenCode skills from skills/ to skill/ directory",
|
|
622
622
|
up: () => {
|
|
623
623
|
const config = loadConfig();
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
);
|
|
639
|
-
const installedDirs = readdirSync3(skillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
640
|
-
for (const dirName of installedDirs) {
|
|
641
|
-
if (!allToolNames.has(dirName)) continue;
|
|
642
|
-
if (trackedTools[dirName]) continue;
|
|
643
|
-
let version2 = "0.0.0";
|
|
644
|
-
const matchingTool = bundledTools.find(
|
|
645
|
-
(tool) => tool.includes.skills.some((s) => s.name === dirName && s.required)
|
|
624
|
+
if (config.platform !== "opencode" /* OpenCode */) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const oldSkillsPath = join6(getSkillsPath("opencode" /* OpenCode */), "..", "skills");
|
|
628
|
+
const newSkillsPath = getSkillsPath("opencode" /* OpenCode */);
|
|
629
|
+
if (!existsSync4(oldSkillsPath)) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
if (!existsSync4(newSkillsPath)) {
|
|
633
|
+
try {
|
|
634
|
+
renameSync(oldSkillsPath, newSkillsPath);
|
|
635
|
+
} catch (error) {
|
|
636
|
+
console.warn(
|
|
637
|
+
`Warning: Could not rename skills directory ${oldSkillsPath}: ${error}`
|
|
646
638
|
);
|
|
647
|
-
|
|
648
|
-
|
|
639
|
+
}
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
const skillDirs = readdirSync3(oldSkillsPath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
643
|
+
for (const skillName of skillDirs) {
|
|
644
|
+
const oldSkillDir = join6(oldSkillsPath, skillName);
|
|
645
|
+
const newSkillDir = join6(newSkillsPath, skillName);
|
|
646
|
+
if (!existsSync4(newSkillDir)) {
|
|
647
|
+
try {
|
|
648
|
+
renameSync(oldSkillDir, newSkillDir);
|
|
649
|
+
} catch (error) {
|
|
650
|
+
console.warn(
|
|
651
|
+
`Warning: Could not move skill ${skillName}: ${error}`
|
|
652
|
+
);
|
|
649
653
|
}
|
|
650
|
-
trackedTools[dirName] = {
|
|
651
|
-
version: version2,
|
|
652
|
-
installed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
653
|
-
};
|
|
654
|
-
platformChanged = true;
|
|
655
|
-
configChanged = true;
|
|
656
654
|
}
|
|
657
|
-
|
|
658
|
-
|
|
655
|
+
}
|
|
656
|
+
try {
|
|
657
|
+
const remaining = readdirSync3(oldSkillsPath);
|
|
658
|
+
if (remaining.length === 0) {
|
|
659
|
+
rmSync(oldSkillsPath, { recursive: true });
|
|
659
660
|
}
|
|
661
|
+
} catch (error) {
|
|
662
|
+
console.warn(
|
|
663
|
+
`Warning: Could not remove old skills directory ${oldSkillsPath}: ${error}`
|
|
664
|
+
);
|
|
660
665
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
function createClaudeCodeCommandCleanupMigration(version) {
|
|
670
|
+
return {
|
|
671
|
+
version,
|
|
672
|
+
description: "Remove non-alias commands from Claude Code",
|
|
673
|
+
up: () => {
|
|
674
|
+
const config = loadConfig();
|
|
675
|
+
if (config.platform !== "claude-code" /* ClaudeCode */) {
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
const commandsPath = getCommandsPath("claude-code" /* ClaudeCode */);
|
|
679
|
+
if (!existsSync4(commandsPath)) {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
const bundledTools = getBundledTools();
|
|
683
|
+
const aliasCommands = /* @__PURE__ */ new Set();
|
|
684
|
+
for (const tool of bundledTools) {
|
|
685
|
+
for (const cmd of tool.includes.commands) {
|
|
686
|
+
if (typeof cmd === "object" && cmd.is_alias) {
|
|
687
|
+
aliasCommands.add(cmd.name);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
const commandFiles = readdirSync3(commandsPath, { withFileTypes: true }).filter((dirent) => dirent.isFile() && dirent.name.endsWith(".md")).map((dirent) => dirent.name);
|
|
692
|
+
for (const file of commandFiles) {
|
|
693
|
+
const commandName = file.replace(".md", "");
|
|
694
|
+
if (!aliasCommands.has(commandName)) {
|
|
695
|
+
const commandFilePath = join6(commandsPath, file);
|
|
696
|
+
try {
|
|
697
|
+
rmSync(commandFilePath);
|
|
698
|
+
} catch (error) {
|
|
699
|
+
console.warn(
|
|
700
|
+
`Warning: Could not remove command ${commandFilePath}: ${error}`
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
664
704
|
}
|
|
665
705
|
}
|
|
666
706
|
};
|
|
@@ -668,7 +708,8 @@ function createFilesystemSyncMigration(version) {
|
|
|
668
708
|
var PACKAGE_MIGRATIONS = [
|
|
669
709
|
createPlatformSyncMigration("0.25.0"),
|
|
670
710
|
createConfigSkillNameMigration("0.27.2"),
|
|
671
|
-
|
|
711
|
+
createOpenCodeSkillsPathMigration("0.28.0"),
|
|
712
|
+
createClaudeCodeCommandCleanupMigration("0.28.0")
|
|
672
713
|
];
|
|
673
714
|
var TOOL_MIGRATIONS = {
|
|
674
715
|
brain: [createConfigDirMigration("droid-brain", "0.2.3")],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/lib/migrations.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/lib/migrations.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,SAAS,CAAC;AA+WjB;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CAE/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAc/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,IAAI,CAmBN;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA2CtC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM,GACvB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAStC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAyDA"}
|
package/package.json
CHANGED
package/src/commands/setup.ts
CHANGED
|
@@ -51,13 +51,6 @@ function detectGitUsername(): string {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/**
|
|
55
|
-
* The opencode-skills plugin name for OpenCode
|
|
56
|
-
* This plugin enables Claude Code-style skills in OpenCode
|
|
57
|
-
* @see https://github.com/malhashemi/opencode-skills
|
|
58
|
-
*/
|
|
59
|
-
const OPENCODE_SKILLS_PLUGIN = 'opencode-skills';
|
|
60
|
-
|
|
61
54
|
/**
|
|
62
55
|
* Configure platform permissions for droid
|
|
63
56
|
*/
|
|
@@ -114,49 +107,14 @@ export function configurePlatformPermissions(platform: Platform): { added: strin
|
|
|
114
107
|
}
|
|
115
108
|
|
|
116
109
|
if (platform === Platform.OpenCode) {
|
|
117
|
-
// OpenCode
|
|
118
|
-
//
|
|
110
|
+
// OpenCode now has native skills support - no configuration needed
|
|
111
|
+
// Ensure config directory exists for when skills are installed
|
|
119
112
|
const globalConfigDir = join(homedir(), '.config', 'opencode');
|
|
120
|
-
const globalConfigPath = join(globalConfigDir, 'opencode.json');
|
|
121
|
-
|
|
122
|
-
// Ensure config directory exists
|
|
123
113
|
if (!existsSync(globalConfigDir)) {
|
|
124
114
|
mkdirSync(globalConfigDir, { recursive: true });
|
|
125
115
|
}
|
|
126
116
|
|
|
127
|
-
|
|
128
|
-
let config: { plugin?: string[] } = {};
|
|
129
|
-
if (existsSync(globalConfigPath)) {
|
|
130
|
-
try {
|
|
131
|
-
config = JSON.parse(readFileSync(globalConfigPath, 'utf-8'));
|
|
132
|
-
} catch {
|
|
133
|
-
console.warn(chalk.yellow('⚠ OpenCode config appears corrupted, resetting'));
|
|
134
|
-
config = {};
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Ensure plugin array exists
|
|
139
|
-
if (!Array.isArray(config.plugin)) {
|
|
140
|
-
config.plugin = [];
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Add opencode-skills plugin if not present
|
|
144
|
-
if (!config.plugin.includes(OPENCODE_SKILLS_PLUGIN)) {
|
|
145
|
-
config.plugin.push(OPENCODE_SKILLS_PLUGIN);
|
|
146
|
-
added.push(OPENCODE_SKILLS_PLUGIN);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Save if we added anything
|
|
150
|
-
if (added.length > 0) {
|
|
151
|
-
try {
|
|
152
|
-
writeFileSync(globalConfigPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
153
|
-
} catch (e) {
|
|
154
|
-
const message = e instanceof Error ? e.message : 'Unknown error';
|
|
155
|
-
return { added: [], alreadyPresent: false, error: `Failed to update OpenCode config: ${message}` };
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return { added, alreadyPresent: added.length === 0 };
|
|
117
|
+
return { added: [], alreadyPresent: true };
|
|
160
118
|
}
|
|
161
119
|
|
|
162
120
|
return { added: [], alreadyPresent: true };
|
|
@@ -285,13 +243,7 @@ export async function setupCommand(): Promise<void> {
|
|
|
285
243
|
console.log(chalk.gray(' Droid permissions already configured in Claude Code'));
|
|
286
244
|
}
|
|
287
245
|
} else if (answers.platform === Platform.OpenCode) {
|
|
288
|
-
|
|
289
|
-
console.log(chalk.green('✓ Added opencode-skills plugin to OpenCode config'));
|
|
290
|
-
console.log(chalk.gray(' This enables Claude Code-style skills in OpenCode'));
|
|
291
|
-
console.log(chalk.gray(' Restart OpenCode to activate the plugin'));
|
|
292
|
-
} else if (alreadyPresent) {
|
|
293
|
-
console.log(chalk.gray(' opencode-skills plugin already configured in OpenCode'));
|
|
294
|
-
}
|
|
246
|
+
console.log(chalk.gray(' OpenCode has native skills support - no configuration needed'));
|
|
295
247
|
}
|
|
296
248
|
|
|
297
249
|
console.log(chalk.gray('\nRun `droid skills` to browse and install skills.'));
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Box, Text } from 'ink';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
isToolInstalled,
|
|
4
|
+
getInstalledToolVersion,
|
|
5
|
+
getToolUpdateStatus,
|
|
6
|
+
} from '../../../lib/tools';
|
|
3
7
|
import type { ToolManifest } from '../../../lib/types';
|
|
4
8
|
import { colors } from '../constants';
|
|
5
9
|
import { ComponentBadges } from './Badge';
|
|
@@ -26,17 +30,26 @@ export function ToolDetails({
|
|
|
26
30
|
const installed = isToolInstalled(tool.name);
|
|
27
31
|
const installedVersion = getInstalledToolVersion(tool.name);
|
|
28
32
|
const updateStatus = getToolUpdateStatus(tool.name);
|
|
29
|
-
const isSystemTool =
|
|
33
|
+
const isSystemTool =
|
|
34
|
+
(tool as ToolManifest & { system?: boolean }).system === true;
|
|
30
35
|
|
|
31
36
|
const actions = installed
|
|
32
37
|
? [
|
|
33
38
|
{ id: 'explore', label: 'Explore', variant: 'default' },
|
|
34
39
|
...(updateStatus.hasUpdate
|
|
35
|
-
? [
|
|
40
|
+
? [
|
|
41
|
+
{
|
|
42
|
+
id: 'update',
|
|
43
|
+
label: `Update (${updateStatus.bundledVersion})`,
|
|
44
|
+
variant: 'primary',
|
|
45
|
+
},
|
|
46
|
+
]
|
|
36
47
|
: []),
|
|
37
48
|
{ id: 'configure', label: 'Configure', variant: 'default' },
|
|
38
49
|
// System tools can't be uninstalled
|
|
39
|
-
...(!isSystemTool
|
|
50
|
+
...(!isSystemTool
|
|
51
|
+
? [{ id: 'uninstall', label: 'Uninstall', variant: 'danger' }]
|
|
52
|
+
: []),
|
|
40
53
|
]
|
|
41
54
|
: [
|
|
42
55
|
{ id: 'explore', label: 'Explore', variant: 'default' },
|
|
@@ -46,7 +59,9 @@ export function ToolDetails({
|
|
|
46
59
|
|
|
47
60
|
return (
|
|
48
61
|
<Box flexDirection="column" paddingLeft={2} flexGrow={1}>
|
|
49
|
-
<Text color={colors.text} bold>
|
|
62
|
+
<Text color={colors.text} bold>
|
|
63
|
+
{tool.name}
|
|
64
|
+
</Text>
|
|
50
65
|
|
|
51
66
|
<Box marginTop={1}>
|
|
52
67
|
<Text color={colors.textDim}>
|
|
@@ -54,7 +69,10 @@ export function ToolDetails({
|
|
|
54
69
|
{tool.status && ` · ${tool.status}`}
|
|
55
70
|
{installed && <Text color={colors.success}> · installed</Text>}
|
|
56
71
|
{updateStatus.hasUpdate && (
|
|
57
|
-
<Text color={colors.primary}>
|
|
72
|
+
<Text color={colors.primary}>
|
|
73
|
+
{' '}
|
|
74
|
+
· update ({installedVersion} → {updateStatus.bundledVersion})
|
|
75
|
+
</Text>
|
|
58
76
|
)}
|
|
59
77
|
</Text>
|
|
60
78
|
</Box>
|
|
@@ -74,19 +92,27 @@ export function ToolDetails({
|
|
|
74
92
|
{tool.includes.skills.length > 0 && (
|
|
75
93
|
<Text>
|
|
76
94
|
<Text color={colors.skill}>Skills: </Text>
|
|
77
|
-
<Text color={colors.textMuted}>
|
|
95
|
+
<Text color={colors.textMuted}>
|
|
96
|
+
{tool.includes.skills.map((s) => s.name).join(', ')}
|
|
97
|
+
</Text>
|
|
78
98
|
</Text>
|
|
79
99
|
)}
|
|
80
100
|
{tool.includes.commands.length > 0 && (
|
|
81
101
|
<Text>
|
|
82
102
|
<Text color={colors.command}>Commands: </Text>
|
|
83
|
-
<Text color={colors.textMuted}>
|
|
103
|
+
<Text color={colors.textMuted}>
|
|
104
|
+
{tool.includes.commands
|
|
105
|
+
.map((c) => (typeof c === 'string' ? `/${c}` : `/${c.name}`))
|
|
106
|
+
.join(', ')}
|
|
107
|
+
</Text>
|
|
84
108
|
</Text>
|
|
85
109
|
)}
|
|
86
110
|
{tool.includes.agents.length > 0 && (
|
|
87
111
|
<Text>
|
|
88
112
|
<Text color={colors.agent}>Agents: </Text>
|
|
89
|
-
<Text color={colors.textMuted}>
|
|
113
|
+
<Text color={colors.textMuted}>
|
|
114
|
+
{tool.includes.agents.join(', ')}
|
|
115
|
+
</Text>
|
|
90
116
|
</Text>
|
|
91
117
|
)}
|
|
92
118
|
</Box>
|
|
@@ -107,7 +133,8 @@ export function ToolDetails({
|
|
|
107
133
|
color={selectedAction === index ? '#ffffff' : colors.textMuted}
|
|
108
134
|
bold={selectedAction === index}
|
|
109
135
|
>
|
|
110
|
-
{' '}
|
|
136
|
+
{' '}
|
|
137
|
+
{action.label}{' '}
|
|
111
138
|
</Text>
|
|
112
139
|
))}
|
|
113
140
|
</Box>
|
package/src/lib/migrations.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
setPlatformTools,
|
|
16
16
|
} from './types';
|
|
17
17
|
import { compareSemver } from './version';
|
|
18
|
-
import { getSkillsPath } from './platforms';
|
|
18
|
+
import { getSkillsPath, getCommandsPath } from './platforms';
|
|
19
19
|
import { getBundledTools } from './tools';
|
|
20
20
|
|
|
21
21
|
const MIGRATIONS_LOG_FILE = '.migrations.log';
|
|
@@ -212,74 +212,138 @@ function createConfigSkillNameMigration(version: string): Migration {
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
215
|
+
* Migration: Move OpenCode skills to native skill directory
|
|
216
|
+
*
|
|
217
|
+
* OpenCode now has native skills support and looks for skills in ~/.config/opencode/skill/
|
|
218
|
+
* (singular) instead of the old ~/.config/opencode/skills/ (plural). This migration
|
|
219
|
+
* moves existing skills to the correct location.
|
|
217
220
|
*/
|
|
218
|
-
function
|
|
221
|
+
function createOpenCodeSkillsPathMigration(version: string): Migration {
|
|
219
222
|
return {
|
|
220
223
|
version,
|
|
221
|
-
description: '
|
|
224
|
+
description: 'Move OpenCode skills from skills/ to skill/ directory',
|
|
222
225
|
up: () => {
|
|
223
226
|
const config = loadConfig();
|
|
224
|
-
const originalPlatform = config.platform;
|
|
225
|
-
let configChanged = false;
|
|
226
227
|
|
|
227
|
-
//
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
// Only run for OpenCode platform
|
|
229
|
+
if (config.platform !== Platform.OpenCode) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
230
232
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
+
const oldSkillsPath = join(getSkillsPath(Platform.OpenCode), '..', 'skills');
|
|
234
|
+
const newSkillsPath = getSkillsPath(Platform.OpenCode);
|
|
233
235
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
236
|
+
// Check if old directory exists
|
|
237
|
+
if (!existsSync(oldSkillsPath)) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
237
240
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
241
|
+
// If new directory doesn't exist, just rename the old one
|
|
242
|
+
if (!existsSync(newSkillsPath)) {
|
|
243
|
+
try {
|
|
244
|
+
renameSync(oldSkillsPath, newSkillsPath);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.warn(
|
|
247
|
+
`Warning: Could not rename skills directory ${oldSkillsPath}: ${error}`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
245
252
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
253
|
+
// Both exist - move skills from old to new
|
|
254
|
+
const skillDirs = readdirSync(oldSkillsPath, { withFileTypes: true })
|
|
255
|
+
.filter((dirent) => dirent.isDirectory())
|
|
256
|
+
.map((dirent) => dirent.name);
|
|
257
|
+
|
|
258
|
+
for (const skillName of skillDirs) {
|
|
259
|
+
const oldSkillDir = join(oldSkillsPath, skillName);
|
|
260
|
+
const newSkillDir = join(newSkillsPath, skillName);
|
|
261
|
+
|
|
262
|
+
// Only move if destination doesn't exist
|
|
263
|
+
if (!existsSync(newSkillDir)) {
|
|
264
|
+
try {
|
|
265
|
+
renameSync(oldSkillDir, newSkillDir);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.warn(
|
|
268
|
+
`Warning: Could not move skill ${skillName}: ${error}`,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
250
273
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
274
|
+
// Remove old directory if empty
|
|
275
|
+
try {
|
|
276
|
+
const remaining = readdirSync(oldSkillsPath);
|
|
277
|
+
if (remaining.length === 0) {
|
|
278
|
+
rmSync(oldSkillsPath, { recursive: true });
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
// Non-fatal: Log warning but continue
|
|
282
|
+
console.warn(
|
|
283
|
+
`Warning: Could not remove old skills directory ${oldSkillsPath}: ${error}`,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
}
|
|
254
289
|
|
|
255
|
-
|
|
256
|
-
|
|
290
|
+
/**
|
|
291
|
+
* Migration: Remove non-alias commands from Claude Code
|
|
292
|
+
*
|
|
293
|
+
* Claude Code v2.1.3+ can invoke skills directly via /{skillName}, so non-alias
|
|
294
|
+
* commands are redundant. This migration removes them from ~/.claude/commands/
|
|
295
|
+
* while keeping aliases (e.g., /scratchpad for /brain).
|
|
296
|
+
*/
|
|
297
|
+
function createClaudeCodeCommandCleanupMigration(version: string): Migration {
|
|
298
|
+
return {
|
|
299
|
+
version,
|
|
300
|
+
description: 'Remove non-alias commands from Claude Code',
|
|
301
|
+
up: () => {
|
|
302
|
+
const config = loadConfig();
|
|
257
303
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
);
|
|
263
|
-
if (matchingTool) {
|
|
264
|
-
version = matchingTool.version;
|
|
265
|
-
}
|
|
304
|
+
// Only run for Claude Code platform
|
|
305
|
+
if (config.platform !== Platform.ClaudeCode) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
266
308
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
platformChanged = true;
|
|
272
|
-
configChanged = true;
|
|
273
|
-
}
|
|
309
|
+
const commandsPath = getCommandsPath(Platform.ClaudeCode);
|
|
310
|
+
if (!existsSync(commandsPath)) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
274
313
|
|
|
275
|
-
|
|
276
|
-
|
|
314
|
+
// Get all bundled tools to check which commands are aliases
|
|
315
|
+
const bundledTools = getBundledTools();
|
|
316
|
+
const aliasCommands = new Set<string>();
|
|
317
|
+
|
|
318
|
+
// Collect all alias command names across all tools
|
|
319
|
+
for (const tool of bundledTools) {
|
|
320
|
+
for (const cmd of tool.includes.commands) {
|
|
321
|
+
if (typeof cmd === 'object' && cmd.is_alias) {
|
|
322
|
+
aliasCommands.add(cmd.name);
|
|
323
|
+
}
|
|
277
324
|
}
|
|
278
325
|
}
|
|
279
326
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
327
|
+
// Check each command file and remove non-aliases
|
|
328
|
+
const commandFiles = readdirSync(commandsPath, { withFileTypes: true })
|
|
329
|
+
.filter((dirent) => dirent.isFile() && dirent.name.endsWith('.md'))
|
|
330
|
+
.map((dirent) => dirent.name);
|
|
331
|
+
|
|
332
|
+
for (const file of commandFiles) {
|
|
333
|
+
const commandName = file.replace('.md', '');
|
|
334
|
+
|
|
335
|
+
// Keep aliases, remove everything else
|
|
336
|
+
if (!aliasCommands.has(commandName)) {
|
|
337
|
+
const commandFilePath = join(commandsPath, file);
|
|
338
|
+
try {
|
|
339
|
+
rmSync(commandFilePath);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
// Non-fatal: Log warning but continue
|
|
342
|
+
console.warn(
|
|
343
|
+
`Warning: Could not remove command ${commandFilePath}: ${error}`,
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
283
347
|
}
|
|
284
348
|
},
|
|
285
349
|
};
|
|
@@ -294,7 +358,8 @@ function createFilesystemSyncMigration(version: string): Migration {
|
|
|
294
358
|
const PACKAGE_MIGRATIONS: Migration[] = [
|
|
295
359
|
createPlatformSyncMigration('0.25.0'),
|
|
296
360
|
createConfigSkillNameMigration('0.27.2'),
|
|
297
|
-
|
|
361
|
+
createOpenCodeSkillsPathMigration('0.28.0'),
|
|
362
|
+
createClaudeCodeCommandCleanupMigration('0.28.0'),
|
|
298
363
|
];
|
|
299
364
|
|
|
300
365
|
/**
|
package/src/lib/platforms.ts
CHANGED
|
@@ -14,7 +14,7 @@ export const PLATFORM_PATHS = {
|
|
|
14
14
|
config: join(homedir(), '.claude', 'CLAUDE.md'),
|
|
15
15
|
},
|
|
16
16
|
[Platform.OpenCode]: {
|
|
17
|
-
skills: join(homedir(), '.config', 'opencode', '
|
|
17
|
+
skills: join(homedir(), '.config', 'opencode', 'skill'),
|
|
18
18
|
commands: join(homedir(), '.config', 'opencode', 'command'),
|
|
19
19
|
agents: join(homedir(), '.config', 'opencode', 'agent'),
|
|
20
20
|
config: join(homedir(), '.config', 'opencode', 'AGENTS.md'),
|
package/src/lib/skills.test.ts
CHANGED
|
@@ -44,7 +44,7 @@ describe("getSkillsInstallPath", () => {
|
|
|
44
44
|
|
|
45
45
|
it("should return OpenCode path", () => {
|
|
46
46
|
const path = getSkillsInstallPath(Platform.OpenCode);
|
|
47
|
-
expect(path).toBe(join(homedir(), ".config", "opencode", "
|
|
47
|
+
expect(path).toBe(join(homedir(), ".config", "opencode", "skill"));
|
|
48
48
|
});
|
|
49
49
|
});
|
|
50
50
|
|