Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,15 +524,16 @@ first time — what is wrong with your `rules.json` instead of ignoring it.
### The engine's settings, the way a herd wants them

An engine can also say how it would like to be configured. Claude Code's
defaults are **ultracode on** (every substantive prompt runs as a workflow of
agents), **small workflows** (Claude's own advisory tier, fewer than 5 agents
each) and a **hard cap of 4 agents at once**, so one session cannot eat the box
the rest of the herd is running on. `moshcode install claude` applies them;
by hand:
defaults are **ultracode off** (a prompt runs as a workflow of agents only when
you ask for one in so many words; neither the setting nor the "ultracode"
keyword turns it on by itself), **small workflows** (Claude's own advisory
tier, fewer than 5 agents each) and a **hard cap of 4 agents at once**, so a
workflow you do ask for cannot eat the box the rest of the herd is running on.
`moshcode install claude` applies them; by hand:

```sh
moshcode engines defaults apply claude
✓ claude — 3 defaults applied (ultracode on by default, small workflows (under 5 agents), 4 agents at once, hard cap)
✓ claude — 4 defaults applied (ultracode off by default, no ultracode keyword trigger, small workflows (under 5 agents), 4 agents at once, hard cap)
```

Same rule as the hooks: the file is merged, never clobbered. A key you already
Expand Down
2 changes: 1 addition & 1 deletion src/cli-schema.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ export const CORE_CLI_COMMANDS = [
examples: [
["moshcode engines", "who is installed"],
["moshcode engines defaults", "per engine: which of its defaults are set, missing, or yours"],
["moshcode engines defaults apply claude", "ultracode on, small workflows, 4 agents at once"],
["moshcode engines defaults apply claude", "ultracode off, small workflows, 4 agents at once"],
["moshcode engines defaults remove claude", "take them back out"],
],
seeAlso: ["agents", "install"],
Expand Down
20 changes: 13 additions & 7 deletions src/engines.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -122,21 +122,27 @@ export const ENGINES = {
// above — a key the operator already set is never touched, so this is a
// floor under a fresh install and not a policy over an old one.
//
// Why these three: a coding session that fans out to a workflow by default
// is what a herd of agents is for, and the two caps keep one session from
// eating the box the rest of the herd is running on. "small" is Claude's
// own advisory tier (fewer than 5 agents per workflow); the env var is the
// hard gate on how many run at once, and 4 leaves room for the other three.
// Why these four: ultracode (every substantive prompt becomes a workflow
// of agents) and its keyword trigger (the word "ultracode" anywhere in a
// prompt does the same for that turn) are both OFF. Left on, a herd of
// sessions fanning out by default ran the bill up by the hour, so a
// workflow now runs only when the operator asks for one in so many words.
// The two caps stay for the workflows that are asked for: "small" is
// Claude's own advisory tier (fewer than 5 agents per workflow); the env
// var is the hard gate on how many run at once, and 4 leaves room for the
// other three sessions on the box.
settings: {
format: "claude-settings",
file: () => path.join(homedir(), ".claude", "settings.json"),
defaults: {
ultracode: true,
ultracode: false,
workflowKeywordTriggerEnabled: false,
workflowSizeGuideline: "small",
env: { CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS: "4" },
},
labels: {
"ultracode": "ultracode on by default",
"ultracode": "ultracode off by default",
"workflowKeywordTriggerEnabled": "no ultracode keyword trigger",
"workflowSizeGuideline": "small workflows (under 5 agents)",
"env.CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS": "4 agents at once, hard cap",
},
Expand Down
43 changes: 23 additions & 20 deletions test/engine-settings.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ const read = (file) => JSON.parse(fs.readFileSync(file, "utf8"));

/* ---------------------------------------------------------------- the spec */

test("claude ships ultracode on, small workflows, and a hard cap of four agents", () => {
// The three values this feature exists to carry. Change them here and in
test("claude ships ultracode off, no keyword trigger, small workflows, and a hard cap of four agents", () => {
// The four values this feature exists to carry. Change them here and in
// the README together — the numbers in the prose are these.
assert.deepEqual(ENGINES.claude.settings.defaults, {
ultracode: true,
ultracode: false,
workflowKeywordTriggerEnabled: false,
workflowSizeGuideline: "small",
env: { CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS: "4" },
});
Expand All @@ -49,7 +50,7 @@ test("the spec points at Claude Code's own settings file", () => {

test("nested defaults flatten to one leaf per key", () => {
const keys = defaultEntries("claude").map((e) => e.key);
assert.deepEqual(keys, ["ultracode", "workflowSizeGuideline", "env.CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS"]);
assert.deepEqual(keys, ["ultracode", "workflowKeywordTriggerEnabled", "workflowSizeGuideline", "env.CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS"]);
assert.ok(defaultEntries("claude").every((e) => e.label && e.label !== e.key), "every default carries a human label");
assert.deepEqual(defaultEntries("codex"), []);
});
Expand All @@ -64,12 +65,13 @@ test("applying fills holes and leaves everything else alone", () => {
}, (file) => {
const result = applyEngineSettings("claude", { file });
assert.equal(result.ok, true);
assert.equal(result.written, 3);
assert.equal(result.written, 4);
const after = read(file);
assert.equal(after.model, "opus", "an unrelated setting was lost");
assert.equal(after.env.FOO, "bar", "a sibling env var was lost");
assert.equal(after.env.CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS, "4");
assert.equal(after.ultracode, true);
assert.equal(after.ultracode, false);
assert.equal(after.workflowKeywordTriggerEnabled, false);
assert.equal(after.workflowSizeGuideline, "small");
assert.equal(after.hooks.Stop[0].hooks[0].command, "echo theirs", "the user's hook was clobbered");
});
Expand All @@ -78,12 +80,12 @@ test("applying fills holes and leaves everything else alone", () => {
test("a key the operator set is never touched, even to the opposite value", () => {
// The whole reason "theirs" is a state and not a fault: a floor under a
// fresh install, not a policy over an old one.
withSettings({ ultracode: false, workflowSizeGuideline: "large" }, (file) => {
withSettings({ ultracode: true, workflowSizeGuideline: "large" }, (file) => {
const result = applyEngineSettings("claude", { file });
assert.equal(result.ok, true);
assert.deepEqual(result.changes.map((c) => c.change), ["kept", "kept", "added"]);
assert.deepEqual(result.changes.map((c) => c.change), ["kept", "added", "kept", "added"]);
const after = read(file);
assert.equal(after.ultracode, false);
assert.equal(after.ultracode, true, "the operator turned it on; that is theirs to keep");
assert.equal(after.workflowSizeGuideline, "large");
assert.equal(after.env.CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS, "4");
});
Expand Down Expand Up @@ -120,7 +122,7 @@ test("--dry-run writes nothing and can still show the change", () => {
const result = applyEngineSettings("claude", { file, dryRun: true });
assert.equal(result.ok, true);
assert.deepEqual(read(file), { model: "opus" }, "a dry run touched the file");
assert.match(result.after, /"ultracode": true/);
assert.match(result.after, /"ultracode": false/);
});
});

Expand All @@ -134,15 +136,15 @@ test("an engine without a spec is refused with a reason", () => {
/* ------------------------------------------------------------------ status */

test("status tells set from missing from theirs", () => {
withSettings({ ultracode: true, workflowSizeGuideline: "large" }, (file) => {
withSettings({ ultracode: false, workflowSizeGuideline: "large" }, (file) => {
const status = settingsStatus("claude", { file });
assert.equal(status.readable, true);
assert.deepEqual(status.entries.map((e) => e.state), ["set", "theirs", "missing"]);
assert.equal(status.entries[1].have, "large");
assert.equal(status.applied, false, "one is still missing");
assert.deepEqual(status.entries.map((e) => e.state), ["set", "missing", "theirs", "missing"]);
assert.equal(status.entries[2].have, "large");
assert.equal(status.applied, false, "two are still missing");
applyEngineSettings("claude", { file });
const after = settingsStatus("claude", { file });
assert.deepEqual(after.entries.map((e) => e.state), ["set", "theirs", "set"]);
assert.deepEqual(after.entries.map((e) => e.state), ["set", "set", "theirs", "set"]);
assert.equal(after.applied, true, "an override is an answer, not a hole");
});
});
Expand All @@ -168,11 +170,12 @@ test("remove takes out only what is still ours", () => {

const result = removeEngineSettings("claude", { file });
assert.equal(result.ok, true);
assert.equal(result.removed, 2);
assert.equal(result.removed, 3);
const after = read(file);
assert.equal(after.model, "opus");
assert.equal(after.env.FOO, "bar", "a sibling env var went with ours");
assert.equal("ultracode" in after, false);
assert.equal("workflowKeywordTriggerEnabled" in after, false);
assert.equal("CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS" in after.env, false);
assert.equal(after.workflowSizeGuideline, "large", "the operator's edit was removed");
});
Expand Down Expand Up @@ -224,18 +227,18 @@ test("`engines defaults claude` is status for claude, not a usage error", async
await withSettings({}, async (file) => {
const { code, lines } = await run(["claude"], file);
assert.equal(code, 0);
assert.match(lines.join("\n"), /3 of 3 not set/);
assert.match(lines.join("\n"), /4 of 4 not set/);
});
});

test("`engines defaults apply claude --json` is machine-readable and reports every change", async () => {
await withSettings({ ultracode: false }, async (file) => {
await withSettings({ ultracode: true }, async (file) => {
const { code, lines } = await run(["apply", "claude", "--json"], file);
assert.equal(code, 0);
const [result] = JSON.parse(lines.join("\n"));
assert.equal(result.engine, "claude");
assert.deepEqual(result.changes.map((c) => c.change), ["kept", "added", "added"]);
assert.equal(read(file).ultracode, false);
assert.deepEqual(result.changes.map((c) => c.change), ["kept", "added", "added", "added"]);
assert.equal(read(file).ultracode, true);
});
});

Expand Down
Loading