@rse/specbook 1.0.1 → 1.0.2
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 +272 -121
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,63 +22,63 @@ CLI with commands `specbook <xxx>`, and an MCP service with tools
|
|
|
22
22
|
|
|
23
23
|
**SpecBook** provides the following distinct features:
|
|
24
24
|
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
25
|
+
- **Hierarchical Object Model**:
|
|
26
|
+
A specification is a tree of typed objects, each with a kind and
|
|
27
|
+
name, an optional id, optional properties, optional description, and
|
|
28
|
+
optional child objects. The allowed object hierarchy is defined per
|
|
29
|
+
context by the YAML schema configuration.
|
|
30
|
+
|
|
31
|
+
- **Versatile Object to Markdown Mapping**:
|
|
32
|
+
Objects are authored as plain Markdown headings, property lists,
|
|
33
|
+
and description prose. The objects can be mapped to nested sections
|
|
34
|
+
("complex" format) or compact bullet point lists ("concise" and
|
|
35
|
+
"group" format).
|
|
36
|
+
|
|
37
|
+
- **Wiki-Style Object Linking**:
|
|
38
|
+
Objects can hierarchically reference each other through Wiki-style
|
|
39
|
+
`[[xxx]]` references, which are resolved against the locally-unique
|
|
40
|
+
ids and names of all objects. In the HTML and PDF exports they become
|
|
41
|
+
navigable links onto precise anchors.
|
|
42
|
+
|
|
43
|
+
- **Strict Validation with Custom Schema**:
|
|
44
|
+
The YAML schema configuration strictly defines the allowed object
|
|
45
|
+
kinds, hierarchies, and properties, whose values are constrained by an
|
|
46
|
+
expression language (regex, enum, tags, list, and reference). Violations
|
|
47
|
+
are reported as file- and line-precise diagnostics. A bundled standard
|
|
48
|
+
schema configuration applies if no particular one is given.
|
|
49
|
+
|
|
50
|
+
- **Object Model Diagram Visualisation**:
|
|
51
|
+
Object kinds can declare "graph", "hub", or "grid" diagrams in the
|
|
52
|
+
schema, whose nodes and edges are automatically derived from the
|
|
53
|
+
object model and its references. The diagrams are rendered with the
|
|
54
|
+
sibling project [Gradia](https://github.com/rse/gradia), which is
|
|
55
|
+
specialized in rendering object models.
|
|
56
|
+
|
|
57
|
+
- **CLI, MCP, and API Interface**:
|
|
58
|
+
All commands are implemented once in the API class `SpecBook`, on
|
|
59
|
+
which both the CLI and the MCP service are just thin wrappers.
|
|
60
|
+
This way, humans, scripts, and AI agents use exactly the same
|
|
61
|
+
functionality.
|
|
62
|
+
|
|
63
|
+
- **AST Exports for AI/LLMs**:
|
|
64
|
+
The parsed specification Abstract Syntax Tree (AST) can be exported in
|
|
65
|
+
JSON, JSON5, YAML, or TOON format for machine consumption. Together
|
|
66
|
+
with the `describe` command, which explains the models and formats,
|
|
67
|
+
this enables LLMs to both read and write specifications.
|
|
68
|
+
|
|
69
|
+
- **HTML Export for Developers**:
|
|
70
|
+
The HTML export is a self-contained single document with a
|
|
71
|
+
table-of-contents, full-text search, embedded images, and a light/dark
|
|
72
|
+
theme toggle. It is intended for the day-to-day online reading during
|
|
73
|
+
development. The HTML export can map all object kinds to nested
|
|
74
|
+
sections or compact tables, or an automatic mixture which collapses
|
|
75
|
+
only the deepest level into tables.
|
|
76
|
+
|
|
77
|
+
- **PDF Export for Customers**:
|
|
78
|
+
The PDF export prints the HTML rendering via Chromium and post-processes
|
|
79
|
+
it with page numbers, headers/footers, and a hierarchical PDF outline.
|
|
80
|
+
It is intended as a polished, paginated offline document for handing
|
|
81
|
+
over to customers.
|
|
82
82
|
|
|
83
83
|
Overview
|
|
84
84
|
--------
|
|
@@ -97,84 +97,235 @@ $ npm install -g @rse/specbook
|
|
|
97
97
|
Usage
|
|
98
98
|
-----
|
|
99
99
|
|
|
100
|
-
###
|
|
100
|
+
### API
|
|
101
101
|
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
```ts
|
|
103
|
+
/* the SpecBook API, providing all commands the CLI and MCP service expose */
|
|
104
|
+
export class SpecBook {
|
|
105
|
+
constructor (options?: SpecBookOptions)
|
|
106
|
+
|
|
107
|
+
/* initialize the configured specification artifact files below the
|
|
108
|
+
base directory and return the list of the generated files */
|
|
109
|
+
init (options: {
|
|
110
|
+
config?: string, /* YAML schema configuration (default: bundled standard one) */
|
|
111
|
+
basedir?: string /* base directory (default: ".") */
|
|
112
|
+
}): Promise<string[]>
|
|
113
|
+
|
|
114
|
+
/* parse and validate the specification Markdown files below the base directory */
|
|
115
|
+
lint (options: {
|
|
116
|
+
config?: string,
|
|
117
|
+
basedir?: string
|
|
118
|
+
}): Promise<LintResult>
|
|
119
|
+
|
|
120
|
+
/* export the specification, parsing the input just once and
|
|
121
|
+
returning one buffer per requested format (strict: any diagnostic
|
|
122
|
+
rejects the export with an "Error") */
|
|
123
|
+
export (options: {
|
|
124
|
+
config?: string,
|
|
125
|
+
basedir?: string,
|
|
126
|
+
formats?: ExportFormat[] /* requested output formats (default: [ "json" ]) */
|
|
127
|
+
}): Promise<Buffer[]>
|
|
128
|
+
|
|
129
|
+
/* describe the SpecBook models and formats, optionally pointing to
|
|
130
|
+
the artifacts of the particular project */
|
|
131
|
+
describe (options: {
|
|
132
|
+
config?: string,
|
|
133
|
+
basedir?: string,
|
|
134
|
+
embed?: boolean, /* embed the schema configuration instead of referencing it */
|
|
135
|
+
format?: DescribeFormat, /* output format (default: "md") */
|
|
136
|
+
part?: DescribePart /* document part (default: "all") */
|
|
137
|
+
}): Promise<string>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* the constructor options and the sink of the verbose messages,
|
|
141
|
+
receiving the emitting command and the message */
|
|
142
|
+
export interface SpecBookOptions {
|
|
143
|
+
verbose?: VerboseSink
|
|
144
|
+
}
|
|
145
|
+
export type VerboseSink = (cmd: string, msg: string) => void
|
|
146
|
+
|
|
147
|
+
/* the result of the "lint" command and its file- and line-precise diagnostics */
|
|
148
|
+
export interface LintResult {
|
|
149
|
+
specification: Spec
|
|
150
|
+
diagnostics: Diagnostic[]
|
|
151
|
+
config?: Schema
|
|
152
|
+
}
|
|
153
|
+
export interface Diagnostic {
|
|
154
|
+
file: string
|
|
155
|
+
line: number
|
|
156
|
+
column: number
|
|
157
|
+
message: string
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* the supported export formats and describe formats/parts */
|
|
161
|
+
export const formats: readonly [ "json", "json5", "yaml", "toon", "html", "pdf", "md" ]
|
|
162
|
+
export const describeFormats: readonly [ "md", "raw" ]
|
|
163
|
+
export const describeParts: readonly [ "all", "meta", "schema", "spec" ]
|
|
164
|
+
export type ExportFormat = typeof formats[number]
|
|
165
|
+
export type DescribeFormat = typeof describeFormats[number]
|
|
166
|
+
export type DescribePart = typeof describeParts[number]
|
|
167
|
+
|
|
168
|
+
/* the Abstract Syntax Tree (AST) of a parsed specification */
|
|
169
|
+
export type Spec = {
|
|
170
|
+
artifacts: SpecArtifact[]
|
|
171
|
+
}
|
|
172
|
+
export type SpecArtifact = {
|
|
173
|
+
created: Date
|
|
174
|
+
modified: Date
|
|
175
|
+
objects: SpecObject[]
|
|
176
|
+
}
|
|
177
|
+
export type SpecObject = {
|
|
178
|
+
kind: string
|
|
179
|
+
id: string
|
|
180
|
+
anchor?: string
|
|
181
|
+
paren?: string
|
|
182
|
+
name: string
|
|
183
|
+
primary?: boolean
|
|
184
|
+
description?: SpecDescription
|
|
185
|
+
properties: SpecProperty[]
|
|
186
|
+
childs: SpecObject[]
|
|
187
|
+
}
|
|
188
|
+
export type SpecDescription = {
|
|
189
|
+
description: string
|
|
190
|
+
rationale?: string
|
|
191
|
+
embedding?: string[]
|
|
192
|
+
}
|
|
193
|
+
export type SpecProperty = {
|
|
194
|
+
key: string
|
|
195
|
+
value: string
|
|
196
|
+
embedding?: string[]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* the YAML schema configuration, where the nested "diagram" and
|
|
200
|
+
"format" structures are detailed in the "describe" output */
|
|
201
|
+
export type Schema = SchemaObject[]
|
|
202
|
+
export type SchemaObject = {
|
|
203
|
+
kind: string
|
|
204
|
+
name?: string
|
|
205
|
+
id?: string
|
|
206
|
+
file?: string
|
|
207
|
+
desc?: string
|
|
208
|
+
optional?: boolean
|
|
209
|
+
diagram?: SchemaDiagram
|
|
210
|
+
format?: SchemaFormat
|
|
211
|
+
props?: SchemaProperty[]
|
|
212
|
+
childs?: SchemaObject[]
|
|
213
|
+
}
|
|
214
|
+
export type SchemaProperty = {
|
|
215
|
+
name: string
|
|
216
|
+
desc?: string
|
|
217
|
+
value?: string
|
|
218
|
+
optional?: boolean
|
|
219
|
+
}
|
|
108
220
|
```
|
|
109
221
|
|
|
110
|
-
|
|
111
|
-
standard schema configuration `specbook-format.yaml`) determines the
|
|
112
|
-
specification: exactly the artifact files its `file` fields reference
|
|
113
|
-
are loaded and parsed; all other Markdown files below the base
|
|
114
|
-
directory are ignored. A referenced
|
|
115
|
-
file which is absent is reported, unless all of its artifacts are
|
|
116
|
-
`optional`, and so is an artifact absent from its present file, unless
|
|
117
|
-
it is `optional` itself. Both `lint` and `export` report all
|
|
118
|
-
diagnostics and fail on any of them, so a partial or invalid
|
|
119
|
-
specification is never exported.
|
|
120
|
-
|
|
121
|
-
The base directory `-b`/`--basedir` (default: `.`) is the directory the
|
|
122
|
-
referenced artifact files are resolved against, and generated
|
|
123
|
-
specification Markdown files are placed inside it, too.
|
|
124
|
-
|
|
125
|
-
The export output option `-o`/`--output` (default: `-` for stdout) can
|
|
126
|
-
occur multiple times. The format (`json`, `json5`, `yaml`, `toon`,
|
|
127
|
-
`html`, `pdf`, or `md`) is inferred from the filename extension, unless
|
|
128
|
-
it is explicitly given as a `<format>:` prefix, and plain `-` (stdout)
|
|
129
|
-
defaults to JSON.
|
|
130
|
-
|
|
131
|
-
The `md` format normalizes the entire corpus into a *single* Markdown
|
|
132
|
-
document, whose single frontmatter block carries the earliest `Created:`
|
|
133
|
-
and the latest `Modified:` timestamp of all artifacts, so the
|
|
134
|
-
per-artifact timestamps do not survive this round-trip.
|
|
135
|
-
|
|
136
|
-
The `describe` command outputs the description of the SpecBook models
|
|
137
|
-
and formats, extended by a *SpecBook Project Instantiation* section which
|
|
138
|
-
points to the YAML schema configuration `-c`/`--config` and the base
|
|
139
|
-
directory `-b`/`--basedir`. The schema configuration falls back onto the
|
|
140
|
-
bundled standard one, embedded verbatim, as long as no particular
|
|
141
|
-
configuration file `-c`/`--config` is given. With `-e`/`--embed` the
|
|
142
|
-
given YAML schema configuration is embedded verbatim instead of just
|
|
143
|
-
being referenced, so the resulting document describes the specification
|
|
144
|
-
format entirely on its own.
|
|
145
|
-
|
|
146
|
-
The `describe` document part `-p`/`--part` (default: `all`) reduces the
|
|
147
|
-
output to a single part: `meta` for the description of the generic
|
|
148
|
-
SpecBook models and formats, `schema` for the YAML schema configuration
|
|
149
|
-
(the given one, referenced or embedded with `-e`/`--embed`, else the
|
|
150
|
-
bundled standard one, embedded), or `spec` for the reference to
|
|
151
|
-
the base directory. The output format `-f`/`--format` (default: `md`)
|
|
152
|
-
switches from this rendered Markdown onto the raw original file content
|
|
153
|
-
with `raw`, which is available for the file-backed parts `meta` and
|
|
154
|
-
`schema` only.
|
|
155
|
-
|
|
156
|
-
The default value of every CLI option `--xxx` can be overridden by a
|
|
157
|
-
corresponding `SPECBOOK_XXX` environment variable (e.g.
|
|
158
|
-
`SPECBOOK_BASEDIR`, `SPECBOOK_CONFIG`, `SPECBOOK_OUTPUT`,
|
|
159
|
-
`SPECBOOK_VERBOSE`), while an explicitly supplied option always wins.
|
|
160
|
-
|
|
161
|
-
### API
|
|
222
|
+
Example:
|
|
162
223
|
|
|
163
224
|
```ts
|
|
164
|
-
import { SpecBook
|
|
225
|
+
import { SpecBook } from "@rse/specbook"
|
|
165
226
|
|
|
166
227
|
const specbook = new SpecBook({
|
|
167
|
-
verbose: (cmd, msg) => console.error(`specbook: ${cmd}: ${
|
|
228
|
+
verbose: (cmd, msg) => console.error(`specbook: ${cmd}: ${msg}`)
|
|
168
229
|
})
|
|
169
230
|
const result = await specbook.lint({
|
|
170
231
|
basedir: "smp/broadcast"
|
|
171
232
|
})
|
|
172
233
|
```
|
|
173
234
|
|
|
174
|
-
###
|
|
235
|
+
### CLI
|
|
236
|
+
|
|
237
|
+
Commands:
|
|
175
238
|
|
|
176
|
-
|
|
177
|
-
|
|
239
|
+
```bash
|
|
240
|
+
$ specbook init \
|
|
241
|
+
[-v|--verbose] \
|
|
242
|
+
[-c|--config <schema-yaml-file>] \
|
|
243
|
+
[-b|--basedir <spec-md-file-basedir>]
|
|
244
|
+
|
|
245
|
+
$ specbook lint \
|
|
246
|
+
[-v|--verbose] \
|
|
247
|
+
[-c|--config <schema-yaml-file>] \
|
|
248
|
+
[-b|--basedir <spec-md-file-basedir>]
|
|
249
|
+
|
|
250
|
+
$ specbook export \
|
|
251
|
+
[-v|--verbose] \
|
|
252
|
+
[-c|--config <schema-yaml-file>] \
|
|
253
|
+
[-b|--basedir <spec-md-file-basedir>] \
|
|
254
|
+
[-o|--output [<format>:]<output-file>] [...]
|
|
255
|
+
|
|
256
|
+
$ specbook describe \
|
|
257
|
+
[-v|--verbose] \
|
|
258
|
+
[-c|--config <schema-yaml-file>] \
|
|
259
|
+
[-b|--basedir <spec-md-file-basedir>] \
|
|
260
|
+
[-e|--embed] \
|
|
261
|
+
[-f|--format <format>] \
|
|
262
|
+
[-p|--part <part>] \
|
|
263
|
+
[-o|--output <output-file>]
|
|
264
|
+
|
|
265
|
+
$ specbook mcp \
|
|
266
|
+
[-v|--verbose]
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Options:
|
|
270
|
+
|
|
271
|
+
- `-v|--verbose`:
|
|
272
|
+
Enable verbose logging of processing information to `stderr`.
|
|
273
|
+
|
|
274
|
+
- `-c|--config <schema-yaml-file>`:
|
|
275
|
+
The YAML schema configuration (default: the bundled standard schema
|
|
276
|
+
configuration `specbook-format.yaml`) determines the specification:
|
|
277
|
+
exactly the artifact files its `file` fields reference are loaded and
|
|
278
|
+
parsed; all other Markdown files below the base directory are ignored.
|
|
279
|
+
A referenced file which is absent is reported, unless all of its
|
|
280
|
+
artifacts are `optional`, and so is an artifact absent from its present
|
|
281
|
+
file, unless it is `optional` itself. Both `lint` and `export` report
|
|
282
|
+
all diagnostics and fail on any of them, so a partial or invalid
|
|
283
|
+
specification is never exported.
|
|
284
|
+
|
|
285
|
+
- `-b|--basedir <spec-md-file-basedir>`:
|
|
286
|
+
The base directory (default: `.`) is the directory the referenced
|
|
287
|
+
artifact files are resolved against, and generated specification
|
|
288
|
+
Markdown files are placed inside it, too.
|
|
289
|
+
|
|
290
|
+
- `-o|--output [<format>:]<output-file>` (`export` only):
|
|
291
|
+
The output file (default: `-` for stdout) can be given multiple times.
|
|
292
|
+
The format (`json`, `json5`, `yaml`, `toon`, `html`, `pdf`, or `md`)
|
|
293
|
+
is inferred from the filename extension, unless it is explicitly given
|
|
294
|
+
as a `<format>:` prefix, and plain `-` (stdout) defaults to JSON.
|
|
295
|
+
The `md` export format normalizes the entire corpus into a *single*
|
|
296
|
+
Markdown document.
|
|
297
|
+
|
|
298
|
+
- `-o|--output <output-file>` (`describe` only):
|
|
299
|
+
The output file (default: `-` for stdout) receives the described
|
|
300
|
+
Markdown document.
|
|
301
|
+
|
|
302
|
+
- `-e|--embed` (`describe` only):
|
|
303
|
+
Embed the given YAML schema configuration verbatim instead of just
|
|
304
|
+
referencing it, so the resulting document describes the specification
|
|
305
|
+
format entirely on its own.
|
|
306
|
+
|
|
307
|
+
- `-f|--format <format>` (`describe` only):
|
|
308
|
+
The output format (default: `md`) switches from the rendered Markdown
|
|
309
|
+
onto the raw original file content with `raw`, which is available for
|
|
310
|
+
the file-backed parts `meta` and `schema` only.
|
|
311
|
+
|
|
312
|
+
- `-p|--part <part>` (`describe` only):
|
|
313
|
+
The document part (default: `all`) reduces the output to a single part:
|
|
314
|
+
`meta` for the description of the generic SpecBook models and formats,
|
|
315
|
+
`schema` for the YAML schema configuration (the given one, referenced
|
|
316
|
+
or embedded with `-e|--embed`, else the bundled standard one,
|
|
317
|
+
embedded), or `spec` for the reference to the base directory.
|
|
318
|
+
|
|
319
|
+
The default value of every CLI option `--xxx` can be overridden
|
|
320
|
+
by a corresponding `SPECBOOK_XXX` environment variable (e.g.
|
|
321
|
+
`SPECBOOK_BASEDIR`, `SPECBOOK_CONFIG`, `SPECBOOK_OUTPUT`,
|
|
322
|
+
`SPECBOOK_VERBOSE`), while an explicitly supplied option always wins.
|
|
323
|
+
|
|
324
|
+
Example:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
$ specbook lint -v -b smp/broadcast
|
|
328
|
+
```
|
|
178
329
|
|
|
179
330
|
Rendering Examples
|
|
180
331
|
------------------
|