|
| 1 | +--- |
| 2 | +name: java-to-typescript |
| 3 | +description: Use when migrating a Java service (Spring Boot, Quarkus, Micronaut, Spring MVC) to TypeScript. Supports full rewrite, strangler-fig, and module-by-module modes; targets Node (default), Bun, or Deno on Express, Koa, Hono, or Restify, with contract-parity verification against the original Java service. |
| 4 | +--- |
| 5 | + |
| 6 | +# java-to-typescript |
| 7 | + |
| 8 | +Migrate a Java HTTP service to TypeScript across four phases with explicit user gates. The LLM owns every translation decision; the bundled scripts only do deterministic plumbing (XML parsing, workspace scaffolding, HTTP capture, JSON diff). No runtime internet access is required. |
| 9 | + |
| 10 | +## When to use |
| 11 | + |
| 12 | +Invoke when the user asks to port, migrate, or rewrite a Java service in TypeScript. Detect Spring Boot / Quarkus / Micronaut / Spring MVC by scanning `pom.xml` or `build.gradle[.kts]` for the canonical dependency coords. |
| 13 | + |
| 14 | +Do not use for: GraphQL Java migrations, Akka/Pekko, Android, JNI, Java agents, or pure-library JARs without an HTTP surface. |
| 15 | + |
| 16 | +## Hard constraints |
| 17 | + |
| 18 | +1. The LLM does every translation. Scripts never translate, never decide a library choice, never write source code. They produce JSON, JSONL, or templated config files only. |
| 19 | +2. No runtime internet. The registry at `references/library-map.yaml` is the only source of library-mapping truth. Unmapped libraries escalate to the user via `AskUserQuestion` — never silently auto-fill, never call `context7` at runtime. |
| 20 | +3. Never skip a phase. Phase 2 is a hard user gate; Phase 4 is the acceptance gate. |
| 21 | + |
| 22 | +## The four phases |
| 23 | + |
| 24 | +### Phase 1 — Analyze (read-only) |
| 25 | + |
| 26 | +Run: |
| 27 | + |
| 28 | +``` |
| 29 | +tsx scripts/pom-to-workspace.ts analyze --repo <java-repo> |
| 30 | +``` |
| 31 | + |
| 32 | +This writes `<java-repo>/migration/analysis.json` with detected build system, modules, framework per module, dependency inventory, and any `unmappedDependencies`. Cross-reference dependencies against `references/library-map.yaml`. |
| 33 | + |
| 34 | +Consult: `references/library-map.yaml`, `references/frameworks/*.md`. |
| 35 | + |
| 36 | +### Phase 2 — Plan (hard user gate, diff preview) |
| 37 | + |
| 38 | +Ask the user (via your platform's interactive-prompt mechanism — `AskUserQuestion` in Claude Code; see `references/platform-adaptation.md` for Copilot CLI / Codex equivalents) for: |
| 39 | + |
| 40 | +- Migration mode (`full-rewrite | strangler-fig | module-by-module`) — consult `references/migration-modes.md`. |
| 41 | +- Target TS framework per Java module (Express default; Koa, Hono, Restify selectable) — consult `references/targets/*.md`. |
| 42 | +- Runtime (Node default; Bun, Deno opt-in) — consult `references/runtimes/*.md`. |
| 43 | +- Package manager (npm default; pnpm, yarn, bun opt-in). |
| 44 | +- DI library (tsyringe default). |
| 45 | +- Validation library (zod default). |
| 46 | + |
| 47 | +For each entry in `analysis.json.unmappedDependencies`, ask the user (interactive prompt) for a target. Do not auto-resolve. Do not call `context7`. |
| 48 | + |
| 49 | +Write `<java-repo>/migration/plan.md` containing per-module decisions, dependency order, and a one-shot diff preview (per module: what gets created, what the projected `package.json` looks like, which Java files map to which TS files). |
| 50 | + |
| 51 | +**Gate:** wait for explicit user approval of `plan.md` before Phase 3. |
| 52 | + |
| 53 | +Consult: `references/migration-modes.md`, `references/targets/*.md`, `references/runtimes/*.md`, `references/build-layout.md`, `references/type-fidelity.md`. |
| 54 | + |
| 55 | +### Phase 3 — Port |
| 56 | + |
| 57 | +Order: |
| 58 | + |
| 59 | +1. Write `<java-repo>/migration/scaffold.json` (derived from `plan.md`). Run `tsx scripts/pom-to-workspace.ts scaffold --plan <java-repo>/migration/scaffold.json --out <ts-repo>`. This emits root `package.json`, per-module `package.json`, `tsconfig.json`, `vitest.config.ts`, and `.gitignore`. No source code. |
| 60 | + |
| 61 | +2. Port deterministic surfaces first, per module: config (zod-validated env), DTOs (zod schemas), entities (Drizzle schema), validation rules. Consult `references/categories/{config,validation,persistence}.md` and `references/type-fidelity.md`. |
| 62 | + |
| 63 | +3. Port handlers one at a time, smallest module first. For each handler: read Java source → consult `references/frameworks/<source>.md` + `references/targets/<target>.md` → emit TS → run `tsc --noEmit` on the touched module → append a one-line entry to `<java-repo>/migration/port-log.md` recording any non-trivial decision. |
| 64 | + |
| 65 | +4. Port tests alongside each handler. Map JUnit→Vitest, Mockito→`vi.mock`, AssertJ→`expect`, Testcontainers→testcontainers-node. Consult `references/categories/testing.md`. |
| 66 | + |
| 67 | +5. Apply type-fidelity policy per `references/type-fidelity.md`. Deviations require a one-line `port-log.md` entry. |
| 68 | + |
| 69 | +6. **Parallelize independent modules** when the dependency graph permits — use whatever subagent/worker dispatch your platform offers. Modules with no cross-edges to in-flight work can be ported concurrently; modules that depend on an unfinished sibling MUST wait. The skill itself does not bundle a subagent runner; orchestration is the platform's job. (See `references/platform-adaptation.md` for the per-platform mechanism: Claude Code subagents, Copilot CLI workers, Codex CLI parallel tasks. If your platform has no parallel-dispatch primitive, port sequentially — correctness is unaffected.) |
| 70 | + |
| 71 | +**Non-negotiable patterns** (from `references/type-fidelity.md` §7.3): |
| 72 | +- One handler per file. |
| 73 | +- DTO ≡ zod schema. No interfaces on HTTP boundaries without a schema. |
| 74 | +- Repository ≡ Drizzle query module. No faked JPA repository interfaces. |
| 75 | +- Config ≡ single zod-parsed env object per module. No scattered `process.env.FOO`. |
| 76 | +- Logger ≡ module-scoped pino child. No `console.log`. |
| 77 | +- Errors ≡ subclasses of a per-module base class, mapped to HTTP status via single middleware. |
| 78 | + |
| 79 | +### Phase 4 — Verify (acceptance gate) |
| 80 | + |
| 81 | +The user starts both the Java service (`localhost:<javaPort>`) and the TS service (`localhost:<tsPort>`). The skill prints the expected commands and waits — it never boots services itself. |
| 82 | + |
| 83 | +1. Generate `<java-repo>/migration/corpus.jsonl` from Java controller signatures + sample DB state. Ask the user to review or extend before recording. |
| 84 | + |
| 85 | +2. Run: |
| 86 | + |
| 87 | + ``` |
| 88 | + tsx scripts/record-fixtures.ts --java-base http://localhost:<javaPort> --corpus migration/corpus.jsonl --out migration/fixtures.jsonl |
| 89 | + ``` |
| 90 | + |
| 91 | +3. Write `<java-repo>/migration/allowlist.json` declaring paths expected to differ (timestamps, generated IDs, trace headers). Schema: |
| 92 | + |
| 93 | + ```json |
| 94 | + { "headers": ["x-request-id", "date", "traceparent"], |
| 95 | + "bodyPaths": ["$.createdAt", "$.updatedAt"], |
| 96 | + "arrayKeys": { "$.items": "id" } } |
| 97 | + ``` |
| 98 | + |
| 99 | +4. Run: |
| 100 | + |
| 101 | + ``` |
| 102 | + tsx scripts/replay-fixtures.ts --ts-base http://localhost:<tsPort> --fixtures migration/fixtures.jsonl --allowlist migration/allowlist.json --report migration/verify-report.md |
| 103 | + ``` |
| 104 | + |
| 105 | +5. Run the ported Vitest suite from the TS workspace root. |
| 106 | + |
| 107 | +**Acceptance gate (both required):** zero unexpected diffs in `verify-report.md` AND all ported Vitest suites green. |
| 108 | + |
| 109 | +Consult: `references/categories/testing.md`. |
| 110 | + |
| 111 | +## Migration artifact location |
| 112 | + |
| 113 | +All artifacts (`analysis.json`, `plan.md`, `scaffold.json`, `corpus.jsonl`, `fixtures.jsonl`, `allowlist.json`, `port-log.md`, `verify-report.md`, `verify-report.json`) live in `<java-repo>/migration/`. On first run, the skill adds `migration/` to the target repo's `.gitignore`. |
| 114 | + |
| 115 | +## When stuck |
| 116 | + |
| 117 | +- Library not in registry: ask the user via `AskUserQuestion`. Do not invent a mapping. |
| 118 | +- Build-system parse warnings (Gradle Kotlin DSL with dynamic deps): show the warning verbatim to the user; ask them to confirm the dependency list. |
| 119 | +- Phase 4 unexpected diffs: report each diff with the Java side and TS side excerpted; return to Phase 3 for the responsible handler. |
| 120 | +- Tests fail after porting: do not weaken assertions to make them pass. Diagnose root cause; if the Java behavior cannot be replicated, escalate. |
0 commit comments