vibeostheog 0.15.34 → 0.18.3

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 CHANGED
@@ -1,3 +1,30 @@
1
+ ## 0.18.3
2
+ - feat: dynamic mode injection + footer hooks fix
3
+ - fix: auto-enable plugin on load + always show footer
4
+
5
+
6
+ ## 0.17.0
7
+ - feat: universal context7 detection — scans local opencode.json, ~/.config/opencode/*.json, system PATH, and npm npx cache
8
+ - feat: `_scanOpenCodeConfigs` — finds context7 in any JSON config under ~/.config/opencode/
9
+ - feat: `_context7InPath` — detects context7 binary in system PATH
10
+ - feat: `_context7InNpmCache` — detects context7 in npx cached installations
11
+ - fix: local project opencode.json added to CONTEXT7_CONFIG_FILES search list
12
+
13
+ ## 0.16.0
14
+ - feat: dopamine-style footer + natural language system directives
15
+ - feat: dopamine-style footer + natural language system directives
16
+ - feat: turn-aware compaction directive at turn 7+
17
+ - feat: add forensic/web-research modes + 1084-datapoint benchmark
18
+ - fix: flash icon only when API connected, unified [VIBE→MODE⚡] format
19
+ - docs: Security section + Context7 cost optimization docs
20
+ - docs: add Security section with API token emphasis and Context7 cost optimization docs
21
+ - docs: persist all benchmark data + compaction research
22
+ - docs: reformat README as user-facing PM doc, move internals to AGENTS.md, cleanup .gitignore
23
+ Merge pull request #35 from DrunkkToys/refactor/simplify-chat-transform
24
+ readme: center VIBE autoswitching as the core value proposition
25
+ Revert "fix: add system prompt cache savings tracking"
26
+
27
+
1
28
  ## 0.15.23
2
29
  - feat: add trinity api-token command to inject VIBEOS_API_TOKEN
3
30
 
package/README.md CHANGED
@@ -103,12 +103,51 @@ npm run dashboard # Start server on http://127.0.0.1:3333
103
103
 
104
104
  Displays model split, savings, session history, stress gauge, trinity controls, reports, and blackbox state with SSE push updates every 1.5s.
105
105
 
106
+ ## Security
107
+
108
+ The API token (`VIBEOS_API_TOKEN`) acts as a **password** for your vibeOS seat. Treat it with the same care as any credential.
109
+
110
+ - **Token-based seat auth** — Each token is bound to a seat. Suspending a seat immediately revokes all associated tokens.
111
+ - **Graceful degradation** — If the token is revoked or the API is unreachable, the plugin falls back to local-only mode with bundled algorithms. No functionality is lost; only remote-optimized routing is disabled.
112
+ - **Never commit tokens** — `.env.production` and `PRODUCTION-CREDENTIALS.md` are gitignored. Do not share or hardcode tokens in source files.
113
+ - **Token rotation** — Generate a new token and update `VIBEOS_API_TOKEN` if you suspect a leak. Old tokens are invalidated immediately on seat suspension.
114
+ - **Local-only mode** — Without an API token, all algorithms run locally. Set `VIBEOS_API_ENABLED=false` to explicitly disable all remote calls.
115
+
116
+ ## Context7 Cost Optimization
117
+
118
+ [context7](https://upstash.com/docs/context7) is an MCP tool that resolves library/framework documentation queries at a fraction of the cost of WebFetch (~$0.06/turn saved).
119
+
120
+ **Install it once:**
121
+
122
+ ```bash
123
+ claude mcp add context7 npx @upstash/context7-mcp
124
+ ```
125
+
126
+ ### How vibeOS uses context7
127
+
128
+ - **Auto-detection** — At module load, vibeOS scans `~/.claude/settings.json`, `~/.claude.json`, `opencode.json`, and `~/.config/opencode/` for a `context7` reference. No manual config needed.
129
+ - **System prompt injection** — When context7 is detected, a cost-policy directive is injected into every system prompt instructing the model to prefer `mcp__context7__resolve-library-id` and `mcp__context7__get-library-docs` over WebFetch/WebSearch for documentation URLs.
130
+ - **Urgency levels** — Controlled by the blackbox engine:
131
+ - `required` (strict/TDD-strict mode) — context7 is mandatory this turn.
132
+ - `preferred` (default) — context7 is encouraged but not forced.
133
+ - `optional` (relaxed mode) — context7 is a nice-to-have.
134
+ - **Docs nudge** — If context7 is not installed and the model uses WebFetch on a documentation URL (docs.*, readthedocs, MDN, npmjs, pypi, crates.io, pkg.go.dev, etc.), vibeOS logs a one-time install suggestion and tracks the missed savings.
135
+ - **Savings tracking** — Every docs URL fetched via WebFetch instead of context7 is recorded as `missed_context7_usd` in `~/.claude/delegation-state.json`. Accumulated misses appear in `trinity project` analytics with an installation suggestion when bypasses exceed 3.
136
+
137
+ ### Force-enable detection
138
+
139
+ ```bash
140
+ export CLAUDE_CONTEXT7_AVAILABLE=true
141
+ ```
142
+
143
+ Use this when context7 is configured but the auto-scan misses it (unusual paths, remote configs, or runtime-loaded MCP definitions).
144
+
106
145
  ## Environment Variables
107
146
 
108
147
  | Variable | Default | Description |
109
148
  |---|---|---|
110
149
  | `VIBEOS_API_URL` | `https://api.vibetheog.com` | API server URL |
111
- | `VIBEOS_API_TOKEN` | — | API token (required for remote mode) |
150
+ | `VIBEOS_API_TOKEN` | — | **API token (password). Protect like a credential.** Required for remote mode. If compromised, rotate immediately via seat suspension. |
112
151
  | `VIBEOS_API_ENABLED` | `true` | Set to `false` for local-only mode |
113
152
  | `CLAUDE_CREDIT_PERCENT` | `100` | Credit percentage override |
114
153
  | `CLAUDE_CONTEXT7_AVAILABLE` | — | Enable context7 cost optimization |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.15.34",
3
+ "version": "0.18.3",
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
@@ -383,8 +383,8 @@ var init_flow_enforcer = __esm({
383
383
 
384
384
  // src/index.ts
385
385
  init_flow_enforcer();
386
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync13, existsSync as existsSync15, mkdirSync as mkdirSync11, copyFileSync as copyFileSync6, renameSync as renameSync6 } from "node:fs";
387
- import { join as join15, dirname as dirname9, basename as basename9 } from "node:path";
386
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, existsSync as existsSync14, mkdirSync as mkdirSync11, copyFileSync as copyFileSync5, renameSync as renameSync6 } from "node:fs";
387
+ import { join as join15, dirname as dirname8, basename as basename8 } from "node:path";
388
388
 
389
389
  // src/vibeOS-lib/session-metrics.js
390
390
  function formatDuration(totalSeconds) {
@@ -2733,7 +2733,7 @@ var MODEL_USD_PER_TURN = {
2733
2733
  "haiku": 22e-4,
2734
2734
  // ── DeepSeek (OC platform + OpenRouter) ──────────────────
2735
2735
  "deepseek/deepseek-v4-pro": 57e-5,
2736
- "deepseek/deepseek-v4-flash": 182e-6,
2736
+ "deepseek/deepseek-v4-flash": 0.00013,
2737
2737
  "deepseek/deepseek-chat": 182e-6,
2738
2738
  "deepseek-chat": 182e-6,
2739
2739
  "deepseek/deepseek-v3": 182e-6,
@@ -2893,6 +2893,41 @@ function _scanOpenCodeConfigs(baseDir) {
2893
2893
  }
2894
2894
  return false;
2895
2895
  }
2896
+ function _context7InPath() {
2897
+ try {
2898
+ const pathDirs = (process.env.PATH || "").split(":");
2899
+ for (const dir of pathDirs) {
2900
+ if (!dir)
2901
+ continue;
2902
+ try {
2903
+ if (existsSync4(join4(dir, "context7")))
2904
+ return true;
2905
+ if (existsSync4(join4(dir, "context7.cmd")))
2906
+ return true;
2907
+ } catch {
2908
+ }
2909
+ }
2910
+ } catch {
2911
+ }
2912
+ return false;
2913
+ }
2914
+ function _context7InNpmCache() {
2915
+ try {
2916
+ const npxDir = join4(USER_HOME3, ".npm/_npx");
2917
+ if (!existsSync4(npxDir))
2918
+ return false;
2919
+ for (const hashDir of readdirSync2(npxDir)) {
2920
+ const ctxDir = join4(npxDir, hashDir, "node_modules", "context7");
2921
+ try {
2922
+ if (existsSync4(join4(ctxDir, "package.json")))
2923
+ return true;
2924
+ } catch {
2925
+ }
2926
+ }
2927
+ } catch {
2928
+ }
2929
+ return false;
2930
+ }
2896
2931
  function detectContext7(files = CONTEXT7_CONFIG_FILES) {
2897
2932
  if (process.env.CLAUDE_CONTEXT7_AVAILABLE)
2898
2933
  return true;
@@ -2905,6 +2940,10 @@ function detectContext7(files = CONTEXT7_CONFIG_FILES) {
2905
2940
  }
2906
2941
  if (_scanOpenCodeConfigs(join4(USER_HOME3, ".config/opencode")))
2907
2942
  return true;
2943
+ if (_context7InPath())
2944
+ return true;
2945
+ if (_context7InNpmCache())
2946
+ return true;
2908
2947
  return false;
2909
2948
  }
2910
2949
  var DOCS_TARGET_RE = /(docs\.|readthedocs|developer\.mozilla|\/api\/|\/reference\/|\/guide\/|npmjs\.com\/package\/|pypi\.org\/project\/|crates\.io\/crates\/|pkg\.go\.dev|api-docs|\/javadoc\/)/i;
@@ -5126,13 +5165,13 @@ async function probeModel(modelId, auth) {
5126
5165
  }
5127
5166
  }
5128
5167
 
5129
- // src/lib/hooks/footer.ts
5130
- import { readFileSync as readFileSync12 } from "node:fs";
5168
+ // src/lib/hooks/footer.js
5169
+ import { readFileSync as readFileSync12, appendFileSync as appendFileSync6, mkdirSync as mkdirSync8 } from "node:fs";
5131
5170
  import { join as join13 } from "node:path";
5132
5171
  import { homedir as homedir9, tmpdir as tmpdir6 } from "node:os";
5133
5172
 
5134
5173
  // src/lib/hooks/chat-transform.js
5135
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "node:fs";
5174
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, appendFileSync as appendFileSync5, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "node:fs";
5136
5175
  import { join as join12, basename as basename6 } from "node:path";
5137
5176
  import { homedir as homedir8 } from "node:os";
5138
5177
  import { createHash as createHash3 } from "node:crypto";
@@ -5545,7 +5584,7 @@ async function apiComputeControlVector(state, action, optimizationMode) {
5545
5584
  }
5546
5585
  const opt = (optimizationMode || "balanced").toLowerCase();
5547
5586
  const isRelaxed = opt === "budget" || opt === "speed" || opt === "audit";
5548
- const isStrict = opt === "quality";
5587
+ const isStrict = opt === "quality" || opt === "forensic" || opt === "defense_in_depth" || opt === "reporting";
5549
5588
  return {
5550
5589
  enforcement_mode: isStrict ? "strict" : "normal",
5551
5590
  enforcement_reason: `[optimize: ${opt}] offline fallback`,
@@ -6065,6 +6104,20 @@ var onSystemTransform = async (_input, output) => {
6065
6104
  }
6066
6105
  }
6067
6106
  pushSystem(output, "[project guard: CRITICAL] AGENTS.md and README.md are protected by vibeOS. Do NOT modify either file without explicit user permission. When implementing new features, update README.md to document them. AGENTS.md defines that AI agents must ask before changing code -- respect this rule.");
6107
+ const currentMode = loadOptimizationMode();
6108
+ if (currentMode === "quality") {
6109
+ pushSystem(output, "[mode: quality] Prioritize quality and thoroughness. Provide complete edge case coverage, comprehensive error handling, full type annotations, production-grade code with tests. Do not cut corners for brevity.");
6110
+ } else if (currentMode === "forensic") {
6111
+ pushSystem(output, "[mode: forensic] Use forensic analysis depth: evidence-based reasoning tracing each claim to source; multi-hypothesis evaluation considering competing explanations; explicit uncertainty flags for assumptions and trade-offs; structured output with clear reasoning traces; thorough verification of all edge cases and failure modes.");
6112
+ } else if (currentMode === "web-research" || currentMode === "exploration") {
6113
+ pushSystem(output, "[mode: exploration] Use a research-oriented approach: gather information from multiple perspectives before converging; document alternative approaches; flag confidence levels for each finding; synthesize into actionable recommendations.");
6114
+ } else if (currentMode === "defense_in_depth") {
6115
+ pushSystem(output, "[mode: defense in depth] For every component: define the threat model, implement with defenses, then verify the defense handles the threat. Never write code without specifying what it defends against. Consider: injection, broken auth, data exposure, logic errors, race conditions.");
6116
+ } else if (currentMode === "reporting") {
6117
+ pushSystem(output, "[mode: formal report] Structure output as a formal engineering report with: executive summary, methodology, detailed findings with evidence, trade-offs documented, conclusions with confidence levels, and recommendations.");
6118
+ } else if (currentMode === "verify") {
6119
+ pushSystem(output, "[mode: verification-first] Before writing code, declare verification criteria: which edge cases must pass, what invariants must hold. After each code block, include a verification section showing how correctness is established against each criterion.");
6120
+ }
6068
6121
  pushSystem(output, contextBudgetDirective(_input, output));
6069
6122
  if (!oneShot(fp2)) {
6070
6123
  pushSystem(output, buildProjectBriefing(currentProjectName || ""));
@@ -6075,21 +6128,55 @@ var onSystemTransform = async (_input, output) => {
6075
6128
  if (!oneShot("trinity_welcome_" + fp2)) {
6076
6129
  pushSystem(output, welcomeDirective());
6077
6130
  }
6131
+ const calDir = join12(homedir8(), ".claude");
6132
+ const calFile = join12(calDir, "calibration-data.jsonl");
6133
+ const regime = _latestBlackboxState3?.sub_regime || classifyTurnSimple(latestUserIntent || "");
6134
+ const calRecord = JSON.stringify({
6135
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
6136
+ sid: _OC_SID4,
6137
+ mode: currentMode,
6138
+ regime,
6139
+ stress: stressScore,
6140
+ fp: currentProjectFingerprint || ""
6141
+ }) + "\n";
6142
+ try {
6143
+ mkdirSync7(calDir, { recursive: true });
6144
+ appendFileSync5(calFile, calRecord);
6145
+ } catch {
6146
+ }
6078
6147
  if (!oneShot("vibeos_dashboard_instruct")) {
6079
6148
  pushSystem(output, "[vibeOS dashboard display] When the trinity tool returns output starting with '[vibeOS-dashboard]', you MUST use the question tool to display that data in a clean, human-readable format. Use the question field (not the header) to show the dashboard data. Format it with clear sections separated by blank lines, aligned columns with spaces, and plain text only (no emojis, no markdown). The header should be 'vibeOS Dashboard'. Include only one option in options: {label: 'Dismiss', description: ''}. Strip the '[vibeOS-dashboard]' marker line before displaying.");
6080
6149
  }
6150
+ if (!oneShot("vibeos_dopamine_style_" + fp2)) {
6151
+ pushSystem(output, "[tool style: dopamine] When calling the bash tool, use an emoji-prefixed, progress-focused description \u2014 e.g. 'Shell \u26A1 Compiling assets...' or 'Shell \u{1F9EA} Running tests...'. Combine independent bash commands into a single call with && or ;. Never use raw technical labels as tool descriptions.");
6152
+ }
6081
6153
  } catch (err) {
6082
6154
  console.error(`[vibeOS] system.transform failed: ${err.message}`);
6083
6155
  }
6084
6156
  };
6085
6157
 
6086
- // src/lib/hooks/footer.ts
6158
+ // src/lib/hooks/footer.js
6087
6159
  var _cachedAutoMode = null;
6088
6160
  var _cachedAutoModeTs = 0;
6089
6161
  var AUTO_CACHE_TTL = 6e4;
6162
+ var DEFAULT_REGIME_MAP = {
6163
+ LOOPING: "forensic",
6164
+ DIVERGENT: "forensic",
6165
+ EXPLORING: "web-research",
6166
+ INIT: "web-research",
6167
+ REFINING: "balanced",
6168
+ CONVERGING: "quality",
6169
+ CLOSED: "quality"
6170
+ };
6171
+ function regimeToMode(regime, stress) {
6172
+ if (stress > 1.5)
6173
+ return "quality";
6174
+ return DEFAULT_REGIME_MAP[regime] || "balanced";
6175
+ }
6090
6176
  async function apiAutoSelectMode(regime, stress) {
6091
6177
  const now = Date.now();
6092
- if (_cachedAutoMode && now - _cachedAutoModeTs < AUTO_CACHE_TTL) return _cachedAutoMode;
6178
+ if (_cachedAutoMode && now - _cachedAutoModeTs < AUTO_CACHE_TTL)
6179
+ return _cachedAutoMode;
6093
6180
  try {
6094
6181
  const res = await remoteCall("blackboxSelectMode", [regime, stress], null);
6095
6182
  if (res?.mode) {
@@ -6100,7 +6187,10 @@ async function apiAutoSelectMode(regime, stress) {
6100
6187
  } catch (e) {
6101
6188
  console.error("[vibeOS] apiAutoSelectMode error:", e.message);
6102
6189
  }
6103
- return _cachedAutoMode || "balanced";
6190
+ const fallback = regimeToMode(regime, stress);
6191
+ if (!_cachedAutoMode || _cachedAutoMode === "balanced")
6192
+ _cachedAutoMode = fallback;
6193
+ return _cachedAutoMode || fallback || "balanced";
6104
6194
  }
6105
6195
  var USER_HOME6 = (() => {
6106
6196
  try {
@@ -6149,24 +6239,33 @@ function readLifetimeSavings2() {
6149
6239
  }
6150
6240
  var _OC_SID5 = "opencode-" + (process.pid || "x") + "-" + Date.now();
6151
6241
  function scoreTaskQuality(outputText, promptText) {
6152
- if (typeof outputText !== "string" || outputText.length === 0) return 0;
6153
- if (typeof promptText !== "string") promptText = "";
6242
+ if (typeof outputText !== "string" || outputText.length === 0)
6243
+ return 0;
6244
+ if (typeof promptText !== "string")
6245
+ promptText = "";
6154
6246
  let score = 50;
6155
- if (promptText.length > 0 && outputText.length > promptText.length * 0.5) score += 10;
6156
- if (outputText.length < 50) score -= 20;
6157
- if (/error|failed|unable|cannot|could not/i.test(outputText)) score -= 10;
6158
- if (/TODO|FIXME|placeholder/i.test(outputText) && outputText.length < 200) score -= 15;
6247
+ if (promptText.length > 0 && outputText.length > promptText.length * 0.5)
6248
+ score += 10;
6249
+ if (outputText.length < 50)
6250
+ score -= 20;
6251
+ if (/error|failed|unable|cannot|could not/i.test(outputText))
6252
+ score -= 10;
6253
+ if (/TODO|FIXME|placeholder/i.test(outputText) && outputText.length < 200)
6254
+ score -= 15;
6159
6255
  const codeBlocks = (outputText.match(/```/g) || []).length;
6160
- if (codeBlocks >= 2) score += 10;
6161
- if (outputText.length > 500) score += 10;
6162
- if (outputText.length > 1e3) score += 5;
6256
+ if (codeBlocks >= 2)
6257
+ score += 10;
6258
+ if (outputText.length > 500)
6259
+ score += 10;
6260
+ if (outputText.length > 1e3)
6261
+ score += 5;
6163
6262
  return Math.max(0, Math.min(100, score));
6164
6263
  }
6165
6264
  async function _appendFooter(input, output, directory3) {
6166
- if (!loadSelection3().enabled) return;
6167
6265
  _refreshModel(directory3);
6168
6266
  let _footerStress = 0;
6169
- if (latestUserIntent) _footerStress = scoreStress(latestUserIntent);
6267
+ if (latestUserIntent)
6268
+ _footerStress = scoreStress(latestUserIntent);
6170
6269
  if (!currentModel) {
6171
6270
  try {
6172
6271
  const cfg = await client.config.get("model");
@@ -6180,10 +6279,12 @@ async function _appendFooter(input, output, directory3) {
6180
6279
  }
6181
6280
  try {
6182
6281
  const messageID = input?.messageID || input?.messageId || input?.message?.id || output?.messageID || output?.messageId || output?.message?.id || null;
6183
- if (messageID && textCompletePainted.has(messageID)) return;
6282
+ if (messageID && textCompletePainted.has(messageID))
6283
+ return;
6184
6284
  const text = typeof output?.text === "string" ? output.text : typeof output?.result === "string" ? output.result : typeof output?.content === "string" ? output.content : "";
6185
- if (!text || text.length < 50) {
6186
- if (messageID) textCompletePainted.add(messageID);
6285
+ if (!text) {
6286
+ if (messageID)
6287
+ textCompletePainted.add(messageID);
6187
6288
  return;
6188
6289
  }
6189
6290
  const { ltTasks, ltCache, ltCost, count, sesTasks, sesEdit, sesCredit, sesC7, sesQuota, sesCache, sesTaskDelegations, sesDuration, sesRatePerHour, sesTrend, sesToolBreakdown, sesModelTurns, quality_avg } = readLifetimeSavings2();
@@ -6233,12 +6334,17 @@ async function _appendFooter(input, output, directory3) {
6233
6334
  if (bbMode === "relaxed") {
6234
6335
  enfTagsFooter.push("[Q&A]");
6235
6336
  } else {
6236
- if (selNowFooter.delegation_enforce) enfTagsFooter.push("[ENF ON]");
6237
- if (selNowFooter.flow_enforce) enfTagsFooter.push("[FLOW ON]");
6238
- if (selNowFooter.tdd_enforce) enfTagsFooter.push("[TDD ON]");
6239
- if (bbMode === "strict") enfTagsFooter.push("[STRICT]");
6337
+ if (selNowFooter.delegation_enforce)
6338
+ enfTagsFooter.push("[ENF ON]");
6339
+ if (selNowFooter.flow_enforce)
6340
+ enfTagsFooter.push("[FLOW ON]");
6341
+ if (selNowFooter.tdd_enforce)
6342
+ enfTagsFooter.push("[TDD ON]");
6343
+ if (bbMode === "strict")
6344
+ enfTagsFooter.push("[STRICT]");
6240
6345
  }
6241
- if (_modelLocked) enfTagsFooter.push("[LOCK ON]");
6346
+ if (_modelLocked)
6347
+ enfTagsFooter.push("[LOCK ON]");
6242
6348
  let enfSuffixFooter = enfTagsFooter.length > 0 ? ` ${enfTagsFooter.join(" ")}` : "";
6243
6349
  if (quality_avg > 0) {
6244
6350
  enfSuffixFooter = ` QA:${Math.round(quality_avg)}% ${enfTagsFooter.join(" ")}`;
@@ -6251,7 +6357,7 @@ async function _appendFooter(input, output, directory3) {
6251
6357
  const autoActive = await apiAutoSelectMode(autoRegime, autoStress);
6252
6358
  optTagFooter = `[VIBE\u2192${autoActive.toUpperCase()}${flashIcon}]`;
6253
6359
  saveOptimizationMode(autoActive);
6254
- const slot2 = autoActive === "quality" ? "brain" : autoActive === "speed" ? "medium" : "cheap";
6360
+ const slot2 = autoActive === "quality" || autoActive === "forensic" || autoActive === "defense_in_depth" || autoActive === "reporting" ? "brain" : autoActive === "speed" || autoActive === "web-research" || autoActive === "verify" ? "medium" : "cheap";
6255
6361
  if (!_modelLocked) {
6256
6362
  writeSessionSlot(_OC_SID5, slot2);
6257
6363
  if (slot2 === "brain" && TRINITY_BRAIN) {
@@ -6268,33 +6374,34 @@ async function _appendFooter(input, output, directory3) {
6268
6374
  } else {
6269
6375
  optTagFooter = `[VIBE\u2192${(optModeFooter || "").toUpperCase()}${flashIcon}]`;
6270
6376
  }
6271
- modelTag = `${modelTag}${optTagFooter}${enfSuffixFooter || ""}`;
6272
6377
  const stripped = text.replace(/\n\n— .+(?: —)?$/, "");
6273
- if (stripped !== text) return;
6378
+ if (stripped !== text)
6379
+ return;
6274
6380
  const ltTotal = ltTasks + ltCache;
6275
- const trendIcon = sesTrend === "down" ? "\u2193" : sesTrend === "up" ? "\u2191" : "\u2192";
6276
- const brainModelCost = currentModel ? modelCostPerTurn(currentModel) ?? 0 : 0;
6277
- const cheapModelCost = _workerModel ? modelCostPerTurn(_workerModel) ?? 0 : 0;
6278
- const imputedMultiplier = brainModelCost > SAVE_EST.WRITE_EDIT && cheapModelCost > 0 && brainModelCost > cheapModelCost ? brainModelCost / cheapModelCost : 0;
6279
- let footerText;
6381
+ const modeVerbMap = {
6382
+ balanced: "routing",
6383
+ budget: "saving",
6384
+ quality: "focusing",
6385
+ speed: "moving",
6386
+ longrun: "pacing",
6387
+ auto: "vibing",
6388
+ "web-research": "researching",
6389
+ forensic: "investigating"
6390
+ };
6391
+ const optMode = (optModeFooter || "balanced").toLowerCase();
6392
+ const modeVerb = modeVerbMap[optMode] || "vibing";
6393
+ let vibeLine = `\u2014 ${modeVerb} on ${shortModelName(brainModel)}`;
6280
6394
  if (ltTotal > 0) {
6281
- let savingsDisplay = `vibeOS: $${formatUsd(ltTotal)} saved up ${trendIcon}`;
6282
- const _todoCount = loadTodos().filter((t) => t.status === "pending").length;
6283
- if (_todoCount > 0) savingsDisplay += " | [" + _todoCount + " todo]";
6284
- if (imputedMultiplier > 2) {
6285
- const imputedActual = ltTotal * imputedMultiplier;
6286
- savingsDisplay += ` ($${formatUsd(imputedActual)} actual)`;
6287
- }
6288
- const stressBar = _footerStress > 0.85 ? "\u2588" : _footerStress > 0.7 ? "\u2586" : _footerStress > 0.5 ? "\u2585" : _footerStress > 0.3 ? "\u2583" : _footerStress > 0.1 ? "\u2582" : "\u2581";
6289
- const stressLabel = _footerStress > 0.7 ? "high" : _footerStress > 0.4 ? "elevated" : "calm";
6290
- footerText = stripped + `
6291
-
6292
- \u2014 ${modelTag} | ${savingsDisplay} | stress: ${stressBar} ${stressLabel} \u2014`;
6293
- } else {
6294
- footerText = stripped + `
6295
-
6296
- \u2014 ${modelTag} \u2014`;
6395
+ vibeLine += ` \u2728 $${formatUsd(ltTotal)} saved`;
6396
+ }
6397
+ vibeLine += `, VIBE${flashIcon ? " \u26A1" : ""}`;
6398
+ if (_footerStress > 0.4) {
6399
+ const stressLabel = _footerStress > 0.7 ? "elevated" : "uneven";
6400
+ vibeLine += ` \xB7 ${stressLabel}`;
6297
6401
  }
6402
+ const footerText = stripped + `
6403
+
6404
+ ${vibeLine} \u2014`;
6298
6405
  if (_blackboxEnabled) {
6299
6406
  try {
6300
6407
  const prevText = _prevOutputText;
@@ -6305,19 +6412,29 @@ async function _appendFooter(input, output, directory3) {
6305
6412
  const tracker = getBlackboxTracker();
6306
6413
  tracker.recordOutcome(outcome);
6307
6414
  syncOutcomeToApi(outcome);
6415
+ try {
6416
+ mkdirSync8(join13(USER_HOME6, ".claude"), { recursive: true });
6417
+ appendFileSync6(join13(USER_HOME6, ".claude", "calibration-data.jsonl"), JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), event: "outcome", sid: _OC_SID5, outcome }) + "\n");
6418
+ } catch {
6419
+ }
6308
6420
  }
6309
6421
  }
6310
6422
  } catch {
6311
6423
  }
6312
6424
  }
6313
- if (typeof output?.text === "string") output.text = footerText;
6314
- else if (typeof output?.result === "string") output.result = footerText;
6315
- else if (typeof output?.content === "string") output.content = footerText;
6316
- else output.text = footerText;
6425
+ if (typeof output?.text === "string")
6426
+ output.text = footerText;
6427
+ else if (typeof output?.result === "string")
6428
+ output.result = footerText;
6429
+ else if (typeof output?.content === "string")
6430
+ output.content = footerText;
6431
+ else
6432
+ output.text = footerText;
6317
6433
  textCompletePainted.add(messageID);
6318
6434
  if (textCompletePainted.size > 500) {
6319
6435
  const it = textCompletePainted.values();
6320
- for (let i = 0; i < 100; i++) textCompletePainted.delete(it.next().value);
6436
+ for (let i = 0; i < 100; i++)
6437
+ textCompletePainted.delete(it.next().value);
6321
6438
  }
6322
6439
  } catch (err) {
6323
6440
  console.error(`[vibeOS] footer failed: ${err.message}`);
@@ -6325,13 +6442,13 @@ async function _appendFooter(input, output, directory3) {
6325
6442
  }
6326
6443
 
6327
6444
  // src/lib/hooks/tool-execute.js
6328
- import { writeFileSync as writeFileSync12, appendFileSync as appendFileSync7, existsSync as existsSync13, mkdirSync as mkdirSync10 } from "node:fs";
6329
- import { dirname as dirname8, basename as basename8 } from "node:path";
6445
+ import { writeFileSync as writeFileSync11, appendFileSync as appendFileSync8, existsSync as existsSync12, mkdirSync as mkdirSync10 } from "node:fs";
6446
+ import { dirname as dirname7, basename as basename7 } from "node:path";
6330
6447
  init_flow_enforcer();
6331
6448
 
6332
6449
  // src/lib/tdd-enforcer.js
6333
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, appendFileSync as appendFileSync6, existsSync as existsSync12, mkdirSync as mkdirSync9, statSync as statSync7, readdirSync as readdirSync4, rmSync as rmSync4, openSync as openSync3 } from "node:fs";
6334
- import { join as join14, dirname as dirname7 } from "node:path";
6450
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, appendFileSync as appendFileSync7, existsSync as existsSync11, mkdirSync as mkdirSync9, statSync as statSync6, readdirSync as readdirSync3, rmSync as rmSync4, openSync as openSync3 } from "node:fs";
6451
+ import { join as join14, dirname as dirname6 } from "node:path";
6335
6452
  import { createHash as createHash4 } from "node:crypto";
6336
6453
 
6337
6454
  // src/utils/tdd-helpers.js
@@ -7371,7 +7488,7 @@ function _detectTestFramework() {
7371
7488
  try {
7372
7489
  const root = directory || process.cwd();
7373
7490
  const pkgPath = join14(root, "package.json");
7374
- if (existsSync12(pkgPath)) {
7491
+ if (existsSync11(pkgPath)) {
7375
7492
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
7376
7493
  const testScript = String(pkg?.scripts?.test || "");
7377
7494
  const deps = { ...pkg?.devDependencies, ...pkg?.dependencies };
@@ -7393,9 +7510,9 @@ function _detectTestFramework() {
7393
7510
  const testDirs = ["src/tests", "tests", "test", "__tests__"];
7394
7511
  for (const td of testDirs) {
7395
7512
  const dirPath = join14(root, td);
7396
- if (!existsSync12(dirPath))
7513
+ if (!existsSync11(dirPath))
7397
7514
  continue;
7398
- const files = readdirSync4(dirPath).filter((f) => /\.test\./.test(f) || /\.spec\./.test(f));
7515
+ const files = readdirSync3(dirPath).filter((f) => /\.test\./.test(f) || /\.spec\./.test(f));
7399
7516
  if (files.length > 0) {
7400
7517
  const content = readFileSync13(join14(dirPath, files[0]), "utf-8");
7401
7518
  if (/from\s+['"]node:test['"]/.test(content)) {
@@ -7440,7 +7557,7 @@ function _acquireLock(testPath) {
7440
7557
  if (err.code !== "EEXIST")
7441
7558
  return false;
7442
7559
  try {
7443
- const st = statSync7(lockPath);
7560
+ const st = statSync6(lockPath);
7444
7561
  if (Date.now() - st.mtimeMs >= LOCK_EXPIRE_MS) {
7445
7562
  rmSync4(lockPath, { force: true });
7446
7563
  try {
@@ -7467,7 +7584,7 @@ function _releaseLock(testPath) {
7467
7584
  }
7468
7585
  function _isInCooldown(testPath) {
7469
7586
  try {
7470
- if (!existsSync12(ENFORCEMENT_COOLDOWN_FILE2))
7587
+ if (!existsSync11(ENFORCEMENT_COOLDOWN_FILE2))
7471
7588
  return false;
7472
7589
  const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
7473
7590
  const lines = readFileSync13(ENFORCEMENT_COOLDOWN_FILE2, "utf-8").trim().split("\n").filter(Boolean);
@@ -7487,13 +7604,13 @@ function _isInCooldown(testPath) {
7487
7604
  }
7488
7605
  function _recordCooldown(testPath) {
7489
7606
  try {
7490
- mkdirSync9(dirname7(ENFORCEMENT_COOLDOWN_FILE2), { recursive: true });
7607
+ mkdirSync9(dirname6(ENFORCEMENT_COOLDOWN_FILE2), { recursive: true });
7491
7608
  const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
7492
7609
  const entry = JSON.stringify({ h: hash, ts: Date.now() }) + "\n";
7493
- appendFileSync6(ENFORCEMENT_COOLDOWN_FILE2, entry);
7610
+ appendFileSync7(ENFORCEMENT_COOLDOWN_FILE2, entry);
7494
7611
  const lines = readFileSync13(ENFORCEMENT_COOLDOWN_FILE2, "utf-8").trim().split("\n").filter(Boolean);
7495
7612
  if (lines.length > 500) {
7496
- writeFileSync11(ENFORCEMENT_COOLDOWN_FILE2, lines.slice(-200).join("\n") + "\n");
7613
+ writeFileSync10(ENFORCEMENT_COOLDOWN_FILE2, lines.slice(-200).join("\n") + "\n");
7497
7614
  }
7498
7615
  } catch {
7499
7616
  }
@@ -7555,13 +7672,13 @@ function buildTestSkeleton(filePath, sourceContent = "", options = {}) {
7555
7672
  testPath = testPath.replace(new RegExp("\\.[^.]+$"), "." + fw.testExt);
7556
7673
  }
7557
7674
  const exports = extractExports(sourceContent, extLower);
7558
- return { path: testPath, content: skeletonFn(name, exports, "full", strict, quality, sourceContent), dir: dirname7(testPath) };
7675
+ return { path: testPath, content: skeletonFn(name, exports, "full", strict, quality, sourceContent), dir: dirname6(testPath) };
7559
7676
  }
7560
7677
  function enforceTestFile(filePath) {
7561
7678
  console.error(`[vibeOS] [tdd-enforce] enforceTestFile called for ${filePath}`);
7562
7679
  let sourceContent = "";
7563
7680
  try {
7564
- if (existsSync12(filePath)) {
7681
+ if (existsSync11(filePath)) {
7565
7682
  sourceContent = readFileSync13(filePath, "utf-8");
7566
7683
  }
7567
7684
  } catch {
@@ -7570,7 +7687,7 @@ function enforceTestFile(filePath) {
7570
7687
  const skeleton = buildTestSkeleton(filePath, sourceContent, { strict: sel.tdd_strict !== false, quality: sel.tdd_quality !== false });
7571
7688
  if (!skeleton)
7572
7689
  return null;
7573
- if (existsSync12(skeleton.path))
7690
+ if (existsSync11(skeleton.path))
7574
7691
  return null;
7575
7692
  if (_enforcementCooldown.has(skeleton.path))
7576
7693
  return null;
@@ -7580,7 +7697,7 @@ function enforceTestFile(filePath) {
7580
7697
  return null;
7581
7698
  try {
7582
7699
  mkdirSync9(skeleton.dir, { recursive: true });
7583
- writeFileSync11(skeleton.path, skeleton.content);
7700
+ writeFileSync10(skeleton.path, skeleton.content);
7584
7701
  _enforcementCooldown.add(skeleton.path);
7585
7702
  _recordCooldown(skeleton.path);
7586
7703
  try {
@@ -7842,7 +7959,7 @@ var onToolExecuteBefore = async (input, output) => {
7842
7959
  if (sel.delegation_enforce && currentTier === "high" && args && typeof args === "object") {
7843
7960
  const actualArgs = args || output && output.args || {};
7844
7961
  const originalPath = actualArgs.filePath || actualArgs.file_path || "";
7845
- const basename10 = originalPath.split("/").pop() || "blocked";
7962
+ const basename9 = originalPath.split("/").pop() || "blocked";
7846
7963
  const apiResult = await remoteCall("delegateCheck", [tLower, currentTier, currentModel, _prompt], () => ({
7847
7964
  blocked: true,
7848
7965
  savings: _estEdit
@@ -7851,7 +7968,7 @@ var onToolExecuteBefore = async (input, output) => {
7851
7968
  const savings = apiResult?.savings ?? _estEdit;
7852
7969
  if (isBlocked) {
7853
7970
  if (tLower === "write") {
7854
- actualArgs.filePath = `/tmp/vibeos-enforcement-blocked-${basename10}`;
7971
+ actualArgs.filePath = `/tmp/vibeos-enforcement-blocked-${basename9}`;
7855
7972
  if (actualArgs.file_path !== void 0)
7856
7973
  actualArgs.file_path = actualArgs.filePath;
7857
7974
  } else if (tLower === "edit" || tLower === "notebookedit") {
@@ -7884,10 +8001,10 @@ var onToolExecuteBefore = async (input, output) => {
7884
8001
  }
7885
8002
  } else {
7886
8003
  const missed = recordMissedContext7(_estC7);
7887
- if (!existsSync13(CONTEXT7_INSTALL_FLAG)) {
8004
+ if (!existsSync12(CONTEXT7_INSTALL_FLAG)) {
7888
8005
  try {
7889
- mkdirSync10(dirname8(CONTEXT7_INSTALL_FLAG), { recursive: true });
7890
- writeFileSync12(CONTEXT7_INSTALL_FLAG, "");
8006
+ mkdirSync10(dirname7(CONTEXT7_INSTALL_FLAG), { recursive: true });
8007
+ writeFileSync11(CONTEXT7_INSTALL_FLAG, "");
7891
8008
  } catch {
7892
8009
  }
7893
8010
  console.error(`[vibeOS] \u{1F4A1} Install context7 MCP to save ~$0.06/turn on docs: \`claude mcp add context7 npx @upstash/context7-mcp\``);
@@ -7910,8 +8027,6 @@ var onToolExecuteBefore = async (input, output) => {
7910
8027
  }
7911
8028
  };
7912
8029
  var onToolExecuteAfter = async (input, output) => {
7913
- if (!loadSelection().enabled)
7914
- return;
7915
8030
  _refreshModel(projectDirectory);
7916
8031
  let _footerText = "";
7917
8032
  try {
@@ -8031,7 +8146,7 @@ var onToolExecuteAfter = async (input, output) => {
8031
8146
  if (t === "task") {
8032
8147
  const quality = scoreTaskQuality(output?.result || output?.text || "", input?.args?.prompt || "");
8033
8148
  try {
8034
- appendFileSync7(SAVINGS_LEDGER_FILE, JSON.stringify({
8149
+ appendFileSync8(SAVINGS_LEDGER_FILE, JSON.stringify({
8035
8150
  at: (/* @__PURE__ */ new Date()).toISOString(),
8036
8151
  kind: "quality",
8037
8152
  score: quality,
@@ -8172,7 +8287,7 @@ ${pendingUiNote}`;
8172
8287
  if (guardRe.test(fp3)) {
8173
8288
  const guardIcons = { flag: "!", warn: "!!", hint: "_" };
8174
8289
  const guardIcon = guardIcons.flag || "!";
8175
- const fn = basename8(fp3);
8290
+ const fn = basename7(fp3);
8176
8291
  console.error(`[flow-enforcer] ${guardIcon} [guard] ${fn}: protected project doc modified \u2014 verify user intent`);
8177
8292
  }
8178
8293
  }
@@ -8250,7 +8365,7 @@ ${pendingUiNote}`;
8250
8365
  };
8251
8366
 
8252
8367
  // src/lib/hooks/session-compact.js
8253
- import { readFileSync as readFileSync14, existsSync as existsSync14 } from "node:fs";
8368
+ import { readFileSync as readFileSync14, existsSync as existsSync13 } from "node:fs";
8254
8369
  var onSessionCompacting = async (_input, output) => {
8255
8370
  if (!loadSelection().enabled)
8256
8371
  return;
@@ -8259,7 +8374,7 @@ var onSessionCompacting = async (_input, output) => {
8259
8374
  const needsCompact = turnCount >= 7;
8260
8375
  const indexPath = getSessionIndexPath();
8261
8376
  let recent = "";
8262
- if (existsSync14(indexPath)) {
8377
+ if (existsSync13(indexPath)) {
8263
8378
  try {
8264
8379
  const lines = readFileSync14(indexPath, "utf-8").trim().split("\n").slice(-30);
8265
8380
  recent = lines.map((l) => {
@@ -8306,6 +8421,8 @@ var setShellDirectory = (dir) => {
8306
8421
  var onShellEnv = async (_input, output) => {
8307
8422
  try {
8308
8423
  _refreshModel(directory2 || process.cwd());
8424
+ if (!output)
8425
+ output = {};
8309
8426
  output.env ??= {};
8310
8427
  output.env.OPENCODE_MODEL_TIER = currentTier || "unknown";
8311
8428
  output.env.OPENCODE_MODEL = currentModel || "unknown";
@@ -8330,8 +8447,8 @@ function _loadOpenCodeProviders() {
8330
8447
  function _readOpenCodeConfigObject(dir) {
8331
8448
  const jsonPath = join15(dir, "opencode.json");
8332
8449
  const jsoncPath = join15(dir, "opencode.jsonc");
8333
- if (existsSync15(jsonPath)) return safeJsonParse3(readFileSync15(jsonPath, "utf-8"));
8334
- if (existsSync15(jsoncPath)) return _parseJsonc(readFileSync15(jsoncPath, "utf-8"));
8450
+ if (existsSync14(jsonPath)) return safeJsonParse3(readFileSync15(jsonPath, "utf-8"));
8451
+ if (existsSync14(jsoncPath)) return _parseJsonc(readFileSync15(jsoncPath, "utf-8"));
8335
8452
  return {};
8336
8453
  }
8337
8454
  function _parseJsonc(raw) {
@@ -8356,11 +8473,11 @@ function _modelTier2(id2) {
8356
8473
  }
8357
8474
  function backupFile(path, label) {
8358
8475
  try {
8359
- if (!existsSync15(path)) return null;
8476
+ if (!existsSync14(path)) return null;
8360
8477
  const bkDir = join15(USER_HOME2, ".claude", ".backups");
8361
8478
  mkdirSync11(bkDir, { recursive: true });
8362
- const bk = join15(bkDir, `${basename9(path)}.${label}.${Date.now()}.bak`);
8363
- copyFileSync6(path, bk);
8479
+ const bk = join15(bkDir, `${basename8(path)}.${label}.${Date.now()}.bak`);
8480
+ copyFileSync5(path, bk);
8364
8481
  return bk;
8365
8482
  } catch {
8366
8483
  return null;
@@ -8382,7 +8499,7 @@ function loadMcpPort() {
8382
8499
  return n;
8383
8500
  }
8384
8501
  try {
8385
- if (existsSync15(TIERS_FILE2)) {
8502
+ if (existsSync14(TIERS_FILE2)) {
8386
8503
  const tiers = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8387
8504
  const cfg = tiers?.selection?.mcp_port ?? tiers?.mcp_port;
8388
8505
  if (cfg === false || cfg === "disabled" || cfg === 0) return 0;
@@ -8395,14 +8512,14 @@ function loadMcpPort() {
8395
8512
  }
8396
8513
  function persistMcpPort(port) {
8397
8514
  try {
8398
- if (!existsSync15(TIERS_FILE2)) return;
8515
+ if (!existsSync14(TIERS_FILE2)) return;
8399
8516
  const tiers = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8400
8517
  tiers.selection ??= {};
8401
8518
  if (Number(tiers.selection.mcp_port) === Number(port)) return;
8402
8519
  tiers.selection.mcp_port = port;
8403
- mkdirSync11(dirname9(TIERS_FILE2), { recursive: true });
8520
+ mkdirSync11(dirname8(TIERS_FILE2), { recursive: true });
8404
8521
  const tmp = TIERS_FILE2 + ".tmp." + Date.now();
8405
- writeFileSync13(tmp, JSON.stringify(tiers, null, 2) + "\n", "utf-8");
8522
+ writeFileSync12(tmp, JSON.stringify(tiers, null, 2) + "\n", "utf-8");
8406
8523
  renameSync6(tmp, TIERS_FILE2);
8407
8524
  } catch {
8408
8525
  }
@@ -8592,12 +8709,12 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8592
8709
  } else {
8593
8710
  console.error("[vibeOS] NO MODEL \u2014 enforcement disabled, will auto-detect on first hook");
8594
8711
  }
8595
- console.error(`[vibeOS] auto-config guard: currentModel=${currentModel ? "SET" : "NONE"}, TIERS_FILE=${TIERS_FILE2}, exists=${existsSync15(TIERS_FILE2)}`);
8596
- if (currentModel || !existsSync15(TIERS_FILE2)) {
8712
+ console.error(`[vibeOS] auto-config guard: currentModel=${currentModel ? "SET" : "NONE"}, TIERS_FILE=${TIERS_FILE2}, exists=${existsSync14(TIERS_FILE2)}`);
8713
+ if (currentModel || !existsSync14(TIERS_FILE2)) {
8597
8714
  try {
8598
8715
  let _tiersData2;
8599
8716
  let _wasCorrupted = false;
8600
- if (existsSync15(TIERS_FILE2)) {
8717
+ if (existsSync14(TIERS_FILE2)) {
8601
8718
  try {
8602
8719
  _tiersData2 = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8603
8720
  } catch {
@@ -8670,9 +8787,9 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8670
8787
  if (_tiersData2) {
8671
8788
  _tiersData2.selection ??= {};
8672
8789
  if (_tiersData2.selection.mcp_port === void 0) _tiersData2.selection.mcp_port = 9578;
8673
- mkdirSync11(dirname9(TIERS_FILE2), { recursive: true });
8790
+ mkdirSync11(dirname8(TIERS_FILE2), { recursive: true });
8674
8791
  const _tmp = TIERS_FILE2 + ".tmp." + Date.now();
8675
- writeFileSync13(_tmp, JSON.stringify(_tiersData2, null, 2) + "\n", "utf-8");
8792
+ writeFileSync12(_tmp, JSON.stringify(_tiersData2, null, 2) + "\n", "utf-8");
8676
8793
  renameSync6(_tmp, TIERS_FILE2);
8677
8794
  console.error(`[vibeOS] auto-synced model-tiers.json: brain=${_brain.id} medium=${_tiersData2.trinity?.medium?.oc || ""} cheap=${_tiersData2.trinity?.cheap?.oc || ""}`);
8678
8795
  const _tiersCfg = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
@@ -8696,7 +8813,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8696
8813
  if (_mt.selection && (_mt.selection.mcp_port === void 0 || _mt.selection.mcp_port === null)) {
8697
8814
  _mt.selection.mcp_port = 9578;
8698
8815
  const _tmp = TIERS_FILE2 + ".tmp." + Date.now();
8699
- writeFileSync13(_tmp, JSON.stringify(_mt, null, 2) + "\n", "utf-8");
8816
+ writeFileSync12(_tmp, JSON.stringify(_mt, null, 2) + "\n", "utf-8");
8700
8817
  renameSync6(_tmp, TIERS_FILE2);
8701
8818
  }
8702
8819
  } catch {
@@ -8716,7 +8833,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8716
8833
  console.error(`[vibeOS] project-memory init failed for ${fp}: ${err.message}`);
8717
8834
  }
8718
8835
  try {
8719
- if (directory3 && existsSync15(directory3)) {
8836
+ if (directory3 && existsSync14(directory3)) {
8720
8837
  const techStack = detectTechStack(directory3);
8721
8838
  const result = ensureProjectDocs(directory3, techStack);
8722
8839
  if (result.created.length > 0) console.error(`[vibeOS] Project Guard: created ${result.created.join(", ")}`);
@@ -8728,6 +8845,10 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8728
8845
  } catch (err) {
8729
8846
  console.error(`[vibeOS] Project Guard init failed: ${err.message}`);
8730
8847
  }
8848
+ try {
8849
+ writeSelection("enabled", true);
8850
+ } catch {
8851
+ }
8731
8852
  const _tiersData = (() => {
8732
8853
  try {
8733
8854
  return safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
@@ -8754,8 +8875,8 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8754
8875
  directory: directory3,
8755
8876
  safeJsonParse: safeJsonParse3,
8756
8877
  readFileSync: readFileSync15,
8757
- writeFileSync: writeFileSync13,
8758
- existsSync: existsSync15,
8878
+ writeFileSync: writeFileSync12,
8879
+ existsSync: existsSync14,
8759
8880
  renameSync: renameSync6,
8760
8881
  TIERS_FILE: TIERS_FILE2,
8761
8882
  USER_HOME: USER_HOME2,
@@ -8825,12 +8946,6 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8825
8946
  "experimental.chat.messages.transform": async (_input, output) => {
8826
8947
  return onMessagesTransform(_input, output);
8827
8948
  },
8828
- "experimental.text.complete": async (input, output) => {
8829
- await _appendFooter(input, output, directory3);
8830
- },
8831
- "message.updated": async (input, output) => {
8832
- await _appendFooter(input, output, directory3);
8833
- },
8834
8949
  "experimental.session.compacting": async (_input, output) => {
8835
8950
  return onSessionCompacting(_input, output);
8836
8951
  },
@@ -8841,6 +8956,12 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8841
8956
  if (typeof setShellDirectory === "function") setShellDirectory(directory3 || "");
8842
8957
  return onShellEnv(_input, output);
8843
8958
  },
8959
+ "experimental.text.complete": async (_input, output) => {
8960
+ await _appendFooter(_input, output, directory3);
8961
+ },
8962
+ "message.updated": async (_input, output) => {
8963
+ await _appendFooter(_input, output, directory3);
8964
+ },
8844
8965
  tool: {
8845
8966
  trinity: tool(createTrinityTool(trinityDeps)),
8846
8967
  "research-audit": tool({
@@ -8954,7 +9075,7 @@ ${report.narrative}`);
8954
9075
  getSessionMetrics: () => computeSessionMetrics(readFullState(), _OC_SID),
8955
9076
  getTodos: () => loadTodos(),
8956
9077
  listReports: (filter) => {
8957
- if (!existsSync15(REPORTS_DIR)) {
9078
+ if (!existsSync14(REPORTS_DIR)) {
8958
9079
  const e = new Error("reports dir not found");
8959
9080
  e.status = 404;
8960
9081
  throw e;