@orderful/droid 0.17.2 → 0.19.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.
Files changed (67) hide show
  1. package/.github/workflows/claude-code-review.yml +58 -0
  2. package/.github/workflows/claude.yml +50 -0
  3. package/CHANGELOG.md +37 -0
  4. package/dist/bin/droid.js +212 -79
  5. package/dist/index.js +188 -69
  6. package/dist/lib/config.d.ts.map +1 -1
  7. package/dist/lib/migrations.d.ts +30 -0
  8. package/dist/lib/migrations.d.ts.map +1 -0
  9. package/dist/lib/skill-config.d.ts.map +1 -1
  10. package/dist/lib/skills.d.ts.map +1 -1
  11. package/dist/lib/tools.d.ts.map +1 -1
  12. package/dist/lib/types.d.ts +10 -0
  13. package/dist/lib/types.d.ts.map +1 -1
  14. package/dist/tools/brain/TOOL.yaml +3 -3
  15. package/{src/tools/brain/skills/brain → dist/tools/brain/skills/droid-brain}/SKILL.md +1 -1
  16. package/dist/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/SKILL.md +1 -1
  17. package/dist/tools/coach/TOOL.yaml +2 -2
  18. package/dist/tools/coach/skills/{coach → droid-coach}/SKILL.md +1 -1
  19. package/dist/tools/code-review/TOOL.yaml +2 -2
  20. package/dist/tools/code-review/skills/{code-review → droid-code-review}/SKILL.md +1 -1
  21. package/dist/tools/comments/TOOL.yaml +2 -2
  22. package/dist/tools/comments/skills/{comments → droid-comments}/SKILL.md +1 -1
  23. package/dist/tools/project/TOOL.yaml +2 -2
  24. package/{src/tools/project/skills/project → dist/tools/project/skills/droid-project}/SKILL.md +1 -1
  25. package/package.json +1 -1
  26. package/src/lib/config.ts +13 -2
  27. package/src/lib/migrations.test.ts +163 -0
  28. package/src/lib/migrations.ts +182 -0
  29. package/src/lib/skill-config.ts +3 -1
  30. package/src/lib/skills.ts +57 -1
  31. package/src/lib/tools.ts +16 -2
  32. package/src/lib/types.ts +11 -0
  33. package/src/tools/brain/TOOL.yaml +3 -3
  34. package/{dist/tools/brain/skills/brain → src/tools/brain/skills/droid-brain}/SKILL.md +1 -1
  35. package/src/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/SKILL.md +1 -1
  36. package/src/tools/coach/TOOL.yaml +2 -2
  37. package/src/tools/coach/skills/{coach → droid-coach}/SKILL.md +1 -1
  38. package/src/tools/code-review/TOOL.yaml +2 -2
  39. package/src/tools/code-review/skills/{code-review → droid-code-review}/SKILL.md +1 -1
  40. package/src/tools/comments/TOOL.yaml +2 -2
  41. package/src/tools/comments/skills/{comments → droid-comments}/SKILL.md +1 -1
  42. package/src/tools/project/TOOL.yaml +2 -2
  43. package/{dist/tools/project/skills/project → src/tools/project/skills/droid-project}/SKILL.md +1 -1
  44. /package/dist/tools/brain/skills/{brain → droid-brain}/references/metadata.md +0 -0
  45. /package/dist/tools/brain/skills/{brain → droid-brain}/references/naming.md +0 -0
  46. /package/dist/tools/brain/skills/{brain → droid-brain}/references/templates.md +0 -0
  47. /package/dist/tools/brain/skills/{brain → droid-brain}/references/workflows.md +0 -0
  48. /package/dist/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/references/templates.md +0 -0
  49. /package/dist/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/references/workflows.md +0 -0
  50. /package/dist/tools/project/skills/{project → droid-project}/references/changelog.md +0 -0
  51. /package/dist/tools/project/skills/{project → droid-project}/references/creating.md +0 -0
  52. /package/dist/tools/project/skills/{project → droid-project}/references/loading.md +0 -0
  53. /package/dist/tools/project/skills/{project → droid-project}/references/templates.md +0 -0
  54. /package/dist/tools/project/skills/{project → droid-project}/references/updating.md +0 -0
  55. /package/dist/tools/project/skills/{project → droid-project}/references/versioning.md +0 -0
  56. /package/src/tools/brain/skills/{brain → droid-brain}/references/metadata.md +0 -0
  57. /package/src/tools/brain/skills/{brain → droid-brain}/references/naming.md +0 -0
  58. /package/src/tools/brain/skills/{brain → droid-brain}/references/templates.md +0 -0
  59. /package/src/tools/brain/skills/{brain → droid-brain}/references/workflows.md +0 -0
  60. /package/src/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/references/templates.md +0 -0
  61. /package/src/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/references/workflows.md +0 -0
  62. /package/src/tools/project/skills/{project → droid-project}/references/changelog.md +0 -0
  63. /package/src/tools/project/skills/{project → droid-project}/references/creating.md +0 -0
  64. /package/src/tools/project/skills/{project → droid-project}/references/loading.md +0 -0
  65. /package/src/tools/project/skills/{project → droid-project}/references/templates.md +0 -0
  66. /package/src/tools/project/skills/{project → droid-project}/references/updating.md +0 -0
  67. /package/src/tools/project/skills/{project → droid-project}/references/versioning.md +0 -0
@@ -0,0 +1,58 @@
1
+ name: Claude Code Review
2
+
3
+ on:
4
+ pull_request:
5
+ # Run only when PR is opened or converted from draft to ready
6
+ types: [opened, ready_for_review]
7
+ # Optional: Only run on specific file changes
8
+ # paths:
9
+ # - "src/**/*.ts"
10
+ # - "src/**/*.tsx"
11
+ # - "src/**/*.js"
12
+ # - "src/**/*.jsx"
13
+
14
+ jobs:
15
+ claude-review:
16
+ # Optional: Filter by PR author
17
+ # if: |
18
+ # github.event.pull_request.user.login == 'external-contributor' ||
19
+ # github.event.pull_request.user.login == 'new-developer' ||
20
+ # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
21
+
22
+ runs-on: ubuntu-latest
23
+ permissions:
24
+ contents: read
25
+ pull-requests: read
26
+ issues: read
27
+ id-token: write
28
+
29
+ steps:
30
+ - name: Checkout repository
31
+ uses: actions/checkout@v4
32
+ with:
33
+ fetch-depth: 1
34
+
35
+ - name: Run Claude Code Review
36
+ id: claude-review
37
+ uses: anthropics/claude-code-action@v1
38
+ with:
39
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
40
+ prompt: |
41
+ REPO: ${{ github.repository }}
42
+ PR NUMBER: ${{ github.event.pull_request.number }}
43
+
44
+ Please review this pull request and provide feedback on:
45
+ - Code quality and best practices
46
+ - Potential bugs or issues
47
+ - Performance considerations
48
+ - Security concerns
49
+ - Test coverage
50
+
51
+ Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
52
+
53
+ Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.
54
+
55
+ # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
56
+ # or https://code.claude.com/docs/en/cli-reference for available options
57
+ claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'
58
+
@@ -0,0 +1,50 @@
1
+ name: Claude Code
2
+
3
+ on:
4
+ issue_comment:
5
+ types: [created]
6
+ pull_request_review_comment:
7
+ types: [created]
8
+ issues:
9
+ types: [opened, assigned]
10
+ pull_request_review:
11
+ types: [submitted]
12
+
13
+ jobs:
14
+ claude:
15
+ if: |
16
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17
+ (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18
+ (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19
+ (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20
+ runs-on: ubuntu-latest
21
+ permissions:
22
+ contents: read
23
+ pull-requests: read
24
+ issues: read
25
+ id-token: write
26
+ actions: read # Required for Claude to read CI results on PRs
27
+ steps:
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v4
30
+ with:
31
+ fetch-depth: 1
32
+
33
+ - name: Run Claude Code
34
+ id: claude
35
+ uses: anthropics/claude-code-action@v1
36
+ with:
37
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38
+
39
+ # This is an optional setting that allows Claude to read CI results on PRs
40
+ additional_permissions: |
41
+ actions: read
42
+
43
+ # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
44
+ # prompt: 'Update the pull request description to include a summary of changes.'
45
+
46
+ # Optional: Add claude_args to customize behavior and configuration
47
+ # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
48
+ # or https://code.claude.com/docs/en/cli-reference for available options
49
+ # claude_args: '--allowed-tools Bash(gh pr:*)'
50
+
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @orderful/droid
2
2
 
3
+ ## 0.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#104](https://github.com/Orderful/droid/pull/104) [`b6eaef4`](https://github.com/Orderful/droid/commit/b6eaef4fb3b003d41b5451e7efdba633a50b525e) Thanks [@frytyler](https://github.com/frytyler)! - Add tool migration system and fix skill config paths
8
+ - **Migration system**: Tools can now define migrations that run after install/update. Migrations are tracked per-tool in config and logged to `~/.droid/.migrations.log`.
9
+ - **Config path fix**: Skill overrides now save to unprefixed paths (`~/.droid/skills/brain/` not `~/.droid/skills/droid-brain/`), matching what SKILL.md tells Claude to read.
10
+ - **Automatic migration**: Existing users with prefixed config directories will have them automatically migrated on tool update.
11
+
12
+ Tool version bumps:
13
+ - brain: 0.2.2 → 0.2.3
14
+ - comments: 0.2.5 → 0.2.6
15
+ - project: 0.1.4 → 0.1.5
16
+ - coach: 0.1.2 → 0.1.3
17
+
18
+ ## 0.18.0
19
+
20
+ ### Minor Changes
21
+
22
+ - [#102](https://github.com/Orderful/droid/pull/102) [`e45e61d`](https://github.com/Orderful/droid/commit/e45e61d812fe9d58288d7976650dda474f568516) Thanks [@frytyler](https://github.com/frytyler)! - **BREAKING:** Rename skill directories to `droid-*` prefix as workaround for Claude Code bug
23
+
24
+ > **Versioning Note:** This changeset uses `minor` for the droid package (0.17.2 → 0.18.0) per pre-1.0 semver convention where breaking changes are allowed in minor bumps. Individual tool versions receive `patch` bumps (0.2.4 → 0.2.5, etc.) since the tool interface remains unchanged - only internal skill directories are renamed.
25
+
26
+ Claude Code v2.0.75+ blocks direct user invocation of slash commands when a skill directory has the same name as the command. Renamed all skill directories from `{name}/` to `droid-{name}/` to avoid collision:
27
+ - `comments/` → `droid-comments/`
28
+ - `code-review/` → `droid-code-review/`
29
+ - `coach/` → `droid-coach/`
30
+ - `project/` → `droid-project/`
31
+ - `brain/` → `droid-brain/`
32
+ - `brain-obsidian/` → `droid-brain-obsidian/`
33
+
34
+ Command names remain unchanged (`/brain`, `/comments`, etc.). Tool names also remain unchanged - only internal skill directories are renamed.
35
+
36
+ **Migration:** After updating, affected tools will show as "update available" in the TUI. Click "Update" to automatically migrate to new skill directories. Old directories and config entries are automatically cleaned up during update - no manual intervention needed.
37
+
38
+ Bug report: https://github.com/anthropics/claude-code/issues/14945
39
+
3
40
  ## 0.17.2
4
41
 
5
42
  ### Patch Changes
package/dist/bin/droid.js CHANGED
@@ -7,8 +7,8 @@ import { program } from "commander";
7
7
  import inquirer from "inquirer";
8
8
  import chalk2 from "chalk";
9
9
  import { execSync as execSync2 } from "child_process";
10
- import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
11
- import { join as join7 } from "path";
10
+ import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "fs";
11
+ import { join as join8 } from "path";
12
12
  import { homedir as homedir3 } from "os";
13
13
 
14
14
  // src/lib/config.ts
@@ -126,8 +126,15 @@ function setConfigValue(key, value) {
126
126
  function getConfigPath() {
127
127
  return CONFIG_FILE;
128
128
  }
129
+ function getConfigDir() {
130
+ return CONFIG_DIR;
131
+ }
132
+ function normalizeSkillNameForConfig(skillName) {
133
+ return skillName.replace(/^droid-/, "");
134
+ }
129
135
  function getSkillOverridesPath(skillName) {
130
- return join(CONFIG_DIR, "skills", skillName, "overrides.yaml");
136
+ const normalizedName = normalizeSkillNameForConfig(skillName);
137
+ return join(CONFIG_DIR, "skills", normalizedName, "overrides.yaml");
131
138
  }
132
139
  function loadSkillOverrides(skillName) {
133
140
  const overridesPath = getSkillOverridesPath(skillName);
@@ -142,8 +149,9 @@ function loadSkillOverrides(skillName) {
142
149
  }
143
150
  }
144
151
  function saveSkillOverrides(skillName, overrides) {
152
+ const normalizedName = normalizeSkillNameForConfig(skillName);
145
153
  const overridesPath = getSkillOverridesPath(skillName);
146
- const skillDir = join(CONFIG_DIR, "skills", skillName);
154
+ const skillDir = join(CONFIG_DIR, "skills", normalizedName);
147
155
  if (!existsSync(skillDir)) {
148
156
  mkdirSync(skillDir, { recursive: true });
149
157
  }
@@ -168,8 +176,8 @@ function setAutoUpdateConfig(updates) {
168
176
  }
169
177
 
170
178
  // src/lib/skills.ts
171
- import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, rmSync } from "fs";
172
- import { join as join6, dirname as dirname4 } from "path";
179
+ import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2 } from "fs";
180
+ import { join as join7, dirname as dirname5, basename } from "path";
173
181
  import { fileURLToPath as fileURLToPath4 } from "url";
174
182
  import YAML4 from "yaml";
175
183
 
@@ -334,6 +342,16 @@ function getInstalledToolVersion(toolName) {
334
342
  if (installedTools[skillName]) {
335
343
  return installedTools[skillName].version;
336
344
  }
345
+ if (skillName.startsWith("droid-")) {
346
+ const oldSkillName = skillName.replace(/^droid-/, "");
347
+ if (installedTools[oldSkillName]) {
348
+ const version2 = installedTools[oldSkillName].version;
349
+ delete installedTools[oldSkillName];
350
+ setPlatformTools(config, installedTools);
351
+ saveConfig(config);
352
+ return version2;
353
+ }
354
+ }
337
355
  }
338
356
  return null;
339
357
  }
@@ -498,11 +516,104 @@ function uninstallAgent(agentName) {
498
516
  }
499
517
  }
500
518
 
519
+ // src/lib/migrations.ts
520
+ import { existsSync as existsSync4, appendFileSync, mkdirSync as mkdirSync3, renameSync, rmSync } from "fs";
521
+ import { join as join6, dirname as dirname4 } from "path";
522
+ var MIGRATIONS_LOG_FILE = ".migrations.log";
523
+ function getMigrationsLogPath() {
524
+ return join6(getConfigDir(), MIGRATIONS_LOG_FILE);
525
+ }
526
+ function logMigration(toolName, fromVersion, toVersion, status, error) {
527
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
528
+ const logEntry = error ? `${timestamp} ${toolName} ${fromVersion} \u2192 ${toVersion} ${status}: ${error}
529
+ ` : `${timestamp} ${toolName} ${fromVersion} \u2192 ${toVersion} ${status}
530
+ `;
531
+ const logPath = getMigrationsLogPath();
532
+ const logDir = dirname4(logPath);
533
+ if (!existsSync4(logDir)) {
534
+ mkdirSync3(logDir, { recursive: true });
535
+ }
536
+ appendFileSync(logPath, logEntry);
537
+ }
538
+ function createConfigDirMigration(skillName, version2) {
539
+ const unprefixedName = skillName.replace(/^droid-/, "");
540
+ return {
541
+ version: version2,
542
+ description: `Move ${skillName} config to unprefixed location`,
543
+ up: (configDir) => {
544
+ const oldDir = join6(configDir, "skills", skillName);
545
+ const newDir = join6(configDir, "skills", unprefixedName);
546
+ if (existsSync4(oldDir) && !existsSync4(newDir)) {
547
+ const parentDir = dirname4(newDir);
548
+ if (!existsSync4(parentDir)) {
549
+ mkdirSync3(parentDir, { recursive: true });
550
+ }
551
+ renameSync(oldDir, newDir);
552
+ } else if (existsSync4(oldDir) && existsSync4(newDir)) {
553
+ rmSync(oldDir, { recursive: true });
554
+ }
555
+ }
556
+ };
557
+ }
558
+ var TOOL_MIGRATIONS = {
559
+ brain: [createConfigDirMigration("droid-brain", "0.2.3")],
560
+ comments: [createConfigDirMigration("droid-comments", "0.2.6")],
561
+ project: [createConfigDirMigration("droid-project", "0.1.5")],
562
+ coach: [createConfigDirMigration("droid-coach", "0.1.3")]
563
+ };
564
+ function getToolMigrations(toolName) {
565
+ return TOOL_MIGRATIONS[toolName] ?? [];
566
+ }
567
+ function getLastMigratedVersion(toolName) {
568
+ const config = loadConfig();
569
+ return config.migrations?.[toolName] ?? "0.0.0";
570
+ }
571
+ function setLastMigratedVersion(toolName, version2) {
572
+ const config = loadConfig();
573
+ if (!config.migrations) {
574
+ config.migrations = {};
575
+ }
576
+ config.migrations[toolName] = version2;
577
+ saveConfig(config);
578
+ }
579
+ function runMigrations(toolName, fromVersion, toVersion) {
580
+ const migrations = getToolMigrations(toolName);
581
+ const pendingMigrations = migrations.filter((m) => {
582
+ const afterFrom = compareSemver(m.version, fromVersion) > 0;
583
+ const beforeOrAtTo = compareSemver(m.version, toVersion) <= 0;
584
+ return afterFrom && beforeOrAtTo;
585
+ });
586
+ if (pendingMigrations.length === 0) {
587
+ setLastMigratedVersion(toolName, toVersion);
588
+ return { success: true };
589
+ }
590
+ const configDir = getConfigDir();
591
+ for (const migration of pendingMigrations) {
592
+ try {
593
+ migration.up(configDir);
594
+ logMigration(toolName, fromVersion, migration.version, "OK");
595
+ } catch (error) {
596
+ const errorMessage = error instanceof Error ? error.message : String(error);
597
+ logMigration(toolName, fromVersion, migration.version, "FAILED", errorMessage);
598
+ return { success: false, error: `Migration ${migration.version} failed: ${errorMessage}` };
599
+ }
600
+ }
601
+ setLastMigratedVersion(toolName, toVersion);
602
+ return { success: true };
603
+ }
604
+ function runToolMigrations(toolName, installedVersion) {
605
+ const lastMigrated = getLastMigratedVersion(toolName);
606
+ if (compareSemver(installedVersion, lastMigrated) <= 0) {
607
+ return { success: true };
608
+ }
609
+ return runMigrations(toolName, lastMigrated, installedVersion);
610
+ }
611
+
501
612
  // src/lib/skills.ts
502
613
  var DROID_SKILLS_START = "<!-- droid-skills-start -->";
503
614
  var DROID_SKILLS_END = "<!-- droid-skills-end -->";
504
- var __dirname4 = dirname4(fileURLToPath4(import.meta.url));
505
- var BUNDLED_SKILLS_DIR = join6(__dirname4, "../tools");
615
+ var __dirname4 = dirname5(fileURLToPath4(import.meta.url));
616
+ var BUNDLED_SKILLS_DIR = join7(__dirname4, "../tools");
506
617
  function getSkillsInstallPath(platform) {
507
618
  return getSkillsPath(platform);
508
619
  }
@@ -515,7 +626,7 @@ function getPlatformConfigPath(platform) {
515
626
  function updatePlatformConfigSkills(platform, installedSkills) {
516
627
  const configPath = getPlatformConfigPath(platform);
517
628
  let content = "";
518
- if (existsSync4(configPath)) {
629
+ if (existsSync5(configPath)) {
519
630
  content = readFileSync5(configPath, "utf-8");
520
631
  }
521
632
  const skillLines = installedSkills.map((name) => {
@@ -534,9 +645,9 @@ ${DROID_SKILLS_END}` : "";
534
645
  } else if (skillsSection) {
535
646
  content = content.trim() + "\n\n" + skillsSection + "\n";
536
647
  }
537
- const configDir = dirname4(configPath);
538
- if (!existsSync4(configDir)) {
539
- mkdirSync3(configDir, { recursive: true });
648
+ const configDir = dirname5(configPath);
649
+ if (!existsSync5(configDir)) {
650
+ mkdirSync4(configDir, { recursive: true });
540
651
  }
541
652
  writeFileSync3(configPath, content, "utf-8");
542
653
  }
@@ -557,8 +668,8 @@ function parseSkillFrontmatter(content) {
557
668
  }
558
669
  }
559
670
  function loadSkillManifest(skillDir) {
560
- const skillMdPath = join6(skillDir, "SKILL.md");
561
- if (!existsSync4(skillMdPath)) {
671
+ const skillMdPath = join7(skillDir, "SKILL.md");
672
+ if (!existsSync5(skillMdPath)) {
562
673
  return null;
563
674
  }
564
675
  const content = readFileSync5(skillMdPath, "utf-8");
@@ -566,7 +677,7 @@ function loadSkillManifest(skillDir) {
566
677
  if (!frontmatter || !frontmatter.name) {
567
678
  return null;
568
679
  }
569
- const toolDir = dirname4(dirname4(skillDir));
680
+ const toolDir = dirname5(dirname5(skillDir));
570
681
  const toolManifest = loadToolManifest(toolDir);
571
682
  return {
572
683
  name: frontmatter.name,
@@ -578,17 +689,17 @@ function loadSkillManifest(skillDir) {
578
689
  };
579
690
  }
580
691
  function findSkillPath(skillName) {
581
- if (!existsSync4(BUNDLED_SKILLS_DIR)) {
692
+ if (!existsSync5(BUNDLED_SKILLS_DIR)) {
582
693
  return null;
583
694
  }
584
695
  const toolDirs = readdirSync3(BUNDLED_SKILLS_DIR, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
585
696
  for (const toolName of toolDirs) {
586
- const skillsDir = join6(BUNDLED_SKILLS_DIR, toolName, "skills");
587
- if (!existsSync4(skillsDir)) continue;
588
- const skillDir = join6(skillsDir, skillName);
589
- if (existsSync4(skillDir) && existsSync4(join6(skillDir, "SKILL.md"))) {
697
+ const skillsDir = join7(BUNDLED_SKILLS_DIR, toolName, "skills");
698
+ if (!existsSync5(skillsDir)) continue;
699
+ const skillDir = join7(skillsDir, skillName);
700
+ if (existsSync5(skillDir) && existsSync5(join7(skillDir, "SKILL.md"))) {
590
701
  return {
591
- toolDir: join6(BUNDLED_SKILLS_DIR, toolName),
702
+ toolDir: join7(BUNDLED_SKILLS_DIR, toolName),
592
703
  skillDir
593
704
  };
594
705
  }
@@ -596,17 +707,17 @@ function findSkillPath(skillName) {
596
707
  return null;
597
708
  }
598
709
  function getBundledSkills() {
599
- if (!existsSync4(BUNDLED_SKILLS_DIR)) {
710
+ if (!existsSync5(BUNDLED_SKILLS_DIR)) {
600
711
  return [];
601
712
  }
602
713
  const toolDirs = readdirSync3(BUNDLED_SKILLS_DIR, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
603
714
  const skills = [];
604
715
  for (const toolName of toolDirs) {
605
- const skillsDir = join6(BUNDLED_SKILLS_DIR, toolName, "skills");
606
- if (!existsSync4(skillsDir)) continue;
716
+ const skillsDir = join7(BUNDLED_SKILLS_DIR, toolName, "skills");
717
+ if (!existsSync5(skillsDir)) continue;
607
718
  const skillSubdirs = readdirSync3(skillsDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
608
719
  for (const skillName of skillSubdirs) {
609
- const manifest = loadSkillManifest(join6(skillsDir, skillName));
720
+ const manifest = loadSkillManifest(join7(skillsDir, skillName));
610
721
  if (manifest) {
611
722
  skills.push(manifest);
612
723
  }
@@ -683,17 +794,33 @@ function installSkill(skillName) {
683
794
  }
684
795
  }
685
796
  const skillsPath = getSkillsInstallPath(config.platform);
686
- const targetSkillDir = join6(skillsPath, skillName);
797
+ const targetSkillDir = join7(skillsPath, skillName);
687
798
  const commandsPath = getCommandsInstallPath(config.platform);
688
799
  const tools = getPlatformTools(config);
689
- const commandsSource = join6(toolDir, "commands");
690
- const agentsSource = join6(toolDir, "agents");
800
+ if (skillName.startsWith("droid-")) {
801
+ const oldSkillName = skillName.replace(/^droid-/, "");
802
+ const oldSkillDir = join7(skillsPath, oldSkillName);
803
+ if (existsSync5(oldSkillDir)) {
804
+ try {
805
+ rmSync2(oldSkillDir, { recursive: true });
806
+ } catch (error) {
807
+ console.warn(`Warning: Could not remove old skill directory ${oldSkillDir}: ${error}`);
808
+ }
809
+ }
810
+ if (tools[oldSkillName]) {
811
+ delete tools[oldSkillName];
812
+ setPlatformTools(config, tools);
813
+ saveConfig(config);
814
+ }
815
+ }
816
+ const commandsSource = join7(toolDir, "commands");
817
+ const agentsSource = join7(toolDir, "agents");
691
818
  if (!tools[skillName]) {
692
- if (existsSync4(commandsSource)) {
819
+ if (existsSync5(commandsSource)) {
693
820
  const commandFiles = readdirSync3(commandsSource).filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md");
694
821
  for (const file of commandFiles) {
695
- const targetCommandPath = join6(commandsPath, file);
696
- if (existsSync4(targetCommandPath)) {
822
+ const targetCommandPath = join7(commandsPath, file);
823
+ if (existsSync5(targetCommandPath)) {
697
824
  const commandName = file.replace(".md", "");
698
825
  return {
699
826
  success: false,
@@ -702,7 +829,7 @@ function installSkill(skillName) {
702
829
  }
703
830
  }
704
831
  }
705
- if (existsSync4(agentsSource)) {
832
+ if (existsSync5(agentsSource)) {
706
833
  const agentDirs = readdirSync3(agentsSource, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
707
834
  for (const agentName of agentDirs) {
708
835
  if (isAgentInstalled(agentName)) {
@@ -714,49 +841,49 @@ function installSkill(skillName) {
714
841
  }
715
842
  }
716
843
  }
717
- if (!existsSync4(skillsPath)) {
718
- mkdirSync3(skillsPath, { recursive: true });
844
+ if (!existsSync5(skillsPath)) {
845
+ mkdirSync4(skillsPath, { recursive: true });
719
846
  }
720
- const skillMdSource = join6(skillDir, "SKILL.md");
721
- if (existsSync4(skillMdSource)) {
722
- if (!existsSync4(targetSkillDir)) {
723
- mkdirSync3(targetSkillDir, { recursive: true });
847
+ const skillMdSource = join7(skillDir, "SKILL.md");
848
+ if (existsSync5(skillMdSource)) {
849
+ if (!existsSync5(targetSkillDir)) {
850
+ mkdirSync4(targetSkillDir, { recursive: true });
724
851
  }
725
- const skillMdTarget = join6(targetSkillDir, "SKILL.md");
852
+ const skillMdTarget = join7(targetSkillDir, "SKILL.md");
726
853
  const content = readFileSync5(skillMdSource, "utf-8");
727
854
  writeFileSync3(skillMdTarget, content);
728
855
  }
729
- const referencesSource = join6(skillDir, "references");
730
- if (existsSync4(referencesSource)) {
731
- const targetReferencesDir = join6(targetSkillDir, "references");
732
- if (!existsSync4(targetReferencesDir)) {
733
- mkdirSync3(targetReferencesDir, { recursive: true });
856
+ const referencesSource = join7(skillDir, "references");
857
+ if (existsSync5(referencesSource)) {
858
+ const targetReferencesDir = join7(targetSkillDir, "references");
859
+ if (!existsSync5(targetReferencesDir)) {
860
+ mkdirSync4(targetReferencesDir, { recursive: true });
734
861
  }
735
862
  const referenceFiles = readdirSync3(referencesSource).filter((f) => f.endsWith(".md"));
736
863
  for (const file of referenceFiles) {
737
- const sourcePath = join6(referencesSource, file);
738
- const targetPath = join6(targetReferencesDir, file);
864
+ const sourcePath = join7(referencesSource, file);
865
+ const targetPath = join7(targetReferencesDir, file);
739
866
  const content = readFileSync5(sourcePath, "utf-8");
740
867
  writeFileSync3(targetPath, content);
741
868
  }
742
869
  }
743
- if (existsSync4(commandsSource)) {
744
- if (!existsSync4(commandsPath)) {
745
- mkdirSync3(commandsPath, { recursive: true });
870
+ if (existsSync5(commandsSource)) {
871
+ if (!existsSync5(commandsPath)) {
872
+ mkdirSync4(commandsPath, { recursive: true });
746
873
  }
747
874
  const commandFiles = readdirSync3(commandsSource).filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md");
748
875
  for (const file of commandFiles) {
749
- const sourcePath = join6(commandsSource, file);
750
- const targetPath = join6(commandsPath, file);
876
+ const sourcePath = join7(commandsSource, file);
877
+ const targetPath = join7(commandsPath, file);
751
878
  const content = readFileSync5(sourcePath, "utf-8");
752
879
  writeFileSync3(targetPath, content);
753
880
  }
754
881
  }
755
882
  const installedAgents = [];
756
- if (existsSync4(agentsSource)) {
883
+ if (existsSync5(agentsSource)) {
757
884
  const agentDirs = readdirSync3(agentsSource, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
758
885
  for (const agentName of agentDirs) {
759
- const agentDir = join6(agentsSource, agentName);
886
+ const agentDir = join7(agentsSource, agentName);
760
887
  const result = installAgentFromPath(agentDir, agentName);
761
888
  if (result.success) {
762
889
  installedAgents.push(agentName);
@@ -775,6 +902,11 @@ function installSkill(skillName) {
775
902
  saveConfig(config);
776
903
  const installedSkillNames = Object.keys(updatedTools);
777
904
  updatePlatformConfigSkills(config.platform, installedSkillNames);
905
+ const toolName = basename(toolDir);
906
+ const migrationResult = runToolMigrations(toolName, manifest.version);
907
+ if (!migrationResult.success) {
908
+ console.warn(`Warning: Migration failed for ${toolName}: ${migrationResult.error}`);
909
+ }
778
910
  return { success: true, message: `Installed ${skillName} v${manifest.version}` };
779
911
  }
780
912
  function uninstallSkill(skillName) {
@@ -784,19 +916,19 @@ function uninstallSkill(skillName) {
784
916
  return { success: false, message: `Skill '${skillName}' is not installed` };
785
917
  }
786
918
  const skillsPath = getSkillsInstallPath(config.platform);
787
- const skillDir = join6(skillsPath, skillName);
788
- if (existsSync4(skillDir)) {
789
- rmSync(skillDir, { recursive: true });
919
+ const skillDir = join7(skillsPath, skillName);
920
+ if (existsSync5(skillDir)) {
921
+ rmSync2(skillDir, { recursive: true });
790
922
  }
791
923
  const skillPath = findSkillPath(skillName);
792
924
  const commandsPath = getCommandsInstallPath(config.platform);
793
- const commandsSource = skillPath ? join6(skillPath.toolDir, "commands") : null;
794
- if (commandsSource && existsSync4(commandsSource)) {
925
+ const commandsSource = skillPath ? join7(skillPath.toolDir, "commands") : null;
926
+ if (commandsSource && existsSync5(commandsSource)) {
795
927
  const commandFiles = readdirSync3(commandsSource).filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md");
796
928
  for (const file of commandFiles) {
797
- const commandPath = join6(commandsPath, file);
798
- if (existsSync4(commandPath)) {
799
- rmSync(commandPath);
929
+ const commandPath = join7(commandsPath, file);
930
+ if (existsSync5(commandPath)) {
931
+ rmSync2(commandPath);
800
932
  }
801
933
  }
802
934
  }
@@ -846,13 +978,13 @@ var OPENCODE_SKILLS_PLUGIN = "opencode-skills";
846
978
  function configurePlatformPermissions(platform) {
847
979
  const added = [];
848
980
  if (platform === "claude-code" /* ClaudeCode */) {
849
- const settingsPath = join7(homedir3(), ".claude", "settings.json");
850
- const claudeDir = join7(homedir3(), ".claude");
851
- if (!existsSync5(claudeDir)) {
852
- mkdirSync4(claudeDir, { recursive: true });
981
+ const settingsPath = join8(homedir3(), ".claude", "settings.json");
982
+ const claudeDir = join8(homedir3(), ".claude");
983
+ if (!existsSync6(claudeDir)) {
984
+ mkdirSync5(claudeDir, { recursive: true });
853
985
  }
854
986
  let settings = {};
855
- if (existsSync5(settingsPath)) {
987
+ if (existsSync6(settingsPath)) {
856
988
  try {
857
989
  settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
858
990
  } catch {
@@ -883,13 +1015,13 @@ function configurePlatformPermissions(platform) {
883
1015
  return { added, alreadyPresent: added.length === 0 };
884
1016
  }
885
1017
  if (platform === "opencode" /* OpenCode */) {
886
- const globalConfigDir = join7(homedir3(), ".config", "opencode");
887
- const globalConfigPath = join7(globalConfigDir, "opencode.json");
888
- if (!existsSync5(globalConfigDir)) {
889
- mkdirSync4(globalConfigDir, { recursive: true });
1018
+ const globalConfigDir = join8(homedir3(), ".config", "opencode");
1019
+ const globalConfigPath = join8(globalConfigDir, "opencode.json");
1020
+ if (!existsSync6(globalConfigDir)) {
1021
+ mkdirSync5(globalConfigDir, { recursive: true });
890
1022
  }
891
1023
  let config = {};
892
- if (existsSync5(globalConfigPath)) {
1024
+ if (existsSync6(globalConfigPath)) {
893
1025
  try {
894
1026
  config = JSON.parse(readFileSync6(globalConfigPath, "utf-8"));
895
1027
  } catch {
@@ -1154,8 +1286,9 @@ async function promptForSkillConfig(skillName, configSchema, askFirst = true) {
1154
1286
  }
1155
1287
  if (Object.keys(overrides).length > 0) {
1156
1288
  saveSkillOverrides(skillName, overrides);
1289
+ const displayName = skillName.replace(/^droid-/, "");
1157
1290
  console.log(chalk4.green(`
1158
- \u2713 Configuration saved to ~/.droid/skills/${skillName}/overrides.yaml`));
1291
+ \u2713 Configuration saved to ~/.droid/skills/${displayName}/overrides.yaml`));
1159
1292
  } else {
1160
1293
  console.log(chalk4.gray("\nNo custom configuration set (using defaults)."));
1161
1294
  }
@@ -2214,8 +2347,8 @@ function ReadmeViewer({ title, content, onClose }) {
2214
2347
  // src/commands/tui/views/ToolExplorer.tsx
2215
2348
  import { Box as Box10, Text as Text11, useInput as useInput5 } from "ink";
2216
2349
  import { useState as useState5, useMemo as useMemo3 } from "react";
2217
- import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
2218
- import { join as join8 } from "path";
2350
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
2351
+ import { join as join9 } from "path";
2219
2352
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2220
2353
  function ToolExplorer({ tool, onViewSource, onClose }) {
2221
2354
  const [selectedIndex, setSelectedIndex] = useState5(0);
@@ -2226,21 +2359,21 @@ function ToolExplorer({ tool, onViewSource, onClose }) {
2226
2359
  result.push({
2227
2360
  type: "skill",
2228
2361
  name: skill.name,
2229
- path: join8(toolDir, tool.name, "skills", skill.name, "SKILL.md")
2362
+ path: join9(toolDir, tool.name, "skills", skill.name, "SKILL.md")
2230
2363
  });
2231
2364
  }
2232
2365
  for (const cmd of tool.includes.commands) {
2233
2366
  result.push({
2234
2367
  type: "command",
2235
2368
  name: `/${cmd}`,
2236
- path: join8(toolDir, tool.name, "commands", `${cmd}.md`)
2369
+ path: join9(toolDir, tool.name, "commands", `${cmd}.md`)
2237
2370
  });
2238
2371
  }
2239
2372
  for (const agent of tool.includes.agents) {
2240
2373
  result.push({
2241
2374
  type: "agent",
2242
2375
  name: agent,
2243
- path: join8(toolDir, tool.name, "agents", agent, "AGENT.md")
2376
+ path: join9(toolDir, tool.name, "agents", agent, "AGENT.md")
2244
2377
  });
2245
2378
  }
2246
2379
  return result;
@@ -2258,12 +2391,12 @@ function ToolExplorer({ tool, onViewSource, onClose }) {
2258
2391
  }
2259
2392
  if (key.return && items.length > 0) {
2260
2393
  const item = items[selectedIndex];
2261
- if (existsSync6(item.path)) {
2394
+ if (existsSync7(item.path)) {
2262
2395
  const content = readFileSync7(item.path, "utf-8");
2263
2396
  onViewSource(`${tool.name} / ${item.name}`, content);
2264
2397
  } else {
2265
2398
  const yamlPath = item.path.replace(".md", ".yaml");
2266
- if (existsSync6(yamlPath)) {
2399
+ if (existsSync7(yamlPath)) {
2267
2400
  const content = readFileSync7(yamlPath, "utf-8");
2268
2401
  onViewSource(`${tool.name} / ${item.name}`, content);
2269
2402
  }