@krtclcdy/tersio 2.0.0 → 2.1.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 +13 -11
- package/extensions/caveman-session/index.js +6 -1
- package/extensions/combo-toggle/index.js +9 -8
- package/extensions/rtk-session/index.js +6 -1
- package/package.json +1 -1
- package/tersio.js +110 -54
package/README.md
CHANGED
|
@@ -61,26 +61,28 @@ User-level installs also register the package in `~/.omp/plugins` (visible in OM
|
|
|
61
61
|
|
|
62
62
|
## Benchmarks
|
|
63
63
|
|
|
64
|
-
Measured
|
|
64
|
+
Measured savings against the same workload without the modes. Token counts are real `o200k_base` BPE tokens (js-tiktoken), not chars÷4 estimates. Sample protocol, per-sample data, and caveats in [BENCHMARK.md](./BENCHMARK.md).
|
|
65
65
|
|
|
66
|
-
|
|
|
67
|
-
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
66
|
+
| Measured surface (n) | Baseline → Tersio | Δ |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| Terse reply, caveman lite — BPE tok (3 samples, p50) | 146 → 73 tok | **−52.6%** |
|
|
69
|
+
| Terse reply, caveman full — BPE tok (3 samples, p50) | 154 → 58 tok | **−62.3%** |
|
|
70
|
+
| Terse reply, caveman ultra — BPE tok (3 samples, p50) | 146 → 33 tok | **−78.6%** |
|
|
71
|
+
| Code diff, ponytail minimal-ladder — BPE tok (2 tasks) | 261–481 → 108–143 tok | **−58.6…−70.3%** |
|
|
72
|
+
| Shell output, rtk — `git status` — BPE tok | 32 → 21 tok | **−34.4%** |
|
|
73
|
+
| Shell output, rtk — repo `grep` — BPE tok | 537 → 469 tok | **−12.7%** |
|
|
74
|
+
|
|
75
|
+
Break-even math, balanced preset (caveman full + rtk + ponytail): `⌈overhead ÷ saving-per-turn⌉ = ⌈480 ÷ 306⌉ = 2 turns`, where a mixed turn (reply + `git status`-class command + code task) costs 944 tok baseline vs 638 tok with Tersio — a 0.68× ratio. Every turn after turn 2 nets ≈ −32%. RTK keeps diffs and failing-test output exact by design (−0.4% and −1.5% there), concentrating savings where noise lives.
|
|
74
76
|
|
|
75
77
|
## CLI
|
|
76
78
|
|
|
77
79
|
| Command | Purpose |
|
|
78
80
|
|---|---|
|
|
79
81
|
| `tersio install` | Install (user scope by default; `--scope project\|both` for more) |
|
|
80
|
-
| `tersio update` | Refresh
|
|
82
|
+
| `tersio update` | Refresh the CLI, extensions, and add-ons (RTK binary, Caveman rule, Ponytail) |
|
|
81
83
|
| `tersio reinstall` | Fresh install, preserving the Ponytail package |
|
|
82
84
|
| `tersio doctor` | Check OMP, extension, Ponytail, and RTK health |
|
|
83
|
-
| `tersio uninstall` | Remove extensions and
|
|
85
|
+
| `tersio uninstall` | Remove extensions, registration, and the Ponytail plugin (`--keep-ponytail` keeps Ponytail; `--remove-rtk` also removes the RTK binary) |
|
|
84
86
|
| `tersio version` | Print version |
|
|
85
87
|
|
|
86
88
|
Flags: `--dry-run`, `--yes`/`-y`, `--verbose`, `--scope`, `--combo-default`/`--caveman-default`/`--rtk-default`/`--ponytail-default`. Legacy `--doctor` / `--uninstall` forms still work.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
|
-
import { asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, normalizeMode, paintStatusBar, sessionEntries, setSharedComboMode } from '../shared/session-state.js';
|
|
4
|
+
import { asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, normalizeMode, paintStatusBar, reconcileSharedComboEntries, sessionEntries, setSharedComboMode } from '../shared/session-state.js';
|
|
5
5
|
import { readCavemanDefault } from '../shared/plugin-settings.js';
|
|
6
6
|
const CAVERN_DIR = dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
const RULE_PATH = join(CAVERN_DIR, 'rule.md');
|
|
@@ -96,6 +96,11 @@ export default function cavemanSessionExtension(pi) {
|
|
|
96
96
|
});
|
|
97
97
|
function restoreMode(ctx) {
|
|
98
98
|
const entries = sessionEntries(ctx);
|
|
99
|
+
// Publish the persisted combo state (incl. combo-level) before painting:
|
|
100
|
+
// bar suppression reads the in-process bridge, which is empty in a fresh
|
|
101
|
+
// host until the combo extension reconciles — and its reconcile is
|
|
102
|
+
// UI-gated. Deriving it here makes suppression independent of load order.
|
|
103
|
+
reconcileSharedComboEntries(entries);
|
|
99
104
|
// Persisted session state wins; a fresh session falls back to the
|
|
100
105
|
// installer/user-configured default (off unless configured).
|
|
101
106
|
const persisted = resolveMode(entries, '');
|
|
@@ -29,7 +29,6 @@ function loadPonytailInstructions(mode) {
|
|
|
29
29
|
}
|
|
30
30
|
export default function comboToggleExtension(pi) {
|
|
31
31
|
pi.setLabel?.('Combo session toggle (all 3 add-ons)');
|
|
32
|
-
let currentState = getSharedComboState();
|
|
33
32
|
let lastCtx = undefined;
|
|
34
33
|
function syncStatus(ctx) {
|
|
35
34
|
if (ctx)
|
|
@@ -43,11 +42,14 @@ export default function comboToggleExtension(pi) {
|
|
|
43
42
|
c.ui.setStatus('combo', undefined);
|
|
44
43
|
return;
|
|
45
44
|
}
|
|
45
|
+
// Read the live bridge, not cached extension state: sibling extensions
|
|
46
|
+
// reconcile it from persisted entries before this one runs.
|
|
47
|
+
const state = getSharedComboState();
|
|
46
48
|
const theme = c.ui.theme;
|
|
47
|
-
const c0 =
|
|
48
|
-
const r =
|
|
49
|
-
const p =
|
|
50
|
-
const lvl =
|
|
49
|
+
const c0 = state.caveman.toUpperCase();
|
|
50
|
+
const r = state.rtk.toUpperCase();
|
|
51
|
+
const p = state.ponytail.toUpperCase();
|
|
52
|
+
const lvl = state.level.toUpperCase();
|
|
51
53
|
const label = `combo ${lvl}: 🪨caveman=${c0} ⚡rtk=${r} 🦥ponytail=${p}`;
|
|
52
54
|
c.ui.setStatus('combo', theme?.fg ? `${theme.fg('accent', '🧩')} ${theme.fg('muted', label)}` : `🧩 ${label}`);
|
|
53
55
|
// Clobber any sibling bars another extension may have painted in a race
|
|
@@ -67,13 +69,12 @@ export default function comboToggleExtension(pi) {
|
|
|
67
69
|
pi.appendEntry?.('combo-level', { level });
|
|
68
70
|
}
|
|
69
71
|
function useState(state, ctx) {
|
|
70
|
-
currentState = state;
|
|
71
72
|
syncStatus(ctx);
|
|
72
73
|
return state;
|
|
73
74
|
}
|
|
74
75
|
function reconcile(ctx) {
|
|
75
76
|
if (!ctx?.hasUI)
|
|
76
|
-
return
|
|
77
|
+
return getSharedComboState();
|
|
77
78
|
return useState(reconcileSharedComboEntries(sessionEntries(ctx)), ctx);
|
|
78
79
|
}
|
|
79
80
|
function listen(ctx) {
|
|
@@ -109,7 +110,7 @@ export default function comboToggleExtension(pi) {
|
|
|
109
110
|
}
|
|
110
111
|
persistPreset(level);
|
|
111
112
|
useState(setSharedComboLevel(level), ctx);
|
|
112
|
-
ctx?.ui?.notify?.(`Combo ${level} applied: ${levelSummary(
|
|
113
|
+
ctx?.ui?.notify?.(`Combo ${level} applied: ${levelSummary(getSharedComboState())}`, 'info');
|
|
113
114
|
if (ctx?.reload)
|
|
114
115
|
await ctx.reload();
|
|
115
116
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, paintStatusBar, sessionEntries, setSharedComboMode } from '../shared/session-state.js';
|
|
3
|
+
import { asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, paintStatusBar, reconcileSharedComboEntries, sessionEntries, setSharedComboMode } from '../shared/session-state.js';
|
|
4
4
|
import { readRtkDefault } from '../shared/plugin-settings.js';
|
|
5
5
|
const DEFAULT_ENABLED = false;
|
|
6
6
|
function asBoolean(value) {
|
|
@@ -102,6 +102,11 @@ export default function rtkSessionExtension(pi) {
|
|
|
102
102
|
});
|
|
103
103
|
function restoreEnabled(ctx) {
|
|
104
104
|
const entries = sessionEntries(ctx);
|
|
105
|
+
// Publish the persisted combo state (incl. combo-level) before painting:
|
|
106
|
+
// bar suppression reads the in-process bridge, which is empty in a fresh
|
|
107
|
+
// host until the combo extension reconciles — and its reconcile is
|
|
108
|
+
// UI-gated. Deriving it here makes suppression independent of load order.
|
|
109
|
+
reconcileSharedComboEntries(entries);
|
|
105
110
|
// Persisted session state wins; a fresh session falls back to the
|
|
106
111
|
// installer/user-configured default (off unless configured).
|
|
107
112
|
const persisted = resolveEnabled(entries);
|
package/package.json
CHANGED
package/tersio.js
CHANGED
|
@@ -69,6 +69,7 @@ const verbose = args.includes('--verbose');
|
|
|
69
69
|
const doctor = command === 'doctor' || args.includes('--doctor');
|
|
70
70
|
const uninstall = command === 'uninstall' || args.includes('--uninstall');
|
|
71
71
|
const removePonytail = args.includes('--remove-ponytail');
|
|
72
|
+
const keepPonytail = args.includes('--keep-ponytail');
|
|
72
73
|
const removeRtk = args.includes('--remove-rtk');
|
|
73
74
|
const scopeFlag = flagValue('--scope')?.toLowerCase() ?? null;
|
|
74
75
|
// --- Profile selection (session-start mode defaults) ---
|
|
@@ -83,7 +84,7 @@ function printHelp() {
|
|
|
83
84
|
|
|
84
85
|
Commands:
|
|
85
86
|
install Install the add-ons (user scope by default)
|
|
86
|
-
update
|
|
87
|
+
update Refresh the CLI and add-ons (RTK binary, Caveman rule, Ponytail)
|
|
87
88
|
reinstall Clean and reinstall the user-scope add-ons
|
|
88
89
|
doctor Check the current installation
|
|
89
90
|
uninstall Remove the managed extensions
|
|
@@ -92,7 +93,7 @@ Commands:
|
|
|
92
93
|
|
|
93
94
|
Options:
|
|
94
95
|
--scope user|project|both
|
|
95
|
-
--
|
|
96
|
+
--keep-ponytail (uninstall: keep the Ponytail plugin — removed by default)
|
|
96
97
|
--remove-rtk (uninstall: also remove the RTK binary)
|
|
97
98
|
--combo-default off|medium|balanced|max (implies caveman, rtk, ponytail)
|
|
98
99
|
--caveman-default off|lite|full|ultra|wenyan (override; default follows combo)
|
|
@@ -660,17 +661,8 @@ async function stepCombo(extDir, options) {
|
|
|
660
661
|
await ensureExtensionInConfig(configPath, dest, 'combo', options);
|
|
661
662
|
}
|
|
662
663
|
// --- Doctor ---
|
|
663
|
-
function presence(label, text) {
|
|
664
|
-
console.log(` ${label}: ${text !== null ? 'installed' : 'MISSING'}`);
|
|
665
|
-
}
|
|
666
|
-
function locationLine(label, ok, loc) {
|
|
667
|
-
console.log(` ${label}: ${ok ? 'ok' : 'MISSING'} ${loc}`);
|
|
668
|
-
}
|
|
669
664
|
async function runDoctor() {
|
|
670
|
-
console.log('\n=== Tersio Doctor
|
|
671
|
-
// Node
|
|
672
|
-
console.log(` Node: ok ${process.version}`);
|
|
673
|
-
// OMP CLI version joins FS probes below; logs keep fixed order.
|
|
665
|
+
console.log('\n=== Tersio Doctor ===');
|
|
674
666
|
// Directories
|
|
675
667
|
const agentDir = OMP_AGENT_DIR;
|
|
676
668
|
const extDir = path.join(agentDir, 'extensions');
|
|
@@ -706,48 +698,66 @@ async function runDoctor() {
|
|
|
706
698
|
readTextIfExists(comboIndex),
|
|
707
699
|
readTextIfExists(modeReinforcement),
|
|
708
700
|
]);
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
presence('Ponytail package', ponytailPkgText);
|
|
716
|
-
presence('Ponytail extension', ponytailExtText);
|
|
717
|
-
if (configText) {
|
|
718
|
-
const hasPonytailPath = configText.includes('ponytail') && configText.includes('pi-extension');
|
|
719
|
-
console.log(` Ponytail in config.yml: ${hasPonytailPath ? 'registered' : 'MISSING'}`);
|
|
720
|
-
}
|
|
721
|
-
// Self plugin registration (Settings → Plugins listing)
|
|
722
|
-
const selfDep = PACKAGE_NAME in (parseJsonObject(pluginsPkgRaw)?.dependencies || {});
|
|
723
|
-
console.log(` Self plugin in plugins/package.json: ${selfDep ? 'registered' : 'MISSING'}`);
|
|
724
|
-
console.log(` Self plugin package: ${selfPkgText !== null ? 'installed' : 'MISSING'}`);
|
|
725
|
-
// RTK
|
|
726
|
-
const rtkExists = rtkBinText !== null;
|
|
727
|
-
console.log(` RTK binary: ${rtkExists ? 'installed' : 'MISSING'} ${rtkBin}`);
|
|
728
|
-
if (rtkExists) {
|
|
729
|
-
try {
|
|
730
|
-
const v = (await execP(rtkBin, ['--version'], { timeout: 5000 })).stdout.trim();
|
|
731
|
-
console.log(` RTK version: ${v}`);
|
|
701
|
+
// Categorized output with a tally; section headers group related probes.
|
|
702
|
+
const tally = { ok: 0, missing: 0, warn: 0 };
|
|
703
|
+
function check(label, ok, detail = '') {
|
|
704
|
+
if (ok) {
|
|
705
|
+
tally.ok++;
|
|
706
|
+
console.log(` ${label}: ok${detail ? ` ${detail}` : ''}`);
|
|
732
707
|
}
|
|
733
|
-
|
|
734
|
-
|
|
708
|
+
else {
|
|
709
|
+
tally.missing++;
|
|
710
|
+
console.log(` ${label}: MISSING${detail ? ` ${detail}` : ''}`);
|
|
735
711
|
}
|
|
736
712
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
713
|
+
function warnLine(label, detail) {
|
|
714
|
+
tally.warn++;
|
|
715
|
+
console.log(` ${label}: warn ${detail}`);
|
|
716
|
+
}
|
|
717
|
+
section('Environment');
|
|
718
|
+
check('Node', true, process.version);
|
|
719
|
+
check('OMP CLI', ompVersion !== null, ompVersion ?? '');
|
|
720
|
+
console.log(` Home: ${HOME}`);
|
|
721
|
+
section('Installation');
|
|
722
|
+
check('OMP agent dir', agentEntries !== null, agentDir);
|
|
723
|
+
check('OMP extensions dir', extEntries !== null, extDir);
|
|
724
|
+
check('OMP config.yml', configText !== null, configPath);
|
|
725
|
+
check('Shared session bridge', sharedStateText !== null);
|
|
726
|
+
section('Extensions');
|
|
727
|
+
check('Caveman extension', cavemanIndexText !== null);
|
|
728
|
+
check('Caveman rule.md', cavemanRuleText !== null);
|
|
729
|
+
check('RTK extension', rtkIndexText !== null);
|
|
730
|
+
check('Updater extension', updaterIndexText !== null);
|
|
731
|
+
check('Combo extension', comboIndexText !== null);
|
|
732
|
+
check('Mode reinforcement extension', modeReinforcementText !== null);
|
|
733
|
+
section('Plugins');
|
|
734
|
+
check('Ponytail package', ponytailPkgText !== null);
|
|
735
|
+
check('Ponytail extension', ponytailExtText !== null);
|
|
747
736
|
if (configText) {
|
|
748
|
-
|
|
749
|
-
|
|
737
|
+
check('Ponytail in config.yml', configText.includes('ponytail') && configText.includes('pi-extension'));
|
|
738
|
+
check('Combo in config.yml', configText.includes('combo-toggle'));
|
|
750
739
|
}
|
|
740
|
+
check('Self plugin package', selfPkgText !== null);
|
|
741
|
+
const selfDep = PACKAGE_NAME in (parseJsonObject(pluginsPkgRaw)?.dependencies || {});
|
|
742
|
+
check('Self plugin in plugins/package.json', selfDep);
|
|
743
|
+
section('RTK');
|
|
744
|
+
check('RTK binary', rtkBinText !== null, rtkBin);
|
|
745
|
+
if (rtkBinText !== null) {
|
|
746
|
+
// rtk may exit non-zero while still printing its version — accept either stream.
|
|
747
|
+
const version = await execP(rtkBin, ['--version'], { timeout: 5000 }).then((r) => r.stdout.trim() || r.stderr.trim() || null, (e) => {
|
|
748
|
+
const err = e;
|
|
749
|
+
return err.stdout?.trim() || err.stderr?.trim() || null;
|
|
750
|
+
});
|
|
751
|
+
if (version)
|
|
752
|
+
check('RTK version', true, version);
|
|
753
|
+
else
|
|
754
|
+
warnLine('RTK version', 'unavailable — binary may not be executable');
|
|
755
|
+
}
|
|
756
|
+
const total = tally.ok + tally.missing + tally.warn;
|
|
757
|
+
console.log(`\n Summary: ${total} checks — ${tally.ok} ok, ${tally.warn} warn, ${tally.missing} missing`);
|
|
758
|
+
}
|
|
759
|
+
function section(name) {
|
|
760
|
+
console.log(`\n${name}`);
|
|
751
761
|
}
|
|
752
762
|
// Read-modify-write a JSON file. mutate returns true when it changed
|
|
753
763
|
// something; no change (or unreadable file) means no output at all.
|
|
@@ -800,7 +810,9 @@ async function removeUninstallTarget(target, shouldDryRun, recursive = true) {
|
|
|
800
810
|
async function runUninstall(options = {}) {
|
|
801
811
|
const shouldDryRun = options.dryRun ?? dryRun;
|
|
802
812
|
const confirmed = (options.yes ?? yes) || shouldDryRun;
|
|
803
|
-
|
|
813
|
+
// Ponytail ships with Tersio's presets, so a full uninstall removes it too;
|
|
814
|
+
// --keep-ponytail opts out and reinstall always preserves it.
|
|
815
|
+
const shouldRemovePonytail = options.removePonytail ?? (removePonytail || !keepPonytail);
|
|
804
816
|
const shouldRemoveRtk = options.removeRtk ?? removeRtk;
|
|
805
817
|
console.log('\n=== Tersio Uninstall ===\n');
|
|
806
818
|
const extDir = path.join(OMP_AGENT_DIR, 'extensions');
|
|
@@ -894,6 +906,7 @@ async function runUninstall(options = {}) {
|
|
|
894
906
|
return true;
|
|
895
907
|
}
|
|
896
908
|
const SCOPE_MAP = { user: '1', project: '2', both: '3' };
|
|
909
|
+
const COMBO_MENU = { '1': 'off', '2': 'medium', '3': 'balanced', '4': 'max' };
|
|
897
910
|
async function runLatestUpdate() {
|
|
898
911
|
const updateScope = scopeFlag || 'user';
|
|
899
912
|
if (!Object.hasOwn(SCOPE_MAP, updateScope)) {
|
|
@@ -920,6 +933,31 @@ async function runLatestUpdate() {
|
|
|
920
933
|
const npmCommandArgs = IS_WINDOWS ? ['/d', '/s', '/c', 'npm', ...npmArgs] : npmArgs;
|
|
921
934
|
console.log('=== Updating Tersio ===');
|
|
922
935
|
console.log(` Running the latest ${PACKAGE_NAME} installer...\n`);
|
|
936
|
+
// The npx delegation below only refreshes the OMP-side files; the globally
|
|
937
|
+
// installed CLI keeps its old version until npm -g runs. Refresh both.
|
|
938
|
+
if (dryRun) {
|
|
939
|
+
console.log(` [dry-run] would run: npm install -g ${PACKAGE_NAME}@latest --no-audit --no-fund`);
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
try {
|
|
943
|
+
const globalArgs = IS_WINDOWS ? ['/d', '/s', '/c', 'npm', 'install', '-g', `${PACKAGE_NAME}@latest`, '--no-audit', '--no-fund'] : ['install', '-g', `${PACKAGE_NAME}@latest`, '--no-audit', '--no-fund'];
|
|
944
|
+
const globalResult = await execP(npmCommand, globalArgs, {
|
|
945
|
+
timeout: 300000,
|
|
946
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
947
|
+
windowsHide: true,
|
|
948
|
+
shell: false,
|
|
949
|
+
});
|
|
950
|
+
if (globalResult.stdout)
|
|
951
|
+
process.stdout.write(globalResult.stdout);
|
|
952
|
+
if (globalResult.stderr)
|
|
953
|
+
process.stderr.write(globalResult.stderr);
|
|
954
|
+
console.log(` [ok] CLI updated to ${PACKAGE_NAME}@latest`);
|
|
955
|
+
}
|
|
956
|
+
catch (e) {
|
|
957
|
+
console.log(` [warn] Global CLI update skipped: ${e.message}`);
|
|
958
|
+
console.log(` [hint] Manual: npm install -g ${PACKAGE_NAME}@latest`);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
923
961
|
try {
|
|
924
962
|
const result = await execP(npmCommand, npmCommandArgs, {
|
|
925
963
|
timeout: 300000,
|
|
@@ -989,12 +1027,27 @@ async function resolveScope() {
|
|
|
989
1027
|
async function resolveProfile() {
|
|
990
1028
|
const profile = defaultProfile();
|
|
991
1029
|
// Single interactive prompt: the Combo preset implies all three modes.
|
|
1030
|
+
// Numbered menu, same pattern as the scope prompt — no typing preset names.
|
|
992
1031
|
// Only for a real user at a terminal, only when no default flags were
|
|
993
1032
|
// given, and never for --apply-update runs.
|
|
994
1033
|
if (tty() && !profileFlagsGiven && !applyUpdate && (install || reinstall)) {
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1034
|
+
console.log(' Session-start defaults — Combo preset:');
|
|
1035
|
+
console.log(' 1) off');
|
|
1036
|
+
console.log(' 2) medium (caveman=lite, rtk=on, ponytail=lite)');
|
|
1037
|
+
console.log(' 3) balanced (caveman=full, rtk=on, ponytail=full)');
|
|
1038
|
+
console.log(' 4) max (caveman=ultra, rtk=on, ponytail=ultra)');
|
|
1039
|
+
for (;;) {
|
|
1040
|
+
const answer = (await ask(' Choose [1-4] (default 1): ')).trim();
|
|
1041
|
+
if (!answer) {
|
|
1042
|
+
profile.comboDefault = 'off';
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
if (COMBO_MENU[answer]) {
|
|
1046
|
+
profile.comboDefault = COMBO_MENU[answer];
|
|
1047
|
+
break;
|
|
1048
|
+
}
|
|
1049
|
+
console.log(` [fail] Invalid choice: ${answer}. Choose 1, 2, 3, or 4.`);
|
|
1050
|
+
}
|
|
998
1051
|
}
|
|
999
1052
|
// Explicit flags always win over the Combo preset.
|
|
1000
1053
|
if (comboDefaultFlag !== undefined)
|
|
@@ -1116,7 +1169,10 @@ async function main() {
|
|
|
1116
1169
|
const userDir = OMP_AGENT_DIR;
|
|
1117
1170
|
const userExtDir = path.join(userDir, 'extensions');
|
|
1118
1171
|
const projectExtDir = path.join(process.cwd(), '.omp', 'extensions');
|
|
1119
|
-
|
|
1172
|
+
// apply-update is `tersio update`'s payload run: treat it like reinstall so
|
|
1173
|
+
// the add-ons refresh too — Ponytail package via npm, self plugin, RTK
|
|
1174
|
+
// binary (always re-downloaded), and the Caveman rule (always re-fetched).
|
|
1175
|
+
const options = { dryRun, verbose, yes, scope, reinstall: reinstall || applyUpdate };
|
|
1120
1176
|
// Check prerequisites
|
|
1121
1177
|
console.log('\nPrerequisites:');
|
|
1122
1178
|
try {
|