@orderful/droid 0.28.0 → 0.28.1

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.
@@ -1,23 +1,17 @@
1
1
  name: Claude Code Review
2
2
 
3
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"
4
+ issue_comment:
5
+ types: [created]
13
6
 
14
7
  jobs:
15
8
  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'
9
+ # Only run on PR comments that mention claude and review, but not from bots
10
+ if: |
11
+ github.event.issue.pull_request &&
12
+ github.event.comment.user.type != 'Bot' &&
13
+ contains(github.event.comment.body, 'claude') &&
14
+ contains(github.event.comment.body, 'review')
21
15
 
22
16
  runs-on: ubuntu-latest
23
17
  permissions:
@@ -39,16 +33,23 @@ jobs:
39
33
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
40
34
  prompt: |
41
35
  REPO: ${{ github.repository }}
42
- PR NUMBER: ${{ github.event.pull_request.number }}
36
+ PR NUMBER: ${{ github.event.issue.number }}
43
37
 
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
38
+ Please review this pull request and identify **HIGH SEVERITY ISSUES ONLY**.
50
39
 
51
- Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
40
+ Focus on:
41
+ - Critical bugs that could cause crashes, data loss, or incorrect behaviour
42
+ - Security vulnerabilities (injection attacks, authentication/authorisation flaws, secrets exposure)
43
+ - Performance issues that could significantly impact production (N+1 queries, memory leaks, infinite loops)
44
+ - Breaking changes or backwards compatibility issues
45
+
46
+ **Do not report:**
47
+ - Style/formatting issues
48
+ - Minor refactoring opportunities
49
+ - Low-impact suggestions
50
+ - Trivial improvements
51
+
52
+ Use the repository's CLAUDE.md for guidance on conventions. Be direct and focus only on the most critical issues.
52
53
 
53
54
  Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.
54
55
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @orderful/droid
2
2
 
3
+ ## 0.28.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#163](https://github.com/Orderful/droid/pull/163) [`8bc0e51`](https://github.com/Orderful/droid/commit/8bc0e5104c735a970d6f64e09a58c467ba41e569) Thanks [@frytyler](https://github.com/frytyler)! - Fix agent cleanup during tool uninstall and retry command cleanup migration.
8
+ - **Agent cleanup**: Previously, agents were only removed if tracked in config's bundled_agents field. Now uninstall also checks the tool's bundled agents directory directly, ensuring cleanup even if config tracking failed or is missing.
9
+ - **Command cleanup**: Retry migration to remove non-alias commands from ~/.claude/commands/ (original 0.28.0 migration did not execute properly for some users)
10
+
3
11
  ## 0.28.0
4
12
 
5
13
  ### Minor Changes
package/dist/bin/droid.js CHANGED
@@ -698,10 +698,6 @@ function createClaudeCodeCommandCleanupMigration(version2) {
698
698
  version: version2,
699
699
  description: "Remove non-alias commands from Claude Code",
700
700
  up: () => {
701
- const config = loadConfig();
702
- if (config.platform !== "claude-code" /* ClaudeCode */) {
703
- return;
704
- }
705
701
  const commandsPath = getCommandsPath("claude-code" /* ClaudeCode */);
706
702
  if (!existsSync4(commandsPath)) {
707
703
  return;
@@ -736,7 +732,9 @@ var PACKAGE_MIGRATIONS = [
736
732
  createPlatformSyncMigration("0.25.0"),
737
733
  createConfigSkillNameMigration("0.27.2"),
738
734
  createOpenCodeSkillsPathMigration("0.28.0"),
739
- createClaudeCodeCommandCleanupMigration("0.28.0")
735
+ createClaudeCodeCommandCleanupMigration("0.28.0"),
736
+ // Retry: 0.28.0 migration had platform check that prevented running after platform switch
737
+ createClaudeCodeCommandCleanupMigration("0.28.1")
740
738
  ];
741
739
  var TOOL_MIGRATIONS = {
742
740
  brain: [createConfigDirMigration("droid-brain", "0.2.3")],
@@ -1251,11 +1249,22 @@ function uninstallSkill(skillName) {
1251
1249
  }
1252
1250
  }
1253
1251
  const installedSkillInfo = tools[skillName];
1252
+ const agentsToRemove = /* @__PURE__ */ new Set();
1254
1253
  if (installedSkillInfo?.bundled_agents) {
1255
1254
  for (const agentName of installedSkillInfo.bundled_agents) {
1256
- uninstallAgent(agentName);
1255
+ agentsToRemove.add(agentName);
1256
+ }
1257
+ }
1258
+ const agentsSource = skillPath ? join7(skillPath.toolDir, "agents") : null;
1259
+ if (agentsSource && existsSync5(agentsSource)) {
1260
+ const agentFiles = readdirSync4(agentsSource, { withFileTypes: true }).filter((dirent) => dirent.isFile() && dirent.name.endsWith(".md")).map((dirent) => dirent.name.replace(".md", ""));
1261
+ for (const agentName of agentFiles) {
1262
+ agentsToRemove.add(agentName);
1257
1263
  }
1258
1264
  }
1265
+ for (const agentName of agentsToRemove) {
1266
+ uninstallAgent(agentName);
1267
+ }
1259
1268
  const { [skillName]: removed, ...remainingTools } = tools;
1260
1269
  setPlatformTools(config, remainingTools);
1261
1270
  saveConfig(config);
package/dist/index.js CHANGED
@@ -671,10 +671,6 @@ function createClaudeCodeCommandCleanupMigration(version) {
671
671
  version,
672
672
  description: "Remove non-alias commands from Claude Code",
673
673
  up: () => {
674
- const config = loadConfig();
675
- if (config.platform !== "claude-code" /* ClaudeCode */) {
676
- return;
677
- }
678
674
  const commandsPath = getCommandsPath("claude-code" /* ClaudeCode */);
679
675
  if (!existsSync4(commandsPath)) {
680
676
  return;
@@ -709,7 +705,9 @@ var PACKAGE_MIGRATIONS = [
709
705
  createPlatformSyncMigration("0.25.0"),
710
706
  createConfigSkillNameMigration("0.27.2"),
711
707
  createOpenCodeSkillsPathMigration("0.28.0"),
712
- createClaudeCodeCommandCleanupMigration("0.28.0")
708
+ createClaudeCodeCommandCleanupMigration("0.28.0"),
709
+ // Retry: 0.28.0 migration had platform check that prevented running after platform switch
710
+ createClaudeCodeCommandCleanupMigration("0.28.1")
713
711
  ];
714
712
  var TOOL_MIGRATIONS = {
715
713
  brain: [createConfigDirMigration("droid-brain", "0.2.3")],
@@ -1229,11 +1227,22 @@ function uninstallSkill(skillName) {
1229
1227
  }
1230
1228
  }
1231
1229
  const installedSkillInfo = tools[skillName];
1230
+ const agentsToRemove = /* @__PURE__ */ new Set();
1232
1231
  if (installedSkillInfo?.bundled_agents) {
1233
1232
  for (const agentName of installedSkillInfo.bundled_agents) {
1234
- uninstallAgent(agentName);
1233
+ agentsToRemove.add(agentName);
1234
+ }
1235
+ }
1236
+ const agentsSource = skillPath ? join7(skillPath.toolDir, "agents") : null;
1237
+ if (agentsSource && existsSync5(agentsSource)) {
1238
+ const agentFiles = readdirSync4(agentsSource, { withFileTypes: true }).filter((dirent) => dirent.isFile() && dirent.name.endsWith(".md")).map((dirent) => dirent.name.replace(".md", ""));
1239
+ for (const agentName of agentFiles) {
1240
+ agentsToRemove.add(agentName);
1235
1241
  }
1236
1242
  }
1243
+ for (const agentName of agentsToRemove) {
1244
+ uninstallAgent(agentName);
1245
+ }
1237
1246
  const { [skillName]: removed, ...remainingTools } = tools;
1238
1247
  setPlatformTools(config, remainingTools);
1239
1248
  saveConfig(config);
@@ -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;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"}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/lib/migrations.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,SAAS,CAAC;AA4WjB;;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"}
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orderful/droid",
3
- "version": "0.28.0",
3
+ "version": "0.28.1",
4
4
  "description": "AI workflow toolkit for sharing skills, commands, and agents across the team",
5
5
  "type": "module",
6
6
  "bin": {
@@ -299,13 +299,8 @@ function createClaudeCodeCommandCleanupMigration(version: string): Migration {
299
299
  version,
300
300
  description: 'Remove non-alias commands from Claude Code',
301
301
  up: () => {
302
- const config = loadConfig();
303
-
304
- // Only run for Claude Code platform
305
- if (config.platform !== Platform.ClaudeCode) {
306
- return;
307
- }
308
-
302
+ // Clean up Claude Code commands directory regardless of current platform
303
+ // Users may have switched platforms, leaving orphaned commands
309
304
  const commandsPath = getCommandsPath(Platform.ClaudeCode);
310
305
  if (!existsSync(commandsPath)) {
311
306
  return;
@@ -360,6 +355,8 @@ const PACKAGE_MIGRATIONS: Migration[] = [
360
355
  createConfigSkillNameMigration('0.27.2'),
361
356
  createOpenCodeSkillsPathMigration('0.28.0'),
362
357
  createClaudeCodeCommandCleanupMigration('0.28.0'),
358
+ // Retry: 0.28.0 migration had platform check that prevented running after platform switch
359
+ createClaudeCodeCommandCleanupMigration('0.28.1'),
363
360
  ];
364
361
 
365
362
  /**
@@ -355,3 +355,68 @@ describe('platform-specific command installation', () => {
355
355
  }
356
356
  });
357
357
  });
358
+
359
+ describe('uninstallSkill agent cleanup', () => {
360
+ let testToolsDir: string;
361
+ let testAgentsDir: string;
362
+ let testSkillsDir: string;
363
+ let originalConfig: any;
364
+
365
+ beforeEach(() => {
366
+ originalConfig = loadConfig();
367
+
368
+ testToolsDir = join(tmpdir(), `droid-test-tools-${Date.now()}`);
369
+ testAgentsDir = join(tmpdir(), `droid-test-agents-${Date.now()}`);
370
+ testSkillsDir = join(tmpdir(), `droid-test-skills-${Date.now()}`);
371
+
372
+ mkdirSync(testToolsDir, { recursive: true });
373
+ mkdirSync(testAgentsDir, { recursive: true });
374
+ mkdirSync(testSkillsDir, { recursive: true });
375
+ });
376
+
377
+ afterEach(() => {
378
+ saveConfig(originalConfig);
379
+
380
+ if (existsSync(testToolsDir)) {
381
+ rmSync(testToolsDir, { recursive: true });
382
+ }
383
+ if (existsSync(testAgentsDir)) {
384
+ rmSync(testAgentsDir, { recursive: true });
385
+ }
386
+ if (existsSync(testSkillsDir)) {
387
+ rmSync(testSkillsDir, { recursive: true });
388
+ }
389
+ });
390
+
391
+ it('should check both config and tool manifest for agents to remove', () => {
392
+ // Create a mock tool structure with agents
393
+ const toolDir = join(testToolsDir, 'test-tool');
394
+ const agentsDir = join(toolDir, 'agents');
395
+ mkdirSync(agentsDir, { recursive: true });
396
+
397
+ // Create agent files
398
+ writeFileSync(join(agentsDir, 'agent-in-manifest.md'), '---\nname: agent-in-manifest\n---\nAgent content');
399
+ writeFileSync(join(agentsDir, 'agent-in-both.md'), '---\nname: agent-in-both\n---\nAgent content');
400
+
401
+ // This test verifies the logic exists to check both sources
402
+ // The actual uninstallSkill would need more mocking to test fully
403
+ expect(existsSync(join(agentsDir, 'agent-in-manifest.md'))).toBe(true);
404
+ expect(existsSync(join(agentsDir, 'agent-in-both.md'))).toBe(true);
405
+ });
406
+
407
+ it('should use Set to deduplicate agents from both sources', () => {
408
+ // Verify Set behavior for deduplication
409
+ const agents = new Set<string>();
410
+
411
+ // Simulate adding from config
412
+ agents.add('agent1');
413
+ agents.add('agent2');
414
+
415
+ // Simulate adding from manifest (agent2 is duplicate)
416
+ agents.add('agent2');
417
+ agents.add('agent3');
418
+
419
+ expect(agents.size).toBe(3);
420
+ expect(Array.from(agents)).toEqual(['agent1', 'agent2', 'agent3']);
421
+ });
422
+ });
package/src/lib/skills.ts CHANGED
@@ -697,13 +697,33 @@ export function uninstallSkill(skillName: string): {
697
697
  }
698
698
 
699
699
  // Remove bundled agents if they were installed with this skill
700
+ // Check both config tracking AND tool manifest to ensure cleanup
700
701
  const installedSkillInfo = tools[skillName];
702
+ const agentsToRemove = new Set<string>();
703
+
704
+ // Add agents from config tracking (if available)
701
705
  if (installedSkillInfo?.bundled_agents) {
702
706
  for (const agentName of installedSkillInfo.bundled_agents) {
703
- uninstallAgent(agentName);
707
+ agentsToRemove.add(agentName);
708
+ }
709
+ }
710
+
711
+ // Also check tool manifest for bundled agents (ensures cleanup even if tracking failed)
712
+ const agentsSource = skillPath ? join(skillPath.toolDir, 'agents') : null;
713
+ if (agentsSource && existsSync(agentsSource)) {
714
+ const agentFiles = readdirSync(agentsSource, { withFileTypes: true })
715
+ .filter((dirent) => dirent.isFile() && dirent.name.endsWith('.md'))
716
+ .map((dirent) => dirent.name.replace('.md', ''));
717
+ for (const agentName of agentFiles) {
718
+ agentsToRemove.add(agentName);
704
719
  }
705
720
  }
706
721
 
722
+ // Remove all agents
723
+ for (const agentName of agentsToRemove) {
724
+ uninstallAgent(agentName);
725
+ }
726
+
707
727
  // Remove from config (destructure to omit the skill being removed)
708
728
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
709
729
  const { [skillName]: removed, ...remainingTools } = tools;