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/dist/commands/render.js
CHANGED
|
@@ -1,17 +1,72 @@
|
|
|
1
|
-
import { writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
2
3
|
import { discoverPlugins, loadPlugins } from '../plugins/loader';
|
|
4
|
+
import { isRenderMultiFileOutput } from '../plugins/util';
|
|
3
5
|
import { buildFormatRegistry } from '../render/registry';
|
|
4
6
|
import { executeRender } from '../render/render';
|
|
5
7
|
export async function render(context, format, options) {
|
|
6
|
-
const result = await executeRender(format, context, {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const result = await executeRender(format, context, {
|
|
9
|
+
filter: options.filter,
|
|
10
|
+
hierarchy: options.hierarchy,
|
|
11
|
+
data: options.data,
|
|
12
|
+
});
|
|
13
|
+
const targetPath = options.out ?? options.output;
|
|
14
|
+
const force = Boolean(options.force);
|
|
15
|
+
if (typeof result === 'string') {
|
|
16
|
+
if (targetPath) {
|
|
17
|
+
const resolved = resolve(targetPath);
|
|
18
|
+
if (existsSync(resolved)) {
|
|
19
|
+
if (statSync(resolved).isDirectory()) {
|
|
20
|
+
throw new Error(`Output path "${targetPath}" is a directory. Please specify a file path.`);
|
|
21
|
+
}
|
|
22
|
+
if (!force) {
|
|
23
|
+
throw new Error(`File "${targetPath}" already exists. Use --force to overwrite.`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
27
|
+
writeFileSync(resolved, result, 'utf8');
|
|
28
|
+
console.error(`Written to ${targetPath}`);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
process.stdout.write(result);
|
|
32
|
+
if (!result.endsWith('\n'))
|
|
33
|
+
process.stdout.write('\n');
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
10
36
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
37
|
+
if (isRenderMultiFileOutput(result)) {
|
|
38
|
+
if (targetPath) {
|
|
39
|
+
const targetDir = resolve(targetPath);
|
|
40
|
+
const conflicts = [];
|
|
41
|
+
for (const file of result.files) {
|
|
42
|
+
const fullPath = join(targetDir, file.path);
|
|
43
|
+
if (existsSync(fullPath)) {
|
|
44
|
+
conflicts.push(file.path);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (conflicts.length > 0 && !force) {
|
|
48
|
+
const preview = conflicts
|
|
49
|
+
.slice(0, 5)
|
|
50
|
+
.map((f) => ` ${f}`)
|
|
51
|
+
.join('\n');
|
|
52
|
+
const extra = conflicts.length > 5 ? `\n ... and ${conflicts.length - 5} more` : '';
|
|
53
|
+
throw new Error(`Refusing to overwrite ${conflicts.length} existing file(s) in "${targetPath}" without --force:\n${preview}${extra}`);
|
|
54
|
+
}
|
|
55
|
+
for (const file of result.files) {
|
|
56
|
+
const fullPath = join(targetDir, file.path);
|
|
57
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
58
|
+
writeFileSync(fullPath, file.content, 'utf8');
|
|
59
|
+
}
|
|
60
|
+
console.error(`Written ${result.files.length} file(s) to ${targetPath}`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.log(`Rendered ${result.files.length} file(s):`);
|
|
64
|
+
for (const file of result.files) {
|
|
65
|
+
const bytes = Buffer.byteLength(file.content, 'utf8');
|
|
66
|
+
console.log(` ${file.path} (${bytes} bytes)`);
|
|
67
|
+
}
|
|
68
|
+
console.log('\nSpecify --out <dir> to write files to disk.');
|
|
69
|
+
}
|
|
15
70
|
}
|
|
16
71
|
}
|
|
17
72
|
export async function renderList(context) {
|
package/dist/commands/schemas.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
3
|
-
import { loadConfig
|
|
4
|
-
import {
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { loadConfig } from '../config';
|
|
4
|
+
import { discoverPlugins } from '../plugins/loader';
|
|
5
|
+
import { normalizePluginName, resolvePluginSchemasDir, resolveSpacePluginSchemas, shortenPluginName, } from '../plugins/util';
|
|
6
|
+
import { ruleSemantic } from '../schema/rule-severity';
|
|
7
|
+
import { bundledSchemasDir, extractEntityInfo, loadSchema, readRawSchema, } from '../schema/schema';
|
|
5
8
|
import { mergeVariantProperties, resolveRef } from '../schema/schema-refs';
|
|
9
|
+
import { DEFAULT_SEVERITY_BY_CATEGORY } from '../schema/validate-rules';
|
|
10
|
+
import { createSpaceContext } from '../space-context';
|
|
6
11
|
function isBundledPath(schemaPath) {
|
|
7
12
|
return dirname(schemaPath) === bundledSchemasDir;
|
|
8
13
|
}
|
|
@@ -73,9 +78,14 @@ function showDefs(defs) {
|
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
80
|
function showMetadata(metadata) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
console.log(
|
|
81
|
+
const hierarchies = metadata.hierarchies ?? [];
|
|
82
|
+
if (hierarchies.length > 0) {
|
|
83
|
+
console.log('');
|
|
84
|
+
for (const hierarchy of hierarchies) {
|
|
85
|
+
const parts = hierarchy.levels.map((l) => (l.selfRef ? `${l.type}(+)` : l.type));
|
|
86
|
+
const main = hierarchy === metadata.hierarchy ? ' (main)' : '';
|
|
87
|
+
console.log(`hierarchy "${hierarchy.name}"${main}: ${parts.join(' → ')}`);
|
|
88
|
+
}
|
|
79
89
|
}
|
|
80
90
|
else {
|
|
81
91
|
console.log('\nhierarchy: (none)');
|
|
@@ -88,15 +98,15 @@ function showMetadata(metadata) {
|
|
|
88
98
|
const groups = new Map();
|
|
89
99
|
for (const rule of metadata.rules) {
|
|
90
100
|
const rules = groups.get(rule.category) ?? [];
|
|
91
|
-
rules.push(
|
|
101
|
+
rules.push(rule);
|
|
92
102
|
groups.set(rule.category, rules);
|
|
93
103
|
}
|
|
94
104
|
if (groups.size > 0) {
|
|
95
105
|
console.log('\nRules:');
|
|
96
|
-
for (const [
|
|
97
|
-
console.log(` ${
|
|
98
|
-
for (const
|
|
99
|
-
console.log(` ${
|
|
106
|
+
for (const [category, rules] of groups) {
|
|
107
|
+
console.log(` ${category} (default severity: ${DEFAULT_SEVERITY_BY_CATEGORY[category]}):`);
|
|
108
|
+
for (const rule of rules) {
|
|
109
|
+
console.log(` ${rule.id}: ${rule.description}${ruleAnnotations(rule)}`);
|
|
100
110
|
}
|
|
101
111
|
}
|
|
102
112
|
}
|
|
@@ -110,7 +120,7 @@ function showMetadata(metadata) {
|
|
|
110
120
|
}
|
|
111
121
|
}
|
|
112
122
|
}
|
|
113
|
-
function showRegistry(schemaPath, schemaRefRegistry) {
|
|
123
|
+
function showRegistry(schemaPath, schemaRefRegistry, pluginSchemas) {
|
|
114
124
|
const bundledIds = new Set();
|
|
115
125
|
if (existsSync(bundledSchemasDir)) {
|
|
116
126
|
for (const file of readdirSync(bundledSchemasDir).filter((f) => f.endsWith('.json'))) {
|
|
@@ -119,9 +129,22 @@ function showRegistry(schemaPath, schemaRefRegistry) {
|
|
|
119
129
|
bundledIds.add(s.$id);
|
|
120
130
|
}
|
|
121
131
|
}
|
|
132
|
+
const pluginIds = new Set();
|
|
133
|
+
if (pluginSchemas) {
|
|
134
|
+
for (const { schemasDir } of pluginSchemas) {
|
|
135
|
+
if (!existsSync(schemasDir))
|
|
136
|
+
continue;
|
|
137
|
+
for (const file of readdirSync(schemasDir).filter((f) => f.endsWith('.json'))) {
|
|
138
|
+
const s = readRawSchema(join(schemasDir, file));
|
|
139
|
+
if (typeof s.$id === 'string')
|
|
140
|
+
pluginIds.add(s.$id);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
122
144
|
console.log(`\nRegistry (${schemaPath}):`);
|
|
123
145
|
for (const [id] of schemaRefRegistry) {
|
|
124
|
-
|
|
146
|
+
const origin = bundledIds.has(id) ? 'bundled' : pluginIds.has(id) ? 'plugin' : 'local';
|
|
147
|
+
console.log(` [${origin}] ${id}`);
|
|
125
148
|
}
|
|
126
149
|
}
|
|
127
150
|
/**
|
|
@@ -130,7 +153,6 @@ function showRegistry(schemaPath, schemaRefRegistry) {
|
|
|
130
153
|
*/
|
|
131
154
|
function generateMermaidErd(metadata, entities) {
|
|
132
155
|
let mmd = 'erDiagram\n';
|
|
133
|
-
const hierarchyLevels = metadata.hierarchy?.levels ?? [];
|
|
134
156
|
// Generate entity definitions with properties
|
|
135
157
|
for (const entity of entities) {
|
|
136
158
|
const safeName = entity.type.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
@@ -145,8 +167,8 @@ function generateMermaidErd(metadata, entities) {
|
|
|
145
167
|
}
|
|
146
168
|
mmd += ' }\n';
|
|
147
169
|
}
|
|
148
|
-
// Generate relationships based on hierarchy
|
|
149
|
-
|
|
170
|
+
// Generate relationships based on the levels of each hierarchy
|
|
171
|
+
for (const { levels: hierarchyLevels } of metadata.hierarchies ?? []) {
|
|
150
172
|
for (let i = 0; i < hierarchyLevels.length - 1; i++) {
|
|
151
173
|
const current = hierarchyLevels[i];
|
|
152
174
|
const next = hierarchyLevels[i + 1];
|
|
@@ -166,7 +188,26 @@ function generateMermaidErd(metadata, entities) {
|
|
|
166
188
|
}
|
|
167
189
|
return mmd;
|
|
168
190
|
}
|
|
169
|
-
|
|
191
|
+
/** Bracketed notes on a rule: semantic, severity (and verdict severities) and `when`. Empty for a plain rule. */
|
|
192
|
+
function ruleAnnotations(rule) {
|
|
193
|
+
const notes = [];
|
|
194
|
+
const semantic = ruleSemantic(rule);
|
|
195
|
+
if (rule.global)
|
|
196
|
+
notes.push('global');
|
|
197
|
+
if (semantic)
|
|
198
|
+
notes.push('semantic');
|
|
199
|
+
if (rule.severity)
|
|
200
|
+
notes.push(`severity: ${rule.severity}`);
|
|
201
|
+
if (semantic?.severity) {
|
|
202
|
+
notes.push(`verdict severity: ${Object.entries(semantic.severity)
|
|
203
|
+
.map(([verdict, sev]) => `${verdict}=${sev}`)
|
|
204
|
+
.join(', ')}`);
|
|
205
|
+
}
|
|
206
|
+
if (rule.when)
|
|
207
|
+
notes.push(`when: ${rule.when}`);
|
|
208
|
+
return notes.length > 0 ? ` [${notes.join('] [')}]` : '';
|
|
209
|
+
}
|
|
210
|
+
export async function listSchemas() {
|
|
170
211
|
const config = loadConfig();
|
|
171
212
|
// List all schemas known to config
|
|
172
213
|
if (existsSync(bundledSchemasDir)) {
|
|
@@ -182,6 +223,23 @@ export function listSchemas() {
|
|
|
182
223
|
}
|
|
183
224
|
}
|
|
184
225
|
}
|
|
226
|
+
// List schemas provided by discovered plugins
|
|
227
|
+
const plugins = await discoverPlugins();
|
|
228
|
+
for (const p of plugins) {
|
|
229
|
+
if (p.schemasDir && existsSync(p.schemasDir)) {
|
|
230
|
+
const files = readdirSync(p.schemasDir)
|
|
231
|
+
.filter((f) => f.endsWith('.json'))
|
|
232
|
+
.sort();
|
|
233
|
+
if (files.length > 0) {
|
|
234
|
+
console.log(`\nPlugin schemas (${p.name}):`);
|
|
235
|
+
for (const file of files) {
|
|
236
|
+
const schema = readRawSchema(join(p.schemasDir, file));
|
|
237
|
+
const id = typeof schema.$id === 'string' ? schema.$id : '(no $id)';
|
|
238
|
+
console.log(` ${file}${file.startsWith('_') ? ' [partial]' : ''} (${id})`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
185
243
|
const configured = [];
|
|
186
244
|
const seen = new Set();
|
|
187
245
|
if (config.schema && !isBundledPath(config.schema)) {
|
|
@@ -201,16 +259,23 @@ export function listSchemas() {
|
|
|
201
259
|
}
|
|
202
260
|
}
|
|
203
261
|
}
|
|
204
|
-
export function showSchema(file, options) {
|
|
262
|
+
export async function showSchema(file, options) {
|
|
205
263
|
const config = loadConfig();
|
|
206
264
|
let schemaPath;
|
|
265
|
+
let schema;
|
|
266
|
+
let schemaRefRegistry;
|
|
267
|
+
let pluginSchemas;
|
|
207
268
|
if (options.space) {
|
|
208
269
|
const space = config.spaces.find((s) => s.name === options.space);
|
|
209
270
|
if (!space) {
|
|
210
271
|
console.error(`Error: Unknown space "${options.space}"`);
|
|
211
272
|
process.exit(1);
|
|
212
273
|
}
|
|
213
|
-
|
|
274
|
+
const context = createSpaceContext(options.space, config);
|
|
275
|
+
schemaPath = context.resolvedSchemaPath;
|
|
276
|
+
schema = context.schema;
|
|
277
|
+
schemaRefRegistry = context.schemaRefRegistry;
|
|
278
|
+
pluginSchemas = resolveSpacePluginSchemas(context.space, config, context.configDir);
|
|
214
279
|
}
|
|
215
280
|
else if (!file) {
|
|
216
281
|
console.error('Error: specify a file argument or use --space');
|
|
@@ -220,7 +285,49 @@ export function showSchema(file, options) {
|
|
|
220
285
|
schemaPath = file;
|
|
221
286
|
}
|
|
222
287
|
else {
|
|
223
|
-
|
|
288
|
+
// Check discovered plugins
|
|
289
|
+
const discovered = await discoverPlugins();
|
|
290
|
+
pluginSchemas = discovered
|
|
291
|
+
.filter((p) => p.schemasDir)
|
|
292
|
+
.map((p) => ({ pluginName: shortenPluginName(p.name), schemasDir: p.schemasDir }));
|
|
293
|
+
let candidatePath;
|
|
294
|
+
if (file.includes('/')) {
|
|
295
|
+
const slashIdx = file.indexOf('/');
|
|
296
|
+
const prefix = file.slice(0, slashIdx);
|
|
297
|
+
const subPath = file.slice(slashIdx + 1);
|
|
298
|
+
const norm = normalizePluginName(prefix);
|
|
299
|
+
const short = shortenPluginName(prefix);
|
|
300
|
+
const pluginDir = resolvePluginSchemasDir(prefix, process.cwd());
|
|
301
|
+
if (pluginDir) {
|
|
302
|
+
candidatePath = resolve(pluginDir, subPath.endsWith('.json') ? subPath : `${subPath}.json`);
|
|
303
|
+
if (!pluginSchemas.some((ps) => ps.pluginName === short)) {
|
|
304
|
+
pluginSchemas.push({ pluginName: short, schemasDir: pluginDir });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
const match = discovered.find((p) => (p.name === norm || p.name === short) && p.schemasDir);
|
|
309
|
+
if (match?.schemasDir) {
|
|
310
|
+
candidatePath = resolve(match.schemasDir, subPath.endsWith('.json') ? subPath : `${subPath}.json`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
for (const p of discovered) {
|
|
316
|
+
if (p.schemasDir) {
|
|
317
|
+
const c = resolve(p.schemasDir, file.endsWith('.json') ? file : `${file}.json`);
|
|
318
|
+
if (existsSync(c)) {
|
|
319
|
+
candidatePath = c;
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (candidatePath && existsSync(candidatePath)) {
|
|
326
|
+
schemaPath = candidatePath;
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
schemaPath = join(bundledSchemasDir, file.endsWith('.json') ? file : `${file}.json`);
|
|
330
|
+
}
|
|
224
331
|
}
|
|
225
332
|
if (!existsSync(schemaPath)) {
|
|
226
333
|
console.error(`Schema not found: ${schemaPath}`);
|
|
@@ -237,7 +344,11 @@ export function showSchema(file, options) {
|
|
|
237
344
|
process.stdout.write(content);
|
|
238
345
|
return;
|
|
239
346
|
}
|
|
240
|
-
|
|
347
|
+
if (!schema || !schemaRefRegistry) {
|
|
348
|
+
const loaded = loadSchema(schemaPath, pluginSchemas);
|
|
349
|
+
schema = loaded.schema;
|
|
350
|
+
schemaRefRegistry = loaded.schemaRefRegistry;
|
|
351
|
+
}
|
|
241
352
|
// Handle --mermaid-erd: generate ERD and exit
|
|
242
353
|
if (options.mermaidErd) {
|
|
243
354
|
const entityInfo = extractEntityInfo(schema, schemaRefRegistry);
|
|
@@ -265,5 +376,5 @@ export function showSchema(file, options) {
|
|
|
265
376
|
console.log(` ${ref}`);
|
|
266
377
|
}
|
|
267
378
|
}
|
|
268
|
-
showRegistry(schemaPath, schemaRefRegistry);
|
|
379
|
+
showRegistry(schemaPath, schemaRefRegistry, pluginSchemas);
|
|
269
380
|
}
|
package/dist/commands/show.d.ts
CHANGED
package/dist/commands/show.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { executeRender } from '../render/render';
|
|
2
2
|
export async function show(context, options) {
|
|
3
|
-
const result = await executeRender('markdown.bullets', context, {
|
|
3
|
+
const result = await executeRender('markdown.bullets', context, {
|
|
4
|
+
filter: options?.filter,
|
|
5
|
+
hierarchy: options?.hierarchy,
|
|
6
|
+
});
|
|
7
|
+
if (typeof result !== 'string') {
|
|
8
|
+
throw new Error('Expected string output from markdown.bullets renderer');
|
|
9
|
+
}
|
|
4
10
|
process.stdout.write(result);
|
|
5
11
|
if (!result.endsWith('\n'))
|
|
6
12
|
process.stdout.write('\n');
|
package/dist/commands/spaces.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { basename, resolve } from 'node:path';
|
|
2
|
-
import { configPath, loadConfig } from '../config';
|
|
2
|
+
import { configPath, getConfigSourceFiles, loadConfig } from '../config';
|
|
3
3
|
function renderConfigValue(v) {
|
|
4
4
|
if (typeof v === 'object' && v !== null)
|
|
5
5
|
return JSON.stringify(v);
|
|
6
6
|
return String(v);
|
|
7
7
|
}
|
|
8
|
-
export function listSpaces() {
|
|
8
|
+
export function listSpaces(options = {}) {
|
|
9
9
|
const path = resolve(configPath());
|
|
10
10
|
const config = loadConfig();
|
|
11
|
+
if (options.json) {
|
|
12
|
+
const spaces = config.spaces.map((space) => ({
|
|
13
|
+
name: space.name,
|
|
14
|
+
path: space.path,
|
|
15
|
+
schema: space.schema ?? config.schema,
|
|
16
|
+
}));
|
|
17
|
+
console.log(JSON.stringify({ configFiles: [...getConfigSourceFiles()], spaces }));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
11
20
|
console.log(`Config: ${path}\n`);
|
|
12
21
|
for (const space of config.spaces) {
|
|
13
22
|
console.log(`${space.name}`);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { loadConfig } from '../config';
|
|
2
|
-
import {
|
|
2
|
+
import { judgeForRun } from '../judge/factory';
|
|
3
|
+
import { parseRuleFilter } from '../schema/rule-filter';
|
|
4
|
+
import { createSpaceContext } from '../space-context';
|
|
5
|
+
import { fileNotInSpace, resolveFileSpace, validateResolvedFile } from '../validate';
|
|
3
6
|
export { validateFile } from '../validate';
|
|
4
7
|
/**
|
|
5
8
|
* CLI command: validate a file and print results to stdout.
|
|
@@ -8,7 +11,19 @@ export { validateFile } from '../validate';
|
|
|
8
11
|
*/
|
|
9
12
|
export async function validateFileCommand(filePath, options = {}) {
|
|
10
13
|
const config = loadConfig();
|
|
11
|
-
const
|
|
14
|
+
const resolved = resolveFileSpace(filePath, config);
|
|
15
|
+
let result;
|
|
16
|
+
if (resolved) {
|
|
17
|
+
const judge = judgeForRun(config, resolved.space, options.semantic);
|
|
18
|
+
result = await validateResolvedFile(filePath, resolved.label, createSpaceContext(resolved.space.name, config), {
|
|
19
|
+
includeGated: options.includeGated,
|
|
20
|
+
judge,
|
|
21
|
+
ruleFilter: options.ruleFilter && parseRuleFilter(options.ruleFilter),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
result = fileNotInSpace(filePath);
|
|
26
|
+
}
|
|
12
27
|
if (!result.inSpace) {
|
|
13
28
|
if (options.json) {
|
|
14
29
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -34,8 +49,8 @@ function printHumanReadable(result) {
|
|
|
34
49
|
}
|
|
35
50
|
if (result.errorCount > 0) {
|
|
36
51
|
console.log(`\n${red}✗${reset} ${result.label} (space: ${result.space}) — ${result.errorCount} error(s)\n`);
|
|
37
|
-
for (const
|
|
38
|
-
console.log(` [${
|
|
52
|
+
for (const issue of Object.values(result.errors)) {
|
|
53
|
+
console.log(` [${issueLabel(issue)}] ${issue.message}`);
|
|
39
54
|
}
|
|
40
55
|
}
|
|
41
56
|
else {
|
|
@@ -43,9 +58,12 @@ function printHumanReadable(result) {
|
|
|
43
58
|
}
|
|
44
59
|
if (result.warningCount > 0) {
|
|
45
60
|
console.log(`\n ${yellow}${result.warningCount} warning(s):${reset}`);
|
|
46
|
-
for (const
|
|
47
|
-
console.log(` ${yellow}[${
|
|
61
|
+
for (const issue of Object.values(result.warnings)) {
|
|
62
|
+
console.log(` ${yellow}[${issueLabel(issue)}]${reset} ${issue.message}`);
|
|
48
63
|
}
|
|
49
64
|
}
|
|
50
65
|
console.log('');
|
|
51
66
|
}
|
|
67
|
+
function issueLabel({ kind, severity }) {
|
|
68
|
+
return severity ? `${kind}:${severity}` : kind;
|
|
69
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { SpaceContext } from '../types';
|
|
2
2
|
export declare function validate(context: SpaceContext, options?: {
|
|
3
3
|
json?: boolean;
|
|
4
|
+
depth?: number;
|
|
5
|
+
includeGated?: boolean;
|
|
6
|
+
semantic?: boolean | string;
|
|
7
|
+
ruleFilter?: string[];
|
|
4
8
|
}): Promise<number>;
|
|
5
9
|
export declare function watchValidate(context: SpaceContext): Promise<never>;
|