vibeostheog 0.24.14 → 0.24.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +21 -3
  2. package/dist/vibeOS.js +270 -137
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,7 +1,26 @@
1
+ ## 0.24.15
2
+ - feat: smooth delegation UX — conversational reasoning over error injection
3
+ - fix: preserve local agent mode in remote control merge
4
+ - fix: harden live regression paths (#140)
5
+ - fix: write npm auth for github release publish
6
+ - fix: reset api fallback on token refresh (#138)
7
+ - fix: _prevBlackboxState -> _latestBlackboxState, update pattern key test
8
+ - fix: sync-ts-build was cleaning JS artifacts without copying compiled output back
9
+ - fix: forensic anti-lying + quality enforcement pipeline
10
+ - docs: add cross-project index, DEV ONLY markers, ESLint cleanup
11
+ - test: add 13 cascade integration tests for forensic quality pipeline
12
+ - chore: v0.24.14
13
+ - chore: sync package version to v0.24.12
14
+ - chore: sync package version to latest github release
15
+ - chore: sync package version to latest release
16
+ Fix live regressions and add integration coverage (#139)
17
+ Merge pull request #136 from DrunkkToys/release/v0.24.8-merge
18
+
19
+
1
20
  ## 0.24.14
2
21
  - feat: smooth delegation UX — conversational reasoning over error injection
3
- - fix: preserve audit and forensic modes
4
- - fix: harden live regression paths
22
+ - fix: preserve local agent mode in remote control merge
23
+ - fix: harden live regression paths (#140)
5
24
  - fix: write npm auth for github release publish
6
25
  - fix: reset api fallback on token refresh (#138)
7
26
  - fix: _prevBlackboxState -> _latestBlackboxState, update pattern key test
@@ -9,7 +28,6 @@
9
28
  - fix: forensic anti-lying + quality enforcement pipeline
10
29
  - docs: add cross-project index, DEV ONLY markers, ESLint cleanup
11
30
  - test: add 13 cascade integration tests for forensic quality pipeline
12
- - chore: sync package version to v0.24.13
13
31
  - chore: sync package version to v0.24.12
14
32
  - chore: sync package version to latest github release
15
33
  - chore: sync package version to latest release
package/dist/vibeOS.js CHANGED
@@ -2534,42 +2534,56 @@ function writeSessionOptMode(sid, mode) {
2534
2534
  }
2535
2535
  }
2536
2536
 
2537
- // src/lib/pattern-helpers.ts
2537
+ // src/lib/pattern-helpers.js
2538
2538
  import { relative, basename as basename2 } from "node:path";
2539
2539
  function normalizeObservedPath(filePath, directory3) {
2540
- if (!filePath || typeof filePath !== "string") return "unknown";
2540
+ if (!filePath || typeof filePath !== "string")
2541
+ return "unknown";
2541
2542
  let p = filePath;
2542
2543
  try {
2543
2544
  if (directory3 && p.startsWith("/")) {
2544
2545
  const rel = relative(directory3, p);
2545
- if (rel && !rel.startsWith("..") && !rel.startsWith("/")) p = rel;
2546
+ if (rel && !rel.startsWith("..") && !rel.startsWith("/"))
2547
+ p = rel;
2546
2548
  }
2547
2549
  } catch {
2548
2550
  }
2549
2551
  p = p.replace(/\\/g, "/").replace(/^\.\/+/, "");
2550
- if (/^(src\/index\.js|package\.json|README\.md|CHANGELOG\.md|tsconfig\.json)$/i.test(p)) return p;
2552
+ if (/^(src\/index\.js|package\.json|README\.md|CHANGELOG\.md|tsconfig\.json)$/i.test(p))
2553
+ return p;
2551
2554
  const m = p.match(/\.([a-z0-9]+)$/i);
2552
- if (p.startsWith("src/") && m) return `src/*.${m[1].toLowerCase()}`;
2553
- if (p.startsWith("tests/") && m) return `tests/*.${m[1].toLowerCase()}`;
2555
+ if (p.startsWith("src/") && m)
2556
+ return `src/*.${m[1].toLowerCase()}`;
2557
+ if (p.startsWith("tests/") && m)
2558
+ return `tests/*.${m[1].toLowerCase()}`;
2554
2559
  return basename2(p) || "unknown";
2555
2560
  }
2556
2561
  function commandFamily(command) {
2557
2562
  const c = String(command || "").trim().toLowerCase();
2558
- if (!c) return "unknown";
2559
- if (/\bnode\s+--check\b/.test(c)) return "syntax-check";
2560
- if (/\bnpm\s+run\s+typecheck\b|\btsc\b.*--noemit/.test(c)) return "typecheck";
2561
- if (/\bnpm\s+test\b|\bnode\s+--test\b|\bvitest\b|\bjest\b|\bpytest\b/.test(c)) return "test";
2562
- if (/\bnpm\s+run\s+build\b|\btsc\s+-p\b/.test(c)) return "build";
2563
- if (/\bgit\s+status\b/.test(c)) return "git-status";
2564
- if (/\bgit\s+commit\b/.test(c)) return "git-commit";
2563
+ if (!c)
2564
+ return "unknown";
2565
+ if (/\bnode\s+--check\b/.test(c))
2566
+ return "syntax-check";
2567
+ if (/\bnpm\s+run\s+typecheck\b|\btsc\b.*--noemit/.test(c))
2568
+ return "typecheck";
2569
+ if (/\bnpm\s+test\b|\bnode\s+--test\b|\bvitest\b|\bjest\b|\bpytest\b/.test(c))
2570
+ return "test";
2571
+ if (/\bnpm\s+run\s+build\b|\btsc\s+-p\b/.test(c))
2572
+ return "build";
2573
+ if (/\bgit\s+status\b/.test(c))
2574
+ return "git-status";
2575
+ if (/\bgit\s+commit\b/.test(c))
2576
+ return "git-commit";
2565
2577
  const first = c.replace(/^[a-z_][a-z0-9_]*=\S+\s+/g, "").split(/\s+/)[0];
2566
2578
  return /^[a-z0-9._/-]{1,30}$/.test(first) ? first : "command";
2567
2579
  }
2568
2580
  function commandFailed(output) {
2569
2581
  const code = output?.exitCode ?? output?.statusCode ?? output?.code;
2570
- if (Number.isFinite(Number(code)) && Number(code) !== 0) return true;
2582
+ if (Number.isFinite(Number(code)) && Number(code) !== 0)
2583
+ return true;
2571
2584
  const raw = output?.result ?? output?.text ?? output?.content ?? output?.data ?? "";
2572
- if (typeof raw !== "string") return false;
2585
+ if (typeof raw !== "string")
2586
+ return false;
2573
2587
  return /\b(exit code|exited with code)\s*[:=]?\s*[1-9]\b|\b(assertionerror|syntaxerror|typeerror|referenceerror)\b|\b(failed|error:|err!)\b/i.test(raw);
2574
2588
  }
2575
2589
  function mergeProjectBucket(dst, src) {
@@ -2586,7 +2600,8 @@ function mergeProjectBucket(dst, src) {
2586
2600
  row.sessions = [.../* @__PURE__ */ new Set([...row.sessions || [], ...v?.sessions || []])].slice(-10);
2587
2601
  row.lastSeen = [row.lastSeen, v?.lastSeen].filter(Boolean).sort().slice(-1)[0] || null;
2588
2602
  row.summary = row.summary || v?.summary || "";
2589
- if (v?.kind) row.kind = v.kind;
2603
+ if (v?.kind)
2604
+ row.kind = v.kind;
2590
2605
  out[key] = row;
2591
2606
  }
2592
2607
  }
@@ -2605,9 +2620,11 @@ function mergeProjectBucket(dst, src) {
2605
2620
  };
2606
2621
  }
2607
2622
  function _pruneOldSessions(state) {
2608
- if (!state?.sessions) return;
2623
+ if (!state?.sessions)
2624
+ return;
2609
2625
  const entries = Object.entries(state.sessions);
2610
- if (entries.length <= 30) return;
2626
+ if (entries.length <= 30)
2627
+ return;
2611
2628
  entries.sort((a, b) => {
2612
2629
  const da = a[1]?.started || a[1]?.last_costed || "";
2613
2630
  const db = b[1]?.started || b[1]?.last_costed || "";
@@ -9684,7 +9701,7 @@ function recordBudgetFirstOutcome(input = {}) {
9684
9701
  import { join as join13 } from "node:path";
9685
9702
  import { writeFileSync as writeFileSync11 } from "node:fs";
9686
9703
 
9687
- // src/lib/text-compress.ts
9704
+ // src/lib/text-compress.js
9688
9705
  var VERBOSE_LINE_RE = [
9689
9706
  /^[\s#*/\\\-_=+|~:;'"`@\$%^&<>{}\[\]()!?.,0-9]+$/,
9690
9707
  /^(Filed|Created|Modified|Deleted|Updated|Renamed|Copied|Moved|Changed):/,
@@ -9701,12 +9718,15 @@ function extractBulletLines(lines, targetChars, minLines) {
9701
9718
  const keyLines = [];
9702
9719
  const otherLines = [];
9703
9720
  for (const line of lines) {
9704
- if (BULLET_PATTERNS.some((re) => re.test(line))) keyLines.push(line);
9705
- else otherLines.push(line);
9721
+ if (BULLET_PATTERNS.some((re) => re.test(line)))
9722
+ keyLines.push(line);
9723
+ else
9724
+ otherLines.push(line);
9706
9725
  }
9707
9726
  const selected = [...keyLines];
9708
9727
  for (const line of otherLines) {
9709
- if (selected.length >= minLines && selected.join("\n").length >= targetChars) break;
9728
+ if (selected.length >= minLines && selected.join("\n").length >= targetChars)
9729
+ break;
9710
9730
  selected.push(line);
9711
9731
  }
9712
9732
  while (selected.length > minLines && selected.join("\n").length > targetChars * 2) {
@@ -9715,7 +9735,8 @@ function extractBulletLines(lines, targetChars, minLines) {
9715
9735
  return selected;
9716
9736
  }
9717
9737
  function compressText(text) {
9718
- if (!text || typeof text !== "string") return text;
9738
+ if (!text || typeof text !== "string")
9739
+ return text;
9719
9740
  let lines = text.split("\n");
9720
9741
  let removed = 0;
9721
9742
  const out = [];
@@ -9728,14 +9749,16 @@ function compressText(text) {
9728
9749
  break;
9729
9750
  }
9730
9751
  }
9731
- if (!skip) out.push(line);
9752
+ if (!skip)
9753
+ out.push(line);
9732
9754
  }
9733
9755
  const collapsed = [];
9734
9756
  let blanks = 0;
9735
9757
  for (const line of out) {
9736
9758
  if (line.trim() === "") {
9737
9759
  blanks++;
9738
- if (blanks <= 2) collapsed.push(line);
9760
+ if (blanks <= 2)
9761
+ collapsed.push(line);
9739
9762
  } else {
9740
9763
  blanks = 0;
9741
9764
  collapsed.push(line);
@@ -9743,10 +9766,7 @@ function compressText(text) {
9743
9766
  }
9744
9767
  let result = collapsed.join("\n").trim();
9745
9768
  if (result.length > COMPRESS_THRESHOLD) {
9746
- const targetChars = Math.max(
9747
- Math.round(result.length * COMPRESS_RATIO),
9748
- COMPRESS_THRESHOLD
9749
- );
9769
+ const targetChars = Math.max(Math.round(result.length * COMPRESS_RATIO), COMPRESS_THRESHOLD);
9750
9770
  const minLines = Math.max(1, Math.round(collapsed.length * MIN_KEPT_LINES_RATIO));
9751
9771
  const bulletLines = extractBulletLines(collapsed, targetChars, minLines);
9752
9772
  result = bulletLines.join("\n").trim();
@@ -10061,7 +10081,7 @@ function recordSaving(tool2, reason, saveEst, meta = {}) {
10061
10081
  }
10062
10082
  }
10063
10083
 
10064
- // src/lib/constants.ts
10084
+ // src/lib/constants.js
10065
10085
  var SAVE_EST = {
10066
10086
  // Realistic: v4-pro (0.00057) - v4-flash (0.000182) = 0.000388/turn
10067
10087
  WRITE_EDIT: 4e-4,
@@ -10079,7 +10099,7 @@ var COMPRESS_MARKER = "[ctx-compressed-v1]";
10079
10099
  var PROTOCOL_MARKER = "[wbp-v1]";
10080
10100
  var PROTOCOL_TEXT = PROTOCOL_MARKER + " [Worker-to-Brain Report Protocol] When synthesizing the preceding Task output: 1) EXTRACT core findings/data. 2) REFORMAT into bullet points. 3) VERIFY against the original ask. 4) SYNTHESIZE into final response.";
10081
10101
 
10082
- // src/lib/templates.ts
10102
+ // src/lib/templates.js
10083
10103
  var TEMPLATES = {
10084
10104
  save: {
10085
10105
  tier_bias: "cheap",
@@ -10129,7 +10149,8 @@ var TEMPLATES = {
10129
10149
  var DEFAULT_TEMPLATE = "save";
10130
10150
  var SEC_KEYWORDS = /\b(security|vuln|exploit|injection|xss|csrf|secret|credential|token leak|auth bypass|privacy|breach|backdoor|sql injection|cve)\b/i;
10131
10151
  function detectSecuritySignal(text) {
10132
- if (!text || typeof text !== "string") return false;
10152
+ if (!text || typeof text !== "string")
10153
+ return false;
10133
10154
  return SEC_KEYWORDS.test(text);
10134
10155
  }
10135
10156
  function detectBudgetSignal(creditPercent) {
@@ -10142,20 +10163,25 @@ function detectStressSpike(stressScore) {
10142
10163
  return delta > 0.3 && stressScore > 0.5;
10143
10164
  }
10144
10165
  function resolveTemplate(prevTemplate, stressScore, userText, creditPercent, subRegime) {
10145
- if (detectSecuritySignal(userText)) return "security";
10166
+ if (detectSecuritySignal(userText))
10167
+ return "security";
10146
10168
  if (detectBudgetSignal(creditPercent)) {
10147
10169
  const regime = String(subRegime || "").toUpperCase();
10148
- if (regime === "LOOPING" || regime === "DIVERGENT") return "speed";
10170
+ if (regime === "LOOPING" || regime === "DIVERGENT")
10171
+ return "speed";
10149
10172
  return "save";
10150
10173
  }
10151
- if (detectStressSpike(stressScore)) return "quality";
10174
+ if (detectStressSpike(stressScore))
10175
+ return "quality";
10152
10176
  return prevTemplate || DEFAULT_TEMPLATE;
10153
10177
  }
10154
10178
  var _turnCount = 0;
10155
10179
  function shouldInjectTemplate(template, prevTemplate) {
10156
10180
  _turnCount++;
10157
- if (template !== prevTemplate) return true;
10158
- if (_turnCount % 10 === 0) return true;
10181
+ if (template !== prevTemplate)
10182
+ return true;
10183
+ if (_turnCount % 10 === 0)
10184
+ return true;
10159
10185
  return false;
10160
10186
  }
10161
10187
 
@@ -10164,6 +10190,18 @@ var BYTES_PER_TOKEN = 4;
10164
10190
  function getVibeOSHome9() {
10165
10191
  return process.env.VIBEOS_HOME || join14(process.env.HOME || "", ".claude");
10166
10192
  }
10193
+ function mergeRemoteControlVector(remoteControlVector, localControlVector) {
10194
+ return {
10195
+ ...remoteControlVector,
10196
+ agent_mode: localControlVector?.agent_mode,
10197
+ tier_bias: localControlVector?.tier_bias,
10198
+ optimization_mode: localControlVector?.optimization_mode,
10199
+ enforcement_mode: localControlVector?.enforcement_mode,
10200
+ flow_mode: localControlVector?.flow_mode,
10201
+ tdd_mode: localControlVector?.tdd_mode,
10202
+ thinking_mode: localControlVector?.thinking_mode
10203
+ };
10204
+ }
10167
10205
  function resolveRestorableOpenCodeAgent(currentSel) {
10168
10206
  const remembered = typeof currentSel?.previous_default_agent === "string" ? currentSel.previous_default_agent.trim() : "";
10169
10207
  if (remembered && remembered !== "plan")
@@ -10216,7 +10254,7 @@ async function apiComputeControlVector(state, action, optimizationMode) {
10216
10254
  const res = await remoteCall("blackboxControlVector", [state, action, optimizationMode], null);
10217
10255
  if (res?.control_vector) {
10218
10256
  const local = computeControlVector2(state, action, optimizationMode);
10219
- return { ...res.control_vector, tier_bias: local.tier_bias, optimization_mode: local.optimization_mode, enforcement_mode: local.enforcement_mode, flow_mode: local.flow_mode, tdd_mode: local.tdd_mode, thinking_mode: local.thinking_mode };
10257
+ return mergeRemoteControlVector(res.control_vector, local);
10220
10258
  }
10221
10259
  } catch {
10222
10260
  }
@@ -11351,7 +11389,7 @@ import { writeFileSync as writeFileSync14, appendFileSync as appendFileSync8, ex
11351
11389
  import { join as join17, dirname as dirname12, basename as basename7 } from "node:path";
11352
11390
  import { createHash as createHash5 } from "node:crypto";
11353
11391
 
11354
- // src/lib/cost-anomaly.ts
11392
+ // src/lib/cost-anomaly.js
11355
11393
  var COST_WINDOW_SIZE = 20;
11356
11394
  var COST_ANOMALY_THRESHOLD = 3;
11357
11395
  var COST_WARMUP_SAMPLES = 5;
@@ -11362,21 +11400,26 @@ var CostAnomalyDetector = class {
11362
11400
  currentAnomalyCost = 0;
11363
11401
  currentAnomalyMean = 0;
11364
11402
  record(cost) {
11365
- if (this.disabled) return;
11403
+ if (this.disabled)
11404
+ return;
11366
11405
  this.costHistory.push(cost);
11367
11406
  if (this.costHistory.length > COST_WINDOW_SIZE) {
11368
11407
  this.costHistory.shift();
11369
11408
  }
11370
11409
  }
11371
11410
  get mean() {
11372
- if (this.costHistory.length === 0) return 0;
11411
+ if (this.costHistory.length === 0)
11412
+ return 0;
11373
11413
  return this.costHistory.reduce((a, b) => a + b, 0) / this.costHistory.length;
11374
11414
  }
11375
11415
  checkAnomaly(model, cost) {
11376
- if (this.disabled) return false;
11377
- if (this.costHistory.length < COST_WARMUP_SAMPLES) return false;
11416
+ if (this.disabled)
11417
+ return false;
11418
+ if (this.costHistory.length < COST_WARMUP_SAMPLES)
11419
+ return false;
11378
11420
  const avg = this.mean;
11379
- if (avg <= 0 || cost <= avg) return false;
11421
+ if (avg <= 0 || cost <= avg)
11422
+ return false;
11380
11423
  const ratio = cost / avg;
11381
11424
  if (ratio > COST_ANOMALY_THRESHOLD) {
11382
11425
  this.currentAnomalyModel = model;
@@ -11398,7 +11441,8 @@ var CostAnomalyDetector = class {
11398
11441
  };
11399
11442
  var _costDetector = null;
11400
11443
  function getCostAnomalyDetector() {
11401
- if (!_costDetector) _costDetector = new CostAnomalyDetector();
11444
+ if (!_costDetector)
11445
+ _costDetector = new CostAnomalyDetector();
11402
11446
  return _costDetector;
11403
11447
  }
11404
11448
 
@@ -11410,9 +11454,10 @@ import { readFileSync as readFileSync15, writeFileSync as writeFileSync13, appen
11410
11454
  import { join as join16, dirname as dirname11 } from "node:path";
11411
11455
  import { createHash as createHash4 } from "node:crypto";
11412
11456
 
11413
- // src/utils/tdd-helpers.ts
11457
+ // src/utils/tdd-helpers.js
11414
11458
  function extractExports(sourceContent, ext) {
11415
- if (!sourceContent || typeof sourceContent !== "string") return [];
11459
+ if (!sourceContent || typeof sourceContent !== "string")
11460
+ return [];
11416
11461
  const exports = [];
11417
11462
  const seen = /* @__PURE__ */ new Set();
11418
11463
  const add = (name, type = "function") => {
@@ -11423,51 +11468,69 @@ function extractExports(sourceContent, ext) {
11423
11468
  };
11424
11469
  switch (ext) {
11425
11470
  case "py": {
11426
- for (const m of sourceContent.matchAll(/^def\s+([a-zA-Z]\w*)\s*\(/gm)) add(m[1]);
11427
- for (const m of sourceContent.matchAll(/^class\s+([a-zA-Z_]\w*)\s*[\(:]/gm)) add(m[1], "class");
11471
+ for (const m of sourceContent.matchAll(/^def\s+([a-zA-Z]\w*)\s*\(/gm))
11472
+ add(m[1]);
11473
+ for (const m of sourceContent.matchAll(/^class\s+([a-zA-Z_]\w*)\s*[\(:]/gm))
11474
+ add(m[1], "class");
11428
11475
  break;
11429
11476
  }
11430
11477
  case "js":
11431
11478
  case "mjs":
11432
11479
  case "jsx": {
11433
- for (const m of sourceContent.matchAll(/export\s+(?:async\s+)?function\s+([a-zA-Z_$]\w*)\s*\(/g)) add(m[1]);
11434
- for (const m of sourceContent.matchAll(/export\s+const\s+([a-zA-Z_$]\w*)\s*=/g)) add(m[1]);
11480
+ for (const m of sourceContent.matchAll(/export\s+(?:async\s+)?function\s+([a-zA-Z_$]\w*)\s*\(/g))
11481
+ add(m[1]);
11482
+ for (const m of sourceContent.matchAll(/export\s+const\s+([a-zA-Z_$]\w*)\s*=/g))
11483
+ add(m[1]);
11435
11484
  if (exports.length === 0) {
11436
- for (const m of sourceContent.matchAll(/^(?:async\s+)?function\s+([a-zA-Z_$]\w*)\s*\(/gm)) add(m[1]);
11485
+ for (const m of sourceContent.matchAll(/^(?:async\s+)?function\s+([a-zA-Z_$]\w*)\s*\(/gm))
11486
+ add(m[1]);
11437
11487
  }
11438
11488
  break;
11439
11489
  }
11440
11490
  case "ts":
11441
11491
  case "tsx": {
11442
- for (const m of sourceContent.matchAll(/export\s+(?:async\s+)?function\s+([a-zA-Z_$]\w*)\s*\(/g)) add(m[1]);
11443
- for (const m of sourceContent.matchAll(/export\s+const\s+([a-zA-Z_$]\w*)\s*[:=]/g)) add(m[1]);
11444
- for (const m of sourceContent.matchAll(/export\s+class\s+([a-zA-Z_$]\w*)/g)) add(m[1], "class");
11492
+ for (const m of sourceContent.matchAll(/export\s+(?:async\s+)?function\s+([a-zA-Z_$]\w*)\s*\(/g))
11493
+ add(m[1]);
11494
+ for (const m of sourceContent.matchAll(/export\s+const\s+([a-zA-Z_$]\w*)\s*[:=]/g))
11495
+ add(m[1]);
11496
+ for (const m of sourceContent.matchAll(/export\s+class\s+([a-zA-Z_$]\w*)/g))
11497
+ add(m[1], "class");
11445
11498
  break;
11446
11499
  }
11447
11500
  case "go": {
11448
- for (const m of sourceContent.matchAll(/func\s+(?:\([^)]+\)\s+)?([A-Z]\w*)\s*\(/g)) add(m[1]);
11501
+ for (const m of sourceContent.matchAll(/func\s+(?:\([^)]+\)\s+)?([A-Z]\w*)\s*\(/g))
11502
+ add(m[1]);
11449
11503
  break;
11450
11504
  }
11451
11505
  case "rs": {
11452
- for (const m of sourceContent.matchAll(/pub\s+fn\s+([a-zA-Z_]\w*)\s*</g)) add(m[1]);
11453
- for (const m of sourceContent.matchAll(/pub\s+fn\s+([a-zA-Z_]\w*)\s*\(/g)) add(m[1]);
11454
- for (const m of sourceContent.matchAll(/pub\s+struct\s+([a-zA-Z_]\w*)/g)) add(m[1], "struct");
11506
+ for (const m of sourceContent.matchAll(/pub\s+fn\s+([a-zA-Z_]\w*)\s*</g))
11507
+ add(m[1]);
11508
+ for (const m of sourceContent.matchAll(/pub\s+fn\s+([a-zA-Z_]\w*)\s*\(/g))
11509
+ add(m[1]);
11510
+ for (const m of sourceContent.matchAll(/pub\s+struct\s+([a-zA-Z_]\w*)/g))
11511
+ add(m[1], "struct");
11455
11512
  break;
11456
11513
  }
11457
11514
  case "rb": {
11458
- for (const m of sourceContent.matchAll(/def\s+(?:self\.)?([a-zA-Z_]\w*[?!=]?)/g)) add(m[1]);
11459
- for (const m of sourceContent.matchAll(/class\s+([A-Z]\w*)/g)) add(m[1], "class");
11515
+ for (const m of sourceContent.matchAll(/def\s+(?:self\.)?([a-zA-Z_]\w*[?!=]?)/g))
11516
+ add(m[1]);
11517
+ for (const m of sourceContent.matchAll(/class\s+([A-Z]\w*)/g))
11518
+ add(m[1], "class");
11460
11519
  break;
11461
11520
  }
11462
11521
  case "java":
11463
11522
  case "kt": {
11464
- for (const m of sourceContent.matchAll(/(?:public|protected)\s+(?:static\s+)?(?:final\s+)?\S+\s+([a-zA-Z_$]\w*)\s*\(/g)) add(m[1]);
11465
- for (const m of sourceContent.matchAll(/fun\s+([a-zA-Z_$]\w*)\s*\(/g)) add(m[1]);
11523
+ for (const m of sourceContent.matchAll(/(?:public|protected)\s+(?:static\s+)?(?:final\s+)?\S+\s+([a-zA-Z_$]\w*)\s*\(/g))
11524
+ add(m[1]);
11525
+ for (const m of sourceContent.matchAll(/fun\s+([a-zA-Z_$]\w*)\s*\(/g))
11526
+ add(m[1]);
11466
11527
  break;
11467
11528
  }
11468
11529
  case "sh": {
11469
- for (const m of sourceContent.matchAll(/^(?:function\s+)?([a-zA-Z_]\w*)\s*\(\)\s*\{/gm)) add(m[1]);
11470
- for (const m of sourceContent.matchAll(/^function\s+([a-zA-Z_]\w*)/gm)) add(m[1]);
11530
+ for (const m of sourceContent.matchAll(/^(?:function\s+)?([a-zA-Z_]\w*)\s*\(\)\s*\{/gm))
11531
+ add(m[1]);
11532
+ for (const m of sourceContent.matchAll(/^function\s+([a-zA-Z_]\w*)/gm))
11533
+ add(m[1]);
11471
11534
  break;
11472
11535
  }
11473
11536
  }
@@ -11489,7 +11552,8 @@ function generateTestCaseNames(funcName, _type, quality = false) {
11489
11552
  ];
11490
11553
  }
11491
11554
  function inferFunctionParams(sourceContent, funcName) {
11492
- if (!sourceContent || !funcName) return [];
11555
+ if (!sourceContent || !funcName)
11556
+ return [];
11493
11557
  const patterns = [
11494
11558
  new RegExp(`(?:export\\s+)?(?:async\\s+)?function\\s+${funcName}\\s*\\(([^)]*)\\)`, "m"),
11495
11559
  new RegExp(`(?:export\\s+)?const\\s+${funcName}\\s*[:=]\\s*(?:async\\s+)?\\(([^)]*)\\)`, "m"),
@@ -11502,7 +11566,8 @@ function inferFunctionParams(sourceContent, funcName) {
11502
11566
  if (m) {
11503
11567
  return m[1].split(",").map((s) => {
11504
11568
  const trimmed = s.trim();
11505
- if (!trimmed) return null;
11569
+ if (!trimmed)
11570
+ return null;
11506
11571
  const nameMatch = trimmed.match(/^\s*((?:public|protected)|static|final|val|var|let|const)?\s*(?:readonly\s+)?(?:[_$a-zA-Z][_$a-zA-Z0-9]*)\s*(?::|(?=\s*=)|(?=\s*[,)]))/);
11507
11572
  const rawName = trimmed.replace(/^[^a-zA-Z_$]*/, "").replace(/[=:].*$/, "").replace(/\s+.*$/, "").trim();
11508
11573
  const defaultMatch = trimmed.match(/=\s*(.+)$/);
@@ -11518,22 +11583,35 @@ function inferFunctionParams(sourceContent, funcName) {
11518
11583
  return [];
11519
11584
  }
11520
11585
  function inferTypeFromName(paramName, defaultValue) {
11521
- if (!paramName) return "any";
11586
+ if (!paramName)
11587
+ return "any";
11522
11588
  const name = paramName.toLowerCase();
11523
11589
  if (defaultValue !== null && defaultValue !== void 0) {
11524
- if (/^["']/.test(defaultValue)) return "string";
11525
- if (/^\d+\.?\d*$/.test(defaultValue)) return "number";
11526
- if (/^(true|false)$/i.test(defaultValue)) return "boolean";
11527
- if (/^\[/.test(defaultValue)) return "array";
11528
- if (/^\{/.test(defaultValue)) return "object";
11529
- if (/^null$/i.test(defaultValue)) return "null";
11530
- }
11531
- if (/^(is|has|can|should|will|did|was|are|contains?_|[A-Z])/.test(name)) return "boolean";
11532
- if (/^(count|index|limit|offset|max|min|size|length|total|num|age)_?/.test(name)) return "number";
11533
- if (/^(name|title|label|msg|message|text|str|prefix|suffix|path|url|email|id)_?/.test(name)) return "string";
11534
- if (/^(items|list|arr|entries|data|values|args)_?/.test(name)) return "array";
11535
- if (/^(obj|config|opts|options|settings|params|props)_?/.test(name)) return "object";
11536
- if (/^(fn|cb|callback|handler|on[A-Z])/.test(name)) return "function";
11590
+ if (/^["']/.test(defaultValue))
11591
+ return "string";
11592
+ if (/^\d+\.?\d*$/.test(defaultValue))
11593
+ return "number";
11594
+ if (/^(true|false)$/i.test(defaultValue))
11595
+ return "boolean";
11596
+ if (/^\[/.test(defaultValue))
11597
+ return "array";
11598
+ if (/^\{/.test(defaultValue))
11599
+ return "object";
11600
+ if (/^null$/i.test(defaultValue))
11601
+ return "null";
11602
+ }
11603
+ if (/^(is|has|can|should|will|did|was|are|contains?_|[A-Z])/.test(name))
11604
+ return "boolean";
11605
+ if (/^(count|index|limit|offset|max|min|size|length|total|num|age)_?/.test(name))
11606
+ return "number";
11607
+ if (/^(name|title|label|msg|message|text|str|prefix|suffix|path|url|email|id)_?/.test(name))
11608
+ return "string";
11609
+ if (/^(items|list|arr|entries|data|values|args)_?/.test(name))
11610
+ return "array";
11611
+ if (/^(obj|config|opts|options|settings|params|props)_?/.test(name))
11612
+ return "object";
11613
+ if (/^(fn|cb|callback|handler|on[A-Z])/.test(name))
11614
+ return "function";
11537
11615
  return "any";
11538
11616
  }
11539
11617
  function _langComment(lang) {
@@ -11546,14 +11624,22 @@ function buildQualityAssertionsForFunc(funcName, params, lang, indent) {
11546
11624
  let block = "";
11547
11625
  const testValues = params.map((p) => {
11548
11626
  const t = p.type || inferTypeFromName(p.name, p.defaultValue);
11549
- if (t === "string" || t === "String") return '"sample_input"';
11550
- if (t === "number" || t === "int" || t === "float" || t === "Number") return "42";
11551
- if (t === "boolean" || t === "bool" || t === "Boolean") return "true";
11552
- if (t === "array" || t === "Array" || t === "list" || t === "List") return "[]";
11553
- if (t === "object" || t === "Object" || t === "dict" || t === "Dict") return "{}";
11554
- if (t === "function" || t === "Function") return "() => {}";
11555
- if (t === "any") return '"test"';
11556
- if (t === "null") return "null";
11627
+ if (t === "string" || t === "String")
11628
+ return '"sample_input"';
11629
+ if (t === "number" || t === "int" || t === "float" || t === "Number")
11630
+ return "42";
11631
+ if (t === "boolean" || t === "bool" || t === "Boolean")
11632
+ return "true";
11633
+ if (t === "array" || t === "Array" || t === "list" || t === "List")
11634
+ return "[]";
11635
+ if (t === "object" || t === "Object" || t === "dict" || t === "Dict")
11636
+ return "{}";
11637
+ if (t === "function" || t === "Function")
11638
+ return "() => {}";
11639
+ if (t === "any")
11640
+ return '"test"';
11641
+ if (t === "null")
11642
+ return "null";
11557
11643
  return '"test"';
11558
11644
  });
11559
11645
  const args = testValues.join(", ");
@@ -11583,8 +11669,10 @@ function buildQualityAssertionsForFunc(funcName, params, lang, indent) {
11583
11669
  `;
11584
11670
  const ecArgs = params.map((p) => {
11585
11671
  const t = p.type || inferTypeFromName(p.name, p.defaultValue);
11586
- if (t === "string") return '""';
11587
- if (t === "number" || t === "int" || t === "float") return "0";
11672
+ if (t === "string")
11673
+ return '""';
11674
+ if (t === "number" || t === "int" || t === "float")
11675
+ return "0";
11588
11676
  return '"edge"';
11589
11677
  }).join(", ");
11590
11678
  block += `${indent} result = ${funcName}(${ecArgs})
@@ -11622,11 +11710,16 @@ function buildQualityAssertionsForFunc(funcName, params, lang, indent) {
11622
11710
  `;
11623
11711
  const ecArgsJS = params.map((p) => {
11624
11712
  const t = p.type || inferTypeFromName(p.name, p.defaultValue);
11625
- if (t === "string") return '""';
11626
- if (t === "number" || t === "int" || t === "float") return "0";
11627
- if (t === "boolean") return "false";
11628
- if (t === "array") return "[]";
11629
- if (t === "object") return "{}";
11713
+ if (t === "string")
11714
+ return '""';
11715
+ if (t === "number" || t === "int" || t === "float")
11716
+ return "0";
11717
+ if (t === "boolean")
11718
+ return "false";
11719
+ if (t === "array")
11720
+ return "[]";
11721
+ if (t === "object")
11722
+ return "{}";
11630
11723
  return "undefined";
11631
11724
  }).join(", ");
11632
11725
  block += `${indent} const result = mod.${funcName}(${ecArgsJS});
@@ -11659,14 +11752,15 @@ function buildQualityAssertionsForFunc(funcName, params, lang, indent) {
11659
11752
  return block;
11660
11753
  }
11661
11754
  function isSkeletonUseless(content) {
11662
- if (!content) return true;
11755
+ if (!content)
11756
+ return true;
11663
11757
  const lines = content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("//") && !l.trim().startsWith("#") && !l.trim().startsWith("/*") && !l.trim().startsWith("*"));
11664
11758
  const todoLines = content.split("\n").filter((l) => /TODO|placeholder|smoke|is exported|module loads/.test(l));
11665
11759
  const meaningfulLines = lines.filter((l) => !/TODO|placeholder|smoke|is exported|module loads|throw new Error|raise AssertionError|pytest\.skip|assert.*true/.test(l));
11666
11760
  return meaningfulLines.length < 2;
11667
11761
  }
11668
11762
 
11669
- // src/lib/test-skeletons.ts
11763
+ // src/lib/test-skeletons.js
11670
11764
  var TEST_SKELETONS = {
11671
11765
  py: (name, exports = [], depth = "full", strict = true, quality = true, sourceContent = "") => {
11672
11766
  const moduleImport = name.replace(/-/g, "_");
@@ -11694,7 +11788,8 @@ var TEST_SKELETONS = {
11694
11788
 
11695
11789
  `;
11696
11790
  for (const exp of exports) {
11697
- if (exp.type === "class") continue;
11791
+ if (exp.type === "class")
11792
+ continue;
11698
11793
  const cases = generateTestCaseNames(exp.name, exp.type, quality);
11699
11794
  content += `# TODO: implement tests for ${exp.name}
11700
11795
  `;
@@ -11702,10 +11797,12 @@ var TEST_SKELETONS = {
11702
11797
  const caseFunc = caseName.replace(/[^a-zA-Z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
11703
11798
  content += `def test_${caseFunc}():
11704
11799
  `;
11705
- if (strict) content += ` raise AssertionError("TODO: implement ${caseName}")
11800
+ if (strict)
11801
+ content += ` raise AssertionError("TODO: implement ${caseName}")
11706
11802
 
11707
11803
  `;
11708
- else content += ` pytest.skip("TODO: implement ${caseName}")
11804
+ else
11805
+ content += ` pytest.skip("TODO: implement ${caseName}")
11709
11806
 
11710
11807
  `;
11711
11808
  }
@@ -11717,10 +11814,12 @@ var TEST_SKELETONS = {
11717
11814
  if (exports.length === 0) {
11718
11815
  content += `def test_${name}_placeholder():
11719
11816
  `;
11720
- if (strict) content += ` raise AssertionError("TODO: implement tests for ${name}")
11817
+ if (strict)
11818
+ content += ` raise AssertionError("TODO: implement tests for ${name}")
11721
11819
 
11722
11820
  `;
11723
- else content += ` pytest.skip("TODO: implement tests for ${name}")
11821
+ else
11822
+ content += ` pytest.skip("TODO: implement tests for ${name}")
11724
11823
 
11725
11824
  `;
11726
11825
  }
@@ -11754,7 +11853,8 @@ var TEST_SKELETONS = {
11754
11853
 
11755
11854
  `;
11756
11855
  for (const exp of exports) {
11757
- if (exp.type === "class") continue;
11856
+ if (exp.type === "class")
11857
+ continue;
11758
11858
  const cases = generateTestCaseNames(exp.name, exp.type, quality);
11759
11859
  content += ` // TODO: implement tests for ${exp.name}
11760
11860
  `;
@@ -11770,9 +11870,11 @@ var TEST_SKELETONS = {
11770
11870
  `;
11771
11871
  content += ` // TODO: implement ${caseName}
11772
11872
  `;
11773
- if (strict) content += ` throw new Error('TODO: implement ${caseName}');
11873
+ if (strict)
11874
+ content += ` throw new Error('TODO: implement ${caseName}');
11774
11875
  `;
11775
- else content += ` expect(true).toBe(true);
11876
+ else
11877
+ content += ` expect(true).toBe(true);
11776
11878
  `;
11777
11879
  content += ` });
11778
11880
 
@@ -11825,7 +11927,8 @@ var TEST_SKELETONS = {
11825
11927
 
11826
11928
  `;
11827
11929
  for (const exp of exports) {
11828
- if (exp.type === "class") continue;
11930
+ if (exp.type === "class")
11931
+ continue;
11829
11932
  const cases = generateTestCaseNames(exp.name, exp.type, quality);
11830
11933
  content += ` // TODO: implement tests for ${exp.name}
11831
11934
  `;
@@ -11841,9 +11944,11 @@ var TEST_SKELETONS = {
11841
11944
  `;
11842
11945
  content += ` // TODO: implement ${caseName}
11843
11946
  `;
11844
- if (strict) content += ` throw new Error('TODO: implement ${caseName}');
11947
+ if (strict)
11948
+ content += ` throw new Error('TODO: implement ${caseName}');
11845
11949
  `;
11846
- else content += ` expect(true).toBe(true);
11950
+ else
11951
+ content += ` expect(true).toBe(true);
11847
11952
  `;
11848
11953
  content += ` });
11849
11954
 
@@ -11896,7 +12001,8 @@ var TEST_SKELETONS = {
11896
12001
 
11897
12002
  `;
11898
12003
  for (const exp of exports) {
11899
- if (exp.type === "class") continue;
12004
+ if (exp.type === "class")
12005
+ continue;
11900
12006
  const cases = generateTestCaseNames(exp.name, exp.type, quality);
11901
12007
  content += ` // TODO: implement tests for ${exp.name}
11902
12008
  `;
@@ -11912,9 +12018,11 @@ var TEST_SKELETONS = {
11912
12018
  `;
11913
12019
  content += ` // TODO: implement ${caseName}
11914
12020
  `;
11915
- if (strict) content += ` throw new Error('TODO: implement ${caseName}');
12021
+ if (strict)
12022
+ content += ` throw new Error('TODO: implement ${caseName}');
11916
12023
  `;
11917
- else content += ` expect(true).toBe(true);
12024
+ else
12025
+ content += ` expect(true).toBe(true);
11918
12026
  `;
11919
12027
  content += ` });
11920
12028
 
@@ -11974,7 +12082,8 @@ var TEST_SKELETONS = {
11974
12082
 
11975
12083
  `;
11976
12084
  for (const exp of exports) {
11977
- if (exp.type === "class") continue;
12085
+ if (exp.type === "class")
12086
+ continue;
11978
12087
  const cases = generateTestCaseNames(exp.name, exp.type, quality);
11979
12088
  const expCap = exp.name.charAt(0).toUpperCase() + exp.name.slice(1);
11980
12089
  content += `// TODO: implement tests for ${exp.name}
@@ -11983,9 +12092,11 @@ var TEST_SKELETONS = {
11983
12092
  const caseFunc = caseName.replace(/[^a-zA-Z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
11984
12093
  content += `func Test${cap}_${caseFunc}(t *testing.T) {
11985
12094
  `;
11986
- if (strict) content += ` t.Error("TODO: implement ${caseName}")
12095
+ if (strict)
12096
+ content += ` t.Error("TODO: implement ${caseName}")
11987
12097
  `;
11988
- else content += ` t.Skip("TODO: implement ${caseName}")
12098
+ else
12099
+ content += ` t.Skip("TODO: implement ${caseName}")
11989
12100
  `;
11990
12101
  content += `}
11991
12102
 
@@ -12005,9 +12116,11 @@ var TEST_SKELETONS = {
12005
12116
  if (exports.length === 0) {
12006
12117
  content += `func Test${cap}_Placeholder(t *testing.T) {
12007
12118
  `;
12008
- if (strict) content += ` t.Error("TODO: implement tests for ${name}")
12119
+ if (strict)
12120
+ content += ` t.Error("TODO: implement tests for ${name}")
12009
12121
  `;
12010
- else content += ` t.Skip("TODO: implement tests for ${name}")
12122
+ else
12123
+ content += ` t.Skip("TODO: implement tests for ${name}")
12011
12124
  `;
12012
12125
  content += `}
12013
12126
  `;
@@ -12040,9 +12153,11 @@ var TEST_SKELETONS = {
12040
12153
  `;
12041
12154
  content += ` echo "TODO: implement ${caseName}"
12042
12155
  `;
12043
- if (strict) content += ` exit 1
12156
+ if (strict)
12157
+ content += ` exit 1
12044
12158
  `;
12045
- else content += ` echo "SKIP: ${caseName}"
12159
+ else
12160
+ content += ` echo "SKIP: ${caseName}"
12046
12161
  `;
12047
12162
  content += `}
12048
12163
 
@@ -12056,9 +12171,11 @@ var TEST_SKELETONS = {
12056
12171
  if (exports.length === 0) {
12057
12172
  content += `function test_smoke {
12058
12173
  `;
12059
- if (strict) content += ` echo "TODO: implement tests for ${name}" && exit 1
12174
+ if (strict)
12175
+ content += ` echo "TODO: implement tests for ${name}" && exit 1
12060
12176
  `;
12061
- else content += ` echo "TODO: implement tests for ${name}"
12177
+ else
12178
+ content += ` echo "TODO: implement tests for ${name}"
12062
12179
  `;
12063
12180
  content += `}
12064
12181
  `;
@@ -12098,7 +12215,8 @@ mod tests {
12098
12215
 
12099
12216
  `;
12100
12217
  for (const exp of exports) {
12101
- if (exp.type === "class") continue;
12218
+ if (exp.type === "class")
12219
+ continue;
12102
12220
  const cases = generateTestCaseNames(exp.name, exp.type, quality);
12103
12221
  content += ` // TODO: implement tests for ${exp.name}
12104
12222
  `;
@@ -12107,9 +12225,11 @@ mod tests {
12107
12225
  content += ` #[test]
12108
12226
  fn test_${caseFunc}() {
12109
12227
  `;
12110
- if (strict) content += ` panic!("TODO: implement ${caseName}");
12228
+ if (strict)
12229
+ content += ` panic!("TODO: implement ${caseName}");
12111
12230
  `;
12112
- else content += ` // TODO: implement ${caseName}
12231
+ else
12232
+ content += ` // TODO: implement ${caseName}
12113
12233
  `;
12114
12234
  content += ` }
12115
12235
 
@@ -12124,9 +12244,11 @@ mod tests {
12124
12244
  content += ` #[test]
12125
12245
  fn ${name}_placeholder() {
12126
12246
  `;
12127
- if (strict) content += ` panic!("TODO: implement tests for ${name}");
12247
+ if (strict)
12248
+ content += ` panic!("TODO: implement tests for ${name}");
12128
12249
  `;
12129
- else content += ` // TODO: implement tests for ${name}
12250
+ else
12251
+ content += ` // TODO: implement tests for ${name}
12130
12252
  `;
12131
12253
  content += ` }
12132
12254
  `;
@@ -12166,7 +12288,8 @@ mod tests {
12166
12288
 
12167
12289
  `;
12168
12290
  for (const exp of exports) {
12169
- if (exp.type === "class") continue;
12291
+ if (exp.type === "class")
12292
+ continue;
12170
12293
  const cases = generateTestCaseNames(exp.name, exp.type, quality);
12171
12294
  content += ` # TODO: implement tests for ${exp.name}
12172
12295
  `;
@@ -12174,9 +12297,11 @@ mod tests {
12174
12297
  const caseFunc = caseName.replace(/[^a-zA-Z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
12175
12298
  content += ` def test_${caseFunc}
12176
12299
  `;
12177
- if (strict) content += ` flunk "TODO: implement ${caseName}"
12300
+ if (strict)
12301
+ content += ` flunk "TODO: implement ${caseName}"
12178
12302
  `;
12179
- else content += ` # TODO: implement ${caseName}
12303
+ else
12304
+ content += ` # TODO: implement ${caseName}
12180
12305
  `;
12181
12306
  content += ` end
12182
12307
 
@@ -12190,9 +12315,11 @@ mod tests {
12190
12315
  if (exports.length === 0) {
12191
12316
  content += ` def test_placeholder
12192
12317
  `;
12193
- if (strict) content += ` flunk "TODO: implement tests for ${name}"
12318
+ if (strict)
12319
+ content += ` flunk "TODO: implement tests for ${name}"
12194
12320
  `;
12195
- else content += ` # TODO: implement tests for ${name}
12321
+ else
12322
+ content += ` # TODO: implement tests for ${name}
12196
12323
  `;
12197
12324
  content += ` end
12198
12325
  `;
@@ -12238,15 +12365,18 @@ mod tests {
12238
12365
  const cases = generateTestCaseNames(exp.name, exp.type, quality);
12239
12366
  for (const caseName of cases) {
12240
12367
  const testFunc = caseName.replace(/[^a-zA-Z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
12241
- if (!strict) content += ` // @Disabled("TODO")
12368
+ if (!strict)
12369
+ content += ` // @Disabled("TODO")
12242
12370
  `;
12243
12371
  content += ` @Test
12244
12372
  `;
12245
12373
  content += ` void test${testFunc.charAt(0).toUpperCase() + testFunc.slice(1)}() {
12246
12374
  `;
12247
- if (strict) content += ` fail("TODO: implement ${caseName}");
12375
+ if (strict)
12376
+ content += ` fail("TODO: implement ${caseName}");
12248
12377
  `;
12249
- else content += ` assertTrue(true); // TODO: implement ${caseName}
12378
+ else
12379
+ content += ` assertTrue(true); // TODO: implement ${caseName}
12250
12380
  `;
12251
12381
  content += ` }
12252
12382
 
@@ -12308,15 +12438,18 @@ mod tests {
12308
12438
  const cases = generateTestCaseNames(exp.name, exp.type, quality);
12309
12439
  for (const caseName of cases) {
12310
12440
  const testFunc = caseName.replace(/[^a-zA-Z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
12311
- if (!strict) content += ` // @Disabled("TODO")
12441
+ if (!strict)
12442
+ content += ` // @Disabled("TODO")
12312
12443
  `;
12313
12444
  content += ` @Test
12314
12445
  `;
12315
12446
  content += ` fun test${testFunc.charAt(0).toUpperCase() + testFunc.slice(1)}() {
12316
12447
  `;
12317
- if (strict) content += ` fail("TODO: implement ${caseName}")
12448
+ if (strict)
12449
+ content += ` fail("TODO: implement ${caseName}")
12318
12450
  `;
12319
- else content += ` assertTrue(true) // TODO: implement ${caseName}
12451
+ else
12452
+ content += ` assertTrue(true) // TODO: implement ${caseName}
12320
12453
  `;
12321
12454
  content += ` }
12322
12455
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibeostheog",
3
- "version": "0.24.14",
3
+ "version": "0.24.15",
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",