slopbrick 0.11.1 → 0.11.2
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/index.cjs +62 -70
- package/dist/index.d.cts +1 -3
- package/dist/index.d.ts +1 -3
- package/dist/index.js +272 -300
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ var VERSION, AI_SECURITY_NUMERIC, REPOSITORY_HEALTH_WEIGHTS;
|
|
|
30
30
|
var init_types = __esm({
|
|
31
31
|
"src/types.ts"() {
|
|
32
32
|
"use strict";
|
|
33
|
-
VERSION = "0.11.
|
|
33
|
+
VERSION = "0.11.2";
|
|
34
34
|
AI_SECURITY_NUMERIC = {
|
|
35
35
|
low: 100,
|
|
36
36
|
medium: 75,
|
|
@@ -3482,7 +3482,7 @@ function discoverTestFiles(cwd) {
|
|
|
3482
3482
|
}
|
|
3483
3483
|
return found;
|
|
3484
3484
|
}
|
|
3485
|
-
function walk(dir, out, readdirSync6,
|
|
3485
|
+
function walk(dir, out, readdirSync6, statSync8) {
|
|
3486
3486
|
let entries;
|
|
3487
3487
|
try {
|
|
3488
3488
|
entries = readdirSync6(dir);
|
|
@@ -3494,12 +3494,12 @@ function walk(dir, out, readdirSync6, statSync7) {
|
|
|
3494
3494
|
const full = `${dir}/${entry}`;
|
|
3495
3495
|
let stat;
|
|
3496
3496
|
try {
|
|
3497
|
-
stat =
|
|
3497
|
+
stat = statSync8(full);
|
|
3498
3498
|
} catch {
|
|
3499
3499
|
continue;
|
|
3500
3500
|
}
|
|
3501
3501
|
if (stat.isDirectory()) {
|
|
3502
|
-
walk(full, out, readdirSync6,
|
|
3502
|
+
walk(full, out, readdirSync6, statSync8);
|
|
3503
3503
|
} else if (stat.isFile()) {
|
|
3504
3504
|
if (/\.(test|spec)\.[jt]sx?$/.test(entry) || /\.stories\.[jt]sx?$/.test(entry)) {
|
|
3505
3505
|
out.push(full);
|
|
@@ -9408,8 +9408,8 @@ function hasExtendedSibling(filePath) {
|
|
|
9408
9408
|
const base = basename(filePath);
|
|
9409
9409
|
for (const ext of SOURCE_EXTENSIONS) {
|
|
9410
9410
|
try {
|
|
9411
|
-
const { statSync:
|
|
9412
|
-
|
|
9411
|
+
const { statSync: statSync8 } = __require("fs");
|
|
9412
|
+
statSync8(join7(dir, base + ext));
|
|
9413
9413
|
return true;
|
|
9414
9414
|
} catch {
|
|
9415
9415
|
}
|
|
@@ -9566,8 +9566,8 @@ var init_git = __esm({
|
|
|
9566
9566
|
import { createHash as createHash2 } from "crypto";
|
|
9567
9567
|
import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync2, renameSync, mkdirSync, unlinkSync } from "fs";
|
|
9568
9568
|
import { dirname as dirname4, isAbsolute, resolve as resolve7 } from "path";
|
|
9569
|
-
function loadCache(
|
|
9570
|
-
const abs = isAbsolute(
|
|
9569
|
+
function loadCache(cachePath4) {
|
|
9570
|
+
const abs = isAbsolute(cachePath4) ? cachePath4 : resolve7(process.cwd(), cachePath4);
|
|
9571
9571
|
if (!existsSync8(abs)) return void 0;
|
|
9572
9572
|
try {
|
|
9573
9573
|
const raw = readFileSync9(abs, "utf-8");
|
|
@@ -9578,8 +9578,8 @@ function loadCache(cachePath6) {
|
|
|
9578
9578
|
return void 0;
|
|
9579
9579
|
}
|
|
9580
9580
|
}
|
|
9581
|
-
function saveCache(
|
|
9582
|
-
const abs = isAbsolute(
|
|
9581
|
+
function saveCache(cachePath4, cache) {
|
|
9582
|
+
const abs = isAbsolute(cachePath4) ? cachePath4 : resolve7(process.cwd(), cachePath4);
|
|
9583
9583
|
mkdirSync(dirname4(abs), { recursive: true });
|
|
9584
9584
|
const tmp = abs + ".tmp";
|
|
9585
9585
|
if (existsSync8(tmp)) {
|
|
@@ -12876,38 +12876,46 @@ var init_heatmap = __esm({
|
|
|
12876
12876
|
}
|
|
12877
12877
|
});
|
|
12878
12878
|
|
|
12879
|
+
// ../core/dist/index.js
|
|
12880
|
+
import { writeFileSync as writeFileSync5, renameSync as renameSync2, existsSync as existsSync12, readFileSync as readFileSync15, statSync as statSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
12881
|
+
import { join as join10, dirname as dirname6 } from "path";
|
|
12882
|
+
function inventoryPath(workspaceDir) {
|
|
12883
|
+
return join10(workspaceDir, ".slopbrick", INVENTORY_FILENAME);
|
|
12884
|
+
}
|
|
12885
|
+
function constitutionPath(workspaceDir) {
|
|
12886
|
+
return join10(workspaceDir, ".slopbrick", CONSTITUTION_FILENAME);
|
|
12887
|
+
}
|
|
12888
|
+
function ensureSlopbrickDir(workspaceDir) {
|
|
12889
|
+
mkdirSync3(join10(workspaceDir, ".slopbrick"), { recursive: true });
|
|
12890
|
+
}
|
|
12891
|
+
function writeJsonAtomic(filePath, payload) {
|
|
12892
|
+
ensureSlopbrickDir(dirname6(filePath));
|
|
12893
|
+
const tmp = `${filePath}.tmp`;
|
|
12894
|
+
writeFileSync5(tmp, JSON.stringify(payload, null, 2), "utf-8");
|
|
12895
|
+
renameSync2(tmp, filePath);
|
|
12896
|
+
}
|
|
12897
|
+
function saveInventory(workspaceDir, inventory) {
|
|
12898
|
+
writeJsonAtomic(inventoryPath(workspaceDir), inventory);
|
|
12899
|
+
}
|
|
12900
|
+
function saveConstitution(workspaceDir, constitution) {
|
|
12901
|
+
writeJsonAtomic(constitutionPath(workspaceDir), constitution);
|
|
12902
|
+
}
|
|
12903
|
+
var MEMORY_SCHEMA_VERSION, INVENTORY_FILENAME, CONSTITUTION_FILENAME;
|
|
12904
|
+
var init_dist = __esm({
|
|
12905
|
+
"../core/dist/index.js"() {
|
|
12906
|
+
"use strict";
|
|
12907
|
+
MEMORY_SCHEMA_VERSION = "2";
|
|
12908
|
+
INVENTORY_FILENAME = "inventory.json";
|
|
12909
|
+
CONSTITUTION_FILENAME = "constitution.json";
|
|
12910
|
+
}
|
|
12911
|
+
});
|
|
12912
|
+
|
|
12879
12913
|
// src/engine/memory.ts
|
|
12880
|
-
import { existsSync as
|
|
12881
|
-
import { dirname as
|
|
12914
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync16, writeFileSync as writeFileSync6 } from "fs";
|
|
12915
|
+
import { dirname as dirname7, join as join11 } from "path";
|
|
12882
12916
|
import { createHash as createHash6 } from "crypto";
|
|
12883
|
-
import {
|
|
12884
|
-
MEMORY_SCHEMA_VERSION,
|
|
12885
|
-
writeCacheFromInventory
|
|
12886
|
-
} from "@usebrick/core";
|
|
12887
|
-
import {
|
|
12888
|
-
MEMORY_SCHEMA_VERSION as MEMORY_SCHEMA_VERSION2,
|
|
12889
|
-
isMemoryPattern as isMemoryPattern2,
|
|
12890
|
-
isComponentFingerprint as isComponentFingerprint2,
|
|
12891
|
-
isInventoryFile as isInventoryFile2,
|
|
12892
|
-
isConstitutionFile as isConstitutionFile2,
|
|
12893
|
-
isFileMtimeEntry as isFileMtimeEntry2,
|
|
12894
|
-
inventoryPath as inventoryPath2,
|
|
12895
|
-
constitutionPath as constitutionPath2,
|
|
12896
|
-
cachePath as cachePath3,
|
|
12897
|
-
loadInventory as loadInventory2,
|
|
12898
|
-
loadConstitution as loadConstitution2,
|
|
12899
|
-
saveConstitution as saveConstitution2,
|
|
12900
|
-
readCache as readCache3,
|
|
12901
|
-
isInventoryFresh as isInventoryFresh2,
|
|
12902
|
-
invalidateFile as invalidateFile2
|
|
12903
|
-
} from "@usebrick/core";
|
|
12904
|
-
async function saveInventory(workspaceDir, inventory, computeHash = computeFileHash) {
|
|
12905
|
-
const { saveInventory: coreSave } = await import("@usebrick/core");
|
|
12906
|
-
coreSave(workspaceDir, inventory);
|
|
12907
|
-
writeCacheFromInventory(workspaceDir, inventory, computeHash);
|
|
12908
|
-
}
|
|
12909
12917
|
function telemetryPath(cwd) {
|
|
12910
|
-
return
|
|
12918
|
+
return join11(cwd, TELEMETRY_FILE);
|
|
12911
12919
|
}
|
|
12912
12920
|
function isSlopAuditRun(value) {
|
|
12913
12921
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -12916,9 +12924,9 @@ function isSlopAuditRun(value) {
|
|
|
12916
12924
|
}
|
|
12917
12925
|
function readRuns(cwd) {
|
|
12918
12926
|
const path = telemetryPath(cwd);
|
|
12919
|
-
if (!
|
|
12927
|
+
if (!existsSync13(path)) return [];
|
|
12920
12928
|
try {
|
|
12921
|
-
const raw =
|
|
12929
|
+
const raw = readFileSync16(path, "utf-8");
|
|
12922
12930
|
const parsed = JSON.parse(raw);
|
|
12923
12931
|
if (!Array.isArray(parsed)) return [];
|
|
12924
12932
|
return parsed.filter(isSlopAuditRun);
|
|
@@ -12948,8 +12956,8 @@ function appendRun(cwd, report, thresholdExceeded2) {
|
|
|
12948
12956
|
runs.splice(0, runs.length - MAX_RUNS);
|
|
12949
12957
|
}
|
|
12950
12958
|
const path = telemetryPath(cwd);
|
|
12951
|
-
|
|
12952
|
-
|
|
12959
|
+
mkdirSync4(dirname7(path), { recursive: true });
|
|
12960
|
+
writeFileSync6(path, JSON.stringify(runs, null, 2));
|
|
12953
12961
|
return run2;
|
|
12954
12962
|
}
|
|
12955
12963
|
async function buildInventoryFromScan(scanResult, config, durationMs) {
|
|
@@ -13068,7 +13076,8 @@ var init_memory = __esm({
|
|
|
13068
13076
|
init_types();
|
|
13069
13077
|
init_patterns();
|
|
13070
13078
|
init_cache_incremental();
|
|
13071
|
-
|
|
13079
|
+
init_dist();
|
|
13080
|
+
TELEMETRY_FILE = join11(".slopbrick", "memory.json");
|
|
13072
13081
|
MAX_RUNS = 1e3;
|
|
13073
13082
|
}
|
|
13074
13083
|
});
|
|
@@ -13076,18 +13085,18 @@ var init_memory = __esm({
|
|
|
13076
13085
|
// src/engine/telemetry.ts
|
|
13077
13086
|
import {
|
|
13078
13087
|
appendFileSync,
|
|
13079
|
-
existsSync as
|
|
13080
|
-
mkdirSync as
|
|
13081
|
-
readFileSync as
|
|
13088
|
+
existsSync as existsSync14,
|
|
13089
|
+
mkdirSync as mkdirSync5,
|
|
13090
|
+
readFileSync as readFileSync17,
|
|
13082
13091
|
readdirSync as readdirSync5,
|
|
13083
|
-
renameSync as
|
|
13092
|
+
renameSync as renameSync3,
|
|
13084
13093
|
rmSync,
|
|
13085
|
-
statSync as
|
|
13094
|
+
statSync as statSync6
|
|
13086
13095
|
} from "fs";
|
|
13087
13096
|
import { createHash as createHash7 } from "crypto";
|
|
13088
|
-
import { dirname as
|
|
13097
|
+
import { dirname as dirname8, join as join12, relative as relative7 } from "path";
|
|
13089
13098
|
function telemetryPath2(cwd) {
|
|
13090
|
-
return
|
|
13099
|
+
return join12(cwd, TELEMETRY_DIR, TELEMETRY_FILE2);
|
|
13091
13100
|
}
|
|
13092
13101
|
function hashString(input) {
|
|
13093
13102
|
return createHash7("sha256").update(input).digest("hex").slice(0, 16);
|
|
@@ -13140,33 +13149,33 @@ function isTelemetryFile(name) {
|
|
|
13140
13149
|
}
|
|
13141
13150
|
function rotateTelemetry(cwd) {
|
|
13142
13151
|
const path = telemetryPath2(cwd);
|
|
13143
|
-
if (!
|
|
13152
|
+
if (!existsSync14(path)) {
|
|
13144
13153
|
return;
|
|
13145
13154
|
}
|
|
13146
|
-
const stats =
|
|
13155
|
+
const stats = statSync6(path);
|
|
13147
13156
|
if (stats.size < MAX_TELEMETRY_BYTES) {
|
|
13148
13157
|
return;
|
|
13149
13158
|
}
|
|
13150
|
-
const dir =
|
|
13159
|
+
const dir = dirname8(path);
|
|
13151
13160
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13152
|
-
|
|
13153
|
-
const rotated = readdirSync5(dir).filter(isTelemetryFile).map((name) => ({ name, mtime:
|
|
13161
|
+
renameSync3(path, join12(dir, `scans-${timestamp}.jsonl`));
|
|
13162
|
+
const rotated = readdirSync5(dir).filter(isTelemetryFile).map((name) => ({ name, mtime: statSync6(join12(dir, name)).mtimeMs })).sort((a, b) => a.mtime - b.mtime);
|
|
13154
13163
|
while (rotated.length > MAX_ROTATED_FILES) {
|
|
13155
13164
|
const oldest = rotated.shift();
|
|
13156
13165
|
if (oldest) {
|
|
13157
|
-
rmSync(
|
|
13166
|
+
rmSync(join12(dir, oldest.name), { force: true });
|
|
13158
13167
|
}
|
|
13159
13168
|
}
|
|
13160
13169
|
}
|
|
13161
13170
|
function readTelemetry(cwd) {
|
|
13162
|
-
const dir =
|
|
13163
|
-
if (!
|
|
13171
|
+
const dir = join12(cwd, TELEMETRY_DIR);
|
|
13172
|
+
if (!existsSync14(dir)) {
|
|
13164
13173
|
return [];
|
|
13165
13174
|
}
|
|
13166
|
-
const files = readdirSync5(dir).filter(isTelemetryFile).sort().map((name) =>
|
|
13175
|
+
const files = readdirSync5(dir).filter(isTelemetryFile).sort().map((name) => join12(dir, name));
|
|
13167
13176
|
const payloads = [];
|
|
13168
13177
|
for (const file of files) {
|
|
13169
|
-
const raw =
|
|
13178
|
+
const raw = readFileSync17(file, "utf-8");
|
|
13170
13179
|
for (const line of raw.split("\n")) {
|
|
13171
13180
|
const trimmed = line.trim();
|
|
13172
13181
|
if (!trimmed) continue;
|
|
@@ -13198,9 +13207,9 @@ function recordTelemetry(cwd, report, results, config) {
|
|
|
13198
13207
|
files: buildFileRecords(cwd, report, results)
|
|
13199
13208
|
};
|
|
13200
13209
|
const path = telemetryPath2(cwd);
|
|
13201
|
-
const dir =
|
|
13202
|
-
if (!
|
|
13203
|
-
|
|
13210
|
+
const dir = dirname8(path);
|
|
13211
|
+
if (!existsSync14(dir)) {
|
|
13212
|
+
mkdirSync5(dir, { recursive: true });
|
|
13204
13213
|
}
|
|
13205
13214
|
rotateTelemetry(cwd);
|
|
13206
13215
|
appendFileSync(path, JSON.stringify(payload) + "\n", "utf-8");
|
|
@@ -13210,7 +13219,7 @@ var TELEMETRY_DIR, TELEMETRY_FILE2, MAX_TELEMETRY_BYTES, MAX_ROTATED_FILES;
|
|
|
13210
13219
|
var init_telemetry = __esm({
|
|
13211
13220
|
"src/engine/telemetry.ts"() {
|
|
13212
13221
|
"use strict";
|
|
13213
|
-
TELEMETRY_DIR =
|
|
13222
|
+
TELEMETRY_DIR = join12(".slopbrick", "flywheel");
|
|
13214
13223
|
TELEMETRY_FILE2 = "scans.jsonl";
|
|
13215
13224
|
MAX_TELEMETRY_BYTES = 10 * 1024 * 1024;
|
|
13216
13225
|
MAX_ROTATED_FILES = 5;
|
|
@@ -13219,8 +13228,8 @@ var init_telemetry = __esm({
|
|
|
13219
13228
|
|
|
13220
13229
|
// src/engine/flywheel.ts
|
|
13221
13230
|
import { createHash as createHash8 } from "crypto";
|
|
13222
|
-
import { existsSync as
|
|
13223
|
-
import { join as
|
|
13231
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync18, writeFileSync as writeFileSync7 } from "fs";
|
|
13232
|
+
import { join as join13 } from "path";
|
|
13224
13233
|
function severityBump(severity) {
|
|
13225
13234
|
const order = ["low", "medium", "high"];
|
|
13226
13235
|
const idx = order.indexOf(severity);
|
|
@@ -13321,30 +13330,30 @@ function migrateFlywheelState(state) {
|
|
|
13321
13330
|
};
|
|
13322
13331
|
}
|
|
13323
13332
|
function loadFlywheelState(cwd) {
|
|
13324
|
-
const path =
|
|
13325
|
-
if (!
|
|
13333
|
+
const path = join13(cwd, FLYWHEEL_DIR, STATE_FILE);
|
|
13334
|
+
if (!existsSync15(path)) {
|
|
13326
13335
|
return migrateFlywheelState({});
|
|
13327
13336
|
}
|
|
13328
13337
|
try {
|
|
13329
|
-
const parsed = JSON.parse(
|
|
13338
|
+
const parsed = JSON.parse(readFileSync18(path, "utf-8"));
|
|
13330
13339
|
return migrateFlywheelState(parsed);
|
|
13331
13340
|
} catch {
|
|
13332
13341
|
return migrateFlywheelState({});
|
|
13333
13342
|
}
|
|
13334
13343
|
}
|
|
13335
13344
|
function loadResearchMetricsFromDisk(cwd) {
|
|
13336
|
-
const flywheelDir =
|
|
13337
|
-
const analysisPath =
|
|
13338
|
-
const candidatesPath =
|
|
13339
|
-
const hasAnalysis =
|
|
13340
|
-
const hasCandidates =
|
|
13345
|
+
const flywheelDir = join13(cwd, FLYWHEEL_DIR);
|
|
13346
|
+
const analysisPath = join13(flywheelDir, "analysis.json");
|
|
13347
|
+
const candidatesPath = join13(flywheelDir, "rule-candidates.json");
|
|
13348
|
+
const hasAnalysis = existsSync15(analysisPath);
|
|
13349
|
+
const hasCandidates = existsSync15(candidatesPath);
|
|
13341
13350
|
if (!hasAnalysis && !hasCandidates) return void 0;
|
|
13342
13351
|
let generatedSampleCount = 0;
|
|
13343
13352
|
let generatedRuleCoverage = 0;
|
|
13344
13353
|
let candidateYield = 0;
|
|
13345
13354
|
if (hasAnalysis) {
|
|
13346
13355
|
try {
|
|
13347
|
-
const raw = JSON.parse(
|
|
13356
|
+
const raw = JSON.parse(readFileSync18(analysisPath, "utf8"));
|
|
13348
13357
|
generatedSampleCount = raw.summary?.total ?? 0;
|
|
13349
13358
|
generatedRuleCoverage = raw.summary?.coverage ?? 0;
|
|
13350
13359
|
} catch {
|
|
@@ -13352,7 +13361,7 @@ function loadResearchMetricsFromDisk(cwd) {
|
|
|
13352
13361
|
}
|
|
13353
13362
|
if (hasCandidates) {
|
|
13354
13363
|
try {
|
|
13355
|
-
const raw = JSON.parse(
|
|
13364
|
+
const raw = JSON.parse(readFileSync18(candidatesPath, "utf8"));
|
|
13356
13365
|
candidateYield = raw.candidates?.length ?? 0;
|
|
13357
13366
|
} catch {
|
|
13358
13367
|
}
|
|
@@ -13365,9 +13374,9 @@ function loadResearchMetricsFromDisk(cwd) {
|
|
|
13365
13374
|
};
|
|
13366
13375
|
}
|
|
13367
13376
|
function saveFlywheelState(cwd, state) {
|
|
13368
|
-
const dir =
|
|
13369
|
-
if (!
|
|
13370
|
-
|
|
13377
|
+
const dir = join13(cwd, FLYWHEEL_DIR);
|
|
13378
|
+
if (!existsSync15(dir)) mkdirSync6(dir, { recursive: true });
|
|
13379
|
+
writeFileSync7(join13(dir, STATE_FILE), JSON.stringify(state, null, 2));
|
|
13371
13380
|
}
|
|
13372
13381
|
function hashFile(filePath) {
|
|
13373
13382
|
return createHash8("sha256").update(filePath).digest("hex").slice(0, 16);
|
|
@@ -13660,7 +13669,7 @@ var init_signal_strength2 = __esm({
|
|
|
13660
13669
|
});
|
|
13661
13670
|
|
|
13662
13671
|
// src/cli/tokens.ts
|
|
13663
|
-
import { readFileSync as
|
|
13672
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
13664
13673
|
function parseDtcgTokens(raw) {
|
|
13665
13674
|
let parsed;
|
|
13666
13675
|
try {
|
|
@@ -13675,7 +13684,7 @@ function parseDtcgTokens(raw) {
|
|
|
13675
13684
|
}
|
|
13676
13685
|
function readDtcgTokensFile(path) {
|
|
13677
13686
|
try {
|
|
13678
|
-
const raw =
|
|
13687
|
+
const raw = readFileSync19(path, "utf-8");
|
|
13679
13688
|
return parseDtcgTokens(raw);
|
|
13680
13689
|
} catch (error) {
|
|
13681
13690
|
return { ok: false, error: `Cannot read ${path}: ${error.message}` };
|
|
@@ -14115,8 +14124,8 @@ __export(doc_freshness_exports, {
|
|
|
14115
14124
|
extractInlineCodeSpans: () => extractInlineCodeSpans,
|
|
14116
14125
|
extractMarkdownLinks: () => extractMarkdownLinks
|
|
14117
14126
|
});
|
|
14118
|
-
import { readFileSync as
|
|
14119
|
-
import { join as
|
|
14127
|
+
import { readFileSync as readFileSync20, existsSync as existsSync16 } from "fs";
|
|
14128
|
+
import { join as join14, dirname as dirname10, relative as relative8 } from "path";
|
|
14120
14129
|
import { globby as globby2 } from "globby";
|
|
14121
14130
|
function extractInlineCodeSpans(source) {
|
|
14122
14131
|
const hits = [];
|
|
@@ -14180,10 +14189,10 @@ function extractMarkdownLinks(source) {
|
|
|
14180
14189
|
}
|
|
14181
14190
|
function declaredPackages(cwd) {
|
|
14182
14191
|
const out = /* @__PURE__ */ new Set();
|
|
14183
|
-
const pkgPath =
|
|
14184
|
-
if (!
|
|
14192
|
+
const pkgPath = join14(cwd, "package.json");
|
|
14193
|
+
if (!existsSync16(pkgPath)) return out;
|
|
14185
14194
|
try {
|
|
14186
|
-
const raw =
|
|
14195
|
+
const raw = readFileSync20(pkgPath, "utf-8");
|
|
14187
14196
|
const pkg = JSON.parse(raw);
|
|
14188
14197
|
for (const k of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
14189
14198
|
const v = pkg[k];
|
|
@@ -14219,7 +14228,7 @@ async function extractExports(cwd, config, maxFiles = 500) {
|
|
|
14219
14228
|
for (const abs of limited) {
|
|
14220
14229
|
let source;
|
|
14221
14230
|
try {
|
|
14222
|
-
source =
|
|
14231
|
+
source = readFileSync20(abs, "utf-8");
|
|
14223
14232
|
} catch {
|
|
14224
14233
|
continue;
|
|
14225
14234
|
}
|
|
@@ -14318,10 +14327,10 @@ function detectExpiredCodeExamples(source, relPath, cwd, packages) {
|
|
|
14318
14327
|
const imports = extractImports(block.body);
|
|
14319
14328
|
for (const imp of imports) {
|
|
14320
14329
|
if (imp.startsWith(".") || imp.startsWith("/")) {
|
|
14321
|
-
const docDir =
|
|
14322
|
-
const candidate =
|
|
14330
|
+
const docDir = dirname10(join14(cwd, relPath));
|
|
14331
|
+
const candidate = join14(docDir, imp);
|
|
14323
14332
|
const exts = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js", "/index.jsx"];
|
|
14324
|
-
const found = exts.some((e) =>
|
|
14333
|
+
const found = exts.some((e) => existsSync16(candidate + e));
|
|
14325
14334
|
if (!found) {
|
|
14326
14335
|
findings.push({
|
|
14327
14336
|
ruleId: "docs/expired-code-example",
|
|
@@ -14360,7 +14369,7 @@ function detectExpiredCodeExamples(source, relPath, cwd, packages) {
|
|
|
14360
14369
|
function detectBrokenLinks(source, relPath, cwd) {
|
|
14361
14370
|
const findings = [];
|
|
14362
14371
|
const links = extractMarkdownLinks(source);
|
|
14363
|
-
const docDir =
|
|
14372
|
+
const docDir = dirname10(join14(cwd, relPath));
|
|
14364
14373
|
for (const link of links) {
|
|
14365
14374
|
const target = link.target;
|
|
14366
14375
|
if (target.startsWith("http://") || target.startsWith("https://")) continue;
|
|
@@ -14368,8 +14377,8 @@ function detectBrokenLinks(source, relPath, cwd) {
|
|
|
14368
14377
|
if (target.startsWith("#")) continue;
|
|
14369
14378
|
if (target.startsWith("//")) continue;
|
|
14370
14379
|
if (target.startsWith("/")) continue;
|
|
14371
|
-
const resolved =
|
|
14372
|
-
if (!
|
|
14380
|
+
const resolved = join14(docDir, target);
|
|
14381
|
+
if (!existsSync16(resolved)) {
|
|
14373
14382
|
findings.push({
|
|
14374
14383
|
ruleId: "docs/broken-link",
|
|
14375
14384
|
severity: "low",
|
|
@@ -14406,7 +14415,7 @@ async function buildDocFreshness(cwd, config, options = {}) {
|
|
|
14406
14415
|
for (const abs of docLimited) {
|
|
14407
14416
|
let source;
|
|
14408
14417
|
try {
|
|
14409
|
-
source =
|
|
14418
|
+
source = readFileSync20(abs, "utf-8");
|
|
14410
14419
|
} catch {
|
|
14411
14420
|
continue;
|
|
14412
14421
|
}
|
|
@@ -14713,7 +14722,7 @@ __export(db_health_exports, {
|
|
|
14713
14722
|
DB_RULE_WEIGHTS: () => DB_RULE_WEIGHTS,
|
|
14714
14723
|
buildDbHealth: () => buildDbHealth
|
|
14715
14724
|
});
|
|
14716
|
-
import { readFileSync as
|
|
14725
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
14717
14726
|
import { relative as relative9 } from "path";
|
|
14718
14727
|
import { globby as globby3 } from "globby";
|
|
14719
14728
|
import { parse as parseSql, loadModule as loadSqlModule } from "pgsql-parser";
|
|
@@ -14724,7 +14733,7 @@ function ensureSqlModule() {
|
|
|
14724
14733
|
async function parseSqlFile(filePath) {
|
|
14725
14734
|
let raw;
|
|
14726
14735
|
try {
|
|
14727
|
-
raw =
|
|
14736
|
+
raw = readFileSync21(filePath, "utf-8");
|
|
14728
14737
|
} catch {
|
|
14729
14738
|
return null;
|
|
14730
14739
|
}
|
|
@@ -14964,7 +14973,7 @@ async function buildDbHealth(cwd, _config, options = {}) {
|
|
|
14964
14973
|
for (const abs of tsFiles.slice(0, maxFiles)) {
|
|
14965
14974
|
let source;
|
|
14966
14975
|
try {
|
|
14967
|
-
source =
|
|
14976
|
+
source = readFileSync21(abs, "utf-8");
|
|
14968
14977
|
} catch {
|
|
14969
14978
|
continue;
|
|
14970
14979
|
}
|
|
@@ -15417,8 +15426,8 @@ __export(scan_exports, {
|
|
|
15417
15426
|
scanProject: () => scanProject,
|
|
15418
15427
|
watchProject: () => watchProject
|
|
15419
15428
|
});
|
|
15420
|
-
import { existsSync as
|
|
15421
|
-
import { resolve as resolve9, join as
|
|
15429
|
+
import { existsSync as existsSync17, writeFileSync as writeFileSync8, watch, statSync as statSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync22 } from "fs";
|
|
15430
|
+
import { resolve as resolve9, join as join16, relative as relative10, extname as extname6, sep as sep2 } from "path";
|
|
15422
15431
|
function buildBaselineCache(report, configHash, gitHead, cwd) {
|
|
15423
15432
|
const scores = {};
|
|
15424
15433
|
for (const component of report.components) {
|
|
@@ -15441,10 +15450,10 @@ async function runScan(options, explicitPaths) {
|
|
|
15441
15450
|
setLoggerQuiet(!!options.quiet);
|
|
15442
15451
|
const startTime = Date.now();
|
|
15443
15452
|
const cwd = resolve9(options.workspace ?? process.cwd());
|
|
15444
|
-
if (!
|
|
15453
|
+
if (!existsSync17(cwd)) {
|
|
15445
15454
|
throw new Error(`Workspace not found: ${cwd}`);
|
|
15446
15455
|
}
|
|
15447
|
-
const cwdStat =
|
|
15456
|
+
const cwdStat = statSync7(cwd);
|
|
15448
15457
|
if (!cwdStat.isDirectory()) {
|
|
15449
15458
|
throw new Error(`Workspace is not a directory: ${cwd}`);
|
|
15450
15459
|
}
|
|
@@ -15486,8 +15495,8 @@ async function runScan(options, explicitPaths) {
|
|
|
15486
15495
|
const resolved = explicitPaths.map((p) => resolve9(cwd, p));
|
|
15487
15496
|
const expanded = [];
|
|
15488
15497
|
for (const p of resolved) {
|
|
15489
|
-
if (
|
|
15490
|
-
const found = await globby4(
|
|
15498
|
+
if (existsSync17(p) && statSync7(p).isDirectory()) {
|
|
15499
|
+
const found = await globby4(join16(p, "**/*"), { absolute: true, onlyFiles: true });
|
|
15491
15500
|
for (const f of found) {
|
|
15492
15501
|
if (!ALL_SOURCE_EXTENSIONS.has(extname6(f).toLowerCase())) continue;
|
|
15493
15502
|
const rel = relative10(cwd, f).split(sep2).join("/");
|
|
@@ -15525,8 +15534,8 @@ async function runScan(options, explicitPaths) {
|
|
|
15525
15534
|
}
|
|
15526
15535
|
let incrementalSummary;
|
|
15527
15536
|
if (options.incremental) {
|
|
15528
|
-
const
|
|
15529
|
-
const existing = loadCache(
|
|
15537
|
+
const cachePath4 = options.cachePath ?? ".slopbrick-cache.json";
|
|
15538
|
+
const existing = loadCache(cachePath4);
|
|
15530
15539
|
const { toScan, unchanged } = partitionByCache(files, existing);
|
|
15531
15540
|
files = toScan;
|
|
15532
15541
|
incrementalSummary = { skipped: unchanged.length, rescanned: toScan.length };
|
|
@@ -15631,7 +15640,7 @@ async function runScan(options, explicitPaths) {
|
|
|
15631
15640
|
const scannedPaths = new Set(results.map((result) => result.filePath));
|
|
15632
15641
|
for (const [filePath, cached] of Object.entries(baseline.scores)) {
|
|
15633
15642
|
if (scannedPaths.has(filePath)) continue;
|
|
15634
|
-
if (!
|
|
15643
|
+
if (!existsSync17(filePath)) continue;
|
|
15635
15644
|
scores.push({
|
|
15636
15645
|
filePath,
|
|
15637
15646
|
rawScore: 0,
|
|
@@ -15945,8 +15954,8 @@ async function runScan(options, explicitPaths) {
|
|
|
15945
15954
|
appendRun(cwd, report, thresholdExceeded(report, config));
|
|
15946
15955
|
}
|
|
15947
15956
|
if (options.incremental) {
|
|
15948
|
-
const
|
|
15949
|
-
const existing = loadCache(
|
|
15957
|
+
const cachePath4 = options.cachePath ?? ".slopbrick-cache.json";
|
|
15958
|
+
const existing = loadCache(cachePath4) ?? emptyCache();
|
|
15950
15959
|
const next = { ...existing, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
15951
15960
|
for (const result of results) {
|
|
15952
15961
|
try {
|
|
@@ -15960,7 +15969,7 @@ async function runScan(options, explicitPaths) {
|
|
|
15960
15969
|
} catch {
|
|
15961
15970
|
}
|
|
15962
15971
|
}
|
|
15963
|
-
saveCache(
|
|
15972
|
+
saveCache(cachePath4, next);
|
|
15964
15973
|
if (incrementalSummary && !options.quiet) {
|
|
15965
15974
|
logger.info(
|
|
15966
15975
|
`Incremental: re-scanned ${incrementalSummary.rescanned}, skipped ${incrementalSummary.skipped} (unchanged).`
|
|
@@ -15992,10 +16001,10 @@ async function runScan(options, explicitPaths) {
|
|
|
15992
16001
|
report.research = state.research;
|
|
15993
16002
|
}
|
|
15994
16003
|
if (flywheelOutput.suggestions.length > 0) {
|
|
15995
|
-
const suggestionsDir =
|
|
15996
|
-
if (!
|
|
15997
|
-
|
|
15998
|
-
|
|
16004
|
+
const suggestionsDir = join16(cwd, ".slopbrick", "flywheel");
|
|
16005
|
+
if (!existsSync17(suggestionsDir)) mkdirSync7(suggestionsDir, { recursive: true });
|
|
16006
|
+
writeFileSync8(
|
|
16007
|
+
join16(suggestionsDir, "rule-suggestions.json"),
|
|
15999
16008
|
JSON.stringify(flywheelOutput.suggestions, null, 2)
|
|
16000
16009
|
);
|
|
16001
16010
|
}
|
|
@@ -16012,7 +16021,7 @@ async function runScan(options, explicitPaths) {
|
|
|
16012
16021
|
);
|
|
16013
16022
|
await saveInventory(cwd, inventory);
|
|
16014
16023
|
const constitution = buildConstitutionFromConfig(config, cwd);
|
|
16015
|
-
await
|
|
16024
|
+
await saveConstitution(cwd, constitution);
|
|
16016
16025
|
if (!options.quiet && !machineReadableStdout) {
|
|
16017
16026
|
logger.info(`Memory persisted to .slopbrick/ (${inventory.patterns.length} patterns, ${inventory.components.length} components).`);
|
|
16018
16027
|
}
|
|
@@ -16043,7 +16052,7 @@ function collectBusinessLogicIssues(cwd, filePaths) {
|
|
|
16043
16052
|
for (const absPath of filePaths) {
|
|
16044
16053
|
let source;
|
|
16045
16054
|
try {
|
|
16046
|
-
source =
|
|
16055
|
+
source = readFileSync22(absPath, "utf-8");
|
|
16047
16056
|
} catch {
|
|
16048
16057
|
continue;
|
|
16049
16058
|
}
|
|
@@ -16112,7 +16121,7 @@ function renderOutput(report, options, cwd) {
|
|
|
16112
16121
|
if (options.html) {
|
|
16113
16122
|
const html = formatHtml(report);
|
|
16114
16123
|
if (typeof options.html === "string") {
|
|
16115
|
-
|
|
16124
|
+
writeFileSync8(resolve9(options.html), html);
|
|
16116
16125
|
if (!options.quiet) {
|
|
16117
16126
|
logger.info(`Wrote HTML report to ${options.html}`);
|
|
16118
16127
|
}
|
|
@@ -16124,7 +16133,7 @@ function renderOutput(report, options, cwd) {
|
|
|
16124
16133
|
if (options.json) {
|
|
16125
16134
|
const json = formatJson(report);
|
|
16126
16135
|
if (typeof options.json === "string") {
|
|
16127
|
-
|
|
16136
|
+
writeFileSync8(resolve9(options.json), json);
|
|
16128
16137
|
if (!options.quiet) {
|
|
16129
16138
|
logger.info(`Wrote JSON report to ${options.json}`);
|
|
16130
16139
|
}
|
|
@@ -16170,7 +16179,7 @@ async function watchProject(options, cwd, paths) {
|
|
|
16170
16179
|
let currentBaseline;
|
|
16171
16180
|
function getBaselineMtime() {
|
|
16172
16181
|
try {
|
|
16173
|
-
return
|
|
16182
|
+
return statSync7(baselinePath(cwd)).mtimeMs;
|
|
16174
16183
|
} catch {
|
|
16175
16184
|
return void 0;
|
|
16176
16185
|
}
|
|
@@ -16327,6 +16336,7 @@ var init_scan = __esm({
|
|
|
16327
16336
|
init_unified_diff();
|
|
16328
16337
|
init_heatmap();
|
|
16329
16338
|
init_memory();
|
|
16339
|
+
init_dist();
|
|
16330
16340
|
init_telemetry();
|
|
16331
16341
|
init_flywheel();
|
|
16332
16342
|
init_logger();
|
|
@@ -16340,17 +16350,17 @@ var init_scan = __esm({
|
|
|
16340
16350
|
});
|
|
16341
16351
|
|
|
16342
16352
|
// src/engine/memory-md.ts
|
|
16343
|
-
import { existsSync as
|
|
16344
|
-
import { dirname as
|
|
16353
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync13, readFileSync as readFileSync31, writeFileSync as writeFileSync14 } from "fs";
|
|
16354
|
+
import { dirname as dirname15, join as join22 } from "path";
|
|
16345
16355
|
async function readMemoryMarkdown(workspaceDir) {
|
|
16346
16356
|
return new Promise((resolve15) => {
|
|
16347
16357
|
try {
|
|
16348
|
-
const path =
|
|
16349
|
-
if (!
|
|
16358
|
+
const path = join22(workspaceDir, MEMORY_MD_FILE);
|
|
16359
|
+
if (!existsSync22(path)) {
|
|
16350
16360
|
resolve15(null);
|
|
16351
16361
|
return;
|
|
16352
16362
|
}
|
|
16353
|
-
const content =
|
|
16363
|
+
const content = readFileSync31(path, "utf-8");
|
|
16354
16364
|
resolve15(content);
|
|
16355
16365
|
} catch {
|
|
16356
16366
|
resolve15(null);
|
|
@@ -16361,7 +16371,7 @@ var MEMORY_MD_FILE;
|
|
|
16361
16371
|
var init_memory_md = __esm({
|
|
16362
16372
|
"src/engine/memory-md.ts"() {
|
|
16363
16373
|
"use strict";
|
|
16364
|
-
MEMORY_MD_FILE =
|
|
16374
|
+
MEMORY_MD_FILE = join22(".slopbrick", "memory.md");
|
|
16365
16375
|
}
|
|
16366
16376
|
});
|
|
16367
16377
|
|
|
@@ -16574,7 +16584,7 @@ __export(tools_exports, {
|
|
|
16574
16584
|
TOOL_DEFINITIONS: () => TOOL_DEFINITIONS,
|
|
16575
16585
|
handleToolCall: () => handleToolCall
|
|
16576
16586
|
});
|
|
16577
|
-
import { readFileSync as
|
|
16587
|
+
import { readFileSync as readFileSync32 } from "fs";
|
|
16578
16588
|
import { resolve as resolve13 } from "path";
|
|
16579
16589
|
function toolError(message) {
|
|
16580
16590
|
return {
|
|
@@ -16718,7 +16728,7 @@ function runCheckConstitution(args, ctx) {
|
|
|
16718
16728
|
const absPath = resolve13(ctx.cwd, path);
|
|
16719
16729
|
let source;
|
|
16720
16730
|
try {
|
|
16721
|
-
source =
|
|
16731
|
+
source = readFileSync32(absPath, "utf-8");
|
|
16722
16732
|
} catch (err) {
|
|
16723
16733
|
return toolError(
|
|
16724
16734
|
`Cannot read file ${absPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -16780,14 +16790,14 @@ async function runBusinessLogicScore(args, ctx) {
|
|
|
16780
16790
|
const maxFiles = typeof maxFilesRaw === "number" && Number.isFinite(maxFilesRaw) && maxFilesRaw > 0 ? Math.min(2e3, Math.floor(maxFilesRaw)) : 500;
|
|
16781
16791
|
try {
|
|
16782
16792
|
const { discoverFiles: discoverFiles2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
|
|
16783
|
-
const { readFileSync:
|
|
16793
|
+
const { readFileSync: readFileSync39 } = await import("fs");
|
|
16784
16794
|
const allFiles = await discoverFiles2(ctx.cwd, ctx.config);
|
|
16785
16795
|
const limited = allFiles.slice(0, maxFiles);
|
|
16786
16796
|
const issues = [];
|
|
16787
16797
|
for (const absPath of limited) {
|
|
16788
16798
|
let source;
|
|
16789
16799
|
try {
|
|
16790
|
-
source =
|
|
16800
|
+
source = readFileSync39(absPath, "utf-8");
|
|
16791
16801
|
} catch {
|
|
16792
16802
|
continue;
|
|
16793
16803
|
}
|
|
@@ -17062,44 +17072,44 @@ __export(migrate_exports, {
|
|
|
17062
17072
|
planMigration: () => planMigration,
|
|
17063
17073
|
runMigrate: () => runMigrate
|
|
17064
17074
|
});
|
|
17065
|
-
import { existsSync as
|
|
17066
|
-
import { join as
|
|
17075
|
+
import { existsSync as existsSync25, readFileSync as readFileSync37, renameSync as renameSync4, writeFileSync as writeFileSync18 } from "fs";
|
|
17076
|
+
import { join as join26 } from "path";
|
|
17067
17077
|
function planMigration(workspaceDir) {
|
|
17068
17078
|
const moves = [];
|
|
17069
17079
|
const rewrites = [];
|
|
17070
17080
|
const gitignoreEdits = [];
|
|
17071
|
-
const oldDir =
|
|
17072
|
-
const newDir =
|
|
17073
|
-
if (
|
|
17081
|
+
const oldDir = join26(workspaceDir, ".slop-audit");
|
|
17082
|
+
const newDir = join26(workspaceDir, ".slopbrick");
|
|
17083
|
+
if (existsSync25(oldDir)) {
|
|
17074
17084
|
moves.push({ from: oldDir, to: newDir, kind: "dir" });
|
|
17075
17085
|
rewrites.push({
|
|
17076
|
-
path:
|
|
17086
|
+
path: join26(newDir, "inventory.json"),
|
|
17077
17087
|
field: "version",
|
|
17078
17088
|
from: '"1"',
|
|
17079
17089
|
to: '"2"'
|
|
17080
17090
|
});
|
|
17081
17091
|
rewrites.push({
|
|
17082
|
-
path:
|
|
17092
|
+
path: join26(newDir, "constitution.json"),
|
|
17083
17093
|
field: "version",
|
|
17084
17094
|
from: '"1"',
|
|
17085
17095
|
to: '"2"'
|
|
17086
17096
|
});
|
|
17087
17097
|
}
|
|
17088
|
-
const oldCache =
|
|
17089
|
-
const newCache =
|
|
17090
|
-
if (
|
|
17098
|
+
const oldCache = join26(workspaceDir, ".slop-audit-cache.json");
|
|
17099
|
+
const newCache = join26(workspaceDir, ".slopbrick-cache.json");
|
|
17100
|
+
if (existsSync25(oldCache)) {
|
|
17091
17101
|
moves.push({ from: oldCache, to: newCache, kind: "file" });
|
|
17092
17102
|
}
|
|
17093
17103
|
for (const ext of ["mjs", "cjs", "js"]) {
|
|
17094
|
-
const oldCfg =
|
|
17095
|
-
const newCfg =
|
|
17096
|
-
if (
|
|
17104
|
+
const oldCfg = join26(workspaceDir, `slop-audit.config.${ext}`);
|
|
17105
|
+
const newCfg = join26(workspaceDir, `slopbrick.config.${ext}`);
|
|
17106
|
+
if (existsSync25(oldCfg)) {
|
|
17097
17107
|
moves.push({ from: oldCfg, to: newCfg, kind: "config" });
|
|
17098
17108
|
}
|
|
17099
17109
|
}
|
|
17100
|
-
const gi =
|
|
17101
|
-
if (
|
|
17102
|
-
const content =
|
|
17110
|
+
const gi = join26(workspaceDir, ".gitignore");
|
|
17111
|
+
if (existsSync25(gi)) {
|
|
17112
|
+
const content = readFileSync37(gi, "utf-8");
|
|
17103
17113
|
if (content.includes(".slop-audit/")) {
|
|
17104
17114
|
gitignoreEdits.push({
|
|
17105
17115
|
path: gi,
|
|
@@ -17118,28 +17128,28 @@ function planMigration(workspaceDir) {
|
|
|
17118
17128
|
return { moves, rewrites, gitignoreEdits };
|
|
17119
17129
|
}
|
|
17120
17130
|
function isAlreadyMigrated(workspaceDir) {
|
|
17121
|
-
return
|
|
17131
|
+
return existsSync25(join26(workspaceDir, ".slopbrick")) && !existsSync25(join26(workspaceDir, ".slop-audit"));
|
|
17122
17132
|
}
|
|
17123
17133
|
function applyMigration(plan, options = {}) {
|
|
17124
17134
|
if (options.dryRun) return;
|
|
17125
17135
|
for (const m of plan.moves) {
|
|
17126
|
-
|
|
17136
|
+
renameSync4(m.from, m.to);
|
|
17127
17137
|
}
|
|
17128
17138
|
for (const r of plan.rewrites) {
|
|
17129
|
-
if (!
|
|
17130
|
-
const content =
|
|
17139
|
+
if (!existsSync25(r.path)) continue;
|
|
17140
|
+
const content = readFileSync37(r.path, "utf-8");
|
|
17131
17141
|
const next = content.replace(`"version": ${r.from}`, `"version": ${r.to}`);
|
|
17132
|
-
|
|
17142
|
+
writeFileSync18(r.path, next);
|
|
17133
17143
|
}
|
|
17134
17144
|
for (const g of plan.gitignoreEdits) {
|
|
17135
|
-
const content =
|
|
17145
|
+
const content = readFileSync37(g.path, "utf-8");
|
|
17136
17146
|
const next = content.replaceAll(g.from, g.to);
|
|
17137
|
-
|
|
17147
|
+
writeFileSync18(g.path, next);
|
|
17138
17148
|
}
|
|
17139
17149
|
}
|
|
17140
17150
|
function runMigrate(options) {
|
|
17141
17151
|
const { workspace, dryRun = false, force = false } = options;
|
|
17142
|
-
if (!
|
|
17152
|
+
if (!existsSync25(workspace)) {
|
|
17143
17153
|
return {
|
|
17144
17154
|
ok: false,
|
|
17145
17155
|
alreadyMigrated: false,
|
|
@@ -17149,9 +17159,9 @@ function runMigrate(options) {
|
|
|
17149
17159
|
};
|
|
17150
17160
|
}
|
|
17151
17161
|
const alreadyMigrated = isAlreadyMigrated(workspace);
|
|
17152
|
-
const newDir =
|
|
17153
|
-
const oldDir =
|
|
17154
|
-
if (
|
|
17162
|
+
const newDir = join26(workspace, ".slopbrick");
|
|
17163
|
+
const oldDir = join26(workspace, ".slop-audit");
|
|
17164
|
+
if (existsSync25(newDir) && existsSync25(oldDir) && !force) {
|
|
17155
17165
|
return {
|
|
17156
17166
|
ok: false,
|
|
17157
17167
|
alreadyMigrated: false,
|
|
@@ -17230,8 +17240,8 @@ init_types();
|
|
|
17230
17240
|
init_config();
|
|
17231
17241
|
|
|
17232
17242
|
// src/cli/program.ts
|
|
17233
|
-
import { existsSync as
|
|
17234
|
-
import { resolve as resolve14, join as
|
|
17243
|
+
import { existsSync as existsSync26, writeFileSync as writeFileSync19, readFileSync as readFileSync38, mkdirSync as mkdirSync14 } from "fs";
|
|
17244
|
+
import { resolve as resolve14, join as join27, dirname as dirname16, extname as extname8 } from "path";
|
|
17235
17245
|
import { performance } from "perf_hooks";
|
|
17236
17246
|
import { Command } from "commander";
|
|
17237
17247
|
|
|
@@ -17276,7 +17286,7 @@ init_scan();
|
|
|
17276
17286
|
// src/cli/drift.ts
|
|
17277
17287
|
init_discover();
|
|
17278
17288
|
init_patterns();
|
|
17279
|
-
import { readFileSync as
|
|
17289
|
+
import { readFileSync as readFileSync23 } from "fs";
|
|
17280
17290
|
import { basename as basename4, relative as relative11 } from "path";
|
|
17281
17291
|
async function runDrift(cwd, config, options = {}) {
|
|
17282
17292
|
const maxFiles = options.maxFiles ?? 1e3;
|
|
@@ -17288,7 +17298,7 @@ async function runDrift(cwd, config, options = {}) {
|
|
|
17288
17298
|
for (const absPath of limited) {
|
|
17289
17299
|
let source;
|
|
17290
17300
|
try {
|
|
17291
|
-
source =
|
|
17301
|
+
source = readFileSync23(absPath, "utf-8");
|
|
17292
17302
|
} catch {
|
|
17293
17303
|
continue;
|
|
17294
17304
|
}
|
|
@@ -17483,7 +17493,7 @@ init_worker();
|
|
|
17483
17493
|
init_metrics();
|
|
17484
17494
|
init_patterns();
|
|
17485
17495
|
init_discover();
|
|
17486
|
-
import { readFileSync as
|
|
17496
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
17487
17497
|
import { extname as extname7, relative as relative12, resolve as resolve11 } from "path";
|
|
17488
17498
|
import { execFile as execFileCb } from "child_process";
|
|
17489
17499
|
import { promisify as promisify2 } from "util";
|
|
@@ -17556,7 +17566,7 @@ async function runPrScan(cwd, config, options = {}) {
|
|
|
17556
17566
|
for (const absPath of candidates) {
|
|
17557
17567
|
let source;
|
|
17558
17568
|
try {
|
|
17559
|
-
source =
|
|
17569
|
+
source = readFileSync24(absPath, "utf-8");
|
|
17560
17570
|
} catch {
|
|
17561
17571
|
continue;
|
|
17562
17572
|
}
|
|
@@ -18061,7 +18071,7 @@ function dbExitCode(result, options = {}) {
|
|
|
18061
18071
|
// src/cli/business-logic.ts
|
|
18062
18072
|
init_discover();
|
|
18063
18073
|
init_business_logic();
|
|
18064
|
-
import { readFileSync as
|
|
18074
|
+
import { readFileSync as readFileSync25 } from "fs";
|
|
18065
18075
|
import { relative as relative13 } from "path";
|
|
18066
18076
|
async function runBusinessLogicScan(cwd, config, options = {}) {
|
|
18067
18077
|
const maxFiles = options.maxFiles ?? 500;
|
|
@@ -18072,7 +18082,7 @@ async function runBusinessLogicScan(cwd, config, options = {}) {
|
|
|
18072
18082
|
for (const absPath of limited) {
|
|
18073
18083
|
let source;
|
|
18074
18084
|
try {
|
|
18075
|
-
source =
|
|
18085
|
+
source = readFileSync25(absPath, "utf-8");
|
|
18076
18086
|
} catch {
|
|
18077
18087
|
continue;
|
|
18078
18088
|
}
|
|
@@ -18177,7 +18187,7 @@ function capitalize(s) {
|
|
|
18177
18187
|
// src/engine/patterns.ts
|
|
18178
18188
|
init_patterns();
|
|
18179
18189
|
init_discover();
|
|
18180
|
-
import { readFileSync as
|
|
18190
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
18181
18191
|
import { basename as basename5, relative as relative14 } from "path";
|
|
18182
18192
|
var PATTERN_CATEGORIES = [
|
|
18183
18193
|
"modal",
|
|
@@ -18280,7 +18290,7 @@ function detectFormsFromFiles(files) {
|
|
|
18280
18290
|
for (const f of files) {
|
|
18281
18291
|
let source;
|
|
18282
18292
|
try {
|
|
18283
|
-
source =
|
|
18293
|
+
source = readFileSync26(f, "utf-8");
|
|
18284
18294
|
} catch {
|
|
18285
18295
|
continue;
|
|
18286
18296
|
}
|
|
@@ -18502,13 +18512,13 @@ init_discover();
|
|
|
18502
18512
|
init_git();
|
|
18503
18513
|
init_cache();
|
|
18504
18514
|
init_logger();
|
|
18505
|
-
import { writeFileSync as
|
|
18506
|
-
import { join as
|
|
18515
|
+
import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync9, rmSync as rmSync2 } from "fs";
|
|
18516
|
+
import { join as join18, dirname as dirname12 } from "path";
|
|
18507
18517
|
import { createInterface } from "readline";
|
|
18508
18518
|
|
|
18509
18519
|
// src/rules/registry-loader.ts
|
|
18510
|
-
import { existsSync as
|
|
18511
|
-
import { dirname as
|
|
18520
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync8, readFileSync as readFileSync27, writeFileSync as writeFileSync9 } from "fs";
|
|
18521
|
+
import { dirname as dirname11, join as join17 } from "path";
|
|
18512
18522
|
|
|
18513
18523
|
// src/data/shadcn-registry.json
|
|
18514
18524
|
var shadcn_registry_default = {
|
|
@@ -18641,13 +18651,13 @@ var shadcn_registry_default = {
|
|
|
18641
18651
|
// src/rules/registry-loader.ts
|
|
18642
18652
|
var REGISTRY_URL = "https://ui.shadcn.com/registry.json";
|
|
18643
18653
|
var BUNDLED_REGISTRY_VERSION = shadcn_registry_default.version;
|
|
18644
|
-
function
|
|
18645
|
-
return
|
|
18654
|
+
function cachePath3(cwd) {
|
|
18655
|
+
return join17(cwd, ".slopbrick", "cache", "registry-snapshot.json");
|
|
18646
18656
|
}
|
|
18647
18657
|
function ensureCacheDir(cwd) {
|
|
18648
|
-
const dir =
|
|
18649
|
-
if (!
|
|
18650
|
-
|
|
18658
|
+
const dir = dirname11(cachePath3(cwd));
|
|
18659
|
+
if (!existsSync18(dir)) {
|
|
18660
|
+
mkdirSync8(dir, { recursive: true });
|
|
18651
18661
|
}
|
|
18652
18662
|
}
|
|
18653
18663
|
function isValidSnapshot(value) {
|
|
@@ -18658,10 +18668,10 @@ function isValidSnapshot(value) {
|
|
|
18658
18668
|
return true;
|
|
18659
18669
|
}
|
|
18660
18670
|
function isRegistryFresh(cwd) {
|
|
18661
|
-
const cached =
|
|
18662
|
-
if (!
|
|
18671
|
+
const cached = cachePath3(cwd);
|
|
18672
|
+
if (!existsSync18(cached)) return false;
|
|
18663
18673
|
try {
|
|
18664
|
-
const parsed = JSON.parse(
|
|
18674
|
+
const parsed = JSON.parse(readFileSync27(cached, "utf8"));
|
|
18665
18675
|
if (!isValidSnapshot(parsed)) return false;
|
|
18666
18676
|
return parsed.version === BUNDLED_REGISTRY_VERSION;
|
|
18667
18677
|
} catch {
|
|
@@ -18695,7 +18705,7 @@ async function refreshRegistrySnapshot(cwd, url = REGISTRY_URL, timeoutMs = 5e3)
|
|
|
18695
18705
|
};
|
|
18696
18706
|
}
|
|
18697
18707
|
ensureCacheDir(cwd);
|
|
18698
|
-
|
|
18708
|
+
writeFileSync9(cachePath3(cwd), JSON.stringify(fetched, null, 2));
|
|
18699
18709
|
const fresh = fetched.version === BUNDLED_REGISTRY_VERSION;
|
|
18700
18710
|
return {
|
|
18701
18711
|
ok: true,
|
|
@@ -18705,7 +18715,7 @@ async function refreshRegistrySnapshot(cwd, url = REGISTRY_URL, timeoutMs = 5e3)
|
|
|
18705
18715
|
}
|
|
18706
18716
|
function copyBundledSnapshotToCache(cwd) {
|
|
18707
18717
|
ensureCacheDir(cwd);
|
|
18708
|
-
|
|
18718
|
+
writeFileSync9(cachePath3(cwd), JSON.stringify(shadcn_registry_default, null, 2));
|
|
18709
18719
|
}
|
|
18710
18720
|
|
|
18711
18721
|
// src/cli/init.ts
|
|
@@ -18858,9 +18868,9 @@ async function runDoctor(cwd) {
|
|
|
18858
18868
|
}
|
|
18859
18869
|
try {
|
|
18860
18870
|
const { parseFile: tryParse } = await Promise.resolve().then(() => (init_parser(), parser_exports));
|
|
18861
|
-
const testFile =
|
|
18862
|
-
|
|
18863
|
-
|
|
18871
|
+
const testFile = join18(cwd, ".slopbrick", ".doctor-test.ts");
|
|
18872
|
+
mkdirSync9(dirname12(testFile), { recursive: true });
|
|
18873
|
+
writeFileSync10(testFile, "export const x = 1;\n");
|
|
18864
18874
|
await tryParse(testFile);
|
|
18865
18875
|
rmSync2(testFile, { force: true });
|
|
18866
18876
|
ok("Parser is working.");
|
|
@@ -18919,12 +18929,12 @@ init_git();
|
|
|
18919
18929
|
// src/cli/installer.ts
|
|
18920
18930
|
import {
|
|
18921
18931
|
chmodSync,
|
|
18922
|
-
existsSync as
|
|
18923
|
-
mkdirSync as
|
|
18924
|
-
readFileSync as
|
|
18925
|
-
writeFileSync as
|
|
18932
|
+
existsSync as existsSync20,
|
|
18933
|
+
mkdirSync as mkdirSync10,
|
|
18934
|
+
readFileSync as readFileSync29,
|
|
18935
|
+
writeFileSync as writeFileSync11
|
|
18926
18936
|
} from "fs";
|
|
18927
|
-
import { dirname as
|
|
18937
|
+
import { dirname as dirname13, join as join19 } from "path";
|
|
18928
18938
|
var BEGIN_SENTINEL = "# slopbrick-hook-begin";
|
|
18929
18939
|
var END_SENTINEL = "# slopbrick-hook-end";
|
|
18930
18940
|
var SENTINEL_BLOCK = `${BEGIN_SENTINEL}
|
|
@@ -18932,14 +18942,14 @@ npx slopbrick --staged
|
|
|
18932
18942
|
${END_SENTINEL}
|
|
18933
18943
|
`;
|
|
18934
18944
|
function hookPath(gitRoot) {
|
|
18935
|
-
const huskyDir =
|
|
18936
|
-
if (
|
|
18937
|
-
return
|
|
18945
|
+
const huskyDir = join19(gitRoot, ".husky");
|
|
18946
|
+
if (existsSync20(huskyDir)) {
|
|
18947
|
+
return join19(huskyDir, "pre-commit");
|
|
18938
18948
|
}
|
|
18939
|
-
return
|
|
18949
|
+
return join19(gitRoot, ".git", "hooks", "pre-commit");
|
|
18940
18950
|
}
|
|
18941
18951
|
function readHookContent(path) {
|
|
18942
|
-
return
|
|
18952
|
+
return readFileSync29(path, "utf8");
|
|
18943
18953
|
}
|
|
18944
18954
|
function sentinelsPresent(content) {
|
|
18945
18955
|
const lines = content.split(/\r?\n/);
|
|
@@ -18961,7 +18971,7 @@ function replaceSentinelBlock(content) {
|
|
|
18961
18971
|
}
|
|
18962
18972
|
function installHook(gitRoot) {
|
|
18963
18973
|
const path = hookPath(gitRoot);
|
|
18964
|
-
if (
|
|
18974
|
+
if (existsSync20(path)) {
|
|
18965
18975
|
const content = readHookContent(path);
|
|
18966
18976
|
const { begin, end } = sentinelsPresent(content);
|
|
18967
18977
|
if (begin && end) {
|
|
@@ -18973,7 +18983,7 @@ function installHook(gitRoot) {
|
|
|
18973
18983
|
exitCode: 0
|
|
18974
18984
|
};
|
|
18975
18985
|
}
|
|
18976
|
-
|
|
18986
|
+
writeFileSync11(path, replaced.endsWith("\n") ? replaced : `${replaced}
|
|
18977
18987
|
`);
|
|
18978
18988
|
chmodSync(path, 493);
|
|
18979
18989
|
return {
|
|
@@ -18993,7 +19003,7 @@ function installHook(gitRoot) {
|
|
|
18993
19003
|
}
|
|
18994
19004
|
const normalized = content.length > 0 && !content.endsWith("\n") ? `${content}
|
|
18995
19005
|
` : content;
|
|
18996
|
-
|
|
19006
|
+
writeFileSync11(path, `${normalized}${SENTINEL_BLOCK}`);
|
|
18997
19007
|
chmodSync(path, 493);
|
|
18998
19008
|
return {
|
|
18999
19009
|
ok: true,
|
|
@@ -19001,8 +19011,8 @@ function installHook(gitRoot) {
|
|
|
19001
19011
|
exitCode: 0
|
|
19002
19012
|
};
|
|
19003
19013
|
}
|
|
19004
|
-
|
|
19005
|
-
|
|
19014
|
+
mkdirSync10(dirname13(path), { recursive: true });
|
|
19015
|
+
writeFileSync11(path, SENTINEL_BLOCK, { mode: 493 });
|
|
19006
19016
|
chmodSync(path, 493);
|
|
19007
19017
|
return {
|
|
19008
19018
|
ok: true,
|
|
@@ -19012,7 +19022,7 @@ function installHook(gitRoot) {
|
|
|
19012
19022
|
}
|
|
19013
19023
|
function uninstallHook(gitRoot) {
|
|
19014
19024
|
const path = hookPath(gitRoot);
|
|
19015
|
-
if (!
|
|
19025
|
+
if (!existsSync20(path)) {
|
|
19016
19026
|
return {
|
|
19017
19027
|
ok: true,
|
|
19018
19028
|
message: "Hook not installed",
|
|
@@ -19048,7 +19058,7 @@ function uninstallHook(gitRoot) {
|
|
|
19048
19058
|
}
|
|
19049
19059
|
const remaining = [...lines.slice(0, start), ...lines.slice(end + 1)];
|
|
19050
19060
|
const result = remaining.join("\n");
|
|
19051
|
-
|
|
19061
|
+
writeFileSync11(path, result.endsWith("\n") ? result : `${result}
|
|
19052
19062
|
`);
|
|
19053
19063
|
return {
|
|
19054
19064
|
ok: true,
|
|
@@ -19114,8 +19124,8 @@ function createProvider(config) {
|
|
|
19114
19124
|
}
|
|
19115
19125
|
|
|
19116
19126
|
// src/research/generator.ts
|
|
19117
|
-
import { mkdirSync as
|
|
19118
|
-
import { join as
|
|
19127
|
+
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync12 } from "fs";
|
|
19128
|
+
import { join as join20 } from "path";
|
|
19119
19129
|
|
|
19120
19130
|
// src/research/prompts.ts
|
|
19121
19131
|
var DEFAULT_PROMPT_TEMPLATES = [
|
|
@@ -19178,14 +19188,14 @@ async function generateSamples(options) {
|
|
|
19178
19188
|
}
|
|
19179
19189
|
const samples = [];
|
|
19180
19190
|
const ext = extForFramework(framework);
|
|
19181
|
-
const dir =
|
|
19182
|
-
|
|
19191
|
+
const dir = join20(outputDir, framework, componentType);
|
|
19192
|
+
mkdirSync11(dir, { recursive: true });
|
|
19183
19193
|
for (let i = 1; i <= count; i += 1) {
|
|
19184
19194
|
const raw = await provider.generateSample(renderPrompt(template), { temperature });
|
|
19185
19195
|
const code = extractCodeFromMarkdown(raw);
|
|
19186
19196
|
const fileName = `sample-${i}${ext}`;
|
|
19187
|
-
const filePath =
|
|
19188
|
-
|
|
19197
|
+
const filePath = join20(dir, fileName);
|
|
19198
|
+
writeFileSync12(filePath, code, "utf8");
|
|
19189
19199
|
const sample = {
|
|
19190
19200
|
filePath,
|
|
19191
19201
|
framework,
|
|
@@ -19196,8 +19206,8 @@ async function generateSamples(options) {
|
|
|
19196
19206
|
};
|
|
19197
19207
|
samples.push(sample);
|
|
19198
19208
|
}
|
|
19199
|
-
const metadataPath =
|
|
19200
|
-
|
|
19209
|
+
const metadataPath = join20(dir, "metadata.json");
|
|
19210
|
+
writeFileSync12(metadataPath, JSON.stringify(samples, null, 2), "utf8");
|
|
19201
19211
|
return samples;
|
|
19202
19212
|
}
|
|
19203
19213
|
|
|
@@ -19403,37 +19413,37 @@ function slugify(value) {
|
|
|
19403
19413
|
|
|
19404
19414
|
// src/research/calibrator.ts
|
|
19405
19415
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
19406
|
-
import { existsSync as
|
|
19407
|
-
import { join as
|
|
19416
|
+
import { existsSync as existsSync21, readFileSync as readFileSync30, writeFileSync as writeFileSync13, mkdirSync as mkdirSync12 } from "fs";
|
|
19417
|
+
import { join as join21, resolve as resolve12 } from "path";
|
|
19408
19418
|
var DEFAULT_POSITIVE = "/Users/cheng/ai-slop-baseline/extracted/positive";
|
|
19409
19419
|
var DEFAULT_NEGATIVE = "/Users/cheng/ai-slop-baseline/extracted/negative";
|
|
19410
19420
|
function buildFileList(dir, extensions) {
|
|
19411
|
-
const tmpList =
|
|
19421
|
+
const tmpList = join21("/tmp", `cal-build-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`);
|
|
19412
19422
|
const expr = extensions.map((e) => `-name '*.${e}'`).join(" -o ");
|
|
19413
19423
|
execFileSync2("bash", ["-c", `find ${dir} -maxdepth 8 -type f \\( ${expr} \\) -print0 | xargs -0 realpath > ${tmpList}`]);
|
|
19414
|
-
const out =
|
|
19424
|
+
const out = readFileSync30(tmpList, "utf8");
|
|
19415
19425
|
execFileSync2("rm", ["-f", tmpList]);
|
|
19416
19426
|
return out.trim().split("\n").filter(Boolean);
|
|
19417
19427
|
}
|
|
19418
19428
|
function runScan2(fileListPath) {
|
|
19419
|
-
const files =
|
|
19429
|
+
const files = readFileSync30(fileListPath, "utf8").trim().split("\n").filter(Boolean);
|
|
19420
19430
|
const CHUNK = 600;
|
|
19421
19431
|
const ruleFires = /* @__PURE__ */ new Map();
|
|
19422
19432
|
const uniqueFilesPerRule = /* @__PURE__ */ new Map();
|
|
19423
19433
|
let fileCount = 0;
|
|
19424
|
-
const tmpOut =
|
|
19434
|
+
const tmpOut = join21("/tmp", `calibrate-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
|
|
19425
19435
|
for (let i = 0; i < files.length; i += CHUNK) {
|
|
19426
19436
|
const chunk = files.slice(i, i + CHUNK);
|
|
19427
19437
|
try {
|
|
19428
19438
|
execFileSync2(
|
|
19429
19439
|
"node",
|
|
19430
|
-
[
|
|
19440
|
+
[join21(process.cwd(), "bin", "slopbrick.js"), "scan", ...chunk, "--json", tmpOut, "--no-telemetry", "--quiet"],
|
|
19431
19441
|
{ encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
|
|
19432
19442
|
);
|
|
19433
19443
|
} catch {
|
|
19434
19444
|
}
|
|
19435
|
-
if (!
|
|
19436
|
-
const report = JSON.parse(
|
|
19445
|
+
if (!existsSync21(tmpOut)) continue;
|
|
19446
|
+
const report = JSON.parse(readFileSync30(tmpOut, "utf8"));
|
|
19437
19447
|
fileCount += report.fileCount;
|
|
19438
19448
|
for (const issue of report.issues) {
|
|
19439
19449
|
ruleFires.set(issue.ruleId, (ruleFires.get(issue.ruleId) ?? 0) + 1);
|
|
@@ -19461,16 +19471,16 @@ function classify(posFiles, negFiles) {
|
|
|
19461
19471
|
async function calibrate(cwd, options = {}) {
|
|
19462
19472
|
const positiveDir = options.positiveDir ?? DEFAULT_POSITIVE;
|
|
19463
19473
|
const negativeDir = options.negativeDir ?? DEFAULT_NEGATIVE;
|
|
19464
|
-
if (!
|
|
19465
|
-
if (!
|
|
19474
|
+
if (!existsSync21(positiveDir)) throw new Error(`Positive corpus not found: ${positiveDir}`);
|
|
19475
|
+
if (!existsSync21(negativeDir)) throw new Error(`Negative corpus not found: ${negativeDir}`);
|
|
19466
19476
|
const positiveFiles = buildFileList(positiveDir, ["tsx", "ts", "jsx", "js"]);
|
|
19467
19477
|
const negativeFiles = buildFileList(negativeDir, ["tsx", "ts"]);
|
|
19468
19478
|
const posSample = options.positiveLimit ? positiveFiles.slice(0, options.positiveLimit) : positiveFiles;
|
|
19469
19479
|
const negSample = options.negativeLimit ? negativeFiles.slice(0, options.negativeLimit) : negativeFiles;
|
|
19470
|
-
const posListPath =
|
|
19471
|
-
const negListPath =
|
|
19472
|
-
|
|
19473
|
-
|
|
19480
|
+
const posListPath = join21("/tmp", `cal-pos-${Date.now()}.txt`);
|
|
19481
|
+
const negListPath = join21("/tmp", `cal-neg-${Date.now()}.txt`);
|
|
19482
|
+
writeFileSync13(posListPath, posSample.join("\n"));
|
|
19483
|
+
writeFileSync13(negListPath, negSample.join("\n"));
|
|
19474
19484
|
const builtins = await Promise.resolve().then(() => (init_builtins(), builtins_exports));
|
|
19475
19485
|
const builtinRules2 = builtins.builtinRules ?? [];
|
|
19476
19486
|
const metaById = /* @__PURE__ */ new Map();
|
|
@@ -19879,7 +19889,7 @@ var RULE_HINTS = {
|
|
|
19879
19889
|
};
|
|
19880
19890
|
|
|
19881
19891
|
// src/snippet/targets.ts
|
|
19882
|
-
import { join as
|
|
19892
|
+
import { join as join24 } from "path";
|
|
19883
19893
|
|
|
19884
19894
|
// src/snippet/render.ts
|
|
19885
19895
|
function aiSpecificRules(rules) {
|
|
@@ -20110,7 +20120,7 @@ var SNIPPET_TARGETS = [
|
|
|
20110
20120
|
}
|
|
20111
20121
|
];
|
|
20112
20122
|
function resolveTargetPath(target) {
|
|
20113
|
-
return target.isFolder ?
|
|
20123
|
+
return target.isFolder ? join24(target.path, target.filename) : target.path;
|
|
20114
20124
|
}
|
|
20115
20125
|
function renderMatrix() {
|
|
20116
20126
|
const lines = [];
|
|
@@ -20251,8 +20261,8 @@ init_unified_diff();
|
|
|
20251
20261
|
init_heatmap();
|
|
20252
20262
|
|
|
20253
20263
|
// src/report/flywheel.ts
|
|
20254
|
-
import { existsSync as
|
|
20255
|
-
import { join as
|
|
20264
|
+
import { existsSync as existsSync23, readFileSync as readFileSync33 } from "fs";
|
|
20265
|
+
import { join as join25 } from "path";
|
|
20256
20266
|
function average(values) {
|
|
20257
20267
|
if (values.length === 0) return 0;
|
|
20258
20268
|
return values.reduce((a, b) => a + b, 0) / values.length;
|
|
@@ -20343,8 +20353,8 @@ init_telemetry();
|
|
|
20343
20353
|
init_memory();
|
|
20344
20354
|
|
|
20345
20355
|
// src/fix/focus-ring.ts
|
|
20346
|
-
import { existsSync as
|
|
20347
|
-
import { readFileSync as
|
|
20356
|
+
import { existsSync as existsSync24 } from "fs";
|
|
20357
|
+
import { readFileSync as readFileSync34, writeFileSync as writeFileSync15 } from "fs";
|
|
20348
20358
|
var ANCHOR_START = "/* @slopbrick:v1.0.0:fix:focus-ring */";
|
|
20349
20359
|
var CSS_BLOCK = `/* @slopbrick:v1.0.0:fix:focus-ring */
|
|
20350
20360
|
:focus-visible {
|
|
@@ -20353,15 +20363,15 @@ var CSS_BLOCK = `/* @slopbrick:v1.0.0:fix:focus-ring */
|
|
|
20353
20363
|
}
|
|
20354
20364
|
/* @slopbrick:v1.0.0:fix:focus-ring-end */`;
|
|
20355
20365
|
function applyFocusRingFix(targetFile) {
|
|
20356
|
-
if (!
|
|
20366
|
+
if (!existsSync24(targetFile)) {
|
|
20357
20367
|
return { applied: false, reason: "missing-global-css-target" };
|
|
20358
20368
|
}
|
|
20359
|
-
const content =
|
|
20369
|
+
const content = readFileSync34(targetFile, "utf-8");
|
|
20360
20370
|
if (content.includes(ANCHOR_START)) {
|
|
20361
20371
|
return { applied: false, reason: "already-present" };
|
|
20362
20372
|
}
|
|
20363
20373
|
const separator = content.endsWith("\n") ? "" : "\n";
|
|
20364
|
-
|
|
20374
|
+
writeFileSync15(targetFile, `${content}${separator}${CSS_BLOCK}
|
|
20365
20375
|
`);
|
|
20366
20376
|
return { applied: true };
|
|
20367
20377
|
}
|
|
@@ -20370,21 +20380,21 @@ function applyFocusRingFix(targetFile) {
|
|
|
20370
20380
|
init_layout_token();
|
|
20371
20381
|
|
|
20372
20382
|
// src/fix/use-client.ts
|
|
20373
|
-
import { readFileSync as
|
|
20383
|
+
import { readFileSync as readFileSync35, writeFileSync as writeFileSync16 } from "fs";
|
|
20374
20384
|
function applyUseClientFix(filePath) {
|
|
20375
|
-
const content =
|
|
20385
|
+
const content = readFileSync35(filePath, "utf-8");
|
|
20376
20386
|
const trimmed = content.trimStart();
|
|
20377
20387
|
if (trimmed.startsWith("'use client'") || trimmed.startsWith('"use client"')) {
|
|
20378
20388
|
return { applied: false, reason: "already-present" };
|
|
20379
20389
|
}
|
|
20380
|
-
|
|
20390
|
+
writeFileSync16(filePath, `"use client";
|
|
20381
20391
|
|
|
20382
20392
|
${content}`);
|
|
20383
20393
|
return { applied: true };
|
|
20384
20394
|
}
|
|
20385
20395
|
|
|
20386
20396
|
// src/fix/visual-codemod.ts
|
|
20387
|
-
import { readFileSync as
|
|
20397
|
+
import { readFileSync as readFileSync36, writeFileSync as writeFileSync17 } from "fs";
|
|
20388
20398
|
|
|
20389
20399
|
// src/fix/visual-codemods/tailwind.ts
|
|
20390
20400
|
var ARBITRARY_ESCAPE_RE = /\b(p|m|px|py|pt|pb|pl|pr|mx|my|mt|mb|ml|mr|gap|space-[xy]|w|h|min-w|max-w|min-h|max-h|text-\w+|leading-\w+|rounded|border)-?\[(-?\d+(?:\.\d+)?)(px|rem|em|%|vh|vw)?\]/g;
|
|
@@ -20610,7 +20620,7 @@ var ALL_CODEMODS = [
|
|
|
20610
20620
|
{ name: "aria-attr-typo", fn: applyAriaAttrTypoCodemod }
|
|
20611
20621
|
];
|
|
20612
20622
|
function applyVisualCodemods(filePath) {
|
|
20613
|
-
const original =
|
|
20623
|
+
const original = readFileSync36(filePath, "utf-8");
|
|
20614
20624
|
let content = original;
|
|
20615
20625
|
const reasons = [];
|
|
20616
20626
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -20626,7 +20636,7 @@ function applyVisualCodemods(filePath) {
|
|
|
20626
20636
|
}
|
|
20627
20637
|
}
|
|
20628
20638
|
if (content !== original) {
|
|
20629
|
-
|
|
20639
|
+
writeFileSync17(filePath, content);
|
|
20630
20640
|
}
|
|
20631
20641
|
return {
|
|
20632
20642
|
filePath,
|
|
@@ -20782,12 +20792,12 @@ async function runCli({ start }) {
|
|
|
20782
20792
|
process.exit(0);
|
|
20783
20793
|
}
|
|
20784
20794
|
const cwd = resolve14(options.workspace ?? process.cwd());
|
|
20785
|
-
const configPath =
|
|
20795
|
+
const configPath = join27(cwd, "slopbrick.config.mjs");
|
|
20786
20796
|
const detected = detectStack(cwd);
|
|
20787
20797
|
const fallbackConfig = { ...DEFAULT_CONFIG, ...detected };
|
|
20788
20798
|
const proposed = serializeConfig(fallbackConfig);
|
|
20789
|
-
if (
|
|
20790
|
-
const current =
|
|
20799
|
+
if (existsSync26(configPath) && !cmdOptions.yes) {
|
|
20800
|
+
const current = readFileSync38(configPath, "utf8");
|
|
20791
20801
|
logger.error(`A config file already exists at ${configPath}.`);
|
|
20792
20802
|
logger.error("To overwrite it with defaults, run `slopbrick init --yes`.");
|
|
20793
20803
|
logger.error("");
|
|
@@ -20808,7 +20818,7 @@ async function runCli({ start }) {
|
|
|
20808
20818
|
config = buildInitConfig(detected, answers);
|
|
20809
20819
|
usedWizard = true;
|
|
20810
20820
|
}
|
|
20811
|
-
|
|
20821
|
+
writeFileSync19(configPath, serializeConfig(config));
|
|
20812
20822
|
appendGitignore(cwd);
|
|
20813
20823
|
const refresh = await refreshRegistrySnapshot(cwd);
|
|
20814
20824
|
if (!refresh.ok) {
|
|
@@ -20838,21 +20848,21 @@ async function runCli({ start }) {
|
|
|
20838
20848
|
return Boolean(opts[t.flag]);
|
|
20839
20849
|
});
|
|
20840
20850
|
for (const target of targetsToWrite) {
|
|
20841
|
-
const snippetPath =
|
|
20842
|
-
|
|
20851
|
+
const snippetPath = join27(cwd, resolveTargetPath(target));
|
|
20852
|
+
mkdirSync14(dirname16(snippetPath), { recursive: true });
|
|
20843
20853
|
const generated = target.generator(builtinRules);
|
|
20844
|
-
if (!target.isFolder &&
|
|
20845
|
-
const existing =
|
|
20854
|
+
if (!target.isFolder && existsSync26(snippetPath)) {
|
|
20855
|
+
const existing = readFileSync38(snippetPath, "utf8");
|
|
20846
20856
|
if (existing.includes("<!-- slopbrick:begin -->")) {
|
|
20847
20857
|
const updated = existing.replace(
|
|
20848
20858
|
/<!-- slopbrick:begin -->[\s\S]*?<!-- slopbrick:end -->/,
|
|
20849
20859
|
"<!-- slopbrick:begin -->\n" + generated + "<!-- slopbrick:end -->"
|
|
20850
20860
|
);
|
|
20851
|
-
|
|
20861
|
+
writeFileSync19(snippetPath, updated, "utf8");
|
|
20852
20862
|
if (!options.quiet) logger.info(`Updated ${snippetPath}`);
|
|
20853
20863
|
continue;
|
|
20854
20864
|
}
|
|
20855
|
-
|
|
20865
|
+
writeFileSync19(
|
|
20856
20866
|
snippetPath,
|
|
20857
20867
|
existing + (existing.endsWith("\n") ? "\n" : "\n\n") + generated,
|
|
20858
20868
|
"utf8"
|
|
@@ -20860,7 +20870,7 @@ async function runCli({ start }) {
|
|
|
20860
20870
|
if (!options.quiet) logger.info(`Wrote ${snippetPath}`);
|
|
20861
20871
|
continue;
|
|
20862
20872
|
}
|
|
20863
|
-
|
|
20873
|
+
writeFileSync19(snippetPath, generated, "utf8");
|
|
20864
20874
|
if (!options.quiet) logger.info(`Wrote ${snippetPath}`);
|
|
20865
20875
|
}
|
|
20866
20876
|
if (options.baseline) {
|
|
@@ -20929,8 +20939,8 @@ async function runCli({ start }) {
|
|
|
20929
20939
|
const summary = summarizeTelemetry(payloads);
|
|
20930
20940
|
if (cmdOptions.export) {
|
|
20931
20941
|
const exportPath = resolve14(cmdOptions.export);
|
|
20932
|
-
|
|
20933
|
-
|
|
20942
|
+
mkdirSync14(dirname16(exportPath), { recursive: true });
|
|
20943
|
+
writeFileSync19(exportPath, JSON.stringify(summary, null, 2), "utf-8");
|
|
20934
20944
|
logger.info(`Wrote flywheel summary to ${exportPath}`);
|
|
20935
20945
|
process.exit(0);
|
|
20936
20946
|
}
|
|
@@ -21088,16 +21098,16 @@ async function runCli({ start }) {
|
|
|
21088
21098
|
research.command("analyze").description("analyze generated samples and report coverage").requiredOption("--input-dir <path>", "directory with generated samples containing metadata.json").option("--output <path>", "analysis output path", ".slopbrick/flywheel/analysis.json").option("--config <path>", "slopbrick config path").option("--framework <name>", "framework multiplier to apply", "react").action(async (cmdOptions) => {
|
|
21089
21099
|
try {
|
|
21090
21100
|
const metadataPath = resolve14(cmdOptions.inputDir, "metadata.json");
|
|
21091
|
-
if (!
|
|
21101
|
+
if (!existsSync26(metadataPath)) {
|
|
21092
21102
|
logger.error(`No metadata.json found in ${cmdOptions.inputDir}`);
|
|
21093
21103
|
process.exit(2);
|
|
21094
21104
|
}
|
|
21095
|
-
const samples = JSON.parse(
|
|
21105
|
+
const samples = JSON.parse(readFileSync38(metadataPath, "utf8"));
|
|
21096
21106
|
const config = cmdOptions.config ? await loadConfig(cmdOptions.config) : { ...DEFAULT_CONFIG, framework: cmdOptions.framework };
|
|
21097
21107
|
const analysis = await analyzeSamples(samples, config);
|
|
21098
21108
|
const outputPath = resolve14(cmdOptions.output);
|
|
21099
|
-
|
|
21100
|
-
|
|
21109
|
+
mkdirSync14(dirname16(outputPath), { recursive: true });
|
|
21110
|
+
writeFileSync19(outputPath, JSON.stringify(analysis, null, 2), "utf8");
|
|
21101
21111
|
logger.info(`Analyzed ${analysis.summary.total} samples; coverage: ${analysis.summary.coverage}%`);
|
|
21102
21112
|
logger.info(`Wrote analysis to ${outputPath}`);
|
|
21103
21113
|
} catch (error) {
|
|
@@ -21108,11 +21118,11 @@ async function runCli({ start }) {
|
|
|
21108
21118
|
research.command("candidates").description("extract patterns from generated samples and emit candidate rules").requiredOption("--input-dir <path>", "directory with generated samples containing metadata.json").option("--output <path>", "output path", ".slopbrick/flywheel/rule-candidates.json").option("--config <path>", "slopbrick config path").option("--framework <name>", "framework multiplier to apply", "react").option("--min-frequency <n>", "minimum cluster frequency", parseCount, 2).option("--include-covered", "include samples already covered by AI-specific rules").action(async (cmdOptions) => {
|
|
21109
21119
|
try {
|
|
21110
21120
|
const metadataPath = resolve14(cmdOptions.inputDir, "metadata.json");
|
|
21111
|
-
if (!
|
|
21121
|
+
if (!existsSync26(metadataPath)) {
|
|
21112
21122
|
logger.error(`No metadata.json found in ${cmdOptions.inputDir}`);
|
|
21113
21123
|
process.exit(2);
|
|
21114
21124
|
}
|
|
21115
|
-
const samples = JSON.parse(
|
|
21125
|
+
const samples = JSON.parse(readFileSync38(metadataPath, "utf8"));
|
|
21116
21126
|
const config = cmdOptions.config ? await loadConfig(cmdOptions.config) : { ...DEFAULT_CONFIG, framework: cmdOptions.framework };
|
|
21117
21127
|
const analysis = await analyzeSamples(samples, config);
|
|
21118
21128
|
const extraction = extractAndCluster(analysis.samples, {
|
|
@@ -21123,7 +21133,7 @@ async function runCli({ start }) {
|
|
|
21123
21133
|
minFrequency: cmdOptions.minFrequency
|
|
21124
21134
|
});
|
|
21125
21135
|
const outputPath = resolve14(cmdOptions.output);
|
|
21126
|
-
|
|
21136
|
+
mkdirSync14(dirname16(outputPath), { recursive: true });
|
|
21127
21137
|
const payload = {
|
|
21128
21138
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21129
21139
|
sampleCount: analysis.summary.total,
|
|
@@ -21131,7 +21141,7 @@ async function runCli({ start }) {
|
|
|
21131
21141
|
fingerprintCount: extraction.total,
|
|
21132
21142
|
candidates
|
|
21133
21143
|
};
|
|
21134
|
-
|
|
21144
|
+
writeFileSync19(outputPath, JSON.stringify(payload, null, 2), "utf8");
|
|
21135
21145
|
logger.info(`Extracted ${extraction.total} fingerprints across ${analysis.summary.total} samples`);
|
|
21136
21146
|
logger.info(`Wrote ${candidates.length} candidate rule(s) to ${outputPath}`);
|
|
21137
21147
|
} catch (error) {
|
|
@@ -21149,8 +21159,8 @@ async function runCli({ start }) {
|
|
|
21149
21159
|
negativeLimit: cmdOptions.negativeLimit
|
|
21150
21160
|
});
|
|
21151
21161
|
const outputPath = cmdOptions.output ? resolve14(cwd, cmdOptions.output) : resolve14(cwd, "corpus", "calibration-empirical.md");
|
|
21152
|
-
|
|
21153
|
-
|
|
21162
|
+
mkdirSync14(dirname16(outputPath), { recursive: true });
|
|
21163
|
+
writeFileSync19(outputPath, reportToMarkdown(report), "utf8");
|
|
21154
21164
|
logger.info(
|
|
21155
21165
|
"Calibrated " + report.rules.length + " rules across " + report.positiveFileCount + " positive + " + report.negativeFileCount + " negative files."
|
|
21156
21166
|
);
|
|
@@ -21554,7 +21564,7 @@ async function runCli({ start }) {
|
|
|
21554
21564
|
});
|
|
21555
21565
|
program.command("validate-config [path]").description("Statically validate a slopbrick.config.mjs without scanning").action(async (configPath) => {
|
|
21556
21566
|
const path = configPath ? resolve14(configPath) : resolve14(process.cwd(), "slopbrick.config.mjs");
|
|
21557
|
-
if (!
|
|
21567
|
+
if (!existsSync26(path)) {
|
|
21558
21568
|
logger.error(`Error: config file not found: ${path}`);
|
|
21559
21569
|
process.exit(2);
|
|
21560
21570
|
}
|
|
@@ -21630,26 +21640,6 @@ ${formatMarkdown3(result.report)}`);
|
|
|
21630
21640
|
init_threshold();
|
|
21631
21641
|
init_render();
|
|
21632
21642
|
init_find_similar();
|
|
21633
|
-
import {
|
|
21634
|
-
MEMORY_SCHEMA_VERSION as MEMORY_SCHEMA_VERSION3,
|
|
21635
|
-
loadInventory as loadInventory3,
|
|
21636
|
-
saveInventory as saveInventory2,
|
|
21637
|
-
loadConstitution as loadConstitution3,
|
|
21638
|
-
saveConstitution as saveConstitution3,
|
|
21639
|
-
isInventoryFresh as isInventoryFresh3,
|
|
21640
|
-
invalidateFile as invalidateFile3,
|
|
21641
|
-
inventoryPath as inventoryPath3,
|
|
21642
|
-
constitutionPath as constitutionPath3,
|
|
21643
|
-
cachePath as cachePath5,
|
|
21644
|
-
INVENTORY_FILENAME,
|
|
21645
|
-
CONSTITUTION_FILENAME,
|
|
21646
|
-
CACHE_FILENAME,
|
|
21647
|
-
isMemoryPattern as isMemoryPattern3,
|
|
21648
|
-
isComponentFingerprint as isComponentFingerprint3,
|
|
21649
|
-
isInventoryFile as isInventoryFile3,
|
|
21650
|
-
isConstitutionFile as isConstitutionFile3,
|
|
21651
|
-
isFileMtimeEntry as isFileMtimeEntry3
|
|
21652
|
-
} from "@usebrick/core";
|
|
21653
21643
|
process.on("uncaughtException", (err) => {
|
|
21654
21644
|
const { logger: logger6 } = (init_logger(), __toCommonJS(logger_exports));
|
|
21655
21645
|
logger6.error(`Unexpected error: ${err.message}`);
|
|
@@ -21657,17 +21647,11 @@ process.on("uncaughtException", (err) => {
|
|
|
21657
21647
|
});
|
|
21658
21648
|
export {
|
|
21659
21649
|
AI_SECURITY_NUMERIC,
|
|
21660
|
-
CACHE_FILENAME,
|
|
21661
|
-
CONSTITUTION_FILENAME,
|
|
21662
21650
|
DEFAULT_CONFIG,
|
|
21663
|
-
INVENTORY_FILENAME,
|
|
21664
|
-
MEMORY_SCHEMA_VERSION3 as MEMORY_SCHEMA_VERSION,
|
|
21665
21651
|
REPOSITORY_HEALTH_WEIGHTS,
|
|
21666
21652
|
VERSION,
|
|
21667
21653
|
baselineStatusMessage,
|
|
21668
|
-
cachePath5 as cachePath,
|
|
21669
21654
|
colorForSlop,
|
|
21670
|
-
constitutionPath3 as constitutionPath,
|
|
21671
21655
|
extractSignatures,
|
|
21672
21656
|
failedThresholdCount,
|
|
21673
21657
|
filterByDisabledDirectives,
|
|
@@ -21677,22 +21661,10 @@ export {
|
|
|
21677
21661
|
formatBadge,
|
|
21678
21662
|
formatReportFromFile,
|
|
21679
21663
|
formatSparkline,
|
|
21680
|
-
invalidateFile3 as invalidateFile,
|
|
21681
|
-
inventoryPath3 as inventoryPath,
|
|
21682
|
-
isComponentFingerprint3 as isComponentFingerprint,
|
|
21683
|
-
isConstitutionFile3 as isConstitutionFile,
|
|
21684
|
-
isFileMtimeEntry3 as isFileMtimeEntry,
|
|
21685
|
-
isInventoryFile3 as isInventoryFile,
|
|
21686
|
-
isInventoryFresh3 as isInventoryFresh,
|
|
21687
|
-
isMemoryPattern3 as isMemoryPattern,
|
|
21688
21664
|
loadConfig,
|
|
21689
|
-
loadConstitution3 as loadConstitution,
|
|
21690
|
-
loadInventory3 as loadInventory,
|
|
21691
21665
|
readReportFile,
|
|
21692
21666
|
runCli,
|
|
21693
21667
|
runInitWizard,
|
|
21694
|
-
saveConstitution3 as saveConstitution,
|
|
21695
|
-
saveInventory2 as saveInventory,
|
|
21696
21668
|
scanProject,
|
|
21697
21669
|
serializeConfig,
|
|
21698
21670
|
signatureSimilarity,
|