bookwright-cli 0.2.0__tar.gz → 0.3.2__tar.gz
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.
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/PKG-INFO +51 -12
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/README.md +50 -11
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/__init__.py +1 -1
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/cli.py +3 -1
- bookwright_cli-0.3.2/src/bookwright/commands/_envelope.py +116 -0
- bookwright_cli-0.3.2/src/bookwright/commands/_graph.py +135 -0
- bookwright_cli-0.3.2/src/bookwright/commands/_project.py +44 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/check.py +3 -2
- bookwright_cli-0.3.2/src/bookwright/commands/focus/__init__.py +24 -0
- bookwright_cli-0.3.2/src/bookwright/commands/focus/clear.py +40 -0
- bookwright_cli-0.3.2/src/bookwright/commands/focus/errors.py +27 -0
- bookwright_cli-0.3.2/src/bookwright/commands/focus/set_.py +71 -0
- bookwright_cli-0.3.2/src/bookwright/commands/focus/show.py +45 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/graph/build.py +18 -78
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/graph/query.py +1 -3
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/init/envelope.py +9 -3
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/integration/use.py +6 -21
- bookwright_cli-0.3.2/src/bookwright/commands/status.py +251 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/validate.py +8 -23
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/version.py +3 -4
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/core/__init__.py +2 -0
- bookwright_cli-0.3.2/src/bookwright/core/_focus_block.py +94 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/core/manifest.py +66 -13
- bookwright_cli-0.3.2/src/bookwright/golem/deferrals.py +63 -0
- bookwright_cli-0.3.2/src/bookwright/integrations/constants.py +86 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/integrations/materialize.py +25 -7
- bookwright_cli-0.3.2/src/bookwright/io/_bible_builders.py +268 -0
- bookwright_cli-0.3.2/src/bookwright/io/_research_identity.py +72 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/io/bible.py +81 -197
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/io/manuscript.py +6 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/io/research.py +38 -3
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-bible.md +9 -4
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-outline.md +4 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-research.md +36 -3
- bookwright_cli-0.3.2/src/bookwright/status/__init__.py +41 -0
- bookwright_cli-0.3.2/src/bookwright/status/model.py +157 -0
- bookwright_cli-0.3.2/src/bookwright/status/queries.py +207 -0
- bookwright_cli-0.3.2/src/bookwright/status/rules.py +176 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/anchor_queries.py +5 -2
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/registry.py +6 -1
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/validators/factual_anchor.py +45 -11
- bookwright_cli-0.2.0/src/bookwright/commands/_envelope.py +0 -36
- bookwright_cli-0.2.0/src/bookwright/commands/graph/envelope.py +0 -26
- bookwright_cli-0.2.0/src/bookwright/integrations/constants.py +0 -38
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/.gitignore +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/LICENSE +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/NOTICE +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/pyproject.toml +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/__main__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/graph/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/init/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/init/conflict.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/init/git.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/init/main.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/init/resolve.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/init/scaffold.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/init/validate.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/commands/integration/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/core/_blocks.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/core/_build.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/core/_research_block.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/core/_translate.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/core/errors.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/core/iso639_1.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/errors.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/base.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/errors.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/modules/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/modules/character.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/modules/event.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/modules/feature.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/modules/inference.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/modules/narrative.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/modules/provenance.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/modules/relationship.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/modules/setting.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/namespaces.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/serialize.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/golem/slug.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/indexers/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/indexers/base.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/indexers/errors.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/indexers/rdflib_indexer.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/integrations/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/integrations/base.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/integrations/claude/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/integrations/descriptions.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/integrations/errors.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/integrations/generic/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/integrations/lint.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/integrations/options.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/io/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/io/errors.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/io/frontmatter.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/io/fs.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/io/project.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/io/report.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-analyze.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-checklist.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-clarify.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-constitution.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-continuity.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-draft.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-scenes.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-synopsis.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/bookwright-verify.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/references/golem-character.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/references/golem-events-timeline.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/references/golem-relationships.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/references/greimas-actants.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/references/pending-protocol.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/references/propp-functions.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/commands/references/research-format.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/.bookwright/cache/.gitkeep +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/.bookwright/schema/.gitkeep +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/.bookwright/templates/.gitkeep +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/.gitignore +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/README.md.j2 +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/characters/.gitkeep +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/constitution.md.j2 +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/glossary.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/locations/.gitkeep +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/pov-structure.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/relationships.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/research/_index.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/research/sources.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/settings/.gitkeep +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/subplots.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/themes.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/bible/timeline.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/manuscript/.gitkeep +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/outline/arcs.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/outline/scenes.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/outline/structure.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/project/outline/synopsis.md +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/schemas/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/schemas/golem-1.1/VERSION +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/schemas/golem-1.1/golem.ttl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/schemas/golem-1.1/version.json +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/templates/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/templates/bible/character.md.tmpl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/templates/bible/location.md.tmpl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/templates/bible/research/_index.md.tmpl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/templates/bible/research/sources.md.tmpl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/templates/bible/research/tema.md.tmpl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/templates/bible/setting.md.tmpl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/templates/manifest.template.toml +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/templates/manuscript/chapter.md.tmpl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/templates/scenes/scene.md.tmpl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/vocabularies/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/vocabularies/greimas.ttl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/vocabularies/propp.ttl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/resources/vocabularies/sources.ttl +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/base.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/queries.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/report.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/runner.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/validators/__init__.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/validators/character_presence.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/validators/focalization.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/validators/setting_continuity.py +0 -0
- {bookwright_cli-0.2.0 → bookwright_cli-0.3.2}/src/bookwright/validation/validators/temporal.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bookwright-cli
|
|
3
|
-
Version: 0.2
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Spec-driven authoring toolkit for novels, essays, and memoirs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/jmorenobl/bookwright
|
|
6
6
|
Project-URL: Repository, https://github.com/jmorenobl/bookwright
|
|
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
<p align="center">
|
|
42
42
|
<a href="https://github.com/jmorenobl/bookwright/actions/workflows/tests.yml"><img src="https://github.com/jmorenobl/bookwright/actions/workflows/tests.yml/badge.svg" alt="CI"></a>
|
|
43
|
-
<a href="https://github.com/jmorenobl/bookwright/blob/main/CHANGELOG.md"><img src="https://img.shields.io/badge/version-0.
|
|
43
|
+
<a href="https://github.com/jmorenobl/bookwright/blob/main/CHANGELOG.md"><img src="https://img.shields.io/badge/version-0.3.0-6f42c1" alt="Versión 0.3.0"></a>
|
|
44
44
|
<a href="https://github.com/jmorenobl/bookwright/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="Licencia: Apache-2.0"></a>
|
|
45
45
|
<img src="https://img.shields.io/badge/python-3.11%2B-3776ab?logo=python&logoColor=white" alt="Python 3.11+">
|
|
46
46
|
<img src="https://img.shields.io/badge/coverage-%E2%89%A580%25-2ea44f" alt="Cobertura ≥80%">
|
|
@@ -61,21 +61,32 @@ IA escriba a partir de *ellos*, no de un chat libre. Tu libro vive en
|
|
|
61
61
|
texto plano, versionado en git, completamente auditable, y sobrevive al
|
|
62
62
|
toolkit.
|
|
63
63
|
|
|
64
|
-
> ### Estado: v0.
|
|
64
|
+
> ### Estado: v0.3.0
|
|
65
65
|
>
|
|
66
|
-
>
|
|
66
|
+
> Tres hitos están en `main`. **v0.1.0** (el toolkit base, iteraciones
|
|
67
67
|
> 1–11): scaffolding del proyecto (`bookwright init`), el modelo de
|
|
68
68
|
> dominio GOLEM, el indexer y los comandos `bookwright graph`, las skills
|
|
69
69
|
> de autoría materializadas como Agent Skills, y el sistema de validación
|
|
70
70
|
> de continuidad. **v0.2.0 / M4** (investigación y verificación,
|
|
71
71
|
> iteraciones 12–18): el modelo de procedencia `Source` / `Finding` /
|
|
72
72
|
> `Anchor`, las skills `/bookwright-research` y `/bookwright-verify`, el
|
|
73
|
-
> validador `factual_anchor` y la envoltura `--json` unificada.
|
|
74
|
-
>
|
|
73
|
+
> validador `factual_anchor` y la envoltura `--json` unificada.
|
|
74
|
+
> **v0.3.0 / M5** (orquestación de contexto, iteraciones 19–23): el foco
|
|
75
|
+
> autoral (`[focus]` + `bookwright focus`), el estado derivado
|
|
76
|
+
> `bookwright status` con sus `next_actions`, y las skills que lo
|
|
77
|
+
> consumen para guiar el siguiente paso. La documentación de usuario
|
|
78
|
+
> completa vive en el
|
|
75
79
|
> [sitio de documentación](https://github.com/jmorenobl/bookwright/blob/main/docs/index.md).
|
|
76
80
|
|
|
77
81
|
## El loop del escritor
|
|
78
82
|
|
|
83
|
+
<p align="center">
|
|
84
|
+
<picture>
|
|
85
|
+
<source srcset="https://raw.githubusercontent.com/jmorenobl/bookwright/main/assets/loop.svg" type="image/svg+xml">
|
|
86
|
+
<img src="https://raw.githubusercontent.com/jmorenobl/bookwright/main/assets/loop.png" alt="El loop del escritor: idea → scaffolding → destila → build y valida → edita, y vuelta a empezar" width="100%">
|
|
87
|
+
</picture>
|
|
88
|
+
</p>
|
|
89
|
+
|
|
79
90
|
1. **Idea libremente** — conversa con tu agente o tu libreta y vuelca un
|
|
80
91
|
brief a Markdown.
|
|
81
92
|
2. **Scaffolding del proyecto** —
|
|
@@ -108,15 +119,33 @@ toolkit.
|
|
|
108
119
|
|
|
109
120
|
## Instalación
|
|
110
121
|
|
|
122
|
+
El paquete en PyPI es `bookwright-cli`; el comando que instala es `bookwright`.
|
|
123
|
+
|
|
124
|
+
Desde PyPI (recomendado):
|
|
125
|
+
|
|
111
126
|
```bash
|
|
112
|
-
uv
|
|
113
|
-
pipx install
|
|
127
|
+
uv tool install bookwright-cli # con uv
|
|
128
|
+
pipx install bookwright-cli # o con pipx
|
|
114
129
|
bookwright version
|
|
115
130
|
```
|
|
116
131
|
|
|
117
|
-
|
|
132
|
+
Directamente desde el repositorio (última versión de `main`):
|
|
118
133
|
|
|
119
134
|
```bash
|
|
135
|
+
uv tool install "git+https://github.com/jmorenobl/bookwright"
|
|
136
|
+
# o: pipx install "git+https://github.com/jmorenobl/bookwright"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
¿Solo quieres probarlo una vez, sin instalar nada?
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
uvx --from bookwright-cli bookwright version
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Para desarrollar sobre el toolkit, clona el repo y sincroniza el entorno:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
git clone https://github.com/jmorenobl/bookwright && cd bookwright
|
|
120
149
|
uv sync
|
|
121
150
|
uv run bookwright --help
|
|
122
151
|
```
|
|
@@ -156,6 +185,14 @@ bookwright graph query "SELECT ?c WHERE { ?c a golem:G1_Character }" --json
|
|
|
156
185
|
bookwright validate # exit 0 si no hay errores
|
|
157
186
|
```
|
|
158
187
|
|
|
188
|
+
Para mantener el hilo conductor entre sesiones, fija tu foco autoral y deja que
|
|
189
|
+
Bookwright derive el estado y el siguiente paso:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
bookwright focus set --target "Cerrar la investigación del libro de jornales"
|
|
193
|
+
bookwright status --json # estado derivado + next_actions
|
|
194
|
+
```
|
|
195
|
+
|
|
159
196
|
¿Quieres cambiar de integración (p. ej. de `claude` a `generic`)?
|
|
160
197
|
|
|
161
198
|
```bash
|
|
@@ -184,9 +221,11 @@ El recorrido completo está en
|
|
|
184
221
|
## Roadmap y fuera de scope
|
|
185
222
|
|
|
186
223
|
Hecho: **v0.2 / M4** — investigación y verificación (modelo de procedencia,
|
|
187
|
-
skills `research`/`verify`, validador `factual_anchor`).
|
|
188
|
-
|
|
189
|
-
|
|
224
|
+
skills `research`/`verify`, validador `factual_anchor`); **v0.3 / M5** —
|
|
225
|
+
orquestación de contexto (foco autoral `[focus]` + `bookwright focus`, estado
|
|
226
|
+
derivado `bookwright status` con `next_actions`, y las skills que lo consumen).
|
|
227
|
+
Planificado: **v0.4** — búsqueda vectorial (ChromaDB sobre rdflib, desacoplada);
|
|
228
|
+
**v1.0** — export a EPUB / PDF / impresión vía pandoc.
|
|
190
229
|
|
|
191
230
|
**Cancelado (decisión del owner), no lo pidas:** presets de género / paquetes
|
|
192
231
|
de plantilla (la resolución es de 2 capas, overrides → core); el motor
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
9
|
<a href="https://github.com/jmorenobl/bookwright/actions/workflows/tests.yml"><img src="https://github.com/jmorenobl/bookwright/actions/workflows/tests.yml/badge.svg" alt="CI"></a>
|
|
10
|
-
<a href="https://github.com/jmorenobl/bookwright/blob/main/CHANGELOG.md"><img src="https://img.shields.io/badge/version-0.
|
|
10
|
+
<a href="https://github.com/jmorenobl/bookwright/blob/main/CHANGELOG.md"><img src="https://img.shields.io/badge/version-0.3.0-6f42c1" alt="Versión 0.3.0"></a>
|
|
11
11
|
<a href="https://github.com/jmorenobl/bookwright/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="Licencia: Apache-2.0"></a>
|
|
12
12
|
<img src="https://img.shields.io/badge/python-3.11%2B-3776ab?logo=python&logoColor=white" alt="Python 3.11+">
|
|
13
13
|
<img src="https://img.shields.io/badge/coverage-%E2%89%A580%25-2ea44f" alt="Cobertura ≥80%">
|
|
@@ -28,21 +28,32 @@ IA escriba a partir de *ellos*, no de un chat libre. Tu libro vive en
|
|
|
28
28
|
texto plano, versionado en git, completamente auditable, y sobrevive al
|
|
29
29
|
toolkit.
|
|
30
30
|
|
|
31
|
-
> ### Estado: v0.
|
|
31
|
+
> ### Estado: v0.3.0
|
|
32
32
|
>
|
|
33
|
-
>
|
|
33
|
+
> Tres hitos están en `main`. **v0.1.0** (el toolkit base, iteraciones
|
|
34
34
|
> 1–11): scaffolding del proyecto (`bookwright init`), el modelo de
|
|
35
35
|
> dominio GOLEM, el indexer y los comandos `bookwright graph`, las skills
|
|
36
36
|
> de autoría materializadas como Agent Skills, y el sistema de validación
|
|
37
37
|
> de continuidad. **v0.2.0 / M4** (investigación y verificación,
|
|
38
38
|
> iteraciones 12–18): el modelo de procedencia `Source` / `Finding` /
|
|
39
39
|
> `Anchor`, las skills `/bookwright-research` y `/bookwright-verify`, el
|
|
40
|
-
> validador `factual_anchor` y la envoltura `--json` unificada.
|
|
41
|
-
>
|
|
40
|
+
> validador `factual_anchor` y la envoltura `--json` unificada.
|
|
41
|
+
> **v0.3.0 / M5** (orquestación de contexto, iteraciones 19–23): el foco
|
|
42
|
+
> autoral (`[focus]` + `bookwright focus`), el estado derivado
|
|
43
|
+
> `bookwright status` con sus `next_actions`, y las skills que lo
|
|
44
|
+
> consumen para guiar el siguiente paso. La documentación de usuario
|
|
45
|
+
> completa vive en el
|
|
42
46
|
> [sitio de documentación](https://github.com/jmorenobl/bookwright/blob/main/docs/index.md).
|
|
43
47
|
|
|
44
48
|
## El loop del escritor
|
|
45
49
|
|
|
50
|
+
<p align="center">
|
|
51
|
+
<picture>
|
|
52
|
+
<source srcset="https://raw.githubusercontent.com/jmorenobl/bookwright/main/assets/loop.svg" type="image/svg+xml">
|
|
53
|
+
<img src="https://raw.githubusercontent.com/jmorenobl/bookwright/main/assets/loop.png" alt="El loop del escritor: idea → scaffolding → destila → build y valida → edita, y vuelta a empezar" width="100%">
|
|
54
|
+
</picture>
|
|
55
|
+
</p>
|
|
56
|
+
|
|
46
57
|
1. **Idea libremente** — conversa con tu agente o tu libreta y vuelca un
|
|
47
58
|
brief a Markdown.
|
|
48
59
|
2. **Scaffolding del proyecto** —
|
|
@@ -75,15 +86,33 @@ toolkit.
|
|
|
75
86
|
|
|
76
87
|
## Instalación
|
|
77
88
|
|
|
89
|
+
El paquete en PyPI es `bookwright-cli`; el comando que instala es `bookwright`.
|
|
90
|
+
|
|
91
|
+
Desde PyPI (recomendado):
|
|
92
|
+
|
|
78
93
|
```bash
|
|
79
|
-
uv
|
|
80
|
-
pipx install
|
|
94
|
+
uv tool install bookwright-cli # con uv
|
|
95
|
+
pipx install bookwright-cli # o con pipx
|
|
81
96
|
bookwright version
|
|
82
97
|
```
|
|
83
98
|
|
|
84
|
-
|
|
99
|
+
Directamente desde el repositorio (última versión de `main`):
|
|
85
100
|
|
|
86
101
|
```bash
|
|
102
|
+
uv tool install "git+https://github.com/jmorenobl/bookwright"
|
|
103
|
+
# o: pipx install "git+https://github.com/jmorenobl/bookwright"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
¿Solo quieres probarlo una vez, sin instalar nada?
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
uvx --from bookwright-cli bookwright version
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Para desarrollar sobre el toolkit, clona el repo y sincroniza el entorno:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
git clone https://github.com/jmorenobl/bookwright && cd bookwright
|
|
87
116
|
uv sync
|
|
88
117
|
uv run bookwright --help
|
|
89
118
|
```
|
|
@@ -123,6 +152,14 @@ bookwright graph query "SELECT ?c WHERE { ?c a golem:G1_Character }" --json
|
|
|
123
152
|
bookwright validate # exit 0 si no hay errores
|
|
124
153
|
```
|
|
125
154
|
|
|
155
|
+
Para mantener el hilo conductor entre sesiones, fija tu foco autoral y deja que
|
|
156
|
+
Bookwright derive el estado y el siguiente paso:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
bookwright focus set --target "Cerrar la investigación del libro de jornales"
|
|
160
|
+
bookwright status --json # estado derivado + next_actions
|
|
161
|
+
```
|
|
162
|
+
|
|
126
163
|
¿Quieres cambiar de integración (p. ej. de `claude` a `generic`)?
|
|
127
164
|
|
|
128
165
|
```bash
|
|
@@ -151,9 +188,11 @@ El recorrido completo está en
|
|
|
151
188
|
## Roadmap y fuera de scope
|
|
152
189
|
|
|
153
190
|
Hecho: **v0.2 / M4** — investigación y verificación (modelo de procedencia,
|
|
154
|
-
skills `research`/`verify`, validador `factual_anchor`).
|
|
155
|
-
|
|
156
|
-
|
|
191
|
+
skills `research`/`verify`, validador `factual_anchor`); **v0.3 / M5** —
|
|
192
|
+
orquestación de contexto (foco autoral `[focus]` + `bookwright focus`, estado
|
|
193
|
+
derivado `bookwright status` con `next_actions`, y las skills que lo consumen).
|
|
194
|
+
Planificado: **v0.4** — búsqueda vectorial (ChromaDB sobre rdflib, desacoplada);
|
|
195
|
+
**v1.0** — export a EPUB / PDF / impresión vía pandoc.
|
|
157
196
|
|
|
158
197
|
**Cancelado (decisión del owner), no lo pidas:** presets de género / paquetes
|
|
159
198
|
de plantilla (la resolución es de 2 capas, overrides → core); el motor
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
4
|
|
|
5
|
-
from bookwright.commands import check, graph, init, integration, validate, version
|
|
5
|
+
from bookwright.commands import check, focus, graph, init, integration, status, validate, version
|
|
6
6
|
|
|
7
7
|
app = typer.Typer(
|
|
8
8
|
name="bookwright",
|
|
@@ -14,6 +14,8 @@ app = typer.Typer(
|
|
|
14
14
|
app.command("version")(version.run)
|
|
15
15
|
app.command("check")(check.run)
|
|
16
16
|
app.command("init", context_settings=init.CONTEXT_SETTINGS)(init.run)
|
|
17
|
+
app.command("status")(status.run)
|
|
17
18
|
app.command("validate")(validate.run)
|
|
18
19
|
app.add_typer(graph.app, name="graph")
|
|
19
20
|
app.add_typer(integration.app, name="integration")
|
|
21
|
+
app.add_typer(focus.app, name="focus")
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Shared command-layer envelope helpers.
|
|
2
|
+
|
|
3
|
+
Several agent-facing commands catch a ``ManifestError`` at their ``--json``
|
|
4
|
+
boundary and remap it to the contract's single ``invalid_manifest`` code. Rather
|
|
5
|
+
than re-build the ``{status,code,message}`` skeleton by hand in each command
|
|
6
|
+
module, the remap routes through the base ``BookwrightError.to_json`` — the one
|
|
7
|
+
place the envelope skeleton lives — exactly as ``commands.validate._UsageError``
|
|
8
|
+
already does for the same case.
|
|
9
|
+
|
|
10
|
+
The :func:`emit_json` / :func:`emit_error` pair is single-sourced here too: a
|
|
11
|
+
single-line ``json.dumps(payload, separators=(",", ":")) + "\\n"`` to stdout,
|
|
12
|
+
with human prose / progress going to stderr via a ``Console(stderr=True)`` owned
|
|
13
|
+
by each command. Every ``--json`` command (``check``, ``focus``, ``graph``,
|
|
14
|
+
``init``, ``integration``, ``validate``, ``version``) routes its stdout encoding
|
|
15
|
+
through :func:`emit_json` instead of hand-rolling a per-group copy.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import sys
|
|
22
|
+
from typing import TYPE_CHECKING, Any
|
|
23
|
+
|
|
24
|
+
from bookwright.errors import BookwrightError
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from bookwright.io.errors import ProjectNotFoundError
|
|
28
|
+
|
|
29
|
+
#: The CLI-wide "configuration fault" exit status (missing project, unparseable
|
|
30
|
+
#: manifest, unknown engine/integration, bad scope, …). Single-sourced here so
|
|
31
|
+
#: the command groups cannot drift to different statuses for the same fault class.
|
|
32
|
+
EXIT_CONFIG = 2
|
|
33
|
+
|
|
34
|
+
#: The CLI-wide "slug collision" exit status. ``graph build`` and ``status``
|
|
35
|
+
#: must exit identically on the same corpus (020 research D4), so the literal
|
|
36
|
+
#: lives once here, beside :data:`EXIT_CONFIG`.
|
|
37
|
+
EXIT_COLLISION = 3
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def ok_payload(**fields: Any) -> dict[str, Any]:
|
|
41
|
+
"""The success-envelope skeleton: ``{"status": "ok", **fields}`` (Principle IX).
|
|
42
|
+
|
|
43
|
+
The success-side complement of ``BookwrightError.to_json()`` — the one place
|
|
44
|
+
the ``"status": "ok"`` literal lives for new success documents (020 research
|
|
45
|
+
D6). Existing ``check``/``focus``/``graph`` call sites keep their hand-built
|
|
46
|
+
dicts for now; migrating them is out of 020's scope.
|
|
47
|
+
"""
|
|
48
|
+
return {"status": "ok", **fields}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def render_json(payload: dict[str, Any]) -> str:
|
|
52
|
+
"""The canonical one-line JSON encoding of a contract document.
|
|
53
|
+
|
|
54
|
+
The single place the compact-separators + trailing-newline format lives:
|
|
55
|
+
:func:`emit_json` routes stdout through it, and ``status`` renders its
|
|
56
|
+
document once here so stdout and the cache file share the exact bytes
|
|
57
|
+
(020 research D6) by construction, not by parallel formatting calls.
|
|
58
|
+
"""
|
|
59
|
+
return json.dumps(payload, separators=(",", ":")) + "\n"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def emit_json(payload: dict[str, Any]) -> None:
|
|
63
|
+
"""Write exactly one JSON document to stdout (the only thing on stdout)."""
|
|
64
|
+
sys.stdout.write(render_json(payload))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def emit_error(payload: dict[str, Any], json_output: bool) -> None:
|
|
68
|
+
"""Surface an error envelope: one JSON doc on stdout under ``--json``, else a
|
|
69
|
+
single ``bookwright: error: <message>`` line on stderr (Principle IX)."""
|
|
70
|
+
if json_output:
|
|
71
|
+
emit_json(payload)
|
|
72
|
+
else:
|
|
73
|
+
sys.stderr.write(f"bookwright: error: {payload['message']}\n")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
#: The contract code every caught ``ManifestError`` collapses to at a ``--json``
|
|
77
|
+
#: boundary. Single-sourced here so the two remap sites — this module and
|
|
78
|
+
#: ``commands.validate._UsageError`` — cannot drift to different literals.
|
|
79
|
+
INVALID_MANIFEST_CODE = "invalid_manifest"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class _InvalidManifestError(BookwrightError):
|
|
83
|
+
"""A caught ``ManifestError`` re-coded to the contract's ``invalid_manifest``.
|
|
84
|
+
|
|
85
|
+
Mirrors ``commands.validate._UsageError(INVALID_MANIFEST_CODE, ...)``: the
|
|
86
|
+
remap is expressed as a ``BookwrightError`` whose canonical ``to_json()``
|
|
87
|
+
builds the envelope, never a hand-rolled dict.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
code = INVALID_MANIFEST_CODE
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def invalid_manifest_payload(exc: Exception) -> dict[str, Any]:
|
|
94
|
+
"""The ``invalid_manifest`` error envelope for a caught ``ManifestError``."""
|
|
95
|
+
return _InvalidManifestError(str(exc)).to_json()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
#: The contract code for "ran outside a project" at a ``--json`` boundary.
|
|
99
|
+
#: Single-sourced here so the two remap sites — ``commands.status`` and
|
|
100
|
+
#: ``commands.validate._UsageError`` — cannot drift to different literals.
|
|
101
|
+
NO_PROJECT_CODE = "no_project"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class _NoProjectError(BookwrightError):
|
|
105
|
+
"""A caught ``ProjectNotFoundError`` re-coded to the contract's ``no_project``.
|
|
106
|
+
|
|
107
|
+
Mirrors :class:`_InvalidManifestError`: the remap is a ``BookwrightError``
|
|
108
|
+
whose canonical ``to_json()`` builds the envelope, never a hand-rolled dict.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
code = NO_PROJECT_CODE
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def no_project_payload(exc: ProjectNotFoundError) -> dict[str, Any]:
|
|
115
|
+
"""The ``no_project`` error envelope for a caught ``ProjectNotFoundError``."""
|
|
116
|
+
return _NoProjectError(str(exc), {"start": exc.start}).to_json()
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""The shared graph-build pipeline (`graph build` + `status`, 020 research D1).
|
|
2
|
+
|
|
3
|
+
The pipeline body extracted from ``commands/graph/build.py``: map the bible to
|
|
4
|
+
GOLEM entities (with CIDOC provenance), map ``bible/research/``, feed every
|
|
5
|
+
triple into one manifest-selected engine, and refresh the derived
|
|
6
|
+
``bible/graph.ttl`` cache. Both verbs consume this one implementation so the
|
|
7
|
+
graph they reason over can never diverge.
|
|
8
|
+
|
|
9
|
+
The fault model is the pipeline's own — :class:`MissingDirectoryError` for
|
|
10
|
+
absent prerequisites, ``UnknownIndexerError`` from engine resolution,
|
|
11
|
+
``SlugCollisionError`` / ``ResearchError`` from the mappers. Callers own
|
|
12
|
+
project/manifest resolution and the exit-code mapping (`graph build` per
|
|
13
|
+
cli-graph.md R7; `status` per 020 research D4/D5).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
from bookwright.golem.namespaces import timeline_uri
|
|
22
|
+
from bookwright.indexers import Indexer, UnknownIndexerError, resolve_indexer
|
|
23
|
+
from bookwright.io.bible import build_provenance, map_bible
|
|
24
|
+
from bookwright.io.errors import MissingDirectoryError, ResearchError
|
|
25
|
+
from bookwright.io.manuscript import manuscript_present
|
|
26
|
+
from bookwright.io.report import BuildReport, ResearchTargetWarning
|
|
27
|
+
from bookwright.io.research import ResearchResult, map_research
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
from bookwright.core.manifest import Manifest
|
|
33
|
+
from bookwright.errors import BookwrightError
|
|
34
|
+
|
|
35
|
+
#: The pipeline's own exit-2 fault classes, single-sourced beside the pipeline
|
|
36
|
+
#: that raises them so the two consuming except-ladders (`graph build`,
|
|
37
|
+
#: `status`) cannot drift apart on the same corpus (020 research D4).
|
|
38
|
+
PIPELINE_CONFIG_FAULTS: tuple[type[BookwrightError], ...] = (
|
|
39
|
+
MissingDirectoryError,
|
|
40
|
+
UnknownIndexerError,
|
|
41
|
+
ResearchError,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def missing_prerequisite(root: Path, manifest: Manifest) -> tuple[str, Path] | None:
|
|
46
|
+
"""The first absent build prerequisite as ``(label, path)``, or ``None``.
|
|
47
|
+
|
|
48
|
+
The single statement of "can this corpus build?": :func:`build_project_graph`
|
|
49
|
+
raises ``MissingDirectoryError`` from it, and ``status`` branches to its
|
|
50
|
+
degraded report on it (020 research D5) — one predicate, no drift.
|
|
51
|
+
"""
|
|
52
|
+
bible_dir = root / manifest.paths.bible
|
|
53
|
+
if not bible_dir.is_dir():
|
|
54
|
+
return ("bible", bible_dir)
|
|
55
|
+
manuscript_dir = root / manifest.paths.manuscript
|
|
56
|
+
if not manuscript_present(manuscript_dir):
|
|
57
|
+
return ("manuscript", manuscript_dir)
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True)
|
|
62
|
+
class BuildOutcome:
|
|
63
|
+
"""Everything one pipeline run yields (data-model 020 § 5).
|
|
64
|
+
|
|
65
|
+
``report`` is what `graph build` emits; ``engine`` (already populated and
|
|
66
|
+
saved) and ``research`` (carrying the authored identities, research D2) are
|
|
67
|
+
what ``status`` aggregates over.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
engine: Indexer
|
|
71
|
+
report: BuildReport
|
|
72
|
+
research: ResearchResult
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def build_project_graph(root: Path, manifest: Manifest) -> BuildOutcome:
|
|
76
|
+
"""Build the project graph from the bible and write ``manifest.paths.graph``.
|
|
77
|
+
|
|
78
|
+
Raises the fault-model exceptions documented in the module docstring; on
|
|
79
|
+
success the returned engine holds the full graph and the Turtle cache on
|
|
80
|
+
disk matches it.
|
|
81
|
+
"""
|
|
82
|
+
missing = missing_prerequisite(root, manifest)
|
|
83
|
+
if missing is not None:
|
|
84
|
+
label, path = missing
|
|
85
|
+
raise MissingDirectoryError(label, str(path))
|
|
86
|
+
|
|
87
|
+
bible_dir = root / manifest.paths.bible
|
|
88
|
+
engine_cls = resolve_indexer(manifest.bookwright.indexer)
|
|
89
|
+
engine = engine_cls()
|
|
90
|
+
|
|
91
|
+
uri_base = manifest.bookwright.uri_base
|
|
92
|
+
result = map_bible(root, bible_dir, uri_base)
|
|
93
|
+
|
|
94
|
+
for mapped in result.mapped:
|
|
95
|
+
for triple in mapped.entity.to_triples():
|
|
96
|
+
engine.add_triple(*triple)
|
|
97
|
+
for assignment in build_provenance(mapped, uri_base):
|
|
98
|
+
for triple in assignment.to_triples():
|
|
99
|
+
engine.add_triple(*triple)
|
|
100
|
+
|
|
101
|
+
# Research pass: map bible/research/ and feed its triples into the same engine
|
|
102
|
+
# (one graph, one save — research D8). Research entities are already E13
|
|
103
|
+
# reifications, so they are NOT routed through build_provenance.
|
|
104
|
+
research = map_research(
|
|
105
|
+
root,
|
|
106
|
+
bible_dir / "research",
|
|
107
|
+
uri_base,
|
|
108
|
+
manifest.book.language,
|
|
109
|
+
result.entity_index,
|
|
110
|
+
timeline_uri(uri_base),
|
|
111
|
+
)
|
|
112
|
+
for entity in research.entities:
|
|
113
|
+
for triple in entity.to_triples():
|
|
114
|
+
engine.add_triple(*triple)
|
|
115
|
+
|
|
116
|
+
graph_rel = manifest.paths.graph
|
|
117
|
+
engine.save(root / graph_rel)
|
|
118
|
+
|
|
119
|
+
report = BuildReport(
|
|
120
|
+
files_processed=result.files_processed + research.files_processed,
|
|
121
|
+
entities=len(result.entities) + len(research.entities),
|
|
122
|
+
triples=engine.count(),
|
|
123
|
+
graph_path=graph_rel,
|
|
124
|
+
skipped=tuple(result.skipped),
|
|
125
|
+
unknown_keys=tuple(result.unknown_keys),
|
|
126
|
+
unresolved_participants=tuple(result.unresolved_participants),
|
|
127
|
+
sources=len(research.sources),
|
|
128
|
+
findings=len(research.findings),
|
|
129
|
+
anchors=len(research.anchors),
|
|
130
|
+
research_warnings=tuple(
|
|
131
|
+
ResearchTargetWarning(path=w.relpath, field=w.field, name=w.name)
|
|
132
|
+
for w in research.warnings
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
return BuildOutcome(engine=engine, report=report, research=research)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Shared project-load + ``--json`` fault boundary for the command layer.
|
|
2
|
+
|
|
3
|
+
Every manifest-reading subcommand locates the project and loads the manifest the
|
|
4
|
+
same way and remaps the same two faults to exit 2 (research D10). Factoring it
|
|
5
|
+
here — next to the envelope helpers in :mod:`bookwright.commands._envelope` —
|
|
6
|
+
keeps each command body to its own logic and gives later iterations (e.g. the
|
|
7
|
+
020 ``bookwright status`` read path) the boundary without reaching into a
|
|
8
|
+
sibling command package.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
import typer
|
|
16
|
+
|
|
17
|
+
from bookwright.core.errors import ManifestError
|
|
18
|
+
from bookwright.core.manifest import Manifest
|
|
19
|
+
from bookwright.io.errors import ProjectNotFoundError
|
|
20
|
+
from bookwright.io.project import find_project_root
|
|
21
|
+
|
|
22
|
+
from ._envelope import EXIT_CONFIG, emit_error, invalid_manifest_payload
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def load_manifest_or_exit(json_output: bool) -> tuple[Path, Manifest]:
|
|
26
|
+
"""Return ``(manifest_path, manifest)`` or emit the fault envelope and exit 2.
|
|
27
|
+
|
|
28
|
+
A caught ``ManifestError`` collapses to the contract's ``invalid_manifest``
|
|
29
|
+
code; ``ProjectNotFoundError`` carries its own ``not_a_project`` code. Both
|
|
30
|
+
exit 2 — the structured distinction lives in the envelope ``code`` (research
|
|
31
|
+
D7), never the exit status.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
root = find_project_root()
|
|
36
|
+
path = root / "manifest.toml"
|
|
37
|
+
manifest = Manifest.load(path)
|
|
38
|
+
except ManifestError as exc:
|
|
39
|
+
emit_error(invalid_manifest_payload(exc), json_output)
|
|
40
|
+
raise typer.Exit(EXIT_CONFIG) from exc
|
|
41
|
+
except ProjectNotFoundError as exc:
|
|
42
|
+
emit_error(exc.to_json(), json_output)
|
|
43
|
+
raise typer.Exit(EXIT_CONFIG) from exc
|
|
44
|
+
return path, manifest
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"""`bookwright check` — verify the running interpreter and runtime dependencies."""
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
|
-
import json
|
|
5
4
|
import sys
|
|
6
5
|
from typing import TypedDict
|
|
7
6
|
|
|
8
7
|
import typer
|
|
9
8
|
from rich.console import Console
|
|
10
9
|
|
|
10
|
+
from ._envelope import emit_json
|
|
11
|
+
|
|
11
12
|
RUNTIME_MODULES: tuple[str, ...] = (
|
|
12
13
|
"typer",
|
|
13
14
|
"rich",
|
|
@@ -64,7 +65,7 @@ def run(
|
|
|
64
65
|
ok = all(c["status"] == "ok" for c in checks)
|
|
65
66
|
payload = {"ok": ok, "checks": checks}
|
|
66
67
|
if json_output:
|
|
67
|
-
|
|
68
|
+
emit_json(payload)
|
|
68
69
|
else:
|
|
69
70
|
console = Console()
|
|
70
71
|
for check in checks:
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""The ``bookwright focus`` Typer sub-app.
|
|
2
|
+
|
|
3
|
+
``show``, ``set``, and ``clear`` live in their own modules (Principle IV) and
|
|
4
|
+
register their callbacks here. The app is wired into the root CLI in
|
|
5
|
+
:mod:`bookwright.cli` via ``app.add_typer(focus.app, name="focus")``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(
|
|
13
|
+
name="focus",
|
|
14
|
+
help="Record, view, and clear the authored focus state.",
|
|
15
|
+
no_args_is_help=True,
|
|
16
|
+
add_completion=False,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Each subcommand registers its callback on `app` at import time; the per-story
|
|
20
|
+
# `from . import …` lines are appended below as the modules land. The `as`
|
|
21
|
+
# redirect marks the import as an intentional re-export (registration side effect).
|
|
22
|
+
from . import clear as clear # noqa: E402
|
|
23
|
+
from . import set_ as set_ # noqa: E402 (module name avoids shadowing builtins.set)
|
|
24
|
+
from . import show as show # noqa: E402
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""``bookwright focus clear [--json]``.
|
|
2
|
+
|
|
3
|
+
Remove the ``[focus]`` block, preserving the rest of the manifest. Absent block ⇒
|
|
4
|
+
a successful no-op (FR-010). Under ``--json`` emit
|
|
5
|
+
``{"status":"ok","cleared":<bool>}`` — the boolean discriminator lets an agent
|
|
6
|
+
tell a real removal from a no-op without a second read; both exit 0. Human prose
|
|
7
|
+
goes to stderr (Principle IX).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
|
|
15
|
+
from .._envelope import emit_json
|
|
16
|
+
from .._project import load_manifest_or_exit
|
|
17
|
+
from . import app
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@app.command("clear")
|
|
21
|
+
def run(
|
|
22
|
+
json_output: bool = typer.Option(
|
|
23
|
+
False, "--json", help="Emit the result as one JSON document on stdout."
|
|
24
|
+
),
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Clear the authored focus state (no-op when none is set)."""
|
|
27
|
+
path, manifest = load_manifest_or_exit(json_output)
|
|
28
|
+
had_focus = manifest.focus is not None
|
|
29
|
+
|
|
30
|
+
manifest.clear_focus()
|
|
31
|
+
# Only rewrite when something actually changed — a no-op leaves the bytes
|
|
32
|
+
# untouched rather than re-serializing an unchanged manifest.
|
|
33
|
+
if had_focus:
|
|
34
|
+
manifest.dump(path, overwrite=True)
|
|
35
|
+
|
|
36
|
+
if json_output:
|
|
37
|
+
emit_json({"status": "ok", "cleared": had_focus})
|
|
38
|
+
else:
|
|
39
|
+
message = "focus cleared" if had_focus else "no focus to clear"
|
|
40
|
+
Console(stderr=True, highlight=False).print(message)
|