Skip to content

Comments

feat: Implement Uplink remote management system#6

Draft
MiniCodeMonkey wants to merge 69 commits intomainfrom
feat/uplink
Draft

feat: Implement Uplink remote management system#6
MiniCodeMonkey wants to merge 69 commits intomainfrom
feat/uplink

Conversation

@MiniCodeMonkey
Copy link
Owner

Summary

Implements the Uplink feature — a complete remote management and control system for Chief. This enables a central server to manage Chief instances running on remote machines via WebSocket, supporting remote PRD sessions, run control, file watching, project management, and automated deployment.

Changes

CLI Foundation

  • Migrated CLI to Cobra framework for extensible command structure (US-001)
  • Extracted shared engine from TUI for reuse across command modes (US-005)

Authentication

  • Credential storage with secure keyring integration (US-002)
  • chief login / chief logout with automatic token refresh (US-003, US-004)
  • One-time setup token for automated provisioning (US-025)

WebSocket Communication Layer

  • WebSocket client with automatic reconnection and exponential backoff (US-006)
  • Protocol handshake with version negotiation (US-007)
  • Typed message serialization with 30+ message types (US-008)
  • Rate limiting to prevent message flooding (US-026)

Server Mode (chief serve)

  • Headless server command for remote-managed operation (US-009)
  • Workspace scanner for project discovery (US-010)
  • Selective file watcher with gitignore-aware filtering (US-011)
  • State snapshots and project handlers (US-012)
  • Graceful shutdown with connection draining (US-027)
  • Configurable WebSocket URL for local development (US-028)

Remote Operations

  • Interactive PRD sessions over WebSocket (US-013)
  • Session timeout with idle warnings (US-014)
  • Run control — start, pause, resume, cancel (US-015)
  • Quota detection and automatic pause (US-016)
  • Real-time run progress streaming (US-017)
  • Project settings management (US-018)
  • Per-story logging and retrieval (US-019)
  • Per-story diff generation (US-020)
  • Git clone and project creation (US-021)
  • Remote chief update triggered via WebSocket (US-022, US-023)

Deployment

  • Systemd service unit file (US-024)
  • Cloud-init script for automated VM provisioning (US-024)

Stats

  • 58 files changed
  • ~19,550 lines added
  • 28 user stories implemented (US-001 through US-028)
  • Comprehensive test coverage across all new packages

@MiniCodeMonkey MiniCodeMonkey marked this pull request as draft February 19, 2026 20:45
MiniCodeMonkey and others added 29 commits February 21, 2026 20:51
Replace manual flag parsing with Cobra command tree. All existing
commands (new, edit, status, list, wiggum) and flags (--max-iterations,
--no-sound, --no-retry, --verbose, --merge, --force, --help, --version)
are preserved. TUI remains the default when no subcommand is given.
Clean, organized help output with command grouping.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create internal/engine package that wraps loop.Manager with fan-out
event subscription, enabling multiple concurrent consumers (TUI and
future WebSocket handler). Refactor TUI to consume events from engine
instead of directly from loop.Manager.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…oding

Set explicit Origin header on WebSocket dial to satisfy Reverb's origin
check. Double-unmarshal the connection_established data field to match
the real Pusher wire format (JSON string containing JSON). Update test
helper to match the real protocol.
The server requires grant_type=refresh_token in the request body and
returns HTML error pages without an Accept: application/json header,
causing JSON parse failures on refresh.
…ages

The server's CommandRelayController sends commands wrapped in
{"type": ..., "payload": {...}} but handlers expected fields at the
top level. Extract the payload before dispatching to handlers.
Add shared JSON fixtures and contract tests that verify both sides
agree on the wire format. Catches serialization mismatches (like
port-as-string, name-vs-project_slug, payload wrapper) with zero
infrastructure — just unit tests against canonical fixture files.

Also fixes mock Pusher server to double-encode connection_established
data, matching the real Pusher protocol.

- contract/fixtures/ synced from chief-uplink (source of truth)
- internal/contract/contract_test.go: 9 contract tests
- Makefile: sync-fixtures and test-contract targets
- .gitignore: exclude synced fixtures
- Add handleGetPRDs handler for the get_prds command, mapping
  CompletionStatus to browser-friendly status (draft/active/done)
- Add handleGetDiffs handler with per-file diff parsing
- Change settings responses from flat "settings" type to payload-wrapped
  "settings_response" and "settings_updated" types
- Add new message types and structs for payload-wrapped responses
- Add contract tests for get_prds, get_settings, get_diffs fixtures
- Update settings tests to match new response format
- TestRunServe_GetPRDs: verifies prds_response payload with status
  mapping (active/done/draft) from CompletionStatus
- TestRunServe_GetPRDs_ProjectNotFound: error handling
- TestRunServe_GetPRDs_EmptyProject: empty PRD list for project
  without .chief directory
- TestMapCompletionStatus: unit tests for status mapping logic
- TestRunServe_GetDiffs: verifies diffs_response with per-file parsed
  diff details (filename, additions, deletions, patch)
- TestRunServe_GetDiffs_ProjectNotFound: error handling
- TestParseDiffFiles: unit tests for unified diff parser
Reverb sends event data as a JSON string (double-encoded), but the
client was passing the raw string to the command handler, causing
"cannot unmarshal string into Go value" errors. Unwrap the string
before forwarding to the receive channel.
…omplete)

Add TypePRDOutput and TypePRDResponseComplete message types so the browser
can distinguish PRD streaming from run streaming. PRD sessions now send
prd_output (with text field) instead of claude_output, and send
prd_response_complete instead of claude_output with Done: true when the
Claude process exits. This allows PrdChat.vue to properly clear the
"thinking" indicator.
Replace openBrowser with a package-level var so tests can stub it out
instead of opening actual browser windows.
Without -p, claude starts an interactive TUI that doesn't work with
piped stdout/stdin, causing PRD chat to hang indefinitely with no
output streamed back to the browser.

- Add -p flag for non-interactive print mode
- Add --dangerously-skip-permissions for unattended subprocess use
- Clear CLAUDECODE env var to prevent nested-session detection
…e binary

Restructure PRDOutputMessage and PRDResponseCompleteMessage to use the
payload wrapper pattern (matching SettingsResponseMessage), so Laravel's
broadcastWith() correctly extracts the payload for the frontend.

Add CHIEF_CLAUDE_BINARY env var to override the claude binary path,
enabling E2E tests to use a mock instead of requiring real auth tokens.
…ormat

Claude detects non-TTY stdin and buffers all output until exit when run
with a plain pipe. Switch to a PTY for stdin so Claude treats input as a
real terminal and streams output immediately. Also switch output format
from default (buffered) to stream-json, parsing JSONL events to extract
assistant text for prd_output messages.
Replace old spiral logo assets with new Chief C-arrow logo.
Add black and white variants, update favicon and OG image.
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.

1 participant