Skip to content

feat(installer): user-defined custom targets — codegraph targets add, spec-driven families - #1459

Open
quickbeard wants to merge 1 commit into
colbymchenry:mainfrom
quickbeard:feat/custom-installer-targets
Open

feat(installer): user-defined custom targets — codegraph targets add, spec-driven families#1459
quickbeard wants to merge 1 commit into
colbymchenry:mainfrom
quickbeard:feat/custom-installer-targets

Conversation

@quickbeard

@quickbeard quickbeard commented Jul 27, 2026

Copy link
Copy Markdown

What

Adds user-defined custom installer targets: codegraph targets add <spec.json> registers an agent from a small declarative spec, and from then on codegraph install --target <id>, --target auto/all, the interactive multiselect, codegraph uninstall's sweep, and --print-config all treat it exactly like a built-in. codegraph targets list / remove manage registrations, stored in ~/.codegraph/targets.json.

A spec names a family — one of the config shapes CodeGraph already knows how to write — plus the paths that identify the agent:

family config shape backing implementation
opencode jsonc mcp.<name> wrapper, XDG dirs, .jsonc-over-.json preference the OpenCode target, extracted into a factory
toml [mcp_servers.codegraph] table the Codex target, extracted into a factory
mcp-json standard mcpServers JSON object new generic implementation, modeled on the Gemini target
# CoDev Code (#1272) becomes:
codegraph targets add '{"id":"codev","displayName":"CoDev Code","family":"opencode","appName":"codev","schemaUrl":"https://opencode.ai/config.json"}'

# Grok Build (#1324 / #1396) becomes:
codegraph targets add '{"id":"grok","displayName":"Grok Build","family":"toml","configDir":"~/.grok","homeEnvVar":"GROK_HOME","localConfigDir":".grok"}'

codegraph install --target codev,grok

Why

Every fork or clone of an existing agent currently costs a permanent registry entry, a bespoke target file, and a maintainer support surface — and they keep coming. A census of the eight open new-target PRs right now, mapped against this mechanism:

PR Agent Config shape Under this PR
#1272 CoDev Code OpenCode jsonc ✅ one-line opencode-family spec
#1324 / #1396 Grok Build (two competing PRs) Codex TOML + GROK_HOME ✅ one-line toml-family spec
#968 Qwen Code Gemini-fork ~/.qwen/settings.json + QWEN.md ✅ one-line mcp-json-family spec (instructionsFileName: "QWEN.md")
#952 Windsurf mcpServers without a type field, global-only ⚠️ one entry-shape knob short (omit type) — trivial follow-up if wanted
#1277 Qoder IDE mcpServers at a platform-dependent dir (Application Support/%APPDATA%) ⚠️ shape matches mcp-json; portable specs would need platform-conditional dirs
#887 OpenClaw nested mcp.servers, absolute binary path + --path injection ❌ bespoke — correctly stays a built-in
#992 Pi not MCP — writes a native Pi extension ❌ out of scope by design

So four of the eight collapse into one-line user-side specs as-is, two more are a small family knob away, and the two that don't fit are exactly the "behavioral writes stay in reviewed built-in code" cases the design doc scopes out. The config-shape families stay reviewed, tested code owned by this repo; the paths become user data — and the next OpenCode/Codex/Gemini-shaped agent needs no PR at all (#1440 shows the registry keeps growing even from the maintainer side).

I maintain the codev fork and opened #1272 — this PR is the generalization I'd rather see land than a per-fork target.

How

  • targets/opencode-family.ts, targets/toml-family.ts (new) — the OpenCode and Codex implementations extracted into spec-driven factories. Behavior-preserving: the built-ins are now thin instantiations, and the existing 162-test installer contract suite passes unchanged against them. The toml family gains the two knobs Grok needs: homeEnvVar (env override for the config dir) and optional localConfigDir (project-scoped installs; unset = global-only, which is how Codex keeps its exact current behavior).
  • targets/mcp-json-family.ts (new) — plain mcpServers shape with serversKey / configFileName / instructionsFileName knobs.
  • targets/custom.ts (new) — spec schema + validation (id shape ^[a-z][a-z0-9-]{0,31}$, reserved words, built-in collisions, config dirs must be absolute or ~/-prefixed, local dirs relative with no ..), a tolerant loader (an invalid spec or unparseable file degrades to a one-line warning — the installer never crashes because of a bad spec; targets add is strict and refuses invalid specs up front), and add/remove persistence.
  • targets/registry.tsgetAllTargets() appends loaded customs after the built-ins; getTarget / listTargetIds / detectAll / resolveTargetFlag resolve against the merged list. ALL_TARGETS stays the frozen built-in list. TargetId widens to string (BuiltinTargetId keeps the union).
  • bin/codegraph.ts — the targets command group (spec from a file path or inline JSON).
  • Design doc: docs/design/custom-installer-targets.md. CHANGELOG entry under [Unreleased].

A spec never says what to write — only where, under a family's rules. That keeps the installer contract (sibling preservation, byte-identical idempotent re-runs, uninstall reverses install) provable per family instead of per spec, and keeps a typo'd spec from scribbling outside an agent config dir.

Deliberate scope calls (flagging for maintainer judgment)

  • The pre-opencode config path on Windows uses %APPDATA% instead of ~/.config/opencode/ #535 %APPDATA% legacy sweep stays opencode-only — it's a factory flag the built-in sets, not a spec field. It heals damage a past CodeGraph release did, and only the built-in OpenCode target ever had that install base; exposing it would make custom installs touch %APPDATA%\<app> dirs their agents never read.
  • Claude-style targets are out of scope for custom specs. The permissions allowlist and the prompt hook are behavioral writes with a different trust level than an MCP entry (they silence approval prompts / run a command on every prompt) — those stay in reviewed built-in code. A claude-json family that writes the MCP entry plus the well-known mcp__<server>__* permission string is listed as future work if Claude-fork demand shows up.

Tests

Suite Result
__tests__/custom-targets.test.ts (new, 30 tests: validation matrix, loader tolerance, per-family install/idempotency/sibling-preservation/uninstall contracts, registry merge, add/remove round-trip) all pass
installer-targets.test.ts (162 tests — regression net proving the OpenCode/Codex factory extractions are behavior-preserving) all pass
Full suite (2,560 tests) all pass
tsc clean
Live CLI smoke test against built dist/ (add → list → install codev + grok specs → verify written configs incl. GROK_HOME override and jsonc comment survival → uninstall reverses → remove) all pass

If this lands I'm happy to close #1272 in its favor (it addresses #1274 via the spec above). #1324 / #1396 (Grok Build) and #968 (Qwen Code) become achievable without registry entries as-is, and #952 (Windsurf) / #1277 (Qoder IDE) get there with the small family knobs noted in the census above.

Demo

Screenshot 2026-07-27 at 12 14 36 PM Screenshot 2026-07-27 at 12 15 13 PM

@quickbeard

quickbeard commented Jul 27, 2026

Copy link
Copy Markdown
Author

@colbymchenry would appreciate your take on this PR — it lets new AI coding agents integrate CodeGraph with a one-line spec instead of a registry PR.

Also, calling on the authors of the other open target PRs — you know your agents best, and I'd love a sanity check on whether the spec form actually covers them:

@leihanchen @Inference1 — your Grok Build targets (#1324 / #1396) map to:

codegraph targets add '{"id":"grok","displayName":"Grok Build","family":"toml","configDir":"~/.grok","homeEnvVar":"GROK_HOME","localConfigDir":".grok"}'

Does this match Grok's behavior as you implemented it? (One known difference from #1324: no AGENTS.md for local installs — the family follows Codex there.)

@ZevGit — Qwen Code (#968) should be fully covered by the mcp-json family: {"id":"qwen","family":"mcp-json","configDir":"~/.qwen","localConfigDir":".qwen","instructionsFileName":"QWEN.md"}. Would appreciate you confirming the entry shape matches what Qwen reads.

@SEPURI-SAI-KRISHNA — Windsurf (#952) is one knob short: the mcp-json family currently always writes type: "stdio", which your PR documents Windsurf omitting. If this direction lands, an omitTypeField-style option would be a small follow-up — happy to add it here if there's interest.

@liuwenji007 — Qoder (#1277) matches the mcp-json shape, but its platform-dependent config dir (Application Support vs %APPDATA%) can't be expressed portably in a spec yet — a per-machine absolute configDir works today.

If your agent is covered by a spec, this mechanism would get you unblocked without waiting on a registry merge; if I've mis-read any of your agents' config formats, corrections very welcome. Thank you!

@SEPURI-SAI-KRISHNA

Copy link
Copy Markdown

@quickbeard nice writeup — the family/spec direction makes a lot of sense for the long tail of simple targets, and thanks for the shout-out.

Confirming the Windsurf detail: yes, the stdio entry is { command, args } with no type field — Windsurf's documented examples omit it, and I found the editor is happier without it, so an omitTypeField (or equivalent) knob on the mcp-json family would cover #952 exactly. Config path is ~/.codeium/windsurf/mcp_config.json, global-only (no project-local config), os.homedir()-based on all three platforms. One extra gotcha worth baking into the spec/notes: Windsurf doesn't pick up mcp_config.json live — the user has to open the MCP panel and hit Refresh, otherwise it looks like the install silently failed.

I'm happy either way on mechanism — whatever gets Windsurf support in. If #1459 lands, glad to contribute the omitTypeField knob + a Windsurf spec here; if the maintainer would rather take a small focused built-in in the meantime, #952 is green (contract suite passing) and ready. @colbymchenry — happy to go whichever direction you prefer.

Adds spec-driven custom installer targets so agents without a built-in
target (opencode forks like CoDev Code colbymchenry#1272, Codex-shaped TOML agents
like Grok Build colbymchenry#1324) can be wired up with a small declarative spec
instead of a registry PR.

- Extract the opencode and codex targets into family factories
  (opencode-family.ts, toml-family.ts) — behavior-preserving, pinned by
  the existing 162-test installer contract suite. Add a third generic
  mcp-json family for standard mcpServers-JSON agents.
- targets/custom.ts: spec schema + validation (id shape, reserved
  words, builtin collisions, path-shape rules), tolerant loader
  (invalid specs skip with a warning, never crash the installer),
  strict add/remove persistence to ~/.codegraph/targets.json
  (CODEGRAPH_TARGETS_FILE override).
- Registry merges customs after built-ins; --target <id>/auto/all, the
  interactive multiselect, uninstall's sweep, and --print-config all
  see them with no further changes.
- CLI: codegraph targets add|list|remove.
- 30 new tests in __tests__/custom-targets.test.ts; design doc at
  docs/design/custom-installer-targets.md; CHANGELOG entry.
@quickbeard
quickbeard force-pushed the feat/custom-installer-targets branch from 0d0ae50 to d46e3f1 Compare July 27, 2026 06:30
@liuwenji007

Copy link
Copy Markdown

Thanks for the ping — and for mapping the open target PRs onto a shared family/spec approach. That direction makes a lot of sense to me, especially for long-tail agents like Qoder where a full registry entry is heavy.

You’re right that the MCP side is the same mcpServers / mcp-json shape. A few Qoder-specific details worth noting so the family covers a working install:

  1. Config path — as you said, it’s platform-dependent (Application Support / %APPDATA% / XDG → …/Qoder/SharedClientCache/mcp.json). A single portable configDir isn’t enough today; a per-machine absolute path works as a stopgap. (This is also distinct from Add Support For Qoder #758, which targets Qoder CLI’s ~/.qoder/settings.json.)
  2. No --path / ${workspaceFolder} — older Qoder builds don’t expand those; we omit them and rely on MCP roots/list.
  3. Absolute command on macOS — Dock/launchd strips nvm PATH (same class of issue as Antigravity).

The .qoder/rules/codegraph.md piece in #1277 is mostly agent-steering (Qoder consumes MCP initialize.instructions weakly). Happy to treat a pure mcp-json install as enough if that’s preferred — we can document the rule file separately.

I’ve also just refreshed #1277 (conflict resolution + a shorter CHANGELOG). Happy to confirm an entry shape against a real Qoder install, or trim further once the family knobs land. Thanks again for looping us in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants