@intentius/chant 0.84.0 → 0.85.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/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.d.ts +2 -1
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/server.d.ts +1 -0
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/tools/composites.d.ts +44 -0
- package/dist/cli/mcp/tools/composites.d.ts.map +1 -0
- package/dist/cli/mcp/tools/search.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +2 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +4 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/composite.d.ts +6 -0
- package/dist/composite.d.ts.map +1 -1
- package/dist/lexicon.d.ts +44 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts +7 -0
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/composites.d.ts +139 -0
- package/dist/workspace/composites.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts +13 -2
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/intent-cli.d.ts +5 -1
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-joins.d.ts +31 -3
- package/dist/workspace/intent-joins.d.ts.map +1 -1
- package/dist/workspace/intent.d.ts +27 -2
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/member-commands.d.ts +7 -2
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/reason-codes.d.ts +14 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +1 -1
- package/dist/workspace/records.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/handlers/graph.ts +4 -0
- package/src/cli/main.test.ts +9 -0
- package/src/cli/main.ts +8 -0
- package/src/cli/mcp/resource-handlers.ts +17 -0
- package/src/cli/mcp/server.test.ts +140 -4
- package/src/cli/mcp/server.ts +5 -1
- package/src/cli/mcp/tools/composites.ts +98 -0
- package/src/cli/mcp/tools/search.ts +47 -5
- package/src/cli/registry.ts +2 -0
- package/src/components/cli-support.test.ts +16 -0
- package/src/components/cli-support.ts +8 -2
- package/src/composite.ts +9 -0
- package/src/lexicon.ts +47 -0
- package/src/workspace/__fixtures__/contract-repo.ts +17 -0
- package/src/workspace/composites.schema.json +471 -0
- package/src/workspace/composites.test.ts +244 -0
- package/src/workspace/composites.ts +295 -0
- package/src/workspace/graph-cli.ts +32 -4
- package/src/workspace/intent-cli.ts +26 -2
- package/src/workspace/intent-joins.ts +48 -3
- package/src/workspace/intent.schema.json +80 -17
- package/src/workspace/intent.test.ts +138 -20
- package/src/workspace/intent.ts +72 -14
- package/src/workspace/member-commands.ts +11 -5
- package/src/workspace/read-contract.test.ts +31 -3
- package/src/workspace/reason-codes.test.ts +34 -1
- package/src/workspace/reason-codes.ts +22 -0
- package/src/workspace/records-contract.test.ts +22 -2
- package/src/workspace/records.schema.json +2 -2
- package/src/workspace/records.test.ts +93 -0
- package/src/workspace/records.ts +54 -10
package/src/workspace/records.ts
CHANGED
|
@@ -62,6 +62,12 @@ export const RECORD_WARNING_CODES = [
|
|
|
62
62
|
"asset-stale",
|
|
63
63
|
/** A supersedes link from a record whose state is weaker than the one it names, so it has no effect yet (#2524 D4). */
|
|
64
64
|
"record-supersedes-pending",
|
|
65
|
+
/**
|
|
66
|
+
* The kind's pins field is an empty list: the record cites no evidence and
|
|
67
|
+
* pins no file. Information for a reviewer, such as a decision made in a
|
|
68
|
+
* product's own design flow with nothing to cite (#2654).
|
|
69
|
+
*/
|
|
70
|
+
"record-no-evidence",
|
|
65
71
|
] as const satisfies readonly ReasonCode[];
|
|
66
72
|
export type RecordWarningCode = (typeof RECORD_WARNING_CODES)[number];
|
|
67
73
|
|
|
@@ -344,22 +350,54 @@ export interface ReadRecordsResult {
|
|
|
344
350
|
|
|
345
351
|
type Validator = (data: unknown) => { ok: true } | { ok: false; errors: string[] };
|
|
346
352
|
|
|
353
|
+
/** One ajv error, compiled with `verbose` so the failing schema and data come with it. */
|
|
354
|
+
interface SchemaError {
|
|
355
|
+
instancePath: string;
|
|
356
|
+
schemaPath: string;
|
|
357
|
+
keyword: string;
|
|
358
|
+
message?: string;
|
|
359
|
+
params?: { failingKeyword?: string };
|
|
360
|
+
parentSchema?: Record<string, unknown>;
|
|
361
|
+
data?: unknown;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* ajv's errors as `<path> <message>` lines. A failed `if` whose `then` or
|
|
366
|
+
* `else` branch has a `description` is reported by that description alone, in
|
|
367
|
+
* place of the branch's own errors, with each `{field}` filled from the value
|
|
368
|
+
* the branch checked. The decision schema words its dissent rule this way, so
|
|
369
|
+
* the message names the reviewer (#2652). The schema stays plain JSON Schema,
|
|
370
|
+
* with no keyword a strict validator would refuse.
|
|
371
|
+
*/
|
|
372
|
+
function renderSchemaErrors(errors: readonly SchemaError[]): string[] {
|
|
373
|
+
const replaced: string[] = [];
|
|
374
|
+
const described = new Map<SchemaError, string>();
|
|
375
|
+
for (const e of errors) {
|
|
376
|
+
const branch = e.keyword === "if" ? e.params?.failingKeyword : undefined;
|
|
377
|
+
const text = branch ? (e.parentSchema?.[branch] as { description?: unknown } | undefined)?.description : undefined;
|
|
378
|
+
if (!branch || typeof text !== "string") continue;
|
|
379
|
+
const at = e.data !== null && typeof e.data === "object" ? (e.data as Record<string, unknown>) : {};
|
|
380
|
+
described.set(e, text.replace(/\{([A-Za-z0-9_]+)\}/g, (all, key: string) => (typeof at[key] === "string" ? (at[key] as string) : all)));
|
|
381
|
+
replaced.push(`${e.schemaPath.replace(/\/if$/, "")}/${branch}/`);
|
|
382
|
+
}
|
|
383
|
+
return errors
|
|
384
|
+
.filter((e) => described.has(e) || !replaced.some((prefix) => e.schemaPath.startsWith(prefix)))
|
|
385
|
+
.map((e) => `${e.instancePath || "/"} ${described.get(e) ?? e.message ?? "is invalid"}`);
|
|
386
|
+
}
|
|
387
|
+
|
|
347
388
|
async function compileSchema(schema: Record<string, unknown>): Promise<Validator> {
|
|
348
389
|
const mod = (await import("ajv")) as unknown as { default: unknown };
|
|
349
390
|
// ajv is CommonJS; its class is the default export, or that export's own default.
|
|
350
391
|
const Ajv = ((mod.default as { default?: unknown }).default ?? mod.default) as new (opts: object) => {
|
|
351
|
-
compile(s: object): ((d: unknown) => boolean) & { errors?:
|
|
392
|
+
compile(s: object): ((d: unknown) => boolean) & { errors?: SchemaError[] | null };
|
|
352
393
|
};
|
|
353
394
|
let validate: ReturnType<InstanceType<typeof Ajv>["compile"]>;
|
|
354
395
|
try {
|
|
355
|
-
validate = new Ajv({ allErrors: true, strict: false }).compile(schema);
|
|
396
|
+
validate = new Ajv({ allErrors: true, strict: false, verbose: true }).compile(schema);
|
|
356
397
|
} catch (err) {
|
|
357
398
|
throw new RecordReadError("schema-invalid", `the kind's schema does not compile: ${message(err)}`);
|
|
358
399
|
}
|
|
359
|
-
return (data) =>
|
|
360
|
-
validate(data)
|
|
361
|
-
? { ok: true }
|
|
362
|
-
: { ok: false, errors: (validate.errors ?? []).map((e) => `${e.instancePath || "/"} ${e.message ?? "is invalid"}`) };
|
|
400
|
+
return (data) => (validate(data) ? { ok: true } : { ok: false, errors: renderSchemaErrors(validate.errors ?? []) });
|
|
363
401
|
}
|
|
364
402
|
|
|
365
403
|
/** Read every record `loaded` locates, through `options.source`. */
|
|
@@ -392,10 +430,16 @@ export async function readRecords(loaded: LoadedRecordKind, options: ReadRecords
|
|
|
392
430
|
if (!result.ok) {
|
|
393
431
|
entry.reasons.push({ code: "record-schema-invalid", message: result.errors.join("; ") });
|
|
394
432
|
}
|
|
395
|
-
if (kind.pins
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
433
|
+
if (kind.pins) {
|
|
434
|
+
const cited = fm.value[kind.pins.field];
|
|
435
|
+
if (Array.isArray(cited) && cited.length === 0) {
|
|
436
|
+
entry.warnings.push({ code: "record-no-evidence", message: `${kind.pins.field} is empty: the record cites nothing and pins no file` });
|
|
437
|
+
}
|
|
438
|
+
if (options.assets) {
|
|
439
|
+
const checked = checkPins(pinEntries(fm.value, kind.pins.field), options.assets);
|
|
440
|
+
entry.assets = checked.assets;
|
|
441
|
+
entry.warnings.push(...checked.warnings);
|
|
442
|
+
}
|
|
399
443
|
}
|
|
400
444
|
}
|
|
401
445
|
|