@krtclcdy/tersio 2.22.1 → 2.23.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/cli/install.js CHANGED
@@ -3,14 +3,13 @@ import { promises as fs } from 'node:fs';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
- import { BUN_BIN_DIR, COMBO_PRESET_MODES, IS_WINDOWS, OMP_AGENT_DIR, OMP_PLUGINS_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_VERSION, RTK_BINARY_NAME, applyUpdate, cavemanDefaultFlag, comboDefaultFlag, command, dryRun, install, ponytailDefaultFlag, profileFlagsGiven, reinstall, rtkDefaultFlag, verbose, yes, dashboardExport, dashboardPort, debug, ensureExtensionAfterConfigEntry, ensureExtensionInConfig, ensurePonytailConfigValue, execP, parseJsonObject, readPluginsPackage, writeIfChanged, } from './common.js';
6
+ import { BUN_BIN_DIR, COMBO_PRESET_MODES, IS_WINDOWS, OMP_AGENT_DIR, OMP_PLUGINS_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_VERSION, RTK_BINARY_NAME, applyUpdate, cavemanDefaultFlag, comboDefaultFlag, command, dryRun, install, ponytailDefaultFlag, profileFlagsGiven, reinstall, rtkDefaultFlag, verbose, yes, dashboardExport, dashboardPort, debug, ensurePonytailConfigValue, execP, parseJsonObject, readPluginsPackage, writeIfChanged, } from './common.js';
7
7
  import { askInteractiveChoice, askInteractiveConfirm, closeRL, execNetwork, tty, withInteractiveSpinner, } from './interactive.js';
8
8
  import { printWelcome } from './banner.js';
9
9
  import { checkForUpdate, runLatestUpdate } from './update.js';
10
10
  import { runUninstall } from './uninstall.js';
11
11
  import { runDoctor } from './doctor.js';
12
12
  import { runReset } from './reset.js';
13
- import { runSettings } from './settings.js';
14
13
  import { runUsage } from './usage.js';
15
14
  import { runDashboard } from './dashboard.js';
16
15
  import { wireRtkOmp } from './rtk-wiring.js';
@@ -20,18 +19,18 @@ import { storedProfile, writePluginSettings } from './profile.js';
20
19
  const EXT_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'extensions');
21
20
  const SHARED_SESSION_STATE = path.join(EXT_DIR, 'shared', 'session-state.ts');
22
21
  const CAVEMAN_INDEX = path.join(EXT_DIR, 'caveman-session', 'index.ts');
22
+ const CAVEMAN_RULE = path.join(EXT_DIR, 'caveman-session', 'rule.md');
23
23
  const RTK_SESSION_INDEX = path.join(EXT_DIR, 'rtk-session', 'index.ts');
24
24
  const UPDATER_INDEX = path.join(EXT_DIR, 'ai-addons-updater', 'index.ts');
25
25
  const COMBO_TOGGLE_INDEX = path.join(EXT_DIR, 'combo-toggle', 'index.ts');
26
26
  const TERSIO_COMMANDS_INDEX = path.join(EXT_DIR, 'tersio-commands', 'index.ts');
27
- const MODE_REINFORCEMENT_INDEX = path.join(EXT_DIR, 'shared', 'mode-reinforcement.ts');
28
27
  const SHARED_TYPES = path.join(EXT_DIR, 'shared', 'types.ts');
29
28
  const LIB_UTILS = path.join(EXT_DIR, 'lib', 'utils.ts');
30
29
  const SHARED_PLUGIN_SETTINGS = path.join(EXT_DIR, 'shared', 'plugin-settings.ts');
31
30
  const SHARED_USAGE_LEDGER = path.join(EXT_DIR, 'shared', 'usage-ledger.ts');
32
31
  const SHARED_PRICING = path.join(EXT_DIR, 'shared', 'pricing.ts');
33
32
  const SHARED_CARBON = path.join(EXT_DIR, 'shared', 'carbon.ts');
34
- async function stepPonytail(pluginsDir, userDir, options) {
33
+ async function stepPonytail(pluginsDir, options) {
35
34
  if (!options.quiet)
36
35
  console.log(' Ponytail — ensure bundled plugin');
37
36
  await fs.mkdir(pluginsDir, { recursive: true });
@@ -92,12 +91,8 @@ async function stepPonytail(pluginsDir, userDir, options) {
92
91
  console.log(' [hint] The /ponytail command won\'t work, but ponytail skills will still load');
93
92
  }
94
93
  else if (!options.dryRun && !options.quiet) {
95
- // Wire extension into config.yml so /ponytail command loads
96
- debug('Bundled Ponytail pi-extension found');
94
+ debug('Bundled Ponytail pi-extension found; OMP loads it from the plugin manifest');
97
95
  }
98
- // Still set Ponytail config defaults even without the extension command
99
- const configPath = path.join(userDir, 'config.yml');
100
- await ensureExtensionInConfig(configPath, ponytailExtPath, 'ponytail', options);
101
96
  await ensurePonytailConfigValue('defaultMode', 'off', options);
102
97
  // hideStatus=true keeps the upstream ponytail bar hidden; combo owns the bar.
103
98
  await ensurePonytailConfigValue('hideStatus', true, options);
@@ -355,14 +350,6 @@ async function stepSharedSessionState(extDir, options) {
355
350
  [SHARED_CARBON, path.join('shared', 'carbon.ts')],
356
351
  ], 'shared/session-state.js', options);
357
352
  }
358
- async function stepModeReinforcement(extDir, ponytailExtPath, options) {
359
- if (!options.quiet)
360
- console.log(' Session helpers — sync shared files');
361
- const dest = path.join(extDir, 'shared', 'mode-reinforcement.ts');
362
- if (!await copySources(extDir, [[MODE_REINFORCEMENT_INDEX, path.join('shared', 'mode-reinforcement.ts')]], 'shared/mode-reinforcement.ts', options))
363
- return;
364
- await ensureExtensionAfterConfigEntry(path.join(path.dirname(extDir), 'config.yml'), dest, ponytailExtPath, 'mode reinforcement', options);
365
- }
366
353
  async function stepRtkSession(extDir, options) {
367
354
  if (!options.quiet)
368
355
  console.log(' RTK session — install session mode');
@@ -371,12 +358,17 @@ async function stepRtkSession(extDir, options) {
371
358
  // One rule fetch serves the install; dry runs stay offline
372
359
  // and fall back to the bundled rule to preview its destination.
373
360
  async function fetchCavemanRule(options) {
361
+ const bundled = await readTextIfExists(CAVEMAN_RULE);
374
362
  if (options.dryRun)
375
- return (await readTextIfExists(path.join(path.dirname(CAVEMAN_INDEX), 'rule.md'))) || '';
363
+ return bundled;
376
364
  try {
377
365
  return await withInteractiveSpinner('Fetching Caveman rule', () => httpsGet(CAVEMAN_REMOTE_RULE));
378
366
  }
379
367
  catch (e) {
368
+ if (bundled !== null) {
369
+ debug(`Using bundled Caveman rule: ${e.message}`);
370
+ return bundled;
371
+ }
380
372
  console.log(` [warn] Could not fetch caveman rule: ${e.message}`);
381
373
  return null;
382
374
  }
@@ -411,12 +403,7 @@ async function stepUpdater(extDir, options) {
411
403
  async function stepCombo(extDir, options) {
412
404
  if (!options.quiet)
413
405
  console.log(' Combo — install preset switch');
414
- const dest = path.join(extDir, 'combo-toggle', 'index.ts');
415
- if (!await copySources(extDir, [[COMBO_TOGGLE_INDEX, path.join('combo-toggle', 'index.ts')]], 'combo-toggle/index.ts', options))
416
- return;
417
- // Auto-register combo in config.yml
418
- const configPath = path.join(path.dirname(extDir), 'config.yml');
419
- await ensureExtensionInConfig(configPath, dest, 'combo', options);
406
+ await copySources(extDir, [[COMBO_TOGGLE_INDEX, path.join('combo-toggle', 'index.ts')]], 'combo-toggle/index.ts', options);
420
407
  }
421
408
  async function resolveProfile(forceReinstall = false, opts = {}) {
422
409
  // Seed from the lock file so flag-less update/reinstall runs keep the
@@ -486,7 +473,6 @@ async function runCommandMenu() {
486
473
  { value: 'usage', label: 'Usage', hint: 'token usage and savings report' },
487
474
  { value: 'dashboard', label: 'Dashboard', hint: 'open the report in your browser' },
488
475
  { value: 'reset', label: 'Reset statistics', hint: 'clear statistics; transcripts and RTK history stay' },
489
- { value: 'settings', label: 'Settings', hint: 'defaults: combo, caveman, rtk, ponytail, currency' },
490
476
  { value: 'uninstall', label: 'Uninstall', hint: 'remove tersio' },
491
477
  ], 'install');
492
478
  if (choice.status !== 'selected') {
@@ -553,9 +539,6 @@ async function runCommandMenu() {
553
539
  await runReset();
554
540
  closeRL();
555
541
  break;
556
- case 'settings':
557
- await runSettings();
558
- break;
559
542
  case 'uninstall':
560
543
  await runUninstall();
561
544
  closeRL();
@@ -644,14 +627,12 @@ async function runInstall(overrides = {}) {
644
627
  await capture('shared', () => stepSharedSessionState(userExtDir, options));
645
628
  let selfPlugin = false;
646
629
  await capture('self-plugin', async () => { selfPlugin = await stepSelfPlugin(OMP_PLUGINS_DIR, options); });
647
- await capture('ponytail', () => stepPonytail(OMP_PLUGINS_DIR, userDir, options));
648
- const ponytailExtPath = path.join(OMP_PLUGINS_DIR, 'node_modules', '@dietrichgebert', 'ponytail', 'pi-extension', 'index.js');
630
+ await capture('ponytail', () => stepPonytail(OMP_PLUGINS_DIR, options));
649
631
  await capture('rtk', () => stepRtk(BUN_BIN_DIR, options));
650
632
  await capture('rtk session', () => stepRtkSession(userExtDir, options));
651
- await capture('caveman', () => stepCaveman(userExtDir, cavemanRule, options));
633
+ await capture('caveman', () => stepCaveman(path.join(OMP_PLUGINS_DIR, 'node_modules', '@krtclcdy', 'tersio', 'extensions'), cavemanRule, options));
652
634
  await capture('combo', () => stepCombo(userExtDir, options));
653
635
  await capture('commands', () => stepTersioCommands(userExtDir, options));
654
- await capture('reinforcement', () => stepModeReinforcement(userExtDir, ponytailExtPath, options));
655
636
  await capture('updater', () => stepUpdater(userExtDir, options));
656
637
  if (selfPlugin)
657
638
  await capture('settings', () => writePluginSettings(profile, options));
package/cli/profile.js CHANGED
@@ -72,6 +72,7 @@ async function writePluginSettings(profile, options) {
72
72
  config.settings[PACKAGE_NAME] = {
73
73
  ...(config.settings[PACKAGE_NAME] || {}),
74
74
  comboDefault: profile.comboDefault,
75
+ comboSetupComplete: true,
75
76
  cavemanDefault: profile.cavemanDefault,
76
77
  rtkDefault: profile.rtkDefault,
77
78
  ponytailDefault: profile.ponytailDefault,
package/cli/settings.js CHANGED
@@ -85,7 +85,7 @@ async function askDiagSchedule(current) {
85
85
  }
86
86
  const SETTING_NAMES = ['combo', 'caveman', 'rtk', 'ponytail', 'currency', 'diagnosis'];
87
87
  function settingsUsage() {
88
- console.log(' Usage: tersio settings [combo|caveman|rtk|ponytail|currency|diagnosis] [--combo-default off|medium|balanced|max] [--caveman-default off|lite|full|ultra|wenyan] [--rtk-default on|off] [--ponytail-default off|lite|full|ultra|review] [--currency USD|PHP|EUR|GBP|JPY|KRW|SGD|AUD|CAD|INR] [--diag-schedule manual|daily|weekly|monthly] [--dry-run]');
88
+ console.log(' Usage: tersio settings [combo|caveman|rtk|ponytail|currency|diagnosis] [--combo-default off|medium|balanced|max] [--caveman-default off|lite|full|ultra|wenyan-lite|wenyan-full|wenyan-ultra] [--rtk-default on|off] [--ponytail-default off|lite|full|ultra] [--currency USD|PHP|EUR|GBP|JPY|KRW|SGD|AUD|CAD|INR] [--diag-schedule manual|daily|weekly|monthly] [--dry-run]');
89
89
  }
90
90
  // Single-setting jump: `tersio settings diagnosis` prompts only that value
91
91
  // instead of walking the whole chain. Returns false when aborted.
package/cli/update.js CHANGED
@@ -2,11 +2,11 @@
2
2
  import { promises as fs } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { spawn } from 'node:child_process';
5
- import { BUN_BIN_DIR, IS_WINDOWS, OMP_AGENT_DIR, OMP_PLUGINS_DIR, PACKAGE_BIN, PACKAGE_NAME, PACKAGE_VERSION, RTK_BINARY_NAME, dryRun, execP, parseJsonObject, verbose, } from './common.js';
5
+ import { IS_WINDOWS, OMP_PLUGINS_DIR, PACKAGE_BIN, PACKAGE_NAME, PACKAGE_VERSION, dryRun, execP, parseJsonObject, verbose, } from './common.js';
6
6
  import { execNetwork } from './interactive.js';
7
- import { readTextIfExists, tersioDataPath } from '../extensions/lib/utils.js';
7
+ import { readTextIfExists, resolveRtkBinary, tersioDataPath } from '../extensions/lib/utils.js';
8
8
  import { refreshPrices } from '../extensions/shared/pricing.js';
9
- import { CAVEMAN_REMOTE_RULE, RTK_RELEASE_API, fetchJson, httpsGet, normalizeRtkVersion, sha256Hex, } from '../extensions/lib/utils.js';
9
+ import { CAVEMAN_REMOTE_RULE, RTK_RELEASE_API, httpsGet, normalizeRtkVersion, sha256Hex, } from '../extensions/lib/utils.js';
10
10
  const UPDATE_CHECK_TTL_MS = 6 * 60 * 60 * 1000;
11
11
  function newerThan(a, b) {
12
12
  const pa = a.replace(/^v/, '').split('.').map((n) => parseInt(n, 10) || 0);
@@ -83,21 +83,22 @@ async function execInherit(cmd, args) {
83
83
  // Current → latest per add-on. Every probe is capped and nullable; the plan
84
84
  // prints `unknown` for anything unreachable and the update proceeds anyway.
85
85
  async function probeUpdatePlan(cliLatest) {
86
- const rtkBin = path.join(BUN_BIN_DIR, RTK_BINARY_NAME);
86
+ const rtkBin = resolveRtkBinary();
87
87
  const ponytailPkg = path.join(OMP_PLUGINS_DIR, 'node_modules', '@dietrichgebert', 'ponytail', 'package.json');
88
- const ruleDest = path.join(OMP_AGENT_DIR, 'extensions', 'caveman-session', 'rule.md');
88
+ const tersioRule = path.join(OMP_PLUGINS_DIR, 'node_modules', '@krtclcdy', 'tersio', 'extensions', 'caveman-session', 'rule.md');
89
+ const controller = new AbortController();
89
90
  const [rtkLocal, rtkRelease, ruleLocalText, ruleRemoteText, ponytailLocalText] = await Promise.all([
90
- settle(execP(rtkBin, ['--version'], { timeout: 5000 }).then((r) => normalizeRtkVersion(r.stdout.trim() || r.stderr.trim()) || null), 6000),
91
- settle(fetchJson(RTK_RELEASE_API).then((r) => normalizeRtkVersion(r.tag_name) || null), 4000),
92
- readTextIfExists(ruleDest),
93
- settle(httpsGet(CAVEMAN_REMOTE_RULE), 4000),
91
+ settle(rtkBin ? execP(rtkBin, ['--version'], { timeout: 5000 }).then((r) => normalizeRtkVersion(r.stdout.trim() || r.stderr.trim()) || null) : Promise.resolve(null), 6000),
92
+ settle(httpsGet(RTK_RELEASE_API, { signal: controller.signal }).then((text) => normalizeRtkVersion(JSON.parse(text).tag_name) || null), 1500),
93
+ readTextIfExists(tersioRule),
94
+ settle(httpsGet(CAVEMAN_REMOTE_RULE, { signal: controller.signal }).then((text) => sha256Hex(text).slice(0, 8)), 1500),
94
95
  readTextIfExists(ponytailPkg),
95
96
  ]);
96
- const shortHash = (text) => text === null ? null : sha256Hex(text).slice(0, 8);
97
+ controller.abort();
97
98
  return {
98
99
  cli: cliLatest,
99
100
  rtk: [rtkLocal, rtkRelease],
100
- rule: [shortHash(ruleLocalText), shortHash(ruleRemoteText)],
101
+ rule: [ruleLocalText ? sha256Hex(ruleLocalText).slice(0, 8) : null, ruleRemoteText],
101
102
  ponytail: ponytailLocalText ? parseJsonObject(ponytailLocalText)?.version ?? null : null,
102
103
  };
103
104
  }
package/cli/usage.js CHANGED
@@ -1,4 +1,6 @@
1
- import { co2GramsFor, displayModelId, energyWhFor, importSessionTokens, ledgerPath, priceFor, readResetWatermark, readUsage, refreshPricesIfStale, sessionsDir, usdCost, } from '../extensions/shared/usage-ledger.js';
1
+ // cli/usage.ts — ledger + session-token usage report, tokscale-style.
2
+ import { co2GramsFor, displayModelId, energyWhFor, importSessionTokens, ledgerPath, priceFor, readResetWatermark, readRtkAdoption, readRtkRecallDiagnostics, readUsage, refreshPricesIfStale, sessionsDir, usdCost, } from '../extensions/shared/usage-ledger.js';
3
+ import { resolveRtkBinary } from '../extensions/lib/utils.js';
2
4
  import { readRtkGain } from '../extensions/shared/rtk-gain.js';
3
5
  import { readUsageDb, syncUsageDb, usageDbPath } from '../extensions/shared/usage-store.js';
4
6
  import { withInteractiveSpinner } from './interactive.js';
@@ -74,6 +76,8 @@ export function summarizeUsage(rows) {
74
76
  est: usdCost({ input: r.i, output: r.o, cacheRead: r.cr ?? 0, cacheWrite: r.cw ?? 0 }, r.m).usd,
75
77
  })),
76
78
  rtkGain: readRtkGain(RTK_COMMAND_ROWS, watermark || undefined),
79
+ rtkAdoption: readRtkAdoption(),
80
+ rtkRecall: readRtkRecallDiagnostics(resolveRtkBinary()),
77
81
  usd,
78
82
  priced,
79
83
  savedUsd,
@@ -134,6 +138,7 @@ function printReport(report) {
134
138
  const t = report.tokens;
135
139
  console.log(` TOKENS ${fmt(t.input)} in · ${fmt(t.output)} out · ${fmt(t.cacheRead)} cache read · ${fmt(t.cacheWrite)} cache write`);
136
140
  console.log(` COST ${formatCurrency(report.usd, report.currency)}${report.priced ? '' : ' (includes default pricing)'} · ~${formatCurrency(report.savedUsd, report.currency)} cache-saved (est.) · ~${report.co2g.toFixed(1)}g CO2 (est.)`);
141
+ console.log(` RTK ${fmt(report.rtkAdoption.rtkCalls)}/${fmt(report.rtkAdoption.eligibleCalls)} eligible OMP Bash calls use RTK (${report.rtkAdoption.adoptionPct.toFixed(1)}%) · ${fmt(report.rtkAdoption.missedCalls)} missed · recall ${report.rtkRecall.available ? `${report.rtkRecall.mode} (${report.rtkRecall.entries})` : 'unavailable'}`);
137
142
  const models = Object.entries(report.byModel).filter(([, b]) => b.input + b.output + b.cacheRead + b.cacheWrite > 0).sort((a, b) => (b[1].input + b[1].output) - (a[1].input + a[1].output)).slice(0, 8);
138
143
  if (models.length) {
139
144
  console.log(' BY MODEL');