circle-ir-ai 2.22.0 → 2.26.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +307 -0
  2. package/dist/agents/mastra/workflow.d.ts.map +1 -1
  3. package/dist/agents/mastra/workflow.js +41 -1
  4. package/dist/agents/mastra/workflow.js.map +1 -1
  5. package/dist/cache/discovery-cache.d.ts +71 -0
  6. package/dist/cache/discovery-cache.d.ts.map +1 -0
  7. package/dist/cache/discovery-cache.js +95 -0
  8. package/dist/cache/discovery-cache.js.map +1 -0
  9. package/dist/cache/tree-cache.d.ts +111 -0
  10. package/dist/cache/tree-cache.d.ts.map +1 -0
  11. package/dist/cache/tree-cache.js +171 -0
  12. package/dist/cache/tree-cache.js.map +1 -0
  13. package/dist/cache/verify-cache.d.ts +60 -0
  14. package/dist/cache/verify-cache.d.ts.map +1 -0
  15. package/dist/cache/verify-cache.js +103 -0
  16. package/dist/cache/verify-cache.js.map +1 -0
  17. package/dist/cluster/cluster.d.ts +174 -0
  18. package/dist/cluster/cluster.d.ts.map +1 -0
  19. package/dist/cluster/cluster.js +392 -0
  20. package/dist/cluster/cluster.js.map +1 -0
  21. package/dist/cluster/index.d.ts +11 -0
  22. package/dist/cluster/index.d.ts.map +1 -0
  23. package/dist/cluster/index.js +10 -0
  24. package/dist/cluster/index.js.map +1 -0
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +4 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/llm/call-logger.d.ts +36 -0
  30. package/dist/llm/call-logger.d.ts.map +1 -1
  31. package/dist/llm/call-logger.js +36 -0
  32. package/dist/llm/call-logger.js.map +1 -1
  33. package/dist/llm/discovery.d.ts +1 -1
  34. package/dist/llm/discovery.d.ts.map +1 -1
  35. package/dist/llm/discovery.js +39 -0
  36. package/dist/llm/discovery.js.map +1 -1
  37. package/dist/llm/verification.d.ts +1 -1
  38. package/dist/llm/verification.d.ts.map +1 -1
  39. package/dist/llm/verification.js +39 -0
  40. package/dist/llm/verification.js.map +1 -1
  41. package/package.json +1 -1
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Cluster Layer — Foundation
3
+ *
4
+ * Top-of-pipeline file clustering (cognium-ai#154, P4 of epic #155).
5
+ * Groups files in a repo by their static structural fingerprint (CK
6
+ * metrics + import/annotation set) so downstream passes can apply
7
+ * per-cluster policy bundles ("library bootstrap" vs "controller
8
+ * surface" vs "data-access shim") instead of one-size-fits-all
9
+ * discovery/verification.
10
+ *
11
+ * This file ships the **foundation only**:
12
+ * - `extractClusterVector(circleIr)` — pull CK vector from analyze() output
13
+ * - `clusterFiles(inputs)` — deterministic k-medoids, returns
14
+ * `Map<filePath, clusterId>` per acceptance #1
15
+ * - distance functions (Euclidean on normalized CK + Jaccard on
16
+ * imports/annotations) + the k-medoids loop itself
17
+ * - `ClusterPolicy` type so downstream wiring can land in a
18
+ * follow-up without re-declaring shape
19
+ *
20
+ * **Explicitly out of scope here** (tracked as follow-up tickets):
21
+ * - `meta.json.files[*].clusterId` field — needs scan-emit schema
22
+ * migration (Acceptance #3)
23
+ * - top-100 HIGH/CRIT reduction validation (Acceptance #4)
24
+ * - CWE-Bench-Java regression check (Acceptance #5)
25
+ * - `--cluster-policy <profile>` CLI flag in cognium-ai
26
+ * (Acceptance #6)
27
+ * - LLM cluster labeling pass (optional future per the issue)
28
+ *
29
+ * The split is deliberate: P1-P3 of epic #155 were single-shot
30
+ * cache wins; P4's policy bundles + the two benchmark validation
31
+ * gates need empirical tuning that can't complete in a single
32
+ * release cycle. Foundation-first lets cognium-ai start consuming
33
+ * `clusterFiles()` as an opaque grouping signal while the policy
34
+ * resolver is iterated separately.
35
+ *
36
+ * **Determinism:** k-medoids is seeded from a digest of (sorted
37
+ * file paths || vector signature) so re-runs on the same input
38
+ * produce the same cluster assignments. Tie-breaking on equal
39
+ * distances falls back to lexicographic path order.
40
+ */
41
+ import type { CircleIR } from 'circle-ir';
42
+ /**
43
+ * Per-file structural fingerprint used as the clustering signal.
44
+ * CK metrics default to 0 when circle-ir's metric pass didn't run
45
+ * (e.g. procedural-only files, languages without classes).
46
+ */
47
+ export interface ClusterVector {
48
+ cbo: number;
49
+ rfc: number;
50
+ lcom: number;
51
+ dit: number;
52
+ noc: number;
53
+ wmc: number;
54
+ imports: string[];
55
+ publicSurface: number;
56
+ annotations: string[];
57
+ }
58
+ export interface ClusterInput {
59
+ filePath: string;
60
+ vector: ClusterVector;
61
+ }
62
+ export interface ClusterAssignment {
63
+ filePath: string;
64
+ clusterId: number;
65
+ }
66
+ export interface ClusterResult {
67
+ k: number;
68
+ assignments: ClusterAssignment[];
69
+ /** Medoid file path per cluster id (index = clusterId). */
70
+ medoids: string[];
71
+ /** Iterations until convergence (for tuning visibility). */
72
+ iterations: number;
73
+ }
74
+ /**
75
+ * Downstream policy bundle attached to a cluster. Shape locked here
76
+ * so wiring tickets can land without re-negotiating the contract;
77
+ * default policy ("application", everything on) is what consumers
78
+ * should apply until cluster-specific overrides ship.
79
+ */
80
+ export type ContextProfile = 'application' | 'library' | 'framework' | 'tool';
81
+ export interface ClusterPolicy {
82
+ contextProfile: ContextProfile;
83
+ /** Discovery source kinds permitted; empty array = all. */
84
+ sourceWhitelist: string[];
85
+ /** Verification sink classes permitted; empty array = all. */
86
+ sinkWhitelist: string[];
87
+ /** When false, downstream LLM passes skip this cluster entirely. */
88
+ llmEnabled: boolean;
89
+ }
90
+ export declare const DEFAULT_CLUSTER_POLICY: ClusterPolicy;
91
+ /**
92
+ * Extract a ClusterVector from a single-file `CircleIR` analyze()
93
+ * result. Missing metrics default to 0 — the cluster vector is
94
+ * structural, so a procedural file with no CK numbers will land
95
+ * in the "low-complexity" cluster naturally (it's a valid signal,
96
+ * not missing data).
97
+ */
98
+ export declare function extractClusterVector(circleIr: CircleIR): ClusterVector;
99
+ /**
100
+ * Per-axis max-normalize the CK + publicSurface scalars across the
101
+ * input set. Max-normalize (rather than z-score) is deliberate:
102
+ * cluster boundaries should be robust to outliers (one god-class
103
+ * with WMC=400 shouldn't squash everything else into the same
104
+ * cluster), and max-normalize gives a clean [0,1] domain that
105
+ * combines naturally with Jaccard.
106
+ *
107
+ * Returns the maxima so callers can normalize new vectors against
108
+ * the same scale (e.g. for the k-medoids loop on the same set).
109
+ */
110
+ export declare function computeNormalization(inputs: ClusterInput[]): {
111
+ cboMax: number;
112
+ rfcMax: number;
113
+ lcomMax: number;
114
+ ditMax: number;
115
+ nocMax: number;
116
+ wmcMax: number;
117
+ publicSurfaceMax: number;
118
+ };
119
+ /** Euclidean distance on the max-normalized CK + publicSurface scalars. */
120
+ export declare function euclideanCkDistance(a: ClusterVector, b: ClusterVector, norm: ReturnType<typeof computeNormalization>): number;
121
+ /**
122
+ * Jaccard distance on two string sets (1 - |intersection|/|union|).
123
+ * Returns 0 when both sets are empty (semantically: nothing
124
+ * distinguishes them on this axis).
125
+ */
126
+ export declare function jaccardDistance(a: string[], b: string[]): number;
127
+ /**
128
+ * Combined per-pair distance. Weights mirror the issue's signal
129
+ * vector ordering (CK is the primary signal; imports + annotations
130
+ * are tiebreakers). Total is in [0,1] so it composes cleanly with
131
+ * other metric-space algorithms.
132
+ *
133
+ * Weights chosen to sum to 1:
134
+ * 0.6 — CK + publicSurface Euclidean (clamped to 1 by sqrt of 7
135
+ * normalized components, since each (a-b)/max is in [-1,1])
136
+ * 0.2 — imports Jaccard
137
+ * 0.2 — annotations Jaccard
138
+ *
139
+ * Euclidean is divided by sqrt(7) to renormalize to [0,1] before
140
+ * weighting — without this, the CK term can exceed 1 and the total
141
+ * stops being a proper [0,1] distance.
142
+ */
143
+ export declare function mixedDistance(a: ClusterVector, b: ClusterVector, norm: ReturnType<typeof computeNormalization>): number;
144
+ interface KMedoidsOptions {
145
+ /** Desired cluster count. Clamped to `[1, inputs.length]`. */
146
+ k: number;
147
+ /** Maximum iterations before bailing. Default 50. */
148
+ maxIter?: number;
149
+ /** Override the deterministic seed (test-only). */
150
+ seed?: number;
151
+ }
152
+ /**
153
+ * PAM-lite k-medoids on `inputs`.
154
+ *
155
+ * 1. Seed selection ("build" phase): pick the medoid that minimizes
156
+ * total distance to all points, then iteratively pick the point
157
+ * that maximizes minimum distance to existing medoids. This is
158
+ * deterministic given the seed and degenerates gracefully when
159
+ * `inputs.length <= k`.
160
+ * 2. Assignment: each point joins its closest medoid (lex path
161
+ * tiebreak on equal distances).
162
+ * 3. Swap phase: for each cluster, find the point that minimizes
163
+ * intra-cluster distance and promote it to medoid. Repeat until
164
+ * no medoid changes (or `maxIter` hits).
165
+ *
166
+ * Complexity is O(n^2 * iter) on the distance matrix. For the
167
+ * top-100 sweep (n ≈ 5000 per repo at the high end) this is ~25M
168
+ * pairwise computations per iteration — fine for the <2s budget
169
+ * in Acceptance #2 because distances are scalar arithmetic on
170
+ * a 9-dim vector.
171
+ */
172
+ export declare function clusterFiles(inputs: ClusterInput[], options: KMedoidsOptions): ClusterResult;
173
+ export {};
174
+ //# sourceMappingURL=cluster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/cluster/cluster.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,WAAW,CAAC;AAMvD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,EAAE,MAAM,CAAC;IACV,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,2DAA2D;IAC3D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;AAE9E,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,cAAc,CAAC;IAC/B,2DAA2D;IAC3D,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,8DAA8D;IAC9D,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,oEAAoE;IACpE,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,sBAAsB,EAAE,aAKpC,CAAC;AAsBF;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,aAAa,CAyDtE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAYA;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,aAAa,EAChB,CAAC,EAAE,aAAa,EAChB,IAAI,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,GAC5C,MAAM,CAWR;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAShE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAC3B,CAAC,EAAE,aAAa,EAChB,CAAC,EAAE,aAAa,EAChB,IAAI,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,GAC5C,MAAM,CAKR;AAwCD,UAAU,eAAe;IACvB,8DAA8D;IAC9D,CAAC,EAAE,MAAM,CAAC;IACV,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,YAAY,EAAE,EACtB,OAAO,EAAE,eAAe,GACvB,aAAa,CAsJf"}
@@ -0,0 +1,392 @@
1
+ /**
2
+ * Cluster Layer — Foundation
3
+ *
4
+ * Top-of-pipeline file clustering (cognium-ai#154, P4 of epic #155).
5
+ * Groups files in a repo by their static structural fingerprint (CK
6
+ * metrics + import/annotation set) so downstream passes can apply
7
+ * per-cluster policy bundles ("library bootstrap" vs "controller
8
+ * surface" vs "data-access shim") instead of one-size-fits-all
9
+ * discovery/verification.
10
+ *
11
+ * This file ships the **foundation only**:
12
+ * - `extractClusterVector(circleIr)` — pull CK vector from analyze() output
13
+ * - `clusterFiles(inputs)` — deterministic k-medoids, returns
14
+ * `Map<filePath, clusterId>` per acceptance #1
15
+ * - distance functions (Euclidean on normalized CK + Jaccard on
16
+ * imports/annotations) + the k-medoids loop itself
17
+ * - `ClusterPolicy` type so downstream wiring can land in a
18
+ * follow-up without re-declaring shape
19
+ *
20
+ * **Explicitly out of scope here** (tracked as follow-up tickets):
21
+ * - `meta.json.files[*].clusterId` field — needs scan-emit schema
22
+ * migration (Acceptance #3)
23
+ * - top-100 HIGH/CRIT reduction validation (Acceptance #4)
24
+ * - CWE-Bench-Java regression check (Acceptance #5)
25
+ * - `--cluster-policy <profile>` CLI flag in cognium-ai
26
+ * (Acceptance #6)
27
+ * - LLM cluster labeling pass (optional future per the issue)
28
+ *
29
+ * The split is deliberate: P1-P3 of epic #155 were single-shot
30
+ * cache wins; P4's policy bundles + the two benchmark validation
31
+ * gates need empirical tuning that can't complete in a single
32
+ * release cycle. Foundation-first lets cognium-ai start consuming
33
+ * `clusterFiles()` as an opaque grouping signal while the policy
34
+ * resolver is iterated separately.
35
+ *
36
+ * **Determinism:** k-medoids is seeded from a digest of (sorted
37
+ * file paths || vector signature) so re-runs on the same input
38
+ * produce the same cluster assignments. Tie-breaking on equal
39
+ * distances falls back to lexicographic path order.
40
+ */
41
+ import * as crypto from 'crypto';
42
+ export const DEFAULT_CLUSTER_POLICY = {
43
+ contextProfile: 'application',
44
+ sourceWhitelist: [],
45
+ sinkWhitelist: [],
46
+ llmEnabled: true,
47
+ };
48
+ // ───────────────────────────────────────────────────────────────────
49
+ // CK vector extraction
50
+ // ───────────────────────────────────────────────────────────────────
51
+ /**
52
+ * Names emitted by circle-ir's CK metric passes. Mirrored from
53
+ * the JSDoc on `MetricValue` in circle-ir's public type surface
54
+ * (canonical: "CBO" | "RFC" | "LCOM" | "DIT" | "NOC" | "WMC").
55
+ * Matching is case-insensitive because metric-pass authors have
56
+ * historically used both casings.
57
+ */
58
+ const CK_METRIC_KEYS = ['CBO', 'RFC', 'LCOM', 'DIT', 'NOC', 'WMC'];
59
+ function findMetric(metrics, name) {
60
+ if (!metrics)
61
+ return 0;
62
+ const lower = name.toLowerCase();
63
+ const hit = metrics.find((m) => m.name.toLowerCase() === lower);
64
+ return hit ? hit.value : 0;
65
+ }
66
+ /**
67
+ * Extract a ClusterVector from a single-file `CircleIR` analyze()
68
+ * result. Missing metrics default to 0 — the cluster vector is
69
+ * structural, so a procedural file with no CK numbers will land
70
+ * in the "low-complexity" cluster naturally (it's a valid signal,
71
+ * not missing data).
72
+ */
73
+ export function extractClusterVector(circleIr) {
74
+ const metrics = circleIr.metrics?.metrics;
75
+ // Import package signatures — keep top-2 segments (e.g.
76
+ // `java.util.List` → `java.util`) so unrelated imports of
77
+ // different leaf types in the same package don't artificially
78
+ // increase Jaccard distance. circle-ir's `ImportInfo` carries
79
+ // `from_package` (the canonical package path) and falls back to
80
+ // `imported_name` when `from_package` is null (wildcard / default
81
+ // imports).
82
+ const imports = Array.from(new Set((circleIr.imports ?? [])
83
+ .map((imp) => {
84
+ const raw = imp.from_package ?? imp.imported_name ?? '';
85
+ const parts = raw.split('.');
86
+ return parts.length >= 2 ? parts.slice(0, 2).join('.') : raw;
87
+ })
88
+ .filter((s) => s.length > 0)));
89
+ // Class-level annotations (Java: `@RestController`, `@Service`,
90
+ // `@Entity`; Python: `@app.route` would surface here if circle-ir
91
+ // attaches it to the class node). Pulled from `types[*].annotations`.
92
+ const annotations = Array.from(new Set((circleIr.types ?? [])
93
+ .flatMap((t) => t.annotations ?? [])
94
+ .filter((s) => typeof s === 'string' && s.length > 0)));
95
+ // Public surface = count of types + methods that aren't explicitly
96
+ // marked private. `TypeInfo` has no visibility field in circle-ir's
97
+ // surface (all top-level types count as "public-ish" for clustering
98
+ // purposes); methods carry `modifiers: string[]` from which we pull
99
+ // the visibility token.
100
+ let publicSurface = 0;
101
+ for (const t of circleIr.types ?? []) {
102
+ publicSurface++;
103
+ for (const m of t.methods ?? []) {
104
+ if (!(m.modifiers ?? []).includes('private'))
105
+ publicSurface++;
106
+ }
107
+ }
108
+ return {
109
+ cbo: findMetric(metrics, 'CBO'),
110
+ rfc: findMetric(metrics, 'RFC'),
111
+ lcom: findMetric(metrics, 'LCOM'),
112
+ dit: findMetric(metrics, 'DIT'),
113
+ noc: findMetric(metrics, 'NOC'),
114
+ wmc: findMetric(metrics, 'WMC'),
115
+ imports,
116
+ publicSurface,
117
+ annotations,
118
+ };
119
+ }
120
+ // ───────────────────────────────────────────────────────────────────
121
+ // Distance functions
122
+ // ───────────────────────────────────────────────────────────────────
123
+ /**
124
+ * Per-axis max-normalize the CK + publicSurface scalars across the
125
+ * input set. Max-normalize (rather than z-score) is deliberate:
126
+ * cluster boundaries should be robust to outliers (one god-class
127
+ * with WMC=400 shouldn't squash everything else into the same
128
+ * cluster), and max-normalize gives a clean [0,1] domain that
129
+ * combines naturally with Jaccard.
130
+ *
131
+ * Returns the maxima so callers can normalize new vectors against
132
+ * the same scale (e.g. for the k-medoids loop on the same set).
133
+ */
134
+ export function computeNormalization(inputs) {
135
+ const max = (sel) => Math.max(1, ...inputs.map((i) => sel(i.vector)));
136
+ return {
137
+ cboMax: max((v) => v.cbo),
138
+ rfcMax: max((v) => v.rfc),
139
+ lcomMax: max((v) => v.lcom),
140
+ ditMax: max((v) => v.dit),
141
+ nocMax: max((v) => v.noc),
142
+ wmcMax: max((v) => v.wmc),
143
+ publicSurfaceMax: max((v) => v.publicSurface),
144
+ };
145
+ }
146
+ /** Euclidean distance on the max-normalized CK + publicSurface scalars. */
147
+ export function euclideanCkDistance(a, b, norm) {
148
+ const dx = [
149
+ (a.cbo - b.cbo) / norm.cboMax,
150
+ (a.rfc - b.rfc) / norm.rfcMax,
151
+ (a.lcom - b.lcom) / norm.lcomMax,
152
+ (a.dit - b.dit) / norm.ditMax,
153
+ (a.noc - b.noc) / norm.nocMax,
154
+ (a.wmc - b.wmc) / norm.wmcMax,
155
+ (a.publicSurface - b.publicSurface) / norm.publicSurfaceMax,
156
+ ];
157
+ return Math.sqrt(dx.reduce((acc, x) => acc + x * x, 0));
158
+ }
159
+ /**
160
+ * Jaccard distance on two string sets (1 - |intersection|/|union|).
161
+ * Returns 0 when both sets are empty (semantically: nothing
162
+ * distinguishes them on this axis).
163
+ */
164
+ export function jaccardDistance(a, b) {
165
+ if (a.length === 0 && b.length === 0)
166
+ return 0;
167
+ const sa = new Set(a);
168
+ const sb = new Set(b);
169
+ let intersect = 0;
170
+ for (const x of sa)
171
+ if (sb.has(x))
172
+ intersect++;
173
+ const union = sa.size + sb.size - intersect;
174
+ if (union === 0)
175
+ return 0;
176
+ return 1 - intersect / union;
177
+ }
178
+ /**
179
+ * Combined per-pair distance. Weights mirror the issue's signal
180
+ * vector ordering (CK is the primary signal; imports + annotations
181
+ * are tiebreakers). Total is in [0,1] so it composes cleanly with
182
+ * other metric-space algorithms.
183
+ *
184
+ * Weights chosen to sum to 1:
185
+ * 0.6 — CK + publicSurface Euclidean (clamped to 1 by sqrt of 7
186
+ * normalized components, since each (a-b)/max is in [-1,1])
187
+ * 0.2 — imports Jaccard
188
+ * 0.2 — annotations Jaccard
189
+ *
190
+ * Euclidean is divided by sqrt(7) to renormalize to [0,1] before
191
+ * weighting — without this, the CK term can exceed 1 and the total
192
+ * stops being a proper [0,1] distance.
193
+ */
194
+ export function mixedDistance(a, b, norm) {
195
+ const ck = euclideanCkDistance(a, b, norm) / Math.sqrt(7);
196
+ const importsD = jaccardDistance(a.imports, b.imports);
197
+ const annotationsD = jaccardDistance(a.annotations, b.annotations);
198
+ return 0.6 * ck + 0.2 * importsD + 0.2 * annotationsD;
199
+ }
200
+ // ───────────────────────────────────────────────────────────────────
201
+ // Seeded PRNG (deterministic across runs)
202
+ // ───────────────────────────────────────────────────────────────────
203
+ /**
204
+ * Mulberry32 — fast, well-distributed seeded PRNG. We don't need
205
+ * cryptographic randomness here, just reproducibility across runs
206
+ * for the same input set. Seed is derived from a digest of the
207
+ * sorted (path || vector) signatures so adding/removing files
208
+ * changes the seed but reordering inputs does not.
209
+ */
210
+ function mulberry32(seed) {
211
+ let s = seed >>> 0;
212
+ return () => {
213
+ s = (s + 0x6d2b79f5) >>> 0;
214
+ let t = s;
215
+ t = Math.imul(t ^ (t >>> 15), t | 1);
216
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
217
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
218
+ };
219
+ }
220
+ function deriveSeed(inputs) {
221
+ const sig = inputs
222
+ .map((i) => `${i.filePath}\0${i.vector.cbo},${i.vector.rfc},${i.vector.lcom},${i.vector.dit},${i.vector.noc},${i.vector.wmc},${i.vector.publicSurface}`)
223
+ .sort()
224
+ .join('|');
225
+ const hash = crypto.createHash('sha256').update(sig).digest();
226
+ return hash.readUInt32BE(0);
227
+ }
228
+ /**
229
+ * PAM-lite k-medoids on `inputs`.
230
+ *
231
+ * 1. Seed selection ("build" phase): pick the medoid that minimizes
232
+ * total distance to all points, then iteratively pick the point
233
+ * that maximizes minimum distance to existing medoids. This is
234
+ * deterministic given the seed and degenerates gracefully when
235
+ * `inputs.length <= k`.
236
+ * 2. Assignment: each point joins its closest medoid (lex path
237
+ * tiebreak on equal distances).
238
+ * 3. Swap phase: for each cluster, find the point that minimizes
239
+ * intra-cluster distance and promote it to medoid. Repeat until
240
+ * no medoid changes (or `maxIter` hits).
241
+ *
242
+ * Complexity is O(n^2 * iter) on the distance matrix. For the
243
+ * top-100 sweep (n ≈ 5000 per repo at the high end) this is ~25M
244
+ * pairwise computations per iteration — fine for the <2s budget
245
+ * in Acceptance #2 because distances are scalar arithmetic on
246
+ * a 9-dim vector.
247
+ */
248
+ export function clusterFiles(inputs, options) {
249
+ const k = Math.max(1, Math.min(options.k, inputs.length));
250
+ const maxIter = options.maxIter ?? 50;
251
+ if (inputs.length === 0) {
252
+ return { k: 0, assignments: [], medoids: [], iterations: 0 };
253
+ }
254
+ // Sort by path so ties are broken lexicographically regardless of
255
+ // input order.
256
+ const sorted = [...inputs].sort((a, b) => a.filePath.localeCompare(b.filePath));
257
+ const n = sorted.length;
258
+ const norm = computeNormalization(sorted);
259
+ // Precompute distance matrix (symmetric, diagonal zero).
260
+ const dist = Array.from({ length: n }, () => new Array(n).fill(0));
261
+ for (let i = 0; i < n; i++) {
262
+ for (let j = i + 1; j < n; j++) {
263
+ const d = mixedDistance(sorted[i].vector, sorted[j].vector, norm);
264
+ dist[i][j] = d;
265
+ dist[j][i] = d;
266
+ }
267
+ }
268
+ // Build phase — deterministic seed init.
269
+ const seed = options.seed ?? deriveSeed(sorted);
270
+ const rng = mulberry32(seed);
271
+ const medoidIdx = [];
272
+ // First medoid: minimize total distance to all points (1-medoid PAM).
273
+ let bestSum = Infinity;
274
+ let bestIdx = 0;
275
+ for (let i = 0; i < n; i++) {
276
+ let sum = 0;
277
+ for (let j = 0; j < n; j++)
278
+ sum += dist[i][j];
279
+ if (sum < bestSum || (sum === bestSum && sorted[i].filePath < sorted[bestIdx].filePath)) {
280
+ bestSum = sum;
281
+ bestIdx = i;
282
+ }
283
+ }
284
+ medoidIdx.push(bestIdx);
285
+ // Remaining medoids: maximize min-distance to existing medoids.
286
+ // RNG breaks ties on equally-far candidates for deterministic but
287
+ // non-degenerate seeding when the set has structural symmetry.
288
+ while (medoidIdx.length < k) {
289
+ let bestPick = -1;
290
+ let bestMinDist = -1;
291
+ const candidates = [];
292
+ for (let i = 0; i < n; i++) {
293
+ if (medoidIdx.includes(i))
294
+ continue;
295
+ let minD = Infinity;
296
+ for (const m of medoidIdx) {
297
+ if (dist[i][m] < minD)
298
+ minD = dist[i][m];
299
+ }
300
+ if (minD > bestMinDist) {
301
+ bestMinDist = minD;
302
+ bestPick = i;
303
+ candidates.length = 0;
304
+ candidates.push(i);
305
+ }
306
+ else if (minD === bestMinDist) {
307
+ candidates.push(i);
308
+ }
309
+ }
310
+ if (bestPick === -1)
311
+ break;
312
+ // Tiebreak: lex-first wins; if rng() < 0.5, allow a randomized
313
+ // alternative from the tied set. This preserves determinism
314
+ // (same seed → same pick) while avoiding pathological pile-ups
315
+ // on highly-symmetric inputs.
316
+ if (candidates.length > 1 && rng() < 0.5) {
317
+ candidates.sort((a, b) => sorted[a].filePath.localeCompare(sorted[b].filePath));
318
+ bestPick = candidates[Math.floor(rng() * candidates.length)];
319
+ }
320
+ else {
321
+ // Default: lex-first.
322
+ bestPick = candidates.reduce((best, c) => (sorted[c].filePath < sorted[best].filePath ? c : best), candidates[0]);
323
+ }
324
+ medoidIdx.push(bestPick);
325
+ }
326
+ // Iterate: assign → swap → check convergence.
327
+ let assignment = new Array(n).fill(0);
328
+ let iterations = 0;
329
+ for (let iter = 0; iter < maxIter; iter++) {
330
+ iterations = iter + 1;
331
+ // Assign each point to closest medoid (lex tiebreak via medoid order).
332
+ for (let i = 0; i < n; i++) {
333
+ let bestC = 0;
334
+ let bestD = dist[i][medoidIdx[0]];
335
+ for (let c = 1; c < medoidIdx.length; c++) {
336
+ const d = dist[i][medoidIdx[c]];
337
+ if (d < bestD ||
338
+ (d === bestD && sorted[medoidIdx[c]].filePath < sorted[medoidIdx[bestC]].filePath)) {
339
+ bestD = d;
340
+ bestC = c;
341
+ }
342
+ }
343
+ assignment[i] = bestC;
344
+ }
345
+ // Swap: for each cluster, find the point that minimizes
346
+ // intra-cluster distance and promote it.
347
+ const newMedoids = [...medoidIdx];
348
+ for (let c = 0; c < medoidIdx.length; c++) {
349
+ const members = [];
350
+ for (let i = 0; i < n; i++)
351
+ if (assignment[i] === c)
352
+ members.push(i);
353
+ if (members.length === 0)
354
+ continue;
355
+ let bestM = newMedoids[c];
356
+ let bestCost = Infinity;
357
+ for (const m of members) {
358
+ let cost = 0;
359
+ for (const other of members)
360
+ cost += dist[m][other];
361
+ if (cost < bestCost || (cost === bestCost && sorted[m].filePath < sorted[bestM].filePath)) {
362
+ bestCost = cost;
363
+ bestM = m;
364
+ }
365
+ }
366
+ newMedoids[c] = bestM;
367
+ }
368
+ // Check convergence (medoid set stable).
369
+ let changed = false;
370
+ for (let c = 0; c < newMedoids.length; c++) {
371
+ if (newMedoids[c] !== medoidIdx[c]) {
372
+ changed = true;
373
+ break;
374
+ }
375
+ }
376
+ medoidIdx.length = 0;
377
+ medoidIdx.push(...newMedoids);
378
+ if (!changed)
379
+ break;
380
+ }
381
+ const assignments = sorted.map((inp, i) => ({
382
+ filePath: inp.filePath,
383
+ clusterId: assignment[i],
384
+ }));
385
+ return {
386
+ k: medoidIdx.length,
387
+ assignments,
388
+ medoids: medoidIdx.map((i) => sorted[i].filePath),
389
+ iterations,
390
+ };
391
+ }
392
+ //# sourceMappingURL=cluster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.js","sourceRoot":"","sources":["../../src/cluster/cluster.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Cluster layer public surface.
3
+ *
4
+ * Foundation only (cognium-ai#154, P4 of epic #155). Re-exports the
5
+ * primitives that downstream wiring tickets (meta.json schema, CLI
6
+ * flag, benchmark validation) will consume — see `cluster.ts`
7
+ * file-header for the scope split.
8
+ */
9
+ export { clusterFiles, extractClusterVector, computeNormalization, euclideanCkDistance, jaccardDistance, mixedDistance, DEFAULT_CLUSTER_POLICY, } from './cluster.js';
10
+ export type { ClusterVector, ClusterInput, ClusterAssignment, ClusterResult, ClusterPolicy, ContextProfile, } from './cluster.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cluster/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,GACf,MAAM,cAAc,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Cluster layer public surface.
3
+ *
4
+ * Foundation only (cognium-ai#154, P4 of epic #155). Re-exports the
5
+ * primitives that downstream wiring tickets (meta.json schema, CLI
6
+ * flag, benchmark validation) will consume — see `cluster.ts`
7
+ * file-header for the scope split.
8
+ */
9
+ export { clusterFiles, extractClusterVector, computeNormalization, euclideanCkDistance, jaccardDistance, mixedDistance, DEFAULT_CLUSTER_POLICY, } from './cluster.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cluster/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,sBAAsB,GACvB,MAAM,cAAc,CAAC"}
package/dist/index.d.ts CHANGED
@@ -55,4 +55,5 @@ export { analyzeComponents, enrichComponent, enrichComponents, clusterComponents
55
55
  export { summarizeFunction, summarizeAllFunctions, summarizeModule, detectRole, type FunctionEffect, type SideEffect, type SideEffectKind, type ModuleSummary, type ModuleRole, type UnderstandOptions, type UnderstandResult, } from './understand/index.js';
56
56
  export { analyzeSpecGap, parseSpecMd, type SpecGapResult, type SpecGapOptions, type RequirementMatch, type UncoveredRequirement, type UndocumentedBehavior, } from './spec-gap/index.js';
57
57
  export { loadCogniumConfig, convertCogniumConfigToPassOptions, type CogniumConfig, type Suppression, } from './config/cognium-config.js';
58
+ export { clusterFiles, extractClusterVector, computeNormalization, euclideanCkDistance, jaccardDistance, mixedDistance, DEFAULT_CLUSTER_POLICY, type ClusterVector, type ClusterInput, type ClusterAssignment, type ClusterResult, type ClusterPolicy, type ContextProfile, } from './cluster/index.js';
58
59
  //# sourceMappingURL=index.d.ts.map