textus 0.5.0 → 0.8.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +83 -1
- data/README.md +29 -21
- data/SPEC.md +75 -142
- data/docs/architecture.md +42 -23
- data/lib/textus/builder/pipeline.rb +56 -0
- data/lib/textus/builder/renderer/json.rb +42 -0
- data/lib/textus/builder/renderer/markdown.rb +22 -0
- data/lib/textus/builder/renderer/text.rb +14 -0
- data/lib/textus/builder/renderer/yaml.rb +42 -0
- data/lib/textus/builder/renderer.rb +17 -0
- data/lib/textus/builder.rb +9 -114
- data/lib/textus/cli/group/hook.rb +11 -0
- data/lib/textus/cli/group/key.rb +12 -0
- data/lib/textus/cli/group/schema.rb +13 -0
- data/lib/textus/cli/verb/accept.rb +15 -0
- data/lib/textus/cli/verb/build.rb +13 -0
- data/lib/textus/cli/verb/delete.rb +16 -0
- data/lib/textus/cli/verb/deps.rb +12 -0
- data/lib/textus/cli/verb/doctor.rb +15 -0
- data/lib/textus/cli/verb/get.rb +12 -0
- data/lib/textus/cli/verb/hook_run.rb +48 -0
- data/lib/textus/cli/verb/hooks.rb +50 -0
- data/lib/textus/cli/verb/init.rb +14 -0
- data/lib/textus/cli/verb/intro.rb +11 -0
- data/lib/textus/cli/verb/list.rb +14 -0
- data/lib/textus/cli/verb/migrate_keys.rb +16 -0
- data/lib/textus/cli/verb/mv.rb +17 -0
- data/lib/textus/cli/verb/published.rb +11 -0
- data/lib/textus/cli/verb/put.rb +50 -0
- data/lib/textus/cli/verb/rdeps.rb +12 -0
- data/lib/textus/cli/verb/refresh.rb +15 -0
- data/lib/textus/cli/verb/schema.rb +12 -0
- data/lib/textus/cli/verb/schema_diff.rb +12 -0
- data/lib/textus/cli/verb/schema_init.rb +16 -0
- data/lib/textus/cli/verb/schema_migrate.rb +16 -0
- data/lib/textus/cli/verb/stale.rb +14 -0
- data/lib/textus/cli/verb/uid.rb +12 -0
- data/lib/textus/cli/verb/where.rb +12 -0
- data/lib/textus/cli.rb +23 -42
- data/lib/textus/doctor/check/audit_log.rb +50 -0
- data/lib/textus/doctor/check/hooks.rb +29 -0
- data/lib/textus/doctor/check/illegal_keys.rb +49 -0
- data/lib/textus/doctor/check/manifest_files.rb +38 -0
- data/lib/textus/doctor/check/schema_violations.rb +22 -0
- data/lib/textus/doctor/check/schemas.rb +26 -0
- data/lib/textus/doctor/check/sentinels.rb +57 -0
- data/lib/textus/doctor/check/templates.rb +26 -0
- data/lib/textus/doctor/check/unowned_schema_fields.rb +34 -0
- data/lib/textus/doctor/check.rb +30 -0
- data/lib/textus/doctor.rb +22 -288
- data/lib/textus/entry/base.rb +30 -0
- data/lib/textus/entry/json.rb +5 -1
- data/lib/textus/entry/markdown.rb +1 -1
- data/lib/textus/entry/text.rb +1 -1
- data/lib/textus/entry/yaml.rb +5 -1
- data/lib/textus/entry.rb +0 -5
- data/lib/textus/envelope.rb +30 -0
- data/lib/textus/hooks/builtin.rb +70 -0
- data/lib/textus/hooks/dispatcher.rb +49 -0
- data/lib/textus/hooks/loader.rb +26 -0
- data/lib/textus/hooks/registry.rb +73 -0
- data/lib/textus/init.rb +13 -10
- data/lib/textus/intro.rb +14 -16
- data/lib/textus/key/distance.rb +55 -0
- data/lib/textus/key/grammar.rb +33 -0
- data/lib/textus/key/path.rb +17 -0
- data/lib/textus/manifest/entry.rb +199 -0
- data/lib/textus/manifest.rb +10 -34
- data/lib/textus/migrate_keys.rb +1 -1
- data/lib/textus/projection.rb +5 -4
- data/lib/textus/proposal.rb +1 -1
- data/lib/textus/refresh.rb +11 -11
- data/lib/textus/schema/tools.rb +89 -0
- data/lib/textus/store/audit_log.rb +71 -0
- data/lib/textus/store/mover.rb +19 -16
- data/lib/textus/store/reader.rb +67 -0
- data/lib/textus/store/staleness.rb +10 -19
- data/lib/textus/store/validator.rb +11 -8
- data/lib/textus/store/view.rb +29 -0
- data/lib/textus/store/writer.rb +132 -0
- data/lib/textus/store.rb +25 -221
- data/lib/textus/version.rb +1 -1
- data/lib/textus.rb +14 -67
- metadata +73 -40
- data/lib/textus/audit_log.rb +0 -67
- data/lib/textus/builtin_actions.rb +0 -68
- data/lib/textus/cli/accept.rb +0 -13
- data/lib/textus/cli/action.rb +0 -51
- data/lib/textus/cli/build.rb +0 -11
- data/lib/textus/cli/delete.rb +0 -14
- data/lib/textus/cli/deprecated_alias.rb +0 -31
- data/lib/textus/cli/deps.rb +0 -10
- data/lib/textus/cli/doctor.rb +0 -13
- data/lib/textus/cli/extension_group.rb +0 -9
- data/lib/textus/cli/extensions.rb +0 -49
- data/lib/textus/cli/get.rb +0 -10
- data/lib/textus/cli/init.rb +0 -12
- data/lib/textus/cli/intro.rb +0 -9
- data/lib/textus/cli/key_group.rb +0 -10
- data/lib/textus/cli/list.rb +0 -12
- data/lib/textus/cli/migrate.rb +0 -41
- data/lib/textus/cli/migrate_keys.rb +0 -19
- data/lib/textus/cli/mv.rb +0 -20
- data/lib/textus/cli/published.rb +0 -9
- data/lib/textus/cli/put.rb +0 -48
- data/lib/textus/cli/rdeps.rb +0 -10
- data/lib/textus/cli/refresh.rb +0 -13
- data/lib/textus/cli/schema.rb +0 -10
- data/lib/textus/cli/schema_diff.rb +0 -15
- data/lib/textus/cli/schema_group.rb +0 -33
- data/lib/textus/cli/schema_init.rb +0 -19
- data/lib/textus/cli/schema_migrate.rb +0 -19
- data/lib/textus/cli/stale.rb +0 -12
- data/lib/textus/cli/uid.rb +0 -15
- data/lib/textus/cli/where.rb +0 -10
- data/lib/textus/extension_registry.rb +0 -61
- data/lib/textus/extensions.rb +0 -33
- data/lib/textus/key_distance.rb +0 -53
- data/lib/textus/manifest_entry.rb +0 -185
- data/lib/textus/migrate_v2.rb +0 -27
- data/lib/textus/schema_tools.rb +0 -87
- data/lib/textus/store/events.rb +0 -31
- data/lib/textus/store_view.rb +0 -27
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 91be0acd415a4b41d96e896015dfd19e58fc7867d007332456499ec5ceb8a6aa
|
|
4
|
+
data.tar.gz: 576d361ebba900b33b2f612b1a0a51d0373cb4a5e1fbd503e43c2bb248ca9306
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c551c60809be1eefc0d964092adc62a8555cb6f0df3585e775fd2fda1549f1e7d811fb2bfeec992dd3ce872dccb16fd9300a66314d5e51e8f24a98b57f29da5b
|
|
7
|
+
data.tar.gz: c96ec13692d6366a916b125266afb70aabcca439594db4e8fd1b434f1db1a88e216938cbf64bc69e64bcfef241ca7314664398c656f1d7333a7480a7d02852f9
|
data/CHANGELOG.md
CHANGED
|
@@ -8,7 +8,89 @@ The **gem version** (`0.x.y`) is distinct from the **protocol version**
|
|
|
8
8
|
(currently `textus/2`, embedded in every envelope as `protocol`). The protocol
|
|
9
9
|
is additive within a major; a new major would change the wire string.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## 0.8.0 — Folder restructure & Zeitwerk autoload (2026-05-21)
|
|
12
|
+
|
|
13
|
+
### Breaking — internal Ruby renames
|
|
14
|
+
Internal Ruby constants renamed. No deprecation aliases; downstream code referencing internals must update directly.
|
|
15
|
+
- `Textus::EventBus` → `Textus::Hooks::Dispatcher`
|
|
16
|
+
- `Textus::HookRegistry` → `Textus::Hooks::Registry`
|
|
17
|
+
- `Textus::BuiltinHooks` → `Textus::Hooks::Builtin`
|
|
18
|
+
- `Textus::Extensions` (module) → `Textus::Hooks::Loader`
|
|
19
|
+
- `Textus::StoreView` → `Textus::Store::View`
|
|
20
|
+
- `Textus::AuditLog` → `Textus::Store::AuditLog`
|
|
21
|
+
- `Textus::ManifestEntry` → `Textus::Manifest::Entry`
|
|
22
|
+
- `Textus::KeyDistance` → `Textus::Key::Distance`
|
|
23
|
+
- `Textus::Path` → `Textus::Key::Path`
|
|
24
|
+
- `Textus::SchemaTools` → `Textus::Schema::Tools`
|
|
25
|
+
- `Textus::CLI::<Verb>` → `Textus::CLI::Verb::<Verb>` (all 23 verbs)
|
|
26
|
+
- `Textus::CLI::<Name>Group` → `Textus::CLI::Group::<Name>` (key, schema, hook)
|
|
27
|
+
- `Textus::Doctor::Check::Extensions` → `Textus::Doctor::Check::Hooks`
|
|
28
|
+
- `Hooks::Registry#initialize` keyword `bus:` renamed to `dispatcher:`.
|
|
29
|
+
|
|
30
|
+
### Breaking — doctor CLI surface
|
|
31
|
+
- `textus doctor --check=extensions` → `textus doctor --check=hooks`. The check name listed in `ALL_CHECKS` and the SPEC §10.2 enumeration changes from `"extensions"` to `"hooks"`, matching the hook subsystem rename in 0.6.
|
|
32
|
+
- Doctor issue `code` for broken hook files: `extension.load_failed` → `hook.load_failed`.
|
|
33
|
+
- Doctor::Check::Hooks now inspects `.textus/hooks/` (matches `Store#load_extensions`). Previously inspected `.textus/extensions/`, which was the pre-0.6 directory — the check was dead code on any store created with current `textus init`.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- `Textus::Entry::Base` — explicit strategy interface for entry formats. Concrete strategies inherit and override.
|
|
37
|
+
- `Textus::Builder::Renderer` — explicit base for output renderers.
|
|
38
|
+
- `Textus::Doctor::Check` — explicit base for doctor checks. Each builtin check (9 total) is now its own file under `lib/textus/doctor/check/`.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- Per-format schema validation moved from `Store::Reader`/`Store::Writer` onto `Entry::Base#validate_against`. Reader/Writer no longer carry a `case mentry.format` switch.
|
|
42
|
+
- `Textus::Doctor` reduced to an orchestrator; the 9 builtin checks live under `Doctor::Check::*`.
|
|
43
|
+
- `lib/textus.rb` switched to Zeitwerk autoload. The manual `require_relative` tree (75 lines) is gone.
|
|
44
|
+
- `lib/textus/builder/renderers/` directory renamed to `renderer/` (singular) to match `Builder::Renderer::*` namespace.
|
|
45
|
+
|
|
46
|
+
### Migration
|
|
47
|
+
External code referencing the old internal constants must rename. `Textus.hook`, `Textus.with_registry`, the entire CLI surface, and the `textus/2` wire format are unchanged. The published API (`Store`, `Manifest`, `Envelope`, `Etag`, `Role`, `Error` hierarchy, `Builder`, `Doctor`, `Refresh`, `Init`, `CLI.run`) is unchanged.
|
|
48
|
+
|
|
49
|
+
## 0.7.0 — Reader/Writer split, EventBus, Builder pipeline (2026-05-21)
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
- `Textus::EventBus` is now the publish/subscribe core for lifecycle events. Embedded callers can `store.bus.subscribe(:put, :name) { ... }` outside the `.textus/hooks/` directory. Hook semantics, audit behavior, and the 2-second timeout are unchanged.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
- Internal: extracted `Textus::Path` and `Textus::Envelope` value modules; `Manifest`, `Store`, `Staleness`, and `Builder` now share the same path/envelope construction.
|
|
56
|
+
- Internal: split `Textus::Store` into `Store::Reader` and `Store::Writer`. Public API unchanged. `Mover`, `Validator`, and `Staleness` now take explicit collaborators instead of the full store.
|
|
57
|
+
- Internal: removed `Store::Events`; replaced by the bus.
|
|
58
|
+
- Internal: restructured `Textus::Builder` as a step pipeline (`LoadSources → Project → Render → Write`) with one renderer per format (`markdown/text/json/yaml`). Adding a new output format is now a single-file change.
|
|
59
|
+
|
|
60
|
+
## 0.6.1 — Deprecation cleanup
|
|
61
|
+
|
|
62
|
+
### Breaking
|
|
63
|
+
- Flat verb aliases promised "removed in 0.6" are now actually removed:
|
|
64
|
+
- `textus mv` → `textus key mv`
|
|
65
|
+
- `textus uid` → `textus key uid`
|
|
66
|
+
- `textus migrate-keys` → `textus key migrate`
|
|
67
|
+
- `textus schema-init` → `textus schema init`
|
|
68
|
+
- `textus schema-diff` → `textus schema diff`
|
|
69
|
+
- `textus schema-migrate` → `textus schema migrate`
|
|
70
|
+
- `textus schema KEY` (positional) → `textus schema show KEY`
|
|
71
|
+
- `textus action NAME` → `textus hook run NAME`
|
|
72
|
+
- `Textus::CLI::Action` class renamed to `Textus::CLI::HookRun`; file `cli/action.rb` → `cli/hook_run.rb`.
|
|
73
|
+
- `Textus::CLI::DeprecatedAliasMixin` module deleted (no remaining users).
|
|
74
|
+
- `textus migrate v2` command removed along with `Textus::MigrateV2` module and `Textus::CLI::Migrate` class. The migration was a one-line manifest rewrite (`version: textus/1` → `version: textus/2`); on-disk entry shapes never changed. To upgrade a `textus/1` manifest, edit `.textus/manifest.yaml` directly. `Manifest.load` still detects the old version and prints the exact edit in its error message.
|
|
75
|
+
|
|
76
|
+
## 0.6.0 — Hook unification
|
|
77
|
+
|
|
78
|
+
### Breaking
|
|
79
|
+
- Four DSL verbs (`Textus.action`, `Textus.reducer`, `Textus.hook`, `Textus.doctor_check`) collapsed into one: `Textus.hook(event, name, **opts) { ... }`.
|
|
80
|
+
- `ExtensionRegistry` class renamed to `HookRegistry`.
|
|
81
|
+
- `.textus/extensions/` directory renamed to `.textus/hooks/`. No back-compat read.
|
|
82
|
+
- Manifest: `source.action:` → `source.fetch:` (also renames the registry event from `:action` to `:fetch` and the `ManifestEntry#action`/`#action_config` accessors to `#fetch`/`#fetch_config`).
|
|
83
|
+
- Manifest: `projection.reducer:` → `projection.reduce:`.
|
|
84
|
+
- CLI: `textus extension list` → `textus hook list`; output rows keyed by `event`+`mode` instead of `kind`.
|
|
85
|
+
- CLI: `textus put --action=NAME` → `textus put --fetch=NAME`.
|
|
86
|
+
- Pub-sub hooks gain an optional `keys:` glob filter for per-key scoping.
|
|
87
|
+
- Hook signatures standardized: `store:` is now mandatory and first on every hook (`:reduce` previously had no `store:`). Event-specific kwargs follow.
|
|
88
|
+
- `:accept` event renames `pending_key:` → `key:` to match every other lifecycle event.
|
|
89
|
+
- All event names are now verbs in a uniform grammar (RPC verbs `:fetch :reduce :check`; pub-sub verbs `:put :delete :refresh :build :accept`).
|
|
90
|
+
|
|
91
|
+
### New
|
|
92
|
+
- `EVENTS` metadata table on `HookRegistry` is the single source of truth for event names, argument shapes, return shapes, and failure semantics (rpc vs pubsub).
|
|
93
|
+
- Shape-check at registration: callable kwargs are verified against the EVENTS table at load time; mismatched signatures raise `UsageError` immediately instead of surfacing at fire time.
|
|
12
94
|
|
|
13
95
|
## 0.5.0 — Wire protocol `textus/2`; CLI restructure; Store split (breaking)
|
|
14
96
|
|
data/README.md
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
A context store for codebases that humans and AI agents both have to read and write. Dotted keys, schema-validated entries, role-gated writes, byte-copy publish, an audit log of every change. Built so an agent landing in your repo can run one command (`textus intro`) and know what to read, what to write, and what's off-limits.
|
|
9
9
|
|
|
10
|
-
Reference implementation in Ruby. Wire format `textus/
|
|
10
|
+
Reference implementation in Ruby. Wire format `textus/2`. SPEC: [`SPEC.md`](SPEC.md). Implementation notes: [`docs/`](docs/).
|
|
11
11
|
|
|
12
12
|
## Versioning
|
|
13
13
|
|
|
14
14
|
Two versions, deliberately independent:
|
|
15
15
|
|
|
16
|
-
- **Protocol wire string:** `textus/
|
|
17
|
-
- **Gem version:** semver, currently `0.
|
|
16
|
+
- **Protocol wire string:** `textus/2`. Stable; breaking changes require `textus/3`.
|
|
17
|
+
- **Gem version:** semver, currently `0.8.0`. The gem version is decoupled from the protocol string — internal refactors bump the gem; only wire-format changes bump the protocol.
|
|
18
18
|
|
|
19
19
|
Envelope payloads carry the `protocol` field. The gem version is irrelevant to the wire format.
|
|
20
20
|
|
|
@@ -60,23 +60,25 @@ Manifest `path:` fields are relative to `.textus/zones/`. So `working.network.or
|
|
|
60
60
|
Read and write:
|
|
61
61
|
|
|
62
62
|
```sh
|
|
63
|
-
textus get working.network.org.jane
|
|
64
|
-
textus list --zone=working
|
|
65
|
-
echo '{"
|
|
66
|
-
| textus put working.network.org.bob --as=human --stdin
|
|
67
|
-
textus stale --zone=derived
|
|
63
|
+
textus get working.network.org.jane
|
|
64
|
+
textus list --zone=working
|
|
65
|
+
echo '{"_meta":{"name":"bob","org":"acme"},"body":"hi\n"}' \
|
|
66
|
+
| textus put working.network.org.bob --as=human --stdin
|
|
67
|
+
textus stale --zone=derived
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
(All verbs return JSON envelopes by default; pass `--format=json` explicitly if you prefer.)
|
|
71
|
+
|
|
70
72
|
For the full shape — Claude plugin with agents, skills, commands, pending walkthrough, intake action — see [`examples/claude-plugin/`](examples/claude-plugin/).
|
|
71
73
|
|
|
72
|
-
## What
|
|
74
|
+
## What ships today
|
|
73
75
|
|
|
74
76
|
- **Per-entry formats.** `format: markdown | json | yaml | text` on a manifest entry. `cat .textus/zones/derived/marketplace.json | jq .` works without going through textus — the in-store file *is* the consumer-shaped artifact. Structured outputs carry `_meta` at the top level (`generated_at`, `from`, `template`, `reducer`).
|
|
75
77
|
- **Per-leaf publishing.** Nested entries declare `publish_each: "skills/{basename}/SKILL.md"`. Every leaf byte-copies to its consumer location on `textus build`. No more hand-mirrored `agents/` / `skills/` / `commands/` directories.
|
|
76
78
|
- **Stable identity (`uid:`).** 16-char hex, auto-minted on first `put`, preserved across writes and moves. `textus key mv old.key new.key` renames in place — uid survives, audit row records `from_key`, `to_key`, `uid`. Reorganising a tree no longer breaks references.
|
|
77
79
|
- **Strict key grammar.** `/^[a-z0-9][a-z0-9-]*$/`, max 8 segments × 64 chars. `textus key migrate --dry-run|--write` rewrites existing stores with illegal segments deterministically.
|
|
78
80
|
- **`textus intro`.** One-shot store orientation: zones with writers + purposes, entry families with schemas and publish targets, loaded extensions, write flows per role, the full CLI verb table. The boot signal for any agent — one tool call and it knows your store.
|
|
79
|
-
- **`textus doctor`.** Health check across
|
|
81
|
+
- **`textus doctor`.** Health check across 9 categories: missing schemas/templates, broken extensions, illegal nested keys, sentinel drift, audit log readability, unowned schema fields, schema violations, and missing manifest files. Returns `ok: true` only when nothing is wrong; warnings and info don't flip the bit.
|
|
80
82
|
- **Actionable hints on every error.** `UnknownKey` carries ranked "did you mean" suggestions. `WriteForbidden` names the role that *would* be allowed. `BadFrontmatter` tells you exactly what to rename. Printed to stderr alongside the JSON envelope on stdout.
|
|
81
83
|
|
|
82
84
|
Symlink-mode publish was removed; publish is `FileUtils.cp` + sentinel. Sentinels for published files live under `.textus/sentinels/<target_rel>.textus-managed.json` so consumer directories stay clean. Legacy sibling sentinels auto-migrate on next publish.
|
|
@@ -98,14 +100,14 @@ All verbs accept `--format=json` and return the envelope defined in SPEC §8. Wr
|
|
|
98
100
|
| `deps K` / `rdeps K` | Forward / reverse projection dependencies |
|
|
99
101
|
| `published` | List `publish_to:` targets and their backing keys |
|
|
100
102
|
| `doctor --check=schema_violations` | Validate every entry against its schema |
|
|
101
|
-
| `
|
|
103
|
+
| `hook list [--event=E]` | Registered hooks grouped by event (fetch, reduce, check, put, delete, refresh, build, accept) |
|
|
102
104
|
|
|
103
105
|
**Write:**
|
|
104
106
|
|
|
105
107
|
| Verb | Role |
|
|
106
108
|
|---|---|
|
|
107
109
|
| `put K --stdin --as=R [--action=NAME]` | per zone |
|
|
108
|
-
| `
|
|
110
|
+
| `hook run NAME [--key=val] [--as=R]` | per zone written (invoke a registered fetch hook) |
|
|
109
111
|
| `delete K --if-etag=E --as=R` | per zone |
|
|
110
112
|
| `refresh K --as=script` | per zone (typically `script`) |
|
|
111
113
|
| `key mv old new --as=R [--dry-run]` | per zone (same-zone moves; uid preserved) |
|
|
@@ -128,8 +130,6 @@ All verbs accept `--format=json` and return the envelope defined in SPEC §8. Wr
|
|
|
128
130
|
| `schema diff NAME` | Compare a schema against entries that claim it |
|
|
129
131
|
| `schema migrate NAME [--rename=OLD:NEW]` | Rewrite frontmatter keys across affected entries |
|
|
130
132
|
|
|
131
|
-
**Deprecated (removed in 0.6):** `mv`, `uid`, `migrate-keys`, `schema-init`, `schema-diff`, `schema-migrate`, `extensions`, `action`.
|
|
132
|
-
|
|
133
133
|
## Zones and roles
|
|
134
134
|
|
|
135
135
|
| Zone | `writable_by` | Purpose |
|
|
@@ -148,16 +148,24 @@ Derived entries declare a `projection:` (`select`, `pluck`, `sort_by`, `limit`,
|
|
|
148
148
|
|
|
149
149
|
`publish_to: [path]` byte-copies a single derived file to one target. `publish_each: "template/{basename}.md"` on a nested entry byte-copies every leaf to its templated target — substitutes `{leaf}`, `{basename}`, `{key}`, `{ext}`. Sentinels for every published file live under `.textus/sentinels/`. See SPEC §5.2, §5.3, §5.12.
|
|
150
150
|
|
|
151
|
-
##
|
|
151
|
+
## Extension points
|
|
152
|
+
|
|
153
|
+
textus exposes one DSL verb:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
Textus.hook(event, name, **opts) { |args| ... }
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Drop `.rb` files into `.textus/hooks/`. Events:
|
|
152
160
|
|
|
153
|
-
|
|
161
|
+
- `:fetch` — bring bytes in from elsewhere (returns `{frontmatter:, body:}`)
|
|
162
|
+
- `:reduce` — transform rows during projection (returns rows)
|
|
163
|
+
- `:check` — custom doctor check (returns issues)
|
|
164
|
+
- `:put`, `:delete`, `:refresh`, `:build`, `:accept` — react to lifecycle events
|
|
154
165
|
|
|
155
|
-
|
|
156
|
-
- **`Textus.reducer(:name) do |rows:, config:|`** — shapes rows in a derived projection. Pure function. Configured via `projection.reducer`. May return an Array (templated builds) or a Hash (templateless json/yaml).
|
|
157
|
-
- **`Textus.hook(:event, :name) do |kwargs|`** — fires on `:put`, `:delete`, `:refresh`, `:build`, or `:accept`. In-process; 2 s timeout per hook; failures land in the audit log as `event_error` rows.
|
|
158
|
-
- **`Textus.doctor_check(:name) do |store:|`** — contributes whole-tree validators to `textus doctor`. Returns an array of issue hashes `{code, level, subject, message, fix}` that merge into the doctor report. Timeouts and exceptions surface as `doctor_check.*` issues; they do not abort the doctor run.
|
|
166
|
+
See SPEC.md §5.10 for the full contract.
|
|
159
167
|
|
|
160
|
-
Schemas (`.textus/schemas/<name>.yaml`) declare field shapes, per-field `maintained_by:` ownership, and an `evolution:` block (`added_in`, `deprecated_at`, `migrate_from`). Full contract in SPEC §5.8
|
|
168
|
+
Schemas (`.textus/schemas/<name>.yaml`) declare field shapes, per-field `maintained_by:` ownership, and an `evolution:` block (`added_in`, `deprecated_at`, `migrate_from`). Full contract in SPEC §5.8.
|
|
161
169
|
|
|
162
170
|
## Examples
|
|
163
171
|
|