Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
ee13d93
checkpoint: pre-yolo 2026-04-26T14:52:15
aksOps Apr 26, 2026
530a357
checkpoint: pre-yolo 2026-05-17T15:05:15
aksOps May 17, 2026
0b0e05e
chore(java-to-typescript): wire new skill module into parent build
aksOps May 17, 2026
48a3534
feat(java-to-typescript): add Maven module + assembly config
aksOps May 17, 2026
c89d306
feat(java-to-typescript): add SKILL.md orchestrator (4 phases, hard g…
aksOps May 17, 2026
9c72b3c
build(java-to-typescript): add npm/tsconfig/vitest configs
aksOps May 17, 2026
f2074b8
fix(java-to-typescript): include vitest.config.ts in tsconfig (resolv…
aksOps May 17, 2026
422ff56
feat(java-to-typescript): add jsonpath-lite helper for allowlist masking
aksOps May 17, 2026
0e7f95f
feat(java-to-typescript): add structural json-diff with allowlist + a…
aksOps May 17, 2026
c025061
feat(java-to-typescript): add loopback-only HTTP helper
aksOps May 17, 2026
ec72de8
vendor(java-to-typescript): fast-xml-parser 4.5.0 (MIT) for air-gap p…
aksOps May 17, 2026
9dc1973
fix(java-to-typescript): exclude vendored fast-xml-parser from tsc in…
aksOps May 17, 2026
80d5534
feat(java-to-typescript): pom-to-workspace analyze subcommand
aksOps May 17, 2026
b7e2ab2
feat(java-to-typescript): pom-to-workspace scaffold subcommand
aksOps May 17, 2026
cca8d4f
feat(java-to-typescript): record-fixtures HTTP capture (loopback-only)
aksOps May 17, 2026
5805107
feat(java-to-typescript): replay-fixtures + JSON diff report
aksOps May 17, 2026
278a305
feat(java-to-typescript): M0 library registry (20 entries + default_p…
aksOps May 17, 2026
6f26376
docs(java-to-typescript): spring-boot porting reference
aksOps May 17, 2026
9e7a731
docs(java-to-typescript): express target reference
aksOps May 17, 2026
351dddb
docs(java-to-typescript): node runtime reference
aksOps May 17, 2026
647b7b0
docs(java-to-typescript): persistence category reference
aksOps May 17, 2026
c77cbb7
docs(java-to-typescript): validation category reference
aksOps May 17, 2026
ec9b8f5
docs(java-to-typescript): di category reference
aksOps May 17, 2026
a2abb9b
docs(java-to-typescript): logging category reference
aksOps May 17, 2026
ba9474b
docs(java-to-typescript): config category reference
aksOps May 17, 2026
99f0820
docs(java-to-typescript): testing category reference
aksOps May 17, 2026
e2e56db
docs(java-to-typescript): type-fidelity policy reference
aksOps May 17, 2026
e0e6c36
docs(java-to-typescript): build-layout reference
aksOps May 17, 2026
0facb43
docs(java-to-typescript): migration-modes reference
aksOps May 17, 2026
e1056f4
feat(java-to-typescript): eval runner (E1 deterministic; E4 via repla…
aksOps May 17, 2026
63ceea6
test(java-to-typescript): spring-boot-users eval fixture (Java sources)
aksOps May 17, 2026
cf8fdd9
test(java-to-typescript): spring-boot-users expected fixtures + E1 green
aksOps May 17, 2026
037abdc
test(java-to-typescript): shared eval rubric templates
aksOps May 17, 2026
2478c83
docs(java-to-typescript): evals README
aksOps May 17, 2026
73c0da1
ci(java-to-typescript): PR-trigger workflow (typecheck + unit + E1)
aksOps May 17, 2026
9dfd451
docs(readme): list java-to-typescript skill
aksOps May 17, 2026
0cbc44d
fix(java-to-typescript): exclude tests, fixtures, IDE metadata from s…
aksOps May 17, 2026
16f47c7
fix(java-to-typescript): align stderr/stdout writes; correct misleadi…
aksOps May 17, 2026
d41532f
build(java-to-typescript): add @anthropic-ai/sdk dev dep for LLM-judg…
aksOps May 17, 2026
90a0a9a
feat(java-to-typescript): LLM judge for E2/E3 evals (mock + Anthropic…
aksOps May 17, 2026
1ddaa1a
feat(java-to-typescript): wire E2 (plan judge) + E3 (port judge with …
aksOps May 17, 2026
38c4eac
test(java-to-typescript): sample plan.md + reference TS port for E2/E…
aksOps May 17, 2026
b81f5f7
fix(java-to-typescript): default ts-repo path includes /api workspace…
aksOps May 17, 2026
910aa22
docs(java-to-typescript): document E2/E3 commands, mock-vs-real judge…
aksOps May 17, 2026
3d1a143
chore(java-to-typescript): pre-set version to 0.0.0 so first auto-pub…
aksOps May 17, 2026
0ba726b
feat(java-to-typescript): make skill standalone — drop superpowers re…
aksOps May 17, 2026
0041a1f
fix(java-to-typescript): exclude sample ts-port from outer tsconfig +…
aksOps May 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/eval.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: eval

on:
pull_request:
paths:
- 'skills/java-to-typescript/**'
- '.github/workflows/eval.yml'
workflow_dispatch:

jobs:
unit-and-e1:
runs-on: ubuntu-latest
defaults:
run:
working-directory: skills/java-to-typescript
steps:
- uses: actions/checkout@v4

- name: Setup Node 20
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: skills/java-to-typescript/package-lock.json

- name: Install dev deps
run: npm ci

- name: Typecheck
run: npm run typecheck

- name: Unit tests (scripts/lib + scripts)
run: npx vitest run scripts/

- name: E1 (analyze accuracy) eval
run: npx vitest run evals/__tests__/runner.test.ts

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
Comment on lines +12 to +36
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ target/
.idea/
*.iml
.vscode/
.project
.settings/
.classpath
.DS_Store
*.log
/tmp/
Expand All @@ -11,3 +14,9 @@ target/

# skill-creator iteration artifacts (sibling to each skill, named <skill>-workspace/)
*-workspace/

# java-to-typescript skill: TS scripts / evals build artifacts
skills/java-to-typescript/node_modules/
skills/java-to-typescript/dist/
skills/java-to-typescript/.vitest-cache/
skills/java-to-typescript/coverage/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ A monorepo of [Claude Code](https://docs.claude.com/claude-code) / Agent SDK ski
| Skill | Maven coordinates | What it does |
|---|---|---|
| [`gitlab-helper`](skills/gitlab-helper) | `io.github.randomcodespace.ai:gitlab-helper` | GitLab CI/CD, pipelines, runners, and API automation. Version-aware doc grounding + `glab`/`python-gitlab` automation. |
| [`java-to-typescript`](skills/java-to-typescript) | `io.github.randomcodespace.ai:java-to-typescript` | Migrate a Java service (Spring Boot, Quarkus, Micronaut, Spring MVC) to TypeScript with four-phase orchestration, deterministic plumbing scripts, and contract-parity verification. Air-gap-friendly. |

## Using a skill

Expand Down
8 changes: 7 additions & 1 deletion assembly/skill-bundle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@
</includes>
<excludes>
<exclude>pom.xml</exclude>
<exclude>target/**</exclude>
<exclude>**/target/**</exclude>
<exclude>**/__tests__/**</exclude>
<exclude>**/*.test.ts</exclude>
<exclude>**/node_modules/**</exclude>
<exclude>**/.vitest-cache/**</exclude>
<exclude>**/coverage/**</exclude>
<exclude>**/dist/**</exclude>
</excludes>
</fileSet>
</fileSets>
Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

<modules>
<module>skills/gitlab-helper</module>
<module>skills/java-to-typescript</module>
</modules>

<properties>
Expand Down
120 changes: 120 additions & 0 deletions skills/java-to-typescript/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
name: java-to-typescript
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.
---

# java-to-typescript

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.

## When to use

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.

Do not use for: GraphQL Java migrations, Akka/Pekko, Android, JNI, Java agents, or pure-library JARs without an HTTP surface.

## Hard constraints

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.
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.
3. Never skip a phase. Phase 2 is a hard user gate; Phase 4 is the acceptance gate.

## The four phases

### Phase 1 — Analyze (read-only)

Run:

```
tsx scripts/pom-to-workspace.ts analyze --repo <java-repo>
```

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`.

Consult: `references/library-map.yaml`, `references/frameworks/*.md`.

### Phase 2 — Plan (hard user gate, diff preview)

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:

- Migration mode (`full-rewrite | strangler-fig | module-by-module`) — consult `references/migration-modes.md`.
- Target TS framework per Java module (Express default; Koa, Hono, Restify selectable) — consult `references/targets/*.md`.
- Runtime (Node default; Bun, Deno opt-in) — consult `references/runtimes/*.md`.
- Package manager (npm default; pnpm, yarn, bun opt-in).
- DI library (tsyringe default).
- Validation library (zod default).

For each entry in `analysis.json.unmappedDependencies`, ask the user (interactive prompt) for a target. Do not auto-resolve. Do not call `context7`.

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).

**Gate:** wait for explicit user approval of `plan.md` before Phase 3.

Consult: `references/migration-modes.md`, `references/targets/*.md`, `references/runtimes/*.md`, `references/build-layout.md`, `references/type-fidelity.md`.

### Phase 3 — Port

Order:

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.

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`.

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.

4. Port tests alongside each handler. Map JUnit→Vitest, Mockito→`vi.mock`, AssertJ→`expect`, Testcontainers→testcontainers-node. Consult `references/categories/testing.md`.

5. Apply type-fidelity policy per `references/type-fidelity.md`. Deviations require a one-line `port-log.md` entry.

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.)

**Non-negotiable patterns** (from `references/type-fidelity.md` §7.3):
- One handler per file.
- DTO ≡ zod schema. No interfaces on HTTP boundaries without a schema.
- Repository ≡ Drizzle query module. No faked JPA repository interfaces.
- Config ≡ single zod-parsed env object per module. No scattered `process.env.FOO`.
- Logger ≡ module-scoped pino child. No `console.log`.
- Errors ≡ subclasses of a per-module base class, mapped to HTTP status via single middleware.

### Phase 4 — Verify (acceptance gate)

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.

1. Generate `<java-repo>/migration/corpus.jsonl` from Java controller signatures + sample DB state. Ask the user to review or extend before recording.

2. Run:

```
tsx scripts/record-fixtures.ts --java-base http://localhost:<javaPort> --corpus migration/corpus.jsonl --out migration/fixtures.jsonl
```

3. Write `<java-repo>/migration/allowlist.json` declaring paths expected to differ (timestamps, generated IDs, trace headers). Schema:

```json
{ "headers": ["x-request-id", "date", "traceparent"],
"bodyPaths": ["$.createdAt", "$.updatedAt"],
"arrayKeys": { "$.items": "id" } }
```

4. Run:

```
tsx scripts/replay-fixtures.ts --ts-base http://localhost:<tsPort> --fixtures migration/fixtures.jsonl --allowlist migration/allowlist.json --report migration/verify-report.md
```

5. Run the ported Vitest suite from the TS workspace root.

**Acceptance gate (both required):** zero unexpected diffs in `verify-report.md` AND all ported Vitest suites green.

Consult: `references/categories/testing.md`.

## Migration artifact location

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`.

## When stuck

- Library not in registry: ask the user via `AskUserQuestion`. Do not invent a mapping.
- Build-system parse warnings (Gradle Kotlin DSL with dynamic deps): show the warning verbatim to the user; ask them to confirm the dependency list.
- Phase 4 unexpected diffs: report each diff with the Java side and TS side excerpted; return to Phase 3 for the responsible handler.
- Tests fail after porting: do not weaken assertions to make them pass. Diagnose root cause; if the Java behavior cannot be replicated, escalate.
98 changes: 98 additions & 0 deletions skills/java-to-typescript/evals/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Evals

How to run the eval suite for the `java-to-typescript` skill.

## Eval types

| ID | What it measures | Determinism | When it runs |
|---|---|---|---|
| E1 | Analyze accuracy (Phase 1) | deterministic | every PR |
| E2 | Plan reasonability (Phase 2) | LLM-judged | tag releases / on demand |
| E3 | Port quality (Phase 3) | hybrid (hard `tsc`+tests; idioms LLM-judged) | tag releases / on demand |
| E4 | Contract parity (Phase 4) | deterministic | manual smoke; CI optional |

## Quick start

```bash
# All evals against the default fixture (spring-boot-users)
npx tsx evals/runner.ts --eval all

# Specific eval
npx tsx evals/runner.ts --eval E1
npx tsx evals/runner.ts --eval E2 --runs 5
npx tsx evals/runner.ts --eval E3 --runs 5

# Specific fixture
npx tsx evals/runner.ts --fixture spring-boot-users --eval E1

# Override the plan/port being judged (defaults to sample/plan.md and sample/ts-port/api)
npx tsx evals/runner.ts --eval E2 --plan /tmp/my-plan.md
npx tsx evals/runner.ts --eval E3 --ts-repo /tmp/my-ts-port
```

Vitest also exercises the deterministic + mock-mode paths:

```bash
npx vitest run evals/__tests__/runner.test.ts
npx vitest run evals/__tests__/judge.test.ts
```

## Flags

| Flag | Default | Notes |
|---|---|---|
| `--fixture <name>` | `spring-boot-users` | Picks fixture under `evals/fixtures/<name>/` |
| `--eval <id>` | `all` | `E1`, `E2`, `E3`, or `all` (E4 is invoked separately via `replay-fixtures.ts`) |
| `--runs <n>` | `1` for deterministic evals, `5` for LLM-judged | N runs aggregated into `mean ± stddev` |
| `--plan <path>` | `<fixture>/sample/plan.md` | Plan content for E2 to judge |
| `--ts-repo <path>` | `<fixture>/sample/ts-port/api` | TS workspace for E3 to gate + judge |

## Judge modes (E2 + E3)

The judge in `evals/judge.ts` runs in two modes:

| Mode | Trigger | Behavior |
|---|---|---|
| **Mock** (default) | Either `MOCK_JUDGE` is unset/non-`0` OR `ANTHROPIC_API_KEY` is absent | Returns a deterministic score in 7-9 derived from `sha256(content + rubric)`. Free, fast, CI-safe, no network. |
| **Real** | `MOCK_JUDGE=0` AND `ANTHROPIC_API_KEY=sk-...` both set | Calls `claude-haiku-4-5-20251001` with the rubric + content + judge instructions, parses a `{score, rationale}` JSON response. |

```bash
# Run E2 + E3 with the real Anthropic judge
MOCK_JUDGE=0 ANTHROPIC_API_KEY=sk-ant-... \
npx tsx evals/runner.ts --eval all --runs 5
```

The real-mode test in `evals/__tests__/judge.test.ts` is auto-skipped when the env vars aren't set, so CI never accidentally hits the API.

## Variance handling

LLM-judged evals (`E2`, `E3` idiom dimension) run N times (default 5). Output is `mean ± stddev`. Sample-stddev with `n-1` denominator. The rule for accepting a skill change as an improvement: **mean delta must exceed 1 stddev** of the prior baseline.

Mock mode produces `stddev=0` because the hash is deterministic — useful for verifying the harness wiring, not for measuring real model judgment.

## Sample inputs (reference shapes E2/E3 judge against)

Per fixture, `sample/` holds reference inputs:

- `sample/plan.md` — example of what a passing Phase 2 plan looks like
- `sample/ts-port/<module>/` — minimal but tsc-clean + vitest-green reference TS workspace

These are *examples* for E2/E3 in mock mode. In real mode against a real migration, you'd pass `--plan /path/to/your/migration/plan.md` and `--ts-repo /path/to/the/produced/ts-workspace`.

## Adding a new fixture

1. `mkdir -p evals/fixtures/<name>/{java,expected,sample}`.
2. Populate `java/` with a minimal runnable Spring Boot / Quarkus / Micronaut / Spring MVC project.
3. Run analyze: `npx tsx scripts/pom-to-workspace.ts analyze --repo evals/fixtures/<name>/java`.
4. Copy `evals/fixtures/<name>/java/migration/analysis.json` to `evals/fixtures/<name>/expected/analysis.json`.
5. Write `expected/corpus.jsonl`, `expected/allowlist.json`, `expected/ts-shape.md`, `expected/plan-rubric.md`.
6. Hand-author `sample/plan.md` (covering every rubric item) + `sample/ts-port/<module>/` (a minimal TS port that compiles + has at least one passing test).
7. Re-run `evals/__tests__/runner.test.ts` to verify E1 + E2 + E3 against the new fixture.

## CI integration

`.github/workflows/eval.yml` runs typecheck + unit tests + E1 on every PR touching `skills/java-to-typescript/**`. E2/E3 in mock mode are exercised by the unit test suite (`runner.test.ts` + `judge.test.ts`). Real-mode E2/E3 against actual Claude API is opt-in only — run manually on tag releases with the env vars set, or wire a separate workflow keyed to `ANTHROPIC_API_KEY` secret if you want it on every release.

## Air-gap note

Evals never hit the public internet in mock mode. In real mode, only the Anthropic API is contacted (egress to `api.anthropic.com` over HTTPS). Java fixtures must compile against locally vendored Maven repositories (Artifactory / Nexus / proxied central). The eval runner itself only parses XML and connects to `localhost` for E4.
Loading
Loading