fix: unblock friendly-battle open and LAN bind#56
Merged
Conversation
Three stacked issues made `/tkm:friendly-battle open` unusable when the host's Claude Code session runs outside the plugin directory and wants to accept LAN guests. - Daemon spawn inherits Claude Code's cwd, which breaks Node ESM's cwd-relative `--import tsx` resolution. Pin the spawn cwd to PLUGIN_ROOT (derived from import.meta.url) so the detached daemon always finds tsx in the plugin's node_modules. - The TCP transport rejects wildcard listen hosts (0.0.0.0 / ::) unless an advertise host is provided. Thread a new `--join-host` flag from the CLI through DaemonOptions into createFriendlyBattleSpikeHost so LAN mode can bind 0.0.0.0 and still publish a concrete address. - Remove the 5-minute foreground polling loop that kept the host's Claude Code session blocked after open. Both `open` and `join` now run a single non-blocking --status probe, then detach. A new `/tkm:friendly-battle resume` subcommand (Step 1c) picks up the turn loop once the peer is connected, and a stand-by rule lets free-form check keywords trigger an implicit resume. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…LI regression tests PR #56 review flagged that validateHostArg was only wired to --join-host. Thread it through --listen-host (host init) and --host (join init) so all three host-shaped flags fail the same way with a clean REASON: line before we spawn a daemon or open a socket. Also add `test/friendly-battle-turn-host-validation.test.ts` with three regression scenarios: - malformed value on each of --listen-host / --join-host / --host is rejected with a uniform REASON: ... must match … message; - wildcard --listen-host 0.0.0.0 with --join-host 10.77.77.1 writes the advertised host into the session record (not the wildcard); - the CLI invoked via bin/run-friendly-battle-turn.sh from a cwd outside the plugin root still reaches DAEMON_READY (emits PORT:) and never produces `Cannot find package 'tsx'` / ERR_MODULE_NOT_FOUND. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sanitizer Two findings from the adversarial review on PR #56. [high] The stand-by/resume flow is no longer orphaned when conversational memory drops the `sessionId`. - New `--list-active --generation <gen>` subcommand in `src/cli/friendly-battle-turn.ts` enumerates session records whose daemon PID is still alive and whose phase is non-terminal, emitting a compact JSON array sorted by `updatedAt` descending. Dead daemons and finished/aborted records are filtered out. - `skills/friendly-battle/SKILL.md` Step 1c now calls `--list-active` when `sessionId` is missing from conversation scope: empty → tell the user to `open`/`join` again; exactly one → adopt it; multiple → AskUserQuestion to pick. The existing `--status` probe runs after sessionId is resolved. [medium] Renamed `validateHostArg` → `sanitizeHostArg` to match what it actually does. The helper was never semantic host validation — it was always a shell-safety character filter; the old name/comment/REASON copy overclaimed. Updated the doc comment and REASON message to say "contains characters outside the shell-safe set" so malformed-but-clean hosts like `::::` or `[::1` that Node's net layer still rejects are honestly described as deferred-to-transport rather than pre-validated at the CLI. Tests: extended `test/friendly-battle-turn-host-validation.test.ts` with two `--list-active` cases (empty sessions dir → `[]`; live init-host run → one entry whose sessionId/role/phase matches). The new cases use a SIGKILL-then-poll helper so the detached daemon is reaped before the temp dir is removed, avoiding an ENOTEMPTY race. Full suite: 1210/1210 passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three stacked bugs made
/tkm:friendly-battle openunusable whenever the host's Claude Code session ran from a cwd outside the plugin directory and wanted to accept LAN guests. This PR fixes all three with minimal churn (3 files, +103/-31).tsxERR_MODULE_NOT_FOUND on daemon spawn — detached daemon was spawned vianode --import tsx <daemon.ts>, inheriting Claude Code's cwd. Node ESM's--import tsxresolution is cwd-relative, so the child crashed withCannot find package 'tsx' imported from <user's project dir>. Fix: pinspawn()cwd toPLUGIN_ROOT(derived fromimport.meta.url).0.0.0.0wildcard bind rejected —tcp-direct.tsrequires anadvertiseHostwhenever the listen host is a wildcard, but there was no CLI flag to supply one. Fix: add--join-hostflag onfriendly-battle-turn.ts, threadadvertiseHostthroughDaemonOptionsintocreateFriendlyBattleSpikeHost, and update the LAN default in SKILL.md to--listen-host 0.0.0.0 --join-host "$LAN_IP".open, waiting for the guest to join. Fix: remove the polling loop from Step 1a (host) and Step 1b (guest). Each flow now runs a single non-blocking--statusprobe, then detaches. A new/tkm:friendly-battle resumesubcommand (Step 1c) picks up the turn loop when the peer is connected, and a stand-by rule lets free-form check keywords (확인,check,resume,왔어,접속, etc.) trigger an implicit resume.Files touched:
src/cli/friendly-battle-turn.tssrc/friendly-battle/daemon.tsskills/friendly-battle/SKILL.mdDerived from the post-mortem in
report.md.Test plan
tsc --noEmit— cleannode --import tsx --test test/friendly-battle-*.test.ts— 115/115 passing across 21 suites0.0.0.0, guest connects via advertised LAN IP,resumeon host enters turn loopopen local+join) — confirm no regressionERR_MODULE_NOT_FOUND🤖 Generated with Claude Code