@krtclcdy/tersio 2.25.4 → 2.25.6
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 +27 -63
- package/dashboard/dist/index.html +18 -16
- package/dist/cli/backups.js +187 -0
- package/dist/cli/banner.js +4 -3
- package/dist/cli/common.js +29 -13
- package/dist/cli/currency.js +1 -1
- package/dist/cli/dashboard.js +168 -110
- package/dist/cli/doctor-fix.js +232 -50
- package/dist/cli/doctor.js +281 -32
- package/dist/cli/hosts.js +29 -9
- package/dist/cli/install.js +204 -69
- package/dist/cli/interactive.js +3 -3
- package/dist/cli/manifest.js +125 -17
- package/dist/cli/profile.js +14 -9
- package/dist/cli/rtk-wiring.js +1 -1
- package/dist/cli/settings.js +20 -5
- package/dist/cli/uninstall.js +24 -8
- package/dist/cli/update.js +9 -10
- package/dist/cli/usage.js +75 -7
- package/dist/extensions/{caveman-session → caveman}/index.js +4 -2
- package/{extensions/caveman-session → dist/extensions/caveman}/rule-megacave.md +5 -3
- package/{extensions/caveman-session → dist/extensions/caveman}/rule-ultra.md +4 -4
- package/dist/extensions/caveman/rule.md +89 -0
- package/dist/extensions/{combo-toggle → combo}/index.js +62 -21
- package/dist/extensions/{tersio-commands → commands}/index.js +2 -2
- package/dist/extensions/pstack/index.js +119 -0
- package/dist/extensions/pstack/rule.md +65 -0
- package/dist/extensions/{rtk-filter/index.js → rtk/filter.js} +1 -2
- package/dist/extensions/{rtk-session/index.js → rtk/session.js} +5 -5
- package/dist/extensions/{opencode/server.js → server.js} +98 -14
- package/dist/extensions/shared/agent-stats.js +1198 -0
- package/dist/extensions/shared/brand-marks.js +153 -0
- package/dist/extensions/shared/carbon.js +1 -1
- package/dist/extensions/shared/plugin-settings.js +12 -7
- package/dist/extensions/shared/pricing.js +86 -30
- package/dist/extensions/shared/rtk-gain.js +4 -9
- package/dist/extensions/shared/session-state.js +15 -10
- package/dist/extensions/shared/sqlite.js +168 -0
- package/dist/extensions/shared/status.js +8 -8
- package/dist/extensions/shared/types.js +1 -1
- package/dist/extensions/shared/usage-ledger.js +113 -39
- package/dist/extensions/shared/usage-store.js +191 -48
- package/dist/extensions/{lib → shared}/utils.js +6 -6
- package/dist/extensions/timing/index.js +31 -0
- package/dist/extensions/{ai-addons-updater → updater}/index.js +13 -6
- package/dist/probe-catalog.js +48 -0
- package/dist/probe-catalog188.js +44 -0
- package/dist/tersio.js +9 -4
- package/extensions/{caveman-session → caveman}/index.ts +4 -2
- package/{dist/extensions/caveman-session → extensions/caveman}/rule-megacave.md +5 -3
- package/{dist/extensions/caveman-session → extensions/caveman}/rule-ultra.md +4 -4
- package/extensions/caveman/rule.md +89 -0
- package/extensions/{combo-toggle → combo}/index.ts +62 -19
- package/extensions/{tersio-commands → commands}/index.ts +2 -2
- package/extensions/pstack/index.ts +128 -0
- package/extensions/pstack/rule.md +65 -0
- package/extensions/{rtk-filter/index.ts → rtk/filter.ts} +1 -2
- package/extensions/{rtk-session/index.ts → rtk/session.ts} +5 -5
- package/extensions/{opencode/server.ts → server.ts} +81 -16
- package/extensions/shared/agent-stats.ts +1605 -0
- package/extensions/shared/brand-marks.ts +163 -0
- package/extensions/shared/carbon.ts +1 -1
- package/extensions/shared/plugin-settings.ts +13 -7
- package/extensions/shared/pricing.ts +95 -32
- package/extensions/shared/rtk-gain.ts +4 -9
- package/extensions/shared/session-state.ts +18 -13
- package/extensions/shared/sqlite.ts +168 -0
- package/extensions/shared/status.ts +9 -8
- package/extensions/shared/types.ts +2 -1
- package/extensions/shared/usage-ledger.ts +134 -46
- package/extensions/shared/usage-store.ts +202 -50
- package/extensions/{lib → shared}/utils.ts +6 -6
- package/extensions/timing/index.ts +45 -0
- package/extensions/{ai-addons-updater → updater}/index.ts +15 -9
- package/package.json +27 -18
- package/dist/extensions/caveman-session/rule.md +0 -79
- package/extensions/caveman-session/rule.md +0 -79
package/dist/cli/manifest.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
// Single list of install writes, shared by installer, doctor, and tests.
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
3
4
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { findPackageRoot } from '../extensions/
|
|
5
|
+
import { findPackageRoot, readTextIfExists } from '../extensions/shared/utils.js';
|
|
6
|
+
import { writeIfChanged } from './common.js';
|
|
5
7
|
export const EXT_DIR = path.join(findPackageRoot(path.dirname(fileURLToPath(import.meta.url))), 'extensions');
|
|
6
|
-
/** Root entrypoint OpenCode resolves for the installed tree; re-exports the versioned entry beside it. Built by join so the path never reads as a relative import (repo-integrity scans literals). */
|
|
7
|
-
const SERVER_SPEC = `./${['opencode', 'server.ts'].join('/')}`;
|
|
8
|
-
export const OPENCODE_SERVER_SHIM = `// Generated by \`tersio install --host opencode\`: OpenCode resolves <pluginDir>/server.ts.
|
|
9
|
-
export { default } from "${SERVER_SPEC}";
|
|
10
|
-
`;
|
|
11
8
|
/** Every file in the tree, as paths relative to the extensions directory. */
|
|
12
9
|
export const TREE_FILES = [
|
|
13
10
|
'shared/host.ts',
|
|
@@ -16,19 +13,23 @@ export const TREE_FILES = [
|
|
|
16
13
|
'shared/types.ts',
|
|
17
14
|
'shared/plugin-settings.ts',
|
|
18
15
|
'shared/usage-ledger.ts',
|
|
16
|
+
'shared/sqlite.ts',
|
|
19
17
|
'shared/pricing.ts',
|
|
20
18
|
'shared/carbon.ts',
|
|
21
|
-
'
|
|
22
|
-
'caveman
|
|
23
|
-
'caveman
|
|
24
|
-
'caveman
|
|
25
|
-
'caveman
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
19
|
+
'shared/utils.ts',
|
|
20
|
+
'caveman/index.ts',
|
|
21
|
+
'caveman/rule.md',
|
|
22
|
+
'caveman/rule-ultra.md',
|
|
23
|
+
'caveman/rule-megacave.md',
|
|
24
|
+
'pstack/index.ts',
|
|
25
|
+
'pstack/rule.md',
|
|
26
|
+
'rtk/session.ts',
|
|
27
|
+
'rtk/filter.ts',
|
|
28
|
+
'timing/index.ts',
|
|
29
|
+
'combo/index.ts',
|
|
30
|
+
'commands/index.ts',
|
|
31
|
+
'updater/index.ts',
|
|
32
|
+
'server.ts',
|
|
32
33
|
];
|
|
33
34
|
export function sourcePath(relative) {
|
|
34
35
|
return path.join(EXT_DIR, ...relative.split('/'));
|
|
@@ -42,3 +43,110 @@ export function filesUnder(dir) {
|
|
|
42
43
|
}
|
|
43
44
|
return found;
|
|
44
45
|
}
|
|
46
|
+
/** The entries at the extensions root, which `filesUnder` cannot name because it takes a directory. */
|
|
47
|
+
export function rootFiles() {
|
|
48
|
+
return TREE_FILES.filter((file) => !file.includes('/')).map((file) => [sourcePath(file), file]);
|
|
49
|
+
}
|
|
50
|
+
/** The OpenCode plugin entry: imports `@opencode/plugin`, absent from omp and pi loaders, so it ships there alone. */
|
|
51
|
+
export const OPENCODE_ENTRY = 'server.ts';
|
|
52
|
+
/** pi's rtk entry manifest, generated rather than copied: no other host loads rtk that way. */
|
|
53
|
+
export const PI_RTK_MANIFEST = 'rtk/package.json';
|
|
54
|
+
/** pi's rtk entry manifest, generated rather than copied: no other host loads rtk that way. */
|
|
55
|
+
export const PI_RTK_MANIFEST_BODY = `${JSON.stringify({ pi: { extensions: ['./session.ts', './filter.ts'] } }, null, 2)}\n`;
|
|
56
|
+
/** Files the install generates, keyed by tree path, so one repair path handles copied and generated alike. */
|
|
57
|
+
export const GENERATED_TREE_FILES = {
|
|
58
|
+
[PI_RTK_MANIFEST]: PI_RTK_MANIFEST_BODY,
|
|
59
|
+
};
|
|
60
|
+
/** The files one host's extension tree holds. The package tree holds all of them. */
|
|
61
|
+
export function treeFilesFor(id) {
|
|
62
|
+
if (id === 'opencode')
|
|
63
|
+
return TREE_FILES;
|
|
64
|
+
const files = TREE_FILES.filter((file) => file !== OPENCODE_ENTRY);
|
|
65
|
+
return id === 'pi' ? [...files, PI_RTK_MANIFEST] : files;
|
|
66
|
+
}
|
|
67
|
+
/** Obsolete install debris; explicit, not a sweep, because these trees are shared with tersio's other steps. */
|
|
68
|
+
const OBSOLETE_DIRS = ['caveman-session', 'rtk-filter', 'rtk-session', 'combo-toggle', 'tersio-commands', 'ai-addons-updater', 'lib', 'opencode'];
|
|
69
|
+
/** Debris test for a tree-relative path; one definition shared by detection and repair. */
|
|
70
|
+
export function isObsoleteTreeFile(rel, id) {
|
|
71
|
+
if (rel.endsWith('.bak') || rel === '.DS_Store')
|
|
72
|
+
return true;
|
|
73
|
+
if (OBSOLETE_DIRS.includes(rel.split('/')[0]))
|
|
74
|
+
return true;
|
|
75
|
+
return rel === 'server.ts' && id !== 'opencode';
|
|
76
|
+
}
|
|
77
|
+
/** A tree file's text: copied files come from the repo, generated ones from their own definition. */
|
|
78
|
+
export function treeFileText(file) {
|
|
79
|
+
const generated = GENERATED_TREE_FILES[file];
|
|
80
|
+
if (generated !== undefined)
|
|
81
|
+
return generated;
|
|
82
|
+
try {
|
|
83
|
+
return readFileSync(sourcePath(file), 'utf8');
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** The ponytail ruleset this build ships, copied to every host that cannot install the package. */
|
|
90
|
+
export const PONYTAIL_SKILL_NAMES = ['ponytail', 'ponytail-audit', 'ponytail-debt', 'ponytail-gain', 'ponytail-help', 'ponytail-review'];
|
|
91
|
+
/** The OpenCode bundle: no node_modules there, so the ruleset rides along as files. */
|
|
92
|
+
export const PONYTAIL_BUNDLE_FILES = [
|
|
93
|
+
'hooks/ponytail-instructions.js',
|
|
94
|
+
'hooks/ponytail-config.js',
|
|
95
|
+
...PONYTAIL_SKILL_NAMES.map((skill) => `skills/${skill}/SKILL.md`),
|
|
96
|
+
];
|
|
97
|
+
/** pi's local package: the same ruleset, plus the extension that injects it. */
|
|
98
|
+
export const PI_PONYTAIL_FILES = ['pi-extension/index.js', ...PONYTAIL_BUNDLE_FILES];
|
|
99
|
+
/** The shipped package manifest, one level above the extension tree. */
|
|
100
|
+
const MANIFEST_PATH = path.join(path.dirname(EXT_DIR), 'package.json');
|
|
101
|
+
/** The `omp` block a host reads for what to load: extensions, features, and the settings schema. */
|
|
102
|
+
export function shippedOmpManifest() {
|
|
103
|
+
try {
|
|
104
|
+
const pkg = JSON.parse(readFileSync(MANIFEST_PATH, 'utf8'));
|
|
105
|
+
return typeof pkg.omp === 'object' && pkg.omp !== null ? pkg.omp : null;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Write the shipped `omp` block over an installed plugin's manifest. The tree files come from this
|
|
113
|
+
* build, so an entry added here reaches the host only when its manifest names it.
|
|
114
|
+
*/
|
|
115
|
+
export async function syncPluginManifest(pluginDir, options = {}) {
|
|
116
|
+
const omp = shippedOmpManifest();
|
|
117
|
+
if (omp === null)
|
|
118
|
+
return;
|
|
119
|
+
const pkgPath = path.join(pluginDir, 'package.json');
|
|
120
|
+
let pkg;
|
|
121
|
+
try {
|
|
122
|
+
pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (JSON.stringify(pkg.omp) === JSON.stringify(omp))
|
|
128
|
+
return;
|
|
129
|
+
pkg.omp = omp;
|
|
130
|
+
await writeIfChanged(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, options);
|
|
131
|
+
}
|
|
132
|
+
/** Copy bundled files into `dest`; misses are reported and skipped, `skip` keeps newer fetched rule bodies. */
|
|
133
|
+
export async function syncTreeFiles(dest, files, options, skip) {
|
|
134
|
+
const missing = [];
|
|
135
|
+
for (const file of files) {
|
|
136
|
+
if (skip?.(file))
|
|
137
|
+
continue;
|
|
138
|
+
const generated = GENERATED_TREE_FILES[file];
|
|
139
|
+
if (generated !== undefined) {
|
|
140
|
+
await writeIfChanged(path.join(dest, ...file.split('/')), generated, options);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const from = sourcePath(file);
|
|
144
|
+
const text = await readTextIfExists(from);
|
|
145
|
+
if (text === null) {
|
|
146
|
+
missing.push(from);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
await writeIfChanged(path.join(dest, ...file.split('/')), text, options);
|
|
150
|
+
}
|
|
151
|
+
return missing;
|
|
152
|
+
}
|
package/dist/cli/profile.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// cli/profile.ts — session-start defaults and the display-currency default,
|
|
2
2
|
import { existsSync, promises as fs, readFileSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { CAVEMAN_DEFAULTS, COMBO_PRESET_MODES, PACKAGE_NAME, PONYTAIL_DEFAULTS, verbose, } from './common.js';
|
|
4
|
+
import { CAVEMAN_DEFAULTS, COMBO_PRESET_MODES, PACKAGE_NAME, PONYTAIL_DEFAULTS, PSTACK_DEFAULTS, verbose, } from './common.js';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import { DEFAULT_CURRENCY, isCurrencyCode } from './currency.js';
|
|
7
|
-
import { readTextIfExists } from '../extensions/
|
|
7
|
+
import { readTextIfExists } from '../extensions/shared/utils.js';
|
|
8
8
|
import { tersioSettingsFile } from '../extensions/shared/plugin-settings.js';
|
|
9
9
|
function readTextIfExistsSync(file) {
|
|
10
10
|
try {
|
|
@@ -21,12 +21,13 @@ function defaultProfile() {
|
|
|
21
21
|
cavemanDefault: 'off',
|
|
22
22
|
rtkDefault: false,
|
|
23
23
|
ponytailDefault: 'off',
|
|
24
|
+
pstackDefault: 'off',
|
|
24
25
|
currency: DEFAULT_CURRENCY,
|
|
25
26
|
backupSchedule: 'monthly',
|
|
26
27
|
subagentMarkers: [],
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
|
-
// Seed
|
|
30
|
+
// Seed from OMP settings so pre-~/.tersio installs keep values. Sync: awaiting let piped stdin beat `ask()`.
|
|
30
31
|
function storedProfileSync() {
|
|
31
32
|
const base = defaultProfile();
|
|
32
33
|
const stored = parseStored(readTextIfExistsSync(tersioSettingsFile()) ?? readTextIfExistsSync(legacyOmpLockPath()));
|
|
@@ -44,6 +45,8 @@ function applyStored(base, stored) {
|
|
|
44
45
|
base.rtkDefault = stored.rtkDefault;
|
|
45
46
|
if (typeof stored.ponytailDefault === 'string' && PONYTAIL_DEFAULTS.has(stored.ponytailDefault))
|
|
46
47
|
base.ponytailDefault = stored.ponytailDefault;
|
|
48
|
+
if (typeof stored.pstackDefault === 'string' && PSTACK_DEFAULTS.has(stored.pstackDefault))
|
|
49
|
+
base.pstackDefault = stored.pstackDefault;
|
|
47
50
|
if (typeof stored.currency === 'string' && isCurrencyCode(stored.currency.trim().toUpperCase())) {
|
|
48
51
|
base.currency = stored.currency.trim().toUpperCase();
|
|
49
52
|
}
|
|
@@ -80,7 +83,7 @@ function parseStored(raw) {
|
|
|
80
83
|
return null;
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
|
-
// Persist
|
|
86
|
+
// Persist profile where every host reads it. `omp plugin config get` no longer reports it; `tersio settings` does.
|
|
84
87
|
async function writePluginSettings(profile, options) {
|
|
85
88
|
const file = tersioSettingsFile();
|
|
86
89
|
const values = {
|
|
@@ -90,6 +93,7 @@ async function writePluginSettings(profile, options) {
|
|
|
90
93
|
cavemanDefault: profile.cavemanDefault,
|
|
91
94
|
rtkDefault: profile.rtkDefault,
|
|
92
95
|
ponytailDefault: profile.ponytailDefault,
|
|
96
|
+
pstackDefault: profile.pstackDefault,
|
|
93
97
|
currency: profile.currency,
|
|
94
98
|
backupSchedule: profile.backupSchedule,
|
|
95
99
|
...(profile.subagentMarkers.length > 0 ? { subagentMarkers: profile.subagentMarkers } : {}),
|
|
@@ -104,21 +108,22 @@ async function writePluginSettings(profile, options) {
|
|
|
104
108
|
console.log(` [write] Session defaults in ${file}`);
|
|
105
109
|
}
|
|
106
110
|
function formatProfile(profile) {
|
|
107
|
-
return `combo=${profile.comboDefault} (caveman=${profile.cavemanDefault} · rtk=${profile.rtkDefault ? 'on' : 'off'} · ponytail=${profile.ponytailDefault}) · currency=${profile.currency}`;
|
|
111
|
+
return `combo=${profile.comboDefault} (caveman=${profile.cavemanDefault} · rtk=${profile.rtkDefault ? 'on' : 'off'} · ponytail=${profile.ponytailDefault} · pstack=${profile.pstackDefault}) · currency=${profile.currency}`;
|
|
108
112
|
}
|
|
109
113
|
export { defaultProfile, formatProfile, storedProfile, storedProfileSync, writePluginSettings };
|
|
110
114
|
/** The status line the extensions print, read from the persisted profile. */
|
|
111
115
|
export function formatCliStatus(profile) {
|
|
112
|
-
const { comboDefault, cavemanDefault, rtkDefault, ponytailDefault } = profile;
|
|
113
|
-
if (comboDefault === 'off' && cavemanDefault === 'off' && !rtkDefault && ponytailDefault === 'off') {
|
|
116
|
+
const { comboDefault, cavemanDefault, rtkDefault, ponytailDefault, pstackDefault } = profile;
|
|
117
|
+
if (comboDefault === 'off' && cavemanDefault === 'off' && !rtkDefault && ponytailDefault === 'off' && pstackDefault === 'off') {
|
|
114
118
|
return '🧩 combo OFF';
|
|
115
119
|
}
|
|
116
120
|
const preset = COMBO_PRESET_MODES[comboDefault];
|
|
117
121
|
const isPreset = preset !== undefined
|
|
118
122
|
&& preset.caveman === cavemanDefault
|
|
119
123
|
&& preset.rtk === rtkDefault
|
|
120
|
-
&& preset.ponytail === ponytailDefault
|
|
124
|
+
&& preset.ponytail === ponytailDefault
|
|
125
|
+
&& preset.pstack === pstackDefault;
|
|
121
126
|
const level = isPreset ? comboDefault.toUpperCase() : 'CUSTOM';
|
|
122
127
|
const rtk = rtkDefault ? 'ON' : 'OFF';
|
|
123
|
-
return `🧩 combo ${level}: 🪨caveman=${cavemanDefault.toUpperCase()} ⚡rtk=${rtk} 🦥ponytail=${ponytailDefault.toUpperCase()}`;
|
|
128
|
+
return `🧩 combo ${level}: 🪨caveman=${cavemanDefault.toUpperCase()} ⚡rtk=${rtk} 🦥ponytail=${ponytailDefault.toUpperCase()} 🥔pstack=${pstackDefault.toUpperCase()}`;
|
|
124
129
|
}
|
package/dist/cli/rtk-wiring.js
CHANGED
|
@@ -61,7 +61,7 @@ function rtkExtensionPath() {
|
|
|
61
61
|
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
62
62
|
return path.join(home, '.omp', 'agent', 'extensions', 'rtk.ts');
|
|
63
63
|
}
|
|
64
|
-
// Append rtk.ts
|
|
64
|
+
// Append rtk.ts so the wire takes effect on the next OMP start.
|
|
65
65
|
export async function ensureRtkInConfig(options) {
|
|
66
66
|
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
67
67
|
const configPath = path.join(home, '.omp', 'agent', 'config.yml');
|
package/dist/cli/settings.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// cli/settings.ts — view/edit session-start defaults and the display currency.
|
|
2
|
-
import { CAVEMAN_DEFAULTS, COMBO_DEFAULTS, COMBO_PRESET_MODES, PONYTAIL_DEFAULTS, cavemanDefaultFlag, comboDefaultFlag, currency, currencyGiven, diagScheduleFlag, dryRun, ponytailDefaultFlag, profileFlagsGiven, rtkDefaultFlag, settingArg, subagentMarkerFlags, subagentMarkersGiven, } from './common.js';
|
|
2
|
+
import { CAVEMAN_DEFAULTS, COMBO_DEFAULTS, COMBO_PRESET_MODES, PONYTAIL_DEFAULTS, PSTACK_DEFAULTS, cavemanDefaultFlag, comboDefaultFlag, currency, currencyGiven, diagScheduleFlag, dryRun, ponytailDefaultFlag, pstackDefaultFlag, profileFlagsGiven, rtkDefaultFlag, settingArg, subagentMarkerFlags, subagentMarkersGiven, } from './common.js';
|
|
3
3
|
import { tersioSettingsFile } from '../extensions/shared/plugin-settings.js';
|
|
4
4
|
import { CURRENCY_CODES } from './currency.js';
|
|
5
5
|
import { textTable } from './usage.js';
|
|
@@ -14,12 +14,15 @@ function applyFlags(base) {
|
|
|
14
14
|
next.cavemanDefault = preset.caveman;
|
|
15
15
|
next.rtkDefault = preset.rtk;
|
|
16
16
|
next.ponytailDefault = preset.ponytail;
|
|
17
|
+
next.pstackDefault = preset.pstack;
|
|
17
18
|
if (cavemanDefaultFlag !== undefined)
|
|
18
19
|
next.cavemanDefault = cavemanDefaultFlag;
|
|
19
20
|
if (rtkDefaultFlag !== undefined)
|
|
20
21
|
next.rtkDefault = rtkDefaultFlag === 'on';
|
|
21
22
|
if (ponytailDefaultFlag !== undefined)
|
|
22
23
|
next.ponytailDefault = ponytailDefaultFlag;
|
|
24
|
+
if (pstackDefaultFlag !== undefined)
|
|
25
|
+
next.pstackDefault = pstackDefaultFlag;
|
|
23
26
|
if (subagentMarkersGiven)
|
|
24
27
|
next.subagentMarkers = subagentMarkerFlags;
|
|
25
28
|
if (currencyGiven)
|
|
@@ -41,6 +44,7 @@ async function askProfile(current) {
|
|
|
41
44
|
cavemanDefault: preset.caveman,
|
|
42
45
|
rtkDefault: preset.rtk,
|
|
43
46
|
ponytailDefault: preset.ponytail,
|
|
47
|
+
pstackDefault: preset.pstack,
|
|
44
48
|
currency: current.currency,
|
|
45
49
|
backupSchedule: current.backupSchedule,
|
|
46
50
|
subagentMarkers: current.subagentMarkers,
|
|
@@ -82,11 +86,11 @@ async function askDiagSchedule(current) {
|
|
|
82
86
|
return null;
|
|
83
87
|
return picked.value;
|
|
84
88
|
}
|
|
85
|
-
const SETTING_NAMES = ['combo', 'caveman', 'rtk', 'ponytail', 'currency', 'diagnosis', 'markers'];
|
|
89
|
+
const SETTING_NAMES = ['combo', 'caveman', 'rtk', 'ponytail', 'pstack', 'currency', 'diagnosis', 'markers'];
|
|
86
90
|
function settingsUsage() {
|
|
87
|
-
console.log(' Usage: tersio settings [combo|caveman|rtk|ponytail|currency|diagnosis|markers] [--combo-default off|medium|balanced|max] [--caveman-default off|lite|full|ultra|megacave-lite|megacave-full|megacave-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] [--subagent-marker <text>]... [--dry-run]');
|
|
91
|
+
console.log(' Usage: tersio settings [combo|caveman|rtk|ponytail|pstack|currency|diagnosis|markers] [--combo-default off|medium|balanced|max] [--caveman-default off|lite|full|ultra|megacave-lite|megacave-full|megacave-ultra] [--rtk-default on|off] [--ponytail-default off|lite|full|ultra] [--pstack-default on|off] [--currency USD|PHP|EUR|GBP|JPY|KRW|SGD|AUD|CAD|INR] [--diag-schedule manual|daily|weekly|monthly] [--subagent-marker <text>]... [--dry-run]');
|
|
88
92
|
}
|
|
89
|
-
// Single-setting jump:
|
|
93
|
+
// Single-setting jump: prompts only that value instead of walking the whole chain; null when aborted.
|
|
90
94
|
async function runSingleSetting(name, current, nextDiag) {
|
|
91
95
|
const next = { ...current };
|
|
92
96
|
let diag = nextDiag;
|
|
@@ -105,6 +109,7 @@ async function runSingleSetting(name, current, nextDiag) {
|
|
|
105
109
|
next.cavemanDefault = preset.caveman;
|
|
106
110
|
next.rtkDefault = preset.rtk;
|
|
107
111
|
next.ponytailDefault = preset.ponytail;
|
|
112
|
+
next.pstackDefault = preset.pstack;
|
|
108
113
|
break;
|
|
109
114
|
}
|
|
110
115
|
case 'caveman': {
|
|
@@ -135,6 +140,15 @@ async function runSingleSetting(name, current, nextDiag) {
|
|
|
135
140
|
next.ponytailDefault = picked.value;
|
|
136
141
|
break;
|
|
137
142
|
}
|
|
143
|
+
case 'pstack': {
|
|
144
|
+
const picked = await askInteractiveChoice('pstack default (rigor mode)', [...PSTACK_DEFAULTS].map((v) => ({
|
|
145
|
+
value: v, label: v,
|
|
146
|
+
})), next.pstackDefault);
|
|
147
|
+
if (picked.status !== 'selected')
|
|
148
|
+
return null;
|
|
149
|
+
next.pstackDefault = picked.value;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
138
152
|
case 'currency': {
|
|
139
153
|
const picked = await askInteractiveChoice('Display currency (usage/dashboard reports)', CURRENCY_CODES.map((v) => ({
|
|
140
154
|
value: v, label: v,
|
|
@@ -173,6 +187,7 @@ function printSettingsTable(current) {
|
|
|
173
187
|
['caveman', current.cavemanDefault, [...CAVEMAN_DEFAULTS].join(' | ')],
|
|
174
188
|
['rtk', current.rtkDefault ? 'on' : 'off', 'on | off'],
|
|
175
189
|
['ponytail', current.ponytailDefault, [...PONYTAIL_DEFAULTS].join(' | ')],
|
|
190
|
+
['pstack', current.pstackDefault, [...PSTACK_DEFAULTS].join(' | ')],
|
|
176
191
|
['diagnosis', readDiagSchedule(), 'manual | daily | weekly | monthly'],
|
|
177
192
|
['markers', current.subagentMarkers.length === 0 ? 'default' : current.subagentMarkers.join(' | '), 'host subagent prompt text'],
|
|
178
193
|
];
|
|
@@ -180,7 +195,7 @@ function printSettingsTable(current) {
|
|
|
180
195
|
console.log(l);
|
|
181
196
|
}
|
|
182
197
|
console.log(` Stored: ${tersioSettingsFile()}`);
|
|
183
|
-
console.log(` Combo ${current.comboDefault} sets
|
|
198
|
+
console.log(` Combo ${current.comboDefault} sets all four modes. Tune one: tersio settings <name>`);
|
|
184
199
|
}
|
|
185
200
|
async function runSettings() {
|
|
186
201
|
const current = await storedProfile();
|
package/dist/cli/uninstall.js
CHANGED
|
@@ -4,19 +4,30 @@ import path from 'node:path';
|
|
|
4
4
|
import { BUN_BIN_DIR, HOME, OMP_AGENT_DIR, OMP_PLUGINS_DIR, PACKAGE_NAME, RTK_BINARY_NAME, args, dryRun, keepPonytail, removePonytail, removeRtk, yes, debug, writeConfigLines, } from './common.js';
|
|
5
5
|
import { askInteractiveChoice, closeRL, confirmDestructive, tty, sayTagged } from './interactive.js';
|
|
6
6
|
import { detectHosts, hostHint, hostLabel, parseHostArg } from './hosts.js';
|
|
7
|
-
import { piAgentDir, readTextIfExists } from '../extensions/
|
|
7
|
+
import { piAgentDir, readTextIfExists } from '../extensions/shared/utils.js';
|
|
8
8
|
import { tersioSettingsFile } from '../extensions/shared/plugin-settings.js';
|
|
9
9
|
// The pi tree mirrors the OMP one. Ponytail and rtk stay: a package and a shared binary are not ours to delete.
|
|
10
10
|
const PONYTAIL_PKG = '@dietrichgebert/ponytail';
|
|
11
11
|
const PI_TREE_DIRS = [
|
|
12
|
+
// What an install writes now.
|
|
13
|
+
'caveman',
|
|
14
|
+
'pstack',
|
|
15
|
+
'rtk',
|
|
16
|
+
'updater',
|
|
17
|
+
'combo',
|
|
18
|
+
'commands',
|
|
19
|
+
'shared',
|
|
20
|
+
'server.ts',
|
|
21
|
+
// Legacy: a tree an older build wrote, or a foreign name that was never shipped here.
|
|
12
22
|
'caveman-session',
|
|
13
23
|
'rtk-session',
|
|
24
|
+
'rtk-filter',
|
|
14
25
|
'ai-addons-updater',
|
|
15
26
|
'combo-toggle',
|
|
16
27
|
'tersio-commands',
|
|
17
|
-
'shared',
|
|
18
28
|
'lib',
|
|
19
29
|
'opencode',
|
|
30
|
+
'aaa-combo-boot',
|
|
20
31
|
];
|
|
21
32
|
// One `pi remove`, for both packages the installer adds there.
|
|
22
33
|
async function piRemove(spec, shouldDryRun) {
|
|
@@ -90,7 +101,7 @@ async function removePiLayer(host, shouldDryRun, shouldRemovePonytail, shouldRem
|
|
|
90
101
|
await removeCopiedPonytailSkills(shouldDryRun);
|
|
91
102
|
}
|
|
92
103
|
await Promise.all(targets.map((t) => removeUninstallTarget(t, shouldDryRun)));
|
|
93
|
-
// rtk's wiring is a loose file, not
|
|
104
|
+
// rtk's wiring is a loose file, not a tree dir, so it survived --remove-rtk on pi while OMP removed its copy.
|
|
94
105
|
if (shouldRemoveRtk) {
|
|
95
106
|
await removeUninstallTarget(rtkWiring, shouldDryRun, false);
|
|
96
107
|
await removeUninstallTarget(path.join(BUN_BIN_DIR, RTK_BINARY_NAME), shouldDryRun, false);
|
|
@@ -237,15 +248,21 @@ async function runUninstall(options = {}) {
|
|
|
237
248
|
const pluginsDir = OMP_PLUGINS_DIR;
|
|
238
249
|
const ponytailPkgDir = path.join(pluginsDir, 'node_modules', '@dietrichgebert', 'ponytail');
|
|
239
250
|
const targets = [
|
|
251
|
+
// What an install writes now.
|
|
252
|
+
'caveman',
|
|
253
|
+
'rtk',
|
|
254
|
+
'updater',
|
|
255
|
+
'combo',
|
|
256
|
+
'commands',
|
|
257
|
+
'shared',
|
|
258
|
+
// Legacy: a tree an older build wrote into the agent tree, or a foreign name never shipped here.
|
|
240
259
|
'caveman-session',
|
|
241
260
|
'rtk-session',
|
|
261
|
+
'rtk-filter',
|
|
242
262
|
'ai-addons-updater',
|
|
243
263
|
'combo-toggle',
|
|
244
264
|
'tersio-commands',
|
|
245
|
-
'shared',
|
|
246
|
-
// Only the updater reads it, and it runs before this.
|
|
247
265
|
'lib',
|
|
248
|
-
// Legacy: imports session-state.js, so it warns once that module is gone.
|
|
249
266
|
'aaa-combo-boot',
|
|
250
267
|
// Foreign to the OMP loader; never shipped here on purpose.
|
|
251
268
|
'opencode',
|
|
@@ -265,7 +282,6 @@ async function runUninstall(options = {}) {
|
|
|
265
282
|
return false;
|
|
266
283
|
// Remove extension directories (independent paths, so concurrently).
|
|
267
284
|
await Promise.all(targets.map((t) => removeUninstallTarget(t, shouldDryRun)));
|
|
268
|
-
// Remove Combo, mode-reinforcement, and rtk registrations; Ponytail on request.
|
|
269
285
|
const configRaw = await readTextIfExists(configPath);
|
|
270
286
|
if (configRaw) {
|
|
271
287
|
let lines = configRaw.split('\n');
|
|
@@ -275,7 +291,7 @@ async function runUninstall(options = {}) {
|
|
|
275
291
|
return false;
|
|
276
292
|
if (shouldRemovePonytail && l.includes('ponytail') && l.includes('pi-extension'))
|
|
277
293
|
return false;
|
|
278
|
-
// Our rtk wiring, absolute or relative. Leaving it listed would keep OMP loading a hook for
|
|
294
|
+
// Our rtk wiring, absolute or relative. Leaving it listed would keep OMP loading a hook for an uninstalled package.
|
|
279
295
|
const entry = l.trim().replace(/^-\s*/, '').replace(/^['"]|['"]$/g, '');
|
|
280
296
|
if (entry.endsWith(`extensions${path.sep}rtk.ts`) || entry.endsWith('extensions/rtk.ts'))
|
|
281
297
|
return false;
|
package/dist/cli/update.js
CHANGED
|
@@ -4,9 +4,9 @@ import path from 'node:path';
|
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
5
|
import { IS_WINDOWS, OMP_PLUGINS_DIR, PACKAGE_BIN, PACKAGE_NAME, PACKAGE_VERSION, args, dryRun, execP, parseJsonObject, verbose, } from './common.js';
|
|
6
6
|
import { execNetwork, sayTagged } from './interactive.js';
|
|
7
|
-
import { readTextIfExists, resolveRtkBinary, tersioDataPath } from '../extensions/
|
|
7
|
+
import { readTextIfExists, resolveRtkBinary, tersioDataPath } from '../extensions/shared/utils.js';
|
|
8
8
|
import { refreshPrices } from '../extensions/shared/pricing.js';
|
|
9
|
-
import { CAVEMAN_REMOTE_ULTRA, CAVEMAN_REMOTE_MEGACAVE, RTK_RELEASE_API, httpsGet, normalizeRtkVersion, sha256Hex, } from '../extensions/
|
|
9
|
+
import { CAVEMAN_REMOTE_ULTRA, CAVEMAN_REMOTE_MEGACAVE, RTK_RELEASE_API, httpsGet, normalizeRtkVersion, sha256Hex, } from '../extensions/shared/utils.js';
|
|
10
10
|
import { parseHostArg } from './hosts.js';
|
|
11
11
|
const UPDATE_CHECK_TTL_MS = 6 * 60 * 60 * 1000;
|
|
12
12
|
function newerThan(a, b) {
|
|
@@ -35,7 +35,7 @@ async function latestPublishedVersion() {
|
|
|
35
35
|
return null;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
// A newer
|
|
38
|
+
// A newer version, null when up to date, 'unknown' when the registry is unreachable. Cached for a TTL unless force.
|
|
39
39
|
async function checkForUpdate(force = false) {
|
|
40
40
|
const cachePath = tersioDataPath('update-check.json', 'tersio-update-check.json');
|
|
41
41
|
const cached = parseJsonObject(await readTextIfExists(cachePath));
|
|
@@ -48,7 +48,7 @@ async function checkForUpdate(force = false) {
|
|
|
48
48
|
await fs.writeFile(cachePath, JSON.stringify({ latest, lastCheck: Date.now() }) + '\n', 'utf8').catch(() => { });
|
|
49
49
|
return newerThan(latest, PACKAGE_VERSION) ? latest : null;
|
|
50
50
|
}
|
|
51
|
-
// Unreachable registry: a stale cache naming a newer release is
|
|
51
|
+
// Unreachable registry: a stale cache naming a newer release is actionable, else unknown, so none claim "latest".
|
|
52
52
|
return cached?.latest && newerThan(cached.latest, PACKAGE_VERSION) ? cached.latest : 'unknown';
|
|
53
53
|
}
|
|
54
54
|
// Race a probe against a timeout; failures resolve null so nothing blocks.
|
|
@@ -74,12 +74,12 @@ async function execInherit(cmd, args) {
|
|
|
74
74
|
if (code !== 0)
|
|
75
75
|
throw new Error(`${cmd} exited with code ${code}`);
|
|
76
76
|
}
|
|
77
|
-
// Current → latest per add-on.
|
|
77
|
+
// Current → latest per add-on. Probes are capped and nullable; the plan prints `unknown` for anything unreachable.
|
|
78
78
|
async function probeUpdatePlan(cliLatest) {
|
|
79
79
|
const rtkBin = resolveRtkBinary();
|
|
80
80
|
const ponytailPkg = path.join(OMP_PLUGINS_DIR, 'node_modules', '@dietrichgebert', 'ponytail', 'package.json');
|
|
81
81
|
// rule.md ships with the release; only the tracked bodies can go stale.
|
|
82
|
-
const tersioRule = (name) => path.join(OMP_PLUGINS_DIR, 'node_modules', '@krtclcdy', 'tersio', 'extensions', 'caveman
|
|
82
|
+
const tersioRule = (name) => path.join(OMP_PLUGINS_DIR, 'node_modules', '@krtclcdy', 'tersio', 'extensions', 'caveman', name);
|
|
83
83
|
const controller = new AbortController();
|
|
84
84
|
const [rtkLocal, rtkRelease, ruleLocal, ruleRemote, ponytailLocalText] = await Promise.all([
|
|
85
85
|
settle(rtkBin ? execP(rtkBin, ['--version'], { timeout: 5000 }).then((r) => normalizeRtkVersion(r.stdout.trim() || r.stderr.trim()) || null) : Promise.resolve(null), 6000),
|
|
@@ -98,7 +98,7 @@ async function probeUpdatePlan(cliLatest) {
|
|
|
98
98
|
ponytail: ponytailLocalText ? parseJsonObject(ponytailLocalText)?.version ?? null : null,
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
|
-
// Every add-on prints a version jump when stale, "up to date" otherwise, and "unknown" when
|
|
101
|
+
// Every add-on prints a version jump when stale, "up to date" otherwise, and "unknown" when unreachable.
|
|
102
102
|
function planLines(plan) {
|
|
103
103
|
const stale = [];
|
|
104
104
|
const status = [];
|
|
@@ -147,7 +147,6 @@ async function runLatestUpdate() {
|
|
|
147
147
|
// `--prefer-online` beats the local cache, so a stale @latest cannot pin down.
|
|
148
148
|
const cliLatest = await latestPublishedVersion();
|
|
149
149
|
const target = cliLatest && /^\d+\.\d+\.\d+$/.test(cliLatest) ? `@${cliLatest}` : '@latest';
|
|
150
|
-
// Best-effort: unreachable probes print as unknown.
|
|
151
150
|
const plan = await probeUpdatePlan(cliLatest);
|
|
152
151
|
const { stale, status } = planLines(plan);
|
|
153
152
|
if (dryRun) {
|
|
@@ -181,7 +180,7 @@ async function runLatestUpdate() {
|
|
|
181
180
|
process.exitCode = 1;
|
|
182
181
|
return;
|
|
183
182
|
}
|
|
184
|
-
// The npx delegation below only refreshes the OMP-side files; the
|
|
183
|
+
// The npx delegation below only refreshes the OMP-side files; the global CLI keeps its old version until npm -g runs.
|
|
185
184
|
const globalArgs = IS_WINDOWS
|
|
186
185
|
? ['/d', '/s', '/c', 'npm', 'install', '-g', `${PACKAGE_NAME}${target}`, '--no-audit', '--no-fund', '--prefer-online']
|
|
187
186
|
: ['install', '-g', `${PACKAGE_NAME}${target}`, '--no-audit', '--no-fund', '--prefer-online'];
|
|
@@ -211,7 +210,7 @@ async function runLatestUpdate() {
|
|
|
211
210
|
...forwardedArgs,
|
|
212
211
|
];
|
|
213
212
|
const npmCommandArgs = IS_WINDOWS ? ['/d', '/s', '/c', 'npm', ...npmArgs] : npmArgs;
|
|
214
|
-
// Inherited stdio, no outer spinner; the
|
|
213
|
+
// Inherited stdio, no outer spinner; the quiet installer runs, so this parent owns the plan line and closing summary.
|
|
215
214
|
try {
|
|
216
215
|
await execInherit(npmCommand, npmCommandArgs);
|
|
217
216
|
console.log(`Done — tersio ${plan.cli ?? PACKAGE_VERSION} and add-ons updated.`);
|