Files
github-spec-kit/src/specify_cli/_invocation_style.py
meymchen 6a3ee9b64e feat: add ZCode (Z.AI) integration (#3063)
* feat: add ZCode (Z.AI) integration

Add a skills-based integration for ZCode, Z.AI's Claude-Code-style
agent. ZCode uses the same SKILL.md layout as Claude Code, so spec-kit
installs workflows into .zcode/skills/speckit-<name>/SKILL.md, invoked
in chat as $speckit-<name>.

- ZcodeIntegration(SkillsIntegration) with .zcode/ folder and --skills option
- Register in INTEGRATION_REGISTRY
- Catalog entry (tags: cli, skills, z-ai)
- Tests via SkillsIntegrationTests mixin
- Document in integrations reference and README

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: render $speckit-* invocations for ZCode skills

ZCode is documented as a skills agent invoked with $speckit-<command>,
but the central invocation rendering only special-cased codex, so
specify init Next Steps and extension hooks rendered the dotted
/speckit.<command> form instead.

Centralize the $speckit-* decision in a DOLLAR_SKILLS_AGENTS set with an
is_dollar_skills_agent() helper, and route both init Next Steps and
HookExecutor._render_hook_invocation through it. Add ZCode invocation
regression tests mirroring the existing Codex/Kimi coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 12:14:18 -05:00

60 lines
2.0 KiB
Python

"""Agent invocation-style constants and helpers.
Agents that scaffold skills (``speckit-<name>/SKILL.md``) use different
slash-command invocation formats depending on the agent. This module
centralises the mapping so that ``HookExecutor._render_hook_invocation``
and ``specify init``'s next-steps output stay consistent.
"""
from __future__ import annotations
# Agents that render $speckit-<name> (chat invocation) when in skills mode.
DOLLAR_SKILLS_AGENTS: frozenset[str] = frozenset({"codex", "zcode"})
# Agents that always render /speckit-<name>, regardless of ai_skills.
ALWAYS_SLASH_AGENTS: frozenset[str] = frozenset({"devin", "trae", "zed"})
# Agents that render /speckit-<name> only when ai_skills is enabled.
CONDITIONAL_SLASH_AGENTS: frozenset[str] = frozenset(
{
"agy",
"claude",
"copilot",
"cursor-agent",
"hermes",
"lingma",
"rovodev",
"vibe",
}
)
def is_dollar_skills_agent(selected_ai: str | None, ai_skills_enabled: bool) -> bool:
"""Return ``True`` if *selected_ai* uses ``$speckit-<name>`` invocations.
Agents in `DOLLAR_SKILLS_AGENTS` (e.g. ``codex``, ``zcode``) render
``$speckit-<name>`` chat invocations when installed in skills mode.
"""
if not isinstance(selected_ai, str):
return False
return selected_ai in DOLLAR_SKILLS_AGENTS and ai_skills_enabled
def is_slash_skills_agent(selected_ai: str | None, ai_skills_enabled: bool) -> bool:
"""Return ``True`` if *selected_ai* uses ``/speckit-<name>`` invocations.
The decision is based on the agent sets defined in this module:
* Agents in `ALWAYS_SLASH_AGENTS` always use slash invocations.
* Agents in `CONDITIONAL_SLASH_AGENTS` only use them when
*ai_skills_enabled* is ``True``.
* All other agents return ``False``.
"""
if selected_ai is None:
return False
if not isinstance(selected_ai, str):
return False
return selected_ai in ALWAYS_SLASH_AGENTS or (
selected_ai in CONDITIONAL_SLASH_AGENTS and ai_skills_enabled
)