@krtclcdy/tersio 2.1.0 → 2.2.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 +6 -0
- package/package.json +1 -1
- package/tersio.js +93 -14
package/README.md
CHANGED
|
@@ -11,6 +11,12 @@ Terse replies, compact shell output, and minimal code decisions for [Oh My Pi (O
|
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
|
+
Quick install (macOS/Linux/WSL — checks npm, then bootstraps the CLI):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
curl -fsSL https://raw.githubusercontent.com/KurutoDenzeru/tersio/main/install.sh | sh
|
|
18
|
+
```
|
|
19
|
+
|
|
14
20
|
OMP plugin (recommended — updates and feature flags handled by OMP):
|
|
15
21
|
|
|
16
22
|
```bash
|
package/package.json
CHANGED
package/tersio.js
CHANGED
|
@@ -680,7 +680,7 @@ async function runDoctor() {
|
|
|
680
680
|
const modeReinforcement = path.join(extDir, 'shared', 'mode-reinforcement.js');
|
|
681
681
|
const selfPkg = path.join(pluginsDir, 'node_modules', PACKAGE_NAME, 'package.json');
|
|
682
682
|
// Independent probes run concurrently; logs below keep fixed order.
|
|
683
|
-
const [ompVersion, agentEntries, extEntries, sharedStateText, configText, ponytailPkgText, ponytailExtText, pluginsPkgRaw, selfPkgText, rtkBinText, cavemanIndexText, cavemanRuleText, rtkIndexText, updaterIndexText, comboIndexText, modeReinforcementText,] = await Promise.all([
|
|
683
|
+
const [ompVersion, agentEntries, extEntries, sharedStateText, configText, ponytailPkgText, ponytailExtText, pluginsPkgRaw, selfPkgText, rtkBinText, cavemanIndexText, cavemanRuleText, rtkIndexText, updaterIndexText, comboIndexText, modeReinforcementText, ruleMtime, rtkMtime, ponytailMtime, updateVersion,] = await Promise.all([
|
|
684
684
|
execP(OMP_BIN, ['--version']).then((r) => r.stdout.trim(), () => null),
|
|
685
685
|
fs.readdir(agentDir).catch(() => null),
|
|
686
686
|
fs.readdir(extDir).catch(() => null),
|
|
@@ -697,6 +697,10 @@ async function runDoctor() {
|
|
|
697
697
|
readTextIfExists(updaterIndex),
|
|
698
698
|
readTextIfExists(comboIndex),
|
|
699
699
|
readTextIfExists(modeReinforcement),
|
|
700
|
+
fs.stat(cavemanRule).catch(() => null),
|
|
701
|
+
fs.stat(rtkBin).catch(() => null),
|
|
702
|
+
fs.stat(ponytailPkg).catch(() => null),
|
|
703
|
+
checkForUpdate(),
|
|
700
704
|
]);
|
|
701
705
|
// Categorized output with a tally; section headers group related probes.
|
|
702
706
|
const tally = { ok: 0, missing: 0, warn: 0 };
|
|
@@ -717,6 +721,10 @@ async function runDoctor() {
|
|
|
717
721
|
section('Environment');
|
|
718
722
|
check('Node', true, process.version);
|
|
719
723
|
check('OMP CLI', ompVersion !== null, ompVersion ?? '');
|
|
724
|
+
if (updateVersion)
|
|
725
|
+
warnLine('Tersio CLI', `${updateVersion} available — run tersio update`);
|
|
726
|
+
else
|
|
727
|
+
check('Tersio CLI', true, PACKAGE_VERSION);
|
|
720
728
|
console.log(` Home: ${HOME}`);
|
|
721
729
|
section('Installation');
|
|
722
730
|
check('OMP agent dir', agentEntries !== null, agentDir);
|
|
@@ -725,7 +733,6 @@ async function runDoctor() {
|
|
|
725
733
|
check('Shared session bridge', sharedStateText !== null);
|
|
726
734
|
section('Extensions');
|
|
727
735
|
check('Caveman extension', cavemanIndexText !== null);
|
|
728
|
-
check('Caveman rule.md', cavemanRuleText !== null);
|
|
729
736
|
check('RTK extension', rtkIndexText !== null);
|
|
730
737
|
check('Updater extension', updaterIndexText !== null);
|
|
731
738
|
check('Combo extension', comboIndexText !== null);
|
|
@@ -737,22 +744,27 @@ async function runDoctor() {
|
|
|
737
744
|
check('Ponytail in config.yml', configText.includes('ponytail') && configText.includes('pi-extension'));
|
|
738
745
|
check('Combo in config.yml', configText.includes('combo-toggle'));
|
|
739
746
|
}
|
|
740
|
-
check('Self plugin package', selfPkgText !== null);
|
|
747
|
+
check('Self plugin package', selfPkgText !== null, parseJsonObject(selfPkgText)?.version ?? '');
|
|
741
748
|
const selfDep = PACKAGE_NAME in (parseJsonObject(pluginsPkgRaw)?.dependencies || {});
|
|
742
749
|
check('Self plugin in plugins/package.json', selfDep);
|
|
743
|
-
section('
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
const
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
750
|
+
section('Add-ons');
|
|
751
|
+
const ruleAge = ruleMtime ? `updated ${relTime(Date.now() - ruleMtime.mtimeMs)}` : '';
|
|
752
|
+
check('Caveman rule', cavemanRuleText !== null, ruleAge);
|
|
753
|
+
const rtkVersion = rtkBinText === null ? null : await execP(rtkBin, ['--version'], { timeout: 5000 }).then((r) => r.stdout.trim() || r.stderr.trim() || null, (e) => {
|
|
754
|
+
const err = e;
|
|
755
|
+
return err.stdout?.trim() || err.stderr?.trim() || null;
|
|
756
|
+
});
|
|
757
|
+
const rtkAge = rtkMtime ? `updated ${relTime(Date.now() - rtkMtime.mtimeMs)}` : '';
|
|
758
|
+
if (rtkBinText === null)
|
|
759
|
+
check('RTK binary', false, rtkBin);
|
|
760
|
+
else {
|
|
761
|
+
const bits = [rtkVersion, rtkAge].filter(Boolean).join(', ');
|
|
762
|
+
check('RTK binary', true, bits);
|
|
763
|
+
if (!rtkVersion)
|
|
754
764
|
warnLine('RTK version', 'unavailable — binary may not be executable');
|
|
755
765
|
}
|
|
766
|
+
const ponytailAge = ponytailMtime ? `updated ${relTime(Date.now() - ponytailMtime.mtimeMs)}` : '';
|
|
767
|
+
check('Ponytail', ponytailPkgText !== null, [parseJsonObject(ponytailPkgText)?.version ?? '', ponytailAge].filter(Boolean).join(', '));
|
|
756
768
|
const total = tally.ok + tally.missing + tally.warn;
|
|
757
769
|
console.log(`\n Summary: ${total} checks — ${tally.ok} ok, ${tally.warn} warn, ${tally.missing} missing`);
|
|
758
770
|
}
|
|
@@ -907,6 +919,66 @@ async function runUninstall(options = {}) {
|
|
|
907
919
|
}
|
|
908
920
|
const SCOPE_MAP = { user: '1', project: '2', both: '3' };
|
|
909
921
|
const COMBO_MENU = { '1': 'off', '2': 'medium', '3': 'balanced', '4': 'max' };
|
|
922
|
+
// --- Update check (cached; used by the bare-invocation banner and doctor) ---
|
|
923
|
+
const UPDATE_CHECK_TTL_MS = 6 * 60 * 60 * 1000;
|
|
924
|
+
function newerThan(a, b) {
|
|
925
|
+
const pa = a.replace(/^v/, '').split('.').map((n) => parseInt(n, 10) || 0);
|
|
926
|
+
const pb = b.replace(/^v/, '').split('.').map((n) => parseInt(n, 10) || 0);
|
|
927
|
+
for (let i = 0; i < 3; i++) {
|
|
928
|
+
if ((pa[i] || 0) !== (pb[i] || 0))
|
|
929
|
+
return (pa[i] || 0) > (pb[i] || 0);
|
|
930
|
+
}
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
async function latestPublishedVersion() {
|
|
934
|
+
try {
|
|
935
|
+
const args = IS_WINDOWS
|
|
936
|
+
? ['/d', '/s', '/c', 'npm', 'view', PACKAGE_NAME, 'version', '--prefer-online']
|
|
937
|
+
: ['view', PACKAGE_NAME, 'version', '--prefer-online'];
|
|
938
|
+
const r = await execP(IS_WINDOWS ? process.env.ComSpec || 'cmd.exe' : 'npm', args, {
|
|
939
|
+
timeout: 4000,
|
|
940
|
+
maxBuffer: 1024 * 1024,
|
|
941
|
+
windowsHide: true,
|
|
942
|
+
shell: false,
|
|
943
|
+
});
|
|
944
|
+
return r.stdout.trim() || null;
|
|
945
|
+
}
|
|
946
|
+
catch {
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
// Returns a newer published version, or null. Reads the registry at most once
|
|
951
|
+
// per TTL (cached under OMP_PLUGINS_DIR) and stays silent on failure.
|
|
952
|
+
async function checkForUpdate() {
|
|
953
|
+
const cachePath = path.join(OMP_PLUGINS_DIR, 'tersio-update-check.json');
|
|
954
|
+
const cached = parseJsonObject(await readTextIfExists(cachePath));
|
|
955
|
+
const cacheFresh = typeof cached?.lastCheck === 'number' && Date.now() - cached.lastCheck < UPDATE_CHECK_TTL_MS;
|
|
956
|
+
if (cacheFresh && cached?.latest)
|
|
957
|
+
return newerThan(cached.latest, PACKAGE_VERSION) ? cached.latest : null;
|
|
958
|
+
const latest = await latestPublishedVersion();
|
|
959
|
+
if (latest) {
|
|
960
|
+
await fs.mkdir(OMP_PLUGINS_DIR, { recursive: true }).catch(() => { });
|
|
961
|
+
await fs.writeFile(cachePath, JSON.stringify({ latest, lastCheck: Date.now() }) + '\n', 'utf8').catch(() => { });
|
|
962
|
+
return newerThan(latest, PACKAGE_VERSION) ? latest : null;
|
|
963
|
+
}
|
|
964
|
+
// Registry unreachable: fall back to a stale cache rather than staying silent.
|
|
965
|
+
return cached?.latest && newerThan(cached.latest, PACKAGE_VERSION) ? cached.latest : null;
|
|
966
|
+
}
|
|
967
|
+
function relTime(ageMs) {
|
|
968
|
+
const mins = Math.floor(ageMs / 60000);
|
|
969
|
+
if (mins < 1)
|
|
970
|
+
return 'just now';
|
|
971
|
+
if (mins < 60)
|
|
972
|
+
return `${mins} min${mins === 1 ? '' : 's'} ago`;
|
|
973
|
+
const hours = Math.floor(mins / 60);
|
|
974
|
+
if (hours < 24)
|
|
975
|
+
return `${hours} hour${hours === 1 ? '' : 's'} ago`;
|
|
976
|
+
const days = Math.floor(hours / 24);
|
|
977
|
+
if (days < 14)
|
|
978
|
+
return `${days} day${days === 1 ? '' : 's'} ago`;
|
|
979
|
+
const weeks = Math.floor(days / 7);
|
|
980
|
+
return `${weeks} week${weeks === 1 ? '' : 's'} ago`;
|
|
981
|
+
}
|
|
910
982
|
async function runLatestUpdate() {
|
|
911
983
|
const updateScope = scopeFlag || 'user';
|
|
912
984
|
if (!Object.hasOwn(SCOPE_MAP, updateScope)) {
|
|
@@ -1163,6 +1235,13 @@ async function main() {
|
|
|
1163
1235
|
console.log(` Platform: ${process.platform}`);
|
|
1164
1236
|
console.log(` Arch: ${process.arch}`);
|
|
1165
1237
|
console.log(` Home: ${HOME}`);
|
|
1238
|
+
// Remind humans a newer release exists; silent for scripts (no TTY) and for
|
|
1239
|
+
// the apply-update payload, which is itself an update run.
|
|
1240
|
+
if (tty() && !applyUpdate && command !== 'uninstall') {
|
|
1241
|
+
const newer = await checkForUpdate();
|
|
1242
|
+
if (newer)
|
|
1243
|
+
console.log(`\n [update] tersio ${newer} available (installed ${PACKAGE_VERSION}) — run \`tersio update\``);
|
|
1244
|
+
}
|
|
1166
1245
|
const scope = await resolveScope();
|
|
1167
1246
|
// Resolve session defaults: flags > interactive prompt > defaults.
|
|
1168
1247
|
const profile = await resolveProfile();
|