@zvndev/darkmown 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +33 -0
- package/docs/cli.md +8 -0
- package/docs/spec-alignment.md +8 -0
- package/grammar/wd-directives.gbnf +58 -0
- package/package.json +3 -1
- package/site/pages/docs/index.wd +15 -0
- package/src/builder.js +5 -0
- package/src/catalog.js +586 -0
- package/src/cli.js +8 -0
- package/src/compiler/actions.js +45 -12
- package/src/compiler/context.js +41 -1
- package/src/compiler/fetch.js +12 -4
- package/src/compiler/forms.js +78 -24
- package/src/compiler/loops.js +15 -4
- package/src/compiler/media.js +21 -7
- package/src/compiler/predicates.js +25 -6
- package/src/compiler/state.js +67 -16
- package/src/compiler/structure.js +33 -10
- package/src/grammar.js +131 -0
- package/src/index.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Darkmown are documented here. Versions follow [semver](https://semver.org); pre-1.0 minor versions may contain breaking changes.
|
|
4
4
|
|
|
5
|
+
## 2.2.0 — 2026-07-12
|
|
6
|
+
|
|
7
|
+
AI authoring becomes a first-class framework surface: a machine-readable directive catalog, a generated prompt cheatsheet, a shipped GBNF grammar for constrained decoding, and compile errors that teach by example — all generated from the compiler's own whitelist tables, so none of it can drift from what actually compiles. Zero runtime impact (7654 B, unchanged).
|
|
8
|
+
|
|
9
|
+
- **`darkmown catalog` — the language, as data.** `directiveCatalog()` (also `import '@zvndev/darkmown/catalog'`) returns every directive, loop clause, action op, format pipe, and predicate operator with syntax, description, a concrete example, and whether it makes a page reactive — 25 directives, 13 action ops, 20 pipes, sourced from the compiler's real tables with drift-guard tests in both directions. `darkmown catalog --llms` renders the same data as a ~92-line cheatsheet, and every build emits it as `dist/llms.txt` — the exact artifact to hand an LLM (or stuff into a small on-device model's system prompt).
|
|
10
|
+
- **A shipped GBNF grammar (`grammar/wd-directives.gbnf`).** Generated from the same tables: directive-line shape, clause order, and the closed op/pipe/action vocabularies as literal alternations, with free-form values left permissive. Point llama.cpp's `grammar` field at it and a local model becomes structurally unable to emit JS/HTML muscle-memory syntax inside `.wd` directives. Kept in sync by test; regenerate with `scripts/gen-grammar.mjs`.
|
|
11
|
+
- **Compile errors now teach by example.** All 24 corrective `Use:` hints with bracket-placeholders gained a trailing concrete line — `… Use: @loop src into item [where …] — e.g. @loop /products.json into p where p.price < 50` — and a test extracts every example from the real thrown error strings and compiles it, so the examples can never rot. Empirically motivated: small local models copy error-message examples verbatim, brackets and all.
|
|
12
|
+
- **Structured error metadata.** Compile errors carry `err.wd = { file, line, hint, example }` alongside the unchanged message string — no more parsing error text in tooling (the playground, editors, agent harnesses).
|
|
13
|
+
|
|
5
14
|
## 2.1.0 — 2026-07-11
|
|
6
15
|
|
|
7
16
|
Two headlines. Reactive pages now run under the **same strict, eval-free CSP as static pages** — the runtime interprets validated expressions instead of building functions, so `'unsafe-eval'` is gone from every page the framework ships. And darkmown.com gained a **browser playground**: the real compiler, bundled, compiling `.wd` live on every keystroke — built on a new fs-free `compileFromMemory` API.
|
package/README.md
CHANGED
|
@@ -59,6 +59,7 @@ npm run dev # live demo site — the same site that runs darkmown.com
|
|
|
59
59
|
- `darkmown build [--target cloudflare] [--drafts]` writes static output to `dist` (plus `sitemap.xml`/`rss.xml`/`robots.txt`); `--drafts` includes `draft: true` pages for staging.
|
|
60
60
|
- `darkmown deploy <vercel|cloudflare> [--prod]` builds and deploys via the platform CLI.
|
|
61
61
|
- `darkmown serve` previews the built `dist` locally.
|
|
62
|
+
- `darkmown catalog [--llms]` prints the machine-readable directive catalog (JSON), or with `--llms` a compact cheatsheet — see [AI authoring](#ai-authoring).
|
|
62
63
|
- `darkmown version` prints the installed package version.
|
|
63
64
|
- `darkmown help` prints CLI usage.
|
|
64
65
|
|
|
@@ -984,6 +985,38 @@ const { html, assets } = compileFromMemory(
|
|
|
984
985
|
|
|
985
986
|
Includes, colocated `.skin`/`.js` detection, and `@loop` JSON-data reads all resolve against the map (anything not in it is simply "absent"), and it throws the **same `file:line` compile errors** as the CLI — the error DX is identical. This is exactly the entry point the [browser playground](https://darkmown.com/playground/) is built on: markdown-it and the compiler bundled to one asset, compiling `.wd`/`.md` on every keystroke and rendering the result into an iframe. On disk, `compilePage(file, paths)` is the same compile with a filesystem reader injected for you.
|
|
986
987
|
|
|
988
|
+
### Structured compile errors — `err.wd`
|
|
989
|
+
|
|
990
|
+
Every compile error is still a plain `Error` with the same `file:line` message, but it now also carries a machine-readable mirror on `err.wd` — so an AI edit loop (or an editor) gets the facts without re-parsing the prose:
|
|
991
|
+
|
|
992
|
+
```js
|
|
993
|
+
import { compileFromMemory } from "@zvndev/darkmown";
|
|
994
|
+
|
|
995
|
+
try {
|
|
996
|
+
compileFromMemory({ "site/pages/index.wd": ":state x" }, "site/pages/index.wd");
|
|
997
|
+
} catch (err) {
|
|
998
|
+
err.message; // "Malformed :state in …/index.wd:1: :state x. Use: :state name = value [persist] — e.g. :state count = 0"
|
|
999
|
+
err.wd; // { file: "…/index.wd", line: 1, hint: ":state name = value [persist] — e.g. …", example: ":state count = 0" }
|
|
1000
|
+
}
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
`example` is always a concrete, **compilable** line (never a `[placeholder]`), and every corrective `Use:` hint that contains bracket-placeholders ends with a matching `— e.g. <valid line>`. This is a deliberate affordance for small local models, which otherwise copy `[optional]`-style placeholders into source verbatim.
|
|
1004
|
+
|
|
1005
|
+
## AI authoring
|
|
1006
|
+
|
|
1007
|
+
Darkmown ships its own description of the `.wd` language — the same tables the compiler validates against — so a tool can teach a model to write `.wd` and constrain its output to what compiles.
|
|
1008
|
+
|
|
1009
|
+
- **Directive catalog** — `darkmown catalog` prints structured JSON: every directive, `@loop` clause, loop variable, button action, format pipe, and predicate operator, each with a syntax template, a one-line description, one concrete example, and whether it needs the reactive runtime. Importable too: `import { directiveCatalog } from "@zvndev/darkmown/catalog"`.
|
|
1010
|
+
- **Cheatsheet (`llms.txt`)** — `darkmown catalog --llms` prints a compact (~90-line) markdown cheatsheet generated from the same data — the artifact you stuff into a model's system prompt. Every build also writes it to `dist/llms.txt`.
|
|
1011
|
+
- **GBNF grammar** — [`grammar/wd-directives.gbnf`](grammar/wd-directives.gbnf) is a generated [GBNF](https://github.com/ggerganov/llama.cpp/blob/master/grammars/README.md) grammar for `.wd` directive lines. Point llama.cpp (or any grammar-constrained decoder) at it to make invalid directive lines — JS idioms like `cart.filter(...)`, HTML muscle-memory like `::: card class="…"` — literally unrepresentable during generation. Regenerate with `node scripts/gen-grammar.mjs`.
|
|
1012
|
+
|
|
1013
|
+
```sh
|
|
1014
|
+
darkmown catalog --llms > system-prompt.md # paste into a model's context
|
|
1015
|
+
darkmown build # also emits dist/llms.txt
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
Because the catalog, cheatsheet, grammar, and error `example`s are all generated from the compiler's own tables, they cannot drift from what actually compiles (enforced by tests).
|
|
1019
|
+
|
|
987
1020
|
## Editor support
|
|
988
1021
|
|
|
989
1022
|
A VS Code extension in [`editors/vscode`](editors/vscode) gives `.wd` and `.skin` files syntax highlighting, snippets, and folding — so a `.wd` file reads as Markdown-plus-directives, never as broken Markdown.
|
package/docs/cli.md
CHANGED
|
@@ -82,6 +82,14 @@ The deploy URL is printed when the CLI reports it. If the platform CLI isn't sig
|
|
|
82
82
|
|
|
83
83
|
Serves the already-built `dist` directory for local preview (static only — use `darkmown dev` to exercise `api/*` locally). Run `darkmown build` first.
|
|
84
84
|
|
|
85
|
+
### `darkmown catalog [--llms]`
|
|
86
|
+
|
|
87
|
+
Prints the machine-readable `.wd` directive catalog as JSON — every directive, `@loop` clause, loop variable, button action, format pipe, and predicate operator, each with a syntax template, description, and concrete example. With `--llms` it prints a compact (~90-line) markdown cheatsheet instead — the artifact to paste into an AI model's system prompt. Every `darkmown build` also writes the cheatsheet to `dist/llms.txt`, and Darkmown ships a generated GBNF grammar (`grammar/wd-directives.gbnf`) for constrained decoding.
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
darkmown catalog --llms > system-prompt.md
|
|
91
|
+
```
|
|
92
|
+
|
|
85
93
|
## Smoke checks
|
|
86
94
|
|
|
87
95
|
From this repository, run:
|
package/docs/spec-alignment.md
CHANGED
|
@@ -204,6 +204,14 @@ The 1.3.0 cycle closes table-stakes gaps a new user expects out of the box. Trac
|
|
|
204
204
|
- **`compileFromMemory(files, entryPath, options)`** — a new public API (exported from `src/compiler.js` and the package root) that compiles a page from an in-memory map of project-relative path → source, with **zero `node:fs`**. It is the minimal-clean vfs refactor of the compiler: a `Reader` abstraction (`src/compiler/reader.js`) is threaded through the `Compilation`/`Ctx` and the page-shell helpers, replacing every direct `node:fs` call in `src/compiler/{page,includes,loops,collections}.js`. The default **`fsReader()`** (`src/compiler/fs-reader.js`, the ONLY compiler module importing `node:fs`) is injected by the Node-facing barrel, so `compilePage`/`compileDocument`/`enhanceImages` keep their exact signatures and the on-disk build stays **byte-identical** (golden: the demo `dist/` is unchanged across the refactor). `memoryReader(files, cwd)` backs the fs-free path: includes, colocated `.skin`/`.js` detection, and `@loop` JSON-data reads all resolve against the map; missing files are simply "absent"; image measuring degrades to no-dimensions (no image bytes in memory). It throws the **same `file:line` compile errors** as the CLI (a compile↔memory parity test asserts identical HTML and error text). This is strategic surface — a planned mobile host needs exactly a filesystem-free compile.
|
|
205
205
|
- **Browser playground (`/playground/`)** — a live editor + preview page on the demo site. `esbuild` (a new **devDependency only**) bundles `compileFromMemory` + markdown-it + a common-languages highlight.js subset into one self-contained ESM asset (`scripts/build-playground.mjs` → the committed colocated `site/pages/playground/index.js`, rebuilt with `npm run build:playground`); `node:path`/`node:crypto`/`image-size` are aliased to tiny browser shims (`scripts/playground/shims/`). The bundle (~128 KB gzipped) loads **only** on `/playground/` and does **not** count against the 8 KB runtime budget (`src/runtime.js` is untouched by the playground). The preview is an `iframe srcdoc` of the compiled HTML; reactive previews load `/__wd/runtime.js` by same-origin URL and inherit the page's CSP — which since 2.1 is the same strict, **eval-free** policy on every page (the runtime interprets expression ASTs; see the 2.1 entry above), so the preview runs under the shipped CSP with no special allowance (`frame-src` does not block `about:srcdoc`; no `'unsafe-inline'` anywhere). The playground page itself dogfoods a real reactive island (a `:state`/`:button` example picker bridged to the bundle via `window.wd.subscribe`), so it is `runtime: true`.
|
|
206
206
|
|
|
207
|
+
## AI authoring — catalog, llms.txt, grammar, structured errors (2026-07-12)
|
|
208
|
+
|
|
209
|
+
- **Machine-readable directive catalog** — `src/catalog.js` (`directiveCatalog()`, exported from the package root and via the `@zvndev/darkmown/catalog` exports subpath) returns structured JSON describing the whole `.wd` surface: every directive, `@loop` clause, loop variable, button-action op, format pipe, and predicate operator, each with a syntax template, one-line description, one **concrete compilable example**, and a reactivity flag. It is sourced from the compiler's own tables — `FORMATTER_NAMES` (format.js), `PREDICATE_OPS` (predicates.js), `LOOP_META` (context.js), the action vocabulary (`ACTION_USE`, actions.js) — and from the shared `*_EXAMPLE` constants the error hints use, so it cannot drift from what compiles (drift-guarded in `tests/catalog.test.js`, which also compiles every example).
|
|
210
|
+
- **`llms.txt` cheatsheet** — `llmsText()` renders a compact (~90-line) markdown cheatsheet from the same catalog; `darkmown catalog` prints the JSON and `darkmown catalog --llms` the cheatsheet, and every build writes `dist/llms.txt`. This is the artifact an app pastes into a small model's system prompt.
|
|
211
|
+
- **Generated GBNF grammar** — `src/grammar.js` (`generateGrammar()`, wrapped by `scripts/gen-grammar.mjs`) emits `grammar/wd-directives.gbnf`, a llama.cpp grammar for `.wd` **directive lines** (plus the `{ path | pipe }` interpolation token). It constrains a line's *shape* — the leading keyword must be a real directive, `@loop` must be `@loop <src> into <item> …clauses`, a condition must be `operand <op> operand`, a `:button` must be `"…" -> <action-chain>` — which deletes the on-device failure class (JS/HTML muscle-memory openers become unrepresentable) while staying small; free-form values remain `text`. Well-formedness + catalog coverage are tested in `tests/grammar.test.js`, and the committed file is kept in sync by the same test.
|
|
212
|
+
- **Structured compile errors** — errors carry a machine-readable mirror on `err.wd = { file, line?, hint?, example? }` (helper `wdError` in context.js) alongside the unchanged string message. Applied across loops, includes, predicates, forms, state, actions, media, fetch, and structure. `example` is always a compilable line.
|
|
213
|
+
- **Concrete examples in error hints** — every corrective `Use:` hint that contained bracket-placeholders now ends with ` — e.g. <concrete valid line>`, drawn from the same `*_EXAMPLE` constants. `tests/error-examples.test.js` extracts each `e.g.` example from the *real* thrown error string and compiles it (enforcement, not convention). Motivation: the on-device model bench (`bench/on-device/RESULTS.md`) found small models echo bracket-placeholders into source verbatim.
|
|
214
|
+
|
|
207
215
|
## Demo-only directives (not part of the public surface)
|
|
208
216
|
|
|
209
217
|
The compiler recognizes three directives — `:note`, `:try`, and `:sprint` — that exist **only to power the demo site** (darkmown.com). They are **not** part of the supported public directive set, are not documented for authors, and may change or be removed without notice. Do not build pages that depend on them. The supported public directives are the ones catalogued above: `:state`, `:store`, `:computed`, `:effect`, `:every`, `:theme`, `:button`, `:bind`, `:if`/`:else if`/`:else`/`:endif`, `:fetch` (with `headers`/`refresh`), `:form`/`:input`/`:submit`, `:slider`, `:carousel`, `:video`/`:audio`/`:embed`, `@loop`/`@empty`/`@endloop` (with `where`, `sort by` — including reactive `{ state }` keys —, `reverse`, `offset`, `limit`, `sortable`), `@include`, and `::: section` (with reactive `.class when`). Interpolation supports **format pipes** (`{ value | name:arg }`) and `:computed`/list-pipe **aggregates** (`sum`/`avg`/`min`/`max`/`count`). The runtime escape hatch is `window.wd` (`get`/`set`/`subscribe`/`state`/`render`).
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Darkmown .wd directive-line grammar — generated by src/grammar.js
|
|
2
|
+
# Constrains a directive line (or a { path | pipe } interpolation). Do not edit by hand.
|
|
3
|
+
|
|
4
|
+
root ::= directive | binding
|
|
5
|
+
directive ::= dir-include | dir-loop | dir-container | dir-if | dir-state | dir-store | dir-computed | dir-fetch | dir-effect | dir-every | dir-theme | dir-button | dir-form | dir-input | dir-textarea | dir-select | dir-checkbox | dir-radio | dir-submit | dir-bind | dir-slider | dir-video | dir-audio | dir-embed | dir-carousel
|
|
6
|
+
|
|
7
|
+
dir-loop ::= "@loop" sp operand sp "into" sp ident loop-clauses
|
|
8
|
+
dir-if ::= ":if" sp ( condition (sp joiner sp condition)* | path )
|
|
9
|
+
dir-button ::= ":button" sp dqstring sp "->" sp action-chain
|
|
10
|
+
dir-container ::= ":::" restofline
|
|
11
|
+
|
|
12
|
+
dir-include ::= "@include" restofline
|
|
13
|
+
dir-state ::= ":state" restofline
|
|
14
|
+
dir-store ::= ":store" restofline
|
|
15
|
+
dir-computed ::= ":computed" restofline
|
|
16
|
+
dir-fetch ::= ":fetch" restofline
|
|
17
|
+
dir-effect ::= ":effect" restofline
|
|
18
|
+
dir-every ::= ":every" restofline
|
|
19
|
+
dir-theme ::= ":theme" restofline
|
|
20
|
+
dir-form ::= ":form" restofline
|
|
21
|
+
dir-input ::= ":input" restofline
|
|
22
|
+
dir-textarea ::= ":textarea" restofline
|
|
23
|
+
dir-select ::= ":select" restofline
|
|
24
|
+
dir-checkbox ::= ":checkbox" restofline
|
|
25
|
+
dir-radio ::= ":radio" restofline
|
|
26
|
+
dir-submit ::= ":submit" restofline
|
|
27
|
+
dir-bind ::= ":bind" restofline
|
|
28
|
+
dir-slider ::= ":slider" restofline
|
|
29
|
+
dir-video ::= ":video" restofline
|
|
30
|
+
dir-audio ::= ":audio" restofline
|
|
31
|
+
dir-embed ::= ":embed" restofline
|
|
32
|
+
dir-carousel ::= ":carousel" restofline
|
|
33
|
+
|
|
34
|
+
loop-clauses ::= ( sp loop-clause )*
|
|
35
|
+
loop-clause ::= where-clause | "sort by" sp path ( sp ( "asc" | "desc" ) )? | "reverse" | "offset" sp number | "limit" sp number | "paginate" sp number | "sortable"
|
|
36
|
+
where-clause ::= "where" sp condition ( sp joiner sp condition )*
|
|
37
|
+
condition ::= operand sp op sp operand
|
|
38
|
+
op ::= "contains" | "==" | "!=" | ">=" | "<=" | ">" | "<"
|
|
39
|
+
joiner ::= "and" | "or"
|
|
40
|
+
|
|
41
|
+
action-chain ::= action ( wsopt ";" wsopt action )*
|
|
42
|
+
action ::= ident action-op
|
|
43
|
+
action-op ::= "++" | "--" | sp "+=" sp value | sp "-=" sp value | sp "=" sp value | sp "toggle" ( sp value )? | sp "append" sp value | sp "prepend" sp value | sp "remove" sp value | sp "clear" | sp "merge" sp ident | sp "delete" sp value | sp "reset" | sp "refetch"
|
|
44
|
+
|
|
45
|
+
binding ::= "{" wsopt path pipe-chain wsopt "}"
|
|
46
|
+
pipe-chain ::= ( wsopt "|" wsopt pipe ( ":" value )* )*
|
|
47
|
+
pipe ::= "money" | "number" | "percent" | "round" | "date" | "time" | "datetime" | "upper" | "lower" | "capitalize" | "truncate" | "trim" | "pluralize" | "default" | "sum" | "avg" | "min" | "max" | "count" | "join"
|
|
48
|
+
|
|
49
|
+
operand ::= path | dqstring | number
|
|
50
|
+
value ::= dqstring | number | path
|
|
51
|
+
ident ::= [a-zA-Z_$] [a-zA-Z0-9_$]*
|
|
52
|
+
path ::= [a-zA-Z0-9_$/.~-]+
|
|
53
|
+
number ::= [0-9]+
|
|
54
|
+
dqstring ::= "\"" [^"]* "\""
|
|
55
|
+
restofline ::= [^\n]*
|
|
56
|
+
sp ::= " "+
|
|
57
|
+
wsopt ::= " "*
|
|
58
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zvndev/darkmown",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"main": "./src/index.js",
|
|
14
14
|
"exports": {
|
|
15
15
|
".": "./src/index.js",
|
|
16
|
+
"./catalog": "./src/catalog.js",
|
|
16
17
|
"./package.json": "./package.json"
|
|
17
18
|
},
|
|
18
19
|
"types": "./types/index.d.ts",
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
"files": [
|
|
46
47
|
"src",
|
|
47
48
|
"types",
|
|
49
|
+
"grammar",
|
|
48
50
|
"site",
|
|
49
51
|
"!site/_/og*.png",
|
|
50
52
|
"docs/cli.md",
|
package/site/pages/docs/index.wd
CHANGED
|
@@ -604,6 +604,20 @@ See [SECURITY.md](https://github.com/ZVN-DEV/darkmown/blob/master/SECURITY.md) i
|
|
|
604
604
|
- Both work for included fragments too, by basename.
|
|
605
605
|
- Any other file under `site/pages/` (an image, font, PDF — anything that isn't a `.md`/`.wd` route or a basename-matched `.skin`/`.js`) copies to `dist/` at its own path with the right content-type. So `site/pages/logo.svg` is served at `/logo.svg`. Assets below hidden path segments (`.`, `-`, or `_`) and symlinked page assets are skipped, matching the routing privacy rule. Use the `site/_/` shelf for assets shared across pages; colocate the ones a single page owns.
|
|
606
606
|
|
|
607
|
+
## AI authoring
|
|
608
|
+
|
|
609
|
+
Darkmown ships a machine-readable description of its own language, so an AI tool can learn `.wd` and be constrained to output that compiles. It is all generated from the compiler's own tables, so it never drifts from the real grammar.
|
|
610
|
+
|
|
611
|
+
- **`darkmown catalog`** prints structured JSON: every directive, `@loop` clause, loop variable, button action, format pipe, and predicate operator — each with a syntax template, a one-line description, one concrete example, and whether it needs the reactive runtime. Import it too: `import { directiveCatalog } from "@zvndev/darkmown/catalog"`.
|
|
612
|
+
- **`darkmown catalog --llms`** prints a compact (~90-line) cheatsheet — the artifact you paste into a model's system prompt. Every build also writes it to `dist/llms.txt`.
|
|
613
|
+
- **`grammar/wd-directives.gbnf`** is a generated GBNF grammar for `.wd` directive lines. Point a grammar-constrained decoder (llama.cpp and friends) at it to make invalid directive lines — JS idioms, HTML muscle-memory — impossible to generate.
|
|
614
|
+
|
|
615
|
+
```sh
|
|
616
|
+
darkmown catalog --llms > system-prompt.md
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
Compile errors help here too: every corrective `Use:` hint ends with a concrete `— e.g. <valid line>`, and each thrown error carries a structured `err.wd = { file, line, hint, example }` mirror alongside its message — so an edit-and-retry loop gets the fix without parsing prose.
|
|
620
|
+
|
|
607
621
|
## CLI reference
|
|
608
622
|
|
|
609
623
|
The `darkmown` command (installed by `npx @zvndev/darkmown`) is the whole tool — every command is one word:
|
|
@@ -612,6 +626,7 @@ The `darkmown` command (installed by `npx @zvndev/darkmown`) is the whole tool
|
|
|
612
626
|
- `darkmown dev` — the live compiler: browser reload, an in-browser error overlay, incremental rebuilds, and a local runner for `api/*` functions.
|
|
613
627
|
- `darkmown build [--target cloudflare] [--drafts]` — write 100% static output to `dist/` (plus `sitemap.xml` / `rss.xml` / `robots.txt`); `--drafts` includes `draft: true` pages for a staging deploy.
|
|
614
628
|
- `darkmown serve` — preview the built `dist/` locally (static only; use `dev` to exercise `api/*`).
|
|
629
|
+
- `darkmown catalog [--llms]` — print the `.wd` directive catalog as JSON, or with `--llms` a compact cheatsheet for priming an AI model (see [AI authoring](#ai-authoring)).
|
|
615
630
|
- `darkmown deploy <vercel|cloudflare> [--prod]` — build (target-aware) and deploy through the platform CLI, printing your URL; an unauthenticated CLI surfaces the login to run first.
|
|
616
631
|
- `darkmown version` / `darkmown help` — print the installed version, or the full command summary and directive syntax.
|
|
617
632
|
|
package/src/builder.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { discoverApiRoutes } from "./api-runner.js";
|
|
5
|
+
import { llmsText } from "./catalog.js";
|
|
5
6
|
import { buildCollections } from "./compiler/collections.js";
|
|
6
7
|
import { compilePage, parseFrontmatter } from "./compiler.js";
|
|
7
8
|
import { createPaths, routesDir, shelfDir } from "./config.js";
|
|
@@ -364,6 +365,10 @@ function buildRoute(route, cwd, paths, collections, feedLink, warned) {
|
|
|
364
365
|
function emitGlobalFiles(manifest, paths) {
|
|
365
366
|
fs.writeFileSync(path.join(paths.distRoot, "routes.json"), JSON.stringify(manifest, null, 2));
|
|
366
367
|
fs.writeFileSync(path.join(paths.distRoot, "_headers"), renderCloudflareHeaders(manifest));
|
|
368
|
+
// `llms.txt` is the compact `.wd` cheatsheet an AI authoring tool fetches to
|
|
369
|
+
// prime a small model. It depends only on the framework version, so it re-emits
|
|
370
|
+
// identically on every build — cheap, and always present next to the site.
|
|
371
|
+
fs.writeFileSync(path.join(paths.distRoot, "llms.txt"), llmsText());
|
|
367
372
|
}
|
|
368
373
|
|
|
369
374
|
/**
|