Skip to content

Commit 3e443ab

Browse files
DeepCodeWorkclaude
andcommitted
chore(cli): repackage as single publishable ui-lineage bundle
Turn the CLI package into one self-contained npm package named `ui-lineage` (the @CodeRadar scope is owned by another account and unusable). The internal @coderadar/core and @coderadar/parser-react workspace packages are bundled into the output via tsup, so consumers depend only on `ui-lineage` plus its three external deps (ts-morph, yaml, commander). - packages/cli → name "ui-lineage"; bin `ui-lineage`; library entry src/lib.ts re-exports the core query API + the React scanner (main/exports/types → lib). - tsup config: bundle @coderadar/* into JS *and* d.ts (dts.resolve), keep the heavy deps external; preserves the CLI shebang; emits index (bin) + lib. - Mark @coderadar/core and @coderadar/parser-react private (their code ships inside ui-lineage; also prevents accidental publish to the taken scope). - Rebrand CLI program name / help / default graph filename to ui-lineage. - Add package README (npm listing) and prepublishOnly; ignore *.graph.json. Verified: pnpm -r build/typecheck/test + eval all green (185 checks, precision/ recall 1.000). npm pack → npm install of the tarball into a clean project works: the `ui-lineage` bin runs and `import { scanReact, journeys } from "ui-lineage"` resolves with no @CodeRadar imports in the bundle. Not published — packaging only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9b0d9e6 commit 3e443ab

9 files changed

Lines changed: 708 additions & 22 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,6 @@ vite.config.ts.timestamp-*
144144

145145
# Eval outputs (scorecard is regenerated every run; history is recorded deliberately)
146146
eval/scorecard.json
147+
148+
# ui-lineage scanned graphs
149+
*.graph.json

packages/cli/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# ui-lineage
2+
3+
**Map UI components to their data sources and user journeys** — trace any screenshot or ticket back to the code, APIs, state, and navigation behind it. Deterministic static analysis for React/TSX. No LLM, no network calls.
4+
5+
`ui-lineage` scans a React codebase into a **lineage graph** and lets you query it three ways:
6+
7+
- **match** — text seen on screen → the component(s) that render it
8+
- **trace** — a component → every API, state slice, and event that feeds it (attributed *per instance*, so a shared `<DataTable>` on the Users page reports `/api/users` while the same component on Invoices reports `/api/invoices`)
9+
- **journeys** — a page → the user-action paths leading out of it (click → navigate → click…), lazily expanded and cycle-safe
10+
11+
## Install
12+
13+
```bash
14+
npm install -g ui-lineage # CLI
15+
npm install ui-lineage # library
16+
```
17+
18+
Requires Node ≥ 20.
19+
20+
## CLI
21+
22+
```bash
23+
# 1. Scan a React app into a graph
24+
ui-lineage scan ./src -o app.graph.json
25+
26+
# 2. Find a component from on-screen text
27+
ui-lineage find "All invoices" -g app.graph.json
28+
29+
# 3. Trace a component (or an instance id) to its data
30+
ui-lineage trace InvoicesPage -g app.graph.json
31+
32+
# 4. Walk the user journeys from a page or route
33+
ui-lineage journeys /users -g app.graph.json
34+
```
35+
36+
`journeys` output reads left-to-right, with `↩ cycle` where a list ⇄ detail loop closes:
37+
38+
```
39+
▸ /users • onClick() → /users/:userId ▸ /users/:userId • onClick() → /users ▸ /users ↩ cycle
40+
▸ /users • onClick() ⇢ fetch /api/users
41+
```
42+
43+
## Library
44+
45+
```ts
46+
import { scanReact, resolveHookEdges, journeys, traceLineage, matchComponentsByText } from "ui-lineage";
47+
48+
const graph = resolveHookEdges(scanReact({ root: "./src" }));
49+
50+
const match = matchComponentsByText(graph, ["All invoices"]);
51+
const lineage = traceLineage(graph, match.candidates[0].value.component.id);
52+
const paths = journeys(graph, "/users", { depth: 3 });
53+
```
54+
55+
Every query returns a `QueryResult` envelope — ranked `candidates` with evidence and confidence, or an honest `ambiguous` / `declined`.
56+
57+
## What it understands
58+
59+
Endpoints (constants, templates, API wrappers, react-query/SWR), i18n text, cross-file instance trees and per-instance prop-flow, Redux/Zustand stores, portals/modals/toasts, React Router & Next.js routes, and action effects (navigate / fetch / dispatch / setState) mined from event handlers.
60+
61+
## Status
62+
63+
Early (v0.1). The matching engine, screenshot adapter, and MCP server are on the roadmap. Output is deterministic and language-agnostic (plain JSON graph), designed to feed AI agents as a context provider — not to be one.
64+
65+
## License
66+
67+
MIT

packages/cli/package.json

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,54 @@
11
{
2-
"name": "@coderadar/cli",
2+
"name": "ui-lineage",
33
"version": "0.1.0",
4-
"description": "CodeRadar CLI — scan a React codebase into a lineage graph and query it.",
4+
"description": "Map UI components to their data sources and user journeys — trace any screenshot back to the code, APIs, state, and navigation behind it. Deterministic static analysis for React/TSX.",
55
"license": "MIT",
66
"type": "module",
77
"bin": {
8-
"coderadar": "dist/index.js"
8+
"ui-lineage": "dist/index.js"
9+
},
10+
"main": "dist/lib.js",
11+
"module": "dist/lib.js",
12+
"types": "dist/lib.d.ts",
13+
"exports": {
14+
".": {
15+
"types": "./dist/lib.d.ts",
16+
"default": "./dist/lib.js"
17+
}
918
},
1019
"files": [
11-
"dist"
20+
"dist",
21+
"README.md"
22+
],
23+
"keywords": [
24+
"react",
25+
"static-analysis",
26+
"data-lineage",
27+
"data-flow",
28+
"ast",
29+
"ts-morph",
30+
"user-journeys",
31+
"component-graph",
32+
"cli"
1233
],
34+
"engines": {
35+
"node": ">=20"
36+
},
1337
"scripts": {
14-
"build": "tsc -p tsconfig.json",
15-
"typecheck": "tsc -p tsconfig.json --noEmit"
38+
"build": "tsup",
39+
"typecheck": "tsc -p tsconfig.json --noEmit",
40+
"prepublishOnly": "pnpm build"
1641
},
1742
"dependencies": {
18-
"@coderadar/core": "workspace:*",
19-
"@coderadar/parser-react": "workspace:*",
20-
"commander": "^13.0.0"
43+
"commander": "^13.0.0",
44+
"ts-morph": "^24.0.0",
45+
"yaml": "^2.9.0"
2146
},
2247
"devDependencies": {
48+
"@coderadar/core": "workspace:*",
49+
"@coderadar/parser-react": "workspace:*",
2350
"@types/node": "^22.20.1",
51+
"tsup": "^8.5.1",
2452
"typescript": "^5.7.0"
2553
}
2654
}

packages/cli/src/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@ import { Command } from "commander";
2020
const program = new Command();
2121

2222
program
23-
.name("coderadar")
23+
.name("ui-lineage")
2424
.description(
25-
"Map UI components to their data sources — trace any screenshot back to the code and data behind it.",
25+
"Map UI components to their data sources and user journeys — trace any screenshot back to the code, APIs, state, and navigation behind it.",
2626
)
2727
.version("0.1.0");
2828

2929
program
3030
.command("scan")
3131
.description("Scan a React codebase and emit a lineage graph JSON")
3232
.argument("<dir>", "directory to scan")
33-
.option("-o, --out <file>", "output file", "coderadar.graph.json")
33+
.option("-o, --out <file>", "output file", "ui-lineage.graph.json")
3434
.action((dir: string, opts: { out: string }) => {
3535
const meta = collectGraphMeta(path.resolve(dir));
3636
const graph = { ...resolveHookEdges(scanReact({ root: dir })), meta };
@@ -56,7 +56,7 @@ program
5656
.command("find")
5757
.description("Find components by text visible on screen (e.g. read off a screenshot)")
5858
.argument("<terms...>", "text fragments seen in the UI")
59-
.option("-g, --graph <file>", "graph file", "coderadar.graph.json")
59+
.option("-g, --graph <file>", "graph file", "ui-lineage.graph.json")
6060
.action((terms: string[], opts: { graph: string }) => {
6161
const graph = loadGraph(opts.graph);
6262
const result = matchComponentsByText(graph, terms);
@@ -76,7 +76,7 @@ program
7676
.command("trace")
7777
.description("Trace a component to every data source, state, and event that feeds it")
7878
.argument("<component>", "component name, definition id, or instance id")
79-
.option("-g, --graph <file>", "graph file", "coderadar.graph.json")
79+
.option("-g, --graph <file>", "graph file", "ui-lineage.graph.json")
8080
.action((component: string, opts: { graph: string }) => {
8181
const graph = loadGraph(opts.graph);
8282
const node =
@@ -127,7 +127,7 @@ program
127127
.command("journeys")
128128
.description("Trace user-journey paths from a page or component (click → navigate → click…)")
129129
.argument("<start>", "route path (/users/:id), component name, or instance id")
130-
.option("-g, --graph <file>", "graph file", "coderadar.graph.json")
130+
.option("-g, --graph <file>", "graph file", "ui-lineage.graph.json")
131131
.option("-d, --depth <n>", "max navigation levels per path", "3")
132132
.action((start: string, opts: { graph: string; depth: string }) => {
133133
const graph = loadGraph(opts.graph);
@@ -181,7 +181,7 @@ function printMatchCandidate(candidate: Candidate<ComponentMatch>): void {
181181

182182
function loadGraph(file: string): LineageGraph {
183183
if (!fs.existsSync(file)) {
184-
console.error(`Graph file not found: ${file} — run \`coderadar scan <dir>\` first.`);
184+
console.error(`Graph file not found: ${file} — run \`ui-lineage scan <dir>\` first.`);
185185
process.exit(1);
186186
}
187187
try {

packages/cli/src/lib.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* ui-lineage — public library API.
3+
*
4+
* One import gives you the whole toolkit: the React/TSX scanner plus the graph
5+
* query layer (match, per-instance lineage, journeys). The internal monorepo
6+
* packages are bundled in at build time, so consumers depend only on `ui-lineage`.
7+
*
8+
* import { scanReact, resolveHookEdges, journeys, traceLineage } from "ui-lineage";
9+
*/
10+
export * from "@coderadar/core";
11+
export { resolveHookEdges, scanReact, type ScanOptions } from "@coderadar/parser-react";

packages/cli/tsup.config.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { defineConfig } from "tsup";
2+
3+
// ui-lineage ships as a single self-contained package: the internal @coderadar/*
4+
// workspace packages are bundled into the output, while the heavy third-party
5+
// deps (ts-morph, yaml, commander) stay external and install normally.
6+
export default defineConfig({
7+
entry: {
8+
index: "src/index.ts", // CLI bin (keeps its #!/usr/bin/env node shebang)
9+
lib: "src/lib.ts", // library entry
10+
},
11+
format: ["esm"],
12+
target: "node20",
13+
// Inline the workspace packages' TYPES too, so the published .d.ts has no
14+
// dangling references to the unpublished @coderadar/* internals.
15+
dts: { resolve: true },
16+
clean: true,
17+
sourcemap: true,
18+
noExternal: [/^@coderadar\//],
19+
external: ["ts-morph", "yaml", "commander"],
20+
});

packages/core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"name": "@coderadar/core",
33
"version": "0.1.0",
4-
"description": "CodeRadar lineage graph schema — the language-agnostic contract every parser emits and every agent consumes.",
4+
"private": true,
5+
"description": "CodeRadar lineage graph schema — the language-agnostic contract every parser emits and every agent consumes. Bundled into the published `ui-lineage` package.",
56
"license": "MIT",
67
"type": "module",
78
"main": "dist/index.js",

packages/parser-react/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"name": "@coderadar/parser-react",
33
"version": "0.1.0",
4-
"description": "React/TSX parser for CodeRadar — extracts components, hooks, data sources, state, and events into a lineage graph.",
4+
"private": true,
5+
"description": "React/TSX parser for CodeRadar — extracts components, hooks, data sources, state, and events into a lineage graph. Bundled into the published `ui-lineage` package.",
56
"license": "MIT",
67
"type": "module",
78
"main": "dist/index.js",

0 commit comments

Comments
 (0)