omo-suites 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/cli/omocs.js +447 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to OMO Suites will be documented in this file.
|
|
|
5
5
|
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.12.0] - 2026-03-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `omocs compact config` — scan and clean stale config entries (duplicate plugins, empty MCP servers, stale provider keys)
|
|
12
|
+
- `omocs compact memory` — trim old workspace memory notes with archiving (--keep, --older-than)
|
|
13
|
+
- `omocs compact index` — clean up orphaned workspace indexes for non-existent workspaces
|
|
14
|
+
- `omocs compact stats` — trim old stats data with archiving (--keep-days)
|
|
15
|
+
- `omocs compact all` — run all compact operations with combined summary
|
|
16
|
+
- All compact subcommands default to dry-run; use `--fix` to apply changes
|
|
17
|
+
- Automatic backup before any destructive operation
|
|
18
|
+
|
|
8
19
|
## [1.11.0] - 2026-03-25
|
|
9
20
|
|
|
10
21
|
### Added
|
package/dist/cli/omocs.js
CHANGED
|
@@ -39804,6 +39804,451 @@ var init_index_cmd = __esm(() => {
|
|
|
39804
39804
|
IGNORED_DIRS = ["node_modules", ".git", "dist", "build", ".next", "__pycache__", "coverage", ".opencode"];
|
|
39805
39805
|
});
|
|
39806
39806
|
|
|
39807
|
+
// src/commands/compact.ts
|
|
39808
|
+
import { existsSync as existsSync21, readFileSync as readFileSync22, writeFileSync as writeFileSync15, mkdirSync as mkdirSync12, readdirSync as readdirSync6, statSync as statSync6, unlinkSync as unlinkSync5, copyFileSync } from "fs";
|
|
39809
|
+
import { join as join15, basename as basename2 } from "path";
|
|
39810
|
+
import { homedir as homedir11 } from "os";
|
|
39811
|
+
function ensureDir(dir) {
|
|
39812
|
+
if (!existsSync21(dir))
|
|
39813
|
+
mkdirSync12(dir, { recursive: true });
|
|
39814
|
+
}
|
|
39815
|
+
function backupFile(filePath) {
|
|
39816
|
+
if (!existsSync21(filePath))
|
|
39817
|
+
return null;
|
|
39818
|
+
const bakPath = filePath + ".bak." + Date.now();
|
|
39819
|
+
copyFileSync(filePath, bakPath);
|
|
39820
|
+
return bakPath;
|
|
39821
|
+
}
|
|
39822
|
+
function readJsonSafe(filePath) {
|
|
39823
|
+
try {
|
|
39824
|
+
return JSON.parse(readFileSync22(filePath, "utf-8"));
|
|
39825
|
+
} catch {
|
|
39826
|
+
return null;
|
|
39827
|
+
}
|
|
39828
|
+
}
|
|
39829
|
+
function formatBytes(bytes) {
|
|
39830
|
+
if (bytes < 1024)
|
|
39831
|
+
return `${bytes} B`;
|
|
39832
|
+
if (bytes < 1024 * 1024)
|
|
39833
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
39834
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
39835
|
+
}
|
|
39836
|
+
function scanConfigIssues() {
|
|
39837
|
+
const issues = [];
|
|
39838
|
+
const configLocations = [
|
|
39839
|
+
join15(process.cwd(), "opencode.json"),
|
|
39840
|
+
join15(process.cwd(), ".opencode", "opencode.json"),
|
|
39841
|
+
join15(homedir11(), ".config", "opencode", "opencode.json")
|
|
39842
|
+
];
|
|
39843
|
+
const omocLocations = [
|
|
39844
|
+
join15(process.cwd(), "oh-my-opencode.json"),
|
|
39845
|
+
join15(process.cwd(), ".opencode", "oh-my-opencode.json"),
|
|
39846
|
+
join15(homedir11(), ".config", "opencode", "oh-my-opencode.json")
|
|
39847
|
+
];
|
|
39848
|
+
for (const loc of configLocations) {
|
|
39849
|
+
if (!existsSync21(loc))
|
|
39850
|
+
continue;
|
|
39851
|
+
const data = readJsonSafe(loc);
|
|
39852
|
+
if (!data) {
|
|
39853
|
+
issues.push({ file: loc, type: "invalid-json", description: "File is not valid JSON", fixable: false });
|
|
39854
|
+
continue;
|
|
39855
|
+
}
|
|
39856
|
+
if (Array.isArray(data.plugin)) {
|
|
39857
|
+
const seen = new Set;
|
|
39858
|
+
const dupes = [];
|
|
39859
|
+
for (const p of data.plugin) {
|
|
39860
|
+
const name = typeof p === "string" ? p : p?.name;
|
|
39861
|
+
if (name && seen.has(name))
|
|
39862
|
+
dupes.push(name);
|
|
39863
|
+
if (name)
|
|
39864
|
+
seen.add(name);
|
|
39865
|
+
}
|
|
39866
|
+
if (dupes.length > 0) {
|
|
39867
|
+
issues.push({ file: loc, type: "duplicate-plugins", description: `Duplicate plugins: ${dupes.join(", ")}`, fixable: true });
|
|
39868
|
+
}
|
|
39869
|
+
}
|
|
39870
|
+
if (data.mcpServers && typeof data.mcpServers === "object") {
|
|
39871
|
+
for (const [key, val] of Object.entries(data.mcpServers)) {
|
|
39872
|
+
if (!val || typeof val === "object" && Object.keys(val).length === 0) {
|
|
39873
|
+
issues.push({ file: loc, type: "empty-mcp", description: `Empty MCP server config: "${key}"`, fixable: true });
|
|
39874
|
+
}
|
|
39875
|
+
}
|
|
39876
|
+
}
|
|
39877
|
+
}
|
|
39878
|
+
for (const loc of omocLocations) {
|
|
39879
|
+
if (!existsSync21(loc))
|
|
39880
|
+
continue;
|
|
39881
|
+
const data = readJsonSafe(loc);
|
|
39882
|
+
if (!data) {
|
|
39883
|
+
issues.push({ file: loc, type: "invalid-json", description: "File is not valid JSON", fixable: false });
|
|
39884
|
+
continue;
|
|
39885
|
+
}
|
|
39886
|
+
if (data.providers && typeof data.providers === "object") {
|
|
39887
|
+
for (const [provider, config] of Object.entries(data.providers)) {
|
|
39888
|
+
const cfg = config;
|
|
39889
|
+
if (cfg && cfg.apiKey === "") {
|
|
39890
|
+
issues.push({ file: loc, type: "empty-api-key", description: `Empty API key for provider: "${provider}"`, fixable: true });
|
|
39891
|
+
}
|
|
39892
|
+
}
|
|
39893
|
+
}
|
|
39894
|
+
}
|
|
39895
|
+
return issues;
|
|
39896
|
+
}
|
|
39897
|
+
function fixConfigIssues(issues) {
|
|
39898
|
+
let fixed = 0;
|
|
39899
|
+
const fileCache = {};
|
|
39900
|
+
for (const issue of issues) {
|
|
39901
|
+
if (!issue.fixable)
|
|
39902
|
+
continue;
|
|
39903
|
+
if (!fileCache[issue.file]) {
|
|
39904
|
+
const data = readJsonSafe(issue.file);
|
|
39905
|
+
if (!data)
|
|
39906
|
+
continue;
|
|
39907
|
+
fileCache[issue.file] = { data, modified: false };
|
|
39908
|
+
}
|
|
39909
|
+
const entry = fileCache[issue.file];
|
|
39910
|
+
if (issue.type === "duplicate-plugins" && Array.isArray(entry.data.plugin)) {
|
|
39911
|
+
const seen = new Set;
|
|
39912
|
+
entry.data.plugin = entry.data.plugin.filter((p) => {
|
|
39913
|
+
const name = typeof p === "string" ? p : p?.name;
|
|
39914
|
+
if (name && seen.has(name))
|
|
39915
|
+
return false;
|
|
39916
|
+
if (name)
|
|
39917
|
+
seen.add(name);
|
|
39918
|
+
return true;
|
|
39919
|
+
});
|
|
39920
|
+
entry.modified = true;
|
|
39921
|
+
fixed++;
|
|
39922
|
+
}
|
|
39923
|
+
if (issue.type === "empty-mcp" && entry.data.mcpServers) {
|
|
39924
|
+
const key = issue.description.match(/"([^"]+)"/)?.[1];
|
|
39925
|
+
if (key && key in entry.data.mcpServers) {
|
|
39926
|
+
delete entry.data.mcpServers[key];
|
|
39927
|
+
entry.modified = true;
|
|
39928
|
+
fixed++;
|
|
39929
|
+
}
|
|
39930
|
+
}
|
|
39931
|
+
if (issue.type === "empty-api-key" && entry.data.providers) {
|
|
39932
|
+
const provider = issue.description.match(/"([^"]+)"/)?.[1];
|
|
39933
|
+
if (provider && entry.data.providers[provider]) {
|
|
39934
|
+
delete entry.data.providers[provider];
|
|
39935
|
+
entry.modified = true;
|
|
39936
|
+
fixed++;
|
|
39937
|
+
}
|
|
39938
|
+
}
|
|
39939
|
+
}
|
|
39940
|
+
for (const [filePath, entry] of Object.entries(fileCache)) {
|
|
39941
|
+
if (entry.modified) {
|
|
39942
|
+
backupFile(filePath);
|
|
39943
|
+
writeFileSync15(filePath, JSON.stringify(entry.data, null, 2) + `
|
|
39944
|
+
`, "utf-8");
|
|
39945
|
+
}
|
|
39946
|
+
}
|
|
39947
|
+
return fixed;
|
|
39948
|
+
}
|
|
39949
|
+
function compactMemory(keepEntries, olderThanDays, fix) {
|
|
39950
|
+
const result = { filesScanned: 0, entriesRemoved: 0, bytesFreed: 0, archived: 0 };
|
|
39951
|
+
if (!existsSync21(MEMORY_DIR2))
|
|
39952
|
+
return result;
|
|
39953
|
+
const files = readdirSync6(MEMORY_DIR2).filter((f) => f.endsWith(".json") && f !== "archive");
|
|
39954
|
+
const cutoff = Date.now() - olderThanDays * 24 * 60 * 60 * 1000;
|
|
39955
|
+
for (const file of files) {
|
|
39956
|
+
const filePath = join15(MEMORY_DIR2, file);
|
|
39957
|
+
const sizeBefore = statSync6(filePath).size;
|
|
39958
|
+
result.filesScanned++;
|
|
39959
|
+
const data = readJsonSafe(filePath);
|
|
39960
|
+
if (!data || !Array.isArray(data.notes))
|
|
39961
|
+
continue;
|
|
39962
|
+
const originalCount = data.notes.length;
|
|
39963
|
+
let filtered = data.notes.filter((note) => {
|
|
39964
|
+
const noteTime = new Date(note.timestamp).getTime();
|
|
39965
|
+
return noteTime > cutoff;
|
|
39966
|
+
});
|
|
39967
|
+
if (filtered.length > keepEntries) {
|
|
39968
|
+
filtered = filtered.slice(-keepEntries);
|
|
39969
|
+
}
|
|
39970
|
+
const removed = originalCount - filtered.length;
|
|
39971
|
+
if (removed === 0)
|
|
39972
|
+
continue;
|
|
39973
|
+
result.entriesRemoved += removed;
|
|
39974
|
+
if (fix) {
|
|
39975
|
+
ensureDir(ARCHIVE_DIR);
|
|
39976
|
+
const removedNotes = data.notes.filter((n) => !filtered.includes(n));
|
|
39977
|
+
if (removedNotes.length > 0) {
|
|
39978
|
+
const archivePath = join15(ARCHIVE_DIR, `${basename2(file, ".json")}-${Date.now()}.json`);
|
|
39979
|
+
writeFileSync15(archivePath, JSON.stringify({ archived: new Date().toISOString(), notes: removedNotes }, null, 2), "utf-8");
|
|
39980
|
+
result.archived++;
|
|
39981
|
+
}
|
|
39982
|
+
data.notes = filtered;
|
|
39983
|
+
writeFileSync15(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
39984
|
+
const sizeAfter = statSync6(filePath).size;
|
|
39985
|
+
result.bytesFreed += sizeBefore - sizeAfter;
|
|
39986
|
+
} else {
|
|
39987
|
+
result.bytesFreed += Math.round(sizeBefore * (removed / originalCount));
|
|
39988
|
+
}
|
|
39989
|
+
}
|
|
39990
|
+
return result;
|
|
39991
|
+
}
|
|
39992
|
+
function compactIndex(fix, rebuildAll) {
|
|
39993
|
+
const result = { totalIndexes: 0, orphanedRemoved: 0, bytesFreed: 0 };
|
|
39994
|
+
if (!existsSync21(INDEX_DIR2))
|
|
39995
|
+
return result;
|
|
39996
|
+
const dirs = readdirSync6(INDEX_DIR2).filter((d) => {
|
|
39997
|
+
const p = join15(INDEX_DIR2, d);
|
|
39998
|
+
return statSync6(p).isDirectory();
|
|
39999
|
+
});
|
|
40000
|
+
result.totalIndexes = dirs.length;
|
|
40001
|
+
for (const dir of dirs) {
|
|
40002
|
+
const indexPath = join15(INDEX_DIR2, dir, "index.json");
|
|
40003
|
+
if (!existsSync21(indexPath))
|
|
40004
|
+
continue;
|
|
40005
|
+
const data = readJsonSafe(indexPath);
|
|
40006
|
+
if (!data)
|
|
40007
|
+
continue;
|
|
40008
|
+
if (data.path && !existsSync21(data.path)) {
|
|
40009
|
+
const size = statSync6(indexPath).size;
|
|
40010
|
+
result.orphanedRemoved++;
|
|
40011
|
+
result.bytesFreed += size;
|
|
40012
|
+
if (fix) {
|
|
40013
|
+
unlinkSync5(indexPath);
|
|
40014
|
+
try {
|
|
40015
|
+
const remaining = readdirSync6(join15(INDEX_DIR2, dir));
|
|
40016
|
+
if (remaining.length === 0) {
|
|
40017
|
+
unlinkSync5(join15(INDEX_DIR2, dir));
|
|
40018
|
+
}
|
|
40019
|
+
} catch {}
|
|
40020
|
+
}
|
|
40021
|
+
}
|
|
40022
|
+
}
|
|
40023
|
+
return result;
|
|
40024
|
+
}
|
|
40025
|
+
function compactStats(keepDays, fix) {
|
|
40026
|
+
const result = { totalEntries: 0, entriesRemoved: 0, bytesFreed: 0 };
|
|
40027
|
+
if (!existsSync21(STATS_FILE))
|
|
40028
|
+
return result;
|
|
40029
|
+
const sizeBefore = statSync6(STATS_FILE).size;
|
|
40030
|
+
const data = readJsonSafe(STATS_FILE);
|
|
40031
|
+
if (!data)
|
|
40032
|
+
return result;
|
|
40033
|
+
const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1000;
|
|
40034
|
+
if (data.daily && typeof data.daily === "object") {
|
|
40035
|
+
const dates = Object.keys(data.daily);
|
|
40036
|
+
result.totalEntries = dates.length;
|
|
40037
|
+
const toRemove = dates.filter((d) => new Date(d).getTime() < cutoff);
|
|
40038
|
+
result.entriesRemoved = toRemove.length;
|
|
40039
|
+
if (fix && toRemove.length > 0) {
|
|
40040
|
+
ensureDir(join15(OMOCS_DIR, "stats-archive"));
|
|
40041
|
+
const archivePath = join15(OMOCS_DIR, "stats-archive", `stats-${Date.now()}.json`);
|
|
40042
|
+
const archived = {};
|
|
40043
|
+
for (const d of toRemove) {
|
|
40044
|
+
archived[d] = data.daily[d];
|
|
40045
|
+
delete data.daily[d];
|
|
40046
|
+
}
|
|
40047
|
+
writeFileSync15(archivePath, JSON.stringify({ archivedAt: new Date().toISOString(), daily: archived }, null, 2), "utf-8");
|
|
40048
|
+
backupFile(STATS_FILE);
|
|
40049
|
+
writeFileSync15(STATS_FILE, JSON.stringify(data, null, 2), "utf-8");
|
|
40050
|
+
const sizeAfter = statSync6(STATS_FILE).size;
|
|
40051
|
+
result.bytesFreed = sizeBefore - sizeAfter;
|
|
40052
|
+
} else {
|
|
40053
|
+
result.bytesFreed = Math.round(sizeBefore * (result.entriesRemoved / Math.max(result.totalEntries, 1)));
|
|
40054
|
+
}
|
|
40055
|
+
} else if (Array.isArray(data)) {
|
|
40056
|
+
result.totalEntries = data.length;
|
|
40057
|
+
const filtered = data.filter((entry) => {
|
|
40058
|
+
const ts = entry.timestamp || entry.date || entry.createdAt;
|
|
40059
|
+
return ts && new Date(ts).getTime() > cutoff;
|
|
40060
|
+
});
|
|
40061
|
+
result.entriesRemoved = data.length - filtered.length;
|
|
40062
|
+
if (fix && result.entriesRemoved > 0) {
|
|
40063
|
+
backupFile(STATS_FILE);
|
|
40064
|
+
writeFileSync15(STATS_FILE, JSON.stringify(filtered, null, 2), "utf-8");
|
|
40065
|
+
const sizeAfter = statSync6(STATS_FILE).size;
|
|
40066
|
+
result.bytesFreed = sizeBefore - sizeAfter;
|
|
40067
|
+
} else {
|
|
40068
|
+
result.bytesFreed = Math.round(sizeBefore * (result.entriesRemoved / Math.max(result.totalEntries, 1)));
|
|
40069
|
+
}
|
|
40070
|
+
}
|
|
40071
|
+
return result;
|
|
40072
|
+
}
|
|
40073
|
+
function registerCompactCommand(program2) {
|
|
40074
|
+
const compact = program2.command("compact").description("Clean up config, memory, indexes, and stats data");
|
|
40075
|
+
compact.command("config").description("Scan and clean stale config entries").option("--fix", "Apply fixes (default: dry-run)").action((opts) => {
|
|
40076
|
+
try {
|
|
40077
|
+
heading("Compact Config");
|
|
40078
|
+
const issues = scanConfigIssues();
|
|
40079
|
+
if (issues.length === 0) {
|
|
40080
|
+
successBox("No config issues found! Everything is clean.");
|
|
40081
|
+
return;
|
|
40082
|
+
}
|
|
40083
|
+
console.log(source_default.yellow(`
|
|
40084
|
+
Found ${issues.length} issue(s):
|
|
40085
|
+
`));
|
|
40086
|
+
for (const issue of issues) {
|
|
40087
|
+
const fixable = issue.fixable ? source_default.green("[fixable]") : source_default.red("[manual]");
|
|
40088
|
+
const icon = issue.fixable ? "⚠️" : "❌";
|
|
40089
|
+
console.log(` ${icon} ${fixable} ${source_default.dim(issue.file)}`);
|
|
40090
|
+
console.log(` ${issue.description}
|
|
40091
|
+
`);
|
|
40092
|
+
}
|
|
40093
|
+
if (opts.fix) {
|
|
40094
|
+
const fixable = issues.filter((i) => i.fixable);
|
|
40095
|
+
if (fixable.length === 0) {
|
|
40096
|
+
warn("No auto-fixable issues found.");
|
|
40097
|
+
return;
|
|
40098
|
+
}
|
|
40099
|
+
const fixed = fixConfigIssues(fixable);
|
|
40100
|
+
success(`Fixed ${fixed} issue(s). Backups created (.bak).`);
|
|
40101
|
+
} else {
|
|
40102
|
+
const fixable = issues.filter((i) => i.fixable).length;
|
|
40103
|
+
info(`Run ${source_default.cyan("omocs compact config --fix")} to auto-fix ${fixable} issue(s).`);
|
|
40104
|
+
}
|
|
40105
|
+
} catch (err) {
|
|
40106
|
+
handleError(err);
|
|
40107
|
+
}
|
|
40108
|
+
});
|
|
40109
|
+
compact.command("memory").description("Trim old workspace memory notes").option("--keep <n>", "Keep last N entries per workspace", "50").option("--older-than <days>", "Remove entries older than N days", "30").option("--fix", "Apply cleanup (default: dry-run)").action((opts) => {
|
|
40110
|
+
try {
|
|
40111
|
+
heading("Compact Memory");
|
|
40112
|
+
const keep = parseInt(opts.keep) || 50;
|
|
40113
|
+
const olderThan = parseInt(opts.olderThan) || 30;
|
|
40114
|
+
const result = compactMemory(keep, olderThan, !!opts.fix);
|
|
40115
|
+
if (result.filesScanned === 0) {
|
|
40116
|
+
info("No memory files found.");
|
|
40117
|
+
return;
|
|
40118
|
+
}
|
|
40119
|
+
console.log(`
|
|
40120
|
+
${source_default.dim("Files scanned:")} ${result.filesScanned}`);
|
|
40121
|
+
console.log(` ${source_default.dim("Entries to remove:")} ${result.entriesRemoved}`);
|
|
40122
|
+
console.log(` ${source_default.dim("Space savings:")} ~${formatBytes(result.bytesFreed)}`);
|
|
40123
|
+
if (result.entriesRemoved === 0) {
|
|
40124
|
+
successBox("Memory is already clean!");
|
|
40125
|
+
} else if (opts.fix) {
|
|
40126
|
+
success(`Removed ${result.entriesRemoved} old entries. ${result.archived} archive(s) created.`);
|
|
40127
|
+
} else {
|
|
40128
|
+
info(`Run ${source_default.cyan("omocs compact memory --fix")} to clean up.`);
|
|
40129
|
+
}
|
|
40130
|
+
} catch (err) {
|
|
40131
|
+
handleError(err);
|
|
40132
|
+
}
|
|
40133
|
+
});
|
|
40134
|
+
compact.command("index").description("Clean up orphaned workspace indexes").option("--all", "Rebuild all indexes").option("--fix", "Apply cleanup (default: dry-run)").action((opts) => {
|
|
40135
|
+
try {
|
|
40136
|
+
heading("Compact Index");
|
|
40137
|
+
const result = compactIndex(!!opts.fix, !!opts.all);
|
|
40138
|
+
if (result.totalIndexes === 0) {
|
|
40139
|
+
info("No workspace indexes found.");
|
|
40140
|
+
return;
|
|
40141
|
+
}
|
|
40142
|
+
console.log(`
|
|
40143
|
+
${source_default.dim("Total indexes:")} ${result.totalIndexes}`);
|
|
40144
|
+
console.log(` ${source_default.dim("Orphaned:")} ${result.orphanedRemoved}`);
|
|
40145
|
+
console.log(` ${source_default.dim("Space savings:")} ~${formatBytes(result.bytesFreed)}`);
|
|
40146
|
+
if (result.orphanedRemoved === 0) {
|
|
40147
|
+
successBox("All indexes are valid!");
|
|
40148
|
+
} else if (opts.fix) {
|
|
40149
|
+
success(`Removed ${result.orphanedRemoved} orphaned index(es). Freed ~${formatBytes(result.bytesFreed)}.`);
|
|
40150
|
+
} else {
|
|
40151
|
+
info(`Run ${source_default.cyan("omocs compact index --fix")} to clean up.`);
|
|
40152
|
+
}
|
|
40153
|
+
} catch (err) {
|
|
40154
|
+
handleError(err);
|
|
40155
|
+
}
|
|
40156
|
+
});
|
|
40157
|
+
compact.command("stats").description("Trim old stats data").option("--keep-days <n>", "Keep last N days of stats", "30").option("--fix", "Apply cleanup (default: dry-run)").action((opts) => {
|
|
40158
|
+
try {
|
|
40159
|
+
heading("Compact Stats");
|
|
40160
|
+
const keepDays = parseInt(opts.keepDays) || 30;
|
|
40161
|
+
const result = compactStats(keepDays, !!opts.fix);
|
|
40162
|
+
if (result.totalEntries === 0) {
|
|
40163
|
+
info("No stats data found.");
|
|
40164
|
+
return;
|
|
40165
|
+
}
|
|
40166
|
+
console.log(`
|
|
40167
|
+
${source_default.dim("Total entries:")} ${result.totalEntries}`);
|
|
40168
|
+
console.log(` ${source_default.dim("To remove:")} ${result.entriesRemoved}`);
|
|
40169
|
+
console.log(` ${source_default.dim("Space savings:")} ~${formatBytes(result.bytesFreed)}`);
|
|
40170
|
+
if (result.entriesRemoved === 0) {
|
|
40171
|
+
successBox("Stats are already clean!");
|
|
40172
|
+
} else if (opts.fix) {
|
|
40173
|
+
success(`Removed ${result.entriesRemoved} old entries. Freed ~${formatBytes(result.bytesFreed)}. Backup + archive created.`);
|
|
40174
|
+
} else {
|
|
40175
|
+
info(`Run ${source_default.cyan("omocs compact stats --fix")} to clean up.`);
|
|
40176
|
+
}
|
|
40177
|
+
} catch (err) {
|
|
40178
|
+
handleError(err);
|
|
40179
|
+
}
|
|
40180
|
+
});
|
|
40181
|
+
compact.command("all").description("Run all compact operations").option("--fix", "Apply all fixes (default: dry-run)").action((opts) => {
|
|
40182
|
+
try {
|
|
40183
|
+
const fix = !!opts.fix;
|
|
40184
|
+
heading("Compact Config");
|
|
40185
|
+
const configIssues = scanConfigIssues();
|
|
40186
|
+
if (configIssues.length === 0) {
|
|
40187
|
+
console.log(` ✅ Config: clean`);
|
|
40188
|
+
} else {
|
|
40189
|
+
console.log(` ⚠️ Config: ${configIssues.length} issue(s)`);
|
|
40190
|
+
if (fix) {
|
|
40191
|
+
const fixed = fixConfigIssues(configIssues.filter((i) => i.fixable));
|
|
40192
|
+
console.log(` ✅ Fixed ${fixed} issue(s)`);
|
|
40193
|
+
}
|
|
40194
|
+
}
|
|
40195
|
+
console.log("");
|
|
40196
|
+
heading("Compact Memory");
|
|
40197
|
+
const memResult = compactMemory(50, 30, fix);
|
|
40198
|
+
if (memResult.entriesRemoved === 0) {
|
|
40199
|
+
console.log(` ✅ Memory: clean (${memResult.filesScanned} files)`);
|
|
40200
|
+
} else {
|
|
40201
|
+
console.log(` ⚠️ Memory: ${memResult.entriesRemoved} old entries (~${formatBytes(memResult.bytesFreed)})`);
|
|
40202
|
+
if (fix)
|
|
40203
|
+
console.log(` ✅ Cleaned & archived`);
|
|
40204
|
+
}
|
|
40205
|
+
console.log("");
|
|
40206
|
+
heading("Compact Index");
|
|
40207
|
+
const idxResult = compactIndex(fix, false);
|
|
40208
|
+
if (idxResult.orphanedRemoved === 0) {
|
|
40209
|
+
console.log(` ✅ Index: clean (${idxResult.totalIndexes} indexes)`);
|
|
40210
|
+
} else {
|
|
40211
|
+
console.log(` ⚠️ Index: ${idxResult.orphanedRemoved} orphaned (~${formatBytes(idxResult.bytesFreed)})`);
|
|
40212
|
+
if (fix)
|
|
40213
|
+
console.log(` ✅ Removed orphaned indexes`);
|
|
40214
|
+
}
|
|
40215
|
+
console.log("");
|
|
40216
|
+
heading("Compact Stats");
|
|
40217
|
+
const statsResult = compactStats(30, fix);
|
|
40218
|
+
if (statsResult.entriesRemoved === 0) {
|
|
40219
|
+
console.log(` ✅ Stats: clean (${statsResult.totalEntries} entries)`);
|
|
40220
|
+
} else {
|
|
40221
|
+
console.log(` ⚠️ Stats: ${statsResult.entriesRemoved} old entries (~${formatBytes(statsResult.bytesFreed)})`);
|
|
40222
|
+
if (fix)
|
|
40223
|
+
console.log(` ✅ Trimmed & archived`);
|
|
40224
|
+
}
|
|
40225
|
+
console.log("");
|
|
40226
|
+
const totalIssues = configIssues.length + memResult.entriesRemoved + idxResult.orphanedRemoved + statsResult.entriesRemoved;
|
|
40227
|
+
if (totalIssues === 0) {
|
|
40228
|
+
successBox("Everything is clean! No action needed.");
|
|
40229
|
+
} else if (fix) {
|
|
40230
|
+
successBox(`Compact complete. ${totalIssues} issues resolved.`);
|
|
40231
|
+
} else {
|
|
40232
|
+
console.log(source_default.yellow(`
|
|
40233
|
+
${totalIssues} total issues found.`));
|
|
40234
|
+
info(`Run ${source_default.cyan("omocs compact all --fix")} to clean everything up.`);
|
|
40235
|
+
}
|
|
40236
|
+
} catch (err) {
|
|
40237
|
+
handleError(err);
|
|
40238
|
+
}
|
|
40239
|
+
});
|
|
40240
|
+
}
|
|
40241
|
+
var OMOCS_DIR, MEMORY_DIR2, ARCHIVE_DIR, INDEX_DIR2, STATS_FILE;
|
|
40242
|
+
var init_compact = __esm(() => {
|
|
40243
|
+
init_source();
|
|
40244
|
+
init_ui();
|
|
40245
|
+
OMOCS_DIR = join15(homedir11(), ".omocs");
|
|
40246
|
+
MEMORY_DIR2 = join15(OMOCS_DIR, "memory");
|
|
40247
|
+
ARCHIVE_DIR = join15(MEMORY_DIR2, "archive");
|
|
40248
|
+
INDEX_DIR2 = join15(OMOCS_DIR, "workspaces");
|
|
40249
|
+
STATS_FILE = join15(OMOCS_DIR, "stats.json");
|
|
40250
|
+
});
|
|
40251
|
+
|
|
39807
40252
|
// src/index.ts
|
|
39808
40253
|
var exports_src = {};
|
|
39809
40254
|
__export(exports_src, {
|
|
@@ -39833,6 +40278,7 @@ var init_src = __esm(() => {
|
|
|
39833
40278
|
init_memory();
|
|
39834
40279
|
init_completion();
|
|
39835
40280
|
init_index_cmd();
|
|
40281
|
+
init_compact();
|
|
39836
40282
|
init_find_package_json();
|
|
39837
40283
|
pkg = readPackageJson(import.meta.url);
|
|
39838
40284
|
VERSION3 = pkg.version;
|
|
@@ -39866,6 +40312,7 @@ var init_src = __esm(() => {
|
|
|
39866
40312
|
registerMemoryCommand(program2);
|
|
39867
40313
|
registerCompletionCommand(program2);
|
|
39868
40314
|
registerIndexCommand(program2);
|
|
40315
|
+
registerCompactCommand(program2);
|
|
39869
40316
|
program2.action(() => {
|
|
39870
40317
|
showBanner();
|
|
39871
40318
|
program2.help();
|
package/package.json
CHANGED