feat: add CLI subcommands for AI-agent access to .wpress archives#23
Merged
feat: add CLI subcommands for AI-agent access to .wpress archives#23
Conversation
Add 5 subcommands (list, info, extract, cat, verify) with JSON output and deterministic exit codes so AI agents can programmatically inspect and extract .wpress archives. Read-only by design — no archive creation. Subcommands: - list: enumerate archive contents (tab-separated or NDJSON) - info: archive metadata summary (key-value or JSON) - extract: full extraction with progress on stderr - cat: stream a single file to stdout (binary, pipe-friendly) - verify: CRC32 integrity check (per-file for v2, structural for v1) Features: - Two-phase QCoreApplication/QApplication init for headless CLI support - TRAKTOR_PASSWORD env var for secure password passing - Path normalization (strip ./ prefix) for reliable file matching - CrcDevice: streaming CRC computation without disk writes - Backward-compatible: existing --source/--destination flags unchanged New files: src/clihandler.h/cpp, tests/tst_cli.cpp New fixtures: v2crc.wpress (v2 format with CRC), compressed.wpress 22 new tests across 6 test groups (74 total, all passing)
Contributor
Build Artifacts
Built from a7292f0. Artifacts expire after 90 days. |
Extract QCommandLineOption variables before passing to addOption() to avoid formatting ambiguity between local clang-format and CI v18.
Build QStringList separately before passing to QCommandLineOption constructor to avoid formatting differences between local clang-format and CI's clang-format v18.
P1: argv subcommand not stripped — QCommandLineParser saw the subcommand name (e.g., "list") as the first positional argument, so every handler tried to open a file named after the subcommand instead of the archive. Fix: add positionalArgs() helper that strips the subcommand name. P1: verify double-advanced file pointer — iterateHeaders() unconditionally seeked past file content after the callback, but the verify callback already consumed the content via processFileContentStreaming(). The double-advance corrupted the archive position for subsequent headers. Fix: iterateHeaders() now checks if the callback already consumed the content before seeking, by comparing position before and after callback. P2: getArchiveInfo() now propagates iterateHeaders() success/failure via a scanComplete field instead of silently ignoring errors.
- Use normalizePath() consistently in extractSingleFile() instead of manual ./ stripping (was inconsistent with list and verify handlers) - Surface processError details in verify's error status instead of silently discarding the decryption/decompression error message - Include error field in verify JSON output when streaming fails
Three workstreams shipping as one coherent "agent-ready" feature: Help system: - Custom --help output listing all 7 subcommands with descriptions, global options, exit codes, and usage examples - Intercepted before QApplication to prevent QCommandLineParser's auto-help from showing the old GUI-mode help - argc == 1 (no args) still launches GUI, not help MCP server (traktor mcp): - JSON-RPC 2.0 over stdin/stdout per MCP spec 2025-11-25 - Synchronous blocking loop, no Qt event loop needed - 5 tools: list, info, extract, cat, verify with typed inputSchema - Calls BackupFile APIs directly (not CLI handler fprintf) - cat returns file content as text via QBuffer (no size limit) - verify uses CrcDevice for streaming CRC without disk writes install-cli (traktor install-cli): - Creates /usr/local/bin/traktor symlink on macOS - Uses osascript with administrator privileges for auth dialog - Pre-flight checks: correct symlink (skip), wrong symlink (overwrite), regular file (refuse with error) - Auto-registers MCP server in ~/.claude.json for Claude Code discovery - Handles invalid JSON in existing config (backup + create new) - GUI menu item: Tools > Install Command Line Tool... - Linux: prints manual symlink instructions - Windows: notes PATH is set by installer New files: src/mcpserver.h/cpp, src/installcli.h/cpp 7 new tests (81 total, all passing)
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
list,info,extract,cat,verify) with--jsonoutput and deterministic exit codes for AI-agent and headless useQCoreApplication/QApplicationinitialization so CLI commands work without a display server (headless servers, CI, Docker)TRAKTOR_PASSWORDenvironment variable for secure password passing (no shell history exposure)CrcDeviceQIODevice subclass for streaming CRC verification without disk writes or memory bloat./prefix for reliable file matching across archive formats--source/--destination/--passwordflags unchangedSubcommands
--jsonoutputtraktor list <archive>traktor info <archive>traktor extract <archive> [dest]traktor cat <archive> <path>traktor verify <archive>Exit Codes
Example Usage
Test plan
--source/--destinationflags still work