Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

add-lang — Claude Code skill: what it does, how to install

Adds a new language to codegraph through tree-sitter, tests the extractor, and measures extraction and retrieval on real repositories.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add colbymchenry/codegraph --skill add-lang -a claude-code
Open on GitHub

What add-lang does

This skill wires a new language into the codegraph extraction pipeline. It locates a tree-sitter grammar, health-checks it, inspects the AST, and updates the language and file-extension mappings. It then creates or updates the extractor and verifies that files of the new type are indexed.

The workflow builds the local project, runs extraction checks, adds focused tests, and selects three popular repositories for benchmarking. Each repository gets extraction checks plus an A/B comparison of agent results with codegraph and without it. The skill also updates the README and CHANGELOG.

Run it from the codegraph repository root. It requires node, git, gh, and a logged-in claude CLI because the benchmark starts real claude -p runs. That is a prerequisite worth checking first. The workflow leaves changes for review and does not commit, push, publish, or tag them.

What it helps with

  • add a language to codegraph
  • health-check a tree-sitter grammar
  • map AST nodes to code entities
  • write tests for a new extractor
  • benchmark extraction across three repositories

Example prompts for the agent

  • Run `/add-lang lua` and add Lua support to `codegraph`.
  • Check whether `elixir` can be added to `codegraph`, including tests and benchmarking.
  • Wire `zig` into extraction, verify its grammar, and update the docs without committing changes.

How to install add-lang

1Skills CLI (Vercel)

npx skills add colbymchenry/codegraph --skill add-lang -a claude-code

2Manually: put the files in place

~/.claude/skills/add-lang/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/add-lang/

From the same set2

The whole set →

Similar

modern-javascript-patterns Seth Hobson This skill guides JavaScript refactors and async work with ES6+ syntax and functional patterns. It includes practical recommendations for code structure. Claude Code CX Codex Cursor 21,553installs rust-async-patterns Seth Hobson A skill for building and debugging async Rust with Tokio, tasks, channels, streams, and error handling. Use it for concurrent services and async I/O. Claude Code CX Codex Cursor 20,625installs javascript-testing-patterns Seth Hobson This skill provides JavaScript and TypeScript testing examples, from unit tests to end-to-end flows. Use it when setting up test infrastructure or working with TDD. Claude Code CX Codex Cursor 20,472installs tdd-rust rtk-ai Skill for RTK filter development using TDD. Write a test with real data, implement minimum code, verify token savings, lock the output format with insta snapshots. Claude Code CX Codex Cursor +1 1,400installs flutter-implement-json-serialization flutter Teaches coding agents to build Dart models with fromJson and toJson methods using dart:convert. Useful for manual JSON mapping in simple Flutter data structures. Claude Code CX Codex Cursor +1 37,200installs mentoring-juniors official github This skill mentors junior developers through questions instead of ready-made answers. Use it for learning code, debugging, and getting started with Copilot. GitHub Copilot 6,900installs migrate-to-shoehorn Matt Pocock Replaces as type assertions in tests with helpers from @total-typescript/shoehorn. Use it when tests need partial objects or deliberately invalid data. Claude Code 475,388installs write-swift emilkowalski This skill guides Swift coding, reviews, and migrations using the language’s current practices. Use it to investigate concurrency errors, hangs, data races, or retain cycles. Claude Code CX Codex Cursor +1 117,500installs

Questions about add-lang

How do I install add-lang in Codex?

Run npx skills add colbymchenry/codegraph --skill add-lang -a codex. The skill file will be available at .agents/skills/add-lang/SKILL.md.

What does add-lang do?

It takes a new language through the codegraph pipeline: grammar validation, extractor wiring, tests, and benchmarking across three repositories.

What does add-lang require?

Run it from the codegraph repository root with node, git, gh, and a logged-in claude CLI. The benchmark starts real claude -p runs.

Source text

SKILL.md · MIT
---
name: add-lang
description: Add tree-sitter language support to codegraph end-to-end — wire the grammar + extractor, write tests, then benchmark extraction quality and retrieval value on 3 popular real-world repos. Use when the user runs /add-lang <language> or asks to add/support a new language (e.g. Lua, Elixir, Zig, OCaml) in codegraph.
---

# Add a language to CodeGraph

Wire a new tree-sitter language into codegraph's extraction pipeline, prove it
extracts real symbols on popular repos, and prove it beats no-codegraph for an
agent. Runs **fully autonomously** — pick repos, benchmark, update docs, then
report. **Never commit, push, publish, or tag** (house rule); leave all changes
for the user to review.

The argument is the language token used throughout the `Language` union, e.g.
`lua`, `elixir`, `zig`. If none was given, ask which language. Use the lowercase
single-token form everywhere (`csharp`, not `c#`).

## Prerequisites
- Run from the codegraph repo root. `node`, `git`, `gh`, and a logged-in
  `claude` CLI (the benchmark spawns real `claude -p` runs).
- The benchmark uses the local dev build — Step 8 builds + links it on PATH.

## Workflow

Copy this checklist and work through it in order:
```
- [ ] 1. Resolve language; bail early if already supported (just benchmark)
- [ ] 2. Find a grammar + health-check it (ABI / heap corruption)
- [ ] 3. Discover the grammar's AST node types (dump-ast.mjs)
- [ ] 4. Wire the language (4 files; sometimes a 5th core touch)
- [ ] 5. Build + verify-extraction loop until PASS
- [ ] 6. Add extraction tests; make them green
- [ ] 7. Auto-pick 3 popular repos by size tier; add to corpus.json
- [ ] 8. Benchmark all 3: extraction + with/without A/B
- [ ] 9. Update README + CHANGELOG
- [ ] 10. Report; do NOT commit
```

### Step 1 — Resolve + short-circuit

Check whether the language is already wired: look for the token in the
`LANGUAGES` const (`src/types.ts`) and the `EXTRACTORS` map
(`src/extraction/languages/index.ts`). If it is already supported (e.g.
`typescript`, `rust`), **skip Steps 2–6** and go straight to benchmarking
(Steps 7–8) to validate/measure it — note in the report that no code changed.

### Step 2 — Find a grammar, then health-check it

```bash
ls node_modules/tree-sitter-wasms/out/ | grep -i <lang>   # csharp -> c_sharp
```
- **Present** → likely off-the-shelf; `grammars.ts` resolves it from
  `tree-sitter-wasms` automatically. (Many languages: elixir, zig, ocaml,
  solidity, toml, yaml, …)
- **Absent** → vendor a `.wasm` into `src/extraction/wasm/` (like `pascal` /
  `scala` / `lua`) and add the token to the vendored branch in Step 4.

**Always health-check before writing an extractor — a *present* grammar can
still be unusable:**
```bash
node scripts/add-lang/check-grammar.mjs <lang> path/to/valid-sample.<ext>
```
It prints the grammar's ABI version and runs it the way indexing does: in a
child process with indexing's V8 flags, beside the vendored grammars, parsing a
valid sample many times and once at the 1 MiB file-size limit. If it **FAILs**
(ERROR trees, or later parses giving a different tree, on valid code — an old ABI
corrupting the shared WASM heap, which silently drops nested calls/imports on
every file after the first; e.g. the tree-sitter-wasms **Lua** grammar is ABI 13
and fails — or the process running it crashing or hanging, even after every
parse finished), do NOT use that wasm. **Vendor a newer (ABI 14/15) build instead:**
```bash
npm pack @tree-sitter-grammars/tree-sitter-<lang>   # often ships a prebuilt *.wasm
# or build one: npx tree-sitter build --wasm   (needs Docker/emscripten)
cp <the>.wasm src/extraction/wasm/tree-sitter-<lang>.wasm
```
then add the token to the vendored branch in Step 4 and re-run check-grammar on
the vendored path until it PASSes. **If you cannot obtain a healthy wasm, STOP
and tell the user.**

### Step 3 — Discover AST node types

Get a representative source file (write a small sample covering functions,
classes/structs, imports, enums; or `curl` a raw file from a known repo), then:
```bash
node scripts/add-lang/dump-ast.mjs <lang> path/to/sample.<ext>
# vendored grammar: pass the wasm path instead of the token
node scripts/add-lang/dump-ast.mjs src/extraction/wasm/tree-sitter-<lang>.wasm sample.<ext>
```
The frequency table + field names (`name:`, `parameters:`, `body:`,
`return_type:`) tell you what to map. Open the existing extractor closest to the
language's paradigm as a model: `rust.ts`/`scala.ts` (functional, traits),
`java.ts`/`csharp.ts` (OO), `python.ts`/`ruby.ts` (scripting), `go.ts`
(top-level methods + receivers).

### Step 4 — Wire the language (4 files)

These are exact, fragile wiring — match the existing style precisely:

1. **`src/types.ts`** — TWO edits:
   - add `'<lang>',` to the `LANGUAGES` const (before `'unknown'`);
   - add `'**/*.<ext>',` to `DEFAULT_CONFIG.include`. **Don't skip this** — it's
     the file-scan allowlist; without the glob, `codegraph init` finds **0
     files** even though detection/extraction are wired.
2. **`src/extraction/grammars.ts`** — three maps:
   - `WASM_GRAMMAR_FILES`: `<lang>: 'tree-sitter-<lang>.wasm',`
   - `EXTENSION_MAP`: each file extension → `'<lang>'` (e.g. `'.lua': 'lua',`)
   - `getLanguageDisplayName`: `<lang>: '<Display Name>',`
   - **vendored only**: add `<lang>` to the
     `(lang === 'pascal' || lang === 'scala' || …)` wasm-path branch.
3. **`src/extraction/languages/<lang>.ts`** — new file exporting
   `export const <lang>Extractor: LanguageExtractor = { … }`. Map the node types
   from Step 3. Required fields: `functionTypes`, `classTypes`, `methodTypes`,
   `interfaceTypes`, `structTypes`, `enumTypes`, `typeAliasTypes`,
   `importTypes`, `callTypes`, `variableTypes`, `nameField`, `bodyField`,
   `paramsField`. Add hooks as the grammar needs them (`getSignature`,
   `getVisibility`, `isExported`, `extractImport`, `visitNode`, `getReceiverType`,
   `interfaceKind`, `enumMemberTypes`, etc. — see
   `src/extraction/tree-sitter-types.ts`).
4. **`src/extraction/languages/index.ts`** — `import { <lang>Extractor } from
   './<lang>';` and add `<lang>: <lang>Extractor,` to `EXTRACTORS`.

**Sometimes a 5th, core touch in `src/extraction/tree-sitter.ts`** — variable
extraction has per-language branches in `extractVariable` (the generic fallback
only finds direct `identifier`/`variable_declarator` children). If the grammar
nests declared names (e.g. Lua's `variable_declaration → variable_list`), add a
`} else if (this.language === '<lang>')` branch there, mirroring the existing
ts/python/go ones. Import forms that aren't a distinct node (Lua/Ruby `require`
is a *call*) are handled in the extractor's `visitNode` hook instead.

### Step 5 — Build + verify loop

```bash
npm run build            # tsc + copy-assets (copies any vendored *.wasm into dist/)
```
Index a small sample repo and check extraction:
```bash
( cd <sample-repo> && codegraph init -i )
node scripts/add-lang/verify-extraction.mjs <sample-repo> <lang>
```
`verify-extraction.mjs` fails (exit 1) if the language isn't detected or only
`file`/`import` nodes were produced — the classic symptom of wrong node-type
names. On FAIL or a thin WARN: re-run `dump-ast.mjs` on a richer file, fix the
mappings in `<lang>.ts`, `npm run build`, re-index, re-verify. **Repeat until
PASS.**

### Step 6 — Tests

Add to `__tests__/extraction.test.ts`, modeled on the `Rust Extraction` block:
- a `detectLanguage` assertion in `describe('Language Detection')`
- a `describe('<Lang> Extraction')` block asserting functions/classes/imports
  are extracted from an inline source string.
```bash
npx vitest run __tests__/extraction.test.ts
```
Green before continuing.

### Step 7 — Auto-pick 3 repos + corpus

Pick **without asking**. Find candidates, then curate 3 that are genuinely
`<lang>`-dominant, one per size tier:
```bash
gh search repos --language=<lang> --sort=stars --limit 40 \
  --json fullName,stargazerCount,description
```
Tiers (match `corpus.json`): **Small** <~150 files · **Medium** ~150–1500 ·
**Large** >~1500. Skip repos that are tagged `<lang>` but mostly another
language. Write one cross-file architecture **question** per repo (the kind that
needs tracing across files). Add a `"<Language>"` block to
`.claude/skills/agent-eval/corpus.json` (fields: `name`, `repo`, `size`,
`files`, `question`) so `/agent-eval` can reuse them.

### Step 8 — Benchmark all 3 (extraction + A/B)

Make the dev build the codegraph on PATH **once**, then loop:
```bash
npm run build && ./scripts/local-install.sh
scripts/add-lang/bench.sh <lang> <name> <url> "<question>" headless   # ×3
```
`bench.sh` clones (shared `/tmp/codegraph-corpus`), wipes + indexes, runs
`verify-extraction.mjs`, then the with/without retrieval A/B via
`scripts/agent-eval/run-all.sh` (skips the paid A/B if extraction is broken).
Read each `parse-run.mjs` summary printed by `run-all.sh`: tool calls, file
`Read`s, Grep/Bash, codegraph-tool calls, duration, and **cost** — for both the
`with` and `without` arms. After the loop, restore the dev link if needed:
`./scripts/local-install.sh`.

### Step 9 — Docs + CHANGELOG

- **README.md**: add `<Lang>` to the "19+ Languages" feature bullet, and add a
  row to the **Supported Languages** table:
  `| <Lang> | \`.ext\` | Full support (classes, methods, …) |`.
- **CHANGELOG.md**: add an `## [Unreleased]` section at the top (above the
  latest version) with `### Added` → a user-perspective bullet, e.g.
  *"CodeGraph now indexes **<Lang>** (`.ext`) — functions, classes, imports, and
  call edges."* If `## [Unreleased]` already exists, append under it. (It's
  folded into the next versioned block at release time.)

### Step 10 — Report (do NOT commit)

Summarize for review:
- **Files changed**: the 4 wiring edits + new extractor + tests + README +
  CHANGELOG + corpus.json (+ any vendored `.wasm`).
- **Extraction** per repo: files / nodes / edges / `verify-extraction` result.
- **A/B** per repo: `with` vs `without` (tool calls, file Reads, cost) and a
  one-line verdict — did codegraph reduce effort, and did both arms reach a
  correct answer?
- **Gaps / follow-ups** (node types not yet mapped, resolution edges missing,
  framework routes, etc.).

Hand the changes to the user. **Do not** run `git commit`/`push` or publish —
releases go through the GitHub Actions Release workflow.

## Notes
- The A/B spawns real **paid** `claude -p` runs (opus, `--max-budget-usd`),
  2 arms × 3 repos. The corpus dir `/tmp/codegraph-corpus` is shared with
  `/agent-eval`, so clones are reused across runs.
- Any new `*.wasm` must live in `src/extraction/wasm/` — `copy-assets` (run by
  `npm run build`) ships it; otherwise it won't be in `dist/`.
- An index must be served by the **same** binary that built it. Step 8 builds +
  links the dev build first, so this holds.
- If a grammar can't be obtained, or extraction can't reach PASS, **STOP and
  report** — don't ship a half-wired language.

© colbymchenry · MIT · colbymchenry/codegraph

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor