moshcode 0.92.0 → 0.94.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 CHANGED
@@ -26,7 +26,7 @@ or miss one that does. A test fails the build when it drifts.
26
26
  <!-- COMMANDS:START -->
27
27
  | command | group | what it does |
28
28
  |---|---|---|
29
- | `moshcode agents` | engines | list engines or launch one autonomously |
29
+ | `moshcode agents` | engines | list engines, open their agent view, or launch autonomously |
30
30
  | `moshcode start` | engines | launch an engine with its native defaults |
31
31
  | `moshcode herd` | runtime | run agent sessions that outlive this terminal |
32
32
  | `moshcode ps` | runtime | list herd sessions and what each one is doing |
@@ -107,7 +107,7 @@ use this only in an isolated container, VM, or workspace you trust:
107
107
  moshcode agents claude # claude agents --dangerously-skip-permissions (agent view)
108
108
  moshcode agents opencode # opencode --auto (autonomous)
109
109
  moshcode agents privacycode # privacycode --auto (autonomous)
110
- moshcode agents codex # codex --dangerously-bypass-approvals-and-sandbox (autonomous)
110
+ moshcode agents codex # codex --dangerously-bypass-approvals-and-sandbox agents (agent view)
111
111
  moshcode agents gemini # gemini --approval-mode=yolo (autonomous)
112
112
  moshcode agents kimi # kimi --yolo (autonomous)
113
113
  moshcode agents qwen # qwen --approval-mode=yolo (autonomous)
@@ -116,6 +116,25 @@ moshcode agents aider # aider --yes-always
116
116
  moshcode agents openagents # openagents (dashboard)
117
117
  ```
118
118
 
119
+ Codex's native agents overview requires a CLI with `codex agents` support
120
+ (verified with 0.151.0) and, for the local daemon, the managed standalone
121
+ installation. `moshcode install codex` / `moshcode upgrade codex` use Codex's
122
+ official standalone installer on macOS/Linux; an npm-only install is not enough.
123
+ Existing npm installations are preserved. Windows retains npm and requires
124
+ `--remote <server>` for the agents overview.
125
+ `/agents codex` opens the same live overview of sessions on Codex's shared local
126
+ app-server daemon. The approval/sandbox bypass is passed for this invocation;
127
+ moshcode does not rewrite your global Codex settings or stop existing sessions.
128
+ Additional arguments are forwarded to the native `agents` subcommand, for example
129
+ `moshcode agents codex --no-alt-screen`.
130
+
131
+ For separate conversations, Codex's `/new` starts a fresh chat in the same CLI,
132
+ `/resume` reopens a saved chat, and `/fork` branches a conversation while preserving
133
+ the original. These commands are distinct from launching parallel subagents:
134
+ ask Codex to delegate work, then use its `/agent` or `/subagents` picker to switch
135
+ threads. See the [Codex command reference](https://learn.chatgpt.com/docs/developer-commands?surface=cli)
136
+ and [subagent guide](https://learn.chatgpt.com/docs/agent-configuration/subagents).
137
+
119
138
  `start` is the explicit raw path. It injects nothing, so the native engine keeps
120
139
  its normal permission model and receives only your arguments:
121
140
 
@@ -723,6 +742,44 @@ login`. Publishing needs one, and `bo login` is a device-code exchange, so a
723
742
  terminal never handles a browser session. `bo mcp config` prints the MCP
724
743
  registration for a coding agent, which is the same graph over a different door.
725
744
 
745
+ ### CrawlProof — what the fleet costs and what it returns
746
+
747
+ [CrawlProof](https://crawlproof.com) knows who arrived on your sites and what
748
+ your ads delivered. `crawlproof` joins that to what the bank actually did, so
749
+ the terminal can answer the question a dashboard usually cannot: is any of this
750
+ paying for itself.
751
+
752
+ ```sh
753
+ moshcode install crawlproof # npm i -g @profullstack/crawlproof
754
+
755
+ moshcode crawlproof # the live dashboard, last day, humans
756
+ moshcode crawlproof dashboard --range=1m
757
+ moshcode crawlproof stats site.com
758
+ moshcode crawlproof dashboard --json | jq .roi.derived
759
+ ```
760
+
761
+ Five screens: ROI, Traffic, Ads, Money, Spend. Two rules run through the
762
+ arithmetic and both exist because breaking either produces a nicer number that
763
+ is false. Where an account advertises on its own slots, ad spend and ad earnings
764
+ are one dollar moving between two pockets, so they are reported under *Internal*
765
+ and counted as neither cost nor revenue. And a bank feed carries groceries next
766
+ to servers, so cost is the business scope only.
767
+
768
+ It also says what it does not know, next to the number: a site that did not
769
+ answer is missing rather than zero, and a fleet whose visits run far above its
770
+ pageviews says so and offers the per-pageview figure instead.
771
+
772
+ Needs a CrawlProof API token in `CRAWLPROOF_TOKEN` or the `token` field of
773
+ `~/.crawlproof.json`. The money screens want a CoinPay merchant session as well;
774
+ without one the other four still work and the money panels say what is missing.
775
+ The dashboard is a TUI and wants Node 22.6+, while `stats` and `--json` run
776
+ anywhere.
777
+
778
+ `crawlproof` also ships in the `cli-tools` set, which symlinks a wrapper of the
779
+ same name that vendors this package. Installing both is fine: the wrapper hands
780
+ over to a `crawlproof` on PATH that is not its own, and refuses to follow one
781
+ that is.
782
+
726
783
  ### Cloud + infra CLIs
727
784
 
728
785
  ```sh
@@ -1017,15 +1074,34 @@ Run a few engines at once on a box you also want to type on and you get the
1017
1074
  failure everyone knows: nothing crashed, but the machine stops answering.
1018
1075
 
1019
1076
  ```text
1020
- /nice on # nice -n10 + ionice -c2 -n7 for every engine started after
1077
+ /nice agents claude # throttle this one engine
1078
+ /nice pnpm -r build # …or this one shell line
1079
+ /nice merge # …or any other pit command
1080
+
1081
+ /nice on # or throttle everything from here on
1021
1082
  /nice mem 2G # a ceiling, so a runaway dies alone
1022
1083
  /nice cpu 15 # yield more (nice takes -20..19)
1023
1084
  /nice # what it is set to
1024
1085
  /nice off # back to normal priority (the default)
1025
1086
  ```
1026
1087
 
1027
- It is off by default a throttle nobody asked for is a slow engine nobody can
1028
- explainand it applies to engines started *after* you turn it on.
1088
+ `/nice <line>` is the form to reach for. It runs **anything the pit can already
1089
+ run**a pit command, an engine, a tool, one of your aliases, or a bare shell
1090
+ line — at low priority, without changing any setting. It works on all of those
1091
+ because it hands the rest of the line back to the top of the dispatcher exactly
1092
+ as an alias expansion does, rather than keeping a list of its own that would
1093
+ drift. The leading slash is optional: `/nice agents claude` and
1094
+ `/nice /agents claude` are the same line.
1095
+
1096
+ The throttle lasts exactly as long as the line that asked for it, including
1097
+ through an alias that expands into something else. The next thing you type is
1098
+ back to normal.
1099
+
1100
+ `/nice on` is the other half, for when the box is shared all day and you would
1101
+ rather decide once. It is off by default — a throttle nobody asked for is a slow
1102
+ engine nobody can explain — and it applies to engines started *after* you turn
1103
+ it on. The settings words (`on`, `off`, `status`, `cpu`, `io`, `mem`) win the
1104
+ first position, so a program named `on` is not reachable through `/nice`.
1029
1105
 
1030
1106
  **`nice` alone is half a fix, and it is worth knowing which half.** It reorders
1031
1107
  CPU, so it buys back the part of a freeze you could have waited out. It does
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moshcode",
3
- "version": "0.92.0",
3
+ "version": "0.94.0",
4
4
  "type": "module",
5
5
  "description": "moshcode \u2014 a metal wrapper for coding engines and native UGig/CoinPay workflow CLIs, with OpenPRD and moshscript",
6
6
  "repository": {
@@ -35,7 +35,7 @@ export const CORE_CLI_COMMANDS = [
35
35
  {
36
36
  name: "agents",
37
37
  group: "engines",
38
- description: "list engines or launch one autonomously",
38
+ description: "list engines, open their agent view, or launch autonomously",
39
39
  synopsis: [
40
40
  ["moshcode agents", "list engines and their install status"],
41
41
  ["moshcode agents --json", "list engine status as machine-readable JSON"],
@@ -48,6 +48,7 @@ export const CORE_CLI_COMMANDS = [
48
48
  ["moshcode agents", "which engines are here"],
49
49
  ["moshcode agents --json", "pipe engine status into a script"],
50
50
  ["moshcode agents claude", "claude's agent list"],
51
+ ["moshcode agents codex", "Codex's shared-daemon agents overview (CLI 0.151.0+)"],
51
52
  ],
52
53
  seeAlso: ["start", "engines", "install"],
53
54
  note: "autonomous modes bypass approval prompts — use them in a container or a workspace you trust.",
@@ -1518,7 +1519,7 @@ export const PIT_COMMANDS = [
1518
1519
  { name: "new", pitOnly: true,
1519
1520
  description: "open and switch to another moshcode tab" },
1520
1521
  { name: "agents", aliases: ["agent", "engines"], args: "[name]", cli: "agents",
1521
- description: "list engines, or launch one autonomously" },
1522
+ description: "list engines, open their agent view, or launch autonomously" },
1522
1523
  { name: "start", args: "<engine> [args…]", cli: "start",
1523
1524
  description: "raw launch; inject no engine arguments" },
1524
1525
  { name: "herd", args: "[verb] [args…]", cli: "herd",
@@ -1596,20 +1597,23 @@ export const PIT_COMMANDS = [
1596
1597
  description: "show the current dir + git repo/branch/origin" },
1597
1598
  { name: "shell", aliases: ["sh"], args: "[cmd]", pitOnly: true,
1598
1599
  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 engines at low priority so the box stays usable",
1600
+ { name: "nice", aliases: ["throttle"], args: "<command> | on | off | cpu <n> | io <n> | mem <size>", pitOnly: true,
1601
+ description: "run something at low priority so the box stays usable",
1601
1602
  synopsis: [
1603
+ ["/nice <command> [args]", "run one line throttled — a pit command, engine, tool, alias or shell line"],
1602
1604
  ["/nice [status]", "what the throttle is set to"],
1603
- ["/nice on | off", "toggle it (off by default)"],
1605
+ ["/nice on | off", "throttle everything from here on (off by default)"],
1604
1606
  ["/nice cpu <-20..19>", "nice level — higher yields more CPU"],
1605
1607
  ["/nice io <0..7>", "ionice best-effort level"],
1606
1608
  ["/nice mem <size> | off", "memory ceiling per engine (needs systemd)"],
1607
1609
  ],
1608
1610
  examples: [
1611
+ ["/nice agents claude", "throttle this engine, leave the setting alone"],
1612
+ ["/nice pnpm -r build", "a shell line works too"],
1609
1613
  ["/nice on", "nice -n10 + ionice -c2 -n7 for every engine started after"],
1610
1614
  ["/nice mem 2G", "the ceiling nice(1) can't give you — a runaway dies alone"],
1611
1615
  ],
1612
- seeAlso: ["agents", "start"] },
1616
+ seeAlso: ["agents", "start", "alias"] },
1613
1617
  { name: "alias", aliases: ["aliases"], args: 'set <name> "<cmd>" | list | get | rm | install <tool>', pitOnly: true,
1614
1618
  description: "name a line you keep retyping; /<name> runs it",
1615
1619
  synopsis: [
package/src/commands.mjs CHANGED
@@ -826,7 +826,7 @@ const COMMANDS = [
826
826
  cliVerb("run", "run another .mosh file (include)"),
827
827
  // shortcut: ai() runs an engine headlessly and RETURNS its output (see PRD R17)
828
828
  aiVerb,
829
- cliVerb("agents", "launch an autonomous agent session (moshcode agents <engine>)"),
829
+ cliVerb("agents", "open the native agent view or launch autonomously (moshcode agents <engine>)"),
830
830
  cliVerb("herd", "drive the herd (moshcode herd <verb>) — see herdStart/herdWait for values"),
831
831
  cliVerb("ps", "print the herd roster"),
832
832
  cliVerb("cost", "print what the herd is spending (moshcode cost [name] [--all])"),
package/src/engines.mjs CHANGED
@@ -5,8 +5,8 @@
5
5
  //
6
6
  // `agentsView` (optional) is the exact argv that opens the engine's native
7
7
  // agent list/view — used by `/agents <name>` when the engine actually has one
8
- // (currently claude). It's the FULL leading args (subcommand + any flags that
9
- // subcommand accepts). Engines without an `agentsView` fall back to
8
+ // (currently Claude and Codex). It's the FULL leading args, including root
9
+ // flags before the subcommand when required. Engines without an `agentsView` fall back to
10
10
  // `agentArgs` — an autonomous session with native approvals
11
11
  // bypassed/auto-approved. Do not use a machine-readable, one-shot list command
12
12
  // as an agents view: `/agents` promises to hand the terminal to a live session.
@@ -131,7 +131,16 @@ export const ENGINES = {
131
131
  desc: "Codex — OpenAI's coding CLI",
132
132
  bin: "codex",
133
133
  agentArgs: ["--dangerously-bypass-approvals-and-sandbox"],
134
- install: { cmd: "npm", args: ["install", "-g", "@openai/codex"] },
134
+ // Codex 0.151.0+: live shared-daemon session overview. The bypass option
135
+ // belongs to the root command, not the `agents` subcommand.
136
+ agentsView: ["--dangerously-bypass-approvals-and-sandbox", "agents"],
137
+ // The local agents daemon requires the installer-managed standalone
138
+ // package; npm alone can launch chats but cannot bootstrap that daemon.
139
+ // Windows uses a remote agents server and retains its npm installation.
140
+ install: process.platform === "win32"
141
+ ? { cmd: "npm", args: ["install", "-g", "@openai/codex"] }
142
+ : { cmd: "bash", args: ["-o", "pipefail", "-c", "curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh"] },
143
+ binDirs: [path.join(homedir(), ".local", "bin")],
135
144
  resume: ["resume", "--last"],
136
145
  state: {
137
146
  blocked: [/\ballow (?:this )?command\b/i, /\bapprove this (?:command|edit|change)\b/i],
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 = loadNice(),
174
+ settings = effectiveNice(),
139
175
  has = haveBin,
140
176
  env = process.env,
141
177
  platform = process.platform,
package/src/tools.mjs CHANGED
@@ -88,6 +88,23 @@ export const TOOLS = {
88
88
  // where those words mean something else entirely.
89
89
  aliases: { cmd: "cli-tools", args: ["aliases", "--json"] },
90
90
  },
91
+ crawlproof: {
92
+ desc: "CrawlProof \u2014 what the fleet costs and what it returns: traffic, ad delivery and the bank behind it",
93
+ bin: "crawlproof",
94
+ // Reachable two ways on purpose. The `cli-tools` set above symlinks a
95
+ // wrapper of the same name, and this entry installs the package that
96
+ // wrapper vendors. Having both is safe: the wrapper hands over to a
97
+ // `crawlproof` on PATH that is not its own, and refuses to follow one that
98
+ // is, which would be an exec loop. It is its own entry because
99
+ // `/install crawlproof` should install CrawlProof rather than seven other
100
+ // commands that happen to travel with it.
101
+ //
102
+ // The dashboard is a TUI and wants Node 22.6+; `crawlproof stats` and
103
+ // `--json` run anywhere. An ordinary global npm package, and
104
+ // `npm install -g` is idempotent, so the install is also the upgrade \u2014
105
+ // no `upgrade` key, the same as bo and railway.
106
+ install: { cmd: "npm", args: ["install", "-g", "@profullstack/crawlproof"] },
107
+ },
91
108
  timer: {
92
109
  desc: "Profullstack timer - track time against projects, for people and for agents",
93
110
  bin: "timer",
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 { loadNice, saveNice, describeNice, canCapMemory, parseMemory } from "./nice.mjs";
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") { niceCommand(rest); continue; }
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");