@krtclcdy/tersio 2.5.0 → 2.7.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.
@@ -0,0 +1,171 @@
1
+ // /tersio unified root command — routes every tersio capability through one
2
+ // namespaced entrypoint. Aliases (/caveman, /rtk, /combo, /ai-addons) keep
3
+ // working: this router drives the same shared bridge + session entries the
4
+ // sibling extensions restore from, then reloads so their local mirrors sync
5
+ // (same mechanism /combo already uses to drive its siblings).
6
+ import { COMBO_LEVELS, activeModesSummary, getSharedComboState, normalizeComboLevel, normalizeMode, reconcileSharedComboEntries, sessionEntries, setSharedComboLevel, setSharedComboMode, } from '../shared/session-state.js';
7
+ import os from 'node:os';
8
+ import path from 'node:path';
9
+ import { appendUsage, readUsage } from '../shared/usage-ledger.js';
10
+ import { checkAddonsSummary, runAddonUpdate } from '../ai-addons-updater/index.js';
11
+ const HELP = [
12
+ '/tersio caveman lite|full|ultra|wenyan|off — terse-reply mode',
13
+ '/tersio combo off|medium|balanced|max — preset for all three modes',
14
+ '/tersio rtk on|off — compact shell output',
15
+ '/tersio ponytail off|lite|full|ultra|review — minimal code',
16
+ '/tersio status — active modes + combo level',
17
+ '/tersio check — add-on version check',
18
+ '/tersio update <ponytail|rtk|caveman|all> [--dry-run]',
19
+ '/tersio gain — savings summary + dashboard hint',
20
+ '/tersio usage — ledger report for this machine',
21
+ '/tersio help — this table',
22
+ 'aliases (still work): /caveman /rtk /combo /ai-addons',
23
+ ].join('\n');
24
+ function statusLine() {
25
+ const s = getSharedComboState();
26
+ const level = s.level === 'custom' ? 'INACTIVE' : s.level.toUpperCase();
27
+ return `tersio — caveman=${s.caveman.toUpperCase()} · rtk=${s.rtk.toUpperCase()} · ponytail=${s.ponytail.toUpperCase()} (combo ${level})`;
28
+ }
29
+ function usageSummary() {
30
+ const rows = readUsage();
31
+ if (rows.length === 0)
32
+ return 'tersio usage: no ledger yet — run commands first.';
33
+ const byKind = {};
34
+ for (const r of rows)
35
+ byKind[r.kind] = (byKind[r.kind] ?? 0) + 1;
36
+ const parts = Object.entries(byKind).map(([k, n]) => `${k}×${n}`);
37
+ const last = new Date(rows[rows.length - 1].ts).toISOString();
38
+ return `tersio usage: ${rows.length} rows (${parts.join(', ')}), last write ${last}.`;
39
+ }
40
+ // Gain opens instantaneously in the default browser: export a file://-ready
41
+ // snapshot (data inlined, no server to babysit) and open it. Falls back to
42
+ // the shell command when export or open fails.
43
+ async function openGainDashboard(pi, ctx) {
44
+ const file = path.join(os.tmpdir(), `tersio-gain-${Date.now()}.html`);
45
+ try {
46
+ const exported = await pi.exec?.('tersio', ['dashboard', '--export', file], { cwd: ctx?.cwd });
47
+ if (!exported || exported.code !== 0)
48
+ throw new Error((exported?.stderr || 'export failed').trim());
49
+ const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
50
+ await pi.exec?.(openCmd, [file], { cwd: ctx?.cwd });
51
+ ctx?.ui?.notify?.(`tersio gain: dashboard opened in your browser.`, 'info');
52
+ }
53
+ catch (e) {
54
+ ctx?.ui?.notify?.(`tersio gain: could not open dashboard (${e.message}). Run 'tersio dashboard --open' in a shell.`, 'warning');
55
+ }
56
+ }
57
+ export default function tersioCommandsExtension(pi) {
58
+ pi.setLabel?.('Tersio unified root command');
59
+ async function reload(ctx) {
60
+ if (ctx?.reload)
61
+ await ctx.reload();
62
+ }
63
+ pi.registerCommand?.('tersio', {
64
+ description: 'Tersio root: caveman|combo|rtk|ponytail|status|check|update|gain|usage|help',
65
+ handler: async (args, ctx) => {
66
+ const parts = String(args || '').trim().toLowerCase().split(/\s+/).filter(Boolean);
67
+ const [sub, rest] = [parts[0], parts.slice(1).join(' ')];
68
+ appendUsage('command', `/tersio ${parts.join(' ')}`.trim());
69
+ if (!sub || sub === 'status') {
70
+ if (ctx?.hasUI)
71
+ reconcileSharedComboEntries(sessionEntries(ctx));
72
+ ctx?.ui?.notify?.(statusLine(), 'info');
73
+ return;
74
+ }
75
+ if (sub === 'help') {
76
+ ctx?.ui?.notify?.(HELP, 'info');
77
+ return;
78
+ }
79
+ if (sub === 'caveman') {
80
+ const mode = normalizeMode('caveman', rest);
81
+ if (!mode) {
82
+ ctx?.ui?.notify?.('Usage: /tersio caveman [lite|full|ultra|wenyan|off|status]', 'warning');
83
+ return;
84
+ }
85
+ pi.appendEntry?.('caveman-mode', { mode });
86
+ setSharedComboMode('caveman', mode);
87
+ appendUsage('toggle', `caveman=${mode}`);
88
+ ctx?.ui?.notify?.(`Caveman ${mode} on — Active: ${activeModesSummary(getSharedComboState())}.`, 'info');
89
+ await reload(ctx);
90
+ return;
91
+ }
92
+ if (sub === 'rtk') {
93
+ if (!rest || rest === 'status') {
94
+ const on = getSharedComboState().rtk === 'on';
95
+ ctx?.ui?.notify?.(`RTK: ${on ? 'on' : 'off'}`, 'info');
96
+ return;
97
+ }
98
+ if (!['on', 'off'].includes(rest)) {
99
+ ctx?.ui?.notify?.('Usage: /tersio rtk [on|off|status]', 'warning');
100
+ return;
101
+ }
102
+ const enabled = rest === 'on';
103
+ pi.appendEntry?.('rtk-mode', { enabled });
104
+ setSharedComboMode('rtk', enabled);
105
+ appendUsage('toggle', `rtk=${rest}`);
106
+ ctx?.ui?.notify?.(`RTK ${rest} — Active: ${activeModesSummary(getSharedComboState())}.`, 'info');
107
+ await reload(ctx);
108
+ return;
109
+ }
110
+ if (sub === 'ponytail') {
111
+ const mode = normalizeMode('ponytail', rest);
112
+ if (!mode) {
113
+ ctx?.ui?.notify?.('Usage: /tersio ponytail [off|lite|full|ultra|review|status]', 'warning');
114
+ return;
115
+ }
116
+ pi.appendEntry?.('ponytail-mode', { mode });
117
+ setSharedComboMode('ponytail', mode);
118
+ appendUsage('toggle', `ponytail=${mode}`);
119
+ ctx?.ui?.notify?.(`Ponytail ${mode} — Active: ${activeModesSummary(getSharedComboState())}.`, 'info');
120
+ await reload(ctx);
121
+ return;
122
+ }
123
+ if (sub === 'combo') {
124
+ if (!rest || rest === 'status') {
125
+ if (ctx?.hasUI)
126
+ reconcileSharedComboEntries(sessionEntries(ctx));
127
+ ctx?.ui?.notify?.(statusLine(), 'info');
128
+ return;
129
+ }
130
+ const level = normalizeComboLevel(rest);
131
+ if (!level) {
132
+ ctx?.ui?.notify?.('Unknown combo level. Use: off | medium | balanced | max', 'warning');
133
+ return;
134
+ }
135
+ const modes = COMBO_LEVELS[level];
136
+ pi.appendEntry?.('caveman-mode', { mode: modes.caveman });
137
+ pi.appendEntry?.('rtk-mode', { enabled: modes.rtk === 'on' });
138
+ pi.appendEntry?.('ponytail-mode', { mode: modes.ponytail });
139
+ pi.appendEntry?.('combo-level', { level });
140
+ setSharedComboLevel(level);
141
+ appendUsage('toggle', `combo=${level}`);
142
+ ctx?.ui?.notify?.(`Combo ${level} on — ${activeModesSummary(getSharedComboState())} active for this session.`, 'info');
143
+ await reload(ctx);
144
+ return;
145
+ }
146
+ if (sub === 'check') {
147
+ await checkAddonsSummary(ctx);
148
+ return;
149
+ }
150
+ if (sub === 'update') {
151
+ const dryRun = parts.includes('--dry-run');
152
+ const target = parts.filter((p) => p !== '--dry-run' && p !== 'update' && p !== 'tersio').join(' ');
153
+ if (!target) {
154
+ ctx?.ui?.notify?.('Usage: /tersio update <ponytail|rtk|caveman|all> [--dry-run]', 'warning');
155
+ return;
156
+ }
157
+ await runAddonUpdate(pi, ctx, target, dryRun);
158
+ return;
159
+ }
160
+ if (sub === 'gain') {
161
+ await openGainDashboard(pi, ctx);
162
+ return;
163
+ }
164
+ if (sub === 'usage') {
165
+ ctx?.ui?.notify?.(usageSummary(), 'info');
166
+ return;
167
+ }
168
+ ctx?.ui?.notify?.(`Unknown subcommand: ${sub}.\n${HELP}`, 'warning');
169
+ },
170
+ });
171
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krtclcdy/tersio",
3
- "version": "2.5.0",
3
+ "version": "2.7.0",
4
4
  "description": "✂️ Token-saving OMP add-ons: terse Caveman replies, compact RTK shell output, lean Ponytail code calls — one-command combo presets.",
5
5
  "keywords": [
6
6
  "omp",
@@ -60,6 +60,13 @@
60
60
  "extensions": [
61
61
  "./extensions/ai-addons-updater/index.js"
62
62
  ]
63
+ },
64
+ "tersio": {
65
+ "description": "Unified /tersio root command (caveman, combo, rtk, ponytail, status, check, update, gain, usage)",
66
+ "default": true,
67
+ "extensions": [
68
+ "./extensions/tersio-commands/index.js"
69
+ ]
63
70
  }
64
71
  },
65
72
  "settings": {
@@ -108,6 +115,8 @@
108
115
  "files": [
109
116
  "tersio.js",
110
117
  "cli/**/*.js",
118
+ "cli/dashboard.html",
119
+ "cli/brand.webp",
111
120
  "extensions/**/*.js"
112
121
  ],
113
122
  "scripts": {
package/tersio.js CHANGED
@@ -2,12 +2,14 @@
2
2
  // tersio.ts — Install Tersio (caveman/rtk/ponytail) add-ons on any OMP device.
3
3
  // Usage: node tersio.js [install|update|reinstall|doctor|uninstall|version|help] [options]
4
4
  // Requires: node/npm and omp CLI
5
- import { PACKAGE_BIN, PACKAGE_VERSION, applyUpdate, commandArg, doctor, showHelp, showVersion, uninstall, unknownCommand, update, } from './cli/common.js';
5
+ import { PACKAGE_BIN, PACKAGE_VERSION, applyUpdate, commandArg, dashboard, dashboardExport, dashboardOpen, dashboardPort, doctor, showHelp, showVersion, uninstall, unknownCommand, update, usage, } from './cli/common.js';
6
6
  import { closeRL } from './cli/interactive.js';
7
7
  import { runInstall } from './cli/install.js';
8
8
  import { runDoctor } from './cli/doctor.js';
9
9
  import { runLatestUpdate } from './cli/update.js';
10
10
  import { runUninstall } from './cli/uninstall.js';
11
+ import { runUsage } from './cli/usage.js';
12
+ import { runDashboard } from './cli/dashboard.js';
11
13
  function printHelp() {
12
14
  console.log(`Usage: ${PACKAGE_BIN} [command] [options]
13
15
 
@@ -16,6 +18,8 @@ Commands:
16
18
  update Refresh the CLI and add-ons (RTK binary, Caveman rule, Ponytail)
17
19
  reinstall Clean and reinstall the user-scope add-ons
18
20
  doctor Check the current installation
21
+ usage Ledger-backed usage + savings report
22
+ dashboard Serve the gain dashboard (localhost only)
19
23
  uninstall Remove the managed extensions
20
24
  version Print the package version
21
25
  help Show this help
@@ -31,6 +35,9 @@ Options:
31
35
  --yes, -y
32
36
  --dry-run
33
37
  --verbose
38
+ --port <n> (dashboard: pin port, default ephemeral)
39
+ --open (dashboard: open browser)
40
+ --export <file> (dashboard: write HTML file instead of serving)
34
41
  --version, -v
35
42
  --help, -h`);
36
43
  }
@@ -62,6 +69,16 @@ async function main() {
62
69
  closeRL();
63
70
  return;
64
71
  }
72
+ if (usage) {
73
+ await runUsage();
74
+ closeRL();
75
+ return;
76
+ }
77
+ if (dashboard) {
78
+ await runDashboard({ port: dashboardPort, open: dashboardOpen, exportFile: dashboardExport });
79
+ closeRL();
80
+ return;
81
+ }
65
82
  if (uninstall) {
66
83
  await runUninstall();
67
84
  closeRL();