slopbrick 0.11.0 → 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 CHANGED
@@ -36,7 +36,7 @@ var VERSION, AI_SECURITY_NUMERIC, REPOSITORY_HEALTH_WEIGHTS;
36
36
  var init_types = __esm({
37
37
  "src/types.ts"() {
38
38
  "use strict";
39
- VERSION = "0.10.0";
39
+ VERSION = "0.11.2";
40
40
  AI_SECURITY_NUMERIC = {
41
41
  low: 100,
42
42
  medium: 75,
@@ -3486,7 +3486,7 @@ function discoverTestFiles(cwd) {
3486
3486
  }
3487
3487
  return found;
3488
3488
  }
3489
- function walk(dir, out, readdirSync6, statSync7) {
3489
+ function walk(dir, out, readdirSync6, statSync8) {
3490
3490
  let entries;
3491
3491
  try {
3492
3492
  entries = readdirSync6(dir);
@@ -3498,12 +3498,12 @@ function walk(dir, out, readdirSync6, statSync7) {
3498
3498
  const full = `${dir}/${entry}`;
3499
3499
  let stat;
3500
3500
  try {
3501
- stat = statSync7(full);
3501
+ stat = statSync8(full);
3502
3502
  } catch {
3503
3503
  continue;
3504
3504
  }
3505
3505
  if (stat.isDirectory()) {
3506
- walk(full, out, readdirSync6, statSync7);
3506
+ walk(full, out, readdirSync6, statSync8);
3507
3507
  } else if (stat.isFile()) {
3508
3508
  if (/\.(test|spec)\.[jt]sx?$/.test(entry) || /\.stories\.[jt]sx?$/.test(entry)) {
3509
3509
  out.push(full);
@@ -9412,8 +9412,8 @@ function hasExtendedSibling(filePath) {
9412
9412
  const base = (0, import_node_path7.basename)(filePath);
9413
9413
  for (const ext of SOURCE_EXTENSIONS) {
9414
9414
  try {
9415
- const { statSync: statSync7 } = require("fs");
9416
- statSync7((0, import_node_path7.join)(dir, base + ext));
9415
+ const { statSync: statSync8 } = require("fs");
9416
+ statSync8((0, import_node_path7.join)(dir, base + ext));
9417
9417
  return true;
9418
9418
  } catch {
9419
9419
  }
@@ -9571,8 +9571,8 @@ var init_git = __esm({
9571
9571
  });
9572
9572
 
9573
9573
  // src/engine/cache-incremental.ts
9574
- function loadCache(cachePath6) {
9575
- const abs = (0, import_node_path8.isAbsolute)(cachePath6) ? cachePath6 : (0, import_node_path8.resolve)(process.cwd(), cachePath6);
9574
+ function loadCache(cachePath4) {
9575
+ const abs = (0, import_node_path8.isAbsolute)(cachePath4) ? cachePath4 : (0, import_node_path8.resolve)(process.cwd(), cachePath4);
9576
9576
  if (!(0, import_node_fs9.existsSync)(abs)) return void 0;
9577
9577
  try {
9578
9578
  const raw = (0, import_node_fs9.readFileSync)(abs, "utf-8");
@@ -9583,8 +9583,8 @@ function loadCache(cachePath6) {
9583
9583
  return void 0;
9584
9584
  }
9585
9585
  }
9586
- function saveCache(cachePath6, cache) {
9587
- const abs = (0, import_node_path8.isAbsolute)(cachePath6) ? cachePath6 : (0, import_node_path8.resolve)(process.cwd(), cachePath6);
9586
+ function saveCache(cachePath4, cache) {
9587
+ const abs = (0, import_node_path8.isAbsolute)(cachePath4) ? cachePath4 : (0, import_node_path8.resolve)(process.cwd(), cachePath4);
9588
9588
  (0, import_node_fs9.mkdirSync)((0, import_node_path8.dirname)(abs), { recursive: true });
9589
9589
  const tmp = abs + ".tmp";
9590
9590
  if ((0, import_node_fs9.existsSync)(tmp)) {
@@ -12889,12 +12889,41 @@ var init_heatmap = __esm({
12889
12889
  }
12890
12890
  });
12891
12891
 
12892
- // src/engine/memory.ts
12893
- async function saveInventory(workspaceDir, inventory, computeHash = computeFileHash) {
12894
- const { saveInventory: coreSave } = await import("@usebrick/core");
12895
- coreSave(workspaceDir, inventory);
12896
- (0, import_core4.writeCacheFromInventory)(workspaceDir, inventory, computeHash);
12892
+ // ../core/dist/index.js
12893
+ function inventoryPath(workspaceDir) {
12894
+ return (0, import_path.join)(workspaceDir, ".slopbrick", INVENTORY_FILENAME);
12895
+ }
12896
+ function constitutionPath(workspaceDir) {
12897
+ return (0, import_path.join)(workspaceDir, ".slopbrick", CONSTITUTION_FILENAME);
12898
+ }
12899
+ function ensureSlopbrickDir(workspaceDir) {
12900
+ (0, import_fs.mkdirSync)((0, import_path.join)(workspaceDir, ".slopbrick"), { recursive: true });
12901
+ }
12902
+ function writeJsonAtomic(filePath, payload) {
12903
+ ensureSlopbrickDir((0, import_path.dirname)(filePath));
12904
+ const tmp = `${filePath}.tmp`;
12905
+ (0, import_fs.writeFileSync)(tmp, JSON.stringify(payload, null, 2), "utf-8");
12906
+ (0, import_fs.renameSync)(tmp, filePath);
12907
+ }
12908
+ function saveInventory(workspaceDir, inventory) {
12909
+ writeJsonAtomic(inventoryPath(workspaceDir), inventory);
12897
12910
  }
12911
+ function saveConstitution(workspaceDir, constitution) {
12912
+ writeJsonAtomic(constitutionPath(workspaceDir), constitution);
12913
+ }
12914
+ var import_fs, import_path, MEMORY_SCHEMA_VERSION, INVENTORY_FILENAME, CONSTITUTION_FILENAME;
12915
+ var init_dist = __esm({
12916
+ "../core/dist/index.js"() {
12917
+ "use strict";
12918
+ import_fs = require("fs");
12919
+ import_path = require("path");
12920
+ MEMORY_SCHEMA_VERSION = "2";
12921
+ INVENTORY_FILENAME = "inventory.json";
12922
+ CONSTITUTION_FILENAME = "constitution.json";
12923
+ }
12924
+ });
12925
+
12926
+ // src/engine/memory.ts
12898
12927
  function telemetryPath(cwd) {
12899
12928
  return (0, import_node_path17.join)(cwd, TELEMETRY_FILE);
12900
12929
  }
@@ -12969,7 +12998,7 @@ async function buildInventoryFromScan(scanResult, config, durationMs) {
12969
12998
  );
12970
12999
  const components = buildComponentFingerprints(scanResult.results);
12971
13000
  return {
12972
- version: import_core4.MEMORY_SCHEMA_VERSION,
13001
+ version: MEMORY_SCHEMA_VERSION,
12973
13002
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
12974
13003
  workspace: scanResult.cwd,
12975
13004
  scannedFiles: inventory.scannedFiles,
@@ -13004,7 +13033,7 @@ function buildConstitutionFromConfig(config, workspace) {
13004
13033
  const forbidden = forbiddenList.filter((e) => !e.endsWith("/"));
13005
13034
  const forbiddenPrefixes = forbiddenList.filter((e) => e.endsWith("/"));
13006
13035
  return {
13007
- version: import_core4.MEMORY_SCHEMA_VERSION,
13036
+ version: MEMORY_SCHEMA_VERSION,
13008
13037
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
13009
13038
  workspace,
13010
13039
  declared,
@@ -13050,7 +13079,7 @@ function buildComponentFingerprints(results) {
13050
13079
  }
13051
13080
  return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
13052
13081
  }
13053
- var import_node_fs16, import_node_path17, import_node_crypto6, import_core4, import_core5, TELEMETRY_FILE, MAX_RUNS;
13082
+ var import_node_fs16, import_node_path17, import_node_crypto6, TELEMETRY_FILE, MAX_RUNS;
13054
13083
  var init_memory = __esm({
13055
13084
  "src/engine/memory.ts"() {
13056
13085
  "use strict";
@@ -13060,8 +13089,7 @@ var init_memory = __esm({
13060
13089
  init_types();
13061
13090
  init_patterns();
13062
13091
  init_cache_incremental();
13063
- import_core4 = require("@usebrick/core");
13064
- import_core5 = require("@usebrick/core");
13092
+ init_dist();
13065
13093
  TELEMETRY_FILE = (0, import_node_path17.join)(".slopbrick", "memory.json");
13066
13094
  MAX_RUNS = 1e3;
13067
13095
  }
@@ -15508,8 +15536,8 @@ async function runScan(options, explicitPaths) {
15508
15536
  }
15509
15537
  let incrementalSummary;
15510
15538
  if (options.incremental) {
15511
- const cachePath6 = options.cachePath ?? ".slopbrick-cache.json";
15512
- const existing = loadCache(cachePath6);
15539
+ const cachePath4 = options.cachePath ?? ".slopbrick-cache.json";
15540
+ const existing = loadCache(cachePath4);
15513
15541
  const { toScan, unchanged } = partitionByCache(files, existing);
15514
15542
  files = toScan;
15515
15543
  incrementalSummary = { skipped: unchanged.length, rescanned: toScan.length };
@@ -15928,8 +15956,8 @@ async function runScan(options, explicitPaths) {
15928
15956
  appendRun(cwd, report, thresholdExceeded(report, config));
15929
15957
  }
15930
15958
  if (options.incremental) {
15931
- const cachePath6 = options.cachePath ?? ".slopbrick-cache.json";
15932
- const existing = loadCache(cachePath6) ?? emptyCache();
15959
+ const cachePath4 = options.cachePath ?? ".slopbrick-cache.json";
15960
+ const existing = loadCache(cachePath4) ?? emptyCache();
15933
15961
  const next = { ...existing, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
15934
15962
  for (const result of results) {
15935
15963
  try {
@@ -15943,7 +15971,7 @@ async function runScan(options, explicitPaths) {
15943
15971
  } catch {
15944
15972
  }
15945
15973
  }
15946
- saveCache(cachePath6, next);
15974
+ saveCache(cachePath4, next);
15947
15975
  if (incrementalSummary && !options.quiet) {
15948
15976
  logger.info(
15949
15977
  `Incremental: re-scanned ${incrementalSummary.rescanned}, skipped ${incrementalSummary.skipped} (unchanged).`
@@ -15995,7 +16023,7 @@ async function runScan(options, explicitPaths) {
15995
16023
  );
15996
16024
  await saveInventory(cwd, inventory);
15997
16025
  const constitution = buildConstitutionFromConfig(config, cwd);
15998
- await (0, import_core5.saveConstitution)(cwd, constitution);
16026
+ await saveConstitution(cwd, constitution);
15999
16027
  if (!options.quiet && !machineReadableStdout) {
16000
16028
  logger.info(`Memory persisted to .slopbrick/ (${inventory.patterns.length} patterns, ${inventory.components.length} components).`);
16001
16029
  }
@@ -16313,6 +16341,7 @@ var init_scan = __esm({
16313
16341
  init_unified_diff();
16314
16342
  init_heatmap();
16315
16343
  init_memory();
16344
+ init_dist();
16316
16345
  init_telemetry();
16317
16346
  init_flywheel();
16318
16347
  init_logger();
@@ -16764,14 +16793,14 @@ async function runBusinessLogicScore(args, ctx) {
16764
16793
  const maxFiles = typeof maxFilesRaw === "number" && Number.isFinite(maxFilesRaw) && maxFilesRaw > 0 ? Math.min(2e3, Math.floor(maxFilesRaw)) : 500;
16765
16794
  try {
16766
16795
  const { discoverFiles: discoverFiles2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
16767
- const { readFileSync: readFileSync37 } = await import("fs");
16796
+ const { readFileSync: readFileSync39 } = await import("fs");
16768
16797
  const allFiles = await discoverFiles2(ctx.cwd, ctx.config);
16769
16798
  const limited = allFiles.slice(0, maxFiles);
16770
16799
  const issues = [];
16771
16800
  for (const absPath of limited) {
16772
16801
  let source;
16773
16802
  try {
16774
- source = readFileSync37(absPath, "utf-8");
16803
+ source = readFileSync39(absPath, "utf-8");
16775
16804
  } catch {
16776
16805
  continue;
16777
16806
  }
@@ -17038,21 +17067,189 @@ var init_tools = __esm({
17038
17067
  }
17039
17068
  });
17040
17069
 
17070
+ // src/cli/migrate.ts
17071
+ var migrate_exports = {};
17072
+ __export(migrate_exports, {
17073
+ applyMigration: () => applyMigration,
17074
+ formatMigrate: () => formatMigrate,
17075
+ isAlreadyMigrated: () => isAlreadyMigrated,
17076
+ logger: () => logger,
17077
+ planMigration: () => planMigration,
17078
+ runMigrate: () => runMigrate
17079
+ });
17080
+ function planMigration(workspaceDir) {
17081
+ const moves = [];
17082
+ const rewrites = [];
17083
+ const gitignoreEdits = [];
17084
+ const oldDir = (0, import_node_path38.join)(workspaceDir, ".slop-audit");
17085
+ const newDir = (0, import_node_path38.join)(workspaceDir, ".slopbrick");
17086
+ if ((0, import_node_fs39.existsSync)(oldDir)) {
17087
+ moves.push({ from: oldDir, to: newDir, kind: "dir" });
17088
+ rewrites.push({
17089
+ path: (0, import_node_path38.join)(newDir, "inventory.json"),
17090
+ field: "version",
17091
+ from: '"1"',
17092
+ to: '"2"'
17093
+ });
17094
+ rewrites.push({
17095
+ path: (0, import_node_path38.join)(newDir, "constitution.json"),
17096
+ field: "version",
17097
+ from: '"1"',
17098
+ to: '"2"'
17099
+ });
17100
+ }
17101
+ const oldCache = (0, import_node_path38.join)(workspaceDir, ".slop-audit-cache.json");
17102
+ const newCache = (0, import_node_path38.join)(workspaceDir, ".slopbrick-cache.json");
17103
+ if ((0, import_node_fs39.existsSync)(oldCache)) {
17104
+ moves.push({ from: oldCache, to: newCache, kind: "file" });
17105
+ }
17106
+ for (const ext of ["mjs", "cjs", "js"]) {
17107
+ const oldCfg = (0, import_node_path38.join)(workspaceDir, `slop-audit.config.${ext}`);
17108
+ const newCfg = (0, import_node_path38.join)(workspaceDir, `slopbrick.config.${ext}`);
17109
+ if ((0, import_node_fs39.existsSync)(oldCfg)) {
17110
+ moves.push({ from: oldCfg, to: newCfg, kind: "config" });
17111
+ }
17112
+ }
17113
+ const gi = (0, import_node_path38.join)(workspaceDir, ".gitignore");
17114
+ if ((0, import_node_fs39.existsSync)(gi)) {
17115
+ const content = (0, import_node_fs39.readFileSync)(gi, "utf-8");
17116
+ if (content.includes(".slop-audit/")) {
17117
+ gitignoreEdits.push({
17118
+ path: gi,
17119
+ from: ".slop-audit/",
17120
+ to: ".slopbrick/"
17121
+ });
17122
+ }
17123
+ if (content.includes(".slop-audit-cache.json")) {
17124
+ gitignoreEdits.push({
17125
+ path: gi,
17126
+ from: ".slop-audit-cache.json",
17127
+ to: ".slopbrick-cache.json"
17128
+ });
17129
+ }
17130
+ }
17131
+ return { moves, rewrites, gitignoreEdits };
17132
+ }
17133
+ function isAlreadyMigrated(workspaceDir) {
17134
+ return (0, import_node_fs39.existsSync)((0, import_node_path38.join)(workspaceDir, ".slopbrick")) && !(0, import_node_fs39.existsSync)((0, import_node_path38.join)(workspaceDir, ".slop-audit"));
17135
+ }
17136
+ function applyMigration(plan, options = {}) {
17137
+ if (options.dryRun) return;
17138
+ for (const m of plan.moves) {
17139
+ (0, import_node_fs39.renameSync)(m.from, m.to);
17140
+ }
17141
+ for (const r of plan.rewrites) {
17142
+ if (!(0, import_node_fs39.existsSync)(r.path)) continue;
17143
+ const content = (0, import_node_fs39.readFileSync)(r.path, "utf-8");
17144
+ const next = content.replace(`"version": ${r.from}`, `"version": ${r.to}`);
17145
+ (0, import_node_fs39.writeFileSync)(r.path, next);
17146
+ }
17147
+ for (const g of plan.gitignoreEdits) {
17148
+ const content = (0, import_node_fs39.readFileSync)(g.path, "utf-8");
17149
+ const next = content.replaceAll(g.from, g.to);
17150
+ (0, import_node_fs39.writeFileSync)(g.path, next);
17151
+ }
17152
+ }
17153
+ function runMigrate(options) {
17154
+ const { workspace, dryRun = false, force = false } = options;
17155
+ if (!(0, import_node_fs39.existsSync)(workspace)) {
17156
+ return {
17157
+ ok: false,
17158
+ alreadyMigrated: false,
17159
+ planned: { moves: [], rewrites: [], gitignoreEdits: [] },
17160
+ applied: false,
17161
+ reason: `Workspace ${workspace} does not exist`
17162
+ };
17163
+ }
17164
+ const alreadyMigrated = isAlreadyMigrated(workspace);
17165
+ const newDir = (0, import_node_path38.join)(workspace, ".slopbrick");
17166
+ const oldDir = (0, import_node_path38.join)(workspace, ".slop-audit");
17167
+ if ((0, import_node_fs39.existsSync)(newDir) && (0, import_node_fs39.existsSync)(oldDir) && !force) {
17168
+ return {
17169
+ ok: false,
17170
+ alreadyMigrated: false,
17171
+ planned: { moves: [], rewrites: [], gitignoreEdits: [] },
17172
+ applied: false,
17173
+ reason: `Both .slopbrick/ and .slop-audit/ exist. Use --force to overwrite the .slopbrick/ directory, or manually resolve the conflict.`
17174
+ };
17175
+ }
17176
+ const planned = planMigration(workspace);
17177
+ const nothingToDo = planned.moves.length === 0 && planned.rewrites.length === 0 && planned.gitignoreEdits.length === 0;
17178
+ if (nothingToDo) {
17179
+ return {
17180
+ ok: true,
17181
+ alreadyMigrated,
17182
+ planned,
17183
+ applied: false
17184
+ };
17185
+ }
17186
+ applyMigration(planned, { dryRun });
17187
+ return {
17188
+ ok: true,
17189
+ alreadyMigrated: false,
17190
+ planned,
17191
+ applied: !dryRun
17192
+ };
17193
+ }
17194
+ function formatMigrate(result) {
17195
+ const lines = [];
17196
+ if (result.alreadyMigrated) {
17197
+ lines.push("Already migrated to v2 (no work needed).");
17198
+ return lines.join("\n");
17199
+ }
17200
+ if (!result.ok) {
17201
+ lines.push(`ERROR: ${result.reason ?? "unknown failure"}`);
17202
+ return lines.join("\n");
17203
+ }
17204
+ if (result.planned.moves.length === 0 && result.planned.gitignoreEdits.length === 0) {
17205
+ lines.push("Nothing to migrate \u2014 workspace is already on slopbrick v0.11.0+.");
17206
+ }
17207
+ if (result.planned.moves.length > 0) {
17208
+ lines.push("Moves:");
17209
+ for (const m of result.planned.moves) {
17210
+ lines.push(` ${m.from}`);
17211
+ lines.push(` \u2192 ${m.to} (${m.kind})`);
17212
+ }
17213
+ }
17214
+ if (result.planned.rewrites.length > 0) {
17215
+ lines.push("Schema version bumps:");
17216
+ for (const r of result.planned.rewrites) {
17217
+ lines.push(` ${r.path} ${r.field}: ${r.from} \u2192 ${r.to}`);
17218
+ }
17219
+ }
17220
+ if (result.planned.gitignoreEdits.length > 0) {
17221
+ lines.push(".gitignore edits:");
17222
+ for (const g of result.planned.gitignoreEdits) {
17223
+ lines.push(` ${g.path}: ${g.from} \u2192 ${g.to}`);
17224
+ }
17225
+ }
17226
+ lines.push("");
17227
+ if (result.applied) {
17228
+ lines.push("Migration applied. Run `slopbrick scan` to regenerate inventory at v2.");
17229
+ } else {
17230
+ lines.push("DRY RUN \u2014 no files were changed. Re-run without --dry-run to apply.");
17231
+ }
17232
+ return lines.join("\n");
17233
+ }
17234
+ var import_node_fs39, import_node_path38;
17235
+ var init_migrate = __esm({
17236
+ "src/cli/migrate.ts"() {
17237
+ "use strict";
17238
+ import_node_fs39 = require("fs");
17239
+ import_node_path38 = require("path");
17240
+ init_logger();
17241
+ }
17242
+ });
17243
+
17041
17244
  // src/index.ts
17042
17245
  var src_exports = {};
17043
17246
  __export(src_exports, {
17044
17247
  AI_SECURITY_NUMERIC: () => AI_SECURITY_NUMERIC,
17045
- CACHE_FILENAME: () => import_core6.CACHE_FILENAME,
17046
- CONSTITUTION_FILENAME: () => import_core6.CONSTITUTION_FILENAME,
17047
17248
  DEFAULT_CONFIG: () => DEFAULT_CONFIG,
17048
- INVENTORY_FILENAME: () => import_core6.INVENTORY_FILENAME,
17049
- MEMORY_SCHEMA_VERSION: () => import_core6.MEMORY_SCHEMA_VERSION,
17050
17249
  REPOSITORY_HEALTH_WEIGHTS: () => REPOSITORY_HEALTH_WEIGHTS,
17051
17250
  VERSION: () => VERSION,
17052
17251
  baselineStatusMessage: () => baselineStatusMessage,
17053
- cachePath: () => import_core6.cachePath,
17054
17252
  colorForSlop: () => colorForSlop,
17055
- constitutionPath: () => import_core6.constitutionPath,
17056
17253
  extractSignatures: () => extractSignatures,
17057
17254
  failedThresholdCount: () => failedThresholdCount,
17058
17255
  filterByDisabledDirectives: () => filterByDisabledDirectives,
@@ -17062,22 +17259,10 @@ __export(src_exports, {
17062
17259
  formatBadge: () => formatBadge,
17063
17260
  formatReportFromFile: () => formatReportFromFile,
17064
17261
  formatSparkline: () => formatSparkline,
17065
- invalidateFile: () => import_core6.invalidateFile,
17066
- inventoryPath: () => import_core6.inventoryPath,
17067
- isComponentFingerprint: () => import_core6.isComponentFingerprint,
17068
- isConstitutionFile: () => import_core6.isConstitutionFile,
17069
- isFileMtimeEntry: () => import_core6.isFileMtimeEntry,
17070
- isInventoryFile: () => import_core6.isInventoryFile,
17071
- isInventoryFresh: () => import_core6.isInventoryFresh,
17072
- isMemoryPattern: () => import_core6.isMemoryPattern,
17073
17262
  loadConfig: () => loadConfig,
17074
- loadConstitution: () => import_core6.loadConstitution,
17075
- loadInventory: () => import_core6.loadInventory,
17076
17263
  readReportFile: () => readReportFile,
17077
17264
  runCli: () => runCli,
17078
17265
  runInitWizard: () => runInitWizard,
17079
- saveConstitution: () => import_core6.saveConstitution,
17080
- saveInventory: () => import_core6.saveInventory,
17081
17266
  scanProject: () => scanProject,
17082
17267
  serializeConfig: () => serializeConfig,
17083
17268
  signatureSimilarity: () => signatureSimilarity,
@@ -17089,8 +17274,8 @@ init_types();
17089
17274
  init_config();
17090
17275
 
17091
17276
  // src/cli/program.ts
17092
- var import_node_fs39 = require("fs");
17093
- var import_node_path38 = require("path");
17277
+ var import_node_fs40 = require("fs");
17278
+ var import_node_path39 = require("path");
17094
17279
  var import_node_perf_hooks = require("perf_hooks");
17095
17280
  var import_commander2 = require("commander");
17096
17281
 
@@ -18500,11 +18685,11 @@ var shadcn_registry_default = {
18500
18685
  // src/rules/registry-loader.ts
18501
18686
  var REGISTRY_URL = "https://ui.shadcn.com/registry.json";
18502
18687
  var BUNDLED_REGISTRY_VERSION = shadcn_registry_default.version;
18503
- function cachePath4(cwd) {
18688
+ function cachePath3(cwd) {
18504
18689
  return (0, import_node_path28.join)(cwd, ".slopbrick", "cache", "registry-snapshot.json");
18505
18690
  }
18506
18691
  function ensureCacheDir(cwd) {
18507
- const dir = (0, import_node_path28.dirname)(cachePath4(cwd));
18692
+ const dir = (0, import_node_path28.dirname)(cachePath3(cwd));
18508
18693
  if (!(0, import_node_fs27.existsSync)(dir)) {
18509
18694
  (0, import_node_fs27.mkdirSync)(dir, { recursive: true });
18510
18695
  }
@@ -18517,7 +18702,7 @@ function isValidSnapshot(value) {
18517
18702
  return true;
18518
18703
  }
18519
18704
  function isRegistryFresh(cwd) {
18520
- const cached = cachePath4(cwd);
18705
+ const cached = cachePath3(cwd);
18521
18706
  if (!(0, import_node_fs27.existsSync)(cached)) return false;
18522
18707
  try {
18523
18708
  const parsed = JSON.parse((0, import_node_fs27.readFileSync)(cached, "utf8"));
@@ -18554,7 +18739,7 @@ async function refreshRegistrySnapshot(cwd, url = REGISTRY_URL, timeoutMs = 5e3)
18554
18739
  };
18555
18740
  }
18556
18741
  ensureCacheDir(cwd);
18557
- (0, import_node_fs27.writeFileSync)(cachePath4(cwd), JSON.stringify(fetched, null, 2));
18742
+ (0, import_node_fs27.writeFileSync)(cachePath3(cwd), JSON.stringify(fetched, null, 2));
18558
18743
  const fresh = fetched.version === BUNDLED_REGISTRY_VERSION;
18559
18744
  return {
18560
18745
  ok: true,
@@ -18564,7 +18749,7 @@ async function refreshRegistrySnapshot(cwd, url = REGISTRY_URL, timeoutMs = 5e3)
18564
18749
  }
18565
18750
  function copyBundledSnapshotToCache(cwd) {
18566
18751
  ensureCacheDir(cwd);
18567
- (0, import_node_fs27.writeFileSync)(cachePath4(cwd), JSON.stringify(shadcn_registry_default, null, 2));
18752
+ (0, import_node_fs27.writeFileSync)(cachePath3(cwd), JSON.stringify(shadcn_registry_default, null, 2));
18568
18753
  }
18569
18754
 
18570
18755
  // src/cli/init.ts
@@ -20634,13 +20819,13 @@ async function runCli({ start }) {
20634
20819
  logger.info(renderMatrix());
20635
20820
  process.exit(0);
20636
20821
  }
20637
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
20638
- const configPath = (0, import_node_path38.join)(cwd, "slopbrick.config.mjs");
20822
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
20823
+ const configPath = (0, import_node_path39.join)(cwd, "slopbrick.config.mjs");
20639
20824
  const detected = detectStack(cwd);
20640
20825
  const fallbackConfig = { ...DEFAULT_CONFIG, ...detected };
20641
20826
  const proposed = serializeConfig(fallbackConfig);
20642
- if ((0, import_node_fs39.existsSync)(configPath) && !cmdOptions.yes) {
20643
- const current = (0, import_node_fs39.readFileSync)(configPath, "utf8");
20827
+ if ((0, import_node_fs40.existsSync)(configPath) && !cmdOptions.yes) {
20828
+ const current = (0, import_node_fs40.readFileSync)(configPath, "utf8");
20644
20829
  logger.error(`A config file already exists at ${configPath}.`);
20645
20830
  logger.error("To overwrite it with defaults, run `slopbrick init --yes`.");
20646
20831
  logger.error("");
@@ -20661,7 +20846,7 @@ async function runCli({ start }) {
20661
20846
  config = buildInitConfig(detected, answers);
20662
20847
  usedWizard = true;
20663
20848
  }
20664
- (0, import_node_fs39.writeFileSync)(configPath, serializeConfig(config));
20849
+ (0, import_node_fs40.writeFileSync)(configPath, serializeConfig(config));
20665
20850
  appendGitignore(cwd);
20666
20851
  const refresh = await refreshRegistrySnapshot(cwd);
20667
20852
  if (!refresh.ok) {
@@ -20691,21 +20876,21 @@ async function runCli({ start }) {
20691
20876
  return Boolean(opts[t.flag]);
20692
20877
  });
20693
20878
  for (const target of targetsToWrite) {
20694
- const snippetPath = (0, import_node_path38.join)(cwd, resolveTargetPath(target));
20695
- (0, import_node_fs39.mkdirSync)((0, import_node_path38.dirname)(snippetPath), { recursive: true });
20879
+ const snippetPath = (0, import_node_path39.join)(cwd, resolveTargetPath(target));
20880
+ (0, import_node_fs40.mkdirSync)((0, import_node_path39.dirname)(snippetPath), { recursive: true });
20696
20881
  const generated = target.generator(builtinRules);
20697
- if (!target.isFolder && (0, import_node_fs39.existsSync)(snippetPath)) {
20698
- const existing = (0, import_node_fs39.readFileSync)(snippetPath, "utf8");
20882
+ if (!target.isFolder && (0, import_node_fs40.existsSync)(snippetPath)) {
20883
+ const existing = (0, import_node_fs40.readFileSync)(snippetPath, "utf8");
20699
20884
  if (existing.includes("<!-- slopbrick:begin -->")) {
20700
20885
  const updated = existing.replace(
20701
20886
  /<!-- slopbrick:begin -->[\s\S]*?<!-- slopbrick:end -->/,
20702
20887
  "<!-- slopbrick:begin -->\n" + generated + "<!-- slopbrick:end -->"
20703
20888
  );
20704
- (0, import_node_fs39.writeFileSync)(snippetPath, updated, "utf8");
20889
+ (0, import_node_fs40.writeFileSync)(snippetPath, updated, "utf8");
20705
20890
  if (!options.quiet) logger.info(`Updated ${snippetPath}`);
20706
20891
  continue;
20707
20892
  }
20708
- (0, import_node_fs39.writeFileSync)(
20893
+ (0, import_node_fs40.writeFileSync)(
20709
20894
  snippetPath,
20710
20895
  existing + (existing.endsWith("\n") ? "\n" : "\n\n") + generated,
20711
20896
  "utf8"
@@ -20713,7 +20898,7 @@ async function runCli({ start }) {
20713
20898
  if (!options.quiet) logger.info(`Wrote ${snippetPath}`);
20714
20899
  continue;
20715
20900
  }
20716
- (0, import_node_fs39.writeFileSync)(snippetPath, generated, "utf8");
20901
+ (0, import_node_fs40.writeFileSync)(snippetPath, generated, "utf8");
20717
20902
  if (!options.quiet) logger.info(`Wrote ${snippetPath}`);
20718
20903
  }
20719
20904
  if (options.baseline) {
@@ -20730,7 +20915,7 @@ async function runCli({ start }) {
20730
20915
  });
20731
20916
  program.command("install").description("install the git pre-commit hook").action(async (_cmdOptions, command) => {
20732
20917
  const options = command.optsWithGlobals();
20733
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
20918
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
20734
20919
  const root = getGitRoot(cwd);
20735
20920
  if (!root) {
20736
20921
  logger.error("Not a Git repository. Run `git init` first, or remove --staged from your command.");
@@ -20744,7 +20929,7 @@ async function runCli({ start }) {
20744
20929
  });
20745
20930
  program.command("uninstall").description("uninstall the git pre-commit hook").action(async (_cmdOptions, command) => {
20746
20931
  const options = command.optsWithGlobals();
20747
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
20932
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
20748
20933
  const root = getGitRoot(cwd);
20749
20934
  if (!root) {
20750
20935
  logger.error("Not a Git repository. Run `git init` first, or remove --staged from your command.");
@@ -20765,7 +20950,7 @@ async function runCli({ start }) {
20765
20950
  program.command("suggest").description("print remediation advice").action(async (_cmdOptions, command) => {
20766
20951
  const options = command.optsWithGlobals();
20767
20952
  const { report } = await runScan(options);
20768
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
20953
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
20769
20954
  logger.info(formatAdvice(report));
20770
20955
  const diff = formatUnifiedDiff(report, cwd);
20771
20956
  if (diff) logger.info(diff);
@@ -20773,7 +20958,7 @@ async function runCli({ start }) {
20773
20958
  });
20774
20959
  program.command("flywheel").description("summarize aggregated scan telemetry").option("--format <pretty|json>", "output format", "pretty").option("--export <path>", "write summary as JSON to <path>").action(async (cmdOptions, command) => {
20775
20960
  const options = command.optsWithGlobals();
20776
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
20961
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
20777
20962
  const payloads = readTelemetry(cwd);
20778
20963
  if (payloads.length === 0) {
20779
20964
  logger.info("No flywheel telemetry found. Run a scan first.");
@@ -20781,9 +20966,9 @@ async function runCli({ start }) {
20781
20966
  }
20782
20967
  const summary = summarizeTelemetry(payloads);
20783
20968
  if (cmdOptions.export) {
20784
- const exportPath = (0, import_node_path38.resolve)(cmdOptions.export);
20785
- (0, import_node_fs39.mkdirSync)((0, import_node_path38.dirname)(exportPath), { recursive: true });
20786
- (0, import_node_fs39.writeFileSync)(exportPath, JSON.stringify(summary, null, 2), "utf-8");
20969
+ const exportPath = (0, import_node_path39.resolve)(cmdOptions.export);
20970
+ (0, import_node_fs40.mkdirSync)((0, import_node_path39.dirname)(exportPath), { recursive: true });
20971
+ (0, import_node_fs40.writeFileSync)(exportPath, JSON.stringify(summary, null, 2), "utf-8");
20787
20972
  logger.info(`Wrote flywheel summary to ${exportPath}`);
20788
20973
  process.exit(0);
20789
20974
  }
@@ -20806,7 +20991,7 @@ async function runCli({ start }) {
20806
20991
  logger.error("--heatmap and --suggest can't be used together. Pick one: a heatmap of severity, or text advice.");
20807
20992
  process.exit(2);
20808
20993
  }
20809
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
20994
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
20810
20995
  if (options.trend !== void 0) {
20811
20996
  const runs = readRuns(cwd);
20812
20997
  if (runs.length === 0) {
@@ -20839,7 +21024,7 @@ async function runCli({ start }) {
20839
21024
  const scanElapsed = Math.round(import_node_perf_hooks.performance.now() - scanStart);
20840
21025
  const totalElapsed = Math.round(import_node_perf_hooks.performance.now() - start);
20841
21026
  if (options.baseline) {
20842
- const cwd2 = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21027
+ const cwd2 = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
20843
21028
  const configHash = hashConfig(config);
20844
21029
  const gitHead = await getGitHead(cwd2) ?? "unknown";
20845
21030
  const cache = buildBaselineCache(report, configHash, gitHead, cwd2);
@@ -20933,24 +21118,24 @@ async function runCli({ start }) {
20933
21118
  framework: cmdOptions.framework,
20934
21119
  componentType: cmdOptions.componentType,
20935
21120
  provider,
20936
- outputDir: (0, import_node_path38.resolve)(cmdOptions.outputDir),
21121
+ outputDir: (0, import_node_path39.resolve)(cmdOptions.outputDir),
20937
21122
  temperature: cmdOptions.temperature
20938
21123
  });
20939
21124
  logger.info(`Generated ${samples.length} samples in ${cmdOptions.outputDir}`);
20940
21125
  });
20941
21126
  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) => {
20942
21127
  try {
20943
- const metadataPath = (0, import_node_path38.resolve)(cmdOptions.inputDir, "metadata.json");
20944
- if (!(0, import_node_fs39.existsSync)(metadataPath)) {
21128
+ const metadataPath = (0, import_node_path39.resolve)(cmdOptions.inputDir, "metadata.json");
21129
+ if (!(0, import_node_fs40.existsSync)(metadataPath)) {
20945
21130
  logger.error(`No metadata.json found in ${cmdOptions.inputDir}`);
20946
21131
  process.exit(2);
20947
21132
  }
20948
- const samples = JSON.parse((0, import_node_fs39.readFileSync)(metadataPath, "utf8"));
21133
+ const samples = JSON.parse((0, import_node_fs40.readFileSync)(metadataPath, "utf8"));
20949
21134
  const config = cmdOptions.config ? await loadConfig(cmdOptions.config) : { ...DEFAULT_CONFIG, framework: cmdOptions.framework };
20950
21135
  const analysis = await analyzeSamples(samples, config);
20951
- const outputPath = (0, import_node_path38.resolve)(cmdOptions.output);
20952
- (0, import_node_fs39.mkdirSync)((0, import_node_path38.dirname)(outputPath), { recursive: true });
20953
- (0, import_node_fs39.writeFileSync)(outputPath, JSON.stringify(analysis, null, 2), "utf8");
21136
+ const outputPath = (0, import_node_path39.resolve)(cmdOptions.output);
21137
+ (0, import_node_fs40.mkdirSync)((0, import_node_path39.dirname)(outputPath), { recursive: true });
21138
+ (0, import_node_fs40.writeFileSync)(outputPath, JSON.stringify(analysis, null, 2), "utf8");
20954
21139
  logger.info(`Analyzed ${analysis.summary.total} samples; coverage: ${analysis.summary.coverage}%`);
20955
21140
  logger.info(`Wrote analysis to ${outputPath}`);
20956
21141
  } catch (error) {
@@ -20960,12 +21145,12 @@ async function runCli({ start }) {
20960
21145
  });
20961
21146
  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) => {
20962
21147
  try {
20963
- const metadataPath = (0, import_node_path38.resolve)(cmdOptions.inputDir, "metadata.json");
20964
- if (!(0, import_node_fs39.existsSync)(metadataPath)) {
21148
+ const metadataPath = (0, import_node_path39.resolve)(cmdOptions.inputDir, "metadata.json");
21149
+ if (!(0, import_node_fs40.existsSync)(metadataPath)) {
20965
21150
  logger.error(`No metadata.json found in ${cmdOptions.inputDir}`);
20966
21151
  process.exit(2);
20967
21152
  }
20968
- const samples = JSON.parse((0, import_node_fs39.readFileSync)(metadataPath, "utf8"));
21153
+ const samples = JSON.parse((0, import_node_fs40.readFileSync)(metadataPath, "utf8"));
20969
21154
  const config = cmdOptions.config ? await loadConfig(cmdOptions.config) : { ...DEFAULT_CONFIG, framework: cmdOptions.framework };
20970
21155
  const analysis = await analyzeSamples(samples, config);
20971
21156
  const extraction = extractAndCluster(analysis.samples, {
@@ -20975,8 +21160,8 @@ async function runCli({ start }) {
20975
21160
  const candidates = clustersToCandidates(extraction.clusters, {
20976
21161
  minFrequency: cmdOptions.minFrequency
20977
21162
  });
20978
- const outputPath = (0, import_node_path38.resolve)(cmdOptions.output);
20979
- (0, import_node_fs39.mkdirSync)((0, import_node_path38.dirname)(outputPath), { recursive: true });
21163
+ const outputPath = (0, import_node_path39.resolve)(cmdOptions.output);
21164
+ (0, import_node_fs40.mkdirSync)((0, import_node_path39.dirname)(outputPath), { recursive: true });
20980
21165
  const payload = {
20981
21166
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
20982
21167
  sampleCount: analysis.summary.total,
@@ -20984,7 +21169,7 @@ async function runCli({ start }) {
20984
21169
  fingerprintCount: extraction.total,
20985
21170
  candidates
20986
21171
  };
20987
- (0, import_node_fs39.writeFileSync)(outputPath, JSON.stringify(payload, null, 2), "utf8");
21172
+ (0, import_node_fs40.writeFileSync)(outputPath, JSON.stringify(payload, null, 2), "utf8");
20988
21173
  logger.info(`Extracted ${extraction.total} fingerprints across ${analysis.summary.total} samples`);
20989
21174
  logger.info(`Wrote ${candidates.length} candidate rule(s) to ${outputPath}`);
20990
21175
  } catch (error) {
@@ -21001,9 +21186,9 @@ async function runCli({ start }) {
21001
21186
  positiveLimit: cmdOptions.positiveLimit,
21002
21187
  negativeLimit: cmdOptions.negativeLimit
21003
21188
  });
21004
- const outputPath = cmdOptions.output ? (0, import_node_path38.resolve)(cwd, cmdOptions.output) : (0, import_node_path38.resolve)(cwd, "corpus", "calibration-empirical.md");
21005
- (0, import_node_fs39.mkdirSync)((0, import_node_path38.dirname)(outputPath), { recursive: true });
21006
- (0, import_node_fs39.writeFileSync)(outputPath, reportToMarkdown(report), "utf8");
21189
+ const outputPath = cmdOptions.output ? (0, import_node_path39.resolve)(cwd, cmdOptions.output) : (0, import_node_path39.resolve)(cwd, "corpus", "calibration-empirical.md");
21190
+ (0, import_node_fs40.mkdirSync)((0, import_node_path39.dirname)(outputPath), { recursive: true });
21191
+ (0, import_node_fs40.writeFileSync)(outputPath, reportToMarkdown(report), "utf8");
21007
21192
  logger.info(
21008
21193
  "Calibrated " + report.rules.length + " rules across " + report.positiveFileCount + " positive + " + report.negativeFileCount + " negative files."
21009
21194
  );
@@ -21041,7 +21226,7 @@ async function runCli({ start }) {
21041
21226
  const options = command.optsWithGlobals();
21042
21227
  const rawFormat = options.format ?? cmdOptions.format ?? "pretty";
21043
21228
  const format = rawFormat === "json" || rawFormat === "pretty" ? rawFormat : "pretty";
21044
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21229
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
21045
21230
  const { config } = await runScan({ ...options, workspace: cwd });
21046
21231
  const result = await runDrift(cwd, config, { maxFiles: cmdOptions.maxFiles });
21047
21232
  logger.info(formatDrift(result, { json: format === "json" }));
@@ -21058,7 +21243,7 @@ async function runCli({ start }) {
21058
21243
  async (cmdOptions, command) => {
21059
21244
  try {
21060
21245
  const options = command.optsWithGlobals();
21061
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21246
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
21062
21247
  const rawFormat = options.format ?? cmdOptions.format ?? "text";
21063
21248
  const format = rawFormat === "json" || rawFormat === "markdown" || rawFormat === "text" ? rawFormat : "text";
21064
21249
  const { config } = await runScan({ ...options, workspace: cwd });
@@ -21085,7 +21270,7 @@ async function runCli({ start }) {
21085
21270
  const options = command.optsWithGlobals();
21086
21271
  const rawFormat = options.format ?? cmdOptions.format ?? "pretty";
21087
21272
  const format = rawFormat === "json" || rawFormat === "pretty" ? rawFormat : "pretty";
21088
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21273
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
21089
21274
  const { report } = await runScan({ ...options, workspace: cwd });
21090
21275
  const securityIssues = report.issues.filter((i) => i.category === "security");
21091
21276
  const { risk, findings } = computeAiSecurityRisk(securityIssues);
@@ -21136,7 +21321,7 @@ async function runCli({ start }) {
21136
21321
  const options = command.optsWithGlobals();
21137
21322
  const rawFormat = options.format ?? cmdOptions.format ?? "pretty";
21138
21323
  const format = rawFormat === "json" || rawFormat === "pretty" ? rawFormat : "pretty";
21139
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21324
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
21140
21325
  const { config } = await runScan({ ...options, workspace: cwd });
21141
21326
  const { result } = await runTestScan(cwd, config, { strict: options.strict });
21142
21327
  logger.info(formatTestReport(result, { json: format === "json" }));
@@ -21155,7 +21340,7 @@ async function runCli({ start }) {
21155
21340
  const options = command.optsWithGlobals();
21156
21341
  const rawFormat = options.format ?? cmdOptions.format ?? "pretty";
21157
21342
  const format = rawFormat === "json" || rawFormat === "pretty" ? rawFormat : "pretty";
21158
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21343
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
21159
21344
  const { config } = await runScan({ ...options, workspace: cwd });
21160
21345
  const score = await buildArchitectureScore(cwd, config, cmdOptions.maxFiles);
21161
21346
  const out = format === "json" ? JSON.stringify(score, null, 2) : formatArchitectureScore(score);
@@ -21175,7 +21360,7 @@ async function runCli({ start }) {
21175
21360
  const options = command.optsWithGlobals();
21176
21361
  const rawFormat = options.format ?? cmdOptions.format ?? "text";
21177
21362
  const format = rawFormat === "json" || rawFormat === "markdown" || rawFormat === "text" ? rawFormat : "text";
21178
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21363
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
21179
21364
  const { config } = await runScan({ ...options, workspace: cwd });
21180
21365
  const result = await runBusinessLogicScan(cwd, config, {
21181
21366
  maxFiles: cmdOptions.maxFiles
@@ -21197,7 +21382,7 @@ async function runCli({ start }) {
21197
21382
  const rawFormat = options.format ?? cmdOptions.format ?? "text";
21198
21383
  const format = rawFormat === "json" || rawFormat === "text" ? rawFormat : "text";
21199
21384
  const strict = options.strict ?? cmdOptions.strict ?? false;
21200
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21385
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
21201
21386
  const { config } = await runScan({ ...options, workspace: cwd });
21202
21387
  const result = await runMaintenanceCostScan(cwd, config, {
21203
21388
  maxFiles: cmdOptions.maxFiles,
@@ -21220,7 +21405,7 @@ async function runCli({ start }) {
21220
21405
  const rawFormat = options.format ?? cmdOptions.format ?? "text";
21221
21406
  const format = rawFormat === "json" || rawFormat === "markdown" || rawFormat === "text" ? rawFormat : "text";
21222
21407
  const strict = options.strict ?? cmdOptions.strict ?? false;
21223
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21408
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
21224
21409
  const { config } = await runScan({ ...options, workspace: cwd });
21225
21410
  const result = await runDocsScan(cwd, config, {
21226
21411
  maxDocFiles: cmdOptions.maxFiles,
@@ -21248,7 +21433,7 @@ async function runCli({ start }) {
21248
21433
  const rawFormat = options.format ?? cmdOptions.format ?? "text";
21249
21434
  const format = rawFormat === "json" || rawFormat === "markdown" || rawFormat === "text" ? rawFormat : "text";
21250
21435
  const strict = options.strict ?? cmdOptions.strict ?? false;
21251
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21436
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
21252
21437
  const { config } = await runScan({ ...options, workspace: cwd });
21253
21438
  const result = await runDbScan(cwd, config, {
21254
21439
  maxFiles: cmdOptions.maxFiles,
@@ -21275,7 +21460,7 @@ async function runCli({ start }) {
21275
21460
  const options = command.optsWithGlobals();
21276
21461
  const rawFormat = options.format ?? cmdOptions.format ?? "text";
21277
21462
  const format = rawFormat === "json" || rawFormat === "markdown" || rawFormat === "text" ? rawFormat : "text";
21278
- const cwd = (0, import_node_path38.resolve)(options.workspace ?? process.cwd());
21463
+ const cwd = (0, import_node_path39.resolve)(options.workspace ?? process.cwd());
21279
21464
  const { config } = await runScan({ ...options, workspace: cwd });
21280
21465
  const result = await runPatternsScan(cwd, config, {
21281
21466
  maxFiles: cmdOptions.maxFiles,
@@ -21299,6 +21484,27 @@ async function runCli({ start }) {
21299
21484
  const exitCode = await runDoctor(process.cwd());
21300
21485
  if (exitCode !== 0) process.exit(exitCode);
21301
21486
  });
21487
+ program.command("migrate").description(
21488
+ "Migrate from slop-audit v0.10.x (.slop-audit/) to slopbrick v0.11.0+ (.slopbrick/). Renames artifact dir + cache + config file + bumps schema to v2 + updates .gitignore. Idempotent. Pass --dry-run to preview."
21489
+ ).option("--dry-run", "print the planned changes without touching the filesystem").option("--force", "overwrite .slopbrick/ if both old and new artifacts exist").option("--workspace <path>", "workspace directory", process.cwd()).option("--format <pretty|json>", "output format", "pretty").action(
21490
+ (cmdOptions, command) => {
21491
+ const globals = command.optsWithGlobals();
21492
+ const format = (cmdOptions.format ?? globals.format) === "json" ? "json" : "pretty";
21493
+ const cwd = (0, import_node_path39.resolve)(cmdOptions.workspace ?? process.cwd());
21494
+ const { runMigrate: runMigrate2, formatMigrate: formatMigrate2 } = (init_migrate(), __toCommonJS(migrate_exports));
21495
+ const result = runMigrate2({
21496
+ workspace: cwd,
21497
+ dryRun: cmdOptions.dryRun,
21498
+ force: cmdOptions.force
21499
+ });
21500
+ if (format === "json") {
21501
+ logger.info(JSON.stringify(result, null, 2));
21502
+ } else {
21503
+ logger.info(formatMigrate2(result));
21504
+ }
21505
+ process.exit(result.ok ? 0 : 1);
21506
+ }
21507
+ );
21302
21508
  program.command("rules").description("list all built-in rules with their categories, severities, and descriptions").option("--category <name>", "filter to a single category (visual, typo, layout, etc.)").option("--ai-only", "only show AI-specific rules").option("--json", "emit JSON instead of a pretty table").option("--show-signal-strength", "print per-rule precision/recall table").action((cmdOptions, command) => {
21303
21509
  const globals = command.optsWithGlobals();
21304
21510
  const wantJson = Boolean(cmdOptions.json || globals.json);
@@ -21385,13 +21591,13 @@ async function runCli({ start }) {
21385
21591
  if ("error" in result) process.exit(2);
21386
21592
  });
21387
21593
  program.command("validate-config [path]").description("Statically validate a slopbrick.config.mjs without scanning").action(async (configPath) => {
21388
- const path = configPath ? (0, import_node_path38.resolve)(configPath) : (0, import_node_path38.resolve)(process.cwd(), "slopbrick.config.mjs");
21389
- if (!(0, import_node_fs39.existsSync)(path)) {
21594
+ const path = configPath ? (0, import_node_path39.resolve)(configPath) : (0, import_node_path39.resolve)(process.cwd(), "slopbrick.config.mjs");
21595
+ if (!(0, import_node_fs40.existsSync)(path)) {
21390
21596
  logger.error(`Error: config file not found: ${path}`);
21391
21597
  process.exit(2);
21392
21598
  }
21393
21599
  try {
21394
- const mod = (0, import_node_path38.extname)(path) === ".cjs" ? require(path) : await import(path);
21600
+ const mod = (0, import_node_path39.extname)(path) === ".cjs" ? require(path) : await import(path);
21395
21601
  const userConfig = mod.default ?? mod;
21396
21602
  const result = validateConfig(userConfig);
21397
21603
  if (result.errors.length === 0) {
@@ -21462,7 +21668,6 @@ ${formatMarkdown3(result.report)}`);
21462
21668
  init_threshold();
21463
21669
  init_render();
21464
21670
  init_find_similar();
21465
- var import_core6 = require("@usebrick/core");
21466
21671
  process.on("uncaughtException", (err) => {
21467
21672
  const { logger: logger6 } = (init_logger(), __toCommonJS(logger_exports));
21468
21673
  logger6.error(`Unexpected error: ${err.message}`);
@@ -21471,17 +21676,11 @@ process.on("uncaughtException", (err) => {
21471
21676
  // Annotate the CommonJS export names for ESM import in node:
21472
21677
  0 && (module.exports = {
21473
21678
  AI_SECURITY_NUMERIC,
21474
- CACHE_FILENAME,
21475
- CONSTITUTION_FILENAME,
21476
21679
  DEFAULT_CONFIG,
21477
- INVENTORY_FILENAME,
21478
- MEMORY_SCHEMA_VERSION,
21479
21680
  REPOSITORY_HEALTH_WEIGHTS,
21480
21681
  VERSION,
21481
21682
  baselineStatusMessage,
21482
- cachePath,
21483
21683
  colorForSlop,
21484
- constitutionPath,
21485
21684
  extractSignatures,
21486
21685
  failedThresholdCount,
21487
21686
  filterByDisabledDirectives,
@@ -21491,22 +21690,10 @@ process.on("uncaughtException", (err) => {
21491
21690
  formatBadge,
21492
21691
  formatReportFromFile,
21493
21692
  formatSparkline,
21494
- invalidateFile,
21495
- inventoryPath,
21496
- isComponentFingerprint,
21497
- isConstitutionFile,
21498
- isFileMtimeEntry,
21499
- isInventoryFile,
21500
- isInventoryFresh,
21501
- isMemoryPattern,
21502
21693
  loadConfig,
21503
- loadConstitution,
21504
- loadInventory,
21505
21694
  readReportFile,
21506
21695
  runCli,
21507
21696
  runInitWizard,
21508
- saveConstitution,
21509
- saveInventory,
21510
21697
  scanProject,
21511
21698
  serializeConfig,
21512
21699
  signatureSimilarity,