feat(targets): add codebuddy as alias of claude#1334
Conversation
There was a problem hiding this comment.
Pull request overview
Adds Tencent CodeBuddy as a claude-compatible target alias so apm compile -t codebuddy can reuse the existing Claude output format without introducing a new compilation target.
Changes:
- Accept
codebuddyindetect_target()for both--targetandapm.yml-provided target values, mapping it to the internalclaudetarget. - Register
codebuddy -> claudeinTARGET_ALIASESso the shared target validator accepts it. - Add
codebuddytoapm.ymlschema validation targets to prevent early rejection before aliasing.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/apm_cli/core/target_detection.py |
Adds codebuddy handling in detection and the alias registry. |
src/apm_cli/core/apm_yml.py |
Extends the YAML target allowlist to accept codebuddy. |
Comments suppressed due to low confidence (3)
src/apm_cli/core/target_detection.py:341
TARGET_ALIASESnow includescodebuddy, which makes it a globally accepted--target/apm.ymlvalue, but the v2 resolver (resolve_targets()) currently returns the raw tokens without alias normalization for single-token inputs. That allowscodebuddyto propagate into later phases that expect canonical TargetProfile names (e.g.,KNOWN_TARGETSkeys). Consider normalizing aliases during resolution (e.g., map tokens viaTARGET_ALIASESbefore returningResolvedTargets) so aliases never leak past the boundary that promises canonical target names.
#: Alias mapping: user-facing name -> canonical internal name.
TARGET_ALIASES: dict[str, str] = {
"copilot": "vscode",
"agents": "vscode",
"vscode": "vscode",
# TNV downstream: Tencent CodeBuddy is Claude-Code-compatible (reads
# root CLAUDE.md, uses .claude/-style hook settings). Register as an
# alias of claude so `apm compile -t codebuddy` resolves to the same
# output. Required by TAC's stepApm which passes each --ide value
# straight to `apm compile -t`.
"codebuddy": "claude",
}
src/apm_cli/core/target_detection.py:340
- This PR introduces a new user-facing
--target/apm.ymlvalue (codebuddy), but the docs and generated APM guide content still list the older target enums (e.g.docs/src/content/docs/reference/cli/{compile,install,deps,init}.mdandpackages/apm-guide/.apm/skills/apm-usage/{commands.md,package-authoring.md}), so users will not discover/understand the alias. Please update the relevant docs/resources to includecodebuddy(as an alias ofclaude) to keep documentation in sync with the CLI contract.
# TNV downstream: Tencent CodeBuddy is Claude-Code-compatible (reads
# root CLAUDE.md, uses .claude/-style hook settings). Register as an
# alias of claude so `apm compile -t codebuddy` resolves to the same
# output. Required by TAC's stepApm which passes each --ide value
# straight to `apm compile -t`.
"codebuddy": "claude",
src/apm_cli/core/target_detection.py:129
- New alias behavior (
explicit_target='codebuddy'andconfig_target='codebuddy'mapping toclaude) is not covered by the existingtests/unit/core/test_target_detection.pysuite, which already has per-target assertions for the other explicit/config cases. Please add unit tests forcodebuddy(and ideally an install-resolution test to ensure--target codebuddyresults in the same TargetProfile set as--target claude) to prevent regressions.
if explicit_target:
if explicit_target in ("copilot", "vscode", "agents"):
return "vscode", "explicit --target flag"
elif explicit_target in ("claude", "codebuddy"): # TNV downstream: codebuddy -> claude
return "claude", "explicit --target flag"
elif explicit_target == "cursor":
| if explicit_target: | ||
| if explicit_target in ("copilot", "vscode", "agents"): | ||
| return "vscode", "explicit --target flag" | ||
| elif explicit_target == "claude": | ||
| elif explicit_target in ("claude", "codebuddy"): # TNV downstream: codebuddy -> claude | ||
| return "claude", "explicit --target flag" | ||
| elif explicit_target == "cursor": |
| # Canonical target names accepted by APM. | ||
| CANONICAL_TARGETS: frozenset[str] = frozenset( | ||
| { | ||
| "claude", | ||
| "copilot", | ||
| "cursor", | ||
| "opencode", | ||
| "codex", | ||
| "gemini", | ||
| "windsurf", | ||
| "agent-skills", | ||
| "codebuddy", # TNV downstream: alias of claude (see target_detection.TARGET_ALIASES) | ||
| } |
Tencent CodeBuddy IDE is a Claude-Code-compatible AI coding assistant.
It reads CLAUDE.md from the project root and uses .claude/-style
settings.json for hook configuration. This patch registers it as a
first-class target with its own deploy directory (.codebuddy/) so
projects using both Claude Code and CodeBuddy can deploy primitives
to separate directories without collision.
Design:
* Own deploy dir (root_dir=".codebuddy") so install/compile output
lands in .codebuddy/agents/, .codebuddy/commands/, .codebuddy/skills/,
etc. — parallel to .claude/.
* Reuses claude_* primitive formatters (claude_rules, claude_agent,
claude_command, skill_standard, claude_hooks) because CodeBuddy
parses Claude Code's file formats verbatim.
* compile_family="claude" so root CLAUDE.md is still generated —
CodeBuddy reads this natively. No new compile family or output
template needed.
Patch surface (4 files, ~37 net additions):
src/apm_cli/integration/targets.py
+22 lines: new KNOWN_TARGETS["codebuddy"] TargetProfile entry
src/apm_cli/core/target_detection.py
+ TargetType / UserTargetType Literals gain "codebuddy"
+ ALL_CANONICAL_TARGETS frozenset gains "codebuddy"
+ CANONICAL_TARGETS_ORDERED appends "codebuddy" (apm targets row)
+ CANONICAL_DEPLOY_DIRS: "codebuddy": ".codebuddy/"
+ CANONICAL_SIGNAL: "codebuddy": ".codebuddy/"
+ SIGNAL_WHITELIST: ("codebuddy", "dir", ".codebuddy")
+ detect_target() --target / apm.yml elif chains return "codebuddy"
+ should_compile_claude_md() accepts "codebuddy" (root CLAUDE.md)
+ get_target_description() describes codebuddy output
src/apm_cli/core/apm_yml.py
+ CANONICAL_TARGETS frozenset gains "codebuddy"
src/apm_cli/compilation/agents_compiler.py
+ _KNOWN_TARGETS tuple gains "codebuddy" so the compiler's local
validation accepts config.target == "codebuddy"
Verified locally against upstream main (219af91):
$ apm targets
TARGET STATUS SOURCE DEPLOY DIR
claude inactive needs CLAUDE.md .claude/
codebuddy inactive needs .codebuddy/ .codebuddy/
...
$ apm install --target codebuddy
Installed 17 APM dependencies in 15.2s
$ apm compile -t codebuddy
[+] Compilation completed successfully!
[i] Targets: codebuddy (source: --target flag)
[i] Compiling for CLAUDE.md + .codebuddy/commands/ +
.codebuddy/agents/ + .codebuddy/skills/ (Claude-compatible)
End-to-end on a real project: a fresh git repo with `apm install
--target codebuddy` deploys primitives to .codebuddy/agents/,
.codebuddy/commands/, .codebuddy/skills/, .codebuddy/hooks/, and
writes hook config to .codebuddy/settings.json. Root CLAUDE.md is
generated by `apm compile -t codebuddy` exactly like for claude.
Multi-IDE (.claude/ + .codebuddy/ in the same project) deploys
primitives to both directories in parallel with no overlap.
CodeBuddy CLI: https://copilot.tencent.com/codebuddy
Note on prior PR version: an earlier revision of this PR registered
codebuddy as a pure alias of claude (codebuddy → claude in
TARGET_ALIASES). That version was 47 lines. It is functionally
correct for invocation (apm compile -t codebuddy does not error),
but the deploy goes to .claude/ which means codebuddy-only projects
end up with a .claude/ directory and no .codebuddy/ content beyond
what callers manually deploy. The first-class registration here is
the design we use downstream and produces a coherent .codebuddy/
deploy parallel to other native targets.
5b164a3 to
2f62057
Compare
|
@xuedizi please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
Summary
Registers Tencent CodeBuddy IDE as a first-class target with its own deploy directory
.codebuddy/. CodeBuddy is a Claude-Code-compatible AI coding assistant — it readsCLAUDE.mdfrom the project root and uses.claude/-stylesettings.jsonfor hook configuration. Reuses the existingclaude_*primitive formatters but deploys to.codebuddy/so projects using both Claude Code and CodeBuddy can co-exist without directory collision.CodeBuddy CLI: https://copilot.tencent.com/codebuddy
Patch surface (4 files, +37 net)
src/apm_cli/integration/targets.py— newKNOWN_TARGETS["codebuddy"]TargetProfile(root_dir=".codebuddy", compile_family="claude", primitives reuseclaude_rules/claude_agent/claude_command/skill_standard/claude_hooks,hooks_config_display=".codebuddy/settings.json")src/apm_cli/core/target_detection.py—TargetType/UserTargetTypeLiterals +ALL_CANONICAL_TARGETS+CANONICAL_TARGETS_ORDERED+CANONICAL_DEPLOY_DIRS+CANONICAL_SIGNAL+SIGNAL_WHITELISTall add codebuddy;detect_target()--target/apm.ymlelif chains return"codebuddy";should_compile_claude_md()acceptscodebuddy(so rootCLAUDE.mdis still generated);get_target_description()describes codebuddy outputsrc/apm_cli/core/apm_yml.py—CANONICAL_TARGETSfrozenset gains"codebuddy"src/apm_cli/compilation/agents_compiler.py—_KNOWN_TARGETStuple gains"codebuddy"so the compiler's local validation acceptsconfig.target == "codebuddy"Verified locally against
upstream/main(219af91)End-to-end on a real project — fresh git repo +
apm install --target codebuddy:.codebuddy/agents/populated with agent files.codebuddy/commands/populated with command files.codebuddy/skills/populated with skills.codebuddy/settings.jsonwritten (hook config)CLAUDE.mdgenerated byapm compile -t codebuddyMulti-IDE (
.claude/+.codebuddy/in the same project) deploys primitives to both directories in parallel with no overlap.Design choices
compile_family="claude"so rootCLAUDE.mdis still generated. CodeBuddy reads Claude Code's format verbatim — adding a newcompile_family="codebuddy"would have required a new template and output writer with no functional benefit. Happy to revisit if maintainers prefer a dedicatedCODEBUDDY.mdmarker.claude_*primitive formatters. Same justification — CodeBuddy's parser is Claude-Code compatible._KNOWN_TARGETSentry inagents_compiler.py. Becausedetect_target()now returns"codebuddy"(not"claude"), the compiler'sconfig.targetis"codebuddy"when codebuddy is the active target; the existing_KNOWN_TARGETSvalidation rejects it unless we register it explicitly.SIGNAL_WHITELISTentry for.codebuddy/soapm installwithout--targetauto-detects codebuddy alongside other harnesses.Why this changed shape vs the earlier revision
An earlier version of this PR (47 lines) registered codebuddy as a pure alias of
claudeinTARGET_ALIASES. That's the smallest possible patch and makesapm compile -t codebuddywork without errors — but the deploy lands in.claude/, so codebuddy-only projects end up with a.claude/directory and no.codebuddy/content beyond what the caller manually places. The first-class registration here is what we ship downstream (Tinnove TAC bundles a patched wheel for our internal users) and produces a coherent.codebuddy/deploy parallel to other native targets.Tests
Local smoke (Python REPL against the patched checkout):
TARGET_ALIASESdoes not contain codebuddy (intentional — first-class now, not an alias);CANONICAL_TARGETS / ALL_CANONICAL_TARGETS / CANONICAL_DEPLOY_DIRS / CANONICAL_SIGNAL / SIGNAL_WHITELIST / CANONICAL_TARGETS_ORDEREDall contain codebuddy;KNOWN_TARGETS["codebuddy"].root_dir == ".codebuddy"and.compile_family == "claude";detect_target(explicit_target="codebuddy")returns("codebuddy", "explicit --target flag"). Two unrelatedtests/unit/marketplace/test_schema_conformance.py/tests/unit/test_plugin_exporter_schema.pycollection errors from missing optionaljsonschemadep on my machine; not affected by this patch.Happy to add a dedicated
tests/test_codebuddy_target.pyif maintainers want unit coverage parallel to the existing target tests.