@shoalrun/cli 0.1.0-beta.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/CODE-REFERENCES.md +137 -0
  3. package/README.md +8 -5
  4. package/adapters/python/requirements.txt +1 -0
  5. package/adapters/python/shoalrun_reference.py +234 -0
  6. package/agent-docs/.well-known/shoalrun/navigation/project-updates-50f26fe22670.txt +4 -2
  7. package/agent-docs/SNAPSHOT.json +5 -5
  8. package/agent-docs/about.md +3 -3
  9. package/agent-docs/api-docs/code-reference-settings.md +160 -0
  10. package/agent-docs/api-docs/code-reference-settings.sections/catalog-settings.md +50 -0
  11. package/agent-docs/api-docs/code-reference-settings.sections/complete-reference-page.md +46 -0
  12. package/agent-docs/api-docs/code-reference-settings.sections/converter-snapshot.md +58 -0
  13. package/agent-docs/api-docs/code-reference-settings.sections/descriptions-type-links-and-source-links.md +40 -0
  14. package/agent-docs/api-docs/code-reference-settings.sections/html-markdown-and-agents.md +38 -0
  15. package/agent-docs/api-docs/code-reference-settings.sections/metadata-and-selection.md +38 -0
  16. package/agent-docs/api-docs/code-reference-settings.sections/publication-sources.md +54 -0
  17. package/agent-docs/api-docs/code-reference-settings.sections/python-adapter-limits.md +38 -0
  18. package/agent-docs/api-docs/code-reference-settings.sections/related-reading.md +32 -0
  19. package/agent-docs/api-docs/code-reference-settings.sections/symbol-fields.md +56 -0
  20. package/agent-docs/api-docs/code-references.md +143 -0
  21. package/agent-docs/author/agent-overview.md +2 -2
  22. package/agent-docs/changelog/code-api-references.md +44 -0
  23. package/agent-docs/changelog.md +9 -1
  24. package/agent-docs/cli.md +11 -1
  25. package/agent-docs/cli.sections/audit-a-project-and-api-sources.md +1 -1
  26. package/agent-docs/cli.sections/author-pages.md +1 -1
  27. package/agent-docs/cli.sections/build-and-serve.md +1 -1
  28. package/agent-docs/cli.sections/inspect-config-and-operations.md +1 -1
  29. package/agent-docs/cli.sections/npm-scripts-in-this-repository.md +1 -1
  30. package/agent-docs/cli.sections/related-reading.md +1 -1
  31. package/agent-docs/cli.sections/search-and-chat.md +1 -1
  32. package/agent-docs/cli.sections/start-a-project.md +1 -1
  33. package/agent-docs/cli.sections/validate-code-reference-snapshots.md +41 -0
  34. package/agent-docs/cli.sections/working-tree-authoring-tools.md +1 -1
  35. package/agent-docs/config/apis.md +5 -1
  36. package/agent-docs/config/examples.md +3 -3
  37. package/agent-docs/config/examples.sections/feature-showcase.md +3 -3
  38. package/agent-docs/config/examples.sections/minimal-site.md +2 -2
  39. package/agent-docs/config/examples.sections/multilingual-help-center.md +2 -2
  40. package/agent-docs/config/examples.sections/parcel-api-documentation.md +2 -2
  41. package/agent-docs/config/examples.sections/related-reading.md +2 -2
  42. package/agent-docs/config/examples.sections/shared-company-preset.md +2 -2
  43. package/agent-docs/config.md +2 -2
  44. package/agent-docs/configuration.md +2 -2
  45. package/agent-docs/configuration.sections/build-several-sites-from-shared-content.md +1 -1
  46. package/agent-docs/configuration.sections/change-inherited-lists-deliberately.md +1 -1
  47. package/agent-docs/configuration.sections/extract-a-section-with-include.md +1 -1
  48. package/agent-docs/configuration.sections/inspect-and-validate-changes.md +1 -1
  49. package/agent-docs/configuration.sections/related-reading.md +1 -1
  50. package/agent-docs/configuration.sections/separate-config-inheritance-from-page-metadata.md +1 -1
  51. package/agent-docs/configuration.sections/share-defaults-through-extends.md +1 -1
  52. package/agent-docs/configuration.sections/start-with-one-working-file.md +2 -2
  53. package/agent-docs/configuration.sections/understand-the-three-stages.md +1 -1
  54. package/agent-docs/configuration.sections/understand-where-paths-point.md +1 -1
  55. package/agent-docs/documents.json +609 -215
  56. package/agent-docs/features.md +3 -2
  57. package/agent-docs/provenance.json +6 -6
  58. package/agent-docs/publish/api-references.md +3 -1
  59. package/agent-docs/publish/api-references.sections/author-the-reference-page.md +3 -1
  60. package/agent-docs/publish/api-references.sections/build-and-verify-the-output.md +3 -1
  61. package/agent-docs/publish/api-references.sections/inspect-the-machine-readable-contract.md +3 -1
  62. package/agent-docs/publish/api-references.sections/next-steps.md +3 -1
  63. package/agent-docs/publish/api-references.sections/preview-a-small-api-reference.md +3 -1
  64. package/agent-docs/publish/api-references.sections/put-the-spec-in-your-project.md +3 -1
  65. package/agent-docs/publish/api-references.sections/register-the-operation.md +3 -1
  66. package/agent-docs/publish/api-references.sections/related-reading.md +3 -1
  67. package/agent-docs/publish/api-references.sections/review-imported-content-before-publishing.md +3 -1
  68. package/agent-docs/publish/api-references.sections/what-readers-get.md +3 -1
  69. package/agent-docs/search-index.json +1 -1
  70. package/agent-docs/start.md +3 -3
  71. package/agent-docs/start.sections/add-the-page-to-navigation.md +2 -2
  72. package/agent-docs/start.sections/build-and-check-both-outputs.md +2 -2
  73. package/agent-docs/start.sections/build-this-site.md +2 -2
  74. package/agent-docs/start.sections/check-writing-style.md +2 -2
  75. package/agent-docs/start.sections/create-a-page.md +2 -2
  76. package/agent-docs/start.sections/make-it-yours.md +2 -2
  77. package/agent-docs/start.sections/migrating-from-github-packages.md +2 -2
  78. package/agent-docs/start.sections/prerequisites.md +2 -2
  79. package/agent-docs/start.sections/related-reading.md +2 -2
  80. package/agent-docs/start.sections/start-your-own-project.md +3 -3
  81. package/agent-docs/status.md +6 -4
  82. package/agent-docs/styling/themes.md +7 -3
  83. package/agent-docs/what-is-shoalrun.md +2 -2
  84. package/dist/artifact-bundle.d.ts +1 -0
  85. package/dist/artifact-bundle.js +11 -3
  86. package/dist/cli-discovery.d.ts +1 -1
  87. package/dist/cli-discovery.js +1 -0
  88. package/dist/cli.js +7 -0
  89. package/dist/code-reference-model.d.ts +85 -0
  90. package/dist/code-reference-model.js +73 -0
  91. package/dist/code-references.d.ts +23 -0
  92. package/dist/code-references.js +371 -0
  93. package/dist/compiler-config.d.ts +1 -0
  94. package/dist/compiler.js +4 -3
  95. package/dist/config.js +1 -1
  96. package/dist/content-source.d.ts +1 -0
  97. package/dist/content-workspace.d.ts +1 -0
  98. package/dist/content-workspace.js +2 -0
  99. package/dist/fonts.d.ts +9 -0
  100. package/dist/fonts.js +58 -3
  101. package/dist/page-materialization.js +3 -2
  102. package/dist/page-renderer.d.ts +1 -0
  103. package/dist/page-renderer.js +3 -1
  104. package/dist/publication-render.d.ts +2 -1
  105. package/dist/publication-render.js +2 -2
  106. package/dist/representation-tree.js +4 -0
  107. package/examples/code-references/content/start.md +53 -0
  108. package/examples/code-references/docs.config.yaml +262 -0
  109. package/examples/code-references/python/orchard/__init__.py +11 -0
  110. package/examples/code-references/python/orchard/client.py +212 -0
  111. package/examples/code-references/python/orchard/errors.py +31 -0
  112. package/examples/code-references/python/orchard/models.py +69 -0
  113. package/examples/code-references/python/orchard/pagination.py +39 -0
  114. package/examples/code-references/reference.json +1244 -0
  115. package/package.json +8 -1
  116. package/schemas/code-reference.schema.json +306 -0
  117. package/skills/shoalrun-author/SKILL.md +4 -0
  118. package/skills/shoalrun-author/references/code-api-references.md +15 -0
  119. package/styles/site.css +61 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0 — 2026-11-06
4
+
5
+ - Generate [code API references](https://www.shoalrun.com/api-docs/code-references)
6
+ from Python source and docstrings, or bring a converter using the shared JSON
7
+ snapshot format. Choose symbol, class, module, or optional complete-page
8
+ references with linked types, field defaults, and native content delivery.
9
+ - Preload local custom body and heading fonts, cache custom assets by content,
10
+ and revalidate bundled fonts. Match the preset's font style and CSS weight
11
+ selection, including overlapping face registrations.
12
+ - Tighten shared page-header spacing around metadata and page actions.
13
+ - Correct npm distribution tagging and verify it separately from upload.
14
+ Stable releases use `latest`; later prereleases preserve that stable channel.
15
+
16
+ Upgrade to CLI `1.0.0` and rebuild. Python extraction is optional and runs
17
+ explicitly; normal site builds consume the saved snapshot without Python.
18
+
19
+
3
20
  ## 0.1.0-beta.1 — 2026-11-05
4
21
 
5
22
  - Add custom code-fence highlighting with local TextMate JSON grammars and aliases.
@@ -0,0 +1,137 @@
1
+ # Code API references
2
+
3
+ Code API references describe a library's classes, functions, methods, fields, parameters, return values, and exceptions. OpenAPI references describe HTTP endpoints. Guides explain how to accomplish tasks; code references explain the interfaces used to accomplish them.
4
+
5
+ Shoalrun consumes a versioned JSON snapshot and generates native reference pages. The pages participate in navigation, search, publication selection, reader filters, customer-profile pruning, Markdown output, content exports, and PDF exports. They use the site's theme and a compact code-reference layout with signatures and member tables.
6
+
7
+ ## Generate Python references
8
+
9
+ The optional Python adapter reads local source with Griffe. It doesn't import the documented package or enable runtime inspection, extensions, or external alias resolution. It requires Python 3.11 or newer and the pinned `griffelib` dependency. Normal Shoalrun builds consume the JSON snapshot and don't require Python.
10
+
11
+ From a checkout of this package:
12
+
13
+ ```sh
14
+ python3.11 -m venv .venv-reference
15
+ .venv-reference/bin/python -m pip install --require-hashes -r adapters/python/requirements.txt
16
+ .venv-reference/bin/python adapters/python/shoalrun_reference.py \
17
+ --root examples/code-references/python --module orchard \
18
+ --out examples/code-references/reference.json
19
+ shoalrun references check examples/code-references/reference.json
20
+ shoalrun build examples/code-references/docs.config.yaml site-dist
21
+ ```
22
+
23
+ Use `--style google`, `--style numpy`, or `--style sphinx` for the docstring convention. Review the generated snapshot before publishing it. Public source declarations and their docstrings can contain sensitive information even when extraction doesn't execute code.
24
+
25
+ The adapter emits locally defined symbols, skips aliases and underscore-prefixed members except constructors, and rejects symlinks in its declared source root. It doesn't expand inherited members, execute decorators, resolve external re-exports, or infer undocumented behavior. Its example SDK stores records in memory and doesn't contact a live service.
26
+
27
+ ## Configure a catalog
28
+
29
+ ```yaml
30
+ codeReferences:
31
+ sdk:
32
+ source: { kind: file, path: ./reference.json }
33
+ pathPrefix: /reference/code
34
+ symbols:
35
+ orchard.client.Client:
36
+ documentId: reference.client
37
+ slug: /reference/client
38
+ navigation:
39
+ - { id: client, doc: reference.client }
40
+ ```
41
+
42
+ Paths resolve relative to the file that declares them and must stay inside the project root. Generated pages use `type: code-reference`. The `programmingLanguage` in a snapshot is distinct from a publication's language, such as `en`.
43
+
44
+ Set `symbols.<symbol-id>.documentId` and `slug` to keep deliberate public identities and URLs. Without these settings, Shoalrun generates deterministic IDs and routes from the catalog and symbol IDs. Moving or renaming a symbol changes those defaults.
45
+
46
+ Use `include` and `exclude` lists of exact symbol IDs to select public symbols. Private symbols and descendants of private symbols aren't published. `metadata` supplies ordinary page metadata; `symbols.<id>.metadata` overrides it for one symbol. A symbol's explicit `shared` or `requires` classification replaces the catalog's classification. Each symbol's feature requirements are evaluated separately; classification isn't inherited from its parent symbol. Project frontmatter defaults and enforced settings still apply.
47
+
48
+ For independent product versions, replace `source` with explicit source bindings:
49
+
50
+ ```yaml
51
+ codeReferences:
52
+ sdk:
53
+ publications:
54
+ - product: sdk
55
+ channel: production
56
+ version: v1
57
+ language: en
58
+ source: { kind: file, path: ./reference-v1.json }
59
+ - product: sdk
60
+ channel: production
61
+ version: v2
62
+ language: en
63
+ source: { kind: file, path: ./reference-v2.json }
64
+ ```
65
+
66
+ Declare those products and versions in `publications` as usual. Each binding uses its own snapshot. Links and member tables include only eligible pages in the same catalog and publication.
67
+
68
+ ## Bring another converter
69
+
70
+ A converter for Kotlin or another language can emit the same snapshot format. Shoalrun doesn't need to load that converter during compilation. Run it explicitly in your own build workflow, validate its output, and point a catalog at the resulting file.
71
+
72
+ The contract is [schemas/code-reference.schema.json](schemas/code-reference.schema.json). A minimal snapshot looks like this:
73
+
74
+ ```json
75
+ {
76
+ "schemaVersion": 1,
77
+ "kind": "shoalrun-code-reference",
78
+ "programmingLanguage": "kotlin",
79
+ "producer": { "name": "example-converter", "version": "1.0.0" },
80
+ "symbols": [{
81
+ "id": "example.greet",
82
+ "name": "greet",
83
+ "kind": "function",
84
+ "summary": "Create a greeting.",
85
+ "signatures": ["fun greet(name: String): String"],
86
+ "parameters": [{ "name": "name", "type": "String", "description": "Reader name." }],
87
+ "returns": { "type": "String", "description": "The greeting." }
88
+ }]
89
+ }
90
+ ```
91
+
92
+ This illustrates the converter contract; it isn't a bundled Kotlin extractor. Supported symbol kinds are module, namespace, class, interface, enum, type, function, method, constructor, property, attribute, and constant. Optional fields include `parent`, `description`, `raises`, `yields`, `examples`, `seeAlso`, relative source provenance, and a source digest. Symbol IDs must be unique, referenced IDs must exist, and parent ownership must be acyclic. `shoalrun references check` performs these semantic checks as well as schema validation. JavaScript tools can import `validateCodeReference` from `@shoalrun/cli/references`.
93
+
94
+ Descriptions use inert Markdown, with raw HTML stripped. They don't run MDX or expand project directives. Use `[Record](symbol:orchard.models.Record)` to link a symbol in the same catalog and publication. External links support HTTPS and mailto; relative links and images aren't supported. Use code examples for runnable-looking snippets; extraction doesn't execute or verify those examples.
95
+
96
+ ## Types, fields, and source links
97
+
98
+ Converters can supply `typeReferences`, mapping the exact type text to a symbol ID in the same snapshot. Shoalrun links those types in parameter, return, yield, base-class, exception, and field descriptions only when the target is published in the same catalog and publication. Excluded targets stay plain text. Signatures retain copyable code. Shoalrun doesn't guess targets from short names.
99
+
100
+ Field symbols can supply `annotation`, `required`, and `default`. A nullable field can still be required; `required` describes whether a value must be supplied, not whether the type admits null. Unknown requirements stay unmarked. Defaults are source expressions, not evaluated values.
101
+
102
+ Class symbols can supply `bases` and `constructorSignature`. The Python adapter extracts local constructors and synthesizes simple local dataclass constructors; it leaves unsupported cases unspecified. Dataclass factory defaults appear as `<factory>` in the constructor signature and retain their source expression in the field details.
103
+
104
+ To show **View source**, configure a catalog's `sourceLink.baseUrl` with an HTTPS repository URL ending at the directory that contains the snapshot's relative source paths. Include the desired immutable commit or release in that URL. For example, a base URL ending in `/blob/<commit>/src` and a source path of `sdk/models.py` produce `/blob/<commit>/src/sdk/models.py#L12` for line 12. Query strings, fragments, and embedded credentials aren't accepted in the base URL. Shoalrun doesn't download source or publish source files automatically. Source links are omitted when no base URL is configured.
105
+
106
+ ## Choose separate or grouped pages
107
+
108
+ Configure each catalog independently:
109
+
110
+ ```yaml
111
+ codeReferences:
112
+ sdk:
113
+ source: { kind: file, path: ./reference.json }
114
+ layout: classes
115
+ singlePage:
116
+ documentId: reference.sdk.complete
117
+ slug: /reference/sdk/all
118
+ title: Complete SDK reference
119
+ navigation:
120
+ - { id: complete-reference, doc: reference.sdk.complete }
121
+ ```
122
+
123
+ `layout` accepts:
124
+
125
+ | Value | Reading experience |
126
+ | --- | --- |
127
+ | `symbols` | Separate symbol pages with linked member tables. This is the default. |
128
+ | `classes` | Class pages include member details inline. Module pages include their direct functions and class overviews. |
129
+ | `modules` | Module pages include details for the classes, functions, and fields in that module. Submodules have their own grouping. |
130
+
131
+ Individual symbol pages and URLs remain available in every layout. Choose class or module pages for the main navigation when you want fewer navigation entries. Grouping controls the reading experience; it doesn't remove the underlying symbol pages or change their content-delivery identities.
132
+
133
+ `singlePage` independently adds a complete reference for the catalog, containing every eligible symbol in the selected publication. It works with any layout. Omit it or set `singlePage: false` to disable it. Within a grouped or complete page, links to symbols included on that page jump to their sections. Links to other symbols retain their individual URLs. Generated heading anchors distinguish repeated headings, such as Parameters and Returns.
134
+
135
+ The complete page defaults to document ID `reference.<catalog>.complete` and slug `<pathPrefix>/all`. You can configure its `documentId`, `slug`, `title`, `summary`, and `metadata`. Its metadata inherits the catalog's settings; an explicit `shared` or `requires` classification replaces the catalog classification. Strictly classified projects must classify the complete page as well as the symbols.
136
+
137
+ Each combined page contains only symbols already allowed by its publication, profile grants, visibility, and build audience selection. Enabling a complete page doesn't grant access to excluded content. If no symbols are eligible, no empty complete page is emitted. Complete and grouped pages support the same HTML, Markdown, content export, and PDF workflows as other reference pages. Their default outline depth is two; explicit frontmatter settings can override it.
package/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  Shoalrun builds a documentation website, Markdown pages, and application content from the same Markdown and MDX source. People can browse the site; applications and agents can read stable documents, sections, and snippet instances. Native components keep essential instructions and API details readable in both HTML and Markdown.
4
4
 
5
- This package provides the `shoalrun` CLI, starter projects, agent skills, and offline documentation. It requires Node.js 22.12 or newer. This beta is distributed through the public npm registry. Installation requires no npm account or package token.
5
+ This package provides the `shoalrun` CLI, starter projects, agent skills, and offline documentation. It requires Node.js 22.12 or newer. The package is distributed through the public npm registry. Installation requires no npm account or package token.
6
6
 
7
7
  ## Install
8
8
 
9
9
  From the project where you want to keep your documentation:
10
10
 
11
11
  ```sh
12
- npm install --save-dev --save-exact @shoalrun/cli@0.1.0-beta.1
12
+ npm install --save-dev --save-exact @shoalrun/cli@1.0.0
13
13
  ```
14
14
 
15
15
  ## Create and preview a site
@@ -38,7 +38,8 @@ The [features overview](https://www.shoalrun.com/features) describes the complet
38
38
  | --- | --- |
39
39
  | A documentation site | Pages, navigation, local preview, themes, palettes, fonts, and project CSS. |
40
40
  | Reusable procedures | Recipes, shared ingredients, and cookbooks that help readers choose a complete task. |
41
- | API reference | OpenAPI operation pages, code examples, focused contracts, and an explicit source-review gate. |
41
+ | HTTP API references | OpenAPI operation pages, code examples, focused contracts, and an explicit source-review gate. |
42
+ | Code API references | [Python docstrings and converter-supplied symbols](CODE-REFERENCES.md), rendered as native reference pages. |
42
43
  | Several product releases | Independent product versions and defaults, publication routes, and a header publication selector. |
43
44
  | Customer editions | External profiles, SKU revisions, feature grants, and conditional pages or sections. |
44
45
  | Application help | HTML and Markdown exports, stable page/section/snippet references, static and REST clients, and instance-safe mounting. |
@@ -51,6 +52,8 @@ Local builds and keyword search need no provider credentials. Semantic retrieval
51
52
 
52
53
  Use `site.logo`, `site.favicon`, and header settings for site identity. Choose a theme preset and configure `theme.palette`, `theme.mode`, and `theme.fonts`. Use `site.customCss` for changes beyond those settings. Customer profiles can supply deployment-owned branding without storing customer records in the shared source.
53
54
 
55
+ Local body and heading fonts registered through `theme.fonts` preload automatically. Custom font URLs include a content digest so browsers can cache them and fetch fresh files after font edits. Preloading starts the download earlier; first-visit layout movement still depends on the font metrics and connection.
56
+
54
57
  Check controls and interactive states as well as article text: a dark header can contain light inputs, menus, and search results. Palette validation doesn't check arbitrary project CSS. See [themes and fonts](https://www.shoalrun.com/styling/themes), the [CSS reference](https://www.shoalrun.com/author/css), and [deployment profiles](DEPLOYMENT-PROFILES.md).
55
58
 
56
59
  ## Export styled PDFs
@@ -346,14 +349,14 @@ with a Hero replaces the standard page header and does not display its dates.
346
349
  If you previously used the private GitHub Packages alpha, check `npm config get @shoalrun:registry`. Remove or replace any `@shoalrun:registry=https://npm.pkg.github.com` entry in your project or user `.npmrc`. You can explicitly select the public registry for the upgrade:
347
350
 
348
351
  ```sh
349
- npm install --save-dev --save-exact @shoalrun/cli@0.1.0-beta.1 --@shoalrun:registry=https://registry.npmjs.org
352
+ npm install --save-dev --save-exact @shoalrun/cli@1.0.0 --@shoalrun:registry=https://registry.npmjs.org
350
353
  ```
351
354
 
352
355
  Commit the updated package manifest and lockfile together. Check that the lockfile resolves `@shoalrun/cli` from `registry.npmjs.org`. Keep any credentials needed by other private dependencies; Shoalrun itself no longer needs a package token.
353
356
 
354
357
  ## Status and development
355
358
 
356
- This beta may change configuration and output formats between releases. No compatibility promise or maintained older release line is offered yet. Creating and previewing a basic site does not require a model provider or Chrome. Optional provider-backed features require their own configuration.
359
+ Shoalrun 1.0 is the stable release line. Pin exact versions and review release notes for configuration or output-contract changes. Older release lines aren't maintained. Creating and previewing a basic site does not require a model provider or Chrome. Optional provider-backed features require their own configuration.
357
360
 
358
361
  Shoalrun is maintainer-led and does not accept unsolicited pull requests at this time. The [repository development instructions](https://github.com/shoalrun/shoalrun#develop-the-framework) explain how to build and test your own checkout or fork.
359
362
 
@@ -0,0 +1 @@
1
+ griffelib==2.4.1 --hash=sha256:76bc562f936ad1ce198d2cf6d0731bd5c92a7d934b69ef828ef22ee0ffdb62ce
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env python3
2
+ """Convert local Python source to Shoalrun code-reference JSON without importing it."""
3
+ import argparse
4
+ import ast
5
+ import hashlib
6
+ import importlib.metadata
7
+ import json
8
+ import os
9
+ from pathlib import Path
10
+ import re
11
+ import sys
12
+
13
+ VERSION = "1.0.0"
14
+ EXTRACTOR_VERSION = "2.4.1"
15
+
16
+
17
+ def convert(root, module, style):
18
+ if importlib.metadata.version("griffelib") != EXTRACTOR_VERSION:
19
+ raise ValueError(f"This adapter requires griffelib=={EXTRACTOR_VERSION}")
20
+ import griffe
21
+
22
+ root = root.resolve(strict=True)
23
+ if not root.is_dir() or not re.fullmatch(r"[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*", module):
24
+ raise ValueError("Provide a local source directory and a dotted Python module name")
25
+ # Reject symlinks before the extractor can follow any source outside this root.
26
+ digest = hashlib.sha256()
27
+ count = size = 0
28
+ for directory, dirs, files in os.walk(root, followlinks=False):
29
+ for name in dirs + files:
30
+ if (Path(directory) / name).is_symlink():
31
+ raise ValueError("Python source roots must not contain symlinks")
32
+ for name in sorted(files):
33
+ file = Path(directory) / name
34
+ if file.suffix not in (".py", ".pyi"):
35
+ continue
36
+ data = file.read_bytes()
37
+ size += len(data)
38
+ count += 1
39
+ if count > 10000 or size > 32 * 1024 * 1024:
40
+ raise ValueError("Python source root exceeds 10,000 files or 32 MiB")
41
+ relative = file.relative_to(root).as_posix().encode()
42
+ digest.update(len(relative).to_bytes(4, "big") + relative + len(data).to_bytes(8, "big") + data)
43
+ dirs.sort()
44
+ target = root.joinpath(*module.split("."))
45
+ if not target.with_suffix(".py").is_file() and not (target / "__init__.py").is_file():
46
+ raise ValueError("Requested module must exist as Python source under --root")
47
+ loaded = griffe.load(module, search_paths=[root], try_relative_path=False,
48
+ extensions=griffe.Extensions(), allow_inspection=False,
49
+ force_inspection=False, resolve_aliases=False,
50
+ resolve_external=False, find_stubs_package=False,
51
+ docstring_parser=style)
52
+ symbols = []
53
+
54
+ def field_info(obj):
55
+ info = {}
56
+ if obj.annotation is not None:
57
+ info["annotation"] = str(obj.annotation)
58
+ if obj.value is not None:
59
+ info["default"] = str(obj.value)
60
+ parent = obj.parent
61
+ if parent and parent.is_class and "dataclass" in parent.labels and "instance-attribute" in obj.labels:
62
+ if obj.value is None:
63
+ info["required"] = True
64
+ elif str(obj.value).startswith("field("):
65
+ call = ast.parse(str(obj.value), mode="eval").body
66
+ options = {keyword.arg: keyword.value for keyword in call.keywords}
67
+ if "init" not in options:
68
+ info["required"] = not ("default" in options or "default_factory" in options)
69
+ elif isinstance(options["init"], ast.Constant) and options["init"].value is False:
70
+ info["required"] = False
71
+ else:
72
+ info["required"] = False
73
+ elif parent and parent.is_class and any(str(base) in ("TypedDict", "typing.TypedDict", "typing_extensions.TypedDict") for base in parent.bases):
74
+ annotation = str(obj.annotation).split("[", 1)[0].rsplit(".", 1)[-1]
75
+ info["required"] = annotation != "NotRequired" and (annotation == "Required" or str(parent.keywords.get("total", "True")) != "False")
76
+ return info
77
+
78
+ def type_references(obj, expressions):
79
+ references = {}
80
+ for expression in expressions:
81
+ if expression is None:
82
+ continue
83
+ if isinstance(expression, str):
84
+ for token in re.findall(r"[A-Za-z_]\w*(?:\.\w+)*", expression):
85
+ try:
86
+ references[token] = obj.resolve(token)
87
+ except griffe.NameResolutionError:
88
+ pass
89
+ else:
90
+ for part in [expression, *expression.iterate()]:
91
+ if isinstance(part, griffe.Expr) and part.reference_path:
92
+ references[str(part)] = part.reference_path
93
+ return references
94
+
95
+ def visit(obj, parent=None):
96
+ if obj.is_alias or (obj.name.startswith("_") and obj.name != "__init__"):
97
+ return
98
+ kind = obj.kind.value
99
+ if kind == "function" and parent is not None and obj.parent.is_class:
100
+ kind = "constructor" if obj.name == "__init__" else "method"
101
+ if obj.is_class and any(str(base) in ("Enum", "IntEnum", "StrEnum", "enum.Enum", "enum.IntEnum", "enum.StrEnum") for base in obj.bases):
102
+ kind = "enum"
103
+ if kind == "type alias":
104
+ kind = "type"
105
+ symbol = {"id": obj.path, "name": obj.name, "kind": kind, "visibility": "public"}
106
+ if parent:
107
+ symbol["parent"] = parent
108
+ file = obj.filepath.resolve(strict=True)
109
+ relative = file.relative_to(root).as_posix()
110
+ symbol["source"] = {"path": relative}
111
+ if obj.lineno:
112
+ symbol["source"]["line"] = obj.lineno
113
+ sections = obj.docstring.parsed if obj.docstring else []
114
+ prose = []
115
+ parameter_docs = {}
116
+ for section in sections:
117
+ section_kind = section.kind.value
118
+ if section_kind == "text":
119
+ prose.append(section.value)
120
+ elif section_kind == "parameters":
121
+ parameter_docs.update({p.name: p for p in section.value})
122
+ elif section_kind == "raises":
123
+ symbol["raises"] = [{"name": str(p.annotation or p.name or "Exception"), "description": p.description} for p in section.value]
124
+ elif section_kind in ("returns", "yields") and len(section.value) == 1:
125
+ item = section.value[0]
126
+ symbol[section_kind] = {"description": item.description}
127
+ if item.annotation is not None:
128
+ symbol[section_kind]["type"] = str(item.annotation)
129
+ elif section_kind == "examples":
130
+ examples = []
131
+ for example_kind, value in section.value:
132
+ if example_kind.value == "text":
133
+ prose.append(value)
134
+ else:
135
+ examples.append({"code": value})
136
+ if examples:
137
+ symbol["examples"] = examples
138
+ else:
139
+ # Preserve less-common sections rather than silently losing their prose.
140
+ if obj.docstring:
141
+ prose = [obj.docstring.value]
142
+ symbol.pop("returns", None)
143
+ symbol.pop("yields", None)
144
+ symbol.pop("raises", None)
145
+ symbol.pop("examples", None)
146
+ break
147
+ if prose:
148
+ symbol["description"] = "\n\n".join(prose)
149
+ symbol["summary"] = prose[0].split("\n", 1)[0][:1000]
150
+ annotations = []
151
+ if obj.is_function:
152
+ annotations.extend([obj.returns, *(p.annotation for p in obj.parameters)])
153
+ prefix = "async def " if "async" in obj.labels else "def "
154
+ signatures = obj.overloads or [obj]
155
+ symbol["signatures"] = [prefix + entry.signature() for entry in signatures]
156
+ params = []
157
+ for parameter in obj.parameters:
158
+ if parameter.name in ("self", "cls") and obj.parent.is_class:
159
+ continue
160
+ detail = {"name": parameter.name}
161
+ if parameter.annotation is not None:
162
+ detail["type"] = str(parameter.annotation)
163
+ if parameter.default is not None:
164
+ detail["default"] = str(parameter.default)
165
+ if parameter.name in parameter_docs:
166
+ detail["description"] = parameter_docs[parameter.name].description
167
+ params.append(detail)
168
+ if params:
169
+ symbol["parameters"] = params
170
+ if obj.returns is not None:
171
+ symbol.setdefault("returns", {})["type"] = str(obj.returns)
172
+ elif obj.is_class:
173
+ annotations.extend(obj.bases)
174
+ if obj.bases:
175
+ symbol["bases"] = [str(base) for base in obj.bases]
176
+ constructor = obj.members.get("__init__")
177
+ if constructor is not None and not constructor.is_alias and constructor.is_function:
178
+ symbol["constructorSignature"] = constructor.signature(name=obj.name, return_type=False)
179
+ annotations.extend(p.annotation for p in constructor.parameters)
180
+ elif "dataclass" in obj.labels and not obj.bases and not any("init=" in str(decorator.value) or "kw_only=" in str(decorator.value) for decorator in obj.decorators):
181
+ # Synthesize only the simple local dataclass case; never execute decorators.
182
+ fields = [member for member in obj.attributes.values() if "instance-attribute" in member.labels]
183
+ if all(field_info(member).get("required") is not None and (not str(member.value).startswith("field(") or str(member.value).startswith("field(default_factory=")) for member in fields):
184
+ declarations = []
185
+ for member in fields:
186
+ info = field_info(member)
187
+ default = "<factory>" if str(member.value).startswith("field(default_factory=") else info.get("default")
188
+ declarations.append(member.name + (": " + info["annotation"] if "annotation" in info else "") + (" = " + default if default is not None else ""))
189
+ annotations.append(member.annotation)
190
+ symbol["constructorSignature"] = obj.name + "(" + ", ".join(declarations) + ")"
191
+ symbol["signatures"] = ["class " + obj.name + ("(" + ", ".join(map(str, obj.bases)) + ")" if obj.bases else "")]
192
+ elif obj.is_attribute:
193
+ annotations.append(obj.annotation)
194
+ symbol.update(field_info(obj))
195
+ annotation = ": " + str(obj.annotation) if obj.annotation is not None else ""
196
+ symbol["signatures"] = [obj.name + annotation]
197
+ annotations.extend(item["name"] for item in symbol.get("raises", []))
198
+ references = type_references(obj, annotations)
199
+ if references:
200
+ symbol["typeReferences"] = references
201
+ symbols.append(symbol)
202
+ for child in sorted(getattr(obj, "members", {}).values(), key=lambda item: item.name):
203
+ visit(child, obj.path)
204
+
205
+ visit(loaded)
206
+ known = {symbol["id"] for symbol in symbols}
207
+ for symbol in symbols:
208
+ if "typeReferences" in symbol:
209
+ symbol["typeReferences"] = {name: target for name, target in symbol["typeReferences"].items() if target in known}
210
+ if not symbol["typeReferences"]:
211
+ del symbol["typeReferences"]
212
+ return {"schemaVersion": 1, "kind": "shoalrun-code-reference", "programmingLanguage": "python",
213
+ "producer": {"name": "shoalrun-python", "version": VERSION, "extractorVersion": EXTRACTOR_VERSION},
214
+ "sourceDigest": digest.hexdigest(), "symbols": symbols}
215
+
216
+
217
+ def main():
218
+ parser = argparse.ArgumentParser(description=__doc__)
219
+ parser.add_argument("--root", type=Path, required=True)
220
+ parser.add_argument("--module", required=True)
221
+ parser.add_argument("--style", choices=("google", "numpy", "sphinx"), default="google")
222
+ parser.add_argument("--out", type=Path, required=True)
223
+ args = parser.parse_args()
224
+ try:
225
+ data = convert(args.root, args.module, args.style)
226
+ args.out.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf8")
227
+ except (ImportError, importlib.metadata.PackageNotFoundError):
228
+ parser.exit(1, "Install the adapter's pinned requirements in Python 3.11+ before running it.\n")
229
+ except (ValueError, OSError) as error:
230
+ parser.exit(1, f"{error}\n")
231
+
232
+
233
+ if __name__ == "__main__":
234
+ main()
@@ -28,13 +28,15 @@ Full-page token counts use cl100k_base. Navigation section ID: project-updates.
28
28
 
29
29
  - [Styled PDFs and custom code languages](https://www.shoalrun.com/changelog/pdf-custom-languages.md) — ID: change.pdf-custom-languages. changelog-entry; 487 tokens. Print branded pages, export a complete PDF manual, and highlight your own code language.
30
30
 
31
+ - [Shoalrun 1.0 — SDK references and smoother font loading](https://www.shoalrun.com/changelog/code-api-references.md) — ID: change.code-api-references. changelog-entry; 538 tokens. Generate code API references from source and docstrings, choose how readers browse them, and load custom fonts earlier.
32
+
31
33
  - [Authoring snapshot previews and review reports](https://www.shoalrun.com/changelog/authoring-preview.md) — ID: change.authoring-preview. changelog-entry; 554 tokens. Snapshot previews with coverage and freshness reports.
32
34
 
33
35
  - [Agent authoring, search, and documentation answers](https://www.shoalrun.com/changelog/authoring-search.md) — ID: change.authoring-search. changelog-entry; 468 tokens. Reviewed agent drafts, search improvements, and answer context.
34
36
 
35
37
  - [Working-tree authoring tools](https://www.shoalrun.com/changelog/authoring-tools.md) — ID: change.authoring-tools. changelog-entry; 384 tokens. Project discovery and captured working-tree validation.
36
38
 
37
- - [Changelog](https://www.shoalrun.com/changelog.md) — ID: reference.changelog. reference; 1472 tokens. Compiler, CLI, and reader changes.
39
+ - [Changelog](https://www.shoalrun.com/changelog.md) — ID: reference.changelog. reference; 1547 tokens. Compiler, CLI, and reader changes.
38
40
 
39
41
  - [MCP hosting, analytics, and component improvements](https://www.shoalrun.com/changelog/2026-10-17-mcp-hosting.md) — ID: change.mcp-hosting. changelog-entry; 1147 tokens. MCP hosting, analytics, and shared component content.
40
42
 
@@ -48,4 +50,4 @@ Full-page token counts use cl100k_base. Navigation section ID: project-updates.
48
50
 
49
51
  - [Non-goals](https://www.shoalrun.com/non-goals.md) — ID: guide.non-goals. explanation; 821 tokens. Scope limits for skill generation, agent orchestration, and reader-view authorization.
50
52
 
51
- - [Project status](https://www.shoalrun.com/status.md) — ID: guide.status. reference; 1918 tokens. Current capabilities, planned features, and known limits.
53
+ - [Project status](https://www.shoalrun.com/status.md) — ID: guide.status. reference; 1990 tokens. Current capabilities, planned features, and known limits.
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "sourceRepository": "https://github.com/shoalrun/docs",
4
- "sourceCommit": "01614c57a52c0157fb48fc12f434962f27f704e7",
5
- "siteBuildId": "30b28d69af454479a90a00f3b015448f97af9be0c46d153441408b899ccaf08f",
6
- "manifestDigest": "7337bcec6d88bb4934afff2eb68562244f9391af3d651ead5111fa2bf794c132",
7
- "indexDigest": "090e6b3f4bd7c9968b75b609a695e6ebd6f596a66135db41968c3905258a25d6",
8
- "packageIntegrity": "sha512-uigk3DRE8FClwgyo5kGeyPtB7CXkpLVEAQGhHxMd0exbFThqvvMxPGGVL7rohJdw+o0xSmFNK0U7/TvwLatyZw=="
4
+ "sourceCommit": "0af50d753fc59c1e332d5e710999e3219c3e2443",
5
+ "siteBuildId": "6b51e1bac3826424f2ec68156866ec40c1761f7765901fc6dfc2f2d4f24a8596",
6
+ "manifestDigest": "2cb3bc1d411d08d4156fb139c6257dd4e56ea79ea1aed38c85eb32986b2a1743",
7
+ "indexDigest": "a6c8b514805bfd808949c3d22a01deee1ae9c35b4c6e54009ae3866b21df2c6e",
8
+ "packageIntegrity": "sha512-TVNgFm5dtSyO7c+njfPC2fta5IWnIVALrTD0CYktEUeuFbKv0pdKg8C7ihBiQ3svOhXNnyjZSDF96N23lVl/Tw=="
9
9
  }
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  id: guide.about
3
3
  title: About Shoalrun
4
- summary: Learn what Shoalrun is exploring and where the beta stands.
4
+ summary: Learn what Shoalrun does and where the project is heading.
5
5
  type: overview
6
6
  area: project
7
7
  subarea: project-overview
@@ -12,7 +12,7 @@ canonical: /about
12
12
 
13
13
  > Documentation index: [llms.min.txt](https://www.shoalrun.com/llms.min.txt) · Document manifest: [documents.json](https://www.shoalrun.com/documents.json)
14
14
 
15
- **Updated: 2026-11-06.**
15
+ **Updated: 2026-11-07.**
16
16
 
17
17
  I'm CT Smith, and I'm building Shoalrun cause there's no such thing as a perfect doc tool. I have opinions, undirected rage, and curiosity about doc-building tools. I built my first documentation generator back in 2015. It was based on Jekyll and relied heavily (probably too heavily) on general templating and... you guessed it... *page metadata*.
18
18
 
@@ -42,7 +42,7 @@ API content follows the same approach. Local OpenAPI files can supply reference
42
42
 
43
43
  I'm building Shoalrun for tinkerers like me, who will never be fully satisfied with a mass market docs tool. Docs are never one-size-fits-all, on any level.
44
44
 
45
- You can try the beta CLI from npm: [Getting started](https://www.shoalrun.com/start.md) walks through installation and your first page. The [project status](https://www.shoalrun.com/status.md) describes what works and what still needs work.
45
+ You can try the CLI from npm: [Getting started](https://www.shoalrun.com/start.md) walks through installation and your first page. The [project status](https://www.shoalrun.com/status.md) describes what works and what still needs work.
46
46
 
47
47
  yee haw forever,
48
48