@orderful/droid 0.27.5 → 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 +14 -0
- package/CHANGELOG.md +28 -0
- package/dist/bin/droid.js +96 -36
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/index.js +94 -2
- package/dist/lib/migrations.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/commands/setup.ts +4 -52
- package/src/lib/migrations.ts +141 -1
- 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/`).
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
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
|
+
|
|
3
31
|
## 0.27.5
|
|
4
32
|
|
|
5
33
|
### 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,9 +642,101 @@ function createConfigSkillNameMigration(version2) {
|
|
|
642
642
|
}
|
|
643
643
|
};
|
|
644
644
|
}
|
|
645
|
+
function createOpenCodeSkillsPathMigration(version2) {
|
|
646
|
+
return {
|
|
647
|
+
version: version2,
|
|
648
|
+
description: "Move OpenCode skills from skills/ to skill/ directory",
|
|
649
|
+
up: () => {
|
|
650
|
+
const config = loadConfig();
|
|
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}`
|
|
665
|
+
);
|
|
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
|
+
);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
try {
|
|
684
|
+
const remaining = readdirSync3(oldSkillsPath);
|
|
685
|
+
if (remaining.length === 0) {
|
|
686
|
+
rmSync(oldSkillsPath, { recursive: true });
|
|
687
|
+
}
|
|
688
|
+
} catch (error) {
|
|
689
|
+
console.warn(
|
|
690
|
+
`Warning: Could not remove old skills directory ${oldSkillsPath}: ${error}`
|
|
691
|
+
);
|
|
692
|
+
}
|
|
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
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
}
|
|
645
735
|
var PACKAGE_MIGRATIONS = [
|
|
646
736
|
createPlatformSyncMigration("0.25.0"),
|
|
647
|
-
createConfigSkillNameMigration("0.27.2")
|
|
737
|
+
createConfigSkillNameMigration("0.27.2"),
|
|
738
|
+
createOpenCodeSkillsPathMigration("0.28.0"),
|
|
739
|
+
createClaudeCodeCommandCleanupMigration("0.28.0")
|
|
648
740
|
];
|
|
649
741
|
var TOOL_MIGRATIONS = {
|
|
650
742
|
brain: [createConfigDirMigration("droid-brain", "0.2.3")],
|
|
@@ -1200,7 +1292,6 @@ function detectGitUsername() {
|
|
|
1200
1292
|
return "";
|
|
1201
1293
|
}
|
|
1202
1294
|
}
|
|
1203
|
-
var OPENCODE_SKILLS_PLUGIN = "opencode-skills";
|
|
1204
1295
|
function configurePlatformPermissions(platform) {
|
|
1205
1296
|
const added = [];
|
|
1206
1297
|
if (platform === "claude-code" /* ClaudeCode */) {
|
|
@@ -1242,35 +1333,10 @@ function configurePlatformPermissions(platform) {
|
|
|
1242
1333
|
}
|
|
1243
1334
|
if (platform === "opencode" /* OpenCode */) {
|
|
1244
1335
|
const globalConfigDir = join8(homedir3(), ".config", "opencode");
|
|
1245
|
-
const globalConfigPath = join8(globalConfigDir, "opencode.json");
|
|
1246
1336
|
if (!existsSync6(globalConfigDir)) {
|
|
1247
1337
|
mkdirSync5(globalConfigDir, { recursive: true });
|
|
1248
1338
|
}
|
|
1249
|
-
|
|
1250
|
-
if (existsSync6(globalConfigPath)) {
|
|
1251
|
-
try {
|
|
1252
|
-
config = JSON.parse(readFileSync6(globalConfigPath, "utf-8"));
|
|
1253
|
-
} catch {
|
|
1254
|
-
console.warn(chalk2.yellow("\u26A0 OpenCode config appears corrupted, resetting"));
|
|
1255
|
-
config = {};
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
if (!Array.isArray(config.plugin)) {
|
|
1259
|
-
config.plugin = [];
|
|
1260
|
-
}
|
|
1261
|
-
if (!config.plugin.includes(OPENCODE_SKILLS_PLUGIN)) {
|
|
1262
|
-
config.plugin.push(OPENCODE_SKILLS_PLUGIN);
|
|
1263
|
-
added.push(OPENCODE_SKILLS_PLUGIN);
|
|
1264
|
-
}
|
|
1265
|
-
if (added.length > 0) {
|
|
1266
|
-
try {
|
|
1267
|
-
writeFileSync4(globalConfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1268
|
-
} catch (e) {
|
|
1269
|
-
const message = e instanceof Error ? e.message : "Unknown error";
|
|
1270
|
-
return { added: [], alreadyPresent: false, error: `Failed to update OpenCode config: ${message}` };
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
return { added, alreadyPresent: added.length === 0 };
|
|
1339
|
+
return { added: [], alreadyPresent: true };
|
|
1274
1340
|
}
|
|
1275
1341
|
return { added: [], alreadyPresent: true };
|
|
1276
1342
|
}
|
|
@@ -1371,13 +1437,7 @@ async function setupCommand() {
|
|
|
1371
1437
|
console.log(chalk2.gray(" Droid permissions already configured in Claude Code"));
|
|
1372
1438
|
}
|
|
1373
1439
|
} else if (answers.platform === "opencode" /* OpenCode */) {
|
|
1374
|
-
|
|
1375
|
-
console.log(chalk2.green("\u2713 Added opencode-skills plugin to OpenCode config"));
|
|
1376
|
-
console.log(chalk2.gray(" This enables Claude Code-style skills in OpenCode"));
|
|
1377
|
-
console.log(chalk2.gray(" Restart OpenCode to activate the plugin"));
|
|
1378
|
-
} else if (alreadyPresent) {
|
|
1379
|
-
console.log(chalk2.gray(" opencode-skills plugin already configured in OpenCode"));
|
|
1380
|
-
}
|
|
1440
|
+
console.log(chalk2.gray(" OpenCode has native skills support - no configuration needed"));
|
|
1381
1441
|
}
|
|
1382
1442
|
console.log(chalk2.gray("\nRun `droid skills` to browse and install skills."));
|
|
1383
1443
|
}
|
|
@@ -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"}
|
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,9 +615,101 @@ function createConfigSkillNameMigration(version) {
|
|
|
615
615
|
}
|
|
616
616
|
};
|
|
617
617
|
}
|
|
618
|
+
function createOpenCodeSkillsPathMigration(version) {
|
|
619
|
+
return {
|
|
620
|
+
version,
|
|
621
|
+
description: "Move OpenCode skills from skills/ to skill/ directory",
|
|
622
|
+
up: () => {
|
|
623
|
+
const config = loadConfig();
|
|
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}`
|
|
638
|
+
);
|
|
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
|
+
);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
try {
|
|
657
|
+
const remaining = readdirSync3(oldSkillsPath);
|
|
658
|
+
if (remaining.length === 0) {
|
|
659
|
+
rmSync(oldSkillsPath, { recursive: true });
|
|
660
|
+
}
|
|
661
|
+
} catch (error) {
|
|
662
|
+
console.warn(
|
|
663
|
+
`Warning: Could not remove old skills directory ${oldSkillsPath}: ${error}`
|
|
664
|
+
);
|
|
665
|
+
}
|
|
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
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
}
|
|
618
708
|
var PACKAGE_MIGRATIONS = [
|
|
619
709
|
createPlatformSyncMigration("0.25.0"),
|
|
620
|
-
createConfigSkillNameMigration("0.27.2")
|
|
710
|
+
createConfigSkillNameMigration("0.27.2"),
|
|
711
|
+
createOpenCodeSkillsPathMigration("0.28.0"),
|
|
712
|
+
createClaudeCodeCommandCleanupMigration("0.28.0")
|
|
621
713
|
];
|
|
622
714
|
var TOOL_MIGRATIONS = {
|
|
623
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.'));
|
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';
|
|
@@ -211,6 +211,144 @@ function createConfigSkillNameMigration(version: string): Migration {
|
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
/**
|
|
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.
|
|
220
|
+
*/
|
|
221
|
+
function createOpenCodeSkillsPathMigration(version: string): Migration {
|
|
222
|
+
return {
|
|
223
|
+
version,
|
|
224
|
+
description: 'Move OpenCode skills from skills/ to skill/ directory',
|
|
225
|
+
up: () => {
|
|
226
|
+
const config = loadConfig();
|
|
227
|
+
|
|
228
|
+
// Only run for OpenCode platform
|
|
229
|
+
if (config.platform !== Platform.OpenCode) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const oldSkillsPath = join(getSkillsPath(Platform.OpenCode), '..', 'skills');
|
|
234
|
+
const newSkillsPath = getSkillsPath(Platform.OpenCode);
|
|
235
|
+
|
|
236
|
+
// Check if old directory exists
|
|
237
|
+
if (!existsSync(oldSkillsPath)) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
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
|
+
}
|
|
252
|
+
|
|
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
|
+
}
|
|
273
|
+
|
|
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
|
+
}
|
|
289
|
+
|
|
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();
|
|
303
|
+
|
|
304
|
+
// Only run for Claude Code platform
|
|
305
|
+
if (config.platform !== Platform.ClaudeCode) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const commandsPath = getCommandsPath(Platform.ClaudeCode);
|
|
310
|
+
if (!existsSync(commandsPath)) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
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
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
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
|
+
}
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
214
352
|
/**
|
|
215
353
|
* Registry of package-level migrations
|
|
216
354
|
* These run when the @orderful/droid npm package updates
|
|
@@ -220,6 +358,8 @@ function createConfigSkillNameMigration(version: string): Migration {
|
|
|
220
358
|
const PACKAGE_MIGRATIONS: Migration[] = [
|
|
221
359
|
createPlatformSyncMigration('0.25.0'),
|
|
222
360
|
createConfigSkillNameMigration('0.27.2'),
|
|
361
|
+
createOpenCodeSkillsPathMigration('0.28.0'),
|
|
362
|
+
createClaudeCodeCommandCleanupMigration('0.28.0'),
|
|
223
363
|
];
|
|
224
364
|
|
|
225
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
|
|