Merge branch 'NOOK-336' — NOOK-336 Muse on the agents page

This commit is contained in:
2026-10-13 23:23:44 -04:00
3 changed files with 84 additions and 7 deletions

View File

@@ -9,7 +9,7 @@ Primary source: `~/Code/the-nook/docs/marketing-strategy.md`.
## The sentence
> **Your Mac and your agents, under control.** The Nook puts your whole coding-agent
> fleet in the notch — twelve agents, local or over SSH — so you see what is running,
> fleet in the notch — thirteen agents, local or over SSH — so you see what is running,
> unblock it in one tap, and keep the machine from overheating while it works.
## The wedge
@@ -50,7 +50,7 @@ pages, marketing copy, comparison tables, meta descriptions, emails, FAQs, all o
### 2. A claim requires a test to exist
Copy that says "12 agents" or "no data leaves the device" must have a test backing it.
Copy that says "13 agents" or "no data leaves the device" must have a test backing it.
If a claim cannot be pointed at code or a test, it does not go in copy.
### 3. Honest privacy scoping
@@ -77,7 +77,7 @@ nothing posts itself.
| Claim | Backing |
|---|---|
| Twelve hooked agents, one glance | `AgentSpec.all` — claude, codex, opencode, pi, omp, hermes, openclaw, zai, gemini, cline, qwen, deepseek |
| Thirteen hooked agents, one glance | `AgentSpec.all` — claude, codex, opencode, pi, omp, muse, hermes, openclaw, zai, gemini, cline, qwen, deepseek |
| Remote agents over SSH | `hook/setup.go` remote install path; `NOOK_HOST` TCP transport |
| Permission gate answered from a card | awaitPolicyTable + bridge directive path (`hook/normalize.go`, `serialize.go`) |
| Fan / thermal / charge control | `helper/` (privileged helper, SMC, no kext) |
@@ -87,7 +87,7 @@ nothing posts itself.
## Voice
- Short, concrete, no hype adjectives. Numbers over adjectives ("twelve agents",
- Short, concrete, no hype adjectives. Numbers over adjectives ("thirteen agents",
"one tap", "$10 once").
- The notch/island is the product's home; agents are the product's point.
- Never apologize for being a utility. The utility is the point.

View File

@@ -0,0 +1,54 @@
/**
* Regression test for the /agents roster (NOOK-336).
*
* The page mirrors the app's registry, `AgentSpec.all` in
* the-nook/Sources/NookCore/Agents/AgentRegistry.swift. When Muse joined
* the registry in 0.10.0 the page kept twelve entries and kept saying
* "twelve", so a shipped agent was missing from the one page that lists
* them. The registry lives in another repo, so the expected roster is
* restated here; when an agent is added there, add it here and on the page.
*
* STATIC SOURCE AUDIT, the same pattern as the downloads regressions.
*/
import { describe, it, expect } from "bun:test";
import { readFileSync } from "node:fs";
import { join } from "node:path";
const SOURCE = readFileSync(join(import.meta.dir, "agents.tsx"), "utf8");
/** AgentSpec.all display names, in registry order. */
const REGISTRY = [
"Claude Code",
"Codex",
"OpenCode",
"Pi",
"Oh My Pi",
"Muse",
"Hermes",
"OpenClaw",
"Z.ai (ZCode)",
"Gemini CLI",
"Cline",
"Qwen Code",
"DeepSeek"
];
const NUMBER_WORDS = ["twelve", "thirteen", "fourteen", "fifteen"];
const entries = [...SOURCE.matchAll(/^\s{4}name: "([^"]+)",$/gm)].map((m) => m[1]);
describe("Nook /agents roster (NOOK-336)", () => {
it("lists every registry agent, in registry order", () => {
expect(entries).toEqual(REGISTRY);
});
it("states the same count everywhere it names one", () => {
const word = NUMBER_WORDS[entries.length - 12];
for (const other of NUMBER_WORDS.filter((w) => w !== word)) {
expect(SOURCE.toLowerCase()).not.toContain(other);
}
expect(SOURCE).toContain(`all ${word}`);
expect(SOURCE).toContain(`The ${entries.length} agents`);
expect(SOURCE).toContain(`watches ${word} coding agents`);
});
});

View File

@@ -105,6 +105,29 @@ const AGENTS: AgentEntry[] = [
gating:
"Full — honors the agent's own approval tiers, per-tool policies included"
},
{
name: "Muse",
wiring:
"Plugin at ~/.nook/plugins/muse, installed, approved and enabled through Muse's own plugins CLI; each hook calls the shared hook binary. This Mac only — remote hosts skip it",
events: [
"SessionStart",
"SessionEnd",
"UserPromptSubmit",
"PreToolUse",
"PermissionRequest",
"PostToolUse",
"PostToolUseFailure",
"Stop",
"StopFailure",
"Notification",
"PreCompact",
"PostCompact",
"SubagentStart",
"SubagentStop"
],
gating:
"Full — permission prompts can be allowed or denied from a card, and every tool call under Verify"
},
{
name: "Hermes",
wiring:
@@ -240,8 +263,8 @@ export default function NookAgentsPage() {
<EdgeCacheHeaders maxAge={300} />
<PageHead
title="Supported agents"
description="Every coding agent The Nook supports — all twelve, with each hook's exact event coverage and what you can do from a card: watch sessions live, answer permission prompts, unblock work."
ogTitle="The 12 agents The Nook supports, and what each hook sees"
description="Every coding agent The Nook supports — all thirteen, with each hook's exact event coverage and what you can do from a card: watch sessions live, answer permission prompts, unblock work."
ogTitle="The 13 agents The Nook supports, and what each hook sees"
/>
<SubdomainHeader />
<main class="min-h-screen px-[8vw] py-[8vh]">
@@ -249,7 +272,7 @@ export default function NookAgentsPage() {
Supported agents
</h1>
<p class="text-subtext0 mb-2 max-w-2xl text-lg">
The Nook watches twelve coding agents in one place — your menu bar and
The Nook watches thirteen coding agents in one place — your menu bar and
notch show every session that is running, thinking, or blocked, and a
card answers permission prompts without leaving your keyboard.
</p>