chore(skill): Own tttk-authoring in the repo
Move the skill to .agents/skills/tttk-authoring and point gen:skill-reference at it. Activate it by symlinking ~/.agents/skills/tttk-authoring to the in-repo copy. Extend the reference drift guard to cover the skill copy now that it is versioned, and document activation in docs/development.md.
This commit is contained in:
@@ -0,0 +1,138 @@
|
|||||||
|
---
|
||||||
|
name: tttk-authoring
|
||||||
|
description: Author and edit TTRPG game design documents for the tttk (Tabletop Toolkit) CLI. Use when writing or modifying markdown content that uses tttk custom elements (:md-dice, :md-table, :md-deck, :md-pins, :md-commander, etc.), custom fenced code blocks (role=spark-table / declare / file / tag), companion CSV data files, or when previewing content with `tttk serve` and validating it with `tttk check`.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Authoring tttk game design documents
|
||||||
|
|
||||||
|
`tttk` (Tabletop Toolkit) renders a folder of markdown into an interactive TTRPG
|
||||||
|
web app. A game design document is **ordinary GFM markdown** plus exactly two
|
||||||
|
extensions:
|
||||||
|
|
||||||
|
1. **Custom elements** written with `marked-directive` syntax: `:name[content]{attrs}`.
|
||||||
|
2. **Custom fenced code blocks** whose info string carries a `role=` attribute.
|
||||||
|
|
||||||
|
Everything else — headings, lists, tables, blockquotes, alerts, mermaid — is
|
||||||
|
standard markdown. Do not invent element names, props, or roles; check
|
||||||
|
`reference.md` when unsure. Deeper topics have their own files: `yaml-tag.md`
|
||||||
|
(deck config), `csv.md` (data files), `layout.md` (columns), and `markdown.md`
|
||||||
|
(alerts and mermaid).
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
- Content lives in a directory (commonly `content/`). Preview with hot reload:
|
||||||
|
`npx tttk serve ./content` (default port 3000, override with `-p 8080`).
|
||||||
|
- **Validate after every edit:** `npx tttk check ./content`. It exits `1` when
|
||||||
|
there are errors and prints `file:line` diagnostics. Use `--json` for
|
||||||
|
machine-readable output and `--quiet` for errors only. This is the primary
|
||||||
|
verification loop: write → check → fix.
|
||||||
|
- The scanner picks up `.md`, `.csv`, `.yarn`, and `.svg` files.
|
||||||
|
- All paths in directives are **relative to the current markdown file**
|
||||||
|
(`./data.csv`, `../shared/npcs.csv`, `./images/map.png`).
|
||||||
|
- A typical game/adventure folder:
|
||||||
|
```
|
||||||
|
my-adventure/
|
||||||
|
├── design.md # design notes (plain markdown)
|
||||||
|
├── adventure.md # the document players/GM read
|
||||||
|
├── locations.csv # data referenced by :md-table
|
||||||
|
├── cast.csv
|
||||||
|
└── route.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
## Directive syntax
|
||||||
|
|
||||||
|
```
|
||||||
|
:name[content]{key="value" .class #id}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `[content]` is the directive body — a path, dice formula, color, etc.
|
||||||
|
- `{key="value"}` sets attributes. `{.class}` and `{#id}` are shorthands.
|
||||||
|
- Container directives use `::: name` … `:::`; nest deeper with `::::`, `:::::`.
|
||||||
|
|
||||||
|
Use the **registered element names**: `:md-dice`, not `:dice`. The full list is
|
||||||
|
in `reference.md`.
|
||||||
|
|
||||||
|
## Code block roles
|
||||||
|
|
||||||
|
Fenced blocks are dispatched by the `role=` attribute in the info string:
|
||||||
|
|
||||||
|
| Info string | Behavior |
|
||||||
|
|---|---|
|
||||||
|
| ` ```csv role=spark-table ` | Body is CSV; replaced with `:md-table[./csv_<hash>]`. First column header must be a dice formula (`d6`, `2d6+1`, …). |
|
||||||
|
| ` ```markdown role=spark-table ` | Body is a GFM pipe table; converted to CSV, then same as above. |
|
||||||
|
| ` ```csv role=declare ` | Defines reactive variables / tag modifiers. Block is stripped. |
|
||||||
|
| ` ```<lang> role=file ` | Body stored as text content, block stripped, referenced by its derived id. |
|
||||||
|
| ` ```yaml role=tag ` | Kept intact; rendered as a custom element from YAML (`tag:`, `body:`, …). |
|
||||||
|
| ` ```<lang> ` (no role) | Rendered as a normal visible code block. |
|
||||||
|
| unknown role | Warned about and stripped. |
|
||||||
|
|
||||||
|
## Rules that prevent broken output
|
||||||
|
|
||||||
|
- Quote any attribute value containing spaces or `"`:
|
||||||
|
`{pins="A:30,40 B:10,30"}`.
|
||||||
|
- In CSV, wrap fields containing `,`, `"`, or a newline in double quotes and
|
||||||
|
escape `"` as `""`. Lines starting with `#` are comments.
|
||||||
|
- `label` and `body` are the conventional CSV columns. Put titles inside `body`
|
||||||
|
as markdown headings; use **bold labels** for sub-fields, not headings.
|
||||||
|
- A `group` column in a table CSV automatically produces group tabs — no
|
||||||
|
attribute needed.
|
||||||
|
- Reference each CSV once per document; don't repeat the same `:md-table`.
|
||||||
|
- For card decks, use the `yaml role=tag` form with `tag: md-deck` (see
|
||||||
|
`yaml-tag.md`). The `:md-deck[...]{...}` directive is legacy and can't express
|
||||||
|
template layers or card backs.
|
||||||
|
- Keep CSV files clean — header row plus data. Put deck config and templates in
|
||||||
|
the YAML block's `data-config`, not in CSV front matter (see `csv.md`).
|
||||||
|
- `{{prop}}` in a CSV `body` interpolates another column (or front-matter key)
|
||||||
|
from the same row; with `{remix}` it pulls from a random row instead.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
`npx tttk check ./content` validates against the same content registry the app
|
||||||
|
uses, so a clean check means the app resolves the content too. Errors exit `1`:
|
||||||
|
|
||||||
|
| Code | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| `TTTK-S002` | container directive `:::` never closed |
|
||||||
|
| `TTTK-D001` | unknown `:md-*` element name |
|
||||||
|
| `TTTK-D002` | malformed attribute block (e.g. an unquoted value with a space) |
|
||||||
|
| `TTTK-R001` | unknown code-block `role=` (the block is silently dropped) |
|
||||||
|
| `TTTK-R003` | `role=declare` failed to parse |
|
||||||
|
| `TTTK-R005` | `role=tag` YAML is invalid |
|
||||||
|
| `TTTK-R006` | `role=tag` missing or naming an unknown element |
|
||||||
|
| `TTTK-C001` | content reference does not resolve |
|
||||||
|
| `TTTK-C004` | `:md-embed` cycle |
|
||||||
|
| `TTTK-L001` | link target file does not exist |
|
||||||
|
| `TTTK-V001` | CSV failed to parse (e.g. an unclosed quote) |
|
||||||
|
|
||||||
|
Warnings are usually real too:
|
||||||
|
|
||||||
|
| Code | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| `TTTK-R002` | spark-table first header is not a dice formula |
|
||||||
|
| `TTTK-R004` | `role=declare` row skipped (empty `key` or `expr`) |
|
||||||
|
| `TTTK-C002` | reference resolves to an unexpected file type |
|
||||||
|
| `TTTK-L002` | `#section` matches no heading |
|
||||||
|
| `TTTK-A001` | icon missing under `assets/` |
|
||||||
|
| `TTTK-A002` | image missing |
|
||||||
|
| `TTTK-V002` | CSV row column count differs from the header |
|
||||||
|
| `TTTK-V003` | duplicate `label` values |
|
||||||
|
| `TTTK-V004` | `{{prop}}` is not a CSV column or front-matter key |
|
||||||
|
| `TTTK-V005` | spark-table roll value is not a number or range |
|
||||||
|
|
||||||
|
`TTTK-C003` (info) flags the same CSV referenced twice by `:md-table` in one
|
||||||
|
document. Icon checks assume no `assets/` directory exists above the checked
|
||||||
|
directory. The full code catalog lives in `reference.md`.
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
- `reference.md` — **generated** catalog of every custom element, inline
|
||||||
|
directive, code block role, and dice notation. Do not edit by hand;
|
||||||
|
regenerate from the repo with `npm run gen:skill-reference`.
|
||||||
|
- `yaml-tag.md` — `role=tag` blocks, `data-config` layers, card backs, and the
|
||||||
|
legacy `:md-deck` layer syntax.
|
||||||
|
- `csv.md` — CSV authoring: columns, `{{prop}}` interpolation, quoting, and the
|
||||||
|
discouraged front-matter form.
|
||||||
|
- `layout.md` — column layout markers.
|
||||||
|
- `markdown.md` — GitHub alerts and mermaid diagrams.
|
||||||
|
- `examples/adventure.md` and `examples/encounters.csv` — a complete minimal
|
||||||
|
document and its data file.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# CSV data files
|
||||||
|
|
||||||
|
**Keep CSV files clean** — a header row and data rows, nothing else. Put deck
|
||||||
|
configuration and templates in the `yaml role=tag` block's `data-config`, not in
|
||||||
|
the CSV. This keeps data portable and presentation in one place. See
|
||||||
|
`reference.md` for the element and role catalog.
|
||||||
|
|
||||||
|
```csv
|
||||||
|
label,body,name,type
|
||||||
|
1,"### 火球术卷轴
|
||||||
|
|
||||||
|
一张泛黄的羊皮纸。",火球术卷轴,消耗品
|
||||||
|
```
|
||||||
|
|
||||||
|
- `label` — unique row id (number or text).
|
||||||
|
- `body` — main content, supports markdown.
|
||||||
|
- Any other columns are custom fields.
|
||||||
|
- `{{prop}}` in `body` interpolates another column from the same row (or a
|
||||||
|
random row when the table uses `{remix}`).
|
||||||
|
- Wrap values containing `,`, `"`, or newlines in double quotes; escape `"` as
|
||||||
|
`""`. Lines beginning with `#` are comments.
|
||||||
|
|
||||||
|
## Front matter (avoid)
|
||||||
|
|
||||||
|
The parser also accepts YAML front matter delimited by `---`, which can declare
|
||||||
|
`fields`, a `deck:` block, and arbitrary keys merged into every row. **Prefer
|
||||||
|
not to use it** — it mixes presentation into the data file. Use the YAML tag
|
||||||
|
block instead.
|
||||||
|
|
||||||
|
```csv
|
||||||
|
---
|
||||||
|
fields:
|
||||||
|
- name: name
|
||||||
|
description: 卡牌名称
|
||||||
|
deck:
|
||||||
|
size: 63x88
|
||||||
|
grid: 5x5
|
||||||
|
game_system: D&D 5e
|
||||||
|
---
|
||||||
|
label,body,name,type
|
||||||
|
1,"### 火球术卷轴",火球术卷轴,消耗品
|
||||||
|
```
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# The Hollow Mill
|
||||||
|
|
||||||
|
## Adventure brief
|
||||||
|
|
||||||
|
The mill on the village edge has stood silent for a year. Last week its wheel
|
||||||
|
began turning again — on its own.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> The mill's guardian is not hostile by default. Attacking it forfeits the
|
||||||
|
> peaceful resolution.
|
||||||
|
|
||||||
|
## Locations
|
||||||
|
|
||||||
|
:md-table[./locations.csv]
|
||||||
|
|
||||||
|
## Random encounters
|
||||||
|
|
||||||
|
:md-table[./encounters.csv]{roll=true}
|
||||||
|
|
||||||
|
## The mill guardian
|
||||||
|
|
||||||
|
:md-dice[1d20+3]{key="guardian-attack"}
|
||||||
|
|
||||||
|
The guardian strikes with :[sword] for :md-dice[2d6+2]{key="guardian-damage"}.
|
||||||
|
|
||||||
|
## Encounter table (inline)
|
||||||
|
|
||||||
|
```csv role=spark-table roll=true
|
||||||
|
d6,Encounter
|
||||||
|
1,A crow watches from the rafters.
|
||||||
|
2,The floorboards groan underfoot.
|
||||||
|
3,A cold draft carries the smell of grain.
|
||||||
|
4,The wheel outside stops for one breath.
|
||||||
|
5,A sack of flour splits open.
|
||||||
|
6,The guardian's lantern flickers to life.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Route
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[Village] --> B[Mill Road]
|
||||||
|
B --> C{Mill Door}
|
||||||
|
C -->|Force| D[Ground Floor]
|
||||||
|
C -->|Listen| E[Secret Cellar]
|
||||||
|
D --> F[Wheel Room]
|
||||||
|
E --> F
|
||||||
|
```
|
||||||
|
|
||||||
|
## Guardian card
|
||||||
|
|
||||||
|
```yaml role=tag
|
||||||
|
tag: md-deck
|
||||||
|
body: ./guardian.csv
|
||||||
|
data-config:
|
||||||
|
size: 63x88
|
||||||
|
grid: 5x5
|
||||||
|
layers:
|
||||||
|
- prop: name
|
||||||
|
pos: 1,1-5,1
|
||||||
|
font: 12
|
||||||
|
- template: |
|
||||||
|
**{{type}}** — {{hp}} HP
|
||||||
|
{{description}}
|
||||||
|
pos: 1,3-5,8
|
||||||
|
font: 3
|
||||||
|
align: l
|
||||||
|
```
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
group,label,body
|
||||||
|
Mill Road,1,"### A crow on the fence
|
||||||
|
|
||||||
|
A crow fixes you with one eye, then flies toward the mill.
|
||||||
|
|
||||||
|
**Interaction**: Follow it (Perception 3/1)
|
||||||
|
|
||||||
|
**Risk**: None"
|
||||||
|
Mill Road,2,"### Broken cart
|
||||||
|
|
||||||
|
An overturned cart blocks half the road.
|
||||||
|
|
||||||
|
**Interaction**: Clear it (Strength 3/2)
|
||||||
|
|
||||||
|
**Risk**: Failure costs 1 Stamina"
|
||||||
|
Mill Road,3,"### Cold spot
|
||||||
|
|
||||||
|
The air turns suddenly cold.
|
||||||
|
|
||||||
|
**Interaction**: Press on (Constitution 3/1)
|
||||||
|
|
||||||
|
**Risk**: Failure deals 1 Constitution damage"
|
||||||
|
Wheel Room,1,"### The turning wheel
|
||||||
|
|
||||||
|
The wheel grinds without water.
|
||||||
|
|
||||||
|
**Interaction**: Jam the gears (Strength 3/3)
|
||||||
|
|
||||||
|
**Risk**: Failure deals 1 Agility damage"
|
||||||
|
Wheel Room,2,"### Flour dust
|
||||||
|
|
||||||
|
Thick dust fills the air.
|
||||||
|
|
||||||
|
**Interaction**: Cover your mouth (Constitution 3/1)
|
||||||
|
|
||||||
|
**Risk**: Failure deals 1 Constitution damage"
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
label,name,type,hp,description
|
||||||
|
1,The Hollow Miller,Spirit,10,"Bound to the wheel by an old promise. Speaks only in the sound of grinding stone."
|
||||||
|
@@ -0,0 +1,47 @@
|
|||||||
|
label,body
|
||||||
|
Village,"### Village
|
||||||
|
|
||||||
|
**Type**: Starting location
|
||||||
|
|
||||||
|
A quiet village at the edge of the marsh. The miller's daughter asks after her
|
||||||
|
father.
|
||||||
|
|
||||||
|
**Interaction**: Ask about the mill (Charisma 3/1)
|
||||||
|
|
||||||
|
**Supplies**: Rope, torches, one healing draught"
|
||||||
|
Mill Road,"### Mill Road
|
||||||
|
|
||||||
|
**Type**: Route
|
||||||
|
|
||||||
|
A muddy track winding between hedgerows toward the mill.
|
||||||
|
|
||||||
|
**Interaction**: Spot tracks (Perception 3/1)
|
||||||
|
|
||||||
|
**Encounters**: 3 random encounters"
|
||||||
|
Ground Floor,"### Ground Floor
|
||||||
|
|
||||||
|
**Type**: Hub
|
||||||
|
|
||||||
|
Sacks of grain, a cold hearth, and a ladder to the wheel room.
|
||||||
|
|
||||||
|
**Interaction**:
|
||||||
|
- Search the hearth (Perception 3/2) — find a brass key
|
||||||
|
- Climb the ladder (Agility 3/1)"
|
||||||
|
Secret Cellar,"### Secret Cellar
|
||||||
|
|
||||||
|
**Type**: Archive
|
||||||
|
|
||||||
|
A low cellar beneath the millstones. Ledgers line the walls.
|
||||||
|
|
||||||
|
**Interaction**: Read the ledgers (Intelligence 3/2) — learn the guardian's name
|
||||||
|
|
||||||
|
**Clue**: The last entry is dated one year ago, in the miller's hand"
|
||||||
|
Wheel Room,"### Wheel Room
|
||||||
|
|
||||||
|
**Type**: Finale
|
||||||
|
|
||||||
|
The great wheel turns in silence, though no water reaches it.
|
||||||
|
|
||||||
|
**Interaction**:
|
||||||
|
- Speak the guardian's name (Charisma 3/3) — peaceful resolution
|
||||||
|
- Break the wheel (Strength 3/4) — hostile resolution"
|
||||||
|
@@ -0,0 +1,20 @@
|
|||||||
|
# Column layout
|
||||||
|
|
||||||
|
Wrap content in columns using line markers. This is a tttk extension on top of
|
||||||
|
standard markdown; see `reference.md` for the element and role catalog.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
|---
|
||||||
|
Left column
|
||||||
|
-|-
|
||||||
|
Right column
|
||||||
|
--|
|
||||||
|
```
|
||||||
|
|
||||||
|
| Marker | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| `\|---` | Start the column container |
|
||||||
|
| `-\|-` | Divider between columns |
|
||||||
|
| `--\|` | End the container |
|
||||||
|
|
||||||
|
Extra dashes add a `-N` class suffix (e.g. `-|--` → `col-2`) for custom widths.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Alerts and mermaid
|
||||||
|
|
||||||
|
Standard markdown features that `tttk` renders. They are not tttk syntax — see
|
||||||
|
`reference.md` for the element and role catalog.
|
||||||
|
|
||||||
|
## GitHub-style alerts
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
> [!NOTE]
|
||||||
|
> 这是一条备注
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> 提示内容
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> 重要信息
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> 警告信息
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> 危险警告
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mermaid diagrams
|
||||||
|
|
||||||
|
Fenced `mermaid` blocks render as diagrams:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[开始] --> B{选择}
|
||||||
|
B -->|选项 1| C[结果 1]
|
||||||
|
B -->|选项 2| D[结果 2]
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
Supported: flowchart, sequenceDiagram, classDiagram, stateDiagram, erDiagram,
|
||||||
|
pie.
|
||||||
@@ -0,0 +1,365 @@
|
|||||||
|
# tttk syntax reference
|
||||||
|
|
||||||
|
Generated from `src/cli/validate/known.ts` by `npm run gen:reference`.
|
||||||
|
Do not edit by hand.
|
||||||
|
|
||||||
|
Complete catalog of the custom elements, inline directives, and code block
|
||||||
|
roles that `tttk` understands. Everything not listed here is standard GFM
|
||||||
|
markdown. Element names are prefixed `md-` and must match exactly.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
- [Directive syntax](#directive-syntax)
|
||||||
|
- [Custom elements](#custom-elements)
|
||||||
|
- [Inline directives](#inline-directives)
|
||||||
|
- [Code block roles](#code-block-roles)
|
||||||
|
- [Dice notation](#dice-notation)
|
||||||
|
- [CSV data files](#csv-data-files)
|
||||||
|
|
||||||
|
## Directive syntax
|
||||||
|
|
||||||
|
```text
|
||||||
|
:name[content]{key="value" .class #id}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Part | Meaning | Example |
|
||||||
|
|---|---|---|
|
||||||
|
| `:name` | Registered element name | `:md-dice` |
|
||||||
|
| `[content]` | Directive body | `[2d6+d8]` |
|
||||||
|
| `{attrs}` | Attributes | `{key="attack"}` |
|
||||||
|
| `{.class}` | Shorthand for `class` | `{.text-red-500}` |
|
||||||
|
| `{#id}` | Shorthand for `id` | `{#my-dice}` |
|
||||||
|
|
||||||
|
Container directives:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
::: container
|
||||||
|
Content here, including nested directives.
|
||||||
|
:dice[1d20]
|
||||||
|
:::
|
||||||
|
```
|
||||||
|
|
||||||
|
Nest with more colons (`::::`, `:::::`).
|
||||||
|
|
||||||
|
## Custom elements
|
||||||
|
|
||||||
|
| Directive | Body | Key attributes |
|
||||||
|
|---|---|---|
|
||||||
|
| `:md-dice` | dice formula | `key` |
|
||||||
|
| `:md-table` | CSV path or inline id | `roll`, `remix` |
|
||||||
|
| `:md-deck` | CSV path | `size`, `grid`, `layers` |
|
||||||
|
| `:md-link` | path, optional #section | — |
|
||||||
|
| `:md-embed` | path, optional #section | `headingBase` |
|
||||||
|
| `:md-bg` | image path or CSS color | `fit` |
|
||||||
|
| `:md-border` | color or border image | `width`, `slice` |
|
||||||
|
| `:md-font` | font family | `source`, `weight`, `color` |
|
||||||
|
| `:md-pins` | image path | `pins`, `fixed`, `labelStart` |
|
||||||
|
| `:md-commander` | command template CSV | `placeholder`, `class`, `height` |
|
||||||
|
| `:md-yarn-spinner` | Yarn Spinner file | `start` |
|
||||||
|
|
||||||
|
### `:md-dice`
|
||||||
|
|
||||||
|
Clickable dice roller; `key` stores the result in the URL.
|
||||||
|
|
||||||
|
Clicking the text rolls; clicking the 🎲 resets to the formula. With `key`, the result is stored in the URL as `?dice-<key>=<value>`.
|
||||||
|
|
||||||
|
- Body: dice formula
|
||||||
|
- Attributes: `key`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-dice[1d20+5]{key="attack"}
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-dice[2d6+3]
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-dice[3d6kh1]
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:md-table`
|
||||||
|
|
||||||
|
Tab-separated view of a CSV with optional group tabs.
|
||||||
|
|
||||||
|
`roll` adds a 🎲 random-tab button; `remix` makes `{{prop}}` references pull from a random row. A `group` column in the CSV produces group tabs automatically.
|
||||||
|
|
||||||
|
- Body: CSV path or inline id
|
||||||
|
- Attributes: `roll`, `remix`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-table[./npcs.csv]
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-table[./encounters.csv]{roll=true}
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-table[./quests.csv]{roll=true remix=true}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:md-deck`
|
||||||
|
|
||||||
|
Card layout from a CSV (legacy; prefer `yaml role=tag`).
|
||||||
|
|
||||||
|
Prefer the `yaml role=tag` form — it is the only form that supports template layers, per-layer alignment, and card backs. The directive form is legacy and only supports the compact `layers` string.
|
||||||
|
|
||||||
|
- Body: CSV path
|
||||||
|
- Attributes: `size`, `grid`, `layers`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-deck[./cards.csv]{size="63x88" grid="5x5" layers="title:1,1-5,1f8 body:1,5-5,8f3"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:md-link`
|
||||||
|
|
||||||
|
Clickable link that expands the target article inline.
|
||||||
|
|
||||||
|
- Body: path, optional #section
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-link[./rules.md]
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-link[./rules.md#combat]
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:md-embed`
|
||||||
|
|
||||||
|
Renders the target article or section inline.
|
||||||
|
|
||||||
|
`headingBase` shifts the heading levels of the embedded content.
|
||||||
|
|
||||||
|
- Body: path, optional #section
|
||||||
|
- Attributes: `headingBase`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-embed[./rules.md#combat]{headingBase=1}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:md-bg`
|
||||||
|
|
||||||
|
Sets the surrounding article card background.
|
||||||
|
|
||||||
|
`fit` is one of `cover` (default), `contain`, `fill`, `none`, `scale-down`.
|
||||||
|
|
||||||
|
- Body: image path or CSS color
|
||||||
|
- Attributes: `fit`
|
||||||
|
- Body may also be a CSS color (e.g. `#2a1a3a`, `crimson`).
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-bg[./images/dungeon-bg.jpg]{fit="cover"}
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-bg[#2a1a3a]
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:md-border`
|
||||||
|
|
||||||
|
Applies a border to the article card.
|
||||||
|
|
||||||
|
Body is a CSS color or a border-image path. Class flags select sides (`t b l r all`), style (`solid dashed dotted double groove ridge inset outset none`), and image repeat (`stretch repeat round space`).
|
||||||
|
|
||||||
|
- Body: color or border image
|
||||||
|
- Attributes: `width`, `slice`
|
||||||
|
- Body may also be a CSS color (e.g. `#2a1a3a`, `crimson`).
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-border[crimson]{.b}
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-border[./frame.png]{width="2mm" slice="10" .all.round}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:md-font`
|
||||||
|
|
||||||
|
Sets the article font.
|
||||||
|
|
||||||
|
`source` is `google`, `emfont`, or `local` (default).
|
||||||
|
|
||||||
|
- Body: font family
|
||||||
|
- Attributes: `source`, `weight`, `color`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-font[Noto Sans SC]{source="google" weight="700" color="#1a1a2e"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:md-pins`
|
||||||
|
|
||||||
|
Numbered markers on an image.
|
||||||
|
|
||||||
|
`pins` is a space-separated list of `label:x,y` (percent coordinates). `fixed` makes it read-only; otherwise click to add and click a pin to remove. `labelStart` sets the first label (letter or number).
|
||||||
|
|
||||||
|
- Body: image path
|
||||||
|
- Attributes: `pins`, `fixed`, `labelStart`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-pins[./images/battle-map.png]{pins="A:25,50 B:75,30 C:50,80" fixed}
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-pins[./images/dungeon.png]{labelStart="1"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:md-commander`
|
||||||
|
|
||||||
|
Interactive command history / tracker panel.
|
||||||
|
|
||||||
|
The body is a CSV of command templates (`command,parameter,label,description,insertedText`).
|
||||||
|
|
||||||
|
- Body: command template CSV
|
||||||
|
- Attributes: `placeholder`, `class`, `height`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-commander[./commands.csv]{placeholder="输入命令…" height="400px"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:md-yarn-spinner`
|
||||||
|
|
||||||
|
Runs a Yarn Spinner dialogue file.
|
||||||
|
|
||||||
|
`start` names the entry node (default `start`).
|
||||||
|
|
||||||
|
- Body: Yarn Spinner file
|
||||||
|
- Attributes: `start`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-yarn-spinner[./story.yarn]{start="start"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inline directives
|
||||||
|
|
||||||
|
### `:[name]`
|
||||||
|
|
||||||
|
Inserts an icon looked up under `assets/` (searching upward from the file).
|
||||||
|
|
||||||
|
Default extension is `.png`; `.svg`, `.gif`, `.jpg`, `.jpeg`, and `.webp` are also supported. `label` overlays text, `repeat` duplicates N times, and `join` places a tag name between repeats.
|
||||||
|
|
||||||
|
- Body: icon name
|
||||||
|
- Attributes: `label`, `repeat`, `join`, `class`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:[attack] :[shield.svg] :[potion]
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:[attack]{label=ATK repeat=3 join=br}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `:cmd[command]`
|
||||||
|
|
||||||
|
Renders a clickable span that dispatches a journal command.
|
||||||
|
|
||||||
|
- Body: journal command
|
||||||
|
- Attributes: `label`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:cmd[roll 1d20+5]{label=Roll initiative}
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:cmd[spark dungeon room-type]{label=Random room}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code block roles
|
||||||
|
|
||||||
|
The `role=` attribute on the info string determines how a fenced block is
|
||||||
|
handled.
|
||||||
|
|
||||||
|
### `role=spark-table`
|
||||||
|
|
||||||
|
CSV (or markdown pipe table) rendered as `:md-table`.
|
||||||
|
|
||||||
|
The first column header must be a dice formula; each row's first cell is the roll value (a number, or a range like `1-3`). Extra fence attributes are forwarded to the generated `:md-table`, e.g. ` ```csv role=spark-table roll=true `.
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```csv role=spark-table
|
||||||
|
d6,结果
|
||||||
|
1,遭遇强盗
|
||||||
|
2,平安无事
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```markdown role=spark-table
|
||||||
|
| d6 | 结果 |
|
||||||
|
|----|------|
|
||||||
|
| 1 | 遭遇强盗 |
|
||||||
|
| 2 | 平安无事 |
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
### `role=declare`
|
||||||
|
|
||||||
|
Reactive variable / tag-modifier definitions.
|
||||||
|
|
||||||
|
Columns (any order): `tag`, `threshold`, `key`, `expr`. A row with an empty `tag` declares a variable; a row with a `#tag` applies a modifier when that tag is active.
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```csv role=declare
|
||||||
|
tag,threshold,key,expr
|
||||||
|
,,$hp,$con*5+$mod_hp
|
||||||
|
#warrior,2,$mod_hp,20
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
### `role=file`
|
||||||
|
|
||||||
|
Stored as inline text content and stripped.
|
||||||
|
|
||||||
|
Referenced by the derived inline id, e.g. `:md-embed[./text_<hash>]`.
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```text role=file
|
||||||
|
Stored as inline content.
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
### `role=tag`
|
||||||
|
|
||||||
|
YAML rendered as a custom element.
|
||||||
|
|
||||||
|
`tag:` names the element, `body:` becomes its content, and any other key becomes an attribute. Fence-line attributes override the YAML body. See `yaml-tag.md`.
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```yaml role=tag
|
||||||
|
tag: md-deck
|
||||||
|
body: ./cards.csv
|
||||||
|
size: 63x88
|
||||||
|
grid: 5x5
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
### No role / unknown role
|
||||||
|
|
||||||
|
No role → a normal visible code block. Unknown role → warned about and
|
||||||
|
stripped.
|
||||||
|
|
||||||
|
## Dice notation
|
||||||
|
|
||||||
|
Used by `:md-dice`, spark-table headers, and the journal `/roll`:
|
||||||
|
|
||||||
|
| Notation | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| `2d6` | two six-sided dice |
|
||||||
|
| `1d20+5` | d20 plus modifier |
|
||||||
|
| `3d6kh1` | keep highest 1 |
|
||||||
|
| `3d6dl2-1` | drop lowest 2, minus 1 |
|
||||||
|
| `{d4, d8}` | choose one of the listed dice |
|
||||||
|
|
||||||
|
## CSV data files
|
||||||
|
|
||||||
|
Keep CSV files clean — a header row and data rows. Put deck configuration
|
||||||
|
and templates in a `yaml role=tag` block's `data-config`.
|
||||||
|
|
||||||
|
- `label` — unique row id (number or text).
|
||||||
|
- `body` — main content, supports markdown.
|
||||||
|
- Any other columns are custom fields.
|
||||||
|
- `{{prop}}` in `body` interpolates another column (or front-matter key)
|
||||||
|
from the same row; with `{remix}` it pulls from a random row.
|
||||||
|
- Wrap values containing `,`, `"`, or newlines in double quotes; escape
|
||||||
|
`"` as `""`. Lines beginning with `#` are comments.
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# YAML tag blocks
|
||||||
|
|
||||||
|
`` ```yaml role=tag `` renders a custom element from YAML. `tag` names the
|
||||||
|
element, `body` becomes its content, and any other key becomes an attribute.
|
||||||
|
Fence-line attributes override the YAML body. See `reference.md` for the
|
||||||
|
element and role catalog.
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```yaml role=tag
|
||||||
|
tag: md-deck
|
||||||
|
body: ./cards.csv
|
||||||
|
data-config:
|
||||||
|
size: 63x88
|
||||||
|
grid: 5x5
|
||||||
|
layers:
|
||||||
|
- prop: title
|
||||||
|
pos: 1,1-5,1
|
||||||
|
font: 12
|
||||||
|
- template: |
|
||||||
|
**{{name}}** — {{type}}
|
||||||
|
{{description}}
|
||||||
|
pos: 1,3-5,8
|
||||||
|
font: 3
|
||||||
|
align: l
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
Simple cases can stay on the fence line:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```yaml role=tag tag=md-dice body=3d6
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
## `data-config` layers
|
||||||
|
|
||||||
|
Layers accept:
|
||||||
|
|
||||||
|
- `prop` — read a CSV column, or `template` — markdown with `{{var}}`
|
||||||
|
substitution.
|
||||||
|
- `pos` — `x1,y1-x2,y2` grid span (1-based).
|
||||||
|
- `font` / `fontSize` — font size in mm.
|
||||||
|
- `orientation` — `n` / `s` / `e` / `w`.
|
||||||
|
- `align` — `l` / `c` / `r`, optionally prefixed `t` / `b`.
|
||||||
|
- `visible` — show or hide the layer.
|
||||||
|
|
||||||
|
Use `back_layers` for card backs.
|
||||||
|
|
||||||
|
## Legacy `:md-deck` directive
|
||||||
|
|
||||||
|
`:md-deck[./cards.csv]{...}` still works but is **legacy** — prefer the YAML tag
|
||||||
|
block above. It only supports the compact `layers` string and cannot express
|
||||||
|
template layers or backs.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
:md-deck[./cards.csv]{size="63x88" grid="5x5" layers="title:1,1-5,1f8 body:1,5-5,8f3"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Compact layer syntax: `prop:x1,y1-x2,y2[f<size>][nsew][[t|b]lcr]`
|
||||||
|
|
||||||
|
- `prop` — CSV column to render.
|
||||||
|
- `x1,y1-x2,y2` — grid span (1-based).
|
||||||
|
- `f<size>` — font size in mm, e.g. `f8`.
|
||||||
|
- `nsew` — text orientation (default `n`).
|
||||||
|
- `l` / `c` / `r` — horizontal align, optionally prefixed `t` / `b` for vertical
|
||||||
|
(`tl`, `bc`, …).
|
||||||
+2
-2
@@ -337,7 +337,7 @@ Exit code is the contract that makes `check` usable in an agent loop.
|
|||||||
11. **Reference generator** — `scripts/generate-reference.ts` renders the
|
11. **Reference generator** — `scripts/generate-reference.ts` renders the
|
||||||
`known.ts` catalog to markdown. Canonical output `docs/reference.md`
|
`known.ts` catalog to markdown. Canonical output `docs/reference.md`
|
||||||
(versioned, testable); `--out` can target the skill copy at
|
(versioned, testable); `--out` can target the skill copy at
|
||||||
`~/.agents/skills/tttk-authoring/reference.md`. Add `npm run
|
`.agents/skills/tttk-authoring/reference.md`. Add `npm run
|
||||||
gen:reference`.
|
gen:reference`.
|
||||||
12. **Drift guard** — a test that greps `customElement("md-*")` call sites
|
12. **Drift guard** — a test that greps `customElement("md-*")` call sites
|
||||||
under `src/components` and asserts each appears in `known.ts` (and vice
|
under `src/components` and asserts each appears in `known.ts` (and vice
|
||||||
@@ -363,7 +363,7 @@ Exit code is the contract that makes `check` usable in an agent loop.
|
|||||||
- Update `docs/cli.md`: replace the `compile` section with `check`; document
|
- Update `docs/cli.md`: replace the `compile` section with `check`; document
|
||||||
codes, `--json`, exit codes.
|
codes, `--json`, exit codes.
|
||||||
- Update `README.md`.
|
- Update `README.md`.
|
||||||
- Update `~/.agents/skills/tttk-authoring/SKILL.md`: the verification step
|
- Update `.agents/skills/tttk-authoring/SKILL.md`: the verification step
|
||||||
becomes **`npx tttk check ./content`**, and the workflow becomes
|
becomes **`npx tttk check ./content`**, and the workflow becomes
|
||||||
write → check → fix. Remove the "compile is a no-op stub" note. Cross-link
|
write → check → fix. Remove the "compile is a no-op stub" note. Cross-link
|
||||||
the code catalog (`reference.md`) with the diagnostic codes.
|
the code catalog (`reference.md`) with the diagnostic codes.
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ tttk/
|
|||||||
│ ├── styles.css # 样式
|
│ ├── styles.css # 样式
|
||||||
│ ├── index.html # HTML 模板
|
│ ├── index.html # HTML 模板
|
||||||
│ └── global.d.ts # 类型声明
|
│ └── global.d.ts # 类型声明
|
||||||
|
├── .agents/
|
||||||
|
│ └── skills/
|
||||||
|
│ └── tttk-authoring/ # 随项目维护的 agent skill(见下文)
|
||||||
├── bin/
|
├── bin/
|
||||||
│ └── cli/ # CLI 二进制文件
|
│ └── cli/ # CLI 二进制文件
|
||||||
├── content/ # 示例内容
|
├── content/ # 示例内容
|
||||||
@@ -71,6 +74,29 @@ tttk/
|
|||||||
└── jest.config.js
|
└── jest.config.js
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Agent skills
|
||||||
|
|
||||||
|
`tttk-authoring` skill 由本项目维护,源码位于
|
||||||
|
`.agents/skills/tttk-authoring/`。要让 agent 加载它,把全局 skill 目录
|
||||||
|
链接到仓库内的副本:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Windows(无需管理员权限的目录联接)
|
||||||
|
cmd //c mklink /J %USERPROFILE%\.agents\skills\tttk-authoring "%CD%\.agents\skills\tttk-authoring"
|
||||||
|
|
||||||
|
# macOS / Linux
|
||||||
|
ln -s "$PWD/.agents/skills/tttk-authoring" ~/.agents/skills/tttk-authoring
|
||||||
|
```
|
||||||
|
|
||||||
|
`reference.md` 由 `known.ts` 生成,不要手改:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run gen:skill-reference
|
||||||
|
```
|
||||||
|
|
||||||
|
其余文件(`SKILL.md`、`yaml-tag.md`、`csv.md`、`layout.md`、`markdown.md`)
|
||||||
|
为手写内容。
|
||||||
|
|
||||||
## 技术栈
|
## 技术栈
|
||||||
|
|
||||||
| 类别 | 技术 |
|
| 类别 | 技术 |
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ skill's `reference.md` is now generated and byte-identical to
|
|||||||
`docs/reference.md`, with the extra content moved to `layout.md`, `yaml-tag.md`,
|
`docs/reference.md`, with the extra content moved to `layout.md`, `yaml-tag.md`,
|
||||||
`csv.md`, and `markdown.md`.
|
`csv.md`, and `markdown.md`.
|
||||||
|
|
||||||
|
The skill is now **owned by the repo** at `.agents/skills/tttk-authoring/` and
|
||||||
|
activated by symlinking `~/.agents/skills/tttk-authoring` to it (see
|
||||||
|
`docs/development.md` § Agent skills).
|
||||||
|
|
||||||
## 1. Goal
|
## 1. Goal
|
||||||
|
|
||||||
Make the `tttk-authoring` skill's `reference.md` a **generated** file — the same
|
Make the `tttk-authoring` skill's `reference.md` a **generated** file — the same
|
||||||
@@ -19,10 +23,10 @@ hand-maintained element list, and no content lost in the swap.
|
|||||||
|
|
||||||
## 2. Current state
|
## 2. Current state
|
||||||
|
|
||||||
The skill (`~/.agents/skills/tttk-authoring/`) has a hand-written
|
The skill (originally `~/.agents/skills/tttk-authoring/`, now
|
||||||
`reference.md` (~465 lines) and `SKILL.md`. The repo now generates
|
`.agents/skills/tttk-authoring/`) had a hand-written `reference.md` (~465 lines)
|
||||||
`docs/reference.md` from `src/cli/validate/known.ts` via `npm run
|
and `SKILL.md`. The repo generates `docs/reference.md` from
|
||||||
gen:reference`.
|
`src/cli/validate/known.ts` via `npm run gen:reference`.
|
||||||
|
|
||||||
What the generated doc already covers:
|
What the generated doc already covers:
|
||||||
|
|
||||||
@@ -109,7 +113,7 @@ generator exists to prevent.
|
|||||||
`package.json`:
|
`package.json`:
|
||||||
|
|
||||||
- `gen:reference` — unchanged (writes `docs/reference.md`).
|
- `gen:reference` — unchanged (writes `docs/reference.md`).
|
||||||
- `gen:skill-reference` — `gen:reference -- --out ~/.agents/skills/tttk-authoring/reference.md`.
|
- `gen:skill-reference` — `gen:reference -- --out .agents/skills/tttk-authoring/reference.md`.
|
||||||
|
|
||||||
## 6. New skill file outlines
|
## 6. New skill file outlines
|
||||||
|
|
||||||
@@ -156,9 +160,10 @@ generator exists to prevent.
|
|||||||
checklist and a manual diff before deleting the old file.
|
checklist and a manual diff before deleting the old file.
|
||||||
- **`known.ts` bloat.** Keep `details` to one paragraph per element; long-form
|
- **`known.ts` bloat.** Keep `details` to one paragraph per element; long-form
|
||||||
material belongs in the companion files.
|
material belongs in the companion files.
|
||||||
- **Skill path portability.** `~` expansion in the generator; the skill lives
|
- **Skill path portability.** The skill now lives in the repo, so
|
||||||
outside the repo, so the drift guard cannot cover it — document that
|
`gen:skill-reference` writes to a stable path; `~` expansion in `--out`
|
||||||
`gen:skill-reference` is the refresh command.
|
remains for ad-hoc targets. Activation is a symlink, which is environment
|
||||||
|
setup rather than repo state — documented in `docs/development.md`.
|
||||||
- **Two references.** `docs/reference.md` and the skill copy are both generated
|
- **Two references.** `docs/reference.md` and the skill copy are both generated
|
||||||
from `known.ts`; they must be regenerated together. Note this in
|
from `known.ts`; they must be regenerated together. Note this in
|
||||||
`docs/check-plan.md` §9.
|
`docs/check-plan.md` §9.
|
||||||
+1
-1
@@ -30,7 +30,7 @@
|
|||||||
"ttrpg": "node --loader ts-node/esm ./src/cli/index.ts",
|
"ttrpg": "node --loader ts-node/esm ./src/cli/index.ts",
|
||||||
"tttk": "node --loader ts-node/esm ./src/cli/index.ts",
|
"tttk": "node --loader ts-node/esm ./src/cli/index.ts",
|
||||||
"gen:reference": "node --loader ts-node/esm ./scripts/generate-reference.ts",
|
"gen:reference": "node --loader ts-node/esm ./scripts/generate-reference.ts",
|
||||||
"gen:skill-reference": "node --loader ts-node/esm ./scripts/generate-reference.ts --out ~/.agents/skills/tttk-authoring/reference.md",
|
"gen:skill-reference": "node --loader ts-node/esm ./scripts/generate-reference.ts --out .agents/skills/tttk-authoring/reference.md",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"prepare": "npx husky",
|
"prepare": "npx husky",
|
||||||
"prepublishOnly": "npm run build"
|
"prepublishOnly": "npm run build"
|
||||||
|
|||||||
@@ -41,11 +41,11 @@ describe("known element catalog", () => {
|
|||||||
expect(known).toEqual(registered);
|
expect(known).toEqual(registered);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("docs/reference.md is up to date", () => {
|
test.each([
|
||||||
const committed = readFileSync(
|
["docs/reference.md"],
|
||||||
join(process.cwd(), "docs", "reference.md"),
|
[join(".agents", "skills", "tttk-authoring", "reference.md")],
|
||||||
"utf-8",
|
])("%s is up to date", (relativePath) => {
|
||||||
);
|
const committed = readFileSync(join(process.cwd(), relativePath), "utf-8");
|
||||||
|
|
||||||
expect(committed).toBe(renderReference());
|
expect(committed).toBe(renderReference());
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user