Skip to content

Add first-class jcode harness support (identity + presence badges) - #825

Open
jmpnop wants to merge 1 commit into
supabitapp:mainfrom
jmpnop:jcode-harness-phase1
Open

Add first-class jcode harness support (identity + presence badges)#825
jmpnop wants to merge 1 commit into
supabitapp:mainfrom
jmpnop:jcode-harness-phase1

Conversation

@jmpnop

@jmpnop jmpnop commented Aug 23, 2026

Copy link
Copy Markdown

Closes #815

Summary

Adds jcode as a first-class coding-agent harness in Supacode, bringing the roster to twelve.
A jcode worktree now gets the same live presence badge (busy / idle / error) and harness identity —
mark, display name, and a Developer-settings row — as Claude, Codex, Kimi, and the rest, in place of
the previous identity-less generic shell.

The change is purely additive:

  • No changes to jcode. Presence is delivered through jcode's own native [hooks] mechanism —
    stock upstream jcode — which Supacode writes into the user's ~/.jcode/config.toml.
  • No changes to any sibling harness. The presence signal jcode emits is byte-identical to every
    other harness, so nothing downstream of the SkillAgent enum — the presence reducer, badge views,
    sidebar fan-out, or notification flows — is touched.

What this adds

  • SkillAgent.jcode — a new harness identity: display name jcode, config directory ~/.jcode,
    mark asset jcode-mark. This slots jcode into the shared agent roster (the sorted settings list and
    identity lookups).
  • AgentIntegrationFactory wiring — a jcode(...) builder (a hooks component plus the shared
    skills component) mirroring the existing Kimi builder, so the integration installs, uninstalls, and
    reports its state exactly like every other harness.
  • JcodeHookSettings + JcodeSettingsInstaller — install Supacode-owned entries in jcode's
    native [hooks] table (one per lifecycle event) plus an executable presence-hook wrapper at
    ~/.jcode/hooks/supacode-presence.sh. Both artifacts are ownership-marked and safe around existing
    user configuration (see below).
  • Developer settings row — a jcode entry with the subtitle
    "Hooks in ~/.jcode/config.toml and skill in ~/.jcode/skills/", so the integration is
    discoverable and toggleable alongside the others.
  • jcode-mark icon asset — jcode's mark (a white "JC" on red), shipped as a full-color vector
    asset rendered in its own colors (like the Claude Code and Codex marks).
  • Tests — a dedicated installer suite plus identity/roster coverage (see Testing).

How it works

Supacode's badges are driven by an OSC 3008 presence signal that a harness emits over its pane TTY
on lifecycle events; Supacode's terminal parses that signal and updates the pane's badge. Every harness
reuses one shared emitter, so the wire format is identical across all of them and is inert in any
terminal that does not handle OSC 3008.

Most harnesses inline that emitter as a shell one-liner in their hook config. jcode differs in one
respect: jcode execs a hook command directly — the command line is split shell-style but never run
through a shell
— so a printf / ps / case one-liner cannot be inlined into the TOML the way it
can for a shell-invoking harness. To bridge that, the installer writes a small executable
presence-hook wrapper and points each entry in jcode's [hooks] table at it:

  1. jcode fires a lifecycle event (session_start / turn_start / turn_end / session_end) and
    execs the wrapper.
  2. The wrapper (#!/bin/sh) resolves the pane TTY and dispatches on $JCODE_HOOK_EVENT, mapping
    turn_start → busy, turn_end → idle (or error, keyed on jcode's own $JCODE_HOOK_STATUS), and
    session start/end to their presence events.
  3. It emits the OSC 3008 presence signal, composed from the same shared snippets every harness
    uses, so the payload is byte-identical.
  4. Supacode's existing badge pipeline parses the signal and updates the pane's badge.

The wrapper is inert outside a Supacode surface (it exits early unless SUPACODE_SURFACE_ID is set),
so it never interferes with a jcode run started elsewhere. It carries a Supacode ownership marker and
is safe to delete — it is reinstalled on demand.

Safety around user configuration

The installer treats the user's config as sacrosanct:

  • Idempotent merge into jcode's [hooks] table. Re-installing is byte-identical. A user's own hook
    on one of the same lifecycle events is preserved — it is merged into an inline array alongside the
    wrapper, never overwritten.
  • Scoped ownership. Managed entries are identified by the wrapper path; all other tables, keys, and
    comments in config.toml are left untouched.
  • Safe uninstall. Uninstall removes only the managed entries and the wrapper, restoring any user
    value on a shared event. The wrapper file is deleted only when it carries Supacode's ownership marker,
    so an unrelated user file at the same path is never removed.
  • Robust parsing. TOML values are parsed for basic/literal strings, inline arrays, and trailing
    comments; CRLF configs are handled; a non-UTF-8 config.toml fails with a clear, actionable error
    rather than corrupting the file.

Out of scope for this PR

  • Per-pane presence for detached / daemonized jcode sessions. The wrapper emits presence per pane
    TTY, which covers the common in-pane case. Binding a specific pane to a session that runs outside its
    TTY (for example, surfacing "awaiting input" for a detached run) needs a session field on the
    presence signal and a bridge that ingests jcode's NDJSON session stream; neither is wired yet, so
    that binding is deliberately omitted rather than emitted with no consumer.
  • pre_tool / post_tool hooks. These are left to the user: a pre_tool hook is a blocking gate,
    not a presence signal, so Supacode does not claim it.
  • Multi-line TOML array values on a hooked event. Value scanning is single-line (a string or an
    inline array). The rare case of a hooked event whose value is a multi-line array is left untouched
    rather than rewritten.

Type of change

  • Bug fix (the linked issue is a bug report)
  • Feature (the linked issue is a feature request marked ready)
  • Documentation
  • Other (please describe)

How was this tested?

Automated (all green):

  • JcodeSettingsInstallerTests covers: a fresh install writes the [hooks] table and an executable
    wrapper; re-install is byte-idempotent; a user hook on the same lifecycle event is preserved (merged
    into an array); uninstall removes only the managed entries and the wrapper while keeping user hooks,
    and never deletes an unmarked user file sharing the wrapper's name; installed / outdated /
    not-installed detection (including wrapper-body drift and partial installs); TOML value parsing
    (string, inline array, literal, trailing comment); CRLF tolerance; invalid-UTF-8 handling; and the
    wrapper's per-event dispatch.
  • Identity and roster tests updated for the new case (SkillAgentTests, plus the agent-integration and
    sidebar coverage).
  • make check (swift-format + swiftlint) is clean, make build-app succeeds, and the full make test
    suite passes.

On-device (verified): installing the integration through the app wrote the managed [hooks] entries
and the executable wrapper under ~/.jcode/, preserving existing user config, and running a real jcode
turn in a Supacode pane drove the pane's badge to busy on turn_start and back to idle on turn_end.
The error path (turn_end with an error status) and per-pane isolation are exercised by the
wrapper-dispatch unit tests and the shared, per-surface OSC 3008 pipeline.

  • make check passes (format + lint)
  • make test passes
  • I built and ran the app to confirm the change works — installed the integration via the app and
    observed a real jcode turn drive the pane badge busy → idle on device (details above).

AI tool disclosure (optional)

  • Model(s): Claude Opus 4.8
  • Harness / tools: Claude Code
  • AI tools assisted with implementation and drafting. A human is the author of record and accountable
    for every line; no commit is authored or co-authored by an AI agent.

Checklist

  • This pull request is linked to an issue with Closes # above.
  • For a feature, the linked issue is labeled ready.
  • I am the author of this work and accountable for it; no commit is authored or co-authored by an AI agent.
  • I have read the Contributing guide and the Code of Conduct.

Make jcode the 12th first-class harness: a SkillAgent identity and mark,
AgentIntegrationFactory wiring, and an installer that writes jcode's
native [hooks] entries plus an executable presence-hook wrapper, so a
jcode pane gets the same busy / idle / error badge as every other harness.
Additive only — no changes to jcode and none to sibling harnesses; the
emitted OSC 3008 presence signal is byte-identical across harnesses.
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.

First-class jcode harness support — identity + presence badges

1 participant