Skip to content

Delegate skills installation to Vercel's add-skill CLI#209

Merged
subsy merged 10 commits intomainfrom
claude/review-issue-197-6yqyl
Jan 23, 2026
Merged

Delegate skills installation to Vercel's add-skill CLI#209
subsy merged 10 commits intomainfrom
claude/review-issue-197-6yqyl

Conversation

@subsy
Copy link
Owner

@subsy subsy commented Jan 23, 2026

Summary

Refactors the ralph-tui skills install command 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-tui delegation

    • Removed installSkillsForAgent() calls and related path management
    • New helper functions: parseInstallArgs(), buildAddSkillArgs(), parseAddSkillOutput()
    • Agent ID mapping via resolveAddSkillAgentId() (e.g., claudeclaude-code)
  • Updated CLI interface

    • Removed --force flag (add-skill always overwrites)
    • Kept --agent, --local, --global flags for compatibility
    • Simplified help text to reference add-skill and supported agents
    • Updated examples to show both wrapper and direct bunx add-skill usage
  • Improved test coverage

    • New unit tests for argument parsing and command building
    • Added parseAddSkillOutput() tests for handling various installation scenarios
    • Simplified install command tests (now just verify help and delegation)
  • Updated documentation

    • README now shows add-skill installation methods instead of skill directory table
    • Mentions 20+ supported agents and direct bunx usage
    • Removed agent-specific path documentation (now handled by add-skill)
  • Migration path

    • migrateConfig() now uses installViaAddSkill() for setup flow
    • Maintains backwards compatibility with existing --force and --all flags

Implementation Details

  • Spawns bunx add-skill as a child process and captures output
  • Parses add-skill output to extract skill count, agent count, and failure details
  • Handles ELOOP-only failures gracefully (symlink-related, non-critical)
  • Provides helpful error messages if bunx is not available
  • Maintains user-friendly output formatting with installation summary

Summary by CodeRabbit

  • Documentation

    • Rewrote skill installation guidance to a command-based workflow with concrete bunx/add-skill and ralph-tui commands.
  • New Features

    • Support for global or agent-specific skill installs via the new command flow and wrapper commands.
  • Improvements

    • Streamlined install flow with clearer output, improved parsing and more helpful error/success summaries.
  • Tests

    • Expanded unit and integration tests covering install subprocess behaviour, parsing, and migration scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

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
@vercel
Copy link

vercel bot commented Jan 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Review Updated (UTC)
ralph-tui Ignored Ignored Preview Jan 23, 2026 4:44pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 23, 2026

Warning

Rate limit exceeded

@subsy has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 14 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

Walkthrough

This pull request replaces the in-process, per-agent skill installation flow with a delegated external bunx add-skill CLI workflow, adds parsing and argument-building utilities, introduces agent ID mapping and ELOOP detection, and updates callers and tests to use the new installViaAddSkill/add-skill-based interface.

Changes

Cohort / File(s) Summary
Docs & Command Surface
README.md, src/commands/skills.ts, src/commands/skills.test.ts, src/commands/skills-install.test.ts
Replaces static local-path guidance with add-skill installation commands and ralph-tui wrapper examples. Adds parseInstallArgs, buildAddSkillArgs, and parseAddSkillOutput; updates CLI help and unit tests to verify argument parsing and subprocess integration.
Skill Installer Core
src/setup/skill-installer.ts, src/setup/skill-installer.test.ts, src/setup/skill-installer-spawn.test.ts
Removes legacy per-agent/per-skill install APIs and replaces them with AGENT_ID_MAP, resolveAddSkillAgentId, buildAddSkillInstallArgs, installViaAddSkill, and isEloopOnlyFailure. Adds path helpers (tilde expansion, bundled skills discovery) and refactors tests to unit-test new exports and spawn behaviour.
Migration & Wizard Flows
src/setup/migration.ts, src/setup/migration-install.test.ts, src/setup/wizard.ts, src/setup/wizard.test.ts
Updates migration and setup wizard to call installViaAddSkill({ agentId, skillName?, global }) instead of installSkillsForAgent(...), adjusts result handling to rely on success/output, and adapts tests/mocks accordingly.
Test Coverage & Organisation
src/setup/*.test.ts, src/commands/*.test.ts (multiple files)
Shifts many integration tests to focused unit tests around parsing, argument building, and subprocess handling; introduces mocks for child_process spawn and reworks prompt mocks to surface printed output.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through code with nimble feet,

bunx add-skill< made installs neat.
Streams and args now dance in line,
One spawn, one parse — installation fine.
A rabbit's cheer: "Skills install, all mine!" 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: delegating skills installation from internal logic to Vercel's add-skill CLI.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Jan 23, 2026

Codecov Report

❌ Patch coverage is 92.89617% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 45.17%. Comparing base (d74e19d) to head (eaa8b1c).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
src/setup/skill-installer.ts 85.71% 9 Missing ⚠️
src/setup/migration.ts 76.92% 3 Missing ⚠️
src/setup/wizard.ts 87.50% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            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     
Files with missing lines Coverage Δ
src/commands/skills.ts 91.02% <100.00%> (+4.16%) ⬆️
src/setup/wizard.ts 65.17% <87.50%> (+7.28%) ⬆️
src/setup/migration.ts 65.04% <76.92%> (+12.12%) ⬆️
src/setup/skill-installer.ts 66.29% <85.71%> (-5.85%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 installViaAddSkill from skill-installer.ts. Both functions spawn bunx, 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 --local and --global flags.

The tests cover individual flags well, but there's no test for what happens when both --local and --global are specified together. Based on the implementation in skills.ts, both flags would be set to true, which could lead to unexpected behaviour in buildAddSkillArgs.

📝 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-skill CLI. 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:

  1. Mixed ELOOP and non-ELOOP failures - to verify eloopOnly is correctly false when both error types exist
  2. 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: Unused consoleErrorSpy in test setup.

The consoleErrorSpy is 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 to console.error per 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();
});

claude and others added 2 commits January 23, 2026 16:03
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
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

♻️ Suggested tweak
-    // Accepted for backwards compat but is now the default
+    // Accepted for backwards compat; default behaviour installs all skills
As per coding guidelines, avoid temporal context in comments.

AI Agent and others added 2 commits January 23, 2026 16:42
- 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
@subsy subsy merged commit 8afc587 into main Jan 23, 2026
9 checks passed
@subsy subsy deleted the claude/review-issue-197-6yqyl branch January 23, 2026 17:11
sakaman pushed a commit to sakaman/ralph-tui that referenced this pull request Feb 15, 2026
Delegate skills installation to Vercel's add-skill CLI
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments