diff --git a/marketing/positioning.md b/marketing/positioning.md index 511b2aa..c328902 100644 --- a/marketing/positioning.md +++ b/marketing/positioning.md @@ -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. diff --git a/src/routes/nook/agents.test.ts b/src/routes/nook/agents.test.ts new file mode 100644 index 0000000..2309628 --- /dev/null +++ b/src/routes/nook/agents.test.ts @@ -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`); + }); +}); diff --git a/src/routes/nook/agents.tsx b/src/routes/nook/agents.tsx index e594cdd..703016c 100644 --- a/src/routes/nook/agents.tsx +++ b/src/routes/nook/agents.tsx @@ -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() {
@@ -249,7 +272,7 @@ export default function NookAgentsPage() { Supported agents

- 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.