1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
|
# 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 '<year> <copyright holders>' 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. <danix@danix.xyz>" "$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 <name> <license> <description> [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. <danix@danix.xyz>"
[[ $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 <year> in their
# how-to-apply appendix, which must stay as published.
if [ $text = mit.txt ]; then
sed "s/<year> <copyright holders>/$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 <<EOF
# AGENTS.md
Guidance for AI coding agents working in this repository.
## What this is
\`$name\`: $desc
## Conventions
- Placeholders only in code, docs, tests and commits.
## Testing
No test suite yet.
## Repo facts
- License: $lic (\`LICENSE\`).
- Work directly on master.
EOF
cat > README.md <<EOF
# $name
$desc
## License
Copyright (C) $year $author
$lic. See [LICENSE](LICENSE) for the full text.
## Development Approach
This project is developed using AI-assisted tools. Code is generated with the help of AI based on human-provided specifications, design decisions, and iterative feedback.
All contributions are reviewed, tested, and curated by the maintainer before being included in the codebase. AI is used as a productivity and exploration tool, while human oversight remains central to all decisions.
The goal is to combine the flexibility of AI-assisted development with standard open-source practices such as transparency, review, and accountability.
EOF
# Signing is never turned off: a cold cache brings up pinentry-qt, hence
# the tool's 120 s timeout. A failed commit keeps the folder and says why.
git init -q -b master && git add -A || exit 1
if err=$(git commit -q -S -m "Initial commit" 2>&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:<it> 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.
|