feat: add Bob Shell adapter package - #6518
Conversation
Introduces the bob-shell adapter package and wires it into both the server-side adapter registry and the UI adapter registry. Touch points: - packages/adapters/bob-shell/ — full adapter implementation - server/src/adapters/registry.ts — import, definition, registration - server/src/adapters/builtin-adapter-types.ts — "bob_shell" in Set - ui/src/adapters/bob-shell/ — UI config fields and adapter - ui/src/adapters/registry.ts — import and registration Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Remove macOS hidden files (._*) - Remove dev-log markdown files (DASHBOARD_STATUS_UPDATES, DEBUG, BOB_SHELL_OUTPUT_FORMAT, MCP_TOOLS_VERIFICATION) - Remove diagnose-mcp.sh debug script - Remove dist/ compiled output - Remove node_modules/ - Remove src/server/test-parse.ts (dev scratch file) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
No corresponding source file exists; dead weight before upstreaming. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The server was importing from @paperclipai/adapter-bob-shell but the dependency was not declared in server/package.json, causing TypeScript compilation to fail. Added @paperclipai/adapter-bob-shell as a workspace dependency to resolve the build error.
The adapter resolved the correct project workspace cwd but passed promptBundle.rootDir (a content-addressed cache path) as the execution cwd to runChildProcess, onMeta, canResumeSession, and buildBobResult. Bob Shell therefore ran in the isolated cache directory instead of the actual repository, blocking all code implementation tasks. Fix: sync .bob/ workspace config into the project cwd before launch, and use cwd everywhere promptBundle.rootDir was used as execution dir. The prompt-cache dir retains its role as a stable key for session bundle validation only. Build: tsc clean. Tests: 127/127 pass. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adds agentRole-based defaults for Bob Shell custom modes, tool groups, and whenToUse descriptions. Roles (ceo, cto, engineer, etc.) now automatically get appropriate tool group permissions and mode slugs without requiring explicit config. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The wrappedOnLog handler was emitting a [paperclip] status line on every stdout chunk, producing hundreds of lines like: [paperclip] I've [paperclip] I've been [paperclip] I've been assigned BOB-26 Now only emits when the summary ends with sentence-ending punctuation (.!?), indicating Bob has completed the thought. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Avoids token-by-token log spam by gating stderr summary logging on sentence-ending punctuation. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Step-by-step guide covering clone, build, Bob API key config, agent creation, role-to-tool-group mapping, and troubleshooting. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Rename ANTHROPIC_API_KEY → BOBSHELL_API_KEY throughout - Remove non-existent bobshell.ai link Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The bob-shell adapter was using stderr for Paperclip coordination messages (session status, progressive updates, error descriptions). This caused noise in stderr output that should be reserved for actual subprocess errors. Changes: - Session status messages now go to stdout (lines 435-439) - Progressive status updates now go to stdout (lines 359-380) - Error descriptions now go to stdout (lines 404-411) This aligns bob-shell with the claude adapter pattern where: - stdout = Paperclip coordination/status messages - stderr = actual subprocess error output only Fixes OCP-14 Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adds retry-strategy, runtime-config, session-management modules and custom-modes/types under workspace/. Refactors execute.ts to use them. Drops macOS resource forks (._*) accidentally committed via earlier cherry-picks. Cherry-picked partially from e04c0e7 (instance-only docs and .bob/ state were excluded).
BOB_SHELL_SETUP.md at repo root was non-idiomatic; Hermes and Droid keep their setup guides inside the adapter package. Also link to it from packages/adapters/bob-shell/README.md and fix fork-specific clone URL to point at upstream. Idempotent: repo root no longer has BOB_SHELL_SETUP.md after this.
Greptile SummaryThis PR adds
Confidence Score: 3/5Safe to merge for non-Bob-Shell users; Bob Shell users will have their API key written to disk in plaintext on every run, which should be resolved before production rollout. The registry changes are purely additive and do not touch any existing adapter paths. The core concern is that workspace.ts writes the actual PAPERCLIP_API_KEY into a file in the project working directory on every run, creating a persistent credential exposure risk. There are also two independent implementations of the same workspace helper functions that will silently diverge over time, plus ROLE_GROUPS duplicated across three files. packages/adapters/bob-shell/src/server/workspace.ts (API key written to disk) and packages/adapters/bob-shell/src/server/workspace/custom-modes.ts (dead code duplicating workspace.ts logic)
|
| Filename | Overview |
|---|---|
| packages/adapters/bob-shell/src/server/workspace.ts | Syncs .bob/ workspace config to project cwd; writes PAPERCLIP_API_KEY in plaintext to mcp.json on every run, and re-implements functions already in workspace/custom-modes.ts |
| packages/adapters/bob-shell/src/server/prompt-cache.ts | Prompt bundle cache using SHA-256 content hashing; reads modeConfig.toolGroups instead of modeConfig.groups; duplicates ROLE_GROUPS/ROLE_WHEN_TO_USE constants |
| packages/adapters/bob-shell/src/server/workspace/custom-modes.ts | Custom modes helpers never imported anywhere; workspace.ts re-implements the same functions locally making this dead code |
| packages/adapters/bob-shell/src/server/execute.ts | Main adapter entry point; orchestrates runtime config, prompt caching, session validation, workspace sync, and retry-backed execution cleanly |
| server/src/adapters/registry.ts | Additive registration of bob_shell adapter following exact same pattern as existing adapters; no existing code modified |
Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 4
packages/adapters/bob-shell/src/server/workspace.ts:183-201
**API key written to plaintext file in project workspace**
`generatePaperclipMcpServer` resolves the live `PAPERCLIP_API_KEY` value and writes it verbatim into `{cwd}/.bob/mcp.json` on every run. Any developer who can read the workspace directory — or who accidentally runs `git add .` without the gitignore in place — will expose the bearer token. This is distinct from the `prompt-cache.ts` approach, which uses `"${PAPERCLIP_API_KEY}"` as a template placeholder and never touches the real value.
Consider aligning with `prompt-cache.ts` and using the env-var placeholder form here, relying on Bob Shell's parent-process environment to supply the value at MCP server startup rather than baking it into the file.
### Issue 2 of 4
packages/adapters/bob-shell/src/server/prompt-cache.ts:266-271
**`modeConfig.toolGroups` key does not match `modeConfig.groups` used everywhere else**
`generateCustomModesYaml` reads `modeConfig.toolGroups`, but `config-fields.tsx` stores the user's selection under `modeConfig.groups`, and both `workspace.ts` and `workspace/custom-modes.ts` also read `modeConfig.groups`. Because Bob Shell uses the files from `syncBobWorkspace` at runtime (which correctly reads `groups`), execution is unaffected today. However, the cached `custom_modes.yaml` will always silently fall through to the `ROLE_GROUPS` default, so the cache content diverges from the workspace content whenever a user customises tool groups.
### Issue 3 of 4
packages/adapters/bob-shell/src/server/workspace/custom-modes.ts:1-5
**`workspace/custom-modes.ts` is unreachable dead code**
Nothing in the package imports from `workspace/custom-modes.ts`. The `workspace.ts` file re-implements `readExistingCustomModes`, `generatePaperclipMode`, and `mergeCustomModes` directly without using this module. Having two independent implementations of the same functions will inevitably drift — any fix or enhancement to one copy won't be applied to the other. Either `workspace.ts` should delegate to `workspace/custom-modes.ts`, or `workspace/custom-modes.ts` should be removed.
### Issue 4 of 4
packages/adapters/bob-shell/src/server/prompt-cache.ts:220-239
**`ROLE_GROUPS` and `ROLE_WHEN_TO_USE` are defined three times independently**
These maps are duplicated verbatim in `prompt-cache.ts` (lines 220-239), `workspace.ts` (lines 80-100), and `workspace/types.ts` (lines 83-104). If a new role is added or an existing role's tool groups are changed, all three copies must be kept in sync. The canonical source should live in `workspace/types.ts` (where it already has JSDoc), and both `prompt-cache.ts` and `workspace.ts` should import from there.
Reviews (1): Last reviewed commit: "chore: regenerate lockfile after bob-she..." | Re-trigger Greptile
| function generatePaperclipMcpServer(env: Record<string, string>): BobMcpServer { | ||
| const apiUrl = env.PAPERCLIP_API_URL || "http://localhost:3100"; | ||
| const apiKey = env.PAPERCLIP_API_KEY || ""; | ||
| const companyId = env.PAPERCLIP_COMPANY_ID || ""; | ||
| const agentId = env.PAPERCLIP_AGENT_ID || ""; | ||
| const runId = env.PAPERCLIP_RUN_ID || ""; | ||
|
|
||
| return { | ||
| command: "npx", | ||
| args: ["-y", "@paperclipai/mcp-server"], | ||
| env: { | ||
| PAPERCLIP_API_URL: apiUrl, | ||
| PAPERCLIP_API_KEY: apiKey, | ||
| PAPERCLIP_COMPANY_ID: companyId, | ||
| PAPERCLIP_AGENT_ID: agentId, | ||
| PAPERCLIP_RUN_ID: runId, | ||
| }, | ||
| }; | ||
| } |
There was a problem hiding this comment.
API key written to plaintext file in project workspace
generatePaperclipMcpServer resolves the live PAPERCLIP_API_KEY value and writes it verbatim into {cwd}/.bob/mcp.json on every run. Any developer who can read the workspace directory — or who accidentally runs git add . without the gitignore in place — will expose the bearer token. This is distinct from the prompt-cache.ts approach, which uses "${PAPERCLIP_API_KEY}" as a template placeholder and never touches the real value.
Consider aligning with prompt-cache.ts and using the env-var placeholder form here, relying on Bob Shell's parent-process environment to supply the value at MCP server startup rather than baking it into the file.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/adapters/bob-shell/src/server/workspace.ts
Line: 183-201
Comment:
**API key written to plaintext file in project workspace**
`generatePaperclipMcpServer` resolves the live `PAPERCLIP_API_KEY` value and writes it verbatim into `{cwd}/.bob/mcp.json` on every run. Any developer who can read the workspace directory — or who accidentally runs `git add .` without the gitignore in place — will expose the bearer token. This is distinct from the `prompt-cache.ts` approach, which uses `"${PAPERCLIP_API_KEY}"` as a template placeholder and never touches the real value.
Consider aligning with `prompt-cache.ts` and using the env-var placeholder form here, relying on Bob Shell's parent-process environment to supply the value at MCP server startup rather than baking it into the file.
How can I resolve this? If you propose a fix, please make it concise.| const toolGroups = | ||
| (Array.isArray(modeConfig.toolGroups) && modeConfig.toolGroups.length > 0 | ||
| ? modeConfig.toolGroups | ||
| : null) ?? | ||
| ROLE_GROUPS[agentRole] ?? | ||
| ["read", "edit", "command", "mcp"]; |
There was a problem hiding this comment.
modeConfig.toolGroups key does not match modeConfig.groups used everywhere else
generateCustomModesYaml reads modeConfig.toolGroups, but config-fields.tsx stores the user's selection under modeConfig.groups, and both workspace.ts and workspace/custom-modes.ts also read modeConfig.groups. Because Bob Shell uses the files from syncBobWorkspace at runtime (which correctly reads groups), execution is unaffected today. However, the cached custom_modes.yaml will always silently fall through to the ROLE_GROUPS default, so the cache content diverges from the workspace content whenever a user customises tool groups.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/adapters/bob-shell/src/server/prompt-cache.ts
Line: 266-271
Comment:
**`modeConfig.toolGroups` key does not match `modeConfig.groups` used everywhere else**
`generateCustomModesYaml` reads `modeConfig.toolGroups`, but `config-fields.tsx` stores the user's selection under `modeConfig.groups`, and both `workspace.ts` and `workspace/custom-modes.ts` also read `modeConfig.groups`. Because Bob Shell uses the files from `syncBobWorkspace` at runtime (which correctly reads `groups`), execution is unaffected today. However, the cached `custom_modes.yaml` will always silently fall through to the `ROLE_GROUPS` default, so the cache content diverges from the workspace content whenever a user customises tool groups.
How can I resolve this? If you propose a fix, please make it concise.| import fs from "node:fs/promises"; | ||
| import path from "node:path"; | ||
| import yaml from "yaml"; | ||
| import type { BobCustomMode, BobCustomModesConfig, ROLE_GROUPS, ROLE_WHEN_TO_USE } from "./types.js"; | ||
|
|
There was a problem hiding this comment.
workspace/custom-modes.ts is unreachable dead code
Nothing in the package imports from workspace/custom-modes.ts. The workspace.ts file re-implements readExistingCustomModes, generatePaperclipMode, and mergeCustomModes directly without using this module. Having two independent implementations of the same functions will inevitably drift — any fix or enhancement to one copy won't be applied to the other. Either workspace.ts should delegate to workspace/custom-modes.ts, or workspace/custom-modes.ts should be removed.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/adapters/bob-shell/src/server/workspace/custom-modes.ts
Line: 1-5
Comment:
**`workspace/custom-modes.ts` is unreachable dead code**
Nothing in the package imports from `workspace/custom-modes.ts`. The `workspace.ts` file re-implements `readExistingCustomModes`, `generatePaperclipMode`, and `mergeCustomModes` directly without using this module. Having two independent implementations of the same functions will inevitably drift — any fix or enhancement to one copy won't be applied to the other. Either `workspace.ts` should delegate to `workspace/custom-modes.ts`, or `workspace/custom-modes.ts` should be removed.
How can I resolve this? If you propose a fix, please make it concise.| const ROLE_GROUPS: Record<string, string[]> = { | ||
| ceo: ["read", "command", "mcp"], | ||
| cto: ["read", "command", "mcp"], | ||
| cmo: ["read", "mcp"], | ||
| cfo: ["read", "mcp"], | ||
| coo: ["read", "command", "mcp"], | ||
| vp: ["read", "command", "mcp"], | ||
| manager: ["read", "mcp"], | ||
| engineer: ["read", "edit", "command", "mcp"], | ||
| }; | ||
|
|
||
| const ROLE_WHEN_TO_USE: Record<string, string> = { | ||
| ceo: "Strategic oversight, executive decisions, and company-level approvals.", | ||
| cto: "Architecture review, technical planning, and engineering governance.", | ||
| cmo: "Marketing strategy, content direction, and brand decisions.", | ||
| cfo: "Financial analysis, budget review, and cost decisions.", | ||
| coo: "Operations coordination, process management, and cross-team work.", | ||
| vp: "Division leadership, team management, and delivery oversight.", | ||
| manager: "Task coordination, team management, and issue triage.", | ||
| engineer: "Coding, debugging, refactoring, testing, and validation.", |
There was a problem hiding this comment.
ROLE_GROUPS and ROLE_WHEN_TO_USE are defined three times independently
These maps are duplicated verbatim in prompt-cache.ts (lines 220-239), workspace.ts (lines 80-100), and workspace/types.ts (lines 83-104). If a new role is added or an existing role's tool groups are changed, all three copies must be kept in sync. The canonical source should live in workspace/types.ts (where it already has JSDoc), and both prompt-cache.ts and workspace.ts should import from there.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/adapters/bob-shell/src/server/prompt-cache.ts
Line: 220-239
Comment:
**`ROLE_GROUPS` and `ROLE_WHEN_TO_USE` are defined three times independently**
These maps are duplicated verbatim in `prompt-cache.ts` (lines 220-239), `workspace.ts` (lines 80-100), and `workspace/types.ts` (lines 83-104). If a new role is added or an existing role's tool groups are changed, all three copies must be kept in sync. The canonical source should live in `workspace/types.ts` (where it already has JSDoc), and both `prompt-cache.ts` and `workspace.ts` should import from there.
How can I resolve this? If you propose a fix, please make it concise.…t plaintext
generatePaperclipMcpServer was resolving the live PAPERCLIP_API_KEY
value and writing it verbatim into {cwd}/.bob/mcp.json on every run.
Anyone with workspace read access could see the bearer token, and a
stray "git add ." before the .gitignore caught .bob/ would commit it.
Aligns with prompt-cache.ts:generateMcpJson, which already uses
"${PAPERCLIP_API_KEY}" placeholders that Bob Shell expands from its
parent-process environment at MCP server startup. The secret never
lands on disk.
Also drops the apiUrl and runId reads — those go through the same
placeholder pattern now (apiUrl was harmless but inconsistent; runId
would otherwise stale on cached files).
companyId and agentId remain inlined since they are stable per-agent
and non-sensitive, matching prompt-cache.ts.
Addresses Greptile P1 review on PR paperclipai#6518:
paperclipai#6518 (comment)
Tests: 127/127 pass. Typecheck: clean.
generateCustomModesYaml in prompt-cache.ts was reading modeConfig.toolGroups, but the UI (config-fields.tsx) writes the user selection under modeConfig.groups, and both workspace.ts and workspace/custom-modes.ts read from modeConfig.groups too. Bob Shell uses the .bob files written by syncBobWorkspace at runtime (which reads the right key), so execution was unaffected today. But the cached custom_modes.yaml in the prompt cache always silently fell through to ROLE_GROUPS defaults, so the cache content diverged from the workspace content whenever a user customised tool groups. Aligning the key name fixes the cache drift and makes prompt-cache match the rest of the package. Addresses Greptile P2 review on PR paperclipai#6518: paperclipai#6518 (comment) Tests: 127/127 pass. Typecheck: clean.
ROLE_GROUPS and ROLE_WHEN_TO_USE were defined verbatim in three places (prompt-cache.ts, workspace.ts, and workspace/types.ts), so adding a role or changing a tool group required keeping three copies in sync. The same drift risk applied to BobWorkspaceSyncInput, BobCustomMode, BobCustomModesConfig, BobMcpServer, BobMcpConfig, and PAPERCLIP_MCP_SERVER_NAME — all triplicated. This commit: - Promotes workspace/types.ts to the canonical source for the shared types, role maps, and PAPERCLIP_MCP_SERVER_NAME (it already had JSDoc and the right exports — it just was not being imported). - workspace.ts and prompt-cache.ts now import from workspace/types instead of redeclaring locally. - Deletes workspace/custom-modes.ts. Nothing in the package imported from it (workspace.ts re-implemented its three exported functions directly), so it was unreachable and just another place where the same logic could drift. Its only consumer of workspace/types.ts is removed with it. No behavior change. The role maps and types had identical contents across all three definitions; this is a deduplication, not a fix. Addresses Greptile P2 reviews on PR paperclipai#6518: - paperclipai#6518 (comment) (dead code) - paperclipai#6518 (comment) (triplicated maps) Tests: 127/127 pass. Typecheck: bob-shell + server clean.
|
Greptile review addressed in three commits on top of bb82163:
Verified after each commit:
Re-requesting Greptile review. |
custom_modes.yaml generation and role-derived mode selection were removed in 5268ae8 (the refactor that hoisted shared types to workspace/types.ts promoted a stripped-down types file, dropping BobCustomMode, BobCustomModesConfig, ROLE_GROUPS, ROLE_WHEN_TO_USE, and the full BobWorkspaceSyncInput along with it). The docs were never updated. - Remove .bob/custom_modes.yaml section from README and agentConfigurationDoc - Rename .bob/rules-{mode}/ -> .bob/rules-paperclip/ (hardcoded in code) - Drop mode, modeConfig.groups, modeConfig.whenToUse, modeConfig.customInstructions from the config table; modeConfig is now described as an opaque cache-key input - Note that --mode must be passed explicitly via extraArgs if needed - Update workspace sync semantics (no mode management, only mcp.json + rules-paperclip/)
|
Hey @danijel-soldo! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Informational:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
Thinking Path
bob_shellas a built-in mirrors the upstream pattern (Claude/Codex/Hermes are all built-in adapter packages registered inserver/src/adapters/registry.ts) and removes that fork-maintenance pain@paperclipai/adapter-bob-shelland registers it in the server + UI registriesCoordination note:
No prior #dev thread — happy to pause this PR for coordination if maintainers prefer. Opening it now to make the implementation concrete and reviewable rather than describing it abstractly. The adapter is structurally identical to the existing built-in adapters and the diff is fully scoped to its own package, so the review surface is small even though the line count is large.
What Changed
packages/adapters/bob-shell/(22 source files, 9 modules + 6 test files, ~6.5k LoC) — server execute, workspace sync, prompt cache, JSON-stream + stdout parsers, error detection + classification, retry strategy, runtime config, session management, UI selector, and 127 unit testsserver/src/adapters/registry.ts,server/src/adapters/builtin-adapter-types.ts,ui/src/adapters/registry.ts;new workspace dep inserver/package.jsonui/src/adapters/bob-shell/{index.ts,config-fields.tsx}for the agent settings panelroleauto-maps to Bob Shell mode + tool groups (engineer→ read,edit,command,mcp; CEO/CTO/COO/VP →read,command,mcp; manager/CMO/CFO →read,mcp`)packages/adapters/bob-shell/docs/SETUP.md, linked from the package README.gitignoreadds._*(macOS resource-fork hygiene; cleanup from cherry-pick noise)Total diff: 38 files, +6813 / −88, all bob-shell-scoped. No existing adapter code is modified.
Verification
End-to-end smoke (set
BOBSHELL_API_KEY, ensurebobon PATH): UI → Agents → New Agent → adapterBob Shell→ Test Environment → green. Full setup steps inpackages/adapters/bob-shell/docs/SETUP.md.Risks
BUILTIN_ADAPTER_TYPESand a new module registration).bobon PATH (or absolute path in agent config). Missing → clear error inTest Environment. No effect on other adapters..bob/dir into the agent's working directory at runtime (custom modes, MCP config, role-specific rules). The setup guide documents this and recommends.gitignore-ing it in user projects.src/server/__tests__/, others as*.test.tssiblings of the source files. Matches the original implementation; happy to normalize in a follow-up if reviewers prefer one layout.server/src/adapters/registry.ts. Hermes in particular has substantially deeper control-plane wiring (auth-token injection, prompt-template patching) and is still built-in on master. Bob Shell's integration is shallower than Hermes's.@paperclipai/adapter-utilsand@paperclipai/shared— no private server imports. If maintainers prefer the Droid model (external-only), the package is structured to convert without API changes: it already exposes./server,./ui,./clientrypoints in the same shape Droid uses.~/.paperclip/adapter-plugins.jsonstep for users adopting Bob Shell, matching the experience for every other first-party CLI agent.Model Used
claude-sonnet-4-5-20250929), 200k context, extended thinking + tool use — used for cherry-pick strategy across 16 commits, conflictresolution in
server/src/adapters/registry.ts, dry-run verification against upstream master, and authoring this PR description.Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>on commits77a41e37,4fa3c5ff,13c1a32f, etc.).Checklist
for the new adapter); no existing UI surface is modified. Happy to add screenshots if a reviewer asks.