Skip to content

Commit a29353b

Browse files
ralyodioclaude
andcommitted
tools: add BufferOverride (bo), and its MCP server to the catalog
`moshcode install bo` installs @profullstack/bufferoverride, and `/bo …` passes straight through to it: wrap a failing command, keep what it printed with the environment that produced it, redact the secrets, and find the answer that already exists. No upgrade key — `npm install -g` is idempotent, the same as mcpjam and railway. Keyed `bo` rather than `bufferoverride`, the other way round from secrets/logicsrc and spinifex/spx: the binary, the product's own documentation and the pit command all say `bo`, and this is a command you type every time something fails. The MCP catalog gains the server too, under the same name `bo mcp config` emits so registering it either way produces one server rather than two. It is a remote HTTP target with no args, and no `env`: the credential is a bearer header, and five read tools work with no credential at all, so the bare `moshcode mcp add bufferoverride` is already a complete registration. That exposed a real gap — a catalog entry's `note` and `docs` were printed only when a required environment variable was missing, so they were invisible for exactly the servers whose credential is a header or which need none. They now print whenever the catalog was used. `catalogList` also derives its column width instead of padding to a fixed 10, which `bufferoverride` overruns. Verified against the published package: `bo --version` is 0.1.0, and `bo search` returns results unauthenticated. Full suite: 2444 pass, 0 fail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent eb9ae2d commit a29353b

8 files changed

Lines changed: 172 additions & 4 deletions

File tree

‎README.md‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,40 @@ moshcode ugig --json gigs list # arguments/output go straight to ugig
590590
moshcode coinpay wallet balance # arguments/output go straight to coinpay
591591
```
592592

593+
### BufferOverride — the failure in front of you, already answered
594+
595+
[BufferOverride](https://bufferoverride.com) is where humans and agents debug
596+
together: every answer declares the versions it works on, who or what wrote it,
597+
and whether anyone independent reproduced it. `bo` is that from a terminal.
598+
599+
```sh
600+
moshcode install bo # npm i -g @profullstack/bufferoverride
601+
602+
moshcode bo run -- pnpm test # run it, keep what it printed, search for it
603+
moshcode bo search "worker exited before finishing"
604+
moshcode bo get a1b2c3d4e5 --markdown
605+
```
606+
607+
The product is BufferOverride and the binary is `bo` — the same split
608+
`secrets`/`logicsrc` and `spinifex`/`spx` have, keyed the short way round here
609+
because this is a command you type every time something fails.
610+
611+
`bo run --` wraps a command rather than replacing it: the wrapped command's exit
612+
code passes straight through, so it can go in front of something already in CI
613+
without changing what CI sees. It captures stdout, stderr, the exit code, the
614+
OS, the architecture and the detected dependency versions, redacts what it
615+
recognises as a secret, and searches for the failure **before** offering to
616+
publish it. Nothing leaves the machine until you have seen it, and outside a TTY
617+
nothing is published at all unless you pass `--ask`.
618+
619+
Redaction is best effort and cannot be complete — no pattern list catches a
620+
custom-format secret — so `--dry-run` is the habit its own docs ask for.
621+
622+
Reads need no credential: `search` and `get` work before you have ever run `bo
623+
login`. Publishing needs one, and `bo login` is a device-code exchange, so a
624+
terminal never handles a browser session. `bo mcp config` prints the MCP
625+
registration for a coding agent, which is the same graph over a different door.
626+
593627
### Cloud + infra CLIs
594628

595629
```sh
@@ -1038,6 +1072,7 @@ Some MCP servers are worth remembering by name rather than by npx invocation:
10381072
```sh
10391073
moshcode mcp catalog # what we know how to run
10401074
moshcode mcp add porkbun # expands to: npx -y @porkbunllc/mcp-server
1075+
moshcode mcp add bufferoverride # expands to: https://bufferoverride.com/mcp
10411076
```
10421077

10431078
That registers it across every engine that supports MCP (claude, gemini, qwen,
@@ -1059,6 +1094,23 @@ from and five to rotate. Porkbun's API access is off by default and enabled
10591094
per-domain — and its documentation tools work with no keys at all, which is a
10601095
sensible way to try the server before trusting it with DNS writes.
10611096

1097+
**BufferOverride is the useful-unauthenticated one.** Five read tools
1098+
(`search_questions`, `get_question`, `list_questions`, `list_tags`, `whoami`)
1099+
work with no credential, and the write tools are gated on the scopes a key
1100+
actually carries — `tools/list` advertises only what your key can use. So the
1101+
bare `mcp add bufferoverride` above is a complete, working registration. To
1102+
publish from an engine, add the credential as a header:
1103+
1104+
```sh
1105+
moshcode mcp add bufferoverride -H "Authorization: Bearer bo_..."
1106+
```
1107+
1108+
`bo mcp config` prints the same thing from a terminal that has already signed
1109+
in, and `bo mcp config --no-token` prints a form safe to paste in public. It is
1110+
registered under the name the CLI uses, so both routes produce one server rather
1111+
than two. See [BufferOverride](#bufferoverride--the-failure-in-front-of-you-already-answered)
1112+
above for the CLI itself.
1113+
10621114
## Claude Code plugins
10631115

10641116
MoshCode publishes its own plugin marketplace, so the pit's slash commands work
@@ -1371,6 +1423,7 @@ chmod +x deploy.mosh
13711423
| `coinpay(args…)` | drive the coinpay workflow CLI |
13721424
| `c0mpute(args…)` | drive the c0mpute workflow CLI |
13731425
| `c0upons(args…)` | drive the c0upons workflow CLI |
1426+
| `bo(args…)` | drive the BufferOverride CLI (capture a failure, search, ask, answer, verify) |
13741427
| `secrets(args…)` | drive the logicsrc secrets CLI |
13751428
| `railway(args…)` | drive the Railway CLI |
13761429
| `gh(args…)` | drive the GitHub CLI |

‎src/commands.mjs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ const COMMANDS = [
668668
cliVerb("coinpay", "drive the coinpay workflow CLI"),
669669
cliVerb("c0mpute", "drive the c0mpute workflow CLI"),
670670
cliVerb("c0upons", "drive the c0upons workflow CLI"),
671+
cliVerb("bo", "drive the BufferOverride CLI (capture a failure, search, ask, answer, verify)"),
671672
cliVerb("secrets", "manage/view team secrets via logicsrc (login, teams, credentials)"),
672673
cliVerb("railway", "drive the Railway CLI (deploys, services, env vars)"),
673674
cliVerb("gh", "drive the GitHub CLI (repos, PRs, issues, releases)"),

‎src/integrations.mjs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,14 @@ export async function mcpCommand(tokens, { run, installedSet } = {}) {
238238
const missing = (parsed.catalog?.env || []).filter((k) => !process.env[k]);
239239
if (missing.length) {
240240
console.log(ash(` note: ${spec.name} needs ${missing.join(" and ")} in the environment.`));
241-
if (parsed.catalog?.note) console.log(ash(` ${parsed.catalog.note}`));
242-
if (parsed.catalog?.docs) console.log(ash(` ${parsed.catalog.docs}`));
243241
}
242+
// The catalog's own note and docs are printed whenever the catalog was used,
243+
// not only when a variable is missing. A server whose credential is a header
244+
// rather than an environment variable — or one that needs none at all to be
245+
// useful — has nothing in `env`, and hanging its note off that check is what
246+
// made the note invisible for exactly the servers it was written for.
247+
if (parsed.catalog?.note) console.log(ash(` note: ${parsed.catalog.note}`));
248+
if (parsed.catalog?.docs) console.log(ash(` ${parsed.catalog.docs}`));
244249
if (spec.headers.length || /^https?:/i.test(spec.target)) {
245250
console.log(ash(" note: OAuth/HTTP servers may still need per-engine auth (e.g. `opencode mcp auth`, `codex mcp login`)."));
246251
}

‎src/mcp-catalog.mjs‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@ export const MCP_CATALOG = {
2424
// before deciding whether to trust it with keys.
2525
note: "API access is off by default and must be enabled per-domain; the docs tools work without keys",
2626
},
27+
bufferoverride: {
28+
// A remote HTTP server, so the target is the URL and there are no args —
29+
// every engine's builder pushes the target alone for a remote server.
30+
target: "https://bufferoverride.com/mcp",
31+
args: [],
32+
desc: "BufferOverride — version-aware technical answers, with provenance and reproductions",
33+
docs: "https://bufferoverride.com/docs/mcp",
34+
// Named to match what the CLI's own `bo mcp config` emits, so registering
35+
// it either way produces one server rather than two under different names.
36+
//
37+
// No `env`: the credential is a bearer header, not a variable, and it is
38+
// deliberately not listed here. Five read tools work with no key at all,
39+
// and the write tools are gated on the scopes a key actually carries — so
40+
// the useful default really is unauthenticated. `bo mcp config` prints the
41+
// header form for a terminal that has signed in.
42+
note: "reads need no credential; to publish, add -H \"Authorization: Bearer bo_…\" (see `bo mcp config`)",
43+
},
2744
};
2845

2946
/** Resolve a catalog name to a spec fragment, or null. Own properties only. */
@@ -44,7 +61,10 @@ export function catalogNames() {
4461

4562
/** One line per known server, for `mcp catalog`. */
4663
export function catalogList() {
64+
// Width from the longest name rather than a fixed pad: `bufferoverride` is
65+
// wider than the old 10, and a name that overruns the pad loses the column.
66+
const width = Math.max(10, ...Object.keys(MCP_CATALOG).map((key) => key.length));
4767
return Object.entries(MCP_CATALOG)
48-
.map(([key, e]) => ` ${key.padEnd(10)} ${e.desc}`)
68+
.map(([key, e]) => ` ${key.padEnd(width)} ${e.desc}`)
4969
.join("\n");
5070
}

‎src/tools.mjs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,19 @@ export const TOOLS = {
8282
// where those words mean something else entirely.
8383
aliases: { cmd: "cli-tools", args: ["aliases", "--json"] },
8484
},
85+
bo: {
86+
desc: "BufferOverride — capture a failing command, redact it, and find the answer that already exists",
87+
// The product is BufferOverride and the binary is `bo`, the same split
88+
// `secrets` → `logicsrc` and `spinifex` → `spx` have. It is keyed the other
89+
// way round from those two on purpose: `bo` is what its own documentation
90+
// tells you to type, and this is a command you run every time something
91+
// fails, so the short word is the one worth having in the pit.
92+
bin: "bo",
93+
// An ordinary global npm package with no dependencies of its own, and
94+
// `npm install -g` is idempotent, so re-running the install IS the upgrade
95+
// — no `upgrade` key, the same as mcpjam and railway.
96+
install: { cmd: "npm", args: ["install", "-g", "@profullstack/bufferoverride"] },
97+
},
8598
secrets: {
8699
desc: "LogicSRC — end-to-end-encrypted team credential sharing (login, teams, credentials)",
87100
// The passthrough target is the `logicsrc` binary; the moshcode command is

‎test/cli.test.mjs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ test("runMoshcode stringifies args and never spawns under dry-run", async () =>
114114

115115
test("the CLI capabilities are all registered as verbs", () => {
116116
const reg = moshVocabulary();
117-
for (const name of ["agents", "start", "install", "upgrade", "mcp", "skill", "prd", "ugig", "coinpay", "c0mpute", "c0upons", "secrets", "alpaca", "trade", "pwd", "ai"]) {
117+
for (const name of ["agents", "start", "install", "upgrade", "mcp", "skill", "prd", "ugig", "coinpay", "c0mpute", "c0upons", "bo", "secrets", "alpaca", "trade", "pwd", "ai"]) {
118118
assert.ok(reg.has(name), `expected ${name}() in the vocabulary`);
119119
}
120120
});
@@ -136,6 +136,7 @@ const VERB_ARGV_CASES = [
136136
{ verb: "coinpay", args: ["wallet", "balance"], expect: /moshcode coinpay wallet balance/ },
137137
{ verb: "c0mpute", args: ["status"], expect: /moshcode c0mpute status/ },
138138
{ verb: "c0upons", args: ["search", "nike"], expect: /moshcode c0upons search nike/ },
139+
{ verb: "bo", args: ["search", "libsql worker"], expect: /moshcode bo search libsql worker/ },
139140
{ verb: "secrets", args: ["teams", "list"], expect: /moshcode secrets teams list/ },
140141
{ verb: "alpaca", args: ["asset", "get", "--symbol-or-asset-id", "AAPL"], expect: /moshcode alpaca asset get --symbol-or-asset-id AAPL/ },
141142
{ verb: "trade", args: ["ticker", "AAPL"], expect: /moshcode trade ticker AAPL/ },

‎test/mcp-catalog.test.mjs‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,40 @@ test("the catalog listing names every entry", () => {
8181
for (const name of catalogNames()) assert.match(listing, new RegExp(name));
8282
assert.match(listing, /Porkbun/);
8383
});
84+
85+
test("bufferoverride resolves to the remote HTTP server, with no args", () => {
86+
const e = resolveCatalog("bufferoverride");
87+
assert.equal(e.key, "bufferoverride");
88+
assert.equal(e.target, "https://bufferoverride.com/mcp");
89+
// A remote server takes no command arguments — every engine's builder pushes
90+
// the target alone — and parseMcp rejects a leftover one rather than dropping
91+
// it, so an entry that carried args would break `mcp add bufferoverride`.
92+
assert.deepEqual(e.args, []);
93+
// The credential is a bearer header, not a variable: nothing to name here.
94+
assert.equal(e.env, undefined);
95+
});
96+
97+
test("`mcp add bufferoverride` expands to the remote spec", () => {
98+
const { spec, catalog, error } = parseMcp(["add", "bufferoverride"]);
99+
assert.equal(error, undefined);
100+
// Named as the CLI's own `bo mcp config` names it, so registering it either
101+
// way produces one server rather than two.
102+
assert.equal(spec.name, "bufferoverride");
103+
assert.equal(spec.target, "https://bufferoverride.com/mcp");
104+
assert.deepEqual(spec.args, []);
105+
assert.deepEqual(spec.headers, []);
106+
assert.equal(catalog.key, "bufferoverride");
107+
});
108+
109+
test("the catalog listing keeps its column when a name overruns the old pad", () => {
110+
// `bufferoverride` is longer than the fixed pad this used to carry, which
111+
// left a single space before its description while porkbun kept a column.
112+
const rows = catalogList().split("\n");
113+
assert.ok(rows.length >= 2, "this only says anything with more than one entry");
114+
const columns = Object.values(MCP_CATALOG).map((e) => {
115+
const row = rows.find((line) => line.includes(e.desc));
116+
assert.ok(row, `no row for ${e.desc}`);
117+
return row.indexOf(e.desc);
118+
});
119+
assert.equal(new Set(columns).size, 1, `descriptions must line up:\n${catalogList()}`);
120+
});

‎test/tools.test.mjs‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,44 @@ test("MCPJam is a workflow tool installed from its official npm package", () =>
185185
assert.match(toolList(), /mcpjam/);
186186
});
187187

188+
test("BufferOverride is a workflow tool installed from its official npm package", () => {
189+
// Keyed `bo` rather than `bufferoverride`: the binary, the pit command and
190+
// the product's own documentation all say `bo`.
191+
assert.deepEqual(resolveTool("BO"), ["bo", TOOLS.bo]);
192+
assert.equal(TOOLS.bo.bin, "bo");
193+
assert.deepEqual(TOOLS.bo.install, {
194+
cmd: "npm",
195+
args: ["install", "-g", "@profullstack/bufferoverride"],
196+
});
197+
// Same reasoning as mcpjam: `npm install -g` is idempotent, so re-running the
198+
// install IS the upgrade. Asserted so adding one later is deliberate.
199+
assert.equal(TOOLS.bo.upgrade, undefined);
200+
assert.deepEqual(toolUpgradeSpec(TOOLS.bo), TOOLS.bo.install);
201+
assert.match(toolList(), /bo /);
202+
assert.match(toolList(), /BufferOverride/);
203+
});
204+
205+
test("install bo delegates to the official npm package", async () => {
206+
const root = tempDir("moshcode-install-bo-");
207+
const nativeBin = path.join(root, "bin");
208+
const capture = path.join(root, "npm-args.json");
209+
mkdirSync(nativeBin);
210+
writeExecutable(nativeBin, "npm", `
211+
import fs from "node:fs";
212+
fs.writeFileSync(process.env.NPM_CAPTURE, JSON.stringify(process.argv.slice(2)));
213+
`);
214+
215+
const result = await run(["install", "bo"], {
216+
binDir: nativeBin,
217+
env: { NPM_CAPTURE: capture },
218+
});
219+
220+
assert.equal(result.status, 0, result.stderr);
221+
assert.deepEqual(JSON.parse(readFileSync(capture, "utf8")), [
222+
"install", "-g", "@profullstack/bufferoverride",
223+
]);
224+
});
225+
188226
test("install mcpjam delegates to the official npm package", async () => {
189227
const root = tempDir("moshcode-install-mcpjam-");
190228
const nativeBin = path.join(root, "bin");

0 commit comments

Comments
 (0)