vibeostheog 0.15.1 → 0.15.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/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/src/index.js +174 -193
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## 0.15.2
|
|
2
|
+
- fix: add missing mergeProjectBucket re-export in state module
|
|
3
|
+
- fix: update pricing.js import assertion to handle additional state imports
|
|
4
|
+
- refactor: extract text-compress, pattern-helpers, consolidate duplicates
|
|
5
|
+
- docs: add mandatory prompt execution directive to LIVE_DEBUG
|
|
6
|
+
- chore: sync compiled output after state module fix
|
|
7
|
+
- chore: update gitignore and untrack internal dev artifacts
|
|
8
|
+
Merge pull request #29 from DrunkkToys/refactor/extract-text-compress-pattern-helpers-consolidate
|
|
9
|
+
|
|
10
|
+
|
|
1
11
|
## 0.15.0
|
|
2
12
|
- fix: autoconfig write bug, API fallback short-circuit, constants extraction (#27)
|
|
3
13
|
- fix: add blackboxSelectMode client method for footer auto-mode routing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeostheog",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "Cost-aware delegation enforcer for OpenCode. Tracks model usage, routes Task subagents to cheaper tiers, surfaces cumulative savings in chat. Includes research audit, reporting framework, project memory, progressive scratchpad decadence, and trinity CLI for brain/medium/cheap slot switching.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"release": "node scripts/release.mjs",
|
package/src/index.js
CHANGED
|
@@ -345,7 +345,7 @@ var init_flow_enforcer = __esm({
|
|
|
345
345
|
// src/index.ts
|
|
346
346
|
init_flow_enforcer();
|
|
347
347
|
import { readFileSync as readFileSync14, writeFileSync as writeFileSync12, existsSync as existsSync15, mkdirSync as mkdirSync10, copyFileSync as copyFileSync6, renameSync as renameSync6 } from "node:fs";
|
|
348
|
-
import { join as join15, dirname as dirname8, basename as
|
|
348
|
+
import { join as join15, dirname as dirname8, basename as basename9 } from "node:path";
|
|
349
349
|
|
|
350
350
|
// src/vibeOS-lib/session-metrics.js
|
|
351
351
|
function formatDuration(totalSeconds) {
|
|
@@ -830,13 +830,13 @@ function createMcpServer(deps) {
|
|
|
830
830
|
|
|
831
831
|
// src/lib/pricing.js
|
|
832
832
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, appendFileSync as appendFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync4, statSync as statSync5, copyFileSync as copyFileSync3, renameSync as renameSync4, openSync as openSync2, closeSync as closeSync2, rmSync as rmSync2 } from "node:fs";
|
|
833
|
-
import { join as join5, dirname as dirname4, basename as
|
|
833
|
+
import { join as join5, dirname as dirname4, basename as basename4 } from "node:path";
|
|
834
834
|
import { homedir as homedir4, tmpdir as tmpdir3 } from "node:os";
|
|
835
835
|
import { createHash as createHash2 } from "node:crypto";
|
|
836
836
|
|
|
837
837
|
// src/lib/state.js
|
|
838
838
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, appendFileSync as appendFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3, statSync as statSync4, readdirSync, openSync, readSync, closeSync, rmSync, copyFileSync as copyFileSync2, renameSync as renameSync3 } from "node:fs";
|
|
839
|
-
import { join as join4, dirname as dirname3,
|
|
839
|
+
import { join as join4, dirname as dirname3, basename as basename3 } from "node:path";
|
|
840
840
|
import { spawn } from "node:child_process";
|
|
841
841
|
import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
|
|
842
842
|
import { createHash } from "node:crypto";
|
|
@@ -977,6 +977,130 @@ function writeSessionOptMode(sid, mode) {
|
|
|
977
977
|
}
|
|
978
978
|
}
|
|
979
979
|
|
|
980
|
+
// src/lib/pattern-helpers.js
|
|
981
|
+
import { relative, basename as basename2 } from "node:path";
|
|
982
|
+
function normalizeObservedPath(filePath, directory3) {
|
|
983
|
+
if (!filePath || typeof filePath !== "string")
|
|
984
|
+
return "unknown";
|
|
985
|
+
let p = filePath;
|
|
986
|
+
try {
|
|
987
|
+
if (directory3 && p.startsWith("/")) {
|
|
988
|
+
const rel = relative(directory3, p);
|
|
989
|
+
if (rel && !rel.startsWith("..") && !rel.startsWith("/"))
|
|
990
|
+
p = rel;
|
|
991
|
+
}
|
|
992
|
+
} catch {
|
|
993
|
+
}
|
|
994
|
+
p = p.replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
995
|
+
if (/^(src\/index\.js|package\.json|README\.md|CHANGELOG\.md|tsconfig\.json)$/i.test(p))
|
|
996
|
+
return p;
|
|
997
|
+
const m = p.match(/\.([a-z0-9]+)$/i);
|
|
998
|
+
if (p.startsWith("src/") && m)
|
|
999
|
+
return `src/*.${m[1].toLowerCase()}`;
|
|
1000
|
+
if (p.startsWith("tests/") && m)
|
|
1001
|
+
return `tests/*.${m[1].toLowerCase()}`;
|
|
1002
|
+
return basename2(p) || "unknown";
|
|
1003
|
+
}
|
|
1004
|
+
function commandFamily(command) {
|
|
1005
|
+
const c = String(command || "").trim().toLowerCase();
|
|
1006
|
+
if (!c)
|
|
1007
|
+
return "unknown";
|
|
1008
|
+
if (/\bnode\s+--check\b/.test(c))
|
|
1009
|
+
return "syntax-check";
|
|
1010
|
+
if (/\bnpm\s+run\s+typecheck\b|\btsc\b.*--noemit/.test(c))
|
|
1011
|
+
return "typecheck";
|
|
1012
|
+
if (/\bnpm\s+test\b|\bnode\s+--test\b|\bvitest\b|\bjest\b|\bpytest\b/.test(c))
|
|
1013
|
+
return "test";
|
|
1014
|
+
if (/\bnpm\s+run\s+build\b|\btsc\s+-p\b/.test(c))
|
|
1015
|
+
return "build";
|
|
1016
|
+
if (/\bgit\s+status\b/.test(c))
|
|
1017
|
+
return "git-status";
|
|
1018
|
+
if (/\bgit\s+commit\b/.test(c))
|
|
1019
|
+
return "git-commit";
|
|
1020
|
+
const first = c.replace(/^[a-z_][a-z0-9_]*=\S+\s+/g, "").split(/\s+/)[0];
|
|
1021
|
+
return /^[a-z0-9._/-]{1,30}$/.test(first) ? first : "command";
|
|
1022
|
+
}
|
|
1023
|
+
function commandFailed(output) {
|
|
1024
|
+
const code = output?.exitCode ?? output?.statusCode ?? output?.code;
|
|
1025
|
+
if (Number.isFinite(Number(code)) && Number(code) !== 0)
|
|
1026
|
+
return true;
|
|
1027
|
+
const raw = output?.result ?? output?.text ?? output?.content ?? output?.data ?? "";
|
|
1028
|
+
if (typeof raw !== "string")
|
|
1029
|
+
return false;
|
|
1030
|
+
return /\b(exit code|exited with code)\s*[:=]?\s*[1-9]\b|\b(assertionerror|syntaxerror|typeerror|referenceerror)\b|\b(failed|error:|err!)\b/i.test(raw);
|
|
1031
|
+
}
|
|
1032
|
+
function mergeProjectBucket(dst, src) {
|
|
1033
|
+
const a = dst || {};
|
|
1034
|
+
const b = src || {};
|
|
1035
|
+
const topics = [.../* @__PURE__ */ new Set([...a.commonTopics || [], ...b.commonTopics || []])].slice(-20);
|
|
1036
|
+
const mergePatterns = (kind) => {
|
|
1037
|
+
const out = {};
|
|
1038
|
+
for (const srcObj of [a.userPatterns?.[kind], b.userPatterns?.[kind]]) {
|
|
1039
|
+
for (const [key, val] of Object.entries(srcObj || {})) {
|
|
1040
|
+
const v = val;
|
|
1041
|
+
const row = out[key] || { count: 0, sessions: [], lastSeen: null, summary: v?.summary || "" };
|
|
1042
|
+
row.count += Number(v?.count || 0);
|
|
1043
|
+
row.sessions = [.../* @__PURE__ */ new Set([...row.sessions || [], ...v?.sessions || []])].slice(-10);
|
|
1044
|
+
row.lastSeen = [row.lastSeen, v?.lastSeen].filter(Boolean).sort().slice(-1)[0] || null;
|
|
1045
|
+
row.summary = row.summary || v?.summary || "";
|
|
1046
|
+
if (v?.kind)
|
|
1047
|
+
row.kind = v.kind;
|
|
1048
|
+
out[key] = row;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return out;
|
|
1052
|
+
};
|
|
1053
|
+
return {
|
|
1054
|
+
totalSessions: (a.totalSessions || 0) + (b.totalSessions || 0),
|
|
1055
|
+
researchChains: Math.max(a.researchChains || 0, b.researchChains || 0),
|
|
1056
|
+
context7Bypasses: (a.context7Bypasses || 0) + (b.context7Bypasses || 0),
|
|
1057
|
+
commonTopics: topics,
|
|
1058
|
+
userPatterns: {
|
|
1059
|
+
friction: mergePatterns("friction"),
|
|
1060
|
+
routines: mergePatterns("routines")
|
|
1061
|
+
},
|
|
1062
|
+
lastSeen: [a.lastSeen, b.lastSeen].filter(Boolean).sort().slice(-1)[0] || (/* @__PURE__ */ new Date()).toISOString()
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
function _pruneOldSessions(state) {
|
|
1066
|
+
if (!state?.sessions)
|
|
1067
|
+
return;
|
|
1068
|
+
const entries = Object.entries(state.sessions);
|
|
1069
|
+
if (entries.length <= 30)
|
|
1070
|
+
return;
|
|
1071
|
+
entries.sort((a, b) => {
|
|
1072
|
+
const da = a[1]?.started || a[1]?.last_costed || "";
|
|
1073
|
+
const db = b[1]?.started || b[1]?.last_costed || "";
|
|
1074
|
+
return db.localeCompare(da);
|
|
1075
|
+
});
|
|
1076
|
+
state.sessions = Object.fromEntries(entries.slice(0, 30));
|
|
1077
|
+
}
|
|
1078
|
+
function _computeSessionMetrics(state, sid) {
|
|
1079
|
+
const session = state?.sessions?.[sid] || {};
|
|
1080
|
+
const warns = Array.isArray(session?.warns) ? session.warns : [];
|
|
1081
|
+
const toolCounts = session?.tool_counts || {};
|
|
1082
|
+
const toolBreakdown = {};
|
|
1083
|
+
for (const [t, c] of Object.entries(toolCounts)) {
|
|
1084
|
+
toolBreakdown[String(t)] = Number(c || 0);
|
|
1085
|
+
}
|
|
1086
|
+
const startedAt = session?.started ? new Date(session.started).getTime() : Date.now();
|
|
1087
|
+
const durationSec = Math.floor((Date.now() - startedAt) / 1e3);
|
|
1088
|
+
const hours = Math.max(durationSec / 3600, 1e-3);
|
|
1089
|
+
return {
|
|
1090
|
+
ltTasks: Number(state?.lifetime?.total_savings_usd || state?.lifetime?.est_savings_usd || 0),
|
|
1091
|
+
ltCache: Number(state?.lifetime?.cache_savings_usd || 0),
|
|
1092
|
+
missedC7: Number(state?.lifetime?.missed_context7_usd || 0),
|
|
1093
|
+
count: warns.length,
|
|
1094
|
+
sesTasks: Number(session?.total_savings_usd || 0),
|
|
1095
|
+
sesDuration: durationSec,
|
|
1096
|
+
sesRatePerHour: Number((((session?.total_savings_usd || 0) + (session?.cache_savings_usd || 0)) / hours).toFixed(2)),
|
|
1097
|
+
sesTrend: "stable",
|
|
1098
|
+
sesToolBreakdown: toolBreakdown,
|
|
1099
|
+
sesModelTurns: session?.model_turns || { brain: 0, worker: 0 },
|
|
1100
|
+
quality_avg: 0
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
|
|
980
1104
|
// src/vibeOS-lib/ml-router.js
|
|
981
1105
|
var SIMPLE_ACTIONS = /* @__PURE__ */ new Set([
|
|
982
1106
|
"check",
|
|
@@ -1645,7 +1769,7 @@ var tool = Object.assign((def) => def, {
|
|
|
1645
1769
|
function _handleStateCorruption2(path) {
|
|
1646
1770
|
const backupDir = join4(USER_HOME2, ".claude", ".backups");
|
|
1647
1771
|
mkdirSync3(backupDir, { recursive: true });
|
|
1648
|
-
const backupPath = join4(backupDir,
|
|
1772
|
+
const backupPath = join4(backupDir, basename3(path) + ".corrupted." + Date.now());
|
|
1649
1773
|
try {
|
|
1650
1774
|
copyFileSync2(path, backupPath);
|
|
1651
1775
|
} catch {
|
|
@@ -2364,39 +2488,6 @@ function ensureProjectBucket(state, fp3) {
|
|
|
2364
2488
|
}
|
|
2365
2489
|
return state.project_hashes[fp3];
|
|
2366
2490
|
}
|
|
2367
|
-
function mergeProjectBucket(dst, src) {
|
|
2368
|
-
const a = dst || {};
|
|
2369
|
-
const b = src || {};
|
|
2370
|
-
const topics = [.../* @__PURE__ */ new Set([...a.commonTopics || [], ...b.commonTopics || []])].slice(-20);
|
|
2371
|
-
const mergePatterns = (kind) => {
|
|
2372
|
-
const out = {};
|
|
2373
|
-
for (const srcObj of [a.userPatterns?.[kind], b.userPatterns?.[kind]]) {
|
|
2374
|
-
for (const [key, val] of Object.entries(srcObj || {})) {
|
|
2375
|
-
const v = val;
|
|
2376
|
-
const row = out[key] || { count: 0, sessions: [], lastSeen: null, summary: v?.summary || "" };
|
|
2377
|
-
row.count += Number(v?.count || 0);
|
|
2378
|
-
row.sessions = [.../* @__PURE__ */ new Set([...row.sessions || [], ...v?.sessions || []])].slice(-10);
|
|
2379
|
-
row.lastSeen = [row.lastSeen, v?.lastSeen].filter(Boolean).sort().slice(-1)[0] || null;
|
|
2380
|
-
row.summary = row.summary || v?.summary || "";
|
|
2381
|
-
if (v?.kind)
|
|
2382
|
-
row.kind = v.kind;
|
|
2383
|
-
out[key] = row;
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2386
|
-
return out;
|
|
2387
|
-
};
|
|
2388
|
-
return {
|
|
2389
|
-
totalSessions: (a.totalSessions || 0) + (b.totalSessions || 0),
|
|
2390
|
-
researchChains: Math.max(a.researchChains || 0, b.researchChains || 0),
|
|
2391
|
-
context7Bypasses: (a.context7Bypasses || 0) + (b.context7Bypasses || 0),
|
|
2392
|
-
commonTopics: topics,
|
|
2393
|
-
userPatterns: {
|
|
2394
|
-
friction: mergePatterns("friction"),
|
|
2395
|
-
routines: mergePatterns("routines")
|
|
2396
|
-
},
|
|
2397
|
-
lastSeen: [a.lastSeen, b.lastSeen].filter(Boolean).sort().slice(-1)[0] || (/* @__PURE__ */ new Date()).toISOString()
|
|
2398
|
-
};
|
|
2399
|
-
}
|
|
2400
2491
|
function detectTechStack(dir) {
|
|
2401
2492
|
const stacks = [];
|
|
2402
2493
|
try {
|
|
@@ -2431,56 +2522,6 @@ function detectTechStack(dir) {
|
|
|
2431
2522
|
}
|
|
2432
2523
|
return [...new Set(stacks)];
|
|
2433
2524
|
}
|
|
2434
|
-
function normalizeObservedPath(filePath, directory3) {
|
|
2435
|
-
if (!filePath || typeof filePath !== "string")
|
|
2436
|
-
return "unknown";
|
|
2437
|
-
let p = filePath;
|
|
2438
|
-
try {
|
|
2439
|
-
if (directory3 && p.startsWith("/")) {
|
|
2440
|
-
const rel = relative(directory3, p);
|
|
2441
|
-
if (rel && !rel.startsWith("..") && !rel.startsWith("/"))
|
|
2442
|
-
p = rel;
|
|
2443
|
-
}
|
|
2444
|
-
} catch {
|
|
2445
|
-
}
|
|
2446
|
-
p = p.replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
2447
|
-
if (/^(src\/index\.js|package\.json|README\.md|CHANGELOG\.md|tsconfig\.json)$/i.test(p))
|
|
2448
|
-
return p;
|
|
2449
|
-
const m = p.match(/\.([a-z0-9]+)$/i);
|
|
2450
|
-
if (p.startsWith("src/") && m)
|
|
2451
|
-
return `src/*.${m[1].toLowerCase()}`;
|
|
2452
|
-
if (p.startsWith("tests/") && m)
|
|
2453
|
-
return `tests/*.${m[1].toLowerCase()}`;
|
|
2454
|
-
return basename2(p) || "unknown";
|
|
2455
|
-
}
|
|
2456
|
-
function commandFamily(command) {
|
|
2457
|
-
const c = String(command || "").trim().toLowerCase();
|
|
2458
|
-
if (!c)
|
|
2459
|
-
return "unknown";
|
|
2460
|
-
if (/\bnode\s+--check\b/.test(c))
|
|
2461
|
-
return "syntax-check";
|
|
2462
|
-
if (/\bnpm\s+run\s+typecheck\b|\btsc\b.*--noemit/.test(c))
|
|
2463
|
-
return "typecheck";
|
|
2464
|
-
if (/\bnpm\s+test\b|\bnode\s+--test\b|\bvitest\b|\bjest\b|\bpytest\b/.test(c))
|
|
2465
|
-
return "test";
|
|
2466
|
-
if (/\bnpm\s+run\s+build\b|\btsc\s+-p\b/.test(c))
|
|
2467
|
-
return "build";
|
|
2468
|
-
if (/\bgit\s+status\b/.test(c))
|
|
2469
|
-
return "git-status";
|
|
2470
|
-
if (/\bgit\s+commit\b/.test(c))
|
|
2471
|
-
return "git-commit";
|
|
2472
|
-
const first = c.replace(/^[a-z_][a-z0-9_]*=\S+\s+/g, "").split(/\s+/)[0];
|
|
2473
|
-
return /^[a-z0-9._/-]{1,30}$/.test(first) ? first : "command";
|
|
2474
|
-
}
|
|
2475
|
-
function commandFailed(output) {
|
|
2476
|
-
const code = output?.exitCode ?? output?.statusCode ?? output?.code;
|
|
2477
|
-
if (Number.isFinite(Number(code)) && Number(code) !== 0)
|
|
2478
|
-
return true;
|
|
2479
|
-
const raw = output?.result ?? output?.text ?? output?.content ?? output?.data ?? "";
|
|
2480
|
-
if (typeof raw !== "string")
|
|
2481
|
-
return false;
|
|
2482
|
-
return /\b(exit code|exited with code)\s*[:=]?\s*[1-9]\b|\b(assertionerror|syntaxerror|typeerror|referenceerror)\b|\b(failed|error:|err!)\b/i.test(raw);
|
|
2483
|
-
}
|
|
2484
2525
|
function promotedProjectPatterns(fp3) {
|
|
2485
2526
|
try {
|
|
2486
2527
|
const p = loadProjectState().project_hashes?.[fp3];
|
|
@@ -2541,19 +2582,6 @@ function clearProjectPatterns(fp3) {
|
|
|
2541
2582
|
return 0;
|
|
2542
2583
|
}
|
|
2543
2584
|
}
|
|
2544
|
-
function _pruneOldSessions(state) {
|
|
2545
|
-
if (!state?.sessions)
|
|
2546
|
-
return;
|
|
2547
|
-
const entries = Object.entries(state.sessions);
|
|
2548
|
-
if (entries.length <= 30)
|
|
2549
|
-
return;
|
|
2550
|
-
entries.sort((a, b) => {
|
|
2551
|
-
const da = a[1]?.started || a[1]?.last_costed || "";
|
|
2552
|
-
const db = b[1]?.started || b[1]?.last_costed || "";
|
|
2553
|
-
return db.localeCompare(da);
|
|
2554
|
-
});
|
|
2555
|
-
state.sessions = Object.fromEntries(entries.slice(0, 30));
|
|
2556
|
-
}
|
|
2557
2585
|
var STATE_FILE2 = DELEGATION_STATE_FILE;
|
|
2558
2586
|
function recordCacheSaving(tool2, saveEst, meta = {}) {
|
|
2559
2587
|
try {
|
|
@@ -2743,31 +2771,6 @@ function saveSessionCheckpoint() {
|
|
|
2743
2771
|
} catch {
|
|
2744
2772
|
}
|
|
2745
2773
|
}
|
|
2746
|
-
function _computeSessionMetrics(state, sid) {
|
|
2747
|
-
const session = state?.sessions?.[sid] || {};
|
|
2748
|
-
const warns = Array.isArray(session?.warns) ? session.warns : [];
|
|
2749
|
-
const toolCounts = session?.tool_counts || {};
|
|
2750
|
-
const toolBreakdown = {};
|
|
2751
|
-
for (const [t, c] of Object.entries(toolCounts)) {
|
|
2752
|
-
toolBreakdown[String(t)] = Number(c || 0);
|
|
2753
|
-
}
|
|
2754
|
-
const startedAt = session?.started ? new Date(session.started).getTime() : Date.now();
|
|
2755
|
-
const durationSec = Math.floor((Date.now() - startedAt) / 1e3);
|
|
2756
|
-
const hours = Math.max(durationSec / 3600, 1e-3);
|
|
2757
|
-
return {
|
|
2758
|
-
ltTasks: Number(state?.lifetime?.total_savings_usd || state?.lifetime?.est_savings_usd || 0),
|
|
2759
|
-
ltCache: Number(state?.lifetime?.cache_savings_usd || 0),
|
|
2760
|
-
missedC7: Number(state?.lifetime?.missed_context7_usd || 0),
|
|
2761
|
-
count: warns.length,
|
|
2762
|
-
sesTasks: Number(session?.total_savings_usd || 0),
|
|
2763
|
-
sesDuration: durationSec,
|
|
2764
|
-
sesRatePerHour: Number((((session?.total_savings_usd || 0) + (session?.cache_savings_usd || 0)) / hours).toFixed(2)),
|
|
2765
|
-
sesTrend: "stable",
|
|
2766
|
-
sesToolBreakdown: toolBreakdown,
|
|
2767
|
-
sesModelTurns: session?.model_turns || { brain: 0, worker: 0 },
|
|
2768
|
-
quality_avg: 0
|
|
2769
|
-
};
|
|
2770
|
-
}
|
|
2771
2774
|
|
|
2772
2775
|
// src/lib/pricing.js
|
|
2773
2776
|
var TRINITY_BRAIN = null;
|
|
@@ -2789,24 +2792,10 @@ var USER_HOME3 = (() => {
|
|
|
2789
2792
|
return tmpdir3();
|
|
2790
2793
|
}
|
|
2791
2794
|
})();
|
|
2792
|
-
function safeJsonParse4(raw) {
|
|
2793
|
-
if (raw == null || raw === "")
|
|
2794
|
-
return null;
|
|
2795
|
-
try {
|
|
2796
|
-
return JSON.parse(raw);
|
|
2797
|
-
} catch {
|
|
2798
|
-
}
|
|
2799
|
-
let cleaned = raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "").replace(/,\s*([}\]])/g, "$1");
|
|
2800
|
-
try {
|
|
2801
|
-
return JSON.parse(cleaned);
|
|
2802
|
-
} catch (e) {
|
|
2803
|
-
throw e;
|
|
2804
|
-
}
|
|
2805
|
-
}
|
|
2806
2795
|
function _handleStateCorruption3(path) {
|
|
2807
2796
|
const backupDir = join5(USER_HOME3, ".claude", ".backups");
|
|
2808
2797
|
mkdirSync4(backupDir, { recursive: true });
|
|
2809
|
-
const backupPath = join5(backupDir,
|
|
2798
|
+
const backupPath = join5(backupDir, basename4(path) + ".corrupted." + Date.now());
|
|
2810
2799
|
try {
|
|
2811
2800
|
copyFileSync3(path, backupPath);
|
|
2812
2801
|
} catch {
|
|
@@ -2868,29 +2857,17 @@ ${Date.now()}
|
|
|
2868
2857
|
throw new Error(`[vibeOS] lock not acquired for ${filePath} after ${timeoutMs}ms`);
|
|
2869
2858
|
}
|
|
2870
2859
|
var _modelLocked2 = false;
|
|
2871
|
-
var FALLBACK_HIGH2 = /opus|gemini-.*-pro|deepseek\/deepseek-v4-pro|gpt-5|(^|\/)o[134]($|-|\/)/i;
|
|
2872
|
-
var FALLBACK_MID2 = /deepseek\/deepseek-v4-flash|claude.*sonnet|gemini-.*-flash|gpt-4o(?!-mini)/i;
|
|
2873
|
-
function _safeRegex2(cfg, fallback, label) {
|
|
2874
|
-
if (!cfg)
|
|
2875
|
-
return fallback;
|
|
2876
|
-
try {
|
|
2877
|
-
return new RegExp(cfg, "i");
|
|
2878
|
-
} catch (e) {
|
|
2879
|
-
console.error(`[vibeOS] Invalid ${label}-tier regex in model-tiers.json: ${e.message}. Falling back.`);
|
|
2880
|
-
return fallback;
|
|
2881
|
-
}
|
|
2882
|
-
}
|
|
2883
2860
|
function loadTierRegexes2() {
|
|
2884
2861
|
try {
|
|
2885
2862
|
const p = join5(USER_HOME3, ".claude/model-tiers.json");
|
|
2886
2863
|
if (!existsSync5(p))
|
|
2887
|
-
return { high:
|
|
2888
|
-
const j =
|
|
2889
|
-
const highRe =
|
|
2890
|
-
const midRe =
|
|
2864
|
+
return { high: FALLBACK_HIGH, mid: FALLBACK_MID };
|
|
2865
|
+
const j = safeJsonParse3(readFileSync4(p, "utf-8"));
|
|
2866
|
+
const highRe = _safeRegex(j?.tiers?.high?.regex, FALLBACK_HIGH, "high");
|
|
2867
|
+
const midRe = _safeRegex(j?.tiers?.mid?.regex, FALLBACK_MID, "mid");
|
|
2891
2868
|
return { high: highRe, mid: midRe };
|
|
2892
2869
|
} catch {
|
|
2893
|
-
return { high:
|
|
2870
|
+
return { high: FALLBACK_HIGH, mid: FALLBACK_MID };
|
|
2894
2871
|
}
|
|
2895
2872
|
}
|
|
2896
2873
|
var { high: HIGH_TIER_RE2, mid: MID_TIER_RE2 } = loadTierRegexes2();
|
|
@@ -2991,7 +2968,7 @@ function _loadDynamicPricingCache() {
|
|
|
2991
2968
|
_dynamicPricingCache = {};
|
|
2992
2969
|
return {};
|
|
2993
2970
|
}
|
|
2994
|
-
const raw =
|
|
2971
|
+
const raw = safeJsonParse3(readFileSync4(PRICING_CACHE_FILE2, "utf-8"));
|
|
2995
2972
|
const map = raw?.models && typeof raw.models === "object" ? raw.models : {};
|
|
2996
2973
|
_dynamicPricingCache = map;
|
|
2997
2974
|
} catch {
|
|
@@ -3124,7 +3101,7 @@ function loadSelection2() {
|
|
|
3124
3101
|
_handleStateCorruption3(TIERS_FILE3);
|
|
3125
3102
|
return DFLT_SEL2;
|
|
3126
3103
|
}
|
|
3127
|
-
const j =
|
|
3104
|
+
const j = safeJsonParse3(readFileSync4(TIERS_FILE3, "utf-8"));
|
|
3128
3105
|
return {
|
|
3129
3106
|
enabled: j?.selection?.enabled !== false,
|
|
3130
3107
|
active_slot: j?.selection?.active_slot || null,
|
|
@@ -3154,13 +3131,13 @@ function parseJsonc(raw) {
|
|
|
3154
3131
|
const noBlockComments = String(raw || "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
3155
3132
|
const noLineComments = noBlockComments.replace(/(^|\s)\/\/.*$/gm, "$1");
|
|
3156
3133
|
const noTrailingCommas = noLineComments.replace(/,\s*([}\]])/g, "$1");
|
|
3157
|
-
return
|
|
3134
|
+
return safeJsonParse3(noTrailingCommas);
|
|
3158
3135
|
}
|
|
3159
3136
|
function readOpenCodeConfigObject(dir) {
|
|
3160
3137
|
const jsonPath = join5(dir, "opencode.json");
|
|
3161
3138
|
const jsoncPath = join5(dir, "opencode.jsonc");
|
|
3162
3139
|
if (existsSync5(jsonPath)) {
|
|
3163
|
-
return
|
|
3140
|
+
return safeJsonParse3(readFileSync4(jsonPath, "utf-8"));
|
|
3164
3141
|
}
|
|
3165
3142
|
if (existsSync5(jsoncPath)) {
|
|
3166
3143
|
return parseJsonc(readFileSync4(jsoncPath, "utf-8"));
|
|
@@ -3173,7 +3150,7 @@ function _refreshModel(directory3) {
|
|
|
3173
3150
|
const sel = loadSelection2();
|
|
3174
3151
|
if (!sel.enabled)
|
|
3175
3152
|
return;
|
|
3176
|
-
const tiersData =
|
|
3153
|
+
const tiersData = safeJsonParse3(readFileSync4(TIERS_FILE3, "utf-8"));
|
|
3177
3154
|
const activeSlot = sel.active_slot || "brain";
|
|
3178
3155
|
let slotOcModel = tiersData?.trinity?.[activeSlot]?.oc || "";
|
|
3179
3156
|
if (slotOcModel && PLACEHOLDER_RE.test(slotOcModel)) {
|
|
@@ -3210,7 +3187,7 @@ function _refreshModel(directory3) {
|
|
|
3210
3187
|
console.error(`[vibeOS] model refresh (config): ${oldModel}(${oldTier}) \u2192 ${currentModel}(${currentTier})`);
|
|
3211
3188
|
try {
|
|
3212
3189
|
if (existsSync5(TIERS_FILE3)) {
|
|
3213
|
-
const t =
|
|
3190
|
+
const t = safeJsonParse3(readFileSync4(TIERS_FILE3, "utf-8"));
|
|
3214
3191
|
for (const s of ["brain", "medium", "cheap"]) {
|
|
3215
3192
|
if (t?.trinity?.[s]?.oc === cfgModel) {
|
|
3216
3193
|
t.selection.active_slot = s;
|
|
@@ -3231,7 +3208,7 @@ function _refreshModel(directory3) {
|
|
|
3231
3208
|
}
|
|
3232
3209
|
function applySlot2(slot) {
|
|
3233
3210
|
try {
|
|
3234
|
-
const j =
|
|
3211
|
+
const j = safeJsonParse3(readFileSync4(TIERS_FILE3, "utf-8"));
|
|
3235
3212
|
const ocModel = j?.trinity?.[slot]?.oc;
|
|
3236
3213
|
if (!ocModel)
|
|
3237
3214
|
return { ok: false, reason: `slot '${slot}' has no oc model` };
|
|
@@ -3242,7 +3219,7 @@ function applySlot2(slot) {
|
|
|
3242
3219
|
const localOcConfig = join5(process.cwd(), "opencode.json");
|
|
3243
3220
|
const ocConfig = existsSync5(localOcConfig) ? localOcConfig : join5(USER_HOME3, ".config/opencode/opencode.json");
|
|
3244
3221
|
if (existsSync5(ocConfig)) {
|
|
3245
|
-
const oc =
|
|
3222
|
+
const oc = safeJsonParse3(readFileSync4(ocConfig, "utf-8"));
|
|
3246
3223
|
oc.model = ocModel;
|
|
3247
3224
|
writeFileSync4(ocConfig, JSON.stringify(oc, null, 2) + "\n");
|
|
3248
3225
|
}
|
|
@@ -3255,7 +3232,7 @@ function applySlot2(slot) {
|
|
|
3255
3232
|
|
|
3256
3233
|
// src/lib/turn-classify.js
|
|
3257
3234
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, appendFileSync as appendFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync5, statSync as statSync6, copyFileSync as copyFileSync4, renameSync as renameSync5, openSync as openSync3, closeSync as closeSync3, rmSync as rmSync3 } from "node:fs";
|
|
3258
|
-
import { join as join6, dirname as dirname5, basename as
|
|
3235
|
+
import { join as join6, dirname as dirname5, basename as basename5 } from "node:path";
|
|
3259
3236
|
import { homedir as homedir5, tmpdir as tmpdir4 } from "node:os";
|
|
3260
3237
|
import { createHash as createHash3 } from "node:crypto";
|
|
3261
3238
|
|
|
@@ -3794,7 +3771,7 @@ var warnCoalesceCounters = /* @__PURE__ */ new Map();
|
|
|
3794
3771
|
function _handleStateCorruption4(path) {
|
|
3795
3772
|
const backupDir = join6(USER_HOME4, ".claude", ".backups");
|
|
3796
3773
|
mkdirSync5(backupDir, { recursive: true });
|
|
3797
|
-
const backupPath = join6(backupDir,
|
|
3774
|
+
const backupPath = join6(backupDir, basename5(path) + ".corrupted." + Date.now());
|
|
3798
3775
|
try {
|
|
3799
3776
|
copyFileSync4(path, backupPath);
|
|
3800
3777
|
} catch {
|
|
@@ -4368,7 +4345,7 @@ function researchAudit({ hours = 24, session: sessionFilter } = {}) {
|
|
|
4368
4345
|
|
|
4369
4346
|
// src/lib/reporting.js
|
|
4370
4347
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync6, statSync as statSync7, copyFileSync as copyFileSync5, rmSync as rmSync4 } from "node:fs";
|
|
4371
|
-
import { join as join8, basename as
|
|
4348
|
+
import { join as join8, basename as basename6 } from "node:path";
|
|
4372
4349
|
import { homedir as homedir7, tmpdir as tmpdir6 } from "node:os";
|
|
4373
4350
|
var USER_HOME6 = (() => {
|
|
4374
4351
|
try {
|
|
@@ -4385,7 +4362,7 @@ var currentProjectName2 = "";
|
|
|
4385
4362
|
function _handleStateCorruption5(path) {
|
|
4386
4363
|
const backupDir = join8(USER_HOME6, ".claude", ".backups");
|
|
4387
4364
|
mkdirSync6(backupDir, { recursive: true });
|
|
4388
|
-
const backupPath = join8(backupDir,
|
|
4365
|
+
const backupPath = join8(backupDir, basename6(path) + ".corrupted." + Date.now());
|
|
4389
4366
|
try {
|
|
4390
4367
|
copyFileSync5(path, backupPath);
|
|
4391
4368
|
} catch {
|
|
@@ -4573,7 +4550,7 @@ function readReport(id2) {
|
|
|
4573
4550
|
// src/lib/credit-api.js
|
|
4574
4551
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, existsSync as existsSync9 } from "node:fs";
|
|
4575
4552
|
import { join as join9 } from "node:path";
|
|
4576
|
-
function
|
|
4553
|
+
function safeJsonParse4(raw) {
|
|
4577
4554
|
try {
|
|
4578
4555
|
return JSON.parse(raw);
|
|
4579
4556
|
} catch {
|
|
@@ -4589,14 +4566,14 @@ function _cachedPct() {
|
|
|
4589
4566
|
try {
|
|
4590
4567
|
if (!existsSync9(CREDIT_CACHE_F))
|
|
4591
4568
|
return null;
|
|
4592
|
-
const s =
|
|
4569
|
+
const s = safeJsonParse4(readFileSync8(CREDIT_CACHE_F, "utf-8"));
|
|
4593
4570
|
if (s?.total == null || !s.ts)
|
|
4594
4571
|
return null;
|
|
4595
4572
|
let budget = 50;
|
|
4596
4573
|
try {
|
|
4597
4574
|
const p = join9(USER_HOME2, ".claude/model-tiers.json");
|
|
4598
4575
|
if (existsSync9(p)) {
|
|
4599
|
-
const j =
|
|
4576
|
+
const j = safeJsonParse4(readFileSync8(p, "utf-8"));
|
|
4600
4577
|
if (j?.selection?.monthly_budget_usd)
|
|
4601
4578
|
budget = j.selection.monthly_budget_usd;
|
|
4602
4579
|
}
|
|
@@ -4799,13 +4776,14 @@ import { homedir as homedir8, tmpdir as tmpdir7 } from "node:os";
|
|
|
4799
4776
|
|
|
4800
4777
|
// src/lib/hooks/chat-transform.js
|
|
4801
4778
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync11, mkdirSync as mkdirSync7 } from "node:fs";
|
|
4802
|
-
import { join as join12, basename as
|
|
4779
|
+
import { join as join12, basename as basename7 } from "node:path";
|
|
4803
4780
|
import { createHash as createHash4 } from "node:crypto";
|
|
4804
4781
|
|
|
4805
4782
|
// src/lib/index-helpers.js
|
|
4806
4783
|
import { join as join11 } from "node:path";
|
|
4807
4784
|
import { writeFileSync as writeFileSync8 } from "node:fs";
|
|
4808
|
-
|
|
4785
|
+
|
|
4786
|
+
// src/lib/text-compress.js
|
|
4809
4787
|
var VERBOSE_LINE_RE = [
|
|
4810
4788
|
/^[\s#*/\\\-_=+|~:;'"`@\$%^&<>{}\[\]()!?.,0-9]+$/,
|
|
4811
4789
|
/^(Filed|Created|Modified|Deleted|Updated|Renamed|Copied|Moved|Changed):/,
|
|
@@ -4838,19 +4816,6 @@ function extractBulletLines(lines, targetChars, minLines) {
|
|
|
4838
4816
|
}
|
|
4839
4817
|
return selected;
|
|
4840
4818
|
}
|
|
4841
|
-
function setActiveJobFromTaskPrompt(prompt) {
|
|
4842
|
-
if (!prompt || typeof prompt !== "string")
|
|
4843
|
-
return;
|
|
4844
|
-
const p = prompt.trim();
|
|
4845
|
-
if (p.length < 24)
|
|
4846
|
-
return;
|
|
4847
|
-
activeJob = {
|
|
4848
|
-
prompt: p.slice(0, 1200),
|
|
4849
|
-
keywords: topKeywords(p, 12),
|
|
4850
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4851
|
-
};
|
|
4852
|
-
saveActiveJobForProject(activeJob);
|
|
4853
|
-
}
|
|
4854
4819
|
function compressText(text) {
|
|
4855
4820
|
if (!text || typeof text !== "string")
|
|
4856
4821
|
return text;
|
|
@@ -4903,6 +4868,22 @@ function compressText(text) {
|
|
|
4903
4868
|
}
|
|
4904
4869
|
return result || text;
|
|
4905
4870
|
}
|
|
4871
|
+
|
|
4872
|
+
// src/lib/index-helpers.js
|
|
4873
|
+
var activeJob = null;
|
|
4874
|
+
function setActiveJobFromTaskPrompt(prompt) {
|
|
4875
|
+
if (!prompt || typeof prompt !== "string")
|
|
4876
|
+
return;
|
|
4877
|
+
const p = prompt.trim();
|
|
4878
|
+
if (p.length < 24)
|
|
4879
|
+
return;
|
|
4880
|
+
activeJob = {
|
|
4881
|
+
prompt: p.slice(0, 1200),
|
|
4882
|
+
keywords: topKeywords(p, 12),
|
|
4883
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4884
|
+
};
|
|
4885
|
+
saveActiveJobForProject(activeJob);
|
|
4886
|
+
}
|
|
4906
4887
|
function noteProjectPattern(kind, key, summary, meta = {}) {
|
|
4907
4888
|
if (!currentProjectFingerprint || !key || !summary)
|
|
4908
4889
|
return;
|
|
@@ -5257,14 +5238,14 @@ function observeUserCorrection(text) {
|
|
|
5257
5238
|
}
|
|
5258
5239
|
}
|
|
5259
5240
|
function buildProjectBriefing(directory3) {
|
|
5260
|
-
const label = currentProjectName || (directory3 ?
|
|
5241
|
+
const label = currentProjectName || (directory3 ? basename7(directory3) : "");
|
|
5261
5242
|
if (!label)
|
|
5262
5243
|
return null;
|
|
5263
5244
|
return `[project memory] Active project: ${label}. Stay focused on the current repository and prefer the existing workflow.`;
|
|
5264
5245
|
}
|
|
5265
5246
|
function ensureProjectSkill(dir, fp3) {
|
|
5266
5247
|
const skillsDir = join12(dir, ".opencode", "skills");
|
|
5267
|
-
const projectName =
|
|
5248
|
+
const projectName = basename7(dir);
|
|
5268
5249
|
const skillDir = join12(skillsDir, projectName);
|
|
5269
5250
|
const skillPath = join12(skillDir, "SKILL.md");
|
|
5270
5251
|
if (existsSync11(skillPath)) {
|
|
@@ -5927,7 +5908,7 @@ async function _appendFooter(input, output, directory3) {
|
|
|
5927
5908
|
|
|
5928
5909
|
// src/lib/hooks/tool-execute.js
|
|
5929
5910
|
import { writeFileSync as writeFileSync11, appendFileSync as appendFileSync7, existsSync as existsSync13, mkdirSync as mkdirSync9 } from "node:fs";
|
|
5930
|
-
import { dirname as dirname7, basename as
|
|
5911
|
+
import { dirname as dirname7, basename as basename8 } from "node:path";
|
|
5931
5912
|
init_flow_enforcer();
|
|
5932
5913
|
|
|
5933
5914
|
// src/lib/tdd-enforcer.js
|
|
@@ -7437,7 +7418,7 @@ var onToolExecuteBefore = async (input, output) => {
|
|
|
7437
7418
|
if (sel.delegation_enforce && currentTier === "high" && args && typeof args === "object") {
|
|
7438
7419
|
const actualArgs = args || output && output.args || {};
|
|
7439
7420
|
const originalPath = actualArgs.filePath || actualArgs.file_path || "";
|
|
7440
|
-
const
|
|
7421
|
+
const basename10 = originalPath.split("/").pop() || "blocked";
|
|
7441
7422
|
const apiResult = await remoteCall("delegateCheck", [tLower, currentTier, currentModel, _prompt], () => ({
|
|
7442
7423
|
blocked: true,
|
|
7443
7424
|
savings: _estEdit
|
|
@@ -7446,7 +7427,7 @@ var onToolExecuteBefore = async (input, output) => {
|
|
|
7446
7427
|
const savings = apiResult?.savings ?? _estEdit;
|
|
7447
7428
|
if (isBlocked) {
|
|
7448
7429
|
if (tLower === "write") {
|
|
7449
|
-
actualArgs.filePath = `/tmp/vibeos-enforcement-blocked-${
|
|
7430
|
+
actualArgs.filePath = `/tmp/vibeos-enforcement-blocked-${basename10}`;
|
|
7450
7431
|
if (actualArgs.file_path !== void 0)
|
|
7451
7432
|
actualArgs.file_path = actualArgs.filePath;
|
|
7452
7433
|
} else if (tLower === "edit" || tLower === "notebookedit") {
|
|
@@ -7735,7 +7716,7 @@ ${pendingUiNote}`;
|
|
|
7735
7716
|
if (guardRe.test(fp4)) {
|
|
7736
7717
|
const guardIcons = { flag: "!", warn: "!!", hint: "_" };
|
|
7737
7718
|
const guardIcon = guardIcons.flag || "!";
|
|
7738
|
-
const fn =
|
|
7719
|
+
const fn = basename8(fp4);
|
|
7739
7720
|
console.error(`[flow-enforcer] ${guardIcon} [guard] ${fn}: protected project doc modified \u2014 verify user intent`);
|
|
7740
7721
|
}
|
|
7741
7722
|
}
|