Skip to content
Development
Skill

/code-migration

Move code from one implementation to another function by function, tests first and code second, and check two implementations of one app for parity, with jscpd --compare as the progress measure and a coverage map binding each function to its tests. Use when porting a library or

BOOST
From plugin
jscpd
6.3k5 skills
Install
$ npx -y skills add kucherenko/jscpd --skill code-migration --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/code-migration

Context preview

The summary Claude sees to decide when to auto-load this skill.

Move code from one implementation to another function by function, tests first and code second, and check two implementations of one app for parity, with jscpd --compare as the progress measure and a coverage map binding each function to its tests. Use when porting a library or

SKILL.md

code-migration.SKILL.md
name: code-migration
description: Move code from one implementation to another function by function, tests first and code second, and check two implementations of one app for parity, with jscpd --compare as the progress measure and a coverage map binding each function to its tests. Use when porting a library or app to another language or framework (Java to Kotlin, JavaScript to Rust, a Python library to TypeScript, an iOS app to Android), when asked what is left to port, or when comparing the Android and iOS versions of an app.

code-migration

`jscpd --compare SOURCE TARGET` pairs every function of one folder with the function of the other folder that does the same job, in any pair of languages, and lists the functions that have no counterpart. This skill uses it to measure a port: what is ported, what is left, and whether the port you just wrote was recognized.

A port runs in two phases: the tests first, then the code they check. A coverage report of the source's tests tells which tests exercise which function, so each function is ported together with the tests that prove it works. See [Plan: tests first, then code](#plan-tests-first-then-code).

Two words are used throughout:

  • **source**: the implementation you port from, such as the iOS app, the Python library, or the JavaScript package;
  • **target**: the implementation you port to, such as the Android app, the Rust crate, or the TypeScript rewrite.

Neither has to be old or new. The source may stay in production and keep changing, and the target may already hold features the source lacks. For two implementations that both live on (`ios/` and `android/`), the same report shows parity; see [Parity](#parity-between-two-implementations).

jscpd finds functions in JavaScript, TypeScript, JSX, TSX, Vue, Svelte, Astro, Python, Rust, Go, Java, Kotlin, C#, C, C++, PHP, Ruby, Scala and Swift. The [compare-codebases](../compare-codebases/SKILL.md) skill explains how the comparison pairs functions and how to check its result; the [jscpd](../jscpd/SKILL.md) skill covers the rest of the tool.

Setup

`--compare` pairs functions with a code embedding model that runs inside jscpd. Download it once (548 MB, CodeRankEmbed). Ask the user before you start the download:

npx jscpd --semantic-download

jscpd caches the vectors, so a repeat run embeds only the functions whose code changed and takes seconds.

Pick the two paths and keep them fixed for the whole port: the source first, the target second. Two paths are required, and they must not overlap (`app/` and `app/android/` is refused). The target may be empty at the start.

One run measures both phases: the report has a `Code` block and a `Tests` block (a `code` and a `tests` section in JSON), and a test pairs only with a test. jscpd tells a test by the conventions of its language: test files such as `*_test.go`, `test_*.py`, `*.test.ts` or `*Test.java`, folders such as `tests/`, `__tests__/` or `src/test/`, Rust tests in `#[cfg(test)]` modules, and JavaScript test cases such as `it('rounds cents', () => …)`. Phase 1 reads the `Tests` block, phase 2 the `Code` block.

Keep vendored and generated code out

`--compare` counts and embeds every function in both paths. Vendored dependencies (`vendor/` from `cargo vendor` or `go mod vendor`, `third_party/`), installed packages (`node_modules/`, `.venv/`), build output (`target/`, `build/`, `dist/`) and generated code are not part of the port, yet a vendored crate tree alone holds thousands of functions. With them in a path, the first run embeds all of them and takes tens of minutes instead of seconds, and the percentages describe the dependencies instead of the port.

jscpd skips what `.gitignore` excludes, but only inside a git repository. Before the first run:

1. Check both paths for such folders. A target you create for the port gets them as soon as you build it or vendor its dependencies, so check again after the first build. 2. Suggest a `.gitignore` to the user that lists the folders the target's language and tools produce, plus the report folder. For a Rust addon built with napi-rs:

   /target/
   /vendor/
   node_modules/
   *.node
   .jscpd-compare/

If the target is not in a git repository, tell the user that jscpd reads the `.gitignore` only after `git init`. 3. Until the `.gitignore` works, pass the same folders with `--ignore`, and keep the globs the same for every run:

   npx jscpd --compare node-lib/ rust-lib/ --ignore "**/vendor/**,**/target/**,**/node_modules/**"

The totals line shows when something slipped through. Suspect vendored or generated code in a path when the target has far more functions than the source, or when a run embeds hundreds of functions after a small change.

Measure

Run the console report for yourself and for the user. Here a Python billing library is being ported to TypeScript:

npx jscpd --compare billing-py/ billing-ts/
 71% 5 of 7 functions in billing-py/ have a counterpart in billing-ts/
 80% 4 of 5 functions in billing-ts/ have a counterpart in billing-py/

billing-py/
  file         paired  similarity  counterpart
  billing.py   4 / 5   0.89        billing.ts
  shipping.py  1 / 2   0.91        shipping.ts

billing-ts/
  file         paired  similarity  counterpart
  billing.ts   3 / 4   0.89        billing.py
  shipping.ts  1 / 1   0.91        shipping.py

Paired under other names (1):
  billing-py/                   billing-ts/             similarity
  billing.py:28 tax_for_region  billing.ts:27 salesTax  0.87 high

Only in billing-py/ (2):
  billing.py (1)
    46  due_date                6 lines
  shipping.py (1)
    18  estimate_delivery_days  8 lines

Only in billing-ts/ (1):
  billing.ts (1)
    39  toCurrency  8 lines
  • The first line is the port's progress: the share of the source's functions that have a counterpart in the target. "Only in" the source is the work left.
  • The second line and "Only
Read more
Ships withjscpd

Duplicate code detector for 220+ languages — plus dead code, complexity hotspots, duplication trends over git history, and one health score for the whole codebase. Rust engine, self-contained binary, AI-ready with an MCP server and a token-efficient reporter.

Get the whole plugin
Stats
6,308
Stars
265
Forks
Active
Maintenance
Rust
Language
MIT
License
2h ago
Last commit
13y ago
Created

Repo: kucherenko/jscpd

Other skills on jscpd.