diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab50d8d..6bf42c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,10 +94,10 @@ jobs: uses: swatinem/rust-cache@v2 - name: Build release binary - run: cargo build --release --bin qmtacp + run: cargo build --release --bin qmacp - name: Smoke CLI help - run: ./target/release/qmtacp --help + run: ./target/release/qmacp --help nix: name: Nix diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a57b245..be595ad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,14 +56,14 @@ jobs: key: ${{ matrix.target }} - name: Build release binary - run: cargo build --release --bin qmtacp --target ${{ matrix.target }} + run: cargo build --release --bin qmacp --target ${{ matrix.target }} - name: Package archive run: | - PKG="qmtacp-${{ github.ref_name }}-${{ matrix.target }}" + PKG="qmacp-${{ github.ref_name }}-${{ matrix.target }}" STAGING="dist/${PKG}" mkdir -p "$STAGING" - cp "target/${{ matrix.target }}/release/qmtacp" "$STAGING/" + cp "target/${{ matrix.target }}/release/qmacp" "$STAGING/" cp README.md LICENSE "$STAGING/" tar -czf "dist/${PKG}.tar.gz" -C dist "$PKG" echo "ASSET_PATH=dist/${PKG}.tar.gz" >> "$GITHUB_ENV" @@ -71,7 +71,7 @@ jobs: - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: qmtacp-${{ matrix.target }} + name: qmacp-${{ matrix.target }} path: ${{ env.ASSET_PATH }} release: @@ -84,7 +84,7 @@ jobs: uses: actions/download-artifact@v4 with: path: artifacts/ - pattern: 'qmtacp-*' + pattern: 'qmacp-*' merge-multiple: true - name: Create GitHub Release diff --git a/Cargo.lock b/Cargo.lock index 7f40d79..c4295e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1228,7 +1228,7 @@ dependencies = [ ] [[package]] -name = "qmtacp" +name = "qmacp" version = "0.1.0" dependencies = [ "agent-client-protocol", diff --git a/Cargo.toml b/Cargo.toml index 4d4a2d0..c626506 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,15 @@ [package] -name = "qmtacp" +name = "qmacp" description = "CLI for controlling a running QueryMT ACP WebSocket server." authors = ["Ivan Zatevakhin "] -repository = "https://github.com/querymt/qmtacp" +repository = "https://github.com/querymt/qmacp" homepage = "https://query.mt" version = "0.1.0" edition = "2024" license = "MIT" [[bin]] -name = "qmtacp" +name = "qmacp" path = "src/main.rs" [dependencies] diff --git a/README.md b/README.md index 5f841e2..2559ddf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# qmtacp +# qmacp CLI for a running QueryMT ACP WebSocket server (`qmtcode --acp-ws`). Default endpoint is `ws://127.0.0.1:3030/acp/ws`. @@ -12,22 +12,22 @@ Cargo: ```sh cargo install --path . -cargo install --git https://github.com/querymt/qmtacp +cargo install --git https://github.com/querymt/qmacp ``` Nix profile: ```sh nix profile install . -nix profile install github:querymt/qmtacp +nix profile install github:querymt/qmacp ``` ```sh -qmtacp --version -qmtacp --help +qmacp --version +qmacp --help ``` -`--version` prints `qmtacp ()` and does not connect. +`--version` prints `qmacp ()` and does not connect. ```sh cargo run -- caps diff --git a/SKILL.md b/SKILL.md index 32877c3..ba62a2c 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,5 +1,5 @@ --- -name: qmtacp +name: qmacp description: "JSON CLI for a running QueryMT ACP WebSocket server (qmtcode --acp-ws). Find/create sessions, prompt, follow, steer, queue, configure, cancel, and inspect live protocol behavior. Use for operating live sessions and for testing/developing qmtcode." version: "0.1.0" compatibility: "*" @@ -7,14 +7,14 @@ allowed-tools: "shell" tags: ["querymt", "acp", "sessions", "automation", "orchestration", "qmtcode"] --- -# qmtacp +# qmacp Each invocation connects, does one job, and exits. Default URL: `ws://127.0.0.1:3030/acp/ws`. The server owns session state. Keep the returned `sessionId`. - Do not invent session, profile, model, mode, effort, or URL values. Discover them (`caps`, `profiles`, `models`, `find`/`sessions`) or take them from JSON. - Do not close a session unless the user asked to end it, or a qmtcode test explicitly requires `close`. -- Do not use qmtacp to edit or test this repo; use ordinary tools. Use it against a running `qmtcode --acp-ws`. +- Do not use qmacp to edit or test this repo; use ordinary tools. Use it against a running `qmtcode --acp-ws`. - If connection is refused, check that `qmtcode --acp-ws` is running. Do not retry blindly. ## Install @@ -23,17 +23,17 @@ Cargo: ```sh cargo install --path . -cargo install --git https://github.com/querymt/qmtacp +cargo install --git https://github.com/querymt/qmacp ``` Nix profile: ```sh nix profile install . -nix profile install github:querymt/qmtacp +nix profile install github:querymt/qmacp ``` -If `qmtacp` is not on `PATH`, install it. Do not treat a missing binary as a server failure. +If `qmacp` is not on `PATH`, install it. Do not treat a missing binary as a server failure. `--version` / `-V` and `--help` / `-h` do not connect. @@ -42,14 +42,14 @@ If `qmtacp` is not on `PATH`, install it. Do not treat a missing binary as a ser All of these come *before* the subcommand: ```sh -qmtacp [--url URL] [--allow-insecure] [--pretty] [--permission allow-once|reject-once|cancel] [--quiet] [--version] [--help] +qmacp [--url URL] [--allow-insecure] [--pretty] [--permission allow-once|reject-once|cancel] [--quiet] [--version] [--help] ``` - `--url` / `-u`: `ws://`/`wss://` URL, or `host[:port][/path]`. Default `ws://127.0.0.1:3030/acp/ws`. - `--allow-insecure`: required for plaintext `ws://` to non-loopback hosts. - `--pretty`: pretty JSON. Do not use on NDJSON streams. - `--quiet`: hide connection logs on stderr. -- `--version` / `-V`: print `qmtacp ()`. +- `--version` / `-V`: print `qmacp ()`. - `--help` / `-h`: print CLI help. ## Output @@ -67,13 +67,13 @@ The terminal prompt/follow event is `"type":"done"`; `text` is the assembled ass New work: ```sh -qmtacp --quiet prompt --new --cwd "$PWD" --timeout 900 'do the task and report' +qmacp --quiet prompt --new --cwd "$PWD" --timeout 900 'do the task and report' ``` Continue: ```sh -qmtacp --quiet prompt --cwd "$PWD" --timeout 900 "$SESSION_ID" 'next step' +qmacp --quiet prompt --cwd "$PWD" --timeout 900 "$SESSION_ID" 'next step' ``` Long prompts: stdin (`-`), not nested quoting. Make prompts self-contained; elicitation is always cancelled. @@ -95,11 +95,11 @@ Do not `steer`, `queue`, `discard-queued`, or `cancel` just because a timeout fi Discover: ```sh -qmtacp --quiet caps -qmtacp --quiet profiles -qmtacp --quiet models -qmtacp --quiet models --query '' --provider '' -qmtacp --quiet models --refresh +qmacp --quiet caps +qmacp --quiet profiles +qmacp --quiet models +qmacp --quiet models --query '' --provider '' +qmacp --quiet models --refresh ``` `caps.querymt == null` means ACP initialize succeeded but QueryMT extension discovery did not. @@ -107,10 +107,10 @@ qmtacp --quiet models --refresh Sessions: ```sh -qmtacp --quiet new --cwd "$PWD" --profile '' --mode build --model '' --effort high -qmtacp --quiet sessions --cwd "$PWD" --limit 20 -qmtacp --quiet sessions --cwd "$PWD" --all -qmtacp --quiet find --cwd "$PWD" --query '' --phase idle --limit 50 +qmacp --quiet new --cwd "$PWD" --profile '' --mode build --model '' --effort high +qmacp --quiet sessions --cwd "$PWD" --limit 20 +qmacp --quiet sessions --cwd "$PWD" --all +qmacp --quiet find --cwd "$PWD" --query '' --phase idle --limit 50 ``` `new` creates only. Prefer `prompt --new` when the next step is a prompt. @@ -119,13 +119,13 @@ If `find` returns multiple matches, do not pick by list order; inspect or ask. Inspect / configure: ```sh -qmtacp --quiet status --cwd "$PWD" "$SESSION_ID" -qmtacp --quiet runtime "$SESSION_ID" -qmtacp --quiet inspect --cwd "$PWD" --messages 20 --tools false "$SESSION_ID" -qmtacp --quiet inspect --cwd "$PWD" --full "$SESSION_ID" -qmtacp --quiet set-mode "$SESSION_ID" plan # build|plan|review -qmtacp --quiet set-model "$SESSION_ID" '' -qmtacp --quiet set-effort "$SESSION_ID" high # auto|low|medium|high|max +qmacp --quiet status --cwd "$PWD" "$SESSION_ID" +qmacp --quiet runtime "$SESSION_ID" +qmacp --quiet inspect --cwd "$PWD" --messages 20 --tools false "$SESSION_ID" +qmacp --quiet inspect --cwd "$PWD" --full "$SESSION_ID" +qmacp --quiet set-mode "$SESSION_ID" plan # build|plan|review +qmacp --quiet set-model "$SESSION_ID" '' +qmacp --quiet set-effort "$SESSION_ID" high # auto|low|medium|high|max ``` `status` is mode/model/profile/effort. `runtime` is phase/queue/steerability. @@ -134,10 +134,10 @@ qmtacp --quiet set-effort "$SESSION_ID" high # auto|low|medium|high|max Prompt: ```sh -qmtacp --quiet prompt --new --cwd "$PWD" --profile '' --mode build --model '' --effort high --timeout 900 'text' -qmtacp --quiet prompt --cwd "$PWD" --delivery auto --timeout 900 "$SESSION_ID" 'text' -qmtacp --quiet prompt --cwd "$PWD" --delivery prompt|steer|queue --until-idle false "$SESSION_ID" 'text' -qmtacp --quiet --permission allow-once prompt --new --cwd "$PWD" --timeout 900 - < instructions.txt +qmacp --quiet prompt --new --cwd "$PWD" --profile '' --mode build --model '' --effort high --timeout 900 'text' +qmacp --quiet prompt --cwd "$PWD" --delivery auto --timeout 900 "$SESSION_ID" 'text' +qmacp --quiet prompt --cwd "$PWD" --delivery prompt|steer|queue --until-idle false "$SESSION_ID" 'text' +qmacp --quiet --permission allow-once prompt --new --cwd "$PWD" --timeout 900 - < instructions.txt ``` `--until-idle` defaults true and waits through queued turns. Default timeout if omitted is 120s. @@ -145,24 +145,24 @@ qmtacp --quiet --permission allow-once prompt --new --cwd "$PWD" --timeout 900 - Live control: ```sh -qmtacp --quiet follow "$SESSION_ID" --timeout 900 --interval 1 -qmtacp --quiet watch "$SESSION_ID" --timeout 900 --interval 1 --idle true -qmtacp --quiet steer "$SESSION_ID" --run-id "$RUN_ID" 'adjust the active work' -qmtacp --quiet queue "$SESSION_ID" 'do this after the current turn' -qmtacp --quiet discard-queued "$SESSION_ID" "$INPUT_ID" -qmtacp --quiet cancel "$SESSION_ID" -qmtacp --quiet close "$SESSION_ID" +qmacp --quiet follow "$SESSION_ID" --timeout 900 --interval 1 +qmacp --quiet watch "$SESSION_ID" --timeout 900 --interval 1 --idle true +qmacp --quiet steer "$SESSION_ID" --run-id "$RUN_ID" 'adjust the active work' +qmacp --quiet queue "$SESSION_ID" 'do this after the current turn' +qmacp --quiet discard-queued "$SESSION_ID" "$INPUT_ID" +qmacp --quiet cancel "$SESSION_ID" +qmacp --quiet close "$SESSION_ID" ``` `follow` streams session events until idle and loads cwd from the current process; run it from the session workspace. `watch` polls `runtime` (`"type":"runtime"`) until idle unless `--idle false`. Default `follow`/`watch` timeout is 120s. -`steer --run-id` is optional; if omitted, qmtacp uses `runtime.active_run_id`. +`steer --run-id` is optional; if omitted, qmacp uses `runtime.active_run_id`. Queued submit returns `clientInputId`. `input_state` events expose `inputId`; pass that exact value to `discard-queued`. Batch: ```sh -qmtacp --quiet exec < Option { diff --git a/flake.nix b/flake.nix index c6d6922..04ad2f5 100644 --- a/flake.nix +++ b/flake.nix @@ -21,33 +21,33 @@ cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); - qmtacp = pkgs.rustPlatform.buildRustPackage { - pname = "qmtacp"; + qmacp = pkgs.rustPlatform.buildRustPackage { + pname = "qmacp"; version = cargoToml.package.version; src = ./.; cargoLock = { lockFile = ./Cargo.lock; }; env = { - QMTACP_GIT_SHA = self.shortRev or self.dirtyShortRev or "unknown"; + QMACP_GIT_SHA = self.shortRev or self.dirtyShortRev or "unknown"; }; auditable = false; doCheck = false; }; in { packages = { - qmtacp = qmtacp; - default = qmtacp; + qmacp = qmacp; + default = qmacp; }; apps = { - qmtacp = { + qmacp = { type = "app"; - program = "${self.packages.${system}.qmtacp}/bin/qmtacp"; + program = "${self.packages.${system}.qmacp}/bin/qmacp"; }; default = { type = "app"; - program = "${self.packages.${system}.qmtacp}/bin/qmtacp"; + program = "${self.packages.${system}.qmacp}/bin/qmacp"; }; }; @@ -59,7 +59,7 @@ ]; shellHook = '' - export PS1="(dev:qmtacp) $PS1" + export PS1="(dev:qmacp) $PS1" ''; }; }; diff --git a/src/client.rs b/src/client.rs index 22ee613..ea9c4fa 100644 --- a/src/client.rs +++ b/src/client.rs @@ -121,10 +121,7 @@ impl AcpClient { .request( acp::InitializeRequest::new(ProtocolVersion::V1) .client_capabilities(client_capabilities()) - .client_info(acp::Implementation::new( - "qmtacp", - env!("CARGO_PKG_VERSION"), - )), + .client_info(acp::Implementation::new("qmacp", env!("CARGO_PKG_VERSION"))), ) .await?; *self.initialized.lock().await = Some(response.clone()); @@ -411,7 +408,7 @@ fn inbound_request_reply( json!({ "jsonrpc": "2.0", "id": id, - "error": { "code": -32601, "message": format!("qmtacp does not support {method}") }, + "error": { "code": -32601, "message": format!("qmacp does not support {method}") }, }), None, )) diff --git a/src/commands.rs b/src/commands.rs index 300cb35..4705e0e 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -974,7 +974,7 @@ async fn exec( } #[derive(Parser)] -#[command(name = "qmtacp")] +#[command(name = "qmacp")] struct ExecLine { #[command(subcommand)] command: Command, @@ -982,7 +982,7 @@ struct ExecLine { fn parse_exec_line(line: &str) -> Result { let args = split_exec_args(line); - let mut argv = vec!["qmtacp".to_string()]; + let mut argv = vec!["qmacp".to_string()]; argv.extend(args); ExecLine::try_parse_from(&argv) .map(|cli| cli.command) diff --git a/src/main.rs b/src/main.rs index e3f60bb..4e486f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,10 +18,10 @@ use crate::error::{CliError, ExitCode}; use crate::policy::PermissionPolicy; use crate::url::{DEFAULT_HOST, normalize_acp_ws_url, safe_endpoint_label}; -const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", env!("QMTACP_GIT_SHA"), ")"); +const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", env!("QMACP_GIT_SHA"), ")"); #[derive(Parser)] -#[command(name = "qmtacp")] +#[command(name = "qmacp")] #[command(about = "JSON CLI for a running QueryMT ACP WebSocket server")] #[command(version = VERSION)] struct Cli {