aboutsummaryrefslogtreecommitdiffstats
path: root/examples/tools/project.sh
blob: 25732d586cc43b5c143de23a3f9bef5e4b23e8bc (plain)
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
#!/bin/bash
# desktop-assistant tool: start a new project.
# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# project.sh <name> <license> <description> [claude|opencode]: a folder in
# ~/Programming/GIT with AGENTS.md, CLAUDE.md, README.md and LICENSE, a
# signed first commit, then a kitty tab running the agent there. Check:
# test-project.sh.

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"