feat: multi-backend agent launcher with Copilot CLI and Claude CLI support (#3) (#3)#40
Merged
feat: multi-backend agent launcher with Copilot CLI and Claude CLI support (#3) (#3)#40
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR introduces a pluggable
AgentBackendabstraction that replaces the hardcoded Copilot CLI spawn logic inAgentLauncher. It adds native support for the Anthropic Claude CLI as a first-class agent runner alongside the existing GitHub Copilot CLI, configured via a newagentsection incadre.config.json. Full backward compatibility is maintained — existing configs with only acopilotkey continue to work unchanged.Closes #3
Changes
src/agents/backend.ts(new): Defines theAgentBackendinterface and provides concreteCopilotBackendandClaudeBackendimplementations. Shared helpers (buildEnv,parseTokenUsage,writeAgentLog) are extracted into module-level functions used by both backends.ClaudeBackendparses Claude's JSON output format for token usage in addition to the text-pattern fallback.src/agents/backend-factory.ts(new):createAgentBackend(config, logger)factory that readsconfig.agent?.backendand returns the appropriate backend instance; throws a descriptive error for unknown values.src/config/schema.ts: AddsAgentConfigSchema(exported) withbackend,model,timeout,copilot, andclaudesub-objects. The existingcopilottop-level section is kept; the newagentfield is optional for full backward compatibility.src/config/loader.ts: After Zod validation, ifagentis absent, synthesizes it fromconfig.copilotso downstream code can always readconfig.agentwithout null-checks.src/core/agent-launcher.ts: Refactored to delegate all spawn logic to the backend.init()callsbackend.init(),launchAgent()callsbackend.invoke(). Directconfig.copilotaccess for spawn settings removed.src/validation/agent-backend-validator.ts: Updated to select the CLI command to validate based on the active backend (copilotorclaude); falls back toconfig.copilot.cliCommandwhenconfig.agentis absent.README.md: Added "Claude CLI Setup" section with install/auth instructions and a completecadre.config.jsonexample.Implementation Details
The backend abstraction uses a simple interface + factory pattern. Both backends share the same environment-building logic (
buildEnv) and log-writing logic (writeAgentLog). Token-usage parsing is shared butClaudeBackendadds a JSON parse pass first (Claude CLI emits--output-format jsonresponses), falling through to text-pattern matching on failure.Backward compatibility is handled in two layers: the Zod schema marks
agentas.optional(), and the config loader synthesizes a normalizedagentobject from the legacycopilotconfig so all downstream consumers always seeconfig.agentdefined.Testing
tests/agent-backend.test.ts/tests/agent-backends.test.ts(53 tests):CopilotBackendandClaudeBackendare tested end-to-end with mockedspawnProcess, covering CLI args, env vars, token parsing, log writing, success/failure/timeout paths, and legacy-config fallbacks.tests/agent-backend-factory.test.ts(8 tests): Factory returns correct backend type and name for each backend value; throws on unknown values.tests/backend-factory.test.ts(3 tests): Additional factory coverage includinginstanceofchecks.tests/config-loader-agent.test.ts(12 tests): Backward-compat normalization — synthesizedagentfromcopilotconfig, preservation of explicitagentconfig, frozen output.tests/config-schema.test.ts(10 new tests):AgentConfigSchemadefaults, all accepted/rejected value combinations inCadreConfigSchema.tests/agent-launcher.test.ts(8 new tests):AgentLauncherdelegatesinit()andlaunchAgent()to the mocked backend; propagates results and errors correctly.tests/agent-backend-validator.test.ts(8 new tests): Validator uses correct CLI key forcopilotandclaudebackends; always checksagentDir.npx vitest runproduces 969 tests, 0 failures.Integration Verification
npm run build, exit 0)npx vitest run, exit 0, 969 tests)Notes
APIBackend(direct Anthropic/OpenAI REST API) described in the issue as a stretch goal is not included in this PR. It can be added as a follow-up by implementingAgentBackendand registering it in the factory.agentDir) are still required for all backends — Claude CLI agents are driven by the same context-file approach as Copilot CLI agents.claudeCLI flag--allowedToolsaccepts a comma-separated list; the current implementation hardcodesBash,Read,Write,Edit,MultiEdit,Glob,Grep,TodoRead,TodoWrite,mcp__*. This list may need adjustment as Claude CLI evolves.Cadre Process Challenges
AgentLauncheris "hardcoded" to spawn via Copilot CLI, but noAgentLauncherfile existed in the described location at analysis time. The analysis correctly flagged this ambiguity (the feature was building new functionality, not purely extracting existing logic). A brief note in the issue pointing to the actual source file would have eliminated this uncertainty.code-writeragent relied on knowing which files to create and the exact exports expected by downstream consumers. Because theAgentBackendinterface,CopilotBackend,ClaudeBackend, and factory were all new, the task decomposition worked well — each task had clear acceptance criteria — but the ordering of tasks (schema → loader → backend → factory → refactor) had to be strictly respected to avoid type errors during intermediate states.ClaudeBackendimplementation required knowing the exact CLI flags for theclaudeCLI (--allowedTools,--output-format). These were not documented in the issue and had to be inferred from public Claude CLI documentation. A link to the relevant CLI reference in the issue would have helped.Closes #3