structured-context 0.9.0 → 0.9.2
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 +60 -21
- package/dist/commands/spaces.js +3 -2
- package/dist/commands/validate.js +24 -14
- package/dist/config.d.ts +1 -1
- package/dist/config.js +19 -4
- package/dist/plugin-api.d.ts +1 -1
- package/dist/plugins/markdown/read-space.js +35 -8
- package/dist/plugins/util.d.ts +3 -3
- package/dist/read/read-space.js +1 -1
- package/dist/schemas/knowledge_wiki.json +40 -1
- package/dist/schemas/{general.json → strategy_general.json} +1 -1
- package/dist/types.d.ts +14 -2
- package/docs/config.md +1 -1
- package/docs/schemas.md +2 -2
- package/package.json +1 -1
- package/schemas/knowledge_wiki.json +40 -1
- package/schemas/{general.json → strategy_general.json} +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,58 @@
|
|
|
1
1
|
# structured-context
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Keep your content clean and your thinking clear.
|
|
4
|
+
|
|
5
|
+
`structured-context` provides assistive tools for working with structured content, including knowledge wikis (a la [Karpathy](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f)), product/strategy frameworks (like [Teresa Torres' Opportunity Solution Trees](https://www.producttalk.org/opportunity-solution-trees/) and [Martin et al's strategy cascade](https://rogerlmartin.com/thought-pillars/strategy), and more. Great for flat sets of documents, hierarchical or layered context trees, or any kind of knowledge graph.
|
|
6
|
+
|
|
7
|
+
Assign a schema to your markdown content to give humans and AI agents consistent structure and content guidelines.
|
|
8
|
+
|
|
9
|
+
In Claude Code:
|
|
10
|
+
```
|
|
11
|
+
❯ Use /structured-context to configure 'my-research' as a knowledge wiki, then organise it
|
|
12
|
+
|
|
13
|
+
⏺ Skill(structured-context)
|
|
14
|
+
⎿ Successfully loaded skill
|
|
15
|
+
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Command line tools:
|
|
21
|
+
```
|
|
22
|
+
> sctx validate strategy
|
|
23
|
+
|
|
24
|
+
Space Validation Results
|
|
25
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
26
|
+
Content and structure
|
|
27
|
+
Valid 16
|
|
28
|
+
Schema validation errors 0
|
|
29
|
+
Broken links 1
|
|
30
|
+
Duplicate keys 0
|
|
31
|
+
Rule violations 2
|
|
32
|
+
Hierarchy violations 0
|
|
33
|
+
Orphans (hierarchy nodes - no parent) 1
|
|
34
|
+
Ignored during parsing 0
|
|
35
|
+
|
|
36
|
+
Orphans (hierarchy nodes - no parent):
|
|
37
|
+
Solution looking for a problem.md
|
|
38
|
+
|
|
39
|
+
Broken links:
|
|
40
|
+
Steal underpants.md: [[Gnome goals]] → Link target "Gnome goals" in field "related" not found
|
|
41
|
+
|
|
42
|
+
Rule violations:
|
|
43
|
+
workflow (1):
|
|
44
|
+
Build Death Star.md: Opportunities should contribute to at least one Outcome
|
|
45
|
+
|
|
46
|
+
best-practice (1):
|
|
47
|
+
High tech juice press.md: Explore multiple candidate solutions (aim for at least three) for an active target opportunity
|
|
48
|
+
```
|
|
4
49
|
|
|
5
50
|
## Installation
|
|
6
51
|
|
|
7
52
|
Requires [Bun](https://bun.sh) runtime.
|
|
8
53
|
|
|
54
|
+
Install globally (recommended):
|
|
55
|
+
|
|
9
56
|
```bash
|
|
10
57
|
bun install -g structured-context
|
|
11
58
|
```
|
|
@@ -18,10 +65,11 @@ bunx structured-context validate <space>
|
|
|
18
65
|
|
|
19
66
|
## Setup for AI Agents
|
|
20
67
|
|
|
21
|
-
|
|
68
|
+
Claude Code plugin, providing validation hooks, slash commands, and agent skills. Install it with:
|
|
22
69
|
|
|
23
70
|
```
|
|
24
|
-
claude plugin
|
|
71
|
+
claude plugin marketplace add mindsocket/structured-context
|
|
72
|
+
claude plugin install structured-context
|
|
25
73
|
```
|
|
26
74
|
|
|
27
75
|
Skills can also be installed standalone without the plugin:
|
|
@@ -38,31 +86,24 @@ See [docs/concepts.md](docs/concepts.md) for the full terminology reference, inc
|
|
|
38
86
|
|
|
39
87
|
`structured-context` looks for its config file in this order:
|
|
40
88
|
|
|
41
|
-
1.
|
|
42
|
-
2.
|
|
43
|
-
3.
|
|
89
|
+
1. `--config` command line argument
|
|
90
|
+
2. `$SCTX_CONFIG` env var
|
|
91
|
+
3. `~/.config/structured-context/config.json` (or `$XDG_CONFIG_HOME/structured-context/config.json`)
|
|
92
|
+
4. `./config.json` in the current working directory
|
|
44
93
|
|
|
45
|
-
See `config.example.json` for the full structure. The config maps space names to paths, with optional
|
|
94
|
+
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.
|
|
46
95
|
|
|
47
96
|
**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.
|
|
48
97
|
|
|
49
98
|
**Plugins and markdown plugin config:** See `sctx docs config` for the full reference including `fieldMap`, `typeInference`, `templateDir`, filter views, and plugin loading rules.
|
|
50
99
|
|
|
51
|
-
### Spaces
|
|
52
|
-
|
|
53
|
-
A space is a named directory or single file registered in the config. Spaces let you reference content by name instead of path:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
sctx validate ProductX
|
|
57
|
-
```
|
|
58
|
-
|
|
59
100
|
### Schemas
|
|
60
101
|
|
|
61
102
|
Schemas define the structure and rules for the entities in a space, allowing customisation and extension to different models.
|
|
62
103
|
|
|
63
|
-
|
|
104
|
+
Several schemas (`strict_ost`, `knowledge_wiki` and `general`) are included. The general (strategy) schema combines a basic vision/mission/goals hierarchy with a hierarchy loosely based on Opportunity Solution Trees. It is intentionally flexible to support rapid initial adoption. The strict OST schema has a narrower scope, and reflects Teresa Torres' specific recommendations for Opportunity Solution Trees more closely. The knowledge wiki schema defines a flatter set of types used for knowledge bases.
|
|
64
105
|
|
|
65
|
-
|
|
106
|
+
structured-context schemas use a metaschema based on JSON Schema Draft-07 that adds a top-level `$metadata` block:
|
|
66
107
|
|
|
67
108
|
```json5
|
|
68
109
|
"$metadata": {
|
|
@@ -91,8 +132,6 @@ sctx schemas use a metaschema based on JSON Schema Draft-07 that adds a top-leve
|
|
|
91
132
|
}
|
|
92
133
|
```
|
|
93
134
|
|
|
94
|
-
Rules are a flat array (`rules[]`) with per-rule `category`.
|
|
95
|
-
|
|
96
135
|
Schema hierarchy levels support DAG (multi-parent) relationships via configurable edge fields. Each entry in `$metadata.hierarchy.levels` can be a plain type name string (defaults to `parent` field on child nodes) or an object:
|
|
97
136
|
|
|
98
137
|
```json5
|
|
@@ -154,7 +193,7 @@ If no provider defines `hierarchy`, hierarchy-specific checks are skipped. Readi
|
|
|
154
193
|
- **Transitive resolution**: `$ref` chains are resolved recursively across files/schemas (including nested `allOf` usage in partials).
|
|
155
194
|
- **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.
|
|
156
195
|
|
|
157
|
-
Schema resolution order: space config `schema` > global config `schema
|
|
196
|
+
Schema resolution order: space config `schema` > global config `schema`. Schema resolution fails if none is configured.
|
|
158
197
|
|
|
159
198
|
**⚠️ Security Notice: Only use schemas and configuration files from trusted sources.**
|
|
160
199
|
|
|
@@ -283,7 +322,7 @@ Generates a Mermaid Entity Relationship Diagram from a schema:
|
|
|
283
322
|
|
|
284
323
|
Example:
|
|
285
324
|
```bash
|
|
286
|
-
sctx schemas show
|
|
325
|
+
sctx schemas show strategy_general --mermaid-erd
|
|
287
326
|
```
|
|
288
327
|
|
|
289
328
|
### Sync space to Miro
|
package/dist/commands/spaces.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { basename, resolve } from 'node:path';
|
|
2
|
-
import { configPath, loadConfig
|
|
2
|
+
import { configPath, loadConfig } from '../config';
|
|
3
3
|
function renderConfigValue(v) {
|
|
4
4
|
if (typeof v === 'object' && v !== null)
|
|
5
5
|
return JSON.stringify(v);
|
|
@@ -12,7 +12,8 @@ export function listSpaces() {
|
|
|
12
12
|
for (const space of config.spaces) {
|
|
13
13
|
console.log(`${space.name}`);
|
|
14
14
|
console.log(` path: ${space.path}`);
|
|
15
|
-
|
|
15
|
+
const schema = space.schema ?? config.schema;
|
|
16
|
+
console.log(` schema: ${schema ? basename(schema) : '(none)'}`);
|
|
16
17
|
if (space.miroBoardId)
|
|
17
18
|
console.log(` miro: configured`);
|
|
18
19
|
const plugins = space.plugins ?? {};
|
|
@@ -97,7 +97,7 @@ export async function validate(context, options = {}) {
|
|
|
97
97
|
const { schema, schemaRefRegistry, schemaValidator } = context;
|
|
98
98
|
const metadata = schema.metadata;
|
|
99
99
|
const readResult = await readSpace(context);
|
|
100
|
-
const { nodes,
|
|
100
|
+
const { nodes, parseIssues } = readResult;
|
|
101
101
|
const result = {
|
|
102
102
|
validCount: 0,
|
|
103
103
|
nodeErrorCount: 0,
|
|
@@ -106,8 +106,8 @@ export async function validate(context, options = {}) {
|
|
|
106
106
|
duplicateErrors: [],
|
|
107
107
|
ruleViolations: [],
|
|
108
108
|
hierarchyViolations: [],
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
orphans: [],
|
|
110
|
+
parseIssues,
|
|
111
111
|
};
|
|
112
112
|
for (const node of nodes) {
|
|
113
113
|
const valid = schemaValidator(node.schemaData);
|
|
@@ -143,7 +143,7 @@ export async function validate(context, options = {}) {
|
|
|
143
143
|
result.hierarchyViolations = [...hierarchyValidation.violations];
|
|
144
144
|
// Calculate orphan count (informational, not a validation error)
|
|
145
145
|
if (metadata.hierarchy) {
|
|
146
|
-
result.
|
|
146
|
+
result.orphans = [...buildSpaceGraph(nodes, metadata.hierarchy.levels).orphans];
|
|
147
147
|
}
|
|
148
148
|
// Load and execute rules validation if schema defines rules
|
|
149
149
|
if (metadata.rules) {
|
|
@@ -180,15 +180,16 @@ export async function validate(context, options = {}) {
|
|
|
180
180
|
for (const v of result.hierarchyViolations) {
|
|
181
181
|
addError(v.file, `hierarchy:${v.description}`, 'hierarchy', v.description);
|
|
182
182
|
}
|
|
183
|
-
const
|
|
183
|
+
const parseErrorCount = result.parseIssues.filter((i) => i.severity === 'error').length;
|
|
184
|
+
const errorCount = Object.values(errorsByFile).reduce((sum, errs) => sum + Object.keys(errs).length, 0) + parseErrorCount;
|
|
184
185
|
console.log(JSON.stringify({
|
|
185
186
|
space: context.space.name,
|
|
186
187
|
valid: errorCount === 0,
|
|
187
188
|
validCount: result.validCount,
|
|
188
189
|
errorCount,
|
|
189
190
|
errors: errorsByFile,
|
|
190
|
-
orphanCount: result.
|
|
191
|
-
|
|
191
|
+
orphanCount: result.orphans.length,
|
|
192
|
+
parseIssues: result.parseIssues,
|
|
192
193
|
}, null, 2));
|
|
193
194
|
return errorCount > 0 ? 1 : 0;
|
|
194
195
|
}
|
|
@@ -223,12 +224,20 @@ export async function validate(context, options = {}) {
|
|
|
223
224
|
console.log(fmt(' Duplicate keys', result.duplicateErrors.length, true));
|
|
224
225
|
console.log(fmt(' Rule violations', result.ruleViolations.length, true));
|
|
225
226
|
console.log(fmt(' Hierarchy violations', result.hierarchyViolations.length, true));
|
|
226
|
-
console.log(fmt(' Orphans (hierarchy nodes - no parent)', result.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
227
|
+
console.log(fmt(' Orphans (hierarchy nodes - no parent)', result.orphans.length, true, true));
|
|
228
|
+
const parseIssueErrorCount = result.parseIssues.filter((i) => i.severity === 'error').length;
|
|
229
|
+
console.log(fmt(' Excluded during parsing', result.parseIssues.length, true, parseIssueErrorCount === 0));
|
|
230
|
+
if (result.orphans.length > 0) {
|
|
231
|
+
console.log(`\nOrphans (hierarchy nodes - no parent):`);
|
|
232
|
+
for (const node of result.orphans)
|
|
233
|
+
console.log(` ${node.label}`);
|
|
234
|
+
}
|
|
235
|
+
if (result.parseIssues.length > 0) {
|
|
236
|
+
console.log(`\nExcluded during parsing:`);
|
|
237
|
+
for (const issue of result.parseIssues) {
|
|
238
|
+
const detail = issue.message ? ` - ${issue.message}` : '';
|
|
239
|
+
console.log(` ${issue.file}: ${issue.severity} - ${issue.type}${detail}`);
|
|
240
|
+
}
|
|
232
241
|
}
|
|
233
242
|
if (result.nodeErrors.length > 0) {
|
|
234
243
|
console.log(`\nSchema validation errors:`);
|
|
@@ -285,7 +294,8 @@ export async function validate(context, options = {}) {
|
|
|
285
294
|
result.refErrors.length > 0 ||
|
|
286
295
|
result.duplicateErrors.length > 0 ||
|
|
287
296
|
result.ruleViolations.length > 0 ||
|
|
288
|
-
result.hierarchyViolations.length > 0
|
|
297
|
+
result.hierarchyViolations.length > 0 ||
|
|
298
|
+
parseIssueErrorCount > 0) {
|
|
289
299
|
return 1;
|
|
290
300
|
}
|
|
291
301
|
return 0;
|
package/dist/config.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export declare function configPath(): string;
|
|
|
28
28
|
export declare function loadConfig(): Config;
|
|
29
29
|
/** Get the full space config entry by name. Throws if not found. */
|
|
30
30
|
export declare function getSpaceConfig(name: string, config: Config): SpaceConfig;
|
|
31
|
-
/** Resolve schema path: CLI arg > space-level config > global config
|
|
31
|
+
/** Resolve schema path: CLI arg > space-level config > global config. Throws if none configured. */
|
|
32
32
|
export declare function resolveSchema(config: Config, space?: SpaceConfig): string;
|
|
33
33
|
type StringFields<T> = {
|
|
34
34
|
[K in keyof T]: T[K] extends string | undefined ? K : never;
|
package/dist/config.js
CHANGED
|
@@ -98,13 +98,24 @@ function resolveRelativePaths(config, configDir) {
|
|
|
98
98
|
return p;
|
|
99
99
|
return resolve(configDir, p);
|
|
100
100
|
};
|
|
101
|
+
const relSchema = (p) => {
|
|
102
|
+
if (!p || isAbsolute(p) || isUrl(p))
|
|
103
|
+
return p;
|
|
104
|
+
if (!p.includes('/') && !p.includes('\\')) {
|
|
105
|
+
const localPath = resolve(configDir, p);
|
|
106
|
+
if (existsSync(localPath))
|
|
107
|
+
return localPath;
|
|
108
|
+
return join(bundledSchemasDir, p);
|
|
109
|
+
}
|
|
110
|
+
return resolve(configDir, p);
|
|
111
|
+
};
|
|
101
112
|
return {
|
|
102
113
|
...config,
|
|
103
|
-
schema:
|
|
114
|
+
schema: relSchema(config.schema),
|
|
104
115
|
spaces: config.spaces.map((s) => ({
|
|
105
116
|
...s,
|
|
106
117
|
path: rel(s.path),
|
|
107
|
-
schema:
|
|
118
|
+
schema: relSchema(s.schema),
|
|
108
119
|
plugins: normalizePlugins(s.plugins),
|
|
109
120
|
})),
|
|
110
121
|
};
|
|
@@ -159,9 +170,13 @@ export function getSpaceConfig(name, config) {
|
|
|
159
170
|
}
|
|
160
171
|
return space;
|
|
161
172
|
}
|
|
162
|
-
/** Resolve schema path: CLI arg > space-level config > global config
|
|
173
|
+
/** Resolve schema path: CLI arg > space-level config > global config. Throws if none configured. */
|
|
163
174
|
export function resolveSchema(config, space) {
|
|
164
|
-
|
|
175
|
+
const schema = space?.schema ?? config.schema;
|
|
176
|
+
if (!schema) {
|
|
177
|
+
throw new Error('No schema configured. Set "schema" in the space config or at the top level of the config file.');
|
|
178
|
+
}
|
|
179
|
+
return schema;
|
|
165
180
|
}
|
|
166
181
|
/** Update a string field on a space entry and persist config. */
|
|
167
182
|
export function updateSpaceField(spaceName, field, value) {
|
package/dist/plugin-api.d.ts
CHANGED
|
@@ -9,4 +9,4 @@ export type { AnySchemaObject, SchemaObject, ValidateFunction } from 'ajv';
|
|
|
9
9
|
export type { ParseHook, ParseResult, PluginContext, RenderFormat, RenderHook, RenderOptions, StructuredContextPlugin, TemplateSyncHook, TemplateSyncOptions, } from './plugins/util';
|
|
10
10
|
export type { SharedEmbeddingFields } from './schema/metadata-contract';
|
|
11
11
|
export type { SpaceGraph } from './space-graph';
|
|
12
|
-
export type { BaseNode, EdgeDefinition, HierarchyLevel, Relationship, SchemaMetadata, SchemaWithMetadata, SpaceContext, SpaceNode, UnresolvedRef, } from './types';
|
|
12
|
+
export type { BaseNode, EdgeDefinition, HierarchyLevel, ParseIssue, Relationship, SchemaMetadata, SchemaWithMetadata, SpaceContext, SpaceNode, UnresolvedRef, } from './types';
|
|
@@ -25,7 +25,13 @@ export function readSpaceOnAPage(context) {
|
|
|
25
25
|
pageType: 'space_on_a_page',
|
|
26
26
|
metadata,
|
|
27
27
|
});
|
|
28
|
-
|
|
28
|
+
const parseIssues = terminatedHeadings.map((heading) => ({
|
|
29
|
+
file: heading,
|
|
30
|
+
severity: 'warning',
|
|
31
|
+
type: 'terminated',
|
|
32
|
+
message: 'Ignored headings detected beyond end of hierarchy.',
|
|
33
|
+
}));
|
|
34
|
+
return { nodes, parseIssues, diagnostics: { kind: 'page', preambleNodeCount } };
|
|
29
35
|
}
|
|
30
36
|
export async function readSpaceDirectory(context, options) {
|
|
31
37
|
const { space, schema: { metadata }, } = context;
|
|
@@ -38,17 +44,33 @@ export async function readSpaceDirectory(context, options) {
|
|
|
38
44
|
const knownTypes = typeInferenceCfg?.mode !== 'off' ? extractSchemaTypeNames(context.schema, context.schemaRefRegistry) : undefined;
|
|
39
45
|
const files = await Array.fromAsync(new Glob('**/*.md').scan({ cwd: directory, followSymlinks: true }));
|
|
40
46
|
const nodes = [];
|
|
41
|
-
const
|
|
42
|
-
const nonSpace = [];
|
|
47
|
+
const parseIssues = [];
|
|
43
48
|
for (const file of files) {
|
|
44
49
|
const absoluteFilePath = resolve(directory, file);
|
|
45
50
|
if (absoluteTemplateDir && absoluteFilePath.startsWith(absoluteTemplateDir)) {
|
|
46
51
|
continue;
|
|
47
52
|
}
|
|
48
53
|
const content = readFileSync(join(directory, file), 'utf-8');
|
|
49
|
-
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = matter(content);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
// gray-matter caches parsed YAML and has a known bug where a caught exception
|
|
60
|
+
// corrupts its internal cache, causing subsequent parses to silently return {}.
|
|
61
|
+
// Clear the cache after any parse error to avoid stale state.
|
|
62
|
+
// See: https://github.com/jonschlinkert/gray-matter/issues/166
|
|
63
|
+
matter.clearCache();
|
|
64
|
+
parseIssues.push({
|
|
65
|
+
file,
|
|
66
|
+
severity: 'error',
|
|
67
|
+
type: 'parse',
|
|
68
|
+
message: err instanceof Error ? err.message : String(err),
|
|
69
|
+
});
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
50
72
|
if (!parsed.data || Object.keys(parsed.data).length === 0) {
|
|
51
|
-
|
|
73
|
+
parseIssues.push({ file, severity: 'warning', type: 'empty' });
|
|
52
74
|
continue;
|
|
53
75
|
}
|
|
54
76
|
const data = coerceDates(applyFieldMap(parsed.data, fieldMap));
|
|
@@ -56,7 +78,7 @@ export async function readSpaceDirectory(context, options) {
|
|
|
56
78
|
data.type = inferTypeFromPath(file, typeInferenceCfg, knownTypes, context.schema.metadata.typeAliases);
|
|
57
79
|
}
|
|
58
80
|
if (!data.type) {
|
|
59
|
-
|
|
81
|
+
parseIssues.push({ file, severity: 'warning', type: 'no-type' });
|
|
60
82
|
continue;
|
|
61
83
|
}
|
|
62
84
|
if (ON_A_PAGE_TYPES.includes(data.type) && !options?.includeOnAPageFiles) {
|
|
@@ -81,9 +103,14 @@ export async function readSpaceDirectory(context, options) {
|
|
|
81
103
|
});
|
|
82
104
|
nodes.push(...embedded);
|
|
83
105
|
for (const heading of terminatedHeadings) {
|
|
84
|
-
|
|
106
|
+
parseIssues.push({
|
|
107
|
+
file: `${file} > ${heading}`,
|
|
108
|
+
severity: 'warning',
|
|
109
|
+
type: 'terminated',
|
|
110
|
+
message: 'Ignored headings detected beyond end of hierarchy.',
|
|
111
|
+
});
|
|
85
112
|
}
|
|
86
113
|
}
|
|
87
114
|
}
|
|
88
|
-
return { nodes,
|
|
115
|
+
return { nodes, parseIssues, diagnostics: { kind: 'directory' } };
|
|
89
116
|
}
|
package/dist/plugins/util.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AnySchemaObject } from 'ajv';
|
|
2
2
|
import type { SpaceGraph } from '../space-graph';
|
|
3
|
-
import type { BaseNode, SpaceContext } from '../types';
|
|
3
|
+
import type { BaseNode, ParseIssue, SpaceContext } from '../types';
|
|
4
4
|
export declare const PLUGIN_PREFIX = "sctx-";
|
|
5
5
|
export declare const CONFIG_PLUGINS_DIR = "plugins";
|
|
6
6
|
/** Normalize a plugin name to its canonical prefixed form. */
|
|
@@ -11,8 +11,8 @@ export type PluginContext = SpaceContext & {
|
|
|
11
11
|
};
|
|
12
12
|
export type ParseResult = {
|
|
13
13
|
nodes: BaseNode[];
|
|
14
|
-
/**
|
|
15
|
-
|
|
14
|
+
/** Issues encountered while parsing files — excluded files with reason and severity. */
|
|
15
|
+
parseIssues: ParseIssue[];
|
|
16
16
|
/** Plugin diagnostics: keyed scalar or list values. */
|
|
17
17
|
diagnostics: Record<string, number | string | string[]>;
|
|
18
18
|
};
|
package/dist/read/read-space.js
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"study": "source",
|
|
10
10
|
"article": "source",
|
|
11
11
|
"paper": "source",
|
|
12
|
-
"research": "source"
|
|
12
|
+
"research": "source",
|
|
13
|
+
"comparison": "synthesis",
|
|
14
|
+
"query": "synthesis"
|
|
13
15
|
},
|
|
14
16
|
"relationships": [
|
|
15
17
|
{
|
|
@@ -26,6 +28,13 @@
|
|
|
26
28
|
"fieldOn": "child",
|
|
27
29
|
"multiple": true
|
|
28
30
|
},
|
|
31
|
+
{
|
|
32
|
+
"parent": "source",
|
|
33
|
+
"type": "entity",
|
|
34
|
+
"field": "sources",
|
|
35
|
+
"fieldOn": "child",
|
|
36
|
+
"multiple": true
|
|
37
|
+
},
|
|
29
38
|
{
|
|
30
39
|
"parent": "concept",
|
|
31
40
|
"type": "synthesis",
|
|
@@ -134,6 +143,36 @@
|
|
|
134
143
|
}
|
|
135
144
|
]
|
|
136
145
|
},
|
|
146
|
+
{
|
|
147
|
+
"type": "object",
|
|
148
|
+
"description": "A concrete entity — a person, organisation, product, model, or other named thing. The building blocks of the wiki's knowledge graph alongside concepts. Entities are distinct from concepts: a concept frames an idea or mechanism; an entity is a specific, identifiable thing in the world.",
|
|
149
|
+
"allOf": [{ "$ref": "sctx://_sctx_base#/$defs/baseNodeProps" }],
|
|
150
|
+
"properties": {
|
|
151
|
+
"type": { "const": "entity" },
|
|
152
|
+
"summary": {
|
|
153
|
+
"$ref": "sctx://_sctx_base#/$defs/summary"
|
|
154
|
+
},
|
|
155
|
+
"sources": {
|
|
156
|
+
"type": "array",
|
|
157
|
+
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
158
|
+
"description": "Wikilinks to source pages that contributed to this entity page"
|
|
159
|
+
},
|
|
160
|
+
"related": {
|
|
161
|
+
"type": "array",
|
|
162
|
+
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
163
|
+
"description": "Wikilinks to related entity, concept, or synthesis pages"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"required": ["type"],
|
|
167
|
+
"additionalProperties": true,
|
|
168
|
+
"examples": [
|
|
169
|
+
{
|
|
170
|
+
"type": "entity",
|
|
171
|
+
"summary": "AI safety research lab founded by Dario Amodei and Daniela Amodei",
|
|
172
|
+
"tags": ["company", "ai", "safety"]
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
},
|
|
137
176
|
{
|
|
138
177
|
"type": "object",
|
|
139
178
|
"description": "A synthesis page — connects multiple concepts and sources into a coherent model, framework, or thesis. The highest-value pages in a knowledge wiki. Must reference contributing sources.",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/mindsocket/structured-context/main/schemas/generated/_structured_context_schema_meta.json",
|
|
3
|
-
"$id": "sctx://
|
|
3
|
+
"$id": "sctx://strategy_general",
|
|
4
4
|
"title": "Strategy and opportunity ladder",
|
|
5
5
|
"description": "Validates frontmatter for space node files",
|
|
6
6
|
"$metadata": {
|
package/dist/types.d.ts
CHANGED
|
@@ -86,6 +86,18 @@ export type SpaceNode = BaseNode & {
|
|
|
86
86
|
*/
|
|
87
87
|
resolvedParents: ResolvedParentRef[];
|
|
88
88
|
};
|
|
89
|
+
export type ParseIssue = {
|
|
90
|
+
/** Source identifier of the file or item (filename or heading title). */
|
|
91
|
+
file: string;
|
|
92
|
+
/** error: prevented the file from producing nodes; warning: file was silently excluded. */
|
|
93
|
+
severity: 'error' | 'warning';
|
|
94
|
+
/** empty: no content; no-type: no type field;
|
|
95
|
+
* parse: parse failure (syntax issue or unexpected content);
|
|
96
|
+
* plugins may choose to provide additional types. */
|
|
97
|
+
type: 'empty' | 'no-type' | 'parse' | string;
|
|
98
|
+
/** Human-readable detail. */
|
|
99
|
+
message?: string;
|
|
100
|
+
};
|
|
89
101
|
/** Rule categories for organizing executable validation rules */
|
|
90
102
|
export type RuleCategory = 'validation' | 'coherence' | 'workflow' | 'best-practice';
|
|
91
103
|
export type { Rule } from './schema/metadata-contract';
|
|
@@ -118,8 +130,8 @@ export type SchemaWithMetadata = SchemaObject & {
|
|
|
118
130
|
export type ReadSpaceResult = {
|
|
119
131
|
/** Fully resolved nodes produced by the plugin and enriched by core. */
|
|
120
132
|
nodes: SpaceNode[];
|
|
121
|
-
/**
|
|
122
|
-
|
|
133
|
+
/** Issues encountered while parsing files — excluded files with reason and severity. */
|
|
134
|
+
parseIssues: ParseIssue[];
|
|
123
135
|
/** Plugin diagnostics: keyed scalar or list values. */
|
|
124
136
|
diagnostics: Record<string, number | string | string[]>;
|
|
125
137
|
/** Name of the plugin that produced the nodes. */
|
package/docs/config.md
CHANGED
package/docs/schemas.md
CHANGED
|
@@ -20,11 +20,11 @@ Set `schema` in the space config entry:
|
|
|
20
20
|
}
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Resolution order: space `schema` > global `schema
|
|
23
|
+
Resolution order: space `schema` > global `schema`. Schema resolution fails if none is configured.
|
|
24
24
|
|
|
25
25
|
## Bundled schemas
|
|
26
26
|
|
|
27
|
-
### `
|
|
27
|
+
### `strategy_general.json`
|
|
28
28
|
|
|
29
29
|
Flexible planning schema spanning strategy + OST-like flow.
|
|
30
30
|
|
package/package.json
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"study": "source",
|
|
10
10
|
"article": "source",
|
|
11
11
|
"paper": "source",
|
|
12
|
-
"research": "source"
|
|
12
|
+
"research": "source",
|
|
13
|
+
"comparison": "synthesis",
|
|
14
|
+
"query": "synthesis"
|
|
13
15
|
},
|
|
14
16
|
"relationships": [
|
|
15
17
|
{
|
|
@@ -26,6 +28,13 @@
|
|
|
26
28
|
"fieldOn": "child",
|
|
27
29
|
"multiple": true
|
|
28
30
|
},
|
|
31
|
+
{
|
|
32
|
+
"parent": "source",
|
|
33
|
+
"type": "entity",
|
|
34
|
+
"field": "sources",
|
|
35
|
+
"fieldOn": "child",
|
|
36
|
+
"multiple": true
|
|
37
|
+
},
|
|
29
38
|
{
|
|
30
39
|
"parent": "concept",
|
|
31
40
|
"type": "synthesis",
|
|
@@ -134,6 +143,36 @@
|
|
|
134
143
|
}
|
|
135
144
|
]
|
|
136
145
|
},
|
|
146
|
+
{
|
|
147
|
+
"type": "object",
|
|
148
|
+
"description": "A concrete entity — a person, organisation, product, model, or other named thing. The building blocks of the wiki's knowledge graph alongside concepts. Entities are distinct from concepts: a concept frames an idea or mechanism; an entity is a specific, identifiable thing in the world.",
|
|
149
|
+
"allOf": [{ "$ref": "sctx://_sctx_base#/$defs/baseNodeProps" }],
|
|
150
|
+
"properties": {
|
|
151
|
+
"type": { "const": "entity" },
|
|
152
|
+
"summary": {
|
|
153
|
+
"$ref": "sctx://_sctx_base#/$defs/summary"
|
|
154
|
+
},
|
|
155
|
+
"sources": {
|
|
156
|
+
"type": "array",
|
|
157
|
+
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
158
|
+
"description": "Wikilinks to source pages that contributed to this entity page"
|
|
159
|
+
},
|
|
160
|
+
"related": {
|
|
161
|
+
"type": "array",
|
|
162
|
+
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
163
|
+
"description": "Wikilinks to related entity, concept, or synthesis pages"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"required": ["type"],
|
|
167
|
+
"additionalProperties": true,
|
|
168
|
+
"examples": [
|
|
169
|
+
{
|
|
170
|
+
"type": "entity",
|
|
171
|
+
"summary": "AI safety research lab founded by Dario Amodei and Daniela Amodei",
|
|
172
|
+
"tags": ["company", "ai", "safety"]
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
},
|
|
137
176
|
{
|
|
138
177
|
"type": "object",
|
|
139
178
|
"description": "A synthesis page — connects multiple concepts and sources into a coherent model, framework, or thesis. The highest-value pages in a knowledge wiki. Must reference contributing sources.",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/mindsocket/structured-context/main/schemas/generated/_structured_context_schema_meta.json",
|
|
3
|
-
"$id": "sctx://
|
|
3
|
+
"$id": "sctx://strategy_general",
|
|
4
4
|
"title": "Strategy and opportunity ladder",
|
|
5
5
|
"description": "Validates frontmatter for space node files",
|
|
6
6
|
"$metadata": {
|