Delegate skills installation to Vercel's add-skill CLI#209
Conversation
Replace custom file-copying installation logic with delegation to Vercel's add-skill CLI, gaining ecosystem compatibility with 20+ agents and simpler maintenance. Remove deprecated Claude-specific functions. https://claude.ai/code/session_01NrCbBbvoR9KsvXqoaovXPo
Replace installSkillTo/installSkillsForAgent with installViaAddSkill in wizard and migration flows. All skill installation now goes through Vercel's add-skill CLI, with shared AGENT_ID_MAP for ID translation. https://claude.ai/code/session_01NrCbBbvoR9KsvXqoaovXPo
When agents share skill directories via symlinks (e.g., ~/.agents/skills/), add-skill reports ELOOP errors for subsequent agents trying to mkdir inside already-symlinked paths. These errors are harmless since the skills are accessible via the shared symlink. Instead of treating non-zero exit as a hard failure, we now detect ELOOP-only failures and treat them as success. https://claude.ai/code/session_01NrCbBbvoR9KsvXqoaovXPo
Instead of inheriting add-skill's stdio (which shows confusing ELOOP errors when agents share skill directories via symlinks), capture the output and display our own concise summary showing skill/agent counts and a note about shared symlinks when applicable. https://claude.ai/code/session_01NrCbBbvoR9KsvXqoaovXPo
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. WalkthroughThis pull request replaces the in-process, per-agent skill installation flow with a delegated external Changes
Sequence DiagramsequenceDiagram
actor User
participant RalphCLI as Ralph CLI
participant ParseMod as Parse/Build (skills.ts)
participant AddSkill as bunx add-skill (external)
participant OutputParse as Output Parser (skills.ts)
User->>RalphCLI: ralph-tui skills install [--agent] [--skill] [--local|--global]
RalphCLI->>ParseMod: parseInstallArgs(args)
ParseMod-->>RalphCLI: { skillName, agentId, local, global }
RalphCLI->>ParseMod: buildAddSkillArgs({skillName, agentId, local, global})
ParseMod-->>RalphCLI: ["bunx","add-skill", ...args]
RalphCLI->>AddSkill: spawn bunx add-skill [args]
AddSkill-->>RalphCLI: stdout / stderr streams, exit
RalphCLI->>OutputParse: parseAddSkillOutput(stdout+stderr)
OutputParse-->>RalphCLI: { skillCount, agentCount, agents, installed, failureCount, eloopOnly }
RalphCLI->>User: print installation summary / hints / errors
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #209 +/- ##
==========================================
+ Coverage 45.04% 45.17% +0.13%
==========================================
Files 84 84
Lines 24411 24299 -112
==========================================
- Hits 10995 10977 -18
+ Misses 13416 13322 -94
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/commands/skills.ts`:
- Around line 376-382: The current guard uses `if (exitCode !== 0 && !output)`
but then logs `${output || 'add-skill failed to run'}`, which is pointless
because `output` is guaranteed falsy there; update the branching to handle the
two cases separately: check `if (exitCode !== 0)` and log `output || 'add-skill
failed to run'` (using the existing `exitCode`, `output`, `addSkillArgs`
variables), and add a separate `else if (!output)` branch to handle the
no-output case with a different message (e.g., 'no output from add-skill') so
messages reflect the actual condition.
🧹 Nitpick comments (5)
src/commands/skills.ts (1)
348-374: Consider extracting shared subprocess spawning logic.The subprocess spawning pattern here duplicates the logic in
installViaAddSkillfrom skill-installer.ts. Both functions spawnbunx, pipe output, and capture stdout/stderr.Consider extracting a shared helper (e.g.,
spawnBunxAddSkill(args: string[])) that both consumers can use, reducing duplication and ensuring consistent error handling.src/commands/skills.test.ts (4)
190-250: Consider adding a test for conflicting--localand--globalflags.The tests cover individual flags well, but there's no test for what happens when both
--localand--globalare specified together. Based on the implementation inskills.ts, both flags would be set totrue, which could lead to unexpected behaviour inbuildAddSkillArgs.📝 Suggested additional test case
test('handles both --local and --global specified', () => { const result = parseInstallArgs(['--local', '--global']); // Document expected behaviour - both true, or one takes precedence? expect(result.local).toBe(true); expect(result.global).toBe(true); });
332-350: Consider adding a note about why install tests are minimal.The install command tests are intentionally minimal since the actual installation is delegated to the external
bunx add-skillCLI. Consider adding a brief comment explaining this design decision, which would help future maintainers understand why more comprehensive install tests aren't present here.📝 Suggested documentation
describe('skills install command', () => { + // NOTE: Actual installation tests are limited because the command delegates + // to the external `bunx add-skill` CLI. Unit tests for argument parsing and + // output parsing are in the parseInstallArgs and parseAddSkillOutput blocks. let consoleSpy: ReturnType<typeof spyOn>;
352-415: Good coverage of output parsing, consider adding edge cases for mixed failures.The tests thoroughly cover the main scenarios including the ELOOP-only failure handling (which is important per the PR objectives). Consider adding tests for:
- Mixed ELOOP and non-ELOOP failures - to verify
eloopOnlyis correctlyfalsewhen both error types exist- Partial/malformed output - to verify graceful handling when some patterns match but others don't
📝 Suggested additional test cases
test('handles mixed ELOOP and non-ELOOP failures', () => { const output = `Found 2 skills Detected 2 agents Installing to: Claude Code, OpenCode Installation complete Failed to install 2 ELOOP: too many symbolic links encountered ENOENT: no such file or directory`; const result = parseAddSkillOutput(output); expect(result.failureCount).toBe(2); expect(result.eloopOnly).toBe(false); // Not ELOOP-only since ENOENT present }); test('handles partial output with only skill count', () => { const output = `Found 3 skills`; const result = parseAddSkillOutput(output); expect(result.skillCount).toBe(3); expect(result.agentCount).toBe(0); expect(result.installed).toBe(false); });
77-89: UnusedconsoleErrorSpyin test setup.The
consoleErrorSpyis set up at line 83 and restored at line 88, but it's not used in any of the tests within this describe block. Either remove it to reduce test setup overhead, or add a test for the unknown subcommand error case (which logs toconsole.errorper the implementation).📝 Option 1: Remove unused spy
describe('executeSkillsCommand', () => { let consoleSpy: ReturnType<typeof spyOn>; - let consoleErrorSpy: ReturnType<typeof spyOn>; beforeEach(() => { consoleSpy = spyOn(console, 'log').mockImplementation(() => {}); - consoleErrorSpy = spyOn(console, 'error').mockImplementation(() => {}); }); afterEach(() => { consoleSpy.mockRestore(); - consoleErrorSpy.mockRestore(); });📝 Option 2: Add test for unknown subcommand
test('shows error for unknown subcommand', async () => { const exitSpy = spyOn(process, 'exit').mockImplementation(() => { throw new Error('exit'); }); try { await executeSkillsCommand(['unknown']); } catch (e) { // Expected due to process.exit mock } expect(consoleErrorSpy).toHaveBeenCalled(); const errorOutput = consoleErrorSpy.mock.calls[0][0]; expect(errorOutput).toContain('Unknown subcommand'); exitSpy.mockRestore(); });
Adds comprehensive test coverage for subprocess-spawning code paths: - skill-installer-spawn.test.ts: 10 tests covering installViaAddSkill success, ELOOP handling, spawn errors, and argument passing - skills-install.test.ts: 12 tests covering handleInstallSkills via executeSkillsCommand with mocked spawn, verifying summary output, error handling, and argument forwarding Also fixes the spawn error detection condition in handleInstallSkills to check output prefix instead of emptiness (error handler sets output). Coverage: skills.ts 100% functions/96% lines, skill-installer.ts 100%/100% https://claude.ai/code/session_01NrCbBbvoR9KsvXqoaovXPo
- Restructure handleInstallSkills error handling into explicit branches for no-output and spawn-error cases (PR feedback) - Add wizard tests for installViaAddSkill success and failure paths - Add migration-install tests covering skill installation during config migration (success, failure, and agent unavailable scenarios) - Fix wizard test mocks to forward printSuccess/printError to console.log for output assertion capture - Use real temp directories in migration tests to avoid node:fs/promises mock interference across test files
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/setup/skill-installer-spawn.test.ts`:
- Around line 147-156: The test "handles null exit code as failure" currently
sets mockSpawnExitCode = 1 so it never exercises the null path; update the mock
to allow null and emit null from the child's 'close' event (instead of 1) so
installViaAddSkill sees a null code and triggers the fallback (code ?? 1).
Specifically, change the test to set mockSpawnExitCode = null (or configure the
spawn-mock event emitter to call close(null)) and keep the rest of the
assertions intact so the null-exit behaviour in installViaAddSkill is actually
tested.
🧹 Nitpick comments (1)
src/commands/skills.ts (1)
231-239: Avoid temporal wording in comments.“Now the default” introduces time‑dependent wording; rephrase to an evergreen statement.
As per coding guidelines, avoid temporal context in comments.♻️ Suggested tweak
- // Accepted for backwards compat but is now the default + // Accepted for backwards compat; default behaviour installs all skills
- Add skills-install.test.ts to skills coverage batch - Add skill-installer-spawn.test.ts as isolated batch (mocks node:child_process, conflicts with migration.test.ts) - Add migration-install.test.ts as isolated batch (mocks config/registry, conflicts with migration.test.ts) - Include new lcov files in codecov upload - Fix null exit code test to actually emit null from close event instead of using exit code 1 as a stand-in
Delegate skills installation to Vercel's add-skill CLI
Summary
Refactors the
ralph-tui skills installcommand to delegate to Vercel's add-skill CLI instead of managing skill installation directly. This enables support for 20+ agents (Claude Code, OpenCode, Codex, Gemini, Kiro, Cursor, Cline, etc.) while simplifying maintenance.Key Changes
Replaced direct installation logic with
bunx add-skill subsy/ralph-tuidelegationinstallSkillsForAgent()calls and related path managementparseInstallArgs(),buildAddSkillArgs(),parseAddSkillOutput()resolveAddSkillAgentId()(e.g.,claude→claude-code)Updated CLI interface
--forceflag (add-skill always overwrites)--agent,--local,--globalflags for compatibilitybunx add-skillusageImproved test coverage
parseAddSkillOutput()tests for handling various installation scenariosUpdated documentation
Migration path
migrateConfig()now usesinstallViaAddSkill()for setup flow--forceand--allflagsImplementation Details
bunx add-skillas a child process and captures outputSummary by CodeRabbit
Documentation
New Features
Improvements
Tests
✏️ Tip: You can customize this high-level summary in your review settings.