@rse/specbook 0.9.4 → 0.9.6
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/README.md +64 -319
- package/dst/specbook-api.d.ts +13 -20
- package/dst/specbook-api.js +28 -25
- package/dst/specbook-cli.js +34 -50
- package/dst/specbook-cmd-describe.d.ts +5 -2
- package/dst/specbook-cmd-describe.js +32 -91
- package/dst/specbook-cmd-export.d.ts +3 -3
- package/dst/specbook-cmd-export.js +4 -3
- package/dst/specbook-cmd-init.d.ts +2 -2
- package/dst/specbook-cmd-init.js +35 -13
- package/dst/specbook-cmd-lint.d.ts +6 -6
- package/dst/specbook-cmd-lint.js +36 -23
- package/dst/specbook-config.d.ts +2 -2
- package/dst/specbook-config.js +2 -2
- package/dst/specbook-diagram.d.ts +4 -4
- package/dst/specbook-diagram.js +21 -15
- package/dst/specbook-export-ast.d.ts +3 -3
- package/dst/specbook-export-common.d.ts +10 -9
- package/dst/specbook-export-common.js +23 -9
- package/dst/specbook-export-html.css +13 -3
- package/dst/specbook-export-html.d.ts +4 -4
- package/dst/specbook-export-html.js +61 -32
- package/dst/specbook-export-logo-dark.svg +35 -0
- package/dst/specbook-export-logo-light.svg +35 -0
- package/dst/specbook-export-md.d.ts +3 -3
- package/dst/{specbook-struct-schema.d.ts → specbook-format-schema.d.ts} +3 -2
- package/dst/{specbook-struct-schema.js → specbook-format-schema.js} +2 -1
- package/dst/specbook-format-spec.d.ts +31 -0
- package/dst/{specbook-struct-spec.js → specbook-format-spec.js} +10 -10
- package/dst/specbook-format.md +734 -0
- package/dst/specbook-link.d.ts +2 -2
- package/dst/specbook-mcp.d.ts +2 -1
- package/dst/specbook-mcp.js +13 -52
- package/dst/specbook-parse-common.d.ts +5 -3
- package/dst/specbook-parse-common.js +8 -0
- package/dst/specbook-parse-semantic.d.ts +4 -4
- package/dst/specbook-parse-semantic.js +4 -65
- package/dst/specbook-parse-syntax.d.ts +2 -2
- package/dst/specbook-parse-syntax.js +18 -14
- package/dst/specbook-parse-value.js +1 -1
- package/dst/specbook-parse.d.ts +4 -4
- package/dst/specbook-parse.js +3 -3
- package/dst/specbook-verbose.d.ts +2 -0
- package/dst/specbook-verbose.js +16 -0
- package/package.json +6 -11
- package/dst/specbook-cmd-edit.d.ts +0 -9
- package/dst/specbook-cmd-edit.js +0 -74
- package/dst/specbook-cmd-import.d.ts +0 -9
- package/dst/specbook-cmd-import.js +0 -78
- package/dst/specbook-export-logo.svg +0 -35
- package/dst/specbook-llm.d.ts +0 -13
- package/dst/specbook-llm.js +0 -51
- package/dst/specbook-struct-spec.d.ts +0 -31
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
<img src="https://raw.githubusercontent.com/rse/specbook/master/etc/specbook-logo.svg" width="400" align="right" alt=""/>
|
|
2
|
+
<img src="https://raw.githubusercontent.com/rse/specbook/master/etc/specbook-logo-blue-light.svg" width="400" align="right" alt=""/>
|
|
3
3
|
|
|
4
4
|
SpecBook
|
|
5
5
|
========
|
|
@@ -13,307 +13,44 @@ About
|
|
|
13
13
|
format* which can be configured for the specifications of particular
|
|
14
14
|
contexts through a YAML-based *schema configuration*. SpecBook allows
|
|
15
15
|
specifications to be *initialized*, *linted*, *exported* (JSON, JSON5,
|
|
16
|
-
YAML, TOON, HTML, PDF, and normalized Markdown), *described* to
|
|
17
|
-
|
|
18
|
-
methods `<cmd>()`, a CLI with commands `specbook <cmd>`, and an MCP
|
|
19
|
-
service with tools `specbook_<cmd>()`.
|
|
16
|
+
YAML, TOON, HTML, PDF, and normalized Markdown), and *described* to
|
|
17
|
+
LLMs.
|
|
20
18
|
|
|
21
|
-
**SpecBook**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- strict Markdown-based input format
|
|
25
|
-
- complex and concise object to Markdown mappings
|
|
26
|
-
- Wiki-style hierarchy-aware object hyperlinks
|
|
27
|
-
- canonical Markdown output format
|
|
28
|
-
- AST-based JSON, JSON5, YAML, and TOON output formats
|
|
29
|
-
- HTML output format for online reading
|
|
30
|
-
- PDF output format for printing and archiving
|
|
31
|
-
- CLI, MCP, and API interfaces
|
|
32
|
-
|
|
33
|
-
Specification Format
|
|
34
|
-
--------------------
|
|
35
|
-
|
|
36
|
-
### Artifact Files
|
|
37
|
-
|
|
38
|
-
A specification consists of Markdown *artifact* files, which are
|
|
39
|
-
scanned recursively from a base directory. Every file has to start with
|
|
40
|
-
a frontmatter block carrying the `Created:` and `Modified:` timestamps
|
|
41
|
-
(format `yyyy-LL-dd HH:mm`):
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
---
|
|
45
|
-
Created: 2026-06-18 10:18
|
|
46
|
-
Modified: 2026-06-18 10:18
|
|
47
|
-
---
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Every level 1 heading opens a new *artifact*, so usually each file
|
|
51
|
-
carries exactly one. Both ATX headings (`# <text/>`, `## <text/>`, ...)
|
|
52
|
-
and Setext headings (`<text/>` underlined with `===` or `---`) are
|
|
53
|
-
supported.
|
|
54
|
-
|
|
55
|
-
### Object Model
|
|
56
|
-
|
|
57
|
-
An artifact carries a tree of *objects*. Every object has a mandatory
|
|
58
|
-
*kind* (e.g. `ENTITY`), a mandatory *name*, a unique anchor *id*
|
|
59
|
-
(explicitly given or derived from the name), an optional *primary*
|
|
60
|
-
marker, optional *properties* (key/value pairs), an optional
|
|
61
|
-
*description* statement with an optional rationale, and optional *child*
|
|
62
|
-
objects. Which kinds, properties, and nestings are allowed is defined by
|
|
63
|
-
the schema configuration (see below); object kinds and property keys are
|
|
64
|
-
case-sensitive and have to be written exactly as configured. Backquotes
|
|
65
|
-
in names and property values are preserved as code markup for rendering,
|
|
66
|
-
but are ignored when matching names, references, and constraints.
|
|
67
|
-
|
|
68
|
-
Three concrete syntaxes exist:
|
|
69
|
-
|
|
70
|
-
### Complex Format
|
|
71
|
-
|
|
72
|
-
Usually used on object hierarchy levels 1-3 (`#`, `##`, `###`). A
|
|
73
|
-
heading opens the object, an optional unordered list carries its
|
|
74
|
-
properties, and the remaining content up to the next heading is its
|
|
75
|
-
description:
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
# <kind/>: <name/> (<id/>)
|
|
79
|
-
|
|
80
|
-
- <key/>: <value/>
|
|
81
|
-
- [...]
|
|
82
|
-
|
|
83
|
-
<statement/>, BECAUSE <rationale/>.
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
The heading level directly reflects the nesting level: a `##` object
|
|
87
|
-
becomes a child of the preceding `#` object, a `###` object a child of
|
|
88
|
-
the preceding `##` object, etc. Skipping a level (a heading without a
|
|
89
|
-
parent object on the level above) is an error.
|
|
90
|
-
|
|
91
|
-
### Concise Format
|
|
19
|
+
**SpecBook** ships with an API class with methods `<xxx>()`, a
|
|
20
|
+
CLI with commands `specbook <xxx>`, and an MCP service with tools
|
|
21
|
+
`specbook_<xxx>()`.
|
|
92
22
|
|
|
93
|
-
|
|
94
|
-
the entire object as `;`-separated segments -- the `<kind/>: <name/>`
|
|
95
|
-
head, the `<key/>: <value/>` properties, and the trailing description --
|
|
96
|
-
and may wrap over multiple (indented) lines:
|
|
97
|
-
|
|
98
|
-
```
|
|
99
|
-
- <kind/>: <name/>; <key/>: <value/>; [...];
|
|
100
|
-
<statement/>, BECAUSE <rationale/>.
|
|
101
|
-
```
|
|
23
|
+
**SpecBook** provides the following distinct features:
|
|
102
24
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
25
|
+
- Hierarchical Object Model
|
|
26
|
+
- Versatile Object to Markdown Mapping
|
|
27
|
+
- Wiki-Style Object Linking
|
|
28
|
+
- Strict Validation with Custom Schema
|
|
29
|
+
- Object Model Diagram Visualisation
|
|
30
|
+
- CLI, MCP, and API Interface
|
|
31
|
+
- AST Exports for AI/LLMs
|
|
32
|
+
- HTML Export for Developers
|
|
33
|
+
- PDF Export for Customers
|
|
110
34
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
<statement/>, BECAUSE <rationale/>.
|
|
114
|
-
- <kind/>: <name/>; <key/>: <value/>;
|
|
115
|
-
<statement/>, BECAUSE <rationale/>.
|
|
116
|
-
```
|
|
35
|
+
Overview Poster
|
|
36
|
+
---------------
|
|
117
37
|
|
|
118
|
-
|
|
38
|
+
[](etc/poster.pdf)
|
|
119
39
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
Concise Format items whose kind comes from that heading, so they start
|
|
123
|
-
with the (optionally backquoted) name directly:
|
|
40
|
+
Rendering Example
|
|
41
|
+
-----------------
|
|
124
42
|
|
|
125
|
-
|
|
126
|
-
### <kinds/>
|
|
43
|
+
### HTML Rendering (Light Theme)
|
|
127
44
|
|
|
128
|
-
-
|
|
129
|
-
<statement/>, BECAUSE <rationale/>.
|
|
130
|
-
```
|
|
45
|
+

|
|
131
46
|
|
|
132
|
-
|
|
133
|
-
`### STATES` and `### STATE` group objects of kind `STATE`. The grouped
|
|
134
|
-
objects become children of the object the heading is nested under. An
|
|
135
|
-
unquoted name must not contain a `:` (else the item would look like a
|
|
136
|
-
key/value pair) -- backquote it in this case.
|
|
137
|
-
|
|
138
|
-
When **SpecBook** itself generates specification Markdown, it emits
|
|
139
|
-
the Complex Format on levels 1-3 and the Concise Format from level 4
|
|
140
|
-
upwards. When **SpecBook** edits existing files, it mirrors the format
|
|
141
|
-
each existing object already uses.
|
|
142
|
-
|
|
143
|
-
### Names, Anchors, and Ids
|
|
144
|
-
|
|
145
|
-
In all formats, the `<name/>` may carry trailing decorations, in any
|
|
146
|
-
order:
|
|
147
|
-
|
|
148
|
-
- `{{<id/>}}`: the explicit Wiki-style anchor, setting the object id
|
|
149
|
-
(e.g. `## ENTITY: Attendee Browser {{attendee}}`).
|
|
150
|
-
|
|
151
|
-
- `(*)`: the *primary* marker, flagging the object as primary (e.g.
|
|
152
|
-
the primary attributes of an entity).
|
|
153
|
-
|
|
154
|
-
- `(<token/>)`: a parenthesized token with three possible roles: on
|
|
155
|
-
level 1 it becomes the artifact id (e.g. `# SPEC: Data Model (DM)`);
|
|
156
|
-
on deeper levels it is either accepted as the value of a still
|
|
157
|
-
missing configured property whose constraint it matches (e.g.
|
|
158
|
-
`### SCENARIO: Successful Token Login (Main)` satisfying an
|
|
159
|
-
`enum(Main,Alternative,Exceptional)` property), or else acts as the
|
|
160
|
-
implicit anchor id.
|
|
161
|
-
|
|
162
|
-
- `<a id="<id/>"></a>`: an HTML anchor at the very end of a heading,
|
|
163
|
-
as an alternative to `{{<id/>}}` (emitted by **SpecBook** itself
|
|
164
|
-
when exporting normalized Markdown).
|
|
165
|
-
|
|
166
|
-
When no explicit id is given, the id is derived by slugifying the name
|
|
167
|
-
(lowercased, with non-alphanumeric character runs dashed). An explicit
|
|
168
|
-
`{{<id/>}}` anchor always takes precedence over a `(<token/>)` id. When
|
|
169
|
-
the schema configures a fixed `id` for an object, it has to be
|
|
170
|
-
explicitly written in the input, via either `{{<id/>}}` or `(<id/>)`.
|
|
171
|
-
|
|
172
|
-
### Properties
|
|
173
|
-
|
|
174
|
-
In the Complex Format, each property is an unordered list item of the
|
|
175
|
-
form `<key/>: <value/>`, whose value may continue on the following
|
|
176
|
-
indented lines of the item (joined with spaces). In the Concise Format,
|
|
177
|
-
each property is a `<key/>: <value/>` segment. A property key may carry
|
|
178
|
-
a trailing parenthesized annotation (e.g. `WHEN (Context)`), which is
|
|
179
|
-
kept for rendering but ignored when matching the key against the schema.
|
|
180
|
-
|
|
181
|
-
A multi-token convenience exists for pattern-constrained properties:
|
|
182
|
-
when a property value fails its (regex or enum) constraint as a whole,
|
|
183
|
-
but consists of multiple whitespace-separated tokens of which one
|
|
184
|
-
matches the property's own constraint, that token is kept as the value
|
|
185
|
-
and the remaining tokens are distributed (case-insensitively) across the
|
|
186
|
-
other still unset pattern-constrained properties of the object. Tokens
|
|
187
|
-
assignable to no property are reported as errors.
|
|
188
|
-
|
|
189
|
-
### Descriptions and Rationales
|
|
190
|
-
|
|
191
|
-
The description of a Complex Format object is all block content below
|
|
192
|
-
its heading (up to the next heading) except the property list:
|
|
193
|
-
paragraphs, blockquotes, ordered lists (e.g. scenario steps), and
|
|
194
|
-
fenced code blocks. A fenced code block
|
|
195
|
-
of language `gradia` is skipped, as it is the derived diagram which
|
|
196
|
-
**SpecBook** itself emits into exported Markdown and which must not
|
|
197
|
-
become authored content on a re-parse. In the Concise Format, the
|
|
198
|
-
description is formed by the non-property segments of the item.
|
|
199
|
-
|
|
200
|
-
A description is split into its *statement* and *rationale* at the first
|
|
201
|
-
`, BECAUSE ` (or `, **BECAUSE** `) occurrence:
|
|
47
|
+
### HTML Rendering (Dark Theme)
|
|
202
48
|
|
|
203
|
-
|
|
204
|
-
The event is created and configured but not visible to attendees,
|
|
205
|
-
BECAUSE an event needs a private setup phase.
|
|
206
|
-
```
|
|
49
|
+

|
|
207
50
|
|
|
208
|
-
###
|
|
209
|
-
|
|
210
|
-
A `[[<reference/>]]` anywhere in a name, property value, description, or
|
|
211
|
-
rationale is a Wiki-style reference to another object, which is checked
|
|
212
|
-
for unique resolvability and rendered as a hyperlink in the HTML/PDF
|
|
213
|
-
exports. A reference is a `.`-separated path of segments, where each
|
|
214
|
-
segment matches a single object as:
|
|
215
|
-
|
|
216
|
-
- `<id/>`: the object id or explicit anchor (e.g. `[[attendee]]`)
|
|
217
|
-
- `<name/>`: the object name (e.g. `[[Attendee Browser]]`)
|
|
218
|
-
- `<kind/>:<name-or-id/>`: kind-qualified (e.g. `[[ENTITY:Event]]`)
|
|
219
|
-
- `<kind/>:*` or `*`: wildcards (for match sets, see below)
|
|
220
|
-
|
|
221
|
-
Each part can be double-quoted to allow spaces, dots, and colons inside
|
|
222
|
-
it (e.g. `[["Vue.js"]]`, whose dot would otherwise be taken as a path
|
|
223
|
-
separator). A multi-segment path matches object chains connected by
|
|
224
|
-
direct parent-to-child steps, where the leading segments up to the root
|
|
225
|
-
may be omitted (e.g. `[[FV.relay]]` matches the object `relay` directly
|
|
226
|
-
below the artifact with id `FV`).
|
|
227
|
-
|
|
228
|
-
A reference has to resolve to exactly one object: a single-segment
|
|
229
|
-
reference tries the variants (1) id/anchor, (2) name, and (3)
|
|
230
|
-
`<kind/>:<name-or-id/>` in order, where the first variant yielding any
|
|
231
|
-
matches decides; several matches are an ambiguity and zero matches an
|
|
232
|
-
unresolvable reference, both reported by the linter. Wildcard references
|
|
233
|
-
resolve into match *sets* and are used in the schema configuration to
|
|
234
|
-
constrain reference-valued properties (e.g. `[[PERSONA:*]]` or
|
|
235
|
-
`[[DM.ENTITY:*]]`).
|
|
236
|
-
|
|
237
|
-
### Image Embeddings
|
|
238
|
-
|
|
239
|
-
A Markdown image `` inside a description or property
|
|
240
|
-
value embeds a local image file, resolved relative to the artifact file:
|
|
241
|
-
SVG files are inlined as-is; PNG/JPEG files are embedded as base64
|
|
242
|
-
`data:` URLs. URLs and other file types are left untouched.
|
|
243
|
-
|
|
244
|
-
### Normalization
|
|
245
|
-
|
|
246
|
-
The semantic phase validates the parsed objects against the schema
|
|
247
|
-
configuration (unknown kinds and properties, missing required properties
|
|
248
|
-
and child kinds, and violated value constraints are reported with
|
|
249
|
-
file/line-precise diagnostics) and then normalizes the specification:
|
|
250
|
-
artifacts, child objects, and properties are stably reordered along the
|
|
251
|
-
schema definition, so the exports always present them in their
|
|
252
|
-
configured canonical order.
|
|
253
|
-
|
|
254
|
-
Schema Configuration
|
|
255
|
-
--------------------
|
|
256
|
-
|
|
257
|
-
The YAML schema configuration (see
|
|
258
|
-
[`ase.specbook.yaml`](smp/ase.specbook.yaml) for a comprehensive
|
|
259
|
-
example) defines which domain-specific objects are allowed: the artifact
|
|
260
|
-
kinds with their `file` mapping on the first level, the object kinds of
|
|
261
|
-
the deeper levels, and their properties with optional value constraints:
|
|
262
|
-
regular expressions `/xxx/`, object references `[[xxx]]`, enumerations
|
|
263
|
-
`enum(xxx,yyy)`, tag sets `tags(xxx,yyy)`, and lists `list(xxx[, ...])`
|
|
264
|
-
of such alternatives. The `file` field is only allowed on the first
|
|
265
|
-
(artifact) level.
|
|
266
|
-
|
|
267
|
-
An object kind can additionally carry a `diagram` field, which derives a
|
|
268
|
-
[Gradia](https://github.com/rse/gradia) diagram for every object of that
|
|
269
|
-
kind. Its `type` selects the diagram shape (`graph`, `hub`, or `grid`),
|
|
270
|
-
`nodes` and `edges` select the participating objects through
|
|
271
|
-
comma-separated `[[xxx]]` reference patterns (`nodes` defaults to the
|
|
272
|
-
object itself plus all objects below it), `center` names the object a
|
|
273
|
-
`hub` is projected onto (default: `self`), `onlyConnected` drops the
|
|
274
|
-
edge-less nodes of a `graph`, `collapse` (default: `true`) omits a
|
|
275
|
-
degenerated diagram consisting of a single node only, `qualified` labels
|
|
276
|
-
every node with its object kind, `properties` lists the property names
|
|
277
|
-
whose values are attached to the nodes as key/value annotations (with
|
|
278
|
-
every `[[xxx]]` reference stripped to its target object name), and
|
|
279
|
-
`config` passes arbitrary Gradia rendering options (e.g.
|
|
280
|
-
`grid-columns-max: 5`) through to the diagram.
|
|
281
|
-
|
|
282
|
-
The edges are derived from the `[[xxx]]` references of the node objects
|
|
283
|
-
-- from their property values only, or, with `links: all`, from their
|
|
284
|
-
descriptions as well. As the *nesting* of the objects carries no such
|
|
285
|
-
reference, `hierarchy: true` additionally derives a containment edge
|
|
286
|
-
from every node object to each of its child objects which is part of the
|
|
287
|
-
node set. This is what turns a parent object into the hub of its own
|
|
288
|
-
children, as the `TIER` objects of the sample schema configuration
|
|
289
|
-
demonstrate:
|
|
290
|
-
|
|
291
|
-
```yaml
|
|
292
|
-
- kind: TIER
|
|
293
|
-
diagram:
|
|
294
|
-
type: hub
|
|
295
|
-
center: self
|
|
296
|
-
hierarchy: true
|
|
297
|
-
```
|
|
51
|
+
### PDF Rendering
|
|
298
52
|
|
|
299
|
-
|
|
300
|
-
nor `hierarchy`.
|
|
301
|
-
|
|
302
|
-
An object kind can furthermore carry a `format` field, which controls
|
|
303
|
-
how the HTML/PDF export renders the child objects of every object of
|
|
304
|
-
that kind. Its `type` selects the rendering: `complex` (nested
|
|
305
|
-
sections), `concise` (compact per-kind tables), or the default `auto`,
|
|
306
|
-
which collapses only the deepest level into tables. Below a `concise`
|
|
307
|
-
object, unconfigured children implicitly stay `concise` and render as
|
|
308
|
-
sub-tables inside the description cells, while an explicitly configured
|
|
309
|
-
`type` is always honored, even a `complex` rendering pressed into a
|
|
310
|
-
cell. `maxTableColumns` (default: `4`) bounds the columns of the compact
|
|
311
|
-
tables below the object (wider groups chunk their properties into
|
|
312
|
-
embedded per-object tables), and `withUnusedProps` (default: `false`)
|
|
313
|
-
unconditionally renders the defined but unused properties of that kind,
|
|
314
|
-
as empty property table lines or table columns. Objects collapsed into
|
|
315
|
-
tables carry no headings and hence leave the table of contents and the
|
|
316
|
-
PDF outline.
|
|
53
|
+

|
|
317
54
|
|
|
318
55
|
Usage
|
|
319
56
|
-----
|
|
@@ -321,17 +58,22 @@ Usage
|
|
|
321
58
|
### CLI
|
|
322
59
|
|
|
323
60
|
```
|
|
324
|
-
$ specbook init [-v]
|
|
325
|
-
$ specbook lint [-v]
|
|
326
|
-
$ specbook export [-v]
|
|
327
|
-
$ specbook describe [-v] [-c <yaml-file>] [-o <markdown-file>]
|
|
328
|
-
$ specbook import [-v] [-c <yaml-file>] [-b <basedir>] <input-files...>
|
|
329
|
-
$ specbook edit [-v] [-c <yaml-file>] [-b <basedir>] <query>
|
|
61
|
+
$ specbook init [-v] -c <yaml-file> [-b <basedir>]
|
|
62
|
+
$ specbook lint [-v] -c <yaml-file> [-b <basedir>]
|
|
63
|
+
$ specbook export [-v] -c <yaml-file> [-b <basedir>] [-o [<format>:]<output-file>] [...]
|
|
64
|
+
$ specbook describe [-v] [-c <yaml-file>] [-b <basedir>] [-e] [-o <markdown-file>]
|
|
330
65
|
$ specbook mcp [-v]
|
|
331
66
|
```
|
|
332
67
|
|
|
333
|
-
The
|
|
334
|
-
|
|
68
|
+
The YAML schema configuration `-c`/`--config` is mandatory for `init`,
|
|
69
|
+
`lint`, and `export`, as it determines the specification: exactly the
|
|
70
|
+
artifact files its `file` fields reference are loaded and parsed, all
|
|
71
|
+
other Markdown files below the base directory are ignored. A referenced
|
|
72
|
+
file which is absent is reported, unless all of its artifacts are
|
|
73
|
+
`optional`.
|
|
74
|
+
|
|
75
|
+
The base directory `-b`/`--basedir` (default: `.`) is the directory the
|
|
76
|
+
referenced artifact files are resolved against, and generated
|
|
335
77
|
specification Markdown files are placed inside it, too.
|
|
336
78
|
|
|
337
79
|
The export output option `-o`/`--output` (default: `-` for stdout) can
|
|
@@ -340,41 +82,44 @@ occur multiple times. The format (`json`, `json5`, `yaml`, `toon`,
|
|
|
340
82
|
it is explicitly given as a `<format>:` prefix, and plain `-` (stdout)
|
|
341
83
|
defaults to JSON.
|
|
342
84
|
|
|
85
|
+
The `describe` command outputs the description of the SpecBook models
|
|
86
|
+
and formats. If the configuration file `-c`/`--config` or the base
|
|
87
|
+
directory `-b`/`--basedir` is given, it additionally appends a *SpecBook
|
|
88
|
+
Project Instantiation* section which points to these artifacts of the
|
|
89
|
+
particular project. With `-e`/`--embed` the YAML schema configuration is
|
|
90
|
+
embedded verbatim instead of just being referenced, so the resulting
|
|
91
|
+
document describes the specification format of the project entirely on
|
|
92
|
+
its own.
|
|
93
|
+
|
|
343
94
|
The default value of every CLI option `--xxx` can be overridden by a
|
|
344
95
|
corresponding `SPECBOOK_XXX` environment variable (e.g.
|
|
345
96
|
`SPECBOOK_BASEDIR`, `SPECBOOK_CONFIG`, `SPECBOOK_OUTPUT`,
|
|
346
|
-
`SPECBOOK_VERBOSE`), while an explicitly supplied option always wins.
|
|
97
|
+
`SPECBOOK_VERBOSE`), while an explicitly supplied option always wins. A
|
|
98
|
+
`SPECBOOK_CONFIG` value also satisfies the otherwise mandatory
|
|
99
|
+
`-c`/`--config` option.
|
|
347
100
|
|
|
348
101
|
### API
|
|
349
102
|
|
|
350
103
|
```ts
|
|
351
|
-
import { SpecBook } from "@rse/specbook"
|
|
104
|
+
import { SpecBook, renderVerbose } from "@rse/specbook"
|
|
352
105
|
|
|
353
|
-
const specbook = new SpecBook({
|
|
354
|
-
|
|
106
|
+
const specbook = new SpecBook({
|
|
107
|
+
verbose: (cmd, msg) => console.error(`specbook: ${cmd}: ${renderVerbose(msg)}`)
|
|
108
|
+
})
|
|
109
|
+
const result = await specbook.lint({
|
|
110
|
+
config: "smp/ase.specbook.yaml",
|
|
111
|
+
basedir: "smp/broadcast"
|
|
112
|
+
})
|
|
355
113
|
```
|
|
356
114
|
|
|
357
115
|
### MCP
|
|
358
116
|
|
|
359
|
-
Run `specbook mcp` to serve the tools `specbook_init`,
|
|
360
|
-
`
|
|
361
|
-
`specbook_import`, and `specbook_edit` over stdio.
|
|
362
|
-
|
|
363
|
-
### AI Configuration
|
|
364
|
-
|
|
365
|
-
The commands `import` and `edit` use an LLM. The environment variables
|
|
366
|
-
`SPECBOOK_AI_PROVIDER` (`anthropic`, `openai`, `openrouter`, or
|
|
367
|
-
`ollama`) and
|
|
368
|
-
`SPECBOOK_AI_MODEL` provide the defaults, which the CLI options
|
|
369
|
-
`--provider`/`--model` (respectively the MCP/API parameters `provider`/
|
|
370
|
-
`model`) override. The provider API keys come from the standard
|
|
371
|
-
environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
|
|
372
|
-
`OPENROUTER_API_KEY`), while `ollama` needs no key and uses the local
|
|
373
|
-
Ollama service.
|
|
117
|
+
Run `specbook mcp` to serve the tools `specbook_init`, `specbook_lint`,
|
|
118
|
+
`specbook_export`, and `specbook_describe` over stdio.
|
|
374
119
|
|
|
375
120
|
License
|
|
376
121
|
-------
|
|
377
122
|
|
|
378
|
-
Copyright © 2026 Dr. Ralf S. Engelschall
|
|
379
|
-
Licensed under Apache 2.0
|
|
123
|
+
Copyright © 2026 [Dr. Ralf S. Engelschall](http://engelschall.com/)<br/>
|
|
124
|
+
Licensed under [Apache 2.0](https://spdx.org/licenses/Apache-2.0)
|
|
380
125
|
|
package/dst/specbook-api.d.ts
CHANGED
|
@@ -1,47 +1,40 @@
|
|
|
1
1
|
import { renderDiagnostic, renderDiagnosticVerbose, type Diagnostic } from "./specbook-diagnostic.js";
|
|
2
2
|
import { type LintResult } from "./specbook-cmd-lint.js";
|
|
3
3
|
import { parseOutputSpec, formats, type ExportFormat } from "./specbook-cmd-export.js";
|
|
4
|
+
import { literal, renderVerbose } from "./specbook-verbose.js";
|
|
5
|
+
export { literal, renderVerbose };
|
|
4
6
|
export { formats, parseOutputSpec, type ExportFormat };
|
|
5
7
|
export { renderDiagnostic, renderDiagnosticVerbose, type Diagnostic };
|
|
6
8
|
export { type LintResult };
|
|
7
|
-
export type {
|
|
8
|
-
export type {
|
|
9
|
+
export type { Spec, SpecArtifact, SpecObject, SpecDescription, SpecProperty } from "./specbook-format-spec.js";
|
|
10
|
+
export type { Schema, SchemaObject, SchemaProperty } from "./specbook-format-schema.js";
|
|
9
11
|
export declare const version: string;
|
|
12
|
+
export type VerboseSink = (cmd: string, msg: string) => void;
|
|
10
13
|
export interface SpecBookOptions {
|
|
11
|
-
verbose?:
|
|
14
|
+
verbose?: VerboseSink;
|
|
12
15
|
}
|
|
13
16
|
export declare class SpecBook {
|
|
14
17
|
private verbose;
|
|
15
18
|
constructor(options?: SpecBookOptions);
|
|
19
|
+
private verboseOf;
|
|
20
|
+
private requireConfigFile;
|
|
16
21
|
private requireConfig;
|
|
17
22
|
init(options: {
|
|
18
|
-
config
|
|
23
|
+
config: string;
|
|
19
24
|
basedir?: string;
|
|
20
25
|
}): Promise<string[]>;
|
|
21
26
|
lint(options: {
|
|
22
|
-
config
|
|
27
|
+
config: string;
|
|
23
28
|
basedir?: string;
|
|
24
29
|
}): Promise<LintResult>;
|
|
25
30
|
export(options: {
|
|
26
|
-
config
|
|
31
|
+
config: string;
|
|
27
32
|
basedir?: string;
|
|
28
33
|
formats?: ExportFormat[];
|
|
29
34
|
}): Promise<Buffer[]>;
|
|
30
35
|
describe(options: {
|
|
31
36
|
config?: string;
|
|
32
|
-
}): Promise<string>;
|
|
33
|
-
import(options: {
|
|
34
|
-
config?: string;
|
|
35
37
|
basedir?: string;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
model?: string;
|
|
39
|
-
}): Promise<string[]>;
|
|
40
|
-
edit(options: {
|
|
41
|
-
config?: string;
|
|
42
|
-
basedir?: string;
|
|
43
|
-
query: string;
|
|
44
|
-
provider?: string;
|
|
45
|
-
model?: string;
|
|
46
|
-
}): Promise<string[]>;
|
|
38
|
+
embed?: boolean;
|
|
39
|
+
}): Promise<string>;
|
|
47
40
|
}
|
package/dst/specbook-api.js
CHANGED
|
@@ -11,10 +11,10 @@ import { renderDiagnostic, renderDiagnosticVerbose } from "./specbook-diagnostic
|
|
|
11
11
|
import { initSpecification } from "./specbook-cmd-init.js";
|
|
12
12
|
import { lint } from "./specbook-cmd-lint.js";
|
|
13
13
|
import { exportSpecification, parseOutputSpec, formats } from "./specbook-cmd-export.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { editSpecification } from "./specbook-cmd-edit.js";
|
|
14
|
+
import { describeFormat } from "./specbook-cmd-describe.js";
|
|
15
|
+
import { literal, renderVerbose } from "./specbook-verbose.js";
|
|
17
16
|
/* re-export the central types for API consumers */
|
|
17
|
+
export { literal, renderVerbose };
|
|
18
18
|
export { formats, parseOutputSpec };
|
|
19
19
|
export { renderDiagnostic, renderDiagnosticVerbose };
|
|
20
20
|
/* the own version, taken from the package manifest, which resides one
|
|
@@ -29,11 +29,20 @@ export class SpecBook {
|
|
|
29
29
|
constructor(options = {}) {
|
|
30
30
|
this.verbose = options.verbose ?? (() => { });
|
|
31
31
|
}
|
|
32
|
-
/*
|
|
33
|
-
|
|
32
|
+
/* bind the verbose sink to a particular command */
|
|
33
|
+
verboseOf(cmd) {
|
|
34
|
+
return (msg) => this.verbose(cmd, msg);
|
|
35
|
+
}
|
|
36
|
+
/* require the file of the mandatory YAML schema configuration */
|
|
37
|
+
requireConfigFile(file) {
|
|
34
38
|
if (file === undefined)
|
|
35
39
|
throw new Error("YAML schema configuration required");
|
|
36
|
-
|
|
40
|
+
return file;
|
|
41
|
+
}
|
|
42
|
+
/* load a mandatory YAML schema configuration, failing on any problem */
|
|
43
|
+
requireConfig(file, verbose) {
|
|
44
|
+
file = this.requireConfigFile(file);
|
|
45
|
+
verbose(`loading configuration "${literal(file)}"`);
|
|
37
46
|
const { config, diagnostics } = loadConfig(file);
|
|
38
47
|
if (config === null)
|
|
39
48
|
throw new Error("invalid configuration:\n" +
|
|
@@ -43,12 +52,14 @@ export class SpecBook {
|
|
|
43
52
|
/* initialize the configured specification artifact files
|
|
44
53
|
below the base directory */
|
|
45
54
|
async init(options) {
|
|
55
|
+
const verbose = this.verboseOf("init");
|
|
46
56
|
return initSpecification({ ...options,
|
|
47
|
-
config: this.requireConfig(options.config
|
|
57
|
+
config: this.requireConfig(options.config, verbose), verbose });
|
|
48
58
|
}
|
|
49
59
|
/* lint the specification Markdown files below the base directory */
|
|
50
60
|
async lint(options) {
|
|
51
|
-
return lint({ config: options.config,
|
|
61
|
+
return lint({ config: this.requireConfigFile(options.config),
|
|
62
|
+
basedir: options.basedir ?? ".", verbose: this.verboseOf("lint") });
|
|
52
63
|
}
|
|
53
64
|
/* export the specification Markdown files below the base directory
|
|
54
65
|
as JSON, JSON5, YAML, TOON, HTML, PDF, or normalized Markdown,
|
|
@@ -56,31 +67,23 @@ export class SpecBook {
|
|
|
56
67
|
requested format (best-effort: diagnostics do not prevent the
|
|
57
68
|
export, as validation is the concern of lint) */
|
|
58
69
|
async export(options) {
|
|
59
|
-
const
|
|
70
|
+
const verbose = this.verboseOf("export");
|
|
71
|
+
const result = lint({ config: this.requireConfigFile(options.config),
|
|
72
|
+
basedir: options.basedir ?? ".", verbose });
|
|
60
73
|
for (const diagnostic of result.diagnostics)
|
|
61
|
-
|
|
74
|
+
verbose(`diagnostic: ${renderDiagnostic(diagnostic)}`);
|
|
62
75
|
if (result.specification.artifacts.length === 0)
|
|
63
76
|
throw new Error("unexportable specification:\n" +
|
|
64
77
|
result.diagnostics.map(renderDiagnostic).join("\n"));
|
|
65
78
|
const buffers = new Array();
|
|
66
79
|
for (const format of options.formats ?? ["json"])
|
|
67
|
-
buffers.push(await exportSpecification(result.specification, format,
|
|
80
|
+
buffers.push(await exportSpecification(result.specification, format, verbose, result.config));
|
|
68
81
|
return buffers;
|
|
69
82
|
}
|
|
70
|
-
/* describe the
|
|
83
|
+
/* describe the generic SpecBook models and formats as Markdown,
|
|
84
|
+
optionally pointing to the artifacts of the particular project */
|
|
71
85
|
async describe(options) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
/* import foreign sources into the specification artifact files
|
|
75
|
-
below the base directory */
|
|
76
|
-
async import(options) {
|
|
77
|
-
return importSpecification({ ...options,
|
|
78
|
-
config: this.requireConfig(options.config), verbose: this.verbose });
|
|
79
|
-
}
|
|
80
|
-
/* apply an edit request to the specification artifact files
|
|
81
|
-
below the base directory */
|
|
82
|
-
async edit(options) {
|
|
83
|
-
return editSpecification({ ...options,
|
|
84
|
-
config: this.requireConfig(options.config), verbose: this.verbose });
|
|
86
|
+
this.verboseOf("describe")("describing the SpecBook models and formats");
|
|
87
|
+
return describeFormat(options);
|
|
85
88
|
}
|
|
86
89
|
}
|