@cleocode/cleo 2026.4.55 → 2026.4.57
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/cli/index.js +498 -2
- package/dist/cli/index.js.map +3 -3
- package/package.json +8 -8
package/dist/cli/index.js
CHANGED
|
@@ -9508,6 +9508,12 @@ var init_brain_schema = __esm({
|
|
|
9508
9508
|
// observation → references → symbol
|
|
9509
9509
|
"modified_by",
|
|
9510
9510
|
// file → modified_by → session
|
|
9511
|
+
"code_reference",
|
|
9512
|
+
// memory node → code_reference → nexus symbol/file (T645)
|
|
9513
|
+
"affects",
|
|
9514
|
+
// observation → affects → symbol/file (impact tracking)
|
|
9515
|
+
"mentions",
|
|
9516
|
+
// observation → mentions → symbol name (weak reference)
|
|
9511
9517
|
// Plasticity (Hebbian + STDP co-retrieval)
|
|
9512
9518
|
"co_retrieved"
|
|
9513
9519
|
// A → co_retrieved → B (Hebbian: frequently retrieved together)
|
|
@@ -45910,7 +45916,9 @@ var init_edge_types = __esm({
|
|
|
45910
45916
|
// Observation → symbol name mention
|
|
45911
45917
|
MENTIONS: "mentions",
|
|
45912
45918
|
// Observation → symbol/file structural link
|
|
45913
|
-
DOCUMENTS: "documents"
|
|
45919
|
+
DOCUMENTS: "documents",
|
|
45920
|
+
// Memory node → nexus symbol/file (T645)
|
|
45921
|
+
CODE_REFERENCE: "code_reference"
|
|
45914
45922
|
};
|
|
45915
45923
|
}
|
|
45916
45924
|
});
|
|
@@ -88221,7 +88229,6 @@ var init_cleo = __esm({
|
|
|
88221
88229
|
// packages/core/src/index.ts
|
|
88222
88230
|
var init_src3 = __esm({
|
|
88223
88231
|
"packages/core/src/index.ts"() {
|
|
88224
|
-
"use strict";
|
|
88225
88232
|
init_src();
|
|
88226
88233
|
init_adapters();
|
|
88227
88234
|
init_admin();
|
|
@@ -141321,6 +141328,495 @@ function registerNexusCommand(program) {
|
|
|
141321
141328
|
);
|
|
141322
141329
|
} else {
|
|
141323
141330
|
process.stderr.write(`[nexus] Error: ${msg}
|
|
141331
|
+
`);
|
|
141332
|
+
}
|
|
141333
|
+
process.exitCode = 1;
|
|
141334
|
+
}
|
|
141335
|
+
});
|
|
141336
|
+
projects.command("scan").description(
|
|
141337
|
+
"Walk filesystem roots to discover .cleo/ directories not registered in the global nexus registry"
|
|
141338
|
+
).option(
|
|
141339
|
+
"--roots <paths>",
|
|
141340
|
+
"Comma-separated search roots (default: ~/code,~/projects,/mnt/projects)"
|
|
141341
|
+
).option("--max-depth <n>", "Maximum directory traversal depth (default: 4)", parseInt, 4).option("--auto-register", "Register all discovered unregistered projects automatically").option("--include-existing", "Also report projects that are already registered").option("--json", "Output as JSON (LAFS envelope format)").action(async (opts) => {
|
|
141342
|
+
const startTime = Date.now();
|
|
141343
|
+
const jsonOutput = !!opts["json"];
|
|
141344
|
+
const autoRegister = !!opts["autoRegister"];
|
|
141345
|
+
const includeExisting = !!opts["includeExisting"];
|
|
141346
|
+
const maxDepth = Math.max(1, Math.min(opts["maxDepth"] ?? 4, 20));
|
|
141347
|
+
const { homedir: homedir8 } = await import("node:os");
|
|
141348
|
+
const home = homedir8();
|
|
141349
|
+
const defaultRoots = [path10.join(home, "code"), path10.join(home, "projects"), "/mnt/projects"];
|
|
141350
|
+
const rawRoots = typeof opts["roots"] === "string" && opts["roots"].trim().length > 0 ? opts["roots"].split(",").map((r) => r.trim()).filter((r) => r.length > 0).map(
|
|
141351
|
+
(r) => r.startsWith("~") ? path10.join(home, r.slice(1)) : path10.resolve(r)
|
|
141352
|
+
) : defaultRoots;
|
|
141353
|
+
const { existsSync: existsSync135, readdirSync: readdirSync42, statSync: statSync22 } = await import("node:fs");
|
|
141354
|
+
const { Dirent } = await import("node:fs");
|
|
141355
|
+
const roots = rawRoots.filter((r) => {
|
|
141356
|
+
try {
|
|
141357
|
+
return existsSync135(r) && statSync22(r).isDirectory();
|
|
141358
|
+
} catch {
|
|
141359
|
+
return false;
|
|
141360
|
+
}
|
|
141361
|
+
});
|
|
141362
|
+
if (!jsonOutput) {
|
|
141363
|
+
process.stdout.write(
|
|
141364
|
+
`[nexus] Scanning ${roots.length} root(s) up to depth ${maxDepth}:
|
|
141365
|
+
` + roots.map((r) => ` ${r}`).join("\n") + "\n"
|
|
141366
|
+
);
|
|
141367
|
+
}
|
|
141368
|
+
const SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
141369
|
+
"node_modules",
|
|
141370
|
+
".git",
|
|
141371
|
+
"target",
|
|
141372
|
+
// Rust build
|
|
141373
|
+
"dist",
|
|
141374
|
+
"build",
|
|
141375
|
+
".svelte-kit",
|
|
141376
|
+
".next",
|
|
141377
|
+
".cache",
|
|
141378
|
+
"coverage",
|
|
141379
|
+
".turbo",
|
|
141380
|
+
".nx",
|
|
141381
|
+
"__pycache__",
|
|
141382
|
+
".venv",
|
|
141383
|
+
"venv",
|
|
141384
|
+
".tox",
|
|
141385
|
+
"vendor"
|
|
141386
|
+
]);
|
|
141387
|
+
function getDevice(p2) {
|
|
141388
|
+
try {
|
|
141389
|
+
return statSync22(p2).dev;
|
|
141390
|
+
} catch {
|
|
141391
|
+
return -1;
|
|
141392
|
+
}
|
|
141393
|
+
}
|
|
141394
|
+
function walkForCleo(dir, depth, rootDev) {
|
|
141395
|
+
if (depth > maxDepth) return [];
|
|
141396
|
+
let entries;
|
|
141397
|
+
try {
|
|
141398
|
+
entries = readdirSync42(dir, { withFileTypes: true });
|
|
141399
|
+
} catch {
|
|
141400
|
+
return [];
|
|
141401
|
+
}
|
|
141402
|
+
const found = [];
|
|
141403
|
+
for (const entry of entries) {
|
|
141404
|
+
if (!entry.isDirectory()) continue;
|
|
141405
|
+
if (entry.isSymbolicLink()) continue;
|
|
141406
|
+
const fullPath = path10.join(dir, entry.name);
|
|
141407
|
+
if (entry.name === ".cleo") {
|
|
141408
|
+
found.push(dir);
|
|
141409
|
+
continue;
|
|
141410
|
+
}
|
|
141411
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
141412
|
+
const childDev = getDevice(fullPath);
|
|
141413
|
+
if (childDev !== rootDev && childDev !== -1) continue;
|
|
141414
|
+
const nested = walkForCleo(fullPath, depth + 1, rootDev);
|
|
141415
|
+
for (const n of nested) found.push(n);
|
|
141416
|
+
}
|
|
141417
|
+
return found;
|
|
141418
|
+
}
|
|
141419
|
+
const allCandidates = [];
|
|
141420
|
+
for (const root of roots) {
|
|
141421
|
+
const rootDev = getDevice(root);
|
|
141422
|
+
const found = walkForCleo(root, 0, rootDev);
|
|
141423
|
+
for (const f2 of found) allCandidates.push(f2);
|
|
141424
|
+
}
|
|
141425
|
+
const candidates = [...new Set(allCandidates)];
|
|
141426
|
+
let registeredPaths = /* @__PURE__ */ new Set();
|
|
141427
|
+
try {
|
|
141428
|
+
const { nexusList: listProjects } = await Promise.resolve().then(() => (init_internal(), internal_exports));
|
|
141429
|
+
const projects2 = await listProjects();
|
|
141430
|
+
for (const p2 of projects2) {
|
|
141431
|
+
registeredPaths.add(path10.resolve(p2.path));
|
|
141432
|
+
}
|
|
141433
|
+
} catch {
|
|
141434
|
+
registeredPaths = /* @__PURE__ */ new Set();
|
|
141435
|
+
}
|
|
141436
|
+
const unregistered = [];
|
|
141437
|
+
const registered = [];
|
|
141438
|
+
for (const candidate of candidates) {
|
|
141439
|
+
const resolved = path10.resolve(candidate);
|
|
141440
|
+
if (registeredPaths.has(resolved)) {
|
|
141441
|
+
registered.push(resolved);
|
|
141442
|
+
} else {
|
|
141443
|
+
unregistered.push(resolved);
|
|
141444
|
+
}
|
|
141445
|
+
}
|
|
141446
|
+
const tally = {
|
|
141447
|
+
total: candidates.length,
|
|
141448
|
+
unregistered: unregistered.length,
|
|
141449
|
+
registered: registered.length
|
|
141450
|
+
};
|
|
141451
|
+
const autoRegistered = [];
|
|
141452
|
+
const autoRegisterErrors = [];
|
|
141453
|
+
if (autoRegister && unregistered.length > 0) {
|
|
141454
|
+
const { nexusRegister: doRegister } = await Promise.resolve().then(() => (init_internal(), internal_exports));
|
|
141455
|
+
for (const projectPath of unregistered) {
|
|
141456
|
+
try {
|
|
141457
|
+
await doRegister(projectPath);
|
|
141458
|
+
autoRegistered.push(projectPath);
|
|
141459
|
+
} catch (err) {
|
|
141460
|
+
autoRegisterErrors.push({
|
|
141461
|
+
path: projectPath,
|
|
141462
|
+
error: err instanceof Error ? err.message : String(err)
|
|
141463
|
+
});
|
|
141464
|
+
}
|
|
141465
|
+
}
|
|
141466
|
+
}
|
|
141467
|
+
try {
|
|
141468
|
+
const { getNexusDb: getNexusDb2 } = await import("@cleocode/core/store/nexus-sqlite");
|
|
141469
|
+
const { nexusAuditLog: auditTable } = await import("@cleocode/core/store/nexus-schema");
|
|
141470
|
+
const { randomUUID: randomUUID15 } = await import("node:crypto");
|
|
141471
|
+
const db = await getNexusDb2();
|
|
141472
|
+
await db.insert(auditTable).values({
|
|
141473
|
+
id: randomUUID15(),
|
|
141474
|
+
action: "projects.scan",
|
|
141475
|
+
domain: "nexus",
|
|
141476
|
+
operation: "projects.scan",
|
|
141477
|
+
success: 1,
|
|
141478
|
+
detailsJson: JSON.stringify({
|
|
141479
|
+
roots,
|
|
141480
|
+
found: candidates.length,
|
|
141481
|
+
unregistered: unregistered.length,
|
|
141482
|
+
registered: registered.length,
|
|
141483
|
+
autoRegistered: autoRegistered.length
|
|
141484
|
+
})
|
|
141485
|
+
});
|
|
141486
|
+
} catch {
|
|
141487
|
+
}
|
|
141488
|
+
const durationMs = Date.now() - startTime;
|
|
141489
|
+
if (jsonOutput) {
|
|
141490
|
+
const data = {
|
|
141491
|
+
roots,
|
|
141492
|
+
unregistered,
|
|
141493
|
+
tally
|
|
141494
|
+
};
|
|
141495
|
+
if (includeExisting) data["registered"] = registered;
|
|
141496
|
+
if (autoRegister) {
|
|
141497
|
+
data["autoRegistered"] = autoRegistered;
|
|
141498
|
+
data["autoRegisterErrors"] = autoRegisterErrors;
|
|
141499
|
+
}
|
|
141500
|
+
process.stdout.write(
|
|
141501
|
+
JSON.stringify(
|
|
141502
|
+
{
|
|
141503
|
+
success: true,
|
|
141504
|
+
data,
|
|
141505
|
+
meta: {
|
|
141506
|
+
operation: "nexus.projects.scan",
|
|
141507
|
+
duration_ms: durationMs,
|
|
141508
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
141509
|
+
}
|
|
141510
|
+
},
|
|
141511
|
+
null,
|
|
141512
|
+
2
|
|
141513
|
+
) + "\n"
|
|
141514
|
+
);
|
|
141515
|
+
} else {
|
|
141516
|
+
process.stdout.write(
|
|
141517
|
+
`
|
|
141518
|
+
[nexus] Scan complete \u2014 ${tally.total} project(s) found (${tally.unregistered} unregistered, ${tally.registered} registered)
|
|
141519
|
+
`
|
|
141520
|
+
);
|
|
141521
|
+
if (unregistered.length > 0) {
|
|
141522
|
+
process.stdout.write("\n Unregistered:\n");
|
|
141523
|
+
for (const p2 of unregistered) {
|
|
141524
|
+
process.stdout.write(` ${p2}
|
|
141525
|
+
`);
|
|
141526
|
+
}
|
|
141527
|
+
if (!autoRegister) {
|
|
141528
|
+
process.stdout.write(
|
|
141529
|
+
"\n Tip: run with --auto-register to register all of the above.\n"
|
|
141530
|
+
);
|
|
141531
|
+
}
|
|
141532
|
+
}
|
|
141533
|
+
if (includeExisting && registered.length > 0) {
|
|
141534
|
+
process.stdout.write("\n Already registered:\n");
|
|
141535
|
+
for (const p2 of registered) {
|
|
141536
|
+
process.stdout.write(` ${p2}
|
|
141537
|
+
`);
|
|
141538
|
+
}
|
|
141539
|
+
}
|
|
141540
|
+
if (autoRegister) {
|
|
141541
|
+
process.stdout.write(
|
|
141542
|
+
`
|
|
141543
|
+
Auto-registered: ${autoRegistered.length} project(s)` + (autoRegisterErrors.length > 0 ? `, ${autoRegisterErrors.length} failed` : "") + "\n"
|
|
141544
|
+
);
|
|
141545
|
+
for (const e of autoRegisterErrors) {
|
|
141546
|
+
process.stdout.write(` FAILED ${e.path}: ${e.error}
|
|
141547
|
+
`);
|
|
141548
|
+
}
|
|
141549
|
+
}
|
|
141550
|
+
}
|
|
141551
|
+
});
|
|
141552
|
+
projects.command("clean").description(
|
|
141553
|
+
"Bulk purge project_registry rows matching path criteria (requires at least one filter flag)"
|
|
141554
|
+
).option("--dry-run", "List matching projects without deleting anything").option("--pattern <regex>", "JS regex matched against project_path").option("--include-temp", "Preset: match paths containing a .temp/ segment").option(
|
|
141555
|
+
"--include-tests",
|
|
141556
|
+
"Preset: match paths containing tmp/test/fixture/scratch/sandbox segments"
|
|
141557
|
+
).option("--unhealthy", 'Also match rows where health_status is "unhealthy"').option("--never-indexed", "Also match rows where last_indexed IS NULL").option("--yes", "Skip confirmation prompt (still shows preview)").option("--json", "Output as JSON (LAFS envelope format)").action(async (opts) => {
|
|
141558
|
+
const startTime = Date.now();
|
|
141559
|
+
const jsonOutput = !!opts["json"];
|
|
141560
|
+
const dryRun = !!opts["dryRun"];
|
|
141561
|
+
const skipPrompt = !!opts["yes"];
|
|
141562
|
+
const patternRaw = opts["pattern"];
|
|
141563
|
+
const includeTemp = !!opts["includeTemp"];
|
|
141564
|
+
const includeTests = !!opts["includeTests"];
|
|
141565
|
+
const matchUnhealthy = !!opts["unhealthy"];
|
|
141566
|
+
const matchNeverIndexed = !!opts["neverIndexed"];
|
|
141567
|
+
const hasCriteria = patternRaw !== void 0 || includeTemp || includeTests || matchUnhealthy || matchNeverIndexed;
|
|
141568
|
+
if (!hasCriteria) {
|
|
141569
|
+
const errMsg = "No filter criteria provided. Refusing to purge all projects without explicit criteria.\nUse at least one of: --pattern <regex>, --include-temp, --include-tests, --unhealthy, --never-indexed";
|
|
141570
|
+
if (jsonOutput) {
|
|
141571
|
+
process.stdout.write(
|
|
141572
|
+
JSON.stringify(
|
|
141573
|
+
{
|
|
141574
|
+
success: false,
|
|
141575
|
+
error: { code: "E_NO_CRITERIA", message: errMsg },
|
|
141576
|
+
meta: {
|
|
141577
|
+
operation: "nexus.projects.clean",
|
|
141578
|
+
duration_ms: Date.now() - startTime,
|
|
141579
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
141580
|
+
}
|
|
141581
|
+
},
|
|
141582
|
+
null,
|
|
141583
|
+
2
|
|
141584
|
+
) + "\n"
|
|
141585
|
+
);
|
|
141586
|
+
} else {
|
|
141587
|
+
process.stderr.write(`[nexus] Error: ${errMsg}
|
|
141588
|
+
`);
|
|
141589
|
+
}
|
|
141590
|
+
process.exitCode = 6;
|
|
141591
|
+
return;
|
|
141592
|
+
}
|
|
141593
|
+
let patternRegex = null;
|
|
141594
|
+
if (patternRaw !== void 0) {
|
|
141595
|
+
try {
|
|
141596
|
+
patternRegex = new RegExp(patternRaw);
|
|
141597
|
+
} catch (err) {
|
|
141598
|
+
const msg = `Invalid --pattern regex: ${err instanceof Error ? err.message : String(err)}`;
|
|
141599
|
+
if (jsonOutput) {
|
|
141600
|
+
process.stdout.write(
|
|
141601
|
+
JSON.stringify(
|
|
141602
|
+
{
|
|
141603
|
+
success: false,
|
|
141604
|
+
error: { code: "E_INVALID_PATTERN", message: msg },
|
|
141605
|
+
meta: {
|
|
141606
|
+
operation: "nexus.projects.clean",
|
|
141607
|
+
duration_ms: Date.now() - startTime,
|
|
141608
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
141609
|
+
}
|
|
141610
|
+
},
|
|
141611
|
+
null,
|
|
141612
|
+
2
|
|
141613
|
+
) + "\n"
|
|
141614
|
+
);
|
|
141615
|
+
} else {
|
|
141616
|
+
process.stderr.write(`[nexus] Error: ${msg}
|
|
141617
|
+
`);
|
|
141618
|
+
}
|
|
141619
|
+
process.exitCode = 6;
|
|
141620
|
+
return;
|
|
141621
|
+
}
|
|
141622
|
+
}
|
|
141623
|
+
const TEMP_RE = /(^|\/)\.temp(\/|$)/;
|
|
141624
|
+
const TESTS_RE = /(^|\/)(tmp|test|fixture|scratch|sandbox)(\/|$)/;
|
|
141625
|
+
function matchesCriteria(projectPath, healthStatus, lastIndexed) {
|
|
141626
|
+
if (patternRegex?.test(projectPath)) return true;
|
|
141627
|
+
if (includeTemp && TEMP_RE.test(projectPath)) return true;
|
|
141628
|
+
if (includeTests && TESTS_RE.test(projectPath)) return true;
|
|
141629
|
+
if (matchUnhealthy && healthStatus === "unhealthy") return true;
|
|
141630
|
+
if (matchNeverIndexed && lastIndexed === null) return true;
|
|
141631
|
+
return false;
|
|
141632
|
+
}
|
|
141633
|
+
try {
|
|
141634
|
+
const { getNexusDb: getNexusDb2 } = await import("@cleocode/core/store/nexus-sqlite");
|
|
141635
|
+
const { projectRegistry: regTable, nexusAuditLog: auditTable } = await import("@cleocode/core/store/nexus-schema");
|
|
141636
|
+
const { randomUUID: randomUUID15 } = await import("node:crypto");
|
|
141637
|
+
const { inArray: inArray8 } = await import("drizzle-orm");
|
|
141638
|
+
const db = await getNexusDb2();
|
|
141639
|
+
const allRows = await db.select({
|
|
141640
|
+
projectId: regTable.projectId,
|
|
141641
|
+
projectPath: regTable.projectPath,
|
|
141642
|
+
healthStatus: regTable.healthStatus,
|
|
141643
|
+
lastIndexed: regTable.lastIndexed
|
|
141644
|
+
}).from(regTable);
|
|
141645
|
+
const matches = allRows.filter(
|
|
141646
|
+
(row) => matchesCriteria(row.projectPath, row.healthStatus, row.lastIndexed)
|
|
141647
|
+
);
|
|
141648
|
+
const totalCount = allRows.length;
|
|
141649
|
+
const matchCount = matches.length;
|
|
141650
|
+
const samplePaths = matches.slice(0, 10).map((r) => r.projectPath);
|
|
141651
|
+
if (!jsonOutput) {
|
|
141652
|
+
process.stdout.write(
|
|
141653
|
+
`[nexus] Clean preview \u2014 ${matchCount} project(s) of ${totalCount} total match criteria:
|
|
141654
|
+
`
|
|
141655
|
+
);
|
|
141656
|
+
if (matchCount === 0) {
|
|
141657
|
+
process.stdout.write(" (no matches)\n");
|
|
141658
|
+
} else {
|
|
141659
|
+
for (const p2 of samplePaths) {
|
|
141660
|
+
process.stdout.write(` ${p2}
|
|
141661
|
+
`);
|
|
141662
|
+
}
|
|
141663
|
+
if (matchCount > 10) {
|
|
141664
|
+
process.stdout.write(` ... and ${matchCount - 10} more
|
|
141665
|
+
`);
|
|
141666
|
+
}
|
|
141667
|
+
}
|
|
141668
|
+
}
|
|
141669
|
+
if (matchCount === 0) {
|
|
141670
|
+
const durationMs2 = Date.now() - startTime;
|
|
141671
|
+
if (jsonOutput) {
|
|
141672
|
+
process.stdout.write(
|
|
141673
|
+
JSON.stringify(
|
|
141674
|
+
{
|
|
141675
|
+
success: true,
|
|
141676
|
+
data: {
|
|
141677
|
+
dryRun,
|
|
141678
|
+
matched: 0,
|
|
141679
|
+
purged: 0,
|
|
141680
|
+
remaining: totalCount,
|
|
141681
|
+
sample: []
|
|
141682
|
+
},
|
|
141683
|
+
meta: {
|
|
141684
|
+
operation: "nexus.projects.clean",
|
|
141685
|
+
duration_ms: durationMs2,
|
|
141686
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
141687
|
+
}
|
|
141688
|
+
},
|
|
141689
|
+
null,
|
|
141690
|
+
2
|
|
141691
|
+
) + "\n"
|
|
141692
|
+
);
|
|
141693
|
+
}
|
|
141694
|
+
return;
|
|
141695
|
+
}
|
|
141696
|
+
if (dryRun) {
|
|
141697
|
+
const durationMs2 = Date.now() - startTime;
|
|
141698
|
+
if (jsonOutput) {
|
|
141699
|
+
process.stdout.write(
|
|
141700
|
+
JSON.stringify(
|
|
141701
|
+
{
|
|
141702
|
+
success: true,
|
|
141703
|
+
data: {
|
|
141704
|
+
dryRun: true,
|
|
141705
|
+
matched: matchCount,
|
|
141706
|
+
purged: 0,
|
|
141707
|
+
remaining: totalCount,
|
|
141708
|
+
sample: samplePaths
|
|
141709
|
+
},
|
|
141710
|
+
meta: {
|
|
141711
|
+
operation: "nexus.projects.clean",
|
|
141712
|
+
duration_ms: durationMs2,
|
|
141713
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
141714
|
+
}
|
|
141715
|
+
},
|
|
141716
|
+
null,
|
|
141717
|
+
2
|
|
141718
|
+
) + "\n"
|
|
141719
|
+
);
|
|
141720
|
+
} else {
|
|
141721
|
+
process.stdout.write(
|
|
141722
|
+
`[nexus] Dry-run \u2014 ${matchCount} project(s) would be purged. Rerun without --dry-run to delete.
|
|
141723
|
+
`
|
|
141724
|
+
);
|
|
141725
|
+
}
|
|
141726
|
+
return;
|
|
141727
|
+
}
|
|
141728
|
+
if (!skipPrompt) {
|
|
141729
|
+
const { createInterface: createInterface3 } = await import("node:readline");
|
|
141730
|
+
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
141731
|
+
const confirmed = await new Promise((resolve17) => {
|
|
141732
|
+
rl.question(
|
|
141733
|
+
`
|
|
141734
|
+
[nexus] Delete ${matchCount} project(s) from the registry? [y/N] `,
|
|
141735
|
+
(answer) => {
|
|
141736
|
+
rl.close();
|
|
141737
|
+
resolve17(answer.trim().toLowerCase() === "y");
|
|
141738
|
+
}
|
|
141739
|
+
);
|
|
141740
|
+
});
|
|
141741
|
+
if (!confirmed) {
|
|
141742
|
+
process.stdout.write("[nexus] Aborted \u2014 no projects deleted.\n");
|
|
141743
|
+
return;
|
|
141744
|
+
}
|
|
141745
|
+
}
|
|
141746
|
+
const idsToDelete = matches.map((r) => r.projectId);
|
|
141747
|
+
await db.delete(regTable).where(inArray8(regTable.projectId, idsToDelete));
|
|
141748
|
+
const remaining = totalCount - matchCount;
|
|
141749
|
+
try {
|
|
141750
|
+
await db.insert(auditTable).values({
|
|
141751
|
+
id: randomUUID15(),
|
|
141752
|
+
action: "projects.clean",
|
|
141753
|
+
domain: "nexus",
|
|
141754
|
+
operation: "projects.clean",
|
|
141755
|
+
success: 1,
|
|
141756
|
+
detailsJson: JSON.stringify({
|
|
141757
|
+
pattern: patternRaw ?? null,
|
|
141758
|
+
presets: {
|
|
141759
|
+
includeTemp,
|
|
141760
|
+
includeTests,
|
|
141761
|
+
matchUnhealthy,
|
|
141762
|
+
matchNeverIndexed
|
|
141763
|
+
},
|
|
141764
|
+
count: matchCount,
|
|
141765
|
+
sample: samplePaths
|
|
141766
|
+
})
|
|
141767
|
+
});
|
|
141768
|
+
} catch {
|
|
141769
|
+
}
|
|
141770
|
+
const durationMs = Date.now() - startTime;
|
|
141771
|
+
if (jsonOutput) {
|
|
141772
|
+
process.stdout.write(
|
|
141773
|
+
JSON.stringify(
|
|
141774
|
+
{
|
|
141775
|
+
success: true,
|
|
141776
|
+
data: {
|
|
141777
|
+
dryRun: false,
|
|
141778
|
+
matched: matchCount,
|
|
141779
|
+
purged: matchCount,
|
|
141780
|
+
remaining,
|
|
141781
|
+
sample: samplePaths
|
|
141782
|
+
},
|
|
141783
|
+
meta: {
|
|
141784
|
+
operation: "nexus.projects.clean",
|
|
141785
|
+
duration_ms: durationMs,
|
|
141786
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
141787
|
+
}
|
|
141788
|
+
},
|
|
141789
|
+
null,
|
|
141790
|
+
2
|
|
141791
|
+
) + "\n"
|
|
141792
|
+
);
|
|
141793
|
+
} else {
|
|
141794
|
+
process.stdout.write(
|
|
141795
|
+
`[nexus] Purged ${matchCount} project(s). ${remaining} project(s) remaining in registry.
|
|
141796
|
+
`
|
|
141797
|
+
);
|
|
141798
|
+
}
|
|
141799
|
+
} catch (err) {
|
|
141800
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
141801
|
+
const durationMs = Date.now() - startTime;
|
|
141802
|
+
if (jsonOutput) {
|
|
141803
|
+
process.stdout.write(
|
|
141804
|
+
JSON.stringify(
|
|
141805
|
+
{
|
|
141806
|
+
success: false,
|
|
141807
|
+
error: { code: "E_CLEAN_FAILED", message: msg },
|
|
141808
|
+
meta: {
|
|
141809
|
+
operation: "nexus.projects.clean",
|
|
141810
|
+
duration_ms: durationMs,
|
|
141811
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
141812
|
+
}
|
|
141813
|
+
},
|
|
141814
|
+
null,
|
|
141815
|
+
2
|
|
141816
|
+
) + "\n"
|
|
141817
|
+
);
|
|
141818
|
+
} else {
|
|
141819
|
+
process.stderr.write(`[nexus] Error: ${msg}
|
|
141324
141820
|
`);
|
|
141325
141821
|
}
|
|
141326
141822
|
process.exitCode = 1;
|