@orkestrel/scaffold 0.0.1

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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +114 -0
  3. package/dist/bin/scaffold.js +1539 -0
  4. package/dist/bin/scaffold.js.map +1 -0
  5. package/dist/host/AGENTS.md +939 -0
  6. package/dist/host/CLAUDE.md +495 -0
  7. package/dist/host/LICENSE +21 -0
  8. package/dist/host/claude/agents/builder.md +48 -0
  9. package/dist/host/claude/agents/checker.md +37 -0
  10. package/dist/host/claude/agents/composer.md +64 -0
  11. package/dist/host/claude/agents/grok.md +50 -0
  12. package/dist/host/claude/agents/orkestrel.md +236 -0
  13. package/dist/host/claude/agents/planner.md +44 -0
  14. package/dist/host/claude/agents/researcher.md +38 -0
  15. package/dist/host/claude/agents/reviewer.md +47 -0
  16. package/dist/host/claude/agents/scout.md +35 -0
  17. package/dist/host/claude/agents/verifier.md +34 -0
  18. package/dist/host/claude/settings.json +26 -0
  19. package/dist/host/dotfiles/editorconfig +17 -0
  20. package/dist/host/dotfiles/gitattributes +3 -0
  21. package/dist/host/dotfiles/gitignore +40 -0
  22. package/dist/host/dotfiles/oxfmtrc.json +18 -0
  23. package/dist/host/dotfiles/oxlintignore +20 -0
  24. package/dist/host/dotfiles/oxlintrc.json +58 -0
  25. package/dist/host/dotfiles/prettierignore +5 -0
  26. package/dist/host/github/workflows/ci.yml +64 -0
  27. package/dist/host/guides/src/guide.md +312 -0
  28. package/dist/host/guides/src/scaffold.md +2152 -0
  29. package/dist/host/manifest.json +137 -0
  30. package/dist/host/scripts/cursor.sh +74 -0
  31. package/dist/host/scripts/deps.sh +38 -0
  32. package/dist/host/scripts/ollama.sh +163 -0
  33. package/dist/src/core/index.cjs +3728 -0
  34. package/dist/src/core/index.cjs.map +1 -0
  35. package/dist/src/core/index.d.cts +1941 -0
  36. package/dist/src/core/index.d.ts +1941 -0
  37. package/dist/src/core/index.js +3636 -0
  38. package/dist/src/core/index.js.map +1 -0
  39. package/dist/src/server/index.cjs +1595 -0
  40. package/dist/src/server/index.cjs.map +1 -0
  41. package/dist/src/server/index.d.cts +779 -0
  42. package/dist/src/server/index.d.ts +779 -0
  43. package/dist/src/server/index.js +1572 -0
  44. package/dist/src/server/index.js.map +1 -0
  45. package/package.json +113 -0
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: scout
3
+ description: 'Fast read-only recon. Use FIRST on unfamiliar ground — before research, planning, or implementation — to map the terrain, which files matter, where things live, and what needs to be read or touched. Cheap and quick; never reads files end-to-end.'
4
+ tools: Read, Grep, Glob
5
+ model: sonnet
6
+ effort: low
7
+ ---
8
+
9
+ You are the **Scout** — the recon unit of this project's orchestration triad (see
10
+ CLAUDE.md). Your job is to map terrain cheaply so no other agent wastes context on
11
+ discovery. You are an Executor: do the work yourself, spawn nothing, return only
12
+ the map.
13
+
14
+ ## Job
15
+
16
+ 1. Locate the relevant files and directories — glob/grep first; open a file only to
17
+ confirm relevance, and skim, never read end-to-end.
18
+ 2. Identify entry points, the contracts/types involved, the matching tests, and the
19
+ governing docs (AGENTS.md always; any guide that covers the area).
20
+ 3. Note shape and size — rough line counts, call-site counts, obvious hot spots.
21
+
22
+ ## Output contract — the Map
23
+
24
+ Return ONLY this, compact (well under ~60 lines):
25
+
26
+ - **Goal restated** — one line.
27
+ - **Files that matter** — path + one-line role each, in read-first order.
28
+ - **Off to the side** — related-looking paths that are NOT relevant, half a line why
29
+ (saves everyone else the detour).
30
+ - **Pointers** — entry points, key symbols, matching test files, governing guide paths.
31
+ - **Flags** — anything surprising (generated code, huge files, duplication), one
32
+ line each.
33
+
34
+ No file contents. No analysis. No recommendations. If the terrain exceeds the
35
+ budget, say which subarea needs its own scout pass instead of inflating the map.
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: verifier
3
+ description: 'Runs the authoritative quality gates — format, check, build, targeted tests — or the exact scoped gate set / evidence commands the dispatch names, and reports true pass/fail per gate with exact failure excerpts. Independent of every builder; its report is the source of truth for green. Never fixes anything.'
4
+ tools: Read, Grep, Glob, Bash
5
+ model: sonnet
6
+ effort: low
7
+ ---
8
+
9
+ You are the **Verifier** — the gate-runner of this project's orchestration triad
10
+ (see CLAUDE.md). Independence is the point: no builder's self-report counts, yours
11
+ does. You are an Executor: run the gates yourself, spawn nothing.
12
+
13
+ ## Job
14
+
15
+ 1. Run EXACTLY the commands the dispatch names, in order. The default authoritative
16
+ sweep, when the dispatch says so: `format` → `check` → `build` → the targeted
17
+ test project(s) it names. Never invent broader or narrower gates than dispatched.
18
+ 2. Evidence runs count as gates: when dispatched to reproduce a failure, run the
19
+ named command and capture its exact output — reproduce, capture, bisect
20
+ mechanically if told to; nothing more.
21
+ 3. Record each gate's TRUE outcome by exit code. A gate that "mostly passes" FAILED.
22
+ 4. On failure, capture the exact failing excerpt — trimmed to the failure, not the
23
+ noise — and the file:line it points to.
24
+
25
+ ## Output contract — the Gate Report
26
+
27
+ - **Per gate** — command → PASS / FAIL (exit code) → on FAIL, the exact failure
28
+ excerpt plus the suspected owning file(s).
29
+ - **Overall verdict** — GREEN only if every gate passed; otherwise the first place
30
+ to look.
31
+ - **Anomalies** — cache weirdness, flakes on rerun, anything off — one line each.
32
+
33
+ You never edit files and never "quick-fix" a failure — you report it. Return only
34
+ the gate report, never your process.
@@ -0,0 +1,26 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|resume",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/deps.sh",
10
+ "timeout": 600
11
+ },
12
+ {
13
+ "type": "command",
14
+ "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/ollama.sh",
15
+ "timeout": 600
16
+ },
17
+ {
18
+ "type": "command",
19
+ "command": "\"$CLAUDE_PROJECT_DIR\"/scripts/cursor.sh",
20
+ "timeout": 120
21
+ }
22
+ ]
23
+ }
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,17 @@
1
+ root = true
2
+
3
+ [*]
4
+ end_of_line = lf
5
+ charset = utf-8
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+ indent_style = tab
9
+ indent_size = 4
10
+
11
+ [*.{json,jsonc,yaml,yml}]
12
+ indent_style = tab
13
+ indent_size = 2
14
+
15
+ [*.md]
16
+ trim_trailing_whitespace = false
17
+
@@ -0,0 +1,3 @@
1
+ # Enforce LF line endings everywhere — prevents CRLF/LF churn on Windows
2
+ * text=auto eol=lf
3
+
@@ -0,0 +1,40 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ tmp
12
+ dist
13
+ dist-ssr
14
+ *.local
15
+
16
+ # Editor directories and files
17
+ .vscode/*
18
+ !.vscode/extensions.json
19
+ .idea
20
+ .DS_Store
21
+ *.suo
22
+ *.ntvs*
23
+ *.njsproj
24
+ *.sln
25
+ *.sw?
26
+
27
+ # Environment variables (keep secret)
28
+ .env
29
+ .env.*
30
+ !.env.example
31
+
32
+ # Test screenshots (generated by vitest browser mode)
33
+ tests/**/__screenshots__/
34
+
35
+ # Playwright
36
+ test-results/
37
+ playwright-report/
38
+
39
+ # Model files
40
+ *.gguf
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxfmt/configuration_schema.json",
3
+ "endOfLine": "lf",
4
+ "semi": false,
5
+ "singleQuote": true,
6
+ "trailingComma": "all",
7
+ "printWidth": 100,
8
+ "useTabs": true,
9
+ "tabWidth": 2,
10
+ "overrides": [
11
+ {
12
+ "files": ["*.json", "*.jsonc"],
13
+ "options": {
14
+ "trailingComma": "none"
15
+ }
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,20 @@
1
+ # Dependencies
2
+ node_modules/
3
+
4
+ # Build outputs
5
+ dist/
6
+ *.tsbuildinfo
7
+
8
+ # Cache
9
+ .cache/
10
+ .eslintcache
11
+
12
+ # IDE
13
+ .idea/
14
+ .vscode/
15
+ *.swp
16
+ *.swo
17
+
18
+ # OS
19
+ .DS_Store
20
+ Thumbs.db
@@ -0,0 +1,58 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json",
3
+ "plugins": ["import", "vitest"],
4
+ "rules": {
5
+ "@typescript-eslint/no-explicit-any": "error",
6
+ "@typescript-eslint/no-non-null-assertion": "error",
7
+ "@typescript-eslint/no-unused-vars": [
8
+ "error",
9
+ {
10
+ "argsIgnorePattern": "^_",
11
+ "varsIgnorePattern": "^_",
12
+ "caughtErrorsIgnorePattern": "^_",
13
+ "ignoreRestSiblings": true
14
+ }
15
+ ],
16
+ "@typescript-eslint/consistent-type-imports": [
17
+ "error",
18
+ {
19
+ "prefer": "type-imports",
20
+ "fixStyle": "separate-type-imports"
21
+ }
22
+ ],
23
+
24
+ "import/no-default-export": "error",
25
+ "import/extensions": "off",
26
+ "import/no-unassigned-import": ["error", { "allow": ["**/*.css", "**/*.scss"] }],
27
+
28
+ "no-var": "error",
29
+ "prefer-const": "error",
30
+ "no-debugger": "error",
31
+ "no-underscore-dangle": "warn",
32
+
33
+ "vitest/warn-todo": "off"
34
+ },
35
+ "categories": {
36
+ "correctness": "error",
37
+ "suspicious": "warn",
38
+ "pedantic": "off",
39
+ "perf": "off",
40
+ "style": "off",
41
+ "restriction": "off"
42
+ },
43
+ "overrides": [
44
+ {
45
+ "files": ["*.config.ts", "*.config.js"],
46
+ "rules": {
47
+ "import/no-default-export": "off"
48
+ }
49
+ },
50
+ {
51
+ "files": ["*.vue"],
52
+ "rules": {
53
+ "import/no-default-export": "off",
54
+ "no-unused-expressions": "off"
55
+ }
56
+ }
57
+ ]
58
+ }
@@ -0,0 +1,5 @@
1
+ # oxfmt (and prettier) read this file by default. Keep generated / minified
2
+ # artifacts out of the formatter so a tree-wide `npm run format` never rewrites them.
3
+
4
+ # Build output (also gitignored, listed for explicitness).
5
+ dist/
@@ -0,0 +1,64 @@
1
+ name: ci.yml
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ ci:
9
+ runs-on: ubuntu-latest
10
+ timeout-minutes: 60
11
+ permissions:
12
+ contents: read
13
+
14
+ steps:
15
+ - name: Checkout code
16
+ uses: actions/checkout@v6
17
+
18
+ - name: Set up Node.js
19
+ uses: actions/setup-node@v6
20
+ with:
21
+ node-version: '24'
22
+ cache: 'npm'
23
+
24
+ - name: Install dependencies
25
+ run: npm ci
26
+
27
+ - name: Install Playwright browsers
28
+ run: npx playwright install --with-deps chromium
29
+
30
+ - name: Install Ollama
31
+ run: curl -fsSL https://ollama.com/install.sh | sh
32
+
33
+ - name: Start Ollama
34
+ run: |
35
+ sudo systemctl stop ollama
36
+ OLLAMA_ORIGINS="*" OLLAMA_HOST="127.0.0.1:11434" nohup ollama serve > /dev/null 2>&1 &
37
+ for i in $(seq 1 30); do
38
+ if curl -sf http://127.0.0.1:11434/api/tags > /dev/null 2>&1; then
39
+ echo "Ollama is ready"
40
+ break
41
+ fi
42
+ echo "Waiting for Ollama... ($i/30)"
43
+ sleep 1
44
+ done
45
+ - name: Pull models
46
+ run: ollama pull qwen3.5:2b-q4_K_M
47
+
48
+ - name: Check formatting
49
+ run: npm run format:check
50
+
51
+ - name: Lint
52
+ run: npm run lint:check
53
+
54
+ - name: Typecheck
55
+ run: npm run check
56
+
57
+ - name: Typecheck src
58
+ run: npm run check:src
59
+
60
+ - name: Build
61
+ run: npm run build
62
+
63
+ - name: Run tests
64
+ run: npm test
@@ -0,0 +1,312 @@
1
+ # Guide
2
+
3
+ > A zero-dependency guides-parity toolkit: `Guide` extracts a markdown guide's documented
4
+ > surface, method groups, links, and test links; `Source` reflects the same shapes from a
5
+ > consumer-supplied file inventory via pure text scanners (no filesystem, no TypeScript
6
+ > compiler API); a small set of pure comparison helpers (`missingSymbols`, `findMissing`,
7
+ > `resolveLink`, …) reduce every guides-parity check to `expect([]).toEqual([])`
8
+ > (AGENTS §22). Source: [`src/core`](../../src/core). Surfaced through the `@src/core` barrel.
9
+
10
+ The doctrine: a guide is a contract, not prose. `createGuide(markdown)` parses a guide's
11
+ source once (via `@orkestrel/markdown`) into a `GuideInterface` — its `## Surface` identifiers
12
+ (kind-tagged), its `## Methods` interface/method groups, every link, and its `## Tests`
13
+ links, each cached at construction. `createSource({ files, module })` builds a
14
+ `SourceInterface` that reflects the SAME two shapes — exported symbols and interface/class
15
+ methods — by scanning a consumer-gathered file inventory with plain-text line scanners
16
+ (`exportsFrom`, `declarationBody` + `memberMethods`), never touching disk itself. A
17
+ guides-parity test then asserts these two views agree in both directions: nothing documented
18
+ is phantom, nothing exported is undocumented. `parseManifest` reads a `guides/README.md`'s
19
+ `## By concept` table into the list of `{ concept, spec, source, tests }` entries a suite
20
+ iterates to run this check once per documented concept.
21
+
22
+ ## Surface
23
+
24
+ ### Types
25
+
26
+ The manifest/extraction shapes every check is built from, from [`types.ts`](../../src/core/types.ts).
27
+
28
+ | Name | Kind | Shape |
29
+ | ----------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
30
+ | `ExportKind` | type | `'type' \| 'interface' \| 'const' \| 'function' \| 'class'` — the declaration kind half of a symbol's bijection key. |
31
+ | `SurfaceSymbol` | interface | `{ name, kind }` — one documented / exported symbol. |
32
+ | `GuideModule` | type | `string \| readonly string[]` — one source directory, or several for a layer guide spanning multiple directories. |
33
+ | `ManifestEntry` | interface | `{ concept, spec, source, tests }` — one `## By concept` manifest row, paths normalized to workspace root. |
34
+ | `MethodGroup` | interface | `{ interface, methods }` — one `#### \`Interface\`` block's documented method names, in table order. |
35
+ | `GuideInterface` | interface | `{ sections, surface, methods, links, tests, patterns }` — the structured, pure view over one parsed guide. See [`## Methods`](#methods). |
36
+ | `SourceInterface` | interface | `{ exports, methods, exists, hidden, examples }` — the reflected source truth a guide's surface is checked against. See [`## Methods`](#methods). |
37
+ | `SourceOptions` | interface | `{ files, module }` — the construction input for a `Source`: a consumer-supplied file inventory plus the module scope to reflect. |
38
+ | `DeclarationHead` | interface | `{ text, end }` — a declaration head joined into one line (across an oxfmt-wrapped signature) plus the index of the line ending in `{`. |
39
+
40
+ ### Constants
41
+
42
+ The section-heading keys and external-link schemes every extractor and link check is keyed
43
+ on, from [`constants.ts`](../../src/core/constants.ts).
44
+
45
+ | Name | Kind | Behavior |
46
+ | ------------------ | ----- | ------------------------------------------------------------------------------------------------------------------------------- |
47
+ | `SURFACE` | const | `'Surface'` — the `## Surface` heading text. |
48
+ | `METHODS` | const | `'Methods'` — the `## Methods` heading text. |
49
+ | `TESTS` | const | `'Tests'` — the `## Tests` heading text. |
50
+ | `MANIFEST` | const | `'By concept'` — the `## By concept` manifest heading text. |
51
+ | `EXTERNAL_SCHEMES` | const | `readonly string[]` — `['http:', 'https:', 'mailto:', 'tel:']`; a link with one of these prefixes is never filesystem-resolved. |
52
+
53
+ ### Helpers
54
+
55
+ Pure, total leaves from [`helpers.ts`](../../src/core/helpers.ts) — the building blocks
56
+ `parsers.ts`'s extractors and a consumer's parity test both reach for directly.
57
+
58
+ | Name | Kind | Signature | Behavior |
59
+ | ---------------- | -------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
60
+ | `moduleDirs` | function | `(module: GuideModule) => readonly string[]` | Normalizes a `GuideModule` scope to its directory list — a single string becomes a one-element list. |
61
+ | `moduleKeys` | function | `(files: Readonly<Record<string, string>>, module: GuideModule) => readonly string[]` | The file inventory's keys belonging to a `GuideModule` scope: `.ts` files under a scope directory, excluding that directory's `index.ts` and `*.test.ts`; sorted. |
62
+ | `symbolKey` | function | `(symbol: SurfaceSymbol) => string` | The bijection key for a surface symbol — `${kind} ${name}` — so a symbol comparison diffs (name, kind) pairs, not names alone. |
63
+ | `findMissing` | function | `(names: readonly string[], source: readonly string[]) => readonly string[]` | The names present in `names` but absent from `source` — the set-difference behind a both-directions bijection assertion. |
64
+ | `missingSymbols` | function | `(symbols: readonly SurfaceSymbol[], source: readonly SurfaceSymbol[]) => readonly string[]` | The `symbolKey` set-difference between two symbol lists. |
65
+ | `isExternalLink` | function | `(href: string) => boolean` | Whether a link `href` should be skipped by guides-parity link checks — an external scheme (`EXTERNAL_SCHEMES`) or a bare `#` anchor. |
66
+ | `resolveLink` | function | `(from: string, target: string) => string` | Resolves a relative link `target` against `from` and normalizes the result, purely (no `node:path`). |
67
+ | `firstCode` | function | `(nodes: readonly InlineNode[]) => string \| undefined` | The first code-span value found by descending an inline node list, following into `emphasis` / `link` children. |
68
+ | `identifierOf` | function | `(code: string) => string` | The identifier prefix of a code-span text — everything before its first `<`, trimmed (strips generic-parameter annotation). |
69
+ | `kindIndex` | function | `(table: TableNode) => number \| undefined` | The index of a table's `Kind` column, found by its header text so it survives column reordering. |
70
+ | `cellLinks` | function | `(cell: readonly InlineNode[]) => readonly string[]` | The link hrefs found within one table cell's inline content, in walk order. |
71
+ | `findUnexampled` | function | `(names: readonly string[], fences: readonly string[], examples: readonly string[]) => readonly string[]` | The names with no fence mention (word boundary) and no `@example` membership — the EX check's core comparison. |
72
+ | `fenceImports` | function | `(fence: string) => readonly { specifier: string, names: readonly string[] }[]` | Parses a fence's `import` statements into per-specifier imported identifier names — the FI check's core comparison. |
73
+
74
+ ### Parsers
75
+
76
+ The guide/manifest extraction pipeline, from [`parsers.ts`](../../src/core/parsers.ts) —
77
+ the orchestration `Guide` composes out of `helpers.ts`'s leaves.
78
+
79
+ | Name | Kind | Signature | Behavior |
80
+ | ----------------- | -------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
81
+ | `exportsFrom` | function | `(source: string) => readonly SurfaceSymbol[]` | The module-scope exports declared in one file's source text, deduped by (kind, name); a generator export scans as `function`. |
82
+ | `hiddenFrom` | function | `(source: string) => readonly SurfaceSymbol[]` | The module-scope declarations LACKING the `export` keyword — the export-discipline (AGENTS §5) mirror of `exportsFrom`, same five-kind grammar. |
83
+ | `joinHead` | function | `(lines: readonly string[], start: number) => DeclarationHead \| undefined` | Joins a declaration head starting at `start` into one space-separated line, consuming lines until the first ending in `{`. |
84
+ | `declarationBody` | function | `(source: string, keyword: 'class' \| 'interface', name: string) => readonly string[]` | The body lines of the named `export class` / `export interface` declaration in one file's source text. |
85
+ | `memberMethods` | function | `(lines: readonly string[]) => readonly string[]` | The declared callable-member names in a declaration body — plain / `async` / generator / optional; getters, setters, `static`, `#` privates never count. |
86
+ | `sectionBlocks` | function | `(document: MarkdownDocument, heading: string) => readonly BlockNode[]` | The block nodes under a named `##` heading, up to the next `##`-or-higher heading (or the document's end). |
87
+ | `extractSurface` | function | `(document: MarkdownDocument) => readonly SurfaceSymbol[]` | Every `## Surface` identifier: each table's rows union every backticked H3 entity heading, deduped by `symbolKey`. |
88
+ | `extractMethods` | function | `(document: MarkdownDocument) => readonly MethodGroup[]` | One `MethodGroup` per documented behavioral interface in `## Methods` — an H4 code span sets the interface, the following table lists its methods. |
89
+ | `extractLinks` | function | `(document: MarkdownDocument) => readonly string[]` | Every link href in the guide document, including table cells — a full, depth-first AST walk. |
90
+ | `extractTests` | function | `(document: MarkdownDocument) => readonly string[]` | The relative test links declared under `## Tests`. |
91
+ | `examplesFrom` | function | `(source: string) => readonly string[]` | The exported functions in one file's source text whose immediately preceding JSDoc block carries `@example`. |
92
+ | `exampleMethods` | function | `(lines: readonly string[]) => readonly string[]` | The `exampleMethods`-flavored `memberMethods` — callable-member names in a declaration body whose preceding JSDoc carries `@example`. |
93
+ | `extractPatterns` | function | `(document: MarkdownDocument) => readonly string[]` | Every fenced `ts` code block's body text anywhere in the guide document — a full AST walk. |
94
+ | `parseManifest` | function | `(markdown: string, base: string) => readonly ManifestEntry[]` | Parses a `## By concept` manifest table into `ManifestEntry` rows, resolving Spec / Source / Tests links against `base`. |
95
+
96
+ ### Shapers
97
+
98
+ Declarative `ContractShape` values (from `@orkestrel/contract`) from
99
+ [`shapers.ts`](../../src/core/shapers.ts) — every documented data type here is
100
+ non-recursive, so each shapes directly.
101
+
102
+ | Name | Kind | Builds |
103
+ | -------------------- | ----- | ----------------------------------------------------------------------------------- |
104
+ | `surfaceSymbolShape` | const | The shape of a `SurfaceSymbol` — `{ name: string, kind: ExportKind }`. |
105
+ | `methodGroupShape` | const | The shape of a `MethodGroup` — `{ interface: string, methods: readonly string[] }`. |
106
+ | `manifestEntryShape` | const | The shape of a `ManifestEntry` — `source` accepting a single directory or several. |
107
+
108
+ ### Validators
109
+
110
+ Total from-unknown guards composed from `@orkestrel/contract` combinators, from
111
+ [`validators.ts`](../../src/core/validators.ts).
112
+
113
+ | Name | Kind | Narrows to / Tests | Behavior |
114
+ | ----------------- | ----- | ------------------ | ------------------------------------------------------------------------ |
115
+ | `isExportKind` | const | `value: unknown` | `true` when `value` is one of the five documented `ExportKind` literals. |
116
+ | `isSurfaceSymbol` | const | `value: unknown` | `true` when `value` is a well-formed `SurfaceSymbol`. |
117
+ | `isMethodGroup` | const | `value: unknown` | `true` when `value` is a well-formed `MethodGroup`. |
118
+ | `isManifestEntry` | const | `value: unknown` | `true` when `value` is a well-formed `ManifestEntry`. |
119
+
120
+ ### Factories
121
+
122
+ From [`factories.ts`](../../src/core/factories.ts).
123
+
124
+ | Name | Kind | Signature | Behavior |
125
+ | ----------------------------- | -------- | --------------------------------------------- | -------------------------------------------------------------------------------- |
126
+ | `createGuide` | function | `(source: string) => GuideInterface` | Creates a structured `GuideInterface` view over one guide's markdown source. |
127
+ | `createSource` | function | `(options: SourceOptions) => SourceInterface` | Creates a pure `SourceInterface` over a consumer-supplied file inventory. |
128
+ | `createSurfaceSymbolContract` | function | `() => ContractInterface<SurfaceSymbol>` | Compiles `surfaceSymbolShape` into a guard / parser / schema / generator bundle. |
129
+ | `createMethodGroupContract` | function | `() => ContractInterface<MethodGroup>` | Compiles `methodGroupShape` into a guard / parser / schema / generator bundle. |
130
+ | `createManifestEntryContract` | function | `() => ContractInterface<ManifestEntry>` | Compiles `manifestEntryShape` into a guard / parser / schema / generator bundle. |
131
+
132
+ ### `Guide`
133
+
134
+ The implementing class of `GuideInterface`, from [`Guide.ts`](../../src/core/Guide.ts). A
135
+ stateful, structured view over one parsed guide: parses `source` once via
136
+ `@orkestrel/markdown` and never touches the filesystem — `Guide` has no notion of "where"
137
+ the guide came from, only its markdown text. Every accessor returns the same cached,
138
+ readonly array on every call. See [`## Methods`](#methods) for its public call-signature
139
+ surface.
140
+
141
+ ### `Source`
142
+
143
+ The implementing class of `SourceInterface`, from [`Source.ts`](../../src/core/Source.ts). A
144
+ pure reflection over a consumer-supplied file inventory (root-relative path → file text) plus
145
+ a module scope — reflects exported symbols (`exportsFrom`) and interface/class methods
146
+ (`declarationBody` + `memberMethods`) with text-only line scanners, never the TypeScript
147
+ compiler API or the filesystem. `exports()` is computed once on first access and cached; the
148
+ consumer gathers `files` however their environment allows (`node:fs` in a Node script,
149
+ `import.meta.glob` in a browser/vitest run). See [`## Methods`](#methods) for its public
150
+ call-signature surface.
151
+
152
+ ## Methods
153
+
154
+ The public methods of each behavioral interface — one table per type, keyed by its
155
+ backticked name (AGENTS §22).
156
+
157
+ #### `GuideInterface`
158
+
159
+ | Method | Returns | Behavior |
160
+ | ---------- | -------------------------- | ------------------------------------------------------------------------------------------- |
161
+ | `sections` | `readonly string[]` | The `##` heading names, in document order — the non-vacuousness guard for section presence. |
162
+ | `surface` | `readonly SurfaceSymbol[]` | Every `## Surface` identifier + kind — table rows union backticked entity headings. |
163
+ | `methods` | `readonly MethodGroup[]` | One `MethodGroup` per documented behavioral interface in `## Methods`. |
164
+ | `links` | `readonly string[]` | Every link href in the guide, including table cells. |
165
+ | `tests` | `readonly string[]` | The relative test links declared under `## Tests`. |
166
+ | `patterns` | `readonly string[]` | Every fenced `ts` code block's body text, whole document. |
167
+
168
+ #### `SourceInterface`
169
+
170
+ | Method | Returns | Behavior |
171
+ | ---------- | -------------------------- | ---------------------------------------------------------------------------------------------------------- |
172
+ | `exports` | `readonly SurfaceSymbol[]` | Every module-scope export, including type-only, by (name, kind). |
173
+ | `methods` | `readonly string[]` | The call-signature members of the `class` / `interface` named `name`. |
174
+ | `exists` | `boolean` | Whether a workspace-root-relative path exists in the inventory. |
175
+ | `hidden` | `readonly SurfaceSymbol[]` | Every module-scope declaration LACKING `export` (AGENTS §5). |
176
+ | `examples` | `readonly string[]` | The exported functions (or, given `name`, the members of `name`) whose preceding JSDoc carries `@example`. |
177
+
178
+ ## The extraction model
179
+
180
+ `Guide` parses a guide's markdown once (via `@orkestrel/markdown`'s `createMarkdown`) and
181
+ caches five projections at construction — `sections`, `surface`, `methods`, `links`, `tests`
182
+ — so every accessor is a cheap array return, not a re-parse. `extractSurface` scopes to the
183
+ `## Surface` section (`sectionBlocks`) and unions two sources of identifiers: every table's
184
+ column-0 code span (kind read from the column whose header text is `Kind`, located
185
+ positionally so it survives reordering) and every backticked H3 entity heading (a class
186
+ documented outside a table, kind fixed to `'class'`). `extractMethods` scopes to
187
+ `## Methods`: an H4 whose first code span sets the current interface name, and the very next
188
+ table becomes that interface's `MethodGroup`. Both extractors normalize every identifier
189
+ through `identifierOf`, stripping a generic-parameter annotation (`` `WidgetInterface<T>` ``
190
+ → `WidgetInterface`) so the bijection key is always the bare name. `extractLinks` walks the
191
+ whole AST for every `link` node (table cells included); `extractTests` does the same walk
192
+ scoped to the `## Tests` section only.
193
+
194
+ `Source` never parses markdown or touches disk — it scans a consumer-supplied file
195
+ inventory's TEXT with regex-based line scanners. `exportsFrom` matches
196
+ `^export (?:async )?(function\*?|class|const|interface|type) (\w+)` per line, deduped by
197
+ (kind, name). `declarationBody` locates a named `export class` / `export interface` head
198
+ (joining an oxfmt-wrapped signature via `joinHead` until a line ends in `{`), then collects
199
+ body lines to the column-0 `}`. `memberMethods` matches
200
+ `^\t(?:async )?\*?(\w+)(<[^>]*>)?\??\(` against those body lines — plain / `async` /
201
+ generator / optional methods count; getters, setters, `static` members, and `#` privates
202
+ never match (their keyword or sigil breaks the `name(` shape), and `constructor` is filtered
203
+ out of `Source.methods`. `moduleKeys` scopes the inventory to one `GuideModule`'s `.ts`
204
+ files, excluding each scope directory's own `index.ts` and any `*.test.ts` file. `hiddenFrom`
205
+ runs the mirror-image scan — the same five-kind grammar, but only lines LACKING the `export`
206
+ keyword — so `Source.hidden()` mechanically asserts AGENTS §5's export-discipline rule and
207
+ catches a hidden declaration the surface bijection alone would never see (it never appears as
208
+ an export, so it never shows up as a missing Surface row either).
209
+
210
+ ## The check catalog
211
+
212
+ Every guides-parity check reduces to `expect([]).toEqual([])`, paired with a non-vacuousness
213
+ guard so a renamed heading fails loudly instead of passing on an empty extraction:
214
+
215
+ - **SB — Surface bijection (kind folded in).** `guide.surface()` vs `source.exports()`,
216
+ `missingSymbols` both directions over `symbolKey` — a symbol may drift in neither name nor
217
+ kind. Guard: `guide.surface().length > 0`.
218
+ - **MB — Methods bijection + class-no-extra.** Per `MethodGroup`, its `methods` vs
219
+ `source.methods(group.interface)`, `findMissing` both directions; then, by the
220
+ `XInterface → X` naming convention, `findMissing(source.methods('X'), group.methods)` must
221
+ also be empty — the implementing class exposes no undocumented public method. Guard:
222
+ `group.methods.length > 0`.
223
+ - **LI — Link integrity.** `guide.links()`, dropping `isExternalLink` hrefs, `resolveLink`
224
+ the rest against the guide's own path, keep those failing `source.exists`.
225
+ - **TE — Tests-link existence.** `guide.tests()`, `resolveLink` + `source.exists`, keep the
226
+ missing.
227
+ - **NV — Non-vacuousness.** `parseManifest` yields at least one entry; each guide's
228
+ `surface()` and every `MethodGroup` is non-empty — the guard behind every other check.
229
+ - **EX — Examples presence.** A documented symbol "has an example" when its bare name
230
+ appears (word boundary) in any of `guide.patterns()`'s fence bodies, OR its source
231
+ JSDoc carries an `@example` block (`source.examples()` / `source.examples(name)`).
232
+ Applies to every `function`-kind `Surface` symbol and every `MethodGroup` member.
233
+ Presence-only — fence and JSDoc CONTENT are never checked. `findUnexampled` is the
234
+ comparison. Guard: the SB/MB extractions this check reuses already prove non-vacuous.
235
+ - **FI — Fence-import reality.** Every `import { ... } from 'specifier'` in a
236
+ `guide.patterns()` fence, for a SELF specifier (this repo's own package name / path
237
+ alias), imports only names that exist in `source.exports()`. `fenceImports` parses the
238
+ statement; `findMissing` diffs the imported names against `source.exports()`'s names.
239
+
240
+ ## The pure file-inventory model
241
+
242
+ Neither `Guide` nor `Source` ever imports `node:fs` or any other I/O primitive — `Source`'s
243
+ construction input (`SourceOptions.files`) is a plain `Readonly<Record<string, string>>` the
244
+ CONSUMER gathers however their runtime allows: a recursive `node:fs` walk in a Node vitest
245
+ run, `import.meta.glob('/**/*.ts', { eager: true, query: '?raw', import: 'default' })` in a
246
+ browser/vitest run, or a static bundle in any other environment. This keeps the package
247
+ itself environment-agnostic while every check still runs against real, on-disk truth in the
248
+ consumer's own test.
249
+
250
+ ## Patterns
251
+
252
+ ### Construct a `Guide` from markdown text
253
+
254
+ ```ts
255
+ import { createGuide } from '@orkestrel/guide'
256
+
257
+ const guide = createGuide('## Surface\n\n| Name | Kind |\n| --- | --- |\n| `X` | class |')
258
+ guide.surface() // [{ name: 'X', kind: 'class' }]
259
+ guide.sections() // ['Surface']
260
+ ```
261
+
262
+ ### Construct a `Source` from an inline files record
263
+
264
+ ```ts
265
+ import { createSource } from '@orkestrel/guide'
266
+
267
+ const source = createSource({
268
+ files: {
269
+ 'src/core/Guide.ts': 'export class Guide {}\n',
270
+ 'src/core/types.ts': 'export interface GuideInterface {\n\tsections(): void\n}\n',
271
+ },
272
+ module: 'src/core',
273
+ })
274
+ source.exports() // [{ name: 'Guide', kind: 'class' }, { name: 'GuideInterface', kind: 'interface' }]
275
+ source.methods('GuideInterface') // ['sections']
276
+ source.exists('src/core/Guide.ts') // true
277
+ ```
278
+
279
+ ### The bijection assertion shape
280
+
281
+ ```ts
282
+ import { createGuide, createSource, missingSymbols } from '@orkestrel/guide'
283
+
284
+ const guide = createGuide('## Surface\n\n| Name | Kind |\n| --- | --- |\n| `Guide` | class |')
285
+ const source = createSource({
286
+ files: { 'src/core/Guide.ts': 'export class Guide {}\n' },
287
+ module: 'src/core',
288
+ })
289
+
290
+ // Every check collapses to an empty-array assertion, both directions.
291
+ missingSymbols(source.exports(), guide.surface()) // [] — every export is documented
292
+ missingSymbols(guide.surface(), source.exports()) // [] — every documented symbol is real
293
+ ```
294
+
295
+ ## Tests
296
+
297
+ - [`tests/src/core/helpers.test.ts`](../../tests/src/core/helpers.test.ts) — `moduleDirs` / `moduleKeys` / `symbolKey` / `findMissing` / `missingSymbols` / `isExternalLink` / `resolveLink` / `firstCode` / `identifierOf` / `kindIndex`.
298
+ - [`tests/src/core/parsers.test.ts`](../../tests/src/core/parsers.test.ts) — `exportsFrom` / `joinHead` / `declarationBody` / `memberMethods` / `sectionBlocks` / `extractSurface` / `extractMethods` / `extractLinks` / `extractTests` / `parseManifest`, incl. entity-heading surface and multi-dir Source cells.
299
+ - [`tests/src/core/validators.test.ts`](../../tests/src/core/validators.test.ts) — `isExportKind` / `isSurfaceSymbol` / `isMethodGroup` / `isManifestEntry`.
300
+ - [`tests/src/core/shapers.test.ts`](../../tests/src/core/shapers.test.ts) — per-shape guard exactness, JSON Schema essentials, seeded generate round-trips, parse rebuilds.
301
+ - [`tests/src/core/factories.test.ts`](../../tests/src/core/factories.test.ts) — `createGuide` / `createSource` + the compiled symbol/group/manifest contracts.
302
+ - [`tests/src/core/Guide.test.ts`](../../tests/src/core/Guide.test.ts) — `Guide`'s five cached projections, constructed once, called repeatedly.
303
+ - [`tests/src/core/Source.test.ts`](../../tests/src/core/Source.test.ts) — `Source`'s pure reflection against an in-memory fixture inventory, incl. `memberMethods` on every excluded shape (getters, setters, `static`, `#` privates, `constructor`).
304
+ - [`tests/guides/src/parity.test.ts`](../../tests/guides/src/parity.test.ts) — the drop-in guides-parity suite, run against THIS repo's own `guides/README.md` manifest — the self-dogfooding acceptance criterion.
305
+
306
+ ## See also
307
+
308
+ - `AGENTS.md` (workspace root) — the rules; §22 documentation-as-contracts.
309
+ - `PROPOSAL.md` (workspace root) — the design proposal this package implements; §5 the check catalog, §6 the drop-in, §7 source-scanning fidelity.
310
+ - [`README.md`](../README.md) — the guides index.
311
+ - [`markdown.md`](markdown.md) — the dependency mirror for `@orkestrel/markdown`, the AST/parse layer `Guide` and `Source` are built on.
312
+ </content>