Skip to content

Commit e807c55

Browse files
chore: drop ZCode from the supported hosts (#1040)
Removes the ZCode install section, its portability row, the isZcode detection and JSON output branch, and its hook test. I can't test ZCode myself, so it should not be listed as supported.
1 parent 24e9cf8 commit e807c55

5 files changed

Lines changed: 2 additions & 71 deletions

File tree

‎INSTALL.md‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,6 @@ Start a new session (or reload plugins). Skills show as `/ponytail`, `/ponytail-
177177

178178
`AGENTS.md` still works instruction-only from a checkout without the plugin.
179179

180-
## ZCode
181-
182-
ZCode loads Claude Code plugins. Open **Settings → Plugins**, choose **Create → Add marketplace**, enter `DietrichGebert/ponytail`, then install ponytail from the Personal tab. The ruleset arrives at session start and `/ponytail` level switches work, like in Claude Code (needs ponytail 4.10.3 or later and `node` on your PATH). ZCode has no `SubagentStart` event, so subagents run without the ruleset.
183-
184180
## Goose
185181

186182
```bash

‎docs/agent-portability.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ to load in a given agent.
1212
| Codex | `.codex-plugin/plugin.json`, `hooks/claude-codex-hooks.json`, `hooks/`, `skills/` | Plugin install with the same skills plus lifecycle hooks for activation and mode tracking. |
1313
| CodeBuddy | `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `hooks/claude-codex-hooks.json`, `hooks/`, `skills/` | CodeBuddy Code reads the Claude-format manifests when no `.codebuddy-plugin/` exists and expands `${CLAUDE_PLUGIN_ROOT}` in hook commands, so `/plugin marketplace add DietrichGebert/ponytail` + `/plugin install ponytail@ponytail` installs the Claude plugin unchanged. The hooks detect it by `CODEBUDDY_PLUGIN_ROOT`, keep the mode flag in `$CODEBUDDY_CONFIG_DIR` or `~/.codebuddy`, and answer in `hookSpecificOutput` JSON. No subagent injection: CodeBuddy discards `SubagentStart` hook output. Verified with CodeBuddy CLI 2.161.2. |
1414
| Grok Build | root `plugin.json`, `.grok-plugin/marketplace.json`, `skills/`, `commands/` | `grok plugin install DietrichGebert/ponytail --trust`, then enable. Grok can auto-invoke ponytail from its coding-task skill description; `/ponytail` makes activation explicit. Grok lifecycle hooks are not used because passive hook output cannot inject instructions. |
15-
| ZCode | `.claude-plugin/plugin.json`, `hooks/claude-codex-hooks.json`, `hooks/`, `skills/` | ZCode loads Claude Code plugins: **Settings → Plugins → Create → Add marketplace** → `DietrichGebert/ponytail`, then install ponytail. The hooks answer in ZCode's strict JSON shape (detected via `ZCODE_APP_VERSION`, #799), so the ruleset arrives at session start and `/ponytail` switches land. ZCode has no `SubagentStart` event, so subagents run without the ruleset; the statusline nudge is skipped. Confirmed by users on ZCode 3.14.4 with ponytail 4.10.3 (#798). |
1615
| Goose | `plugin.json`, `skills/`, `AGENTS.md` | `goose plugin install https://github.com/DietrichGebert/ponytail.git` clones the repo into `~/.agents/plugins/ponytail/` and imports the six skills as `ponytail:<skill>`. Always-on rules come from `AGENTS.md`: goose reads it from the project (git root down to the working directory) and from `~/.agents/AGENTS.md`, plus `.goosehints` from `~/.config/goose/`. No lifecycle hooks, so no persistent `/ponytail` levels. Verified with Goose 1.53.0. |
1716
| Muse Code (Meta) | `AGENTS.md`, `skills/` | Muse reads `AGENTS.md` (or `CLAUDE.md`) in the project once the workspace is trusted, and `~/.config/muse/AGENTS.md` for every project. Skills: copy the folders from `skills/` to `~/.agents/skills/`, or run `muse skills install ./skills/<name> --scope user` per skill. Instruction-tier, no `/ponytail` levels or hooks. Verified with Muse Code 1.4.2. |
1817
| OpenCode | `.opencode/plugins/ponytail.mjs`, `.opencode/command/`, `hooks/`, `skills/` | Server plugin injects the ruleset each turn via `experimental.chat.system.transform` and persists `/ponytail` switches; reuses the shared instruction builder. Installs globally with `opencode plugin add @dietrichgebert/ponytail` (OpenCode writes the `plugins` entry itself), or per project through the `plugins` array in `opencode.json`. |

‎hooks/ponytail-activate.js‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const {
1919
isCodex,
2020
isCopilot,
2121
isCursor,
22-
isZcode,
2322
setMode,
2423
writeHookOutput,
2524
} = require('./ponytail-runtime');
@@ -61,9 +60,7 @@ try {
6160
let output = getPonytailInstructions(mode);
6261

6362
// 3. Detect missing statusline config — nudge Claude to help set it up
64-
// Skipped on ZCode: its statusline configuration story is unverified, and a
65-
// wrong pointer at Claude's settings.json would just mislead the agent.
66-
if (!isCodex && !isCopilot && !isCursor && !isZcode && !isCodeBuddy) try {
63+
if (!isCodex && !isCopilot && !isCursor && !isCodeBuddy) try {
6764
const isWindows = process.platform === 'win32';
6865
let statusCommand = null;
6966
if (fs.existsSync(settingsPath)) {

‎hooks/ponytail-runtime.js‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ const isCodeBuddy = !isCopilot && !isCodex && !isQoder && Boolean(process.env.CO
3131
// hooks next to CLAUDE_PLUGIN_ROOT, and it needs Cursor-shaped JSON either
3232
// way, so this check comes after the hosts with their own data dirs.
3333
const isCursor = !isCopilot && !isCodex && !isQoder && !isCodeBuddy && Boolean(process.env.CURSOR_VERSION);
34-
// ZCode injects ZCODE_APP_VERSION into every child process, hooks included.
35-
const isZcode = !isCopilot && !isCodex && !isQoder && !isCursor &&
36-
Boolean(process.env.ZCODE_APP_VERSION);
3734

3835
let stateDir = getClaudeDir();
3936
if (isCodex) stateDir = process.env.PLUGIN_DATA;
@@ -128,13 +125,9 @@ function writeHookOutput(event, mode, context = '') {
128125
process.stdout.write(JSON.stringify(output));
129126
return;
130127
}
131-
if (isQoder || isZcode || isCodeBuddy) {
128+
if (isQoder || isCodeBuddy) {
132129
// Qoder: hookSpecificOutput JSON, same shape as Codex minus systemMessage.
133130
// UserPromptSubmit additionalContext is injected into the Agent's conversation.
134-
// ZCode parses hook stdout as strict JSON too — raw text fails validation
135-
// and is silently discarded (#798). Unlike Qoder it has SessionStart, so
136-
// activate.js handles startup injection and only the output shape differs
137-
// from Claude Code.
138131
// CodeBuddy would take raw stdout too, but also echoes it into the chat.
139132
const output = {};
140133
if (context) {
@@ -177,7 +170,6 @@ module.exports = {
177170
isCopilot,
178171
isCursor,
179172
isQoder,
180-
isZcode,
181173
readMode,
182174
setMode,
183175
writeHookOutput,

‎tests/hooks.test.js‎

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ delete process.env.COPILOT_PLUGIN_DATA;
3838
// A leaked subagent matcher would scope the inject-into-every-subagent assertions.
3939
delete process.env.PONYTAIL_SUBAGENT_MATCHER;
4040
delete process.env.QODER_SESSION_ID;
41-
delete process.env.ZCODE_APP_VERSION;
4241
// A leaked project dir would move the flag into ponytail-modes/ (#662).
4342
delete process.env.CLAUDE_PROJECT_DIR;
4443
// Cursor sets these only for hook processes, but a suite launched from a Cursor
@@ -700,58 +699,6 @@ result = run('ponytail-activate.js', { ...codebuddyEnv, CODEBUDDY_CONFIG_DIR: co
700699
assert.equal(result.status, 0, result.stderr);
701700
assert.equal(fs.readFileSync(path.join(codebuddyConfigDir, '.ponytail-active'), 'utf8'), 'lite');
702701

703-
// Zcode: parses hook stdout as strict JSON, so the native-Claude raw-text
704-
// SessionStart output is silently discarded (#798). Same hookSpecificOutput
705-
// shape as Qoder, but Zcode does have SessionStart — activate.js injects the
706-
// ruleset at startup and the mode-tracker only speaks up on mode switches.
707-
const zcodeHome = path.join(temp, 'zcode-home');
708-
const zcodeState = path.join(zcodeHome, '.claude', '.ponytail-active');
709-
fs.mkdirSync(zcodeHome, { recursive: true });
710-
711-
const zcodeEnv = {
712-
HOME: zcodeHome,
713-
USERPROFILE: zcodeHome,
714-
ZCODE_APP_VERSION: '3.10.2',
715-
PONYTAIL_DEFAULT_MODE: 'full',
716-
};
717-
718-
// SessionStart: flag written, ruleset emitted as hookSpecificOutput JSON.
719-
result = run('ponytail-activate.js', zcodeEnv);
720-
assert.equal(result.status, 0, result.stderr);
721-
assert.equal(fs.readFileSync(zcodeState, 'utf8'), 'full');
722-
output = JSON.parse(result.stdout);
723-
assert.equal(output.systemMessage, undefined, 'Zcode must not emit systemMessage');
724-
assert.equal(output.hookSpecificOutput.hookEventName, 'SessionStart');
725-
assert.match(
726-
output.hookSpecificOutput.additionalContext,
727-
/PONYTAIL MODE ACTIVE — level: full/,
728-
);
729-
730-
// '@ponytail lite': mode tracker updates the flag and confirms via JSON.
731-
result = run(
732-
'ponytail-mode-tracker.js',
733-
zcodeEnv,
734-
JSON.stringify({ prompt: '@ponytail lite' }),
735-
);
736-
assert.equal(result.status, 0, result.stderr);
737-
assert.equal(fs.readFileSync(zcodeState, 'utf8'), 'lite');
738-
output = JSON.parse(result.stdout);
739-
assert.equal(output.hookSpecificOutput.hookEventName, 'UserPromptSubmit');
740-
assert.match(
741-
output.hookSpecificOutput.additionalContext,
742-
/PONYTAIL MODE CHANGED — level: lite/,
743-
);
744-
745-
// "stop ponytail": deactivates, clears flag, short confirmation as JSON.
746-
result = run(
747-
'ponytail-mode-tracker.js',
748-
zcodeEnv,
749-
JSON.stringify({ prompt: 'stop ponytail' }),
750-
);
751-
assert.equal(result.status, 0, result.stderr);
752-
assert.equal(fs.existsSync(zcodeState), false, 'flag must be cleared after stop ponytail');
753-
output = JSON.parse(result.stdout);
754-
assert.equal(output.hookSpecificOutput.additionalContext, 'PONYTAIL MODE OFF');
755702
// writeDefaultMode must merge into existing config, not overwrite it (#490).
756703
const mergeHome = path.join(temp, 'merge-home');
757704
const mergeConfigDir = path.join(mergeHome, '.config', 'ponytail');

0 commit comments

Comments
 (0)