From 981958de582740b3c8ecfb93348536494e0bb995 Mon Sep 17 00:00:00 2001 From: calvin-archastro Date: Wed, 9 Sep 2026 09:21:39 -0700 Subject: [PATCH] Add Tasks skill for CLI bootstrap and browser plan review --- .github/workflows/installer-smoke-test.yml | 8 + README.md | 15 ++ skills/tasks/SKILL.md | 224 +++++++++++++++++++++ skills/tasks/scripts/bootstrap.ps1 | 46 +++++ skills/tasks/scripts/bootstrap.sh | 48 +++++ tests/tasks-skill.sh | 60 ++++++ 6 files changed, 401 insertions(+) create mode 100644 skills/tasks/SKILL.md create mode 100644 skills/tasks/scripts/bootstrap.ps1 create mode 100755 skills/tasks/scripts/bootstrap.sh create mode 100755 tests/tasks-skill.sh diff --git a/.github/workflows/installer-smoke-test.yml b/.github/workflows/installer-smoke-test.yml index 037786d..cf847c5 100644 --- a/.github/workflows/installer-smoke-test.yml +++ b/.github/workflows/installer-smoke-test.yml @@ -15,6 +15,14 @@ jobs: - name: Verify machine and repository skill installation run: bash tests/rooms-skill.sh + tasks-skill: + name: Tasks Skill Installation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Verify Tasks skill packaging and bootstrap + run: bash tests/tasks-skill.sh + unix-installer: name: Unix Installer (${{ matrix.os }}, ${{ matrix.shell_name }}) runs-on: ${{ matrix.os }} diff --git a/README.md b/README.md index fc6608e..f08d2bb 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,21 @@ Then ask your coding agent to connect to the company Room, search what the team knows, or start a substantial piece of work. The first participant creates the Room; later participants join the same Room automatically. +## Install Tasks independently + +Tasks turns a conversation or an existing coding-agent plan into a dependency +graph for browser review. The agent opens the review, reads your feedback, +revises the same plan, and verifies that **Approve & save** saved the Tasks. +The skill installs or updates ArchDev on first use; no Factory or daemon is +required. + +```bash +npx skills add ArchAstro/archdev --skill tasks --global --yes +``` + +Omit `--global` to install only in the current repository. Then ask your agent: +“Turn our plan into Tasks, open the web review, and iterate on my feedback.” + ## Repository scope This repository owns public distribution: installers, skills, release diff --git a/skills/tasks/SKILL.md b/skills/tasks/SKILL.md new file mode 100644 index 0000000..be1ce4f --- /dev/null +++ b/skills/tasks/SKILL.md @@ -0,0 +1,224 @@ +--- +name: tasks +description: Use when someone wants to turn a conversation or coding-harness plan into ArchDev Tasks, build a dependency graph, review a task plan in the browser, collect feedback and revise it until approved, or resume work on saved Tasks. +--- + +# Tasks + +Use ArchDev Tasks as the shared plan and work record. You drive drafting, +browser handoff, feedback collection, revisions, and save verification; the +human reviews the plan in the web UI. This works from any coding harness and +does not require Factory, a daemon, a resident agent, or `archdev setup`. + +## 1. Connect this machine + +Resolve the absolute directory containing this loaded `SKILL.md`, independently +of the current repository. Its bootstrap installs ArchDev when missing and +updates an older CLI that lacks the review commands. Capture its stdout, which +is the executable's absolute path; diagnostics go to stderr. + +Bash/Zsh: + +```sh +archdev="$(bash /absolute/path/to/tasks/scripts/bootstrap.sh)" +``` + +Fish: + +```fish +set archdev (bash /absolute/path/to/tasks/scripts/bootstrap.sh) +``` + +Windows PowerShell: + +```powershell +$archdev = & powershell -NoProfile -File 'C:\absolute\path\to\tasks\scripts\bootstrap.ps1' +``` + +Commands below use `"$archdev"`; PowerShell uses `& $archdev` with the same +arguments. If bootstrap fails, report its error and point to the +[official installer](https://github.com/ArchAstro/archdev#install). + +1. Run `"$archdev" auth status`. If signed out, run `"$archdev" auth login`, + let the human finish browser sign-in, then check status again. +2. Read `"$archdev" tasks guide` and `"$archdev" tasks graph schema` for the + installed CLI's contract. Prefer `--json` for output you need to parse. +3. Choose the destination from the user's request and existing context. + Review defaults to the signed-in user's personal Tasks. For team work, + supply a known `--team `; use `--repository ` when applicable. + If the team is ambiguous, ask which team before saving. Never guess an ID. +4. When resuming work, inspect the existing Tasks and local plan/receipt before + creating another plan. `tasks list --team ` and `tasks show ` + expose existing work; consult their help for other scopes. + +## 2. Turn the source into a draft DAG + +A DAG is a task graph with prerequisite edges and no cycles. Draft locally +first; opening review does not create Tasks. Do not pre-create every node with +`tasks create`, or import it separately, before sending the same graph through +review: approval owns saving this plan. + +1. **From conversation:** extract the requested outcome, agreed decisions, + constraints, exclusions, and unresolved choices. Inspect relevant code when + needed to make the tasks actionable. Do not invent requirements. +2. **From a harness plan:** read the actual plan artifact or harness-provided + plan state. Translate its steps into graph nodes while preserving intent, + acceptance criteria, references, and dependencies. A harness checklist is + input, not a second task authority after saving. Use stable source IDs as + node keys where possible; do not invent harness/session metadata. +3. Name tasks by observable outcome. Give each a concrete objective, acceptance + criteria, and verification. For implementation work, name the planned + end-to-end test file/scenario and the boundary and observable outcome it + proves; identify any missing prerequisites honestly. +4. Add `depends_on` only where a task needs another task's result. Explain each + edge with `dependency_reasons`. Leave independent tasks parallel. Every + dependency must reference a node key in this graph; no self-edges or cycles. +5. Keep unresolved design choices in `major_decisions` for the human to review. + Do not silently choose on their behalf when the choice changes scope. + +Write a UTF-8 JSON file at a stable path, such as `plans/task-review.json`. +Use the graph schema's `action: "preview"` format. Required node fields are +`key`, `title`, `objective`, `acceptance`, and `verification`. The review command +also accepts optional `major_decisions` (not part of `tasks graph schema`). + +```json +{ + "action": "preview", + "epic": "Users can export their filtered results", + "nodes": [ + { + "key": "export-api", + "title": "Export respects the user's filters", + "objective": "Add the export endpoint using the existing filtered query.", + "acceptance": ["Export includes exactly the authorized filtered results."], + "verification": ["Planned tests/export-api.e2e.test.ts: filtered export crosses HTTP and database boundaries and asserts returned rows."], + "scope": ["Export API"], + "exclusions": ["Scheduled exports"] + }, + { + "key": "export-ui", + "title": "Users can download filtered results", + "objective": "Connect the results page's download action to the export endpoint.", + "acceptance": ["The downloaded file matches the active filters."], + "verification": ["Planned tests/export-ui.e2e.test.ts: download filtered results through the browser and inspect file contents."], + "depends_on": ["export-api"], + "dependency_reasons": {"export-api": "The download action needs the export endpoint."} + } + ], + "major_decisions": [ + { + "id": "export-format", + "name": "Choose the export format", + "description": "Which format should the first version support?", + "options": ["CSV", "JSON"] + } + ] +} +``` + +Replace illustrative paths with this project's real planned proof. Optional +node fields also include `context`, `deliverables`, and `priority` (0–4). +Keep context concise and omit secrets and customer data. The CLI validates the +preview and computes its hash; do not manufacture `plan_hash`. + +## 3. Open the review and keep it running + +```sh +"$archdev" tasks review start --file plans/task-review.json --no-open +``` + +Add the chosen `--team` and `--repository` flags to that command when needed. +Run it in the harness's persistent process/PTY facility: it stays running and +streams JSONL. Do not wrap it in a short timeout, wait for it to exit before +opening the page, or kill it when the command tool yields. + +1. Read the `event: "ready"` record. Retain its `url`, `sessionFile`, + `stateFile`, `revision`, and `planHash` along with the process handle. +2. Open that exact URL once in the human's main browser profile using the + available browser/open tool. The page updates in place on revisions. + If browser control is unavailable, hand the URL to the human. Without + `--no-open`, the CLI attempts to open the default browser itself. +3. Tell the human where to review and that **Approve & save** writes Tasks to + the selected destination. Begin listening for feedback in the same turn. + +The URL and session handle contain a local bearer capability. Do not commit +or publish them, dump the handle contents, or put them in shared logs. The +browser must run on a machine that can reach this CLI's loopback server; +a remote sandbox's localhost is not the human's localhost. If that boundary +prevents review, arrange execution on the human's machine instead of claiming +the page is reachable or exposing the server publicly. + +## 4. Listen, revise, and verify saving + +Start the feedback cursor at `0` for this session. + +```sh +"$archdev" tasks review feedback --after +"$archdev" tasks review status +"$archdev" tasks review update --file plans/task-review.json --revision +``` + +Drive this loop until the current revision is saved, the user pauses/cancels, +or a concrete blocker needs their input: + +1. Read feedback and status. Feedback returns `{events, cursor}` without + consuming events. Process new events, then retain the returned cursor. + The start process also streams events; avoid processing the same sequence + twice. Poll with a modest wait (for example 5–10 seconds), staying responsive + to user messages; empty feedback is not approval or a reason to stop. +2. Match feedback to its `revision` and `planHash`. Read plan-wide, + task-specific, and decision-specific notes. Map task IDs through the + current status request to the stable node keys. Old-revision notes are + historical context, not instructions to apply automatically to a new plan. +3. For requested changes, edit the same JSON file. Preserve keys for unchanged + work; add keys for new work, remove obsolete nodes, and repair dependency + edges. Resolve reviewed decisions in the plan and remove resolved entries + from `major_decisions`. Explain briefly what changed and why. +4. Publish with the current revision from status. On a revision conflict, + re-read status and feedback and reconcile; do not blindly retry with an + incremented number. Retain the returned revision/hash. Continue on the + existing browser page rather than starting another review session. +5. For approval, wait for status `saved` for the current revision/hash and + inspect its `saved` receipt. `saving` is still in progress. `save_failed` + may mean some Tasks were already written. For a transient failure, report + the error, address it, and use the browser's save retry. If the error says + a Task changed outside this review, inspect that Task, reconcile the draft, + publish a new revision, and obtain fresh human approval; retrying the old + revision cannot resolve that conflict. Keep the same receipt and session; + do not recreate the plan to retry. Never submit approval through a private + HTTP endpoint or click **Approve & save** on the human's behalf. +6. Verify returned task IDs with `tasks show ` and check prerequisite + edges with `tasks deps list ` (follow pagination when present). + Report the saved outcome and task IDs. If the user + requests another revision, continue with the same plan and receipt. + +The durable `stateFile` maps node keys to task IDs. Later approved revisions +update those IDs; removing a previously saved node closes its Task and retains +history. Preserve the receipt even after review ends. Restarting with the same +absolute plan path reuses the default receipt; if moving the plan, pass the +original `--state-file `. A restarted server has a new session handle and +URL, so open its new ready URL and reset the feedback cursor. Do not delete a +receipt or change its destination to bypass an error. + +When finished or cancelled, run `"$archdev" tasks review stop `. +This stops the local server and removes its private session handle; it does +not delete saved Tasks. If explicitly pausing for later, retain the draft and +receipt and explain how to restart. Do not report a paused review as saved. + +## 5. Work from the saved Tasks when asked + +Approval of a plan saves Tasks; it does not itself request implementation. +When implementation is authorized, use the installed `tasks guide` lifecycle: + +1. Read `tasks ready --explain --team ` (or the intended user scope). +2. Claim with `tasks claim --session-name ""` before + working. Retain the returned `lease_id` and `session_id`; readiness alone + is not ownership. +3. Record progress with `tasks comment`. Use both `--lease-id` and + `--session-id` on fenced updates, close, and release. Close only after + verification; re-read ready Tasks when a prerequisite finishes. + +Keep shared progress on these saved Tasks rather than creating duplicate +harness tasks. Use `tasks deps add --blocked-by ` +for explicit changes to existing dependencies, and consult command help for +other lifecycle operations. diff --git a/skills/tasks/scripts/bootstrap.ps1 b/skills/tasks/scripts/bootstrap.ps1 new file mode 100644 index 0000000..f8c3734 --- /dev/null +++ b/skills/tasks/scripts/bootstrap.ps1 @@ -0,0 +1,46 @@ +$ErrorActionPreference = "Stop" + +function Resolve-ArchDevPath([string]$Candidate) { + return (Resolve-Path -LiteralPath $Candidate).Path +} + +function Install-ArchDev { + $installerUrl = if ($env:ARCHDEV_INSTALLER_URL) { + $env:ARCHDEV_INSTALLER_URL + } else { + "https://raw.githubusercontent.com/ArchAstro/archdev/7c16002d66a004b13812cf675042cb1c50fbf6df/install.ps1" + } + $installDir = if ($env:ARCHDEV_INSTALL_DIR) { + $env:ARCHDEV_INSTALL_DIR + } else { + Join-Path $env:LOCALAPPDATA "ArchDev\bin" + } + $installerPath = Join-Path ([IO.Path]::GetTempPath()) ("archdev-install-" + [Guid]::NewGuid().ToString("N") + ".ps1") + try { + Invoke-WebRequest -Uri $installerUrl -OutFile $installerPath + $env:ARCHDEV_INSTALL_DIR = $installDir + & $installerPath -SkipPathUpdate *> $null + if (-not $?) { throw "ArchDev installer failed" } + } finally { + Remove-Item $installerPath -Force -ErrorAction SilentlyContinue + } + return (Resolve-ArchDevPath (Join-Path $installDir "archdev.exe")) +} + +$existing = Get-Command archdev -ErrorAction SilentlyContinue +$archdev = if ($existing) { Resolve-ArchDevPath $existing.Source } else { Install-ArchDev } + +& $archdev tasks review update --help *> $null +if ($LASTEXITCODE -ne 0) { + [Console]::Error.WriteLine("Updating ArchDev because this version lacks Tasks web review commands.") + $archdev = Install-ArchDev +} + +if (-not (Test-Path -LiteralPath $archdev -PathType Leaf)) { + throw "ArchDev installer did not create an executable at $archdev" +} +& $archdev --version *> $null +if ($LASTEXITCODE -ne 0) { throw "ArchDev version verification failed" } +& $archdev tasks review update --help *> $null +if ($LASTEXITCODE -ne 0) { throw "Installed ArchDev does not provide Tasks web review commands" } +Write-Output $archdev diff --git a/skills/tasks/scripts/bootstrap.sh b/skills/tasks/scripts/bootstrap.sh new file mode 100755 index 0000000..2914997 --- /dev/null +++ b/skills/tasks/scripts/bootstrap.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -euo pipefail + +installer_revision="7c16002d66a004b13812cf675042cb1c50fbf6df" +installer_url="${ARCHDEV_INSTALLER_URL:-https://raw.githubusercontent.com/ArchAstro/archdev/${installer_revision}/install.sh}" +install_dir="${ARCHDEV_INSTALL_DIR:-$HOME/.local/bin}" + +absolute_path() { + local candidate="$1" + local directory + directory="$(cd -P "$(dirname "$candidate")" && pwd)" + printf '%s/%s\n' "$directory" "$(basename "$candidate")" +} + +install_archdev() { + curl --fail --silent --show-error --location "$installer_url" | + ARCHDEV_INSTALL_DIR="$install_dir" \ + ARCHDEV_INSTALL_SKIP_PATH_UPDATE=true \ + ARCHDEV_INSTALL_SKIP_COMPLETIONS=true \ + bash >&2 +} + +candidate="$(command -v archdev 2>/dev/null || true)" +if [[ -n "$candidate" ]]; then + executable="$(absolute_path "$candidate")" +else + install_archdev + executable="$(absolute_path "$install_dir/archdev")" +fi + +if ! "$executable" tasks review update --help >/dev/null 2>&1; then + printf 'Updating ArchDev because this version lacks Tasks web review commands.\n' >&2 + install_archdev + executable="$(absolute_path "$install_dir/archdev")" +fi + +[[ -x "$executable" ]] || { + printf 'ArchDev installer did not create an executable at %s\n' "$executable" >&2 + exit 1 +} + +"$executable" --version >&2 +"$executable" tasks review update --help >/dev/null 2>&1 || { + printf 'Installed ArchDev does not provide Tasks web review commands.\n' >&2 + exit 1 +} +printf '%s\n' "$executable" diff --git a/tests/tasks-skill.sh b/tests/tasks-skill.sh new file mode 100755 index 0000000..e8af91b --- /dev/null +++ b/tests/tasks-skill.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +set -euo pipefail +repo="$(cd -P "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +root="$(mktemp -d)" +root="$(cd -P "$root" && pwd)" +trap 'rm -rf "$root"' EXIT +mkdir -p "$root/home" "$root/project" "$root/installer" +git -C "$root/project" init -q + +# Cross the real skill-manager boundary in both supported installation scopes. +HOME="$root/home" npx --yes skills add "$repo" --global --skill tasks --agent codex --yes --copy >/dev/null +test -x "$root/home/.agents/skills/tasks/scripts/bootstrap.sh" +( + cd "$root/project" + HOME="$root/home" npx --yes skills add "$repo" --skill tasks --agent codex --yes --copy >/dev/null +) +bootstrap="$root/project/.agents/skills/tasks/scripts/bootstrap.sh" +test -x "$bootstrap" + +# Substitute only the release boundary: no real installation or account writes. +cat > "$root/installer/install.sh" <<'INSTALLER' +set -eu +printf 'install\n' >> "$ARCHDEV_TEST_INSTALL_LOG" +mkdir -p "$ARCHDEV_INSTALL_DIR" +cat > "$ARCHDEV_INSTALL_DIR/archdev" <<'CLI' +#!/usr/bin/env bash +if [[ "$*" == '--version' ]]; then echo fixture; exit 0; fi +[[ "$*" == 'tasks review update --help' ]] +CLI +chmod +x "$ARCHDEV_INSTALL_DIR/archdev" +INSTALLER +export ARCHDEV_INSTALLER_URL="file://$root/installer/install.sh" +export ARCHDEV_INSTALL_DIR="$root/bin" +export ARCHDEV_TEST_INSTALL_LOG="$root/installs" + +# A cold install returns one absolute executable path despite PATH omitting it. +binary="$(HOME="$root/home" PATH=/usr/bin:/bin bash "$bootstrap")" +test "$binary" = "$root/bin/archdev" +"$binary" tasks review update --help +test "$(wc -l < "$root/installs" | tr -d ' ')" = 1 + +# A capable PATH installation must be reused without contacting the installer. +reused="$(PATH="$root/bin:/usr/bin:/bin" bash "$bootstrap")" +test "$reused" = "$binary" +test "$(wc -l < "$root/installs" | tr -d ' ')" = 1 + +# An older CLI is upgraded once; its replacement must satisfy the review probe. +printf '#!/usr/bin/env bash\nexit 1\n' > "$binary" +updated="$(PATH="$root/bin:/usr/bin:/bin" bash "$bootstrap")" +test "$updated" = "$binary" +test "$(wc -l < "$root/installs" | tr -d ' ')" = 2 +"$updated" tasks review update --help + +# An installer failure must fail bootstrap rather than emit a usable-looking path. +if ARCHDEV_INSTALLER_URL="file://$root/missing" PATH=/usr/bin:/bin bash "$bootstrap" > "$root/failed-output" 2>/dev/null; then + echo 'Expected installer failure' >&2 + exit 1 +fi +test ! -s "$root/failed-output" +printf 'Tasks skill packages in both scopes; bootstrap handles cold, current, outdated, and failed installs.\n'