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.
Files changed (3) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/package.json +1 -1
  3. 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.1",
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 basename8 } from "node:path";
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 basename3 } from "node:path";
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, relative, basename as basename2 } from "node:path";
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, basename2(path) + ".corrupted." + Date.now());
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, basename3(path) + ".corrupted." + Date.now());
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: FALLBACK_HIGH2, mid: FALLBACK_MID2 };
2888
- const j = safeJsonParse4(readFileSync4(p, "utf-8"));
2889
- const highRe = _safeRegex2(j?.tiers?.high?.regex, FALLBACK_HIGH2, "high");
2890
- const midRe = _safeRegex2(j?.tiers?.mid?.regex, FALLBACK_MID2, "mid");
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: FALLBACK_HIGH2, mid: FALLBACK_MID2 };
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 = safeJsonParse4(readFileSync4(PRICING_CACHE_FILE2, "utf-8"));
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 = safeJsonParse4(readFileSync4(TIERS_FILE3, "utf-8"));
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 safeJsonParse4(noTrailingCommas);
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 safeJsonParse4(readFileSync4(jsonPath, "utf-8"));
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 = safeJsonParse4(readFileSync4(TIERS_FILE3, "utf-8"));
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 = safeJsonParse4(readFileSync4(TIERS_FILE3, "utf-8"));
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 = safeJsonParse4(readFileSync4(TIERS_FILE3, "utf-8"));
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 = safeJsonParse4(readFileSync4(ocConfig, "utf-8"));
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 basename4 } from "node:path";
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, basename4(path) + ".corrupted." + Date.now());
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 basename5 } from "node:path";
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, basename5(path) + ".corrupted." + Date.now());
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 safeJsonParse5(raw) {
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 = safeJsonParse5(readFileSync8(CREDIT_CACHE_F, "utf-8"));
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 = safeJsonParse5(readFileSync8(p, "utf-8"));
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 basename6 } from "node:path";
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
- var activeJob = null;
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 ? basename6(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 = basename6(dir);
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 basename7 } from "node:path";
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 basename9 = originalPath.split("/").pop() || "blocked";
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-${basename9}`;
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 = basename7(fp4);
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
  }