Convert Internal Package to TypeScript
TryGhost/Ghost
Moves a legacy internal Ghost package from JavaScript and CommonJS to TypeScript and ESM in three focused commits that keep git file history intact.
Migrates a direct command call in Ruby Git's Git::Lib to a Git::Commands class, as part of a Strangler Fig redesign, with a plan, legacy tests and a pull request.
$ npx skills add ruby-git/ruby-git --skill extract-command-from-lib -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install ruby-git/ruby-git extract-command-from-lib --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/ruby-git/ruby-git.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills-deprecated/extract-command-from-lib .claude/skills/extract-command-from-lib && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "extract-command-from-lib" agent skill from https://github.com/ruby-git/ruby-git/tree/main/.github/skills-deprecated/extract-command-from-lib into .claude/skills/extract-command-from-lib/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "extract-command-from-lib", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/ruby-git/ruby-git/tree/main/.github/skills-deprecated/extract-command-from-libType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add ruby-git/ruby-git --skill extract-command-from-lib -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install ruby-git/ruby-git extract-command-from-lib --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ruby-git/ruby-git.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.github/skills-deprecated/extract-command-from-lib .agents/skills/extract-command-from-lib && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "extract-command-from-lib" agent skill from https://github.com/ruby-git/ruby-git/tree/main/.github/skills-deprecated/extract-command-from-lib into .agents/skills/extract-command-from-lib/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "extract-command-from-lib", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add ruby-git/ruby-git --skill extract-command-from-lib -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install ruby-git/ruby-git extract-command-from-lib --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ruby-git/ruby-git.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.github/skills-deprecated/extract-command-from-lib .cursor/skills/extract-command-from-lib && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "extract-command-from-lib" agent skill from https://github.com/ruby-git/ruby-git/tree/main/.github/skills-deprecated/extract-command-from-lib into .cursor/skills/extract-command-from-lib/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "extract-command-from-lib", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/ruby-git/ruby-git.git --path .github/skills-deprecated/extract-command-from-lib--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add ruby-git/ruby-git --skill extract-command-from-lib -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install ruby-git/ruby-git extract-command-from-lib --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ruby-git/ruby-git.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.github/skills-deprecated/extract-command-from-lib .gemini/skills/extract-command-from-lib && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "extract-command-from-lib" agent skill from https://github.com/ruby-git/ruby-git/tree/main/.github/skills-deprecated/extract-command-from-lib into .gemini/skills/extract-command-from-lib/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "extract-command-from-lib", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install ruby-git/ruby-git extract-command-from-libInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add ruby-git/ruby-git --skill extract-command-from-lib -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/ruby-git/ruby-git.git skills-src && mkdir -p .github/skills && cp -r skills-src/.github/skills-deprecated/extract-command-from-lib .github/skills/extract-command-from-lib && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "extract-command-from-lib" agent skill from https://github.com/ruby-git/ruby-git/tree/main/.github/skills-deprecated/extract-command-from-lib into .github/skills/extract-command-from-lib/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "extract-command-from-lib", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add ruby-git/ruby-git --skill extract-command-from-lib -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install ruby-git/ruby-git extract-command-from-lib --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ruby-git/ruby-git.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.github/skills-deprecated/extract-command-from-lib .opencode/skills/extract-command-from-lib && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "extract-command-from-lib" agent skill from https://github.com/ruby-git/ruby-git/tree/main/.github/skills-deprecated/extract-command-from-lib into .opencode/skills/extract-command-from-lib/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "extract-command-from-lib", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
extract-command-from-libMigrates a direct command call in Ruby Git's Git::Lib to a Git::Commands class, as part of a Strangler Fig redesign, with a plan, legacy tests and a pull request.
This is a step-by-step migration recipe for the ruby-git library. It replaces a direct command call inside Git::Lib with delegation to a matching Git::Commands class, where the git subcommand is read from the first arguments of the call. You invoke it by attaching the file to Copilot Chat and naming the Git::Lib method or the subcommand string, such as a worktree add or ls-tree call. It requires loading the YARD Documentation skill in full beforehand.
The workflow covers branch setup, identifying the call, planning the migration and getting approval, making sure legacy tests are adequate, making sure the command class exists, and updating Git::Lib to delegate. Further sections cover commit discipline, creating a pull request, quality gates at every step, and patterns for simple delegation, post-processing, parsed return values, option key normalization and option filtering. It also lists what stays in Git::Lib and what moves. The excerpt is cut off early.
5 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit f3bf20f. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
bundlegitFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use git, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Extract Command from Git::Lib loads about 4.4k tokens when it runs. Until then it costs about 53 tokens; SKILL.md has 1,650 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from ruby-git/ruby-git at commit f3bf20f, republished under its MIT licence (© ruby-git). 1,650 words, ~4,383 tokens.
.claude/skills/extract-command-from-lib/SKILL.md (or your agent's skills folder).Replace a direct #command call in Git::Lib with a call to a Git::Commands::*
class. The git subcommand is determined by the first (or first few) arguments to the
#command method call.
Git::LibGit::Commands::*Attach this file to your Copilot Chat context, then invoke it with a short message
identifying the Git::Lib method or #command call to migrate. Examples:
Using the Extract Command from Lib skill, migrate Git::Lib#worktree_add —
it calls command('worktree', 'add', ...).Extract Command from Lib: command('ls-tree', ...)The invocation needs either the Git::Lib method name or the git subcommand string
from the #command call (or both).
Before starting, you MUST load the following skill(s) in their entirety:
Run or reference these skills during the workflow:
Git::Commands::*
classes, unit tests, integration tests, and YARD docs (used in Step 4 if the
command class does not exist yet); also the canonical class-shape checklist,
phased rollout gates, and internal compatibility contractsGit::Lib return-value contractsGit::Base /
Git::Lib into a Git::Repository::* facade method (Phase 4 deletes both
Git::Base and Git::Lib)Required:
Git::Lib method that contains one or more command(...) calls to replace#command)All work must be done on a feature branch. Never commit or push directly to
main.
Before starting, create a new branch:
git checkout -b <feature-branch-name>All commits in this workflow go on the feature branch. When work is complete,
open a pull request — do not merge or push directly into main.
#command callLocate the Git::Lib method that calls command(...).
Note:
#command)timeout:, out:, err:, env:).stdout, parsing, regex matching)Document the method's current public contract: signature, return type, and return-value format (String, Array, Hash, Boolean, etc.)
Run linters and rubocop to confirm a clean baseline:
bundle exec rubocopFix any issues before continuing.
Before writing or changing any code, present a migration plan and wait for explicit confirmation from the user. Do not proceed until they approve.
The plan must cover every #command call identified above. For each one, state:
Git::Lib method | #command call | Target Git::Commands class | Class exists? | Notes |
|---|---|---|---|---|
some_method | command('sub', '--flag', arg) | Git::Commands::Sub (new) or existing | ✅ / 🆕 | any mapping decisions |
Also state:
Git::Commands::* classes need to be created'' operands)Git::LibThen ask:
Does this mapping look correct? Any changes before I start implementing?
Do not move to Step 3 until the user confirms the plan.
Before making any changes, verify that tests/units/ has adequate tests for the
Git::Lib method being migrated.
Search existing legacy tests for coverage of the method:
grep -rn '<method_name>' tests/units/If coverage is insufficient, add minimal new tests to the legacy test suite that exercise the method's current behavior. These tests ensure the refactor does not break backward compatibility.
Do not change existing tests.
Follow existing legacy test conventions (Test::Unit::TestCase,
assert_command_line_eq, in_temp_dir, etc.).
Verify new tests pass:
bundle exec bin/test <test-file-basename>Run rubocop against the new test file:
bundle exec rubocop tests/units/<test-file>Fix any issues before continuing.
Commit the new legacy tests:
git add tests/units/<test-file>
git commit -m "refactor(test): add legacy tests for <method_name>"Git::Commands::* class existsSearch lib/git/commands/ for an existing command class that matches the git
subcommand:
find lib/git/commands -name '*.rb' | sortAlso check the class contents to confirm the existing class covers the same
subcommand variation (e.g., branch --show-current vs. branch --list).
If the command class already exists, skip to Step 5.
If the command class does not exist, scaffold it using the Command Implementation skill. This produces:
lib/git/commands/<command>.rb (or lib/git/commands/<family>/<action>.rb)spec/unit/git/commands/<command>_spec.rbspec/integration/git/commands/<command>_spec.rbVerify the new command class:
bundle exec rspec spec/unit/git/commands/<command>_spec.rb
bundle exec rspec spec/integration/git/commands/<command>_spec.rb
bundle exec rubocop lib/git/commands/<command>.rb
bundle exec rake yardFix any issues before continuing.
Commit the new command class and its tests:
git add lib/git/commands/<command>*.rb spec/
git commit -m "refactor(command): add Git::Commands::<Command> class"Git::Lib to delegate to the command classReplace the command(...) call with a call to the Git::Commands::* class:
# Before
def some_method(args)
command('some-command', '--flag', args).stdout
end
# After
def some_method(args)
Git::Commands::SomeCommand.new(self).call(args, flag: true).stdout
endPreserve the method's exact return value contract — apply any parsing or
transformation after .stdout / .stderr / .status to match the original
return type.
Prevent API expansion — the command class may accept many more options than
the legacy Git::Lib method ever exposed. Only forward the options that were
part of the original Git::Lib method's public API. Use a <COMMAND>_ALLOWED_OPTS
constant to whitelist permitted option keys, call assert_valid_opts to raise
on unknown keys, then filter with opts.slice before forwarding:
PULL_ALLOWED_OPTS = %i[allow_unrelated_histories].freeze
def pull(remote = nil, branch = nil, opts = {})
assert_valid_opts(opts, PULL_ALLOWED_OPTS)
allowed_opts = opts.slice(*PULL_ALLOWED_OPTS)
Git::Commands::Pull.new(self).call(remote, branch, **allowed_opts).stdout
endassert_valid_opts raises ArgumentError for any unrecognised key, giving
callers a clear error instead of silently ignoring unknown options. This
ensures that callers cannot accidentally pass options that happen to match
command DSL option names but were never part of the public contract.
Add the appropriate require_relative at the top of lib/git/lib.rb if not
already present.
Verify:
bundle exec bin/test <legacy-test-file-basename>
bundle exec rspec
bundle exec rubocop
bundle exec rake yardFix any issues before continuing.
Commit the Git::Lib change:
git add lib/git/lib.rb
git commit -m "refactor(lib): delegate <method_name> to Git::Commands::<Command>"Keep work organized into three logical commit categories (each optional if no changes were needed for that step):
refactor(test): add legacy tests for <method_name> — new tests in
tests/units/refactor(command): add Git::Commands::<Command> class — new command class,
unit specs, and integration specsrefactor(lib): delegate <method_name> to Git::Commands::<Command> — Git::Lib
changes onlyDuring implementation, you may use multiple task-level commits. Before opening a PR, follow the repository finalize workflow (see Development Workflow) and squash commits as required.
Issue and PR references in commit bodies: Do not use #<number> in the
commit body — write issue 1000 not issue #1000. A commitlint parser flaw
treats any line containing #<number> as a footer token, breaking the
body/footer split. To close an issue/PR, use Closes/Fixes/Resolves #<number>
in the footer. To merely mention one for context, omit the # and no footer line
is needed.
If further changes are needed after task commits are created:
Amend the change to the appropriate commit (e.g., a command class fix goes
into the refactor(command) commit).
Rebase the later commits on top:
git rebase -i <base-commit>After rebasing, verify all quality gates still pass:
bundle exec rspec && bundle exec rake test && bundle exec rubocop && bundle exec rake yardOnce all commits are clean and quality gates pass, create a PR for the branch.
If changes are made after the PR is created:
Amend the change to the appropriate commit.
Rebase later commits on top.
Force-push the branch:
git push --force-with-leasebundle exec rspec
bundle exec rake test
bundle exec rubocop
bundle exec rake yardAll four must pass before committing at each step. If errors are found, fix them before continuing.
# Before
def symbolic_ref(branch_name)
command('symbolic-ref', 'HEAD', "refs/heads/#{branch_name}")
end
# After
def symbolic_ref(branch_name)
Git::Commands::SymbolicRef.new(self).call(branch_name).stdout
end# Before
def cat_file_type(object)
command('cat-file', '-t', object).stdout
end
# After
def cat_file_type(object)
Git::Commands::CatFile::Type.new(self).call(object).stdout
end# Before
def worktree_list
worktrees = {}
command('worktree', 'list', '--porcelain').stdout.split("\n").each do |w|
# ... parsing ...
end
worktrees
end
# After
def worktree_list
result = Git::Commands::Worktree::List.new(self).call
worktrees = {}
result.stdout.split("\n").each do |w|
# ... parsing stays in Git::Lib ...
end
worktrees
endWhen the legacy method accepted a flat opts hash and uses a KEY_NORMALIZATIONS
constant to rename option keys before forwarding them, the constant's keys must be
the same type as the keys callers actually pass.
Ruby's 'key': symbol-literal syntax creates a symbol key — { 'update-head-ok': :x }
stores the key :'update-head-ok', not the string 'update-head-ok'. If legacy
callers pass string keys, the lookup misses and the raw key is forwarded unchanged,
causing a git "unsupported option" error at runtime.
Always symbolize keys before the normalization lookup:
# ❌ Bug — string key 'update-head-ok' misses the symbol key :'update-head-ok'
opts = opts.transform_keys { |k| KEY_NORMALIZATIONS.fetch(k, k) }
# ✅ Correct — symbolize first so both string and symbol callers match
opts = opts.transform_keys do |k|
sym = k.is_a?(Symbol) ? k : k.to_sym
KEY_NORMALIZATIONS.fetch(sym, sym)
endWhen to apply this pattern: Whenever transform_keys is combined with a
normalization constant whose keys use the 'hyphenated-name': symbol-literal
syntax. Scan the constant's definition to confirm its keys are symbols, then
confirm whether existing callers pass strings or symbols. If callers are a mix —
or if callers are Git::Base or Git::Lib methods that forward user-supplied
hashes — add the symbolization guard.
A command class may expose many more options than the legacy Git::Lib method
ever accepted. Without filtering, callers could accidentally pass options that
happen to match command DSL names but were never part of the public contract.
The facade is also where policy options are set as safe defaults — options
that support non-interactive execution, control output format for parsing, or
set other command-level defaults. The command class stays neutral; the facade
makes the defaults explicit. Some defaults are fixed (not in ALLOWED_OPTS —
assert_valid_opts rejects them if a caller supplies them); others are
overridable (in ALLOWED_OPTS, placed before **opts so the caller's value
wins on collision). Examples: no_edit: true, verbose: true,
no_progress: true, no_color: true.
See "Command-layer neutrality" in CONTRIBUTING.md.
Declare an <COMMAND>_ALLOWED_OPTS constant listing only the options that were
present in the original method. Call assert_valid_opts first to raise
ArgumentError on unrecognised keys, then use opts.slice to filter before
forwarding:
# Only :allow_unrelated_histories was accepted by the original Git::Lib#pull
PULL_ALLOWED_OPTS = %i[allow_unrelated_histories].freeze
def pull(remote = nil, branch = nil, opts = {})
raise ArgumentError, 'You must specify a remote if a branch is specified' if remote.nil? && !branch.nil?
assert_valid_opts(opts, PULL_ALLOWED_OPTS)
allowed_opts = opts.slice(*PULL_ALLOWED_OPTS)
positional_args = [remote, branch].compact
# no_edit: true is the non-interactive default (see CONTRIBUTING.md)
Git::Commands::Pull.new(self).call(*positional_args, no_edit: true, **allowed_opts).stdout
endassert_valid_opts is a private helper already defined in Git::Lib — no extra
require is needed. It raises ArgumentError: Unknown options: <key> when any
unrecognised key is present, giving callers a clear error rather than silently
dropping the option.
Name the constant after the git subcommand (PULL_ALLOWED_OPTS, FETCH_ALLOWED_OPTS,
etc.) and place it immediately before the method definition.
Git::Lib<COMMAND>_ALLOWED_OPTS + assert_valid_opts pattern)Git::Commands::*#command invocationallow_exit_status© ruby-git, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .github/skills-deprecated/extract-command-from-lib of ruby-git/ruby-git.
Open the folder on GitHubat commit f3bf20f
Extract Command from Git::Lib next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Extract Command from Git::Lib this skillruby-git/ruby-git | 1.8k | — | ~4.4k | Automated safety check: Pass | MIT | |
| Convert Internal Package to TypeScriptTryGhost/Ghost | 55k | — | ~1.2k | Automated safety check: Pass | MIT | |
| Legacy ModernizerJeffallan/claude-skills | 12k | — | ~1.6k | Automated safety check: Pass | MIT | |
| Golang Modernizeaiskillstore/marketplace | 430 | 1 repos | ~3.3k | Automated safety check: Pass | MIT | |
| Migrate Core Code to Submodulestinyhumansai/openhuman | 41k | — | ~2.6k | Automated safety check: Pass | GPL-3.0 | |
| Migrate Internal Package into GhostTryGhost/Ghost | 55k | — | ~3.8k | Automated safety check: Pass | MIT |
TryGhost/Ghost
Moves a legacy internal Ghost package from JavaScript and CommonJS to TypeScript and ESM in three focused commits that keep git file history intact.
Jeffallan/claude-skills
Plans incremental migrations of aging systems with the strangler fig pattern, using dependency maps, rollback plans, characterization tests and gradual traffic shifts.
aiskillstore/marketplace
Modernize Golang code to use recent language features, standard library improvements, and idiomatic patterns.
tinyhumansai/openhuman
Plans and carries out moving non-host-specific code and its tests from the OpenHuman core into vendored tiny submodule libraries, then releases the submodule and re-pins the host.
TryGhost/Ghost
Moves a package from another TryGhost repository into Ghost as an internal workspace package while keeping its Git history, with checkpoints for the steps that need an administrator.
code-yeongyu/oh-my-openagent
Searches and rewrites code by syntax-tree shape across 25 languages with ast-grep, for codemods, structural queries and YAML lint rules, using a Python wrapper script.
ruby-git/ruby-git
Addresses unresolved pull request review threads and suppressed (low-confidence) Copilot review comments on the current branch, folds each fix into the…
ruby-git/ruby-git
Assesses what an API change would break before it is made, finds every usage, documents the impact and plans a deprecation or migration path.
ruby-git/ruby-git
Diagnoses and fixes failing GitHub Actions runs by identifying the failure, fetching only the relevant logs, finding the root cause and reproducing it locally.
ruby-git/ruby-git
Scaffolds and reviews `Git::Commands::*` classes in the ruby-git library, with unit tests, integration tests and YARD docs, using the Base command architecture.
ruby-git/ruby-git
Workflow for updating gem dependencies and fixing CVEs in the ruby-git project: assess with bundle outdated and audit, edit the gemspec, test, then commit with conventional messages.
ruby-git/ruby-git
Scaffolds and reviews facade methods on Git::Repository in ruby-git, with unit tests, integration tests and YARD documentation.
Categories
Migrates a direct command call in Ruby Git's Git::Lib to a Git::Commands class, as part of a Strangler Fig redesign, with a plan, legacy tests and a pull request. This is a step-by-step migration recipe for the ruby-git library. It replaces a direct command call inside Git::Lib with delegation to a matching Git::Commands class, where the git subcommand is read from the first arguments of the call.
Extract Command from Git::Lib fits situations like: moving one git subcommand call out of Git::Lib into its own command class; checking which parts of a Git::Lib method stay and which move; writing legacy tests before refactoring a Git::Lib method; preparing a pull request for one step of the ruby-git redesign.
Run `npx skills add ruby-git/ruby-git --skill extract-command-from-lib -a claude-code`. Or copy the skill folder (.github/skills-deprecated/extract-command-from-lib in ruby-git/ruby-git) into .claude/skills/extract-command-from-lib in your project. Claude Code loads it when a task matches its description.
Run `npx skills add ruby-git/ruby-git --skill extract-command-from-lib -a codex`. Or copy the skill folder (.github/skills-deprecated/extract-command-from-lib in ruby-git/ruby-git) into .agents/skills/extract-command-from-lib in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add ruby-git/ruby-git --skill extract-command-from-lib -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/extract-command-from-lib, .gemini/skills/extract-command-from-lib, .github/skills/extract-command-from-lib and .opencode/skills/extract-command-from-lib in your project.
Going by SKILL.md and its folder, Extract Command from Git::Lib needs the command-line tools its instructions call (bundle and git). Our summary lists: A checkout of the ruby-git repository; The YARD Documentation skill.
SKILL.md contains no URLs. Its commands use git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Extract Command from Git::Lib is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.4k tokens (SKILL.md is roughly 18k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Extract Command from Git::Lib: Convert Internal Package to TypeScript (TryGhost/Ghost, 55k stars), Legacy Modernizer (Jeffallan/claude-skills, 12k stars), Golang Modernize (aiskillstore/marketplace, 430 stars) and Migrate Core Code to Submodules (tinyhumansai/openhuman, 41k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
ruby-git (a GitHub organization) maintains it in ruby-git/ruby-git, which has 1,799 GitHub stars. The repository holds 30 skills in this directory. The repository was last updated on October 2, 2026.
Source: ruby-git/ruby-git on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.