Skip to content

feat(qoder): add Qoder CLI harness - #79

Open
HappyWalkers wants to merge 1 commit into
skillsynchq:mainfrom
HappyWalkers:feat/qoder-harness
Open

HappyWalkers wants to merge 1 commit into
skillsynchq:mainfrom
HappyWalkers:feat/qoder-harness

Conversation

@HappyWalkers

Copy link
Copy Markdown

Issue: #78

What changed and why?

txcript can now read, list, and write Qoder CLI sessions. The harness id is qoder.

Before, a Qoder session was invisible to txcript, and there was no way to continue a Claude Code or Codex session in Qoder.

After:

txcript list --from qoder
txcript continue <claude-code-session-id> --with qoder
qodercli --resume <new id>        # opens with the full history, tool calls and results
txcript continue <qoder-session-id> --with codex

Qoder stores sessions in Claude Code's format, so src/harness/qoder.rs reuses the Claude Code codec (the way Campfire reuses pi) and adds what is Qoder's own:

  • Store. ~/.qoder/projects/<project>/<session>.jsonl, or $QODER_CONFIG_DIR/projects. Discovery reads only the session files and never the <session>/ side folders.
  • Project folder name. Every character outside [A-Za-z0-9] becomes - (counted in UTF-16 units, so an emoji is two dashes). Names over 200 characters are cut to 200, then -, then a hash of the full path. I worked the hash out from real folders and checked it against three of them.
  • Titles and forks. custom-title wins over ai-title. forkedFrom on the copied lines gives the parent session as a fork.
  • Writing. from_common writes the conversation lines like Claude Code does, plus an ai-title line and a final active-leaf line. Qoder's state.json side files are not needed, so they are not written.
  • Delete. Removes only the .jsonl, the same as qodercli --delete-session. It only touches a file at <root>/<project>/<session>.jsonl, and a symlink there is unlinked itself rather than followed.

The China build (qoderclicn, ~/.qoder-cn) uses the same format but ignores QODER_CONFIG_DIR itself. Point txcript at it with QODER_CONFIG_DIR=~/.qoder-cn and resume with TRANSCRIPT_QODER_RESUME_CMD='qoderclicn --resume {id}'.

How I checked it

I used the real CLIs, with a codeword in each session and three or more tries per case, because one answer from a small model can be wrong by chance.

  • Claude Code → Qoder: 3 of 3 resumed sessions recalled the codeword, the command output, and the failed command.
  • Codex → Qoder: 3 of 3.
  • Qoder → Claude Code: 2 of 2. Qoder → Codex: 2 of 2.
  • A session with only user and assistant lines resumes fine, so nothing else is required. A foreign model name, message id, and thinking signature are accepted.
  • Real corpus (108 Qoder sessions on my machine): no panics, discover and load agree on every field, text round trip is exact.
  • CI steps run locally with -D warnings: fmt, clippy (both configs), cargo test --workspace --all-features (482 tests), and the two wasm32 checks. cargo test -p txcript --no-default-features passes except store_delete::antigravity_delete_roundtrip, which fails the same way on main. I built with Rust 1.97.1 and did not run the MSRV (1.96) check.
  • Two independent reviews of the diff found gaps that are fixed in this PR, each with a test I checked fails without the fix: discover listed any .jsonl file, even an empty one (it now needs a record with a type and a sessionId); delete followed a symlink to its target (it now unlinks the link itself); a session could end up as its own fork parent; and the active-leaf timestamp could come from a later line than the leaf.

Not covered: I did not open the Qoder TUI by hand; resume was exercised through qodercli --resume in print mode. The 200-character cut-off was observed kept at 160 characters and cut at 239, 256, and 315, not at exactly 200 and 201. Fork lineage is read but not written, like the other harnesses.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PsZXW7oFJkaFWqY6GmtQAG

Qoder CLI stores sessions in Claude Code's JSONL format, so the harness
reuses the Claude Code codec and adds Qoder's own store:

- the project directory name: every UTF-16 unit outside [A-Za-z0-9]
  becomes "-", and names over 200 characters are cut to 200 and suffixed
  with "-" and a hash of the full path
- titles (custom-title wins over ai-title) and fork lineage (forkedFrom)
- discovery that lists only files holding a session record
- a delete that removes only the .jsonl, like `qodercli --delete-session`

from_common writes the conversation lines, an ai-title line and a final
active-leaf line. Resume was checked against qodercli 1.1.65 in both
directions with Claude Code and Codex.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsZXW7oFJkaFWqY6GmtQAG

This branch has not been deployed

No deployments
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.

2 participants