moshcode 0.92.0 → 0.93.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 +22 -3
- package/package.json +1 -1
- package/src/cli-schema.mjs +7 -4
- package/src/nice.mjs +37 -1
- package/src/tui.mjs +44 -2
package/README.md
CHANGED
|
@@ -1017,15 +1017,34 @@ Run a few engines at once on a box you also want to type on and you get the
|
|
|
1017
1017
|
failure everyone knows: nothing crashed, but the machine stops answering.
|
|
1018
1018
|
|
|
1019
1019
|
```text
|
|
1020
|
-
/nice
|
|
1020
|
+
/nice agents claude # throttle this one engine
|
|
1021
|
+
/nice pnpm -r build # …or this one shell line
|
|
1022
|
+
/nice merge # …or any other pit command
|
|
1023
|
+
|
|
1024
|
+
/nice on # or throttle everything from here on
|
|
1021
1025
|
/nice mem 2G # a ceiling, so a runaway dies alone
|
|
1022
1026
|
/nice cpu 15 # yield more (nice takes -20..19)
|
|
1023
1027
|
/nice # what it is set to
|
|
1024
1028
|
/nice off # back to normal priority (the default)
|
|
1025
1029
|
```
|
|
1026
1030
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1031
|
+
`/nice <line>` is the form to reach for. It runs **anything the pit can already
|
|
1032
|
+
run** — a pit command, an engine, a tool, one of your aliases, or a bare shell
|
|
1033
|
+
line — at low priority, without changing any setting. It works on all of those
|
|
1034
|
+
because it hands the rest of the line back to the top of the dispatcher exactly
|
|
1035
|
+
as an alias expansion does, rather than keeping a list of its own that would
|
|
1036
|
+
drift. The leading slash is optional: `/nice agents claude` and
|
|
1037
|
+
`/nice /agents claude` are the same line.
|
|
1038
|
+
|
|
1039
|
+
The throttle lasts exactly as long as the line that asked for it, including
|
|
1040
|
+
through an alias that expands into something else. The next thing you type is
|
|
1041
|
+
back to normal.
|
|
1042
|
+
|
|
1043
|
+
`/nice on` is the other half, for when the box is shared all day and you would
|
|
1044
|
+
rather decide once. It is off by default — a throttle nobody asked for is a slow
|
|
1045
|
+
engine nobody can explain — and it applies to engines started *after* you turn
|
|
1046
|
+
it on. The settings words (`on`, `off`, `status`, `cpu`, `io`, `mem`) win the
|
|
1047
|
+
first position, so a program named `on` is not reachable through `/nice`.
|
|
1029
1048
|
|
|
1030
1049
|
**`nice` alone is half a fix, and it is worth knowing which half.** It reorders
|
|
1031
1050
|
CPU, so it buys back the part of a freeze you could have waited out. It does
|
package/package.json
CHANGED
package/src/cli-schema.mjs
CHANGED
|
@@ -1596,20 +1596,23 @@ export const PIT_COMMANDS = [
|
|
|
1596
1596
|
description: "show the current dir + git repo/branch/origin" },
|
|
1597
1597
|
{ name: "shell", aliases: ["sh"], args: "[cmd]", pitOnly: true,
|
|
1598
1598
|
description: "drop into $SHELL (exit → back to the pit); also !cmd" },
|
|
1599
|
-
{ name: "nice", aliases: ["throttle"], args: "on | off | cpu <n> | io <n> | mem <size>", pitOnly: true,
|
|
1600
|
-
description: "run
|
|
1599
|
+
{ name: "nice", aliases: ["throttle"], args: "<command> | on | off | cpu <n> | io <n> | mem <size>", pitOnly: true,
|
|
1600
|
+
description: "run something at low priority so the box stays usable",
|
|
1601
1601
|
synopsis: [
|
|
1602
|
+
["/nice <command> [args]", "run one line throttled — a pit command, engine, tool, alias or shell line"],
|
|
1602
1603
|
["/nice [status]", "what the throttle is set to"],
|
|
1603
|
-
["/nice on | off", "
|
|
1604
|
+
["/nice on | off", "throttle everything from here on (off by default)"],
|
|
1604
1605
|
["/nice cpu <-20..19>", "nice level — higher yields more CPU"],
|
|
1605
1606
|
["/nice io <0..7>", "ionice best-effort level"],
|
|
1606
1607
|
["/nice mem <size> | off", "memory ceiling per engine (needs systemd)"],
|
|
1607
1608
|
],
|
|
1608
1609
|
examples: [
|
|
1610
|
+
["/nice agents claude", "throttle this engine, leave the setting alone"],
|
|
1611
|
+
["/nice pnpm -r build", "a shell line works too"],
|
|
1609
1612
|
["/nice on", "nice -n10 + ionice -c2 -n7 for every engine started after"],
|
|
1610
1613
|
["/nice mem 2G", "the ceiling nice(1) can't give you — a runaway dies alone"],
|
|
1611
1614
|
],
|
|
1612
|
-
seeAlso: ["agents", "start"] },
|
|
1615
|
+
seeAlso: ["agents", "start", "alias"] },
|
|
1613
1616
|
{ name: "alias", aliases: ["aliases"], args: 'set <name> "<cmd>" | list | get | rm | install <tool>', pitOnly: true,
|
|
1614
1617
|
description: "name a line you keep retyping; /<name> runs it",
|
|
1615
1618
|
synopsis: [
|
package/src/nice.mjs
CHANGED
|
@@ -52,6 +52,42 @@ export function niceFile() {
|
|
|
52
52
|
return path.join(os.homedir(), ".moshcode", "nice.json");
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* `/nice <line>` throttles one line without changing the saved setting.
|
|
57
|
+
*
|
|
58
|
+
* Module-level rather than threaded through every call because the thing being
|
|
59
|
+
* throttled is not a function argument -- it is whatever that line eventually
|
|
60
|
+
* spawns, which may be an alias that expands to a pit command that starts an
|
|
61
|
+
* engine, three dispatch rounds later. The pit reads one line at a time and
|
|
62
|
+
* fully awaits it, so "armed until the next line the user types" is both the
|
|
63
|
+
* simplest implementation and exactly the intent.
|
|
64
|
+
*/
|
|
65
|
+
let oneShot = false;
|
|
66
|
+
|
|
67
|
+
/** Throttle whatever the current line ends up spawning. */
|
|
68
|
+
export function armOneShot() { oneShot = true; }
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Stop throttling. The pit calls this when it reads a fresh line, NOT when a
|
|
72
|
+
* command finishes: one typed line can dispatch several times through alias
|
|
73
|
+
* expansion, and all of it is the line the user asked to be nice.
|
|
74
|
+
*/
|
|
75
|
+
export function disarmOneShot() { oneShot = false; }
|
|
76
|
+
|
|
77
|
+
/** Is a one-shot throttle in force? */
|
|
78
|
+
export function oneShotArmed() { return oneShot; }
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The settings a spawn should actually use: what is saved, plus a one-shot.
|
|
82
|
+
*
|
|
83
|
+
* `/nice on` and `/nice <cmd>` end in the same place by design -- a spawn does
|
|
84
|
+
* not need to know which of the two asked for it.
|
|
85
|
+
*/
|
|
86
|
+
export function effectiveNice() {
|
|
87
|
+
const saved = loadNice();
|
|
88
|
+
return oneShot ? { ...saved, on: true } : saved;
|
|
89
|
+
}
|
|
90
|
+
|
|
55
91
|
/**
|
|
56
92
|
* The current settings, always a complete object.
|
|
57
93
|
*
|
|
@@ -135,7 +171,7 @@ export function canCapMemory({ has = haveBin, env = process.env } = {}) {
|
|
|
135
171
|
* rather than a wrong guess.
|
|
136
172
|
*/
|
|
137
173
|
export function throttleSpec(spec, {
|
|
138
|
-
settings =
|
|
174
|
+
settings = effectiveNice(),
|
|
139
175
|
has = haveBin,
|
|
140
176
|
env = process.env,
|
|
141
177
|
platform = process.platform,
|
package/src/tui.mjs
CHANGED
|
@@ -22,7 +22,10 @@ import { activeChildInput, createMirror, pressKey, setActiveSink, teeOutput } fr
|
|
|
22
22
|
import { fetchMotdAd } from "./ads.mjs";
|
|
23
23
|
import { runScript } from "./runtime.mjs";
|
|
24
24
|
import { moshVocabulary } from "./commands.mjs";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
loadNice, saveNice, describeNice, canCapMemory, parseMemory,
|
|
27
|
+
armOneShot, disarmOneShot,
|
|
28
|
+
} from "./nice.mjs";
|
|
26
29
|
import { mcpCommand, pluginCommand, skillCommand } from "./integrations.mjs";
|
|
27
30
|
import { stocksCommand } from "./advisor.mjs";
|
|
28
31
|
import { cryptoCommand } from "./crypto.mjs";
|
|
@@ -389,6 +392,16 @@ function isInstalledTool(key) {
|
|
|
389
392
|
* line, not an argument list: re-joining tokens would drop the quoting that the
|
|
390
393
|
* shell still has to read.
|
|
391
394
|
*/
|
|
395
|
+
/**
|
|
396
|
+
* The words that mean "configure the throttle" rather than "run this".
|
|
397
|
+
*
|
|
398
|
+
* Kept next to the command that implements them so the two cannot drift: every
|
|
399
|
+
* other first word after `/nice` is a line to run, so adding a verb here
|
|
400
|
+
* without teaching niceCommand about it would make that word silently
|
|
401
|
+
* unrunnable rather than produce an error.
|
|
402
|
+
*/
|
|
403
|
+
const NICE_SETTING_VERBS = new Set(["on", "off", "status", "cpu", "io", "mem", "memory"]);
|
|
404
|
+
|
|
392
405
|
/**
|
|
393
406
|
* `/nice` — run the CLIs the pit starts at a lower priority than your terminal.
|
|
394
407
|
*
|
|
@@ -929,6 +942,11 @@ export async function tui() {
|
|
|
929
942
|
finally { atPrompt(null); }
|
|
930
943
|
if (line == null) break; // Ctrl-D
|
|
931
944
|
expansions = 0;
|
|
945
|
+
// A one-shot `/nice <line>` lasts exactly as long as the line that asked
|
|
946
|
+
// for it. Cleared here, where a REAL line is read, and not when a command
|
|
947
|
+
// returns: one typed line can dispatch several times through alias
|
|
948
|
+
// expansion, and all of that is still the line the user said to be nice.
|
|
949
|
+
disarmOneShot();
|
|
932
950
|
line = line.trim();
|
|
933
951
|
if (!line) continue;
|
|
934
952
|
saveHistory(); // readline just recorded this line into the shared history
|
|
@@ -994,7 +1012,31 @@ export async function tui() {
|
|
|
994
1012
|
continue;
|
|
995
1013
|
}
|
|
996
1014
|
if (cmd === "alias" || cmd === "aliases") { aliasCommand(rest, line); continue; }
|
|
997
|
-
if (cmd === "nice" || cmd === "throttle") {
|
|
1015
|
+
if (cmd === "nice" || cmd === "throttle") {
|
|
1016
|
+
// `/nice <line>` runs one line throttled without touching the setting --
|
|
1017
|
+
// the form anyone who has used nice(1) reaches for first. The settings
|
|
1018
|
+
// verbs win the name, so a command called `on` is unreachable this way;
|
|
1019
|
+
// that is the right trade for `/nice on` meaning what it obviously means.
|
|
1020
|
+
const sub = String(rest[0] ?? "").toLowerCase().replace(/^\//, "");
|
|
1021
|
+
// A leading flag (`/nice --json`) is asking about the throttle, not
|
|
1022
|
+
// naming a program: no line the pit runs starts with a dash.
|
|
1023
|
+
if (!rest.length || sub.startsWith("-") || NICE_SETTING_VERBS.has(sub)) {
|
|
1024
|
+
niceCommand(rest);
|
|
1025
|
+
continue;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Hand the remainder back to the top of this loop rather than dispatching
|
|
1029
|
+
// it here, exactly as alias expansion does. That is what makes `/nice`
|
|
1030
|
+
// work on anything the pit can already run -- a pit command, an engine, a
|
|
1031
|
+
// tool, an alias, or a bare shell line -- with no roster of its own to
|
|
1032
|
+
// drift out of date. The leading slash is optional because the dispatcher
|
|
1033
|
+
// strips one anyway, so `/nice agents claude` and `/nice /agents claude`
|
|
1034
|
+
// are the same line.
|
|
1035
|
+
armOneShot();
|
|
1036
|
+
pending = commandRemainder(line);
|
|
1037
|
+
console.log(ash(` ▸ throttled: ${pending}`));
|
|
1038
|
+
continue;
|
|
1039
|
+
}
|
|
998
1040
|
if (cmd === "pwd" || cmd === "where") { printPwd(); continue; }
|
|
999
1041
|
if (cmd === "login") {
|
|
1000
1042
|
const device = rest.includes("--device") || rest.includes("device") || rest.includes("-d");
|