structured-context 0.11.0 → 0.13.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.
- package/README.md +52 -198
- package/dist/api.d.ts +17 -15
- package/dist/api.js +12 -9
- package/dist/commands/diagram.d.ts +1 -0
- package/dist/commands/diagram.js +7 -1
- package/dist/commands/docs.js +1 -0
- package/dist/commands/dump.d.ts +15 -2
- package/dist/commands/dump.js +18 -4
- package/dist/commands/providers.d.ts +4 -0
- package/dist/commands/providers.js +52 -0
- package/dist/commands/render.d.ts +4 -0
- package/dist/commands/render.js +64 -9
- package/dist/commands/schemas.d.ts +2 -2
- package/dist/commands/schemas.js +133 -22
- package/dist/commands/show.d.ts +1 -0
- package/dist/commands/show.js +7 -1
- package/dist/commands/spaces.d.ts +3 -1
- package/dist/commands/spaces.js +11 -2
- package/dist/commands/validate-file.d.ts +3 -0
- package/dist/commands/validate-file.js +24 -6
- package/dist/commands/validate.d.ts +4 -0
- package/dist/commands/validate.js +79 -23
- package/dist/config.d.ts +25 -1
- package/dist/config.js +106 -12
- package/dist/filter/augment-nodes.d.ts +3 -1
- package/dist/filter/augment-nodes.js +12 -7
- package/dist/filter/filter-nodes.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +96 -31
- package/dist/judge/context.d.ts +20 -0
- package/dist/judge/context.js +105 -0
- package/dist/judge/factory.d.ts +44 -0
- package/dist/judge/factory.js +148 -0
- package/dist/judge/index.d.ts +13 -0
- package/dist/judge/index.js +8 -0
- package/dist/judge/judge-node.d.ts +27 -0
- package/dist/judge/judge-node.js +122 -0
- package/dist/judge/prompt.d.ts +10 -0
- package/dist/judge/prompt.js +92 -0
- package/dist/judge/provider/anthropic.d.ts +17 -0
- package/dist/judge/provider/anthropic.js +43 -0
- package/dist/judge/provider/fake.d.ts +26 -0
- package/dist/judge/provider/fake.js +56 -0
- package/dist/judge/provider/jev.d.ts +37 -0
- package/dist/judge/provider/jev.js +169 -0
- package/dist/judge/provider/openai-decisions.d.ts +21 -0
- package/dist/judge/provider/openai-decisions.js +84 -0
- package/dist/judge/provider/openai.d.ts +20 -0
- package/dist/judge/provider/openai.js +53 -0
- package/dist/judge/provider/per-rule.d.ts +10 -0
- package/dist/judge/provider/per-rule.js +18 -0
- package/dist/judge/trim.d.ts +27 -0
- package/dist/judge/trim.js +62 -0
- package/dist/judge/types.d.ts +92 -0
- package/dist/judge/types.js +9 -0
- package/dist/judge/verdicts.d.ts +24 -0
- package/dist/judge/verdicts.js +82 -0
- package/dist/load-space-graph.d.ts +7 -5
- package/dist/load-space-graph.js +19 -8
- package/dist/plugins/loader.js +3 -3
- package/dist/plugins/markdown/extract-content-links.js +8 -29
- package/dist/plugins/markdown/index.js +8 -2
- package/dist/plugins/markdown/parse-embedded.d.ts +8 -0
- package/dist/plugins/markdown/parse-embedded.js +195 -24
- package/dist/plugins/markdown/read-space.js +42 -14
- package/dist/plugins/markdown/render-directory.d.ts +11 -0
- package/dist/plugins/markdown/render-directory.js +130 -0
- package/dist/plugins/markdown/template-sync.js +3 -4
- package/dist/plugins/markdown/util.d.ts +26 -5
- package/dist/plugins/markdown/util.js +56 -15
- package/dist/plugins/mermaid/render-mermaid.js +2 -1
- package/dist/plugins/miro/sync.js +7 -6
- package/dist/plugins/util.d.ts +39 -1
- package/dist/plugins/util.js +139 -0
- package/dist/read/resolve-graph-edges.d.ts +1 -1
- package/dist/read/resolve-graph-edges.js +46 -27
- package/dist/read/wikilink-utils.d.ts +17 -0
- package/dist/read/wikilink-utils.js +40 -0
- package/dist/render/render.d.ts +3 -1
- package/dist/render/render.js +2 -2
- package/dist/schema/evaluate-rule.d.ts +33 -2
- package/dist/schema/evaluate-rule.js +58 -4
- package/dist/schema/gating.d.ts +69 -0
- package/dist/schema/gating.js +253 -0
- package/dist/schema/meta-schemas.d.ts +13 -0
- package/dist/schema/meta-schemas.js +50 -0
- package/dist/schema/metadata-contract.d.ts +952 -183
- package/dist/schema/metadata-contract.js +177 -33
- package/dist/schema/rule-filter.d.ts +12 -0
- package/dist/schema/rule-filter.js +46 -0
- package/dist/schema/rule-severity.d.ts +13 -0
- package/dist/schema/rule-severity.js +33 -0
- package/dist/schema/schema.d.ts +14 -8
- package/dist/schema/schema.js +308 -199
- package/dist/schema/validate-graph.d.ts +2 -1
- package/dist/schema/validate-graph.js +40 -23
- package/dist/schema/validate-rules.d.ts +16 -3
- package/dist/schema/validate-rules.js +91 -29
- package/dist/schemas/_knowledge_wiki.json +24 -24
- package/dist/schemas/_okf.json +172 -0
- package/dist/schemas/_ost_strict.json +17 -8
- package/dist/schemas/_sctx_base.json +3 -3
- package/dist/schemas/_strategy_general.json +38 -38
- package/dist/schemas/generated/0.12.0/_structured_context_schema_meta.json +229 -0
- package/dist/schemas/generated/0.13.0/_structured_context_schema_meta.json +400 -0
- package/dist/schemas/generated/latest/_structured_context_schema_meta.json +400 -0
- package/dist/schemas/knowledge_wiki.json +10 -9
- package/dist/schemas/okf.json +8 -0
- package/dist/schemas/strategy_general.json +15 -14
- package/dist/schemas/strict_ost.json +17 -16
- package/dist/space-context.d.ts +5 -2
- package/dist/space-context.js +9 -4
- package/dist/space-graph.d.ts +9 -7
- package/dist/space-graph.js +7 -5
- package/dist/testing.d.ts +33 -0
- package/dist/testing.js +72 -0
- package/dist/types.d.ts +64 -16
- package/dist/validate.d.ts +62 -9
- package/dist/validate.js +229 -114
- package/docs/concepts.md +65 -9
- package/docs/config.md +78 -1
- package/docs/rules.md +168 -50
- package/docs/schemas.md +152 -52
- package/package.json +17 -11
- package/schemas/_knowledge_wiki.json +24 -24
- package/schemas/_okf.json +172 -0
- package/schemas/_ost_strict.json +17 -8
- package/schemas/_sctx_base.json +3 -3
- package/schemas/_strategy_general.json +38 -38
- package/schemas/generated/0.12.0/_structured_context_schema_meta.json +229 -0
- package/schemas/generated/0.13.0/_structured_context_schema_meta.json +400 -0
- package/schemas/generated/latest/_structured_context_schema_meta.json +400 -0
- package/schemas/knowledge_wiki.json +10 -9
- package/schemas/okf.json +8 -0
- package/schemas/strategy_general.json +15 -14
- package/schemas/strict_ost.json +17 -16
- package/dist/schemas/generated/_structured_context_schema_meta.json +0 -191
- package/schemas/generated/_structured_context_schema_meta.json +0 -191
package/README.md
CHANGED
|
@@ -28,10 +28,13 @@ Content and structure
|
|
|
28
28
|
Schema validation errors 0
|
|
29
29
|
Broken links 1
|
|
30
30
|
Duplicate keys 0
|
|
31
|
-
Rule violations
|
|
31
|
+
Rule violations (error) 0
|
|
32
|
+
Rule violations (warning) 1
|
|
33
|
+
Rule violations (info) 1
|
|
32
34
|
Hierarchy violations 0
|
|
33
35
|
Orphans (hierarchy nodes - no parent) 1
|
|
34
|
-
|
|
36
|
+
Unresolved content links 0
|
|
37
|
+
Excluded during parsing 0
|
|
35
38
|
|
|
36
39
|
Orphans (hierarchy nodes - no parent):
|
|
37
40
|
Solution looking for a problem.md
|
|
@@ -40,13 +43,14 @@ Broken links:
|
|
|
40
43
|
Steal underpants.md: [[Gnome goals]] → Link target "Gnome goals" in field "related" not found
|
|
41
44
|
|
|
42
45
|
Rule violations:
|
|
43
|
-
|
|
44
|
-
Build Death Star.md: Opportunities should contribute to at least one Outcome
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
High tech juice press.md: Explore multiple candidate solutions (aim for at least three) for an active target opportunity
|
|
46
|
+
WARNING (1):
|
|
47
|
+
Build Death Star.md: Opportunities should contribute to at least one Outcome [workflow: opportunity-has-outcome]
|
|
48
|
+
INFO (1):
|
|
49
|
+
High tech juice press.md: Explore multiple candidate solutions (aim for at least three) for the target opportunity [best-practice: solution-quantity]
|
|
48
50
|
```
|
|
49
51
|
|
|
52
|
+
Rule violations carry a severity (`error`, `warning` or `info`) that defaults from the rule's category. Only errors fail validation — see [docs/rules.md](docs/rules.md#rule-shape).
|
|
53
|
+
|
|
50
54
|
## Installation
|
|
51
55
|
|
|
52
56
|
Requires [Bun](https://bun.sh) runtime.
|
|
@@ -81,230 +85,99 @@ npx skills add https://github.com/mindsocket/structured-context/tree/main/plugin
|
|
|
81
85
|
|
|
82
86
|
## Concepts
|
|
83
87
|
|
|
84
|
-
See [docs/concepts.md](docs/concepts.md)
|
|
88
|
+
See [docs/concepts.md](docs/concepts.md) (`sctx docs concepts`) for terminology: spaces, nodes, embedded nodes, schemas, hierarchies, rules and more.
|
|
85
89
|
|
|
86
90
|
## Configuration
|
|
87
91
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
1. `--config` command line argument
|
|
91
|
-
2. `$SCTX_CONFIG` env var
|
|
92
|
-
3. `~/.config/structured-context/config.json` (or `$XDG_CONFIG_HOME/structured-context/config.json`)
|
|
93
|
-
4. `./config.json` in the current working directory
|
|
94
|
-
|
|
95
|
-
See `config.example.json` for the full structure. The config maps space names to paths, with optional additional configuration options for parsing and integrations. Paths in config files are resolved relative to the config file.
|
|
96
|
-
|
|
97
|
-
**Including spaces from other configs:** Use `includeSpacesFrom` to import space definitions from other config files. This is useful for aggregating spaces from multiple projects into a central config, reducing the need to specify `--config` on CLI commands. Duplicate space names are not allowed.
|
|
92
|
+
The config file maps space names to paths and schemas, with optional plugin settings. It is found from `--config`, then `$SCTX_CONFIG`, then `$XDG_CONFIG_HOME/structured-context/config.json` (default `~/.config/...`), then `./config.json`. Paths resolve relative to the config file, and `includeSpacesFrom` pulls in spaces from other config files.
|
|
98
93
|
|
|
99
|
-
|
|
94
|
+
See `config.example.json` and [docs/config.md](docs/config.md) (`sctx docs config`) for the full reference, including `fieldMap`, `typeInference`, templates, filter views and plugin loading.
|
|
100
95
|
|
|
101
96
|
### Schemas
|
|
102
97
|
|
|
103
|
-
Schemas define the
|
|
98
|
+
Schemas define the entity types, fields, hierarchy and rules for a space. Set one per space with `schema` in config. Bundled schemas:
|
|
104
99
|
|
|
105
|
-
|
|
100
|
+
- `strategy_general` — flexible strategy ladder (vision, mission, goals) flowing into an OST-style opportunity → solution → experiment hierarchy.
|
|
101
|
+
- `strict_ost` — Teresa Torres' canonical four levels: outcome → opportunity → solution → assumption test.
|
|
102
|
+
- `knowledge_wiki` — flat, LLM-maintained knowledge base of sources, concepts, entities, syntheses and notes, with provenance.
|
|
103
|
+
- `okf` — [Open Knowledge Format](https://github.com/GoogleCloudPlatform/open-knowledge-format) v0.2 bundles: any concept type, with provenance, trust, lifecycle and Attested Computation fields checked when present.
|
|
106
104
|
|
|
107
|
-
|
|
105
|
+
Partials (`_*.json`, e.g. `_strategy_general`, `_knowledge_wiki`) are reusable building blocks for your own schemas. Plugins can contribute further schemas.
|
|
106
|
+
|
|
107
|
+
Schemas are JSON Schema 2020-12 plus a top-level `$metadata` block for hierarchy, relationships, aliases, imports and executable rules, for example:
|
|
108
108
|
|
|
109
109
|
```json5
|
|
110
110
|
"$metadata": {
|
|
111
|
-
"hierarchy": {
|
|
112
|
-
"levels": ["outcome", { "type": "opportunity", "selfRef": true }, "solution", "assumption_test"],
|
|
113
|
-
"allowSkipLevels": false
|
|
114
|
-
},
|
|
115
|
-
"relationships": [
|
|
116
|
-
{
|
|
117
|
-
"parent": "opportunity",
|
|
118
|
-
"type": "assumption",
|
|
119
|
-
"templateFormat": "table",
|
|
120
|
-
"matchers": ["Assumptions"]
|
|
121
|
-
}
|
|
122
|
-
],
|
|
111
|
+
"hierarchy": { "levels": ["outcome", { "type": "opportunity", "selfRef": true }, "solution", "assumption_test"] },
|
|
123
112
|
"aliases": { "experiment": "assumption_test" },
|
|
124
113
|
"rules": [
|
|
125
114
|
{
|
|
126
115
|
"id": "active-outcome-count",
|
|
127
116
|
"category": "workflow",
|
|
128
117
|
"description": "Only one outcome should be active at a time",
|
|
129
|
-
"
|
|
130
|
-
"
|
|
118
|
+
"global": true,
|
|
119
|
+
"expression": "$count(nodes[resolvedType='outcome' and status='active']) <= 1"
|
|
131
120
|
}
|
|
132
121
|
]
|
|
133
122
|
}
|
|
134
123
|
```
|
|
135
124
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
```json5
|
|
139
|
-
// Example fragments for hierarchy level objects:
|
|
140
|
-
{ "type": "opportunity", "selfRef": true }
|
|
141
|
-
{ "type": "solution", "field": "fulfills", "multiple": true }
|
|
142
|
-
{ "type": "requirement", "field": "generates", "fieldOn": "parent", "multiple": true }
|
|
143
|
-
{ "type": "solution", "field": "solutions", "fieldOn": "parent", "multiple": true, "selfRefField": "parent" }
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
| Property | Default | Description |
|
|
147
|
-
|---|---|---|
|
|
148
|
-
| `type` | required | The node type at this hierarchy level |
|
|
149
|
-
| `field` | `"parent"` | Name of the edge field |
|
|
150
|
-
| `fieldOn` | `"child"` | Which side holds the field: `"child"` (child points up) or `"parent"` (parent points down) |
|
|
151
|
-
| `multiple` | `false` | Whether the field is an array of wikilinks (enables multi-parent DAG) |
|
|
152
|
-
| `selfRef` | `false` | Whether a node of this type may reference a same-type parent |
|
|
153
|
-
| `selfRefField` | _undefined_ | Optional field for same-type parent relationships (always on child-side and singular) |
|
|
154
|
-
| `templateFormat` | _undefined_ | Embedding hint (`"list"`, `"table"`, `"heading"`). When set alongside `matchers`, enables hierarchy embedding in typed pages |
|
|
155
|
-
| `matchers` | _undefined_ | Heading patterns (strings or `/regex/`) to match for hierarchy embedding. Case-insensitive. |
|
|
156
|
-
| `embeddedTemplateFields` | _undefined_ | Column names for table stubs when `template-sync` generates templates |
|
|
157
|
-
|
|
158
|
-
The `selfRefField` property enables different fields for regular vs same-type relationships. For example, requirements can list solutions via `solutions` on the requirement node, while solutions can reference parent solutions via `parent` on the solution node.
|
|
159
|
-
|
|
160
|
-
**Hierarchy embedding** — when `templateFormat` and `matchers` are set on a level, typed pages may include section headings that signal embedded content for that type without explicit `[type:: x]` annotations. Two patterns:
|
|
161
|
-
|
|
162
|
-
- **Child-level**: heading matches the child level's type/matchers → list or table items create child nodes.
|
|
163
|
-
- **Parent-level references**: heading matches the *parent* level's type/matchers → bare wikilink items (`- [[X]]`) populate the current node's reference field rather than creating new nodes. Useful for listing parent relationships inline.
|
|
125
|
+
See [docs/schemas.md](docs/schemas.md) (`sctx docs schema`) for the dialect, composition, multiple hierarchies and `$id` namespaces; [docs/rules.md](docs/rules.md) (`sctx docs rules`) for rules; and [docs/concepts.md](docs/concepts.md) for hierarchy embedding and relationships.
|
|
164
126
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
**Adjacent Relationships** (`$metadata.relationships`) define connections between types outside the primary hierarchy — such as an `activity` having many `task` nodes. They drive embedded parsing (typed headings, lists, tables) and template generation.
|
|
168
|
-
|
|
169
|
-
| Property | Default | Description |
|
|
170
|
-
|---|---|---|
|
|
171
|
-
| `parent` | required | Parent canonical type |
|
|
172
|
-
| `type` | required | Child canonical type |
|
|
173
|
-
| `field` | `"parent"` | Frontmatter field holding the wikilink(s). Required when `fieldOn: "parent"`. |
|
|
174
|
-
| `fieldOn` | `"child"` | `"child"`: child holds a link pointing up. `"parent"`: parent holds an array of child links. |
|
|
175
|
-
| `templateFormat` | `"page"` | Hint for `template-sync`: `"table"`, `"list"`, or `"heading"` |
|
|
176
|
-
| `matchers` | `[]` | Heading text to match for embedded parsing (strings or `/regex/`). Case-insensitive. |
|
|
177
|
-
| `multiple` | `true` | Whether multiple children are expected |
|
|
178
|
-
| `embeddedTemplateFields` | `[]` | Field names to include as table columns in templates |
|
|
179
|
-
|
|
180
|
-
With `fieldOn: "parent"`, embedded child nodes (parsed from a matching heading's list or table) are appended as wikilinks to the parent's `field` array, rather than receiving a `parent` field. This matches schemas where the content model naturally lists children on the parent (e.g. `activity.tasks: ["[[Task A]]"]`).
|
|
181
|
-
|
|
182
|
-
Metadata is composable across `$ref` graphs:
|
|
183
|
-
- `hierarchy`: multiple schema files may define a hierarchy; **last one wins** (root schema overrides partials)
|
|
184
|
-
- `aliases` are shallow-merged (later wins)
|
|
185
|
-
- `rules` merge by `id`; conflicts error unless the later rule sets `override: true`
|
|
186
|
-
- `$metadata.rules` supports `$ref` imports for reusable rule packs
|
|
187
|
-
- `relationships` are collected from all files
|
|
188
|
-
|
|
189
|
-
If no file defines `hierarchy`, hierarchy-specific checks are skipped.
|
|
190
|
-
|
|
191
|
-
**Customizing Schemas:**
|
|
192
|
-
- **Partial schemas**: Files starting with an underscore (like `_sctx_base.json`, `_strategy_general.json`, `_knowledge_wiki.json`) are loaded and used to resolve references (using `$ref`).
|
|
193
|
-
- **Partials as entity libraries**: Partials can define reusable entity types in `$defs` that composing schemas reference via `$ref`. Bundled partials like `_strategy_general` and `_knowledge_wiki` provide common entity sets for strategy and wiki content.
|
|
194
|
-
- **Partials can carry metadata**: Unlike plain JSON Schema, partials may include `$metadata` (hierarchy, aliases, relationships, rules). This makes them self-contained units that bundle both type definitions and behavioral metadata.
|
|
195
|
-
- **No-metadata partials**: If a partial has no `$metadata`, prefer `$schema: "http://json-schema.org/draft-07/schema#"` so it validates standalone as plain JSON Schema.
|
|
196
|
-
- **Loading priority**: Partial schemas are loaded from both the default schema directory and the directory of your specified target schema.
|
|
197
|
-
- **Transitive resolution**: `$ref` chains are resolved recursively across files/schemas (including nested `allOf` usage in partials).
|
|
198
|
-
- **Unique IDs**: To encourage clean namespacing, local partial schemas **must** have unique `$id`s that do not collide with the default schemas. If a collision is detected, validation will fail with an error.
|
|
199
|
-
|
|
200
|
-
Schema resolution order: space config `schema` > global config `schema`. Schema resolution fails if none is configured.
|
|
201
|
-
|
|
202
|
-
**⚠️ Security Notice: Only use schemas and configuration files from trusted sources.**
|
|
203
|
-
|
|
204
|
-
The tool executes JSONata expressions defined in schema files for rule validation. A maliciously crafted schema could make JSONata access JavaScript's prototype chain and execute arbitrary code. Only use schemas you've created or reviewed personally.
|
|
127
|
+
**⚠️ Only use schemas and configuration from trusted sources:** rules are JSONata expressions, and a malicious schema can execute arbitrary code. See [docs/config.md](docs/config.md#security-notice).
|
|
205
128
|
|
|
206
129
|
## Usage
|
|
207
130
|
|
|
208
131
|
### Validate nodes
|
|
209
132
|
|
|
210
133
|
```bash
|
|
211
|
-
sctx validate <space> [--watch]
|
|
134
|
+
sctx validate <space> [--watch] [--depth <n>] [--include-gated] [--semantic [provider]] [--rule-filter <dim=values>]
|
|
212
135
|
```
|
|
213
136
|
|
|
214
137
|
Validates markdown files against the JSON schema:
|
|
215
138
|
- Extracts YAML frontmatter from each `.md` file
|
|
216
139
|
- Skips files without frontmatter or without a `type` field
|
|
217
140
|
- Reports validation results with counts and per-file errors
|
|
141
|
+
- Runs executable rules (`$metadata.rules`) with **cascade gating**: a schema, hierarchy, or blocking-rule failure on a node suppresses rule evaluation for that node's hierarchy subtree. Gated rules are reported in a Coverage section so you know what wasn't evaluated and why. See [docs/rules.md](docs/rules.md#cascade-gating) for details.
|
|
142
|
+
- `--depth <n>` limits evaluation to hierarchy nodes within `n` breadth-first levels from the top — useful for staged agentic fix loops (fix the top level, then request the next).
|
|
143
|
+
- `--include-gated` evaluates rules even on gated nodes (still reported as gated) — for surfacing cheap bottom-up checks on request.
|
|
144
|
+
- `--semantic` also evaluates the `semantic` part of rules with the configured model provider (Jev, Anthropic or OpenAI-compatible); `--semantic <provider>` uses that configured model provider instead; `--rule-filter` narrows which rules run. See [docs/semantic-rules.md](docs/semantic-rules.md) (`sctx docs semantic-rules`).
|
|
218
145
|
|
|
219
|
-
###
|
|
146
|
+
### Render space
|
|
220
147
|
|
|
221
148
|
```bash
|
|
222
|
-
sctx
|
|
149
|
+
sctx render <space> <format> [--out <dir-or-file>] [--force] [--filter <view>] [--hierarchy <name>] [--data key=value]
|
|
223
150
|
```
|
|
224
151
|
|
|
225
|
-
|
|
152
|
+
Renders a space in a plugin-provided format. Use `sctx render list [space]` to list available formats.
|
|
226
153
|
|
|
227
|
-
|
|
154
|
+
- `markdown.bullets` — Indented bullet list tree (outputs to stdout or `--out <file>`).
|
|
155
|
+
- `markdown.directory` — Markdown space directory with one file per node. Writes to `--out <dir>` (refuses to overwrite existing files unless `--force` is passed). Flat directory by default, or organized into per-type subfolders with `--data folders=type` or when configured with `typeInference.folderMap`. Without `--out`, prints a summary of files to be written.
|
|
228
156
|
|
|
229
|
-
|
|
157
|
+
### Show space tree
|
|
230
158
|
|
|
231
159
|
```bash
|
|
232
|
-
|
|
233
|
-
sctx show <space> --filter "WHERE resolvedType='solution' and status='active'"
|
|
234
|
-
|
|
235
|
-
# Named view from config
|
|
236
|
-
sctx show <space> --filter active-solutions
|
|
160
|
+
sctx show <space> [--filter <view-or-expression>] [--hierarchy <name>]
|
|
237
161
|
```
|
|
238
162
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
### Filter expressions
|
|
242
|
-
|
|
243
|
-
Filter expressions are used with `--filter` and in config `views`. They use a `SELECT ... WHERE ...` pseudo-DSL:
|
|
244
|
-
|
|
245
|
-
| Form | Meaning |
|
|
246
|
-
|------|---------|
|
|
247
|
-
| `WHERE {jsonata}` | Return nodes where the JSONata predicate is truthy |
|
|
248
|
-
| `SELECT {spec} WHERE {jsonata}` | Filter by WHERE, then expand result via SELECT |
|
|
249
|
-
| `SELECT {spec}` | Expand from all nodes via SELECT (no WHERE filter — returns all nodes, expanded per spec) |
|
|
250
|
-
| `{jsonata}` | Bare JSONata, treated as a WHERE predicate (convenience shorthand) |
|
|
251
|
-
|
|
252
|
-
The WHERE predicate is a [JSONata](https://docs.jsonata.org/overview) expression evaluated per node. Within the expression, each node's fields are accessible directly (e.g. `resolvedType`, `status`, any schema fields like `title`). Two built-in fields are always available regardless of schema: `label` (relative file path, e.g. `"solutions/My Solution.md"`) and `title` (node display name). Additionally, two pre-computed traversal arrays are available:
|
|
253
|
-
|
|
254
|
-
- **`ancestors[]`** — flat array of ancestor nodes, nearest first, deduplicated. Each entry includes all schema fields of the ancestor node, plus:
|
|
255
|
-
- `_field` — the edge field name that connects to the ancestor
|
|
256
|
-
- `_source` — `'hierarchy'` or `'relationship'`
|
|
257
|
-
- `_selfRef` — whether the edge is a same-type (self-referential) link
|
|
258
|
-
- **`descendants[]`** — same structure, for descendant nodes
|
|
259
|
-
|
|
260
|
-
**SELECT spec** expands the result set by walking the graph from matched nodes. The spec is a comma-separated list of directives:
|
|
261
|
-
|
|
262
|
-
| Directive | Meaning |
|
|
263
|
-
|-----------|---------|
|
|
264
|
-
| `ancestors` | All ancestor nodes |
|
|
265
|
-
| `ancestors(type)` | Ancestors of the given resolved type |
|
|
266
|
-
| `descendants` | All descendant nodes |
|
|
267
|
-
| `descendants(type)` | Descendants of the given resolved type |
|
|
268
|
-
| `siblings` | Nodes sharing at least one parent with matched nodes |
|
|
269
|
-
| `relationships` | All nodes connected via a relationship (non-hierarchy) edge |
|
|
270
|
-
| `relationships(childType)` | Relationship-connected nodes of the given child type |
|
|
271
|
-
| `relationships(parentType:childType)` | As above, also filtering by parent type |
|
|
272
|
-
| `relationships(parentType:field:childType)` | Fully qualified: also filtering by edge field name |
|
|
273
|
-
|
|
274
|
-
Multiple directives may be combined: `SELECT ancestors(goal), siblings WHERE ...`
|
|
275
|
-
|
|
276
|
-
**Examples:**
|
|
277
|
-
|
|
278
|
-
```jsonata
|
|
279
|
-
// All solutions
|
|
280
|
-
WHERE resolvedType='solution'
|
|
281
|
-
|
|
282
|
-
// Active solutions only
|
|
283
|
-
WHERE resolvedType='solution' and status='active'
|
|
284
|
-
|
|
285
|
-
// Solutions whose nearest opportunity ancestor is active
|
|
286
|
-
WHERE resolvedType='solution' and $exists(ancestors[resolvedType='opportunity' and status='active'])
|
|
163
|
+
Prints the space as an indented hierarchy tree. Hierarchy roots are listed first, followed by orphans (nodes in the hierarchy but with no resolved parent) and non-hierarchy nodes. The main hierarchy is shown by default; `--hierarchy <name>` selects another (see [Multiple hierarchies](docs/schemas.md#multiple-hierarchies)). `diagram` and `render` accept the same option.
|
|
287
164
|
|
|
288
|
-
|
|
289
|
-
WHERE $exists(ancestors[resolvedType='goal'])
|
|
290
|
-
|
|
291
|
-
// Bare JSONata shorthand (no WHERE keyword)
|
|
292
|
-
resolvedType='solution' and status='active'
|
|
293
|
-
|
|
294
|
-
// Solutions + their opportunity ancestors
|
|
295
|
-
SELECT ancestors(opportunity) WHERE resolvedType='solution'
|
|
165
|
+
When a node appears under multiple parents (DAG hierarchy), it is printed in full under its first parent. Subsequent appearances with children show a `(*)` marker indicating the subtree is omitted.
|
|
296
166
|
|
|
297
|
-
|
|
298
|
-
SELECT siblings WHERE resolvedType='solution' and status='active'
|
|
167
|
+
**Filtering:** `--filter` (on `show` and `render`) takes a named view from the space config or an inline expression: `WHERE {jsonata}`, optionally with `SELECT {spec}` to expand results along the graph.
|
|
299
168
|
|
|
300
|
-
|
|
301
|
-
|
|
169
|
+
```bash
|
|
170
|
+
sctx show <space> --filter "WHERE resolvedType='solution' and status='active'"
|
|
171
|
+
sctx show <space> --filter "SELECT ancestors(opportunity) WHERE resolvedType='solution'"
|
|
172
|
+
sctx show <space> --filter active-solutions # named view
|
|
302
173
|
```
|
|
303
174
|
|
|
175
|
+
See [Filter expressions](docs/concepts.md#filter-expressions) (`sctx docs concepts`) for the full syntax.
|
|
176
|
+
|
|
304
177
|
### Generate Mermaid diagram
|
|
305
178
|
|
|
306
179
|
```bash
|
|
307
|
-
sctx diagram <space> [--output path/to/output.mmd]
|
|
180
|
+
sctx diagram <space> [--output path/to/output.mmd] [--hierarchy <name>]
|
|
308
181
|
```
|
|
309
182
|
|
|
310
183
|
Generates a Mermaid `graph TD` diagram from validated space nodes:
|
|
@@ -321,7 +194,7 @@ sctx schemas show <schema-file> [--mermaid-erd] [--space <name>]
|
|
|
321
194
|
|
|
322
195
|
Generates a Mermaid Entity Relationship Diagram from a schema:
|
|
323
196
|
- Shows all entity types and their properties
|
|
324
|
-
- Displays parent-child relationships based on hierarchy
|
|
197
|
+
- Displays parent-child relationships based on the levels of each hierarchy
|
|
325
198
|
- Useful for visualizing schema structure during development
|
|
326
199
|
|
|
327
200
|
Example:
|
|
@@ -335,28 +208,9 @@ sctx schemas show strategy_general --mermaid-erd
|
|
|
335
208
|
sctx miro-sync <space> [--new-frame <title>] [--dry-run] [--verbose]
|
|
336
209
|
```
|
|
337
210
|
|
|
338
|
-
Syncs space nodes to a Miro board as cards with connectors. Requires `MIRO_TOKEN` env var and `boardId`
|
|
339
|
-
|
|
340
|
-
- `--new-frame <title>` — create a new frame on the board and sync into it; auto-saves the resulting `frameId` back to the miro plugin config
|
|
341
|
-
- `--dry-run` — show what would change without touching Miro
|
|
342
|
-
- `--verbose` / `-v` — detailed per-card and per-connector output
|
|
343
|
-
|
|
344
|
-
Configure the miro plugin in the space's config entry:
|
|
345
|
-
|
|
346
|
-
```json
|
|
347
|
-
{
|
|
348
|
-
"plugins": {
|
|
349
|
-
"miro": {
|
|
350
|
-
"boardId": "your-board-id",
|
|
351
|
-
"frameId": "your-frame-id"
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
On subsequent runs, the cached `frameId` is used automatically. Cards are colour-coded by node type and linked by parent→child connectors. A local `.miro-cache/` directory tracks Miro IDs to enable incremental updates.
|
|
211
|
+
Syncs space nodes to a Miro board as colour-coded cards with parent→child connectors. Requires a `MIRO_TOKEN` env var and `plugins.miro.boardId` in the space config. `--new-frame` creates a frame and saves its `frameId` to config; `--dry-run` previews changes.
|
|
358
212
|
|
|
359
|
-
Sync is one-way
|
|
213
|
+
Sync is one-way and scoped to one frame: only cards and connectors this tool created there are managed, and edits made in Miro to those cards are overwritten on the next sync. A local `.miro-cache/` tracks Miro IDs for incremental updates.
|
|
360
214
|
|
|
361
215
|
### Sync templates with schema
|
|
362
216
|
|
package/dist/api.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Import from this module to use structured-context as a library:
|
|
5
5
|
*
|
|
6
|
-
* import { loadConfig, loadSpaceGraph, validateSpace, validateFile } from '
|
|
7
|
-
* import type { StructuredContextPlugin, PluginContext, ParseResult } from '
|
|
6
|
+
* import { loadConfig, loadSpaceGraph, validateSpace, validateFile } from './api.js';
|
|
7
|
+
* import type { StructuredContextPlugin, PluginContext, ParseResult } from './api.js';
|
|
8
8
|
*
|
|
9
9
|
* Common patterns:
|
|
10
10
|
*
|
|
@@ -18,16 +18,18 @@
|
|
|
18
18
|
* const validation = await validateSpace(ctx, { readResult });
|
|
19
19
|
*/
|
|
20
20
|
export type { AnySchemaObject, SchemaObject, ValidateFunction } from 'ajv';
|
|
21
|
-
export type { Config, SpaceConfig } from './config';
|
|
22
|
-
export { loadConfig, setConfigPath } from './config';
|
|
23
|
-
export { PLUGIN_PREFIX } from './constants';
|
|
24
|
-
export { assembleSpaceGraph, loadSpaceGraph } from './load-space-graph';
|
|
25
|
-
export type { ParseHook, ParseResult, PluginContext, RenderFormat, RenderHook, RenderOptions, StructuredContextPlugin, TemplateSyncHook, TemplateSyncOptions, } from './plugins/util';
|
|
26
|
-
export {
|
|
27
|
-
export
|
|
28
|
-
export {
|
|
29
|
-
export {
|
|
30
|
-
export type
|
|
31
|
-
export
|
|
32
|
-
export type
|
|
33
|
-
export {
|
|
21
|
+
export type { Config, SpaceConfig } from './config.js';
|
|
22
|
+
export { loadConfig, resolveSchema, resolveSchemaPath, setConfigPath } from './config.js';
|
|
23
|
+
export { PLUGIN_PREFIX } from './constants.js';
|
|
24
|
+
export { assembleSpaceGraph, loadSpaceGraph } from './load-space-graph.js';
|
|
25
|
+
export type { ParseHook, ParseResult, PluginContext, RenderFileOutput, RenderFormat, RenderHook, RenderMultiFileOutput, RenderOptions, RenderResult, StructuredContextPlugin, TemplateSyncHook, TemplateSyncOptions, } from './plugins/util.js';
|
|
26
|
+
export { isRenderMultiFileOutput, resolvePluginDir, resolvePluginSchemasDir, resolveSpacePluginSchemas, } from './plugins/util.js';
|
|
27
|
+
export { readSpace } from './read/read-space.js';
|
|
28
|
+
export { buildTargetIndex, formatWikilink, type ParsedWikilink, parseWikilink, wikilinkToTarget, } from './read/wikilink-utils.js';
|
|
29
|
+
export type { SharedEmbeddingFields } from './schema/metadata-contract.js';
|
|
30
|
+
export { bundledSchemasDir, loadSchema, type PluginSchemaSource, setBundledSchemasDir } from './schema/schema.js';
|
|
31
|
+
export { createSpaceContext, SpaceNotFoundError } from './space-context.js';
|
|
32
|
+
export { buildSpaceGraph, type SpaceGraph } from './space-graph.js';
|
|
33
|
+
export type { BaseNode, ContentLink, EdgeDefinition, FileNotInSpaceResult, FileValidationIssue, FileValidationResult, Hierarchy, HierarchyLevel, ParseIssue, ReadSpaceResult, Relationship, ResolvedContentLink, RuleCategory, RuleSeverity, RuleViolation, SchemaMetadata, SchemaWithMetadata, SourceLocation, SpaceContext, SpaceNode, UnresolvedRef, ValidateFileOutput, } from './types.js';
|
|
34
|
+
export type { ValidationResult } from './validate.js';
|
|
35
|
+
export { validateFile, validateSpace } from './validate.js';
|
package/dist/api.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Import from this module to use structured-context as a library:
|
|
5
5
|
*
|
|
6
|
-
* import { loadConfig, loadSpaceGraph, validateSpace, validateFile } from '
|
|
7
|
-
* import type { StructuredContextPlugin, PluginContext, ParseResult } from '
|
|
6
|
+
* import { loadConfig, loadSpaceGraph, validateSpace, validateFile } from './api.js';
|
|
7
|
+
* import type { StructuredContextPlugin, PluginContext, ParseResult } from './api.js';
|
|
8
8
|
*
|
|
9
9
|
* Common patterns:
|
|
10
10
|
*
|
|
@@ -17,10 +17,13 @@
|
|
|
17
17
|
* const graph = await assembleSpaceGraph(ctx, { readResult });
|
|
18
18
|
* const validation = await validateSpace(ctx, { readResult });
|
|
19
19
|
*/
|
|
20
|
-
export { loadConfig, setConfigPath } from './config';
|
|
21
|
-
export { PLUGIN_PREFIX } from './constants';
|
|
22
|
-
export { assembleSpaceGraph, loadSpaceGraph } from './load-space-graph';
|
|
23
|
-
export {
|
|
24
|
-
export {
|
|
25
|
-
export {
|
|
26
|
-
export {
|
|
20
|
+
export { loadConfig, resolveSchema, resolveSchemaPath, setConfigPath } from './config.js';
|
|
21
|
+
export { PLUGIN_PREFIX } from './constants.js';
|
|
22
|
+
export { assembleSpaceGraph, loadSpaceGraph } from './load-space-graph.js';
|
|
23
|
+
export { isRenderMultiFileOutput, resolvePluginDir, resolvePluginSchemasDir, resolveSpacePluginSchemas, } from './plugins/util.js';
|
|
24
|
+
export { readSpace } from './read/read-space.js';
|
|
25
|
+
export { buildTargetIndex, formatWikilink, parseWikilink, wikilinkToTarget, } from './read/wikilink-utils.js';
|
|
26
|
+
export { bundledSchemasDir, loadSchema, setBundledSchemasDir } from './schema/schema.js';
|
|
27
|
+
export { createSpaceContext, SpaceNotFoundError } from './space-context.js';
|
|
28
|
+
export { buildSpaceGraph } from './space-graph.js';
|
|
29
|
+
export { validateFile, validateSpace } from './validate.js';
|
package/dist/commands/diagram.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { writeFileSync } from 'node:fs';
|
|
2
2
|
import { executeRender } from '../render/render';
|
|
3
3
|
export async function diagram(context, options) {
|
|
4
|
-
const result = await executeRender('mermaid.graph', context, {
|
|
4
|
+
const result = await executeRender('mermaid.graph', context, {
|
|
5
|
+
filter: options.filter,
|
|
6
|
+
hierarchy: options.hierarchy,
|
|
7
|
+
});
|
|
8
|
+
if (typeof result !== 'string') {
|
|
9
|
+
throw new Error('Expected string output from mermaid diagram renderer');
|
|
10
|
+
}
|
|
5
11
|
if (options.output) {
|
|
6
12
|
writeFileSync(options.output, result);
|
|
7
13
|
console.log(`Mermaid diagram written to ${options.output}`);
|
package/dist/commands/docs.js
CHANGED
package/dist/commands/dump.d.ts
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
import type { SpaceContext } from '../types';
|
|
2
|
-
export
|
|
1
|
+
import type { ReadSpaceResult, SpaceContext } from '../types';
|
|
2
|
+
export interface DumpOptions {
|
|
3
|
+
/** Named view (resolved via space.views) or a raw filter DSL expression. */
|
|
4
|
+
filter?: string;
|
|
5
|
+
/** Hierarchy used when evaluating the filter (default: the main hierarchy). */
|
|
6
|
+
hierarchy?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Build the dump payload: every parsed node, or only nodes matched by a filter.
|
|
10
|
+
*
|
|
11
|
+
* Unfiltered dumps include schema-invalid nodes, since dump is a debugging aid.
|
|
12
|
+
* Filtered dumps match show/render: the filter runs over the graph of schema-valid nodes.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildDump(context: SpaceContext, options?: DumpOptions): Promise<ReadSpaceResult>;
|
|
15
|
+
export declare function dump(context: SpaceContext, options?: DumpOptions): Promise<void>;
|
package/dist/commands/dump.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { assembleSpaceGraph } from '../load-space-graph';
|
|
2
2
|
import { readSpace } from '../read/read-space';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Build the dump payload: every parsed node, or only nodes matched by a filter.
|
|
5
|
+
*
|
|
6
|
+
* Unfiltered dumps include schema-invalid nodes, since dump is a debugging aid.
|
|
7
|
+
* Filtered dumps match show/render: the filter runs over the graph of schema-valid nodes.
|
|
8
|
+
*/
|
|
9
|
+
export async function buildDump(context, options = {}) {
|
|
10
|
+
const readResult = await readSpace(context);
|
|
11
|
+
if (!options.filter)
|
|
12
|
+
return readResult;
|
|
13
|
+
const graph = await assembleSpaceGraph(context, { ...options, readResult });
|
|
14
|
+
const nodes = readResult.nodes.filter((node) => graph.nodes.get(node.title) === node);
|
|
15
|
+
return { ...readResult, nodes };
|
|
16
|
+
}
|
|
17
|
+
export async function dump(context, options = {}) {
|
|
18
|
+
const { nodes, source, diagnostics } = await buildDump(context, options);
|
|
19
|
+
console.log(JSON.stringify({ nodes, source, diagnostics }, null, 2));
|
|
6
20
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { loadConfig } from '../config';
|
|
2
|
+
import { BUNDLED_PROVIDERS, hydrateProvider } from '../judge/factory';
|
|
3
|
+
function row(name, def, source) {
|
|
4
|
+
try {
|
|
5
|
+
const h = hydrateProvider(name, def);
|
|
6
|
+
const needsKey = h.api !== 'openai-completions' && h.api !== 'fake';
|
|
7
|
+
return {
|
|
8
|
+
name,
|
|
9
|
+
source,
|
|
10
|
+
api: h.api,
|
|
11
|
+
model: h.model,
|
|
12
|
+
endpoint: h.endpoint,
|
|
13
|
+
keyEnv: h.keyEnv,
|
|
14
|
+
valid: !needsKey || h.apiKey !== undefined,
|
|
15
|
+
error: needsKey && h.apiKey === undefined ? `no API key; set ${h.keyEnv}` : undefined,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return {
|
|
20
|
+
name,
|
|
21
|
+
source,
|
|
22
|
+
api: def.api,
|
|
23
|
+
model: def.model,
|
|
24
|
+
endpoint: def.endpoint,
|
|
25
|
+
keyEnv: def.apiKeyEnv,
|
|
26
|
+
valid: false,
|
|
27
|
+
error: err instanceof Error ? err.message : String(err),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** List bundled and configured model providers with their effective config; keys are never shown. */
|
|
32
|
+
export function listProviders(options = {}) {
|
|
33
|
+
const config = loadConfig();
|
|
34
|
+
const configured = config.modelProviders ?? {};
|
|
35
|
+
const rows = [
|
|
36
|
+
...Object.entries(configured).map(([name, def]) => row(name, def, 'config')),
|
|
37
|
+
...Object.entries(BUNDLED_PROVIDERS)
|
|
38
|
+
.filter(([name]) => !(name in configured))
|
|
39
|
+
.map(([name, def]) => row(name, def, 'bundled')),
|
|
40
|
+
];
|
|
41
|
+
if (options.json) {
|
|
42
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
for (const r of rows) {
|
|
46
|
+
const origin = r.source === 'bundled' ? '' : ' (config)';
|
|
47
|
+
const model = r.model ?? '(api default)';
|
|
48
|
+
const endpoint = r.endpoint ?? '(api default)';
|
|
49
|
+
const key = r.valid ? `key via ${r.keyEnv}` : (r.error ?? 'no key');
|
|
50
|
+
console.log(`${r.name}${origin}\n api=${r.api}\n endpoint=${endpoint}\n model=${model}\n ${key}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { SpaceContext } from '../types';
|
|
2
2
|
export declare function render(context: SpaceContext, format: string, options: {
|
|
3
3
|
filter?: string;
|
|
4
|
+
hierarchy?: string;
|
|
4
5
|
output?: string;
|
|
6
|
+
out?: string;
|
|
7
|
+
force?: boolean;
|
|
8
|
+
data?: Record<string, unknown>;
|
|
5
9
|
}): Promise<void>;
|
|
6
10
|
export declare function renderList(context?: SpaceContext): Promise<void>;
|