@hegemonart/get-design-done 1.52.0 → 1.53.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +49 -0
- package/README.md +2 -0
- package/agents/design-context-reviewer-gate.md +102 -0
- package/agents/design-context-reviewer.md +186 -0
- package/dist/claude-code/.claude/skills/discover/SKILL.md +7 -1
- package/dist/claude-code/.claude/skills/explore/SKILL.md +3 -1
- package/package.json +1 -1
- package/scripts/lib/explore-parallel-runner/index.ts +58 -0
- package/scripts/lib/explore-parallel-runner/types.ts +58 -0
- package/scripts/lib/manifest/skills.json +2 -2
- package/scripts/lib/mappers/compute-batches.mjs +625 -0
- package/scripts/lib/mappers/graph-adjacency.mjs +129 -0
- package/scripts/lib/mappers/incremental-discover.cjs +617 -0
- package/scripts/lib/mappers/incremental-discover.d.cts +133 -0
- package/scripts/lib/mappers/neighbor-map.mjs +0 -0
- package/sdk/cli/index.js +369 -2
- package/sdk/fingerprint/classify.cjs +406 -0
- package/sdk/fingerprint/index.ts +405 -0
- package/sdk/fingerprint/store.cjs +523 -0
- package/sdk/index.ts +1 -0
- package/skills/discover/SKILL.md +7 -1
- package/skills/explore/SKILL.md +3 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// scripts/lib/mappers/incremental-discover.d.cts — types for incremental-discover.cjs (Phase 53 DISC-01).
|
|
2
|
+
//
|
|
3
|
+
// Mirrors the .d.cts sidecar convention used by concurrency-tuner.cjs so the
|
|
4
|
+
// .ts explore-parallel-runner can import planIncremental without a TS7016
|
|
5
|
+
// implicit-any. The runtime module is dep-free CJS (it dynamic-import()s the
|
|
6
|
+
// .mjs batchers + the .ts fingerprint engine internally).
|
|
7
|
+
|
|
8
|
+
/** A community batch (the A-subsystem `computeBatches` shape). */
|
|
9
|
+
export interface Batch {
|
|
10
|
+
id: string;
|
|
11
|
+
members: string[];
|
|
12
|
+
mergeable: boolean;
|
|
13
|
+
kind: 'code' | 'token' | 'motion' | 'a11y' | 'misc';
|
|
14
|
+
source: 'louvain' | 'fallback' | 'subsplit' | 'merge';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A single per-node fingerprint-compare entry fed to the classifier. */
|
|
18
|
+
export interface CompareResult {
|
|
19
|
+
id: string;
|
|
20
|
+
type?: string;
|
|
21
|
+
change: 'NONE' | 'COSMETIC' | 'STRUCTURAL';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The change-classifier result (sdk/fingerprint/classify.cjs). */
|
|
25
|
+
export interface Classification {
|
|
26
|
+
action: 'SKIP' | 'PARTIAL_UPDATE' | 'ARCHITECTURE_UPDATE' | 'FULL_UPDATE';
|
|
27
|
+
structuralCount: number;
|
|
28
|
+
pct: number;
|
|
29
|
+
dirChanged: boolean;
|
|
30
|
+
majorRestructure: boolean;
|
|
31
|
+
affectedBatchHints: string[];
|
|
32
|
+
reason: string;
|
|
33
|
+
thresholds: { fullFileCount: number; fullPct: number; archPctMax: number };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A per-node fingerprint pair plus its node type, as persisted to the store. */
|
|
37
|
+
export interface StoredFingerprint {
|
|
38
|
+
full: string;
|
|
39
|
+
structural: string;
|
|
40
|
+
type?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The neighborMap sidecar for one batch (neighbor-map.mjs shape). */
|
|
44
|
+
export interface NeighborMap {
|
|
45
|
+
batchId: string | null;
|
|
46
|
+
neighbors: Record<string, unknown[]>;
|
|
47
|
+
truncated: Record<string, { omitted: number }>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Options bag for planIncremental. */
|
|
51
|
+
export interface PlanIncrementalOptions {
|
|
52
|
+
/** The `--full` opt-out: force a FULL re-map regardless of the classifier. */
|
|
53
|
+
forceFull?: boolean;
|
|
54
|
+
/** Forwarded to computeBatches (resolution, maxCommunitySize, configCwd, …). */
|
|
55
|
+
computeBatchesOpts?: unknown;
|
|
56
|
+
/** buildNeighborMap cap (default 50). */
|
|
57
|
+
neighborCap?: number;
|
|
58
|
+
/** Forwarded into classify's projectStats.thresholds. */
|
|
59
|
+
thresholds?: unknown;
|
|
60
|
+
/** Force the bootstrap signal off when the store had a snapshot but it was empty. */
|
|
61
|
+
hadPriorBaseline?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The incremental plan returned by planIncremental. */
|
|
65
|
+
export interface IncrementalPlan {
|
|
66
|
+
action: 'SKIP' | 'PARTIAL_UPDATE' | 'ARCHITECTURE_UPDATE' | 'FULL_UPDATE';
|
|
67
|
+
batches: Batch[];
|
|
68
|
+
batchesToMap: Batch[];
|
|
69
|
+
neighborMaps: Record<string, NeighborMap>;
|
|
70
|
+
fingerprints: Record<string, StoredFingerprint>;
|
|
71
|
+
compareResults: CompareResult[];
|
|
72
|
+
classification: Classification;
|
|
73
|
+
method: 'louvain' | 'count-fallback';
|
|
74
|
+
modularity: number | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** A DirShape derived from node-id provenance. */
|
|
78
|
+
export interface DirShape {
|
|
79
|
+
dirs: string[];
|
|
80
|
+
counts: Record<string, number>;
|
|
81
|
+
layerHist: Record<string, number>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Plan an incremental discover/explore cycle: compute community batches, run the
|
|
86
|
+
* change classifier against the prior fingerprint snapshot, and elect which
|
|
87
|
+
* batches to re-map (SKIP=[], PARTIAL=affected, FULL=all). Async (it
|
|
88
|
+
* dynamic-import()s the .mjs batchers + the .ts fingerprint engine).
|
|
89
|
+
*/
|
|
90
|
+
export function planIncremental(args: {
|
|
91
|
+
graph: unknown;
|
|
92
|
+
prevFingerprints?: unknown;
|
|
93
|
+
opts?: PlanIncrementalOptions;
|
|
94
|
+
}): Promise<IncrementalPlan>;
|
|
95
|
+
|
|
96
|
+
/** Pure batch-selection matrix: SKIP→[], FULL→all, PARTIAL/ARCH→hint intersection. */
|
|
97
|
+
export function selectBatches(
|
|
98
|
+
batches: Batch[],
|
|
99
|
+
action: string,
|
|
100
|
+
affectedBatchHints: string[],
|
|
101
|
+
): Batch[];
|
|
102
|
+
|
|
103
|
+
/** Derive the current DirShape (+ totalFiles) from a graph's file-nodes. */
|
|
104
|
+
export function deriveDirShape(
|
|
105
|
+
graph: unknown,
|
|
106
|
+
): DirShape & { totalFiles: number };
|
|
107
|
+
|
|
108
|
+
/** Reconstruct the prior DirShape from a prevFingerprints map (null when empty). */
|
|
109
|
+
export function derivePrevDirShape(
|
|
110
|
+
prevFingerprints: unknown,
|
|
111
|
+
): DirShape | null;
|
|
112
|
+
|
|
113
|
+
/** Build current fingerprints + the compareResults change set for a graph. */
|
|
114
|
+
export function buildCompareResults(
|
|
115
|
+
graph: unknown,
|
|
116
|
+
prevFingerprints: unknown,
|
|
117
|
+
fingerprint: (input: unknown, type: string) => { full: string; structural: string },
|
|
118
|
+
compareFingerprints: (
|
|
119
|
+
a: { full: string; structural: string } | null,
|
|
120
|
+
b: { full: string; structural: string } | null,
|
|
121
|
+
) => string,
|
|
122
|
+
): { fingerprints: Record<string, StoredFingerprint>; compareResults: CompareResult[] };
|
|
123
|
+
|
|
124
|
+
/** Project one fingerprintable node into its per-type fingerprint input. */
|
|
125
|
+
export function projectNode(node: unknown, fpType: string, idx: unknown): unknown;
|
|
126
|
+
|
|
127
|
+
/** Build the once-per-graph relationship index used by projectNode. */
|
|
128
|
+
export function indexForProjection(
|
|
129
|
+
graph: unknown,
|
|
130
|
+
): { byId: Map<string, unknown>; tokensOf: Map<string, Set<string>>; variantsOf: Map<string, Set<string>> };
|
|
131
|
+
|
|
132
|
+
/** The graph node `type` values that are independently fingerprinted. */
|
|
133
|
+
export const FINGERPRINTABLE: Map<string, string>;
|
|
Binary file
|
package/sdk/cli/index.js
CHANGED
|
@@ -527,6 +527,329 @@ var require_concurrency_tuner = __commonJS({
|
|
|
527
527
|
}
|
|
528
528
|
});
|
|
529
529
|
|
|
530
|
+
// scripts/lib/mappers/incremental-discover.cjs
|
|
531
|
+
var require_incremental_discover = __commonJS({
|
|
532
|
+
"scripts/lib/mappers/incremental-discover.cjs"(exports2, module2) {
|
|
533
|
+
"use strict";
|
|
534
|
+
var path = require("node:path");
|
|
535
|
+
var fs = require("node:fs");
|
|
536
|
+
var { pathToFileURL } = require("node:url");
|
|
537
|
+
function pkgRoot() {
|
|
538
|
+
let dir = __dirname;
|
|
539
|
+
for (let i = 0; i < 10; i += 1) {
|
|
540
|
+
if (fs.existsSync(path.join(dir, "package.json"))) return dir;
|
|
541
|
+
const parent = path.dirname(dir);
|
|
542
|
+
if (parent === dir) break;
|
|
543
|
+
dir = parent;
|
|
544
|
+
}
|
|
545
|
+
return process.cwd();
|
|
546
|
+
}
|
|
547
|
+
var ROOT = pkgRoot();
|
|
548
|
+
var MAPPERS_DIR = path.join(ROOT, "scripts", "lib", "mappers");
|
|
549
|
+
var SDK_FP_DIR = path.join(ROOT, "sdk", "fingerprint");
|
|
550
|
+
var _classify = null;
|
|
551
|
+
function classify(...args) {
|
|
552
|
+
if (!_classify) _classify = require(path.join(SDK_FP_DIR, "classify.cjs")).classify;
|
|
553
|
+
return _classify(...args);
|
|
554
|
+
}
|
|
555
|
+
var _batchMod = null;
|
|
556
|
+
var _neighborMod = null;
|
|
557
|
+
var _fpMod = null;
|
|
558
|
+
async function loadBatchMod() {
|
|
559
|
+
if (!_batchMod) {
|
|
560
|
+
_batchMod = await import(pathToFileURL(path.join(MAPPERS_DIR, "compute-batches.mjs")).href);
|
|
561
|
+
}
|
|
562
|
+
return _batchMod;
|
|
563
|
+
}
|
|
564
|
+
async function loadNeighborMod() {
|
|
565
|
+
if (!_neighborMod) {
|
|
566
|
+
_neighborMod = await import(pathToFileURL(path.join(MAPPERS_DIR, "neighbor-map.mjs")).href);
|
|
567
|
+
}
|
|
568
|
+
return _neighborMod;
|
|
569
|
+
}
|
|
570
|
+
async function loadFingerprintMod() {
|
|
571
|
+
if (!_fpMod) {
|
|
572
|
+
_fpMod = await import(pathToFileURL(path.join(SDK_FP_DIR, "index.ts")).href);
|
|
573
|
+
}
|
|
574
|
+
return _fpMod;
|
|
575
|
+
}
|
|
576
|
+
function nodeList(graph) {
|
|
577
|
+
return Array.isArray(graph && graph.nodes) ? graph.nodes : [];
|
|
578
|
+
}
|
|
579
|
+
function edgeList(graph) {
|
|
580
|
+
return Array.isArray(graph && graph.edges) ? graph.edges : [];
|
|
581
|
+
}
|
|
582
|
+
var STRUCTURAL_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
583
|
+
"composes",
|
|
584
|
+
"extends",
|
|
585
|
+
"depends-on",
|
|
586
|
+
"consumes-context",
|
|
587
|
+
"provides-context"
|
|
588
|
+
]);
|
|
589
|
+
var FINGERPRINTABLE = /* @__PURE__ */ new Map([
|
|
590
|
+
["component", "component"],
|
|
591
|
+
["token", "token"],
|
|
592
|
+
["motion-fragment", "motion"]
|
|
593
|
+
]);
|
|
594
|
+
function indexForProjection(graph) {
|
|
595
|
+
const byId = /* @__PURE__ */ new Map();
|
|
596
|
+
for (const n of nodeList(graph)) {
|
|
597
|
+
if (n && typeof n.id === "string") byId.set(n.id, n);
|
|
598
|
+
}
|
|
599
|
+
const tokensOf = /* @__PURE__ */ new Map();
|
|
600
|
+
const variantsOf = /* @__PURE__ */ new Map();
|
|
601
|
+
const ensure = (map, id) => {
|
|
602
|
+
let s = map.get(id);
|
|
603
|
+
if (!s) {
|
|
604
|
+
s = /* @__PURE__ */ new Set();
|
|
605
|
+
map.set(id, s);
|
|
606
|
+
}
|
|
607
|
+
return s;
|
|
608
|
+
};
|
|
609
|
+
const typeOf = (id) => byId.get(id) && byId.get(id).type;
|
|
610
|
+
const nameOf = (id) => {
|
|
611
|
+
const node = byId.get(id);
|
|
612
|
+
return node && typeof node.name === "string" ? node.name : id;
|
|
613
|
+
};
|
|
614
|
+
for (const e of edgeList(graph)) {
|
|
615
|
+
if (!e || typeof e.source !== "string" || typeof e.target !== "string") continue;
|
|
616
|
+
const sType = typeOf(e.source);
|
|
617
|
+
const tType = typeOf(e.target);
|
|
618
|
+
if (e.type === "uses-token") {
|
|
619
|
+
if (sType === "component" && tType === "token") ensure(tokensOf, e.source).add(e.target);
|
|
620
|
+
else if (tType === "component" && sType === "token") ensure(tokensOf, e.target).add(e.source);
|
|
621
|
+
} else if (e.type === "extends") {
|
|
622
|
+
if (tType === "component" && (sType === "variant" || sType === "state")) {
|
|
623
|
+
ensure(variantsOf, e.target).add(nameOf(e.source));
|
|
624
|
+
} else if (sType === "component" && (tType === "variant" || tType === "state")) {
|
|
625
|
+
ensure(variantsOf, e.source).add(nameOf(e.target));
|
|
626
|
+
}
|
|
627
|
+
} else if (STRUCTURAL_EDGE_TYPES.has(e.type)) {
|
|
628
|
+
if (sType === "component" && tType === "state") ensure(variantsOf, e.source).add(nameOf(e.target));
|
|
629
|
+
else if (tType === "component" && sType === "state") ensure(variantsOf, e.target).add(nameOf(e.source));
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return { byId, tokensOf, variantsOf };
|
|
633
|
+
}
|
|
634
|
+
function strArray(v) {
|
|
635
|
+
if (!Array.isArray(v)) return [];
|
|
636
|
+
const out = [];
|
|
637
|
+
const seen = /* @__PURE__ */ new Set();
|
|
638
|
+
for (const x of v) {
|
|
639
|
+
const s = typeof x === "string" ? x : x && typeof x.name === "string" ? x.name : null;
|
|
640
|
+
if (s != null && !seen.has(s)) {
|
|
641
|
+
seen.add(s);
|
|
642
|
+
out.push(s);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
out.sort();
|
|
646
|
+
return out;
|
|
647
|
+
}
|
|
648
|
+
function propShape(props) {
|
|
649
|
+
if (!Array.isArray(props)) return [];
|
|
650
|
+
const out = [];
|
|
651
|
+
for (const p of props) {
|
|
652
|
+
if (typeof p === "string") {
|
|
653
|
+
out.push({ name: p, type: "" });
|
|
654
|
+
} else if (p && typeof p === "object" && typeof p.name === "string") {
|
|
655
|
+
out.push({
|
|
656
|
+
name: p.name,
|
|
657
|
+
type: typeof p.type === "string" ? p.type : "",
|
|
658
|
+
...typeof p.optional === "boolean" ? { optional: p.optional } : {}
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return out;
|
|
663
|
+
}
|
|
664
|
+
function projectNode(node, fpType, idx) {
|
|
665
|
+
if (fpType === "component") {
|
|
666
|
+
const usedTokens = idx.tokensOf.has(node.id) ? [...idx.tokensOf.get(node.id)].sort() : [];
|
|
667
|
+
const variantsFromGraph = idx.variantsOf.has(node.id) ? [...idx.variantsOf.get(node.id)] : [];
|
|
668
|
+
const variantsFromNode = strArray(node.exported_variants || node.variants);
|
|
669
|
+
const exportedVariants = [.../* @__PURE__ */ new Set([...variantsFromGraph, ...variantsFromNode])].sort();
|
|
670
|
+
return {
|
|
671
|
+
component_signature: {
|
|
672
|
+
name: typeof node.name === "string" ? node.name : node.id,
|
|
673
|
+
members: strArray(node.members)
|
|
674
|
+
},
|
|
675
|
+
props_shape: propShape(node.props),
|
|
676
|
+
used_tokens: usedTokens,
|
|
677
|
+
exported_variants: exportedVariants
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
if (fpType === "token") {
|
|
681
|
+
return {
|
|
682
|
+
token_name: typeof node.name === "string" ? node.name : node.id,
|
|
683
|
+
token_value: node.value === void 0 ? null : typeof node.value === "object" ? JSON.stringify(node.value) : node.value,
|
|
684
|
+
token_type: typeof node.subtype === "string" ? node.subtype : typeof node.token_type === "string" ? node.token_type : "",
|
|
685
|
+
...typeof node.subtype === "string" ? { subtype: node.subtype } : {},
|
|
686
|
+
...typeof node.theme_scope === "string" ? { theme_scope: node.theme_scope } : {}
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
return {
|
|
690
|
+
animation_target: typeof node.name === "string" ? node.name : node.id,
|
|
691
|
+
...Number.isFinite(node.duration_ms) ? { duration_ms: node.duration_ms } : {},
|
|
692
|
+
...typeof node.easing === "string" ? { easing: node.easing } : {}
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
function idNamespace(id) {
|
|
696
|
+
if (typeof id !== "string") return "unknown";
|
|
697
|
+
const i = id.indexOf(":");
|
|
698
|
+
return i > 0 ? id.slice(0, i) : id;
|
|
699
|
+
}
|
|
700
|
+
function isFileNode(node) {
|
|
701
|
+
return !!node && FINGERPRINTABLE.has(node.type);
|
|
702
|
+
}
|
|
703
|
+
function deriveDirShape(graph) {
|
|
704
|
+
const counts = {};
|
|
705
|
+
const layerHist = {};
|
|
706
|
+
let totalFiles = 0;
|
|
707
|
+
for (const n of nodeList(graph)) {
|
|
708
|
+
if (!isFileNode(n)) continue;
|
|
709
|
+
totalFiles += 1;
|
|
710
|
+
const ns = idNamespace(n.id);
|
|
711
|
+
let dir = ns;
|
|
712
|
+
if (n.type === "token" && typeof n.subtype === "string" && n.subtype.length) {
|
|
713
|
+
dir = `token:${n.subtype}`;
|
|
714
|
+
}
|
|
715
|
+
counts[dir] = (counts[dir] || 0) + 1;
|
|
716
|
+
layerHist[ns] = (layerHist[ns] || 0) + 1;
|
|
717
|
+
}
|
|
718
|
+
const dirs = Object.keys(counts).sort();
|
|
719
|
+
return { dirs, counts, layerHist, totalFiles };
|
|
720
|
+
}
|
|
721
|
+
function derivePrevDirShape(prevFingerprints) {
|
|
722
|
+
const fps = prevFingerprints && typeof prevFingerprints === "object" ? prevFingerprints : {};
|
|
723
|
+
const ids = Object.keys(fps);
|
|
724
|
+
if (ids.length === 0) return null;
|
|
725
|
+
const counts = {};
|
|
726
|
+
const layerHist = {};
|
|
727
|
+
for (const id of ids) {
|
|
728
|
+
const ns = idNamespace(id);
|
|
729
|
+
let dir = ns;
|
|
730
|
+
if (ns === "token") {
|
|
731
|
+
const parts = id.split(":");
|
|
732
|
+
if (parts.length >= 3 && parts[1]) dir = `token:${parts[1]}`;
|
|
733
|
+
}
|
|
734
|
+
counts[dir] = (counts[dir] || 0) + 1;
|
|
735
|
+
layerHist[ns] = (layerHist[ns] || 0) + 1;
|
|
736
|
+
}
|
|
737
|
+
return { dirs: Object.keys(counts).sort(), counts, layerHist };
|
|
738
|
+
}
|
|
739
|
+
function asFingerprint(v) {
|
|
740
|
+
if (v == null) return null;
|
|
741
|
+
if (typeof v === "string") return { full: v, structural: v };
|
|
742
|
+
if (typeof v === "object" && typeof v.full === "string") {
|
|
743
|
+
return { full: v.full, structural: typeof v.structural === "string" ? v.structural : v.full };
|
|
744
|
+
}
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
function buildCompareResults(graph, prevFingerprints, fingerprint, compareFingerprints) {
|
|
748
|
+
const idx = indexForProjection(graph);
|
|
749
|
+
const prev = prevFingerprints && typeof prevFingerprints === "object" ? prevFingerprints : {};
|
|
750
|
+
const fingerprints = {};
|
|
751
|
+
const compareResults = [];
|
|
752
|
+
const currentIds = /* @__PURE__ */ new Set();
|
|
753
|
+
for (const node of nodeList(graph)) {
|
|
754
|
+
if (!node || typeof node.id !== "string") continue;
|
|
755
|
+
const fpType = FINGERPRINTABLE.get(node.type);
|
|
756
|
+
if (!fpType) continue;
|
|
757
|
+
currentIds.add(node.id);
|
|
758
|
+
let fp;
|
|
759
|
+
try {
|
|
760
|
+
fp = fingerprint(projectNode(node, fpType, idx), fpType);
|
|
761
|
+
} catch {
|
|
762
|
+
compareResults.push({ id: node.id, type: node.type, change: "STRUCTURAL" });
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
fingerprints[node.id] = { full: fp.full, structural: fp.structural, type: node.type };
|
|
766
|
+
const before = asFingerprint(prev[node.id]);
|
|
767
|
+
const change = compareFingerprints(before, { full: fp.full, structural: fp.structural });
|
|
768
|
+
compareResults.push({ id: node.id, type: node.type, change });
|
|
769
|
+
}
|
|
770
|
+
for (const id of Object.keys(prev)) {
|
|
771
|
+
if (currentIds.has(id)) continue;
|
|
772
|
+
const before = asFingerprint(prev[id]);
|
|
773
|
+
const change = compareFingerprints(before, null);
|
|
774
|
+
const t = prev[id] && typeof prev[id] === "object" && typeof prev[id].type === "string" ? prev[id].type : idNamespace(id);
|
|
775
|
+
compareResults.push({ id, type: t, change });
|
|
776
|
+
}
|
|
777
|
+
return { fingerprints, compareResults };
|
|
778
|
+
}
|
|
779
|
+
function selectBatches(batches, action, affectedBatchHints) {
|
|
780
|
+
const all = Array.isArray(batches) ? batches : [];
|
|
781
|
+
if (action === "SKIP") return [];
|
|
782
|
+
if (action === "FULL_UPDATE") return all.slice();
|
|
783
|
+
const hints = new Set(Array.isArray(affectedBatchHints) ? affectedBatchHints : []);
|
|
784
|
+
if (hints.size === 0) return [];
|
|
785
|
+
const out = [];
|
|
786
|
+
for (const b of all) {
|
|
787
|
+
const members = Array.isArray(b && b.members) ? b.members : [];
|
|
788
|
+
if (members.some((m) => hints.has(m))) out.push(b);
|
|
789
|
+
}
|
|
790
|
+
return out;
|
|
791
|
+
}
|
|
792
|
+
async function planIncremental2(args) {
|
|
793
|
+
const { graph, prevFingerprints, opts } = args && typeof args === "object" ? args : {};
|
|
794
|
+
const o = opts && typeof opts === "object" ? opts : {};
|
|
795
|
+
const prev = prevFingerprints && typeof prevFingerprints === "object" ? prevFingerprints : {};
|
|
796
|
+
const { computeBatches } = await loadBatchMod();
|
|
797
|
+
const { batches, modularity, method } = computeBatches(graph, o.computeBatchesOpts);
|
|
798
|
+
const { fingerprint, compareFingerprints } = await loadFingerprintMod();
|
|
799
|
+
const { fingerprints, compareResults } = buildCompareResults(
|
|
800
|
+
graph,
|
|
801
|
+
prev,
|
|
802
|
+
fingerprint,
|
|
803
|
+
compareFingerprints
|
|
804
|
+
);
|
|
805
|
+
const currDirShape = deriveDirShape(graph);
|
|
806
|
+
let prevDirShape = derivePrevDirShape(prev);
|
|
807
|
+
if (o.hadPriorBaseline === false) prevDirShape = null;
|
|
808
|
+
const projectStats = {
|
|
809
|
+
totalFiles: currDirShape.totalFiles,
|
|
810
|
+
prevDirShape,
|
|
811
|
+
currDirShape: { dirs: currDirShape.dirs, counts: currDirShape.counts, layerHist: currDirShape.layerHist },
|
|
812
|
+
...o.thresholds && typeof o.thresholds === "object" ? { thresholds: o.thresholds } : {}
|
|
813
|
+
};
|
|
814
|
+
const classification = classify(compareResults, projectStats);
|
|
815
|
+
const effectiveAction = o.forceFull ? "FULL_UPDATE" : classification.action;
|
|
816
|
+
const batchesToMap = selectBatches(batches, effectiveAction, classification.affectedBatchHints);
|
|
817
|
+
const neighborMaps = {};
|
|
818
|
+
if (batchesToMap.length > 0) {
|
|
819
|
+
const { buildNeighborMap } = await loadNeighborMod();
|
|
820
|
+
const cap = Number.isInteger(o.neighborCap) && o.neighborCap >= 0 ? o.neighborCap : 50;
|
|
821
|
+
for (const b of batchesToMap) {
|
|
822
|
+
if (b && typeof b.id === "string") {
|
|
823
|
+
neighborMaps[b.id] = buildNeighborMap(b, graph, { cap });
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return {
|
|
828
|
+
action: effectiveAction,
|
|
829
|
+
batches,
|
|
830
|
+
batchesToMap,
|
|
831
|
+
neighborMaps,
|
|
832
|
+
fingerprints,
|
|
833
|
+
compareResults,
|
|
834
|
+
classification,
|
|
835
|
+
method,
|
|
836
|
+
modularity
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
module2.exports = {
|
|
840
|
+
planIncremental: planIncremental2,
|
|
841
|
+
// exported for the wiring layer + tests (pure helpers, no side effects).
|
|
842
|
+
selectBatches,
|
|
843
|
+
deriveDirShape,
|
|
844
|
+
derivePrevDirShape,
|
|
845
|
+
buildCompareResults,
|
|
846
|
+
projectNode,
|
|
847
|
+
indexForProjection,
|
|
848
|
+
FINGERPRINTABLE
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
|
|
530
853
|
// sdk/cli/index.ts
|
|
531
854
|
var index_exports = {};
|
|
532
855
|
__export(index_exports, {
|
|
@@ -5560,6 +5883,7 @@ var import_node_path14 = require("node:path");
|
|
|
5560
5883
|
// scripts/lib/explore-parallel-runner/index.ts
|
|
5561
5884
|
var import_node_path12 = require("node:path");
|
|
5562
5885
|
var import_concurrency_tuner = __toESM(require_concurrency_tuner());
|
|
5886
|
+
var import_incremental_discover = __toESM(require_incremental_discover());
|
|
5563
5887
|
|
|
5564
5888
|
// scripts/lib/explore-parallel-runner/mappers.ts
|
|
5565
5889
|
var import_node_fs12 = require("node:fs");
|
|
@@ -5903,6 +6227,47 @@ async function run3(opts) {
|
|
|
5903
6227
|
const concurrency = opts.concurrency ?? (0, import_concurrency_tuner.resolveConcurrency)();
|
|
5904
6228
|
const logger = getLogger().child("explore.runner");
|
|
5905
6229
|
const outputPath = (0, import_node_path12.resolve)(cwd, ".design/DESIGN-PATTERNS.md");
|
|
6230
|
+
let batching = void 0;
|
|
6231
|
+
if (opts.incremental && opts.incremental.graph !== void 0 && opts.incremental.graph !== null) {
|
|
6232
|
+
try {
|
|
6233
|
+
const plan = await (0, import_incremental_discover.planIncremental)({
|
|
6234
|
+
graph: opts.incremental.graph,
|
|
6235
|
+
prevFingerprints: opts.incremental.prevFingerprints,
|
|
6236
|
+
opts: {
|
|
6237
|
+
...opts.incremental.forceFull !== void 0 ? { forceFull: opts.incremental.forceFull } : {},
|
|
6238
|
+
...opts.incremental.computeBatchesOpts !== void 0 ? { computeBatchesOpts: opts.incremental.computeBatchesOpts } : {},
|
|
6239
|
+
...opts.incremental.neighborCap !== void 0 ? { neighborCap: opts.incremental.neighborCap } : {},
|
|
6240
|
+
...opts.incremental.thresholds !== void 0 ? { thresholds: opts.incremental.thresholds } : {}
|
|
6241
|
+
}
|
|
6242
|
+
});
|
|
6243
|
+
batching = Object.freeze({
|
|
6244
|
+
action: plan.action,
|
|
6245
|
+
method: plan.method,
|
|
6246
|
+
modularity: plan.modularity,
|
|
6247
|
+
batches: Object.freeze(plan.batches.map((b) => Object.freeze({
|
|
6248
|
+
id: b.id,
|
|
6249
|
+
members: Object.freeze([...b.members]),
|
|
6250
|
+
mergeable: b.mergeable,
|
|
6251
|
+
kind: b.kind,
|
|
6252
|
+
source: b.source
|
|
6253
|
+
}))),
|
|
6254
|
+
batchesToMap: Object.freeze(plan.batchesToMap.map((b) => b.id)),
|
|
6255
|
+
neighborMaps: Object.freeze({ ...plan.neighborMaps }),
|
|
6256
|
+
classification: Object.freeze({ ...plan.classification })
|
|
6257
|
+
});
|
|
6258
|
+
logger.info("explore.runner.batching", {
|
|
6259
|
+
action: plan.action,
|
|
6260
|
+
method: plan.method,
|
|
6261
|
+
batch_count: plan.batches.length,
|
|
6262
|
+
batches_to_map: plan.batchesToMap.length,
|
|
6263
|
+
structural_count: plan.classification.structuralCount
|
|
6264
|
+
});
|
|
6265
|
+
} catch (err) {
|
|
6266
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6267
|
+
logger.warn("explore.runner.batching_failed", { message });
|
|
6268
|
+
batching = void 0;
|
|
6269
|
+
}
|
|
6270
|
+
}
|
|
5906
6271
|
logger.info("explore.runner.started", {
|
|
5907
6272
|
mapper_count: specs.length,
|
|
5908
6273
|
concurrency
|
|
@@ -5924,7 +6289,8 @@ async function run3(opts) {
|
|
|
5924
6289
|
}),
|
|
5925
6290
|
parallel_count: 0,
|
|
5926
6291
|
serial_count: 0,
|
|
5927
|
-
total_usage: { input_tokens: 0, output_tokens: 0, usd_cost: 0 }
|
|
6292
|
+
total_usage: { input_tokens: 0, output_tokens: 0, usd_cost: 0 },
|
|
6293
|
+
...batching !== void 0 ? { batching } : {}
|
|
5928
6294
|
});
|
|
5929
6295
|
}
|
|
5930
6296
|
const safeSpecs = [];
|
|
@@ -6040,7 +6406,8 @@ async function run3(opts) {
|
|
|
6040
6406
|
input_tokens: totalInput,
|
|
6041
6407
|
output_tokens: totalOutput,
|
|
6042
6408
|
usd_cost: totalCost
|
|
6043
|
-
}
|
|
6409
|
+
},
|
|
6410
|
+
...batching !== void 0 ? { batching } : {}
|
|
6044
6411
|
});
|
|
6045
6412
|
}
|
|
6046
6413
|
|