structured-context 0.19.0 → 0.20.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/dist/api.d.ts CHANGED
@@ -23,6 +23,7 @@ export type { Config, SpaceConfig } from './config';
23
23
  export { loadConfig, resolveConfigPath, resolveSchema, resolveSchemaPath, setConfigPath } from './config';
24
24
  export { addSpace } from './config/write';
25
25
  export { LOCAL_CONFIG_FILE, PLUGIN_PREFIX } from './constants';
26
+ export { SctxError, type SctxErrorCode } from './errors';
26
27
  export { assembleSpaceGraph, loadSpaceGraph } from './load-space-graph';
27
28
  export type { ParseHook, ParseResult, PluginContext, RenderFileOutput, RenderFormat, RenderHook, RenderMultiFileOutput, RenderOptions, RenderResult, StructuredContextPlugin, TemplateSyncHook, TemplateSyncOptions, } from './plugins/util';
28
29
  export { isRenderMultiFileOutput, resolvePluginDir, resolvePluginSchemasDir, resolveSpacePluginSchemas, } from './plugins/util';
package/dist/api.js CHANGED
@@ -22,6 +22,7 @@ export { spaceGraphFrom } from './browser';
22
22
  export { loadConfig, resolveConfigPath, resolveSchema, resolveSchemaPath, setConfigPath } from './config';
23
23
  export { addSpace } from './config/write';
24
24
  export { LOCAL_CONFIG_FILE, PLUGIN_PREFIX } from './constants';
25
+ export { SctxError } from './errors';
25
26
  export { assembleSpaceGraph, loadSpaceGraph } from './load-space-graph';
26
27
  export { isRenderMultiFileOutput, resolvePluginDir, resolvePluginSchemasDir, resolveSpacePluginSchemas, } from './plugins/util';
27
28
  export { readSpace } from './read/read-space';
@@ -1,3 +1,4 @@
1
+ import { type ValidateFileOptions } from '../validate';
1
2
  export { validateFile } from '../validate';
2
3
  /**
3
4
  * CLI command: validate a file and print results to stdout.
@@ -6,7 +7,4 @@ export { validateFile } from '../validate';
6
7
  */
7
8
  export declare function validateFileCommand(filePath: string, options?: {
8
9
  json?: boolean;
9
- includeGated?: boolean;
10
- semantic?: boolean | string;
11
- ruleFilter?: string[];
12
- }): Promise<number>;
10
+ } & Omit<ValidateFileOptions, 'configDir'>): Promise<number>;
@@ -1,6 +1,4 @@
1
1
  import { loadConfig } from '../config';
2
- import { judgeForRun } from '../judge/factory';
3
- import { parseRuleFilter } from '../schema/rule-filter';
4
2
  import { createSpaceContext } from '../space-context';
5
3
  import { fileNotInSpace, resolveFileSpace, validateResolvedFile } from '../validate';
6
4
  export { validateFile } from '../validate';
@@ -14,12 +12,7 @@ export async function validateFileCommand(filePath, options = {}) {
14
12
  const resolved = resolveFileSpace(filePath, config);
15
13
  let result;
16
14
  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
- });
15
+ result = await validateResolvedFile(filePath, resolved.label, createSpaceContext(resolved.space.name, config), options);
23
16
  }
24
17
  else {
25
18
  result = fileNotInSpace(filePath);
@@ -1,9 +1,6 @@
1
1
  import type { SpaceContext } from '../types';
2
+ import { type ValidateSpaceOptions } from '../validate';
2
3
  export declare function validate(context: SpaceContext, options?: {
3
4
  json?: boolean;
4
- depth?: number;
5
- includeGated?: boolean;
6
- semantic?: boolean | string;
7
- ruleFilter?: string[];
8
- }): Promise<number>;
5
+ } & ValidateSpaceOptions): Promise<number>;
9
6
  export declare function watchValidate(context: SpaceContext): Promise<never>;
@@ -1,18 +1,11 @@
1
1
  import { dirname } from 'node:path';
2
2
  import chokidar from 'chokidar';
3
3
  import { getConfigSourceFiles } from '../config';
4
- import { judgeForRun } from '../judge/factory';
5
4
  import { resolveSpacePluginSchemas } from '../plugins/util';
6
- import { parseRuleFilter } from '../schema/rule-filter';
7
5
  import { bundledSchemasDir } from '../schema/schema';
8
6
  import { formatSource, ruleViolationMessage, validateSpace } from '../validate';
9
7
  export async function validate(context, options = {}) {
10
- const result = await validateSpace(context, {
11
- depth: options.depth,
12
- includeGated: options.includeGated,
13
- judge: judgeForRun(context.config, context.space, options.semantic),
14
- ruleFilter: options.ruleFilter && parseRuleFilter(options.ruleFilter),
15
- });
8
+ const result = await validateSpace(context, options);
16
9
  const ruleCounts = { error: 0, warning: 0, info: 0 };
17
10
  for (const v of result.ruleViolations)
18
11
  ruleCounts[v.severity]++;
package/dist/errors.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Stable codes for configuration and environment problems. Hooks and agent skills map a code to a
3
3
  * fix, so the message wording can change without breaking them.
4
4
  */
5
- export type SctxErrorCode = 'E_CONFIG_NOT_FOUND' | 'E_CONFIG_INVALID' | 'E_INCLUDE_NOT_FOUND' | 'E_NO_SPACES' | 'E_SPACE_PATH_MISSING' | 'E_SCHEMA_MISSING' | 'E_SCHEMA_NOT_FOUND' | 'E_SCHEMA_INVALID' | 'E_SPACE_EXISTS' | 'E_TARGET_REQUIRED' | 'E_CONFIG_NEEDS_MANUAL_EDIT';
5
+ export type SctxErrorCode = 'E_CONFIG_NOT_FOUND' | 'E_CONFIG_INVALID' | 'E_INCLUDE_NOT_FOUND' | 'E_NO_SPACES' | 'E_SPACE_PATH_MISSING' | 'E_SCHEMA_MISSING' | 'E_SCHEMA_NOT_FOUND' | 'E_SCHEMA_INVALID' | 'E_SPACE_EXISTS' | 'E_TARGET_REQUIRED' | 'E_CONFIG_NEEDS_MANUAL_EDIT' | 'E_MODEL_PROVIDER_INVALID';
6
6
  export declare class SctxError extends Error {
7
7
  readonly code: SctxErrorCode;
8
8
  constructor(code: SctxErrorCode, message: string);
package/dist/index.js CHANGED
@@ -17,7 +17,6 @@ import { validateFileCommand } from './commands/validate-file';
17
17
  import { loadConfig, setConfigPath } from './config';
18
18
  import { CLI_NAME } from './constants';
19
19
  import { SctxError } from './errors';
20
- import { JudgeConfigError } from './judge/factory';
21
20
  import { createSpaceContext, SpaceNotFoundError } from './space-context';
22
21
  // Once node:process is loaded (vfile, via unified, loads it), Bun's console.log drops piped output
23
22
  // past 64KB when the reader is slow. process.stdout.write delivers all of it before exit.
@@ -36,18 +35,6 @@ export function buildSpaceContext(spaceName) {
36
35
  throw err;
37
36
  }
38
37
  }
39
- /** Run a validate command, reporting a misconfigured model provider as a plain error rather than a stack trace. */
40
- async function reportJudgeConfigErrors(run) {
41
- try {
42
- return await run();
43
- }
44
- catch (err) {
45
- if (!(err instanceof JudgeConfigError))
46
- throw err;
47
- console.error(`Error: ${err.message}`);
48
- return 1;
49
- }
50
- }
51
38
  const require = createRequire(import.meta.url);
52
39
  const packageJson = require('../package.json');
53
40
  const program = new Command();
@@ -68,12 +55,12 @@ program
68
55
  .option('--rule-filter <dimension=values>', 'Evaluate only matching rules; dimensions: category, severity, type, id (repeatable, e.g. severity=error)', (v, prev = []) => [...prev, v])
69
56
  .option('--semantic [provider]', 'Evaluate semantic rules, with the configured model provider or the one named')
70
57
  .action(async (filePath, options) => {
71
- process.exitCode = await reportJudgeConfigErrors(() => validateFileCommand(filePath, {
58
+ process.exitCode = await validateFileCommand(filePath, {
72
59
  json: options.json,
73
60
  includeGated: options.includeGated,
74
61
  semantic: options.semantic,
75
62
  ruleFilter: options.ruleFilter,
76
- }));
63
+ });
77
64
  });
78
65
  program
79
66
  .command('validate')
@@ -92,13 +79,13 @@ program
92
79
  }
93
80
  else {
94
81
  // exitCode rather than process.exit(): exiting straight away can cut off large piped output
95
- process.exitCode = await reportJudgeConfigErrors(() => validate(context, {
82
+ process.exitCode = await validate(context, {
96
83
  json: options.json,
97
84
  depth: options.depth,
98
85
  includeGated: options.includeGated,
99
86
  semantic: options.semantic,
100
87
  ruleFilter: options.ruleFilter,
101
- }));
88
+ });
102
89
  }
103
90
  });
104
91
  program
@@ -7,8 +7,6 @@ type Env = Record<string, string | undefined>;
7
7
  */
8
8
  export declare const BUNDLED_PROVIDERS: Record<string, ModelProviderConfig>;
9
9
  /** Semantic evaluation is misconfigured (no provider, unknown provider, missing key). Reported as a plain CLI error. */
10
- export declare class JudgeConfigError extends Error {
11
- }
12
10
  /**
13
11
  * Hydrate a provider definition into concrete values, in one place. Per field: the explicit value
14
12
  * wins, then the named environment variable if it is set (a set-but-empty variable is an error, not
@@ -29,7 +27,7 @@ export declare function judgeForRun(config: Parameters<typeof createJudge>[0], s
29
27
  /**
30
28
  * The provider for a run: the one named by `name` (the `--semantic <provider>` override), else the space's
31
29
  * `semantic.provider`, else the top-level one. The name resolves against `modelProviders` first, then
32
- * the bundled providers. Throws {@link JudgeConfigError} when it cannot be built.
30
+ * the bundled providers. Throws {@link SctxError} `E_MODEL_PROVIDER_INVALID` when it cannot be built.
33
31
  */
34
32
  export declare function createJudge(config: {
35
33
  modelProviders?: Record<string, ModelProviderConfig>;
@@ -1,3 +1,4 @@
1
+ import { SctxError } from '../errors';
1
2
  import { AnthropicJudgeProvider, RECOMMENDED_ANTHROPIC_MODEL } from './provider/anthropic';
2
3
  import { FakeJudgeProvider } from './provider/fake';
3
4
  import { JEV_LATEST, JevJudgeProvider } from './provider/jev';
@@ -66,8 +67,6 @@ const DEFAULT_KEY_ENV = {
66
67
  'typesafe-systemone': 'TYPESAFE_API_KEY',
67
68
  };
68
69
  /** Semantic evaluation is misconfigured (no provider, unknown provider, missing key). Reported as a plain CLI error. */
69
- export class JudgeConfigError extends Error {
70
- }
71
70
  /**
72
71
  * Hydrate a provider definition into concrete values, in one place. Per field: the explicit value
73
72
  * wins, then the named environment variable if it is set (a set-but-empty variable is an error, not
@@ -75,7 +74,7 @@ export class JudgeConfigError extends Error {
75
74
  */
76
75
  export function hydrateProvider(name, def, env = process.env) {
77
76
  if (!MODEL_PROVIDER_APIS.includes(def.api)) {
78
- throw new JudgeConfigError(`Model provider "${name}" has unknown api "${def.api}". Use one of: ${MODEL_PROVIDER_APIS.join(', ')}.`);
77
+ throw new SctxError('E_MODEL_PROVIDER_INVALID', `Model provider "${name}" has unknown api "${def.api}". Use one of: ${MODEL_PROVIDER_APIS.join(', ')}.`);
79
78
  }
80
79
  const fromEnv = (value, envName) => {
81
80
  if (value !== undefined)
@@ -84,7 +83,7 @@ export function hydrateProvider(name, def, env = process.env) {
84
83
  return undefined;
85
84
  const envValue = env[envName];
86
85
  if (envValue !== undefined && envValue.trim() === '') {
87
- throw new JudgeConfigError(`Model provider "${name}" has ${envName} set but empty; set a value or unset the variable.`);
86
+ throw new SctxError('E_MODEL_PROVIDER_INVALID', `Model provider "${name}" has ${envName} set but empty; set a value or unset the variable.`);
88
87
  }
89
88
  return envValue;
90
89
  };
@@ -104,7 +103,7 @@ export function createModelProvider(name, def, env = process.env) {
104
103
  // OpenAI-compatible endpoints (Ollama, vLLM, ...) may need no key; the others must resolve one.
105
104
  // A key never borrows another provider's variable: only the named apiKeyEnv (or literal apiKey) counts.
106
105
  if (!h.apiKey && h.api !== 'openai-completions' && h.api !== 'fake') {
107
- throw new JudgeConfigError(`Model provider "${name}" (${h.api}) has no API key. Set ${h.keyEnv} in the environment, or "apiKey" / "apiKeyEnv" on the provider.`);
106
+ throw new SctxError('E_MODEL_PROVIDER_INVALID', `Model provider "${name}" (${h.api}) has no API key. Set ${h.keyEnv} in the environment, or "apiKey" / "apiKeyEnv" on the provider.`);
108
107
  }
109
108
  switch (h.api) {
110
109
  case 'fake':
@@ -132,17 +131,17 @@ export function judgeForRun(config, space, semantic) {
132
131
  /**
133
132
  * The provider for a run: the one named by `name` (the `--semantic <provider>` override), else the space's
134
133
  * `semantic.provider`, else the top-level one. The name resolves against `modelProviders` first, then
135
- * the bundled providers. Throws {@link JudgeConfigError} when it cannot be built.
134
+ * the bundled providers. Throws {@link SctxError} `E_MODEL_PROVIDER_INVALID` when it cannot be built.
136
135
  */
137
136
  export function createJudge(config, space = {}, name, env = process.env) {
138
137
  const chosen = name ?? space.semantic?.provider ?? config.semantic?.provider;
139
138
  if (!chosen) {
140
- throw new JudgeConfigError('--semantic needs a model provider. Set "semantic.provider" in config.json, or pass --semantic <provider>.');
139
+ throw new SctxError('E_MODEL_PROVIDER_INVALID', '--semantic needs a model provider. Set "semantic.provider" in config.json, or pass --semantic <provider>.');
141
140
  }
142
141
  const def = config.modelProviders?.[chosen] ?? BUNDLED_PROVIDERS[chosen];
143
142
  if (!def) {
144
143
  const known = [...Object.keys(BUNDLED_PROVIDERS), ...Object.keys(config.modelProviders ?? {})];
145
- throw new JudgeConfigError(`Model provider "${chosen}" is not configured.${known.length ? ` Known providers: ${known.join(', ')}.` : ''}`);
144
+ throw new SctxError('E_MODEL_PROVIDER_INVALID', `Model provider "${chosen}" is not configured.${known.length ? ` Known providers: ${known.join(', ')}.` : ''}`);
146
145
  }
147
146
  return createModelProvider(chosen, def, env);
148
147
  }
@@ -62,26 +62,64 @@ export interface ValidationResult {
62
62
  * Groups related errors and extracts helpful context like allowed values.
63
63
  */
64
64
  export declare function formatErrors(errors: ErrorObject[], schema: SchemaWithMetadata, schemaRefRegistry: Parameters<typeof extractEntityInfo>[1], nodeData: Record<string, unknown>): FormattedError[];
65
- export interface ValidateSpaceOptions {
66
- readResult?: ReadSpaceResult;
67
- /** Limit validation to hierarchy nodes within N breadth-first levels from the top. */
68
- depth?: number;
65
+ interface CommonValidateOptions {
69
66
  /** Evaluate rules on gated nodes anyway (gates are still reported). */
70
67
  includeGated?: boolean;
71
- /** Evaluate only rules matching this filter (see `parseRuleFilter`). */
68
+ /** Evaluate only rules matching these `dimension=values` filters (category, severity, type, id). */
69
+ ruleFilter?: string[];
70
+ /**
71
+ * Evaluate `semantic` rules with the model provider named here, or with the configured one for `true`.
72
+ * Without it, a rule runs only its `expression`, and a semantic-only rule is skipped.
73
+ */
74
+ semantic?: boolean | string;
75
+ }
76
+ /** What a run evaluates with: the public options after the filter is parsed and the judge is built. */
77
+ export interface RunOptions {
78
+ includeGated?: boolean;
72
79
  ruleFilter?: RuleFilter;
73
- /** Evaluates `semantic` rules. Without one, a rule runs only its `expression`, and a semantic-only rule is skipped. */
74
80
  judge?: SemanticJudgeProvider;
81
+ /** Limit judging to one file (and the ancestors that can gate it). */
82
+ judgeTarget?: string;
83
+ }
84
+ export interface ValidateSpaceOptions extends CommonValidateOptions {
85
+ readResult?: ReadSpaceResult;
86
+ /** Limit validation to hierarchy nodes within N breadth-first levels from the top. */
87
+ depth?: number;
75
88
  }
76
89
  /** `file`, `file:line` or `file:line:column`, for messages. */
77
90
  export declare function formatSource({ file, line, column }: SourceLocation): string;
78
91
  /** One-line text for a rule violation: rule, description, and any evidence and verdict guidance. */
79
92
  export declare function ruleViolationMessage(v: RuleViolation): string;
93
+ /** A node that failed type or schema validation. */
94
+ interface NodeValidationFailure {
95
+ node: SpaceNode;
96
+ issues: FormattedError[];
97
+ }
98
+ /** The outcome of validating a set of nodes, before either view shapes it. */
99
+ export interface SpaceValidationResult {
100
+ validCount: number;
101
+ nodeFailures: NodeValidationFailure[];
102
+ refErrors: ValidationResult['refErrors'];
103
+ duplicateErrors: ValidationResult['duplicateErrors'];
104
+ ruleViolations: RuleViolation[];
105
+ hierarchyViolations: GraphViolation[];
106
+ orphans: SpaceNode[];
107
+ unresolvedContentLinks: ValidationResult['unresolvedContentLinks'];
108
+ coverageGates: CoverageGate[];
109
+ contextTrimmed: ValidationResult['contextTrimmed'];
110
+ }
80
111
  /**
81
112
  * Nodes that share an id, with where each is defined. A plugin should keep ids unique, so this guards that contract.
82
113
  * Display titles may repeat.
83
114
  */
84
115
  export declare function findDuplicateIds(nodes: SpaceNode[]): ValidationResult['duplicateErrors'];
116
+ /**
117
+ * The one validation pipeline: type and schema checks, duplicate titles, reference and hierarchy
118
+ * checks, cascade-gated rules, and unresolved content links. Returns every entry; `validateSpace`
119
+ * and `validateFile` are views over it, so a file's results match its entry in a full run.
120
+ * `judgeTarget` limits judging to one file (and the ancestors that can gate it).
121
+ */
122
+ export declare function validateNodes(context: SpaceContext, nodes: SpaceNode[], unresolvedRefs: ReadSpaceResult['unresolvedRefs'], options?: RunOptions): Promise<SpaceValidationResult>;
85
123
  /**
86
124
  * Run full validation on a space and return a structured result.
87
125
  * Does not output to console or call process.exit.
@@ -92,14 +130,8 @@ export declare function resolveFileSpace(filePath: string, config: Config): {
92
130
  space: SpaceConfig;
93
131
  label: string;
94
132
  } | null;
95
- export interface ValidateFileOptions {
133
+ export interface ValidateFileOptions extends CommonValidateOptions {
96
134
  configDir?: string;
97
- /** Evaluate rules on gated nodes anyway (gates are still reported). */
98
- includeGated?: boolean;
99
- /** Evaluate only rules matching this filter (see `parseRuleFilter`). */
100
- ruleFilter?: RuleFilter;
101
- /** Evaluates `semantic` rules. Without one, a rule runs only its `expression`, and a semantic-only rule is skipped. */
102
- judge?: SemanticJudgeProvider;
103
135
  }
104
136
  /** The result for a file that belongs to no configured space. */
105
137
  export declare function fileNotInSpace(filePath: string): FileNotInSpaceResult;
@@ -108,6 +140,8 @@ export declare function fileNotInSpace(filePath: string): FileNotInSpaceResult;
108
140
  * correctness), judging only the file, then selects the file's issues from the result.
109
141
  */
110
142
  export declare function validateResolvedFile(filePath: string, label: string, context: SpaceContext, options?: Omit<ValidateFileOptions, 'configDir'>): Promise<FileValidationResult>;
143
+ /** `validateResolvedFile` with the options already resolved, so a caller can supply its own judge. */
144
+ export declare function validateResolvedFileWith(filePath: string, label: string, context: SpaceContext, runOptions: RunOptions): Promise<FileValidationResult>;
111
145
  /**
112
146
  * Validate a single file within its space.
113
147
  *
@@ -118,3 +152,4 @@ export declare function validateResolvedFile(filePath: string, label: string, co
118
152
  * If the file is not in any configured space, returns a result with `inSpace: false`.
119
153
  */
120
154
  export declare function validateFile(filePath: string, config: Config, options?: ValidateFileOptions): Promise<ValidateFileOutput>;
155
+ export {};
package/dist/validate.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { isAbsolute, relative, resolve } from 'node:path';
2
+ import { judgeForRun } from './judge/factory';
2
3
  import { readSpace } from './read/read-space';
3
4
  import { computeHierarchyDepths, evaluateRulesWithGating } from './schema/gating';
4
- import { filterRules } from './schema/rule-filter';
5
+ import { filterRules, parseRuleFilter } from './schema/rule-filter';
5
6
  import { extractEntityInfo, typeValidatorFor } from './schema/schema';
6
7
  import { validateGraph } from './schema/validate-graph';
7
8
  import { validateRules } from './schema/validate-rules';
@@ -94,6 +95,14 @@ export function formatErrors(errors, schema, schemaRefRegistry, nodeData) {
94
95
  }
95
96
  return formatted;
96
97
  }
98
+ /** Throws `SctxError` `E_MODEL_PROVIDER_INVALID` when `semantic` names a provider that cannot be built. */
99
+ function runOptionsFrom(context, options) {
100
+ return {
101
+ includeGated: options.includeGated,
102
+ ruleFilter: options.ruleFilter && parseRuleFilter(options.ruleFilter),
103
+ judge: judgeForRun(context.config, context.space, options.semantic),
104
+ };
105
+ }
97
106
  /** `file`, `file:line` or `file:line:column`, for messages. */
98
107
  export function formatSource({ file, line, column }) {
99
108
  return line === undefined ? file : `${file}:${line}${column === undefined ? '' : `:${column}`}`;
@@ -159,7 +168,7 @@ export function findDuplicateIds(nodes) {
159
168
  * and `validateFile` are views over it, so a file's results match its entry in a full run.
160
169
  * `judgeTarget` limits judging to one file (and the ancestors that can gate it).
161
170
  */
162
- async function validateNodes(context, nodes, unresolvedRefs, options = {}) {
171
+ export async function validateNodes(context, nodes, unresolvedRefs, options = {}) {
163
172
  const { schema, schemaRefRegistry, schemaValidator } = context;
164
173
  const metadata = schema.metadata;
165
174
  // Pre-extract valid types for early type validation
@@ -248,6 +257,7 @@ async function validateNodes(context, nodes, unresolvedRefs, options = {}) {
248
257
  */
249
258
  export async function validateSpace(context, options = {}) {
250
259
  const metadata = context.schema.metadata;
260
+ const runOptions = runOptionsFrom(context, options);
251
261
  const readResult = options.readResult ?? (await readSpace(context));
252
262
  let nodes = readResult.nodes;
253
263
  // Depth limiting: evaluate only hierarchy nodes within N breadth-first levels from the
@@ -268,11 +278,7 @@ export async function validateSpace(context, options = {}) {
268
278
  }
269
279
  nodes = included;
270
280
  }
271
- const result = await validateNodes(context, nodes, readResult.unresolvedRefs, {
272
- includeGated: options.includeGated,
273
- ruleFilter: options.ruleFilter,
274
- judge: options.judge,
275
- });
281
+ const result = await validateNodes(context, nodes, readResult.unresolvedRefs, runOptions);
276
282
  return {
277
283
  validCount: result.validCount,
278
284
  nodeErrorCount: result.nodeFailures.length,
@@ -390,15 +396,17 @@ export function fileNotInSpace(filePath) {
390
396
  * Validate a file in an already-resolved space. Checks the whole space (required for graph
391
397
  * correctness), judging only the file, then selects the file's issues from the result.
392
398
  */
393
- export async function validateResolvedFile(filePath, label, context, options = {}) {
399
+ export function validateResolvedFile(filePath, label, context, options = {}) {
400
+ return validateResolvedFileWith(filePath, label, context, runOptionsFrom(context, options));
401
+ }
402
+ /** `validateResolvedFile` with the options already resolved, so a caller can supply its own judge. */
403
+ export async function validateResolvedFileWith(filePath, label, context, runOptions) {
394
404
  const readResult = await readSpace(context);
395
405
  const result = await validateNodes(context, readResult.nodes, readResult.unresolvedRefs, {
396
- includeGated: options.includeGated,
397
- ruleFilter: options.ruleFilter,
398
- judge: options.judge,
406
+ ...runOptions,
399
407
  judgeTarget: label,
400
408
  });
401
- const { errors, warnings } = issuesForFile(result, label, options.includeGated);
409
+ const { errors, warnings } = issuesForFile(result, label, runOptions.includeGated);
402
410
  return {
403
411
  file: isAbsolute(filePath) ? filePath : resolve(process.cwd(), filePath),
404
412
  label,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "structured-context",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "Keep your content clean and your thinking clear with assistive tools for structured knowledge",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",
@@ -1,13 +0,0 @@
1
- export { createJudge, createModelProvider, JudgeConfigError } from './factory';
2
- export { buildJudgeSystemPrompt, buildJudgeUserPrompt, parseJudgeResponse } from './prompt';
3
- export type { AnthropicJudgeProviderOptions } from './provider/anthropic';
4
- export { AnthropicJudgeProvider } from './provider/anthropic';
5
- export type { FakeAnswer, FakeJudgeHandler } from './provider/fake';
6
- export { FakeJudgeProvider } from './provider/fake';
7
- export type { JevJudgeProviderOptions } from './provider/jev';
8
- export { JevJudgeProvider } from './provider/jev';
9
- export type { OpenAIJudgeProviderOptions } from './provider/openai';
10
- export { OpenAIJudgeProvider } from './provider/openai';
11
- export { PerRuleJudgeProvider } from './provider/per-rule';
12
- export type { JudgeConfig, ModelProviderConfig, SemanticJudgeInput, SemanticJudgeProvider, SemanticJudgeResult, SemanticJudgeVerdict, SemanticRuleSpec, } from './types';
13
- export { DEFAULT_VERDICTS, isJudgeError, semanticRuleSpec } from './verdicts';
@@ -1,8 +0,0 @@
1
- export { createJudge, createModelProvider, JudgeConfigError } from './factory';
2
- export { buildJudgeSystemPrompt, buildJudgeUserPrompt, parseJudgeResponse } from './prompt';
3
- export { AnthropicJudgeProvider } from './provider/anthropic';
4
- export { FakeJudgeProvider } from './provider/fake';
5
- export { JevJudgeProvider } from './provider/jev';
6
- export { OpenAIJudgeProvider } from './provider/openai';
7
- export { PerRuleJudgeProvider } from './provider/per-rule';
8
- export { DEFAULT_VERDICTS, isJudgeError, semanticRuleSpec } from './verdicts';