Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,22 @@ 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"

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: qmtacp-${{ matrix.target }}
name: qmacp-${{ matrix.target }}
path: ${{ env.ASSET_PATH }}

release:
Expand All @@ -84,7 +84,7 @@ jobs:
uses: actions/download-artifact@v4
with:
path: artifacts/
pattern: 'qmtacp-*'
pattern: 'qmacp-*'
merge-multiple: true

- name: Create GitHub Release
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
[package]
name = "qmtacp"
name = "qmacp"
description = "CLI for controlling a running QueryMT ACP WebSocket server."
authors = ["Ivan Zatevakhin <ivan@query.mt>"]
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]
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -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`.
Expand All @@ -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 <crate-version> (<short-git-sha>)` and does not connect.
`--version` prints `qmacp <crate-version> (<short-git-sha>)` and does not connect.

```sh
cargo run -- caps
Expand Down
84 changes: 42 additions & 42 deletions SKILL.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
---
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: "*"
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
Expand All @@ -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.

Expand All @@ -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] <command>
qmacp [--url URL] [--allow-insecure] [--pretty] [--permission allow-once|reject-once|cancel] [--quiet] [--version] [--help] <command>
```

- `--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 <crate-version> (<short-git-sha>)`.
- `--version` / `-V`: print `qmacp <crate-version> (<short-git-sha>)`.
- `--help` / `-h`: print CLI help.

## Output
Expand All @@ -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.
Expand All @@ -95,22 +95,22 @@ 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 '<term>' --provider '<provider>'
qmtacp --quiet models --refresh
qmacp --quiet caps
qmacp --quiet profiles
qmacp --quiet models
qmacp --quiet models --query '<term>' --provider '<provider>'
qmacp --quiet models --refresh
```

`caps.querymt == null` means ACP initialize succeeded but QueryMT extension discovery did not.

Sessions:

```sh
qmtacp --quiet new --cwd "$PWD" --profile '<profile>' --mode build --model '<model-id>' --effort high
qmtacp --quiet sessions --cwd "$PWD" --limit 20
qmtacp --quiet sessions --cwd "$PWD" --all
qmtacp --quiet find --cwd "$PWD" --query '<title-or-id-fragment>' --phase idle --limit 50
qmacp --quiet new --cwd "$PWD" --profile '<profile>' --mode build --model '<model-id>' --effort high
qmacp --quiet sessions --cwd "$PWD" --limit 20
qmacp --quiet sessions --cwd "$PWD" --all
qmacp --quiet find --cwd "$PWD" --query '<title-or-id-fragment>' --phase idle --limit 50
```

`new` creates only. Prefer `prompt --new` when the next step is a prompt.
Expand All @@ -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" '<model-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" '<model-id>'
qmacp --quiet set-effort "$SESSION_ID" high # auto|low|medium|high|max
```

`status` is mode/model/profile/effort. `runtime` is phase/queue/steerability.
Expand All @@ -134,35 +134,35 @@ qmtacp --quiet set-effort "$SESSION_ID" high # auto|low|medium|high|max
Prompt:

```sh
qmtacp --quiet prompt --new --cwd "$PWD" --profile '<profile>' --mode build --model '<id>' --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 '<profile>' --mode build --model '<id>' --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.

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 <<EOF
qmacp --quiet exec <<EOF
status --cwd "$PWD" "$SESSION_ID"
runtime "$SESSION_ID"
inspect --cwd "$PWD" --messages 20 --tools false "$SESSION_ID"
Expand All @@ -173,7 +173,7 @@ EOF

## Testing and developing qmtcode

qmtacp is a protocol client for `qmtcode --acp-ws`. Use it as a harness while changing qmtcode: start the server, hit it with qmtacp, and treat stdout JSON/NDJSON as the assertion surface.
qmacp is a protocol client for `qmtcode --acp-ws`. Use it as a harness while changing qmtcode: start the server, hit it with qmacp, and treat stdout JSON/NDJSON as the assertion surface.

Typical checks:

Expand All @@ -183,9 +183,9 @@ Typical checks:
- Busy-session behavior: prompt a running session with `--delivery auto|steer|queue`; confirm `input_state` and `clientInputId`.
- Config RPCs: `set-mode` / `set-model` / `set-effort`, then `status` to verify the server accepted the change.
- Permission path: `--permission allow-once|reject-once|cancel` and inspect `permission` events. Do not assume fail-closed unless `cancel`.
- Elicitation: qmtacp always cancels; expect `"type":"elicitation"` with `cancelled: true`.
- Elicitation: qmacp always cancels; expect `"type":"elicitation"` with `cancelled: true`.
- Load/list features: if `caps.acp.loadSession`/`list`/`close` are false, do not keep calling `inspect`/`sessions`/`close`.

Do not treat a previous command’s output as current runtime state; query `runtime` or `status` again.
Do not claim a cancel/discard/close/config change succeeded unless the command exited 0 and JSON `ok` is true.
For qmtcode work, pass `--cwd` to the qmtcode checkout (or the fixture workspace), not to this qmtacp repo unless that is the session under test.
For qmtcode work, pass `--cwd` to the qmtcode checkout (or the fixture workspace), not to this qmacp repo unless that is the session under test.
6 changes: 3 additions & 3 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use std::process::Command;

fn main() {
println!("cargo:rerun-if-env-changed=QMTACP_GIT_SHA");
println!("cargo:rerun-if-env-changed=QMACP_GIT_SHA");
println!("cargo:rerun-if-changed=.git/HEAD");
println!("cargo:rerun-if-changed=.git/refs/heads");

let sha = std::env::var("QMTACP_GIT_SHA")
let sha = std::env::var("QMACP_GIT_SHA")
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.or_else(git_short_sha)
.unwrap_or_else(|| "unknown".to_string());

println!("cargo:rustc-env=QMTACP_GIT_SHA={sha}");
println!("cargo:rustc-env=QMACP_GIT_SHA={sha}");
}

fn git_short_sha() -> Option<String> {
Expand Down
18 changes: 9 additions & 9 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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";
};
};

Expand All @@ -59,7 +59,7 @@
];

shellHook = ''
export PS1="(dev:qmtacp) $PS1"
export PS1="(dev:qmacp) $PS1"
'';
};
};
Expand Down
Loading
Loading