← ArchLangCONTENT HISTORY
WHAT CHANGED
Update to ArchLang
Snapshot Nov 1, 2026 · 07:17 UTC · version 1.50.0
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save ArchLang and check future changes in Following.
Up to 10 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Use when the user wants to create, edit, or inspect an architectural floor plan / building layout as code — e.g. \"draw a 2-bedroom apartment\", \"add a bathroom to this plan\", \"make the bedroom 1 m wider\", \"lay out an office floor\". ArchLang is a tiny text language that compiles a .arch file to a professional floor-plan drawing (SVG/PNG/PDF/DXF). Drive it entirely through the `archlang` CLI; do not hand-render.",
"included_files": [],
"name": "archlang",
"skill_md_contents": "---\nname: archlang\ndescription: Use when the user wants to create, edit, or inspect an architectural floor plan / building layout as code — e.g. \"draw a 2-bedroom apartment\", \"add a bathroom to this plan\", \"make the bedroom 1 m wider\", \"lay out an office floor\". ArchLang is a tiny text language that compiles a .arch file to a professional floor-plan drawing (SVG/PNG/PDF/DXF). Drive it entirely through the `archlang` CLI; do not hand-render.\nlicense: MIT\ncompatibility: Needs a shell with Node 18+ and the archlang CLI (npx -y @chanmeng666/archlang@1.50.0, or npm i -g @chanmeng666/archlang@1.50.0 for a global archlang).\n---\n\n# ArchLang — author floor plans as code\n\nArchLang turns a small `.arch` text file into a professional floor-plan drawing. It is built for\nagents: deterministic, self-correcting (errors carry a machine code, a prose `fix`, and often a\n**machine-applicable** fix `archlang fix` can apply), and verifiable without ever looking at an image\n(`archlang describe`).\n\n## Setup (zero-install)\n\nThe CLI runs straight from npm — no clone, no build:\n\n```bash\nnpx -y @chanmeng666/archlang@1.50.0 --help\n```\n\nOr `npm i -g @chanmeng666/archlang@1.50.0` for a global `archlang` command. Every call below is written\n`archlang <cmd>`; without the global install, write `npx -y @chanmeng666/archlang@1.50.0 <cmd>`. Never run\na bare `arch`: the package installs it as an alias, but on Linux, macOS and Git Bash coreutils\nowns that name, so the shell runs the wrong program.\n\n## The loop (always follow this)\n\n1. **Learn the language first.** Run `archlang spec` and read it — the entire language in one page.\n (`archlang context` prints *everything*: spec + this workflow + CLI reference + error catalog; a small model starts with `archlang context --level starter`, one screen.)\n Do this before writing any `.arch`.\n2. **Write the plan** to a `.arch` file (or pipe via stdin with `-`), preferring the **placement\n sugar** below so you never hand-compute a coordinate.\n3. **Render it:** `archlang compile plan.arch -o plan.svg --json`. The JSON is `{ ok, diagnostics,\n summary }`.\n4. **Auto-fix the mechanical faults:** if `ok` is false, run `archlang fix plan.arch --dry-run --json`\n to preview the **machine-applicable** edits (off-wall opening → attachment form, out-of-range\n position clamped, …), then re-run without `--dry-run` to apply. Anything `fix` can't resolve stays\n in `diagnostics[].fix` for you to edit by hand. Exit code `2` means a deterministic user error —\n fix it, don't blindly retry (`1` = IO/internal, `3` = bad usage).\n5. **See the plan without an image:** `archlang compile plan.arch -f txt` (or `archlang preview plan.arch\n --ascii`) prints a zero-dependency ASCII floor plan you can read straight from stdout.\n6. **Verify intent:** `archlang describe plan.arch --json` returns the rooms (areas, adjacency), what each\n door connects, and totals. Confirm the room count, labels, and areas match what was asked.\n Each room reports two areas: `area_m2` on wall centrelines and `net_area_m2`, the floor no wall\n stands on (the room less every wall band inside it, computed exactly, or absent when it cannot\n be, and then so is the net total); `totals` sums both. Quote `net_area_m2` as floor area, and name\n which figure you report.\n7. **Gate on soundness — don't ship a flagged plan.** `archlang validate plan.arch --strict --json`\n (parse + resolve + lint). `--strict` makes **every advisory warning fail** (exit `2`) — the gate a\n generation pipeline runs before it ships. Add `--graph g.json` to also assert the intended\n room-to-room adjacency (`{ \"living\": [\"kitchen\",\"hall\"], … }`); a mismatch fails. Read each\n `diagnostics[].fix`, edit, and re-run until it passes — or, if a warning is deliberate, say so.\n The author's loop is compile → `validate --json` (fix its `next` first) → lint → repair, **one\n stage at a time**: the rooms (with stairs and outdoor surfaces), then\n `archlang validate plan.arch --stage rooms --json` until `at_stage.clean` is true; then the walls\n (`walls from rooms`) and `--stage walls`; then doors and windows, `--stage openings`; then fixtures\n and furniture, `--stage fixtures`; then the sheet and the full `validate --strict`. Advance when\n the stage is clean, or when every remaining finding is one you accept and say so: a later stage's\n findings are held back (the `summary` counts them), because they mean nothing until the earlier\n decisions stand; errors always show. Clean is not permanent: a later stage can add or re-raise an\n earlier stage's finding (walls narrow a room to its clear width, a bed makes a double, furniture\n pinches a walk), and each later stage shows the earlier stages' codes again. Read the\n `summary` (first key of `lint|validate --json`, first line of the text report) before anything\n else: it counts the warnings and the distinct codes and leads with the commonest, so a truncated\n read still tells you how much is hidden.\n **The order inside the loop** is the stage list, `brief → rooms → walls → openings → fixtures →\n drafting`, and within a stage: columns, then the exterior walls, then the other walls, then fences;\n doors, then windows, then plain openings; catalogued fixtures, then loose furniture; dimensions,\n labels and the sheet last. Rooms (with stairs, voids and outdoor surfaces) come first because they\n fix the program and the walls can be derived from them; the exterior goes before the interior; an\n opening is cut into a wall that must already exist; drafting reads everything else. This is authoring order only: the order shapes are painted in\n the SVG is a separate thing and does not change.\n **Review, once that loop is clean:** the reviewer (not the author) runs\n `archlang score plan.arch --taste generic --json` and reads the advice: a sourced 0–1 score\n (privacy gradient, daylight, proportion, corner doors, …) whose `criteria[].advice` `A_*` codes\n name what to improve. It never gates, the author does not iterate on it, and a score capped at 0.5\n means a validity lint warning still stands. The reviewer then looks at\n `archlang preview plan.arch --review -o review.png`: every warned element ringed and numbered on\n the plan, a legend, and close-ups of flagged openings and pieces. On a lint-clean plan it still\n shows the access graph (which rooms each door joins, which rooms the entrance reaches) and which\n door is the main entrance or that the choice is a tie: check those against the brief. If no one\n reviews for you, run both once after the loop is clean and report the score and its `A_*`\n advice; do not iterate on it.\n8. **Check the plan against the brief.** Write the user's brief as an `intent.json` — its checkable\n expectations as data (room count, per-room concepts with area/window bands, total area, optional\n adjacency/reachability). A stated size binds: \"about 70 m²\" → `grossInternalAreaM2` (inside the\n shell's inner faces); a room's stated width/depth/W × H → `roomDimensions`. Two disciplines keep\n it brief-grounded: assert an area band only where the brief gives a number (\"about/~N\" → ±10%; \"at least N\" → `min` only; qualitative words → nothing),\n and assert the top-level room `count` only when the brief **enumerates** the rooms. Gate with\n `archlang validate plan.arch --intent intent.json --feedback --json`: a gating miss (room\n count/existence/area/total-area/window/size) fails (exit `2`) with a per-violation correction prompt —\n iterate on the feedback and re-run. Adjacency/reachability are advisory (reported, never fail the\n gate). Use `archlang score plan.arch --brief intent.json --json` as a continuous satisfaction meter\n (always exit `0`) to watch the plan approach the brief across edits. See\n [`/intent.schema.json`](https://archlang.uk/intent.schema.json). A number or relation the brief\n fixes and every edit must keep (a 3.6 m clear living room, a partition on axis 2, a bathroom over a\n bathroom) goes in the source as a `keep` (`keep min clear living 3600`); it moves and draws nothing, a\n broken hard keep (the default) is `W_KEEP_VIOLATED` with a signed residual and fails `--strict`,\n and `archlang impact --set` lists which keeps a change breaks, holds or heals, each with its\n residual before and after.\n9. **Fix furniture geometry:** `archlang repair plan.arch -o fixed.arch` pushes furniture out of\n walls/doorways/swing arcs (the geometric corrector; distinct from `fix`). It rewrites a piece in\n the form you wrote it (an `at` point, or the `inset` of an `in <room> anchor …` placement) — and\n **always read `unresolved`**: a piece it may not rewrite (one `for`/component statement drawing\n several pieces, expression coordinates, an `against wall` anchor) is reported there with the fault\n and why, never silently skipped. `changed: false` with a non-empty `unresolved` means *you* edit\n the source.\n10. **Finish the drawing:** once lint is clean, run `archlang finish plan.arch --dry-run --json`, then\n without `--dry-run` to apply. It first furnishes each rectangular room that holds no furniture,\n by the room's use (single-storey plans; a furnished room is never touched — a partly furnished one is\n named in `unresolved` with the categories it lacks, not topped up), then adds the sheet\n statements the plan lacks — `paper`, `scale`, `dims auto`, `title`, `schedule rooms`,\n `legend` — on the smallest sheet the drawing fits. It fills only what is missing and deletes\n nothing. Read `unresolved`: a room it left empty or a statement it left out is named there\n with the reason. A room it has no pieces for (a hall, a store, a room of no known use) is named\n too, with `scope: \"outside-table\"`; that note asks nothing of you unless the room should hold\n furniture, so `changed: false` with only such notes means the plan is finished (`--only\n furnish`/`--only sheet` runs one step;\n `--reissue` lets it replace a `paper`/`scale` the drawing does not fit).\n11. **Show the user:** `archlang preview plan.arch -o plan.png` renders a PNG (`--install` fetches the\n optional renderer if missing). To hand the user the plan itself, rendered and with nothing to\n install, run `archlang link plan.arch` (or `npx -y @chanmeng666/archlang@1.50.0 link plan.arch`): it\n prints a playground URL that opens the plan; `--json` adds the embed URL.\n\n## Write it right the first time (placement sugar — the preferred path)\n\nA geometry-blind generator that emits absolute coordinates produces plans that render but are\nphysically wrong (openings off their wall, furniture through walls). Author by **relation** instead\n— the compiler computes the coordinate, and fails loudly if the reference is ambiguous.\n\n**Start from the rooms.** Write the rooms (a corridor is a room), let `walls from rooms` derive\nevery wall, and host each opening by the rooms it joins. This plan compiles and lints clean:\n\n```arch\nplan \"Room graph\" {\n units mm\n grid 100\n room id=living at (0,0) size 4000x4000 uses living\n room id=hall right-of living size 1500x4000 uses hall\n room id=bedroom right-of hall size 3000x4000 uses bedroom\n walls from rooms exterior 200 partition 100 { open between living and hall }\n door on hall.bottom at center width 900 swing into hall\n door between hall and bedroom at 800 from start width 800 swing into bedroom\n window on living.left width 1500\n window on bedroom.right width 1200\n furniture bed in bedroom anchor bottom-right flush size 1500x2000\n}\n```\n\n- **Let the rooms make the walls.** When the rooms are rectangles, or polygons with only horizontal and\n vertical edges, that tile the floor (write a corridor as a room), `walls from rooms exterior 200 partition 100` derives the shell on their outline\n and a partition on every shared edge, so no wall can go missing. Host openings by the rooms:\n `door between living and bed at 800 from end width 800` (a door must say where: `center`,\n `<mm> from start|end|<room>.<edge>`, `axis <n>`, offsets to the centre), `window on bed.top\n width 1500` (centred by default). A jamb too near a meeting wall is refused (`E_OPENING_NIB`\n names the legal spot), never slid. `{ except <room> }` leaves a room out. For an\n open-plan join write `open between living and kitchen` in the body (an opening across the whole\n shared edge, or `width <mm>` centred; never a door on that edge), and `between a and b thickness\n 150` for one thicker partition. Refusals name the rooms (`E_ROOMS_OVERLAP`, `E_ROOMS_NOT_TILING`,\n `E_PARTITION_TOO_SHORT`, `E_ROOM_NOT_RECTANGLE`, `E_WALL_BETWEEN_NOT_ADJACENT`).\n- **Or name one wall by its rooms.** For a plan that derives some walls and types others,\n `wall id=p partition thickness 100 between hall and bed` stands on the two rooms' shared edge\n and `wall id=shell exterior thickness 200 around [hall bed bath]` is one ring on their outline\n (no hole). They keep your id, thickness and material, host doors like any wall, and replace\n the derived span beside `walls from rooms`.\n- **Read wall points off the rooms.** A rectangular room `r` exposes `r.top`, `r.bottom`, `r.left`,\n `r.right`, `r.cx`, `r.cy`, so `wall partition thickness 100 { (hall.right, hall.top) (hall.right,\n hall.bottom) }` follows the room when it grows. Use them in walls, openings, furniture and dims,\n not in a `let` or a room's own `at`/`size` (`E_DATUM_BEFORE_ROOMS`).\n- **Attach openings to a wall by position, not `at (x,y)`.** `door on <wall> at <pos> …` /\n `window on <wall> at <pos> …` / `opening on <wall> at <pos> …`, where `<pos>` is millimetres along\n the wall or a percentage (`50%`). `swing into <room>` picks the swing direction toward a named room,\n `swing inward|outward` towards/away from the building's floor (`in|out` follow the wall's drawn\n direction: `in` = right of travel on the page, so they flip on a wall drawn the other way);\n `hinge near start|end` hinges at the segment end nearer a wall end. (Off-wall/ambiguous →\n `E_ATTACH_WALL_REF`; past the wall → `E_ATTACH_POS_RANGE`.)\n- **Lay rooms with `strip`.** `strip right at (0,0) gap 0 height 4000 { room … room … }` places a row\n (or column, with `down`/`up` + `width`) of rooms end to end — no per-room `at`.\n- **Place furniture by anchor.** `furniture <kind> in <room> anchor <9-point anchor> [flush] [inset <mm>] …`\n snaps a piece to a room corner or edge — the anchor is one of `top-left`, `top`, `top-right`,\n `left`, `center`, `right`, `bottom-left`, `bottom`, `bottom-right`; `against wall <id>` backs\n plumbing/kitchen fixtures onto a real wall face. Both are closed-form and never float or penetrate.\n- **Say what a piece faces, not its `rotate`.** `furniture chair at (…) size 450x450 facing t` turns\n the chair to table `t` (and a sofa to its TV); `against <room>.<edge>` backs a piece onto a derived\n wall; `clear front 600` asks lint to keep that depth free. Lint flags a chair facing away from its\n table (`W_SEAT_FACING_AWAY`).\n- **Add `flush` and never compute half a wall thickness.** A room's rectangle runs along wall\n *centerlines*, so a bare `anchor bottom` leaves the piece's back inside the solid\n (`W_FURNITURE_WALL_COLLISION`). `anchor bottom flush` measures from the backing wall's inner **face**\n instead, and `flush inset 50` from 50 mm off it — so write `furniture wc in r_bath anchor bottom flush\n size 400x700`, not an `inset 100` you worked out from a thickness you never named. (`flush` needs an\n anchored edge: on `centered`/`anchor center` it is `E_FURN_FLUSH`.)\n- **Every room still needs a way in** — put a `door` or cased `opening` on a wall of *every* room\n (an open-plan space still needs a modeled opening), and keep furniture out of the doorway approach\n (≥300 mm) and the leaf's swing.\n- **Absolute `at (x,y)` is the fallback**, not the default — reach for it only when no attachment\n expresses what you mean.\n\nSee `examples/two-bed.arch` for a furnished two-bedroom flat authored this way, and `archlang spec` for the grammar.\n\n## Author a repeating piece ONCE, then place it (v1.22)\n\nWhen a building repeats — two wings, six wards, a floor of identical units — do **not** copy the\nstatements and re-add an offset to every coordinate. That is how a plan gets 40 hand-edited numbers\nthat drift apart the first time the brief changes. Author the piece in its **own** coordinates from\n`(0,0)` and place it:\n\n```\ncomponent wing() {\n wall id=shell exterior thickness 300 { (0,0) (18000,0) (18000,12000) (0,12000) close }\n room id=main at (0,0) size 18000x9000 label \"Gallery\" uses living\n room id=corr at (0,9000) size 18000x3000 label \"Corridor\" uses circulation\n}\n\nplace wing() as west at (0,0)\nplace wing() as east at (42000,0) mirror x\n```\n\n- **`as` names the instance, `at` places it; both are required.** Ids inside become\n `west.main` / `east.main`, and you address them by that dotted name everywhere a reference is\n taken: `wall west.shell`, `in west.main`, `archlang describe --room west.main`. (A dotted name in a\n *declaration* is `E_DOTTED_DECL` — the namespace belongs to the `place`.)\n- **`rotate 0|90|180|270` and `mirror x|y` are exact**, and a mirror is real physics: door swings,\n fire exits and fixture facings all come out mirror-image. Prefer `mirror x` over `rotate 180` for a\n symmetrical pair — a 180° turn also swaps which side the corridor is on.\n- **A whole FILE can be the component:** `import \"wing.arch\" as wing` binds that file's top-level\n drawable statements as a zero-argument component (its plan settings are ignored — the root plan\n owns the sheet). One file, one room or wing, exactly as you would organise components in code.\n- **A `place`d instance is also a zone**, so `describe().zones`, `archlang describe --zone west` and the\n grouped `schedule rooms` table work with no extra declaration.\n- **The bare call `wing()` is the OLD inline macro** — caller's coordinates, caller's id counters, no\n namespace. Keep it for a small parameterised motif; use `place` for a piece of building.\n- **A component is a closed world going out:** the plan can reach into it (`wall west.shell`), but\n the component cannot reach out. Let the parent draw the connecting doors — that is the whole\n composition contract. Verify with `archlang describe --json` → `instances[]`.\n\nSee `examples/museum-wing.arch` + `examples/museum-wings.arch`.\n\n## Self-correct with data, not guesswork\n\n`archlang compile --json` returns every problem as a `Diagnostic` with a byte span, `line`/`col`, a\ncatalogued `E_*`/`W_*` code, and a prose `fix`. Where the correction is a mechanical text edit, the\ndiagnostic also carries **machine-applicable `fixes`**:\n\n- **`archlang fix`** applies them in a bounded, self-checking fixpoint — **only `machine-applicable` by\n default** (`--unsafe` also applies `maybe-incorrect`; `--dry-run` previews; `--force` keeps a pass\n that would otherwise roll back). Use it to clear the syntactic faults before you touch anything by\n hand.\n- **`archlang fix` is syntactic; `archlang repair` is geometric.** `fix` rewrites text where the right text is\n known (e.g. an off-wall door → the attachment form); `repair` *moves furniture* to a position no\n text edit could express. They compose — fix first, then repair.\n- **`archlang fix` also applies fix-carrying *lint* advisories**, not only compile-stage faults — e.g.\n `W_ALIAS_MATCH` (a room's use inferred from an indirect label alias) fixes by inserting the explicit\n `uses …` it inferred. Before editing, `archlang describe --json`'s **`freedom`** block tells you which\n element positions were **hand-authored** (`absolute`) vs **derived** by the resolver\n (relational/strip/attached/anchored/against-wall), so you know which numbers are safe to nudge.\n\n## Fix the topology: add doors & windows the room graph needs\n\n`fix`/`repair` never add a door or a window — *where* to put one is a design choice the compiler must\nnot make. When lint reports `W_ROOM_UNREACHABLE`, `W_ROOM_DISCONNECTED`, `W_NO_ENTRANCE`,\n`W_BATH_VIA_BEDROOM`, or `W_BEDROOM_NO_WINDOW`, ask ArchLang for candidates:\n\n- **`archlang suggest plan.arch --json`** returns ready-to-paste `door`/`window` statements (furniture-aware\n — a door candidate never opens onto a wardrobe; each references its wall by a **stable ref** — an\n authored id or a unique category — or absolute coordinates, never a re-bindable positional id) plus a\n rationale for each — for a room with no path back (`W_ROOM_UNREACHABLE`), a building with no way in\n (`W_NO_ENTRANCE`), a bath reachable only through a bedroom (`W_BATH_VIA_BEDROOM`), or a windowless\n bedroom (`W_BEDROOM_NO_WINDOW`). Choose one and insert it, then re-run the loop. This replaces\n hand-computing coordinates.\n- **Manual fallback** (if `suggest` offers nothing that fits): from `describe().access`, connect each\n unreachable room in priority — (1) a new **exterior entrance** `door on <exterior wall> at <pos>`\n into a living/kitchen/hall with an exterior edge (avoids routing through a bedroom); else (2) a\n `door on <shared wall> at <pos>` to an adjacent reachable, non-bedroom room; and give a windowless\n bedroom a `window on <its exterior wall> at <pos> width 1200`. Never make a bathroom reachable only\n through a bedroom. Then `archlang repair` (a new door may pinch furniture) and re-gate.\n\n> An *existing* opening `validate` reports **off its wall** (`W_DOOR_OFF_WALL` /\n> `W_WINDOW_OFF_WALL` / `W_OPENING_OFF_WALL`) is a mis-coordinate, not a missing connector — run\n> `archlang fix` (it rewrites it to the attachment form) rather than adding a new one.\n\n### Pick the door KIND before you widen the room\n\nA door has a kind — `hinged` (the default) · `sliding` · `barn` · `bifold` · `pocket` · `garage` ·\n`double` — and it changes what the checks can say, not just the drawing. **Only `hinged` and `double`\n(two leaves, one per jamb) sweep arcs**, so `W_SWING_OBSTRUCTED` cannot apply to any of the others.\nA hinged leaf over 1219 mm raises `W_LEAF_TOO_WIDE`: a wide doorway is a `double` door.\n\nThat makes the kind a legitimate answer to a diagnostic, and often the *right* one:\n\n- `W_SWING_OBSTRUCTED` on a door into a tight room — a `sliding` or `pocket` leaf sweeps nothing, so\n the warning goes away because the plan genuinely fixed it, not because you silenced a rule. Moving\n the furniture is the alternative; pick whichever the brief actually wants.\n- A `pocket` earns its own check instead: `W_POCKET_RUN` measures the wall the panel must slide into.\n Swapping one warning for the other is a real trade, so read the new one rather than assuming a win.\n- Clauses are kind-specific and a wrong pairing **refuses** (`E_DOOR_KIND_CLAUSE`): `hinge` is\n hinged-only, `slide`/`open` are sliding-family-only, and `garage` takes no clause at all — which\n side it parks on is derived from which face has floor. A kind other than `hinged` or `double` on an\n `arc` wall also refuses (`E_DOOR_KIND_CURVED`).\n\n`archlang spec` has the full grammar; `examples/bungalow.arch` is the worked plan.\n\n### Declare `site` when the brief mentions orientation\n\n`site { street <north|south|east|west> [hemisphere …] }` **draws nothing**. It exists so orientation\nbecomes a *fact the tools can check*, and without it two things silently cannot run:\n\n- `describe --json` gains a `site` block naming five directions — `street`, `back`, `equator_side`,\n `sunrise_side`, `sunset_side` — and an intent's `windows.facing` may assert those names. **With no\n `site` declared, that assertion fails with `E_INTENT_NO_SITE`** rather than passing vacuously.\n- `W_ROOM_NOT_EQUATOR_FACING` is the one rule that reads it, flagging a room whose windows all face\n away from the equator side.\n\nSo: if the brief says \"south-facing living room\", \"morning light in the kitchen\", or \"faces the\nstreet\", declare `site` first — otherwise you have written a plan whose central requirement nothing\ncan verify. **It is a drafting heuristic for an aspect, not a daylight measurement**: there is no sun\nmodel, no latitude and no date, and `_side` names must not be read as more than an orientation.\n\n`examples/bungalow.arch` demonstrates both this and the door kinds above.\n\n### Write a `height` only when the brief gives one\n\n`height <mm>` (plan, `level` or `wall`) and `sill`/`head` on an opening are the vertical **datum**:\nthey draw nothing, because a plan is a horizontal cut, and `describe --json` reports them **only if\nthe source wrote one** — so adding a height you were not asked for changes what every consumer sees\nfor no gain. Write them when the brief names a ceiling, a parapet or a cill line; otherwise leave\nthem out and read the defaults from `archlang manifest --json`'s `datum` block.\n\n## Ask the CLI, and read only what you need\n\nThe CLI documents itself, and every read can be narrowed at the source — never pull a whole plan's\nfacts into context just to filter them yourself.\n\n- **`archlang <cmd> --help`** prints that one command's flags *and worked\n examples* — every command carries at least one copy-pasteable invocation. `archlang --help` lists the\n commands; `archlang --version` prints the version. A flag a command doesn't take is a usage error\n (exit `3`) with a did-you-mean, never a silently-swallowed filename — so a typo fails loudly\n instead of compiling the wrong thing.\n- **`archlang describe --select <keys>`** emits only the named top-level keys (`rooms`, `doors`,\n `windows`, `openings`, `furniture`, `access`, `circulation`, `totals`, `freedom`, `caption`, …);\n the `ok`/`plan`/`units`/`diagnostics` envelope is always kept, so narrowing can't lose the verdict.\n **`archlang describe --room <ids>`** keeps only those rooms plus the doors/windows/furniture that touch\n them (whole-plan facts — `bbox`, `totals`, `caption`, each room's `adjacent` — stay whole-plan, so a\n narrowed read never lies about the building). Both mark the result with `filtered: true`.\n- **`archlang lint|validate --code <CODE,…>` / `--severity error|warning`** show only the diagnostics you\n asked for. These are **display filters only**: `ok` and the exit code are always computed from the\n *unfiltered* set, so reading less can never turn a failing plan green. A filtered result carries\n `filtered: true` + `total_diagnostics`.\n- **`archlang context --section spec|workflow|cli|errors`** prints one section of the bundle\n instead of all of it (about 145 KB in all) — `errors` for the diagnostic catalog, `cli` for the\n command reference.\n- **`archlang fix --dry-run`** prints the exact unified diff it would write (to stderr; `--json` also\n carries it as `diff`) and touches nothing. When you do apply in place, **`--backup`** keeps the\n original bytes at `<file>.bak`.\n\n## Structured authoring & constrained generation (optional)\n\n- **Plan JSON.** Author or ingest the machine-native shape and compile it: `archlang compile plan.json\n --from-json -o out.svg`. The schema is served at\n [`/plan.schema.json`](https://archlang.uk/plan.schema.json).\n- **GBNF.** To force a local model to emit only parseable ArchLang, constrain decoding with\n [`/archlang.gbnf`](https://archlang.uk/archlang.gbnf).\n\n## Commands\n\n```bash\narchlang spec # the whole language in one page — READ THIS FIRST\narchlang context # everything in one call: spec + this workflow + CLI reference + error catalog\narchlang context --section errors # just one section of it (spec|workflow|cli|errors)\narchlang --help # list the commands\narchlang manifest --json # the whole CLI API as data: commands, flags, formats, lint rules, error codes\narchlang compile plan.arch -o out.svg --json # render (also -f dxf|txt|json|pdf|png)\narchlang compile plan.arch -f txt # zero-dependency ASCII text plan on stdout\narchlang compile plan.json --from-json -o out.svg # compile structured Plan JSON (see /plan.schema.json)\necho '<source>' | archlang compile - -o - -f svg # compile stdin → SVG on stdout\narchlang fix plan.arch --dry-run --json # preview the machine-applicable fixes as a unified diff (drop --dry-run to apply; --backup keeps <file>.bak)\narchlang suggest plan.arch --json # advisory door/window statements: unreachable room / no entrance / bath-via-bedroom / windowless bedroom\narchlang describe plan.arch --json # semantic facts: rooms, areas, adjacency, door connections, circulation\narchlang describe plan.arch --select rooms,totals --room kitchen --json # narrow the facts to what you actually need\narchlang impact plan.arch --set W=4500 --json # change one let: what moved and why, what held, what broke or healed\narchlang lint plan.arch --json # architectural soundness warnings\narchlang lint plan.arch --code W_NO_ENTRANCE --json # display filter only — never changes `ok` or the exit code\narchlang validate plan.arch --strict --json # parse + resolve + lint; --strict fails on warnings (the ship gate)\narchlang validate plan.arch --graph g.json --json # also check interior-door adjacency against an intended graph\narchlang repair plan.arch -o fixed.arch # geometric corrector: furniture out of walls/doorways/swings + change log\narchlang finish plan.arch --dry-run --json # after lint is clean: preview the furniture and sheet statements it would add (drop --dry-run to apply)\narchlang fmt plan.arch --write # canonical formatting\narchlang batch a.arch b.arch -f svg --json # render many plans/variants at once → results[]\narchlang preview plan.arch -o plan.png # render a PNG to SHOW the user (--install fetches resvg if missing)\narchlang new -o plan.arch # scaffold a starter plan\narchlang explain E_ROOM_SIZE --json # look up any diagnostic code\n```\n\n(An optional MCP server, `@chanmeng666/archlang-mcp`, wraps these same library functions for\nMCP-native hosts — prefer the CLI when you have a shell; it costs nothing in context until called.)\n\n## Key rules (full detail in `archlang spec`)\n\n- **Units are millimetres** (a 4 m wall is `4000`); **origin top-left, +x right, +y DOWN**. An\n optional metric suffix is exact sugar for the same mm value — `4m` = `4000`, `40cm` = `400`,\n `20mm` = `20` — so you can write `4m` instead of hand-multiplying; bare numbers are unchanged.\n- **Attach openings to walls** (`on <wall> at <pos>`) so they always sit on a segment; a raw `at`\n that lands off any wall warns (and `archlang fix` rewrites it).\n- **Furniture draws real symbols, not boxes.** Every catalogued kind (`archlang manifest --json` →\n `fixtureCategories`) renders a plan symbol and **ignores `label`**; an uncatalogued word falls\n back to a labelled rectangle. Most kinds also carry a footprint, so `furniture <kind> against\n wall <id> in <room>` needs no `size` — `offset` is the piece's CENTRE along the wall run. Put\n fixtures in every bath and kitchen so lint stays quiet.\n- **`dims auto`** draws dimension strings for you (`overall`, `rooms`, `walls`, or `all`).\n- Edit is cheap: \"make the bedroom 1 m wider\" is a one-number change, then recompile.\n\nTreat the CLI as the source of truth — author, render, and verify through it rather than reasoning\nabout SVG by hand.\n\n## Task skills\n\nFour short skills sit beside this one in `skills/`, one job each\n(`npx skills add ChanMeng666/archlang` offers them with this one). Each is a\nchecklist of CLI calls and holds no language reference of its own:\n\n- `archlang-brief-to-plan`: write a new plan from a written brief and check it against the brief.\n- `archlang-edit-plan`: change an existing `.arch` with the smallest edit, then re-verify.\n- `archlang-export`: deliver SVG, PNG, PDF or DXF files, with paper size, scale and storeys.\n- `archlang-review-plan`: check a plan (lint profiles, the intent gate, suggestions) without editing it.\n"
}SHA-256 of public snapshot: 342d81a51e5e09ca60f1854524a02f99fe3356ea9cd4525142a56775a46da8c1