feat: agent-ready CLI substrate — adapters + contracts + EcoMap - #163
feat: agent-ready CLI substrate — adapters + contracts + EcoMap#163atomeam wants to merge 3 commits into
Conversation
- adapter.ps1: PowerShell entry point for Windows - adapter-wsl.sh: Bash bridge running in WSL - ADAPTER-CONTRACTS.md: consistent JSON shapes + exit codes for all 4 adapters - ECOMAP.md: updated with adapter inventory - Taskwarrior 2.6.2 expression bug worked around via jq filtering - All commands verified: list, get, create, done
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
aether | 6d5693b | Jul 18 2026, 09:25 AM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
loxa | 6d5693b | Jul 18 2026, 09:25 AM |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoAdd Taskwarrior WSL adapter and document unified CLI adapter contracts
AI Description
Diagram
High-Level Assessment
Files changed (5)
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
4 rules 1. Bash command string injection
|
| $WslArgs = @('-e', 'bash', '--noprofile', '--norc', '-c', | ||
| "export PATH=/usr/local/sbin:/usr/local/bin:$HOME/.local/bin:/usr/sbin:/usr/bin:/sbin:/bin; " + | ||
| "bash $AdapterSh $Command $($Args -join ' ')" | ||
| ) |
There was a problem hiding this comment.
1. Bash command string injection 🐞 Bug ⛨ Security
integrations/taskwarrior/adapter.ps1 builds a single bash -c string by concatenating $Command and $Args, so spaced arguments (e.g., task descriptions) are split and metacharacters can inject additional shell tokens. The PATH export also embeds $HOME in a PowerShell-interpolated string, expanding it to a Windows path and breaking the intended WSL PATH.
Agent Prompt
## Issue description
The PowerShell adapter constructs a `bash -c "..."` script that interpolates `$AdapterSh`, `$Command`, and `$Args -join ' '`, which (1) breaks argument boundaries (spaces) and (2) allows shell token injection. It also mistakenly interpolates `$HOME` on the PowerShell side, producing a Windows path in the WSL PATH export.
## Issue Context
The contract explicitly states "No shell interpolation — argument arrays only".
## Fix Focus Areas
- integrations/taskwarrior/adapter.ps1[68-72]
- integrations/ADAPTER-CONTRACTS.md[89-94]
## Suggested fix
- Avoid `bash -c` for argument transport. Invoke WSL with an argument array, e.g. `wsl.exe -d $WslDistro -- bash --noprofile --norc $AdapterSh $Command @Args`.
- If you must set PATH, do it without PowerShell `$HOME` interpolation (escape `$` or set PATH via `env` with a literal WSL-side value).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| $WslDistro = if ($env:AETHER_WSL_DISTRO) { $env:AETHER_WSL_DISTRO } else { 'Ubuntu' } | ||
| $AdapterSh = if ($env:AETHER_TASK_ADAPTER_SH) { $env:AETHER_TASK_ADAPTER_SH } else { | ||
| '~/.local/share/aether/integrations/taskwarrior/adapter.sh' | ||
| } |
There was a problem hiding this comment.
2. Wrong wsl script path 🐞 Bug ≡ Correctness
The PowerShell adapter defaults to an adapter.sh path, and the README documents adapter.sh, but this PR only adds adapter-wsl.sh in-repo. This mismatch makes the integration fail unless users manually override AETHER_TASK_ADAPTER_SH and/or rename/install the WSL script separately.
Agent Prompt
## Issue description
The documented/expected WSL bridge script name and the shipped file name do not match (`adapter.sh` vs `adapter-wsl.sh`). The PowerShell adapter’s default path and README both reference `adapter.sh`, but the repo contains only `adapter-wsl.sh`.
## Issue Context
This breaks out-of-the-box execution and makes the integration dependent on undocumented manual setup.
## Fix Focus Areas
- integrations/taskwarrior/adapter.ps1[36-39]
- integrations/taskwarrior/README.md[53-56]
- integrations/taskwarrior/adapter-wsl.sh[1-3]
## Suggested fix
Choose one:
- Rename `adapter-wsl.sh` to `adapter.sh` and update docs accordingly, or
- Update `adapter.ps1` default + README to refer to `adapter-wsl.sh`, and document/provide an install step that places it in the expected WSL location.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| cmd_get() { | ||
| local uuid="$1"; [ -z "$uuid" ] && echo '{"error":"uuid required"}' >&2 && exit 1 | ||
| local t; t=$(safe_export "") || return 1 | ||
| jq "map(select(.uuid == \"$uuid\"))[0] // {\"error\":\"not found\",\"uuid\":\"$uuid\"}" "$t" | ||
| rm -f "$t" |
There was a problem hiding this comment.
3. Not-found exit codes wrong 🐞 Bug ≡ Correctness
integrations/taskwarrior/adapter-wsl.sh does not exit with code 4 when a UUID is missing: get returns a not-found object but still exits 0, and done exits 1. This violates ADAPTER-CONTRACTS.md and breaks callers that use exit code 4 to distinguish missing UUIDs from success/usage errors.
Agent Prompt
## Issue description
The Taskwarrior adapter contract requires exit code 4 for not-found UUIDs. The current WSL adapter returns 0 for `get` not-found (because `jq` succeeds) and returns 1 for `done` not-found.
## Issue Context
This violates the standardized adapter exit-code semantics documented in `integrations/ADAPTER-CONTRACTS.md`.
## Fix Focus Areas
- integrations/taskwarrior/adapter-wsl.sh[34-39]
- integrations/taskwarrior/adapter-wsl.sh[51-57]
- integrations/ADAPTER-CONTRACTS.md[69-80]
## Suggested fix
- In `cmd_get`, detect the not-found case and `exit 4` after emitting the JSON error object.
- In `cmd_done`, replace `exit 1` with `exit 4` for missing UUIDs.
- Ensure the error object is emitted consistently with your stderr/stdout policy.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| cmd_list() { | ||
| local filter="${*:-}"; local t; t=$(safe_export "") || return 1 | ||
| local f; f=$(apply_filter "$t" "$filter"); clean_task "$f"; rm -f "$f" "$t" | ||
| } |
There was a problem hiding this comment.
4. Runtime errors become usage 🐞 Bug ☼ Reliability
When safe_export fails, multiple commands coerce the failure into return code 1 via || return 1, masking runtime/tool failures as usage errors. This violates the contract’s exit code 2 semantics for runtime errors and can cause incorrect retry/diagnosis behavior.
Agent Prompt
## Issue description
`safe_export` can fail due to Taskwarrior/tool/runtime problems, but callers commonly do `t=$(safe_export ...) || return 1`, which forces a usage error code instead of the contract’s runtime error code.
## Issue Context
Contract defines:
- 1 = usage
- 2 = runtime/tool failure
## Fix Focus Areas
- integrations/taskwarrior/adapter-wsl.sh[30-33]
- integrations/taskwarrior/adapter-wsl.sh[40-43]
- integrations/taskwarrior/adapter-wsl.sh[44-49]
- integrations/taskwarrior/adapter-wsl.sh[51-54]
- integrations/ADAPTER-CONTRACTS.md[11-15]
## Suggested fix
- Replace `|| return 1` with `|| return 2` (or a helper that maps tool failures to 2).
- Consider standardizing: `safe_export` emits JSON error + returns 2, and callers propagate 2 without rewriting it.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| $TASK_BIN "$id" done 2>&1 >/dev/null | ||
| echo "{\"uuid\":\"$uuid\",\"id\":$id,\"status\":\"completed\",\"action\":\"done\"}" |
There was a problem hiding this comment.
5. Done stderr leaks to stdout 🐞 Bug ☼ Reliability
cmd_done uses redirections 2>&1 >/dev/null, which can leak Taskwarrior stderr into stdout and corrupt JSON-only output. The call also lacks explicit error mapping (e.g., emitting a JSON error and exiting 2) if task done fails for reasons other than set -e termination.
Agent Prompt
## Issue description
The stderr/stdout redirection order is incorrect, so stderr may still reach the caller even when stdout is discarded. This can break consumers expecting valid JSON on stdout.
## Issue Context
The adapter contract emphasizes consistent machine-consumable output.
## Fix Focus Areas
- integrations/taskwarrior/adapter-wsl.sh[51-57]
## Suggested fix
- Change to `>/dev/null 2>&1` (if the goal is to suppress all output from `task done`).
- Wrap the call to capture status and, on failure, emit `{ "error": "done failed", "code": <rc> }` and exit 2 (runtime).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| Write-Error "Usage: adapter.ps1 $Command <uuid>" | ||
| exit 1 | ||
| } | ||
| if ($Args[0] -notmatch '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') { | ||
| Write-Error "Invalid UUID format: $($Args[0])" | ||
| exit 1 |
There was a problem hiding this comment.
6. Non-json powershell errors 🐞 Bug ≡ Correctness
The PowerShell adapter emits plain Write-Error strings for usage and runtime errors, rather than a JSON error object with an error key as required by the adapter contract. This makes error handling inconsistent and can break JSON-only consumers.
Agent Prompt
## Issue description
The contract requires errors to include an `error` key, but the PowerShell adapter outputs non-JSON error records/messages (`Write-Error "Usage: ..."`, and `Write-Error "...: $result"`).
## Issue Context
Downstream agent tooling typically expects machine-readable JSON on failures too.
## Fix Focus Areas
- integrations/taskwarrior/adapter.ps1[41-46]
- integrations/taskwarrior/adapter.ps1[48-66]
- integrations/taskwarrior/adapter.ps1[78-81]
- integrations/ADAPTER-CONTRACTS.md[89-94]
## Suggested fix
- Replace `Write-Error` messages with JSON error objects written to stderr (e.g., `[Console]::Error.WriteLine('{"error":"..."}')`) and exit with the appropriate code.
- On WSL nonzero exit, forward the WSL stderr as-is (without wrapping) or wrap it into a JSON error object while preserving the exit code.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Summary
Adds a coherent CLI adapter layer for agent orchestration: four adapters (thyme, wuzz, fx, taskwarrior) with unified JSON contracts and exit codes, plus a full WSL utility layer and tool inventory.
What's in this branch
Adapters
Contracts (
ADAPTER-CONTRACTS.md)"error"keyWSL utility layer
jq 1.8.1, rg 15.1.0, fd 10.3.0, bat 0.25.0, fzf 0.67.0, tree, htop, task 2.6.2 — plus fd/bat shims for distro-agnostic paths.
EcoMap (
integrations/ECOMAP.md)Full inventory: 31 tools across WSL and Windows, with paths and versions.
Key decisions
No changes to main, no vendored sources, no config modifications.