Skip to content

feat(adapters): native Qwen Code, iFlow CLI, Trae, Kiro IDE & ECA support#530

Merged
garysheng merged 1 commit into
PeonPing:mainfrom
muunkky:feat/native-everywhere
Jun 8, 2026
Merged

feat(adapters): native Qwen Code, iFlow CLI, Trae, Kiro IDE & ECA support#530
garysheng merged 1 commit into
PeonPing:mainfrom
muunkky:feat/native-everywhere

Conversation

@muunkky

@muunkky muunkky commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

peon-ping's promise is "whatever AI coding agent you run, the Peon shows up — natively." Until this branch, five increasingly-common agentic IDEs broke that promise: a Qwen Code or iFlow user got silence, a Trae user got silence, anyone on Kiro's IDE (as opposed to its CLI, which was already supported) got silence, and the ECA community had a working adapter living only in an unmerged PR. This branch closes all five gaps at once. The reason they ship together isn't convenience — it's that each one exercises a different integration shape, and getting all four shapes right in one cut is what makes the routing layer below provably general rather than tuned to one IDE.

After this branch, a session started in any of these five tools attributes its sounds to the right IDE, on both Unix/WSL2 and native Windows, with zero new runtime dependencies on the IDEs that already had a hook API. The change is purely additive — no existing adapter's behavior changes, and the routing additions are new map entries, not edits to existing ones.

Summary

Adapter Vendor Integration shape Why this shape
Qwen Code Alibaba Claude-Code-style stdin-JSON hooks Vocabulary already matches CESP — thin passthrough
iFlow CLI iflow-ai Claude-Code-style stdin-JSON hooks Same, plus one event remap (PostToolUse failure → PostToolUseFailure)
Trae ByteDance Filesystem watcher No synchronous shell-hook API exists
Kiro IDE Amazon runCommand agent hooks, event on argv IDE's hooks pass no stdin JSON; distinct from the existing Kiro CLI
ECA eca.dev stdin-JSON shell hooks Editor-agnostic integration; vendored from community PR #261

Qwen Code and iFlow CLI — passthroughs over a CESP-shaped hook API

The constraint. Qwen Code (Alibaba) and iFlow CLI (iflow-ai) both ship a hook system modelled directly on Claude Code's: events are piped to the hook command as JSON on stdin, and the event names are already the PascalCase CESP vocabulary peon-ping speaks (SessionStart, UserPromptSubmit, Stop, Notification, …). The only thing standing between these IDEs and working sounds was that nothing told peon-ping the events came from them.

What's now possible. A Qwen or iFlow session now fires peon-ping sounds, and those sounds attribute to "Qwen Code" / "iFlow CLI" rather than falling back to a generic label, because each adapter re-tags the session id with a qwen- / iflow- prefix the router recognizes.

Why this design. The natural alternative when the upstream vocabulary already matches is to register peon-ping's hook script directly in the IDE's settings, no adapter at all. We didn't, for two reasons: peon-ping needs the IDE-attribution prefix on the session id (the raw IDE never adds it), and the raw hook stream is noisy — every PreToolUse/PostToolUse success would make the Peon chatter on every file read. The adapter is therefore a thin filter: it forwards an allowlist of meaningful lifecycle events unchanged and drops the per-tool-call success chatter. The price is one tiny script per IDE to maintain, which is the floor for any attribution-aware integration.

The one place iFlow and Qwen diverge: iFlow surfaces tool failures through a plain PostToolUse with an error payload rather than a dedicated failure event, so its adapter remaps that case to PostToolUseFailure (Qwen already emits the dedicated event and needs no remap).

What it looks like. The Qwen adapter's core is an allowlist-and-retag:

allow = {
    'SessionStart', 'UserPromptSubmit', 'Stop', 'Notification',
    'PostToolUseFailure', 'PermissionRequest', 'SessionEnd',
}
if event not in allow:
    sys.exit(0)              # drop tool-call chatter, subagent/compaction noise
sid = str(data.get('session_id') or os.getpid())
payload = { 'hook_event_name': event, 'session_id': 'qwen-' + sid, ... }

Setup is a hooks block in ~/.qwen/settings.json / ~/.iflow/settings.json (full JSON in the README setup sections), or the .ps1 variant on Windows.

How we know it works. tests/qwen.bats and tests/iflow.bats assert the allowlisted events pass through with the right prefix, the noisy events are dropped, and — for iFlow — that a failed PostToolUse arrives at peon-ping as PostToolUseFailure.

Trae — a filesystem watcher, because there are no hooks to register

The constraint. Trae (ByteDance) is a VS Code-derived AI IDE. It exposes MCP and VS Code extensions but no synchronous shell-hook API — there is no settings key where you can register "run this command when the agent stops." A passthrough adapter like Qwen's has nothing to attach to.

What's now possible. Running the Trae watcher in the background gives a Trae user the same "agent finished" / "new session" sounds every other supported IDE gets, despite Trae offering no hook surface — peon-ping infers the lifecycle from Trae's own session files on disk.

Why this design. The watcher reuses the exact pattern already proven for Amp and Antigravity (the project's other hookless integrations): a new session file means SessionStart; the session file going idle past a timer means Stop. The alternative — a VS Code extension that taps Trae's internal agent events — would be more precise but drags in a whole second distribution channel (a packaged, separately-installed extension) and couples peon-ping to Trae's extension API surface. The watcher needs only a shell loop and ships in the same tarball as every other adapter. The price we accept is that Trae's on-disk layout is undocumented and version-dependent, so the watched paths are environment-overridable rather than hardcoded:

Variable Default
TRAE_DATA_DIR ~/.trae
TRAE_SESSIONS_DIR $TRAE_DATA_DIR/sessions
TRAE_SESSION_GLOB *.json

What it looks like. On Unix the watcher uses fswatch/inotifywait; on Windows the .ps1 uses .NET FileSystemWatcher, so neither platform needs a new dependency it didn't already have for Amp/Antigravity.

bash ~/.claude/hooks/peon-ping/adapters/trae.sh &      # Unix/WSL2 background
powershell -File adapters\trae.ps1 -Install            # Windows background watcher

How we know it works. tests/trae.bats drives the watcher against a synthetic sessions directory: creating a file emits SessionStart, letting it go idle emits Stop, and the env-var overrides redirect the watch.

Kiro IDE — distinct from the Kiro CLI, with the event name on argv

The constraint. peon-ping already supported the Kiro CLI (adapters/kiro.sh), which pipes camelCase JSON on stdin. The Kiro IDE (Amazon) is a different product with a different hook mechanism: its Agent Hooks are .kiro/hooks/*.kiro.hook JSON files whose then.type: runCommand action runs a shell command with no stdin JSON at all — the triggering event name is passed to the command as an argv argument. The existing CLI adapter, which reads stdin, gets nothing from it.

What's now possible. A Kiro IDE user can wire one .kiro.hook file per lifecycle event to adapters/kiro-ide.sh, and peon-ping reacts — without disturbing or being confused with the already-supported Kiro CLI. The two coexist: IDE sessions carry a kiro-ide- prefix, CLI sessions carry kiro-.

Why this design. The tempting shortcut was to extend the existing kiro.sh to also handle the argv-style invocation — one adapter, one prefix. We kept them separate because they're genuinely different products with different event vocabularies and session-id schemes, and conflating them would make a Kiro-IDE bug a risk to working Kiro-CLI users. The price is a second small adapter and a second prefix the router must distinguish — which surfaces the one real subtlety in this PR, handled in the routing section below.

What it looks like. Each event is one hook file; the event name rides on the command line:

{
  "version": "1.0.0", "enabled": true, "name": "peon-ping-stop",
  "when": { "type": "agentStop" },
  "then": { "type": "runCommand",
            "command": "bash ~/.claude/hooks/peon-ping/adapters/kiro-ide.sh agentStop" }
}

agentStop → Stop, promptSubmit → UserPromptSubmit, preToolUse → PermissionRequest, sessionStart → SessionStart; postToolUse and file/user-triggered hooks carry no peon-relevant signal and exit silently.

How we know it works. tests/kiro-ide.bats asserts each when.type maps to the right CESP event and that the session id is prefixed kiro-ide-, keeping it disjoint from the CLI's kiro-.

ECA — vendoring a community adapter as first-party

The constraint. ECA (Editor Code Assistant, eca.dev) is an editor-agnostic LLM-agent integration. A working adapter for it already existed — as community contribution #261 — but living in an unmerged PR meant ECA users had to find that PR and copy the file by hand; it wasn't shipped, tested, or routed.

What's now possible. ECA support is now first-party: the adapter ships in the tarball, is covered by the test suite, and is recognized by the router with a stable eca- session prefix, so an ECA user gets sounds via the standard install with no PR-archaeology.

Why this design. Rather than re-implement from scratch, we vendored the contributed adapter with attribution to its author, then made it match the conventions the other four follow (CESP event mapping, prefix-tagged session id, a BATS file). The one piece worth calling out: ECA provides no obvious stable session id, so the adapter derives one from ECA's db_cache_path (constant for the life of a session) rather than minting a random id per event — without that, every event would look like a different session and the no-repeat / attribution logic would misbehave.

What it looks like. Events map sessionStart/chatStart → SessionStart, preRequest → UserPromptSubmit, postRequest/subagentPostRequest/postToolCall → Stop, preToolCall → PermissionRequest, sessionEnd → SessionEnd, and the session id is eca- plus a sanitized slice of the cache path.

How we know it works. tests/eca.bats covers the event mapping and asserts the eca--prefixed session id is derived deterministically from db_cache_path.

Routing: the longer-prefix-wins ordering

All five IDEs are recognized in one place per platform: peon.sh (via IDE_ALIASES, the session-id prefix_map, and IDE_DISPLAY_NAMES) and the embedded Windows runtime that install.ps1 generates (via Normalize-IdeId, Detect-SessionIde, and $ideDisplayNames). Adding an IDE is three new map entries, not a code edit — which is why five IDEs of four different shapes land without touching any existing adapter's path.

The one non-obvious detail is prefix collision: kiro-ide- starts with kiro-. A naïve match would classify every Kiro IDE session as Kiro CLI. The fix is ordering — kiro-ide- is matched before kiro-, so the longer, more specific prefix wins:

prefix_map = (
    ...
    ('kiro-ide-', 'kiro-ide'),   # must precede 'kiro-'
    ('kiro-',     'kiro'),
    ...
)

This only works if iteration order is guaranteed, which is why the PowerShell side uses [ordered]@{ ... } rather than a plain hashtable — a regular PowerShell hashtable has non-deterministic key order, and kiro- could win on some runs and not others. The ordered map makes the longer-prefix-first guarantee hold identically on both platforms.

Validation

Surface Coverage
BATS (macOS CI, test) New per-adapter files: tests/{qwen,iflow,trae,kiro-ide,eca}.bats — event mapping, prefix tagging, noise dropping, Trae watcher lifecycle. Passing.
Pester (Windows CI, test-windows) Extended to syntax-check all five new .ps1 adapters and assert none use ExecutionPolicy Bypass. Passing.
Vercel Fails with "Authorization required to deploy" — the docs-site deploy can't auth from a fork PR. Not a code failure; nothing in this branch touches the deploy.

Both code CI jobs are green on the head commit. The Vercel check is the only red and is expected for any fork PR against this repo.

Docs and version

  • README.md and README_zh.md: a setup section per adapter, plus the IDE table rows, badge row, and intro list.
  • docs/public/llms.txt updated for the five new IDEs.
  • VERSION 2.29.0 → 2.30.0 (minor — new platform support, additive), with a matching CHANGELOG.md entry.

How to review

The diff is 24 files but reads in three passes:

  1. One adapter end-to-end — read adapters/qwen.sh (simplest) then adapters/trae.sh (the watcher) to see the two integration shapes; their .ps1 siblings mirror them.
  2. Routing — the peon.sh and install.ps1 hunks are pure additive map entries; verify kiro-ide- precedes kiro- in both, and that the PowerShell map is [ordered].
  3. Everything else is mechanical — install lists, README rows, llms.txt, version bump. Skim for completeness.
…pters

Extends "whatever AI coding agent you run, the Peon shows up — natively" to
five more agentic IDEs. Each ships .sh + .ps1 versions with BATS coverage and
is wired into session-id routing so sounds attribute to the right IDE.

- Qwen Code (Alibaba) & iFlow CLI (iflow-ai): Claude-Code-style stdin-JSON
  hook passthroughs (qwen-/iflow- session prefix, drop noisy per-tool events);
  iFlow maps a failed PostToolUse to PostToolUseFailure.
- Trae (ByteDance): filesystem-watcher adapter (Amp/Antigravity pattern) since
  Trae has no synchronous shell-hook API; watched paths overridable via
  TRAE_DATA_DIR/TRAE_SESSIONS_DIR/TRAE_SESSION_GLOB.
- Kiro IDE (Amazon): .kiro/hooks/*.kiro.hook runCommand adapter (event name on
  argv, no stdin JSON), distinct from the existing Kiro CLI adapter; kiro-ide-
  session prefix.
- ECA (Editor Code Assistant): stdin-JSON shell hooks, eca- prefix derived from
  db_cache_path; vendored first-party from community contribution PeonPing#261.

peon.sh and the embedded Windows runtime (generated by install.ps1) recognize
all five via IDE_ALIASES, the session-id prefix_map / Detect-SessionIde, and
IDE_DISPLAY_NAMES (kiro-ide- ordered before kiro- so the longer prefix wins).
install.sh and install.ps1 ship the adapters; README.md, README_zh.md, and
docs/public/llms.txt document setup for each. Bumps VERSION to 2.30.0.
@vercel

vercel Bot commented Jun 7, 2026

Copy link
Copy Markdown

@muunkky is attempting to deploy a commit to the Gary Sheng's projects Team on Vercel.

A member of the Team first needs to authorize it.

@muunkky muunkky changed the title feat: native adapters for Qwen Code, iFlow CLI, Trae, Kiro IDE & ECA feat(adapters): native Qwen Code, iFlow CLI, Trae, Kiro IDE & ECA support Jun 8, 2026
@garysheng
garysheng marked this pull request as ready for review June 8, 2026 14:48
@garysheng
garysheng merged commit ae1058f into PeonPing:main Jun 8, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants