@krtclcdy/tersio 2.14.0 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -27
- package/cli/banner.js +9 -2
- package/cli/common.js +17 -10
- package/cli/doctor.js +52 -60
- package/cli/install.js +144 -115
- package/cli/rtk-wiring.js +66 -8
- package/cli/update.js +54 -42
- package/cli/usage.js +2 -1
- package/dashboard/app.js +16 -1
- package/extensions/combo-toggle/index.js +20 -5
- package/extensions/shared/plugin-settings.js +4 -0
- package/extensions/shared/pricing.js +112 -70
- package/extensions/shared/usage-ledger.js +2 -2
- package/extensions/tersio-commands/index.js +15 -79
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,24 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
Terse replies, compact shell output, and minimal code decisions for [Oh My Pi (OMP)](https://github.com/can1357/oh-my-pi) — one-command combo presets.
|
|
12
12
|
|
|
13
|
-
## ✨ Features
|
|
14
|
-
|
|
15
|
-
- **Caveman** — Shortens replies, keeps technical substance. Modes: `lite`, `full`, `ultra`, `wenyan`.
|
|
16
|
-
- **RTK** — Routes noisy shell commands through the RTK binary for compact output; exact bytes bypass it by policy. The installer wires rtk into OMP automatically (`rtk init -g --agent omp`), so bash tool calls rewrite to `rtk` before execution and meter into the gain dashboard.
|
|
17
|
-
- **Ponytail** — Minimal, YAGNI-oriented code decisions, from nudge (`lite`) to requirement-challenger (`ultra`).
|
|
18
|
-
- **Combo** — Toggles all three at once. Presets: `off`, `medium`, `balanced`, `max`.
|
|
19
|
-
- **Updater** — `/tersio check` and `/tersio update` cover Ponytail, RTK, and Caveman in-session, with dry-run and backups.
|
|
20
|
-
- **Gain dashboard** — `tersio gain` serves a local-only savings dashboard: cost with currency conversion, activity graph, command tools, and model breakdowns.
|
|
21
|
-
- **Usage ledger** — `tersio usage` prints a ledger-backed savings report for the machine, local only.
|
|
22
|
-
|
|
23
|
-
## 🧱 Tech Stack
|
|
24
|
-
|
|
25
|
-
- [TypeScript](https://www.typescriptlang.org/) + [Node.js](https://nodejs.org/): CLI and OMP extensions, compiled to dependency-free JS for install.
|
|
26
|
-
- Vanilla dashboard: dependency-free HTML/CSS/JS export — no build step, no runtime deps.
|
|
27
|
-
- [js-tiktoken](https://github.com/dqbd/tiktoken): Real `o200k_base` BPE token counts for benchmarks (dev only).
|
|
28
|
-
- [tokscale](https://github.com/junhoyeo/tokscale): Token-bucket convention (input / output / cache read / cache write) for parsing OMP session transcripts.
|
|
29
|
-
- [EcoLogits](https://github.com/genai-impact/ecologits): CO2 model (v0.8.2 port — per-model params, provider grids, served ÷32 amortization).
|
|
30
|
-
|
|
31
13
|
## ⚡ Getting Started
|
|
32
14
|
|
|
33
15
|
**Default** (macOS/Linux/WSL) — one line. Installs the CLI via npm, then runs the main installer (scope + Combo preset menus) in the same pass:
|
|
@@ -52,10 +34,10 @@ omp plugin features @krtclcdy/tersio --disable rtk
|
|
|
52
34
|
Then restart OMP and enable a preset:
|
|
53
35
|
|
|
54
36
|
```text
|
|
55
|
-
/
|
|
37
|
+
/combo balanced
|
|
56
38
|
```
|
|
57
39
|
|
|
58
|
-
Individual toggles: `/
|
|
40
|
+
Individual toggles: `/caveman full` · `/rtk on` · `/ponytail full`. Everything starts off until you enable it.
|
|
59
41
|
|
|
60
42
|
Session-start defaults (prompted during `install`, or flags):
|
|
61
43
|
|
|
@@ -63,7 +45,7 @@ Session-start defaults (prompted during `install`, or flags):
|
|
|
63
45
|
tersio install --combo-default balanced --caveman-default lite --rtk-default on
|
|
64
46
|
```
|
|
65
47
|
|
|
66
|
-
Defaults apply to fresh sessions only — anything persisted with `/
|
|
48
|
+
Defaults apply to fresh sessions only — anything persisted with `/combo`, `/caveman`, or `/rtk` wins. Stored as plugin settings (`omp plugin config get @krtclcdy/tersio comboDefault`).
|
|
67
49
|
|
|
68
50
|
One-off use without installing:
|
|
69
51
|
|
|
@@ -116,21 +98,21 @@ Flags: `--dry-run`, `--yes`/`-y`, `--verbose`, `--combo-default`/`--caveman-defa
|
|
|
116
98
|
|
|
117
99
|
## ⌨️ Commands reference
|
|
118
100
|
|
|
119
|
-
|
|
101
|
+
Mode switches live on their own commands; bare `/tersio` prints status.
|
|
120
102
|
|
|
121
103
|
| Command | Purpose |
|
|
122
104
|
|---|---|
|
|
123
|
-
| `/
|
|
124
|
-
| `/
|
|
125
|
-
| `/
|
|
126
|
-
| `/
|
|
105
|
+
| `/combo off\|medium\|balanced\|max\|status` | One preset for all three (start here): medium = lite/lite/on, balanced = full/full/on, max = ultra/ultra/on; per-mode tweaks drop to `custom`. |
|
|
106
|
+
| `/caveman lite\|full\|ultra\|wenyan\|off\|status` | Terse replies (substance stays); `ultra` uses fragments only, `wenyan` a compressed classical-Chinese-inspired register. |
|
|
107
|
+
| `/rtk on\|off\|status` | Compact shell output via the real `rtk` binary; exact bytes (checksums, patches) bypass it by policy. |
|
|
108
|
+
| `/ponytail off\|lite\|full\|ultra\|review\|status` | Minimal code (YAGNI): `lite` nudges, `full` enforces, `ultra` challenges the requirement, `review` audits for over-engineering (upstream command). |
|
|
127
109
|
| `/tersio status` | Active modes + combo level; state persists, propagates to subagents, and lights the Combo footer indicator. |
|
|
128
110
|
| `/tersio check` | Add-on version check |
|
|
129
111
|
| `/tersio update <ponytail\|rtk\|caveman\|all> [--dry-run]` | Update add-ons, preview with dry-run |
|
|
130
112
|
| `/tersio gain` | Savings summary; the gain dashboard charts `~/.omp/plugins/tersio-usage.jsonl` (`tersio gain --open` serves on 127.0.0.1, `--export` writes one file). |
|
|
131
113
|
| `/tersio usage` | Ledger report for this machine (rows in `~/.omp/plugins/tersio-usage.jsonl`, local only) |
|
|
132
114
|
| `/tersio help` | This table |
|
|
133
|
-
| `/
|
|
115
|
+
| `/ai-addons` | Add-on updater alias, still works. |
|
|
134
116
|
|
|
135
117
|
## 🗂️ Files and backups
|
|
136
118
|
|
package/cli/banner.js
CHANGED
|
@@ -72,13 +72,20 @@ function bannerLines(tier = bannerTier()) {
|
|
|
72
72
|
return out;
|
|
73
73
|
}
|
|
74
74
|
let shown = false;
|
|
75
|
-
// OMP-style welcome: intro line + note box with art
|
|
75
|
+
// OMP-style welcome: intro line + note box with art left, commands right.
|
|
76
76
|
// No-op unless interactive (and never on --dry-run).
|
|
77
77
|
function printWelcome() {
|
|
78
78
|
if (shown || dryRun || !tty())
|
|
79
79
|
return;
|
|
80
80
|
shown = true;
|
|
81
81
|
clackIntro(`✂ tersio v${PACKAGE_VERSION}`);
|
|
82
|
-
|
|
82
|
+
const tips = [
|
|
83
|
+
'tersio install set up add-ons',
|
|
84
|
+
'tersio update refresh CLI + add-ons',
|
|
85
|
+
'tersio doctor verify the install',
|
|
86
|
+
];
|
|
87
|
+
const art = bannerLines();
|
|
88
|
+
const rows = art.map((line, i) => (i >= 1 && i <= 3 ? `${line} ${tips[i - 1]}` : line));
|
|
89
|
+
clackNote(rows.join('\n'));
|
|
83
90
|
}
|
|
84
91
|
export { bannerLines, bannerTier, printWelcome };
|
package/cli/common.js
CHANGED
|
@@ -104,7 +104,8 @@ async function writeIfChanged(dest, content, options = {}) {
|
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
106
|
if (options.dryRun) {
|
|
107
|
-
|
|
107
|
+
if (verbose)
|
|
108
|
+
console.log(` [dry-run] would write ${dest}`);
|
|
108
109
|
return true;
|
|
109
110
|
}
|
|
110
111
|
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
@@ -113,7 +114,8 @@ async function writeIfChanged(dest, content, options = {}) {
|
|
|
113
114
|
debug(`${path.basename(dest)} → ${path.basename(dest)}.bak`);
|
|
114
115
|
}
|
|
115
116
|
await fs.writeFile(dest, content, 'utf8');
|
|
116
|
-
|
|
117
|
+
if (verbose && !options.quiet)
|
|
118
|
+
console.log(` [write] ${dest}`);
|
|
117
119
|
return true;
|
|
118
120
|
}
|
|
119
121
|
// omp ships 'extensions: null'; appending list items under a null scalar breaks
|
|
@@ -127,11 +129,12 @@ function normalizeExtensionsKey(lines) {
|
|
|
127
129
|
lines[idx] = 'extensions:';
|
|
128
130
|
return true;
|
|
129
131
|
}
|
|
130
|
-
async function writeConfigLines(configPath, lines, logMsg) {
|
|
132
|
+
async function writeConfigLines(configPath, lines, logMsg, options = {}) {
|
|
131
133
|
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
132
134
|
await fs.copyFile(configPath, `${configPath}.bak`).catch(() => { });
|
|
133
135
|
await fs.writeFile(configPath, lines.join('\n'), 'utf8');
|
|
134
|
-
|
|
136
|
+
if (verbose && !options.quiet)
|
|
137
|
+
console.log(logMsg);
|
|
135
138
|
}
|
|
136
139
|
async function ensureExtensionInConfig(configPath, extensionPath, label, options = {}) {
|
|
137
140
|
const normalizedPath = extensionPath.replace(/\\/g, '/');
|
|
@@ -144,7 +147,8 @@ async function ensureExtensionInConfig(configPath, extensionPath, label, options
|
|
|
144
147
|
}
|
|
145
148
|
const extLineIdx = lines.findIndex((l) => EXTENSIONS_KEY_RE.test(l));
|
|
146
149
|
if (options.dryRun) {
|
|
147
|
-
|
|
150
|
+
if (verbose && !options.quiet)
|
|
151
|
+
console.log(` [dry-run] would add ${label} to config.yml`);
|
|
148
152
|
return true;
|
|
149
153
|
}
|
|
150
154
|
if (normalizeExtensionsKey(lines)) {
|
|
@@ -158,7 +162,7 @@ async function ensureExtensionInConfig(configPath, extensionPath, label, options
|
|
|
158
162
|
else {
|
|
159
163
|
lines.splice(extLineIdx + 1, 0, line);
|
|
160
164
|
}
|
|
161
|
-
await writeConfigLines(configPath, lines, ` [write] Added ${label} to config.yml
|
|
165
|
+
await writeConfigLines(configPath, lines, ` [write] Added ${label} to config.yml`, options);
|
|
162
166
|
return true;
|
|
163
167
|
}
|
|
164
168
|
async function ensureExtensionAfterConfigEntry(configPath, extensionPath, afterPath, label, options = {}) {
|
|
@@ -172,7 +176,8 @@ async function ensureExtensionAfterConfigEntry(configPath, extensionPath, afterP
|
|
|
172
176
|
if (existingIndex !== -1 && afterIndex !== -1 && existingIndex === afterIndex + 1)
|
|
173
177
|
return false;
|
|
174
178
|
if (options.dryRun) {
|
|
175
|
-
|
|
179
|
+
if (verbose && !options.quiet)
|
|
180
|
+
console.log(` [dry-run] would place ${label} after Ponytail in config.yml`);
|
|
176
181
|
return true;
|
|
177
182
|
}
|
|
178
183
|
if (existingIndex !== -1)
|
|
@@ -191,7 +196,7 @@ async function ensureExtensionAfterConfigEntry(configPath, extensionPath, afterP
|
|
|
191
196
|
lines.splice(extensionsIndex + 1, 0, line);
|
|
192
197
|
}
|
|
193
198
|
}
|
|
194
|
-
await writeConfigLines(configPath, lines, ` [write] Placed ${label} after Ponytail in config.yml
|
|
199
|
+
await writeConfigLines(configPath, lines, ` [write] Placed ${label} after Ponytail in config.yml`, options);
|
|
195
200
|
return true;
|
|
196
201
|
}
|
|
197
202
|
function readPonytailConfig() {
|
|
@@ -219,7 +224,8 @@ function parsePonytailConfig(existing) {
|
|
|
219
224
|
async function patchPonytailConfig(apply, dryRunNote, writeNote, options = {}) {
|
|
220
225
|
const config = readPonytailConfig();
|
|
221
226
|
if (options.dryRun) {
|
|
222
|
-
|
|
227
|
+
if (verbose && !options.quiet)
|
|
228
|
+
console.log(` [dry-run] ${dryRunNote}`);
|
|
223
229
|
return;
|
|
224
230
|
}
|
|
225
231
|
const cfg = parsePonytailConfig(await readTextIfExists(config.path));
|
|
@@ -229,7 +235,8 @@ async function patchPonytailConfig(apply, dryRunNote, writeNote, options = {}) {
|
|
|
229
235
|
}
|
|
230
236
|
await fs.mkdir(config.dir, { recursive: true });
|
|
231
237
|
await fs.writeFile(config.path, JSON.stringify(cfg, null, 2) + '\n', 'utf8');
|
|
232
|
-
|
|
238
|
+
if (verbose && !options.quiet)
|
|
239
|
+
console.log(` [write] ${writeNote}`);
|
|
233
240
|
}
|
|
234
241
|
async function ensurePonytailConfigValue(key, value, options = {}) {
|
|
235
242
|
await patchPonytailConfig((cfg) => {
|
package/cli/doctor.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// cli/doctor.ts — installation health checks.
|
|
2
2
|
import { promises as fs } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { BUN_BIN_DIR,
|
|
4
|
+
import { BUN_BIN_DIR, OMP_AGENT_DIR, OMP_PLUGINS_DIR, OMP_BIN, PACKAGE_NAME, PACKAGE_VERSION, RTK_BINARY_NAME, execP, parseJsonObject, relTime, } from './common.js';
|
|
5
5
|
import { runInteractivePhase } from './interactive.js';
|
|
6
6
|
import { ledgerPath, readUsage, sessionsDir } from '../extensions/shared/usage-ledger.js';
|
|
7
7
|
import { readRtkGain, rtkDbPath } from '../extensions/shared/rtk-gain.js';
|
|
@@ -56,50 +56,53 @@ async function runDoctor() {
|
|
|
56
56
|
const err = e;
|
|
57
57
|
return err.stdout?.trim() || err.stderr?.trim() || null;
|
|
58
58
|
}));
|
|
59
|
-
const [ompVersion, agentEntries, extEntries, sharedStateText, configText, updateVersion] = await runInteractivePhase('Checking
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
const [[ompVersion, agentEntries, extEntries, sharedStateText, configText, updateVersion], [cavemanIndexText, rtkIndexText, updaterIndexText, comboIndexText, tersioIndexText, modeReinforcementText, ponytailPkgText, ponytailExtText, pluginsPkgRaw, selfPkgText], [cavemanRuleText, ruleMtime, rtkBinText, rtkMtime, rtkVersion, rtkOmpText, ponytailMtime]] = await runInteractivePhase('Checking installation', () => Promise.all([
|
|
60
|
+
Promise.all([
|
|
61
|
+
probes.ompVersion,
|
|
62
|
+
probes.agentEntries,
|
|
63
|
+
probes.extEntries,
|
|
64
|
+
probes.sharedStateText,
|
|
65
|
+
probes.configText,
|
|
66
|
+
probes.updateVersion,
|
|
67
|
+
]),
|
|
68
|
+
Promise.all([
|
|
69
|
+
probes.cavemanIndexText,
|
|
70
|
+
probes.rtkIndexText,
|
|
71
|
+
probes.updaterIndexText,
|
|
72
|
+
probes.comboIndexText,
|
|
73
|
+
probes.tersioIndexText,
|
|
74
|
+
probes.modeReinforcementText,
|
|
75
|
+
probes.ponytailPkgText,
|
|
76
|
+
probes.ponytailExtText,
|
|
77
|
+
probes.pluginsPkgRaw,
|
|
78
|
+
probes.selfPkgText,
|
|
79
|
+
]),
|
|
80
|
+
Promise.all([
|
|
81
|
+
probes.cavemanRuleText,
|
|
82
|
+
probes.ruleMtime,
|
|
83
|
+
probes.rtkBinText,
|
|
84
|
+
probes.rtkMtime,
|
|
85
|
+
rtkVersionProbe,
|
|
86
|
+
probes.rtkOmpText,
|
|
87
|
+
probes.ponytailMtime,
|
|
88
|
+
]),
|
|
66
89
|
]));
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
probes.rtkIndexText,
|
|
70
|
-
probes.updaterIndexText,
|
|
71
|
-
probes.comboIndexText,
|
|
72
|
-
probes.tersioIndexText,
|
|
73
|
-
probes.modeReinforcementText,
|
|
74
|
-
probes.ponytailPkgText,
|
|
75
|
-
probes.ponytailExtText,
|
|
76
|
-
probes.pluginsPkgRaw,
|
|
77
|
-
probes.selfPkgText,
|
|
78
|
-
]));
|
|
79
|
-
const [cavemanRuleText, ruleMtime, rtkBinText, rtkMtime, rtkVersion, rtkOmpText, ponytailMtime] = await runInteractivePhase('Checking add-ons', () => Promise.all([
|
|
80
|
-
probes.cavemanRuleText,
|
|
81
|
-
probes.ruleMtime,
|
|
82
|
-
probes.rtkBinText,
|
|
83
|
-
probes.rtkMtime,
|
|
84
|
-
rtkVersionProbe,
|
|
85
|
-
probes.rtkOmpText,
|
|
86
|
-
probes.ponytailMtime,
|
|
87
|
-
]));
|
|
88
|
-
// Categorized output with a tally; section headers group related probes.
|
|
90
|
+
// Categorized output with a tally; success rows stay quiet (no path echoes)
|
|
91
|
+
// while failures print the expected path or fix so they stay actionable.
|
|
89
92
|
const tally = { ok: 0, missing: 0, warn: 0 };
|
|
90
93
|
function check(label, ok, detail = '') {
|
|
91
94
|
if (ok) {
|
|
92
95
|
tally.ok++;
|
|
93
|
-
console.log(` ${label}: ok${detail ? ` ${detail}` : ''}`);
|
|
96
|
+
console.log(` ✅ ${label}: ok${detail ? ` ${detail}` : ''}`);
|
|
94
97
|
}
|
|
95
98
|
else {
|
|
96
99
|
tally.missing++;
|
|
97
|
-
console.log(` ${label}: MISSING${detail ? ` ${detail}` : ''}`);
|
|
100
|
+
console.log(` ❌ ${label}: MISSING${detail ? ` ${detail}` : ''}`);
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
103
|
function warnLine(label, detail) {
|
|
101
104
|
tally.warn++;
|
|
102
|
-
console.log(` ${label}: warn ${detail}`);
|
|
105
|
+
console.log(` ⚠️ ${label}: warn ${detail}`);
|
|
103
106
|
}
|
|
104
107
|
section('Environment');
|
|
105
108
|
check('Node', true, process.version);
|
|
@@ -110,33 +113,27 @@ async function runDoctor() {
|
|
|
110
113
|
warnLine('Tersio CLI', `${PACKAGE_VERSION} (version check unreachable)`);
|
|
111
114
|
else
|
|
112
115
|
check('Tersio CLI', true, PACKAGE_VERSION);
|
|
113
|
-
console.log(` Home: ${HOME}`);
|
|
114
116
|
section('Installation');
|
|
115
|
-
check('OMP agent dir', agentEntries !== null, agentDir);
|
|
116
|
-
check('OMP extensions dir', extEntries !== null, extDir);
|
|
117
|
-
check('OMP config.yml', configText !== null, configPath);
|
|
117
|
+
check('OMP agent dir', agentEntries !== null, agentEntries === null ? agentDir : '');
|
|
118
|
+
check('OMP extensions dir', extEntries !== null, extEntries === null ? extDir : '');
|
|
119
|
+
check('OMP config.yml', configText !== null, configText === null ? configPath : '');
|
|
118
120
|
check('Shared session bridge', sharedStateText !== null);
|
|
119
|
-
section('Extensions');
|
|
121
|
+
section('Extensions & plugins');
|
|
120
122
|
check('Caveman extension', cavemanIndexText !== null);
|
|
121
123
|
check('RTK extension', rtkIndexText !== null);
|
|
122
124
|
check('Updater extension', updaterIndexText !== null);
|
|
123
125
|
check('Combo extension', comboIndexText !== null);
|
|
124
126
|
check('Tersio commands extension', tersioIndexText !== null);
|
|
125
127
|
check('Mode reinforcement extension', modeReinforcementText !== null);
|
|
126
|
-
section('Plugins');
|
|
127
|
-
check('Ponytail package', ponytailPkgText !== null);
|
|
128
128
|
check('Ponytail extension', ponytailExtText !== null);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
check('Combo in config.yml', configText.includes('combo-toggle'));
|
|
132
|
-
}
|
|
129
|
+
check('Ponytail in config.yml', (configText ?? '').includes('ponytail') && (configText ?? '').includes('pi-extension'));
|
|
130
|
+
check('Combo in config.yml', (configText ?? '').includes('combo-toggle'));
|
|
133
131
|
check('Self plugin package', selfPkgText !== null, parseJsonObject(selfPkgText)?.version ?? '');
|
|
134
132
|
const selfDep = PACKAGE_NAME in (parseJsonObject(pluginsPkgRaw)?.dependencies || {});
|
|
135
133
|
check('Self plugin in plugins/package.json', selfDep);
|
|
136
|
-
section('Usage');
|
|
134
|
+
section('Usage & records');
|
|
137
135
|
const usageRows = readUsage();
|
|
138
|
-
check('Usage ledger',
|
|
139
|
-
section('Records');
|
|
136
|
+
check('Usage ledger', true, usageRows.length ? `${usageRows.length} rows` : 'empty — no records yet');
|
|
140
137
|
console.log(` Usage ledger (tersio-owned, tersio reset clears): ${ledgerPath()} · ${usageRows.length} rows`);
|
|
141
138
|
console.log(` Session transcripts (host-owned, never touched): ${sessionsDir()}`);
|
|
142
139
|
const rtkDb = rtkDbPath();
|
|
@@ -146,22 +143,17 @@ async function runDoctor() {
|
|
|
146
143
|
const ruleAge = ruleMtime ? `updated ${relTime(Date.now() - ruleMtime.mtimeMs)}` : '';
|
|
147
144
|
check('Caveman rule', cavemanRuleText !== null, ruleAge);
|
|
148
145
|
const rtkAge = rtkMtime ? `updated ${relTime(Date.now() - rtkMtime.mtimeMs)}` : '';
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (rtkOmpText === null)
|
|
157
|
-
check('RTK OMP wiring (rtk.ts)', false, `run: rtk init -g --agent omp`);
|
|
158
|
-
else
|
|
159
|
-
check('RTK OMP wiring (rtk.ts)', true, path.join(extDir, 'rtk.ts'));
|
|
160
|
-
}
|
|
146
|
+
check('RTK binary', rtkBinText !== null, rtkBinText === null ? rtkBin : [rtkVersion, rtkAge].filter(Boolean).join(', '));
|
|
147
|
+
if (rtkBinText !== null && !rtkVersion)
|
|
148
|
+
warnLine('RTK version', 'unavailable — binary may not be executable');
|
|
149
|
+
const rtkRegistered = rtkOmpText !== null && (configText ?? '').includes('extensions/rtk.ts');
|
|
150
|
+
check('RTK OMP wiring (rtk.ts)', rtkOmpText !== null, rtkOmpText === null ? 'run: rtk init -g --agent omp' : '');
|
|
151
|
+
if (rtkOmpText !== null && !rtkRegistered)
|
|
152
|
+
warnLine('RTK in config.yml', 'rtk.ts not listed — OMP will not load it; rerun install');
|
|
161
153
|
const ponytailAge = ponytailMtime ? `updated ${relTime(Date.now() - ponytailMtime.mtimeMs)}` : '';
|
|
162
154
|
check('Ponytail', ponytailPkgText !== null, [parseJsonObject(ponytailPkgText)?.version ?? '', ponytailAge].filter(Boolean).join(', '));
|
|
163
155
|
const total = tally.ok + tally.missing + tally.warn;
|
|
164
|
-
console.log(`\n Summary: ${total} checks — ${tally.ok} ok, ${tally.warn} warn, ${tally.missing} missing`);
|
|
156
|
+
console.log(`\n Summary: ${total} checks — ✅ ${tally.ok} ok, ⚠️ ${tally.warn} warn, ❌ ${tally.missing} missing`);
|
|
165
157
|
}
|
|
166
158
|
function section(name) {
|
|
167
159
|
console.log(`\n${name}`);
|