slopbrick 0.18.2 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -35275,10 +35275,10 @@ function parseSvelte(source) {
35275
35275
  const ast = parseScriptContent(script.content, isTypeScriptScript(script.openTag));
35276
35276
  return { ast, source };
35277
35277
  }
35278
- function cacheEnabled() {
35278
+ function legacyCacheEnabled() {
35279
35279
  return process.env.SLOP_AUDIT_CACHE === "1" || process.env.SLOP_AUDIT_CACHE === "true";
35280
35280
  }
35281
- function cacheRoot() {
35281
+ function legacyCacheRoot() {
35282
35282
  const override = process.env.SLOP_AUDIT_CACHE_ROOT;
35283
35283
  if (override) return override;
35284
35284
  return join10(process.cwd(), ".slopbrick", "cache", "ast");
@@ -35286,11 +35286,11 @@ function cacheRoot() {
35286
35286
  function hashContent(content) {
35287
35287
  return createHash("md5").update(content, "utf-8").digest("hex");
35288
35288
  }
35289
- function cachePath2(content) {
35290
- return join10(cacheRoot(), `${hashContent(content)}.json`);
35289
+ function cachePathWithRoot(content, root) {
35290
+ return join10(root, `${hashContent(content)}.json`);
35291
35291
  }
35292
- async function readCache2(filePath, content) {
35293
- const path = cachePath2(content);
35292
+ async function readCacheWithRoot(filePath, content, root) {
35293
+ const path = cachePathWithRoot(content, root);
35294
35294
  try {
35295
35295
  await access(path);
35296
35296
  const raw = await readFile(path, "utf8");
@@ -35303,8 +35303,8 @@ async function readCache2(filePath, content) {
35303
35303
  }
35304
35304
  return void 0;
35305
35305
  }
35306
- async function writeCache(content, result) {
35307
- const path = cachePath2(content);
35306
+ async function writeCacheWithRoot(content, result, root) {
35307
+ const path = cachePathWithRoot(content, root);
35308
35308
  await mkdir(dirname6(path), { recursive: true });
35309
35309
  await writeFile(path, JSON.stringify(result), "utf8");
35310
35310
  }
@@ -35335,15 +35335,17 @@ function parseSource(source, filePath) {
35335
35335
  return parseWithSwc(source, filePath);
35336
35336
  }
35337
35337
  }
35338
- async function parseFile(filePath) {
35338
+ async function parseFile(filePath, opts) {
35339
35339
  const source = await readFile(filePath, "utf-8");
35340
- if (cacheEnabled()) {
35341
- const cached = await readCache2(filePath, source);
35340
+ const useCache = opts?.cache?.enabled ?? legacyCacheEnabled();
35341
+ const cacheDir = opts?.cache?.root ?? legacyCacheRoot();
35342
+ if (useCache) {
35343
+ const cached = await readCacheWithRoot(filePath, source, cacheDir);
35342
35344
  if (cached) return cached;
35343
35345
  }
35344
35346
  const result = parseSource(source, filePath);
35345
- if (cacheEnabled()) {
35346
- await writeCache(source, result);
35347
+ if (useCache) {
35348
+ await writeCacheWithRoot(source, result, cacheDir);
35347
35349
  }
35348
35350
  return result;
35349
35351
  }
@@ -45773,8 +45775,8 @@ var init_git = __esm({
45773
45775
  import { createHash as createHash5 } from "crypto";
45774
45776
  import { existsSync as existsSync12, readFileSync as readFileSync13, writeFileSync as writeFileSync3, renameSync as renameSync2, mkdirSync as mkdirSync2, unlinkSync } from "fs";
45775
45777
  import { dirname as dirname8, isAbsolute, resolve as resolve8 } from "path";
45776
- function loadCache(cachePath4) {
45777
- const abs = isAbsolute(cachePath4) ? cachePath4 : resolve8(process.cwd(), cachePath4);
45778
+ function loadCache(cachePath3) {
45779
+ const abs = isAbsolute(cachePath3) ? cachePath3 : resolve8(process.cwd(), cachePath3);
45778
45780
  if (!existsSync12(abs)) return void 0;
45779
45781
  try {
45780
45782
  const raw = readFileSync13(abs, "utf-8");
@@ -45785,8 +45787,8 @@ function loadCache(cachePath4) {
45785
45787
  return void 0;
45786
45788
  }
45787
45789
  }
45788
- function saveCache(cachePath4, cache) {
45789
- const abs = isAbsolute(cachePath4) ? cachePath4 : resolve8(process.cwd(), cachePath4);
45790
+ function saveCache(cachePath3, cache) {
45791
+ const abs = isAbsolute(cachePath3) ? cachePath3 : resolve8(process.cwd(), cachePath3);
45790
45792
  mkdirSync2(dirname8(abs), { recursive: true });
45791
45793
  const tmp = abs + ".tmp";
45792
45794
  if (existsSync12(tmp)) {
@@ -47128,6 +47130,13 @@ var init_signal_strength2 = __esm({
47128
47130
  // src/engine/worker.ts
47129
47131
  import { isMainThread, parentPort, workerData } from "worker_threads";
47130
47132
  import { extname as extname6 } from "path";
47133
+ import { join as join13 } from "path";
47134
+ function buildParserCacheConfig(cwd) {
47135
+ const envVal = process.env.SLOP_AUDIT_CACHE;
47136
+ const enabled = envVal === "1" || envVal === "true";
47137
+ const root = process.env.SLOP_AUDIT_CACHE_ROOT ?? join13(cwd, ".slopbrick", "cache", "ast");
47138
+ return { enabled, root };
47139
+ }
47131
47140
  function applyRuleOverrides(issues, rules) {
47132
47141
  const result = [];
47133
47142
  for (const issue of issues) {
@@ -47142,6 +47151,7 @@ function applyRuleOverrides(issues, rules) {
47142
47151
  return result;
47143
47152
  }
47144
47153
  async function scanFile(filePath, config, registry, cwd = process.cwd()) {
47154
+ const cache = buildParserCacheConfig(cwd);
47145
47155
  const ext = extname6(filePath).toLowerCase();
47146
47156
  const UNSUPPORTED_LANGS = /* @__PURE__ */ new Set([
47147
47157
  ".swift",
@@ -47172,7 +47182,7 @@ async function scanFile(filePath, config, registry, cwd = process.cwd()) {
47172
47182
  };
47173
47183
  }
47174
47184
  try {
47175
- const { ast, source } = await parseFile(filePath);
47185
+ const { ast, source } = await parseFile(filePath, { cache });
47176
47186
  const facts = extractFacts(filePath, ast, source, config.supportsRsc ?? true, config.framework ?? "react", config);
47177
47187
  const activeRegistry = registry ?? new RuleRegistry();
47178
47188
  if (!registry) {
@@ -47281,7 +47291,7 @@ var init_worker = __esm({
47281
47291
  // src/engine/flywheel.ts
47282
47292
  import { createHash as createHash6 } from "crypto";
47283
47293
  import { existsSync as existsSync14, mkdirSync as mkdirSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync4 } from "fs";
47284
- import { join as join13 } from "path";
47294
+ import { join as join14 } from "path";
47285
47295
  function severityBump(severity) {
47286
47296
  const order = ["low", "medium", "high"];
47287
47297
  const idx = order.indexOf(severity);
@@ -47382,7 +47392,7 @@ function migrateFlywheelState(state) {
47382
47392
  };
47383
47393
  }
47384
47394
  function loadFlywheelState(cwd) {
47385
- const path = join13(cwd, FLYWHEEL_DIR, STATE_FILE);
47395
+ const path = join14(cwd, FLYWHEEL_DIR, STATE_FILE);
47386
47396
  if (!existsSync14(path)) {
47387
47397
  return migrateFlywheelState({});
47388
47398
  }
@@ -47394,9 +47404,9 @@ function loadFlywheelState(cwd) {
47394
47404
  }
47395
47405
  }
47396
47406
  function loadResearchMetricsFromDisk(cwd) {
47397
- const flywheelDir = join13(cwd, FLYWHEEL_DIR);
47398
- const analysisPath = join13(flywheelDir, "analysis.json");
47399
- const candidatesPath = join13(flywheelDir, "rule-candidates.json");
47407
+ const flywheelDir = join14(cwd, FLYWHEEL_DIR);
47408
+ const analysisPath = join14(flywheelDir, "analysis.json");
47409
+ const candidatesPath = join14(flywheelDir, "rule-candidates.json");
47400
47410
  const hasAnalysis = existsSync14(analysisPath);
47401
47411
  const hasCandidates = existsSync14(candidatesPath);
47402
47412
  if (!hasAnalysis && !hasCandidates) return void 0;
@@ -47426,9 +47436,9 @@ function loadResearchMetricsFromDisk(cwd) {
47426
47436
  };
47427
47437
  }
47428
47438
  function saveFlywheelState(cwd, state) {
47429
- const dir = join13(cwd, FLYWHEEL_DIR);
47439
+ const dir = join14(cwd, FLYWHEEL_DIR);
47430
47440
  if (!existsSync14(dir)) mkdirSync3(dir, { recursive: true });
47431
- writeFileSync4(join13(dir, STATE_FILE), JSON.stringify(state, null, 2));
47441
+ writeFileSync4(join14(dir, STATE_FILE), JSON.stringify(state, null, 2));
47432
47442
  }
47433
47443
  function hashFile(filePath) {
47434
47444
  return createHash6("sha256").update(filePath).digest("hex").slice(0, 16);
@@ -47784,7 +47794,7 @@ var init_metrics = __esm({
47784
47794
  // src/engine/cache.ts
47785
47795
  import { createHash as createHash7 } from "crypto";
47786
47796
  import { existsSync as existsSync15, mkdirSync as mkdirSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync5 } from "fs";
47787
- import { join as join14 } from "path";
47797
+ import { join as join15 } from "path";
47788
47798
  function parseVersion(version) {
47789
47799
  const parts = version.split(".").map((part) => parseInt(part, 10));
47790
47800
  return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
@@ -47859,7 +47869,7 @@ function hashConfig(config) {
47859
47869
  return createHash7("sha256").update(JSON.stringify(sanitizeForHash(pickBaselineConfig(config)))).digest("hex");
47860
47870
  }
47861
47871
  function baselinePath(projectPath) {
47862
- return join14(projectPath, ".slopbrick", "cache", "baseline.json");
47872
+ return join15(projectPath, ".slopbrick", "cache", "baseline.json");
47863
47873
  }
47864
47874
  function isBaselineCache(value) {
47865
47875
  if (!value || typeof value !== "object") return false;
@@ -47902,7 +47912,7 @@ function loadBaseline(projectPath) {
47902
47912
  }
47903
47913
  function saveBaseline(projectPath, cache) {
47904
47914
  const path = baselinePath(projectPath);
47905
- mkdirSync4(join14(projectPath, ".slopbrick", "cache"), { recursive: true });
47915
+ mkdirSync4(join15(projectPath, ".slopbrick", "cache"), { recursive: true });
47906
47916
  writeFileSync5(path, JSON.stringify(cache, null, 2));
47907
47917
  }
47908
47918
  function tightenBaseline(cache) {
@@ -49816,9 +49826,9 @@ import {
49816
49826
  statSync as statSync6
49817
49827
  } from "fs";
49818
49828
  import { createHash as createHash8 } from "crypto";
49819
- import { dirname as dirname11, join as join16, relative as relative8 } from "path";
49829
+ import { dirname as dirname11, join as join17, relative as relative8 } from "path";
49820
49830
  function telemetryPath2(cwd) {
49821
- return join16(cwd, TELEMETRY_DIR, TELEMETRY_FILE2);
49831
+ return join17(cwd, TELEMETRY_DIR, TELEMETRY_FILE2);
49822
49832
  }
49823
49833
  function hashString(input) {
49824
49834
  return createHash8("sha256").update(input).digest("hex").slice(0, 16);
@@ -49880,21 +49890,21 @@ function rotateTelemetry(cwd) {
49880
49890
  }
49881
49891
  const dir = dirname11(path);
49882
49892
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
49883
- renameSync3(path, join16(dir, `scans-${timestamp}.jsonl`));
49884
- const rotated = readdirSync6(dir).filter(isTelemetryFile).map((name) => ({ name, mtime: statSync6(join16(dir, name)).mtimeMs })).sort((a, b) => a.mtime - b.mtime);
49893
+ renameSync3(path, join17(dir, `scans-${timestamp}.jsonl`));
49894
+ const rotated = readdirSync6(dir).filter(isTelemetryFile).map((name) => ({ name, mtime: statSync6(join17(dir, name)).mtimeMs })).sort((a, b) => a.mtime - b.mtime);
49885
49895
  while (rotated.length > MAX_ROTATED_FILES) {
49886
49896
  const oldest = rotated.shift();
49887
49897
  if (oldest) {
49888
- rmSync(join16(dir, oldest.name), { force: true });
49898
+ rmSync(join17(dir, oldest.name), { force: true });
49889
49899
  }
49890
49900
  }
49891
49901
  }
49892
49902
  function readTelemetry(cwd) {
49893
- const dir = join16(cwd, TELEMETRY_DIR);
49903
+ const dir = join17(cwd, TELEMETRY_DIR);
49894
49904
  if (!existsSync16(dir)) {
49895
49905
  return [];
49896
49906
  }
49897
- const files = readdirSync6(dir).filter(isTelemetryFile).sort().map((name) => join16(dir, name));
49907
+ const files = readdirSync6(dir).filter(isTelemetryFile).sort().map((name) => join17(dir, name));
49898
49908
  const payloads = [];
49899
49909
  for (const file of files) {
49900
49910
  const raw = readFileSync19(file, "utf-8");
@@ -49945,7 +49955,7 @@ var TELEMETRY_DIR, TELEMETRY_FILE2, MAX_TELEMETRY_BYTES, MAX_ROTATED_FILES;
49945
49955
  var init_telemetry = __esm({
49946
49956
  "src/engine/telemetry.ts"() {
49947
49957
  "use strict";
49948
- TELEMETRY_DIR = join16(".slopbrick", "flywheel");
49958
+ TELEMETRY_DIR = join17(".slopbrick", "flywheel");
49949
49959
  TELEMETRY_FILE2 = "scans.jsonl";
49950
49960
  MAX_TELEMETRY_BYTES = 10 * 1024 * 1024;
49951
49961
  MAX_ROTATED_FILES = 5;
@@ -49960,7 +49970,7 @@ __export(structure_md_exports, {
49960
49970
  writeStructureMarkdown: () => writeStructureMarkdown
49961
49971
  });
49962
49972
  import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync20, writeFileSync as writeFileSync6 } from "fs";
49963
- import { dirname as dirname12, join as join17 } from "path";
49973
+ import { dirname as dirname12, join as join18 } from "path";
49964
49974
  function formatDuration(ms) {
49965
49975
  if (!Number.isFinite(ms) || ms < 0) return "unknown";
49966
49976
  if (ms < 1e3) return `${Math.round(ms)}ms`;
@@ -50103,7 +50113,7 @@ function renderStructureMarkdown(inventory, constitution) {
50103
50113
  async function writeStructureMarkdown(workspaceDir, md) {
50104
50114
  await new Promise((resolve42, reject) => {
50105
50115
  try {
50106
- const path = join17(workspaceDir, STRUCTURE_MD_FILE);
50116
+ const path = join18(workspaceDir, STRUCTURE_MD_FILE);
50107
50117
  mkdirSync6(dirname12(path), { recursive: true });
50108
50118
  writeFileSync6(path, md, "utf-8");
50109
50119
  resolve42();
@@ -50115,7 +50125,7 @@ async function writeStructureMarkdown(workspaceDir, md) {
50115
50125
  async function readStructureMarkdown(workspaceDir) {
50116
50126
  return new Promise((resolve42) => {
50117
50127
  try {
50118
- const path = join17(workspaceDir, STRUCTURE_MD_FILE);
50128
+ const path = join18(workspaceDir, STRUCTURE_MD_FILE);
50119
50129
  if (!existsSync17(path)) {
50120
50130
  resolve42(null);
50121
50131
  return;
@@ -50131,7 +50141,7 @@ var STRUCTURE_MD_FILE, CATEGORY_LABELS, CATEGORY_ORDER, DECLARED_FIELDS;
50131
50141
  var init_structure_md = __esm({
50132
50142
  "src/engine/structure-md.ts"() {
50133
50143
  "use strict";
50134
- STRUCTURE_MD_FILE = join17(".slopbrick", "structure.md");
50144
+ STRUCTURE_MD_FILE = join18(".slopbrick", "structure.md");
50135
50145
  CATEGORY_LABELS = {
50136
50146
  stateManagement: "State management",
50137
50147
  dataFetching: "Data fetching",
@@ -50173,7 +50183,7 @@ var init_structure_md = __esm({
50173
50183
 
50174
50184
  // src/cli/report/persistRun.ts
50175
50185
  import { existsSync as existsSync18, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
50176
- import { join as join18, relative as relative9 } from "path";
50186
+ import { join as join19, relative as relative9 } from "path";
50177
50187
  async function persistRun(input) {
50178
50188
  const {
50179
50189
  cwd,
@@ -50207,8 +50217,8 @@ async function persistRun(input) {
50207
50217
  );
50208
50218
  }
50209
50219
  if (options.incremental) {
50210
- const cachePath4 = options.cachePath ?? ".slopbrick-cache.json";
50211
- const existing = loadCache(cachePath4) ?? emptyCache();
50220
+ const cachePath3 = options.cachePath ?? ".slopbrick-cache.json";
50221
+ const existing = loadCache(cachePath3) ?? emptyCache();
50212
50222
  const next = { ...existing, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
50213
50223
  for (const result of results) {
50214
50224
  try {
@@ -50222,7 +50232,7 @@ async function persistRun(input) {
50222
50232
  } catch {
50223
50233
  }
50224
50234
  }
50225
- saveCache(cachePath4, next);
50235
+ saveCache(cachePath3, next);
50226
50236
  if (incrementalSummary && !options.quiet) {
50227
50237
  logger.info(
50228
50238
  `Incremental: re-scanned ${incrementalSummary.rescanned}, skipped ${incrementalSummary.skipped} (unchanged).`
@@ -50254,10 +50264,10 @@ async function persistRun(input) {
50254
50264
  report.research = state.research;
50255
50265
  }
50256
50266
  if (flywheelOutput.suggestions.length > 0) {
50257
- const suggestionsDir = join18(cwd, ".slopbrick", "flywheel");
50267
+ const suggestionsDir = join19(cwd, ".slopbrick", "flywheel");
50258
50268
  if (!existsSync18(suggestionsDir)) mkdirSync7(suggestionsDir, { recursive: true });
50259
50269
  writeFileSync7(
50260
- join18(suggestionsDir, "rule-suggestions.json"),
50270
+ join19(suggestionsDir, "rule-suggestions.json"),
50261
50271
  JSON.stringify(flywheelOutput.suggestions, null, 2)
50262
50272
  );
50263
50273
  }
@@ -52467,8 +52477,8 @@ async function runScan(options, explicitPaths) {
52467
52477
  }
52468
52478
  let incrementalSummary;
52469
52479
  if (options.incremental) {
52470
- const cachePath4 = options.cachePath ?? ".slopbrick-cache.json";
52471
- const existing = loadCache(cachePath4);
52480
+ const cachePath3 = options.cachePath ?? ".slopbrick-cache.json";
52481
+ const existing = loadCache(cachePath3);
52472
52482
  const { toScan, unchanged } = partitionByCache(files, existing);
52473
52483
  files = toScan;
52474
52484
  incrementalSummary = { skipped: unchanged.length, rescanned: toScan.length };
@@ -53273,12 +53283,12 @@ init_git();
53273
53283
  init_cache();
53274
53284
  init_logger();
53275
53285
  import { writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, rmSync as rmSync2 } from "fs";
53276
- import { join as join20, dirname as dirname14 } from "path";
53286
+ import { join as join21, dirname as dirname14 } from "path";
53277
53287
  import { createInterface } from "readline";
53278
53288
 
53279
53289
  // src/rules/registry-loader.ts
53280
53290
  import { existsSync as existsSync21, mkdirSync as mkdirSync8, readFileSync as readFileSync24, writeFileSync as writeFileSync10 } from "fs";
53281
- import { dirname as dirname13, join as join19 } from "path";
53291
+ import { dirname as dirname13, join as join20 } from "path";
53282
53292
 
53283
53293
  // src/data/shadcn-registry.json
53284
53294
  var shadcn_registry_default = {
@@ -53411,11 +53421,11 @@ var shadcn_registry_default = {
53411
53421
  // src/rules/registry-loader.ts
53412
53422
  var REGISTRY_URL = "https://ui.shadcn.com/registry.json";
53413
53423
  var BUNDLED_REGISTRY_VERSION = shadcn_registry_default.version;
53414
- function cachePath3(cwd) {
53415
- return join19(cwd, ".slopbrick", "cache", "registry-snapshot.json");
53424
+ function cachePath2(cwd) {
53425
+ return join20(cwd, ".slopbrick", "cache", "registry-snapshot.json");
53416
53426
  }
53417
53427
  function ensureCacheDir(cwd) {
53418
- const dir = dirname13(cachePath3(cwd));
53428
+ const dir = dirname13(cachePath2(cwd));
53419
53429
  if (!existsSync21(dir)) {
53420
53430
  mkdirSync8(dir, { recursive: true });
53421
53431
  }
@@ -53428,7 +53438,7 @@ function isValidSnapshot(value) {
53428
53438
  return true;
53429
53439
  }
53430
53440
  function isRegistryFresh(cwd) {
53431
- const cached = cachePath3(cwd);
53441
+ const cached = cachePath2(cwd);
53432
53442
  if (!existsSync21(cached)) return false;
53433
53443
  try {
53434
53444
  const parsed = JSON.parse(readFileSync24(cached, "utf8"));
@@ -53465,7 +53475,7 @@ async function refreshRegistrySnapshot(cwd, url = REGISTRY_URL, timeoutMs = 5e3)
53465
53475
  };
53466
53476
  }
53467
53477
  ensureCacheDir(cwd);
53468
- writeFileSync10(cachePath3(cwd), JSON.stringify(fetched, null, 2));
53478
+ writeFileSync10(cachePath2(cwd), JSON.stringify(fetched, null, 2));
53469
53479
  const fresh = fetched.version === BUNDLED_REGISTRY_VERSION;
53470
53480
  return {
53471
53481
  ok: true,
@@ -53475,7 +53485,7 @@ async function refreshRegistrySnapshot(cwd, url = REGISTRY_URL, timeoutMs = 5e3)
53475
53485
  }
53476
53486
  function copyBundledSnapshotToCache(cwd) {
53477
53487
  ensureCacheDir(cwd);
53478
- writeFileSync10(cachePath3(cwd), JSON.stringify(shadcn_registry_default, null, 2));
53488
+ writeFileSync10(cachePath2(cwd), JSON.stringify(shadcn_registry_default, null, 2));
53479
53489
  }
53480
53490
 
53481
53491
  // src/cli/init.ts
@@ -53664,7 +53674,7 @@ async function runDoctor(cwd) {
53664
53674
  }
53665
53675
  try {
53666
53676
  const { parseFile: tryParse } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
53667
- const testFile = join20(cwd, ".slopbrick", ".doctor-test.ts");
53677
+ const testFile = join21(cwd, ".slopbrick", ".doctor-test.ts");
53668
53678
  mkdirSync9(dirname14(testFile), { recursive: true });
53669
53679
  writeFileSync11(testFile, "export const x = 1;\n");
53670
53680
  await tryParse(testFile);
@@ -53997,7 +54007,7 @@ import {
53997
54007
  readFileSync as readFileSync26,
53998
54008
  writeFileSync as writeFileSync12
53999
54009
  } from "fs";
54000
- import { dirname as dirname15, join as join21 } from "path";
54010
+ import { dirname as dirname15, join as join23 } from "path";
54001
54011
  var BEGIN_SENTINEL = "# slopbrick-hook-begin";
54002
54012
  var END_SENTINEL = "# slopbrick-hook-end";
54003
54013
  var SENTINEL_BLOCK = `${BEGIN_SENTINEL}
@@ -54005,11 +54015,11 @@ npx slopbrick --staged
54005
54015
  ${END_SENTINEL}
54006
54016
  `;
54007
54017
  function hookPath(gitRoot) {
54008
- const huskyDir = join21(gitRoot, ".husky");
54018
+ const huskyDir = join23(gitRoot, ".husky");
54009
54019
  if (existsSync23(huskyDir)) {
54010
- return join21(huskyDir, "pre-commit");
54020
+ return join23(huskyDir, "pre-commit");
54011
54021
  }
54012
- return join21(gitRoot, ".git", "hooks", "pre-commit");
54022
+ return join23(gitRoot, ".git", "hooks", "pre-commit");
54013
54023
  }
54014
54024
  function readHookContent(path) {
54015
54025
  return readFileSync26(path, "utf8");
@@ -54427,41 +54437,41 @@ import { resolve as resolve22 } from "path";
54427
54437
  // src/cli/migrate.ts
54428
54438
  init_logger();
54429
54439
  import { existsSync as existsSync24, readFileSync as readFileSync28, renameSync as renameSync4, writeFileSync as writeFileSync13 } from "fs";
54430
- import { join as join23 } from "path";
54440
+ import { join as join24 } from "path";
54431
54441
  function planMigration(workspaceDir) {
54432
54442
  const moves = [];
54433
54443
  const rewrites = [];
54434
54444
  const gitignoreEdits = [];
54435
- const oldDir = join23(workspaceDir, ".slop-audit");
54436
- const newDir = join23(workspaceDir, ".slopbrick");
54445
+ const oldDir = join24(workspaceDir, ".slop-audit");
54446
+ const newDir = join24(workspaceDir, ".slopbrick");
54437
54447
  if (existsSync24(oldDir)) {
54438
54448
  moves.push({ from: oldDir, to: newDir, kind: "dir" });
54439
54449
  rewrites.push({
54440
- path: join23(newDir, "inventory.json"),
54450
+ path: join24(newDir, "inventory.json"),
54441
54451
  field: "version",
54442
54452
  from: '"1"',
54443
54453
  to: '"2"'
54444
54454
  });
54445
54455
  rewrites.push({
54446
- path: join23(newDir, "constitution.json"),
54456
+ path: join24(newDir, "constitution.json"),
54447
54457
  field: "version",
54448
54458
  from: '"1"',
54449
54459
  to: '"2"'
54450
54460
  });
54451
54461
  }
54452
- const oldCache = join23(workspaceDir, ".slop-audit-cache.json");
54453
- const newCache = join23(workspaceDir, ".slopbrick-cache.json");
54462
+ const oldCache = join24(workspaceDir, ".slop-audit-cache.json");
54463
+ const newCache = join24(workspaceDir, ".slopbrick-cache.json");
54454
54464
  if (existsSync24(oldCache)) {
54455
54465
  moves.push({ from: oldCache, to: newCache, kind: "file" });
54456
54466
  }
54457
54467
  for (const ext of ["mjs", "cjs", "js"]) {
54458
- const oldCfg = join23(workspaceDir, `slop-audit.config.${ext}`);
54459
- const newCfg = join23(workspaceDir, `slopbrick.config.${ext}`);
54468
+ const oldCfg = join24(workspaceDir, `slop-audit.config.${ext}`);
54469
+ const newCfg = join24(workspaceDir, `slopbrick.config.${ext}`);
54460
54470
  if (existsSync24(oldCfg)) {
54461
54471
  moves.push({ from: oldCfg, to: newCfg, kind: "config" });
54462
54472
  }
54463
54473
  }
54464
- const gi = join23(workspaceDir, ".gitignore");
54474
+ const gi = join24(workspaceDir, ".gitignore");
54465
54475
  if (existsSync24(gi)) {
54466
54476
  const content = readFileSync28(gi, "utf-8");
54467
54477
  if (content.includes(".slop-audit/")) {
@@ -54482,7 +54492,7 @@ function planMigration(workspaceDir) {
54482
54492
  return { moves, rewrites, gitignoreEdits };
54483
54493
  }
54484
54494
  function isAlreadyMigrated(workspaceDir) {
54485
- return existsSync24(join23(workspaceDir, ".slopbrick")) && !existsSync24(join23(workspaceDir, ".slop-audit"));
54495
+ return existsSync24(join24(workspaceDir, ".slopbrick")) && !existsSync24(join24(workspaceDir, ".slop-audit"));
54486
54496
  }
54487
54497
  function applyMigration(plan, options = {}) {
54488
54498
  if (options.dryRun) return;
@@ -54513,8 +54523,8 @@ function runMigrate(options) {
54513
54523
  };
54514
54524
  }
54515
54525
  const alreadyMigrated = isAlreadyMigrated(workspace);
54516
- const newDir = join23(workspace, ".slopbrick");
54517
- const oldDir = join23(workspace, ".slop-audit");
54526
+ const newDir = join24(workspace, ".slopbrick");
54527
+ const oldDir = join24(workspace, ".slop-audit");
54518
54528
  if (existsSync24(newDir) && existsSync24(oldDir) && !force) {
54519
54529
  return {
54520
54530
  ok: false,
@@ -54985,7 +54995,7 @@ function createProvider(config) {
54985
54995
 
54986
54996
  // src/research/generator.ts
54987
54997
  import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync14 } from "fs";
54988
- import { join as join24 } from "path";
54998
+ import { join as join25 } from "path";
54989
54999
 
54990
55000
  // src/research/prompts.ts
54991
55001
  var DEFAULT_PROMPT_TEMPLATES = [
@@ -55048,13 +55058,13 @@ async function generateSamples(options) {
55048
55058
  }
55049
55059
  const samples = [];
55050
55060
  const ext = extForFramework(framework);
55051
- const dir = join24(outputDir, framework, componentType);
55061
+ const dir = join25(outputDir, framework, componentType);
55052
55062
  mkdirSync11(dir, { recursive: true });
55053
55063
  for (let i = 1; i <= count; i += 1) {
55054
55064
  const raw = await provider.generateSample(renderPrompt(template), { temperature });
55055
55065
  const code = extractCodeFromMarkdown(raw);
55056
55066
  const fileName = `sample-${i}${ext}`;
55057
- const filePath = join24(dir, fileName);
55067
+ const filePath = join25(dir, fileName);
55058
55068
  writeFileSync14(filePath, code, "utf8");
55059
55069
  const sample = {
55060
55070
  filePath,
@@ -55066,7 +55076,7 @@ async function generateSamples(options) {
55066
55076
  };
55067
55077
  samples.push(sample);
55068
55078
  }
55069
- const metadataPath = join24(dir, "metadata.json");
55079
+ const metadataPath = join25(dir, "metadata.json");
55070
55080
  writeFileSync14(metadataPath, JSON.stringify(samples, null, 2), "utf8");
55071
55081
  return samples;
55072
55082
  }
@@ -55274,20 +55284,20 @@ function slugify(value) {
55274
55284
  // src/research/calibrator.ts
55275
55285
  import { execFileSync as execFileSync2 } from "child_process";
55276
55286
  import { existsSync as existsSync26, readFileSync as readFileSync29, writeFileSync as writeFileSync15, mkdirSync as mkdirSync12 } from "fs";
55277
- import { join as join26, resolve as resolve24 } from "path";
55287
+ import { join as join27, resolve as resolve24 } from "path";
55278
55288
 
55279
55289
  // src/corpus-paths.ts
55280
- import { join as join25 } from "path";
55290
+ import { join as join26 } from "path";
55281
55291
  var CORPUS_ROOT = process.env["SLOPBRICK_CORPUS_DIR"] ?? "/Users/cheng/corpus-expansion";
55282
- var POSITIVE_DIR = join25(CORPUS_ROOT, "positive");
55283
- var NEGATIVE_DIR = join25(CORPUS_ROOT, "negative");
55284
- var FILELISTS_DIR = join25(CORPUS_ROOT, "filelists");
55292
+ var POSITIVE_DIR = join26(CORPUS_ROOT, "positive");
55293
+ var NEGATIVE_DIR = join26(CORPUS_ROOT, "negative");
55294
+ var FILELISTS_DIR = join26(CORPUS_ROOT, "filelists");
55285
55295
 
55286
55296
  // src/research/calibrator.ts
55287
55297
  var DEFAULT_POSITIVE = POSITIVE_DIR;
55288
55298
  var DEFAULT_NEGATIVE = NEGATIVE_DIR;
55289
55299
  function buildFileList(dir, extensions) {
55290
- const tmpList = join26("/tmp", `cal-build-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`);
55300
+ const tmpList = join27("/tmp", `cal-build-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`);
55291
55301
  const expr = extensions.map((e) => `-name '*.${e}'`).join(" -o ");
55292
55302
  execFileSync2("bash", ["-c", `find ${dir} -maxdepth 8 -type f \\( ${expr} \\) -print0 | xargs -0 realpath > ${tmpList}`]);
55293
55303
  const out = readFileSync29(tmpList, "utf8");
@@ -55300,13 +55310,13 @@ function runScan2(fileListPath) {
55300
55310
  const ruleFires = /* @__PURE__ */ new Map();
55301
55311
  const uniqueFilesPerRule = /* @__PURE__ */ new Map();
55302
55312
  let fileCount = 0;
55303
- const tmpOut = join26("/tmp", `calibrate-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
55313
+ const tmpOut = join27("/tmp", `calibrate-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
55304
55314
  for (let i = 0; i < files.length; i += CHUNK) {
55305
55315
  const chunk = files.slice(i, i + CHUNK);
55306
55316
  try {
55307
55317
  execFileSync2(
55308
55318
  "node",
55309
- [join26(process.cwd(), "bin", "slopbrick.js"), "scan", ...chunk, "--json", tmpOut, "--no-telemetry", "--quiet"],
55319
+ [join27(process.cwd(), "bin", "slopbrick.js"), "scan", ...chunk, "--json", tmpOut, "--no-telemetry", "--quiet"],
55310
55320
  { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
55311
55321
  );
55312
55322
  } catch {
@@ -55346,8 +55356,8 @@ async function calibrate(cwd, options = {}) {
55346
55356
  const negativeFiles = buildFileList(negativeDir, ["tsx", "ts"]);
55347
55357
  const posSample = options.positiveLimit ? positiveFiles.slice(0, options.positiveLimit) : positiveFiles;
55348
55358
  const negSample = options.negativeLimit ? negativeFiles.slice(0, options.negativeLimit) : negativeFiles;
55349
- const posListPath = join26("/tmp", `cal-pos-${Date.now()}.txt`);
55350
- const negListPath = join26("/tmp", `cal-neg-${Date.now()}.txt`);
55359
+ const posListPath = join27("/tmp", `cal-pos-${Date.now()}.txt`);
55360
+ const negListPath = join27("/tmp", `cal-neg-${Date.now()}.txt`);
55351
55361
  writeFileSync15(posListPath, posSample.join("\n"));
55352
55362
  writeFileSync15(negListPath, negSample.join("\n"));
55353
55363
  const builtins = await Promise.resolve().then(() => (init_builtins(), builtins_exports));
@@ -57250,7 +57260,7 @@ function registerResearch(program) {
57250
57260
  init_logger();
57251
57261
  init_builtins();
57252
57262
  import { existsSync as existsSync28, mkdirSync as mkdirSync15, readFileSync as readFileSync35, writeFileSync as writeFileSync18 } from "fs";
57253
- import { dirname as dirname19, join as join28, resolve as resolve39 } from "path";
57263
+ import { dirname as dirname19, join as join29, resolve as resolve39 } from "path";
57254
57264
  init_scan();
57255
57265
  init_config();
57256
57266
  init_threshold();
@@ -57258,7 +57268,7 @@ init_git();
57258
57268
  init_cache();
57259
57269
 
57260
57270
  // src/snippet/targets.ts
57261
- import { join as join27 } from "path";
57271
+ import { join as join28 } from "path";
57262
57272
 
57263
57273
  // src/snippet/render.ts
57264
57274
  function aiSpecificRules(rules) {
@@ -57489,7 +57499,7 @@ var SNIPPET_TARGETS = [
57489
57499
  }
57490
57500
  ];
57491
57501
  function resolveTargetPath(target) {
57492
- return target.isFolder ? join27(target.path, target.filename) : target.path;
57502
+ return target.isFolder ? join28(target.path, target.filename) : target.path;
57493
57503
  }
57494
57504
  function renderMatrix() {
57495
57505
  const lines = [];
@@ -57512,7 +57522,7 @@ function registerInit(program) {
57512
57522
  process.exit(0);
57513
57523
  }
57514
57524
  const cwd = resolve39(options.workspace ?? process.cwd());
57515
- const configPath = join28(cwd, "slopbrick.config.mjs");
57525
+ const configPath = join29(cwd, "slopbrick.config.mjs");
57516
57526
  const detected = detectStack(cwd);
57517
57527
  const fallbackConfig = { ...DEFAULT_CONFIG, ...detected };
57518
57528
  const proposed = serializeConfig(fallbackConfig);
@@ -57568,7 +57578,7 @@ function registerInit(program) {
57568
57578
  return Boolean(opts[t.flag]);
57569
57579
  });
57570
57580
  for (const target of targetsToWrite) {
57571
- const snippetPath = join28(cwd, resolveTargetPath(target));
57581
+ const snippetPath = join29(cwd, resolveTargetPath(target));
57572
57582
  mkdirSync15(dirname19(snippetPath), { recursive: true });
57573
57583
  const generated = target.generator(builtinRules);
57574
57584
  if (!target.isFolder && existsSync28(snippetPath)) {
@@ -57615,7 +57625,7 @@ import { dirname as dirname20, resolve as resolve40 } from "path";
57615
57625
 
57616
57626
  // src/report/flywheel.ts
57617
57627
  import { existsSync as existsSync29, readFileSync as readFileSync36 } from "fs";
57618
- import { join as join29 } from "path";
57628
+ import { join as join30 } from "path";
57619
57629
  function average(values) {
57620
57630
  if (values.length === 0) return 0;
57621
57631
  return values.reduce((a, b) => a + b, 0) / values.length;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopbrick",
3
- "version": "0.18.2",
3
+ "version": "0.18.3",
4
4
  "description": "Discovered, modeled, and governed repository structure. SlopBrick scans source code, classifies it against 95 rules in 15 categories, computes 4 scores (aiQuality, engineeringHygiene, security, repositoryHealth), and persists the structure for AI agents and CI.",
5
5
  "type": "module",
6
6
  "bin": {