# New Project Tool Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Let the assistant start a new project: a folder in `~/Programming/GIT` with AGENTS.md, CLAUDE.md, README.md, LICENSE, a signed first commit, and a kitty tab running Claude Code or opencode in it. **Architecture:** One tool file (`project.toml`), one script (`project.sh`) and the license texts it copies (`examples/tools/licenses/`), in the existing tool format. No change to `assistant.py`. kitty gets remote control in the `kitty-conf` repo. Spec: `docs/superpowers/specs/2026-10-19-project-tool-design.md`. **Tech Stack:** bash, git (signed commits, global hooks in `~/.config/git/hooks`), kitty remote control, hyprctl. --- ### Task 1: License texts **Files:** - Create: `examples/tools/licenses/gpl-2.0.txt`, `gpl-3.0.txt`, `mit.txt` - [ ] **Step 1: Fetch them** from the official sources, never from memory. ```bash d=examples/tools/licenses; mkdir -p $d curl -fsSL --max-time 20 https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt -o $d/gpl-2.0.txt curl -fsSL --max-time 20 https://www.gnu.org/licenses/gpl-3.0.txt -o $d/gpl-3.0.txt curl -fsSL --max-time 20 https://raw.githubusercontent.com/spdx/license-list-data/main/text/MIT.txt -o $d/mit.txt ``` If gnu.org times out, copy `gpl-2.0.txt` from `LICENSE` in this repo (`cmp` it against `../wallhaven-dl/LICENSE`). - [ ] **Step 2: Check** `grep -c ' ' examples/tools/licenses/mit.txt` prints `1` (project.sh fills those two placeholders), and `head -3` of both GPL files shows the license title. ### Task 2: project.sh with its check **Files:** - Create: `examples/tools/test-project.sh` - Create: `examples/tools/project.sh` - [ ] **Step 1: Write the check** (GPLv2 header as in `test-mode.sh`). It creates projects in a throwaway `PROJECTS_DIR` with `KITTY=true`, so no tab opens. Commits are really signed: with a cold PIN cache the dialog appears once. ```bash set -u here=$(dirname "$0") PROJECTS_DIR=$(mktemp -d) KITTY=true export PROJECTS_DIR KITTY trap 'rm -rf "$PROJECTS_DIR"' EXIT fail=0 check() { [ "$2" = "$3" ] || { echo "FAIL $1: got '$2', want '$3'"; fail=1; }; } p=$PROJECTS_DIR/demo year=$(date +%Y) out=$("$here/project.sh" demo "" "A demo project." "") case "$out" in "created $p (GPLv2 only), committed "*", opened claude in a kitty "*) ;; *) echo "FAIL output: $out"; fail=1 ;; esac check "files" "$(cd "$p" && ls)" "$(printf '%s\n' AGENTS.md CLAUDE.md LICENSE README.md)" check "signed" "$(git -C "$p" log --format=%G?)" "G" check "branch" "$(git -C "$p" branch --show-current)" "master" check "clean" "$(git -C "$p" status --porcelain)" "" cmp -s "$p/LICENSE" "$here/licenses/gpl-2.0.txt"; check "license text" "$?" "0" check "claude" "$(tail -1 "$p/CLAUDE.md")" "@AGENTS.md" grep -qF '`demo`: A demo project.' "$p/AGENTS.md"; check "agents" "$?" "0" grep -qF 'GPLv2 only. See [LICENSE]' "$p/README.md"; check "readme license" "$?" "0" grep -qF '## Development Approach' "$p/README.md"; check "readme ai" "$?" "0" out=$("$here/project.sh" m mit "MIT one." opencode) case "$out" in *", opened opencode in a kitty "*) ;; *) echo "FAIL opencode: $out"; fail=1 ;; esac check "mit" "$(grep -c "Copyright (c) $year Danilo M. " "$PROJECTS_DIR/m/LICENSE")" "1" for bad in Foo ../x "" .x; do "$here/project.sh" "$bad" "" "x." >/dev/null 2>&1; check "name '$bad'" "$?" "1" done "$here/project.sh" demo "" "x." >/dev/null 2>&1; check "exists" "$?" "1" "$here/project.sh" n bsd "x." >/dev/null 2>&1; check "license" "$?" "1" "$here/project.sh" n "" "" >/dev/null 2>&1; check "no description" "$?" "1" "$here/project.sh" n "" "x." vim >/dev/null 2>&1; check "agent" "$?" "1" check "nothing else made" "$(ls "$PROJECTS_DIR" | tr '\n' ' ')" "demo m " [ $fail = 0 ] && echo "ok" exit $fail ``` - [ ] **Step 2: Run it, expect failure** (`project.sh` missing). Run: `bash examples/tools/test-project.sh` - [ ] **Step 3: Write `examples/tools/project.sh`** (GPLv2 header as in `mode.sh`, usage comment `project.sh [claude|opencode]`, check: `test-project.sh`). ```bash set -uo pipefail name=${1:-} license=${2:-} desc=${3:-} agent=${4:-claude} root=${PROJECTS_DIR:-$HOME/Programming/GIT} kitty=${KITTY:-kitty} here=$(dirname "$(readlink -f "$0")") year=$(date +%Y) author="Danilo M. " [[ $name =~ ^[a-z0-9][a-z0-9._-]*$ ]] || { echo "name must be lowercase letters, digits, - _ . (not first), not: '$name'" >&2; exit 1; } case "${license:-gpl2}" in gpl2) lic="GPLv2 only"; text=gpl-2.0.txt ;; gpl2+) lic="GPLv2 or later"; text=gpl-2.0.txt ;; gpl3) lic="GPLv3 only"; text=gpl-3.0.txt ;; mit) lic="MIT"; text=mit.txt ;; *) echo "license must be gpl2, gpl2+, gpl3 or mit, not: $license" >&2; exit 1 ;; esac [ -n "$desc" ] || { echo "description is empty" >&2; exit 1; } case "$agent" in claude|opencode) ;; *) echo "agent must be claude or opencode, not: $agent" >&2; exit 1 ;; esac dir=$root/$name [ -e "$dir" ] && { echo "$dir already exists" >&2; exit 1; } mkdir -p "$dir" && cd "$dir" || exit 1 # Only MIT has its holder filled in: the GPL texts carry in their # how-to-apply appendix, which must stay as published. if [ $text = mit.txt ]; then sed "s/ /$year $author/" "$here/licenses/$text" > LICENSE else cp "$here/licenses/$text" LICENSE fi cat > CLAUDE.md <<'EOF' # CLAUDE.md This file is intentionally thin. AGENTS.md is the single source of truth for project guidance, shared across every agent tool. Edit AGENTS.md instead. @AGENTS.md EOF cat > AGENTS.md < README.md <&1); then c="committed $(git rev-parse --short HEAD)" else c="not committed: $(grep -m1 -E 'error|fatal' <<< "$err" || tail -1 <<< "$err")" fi # A tab in the running kitty if one listens (kitty-conf: listen_on # kitty-{kitty_pid}), else a new window. # ponytail: focuses some kitty window, not necessarily the one with the new # tab when several are open; match by kitty's window id if that bites. prompt="New project: ${desc%.}. Fill in AGENTS.md and README.md from this, then wait." # opencode starts in its plan agent (default_agent in its config), which # cannot edit: it proposes, the user switches to build to write. [ "$agent" = claude ] && run=(claude "$prompt") || run=(opencode --prompt "New project: ${desc%.}. Read AGENTS.md and README.md and propose what they should say, then wait.") how=window for s in $(ls -t "${XDG_RUNTIME_DIR:-/tmp}"/kitty-* 2>/dev/null); do if "$kitty" @ --to "unix:$s" launch --type=tab --cwd="$dir" "${run[@]}" >/dev/null 2>&1; then how=tab hyprctl dispatch focuswindow class:kitty >/dev/null 2>&1 break fi done [ $how = tab ] || setsid -f "$kitty" -d "$dir" "${run[@]}" >/dev/null 2>&1 echo "created $dir ($lic), $c, opened $agent in a kitty $how" ``` - [ ] **Step 4: Run the check, expect `ok`.** Run: `chmod +x examples/tools/project.sh examples/tools/test-project.sh && examples/tools/test-project.sh` ### Task 3: project.toml and commit **Files:** - Create: `examples/tools/project.toml` - [ ] **Step 1: Write it**, exactly as in the spec's "Tool" section. - [ ] **Step 2: Self-check still passes:** `python3 assistant.py --test`. - [ ] **Step 3: Ask, then commit** `examples/tools/licenses/`, `project.sh`, `test-project.sh`, `project.toml`. ### Task 4: kitty remote control (kitty-conf repo) **Files:** - Modify: `~/Programming/GIT/kitty-conf/kitty.conf` (after `shell_integration`) - [ ] **Step 1: Ask the user**, then add: ``` allow_remote_control socket-only listen_on unix:${XDG_RUNTIME_DIR}/kitty-{kitty_pid} ``` - [ ] **Step 2:** User restarts kitty. Check `ls $XDG_RUNTIME_DIR/kitty-*` shows one socket and `kitty @ --to unix: ls >/dev/null` exits 0. - [ ] **Step 3: Ask, then commit** in `kitty-conf` (not `my-dotfiles`). ### Task 5: Install and live check - [ ] **Step 1:** `cp -r examples/tools/project.sh examples/tools/project.toml examples/tools/licenses ~/.config/desktop-assistant/tools/` then `./assistant.sh restart`. - [ ] **Step 2:** Live, over the socket: 5 phrasings it/en ("start a new project called photo-sorter, it sorts photos by date", "crea un nuovo progetto ..."). No tool call before the confirming yes, 5/5. Answer "no" on all but one. - [ ] **Step 3:** One "yes": folder in `~/Programming/GIT`, `%G?` is `G`, kitty tab focused, Claude Code started with the prompt. Then one with "...with opencode": opencode starts with the prompt. Delete that folder afterwards only if the user says so. - [ ] **Step 4:** With kitty closed, one creation opens a new window. - [ ] **Step 5:** Record results under Findings in `AGENTS.md` (and the tool in the Status paragraph), mark the spec implemented, ask, commit.