@krtclcdy/tersio 2.22.0 → 2.22.1

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/README.md CHANGED
@@ -79,21 +79,26 @@ Measured savings against the same workload without the modes. Token counts are r
79
79
 
80
80
  Break-even math, balanced preset (caveman full + rtk + ponytail): a mixed turn costs 941 tok baseline vs 399 with Tersio (0.42×, **−57.6% per turn**). Overhead repays in turn 1 with the bundled floor (464 tok); turn 4 with the real installed Ponytail plugin (v4.9.0, 1,264 tok). RTK concentrates savings where noise lives — hook-wired sessions record **−97.5% on summarized test suites** (rtk `history.db`, 4 runs).
81
81
 
82
- ## 📈 Gain Dashboard
82
+ ## 📈 Dashboard
83
83
 
84
- `tersio gain --open` serves a local dashboard (127.0.0.1 only) that charts your own savings from `~/.tersio/usage.db`. Three views:
84
+ `tersio dashboard --open` serves a local dashboard (127.0.0.1 only) that charts your own savings from `~/.tersio/usage.db`. Three views:
85
85
 
86
86
  **Live feed and savings.** Token throughput, cost, savings bento, and recent activity — the top of the dashboard.
87
87
 
88
- ![Gain dashboard: live token feed, cost, savings, and activity](/assets/GainHero.webp)
88
+ ![Dashboard: live token feed, cost, savings, and activity](/assets/GainHero.webp)
89
89
 
90
90
  **Top models and recent requests.** Per-model cost breakdown plus the last requests with elapsed time and token speed.
91
91
 
92
- ![Gain dashboard: top models with per-model cost, recent requests with elapsed time and token speed](/assets/GainModels.webp)
92
+ ![Dashboard: top models with per-model cost, recent requests with elapsed time and token speed](/assets/GainModels.webp)
93
93
 
94
94
  **Command tools.** Token savings, average rate, and timing per shell tool.
95
95
 
96
- ![Gain dashboard: command tools with token savings, average rate, and timing](/assets/GainTools.webp)
96
+ ![Dashboard: command tools with token savings, average rate, and timing](/assets/GainTools.webp)
97
+
98
+ ### Measurement references
99
+
100
+ - **[EcoLogits](https://github.com/mlco2/ecologits)** informs Tersio's local CO₂ and energy estimates. Tersio ports the model to TypeScript; EcoLogits is not a runtime dependency.
101
+ - **[Tokscale](https://github.com/junhoyeo/tokscale)** informs Tersio's Input, Output, Cache Read, and Cache Write accounting from local OMP sessions. Tokscale is a reference, not a runtime dependency.
97
102
 
98
103
  ## 🖥️ CLI
99
104
 
@@ -104,12 +109,12 @@ Break-even math, balanced preset (caveman full + rtk + ponytail): a mixed turn c
104
109
  | `tersio reinstall` | Fresh install, preserving the Ponytail package |
105
110
  | `tersio doctor` | Check OMP, extension, Ponytail, and RTK health (`--fix` repairs, `--fix=<scope>` one scope, `--dry-run` previews) |
106
111
  | `tersio usage` | Ledger-backed usage + savings report |
107
- | `tersio gain` | Open the gain dashboard (`--open`, `--export <file>`, `--port <n>`, `--currency <code>`; serves localhost only) |
112
+ | `tersio dashboard` | Open the Dashboard (`--open`, `--export <file>`, `--port <n>`, `--currency <code>`; serves localhost only) |
108
113
  | `tersio reset` | Clear tersio statistics: usage ledger + a watermark that hides pre-reset rows from every derived view (Y/N confirm, `--dry-run`, `--yes`) — session transcripts and RTK history stay intact on disk |
109
114
  | `tersio uninstall` | Remove extensions, registration, and the Ponytail plugin (`--keep-ponytail` keeps Ponytail; `--remove-rtk` also removes the RTK binary and its `rtk.ts` OMP wiring) |
110
115
  | `tersio version` | Print version |
111
116
 
112
- Flags: `--dry-run`, `--yes`/`-y`, `--verbose`, `--combo-default`/`--caveman-default`/`--rtk-default`/`--ponytail-default`, `--currency <code>` (usage|gain display currency; flag wins, then the `tersio settings` default, then USD). Legacy `--doctor` / `--uninstall` forms still work.
117
+ Flags: `--dry-run`, `--yes`/`-y`, `--verbose`, `--combo-default`/`--caveman-default`/`--rtk-default`/`--ponytail-default`, `--currency <code>` (usage/dashboard display currency; flag wins, then the `tersio settings` default, then USD). Legacy `--doctor` / `--uninstall` forms still work.
113
118
 
114
119
  ## ⌨️ Commands reference
115
120
 
@@ -124,7 +129,7 @@ Mode switches live on their own commands; bare `/tersio` prints status.
124
129
  | `/tersio status` | Active modes + combo level; state persists, propagates to subagents, and lights the Combo footer indicator. |
125
130
  | `/tersio check` | Add-on version check |
126
131
  | `/tersio update <ponytail\|rtk\|caveman\|all> [--dry-run]` | Update add-ons, preview with dry-run |
127
- | `/tersio gain` | Savings summary; the gain dashboard charts `~/.tersio/usage.db` (`tersio gain --open` serves on 127.0.0.1, `--export` writes one file). |
132
+ | `/tersio dashboard` | Open the Dashboard. |
128
133
  | `/tersio usage` | Ledger report for this machine (rows in `~/.tersio/usage.db`, local only) |
129
134
  | `/tersio help` | This table |
130
135
  | `/ai-addons` | Add-on updater alias, still works. |
@@ -149,7 +154,7 @@ User-level installs also register the package in `~/.omp/plugins` (visible in OM
149
154
 
150
155
  **RTK missing or not executable:** run `tersio reinstall`, then `tersio doctor`. On Linux/macOS: `chmod +x ~/.bun/bin/rtk`.
151
156
 
152
- **RTK commands not metered in the gain dashboard:** check `tersio doctor` — the `RTK OMP wiring (rtk.ts)` row must be ok. Missing: run `rtk init -g --agent omp` (needs rtk ≥ 0.49) and restart OMP. Native tool calls (`read`/`edit`/`eval`) stay unmetered — only bash tool calls pass through rtk.
157
+ **RTK commands not metered in the Dashboard:** check `tersio doctor` — the `RTK OMP wiring (rtk.ts)` row must be ok. Missing: run `rtk init -g --agent omp` (needs rtk ≥ 0.49) and restart OMP. Native tool calls (`read`/`edit`/`eval`) stay unmetered — only bash tool calls pass through rtk.
153
158
 
154
159
  **Checksum warning or failure:** the installer aborts on RTK checksum mismatch but warns and continues when checksum metadata is unavailable; `/tersio update rtk` aborts when metadata is missing.
155
160
 
package/cli/common.js CHANGED
@@ -48,7 +48,7 @@ function flagValue(name) {
48
48
  return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
49
49
  }
50
50
  // --- CLI flags ---
51
- const COMMANDS = { install: true, update: true, reinstall: true, doctor: true, uninstall: true, usage: true, gain: true, reset: true, settings: true, version: true, help: true };
51
+ const COMMANDS = { install: true, update: true, reinstall: true, doctor: true, uninstall: true, usage: true, dashboard: true, reset: true, settings: true, version: true, help: true };
52
52
  const args = process.argv.slice(2);
53
53
  const commandArg = args.find((arg) => !arg.startsWith('-'));
54
54
  const command = commandArg?.toLowerCase() || null;
@@ -75,13 +75,13 @@ const verbose = args.includes('--verbose');
75
75
  const doctor = command === 'doctor' || args.includes('--doctor');
76
76
  const fix = args.includes('--fix') || args.some((a) => a.startsWith('--fix='));
77
77
  const usage = command === 'usage' || args.includes('--usage');
78
- const gain = command === 'gain';
78
+ const dashboard = command === 'dashboard';
79
79
  const reset = command === 'reset';
80
80
  const settings = command === 'settings';
81
81
  const dashboardPort = Number.parseInt(flagValue('--port') ?? '', 10) || 0;
82
82
  const dashboardOpen = args.includes('--open');
83
83
  const dashboardExport = flagValue('--export') ?? null;
84
- // Display currency for usage/gain: --currency flag wins, then the stored
84
+ // Display currency for usage/dashboard: --currency flag wins, then the stored
85
85
  // plugin default (tersio settings), then USD.
86
86
  const currency = parseCurrencyFlag(flagValue('--currency')) ?? readStoredCurrency();
87
87
  const currencyGiven = flagValue('--currency') !== undefined;
@@ -298,4 +298,4 @@ function relTime(ageMs) {
298
298
  const weeks = Math.floor(days / 7);
299
299
  return `${weeks} week${weeks === 1 ? '' : 's'} ago`;
300
300
  }
301
- export { IS_WINDOWS, RTK_BINARY_NAME, HOME, OMP_AGENT_DIR, OMP_PLUGINS_DIR, BUN_BIN_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_BIN, PACKAGE_VERSION, CAVEMAN_DEFAULTS, PONYTAIL_DEFAULTS, RTK_DEFAULTS, DIAG_SCHEDULES, COMBO_PRESET_MODES, COMBO_DEFAULTS, parseEnum, flagValue, COMMANDS, commandArg, command, settingArg, unknownCommand, install, update, reinstall, showVersion, showHelp, applyUpdate, args, dryRun, yes, verbose, doctor, fix, uninstall, usage, gain, reset, settings, dashboardPort, dashboardOpen, dashboardExport, currency, currencyGiven, removePonytail, keepPonytail, removeRtk, comboDefaultFlag, cavemanDefaultFlag, ponytailDefaultFlag, rtkDefaultFlag, diagScheduleFlag, profileFlagsGiven, debug, execFileP, execP, writeIfChanged, normalizeExtensionsKey, EXTENSIONS_KEY_RE, writeConfigLines, ensureExtensionInConfig, ensureExtensionAfterConfigEntry, readPonytailConfig, parseJsonObject, parsePonytailConfig, patchPonytailConfig, ensurePonytailConfigValue, readPluginsPackage, relTime, };
301
+ export { IS_WINDOWS, RTK_BINARY_NAME, HOME, OMP_AGENT_DIR, OMP_PLUGINS_DIR, BUN_BIN_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_BIN, PACKAGE_VERSION, CAVEMAN_DEFAULTS, PONYTAIL_DEFAULTS, RTK_DEFAULTS, DIAG_SCHEDULES, COMBO_PRESET_MODES, COMBO_DEFAULTS, parseEnum, flagValue, COMMANDS, commandArg, command, settingArg, unknownCommand, install, update, reinstall, showVersion, showHelp, applyUpdate, args, dryRun, yes, verbose, doctor, fix, uninstall, usage, dashboard, reset, settings, dashboardPort, dashboardOpen, dashboardExport, currency, currencyGiven, removePonytail, keepPonytail, removeRtk, comboDefaultFlag, cavemanDefaultFlag, ponytailDefaultFlag, rtkDefaultFlag, diagScheduleFlag, profileFlagsGiven, debug, execFileP, execP, writeIfChanged, normalizeExtensionsKey, EXTENSIONS_KEY_RE, writeConfigLines, ensureExtensionInConfig, ensureExtensionAfterConfigEntry, readPonytailConfig, parseJsonObject, parsePonytailConfig, patchPonytailConfig, ensurePonytailConfigValue, readPluginsPackage, relTime, };
package/cli/currency.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // cli/currency.ts — display-currency helpers (USD-base, offline snapshot rates).
2
- // Same 10 currencies, symbols, and snapshot rates as the gain dashboard's
3
- // converter (dashboard/app.js); the dashboard replaces rates with live
2
+ // Same 10 currencies, symbols, and snapshot rates as the dashboard's
3
+ // converter (dashboard/app); the dashboard replaces rates with live
4
4
  // frankfurter figures when reachable, the CLI always uses the snapshot.
5
5
  import { existsSync, readFileSync } from 'node:fs';
6
6
  import os from 'node:os';
package/cli/dashboard.js CHANGED
@@ -1,5 +1,4 @@
1
- // cli/dashboard.ts — gain dashboard. Serves the single plain-HTML
2
- // template (no deps, no build) plus a /data.json endpoint from the ledger.
1
+ // cli/dashboard.ts — serves the built shadcn Dashboard with local usage APIs.
3
2
  // Binds 127.0.0.1 only; --export writes a file://-ready file instead.
4
3
  // Note: no Promise.withResolvers here — engines still allow Node 20.12,
5
4
  // which lacks it; the listening server itself keeps the process alive.
@@ -16,22 +15,13 @@ import { summarizeUsage } from './usage.js';
16
15
  import { isCurrencyCode } from './currency.js';
17
16
  import { BUN_BIN_DIR, OMP_AGENT_DIR, OMP_PLUGINS_DIR, PACKAGE_VERSION, RTK_BINARY_NAME, } from './common.js';
18
17
  import { storedProfile, writePluginSettings } from './profile.js';
19
- import { withInteractiveSpinner } from './interactive.js';
20
- const DASH_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'dashboard');
21
- const TEMPLATE = path.join(DASH_DIR, 'template.html');
22
- const STYLES = path.join(DASH_DIR, 'styles.css');
23
- const CORE_JS = path.join(DASH_DIR, 'core.js');
24
- const CHARTS_JS = path.join(DASH_DIR, 'charts.js');
25
- const SETTINGS_JS = path.join(DASH_DIR, 'settings.js');
26
- const SHARE_JS = path.join(DASH_DIR, 'share.js');
27
- const BRAND = path.join(DASH_DIR, 'brand.webp');
28
- // shadcn gain app (gain/). When its production bundle exists the server
29
- // binds to it; otherwise it falls back to the legacy dashboard/ segments.
30
- const GAIN_DIST = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'gain', 'dist');
31
- const GAIN_INDEX = path.join(GAIN_DIST, 'index.html');
32
- const GAIN_BRAND = path.join(GAIN_DIST, 'brand.webp');
33
- function useGainBundle() {
34
- return existsSync(GAIN_INDEX);
18
+ const DASHBOARD_DIST = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'dashboard', 'dist');
19
+ const DASHBOARD_INDEX = path.join(DASHBOARD_DIST, 'index.html');
20
+ const DASHBOARD_BRAND = path.join(DASHBOARD_DIST, 'brand.webp');
21
+ function requireDashboardBundle() {
22
+ if (existsSync(DASHBOARD_INDEX))
23
+ return;
24
+ throw new Error('Dashboard bundle missing. Run `bun run build` before using `tersio dashboard`.');
35
25
  }
36
26
  function contentType(file) {
37
27
  if (file.endsWith('.html'))
@@ -53,9 +43,9 @@ function contentType(file) {
53
43
  async function readSegment(file) {
54
44
  return fs.readFile(file, 'utf8');
55
45
  }
56
- async function faviconDataUri() {
57
- const png = await fs.readFile(BRAND);
58
- return `data:image/webp;base64,${png.toString('base64')}`;
46
+ async function brandDataUri() {
47
+ const brand = await fs.readFile(DASHBOARD_BRAND);
48
+ return `data:image/webp;base64,${brand.toString('base64')}`;
59
49
  }
60
50
  function dataJson() {
61
51
  return JSON.stringify(summarizeUsage(readUsage()));
@@ -273,7 +263,7 @@ function readDiagSchedule() {
273
263
  return readDiagReport()?.schedule ?? 'manual';
274
264
  }
275
265
  // Persist the dashboard's picker choice so close → reopen keeps it: each
276
- // `tersio gain` run serves a fresh ephemeral port (a new origin), so the
266
+ // `tersio dashboard` run serves a fresh ephemeral port (a new origin), so the
277
267
  // browser's localStorage alone cannot survive a restart. The stored default
278
268
  // feeds data.json and `tersio usage` on the next run.
279
269
  async function saveDashboardCurrency(raw) {
@@ -310,45 +300,22 @@ function escapeInline(json) {
310
300
  return json.replace(/<\/(script)/gi, '<\\/$1');
311
301
  }
312
302
  async function exportDashboard(exportFile) {
313
- if (useGainBundle()) {
314
- const [bundle, icon] = await Promise.all([readSegment(GAIN_INDEX), faviconDataUri()]);
315
- const inline = bundle
316
- .replace('window.__TERSIO_SNAP = null;', () => `window.__TERSIO_SNAP = ${escapeInline(JSON.stringify({ data: JSON.parse(dataJson()), health: JSON.parse(healthJson()), doctor: getDoctorReport(false) }))};`)
317
- .replace('href="brand.webp"', () => `href="${icon}"`)
318
- .replace(/src="brand.webp"/g, () => `src="${icon}"`);
319
- await fs.writeFile(exportFile, inline, 'utf8');
320
- console.log(`[ok] gain exported → ${exportFile}`);
321
- return;
322
- }
323
- const [template, css, core, charts, settings, share, legacyIcon] = await Promise.all([
324
- readSegment(TEMPLATE), readSegment(STYLES), readSegment(CORE_JS), readSegment(CHARTS_JS),
325
- readSegment(SETTINGS_JS), readSegment(SHARE_JS), faviconDataUri(),
326
- ]);
327
- const inline = template
328
- .replace('<link rel="stylesheet" href="styles.css">', () => `<style>\n${css}</style>`)
329
- .replace('<script src="core.js" defer></script>', () => `<script>\n${core}</script>`)
330
- .replace('<script src="charts.js" defer></script>', () => `<script>\n${charts}</script>`)
331
- .replace('<script src="settings.js" defer></script>', () => `<script>\n${settings}</script>`)
332
- .replace('<script src="share.js" defer></script>', () => `<script>\n${share}</script>`)
333
- .replace('window.__TERSIO_SNAP = null;', () => `window.__TERSIO_SNAP = ${escapeInline(JSON.stringify({ health: JSON.parse(healthJson()), doctor: getDoctorReport(false) }))};`)
334
- .replace("fetch('data.json')", () => `Promise.resolve({ json: function () { return ${escapeInline(dataJson())}; } })`)
335
- // Exported file runs on file:// where load() early-returns before the
336
- // stub above ever runs, so nothing ever renders. Drop that guard in
337
- // the export only; template.html keeps it to avoid 404 polling loops.
338
- .replace("if (window.location.protocol === 'file:') return;", () => `if (false) return;`)
339
- .replace('href="brand.webp"', () => `href="${legacyIcon}"`)
340
- .replace(/src="brand.webp"/g, () => `src="${legacyIcon}"`);
303
+ requireDashboardBundle();
304
+ const [bundle, icon] = await Promise.all([readSegment(DASHBOARD_INDEX), brandDataUri()]);
305
+ const inline = bundle
306
+ .replace('window.__TERSIO_SNAP = null;', () => `window.__TERSIO_SNAP = ${escapeInline(JSON.stringify({ data: JSON.parse(dataJson()), health: JSON.parse(healthJson()), doctor: getDoctorReport(false) }))};`)
307
+ .replace(/href="brand\.webp"/g, () => `href="${icon}"`)
308
+ .replace(/src="brand\.webp"/g, () => `src="${icon}"`)
309
+ .replace('fetch("brand.webp")', () => `Promise.resolve({ ok: true, blob: async () => new Blob([atob("${icon.split(',')[1]}")], { type: "image/webp" }) })`);
341
310
  await fs.writeFile(exportFile, inline, 'utf8');
342
- console.log(`[ok] gain exported → ${exportFile}`);
311
+ console.log(`[ok] Dashboard exported → ${exportFile}`);
343
312
  }
344
313
  async function runDashboard(options) {
345
314
  if (options.exportFile) {
346
315
  await exportDashboard(options.exportFile);
347
316
  return;
348
317
  }
349
- const html = useGainBundle()
350
- ? null
351
- : await withInteractiveSpinner('Loading dashboard template', () => readSegment(TEMPLATE));
318
+ requireDashboardBundle();
352
319
  const server = http.createServer(async (req, res) => {
353
320
  if (req.url === '/reset' && req.method === 'POST') {
354
321
  const rows = clearUsageLedger();
@@ -419,67 +386,29 @@ async function runDashboard(options) {
419
386
  res.end(JSON.stringify(getDoctorReport(fresh)));
420
387
  return;
421
388
  }
422
- if (req.url === '/styles.css') {
423
- res.writeHead(200, { 'Content-Type': 'text/css; charset=utf-8' });
424
- res.end(await readSegment(STYLES));
425
- return;
426
- }
427
- const scripts = {
428
- '/core.js': CORE_JS,
429
- '/charts.js': CHARTS_JS,
430
- '/settings.js': SETTINGS_JS,
431
- '/share.js': SHARE_JS,
432
- };
433
- if (req.url !== undefined && req.url in scripts) {
434
- res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8' });
435
- res.end(await readSegment(scripts[req.url]));
436
- return;
437
- }
438
- if (req.url === '/brand.webp') {
439
- // Prefer the bundled brand; fall back to the legacy dashboard copy.
440
- for (const file of [GAIN_BRAND, BRAND]) {
441
- try {
442
- const png = await fs.readFile(file);
443
- res.writeHead(200, { 'Content-Type': 'image/webp' });
444
- res.end(png);
445
- return;
446
- }
447
- catch { /* try the next copy */ }
448
- }
449
- res.writeHead(404);
450
- res.end();
451
- return;
452
- }
453
- if (useGainBundle()) {
454
- // shadcn bundle: index.html at /, sibling files (brand.webp,
455
- // vite.svg) by path. No path escapes outside gain/dist.
456
- const name = (req.url ?? '/').split('?')[0];
457
- const file = path.normalize(path.join(GAIN_DIST, name === '/' ? 'index.html' : name.slice(1)));
458
- if (file === GAIN_DIST || file.startsWith(GAIN_DIST + path.sep)) {
459
- try {
460
- const body = await fs.readFile(file);
461
- res.writeHead(200, { 'Content-Type': contentType(file) });
462
- res.end(body);
463
- return;
464
- }
465
- catch { /* fall through to index */ }
389
+ const name = (req.url ?? '/').split('?')[0];
390
+ const file = path.normalize(path.join(DASHBOARD_DIST, name === '/' ? 'index.html' : name.slice(1)));
391
+ if (file === DASHBOARD_DIST || file.startsWith(DASHBOARD_DIST + path.sep)) {
392
+ try {
393
+ const body = await fs.readFile(file);
394
+ res.writeHead(200, { 'Content-Type': contentType(file) });
395
+ res.end(body);
396
+ return;
466
397
  }
467
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
468
- res.end(await readSegment(GAIN_INDEX));
469
- return;
398
+ catch { /* fall through to index */ }
470
399
  }
471
400
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
472
- res.end(html ?? '');
401
+ res.end(await readSegment(DASHBOARD_INDEX));
473
402
  });
474
403
  server.listen(options.port, '127.0.0.1', () => {
475
404
  try {
476
- process.title = 'tersio gain';
405
+ process.title = 'tersio dashboard';
477
406
  }
478
407
  catch { /* non-POSIX shells keep node */ }
479
408
  const address = server.address();
480
409
  const port = typeof address === 'object' && address ? address.port : options.port;
481
410
  const url = `http://127.0.0.1:${port}`;
482
- console.log(`[ok] gain live → ${url} (Ctrl-C to stop)`);
411
+ console.log(`[ok] Dashboard live → ${url} (Ctrl-C to stop)`);
483
412
  if (options.open)
484
413
  openBrowser(url);
485
414
  });
package/cli/install.js CHANGED
@@ -484,7 +484,7 @@ async function runCommandMenu() {
484
484
  { value: 'reinstall', label: 'Reinstall', hint: 'clean and reinstall the add-ons, Ponytail package kept' },
485
485
  { value: 'doctor', label: 'Doctor', hint: 'verify the installation' },
486
486
  { value: 'usage', label: 'Usage', hint: 'token usage and savings report' },
487
- { value: 'gain', label: 'Gain dashboard', hint: 'open the report in your browser' },
487
+ { value: 'dashboard', label: 'Dashboard', hint: 'open the report in your browser' },
488
488
  { value: 'reset', label: 'Reset statistics', hint: 'clear statistics; transcripts and RTK history stay' },
489
489
  { value: 'settings', label: 'Settings', hint: 'defaults: combo, caveman, rtk, ponytail, currency' },
490
490
  { value: 'uninstall', label: 'Uninstall', hint: 'remove tersio' },
@@ -545,7 +545,7 @@ async function runCommandMenu() {
545
545
  await runUsage();
546
546
  closeRL();
547
547
  break;
548
- case 'gain':
548
+ case 'dashboard':
549
549
  await runDashboard({ port: dashboardPort, open: true, exportFile: dashboardExport });
550
550
  closeRL();
551
551
  break;
package/cli/profile.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // cli/profile.ts — session-start defaults profile (combo/caveman/rtk/ponytail)
2
- // plus the display-currency default for usage/gain reports.
2
+ // plus the display-currency default for usage/dashboard reports.
3
3
  // Single source for reading/writing the omp plugin settings lock entry.
4
4
  // Extracted from cli/install.ts so both install and settings share it.
5
5
  import { promises as fs } from 'node:fs';
package/cli/settings.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // cli/settings.ts — view/edit session-start defaults (combo/caveman/rtk/ponytail)
2
- // plus the display-currency default for usage/gain reports.
2
+ // plus the display-currency default for usage/dashboard reports.
3
3
  // Non-interactive: `tersio settings --combo-default balanced` (plus per-mode
4
4
  // override flags and --currency). Interactive: clack selects seeded from the
5
5
  // stored profile.
@@ -64,7 +64,7 @@ async function askProfile(current) {
64
64
  if (ponytail.status !== 'selected')
65
65
  return null;
66
66
  next.ponytailDefault = ponytail.value;
67
- const cur = await askInteractiveChoice('Display currency (usage/gain reports)', CURRENCY_CODES.map((v) => ({
67
+ const cur = await askInteractiveChoice('Display currency (usage/dashboard reports)', CURRENCY_CODES.map((v) => ({
68
68
  value: v, label: v,
69
69
  })), next.currency);
70
70
  if (cur.status !== 'selected')
@@ -138,7 +138,7 @@ async function runSingleSetting(name, current, nextDiag) {
138
138
  break;
139
139
  }
140
140
  case 'currency': {
141
- const picked = await askInteractiveChoice('Display currency (usage/gain reports)', CURRENCY_CODES.map((v) => ({
141
+ const picked = await askInteractiveChoice('Display currency (usage/dashboard reports)', CURRENCY_CODES.map((v) => ({
142
142
  value: v, label: v,
143
143
  })), next.currency);
144
144
  if (picked.status !== 'selected')
@@ -162,7 +162,7 @@ async function runSingleSetting(name, current, nextDiag) {
162
162
  }
163
163
  function printSettingsTable(current) {
164
164
  console.log('\n=== Tersio Settings ===');
165
- // Currency lives here no longer: the gain dashboard owns displaying and
165
+ // Currency lives here no longer: the dashboard owns displaying and
166
166
  // persisting it (its picker POSTs to /currency); settings can still set
167
167
  // the stored default via --currency or the prompt below.
168
168
  const rows = [
@@ -239,7 +239,7 @@ async function runSettings() {
239
239
  setDiagSchedule(nextDiag);
240
240
  console.log(` Defaults: ${formatProfile(next)} · diagnosis=${nextDiag}`);
241
241
  console.log(' Applies to fresh sessions only — anything persisted with /combo, /caveman, or /rtk wins.');
242
- console.log(' Currency applies to usage/gain reports; --currency overrides it per run.');
242
+ console.log(' Currency applies to usage/dashboard reports; --currency overrides it per run.');
243
243
  closeRL();
244
244
  }
245
245
  export { runSettings };