archlet
Interactive, provenance-backed architecture maps in .archlet/. Two modes: MAP — analyze the whole codebase into .archlet/data.js and seed a starter overlay from a recent change; DIFF — given a PR, commit, range, branch, or working-tree changes, overlay what they touch. Use when the user asks to map / diagram / visualize a codebase's architecture, refresh the map, or show what a PR / commit / change touches.
소스 정보
- 저장소
- superdesigndev/archlet
- 최근 소스 활동
- 2026년 6월 5일 10:45
- 감지된 SKILL.md 언어
- 영어
- 스타
- 53
- 포크
- 8
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
SKILL.md 표시 중
SKILL.md
소스 지침 · 읽기 전용 미리보기- name
- archlet
- description
- Interactive, provenance-backed architecture maps in .archlet/. Two modes: MAP — analyze the whole codebase into .archlet/data.js and seed a starter overlay from a recent change; DIFF — given a PR, commit, range, branch, or working-tree changes, overlay what they touch. Use when the user asks to map / diagram / visualize a codebase's architecture, refresh the map, or show what a PR / commit / change touches.
# archlet
You produce files under `.archlet/` that the viewer renders. Pick the mode from the request:
- **MAP** — "map / visualize the architecture", "refresh the map", no specific change given.
- **DIFF** — a change is referenced (PR, commit, range `A..B`, branch, or "my changes / staged").
Needs an existing `.archlet/data.js` — run MAP first if absent.
Nodes form a **tree of any depth** via `parent`: top-level nodes (source roots / apps) contain **modules**,
which can nest further when a module is big enough to warrant it. Keep it shallow — add a level only when it
earns its keep; the viewer expands/collapses at every level. Always finish by launching the viewer (see end).
---
## Mode 1 — MAP → `.archlet/data.js`
The single source of truth the viewer reads:
```js
/* GENERATED by the archlet skill. */
window.ARCH = {
config: { project, layers: { <key>: { label, border, ink, fill? }, ... } }, // project = repo name (viewer title); layers = the taxonomy YOU choose
nodes: [{ id, name, g, parent?, brief, role, rt, path, methods? }], // g = a layer key
edges: [{ s, t, k, l?, src, method }], // src = "file:line"
methodEdges: [{ sNode, sMethod, tNode, tMethod, src }], // method→method calls (step 5)
};
```
- **node** — `id` internal unique slug (never shown; namespace it, e.g. `coding-tools` vs `foundation-tools`) ·
`name` short heading on the card · `g` a layer key · `parent` the containing node's id, nested as deep as the
structure needs (omit for top-level nodes) ·
`brief` ≤ ~6-word tagline · `role` one sentence · `rt` key file(s) · `path` dir relative to repo root ·
`methods` (optional, leaf nodes) `[{ name, src:"file:line", calls? }]` — the top ~3–6 functions by call count.
- **edge.k** — a short lowercase kind YOU choose (static `import`, runtime wire, app→engine…); keep the
vocabulary small so the viewer can style by kind. `method`: `mechanical` (tool) | `manual` (grepped).
Containment is implicit via `parent` — never emit containment edges.
- **edge.l** (optional) — what flows over a wire edge (`messages.create`, `POST /webhook`); set it only where
it adds signal, never a count (`3×`).
- **methodEdge** — a call from one node's method to another's. `sMethod`/`tMethod` must exist in those nodes'
`methods` and `src` is the call site, else the edge is dropped.
Procedure:
1. **Scan structure** — find source roots (`apps/*`, `packages/*`, `src`); they become the top-level nodes. Read docs.
2. **Choose the layer taxonomy** (the key judgment) — a few layers, calm palette, e.g.
frontends / control-plane / runtime / shared-pkg / external. Put them in `config.layers`.
3. **Extract the mechanical floor — required, via `npx`:**
- **Routes + import/call graph (codegraph):** `npx -y @colbymchenry/codegraph@latest init -i`, then
`npx -y @colbymchenry/codegraph@latest query "" --kind route --limit 99999 --json` for the route
inventory. The import + call graph lives in `.codegraph/codegraph.db` (query it directly).
- **Import graph (madge), JS/TS roots only:** `npx -y madge@latest --json --ts-config <root>/tsconfig.json <root>/src`,
then collapse file→file edges to module→module. Non-JS/TS roots: use that language's tool
(`go mod graph`, `pydeps`) or codegraph's import graph instead.
Build on these — only drop to Grep/Read for what a static tool can't see (most **wire/cross-process edges**).
4. **Build — use a workflow, one agent per major module.** Split the repo into its handful of large/important
modules (in a monorepo, each app/package; in a single package, the main subsystems) and give each its own
agent. Each agent picks core modules (routes/controller/plugin/tool/store/service, or doc-named), emits
intra-module import edges (`mechanical`), and stitches **wire edges** out of its module by matching client
call sites (fetch/SDK/db/proxy/queue) to a target or external node, each with a real `file:line` (`manual`).
Wire edges are NOT in the import graph and are the most valuable.
5. **Methods + methodEdges — from codegraph's call graph** (`.codegraph/codegraph.db`; don't skip — this is
what makes nodes legible). Derive mechanically, don't invent names:
- **`node.methods`** — top functions defined under each leaf node's `path`, ranked by call in-degree:
`SELECT n.name, n.file_path||':'||n.start_line AS src,
(SELECT COUNT(*) FROM edges e WHERE e.kind='calls' AND e.target=n.id) AS calls FROM nodes n
WHERE n.kind IN ('method','function') AND n.file_path LIKE '<node.path>/%' ORDER BY calls DESC LIMIT 6;`
- **`methodEdges`** — `calls` edges crossing node boundaries, mapped to `{ sNode, sMethod, tNode, tMethod, src }`;
keep only if both method names survived into their nodes' `methods`. Bucket each side's `file_path` to its
node by longest `path` prefix.
6. **Write, then validate** — write `.archlet/data.js`, run **`npx archlet validate`** (checks shape +
referential integrity), fix what it reports, re-run until clean. Spot-check wire edges against their
`file:line` — drop, don't fabricate.
7. **Seed a starter diff — by default.** A fresh map is static; overlaying a real recent change shows the DIFF
feature working and where activity is concentrated. Pick one coherent change (prefer a recently-merged PR via
`gh pr list --state merged --limit 15 --json number,title,additions,changedFiles,url`; else the largest
meaningful commit from `git log`), discounting lockfile / generated / `dist` noise. Build it as in **Mode 2**,
register it in the manifest but leave `default` null so the clean map opens first. Skip silently if history is
too thin (shallow clone, lone initial commit, only trivial changes); never fabricate one. Mention it on handoff.
**Launch the viewer** — `npx archlet view` is a blocking server that auto-opens the browser; start it in the
background and tell the user it's at `http://localhost:4173` (stop the process to shut it down).
---
## Mode 2 — DIFF → `.archlet/diffs/<name>.js`
Overlay the files a change touches onto the existing map. Read `.archlet/data.js` first for node `path`s.
**1. Get the changed files** (each: path, additions, deletions):
| source | command |
|---|---|
| PR | `gh pr view <n> --repo <owner/name> --json files,title,additions,deletions,changedFiles,url,number` |
| commit | `git show --numstat --format= <sha>` |
| range / branch | `git diff --numstat <A>..<B>` (use `<base>...<head>` for branch-vs-merge-base) |
| working tree / staged | `git diff --numstat` · `git diff --numstat --staged` |
`--numstat` lines are `<add>\t<del>\t<path>` (`-` = binary).
**2. Map files → nodes.** For each path, find the node whose `path` is the **longest prefix**; else fall back to
its top-level root/app node; else report it unmapped. Aggregate per node `{ files, add, del, paths }`. Compute
`contains` = ancestor node ids of every changed node (walk `parent`).
**3. Write the overlay** `.archlet/diffs/<name>.js`:
```js
window.ARCH = window.ARCH || {};
window.ARCH.pr = {
label: "<human label>", // "PR #565" · "commit a1b2c3d" · "working tree"
title: "<optional>", url: "<optional>", number: <n>, // number only for PRs (enables ?pr=<n>)
add, del, files,
nodes: { "<nodeId>": { files, add, del, paths:[...] }, ... },
contains: [ "<ancestorNodeId>", ... ],
links: { // OPTIONAL — connectivity the change alters
add: [ { s, t }, ... ], // a dependency/wire INTRODUCED
cut: [ { s, t }, ... ], // one REMOVED
},
};
```
`links.add` renders as a green + arrow, `links.cut` as a dashed ✕ — between the nodes' visible ancestors. Only
fill `links` when the change actually adds/removes a node→node connection (a new/removed import, fetch, db/proxy
call); omit for internal-only changes. `<name>`: `pr-<n>`, else `commit-<sha7>` / `wip` / `staged`.
**4. Register it** in `.archlet/diffs/manifest.js` (create if missing; keep existing entries):
```js
window.DIFFS = {
default: "<name>",
available: [ { name, label, number?, add, del, files }, ... ], // newest first
};
```
Run **`npx archlet validate`**, fix any issues, then **launch the viewer** in the background (as above) at
`http://localhost:4173`. The viewer has a **Diffs menu**; switch via the menu, `?pr=<n>`, or `?diff=<name>`.
---
## Rules
- **Provenance or it didn't happen** — every wire edge needs a real `file:line`; never invent edges.
- **You decide the taxonomy** (small); `data.js` is the single source of truth and a draft the user can hand-edit.
- **Always end by opening the map** in the background, whichever mode you ran — don't leave a command to paste.
- Monorepo gotchas: CommonJS/NestJS DI deps aren't plain imports; apps importing another app's built `dist`;
dynamic plugin/route registration (invisible to static tools — read the registrar).
GitHub에서 보기