@yourtechtribe-labs/koncept-core 0.2.0-alpha.2 → 0.2.0-alpha.3
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/affected.d.ts +10 -16
- package/dist/affected.d.ts.map +1 -1
- package/dist/affected.js +12 -27
- package/dist/affected.js.map +1 -1
- package/dist/checker.js +1 -1
- package/dist/checker.js.map +1 -1
- package/dist/classify.d.ts +31 -0
- package/dist/classify.d.ts.map +1 -0
- package/dist/classify.js +50 -0
- package/dist/classify.js.map +1 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/load-concepts.d.ts +19 -0
- package/dist/load-concepts.d.ts.map +1 -0
- package/dist/load-concepts.js +34 -0
- package/dist/load-concepts.js.map +1 -0
- package/dist/review.d.ts +56 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +128 -0
- package/dist/review.js.map +1 -0
- package/package.json +2 -2
package/dist/affected.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* on `core.ignorecase=true` may surface either case). Matching is exact after
|
|
8
8
|
* normalization — no glob, no prefix.
|
|
9
9
|
*/
|
|
10
|
+
import { type AffectedSummary, type InvariantClass } from './classify.js';
|
|
10
11
|
import type { AutomatedCheck, Concept, ConceptType, Role, Severity } from './schema.js';
|
|
11
12
|
export type MatchedRole = Role | 'source_of_truth';
|
|
12
13
|
export interface AffectedInvariant {
|
|
@@ -16,6 +17,9 @@ export interface AffectedInvariant {
|
|
|
16
17
|
description: string;
|
|
17
18
|
severity: Severity;
|
|
18
19
|
check: AutomatedCheck;
|
|
20
|
+
klass: InvariantClass;
|
|
21
|
+
/** Only populated in ack-mode (acks passed to computeAffected); advisory only. */
|
|
22
|
+
acknowledged?: boolean;
|
|
19
23
|
}
|
|
20
24
|
export interface AffectedConcept {
|
|
21
25
|
id: string;
|
|
@@ -33,6 +37,7 @@ export interface AffectedReport {
|
|
|
33
37
|
changed_files: string[];
|
|
34
38
|
concepts: AffectedConcept[];
|
|
35
39
|
unmatched_files: string[];
|
|
40
|
+
summary: AffectedSummary;
|
|
36
41
|
}
|
|
37
42
|
/**
|
|
38
43
|
* Extracts the bare ids from `related_concepts`, normalizing the union
|
|
@@ -42,21 +47,10 @@ export interface AffectedReport {
|
|
|
42
47
|
export declare function resolveRelatedIds(concept: Concept): string[];
|
|
43
48
|
/**
|
|
44
49
|
* Pure computation: which concepts are touched by `changedFiles`?
|
|
50
|
+
*
|
|
51
|
+
* `acks` (optional) turns on ack-mode: when provided (even empty), each
|
|
52
|
+
* advisory invariant gets an `acknowledged` flag and the summary reports
|
|
53
|
+
* `unacknowledged_high`. Omitted → ack-mode off (MCP enumeration path).
|
|
45
54
|
*/
|
|
46
|
-
export declare function computeAffected(concepts: Concept[], changedFiles: string[]): AffectedReport;
|
|
47
|
-
/**
|
|
48
|
-
* Convenience loader: parse every YAML under `.koncept/concepts/` and return
|
|
49
|
-
* the valid Concept[] plus any parse errors. CLI/MCP server uses this when
|
|
50
|
-
* they don't already have a parsed list (otherwise call `computeAffected`
|
|
51
|
-
* directly with the concepts they hold).
|
|
52
|
-
*/
|
|
53
|
-
export interface LoadConceptsResult {
|
|
54
|
-
concepts: Concept[];
|
|
55
|
-
parseErrors: Array<{
|
|
56
|
-
filePath: string;
|
|
57
|
-
message: string;
|
|
58
|
-
field?: string;
|
|
59
|
-
}>;
|
|
60
|
-
}
|
|
61
|
-
export declare function loadConcepts(rootDir: string): Promise<LoadConceptsResult>;
|
|
55
|
+
export declare function computeAffected(concepts: Concept[], changedFiles: string[], acks?: ReadonlySet<string>): AffectedReport;
|
|
62
56
|
//# sourceMappingURL=affected.d.ts.map
|
package/dist/affected.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"affected.d.ts","sourceRoot":"","sources":["../src/affected.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"affected.d.ts","sourceRoot":"","sources":["../src/affected.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,eAAe,CAAA;AAEtB,OAAO,KAAK,EACV,cAAc,EACd,OAAO,EACP,WAAW,EACX,IAAI,EACJ,QAAQ,EACT,MAAM,aAAa,CAAA;AAEpB,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,iBAAiB,CAAA;AAElD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,QAAQ,CAAA;IAClB,KAAK,EAAE,cAAc,CAAA;IACrB,KAAK,EAAE,cAAc,CAAA;IACrB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,CAAA;IACjB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC,CAAA;IACzD,kBAAkB,EAAE,MAAM,EAAE,CAAA;IAC5B,UAAU,EAAE,iBAAiB,EAAE,CAAA;IAC/B,YAAY,EAAE,QAAQ,GAAG,IAAI,CAAA;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,OAAO,EAAE,eAAe,CAAA;CACzB;AAQD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAE5D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,OAAO,EAAE,EACnB,YAAY,EAAE,MAAM,EAAE,EACtB,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GACzB,cAAc,CAqBhB"}
|
package/dist/affected.js
CHANGED
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
* on `core.ignorecase=true` may surface either case). Matching is exact after
|
|
8
8
|
* normalization — no glob, no prefix.
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import { parseConceptFile } from './parser.js';
|
|
10
|
+
import { annotateAcks, classifyCheck, computeSummary, } from './classify.js';
|
|
12
11
|
import { normalizeForward } from './paths.js';
|
|
13
12
|
const SEVERITY_RANK = { high: 3, medium: 2, low: 1 };
|
|
14
13
|
function normalizeKey(p) {
|
|
@@ -24,20 +23,28 @@ export function resolveRelatedIds(concept) {
|
|
|
24
23
|
}
|
|
25
24
|
/**
|
|
26
25
|
* Pure computation: which concepts are touched by `changedFiles`?
|
|
26
|
+
*
|
|
27
|
+
* `acks` (optional) turns on ack-mode: when provided (even empty), each
|
|
28
|
+
* advisory invariant gets an `acknowledged` flag and the summary reports
|
|
29
|
+
* `unacknowledged_high`. Omitted → ack-mode off (MCP enumeration path).
|
|
27
30
|
*/
|
|
28
|
-
export function computeAffected(concepts, changedFiles) {
|
|
31
|
+
export function computeAffected(concepts, changedFiles, acks) {
|
|
29
32
|
const normalizedChanged = changedFiles.map(normalizeKey);
|
|
30
33
|
const fileToConcepts = buildFileIndex(concepts);
|
|
31
34
|
const { matched, matchedKeys } = matchChangedFiles(fileToConcepts, changedFiles, normalizedChanged);
|
|
32
35
|
for (const affected of matched.values()) {
|
|
33
36
|
const concept = concepts.find((c) => c.id === affected.id);
|
|
34
37
|
affected.other_participants = computeOtherParticipants(concept, affected);
|
|
38
|
+
if (acks !== undefined)
|
|
39
|
+
annotateAcks(affected, acks);
|
|
35
40
|
}
|
|
36
41
|
const unmatched = changedFiles.filter((_, i) => !matchedKeys.has(normalizedChanged[i]));
|
|
42
|
+
const concepts_ = [...matched.values()].sort(byMaxSeverityThenId);
|
|
37
43
|
return {
|
|
38
44
|
changed_files: changedFiles.map(normalizeForward),
|
|
39
|
-
concepts:
|
|
45
|
+
concepts: concepts_,
|
|
40
46
|
unmatched_files: unmatched.map(normalizeForward),
|
|
47
|
+
summary: computeSummary(concepts_, acks !== undefined),
|
|
41
48
|
};
|
|
42
49
|
}
|
|
43
50
|
function buildFileIndex(concepts) {
|
|
@@ -88,6 +95,7 @@ function makeAffected(concept) {
|
|
|
88
95
|
description: inv.description,
|
|
89
96
|
severity: inv.severity,
|
|
90
97
|
check: inv.check,
|
|
98
|
+
klass: classifyCheck(inv.check),
|
|
91
99
|
}));
|
|
92
100
|
invariants.sort((a, b) => SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity]);
|
|
93
101
|
const maxSev = invariants.length > 0 ? invariants[0].severity : null;
|
|
@@ -108,27 +116,4 @@ function byMaxSeverityThenId(a, b) {
|
|
|
108
116
|
return bv - av;
|
|
109
117
|
return a.id.localeCompare(b.id);
|
|
110
118
|
}
|
|
111
|
-
const CONCEPTS_GLOB = '.koncept/concepts/*.yaml';
|
|
112
|
-
export async function loadConcepts(rootDir) {
|
|
113
|
-
const files = await glob(CONCEPTS_GLOB, {
|
|
114
|
-
cwd: rootDir,
|
|
115
|
-
absolute: true,
|
|
116
|
-
nodir: true,
|
|
117
|
-
posix: true,
|
|
118
|
-
});
|
|
119
|
-
const parsed = await Promise.all(files.map((f) => parseConceptFile(f)));
|
|
120
|
-
const concepts = [];
|
|
121
|
-
const parseErrors = [];
|
|
122
|
-
for (const r of parsed) {
|
|
123
|
-
if (r.concept !== null) {
|
|
124
|
-
concepts.push(r.concept);
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
for (const e of r.errors) {
|
|
128
|
-
parseErrors.push({ filePath: r.filePath, message: e.message, field: e.field });
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return { concepts, parseErrors };
|
|
133
|
-
}
|
|
134
119
|
//# sourceMappingURL=affected.js.map
|
package/dist/affected.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"affected.js","sourceRoot":"","sources":["../src/affected.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"affected.js","sourceRoot":"","sources":["../src/affected.ts"],"names":[],"mappings":"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"}
|
package/dist/checker.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { readFileSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { spawnSync } from 'node:child_process';
|
|
11
|
-
import { loadConcepts } from './
|
|
11
|
+
import { loadConcepts } from './load-concepts.js';
|
|
12
12
|
// ─── Main entry point ─────────────────────────────────────────────────────────
|
|
13
13
|
export async function runChecks(opts) {
|
|
14
14
|
const loaded = await loadConcepts(opts.cwd);
|
package/dist/checker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant classification & sign-off — the projection layer on top of the
|
|
3
|
+
* raw affected graph. `klass` derives from `check.kind`; the summary + ack
|
|
4
|
+
* annotation power `koncepto affected --require-ack`. Kept out of affected.ts
|
|
5
|
+
* so that file stays a focused graph computation under the size budget.
|
|
6
|
+
*
|
|
7
|
+
* Type-only imports from affected.ts (AffectedConcept) erase at runtime, so
|
|
8
|
+
* there is no import cycle: affected.ts → classify.ts is the only value edge.
|
|
9
|
+
*/
|
|
10
|
+
import type { AffectedConcept } from './affected.js';
|
|
11
|
+
import type { AutomatedCheck } from './schema.js';
|
|
12
|
+
/**
|
|
13
|
+
* Derived from `check.kind`: `none` invariants need human/LLM judgment
|
|
14
|
+
* (advisory); `grep`/`command` are machine-verified by `koncepto check`
|
|
15
|
+
* (automated). A pure projection of existing data — no schema field.
|
|
16
|
+
*/
|
|
17
|
+
export type InvariantClass = 'automated' | 'advisory';
|
|
18
|
+
export declare function classifyCheck(check: AutomatedCheck): InvariantClass;
|
|
19
|
+
/** Ack key for an invariant: `"<concept-id>:<invariant-id>"`. */
|
|
20
|
+
export declare function ackKey(conceptId: string, invariantId: string): string;
|
|
21
|
+
export interface AffectedSummary {
|
|
22
|
+
automated: number;
|
|
23
|
+
advisory: number;
|
|
24
|
+
advisory_high: number;
|
|
25
|
+
/** advisory_high not acked; always 0 unless acks were passed (ack-mode). */
|
|
26
|
+
unacknowledged_high: number;
|
|
27
|
+
}
|
|
28
|
+
/** Sets `acknowledged` on every advisory invariant (ack-mode only). */
|
|
29
|
+
export declare function annotateAcks(affected: AffectedConcept, acks: ReadonlySet<string>): void;
|
|
30
|
+
export declare function computeSummary(concepts: AffectedConcept[], ackMode: boolean): AffectedSummary;
|
|
31
|
+
//# sourceMappingURL=classify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../src/classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,UAAU,CAAA;AAErD,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAEnE;AAED,iEAAiE;AACjE,wBAAgB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,4EAA4E;IAC5E,mBAAmB,EAAE,MAAM,CAAA;CAC5B;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAKvF;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,eAAe,EAAE,EAC3B,OAAO,EAAE,OAAO,GACf,eAAe,CAqBjB"}
|
package/dist/classify.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant classification & sign-off — the projection layer on top of the
|
|
3
|
+
* raw affected graph. `klass` derives from `check.kind`; the summary + ack
|
|
4
|
+
* annotation power `koncepto affected --require-ack`. Kept out of affected.ts
|
|
5
|
+
* so that file stays a focused graph computation under the size budget.
|
|
6
|
+
*
|
|
7
|
+
* Type-only imports from affected.ts (AffectedConcept) erase at runtime, so
|
|
8
|
+
* there is no import cycle: affected.ts → classify.ts is the only value edge.
|
|
9
|
+
*/
|
|
10
|
+
export function classifyCheck(check) {
|
|
11
|
+
return check.kind === 'none' ? 'advisory' : 'automated';
|
|
12
|
+
}
|
|
13
|
+
/** Ack key for an invariant: `"<concept-id>:<invariant-id>"`. */
|
|
14
|
+
export function ackKey(conceptId, invariantId) {
|
|
15
|
+
return `${conceptId}:${invariantId}`;
|
|
16
|
+
}
|
|
17
|
+
/** Sets `acknowledged` on every advisory invariant (ack-mode only). */
|
|
18
|
+
export function annotateAcks(affected, acks) {
|
|
19
|
+
for (const inv of affected.invariants) {
|
|
20
|
+
if (inv.klass !== 'advisory')
|
|
21
|
+
continue;
|
|
22
|
+
inv.acknowledged = acks.has(ackKey(inv.concept_id, inv.invariant_id));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function computeSummary(concepts, ackMode) {
|
|
26
|
+
let automated = 0;
|
|
27
|
+
let advisory = 0;
|
|
28
|
+
let advisoryHigh = 0;
|
|
29
|
+
let unacknowledgedHigh = 0;
|
|
30
|
+
for (const c of concepts) {
|
|
31
|
+
for (const inv of c.invariants) {
|
|
32
|
+
if (inv.klass === 'automated')
|
|
33
|
+
automated++;
|
|
34
|
+
else
|
|
35
|
+
advisory++;
|
|
36
|
+
if (inv.klass === 'advisory' && inv.severity === 'high') {
|
|
37
|
+
advisoryHigh++;
|
|
38
|
+
if (ackMode && inv.acknowledged === false)
|
|
39
|
+
unacknowledgedHigh++;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
automated,
|
|
45
|
+
advisory,
|
|
46
|
+
advisory_high: advisoryHigh,
|
|
47
|
+
unacknowledged_high: ackMode ? unacknowledgedHigh : 0,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../src/classify.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,MAAM,UAAU,aAAa,CAAC,KAAqB;IACjD,OAAO,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAA;AACzD,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,MAAM,CAAC,SAAiB,EAAE,WAAmB;IAC3D,OAAO,GAAG,SAAS,IAAI,WAAW,EAAE,CAAA;AACtC,CAAC;AAUD,uEAAuE;AACvE,MAAM,UAAU,YAAY,CAAC,QAAyB,EAAE,IAAyB;IAC/E,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QACtC,IAAI,GAAG,CAAC,KAAK,KAAK,UAAU;YAAE,SAAQ;QACtC,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAA;IACvE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,QAA2B,EAC3B,OAAgB;IAEhB,IAAI,SAAS,GAAG,CAAC,CAAA;IACjB,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,IAAI,YAAY,GAAG,CAAC,CAAA;IACpB,IAAI,kBAAkB,GAAG,CAAC,CAAA;IAC1B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;YAC/B,IAAI,GAAG,CAAC,KAAK,KAAK,WAAW;gBAAE,SAAS,EAAE,CAAA;;gBACrC,QAAQ,EAAE,CAAA;YACf,IAAI,GAAG,CAAC,KAAK,KAAK,UAAU,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;gBACxD,YAAY,EAAE,CAAA;gBACd,IAAI,OAAO,IAAI,GAAG,CAAC,YAAY,KAAK,KAAK;oBAAE,kBAAkB,EAAE,CAAA;YACjE,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO;QACL,SAAS;QACT,QAAQ;QACR,aAAa,EAAE,YAAY;QAC3B,mBAAmB,EAAE,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;KACtD,CAAA;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,10 +7,16 @@ export { indexConcepts, writeIndex, isIndexClean } from './indexer.js';
|
|
|
7
7
|
export type { IndexResult, IndexErrorEntry, DuplicateId, UnresolvedRelated, MissingFile, } from './indexer.js';
|
|
8
8
|
export { searchEntries } from './search.js';
|
|
9
9
|
export type { SearchHit, MatchField } from './search.js';
|
|
10
|
-
export { computeAffected,
|
|
11
|
-
export type { AffectedReport, AffectedConcept, AffectedInvariant, MatchedRole,
|
|
10
|
+
export { computeAffected, resolveRelatedIds } from './affected.js';
|
|
11
|
+
export type { AffectedReport, AffectedConcept, AffectedInvariant, MatchedRole, } from './affected.js';
|
|
12
|
+
export { classifyCheck, ackKey } from './classify.js';
|
|
13
|
+
export type { InvariantClass, AffectedSummary } from './classify.js';
|
|
14
|
+
export { loadConcepts } from './load-concepts.js';
|
|
15
|
+
export type { LoadConceptsResult } from './load-concepts.js';
|
|
12
16
|
export { suggestLinks } from './suggest-links.js';
|
|
13
17
|
export type { LinkSuggestion, SuggestLinksOptions } from './suggest-links.js';
|
|
14
18
|
export { runChecks } from './checker.js';
|
|
15
19
|
export type { CheckOptions, CheckStatus, InvariantCheckResult, CheckResult, } from './checker.js';
|
|
20
|
+
export { parseVerdict, reviewAffected, buildPrompt } from './review.js';
|
|
21
|
+
export type { Verdict, InvariantReview, ReviewResult, ReviewOptions, } from './review.js';
|
|
16
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAItC,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,UAAU,EACV,oBAAoB,EACpB,YAAY,EACZ,aAAa,GACd,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,OAAO,EACP,UAAU,EACV,WAAW,EACX,SAAS,EACT,aAAa,EACb,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,MAAM,EACN,cAAc,EACd,QAAQ,EACR,OAAO,GACR,MAAM,aAAa,CAAA;AAGpB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAClE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAG1E,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACtE,YAAY,EACV,WAAW,EACX,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,WAAW,GACZ,MAAM,cAAc,CAAA;AAGrB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxD,OAAO,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAItC,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,UAAU,EACV,oBAAoB,EACpB,YAAY,EACZ,aAAa,GACd,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,OAAO,EACP,UAAU,EACV,WAAW,EACX,SAAS,EACT,aAAa,EACb,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,MAAM,EACN,cAAc,EACd,QAAQ,EACR,OAAO,GACR,MAAM,aAAa,CAAA;AAGpB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAClE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAG1E,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACtE,YAAY,EACV,WAAW,EACX,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,WAAW,GACZ,MAAM,cAAc,CAAA;AAGrB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAClE,YAAY,EACV,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,WAAW,GACZ,MAAM,eAAe,CAAA;AAGtB,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACrD,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAG5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAG7E,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,YAAY,EACV,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,WAAW,GACZ,MAAM,cAAc,CAAA;AAGrB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACvE,YAAY,EACV,OAAO,EACP,eAAe,EACf,YAAY,EACZ,aAAa,GACd,MAAM,aAAa,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -12,9 +12,15 @@ export { indexConcepts, writeIndex, isIndexClean } from './indexer.js';
|
|
|
12
12
|
// Search
|
|
13
13
|
export { searchEntries } from './search.js';
|
|
14
14
|
// Affected (impact analysis)
|
|
15
|
-
export { computeAffected,
|
|
15
|
+
export { computeAffected, resolveRelatedIds } from './affected.js';
|
|
16
|
+
// Classification & sign-off (klass, summary, acks)
|
|
17
|
+
export { classifyCheck, ackKey } from './classify.js';
|
|
18
|
+
// Concept loader (fs bridge for the pure graph functions)
|
|
19
|
+
export { loadConcepts } from './load-concepts.js';
|
|
16
20
|
// Auto-link inference
|
|
17
21
|
export { suggestLinks } from './suggest-links.js';
|
|
18
22
|
// Checker (invariant.check executor)
|
|
19
23
|
export { runChecks } from './checker.js';
|
|
24
|
+
// Review (LLM semantic reviewer — pure; llm + diff injected by the CLI)
|
|
25
|
+
export { parseVerdict, reviewAffected, buildPrompt } from './review.js';
|
|
20
26
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,uEAAuE;AACvE,4CAA4C;AAE5C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,gFAAgF;AAChF,uDAAuD;AACvD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,UAAU,EACV,oBAAoB,EACpB,YAAY,EACZ,aAAa,GACd,MAAM,aAAa,CAAA;AAgBpB,SAAS;AACT,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAGlE,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAStE,SAAS;AACT,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3C,6BAA6B;AAC7B,OAAO,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,uEAAuE;AACvE,4CAA4C;AAE5C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,gFAAgF;AAChF,uDAAuD;AACvD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,UAAU,EACV,oBAAoB,EACpB,YAAY,EACZ,aAAa,GACd,MAAM,aAAa,CAAA;AAgBpB,SAAS;AACT,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAGlE,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAStE,SAAS;AACT,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3C,6BAA6B;AAC7B,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAQlE,mDAAmD;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAGrD,0DAA0D;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGjD,sBAAsB;AACtB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGjD,qCAAqC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAQxC,wEAAwE;AACxE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concept loader — the one fs-touching helper that bridges the pure graph
|
|
3
|
+
* functions (computeAffected, runChecks) and the YAML registry on disk.
|
|
4
|
+
*
|
|
5
|
+
* Kept separate from affected.ts so that file stays a pure computation over an
|
|
6
|
+
* already-parsed Concept[] (no glob, no fs). CLI / MCP server call this when
|
|
7
|
+
* they don't already hold a parsed list.
|
|
8
|
+
*/
|
|
9
|
+
import type { Concept } from './schema.js';
|
|
10
|
+
export interface LoadConceptsResult {
|
|
11
|
+
concepts: Concept[];
|
|
12
|
+
parseErrors: Array<{
|
|
13
|
+
filePath: string;
|
|
14
|
+
message: string;
|
|
15
|
+
field?: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
export declare function loadConcepts(rootDir: string): Promise<LoadConceptsResult>;
|
|
19
|
+
//# sourceMappingURL=load-concepts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-concepts.d.ts","sourceRoot":"","sources":["../src/load-concepts.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1E;AAID,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoB/E"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concept loader — the one fs-touching helper that bridges the pure graph
|
|
3
|
+
* functions (computeAffected, runChecks) and the YAML registry on disk.
|
|
4
|
+
*
|
|
5
|
+
* Kept separate from affected.ts so that file stays a pure computation over an
|
|
6
|
+
* already-parsed Concept[] (no glob, no fs). CLI / MCP server call this when
|
|
7
|
+
* they don't already hold a parsed list.
|
|
8
|
+
*/
|
|
9
|
+
import { glob } from 'glob';
|
|
10
|
+
import { parseConceptFile } from './parser.js';
|
|
11
|
+
const CONCEPTS_GLOB = '.koncept/concepts/*.yaml';
|
|
12
|
+
export async function loadConcepts(rootDir) {
|
|
13
|
+
const files = await glob(CONCEPTS_GLOB, {
|
|
14
|
+
cwd: rootDir,
|
|
15
|
+
absolute: true,
|
|
16
|
+
nodir: true,
|
|
17
|
+
posix: true,
|
|
18
|
+
});
|
|
19
|
+
const parsed = await Promise.all(files.map((f) => parseConceptFile(f)));
|
|
20
|
+
const concepts = [];
|
|
21
|
+
const parseErrors = [];
|
|
22
|
+
for (const r of parsed) {
|
|
23
|
+
if (r.concept !== null) {
|
|
24
|
+
concepts.push(r.concept);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
for (const e of r.errors) {
|
|
28
|
+
parseErrors.push({ filePath: r.filePath, message: e.message, field: e.field });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { concepts, parseErrors };
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=load-concepts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-concepts.js","sourceRoot":"","sources":["../src/load-concepts.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAQ9C,MAAM,aAAa,GAAG,0BAA0B,CAAA;AAEhD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe;IAChD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE;QACtC,GAAG,EAAE,OAAO;QACZ,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,IAAI;KACZ,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACvE,MAAM,QAAQ,GAAc,EAAE,CAAA;IAC9B,MAAM,WAAW,GAAsC,EAAE,CAAA;IACzD,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YACvB,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QAC1B,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;gBACzB,WAAW,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAA;YAChF,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAA;AAClC,CAAC"}
|
package/dist/review.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic invariant review (Phase 2) — for each touched ADVISORY invariant,
|
|
3
|
+
* ask an injected LLM whether the change still upholds it. Pure: the LLM
|
|
4
|
+
* provider and the per-file diff are injected by the CLI (mirrors how
|
|
5
|
+
* checker.ts injects cwd), so unit tests run with a fake llm and no network.
|
|
6
|
+
*
|
|
7
|
+
* Boundary (D-002 / D-004): network + cost live in the CLI, never the MCP
|
|
8
|
+
* server. This module only shapes prompts and tallies verdicts.
|
|
9
|
+
*/
|
|
10
|
+
import { type AffectedConcept, type AffectedInvariant } from './affected.js';
|
|
11
|
+
import type { Concept, Severity } from './schema.js';
|
|
12
|
+
export type Verdict = 'holds' | 'violated' | 'uncertain';
|
|
13
|
+
export interface InvariantReview {
|
|
14
|
+
conceptId: string;
|
|
15
|
+
invariantId: string;
|
|
16
|
+
severity: Severity;
|
|
17
|
+
verdict: Verdict;
|
|
18
|
+
rationale: string;
|
|
19
|
+
files: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface ReviewResult {
|
|
22
|
+
reviews: InvariantReview[];
|
|
23
|
+
holds: number;
|
|
24
|
+
violated: number;
|
|
25
|
+
uncertain: number;
|
|
26
|
+
/** automated invariants + advisory invariants below --severity */
|
|
27
|
+
skipped: number;
|
|
28
|
+
}
|
|
29
|
+
export interface ReviewOptions {
|
|
30
|
+
rootDir: string;
|
|
31
|
+
changedFiles: string[];
|
|
32
|
+
/** Injected: unified diff text for a single file (sync, like spawnSync). */
|
|
33
|
+
diff: (file: string) => string;
|
|
34
|
+
minSeverity: Severity;
|
|
35
|
+
/** Injected provider: prompt → raw model text. Throws propagate (→ exit 2). */
|
|
36
|
+
llm: (prompt: string) => Promise<string>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Lenient parse of the model's reply: prefer an embedded JSON object
|
|
40
|
+
* `{verdict, rationale}`; fall back to scanning prose for a verdict keyword;
|
|
41
|
+
* otherwise coerce to `uncertain` (never throws — unparseable ≠ failure).
|
|
42
|
+
*/
|
|
43
|
+
export declare function parseVerdict(raw: string): {
|
|
44
|
+
verdict: Verdict;
|
|
45
|
+
rationale: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* For each ADVISORY invariant touched by the diff and at/above `minSeverity`,
|
|
49
|
+
* one LLM call judges whether the change still upholds it. Automated and
|
|
50
|
+
* below-threshold invariants are counted in `skipped`. LLM *errors* propagate
|
|
51
|
+
* (tagged `concept:invariant`) so the CLI can exit 2; unparseable *replies*
|
|
52
|
+
* are coerced to `uncertain` and continue.
|
|
53
|
+
*/
|
|
54
|
+
export declare function reviewAffected(concepts: Concept[], opts: ReviewOptions): Promise<ReviewResult>;
|
|
55
|
+
export declare function buildPrompt(affected: AffectedConcept, inv: AffectedInvariant, files: string[], diff: (file: string) => string): string;
|
|
56
|
+
//# sourceMappingURL=review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAmB,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAC7F,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEpD,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,CAAA;AAMxD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,QAAQ,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,eAAe,EAAE,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,4EAA4E;IAC5E,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IAC9B,WAAW,EAAE,QAAQ,CAAA;IACrB,+EAA+E;IAC/E,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CACzC;AAID;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAOjF;AA8BD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,OAAO,EAAE,EACnB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,YAAY,CAAC,CAiBvB;AA+BD,wBAAgB,WAAW,CACzB,QAAQ,EAAE,eAAe,EACzB,GAAG,EAAE,iBAAiB,EACtB,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAC7B,MAAM,CAUR"}
|
package/dist/review.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic invariant review (Phase 2) — for each touched ADVISORY invariant,
|
|
3
|
+
* ask an injected LLM whether the change still upholds it. Pure: the LLM
|
|
4
|
+
* provider and the per-file diff are injected by the CLI (mirrors how
|
|
5
|
+
* checker.ts injects cwd), so unit tests run with a fake llm and no network.
|
|
6
|
+
*
|
|
7
|
+
* Boundary (D-002 / D-004): network + cost live in the CLI, never the MCP
|
|
8
|
+
* server. This module only shapes prompts and tallies verdicts.
|
|
9
|
+
*/
|
|
10
|
+
import { computeAffected } from './affected.js';
|
|
11
|
+
const VERDICTS = ['holds', 'violated', 'uncertain'];
|
|
12
|
+
const NO_RATIONALE = '(no rationale provided)';
|
|
13
|
+
const SEVERITY_RANK = { high: 3, medium: 2, low: 1 };
|
|
14
|
+
// ─── Verdict parsing ────────────────────────────────────────────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Lenient parse of the model's reply: prefer an embedded JSON object
|
|
17
|
+
* `{verdict, rationale}`; fall back to scanning prose for a verdict keyword;
|
|
18
|
+
* otherwise coerce to `uncertain` (never throws — unparseable ≠ failure).
|
|
19
|
+
*/
|
|
20
|
+
export function parseVerdict(raw) {
|
|
21
|
+
const text = raw.trim();
|
|
22
|
+
const fromJson = verdictFromJson(text);
|
|
23
|
+
if (fromJson)
|
|
24
|
+
return fromJson;
|
|
25
|
+
const scanned = text.match(/\b(holds|violated|uncertain)\b/);
|
|
26
|
+
if (scanned)
|
|
27
|
+
return { verdict: scanned[1], rationale: truncate(text) };
|
|
28
|
+
return { verdict: 'uncertain', rationale: 'unparseable model output' };
|
|
29
|
+
}
|
|
30
|
+
function verdictFromJson(text) {
|
|
31
|
+
const start = text.indexOf('{');
|
|
32
|
+
const end = text.lastIndexOf('}');
|
|
33
|
+
if (start === -1 || end <= start)
|
|
34
|
+
return null;
|
|
35
|
+
let parsed;
|
|
36
|
+
try {
|
|
37
|
+
parsed = JSON.parse(text.slice(start, end + 1));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
43
|
+
return null;
|
|
44
|
+
const obj = parsed;
|
|
45
|
+
if (typeof obj.verdict !== 'string' || !VERDICTS.includes(obj.verdict)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const rationale = typeof obj.rationale === 'string' && obj.rationale.trim().length > 0
|
|
49
|
+
? obj.rationale.trim()
|
|
50
|
+
: NO_RATIONALE;
|
|
51
|
+
return { verdict: obj.verdict, rationale };
|
|
52
|
+
}
|
|
53
|
+
function truncate(s, max = 300) {
|
|
54
|
+
return s.length > max ? s.slice(0, max) + '…' : s;
|
|
55
|
+
}
|
|
56
|
+
// ─── Review orchestration ─────────────────────────────────────────────────────
|
|
57
|
+
/**
|
|
58
|
+
* For each ADVISORY invariant touched by the diff and at/above `minSeverity`,
|
|
59
|
+
* one LLM call judges whether the change still upholds it. Automated and
|
|
60
|
+
* below-threshold invariants are counted in `skipped`. LLM *errors* propagate
|
|
61
|
+
* (tagged `concept:invariant`) so the CLI can exit 2; unparseable *replies*
|
|
62
|
+
* are coerced to `uncertain` and continue.
|
|
63
|
+
*/
|
|
64
|
+
export async function reviewAffected(concepts, opts) {
|
|
65
|
+
const report = computeAffected(concepts, opts.changedFiles);
|
|
66
|
+
const reviews = [];
|
|
67
|
+
let skipped = 0;
|
|
68
|
+
for (const affected of report.concepts) {
|
|
69
|
+
const files = affected.matched_files.map((m) => m.file);
|
|
70
|
+
for (const inv of affected.invariants) {
|
|
71
|
+
if (!isReviewable(inv, opts.minSeverity)) {
|
|
72
|
+
skipped++;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
reviews.push(await reviewOne(affected, inv, files, opts));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return tally(reviews, skipped);
|
|
79
|
+
}
|
|
80
|
+
function isReviewable(inv, minSeverity) {
|
|
81
|
+
return inv.klass === 'advisory' && SEVERITY_RANK[inv.severity] >= SEVERITY_RANK[minSeverity];
|
|
82
|
+
}
|
|
83
|
+
async function reviewOne(affected, inv, files, opts) {
|
|
84
|
+
const prompt = buildPrompt(affected, inv, files, opts.diff);
|
|
85
|
+
let raw;
|
|
86
|
+
try {
|
|
87
|
+
raw = await opts.llm(prompt);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
91
|
+
throw new Error(`review failed for ${inv.concept_id}:${inv.invariant_id}: ${detail}`);
|
|
92
|
+
}
|
|
93
|
+
const { verdict, rationale } = parseVerdict(raw);
|
|
94
|
+
return {
|
|
95
|
+
conceptId: inv.concept_id,
|
|
96
|
+
invariantId: inv.invariant_id,
|
|
97
|
+
severity: inv.severity,
|
|
98
|
+
verdict,
|
|
99
|
+
rationale,
|
|
100
|
+
files,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function buildPrompt(affected, inv, files, diff) {
|
|
104
|
+
const diffs = files.map((f) => `--- ${f} ---\n${diff(f)}`).join('\n\n');
|
|
105
|
+
return [
|
|
106
|
+
'You are reviewing whether a code change still upholds a documented semantic invariant.',
|
|
107
|
+
`\nConcept: ${affected.name}\n${affected.type}`,
|
|
108
|
+
`\nInvariant (${inv.severity}): ${inv.description}`,
|
|
109
|
+
`\nChanged files:\n${diffs}`,
|
|
110
|
+
'\nDoes the change uphold the invariant? Reply with a JSON object',
|
|
111
|
+
'{"verdict": "holds" | "violated" | "uncertain", "rationale": "<1-3 sentences>"}.',
|
|
112
|
+
].join('\n');
|
|
113
|
+
}
|
|
114
|
+
function tally(reviews, skipped) {
|
|
115
|
+
let holds = 0;
|
|
116
|
+
let violated = 0;
|
|
117
|
+
let uncertain = 0;
|
|
118
|
+
for (const r of reviews) {
|
|
119
|
+
if (r.verdict === 'holds')
|
|
120
|
+
holds++;
|
|
121
|
+
else if (r.verdict === 'violated')
|
|
122
|
+
violated++;
|
|
123
|
+
else
|
|
124
|
+
uncertain++;
|
|
125
|
+
}
|
|
126
|
+
return { reviews, holds, violated, uncertain, skipped };
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yourtechtribe-labs/koncept-core",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.3",
|
|
4
4
|
"description": "Core schema, parser, and indexer for koncepto semantic concept graphs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"yaml",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"glob": "13.0.6",
|
|
31
|
-
"yaml": "2.
|
|
31
|
+
"yaml": "2.9.0",
|
|
32
32
|
"zod": "4.4.3"
|
|
33
33
|
},
|
|
34
34
|
"publishConfig": {
|