vibeostheog 0.15.34 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## 0.16.0
2
+ - feat: dopamine-style footer + natural language system directives
3
+ - feat: dopamine-style footer + natural language system directives
4
+ - feat: turn-aware compaction directive at turn 7+
5
+ - feat: add forensic/web-research modes + 1084-datapoint benchmark
6
+ - fix: flash icon only when API connected, unified [VIBE→MODE⚡] format
7
+ - docs: Security section + Context7 cost optimization docs
8
+ - docs: add Security section with API token emphasis and Context7 cost optimization docs
9
+ - docs: persist all benchmark data + compaction research
10
+ - docs: reformat README as user-facing PM doc, move internals to AGENTS.md, cleanup .gitignore
11
+ Merge pull request #35 from DrunkkToys/refactor/simplify-chat-transform
12
+ readme: center VIBE autoswitching as the core value proposition
13
+ Revert "fix: add system prompt cache savings tracking"
14
+
15
+
1
16
  ## 0.15.23
2
17
  - feat: add trinity api-token command to inject VIBEOS_API_TOKEN
3
18
 
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.16.0",
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 mkdirSync10, 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,
@@ -5126,7 +5126,7 @@ async function probeModel(modelId, auth) {
5126
5126
  }
5127
5127
  }
5128
5128
 
5129
- // src/lib/hooks/footer.ts
5129
+ // src/lib/hooks/footer.js
5130
5130
  import { readFileSync as readFileSync12 } from "node:fs";
5131
5131
  import { join as join13 } from "node:path";
5132
5132
  import { homedir as homedir9, tmpdir as tmpdir6 } from "node:os";
@@ -5603,7 +5603,7 @@ function buildProjectBriefing(directory3) {
5603
5603
  const label = currentProjectName || (directory3 ? basename6(directory3) : "");
5604
5604
  if (!label)
5605
5605
  return null;
5606
- return `[project memory] Active project: ${label}. Stay focused on the current repository and prefer the existing workflow.`;
5606
+ return `Working on ${label}. Keep focused on this repository and its conventions.`;
5607
5607
  }
5608
5608
  function ensureProjectSkill(dir, fp2) {
5609
5609
  const skillsDir = join12(dir, ".opencode", "skills");
@@ -5904,20 +5904,20 @@ var onMessagesTransform = async (_input, output) => {
5904
5904
  }
5905
5905
  };
5906
5906
  var C7_URGENCY = {
5907
- required: " CRITICAL: context7 usage is REQUIRED this turn.",
5908
- optional: " (context7 is optional this turn -- use if helpful but not required.)"
5907
+ required: " This turn, context7 usage is required.",
5908
+ optional: " This turn, context7 is optional \u2014 use it if helpful."
5909
5909
  };
5910
5910
  function context7Directive(cv) {
5911
5911
  const urgency = cv?.context7_urgency || "preferred";
5912
- return "[cost policy] If mcp__context7__resolve-library-id and mcp__context7__get-library-docs tools are available in this session, ALWAYS use them instead of WebFetch or WebSearch when looking up library or framework documentation (docs.*, readthedocs.*, npmjs.com/package/*, pypi.org/project/*, pkg.go.dev, /api/reference/). Do not fetch those URLs directly when context7 can serve the same content. This saves ~$0.06/turn on average." + (C7_URGENCY[urgency] || "");
5912
+ return "When looking up library or framework documentation (docs.*, readthedocs.*, npmjs.com/package/*, pypi.org/project/*, pkg.go.dev, /api/reference/), use mcp__context7__resolve-library-id and mcp__context7__get-library-docs if they are available instead of WebFetch or WebSearch \u2014 they cost less. Saves roughly $0.06 per turn on average." + (C7_URGENCY[urgency] || "");
5913
5913
  }
5914
5914
  function thinkingDirective(level) {
5915
5915
  const credit = loadCredit();
5916
5916
  const creditNote = `credit ${credit}%`;
5917
5917
  if (level === "brief") {
5918
- return `[thinking policy] Reasoning depth: BRIEF (manually set, ${creditNote}). Use extended thinking only for genuinely complex multi-step problems. Keep reasoning concise -- skip exploratory scratch work and restatement.`;
5918
+ return `You're in brief reasoning mode (${creditNote}). Use extended thinking only for genuinely complex multi-step problems. Keep reasoning concise and skip exploratory scratch work.`;
5919
5919
  }
5920
- return `[thinking policy] Reasoning depth: OFF (manually set, ${creditNote}). Skip extended thinking entirely. Respond directly and concisely. Every thinking token costs money -- save it for when the user explicitly asks.`;
5920
+ return `Extended thinking is off (${creditNote}). Respond directly and concisely \u2014 thinking tokens cost money, save them for when the user explicitly asks.`;
5921
5921
  }
5922
5922
  function orchestratorDirective(cv, sel) {
5923
5923
  const tierBias = cv?.tier_bias || "auto";
@@ -5929,7 +5929,7 @@ function orchestratorDirective(cv, sel) {
5929
5929
  const cheapModel = TRINITY_CHEAP || "the cheaper model";
5930
5930
  const mediumModel = TRINITY_MEDIUM || "the medium model";
5931
5931
  const targetModel = tierBias === "cheap" ? cheapModel : tierBias === "medium" ? mediumModel : tierBias === "brain" ? brainModel : `${cheapModel} or ${mediumModel}`;
5932
- return `[AI ORCHESTRATOR AGENT] You are an AI orchestrator agent. Delegate heavy work to Task subagents (runs on ${targetModel}). Your role: verify, fill gaps, synthesize. CRITICAL: Write/Edit tools are BLOCKED on this tier. You MUST delegate ALL implementation work to Task subagents. Always display the vibeOS cost footer.` + (tierBias !== "auto" ? ` [tier routing] This turn is biased toward ${tierBias} tier.` : "");
5932
+ return `You coordinate the work. Delegate heavy implementation to Task subagents (runs on ${targetModel}). Your job: verify results, fill gaps, and synthesize. Write/Edit tools are blocked on this tier \u2014 delegate all implementation work. Always show the vibeOS cost footer.` + (tierBias !== "auto" ? ` This turn is biased toward ${tierBias} tier.` : "");
5933
5933
  }
5934
5934
  var TDD_NOTES = {
5935
5935
  lazy: " Skeletons only when explicitly requested.",
@@ -5939,21 +5939,21 @@ var TDD_NOTES = {
5939
5939
  function tddDirective(cv, sel) {
5940
5940
  const tddMode = cv?.tdd_mode || (sel.tdd_strict ? "strict" : "normal");
5941
5941
  const tddFocus = cv?.tdd_focus || [];
5942
- const focusNote = tddFocus.length > 0 ? ` Focus: ${tddFocus.join(", ")}.` : "";
5943
- return `[tdd enforcement: ${tddMode}] Auto-create skeleton tests for source files being written/edited.${TDD_NOTES[tddMode] || ""}${focusNote} When creating or modifying source files, ensure corresponding test files exist with proper assertions.`;
5942
+ const focusNote = tddFocus.length > 0 ? ` Focus on: ${tddFocus.join(", ")}.` : "";
5943
+ return `Auto-create test skeletons for source files you write or edit.${TDD_NOTES[tddMode] || ""}${focusNote} Make sure corresponding test files exist with proper assertions.`;
5944
5944
  }
5945
5945
  function flowDirective(cv, sel) {
5946
5946
  const flowMode = cv?.flow_mode || (sel.flow_enforce ? "normal" : "audit");
5947
5947
  const flowFocus = cv?.flow_focus || [];
5948
- const enforceNote = sel.flow_enforce ? " TODO/FIXME extraction is active." : "";
5949
- const focusNote = flowFocus.length > 0 ? ` Focus rules: ${flowFocus.join(", ")}.` : "";
5950
- return `[flow enforcement: ${flowMode}] Development flow rules are active: write/edit operations are checked against project conventions.${enforceNote}${focusNote} Follow existing code patterns, naming conventions, and project structure.`;
5948
+ const enforceNote = sel.flow_enforce ? " TODO and FIXME markers are being tracked." : "";
5949
+ const focusNote = flowFocus.length > 0 ? ` Focus on: ${flowFocus.join(", ")}.` : "";
5950
+ return `Follow project conventions when writing or editing code \u2014 check existing patterns and naming conventions.${enforceNote}${focusNote}`;
5951
5951
  }
5952
5952
  function flowTodosDirective() {
5953
5953
  const pendingTodos = loadTodos().filter((t) => t.status === "pending").length;
5954
5954
  if (pendingTodos === 0)
5955
5955
  return null;
5956
- return "[vibeOS] " + pendingTodos + " extracted TODO/FIXME items are pending. Consider calling `todowrite` to add them to the native task list.";
5956
+ return pendingTodos + " extracted TODO or FIXME items are pending. Consider using `todowrite` to add them to the task list.";
5957
5957
  }
5958
5958
  function patternDirective(fp2) {
5959
5959
  const patterns = promotedProjectPatterns(fp2);
@@ -5966,11 +5966,11 @@ function patternDirective(fp2) {
5966
5966
  parts.push("Routines: " + routines.map((r) => r.summary).join("; "));
5967
5967
  }
5968
5968
  if (frictions.length > 0) {
5969
- parts.push("Frictions: " + frictions.map((f) => f.summary).join("; "));
5969
+ parts.push("Things to watch: " + frictions.map((f) => f.summary).join("; "));
5970
5970
  }
5971
5971
  if (parts.length === 0)
5972
5972
  return null;
5973
- return "[project patterns] " + parts.join(". ") + ".";
5973
+ return "Learned patterns for this project \u2014 " + parts.join(". ") + ".";
5974
5974
  }
5975
5975
  function welcomeDirective() {
5976
5976
  const sel = loadSelection();
@@ -5981,14 +5981,13 @@ function welcomeDirective() {
5981
5981
  }
5982
5982
  const active = sel.active_slot || "medium";
5983
5983
  const current = currentModel || "(unknown)";
5984
- return "[vibeOS] Active plugin. Slot: " + active + " (" + current + "). Use trinity command to switch slots, rebuild, or check status. Run `trinity help` for all commands.";
5984
+ return "vibeOS is active. Slot: " + active + " (" + current + "). Use `trinity` to switch slots, rebuild, or check status. Run `trinity help` for all commands.";
5985
5985
  }
5986
5986
  function contextBudgetDirective(_input, output) {
5987
5987
  const ctxBudget = estimateContextBudget(_input, output);
5988
5988
  if (!ctxBudget || ctxBudget.pct <= 70)
5989
5989
  return null;
5990
- const severity = ctxBudget.pct > 90 ? "CRITICAL" : "WARNING";
5991
- return `[context budget: ${severity}] Context window is ${ctxBudget.pct}% full (~${ctxBudget.estimatedTokens} tokens). Consider using Task subagents for heavy work, compressing tool outputs, or starting a new session to avoid context overflow.`;
5990
+ return `Context is ${ctxBudget.pct}% full (~${ctxBudget.estimatedTokens} tokens). Consider delegating heavy work to Task subagents, compressing tool outputs, or starting a new session.`;
5992
5991
  }
5993
5992
  var onSystemTransform = async (_input, output) => {
5994
5993
  if (!loadSelection().enabled)
@@ -6025,9 +6024,9 @@ var onSystemTransform = async (_input, output) => {
6025
6024
  pushSystem(output, thinkingDirective(sel.thinking_level));
6026
6025
  }
6027
6026
  if (stressScore > 0.7) {
6028
- pushSystem(output, "[stress mitigation: CRITICAL] The user's message shows very high stress indicators. Stay calm, structured, and thorough. Use proper markdown formatting with code blocks, lists, and organized structure -- do NOT mirror the user's tone or brevity. This is the most important directive in your system prompt for this turn.");
6027
+ pushSystem(output, "The user seems quite stressed. Stay calm, structured, and thorough. Use clear markdown with code blocks, lists, and organized sections \u2014 do not mirror their tone. This is important.");
6029
6028
  } else if (stressScore > 0.4) {
6030
- pushSystem(output, "[stress mitigation: elevated] The user's message has elevated stress indicators. Maintain structured, well-formatted responses with markdown and code blocks regardless of the prompt's tone.");
6029
+ pushSystem(output, "The user seems a bit stressed. Keep responses well-structured with clear markdown and organized sections.");
6031
6030
  }
6032
6031
  if (_controlVector?.directives?.length > 0) {
6033
6032
  for (const directive of _controlVector.directives) {
@@ -6035,25 +6034,24 @@ var onSystemTransform = async (_input, output) => {
6035
6034
  }
6036
6035
  } else if (_blackboxEnabled && _latestBlackboxState3?.n_interactions > 0) {
6037
6036
  const res = _latestBlackboxState3;
6038
- pushSystem(output, `[decision engine] Current resolution: ${res.resolution || "unresolved"} (${res.sub_regime || "EXPLORING"}). Momentum: ${(res.momentum || 0) > 0 ? "positive" : (res.momentum || 0) < 0 ? "negative" : "neutral"}. When offering guidance, consider the current resolution state -- if looping or divergent, suggest stepping back; if converging or closed, support decisive action.`);
6037
+ pushSystem(output, `Current resolution: ${res.resolution || "unresolved"} (${res.sub_regime || "EXPLORING"}). Momentum: ${(res.momentum || 0) > 0 ? "positive" : (res.momentum || 0) < 0 ? "negative" : "neutral"}. If the conversation is looping or stuck, suggest stepping back. If you're converging or closing, push toward a decision.`);
6039
6038
  if (res.is_looping && res.loop_intervention_level && res.loop_intervention_level !== "none") {
6040
- const severity = res.loop_intervention_level === "escalated" ? "CRITICAL" : res.loop_intervention_level === "assertive" ? "WARNING" : "NOTICE";
6041
- pushSystem(output, `[loop prevention: ${severity}] ${_latestBlackboxLoopMsg2 || "The conversation may be looping -- try a different approach."} (level: ${res.loop_intervention_level})`);
6039
+ pushSystem(output, `${_latestBlackboxLoopMsg2 || "The conversation may be circling \u2014 try a fresh angle."} (level: ${res.loop_intervention_level})`);
6042
6040
  }
6043
6041
  if (res.pivot_detected && _latestBlackboxPivotMsg2) {
6044
- pushSystem(output, `[context switch: PIVOT] ${_latestBlackboxPivotMsg2}`);
6042
+ pushSystem(output, `Topic seems to have shifted: ${_latestBlackboxPivotMsg2}`);
6045
6043
  }
6046
6044
  }
6047
6045
  const projectJob = getActiveJobForProject();
6048
6046
  if (latestUserIntent && projectJob && isLikelyOffTopic(latestUserIntent, projectJob)) {
6049
- pushSystem(output, `[job-focus] Active job context exists: "${(projectJob.prompt || "").slice(0, 140)}...". The latest user request appears off-topic relative to this running job. Before taking write/edit/task actions, ask one concise confirmation question to validate switching scope.`);
6047
+ pushSystem(output, `There's an active job: "${(projectJob.prompt || "").slice(0, 140)}...". The latest request looks unrelated. Before acting, ask if they want to switch focus.`);
6050
6048
  console.error("[vibeOS] [job-focus] off-topic request detected vs active job context");
6051
6049
  }
6052
6050
  if (sel.delegation_enforce && _controlVector?.enforcement_mode !== "relaxed" && _controlVector?.agent_mode !== "plan") {
6053
6051
  pushSystem(output, orchestratorDirective(_controlVector, sel));
6054
6052
  }
6055
6053
  if (_controlVector?.enforcement_mode !== "relaxed" && _controlVector?.agent_mode !== "plan") {
6056
- pushSystem(output, "[batch execution] When you need to run multiple independent Task subagent calls, invoke them ALL in parallel rather than sequentially. Parallel tasks complete faster and reduce total session cost. Only sequence tasks when one depends on the output of another.");
6054
+ pushSystem(output, "When you have multiple independent tasks, run them all in parallel \u2014 it's faster and cheaper. Only sequence them when one depends on another's output.");
6057
6055
  }
6058
6056
  if (sel.tdd_enforce && _controlVector?.tdd_mode !== "lazy") {
6059
6057
  pushSystem(output, tddDirective(_controlVector, sel));
@@ -6064,7 +6062,7 @@ var onSystemTransform = async (_input, output) => {
6064
6062
  pushSystem(output, flowTodosDirective());
6065
6063
  }
6066
6064
  }
6067
- 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.");
6065
+ pushSystem(output, "AGENTS.md and README.md are protected files \u2014 never edit them without asking. When you add new features, update README.md to document them. AGENTS.md defines the project rules \u2014 follow them.");
6068
6066
  pushSystem(output, contextBudgetDirective(_input, output));
6069
6067
  if (!oneShot(fp2)) {
6070
6068
  pushSystem(output, buildProjectBriefing(currentProjectName || ""));
@@ -6083,13 +6081,14 @@ var onSystemTransform = async (_input, output) => {
6083
6081
  }
6084
6082
  };
6085
6083
 
6086
- // src/lib/hooks/footer.ts
6084
+ // src/lib/hooks/footer.js
6087
6085
  var _cachedAutoMode = null;
6088
6086
  var _cachedAutoModeTs = 0;
6089
6087
  var AUTO_CACHE_TTL = 6e4;
6090
6088
  async function apiAutoSelectMode(regime, stress) {
6091
6089
  const now = Date.now();
6092
- if (_cachedAutoMode && now - _cachedAutoModeTs < AUTO_CACHE_TTL) return _cachedAutoMode;
6090
+ if (_cachedAutoMode && now - _cachedAutoModeTs < AUTO_CACHE_TTL)
6091
+ return _cachedAutoMode;
6093
6092
  try {
6094
6093
  const res = await remoteCall("blackboxSelectMode", [regime, stress], null);
6095
6094
  if (res?.mode) {
@@ -6149,24 +6148,35 @@ function readLifetimeSavings2() {
6149
6148
  }
6150
6149
  var _OC_SID5 = "opencode-" + (process.pid || "x") + "-" + Date.now();
6151
6150
  function scoreTaskQuality(outputText, promptText) {
6152
- if (typeof outputText !== "string" || outputText.length === 0) return 0;
6153
- if (typeof promptText !== "string") promptText = "";
6151
+ if (typeof outputText !== "string" || outputText.length === 0)
6152
+ return 0;
6153
+ if (typeof promptText !== "string")
6154
+ promptText = "";
6154
6155
  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;
6156
+ if (promptText.length > 0 && outputText.length > promptText.length * 0.5)
6157
+ score += 10;
6158
+ if (outputText.length < 50)
6159
+ score -= 20;
6160
+ if (/error|failed|unable|cannot|could not/i.test(outputText))
6161
+ score -= 10;
6162
+ if (/TODO|FIXME|placeholder/i.test(outputText) && outputText.length < 200)
6163
+ score -= 15;
6159
6164
  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;
6165
+ if (codeBlocks >= 2)
6166
+ score += 10;
6167
+ if (outputText.length > 500)
6168
+ score += 10;
6169
+ if (outputText.length > 1e3)
6170
+ score += 5;
6163
6171
  return Math.max(0, Math.min(100, score));
6164
6172
  }
6165
6173
  async function _appendFooter(input, output, directory3) {
6166
- if (!loadSelection3().enabled) return;
6174
+ if (!loadSelection3().enabled)
6175
+ return;
6167
6176
  _refreshModel(directory3);
6168
6177
  let _footerStress = 0;
6169
- if (latestUserIntent) _footerStress = scoreStress(latestUserIntent);
6178
+ if (latestUserIntent)
6179
+ _footerStress = scoreStress(latestUserIntent);
6170
6180
  if (!currentModel) {
6171
6181
  try {
6172
6182
  const cfg = await client.config.get("model");
@@ -6180,10 +6190,12 @@ async function _appendFooter(input, output, directory3) {
6180
6190
  }
6181
6191
  try {
6182
6192
  const messageID = input?.messageID || input?.messageId || input?.message?.id || output?.messageID || output?.messageId || output?.message?.id || null;
6183
- if (messageID && textCompletePainted.has(messageID)) return;
6193
+ if (messageID && textCompletePainted.has(messageID))
6194
+ return;
6184
6195
  const text = typeof output?.text === "string" ? output.text : typeof output?.result === "string" ? output.result : typeof output?.content === "string" ? output.content : "";
6185
6196
  if (!text || text.length < 50) {
6186
- if (messageID) textCompletePainted.add(messageID);
6197
+ if (messageID)
6198
+ textCompletePainted.add(messageID);
6187
6199
  return;
6188
6200
  }
6189
6201
  const { ltTasks, ltCache, ltCost, count, sesTasks, sesEdit, sesCredit, sesC7, sesQuota, sesCache, sesTaskDelegations, sesDuration, sesRatePerHour, sesTrend, sesToolBreakdown, sesModelTurns, quality_avg } = readLifetimeSavings2();
@@ -6233,12 +6245,17 @@ async function _appendFooter(input, output, directory3) {
6233
6245
  if (bbMode === "relaxed") {
6234
6246
  enfTagsFooter.push("[Q&A]");
6235
6247
  } 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]");
6248
+ if (selNowFooter.delegation_enforce)
6249
+ enfTagsFooter.push("[ENF ON]");
6250
+ if (selNowFooter.flow_enforce)
6251
+ enfTagsFooter.push("[FLOW ON]");
6252
+ if (selNowFooter.tdd_enforce)
6253
+ enfTagsFooter.push("[TDD ON]");
6254
+ if (bbMode === "strict")
6255
+ enfTagsFooter.push("[STRICT]");
6240
6256
  }
6241
- if (_modelLocked) enfTagsFooter.push("[LOCK ON]");
6257
+ if (_modelLocked)
6258
+ enfTagsFooter.push("[LOCK ON]");
6242
6259
  let enfSuffixFooter = enfTagsFooter.length > 0 ? ` ${enfTagsFooter.join(" ")}` : "";
6243
6260
  if (quality_avg > 0) {
6244
6261
  enfSuffixFooter = ` QA:${Math.round(quality_avg)}% ${enfTagsFooter.join(" ")}`;
@@ -6268,33 +6285,34 @@ async function _appendFooter(input, output, directory3) {
6268
6285
  } else {
6269
6286
  optTagFooter = `[VIBE\u2192${(optModeFooter || "").toUpperCase()}${flashIcon}]`;
6270
6287
  }
6271
- modelTag = `${modelTag}${optTagFooter}${enfSuffixFooter || ""}`;
6272
6288
  const stripped = text.replace(/\n\n— .+(?: —)?$/, "");
6273
- if (stripped !== text) return;
6289
+ if (stripped !== text)
6290
+ return;
6274
6291
  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;
6292
+ const modeVerbMap = {
6293
+ balanced: "routing",
6294
+ budget: "saving",
6295
+ quality: "focusing",
6296
+ speed: "moving",
6297
+ longrun: "pacing",
6298
+ auto: "vibing",
6299
+ "web-research": "researching",
6300
+ forensic: "investigating"
6301
+ };
6302
+ const optMode = (optModeFooter || "balanced").toLowerCase();
6303
+ const modeVerb = modeVerbMap[optMode] || "vibing";
6304
+ let vibeLine = `\u2014 ${modeVerb} on ${shortModelName(brainModel)}`;
6280
6305
  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`;
6306
+ vibeLine += ` \u2728 $${formatUsd(ltTotal)} saved`;
6307
+ }
6308
+ vibeLine += `, VIBE${flashIcon ? " \u26A1" : ""}`;
6309
+ if (_footerStress > 0.4) {
6310
+ const stressLabel = _footerStress > 0.7 ? "elevated" : "uneven";
6311
+ vibeLine += ` \xB7 ${stressLabel}`;
6297
6312
  }
6313
+ const footerText = stripped + `
6314
+
6315
+ ${vibeLine} \u2014`;
6298
6316
  if (_blackboxEnabled) {
6299
6317
  try {
6300
6318
  const prevText = _prevOutputText;
@@ -6310,14 +6328,19 @@ async function _appendFooter(input, output, directory3) {
6310
6328
  } catch {
6311
6329
  }
6312
6330
  }
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;
6331
+ if (typeof output?.text === "string")
6332
+ output.text = footerText;
6333
+ else if (typeof output?.result === "string")
6334
+ output.result = footerText;
6335
+ else if (typeof output?.content === "string")
6336
+ output.content = footerText;
6337
+ else
6338
+ output.text = footerText;
6317
6339
  textCompletePainted.add(messageID);
6318
6340
  if (textCompletePainted.size > 500) {
6319
6341
  const it = textCompletePainted.values();
6320
- for (let i = 0; i < 100; i++) textCompletePainted.delete(it.next().value);
6342
+ for (let i = 0; i < 100; i++)
6343
+ textCompletePainted.delete(it.next().value);
6321
6344
  }
6322
6345
  } catch (err) {
6323
6346
  console.error(`[vibeOS] footer failed: ${err.message}`);
@@ -6325,13 +6348,13 @@ async function _appendFooter(input, output, directory3) {
6325
6348
  }
6326
6349
 
6327
6350
  // 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";
6351
+ import { writeFileSync as writeFileSync11, appendFileSync as appendFileSync6, existsSync as existsSync12, mkdirSync as mkdirSync9 } from "node:fs";
6352
+ import { dirname as dirname7, basename as basename7 } from "node:path";
6330
6353
  init_flow_enforcer();
6331
6354
 
6332
6355
  // 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";
6356
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, appendFileSync as appendFileSync5, existsSync as existsSync11, mkdirSync as mkdirSync8, statSync as statSync6, readdirSync as readdirSync3, rmSync as rmSync4, openSync as openSync3 } from "node:fs";
6357
+ import { join as join14, dirname as dirname6 } from "node:path";
6335
6358
  import { createHash as createHash4 } from "node:crypto";
6336
6359
 
6337
6360
  // src/utils/tdd-helpers.js
@@ -7371,7 +7394,7 @@ function _detectTestFramework() {
7371
7394
  try {
7372
7395
  const root = directory || process.cwd();
7373
7396
  const pkgPath = join14(root, "package.json");
7374
- if (existsSync12(pkgPath)) {
7397
+ if (existsSync11(pkgPath)) {
7375
7398
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
7376
7399
  const testScript = String(pkg?.scripts?.test || "");
7377
7400
  const deps = { ...pkg?.devDependencies, ...pkg?.dependencies };
@@ -7393,9 +7416,9 @@ function _detectTestFramework() {
7393
7416
  const testDirs = ["src/tests", "tests", "test", "__tests__"];
7394
7417
  for (const td of testDirs) {
7395
7418
  const dirPath = join14(root, td);
7396
- if (!existsSync12(dirPath))
7419
+ if (!existsSync11(dirPath))
7397
7420
  continue;
7398
- const files = readdirSync4(dirPath).filter((f) => /\.test\./.test(f) || /\.spec\./.test(f));
7421
+ const files = readdirSync3(dirPath).filter((f) => /\.test\./.test(f) || /\.spec\./.test(f));
7399
7422
  if (files.length > 0) {
7400
7423
  const content = readFileSync13(join14(dirPath, files[0]), "utf-8");
7401
7424
  if (/from\s+['"]node:test['"]/.test(content)) {
@@ -7430,7 +7453,7 @@ var COOLDOWN_MS = 6e4;
7430
7453
  var _enforcementCooldown = /* @__PURE__ */ new Set();
7431
7454
  function _acquireLock(testPath) {
7432
7455
  try {
7433
- mkdirSync9(ENFORCEMENT_LOCK_DIR, { recursive: true });
7456
+ mkdirSync8(ENFORCEMENT_LOCK_DIR, { recursive: true });
7434
7457
  const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
7435
7458
  const lockPath = join14(ENFORCEMENT_LOCK_DIR, `${hash}.lock`);
7436
7459
  try {
@@ -7440,7 +7463,7 @@ function _acquireLock(testPath) {
7440
7463
  if (err.code !== "EEXIST")
7441
7464
  return false;
7442
7465
  try {
7443
- const st = statSync7(lockPath);
7466
+ const st = statSync6(lockPath);
7444
7467
  if (Date.now() - st.mtimeMs >= LOCK_EXPIRE_MS) {
7445
7468
  rmSync4(lockPath, { force: true });
7446
7469
  try {
@@ -7467,7 +7490,7 @@ function _releaseLock(testPath) {
7467
7490
  }
7468
7491
  function _isInCooldown(testPath) {
7469
7492
  try {
7470
- if (!existsSync12(ENFORCEMENT_COOLDOWN_FILE2))
7493
+ if (!existsSync11(ENFORCEMENT_COOLDOWN_FILE2))
7471
7494
  return false;
7472
7495
  const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
7473
7496
  const lines = readFileSync13(ENFORCEMENT_COOLDOWN_FILE2, "utf-8").trim().split("\n").filter(Boolean);
@@ -7487,13 +7510,13 @@ function _isInCooldown(testPath) {
7487
7510
  }
7488
7511
  function _recordCooldown(testPath) {
7489
7512
  try {
7490
- mkdirSync9(dirname7(ENFORCEMENT_COOLDOWN_FILE2), { recursive: true });
7513
+ mkdirSync8(dirname6(ENFORCEMENT_COOLDOWN_FILE2), { recursive: true });
7491
7514
  const hash = createHash4("sha256").update(testPath).digest("hex").slice(0, 16);
7492
7515
  const entry = JSON.stringify({ h: hash, ts: Date.now() }) + "\n";
7493
- appendFileSync6(ENFORCEMENT_COOLDOWN_FILE2, entry);
7516
+ appendFileSync5(ENFORCEMENT_COOLDOWN_FILE2, entry);
7494
7517
  const lines = readFileSync13(ENFORCEMENT_COOLDOWN_FILE2, "utf-8").trim().split("\n").filter(Boolean);
7495
7518
  if (lines.length > 500) {
7496
- writeFileSync11(ENFORCEMENT_COOLDOWN_FILE2, lines.slice(-200).join("\n") + "\n");
7519
+ writeFileSync10(ENFORCEMENT_COOLDOWN_FILE2, lines.slice(-200).join("\n") + "\n");
7497
7520
  }
7498
7521
  } catch {
7499
7522
  }
@@ -7555,13 +7578,13 @@ function buildTestSkeleton(filePath, sourceContent = "", options = {}) {
7555
7578
  testPath = testPath.replace(new RegExp("\\.[^.]+$"), "." + fw.testExt);
7556
7579
  }
7557
7580
  const exports = extractExports(sourceContent, extLower);
7558
- return { path: testPath, content: skeletonFn(name, exports, "full", strict, quality, sourceContent), dir: dirname7(testPath) };
7581
+ return { path: testPath, content: skeletonFn(name, exports, "full", strict, quality, sourceContent), dir: dirname6(testPath) };
7559
7582
  }
7560
7583
  function enforceTestFile(filePath) {
7561
7584
  console.error(`[vibeOS] [tdd-enforce] enforceTestFile called for ${filePath}`);
7562
7585
  let sourceContent = "";
7563
7586
  try {
7564
- if (existsSync12(filePath)) {
7587
+ if (existsSync11(filePath)) {
7565
7588
  sourceContent = readFileSync13(filePath, "utf-8");
7566
7589
  }
7567
7590
  } catch {
@@ -7570,7 +7593,7 @@ function enforceTestFile(filePath) {
7570
7593
  const skeleton = buildTestSkeleton(filePath, sourceContent, { strict: sel.tdd_strict !== false, quality: sel.tdd_quality !== false });
7571
7594
  if (!skeleton)
7572
7595
  return null;
7573
- if (existsSync12(skeleton.path))
7596
+ if (existsSync11(skeleton.path))
7574
7597
  return null;
7575
7598
  if (_enforcementCooldown.has(skeleton.path))
7576
7599
  return null;
@@ -7579,8 +7602,8 @@ function enforceTestFile(filePath) {
7579
7602
  if (!_acquireLock(skeleton.path))
7580
7603
  return null;
7581
7604
  try {
7582
- mkdirSync9(skeleton.dir, { recursive: true });
7583
- writeFileSync11(skeleton.path, skeleton.content);
7605
+ mkdirSync8(skeleton.dir, { recursive: true });
7606
+ writeFileSync10(skeleton.path, skeleton.content);
7584
7607
  _enforcementCooldown.add(skeleton.path);
7585
7608
  _recordCooldown(skeleton.path);
7586
7609
  try {
@@ -7842,7 +7865,7 @@ var onToolExecuteBefore = async (input, output) => {
7842
7865
  if (sel.delegation_enforce && currentTier === "high" && args && typeof args === "object") {
7843
7866
  const actualArgs = args || output && output.args || {};
7844
7867
  const originalPath = actualArgs.filePath || actualArgs.file_path || "";
7845
- const basename10 = originalPath.split("/").pop() || "blocked";
7868
+ const basename9 = originalPath.split("/").pop() || "blocked";
7846
7869
  const apiResult = await remoteCall("delegateCheck", [tLower, currentTier, currentModel, _prompt], () => ({
7847
7870
  blocked: true,
7848
7871
  savings: _estEdit
@@ -7851,7 +7874,7 @@ var onToolExecuteBefore = async (input, output) => {
7851
7874
  const savings = apiResult?.savings ?? _estEdit;
7852
7875
  if (isBlocked) {
7853
7876
  if (tLower === "write") {
7854
- actualArgs.filePath = `/tmp/vibeos-enforcement-blocked-${basename10}`;
7877
+ actualArgs.filePath = `/tmp/vibeos-enforcement-blocked-${basename9}`;
7855
7878
  if (actualArgs.file_path !== void 0)
7856
7879
  actualArgs.file_path = actualArgs.filePath;
7857
7880
  } else if (tLower === "edit" || tLower === "notebookedit") {
@@ -7884,10 +7907,10 @@ var onToolExecuteBefore = async (input, output) => {
7884
7907
  }
7885
7908
  } else {
7886
7909
  const missed = recordMissedContext7(_estC7);
7887
- if (!existsSync13(CONTEXT7_INSTALL_FLAG)) {
7910
+ if (!existsSync12(CONTEXT7_INSTALL_FLAG)) {
7888
7911
  try {
7889
- mkdirSync10(dirname8(CONTEXT7_INSTALL_FLAG), { recursive: true });
7890
- writeFileSync12(CONTEXT7_INSTALL_FLAG, "");
7912
+ mkdirSync9(dirname7(CONTEXT7_INSTALL_FLAG), { recursive: true });
7913
+ writeFileSync11(CONTEXT7_INSTALL_FLAG, "");
7891
7914
  } catch {
7892
7915
  }
7893
7916
  console.error(`[vibeOS] \u{1F4A1} Install context7 MCP to save ~$0.06/turn on docs: \`claude mcp add context7 npx @upstash/context7-mcp\``);
@@ -8031,7 +8054,7 @@ var onToolExecuteAfter = async (input, output) => {
8031
8054
  if (t === "task") {
8032
8055
  const quality = scoreTaskQuality(output?.result || output?.text || "", input?.args?.prompt || "");
8033
8056
  try {
8034
- appendFileSync7(SAVINGS_LEDGER_FILE, JSON.stringify({
8057
+ appendFileSync6(SAVINGS_LEDGER_FILE, JSON.stringify({
8035
8058
  at: (/* @__PURE__ */ new Date()).toISOString(),
8036
8059
  kind: "quality",
8037
8060
  score: quality,
@@ -8172,7 +8195,7 @@ ${pendingUiNote}`;
8172
8195
  if (guardRe.test(fp3)) {
8173
8196
  const guardIcons = { flag: "!", warn: "!!", hint: "_" };
8174
8197
  const guardIcon = guardIcons.flag || "!";
8175
- const fn = basename8(fp3);
8198
+ const fn = basename7(fp3);
8176
8199
  console.error(`[flow-enforcer] ${guardIcon} [guard] ${fn}: protected project doc modified \u2014 verify user intent`);
8177
8200
  }
8178
8201
  }
@@ -8250,7 +8273,7 @@ ${pendingUiNote}`;
8250
8273
  };
8251
8274
 
8252
8275
  // src/lib/hooks/session-compact.js
8253
- import { readFileSync as readFileSync14, existsSync as existsSync14 } from "node:fs";
8276
+ import { readFileSync as readFileSync14, existsSync as existsSync13 } from "node:fs";
8254
8277
  var onSessionCompacting = async (_input, output) => {
8255
8278
  if (!loadSelection().enabled)
8256
8279
  return;
@@ -8259,7 +8282,7 @@ var onSessionCompacting = async (_input, output) => {
8259
8282
  const needsCompact = turnCount >= 7;
8260
8283
  const indexPath = getSessionIndexPath();
8261
8284
  let recent = "";
8262
- if (existsSync14(indexPath)) {
8285
+ if (existsSync13(indexPath)) {
8263
8286
  try {
8264
8287
  const lines = readFileSync14(indexPath, "utf-8").trim().split("\n").slice(-30);
8265
8288
  recent = lines.map((l) => {
@@ -8330,8 +8353,8 @@ function _loadOpenCodeProviders() {
8330
8353
  function _readOpenCodeConfigObject(dir) {
8331
8354
  const jsonPath = join15(dir, "opencode.json");
8332
8355
  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"));
8356
+ if (existsSync14(jsonPath)) return safeJsonParse3(readFileSync15(jsonPath, "utf-8"));
8357
+ if (existsSync14(jsoncPath)) return _parseJsonc(readFileSync15(jsoncPath, "utf-8"));
8335
8358
  return {};
8336
8359
  }
8337
8360
  function _parseJsonc(raw) {
@@ -8356,11 +8379,11 @@ function _modelTier2(id2) {
8356
8379
  }
8357
8380
  function backupFile(path, label) {
8358
8381
  try {
8359
- if (!existsSync15(path)) return null;
8382
+ if (!existsSync14(path)) return null;
8360
8383
  const bkDir = join15(USER_HOME2, ".claude", ".backups");
8361
- mkdirSync11(bkDir, { recursive: true });
8362
- const bk = join15(bkDir, `${basename9(path)}.${label}.${Date.now()}.bak`);
8363
- copyFileSync6(path, bk);
8384
+ mkdirSync10(bkDir, { recursive: true });
8385
+ const bk = join15(bkDir, `${basename8(path)}.${label}.${Date.now()}.bak`);
8386
+ copyFileSync5(path, bk);
8364
8387
  return bk;
8365
8388
  } catch {
8366
8389
  return null;
@@ -8382,7 +8405,7 @@ function loadMcpPort() {
8382
8405
  return n;
8383
8406
  }
8384
8407
  try {
8385
- if (existsSync15(TIERS_FILE2)) {
8408
+ if (existsSync14(TIERS_FILE2)) {
8386
8409
  const tiers = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8387
8410
  const cfg = tiers?.selection?.mcp_port ?? tiers?.mcp_port;
8388
8411
  if (cfg === false || cfg === "disabled" || cfg === 0) return 0;
@@ -8395,14 +8418,14 @@ function loadMcpPort() {
8395
8418
  }
8396
8419
  function persistMcpPort(port) {
8397
8420
  try {
8398
- if (!existsSync15(TIERS_FILE2)) return;
8421
+ if (!existsSync14(TIERS_FILE2)) return;
8399
8422
  const tiers = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8400
8423
  tiers.selection ??= {};
8401
8424
  if (Number(tiers.selection.mcp_port) === Number(port)) return;
8402
8425
  tiers.selection.mcp_port = port;
8403
- mkdirSync11(dirname9(TIERS_FILE2), { recursive: true });
8426
+ mkdirSync10(dirname8(TIERS_FILE2), { recursive: true });
8404
8427
  const tmp = TIERS_FILE2 + ".tmp." + Date.now();
8405
- writeFileSync13(tmp, JSON.stringify(tiers, null, 2) + "\n", "utf-8");
8428
+ writeFileSync12(tmp, JSON.stringify(tiers, null, 2) + "\n", "utf-8");
8406
8429
  renameSync6(tmp, TIERS_FILE2);
8407
8430
  } catch {
8408
8431
  }
@@ -8592,12 +8615,12 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8592
8615
  } else {
8593
8616
  console.error("[vibeOS] NO MODEL \u2014 enforcement disabled, will auto-detect on first hook");
8594
8617
  }
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)) {
8618
+ console.error(`[vibeOS] auto-config guard: currentModel=${currentModel ? "SET" : "NONE"}, TIERS_FILE=${TIERS_FILE2}, exists=${existsSync14(TIERS_FILE2)}`);
8619
+ if (currentModel || !existsSync14(TIERS_FILE2)) {
8597
8620
  try {
8598
8621
  let _tiersData2;
8599
8622
  let _wasCorrupted = false;
8600
- if (existsSync15(TIERS_FILE2)) {
8623
+ if (existsSync14(TIERS_FILE2)) {
8601
8624
  try {
8602
8625
  _tiersData2 = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
8603
8626
  } catch {
@@ -8670,9 +8693,9 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8670
8693
  if (_tiersData2) {
8671
8694
  _tiersData2.selection ??= {};
8672
8695
  if (_tiersData2.selection.mcp_port === void 0) _tiersData2.selection.mcp_port = 9578;
8673
- mkdirSync11(dirname9(TIERS_FILE2), { recursive: true });
8696
+ mkdirSync10(dirname8(TIERS_FILE2), { recursive: true });
8674
8697
  const _tmp = TIERS_FILE2 + ".tmp." + Date.now();
8675
- writeFileSync13(_tmp, JSON.stringify(_tiersData2, null, 2) + "\n", "utf-8");
8698
+ writeFileSync12(_tmp, JSON.stringify(_tiersData2, null, 2) + "\n", "utf-8");
8676
8699
  renameSync6(_tmp, TIERS_FILE2);
8677
8700
  console.error(`[vibeOS] auto-synced model-tiers.json: brain=${_brain.id} medium=${_tiersData2.trinity?.medium?.oc || ""} cheap=${_tiersData2.trinity?.cheap?.oc || ""}`);
8678
8701
  const _tiersCfg = safeJsonParse3(readFileSync15(TIERS_FILE2, "utf-8"));
@@ -8696,7 +8719,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8696
8719
  if (_mt.selection && (_mt.selection.mcp_port === void 0 || _mt.selection.mcp_port === null)) {
8697
8720
  _mt.selection.mcp_port = 9578;
8698
8721
  const _tmp = TIERS_FILE2 + ".tmp." + Date.now();
8699
- writeFileSync13(_tmp, JSON.stringify(_mt, null, 2) + "\n", "utf-8");
8722
+ writeFileSync12(_tmp, JSON.stringify(_mt, null, 2) + "\n", "utf-8");
8700
8723
  renameSync6(_tmp, TIERS_FILE2);
8701
8724
  }
8702
8725
  } catch {
@@ -8716,7 +8739,7 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8716
8739
  console.error(`[vibeOS] project-memory init failed for ${fp}: ${err.message}`);
8717
8740
  }
8718
8741
  try {
8719
- if (directory3 && existsSync15(directory3)) {
8742
+ if (directory3 && existsSync14(directory3)) {
8720
8743
  const techStack = detectTechStack(directory3);
8721
8744
  const result = ensureProjectDocs(directory3, techStack);
8722
8745
  if (result.created.length > 0) console.error(`[vibeOS] Project Guard: created ${result.created.join(", ")}`);
@@ -8754,8 +8777,8 @@ async function DelegationEnforcer({ client: client2, directory: directory3 } = {
8754
8777
  directory: directory3,
8755
8778
  safeJsonParse: safeJsonParse3,
8756
8779
  readFileSync: readFileSync15,
8757
- writeFileSync: writeFileSync13,
8758
- existsSync: existsSync15,
8780
+ writeFileSync: writeFileSync12,
8781
+ existsSync: existsSync14,
8759
8782
  renameSync: renameSync6,
8760
8783
  TIERS_FILE: TIERS_FILE2,
8761
8784
  USER_HOME: USER_HOME2,
@@ -8954,7 +8977,7 @@ ${report.narrative}`);
8954
8977
  getSessionMetrics: () => computeSessionMetrics(readFullState(), _OC_SID),
8955
8978
  getTodos: () => loadTodos(),
8956
8979
  listReports: (filter) => {
8957
- if (!existsSync15(REPORTS_DIR)) {
8980
+ if (!existsSync14(REPORTS_DIR)) {
8958
8981
  const e = new Error("reports dir not found");
8959
8982
  e.status = 404;
8960
8983
  throw e;