@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.
- package/.github/workflows/claude-code-review.yml +58 -0
- package/.github/workflows/claude.yml +50 -0
- package/CHANGELOG.md +37 -0
- package/dist/bin/droid.js +212 -79
- package/dist/index.js +188 -69
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/migrations.d.ts +30 -0
- package/dist/lib/migrations.d.ts.map +1 -0
- package/dist/lib/skill-config.d.ts.map +1 -1
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/tools.d.ts.map +1 -1
- package/dist/lib/types.d.ts +10 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/tools/brain/TOOL.yaml +3 -3
- package/{src/tools/brain/skills/brain → dist/tools/brain/skills/droid-brain}/SKILL.md +1 -1
- package/dist/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/SKILL.md +1 -1
- package/dist/tools/coach/TOOL.yaml +2 -2
- package/dist/tools/coach/skills/{coach → droid-coach}/SKILL.md +1 -1
- package/dist/tools/code-review/TOOL.yaml +2 -2
- package/dist/tools/code-review/skills/{code-review → droid-code-review}/SKILL.md +1 -1
- package/dist/tools/comments/TOOL.yaml +2 -2
- package/dist/tools/comments/skills/{comments → droid-comments}/SKILL.md +1 -1
- package/dist/tools/project/TOOL.yaml +2 -2
- package/{src/tools/project/skills/project → dist/tools/project/skills/droid-project}/SKILL.md +1 -1
- package/package.json +1 -1
- package/src/lib/config.ts +13 -2
- package/src/lib/migrations.test.ts +163 -0
- package/src/lib/migrations.ts +182 -0
- package/src/lib/skill-config.ts +3 -1
- package/src/lib/skills.ts +57 -1
- package/src/lib/tools.ts +16 -2
- package/src/lib/types.ts +11 -0
- package/src/tools/brain/TOOL.yaml +3 -3
- package/{dist/tools/brain/skills/brain → src/tools/brain/skills/droid-brain}/SKILL.md +1 -1
- package/src/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/SKILL.md +1 -1
- package/src/tools/coach/TOOL.yaml +2 -2
- package/src/tools/coach/skills/{coach → droid-coach}/SKILL.md +1 -1
- package/src/tools/code-review/TOOL.yaml +2 -2
- package/src/tools/code-review/skills/{code-review → droid-code-review}/SKILL.md +1 -1
- package/src/tools/comments/TOOL.yaml +2 -2
- package/src/tools/comments/skills/{comments → droid-comments}/SKILL.md +1 -1
- package/src/tools/project/TOOL.yaml +2 -2
- package/{dist/tools/project/skills/project → src/tools/project/skills/droid-project}/SKILL.md +1 -1
- /package/dist/tools/brain/skills/{brain → droid-brain}/references/metadata.md +0 -0
- /package/dist/tools/brain/skills/{brain → droid-brain}/references/naming.md +0 -0
- /package/dist/tools/brain/skills/{brain → droid-brain}/references/templates.md +0 -0
- /package/dist/tools/brain/skills/{brain → droid-brain}/references/workflows.md +0 -0
- /package/dist/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/references/templates.md +0 -0
- /package/dist/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/references/workflows.md +0 -0
- /package/dist/tools/project/skills/{project → droid-project}/references/changelog.md +0 -0
- /package/dist/tools/project/skills/{project → droid-project}/references/creating.md +0 -0
- /package/dist/tools/project/skills/{project → droid-project}/references/loading.md +0 -0
- /package/dist/tools/project/skills/{project → droid-project}/references/templates.md +0 -0
- /package/dist/tools/project/skills/{project → droid-project}/references/updating.md +0 -0
- /package/dist/tools/project/skills/{project → droid-project}/references/versioning.md +0 -0
- /package/src/tools/brain/skills/{brain → droid-brain}/references/metadata.md +0 -0
- /package/src/tools/brain/skills/{brain → droid-brain}/references/naming.md +0 -0
- /package/src/tools/brain/skills/{brain → droid-brain}/references/templates.md +0 -0
- /package/src/tools/brain/skills/{brain → droid-brain}/references/workflows.md +0 -0
- /package/src/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/references/templates.md +0 -0
- /package/src/tools/brain/skills/{brain-obsidian → droid-brain-obsidian}/references/workflows.md +0 -0
- /package/src/tools/project/skills/{project → droid-project}/references/changelog.md +0 -0
- /package/src/tools/project/skills/{project → droid-project}/references/creating.md +0 -0
- /package/src/tools/project/skills/{project → droid-project}/references/loading.md +0 -0
- /package/src/tools/project/skills/{project → droid-project}/references/templates.md +0 -0
- /package/src/tools/project/skills/{project → droid-project}/references/updating.md +0 -0
- /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
|
|
11
|
-
import { join as
|
|
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
|
-
|
|
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",
|
|
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
|
|
172
|
-
import { join as
|
|
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 =
|
|
505
|
-
var BUNDLED_SKILLS_DIR =
|
|
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 (
|
|
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 =
|
|
538
|
-
if (!
|
|
539
|
-
|
|
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 =
|
|
561
|
-
if (!
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
587
|
-
if (!
|
|
588
|
-
const skillDir =
|
|
589
|
-
if (
|
|
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:
|
|
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 (!
|
|
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 =
|
|
606
|
-
if (!
|
|
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(
|
|
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 =
|
|
797
|
+
const targetSkillDir = join7(skillsPath, skillName);
|
|
687
798
|
const commandsPath = getCommandsInstallPath(config.platform);
|
|
688
799
|
const tools = getPlatformTools(config);
|
|
689
|
-
|
|
690
|
-
|
|
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 (
|
|
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 =
|
|
696
|
-
if (
|
|
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 (
|
|
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 (!
|
|
718
|
-
|
|
844
|
+
if (!existsSync5(skillsPath)) {
|
|
845
|
+
mkdirSync4(skillsPath, { recursive: true });
|
|
719
846
|
}
|
|
720
|
-
const skillMdSource =
|
|
721
|
-
if (
|
|
722
|
-
if (!
|
|
723
|
-
|
|
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 =
|
|
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 =
|
|
730
|
-
if (
|
|
731
|
-
const targetReferencesDir =
|
|
732
|
-
if (!
|
|
733
|
-
|
|
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 =
|
|
738
|
-
const targetPath =
|
|
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 (
|
|
744
|
-
if (!
|
|
745
|
-
|
|
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 =
|
|
750
|
-
const targetPath =
|
|
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 (
|
|
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 =
|
|
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 =
|
|
788
|
-
if (
|
|
789
|
-
|
|
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 ?
|
|
794
|
-
if (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 =
|
|
798
|
-
if (
|
|
799
|
-
|
|
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 =
|
|
850
|
-
const claudeDir =
|
|
851
|
-
if (!
|
|
852
|
-
|
|
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 (
|
|
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 =
|
|
887
|
-
const globalConfigPath =
|
|
888
|
-
if (!
|
|
889
|
-
|
|
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 (
|
|
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/${
|
|
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
|
|
2218
|
-
import { join as
|
|
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:
|
|
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:
|
|
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:
|
|
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 (
|
|
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 (
|
|
2399
|
+
if (existsSync7(yamlPath)) {
|
|
2267
2400
|
const content = readFileSync7(yamlPath, "utf-8");
|
|
2268
2401
|
onViewSource(`${tool.name} / ${item.name}`, content);
|
|
2269
2402
|
}
|