跳到正文
GitHub·· 6 天前AI 评分62

codeclue 发布:用 ts-morph 剥离函数体,为编码智能体压缩仓库上下文

rhrdianbaihaqi/codeclue

AI 导读

codeclue 是一个无需安装配置的 CLI,用 ts-morph 解析 TS/JS 文件并抹掉函数体、保留签名与类型,让编码智能体读取代码骨架而非完整实现。

正文

Point an AI coding agent at an unfamiliar repo and watch it burn context: it opens whole files to answer one question. But it rarely needs the implementation — it needs to know what exists and what shape it has.

codeclue deletes the bodies and keeps the shape.

npx github:rhrdianbaihaqi/codeclue outline src/context.ts
~5673 tokens -> ~3044 tokens (46% saved)

No install, no config, no API key. It reads one file and prints its skeleton.

See it

// before — the implementation you mostly don't need
/**
 * Fetch a user from the API.
 * Retries twice on network failure, then throws.
 */
export async function fetchUser(id: string): Promise<User> {
  const res = await fetch(`/users/${id}`);
  if (!res.ok) throw new HttpError(res.status);
  return (await res.json()) as User;
}

export class Repo<T extends { id: string }> {
  private readonly items = new Map<string, T>();
  constructor(private readonly label: string) {
    this.items.clear();
  }
  get size(): number {
    return this.items.size;
  }
}
// after — everything an agent needs to call this code correctly
/** Fetch a user from the API. */
export async function fetchUser(id: string): Promise<User> { /* ... */ }

export class Repo<T extends { id: string }> {
  private readonly items = new Map<string, T>();
  constructor(private readonly label: string) { /* ... */ }
  get size(): number { /* ... */ }
}

Generics survive. Overloads survive. Your formatting survives. Nothing is re-typed by hand — the file is parsed with ts-morph, bodies are blanked in memory, and the original text is printed back.

The receipts

Run across every non-test .ts/.tsx file in hono and h3:

Files processed 274
Failures 0
Tokens 316,933 → 138,779
Saved overall 56%
Median file 58%

A sample, largest first:

File Before After Saved
h3/src/utils/proxy.ts 6,826 825 88%
h3/src/utils/request.ts 5,628 1,378 76%
hono/src/jsx/dom/render.ts 6,637 1,894 71%
hono/src/hono-base.ts 4,079 1,312 68%
hono/src/context.ts 5,673 3,044 46%
hono/src/types.ts 22,457 22,422 0%

That last row is not a bug — types.ts is pure type declarations. There are no bodies to remove, so nothing is removed. When in doubt, codeclue keeps it. Losing something you needed is far worse than leaving a little noise.

Install

npx github:rhrdianbaihaqi/codeclue outline src/index.ts   # no install, builds from GitHub
npm install -g github:rhrdianbaihaqi/codeclue             # or keep it around

Requires Node 18+. The package is codeclue; the command is clue. The first run clones and builds the repo (about 10-20 seconds); later runs are cached. The npm package codeclue currently only contains the first alpha (version command), so install from GitHub until a newer release is published to npm.

Use it as a Claude Code skill

The repo ships a skill that makes the agent run clue trace before it opens any file:

/plugin marketplace add rhrdianbaihaqi/codeclue
/plugin install codeclue@codeclue

Built for agents, not just humans

Every command speaks JSON:

clue outline src/index.ts --json
{
  "ok": true,
  "command": "outline",
  "version": "0.1.0-alpha.1",
  "contract": 1,
  "data": {
    "file": "src/index.ts",
    "outline": "export function hi(name: string): string { /* ... */ }\n",
    "originalTokens": 569,
    "outlineTokens": 371,
    "savedPercent": 35
  },
  "error": null
}

contract is a version handshake for the shape of data. Bump-on-break means a consumer built against contract 1 fails loudly instead of silently misreading a future payload. Check it before you trust data.

Errors use the same envelope — ok: false, data: null, and a machine code:

Code Meaning
EARGS Required argument missing
ENOENT File or directory does not exist
EISDIR Path is a directory, not a file
ENOTDIR survey was given a file, not a directory
EUNSUPPORTED Not a TS/JS source file
UNKNOWN_COMMAND No such command
NO_COMMAND --json with no command
INTERNAL_ERROR Anything unexpected

Exit 0 on success, 1 on failure.

Results to stdout, noise to stderr

clue outline src/index.ts | pbcopy   # copies the outline. Only the outline.

The ~5673 tokens -> ~3044 tokens line, warnings, and progress all go to stderr. Your pipe stays clean.

It never touches your files

codeclue only reads. It never calls .save(), never writes, never drops a file next to yours. The test suite hashes every fixture before and after each run and fails if one byte changes — plus a static check that no write path exists anywhere in src/.

Commands

Alpha. All four MVP commands work. The JSON shape may still shift before 0.1.0 — that is what the contract field is for.

Command What it does
clue outline <file> One file's skeleton: signatures, types, exports
clue graph <file> --depth N Walk the import graph, never entering node_modules
clue trace <entry> --depth 2 Layered map: entry in full, depth 1 as skeletons, depth 2 as export lists
clue survey [dir] Token profile of a repo: heaviest files, suggested ignore list
clue version Version + contract

trace is the one this is all building toward: one command that hands an agent a whole subsystem at the right level of detail.

$ clue trace src/index.ts --depth 2

### src/index.ts [entry · full]
export * from "./a";
export * from "./b";

### src/a.ts [outline]
import { b } from "./b";
export function a(user: User, next: typeof b = b): string { /* ... */ }

### src/types.ts [exports]
interface: User
type: Id

### cycles
src/a.ts <-> src/b.ts

survey tells you what is eating your context before you start, and respects .gitignore (it uses git ls-files when it can, and its own matcher otherwise):

$ clue survey .

.: 4 files, ~1636 tokens

Top 4 files by tokens
tokens    %  path
  ~883  54%  package-lock.json
  ~679  42%  src/big.ts
   ~68   4%  __snapshots__/a.test.ts.snap
    ~6   0%  src/small.ts

Suggested ignores (would drop ~951 tokens, 58%)
pattern            files  tokens    %  reason
package-lock.json      1    ~883  54%  lockfile
__snapshots__/         1     ~68   4%  test snapshots

tsconfig, handled

codeclue walks up from your file to find tsconfig.json, uses your compilerOptions and path aliases — but does not load its file list, so outlining one file in a 5,000-file monorepo stays instant. No tsconfig? It falls back to defaults, warns on stderr, and keeps going.

Token counts are estimates

characters ÷ 4, always printed with a ~. Tokenizers differ between models — read these as a ratio, not a bill.

Why it's small

One runtime dependency: ts-morph. No arg parser, no colour library, no framework. argv is parsed by hand, in about 50 lines.

The idea

The CLI does what is deterministic. The model does what needs reasoning.

Parsing an AST, walking an import graph, counting tokens — these always produce the same answer. They have no business consuming a context window. Every mechanical step moved out of the model is a token spent on actual thinking.

License

MIT © 2026 rahardianz

Contributing: see CONTRIBUTING.md. Security: SECURITY.md.

来源:GitHub · github.com