Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Run without installing:
./devloop --help
```

`install.sh` also installs the bundled Agent Skills globally under `~/.agents/skills`.
`install.sh` also installs the bundled Agent Skills globally for Codex under `~/.agents/skills`
and Claude Code under `~/.claude/skills`.
After install or update, verify the local setup:

```sh
Expand Down Expand Up @@ -126,8 +127,8 @@ When stdout is a terminal, running `devloop` without arguments opens a menu:
bash -n devloop install.sh
./devloop --help
tmp="$(mktemp -d)"
DEVLOOP_BIN_DIR="$tmp/bin" DEVLOOP_SKILLS_DIR="$tmp/skills" ./install.sh
PATH="$tmp/bin:$PATH" DEVLOOP_SKILLS_DIR="$tmp/skills" devloop doctor
DEVLOOP_BIN_DIR="$tmp/bin" HOME="$tmp/home" ./install.sh
PATH="$tmp/bin:$PATH" HOME="$tmp/home" devloop doctor
```

The supported runtime is the root [`devloop`](devloop) Bash script.
Expand Down
44 changes: 38 additions & 6 deletions skill_helpers.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/env bash

devloop_skills_dir() {
printf '%s\n' "${DEVLOOP_SKILLS_DIR:-$HOME/.agents/skills}"
devloop_skills_dirs() {
printf '%s\n' "$HOME/.agents/skills"
printf '%s\n' "$HOME/.claude/skills"
}

devloop_checksum_file() {
Expand Down Expand Up @@ -59,8 +60,7 @@ devloop_can_replace_skill() {

devloop_install_skills() {
local root="$1"
local skills_dir mode source name dest checksum status
skills_dir="$(devloop_skills_dir)"
local skills_dir mode status
mode="${DEVLOOP_SKILL_INSTALL:-copy}"
status=0

Expand All @@ -72,6 +72,23 @@ devloop_install_skills() {
;;
esac

while IFS= read -r skills_dir; do
if [ -z "$skills_dir" ]; then continue; fi
devloop_install_skills_to_dir "$root" "$skills_dir" "$mode" || status=1
done <<EOF
$(devloop_skills_dirs)
EOF

return "$status"
}

devloop_install_skills_to_dir() {
local root="$1"
local skills_dir="$2"
local mode="$3"
local source name dest checksum status
status=0

if ! mkdir -p "$skills_dir"; then
printf 'failed to create skills directory: %s\n' "$skills_dir" >&2
return 1
Expand Down Expand Up @@ -153,8 +170,23 @@ devloop_doctor_optional_command() {

devloop_doctor_skills() {
local root="$1"
local skills_dir source name dest declared bundled installed status
skills_dir="$(devloop_skills_dir)"
local skills_dir status
status=0

while IFS= read -r skills_dir; do
if [ -z "$skills_dir" ]; then continue; fi
devloop_doctor_skills_in_dir "$root" "$skills_dir" || status=1
done <<EOF
$(devloop_skills_dirs)
EOF

return "$status"
}

devloop_doctor_skills_in_dir() {
local root="$1"
local skills_dir="$2"
local source name dest declared bundled installed status
status=0

for source in "$root"/skills/*; do
Expand Down
29 changes: 17 additions & 12 deletions tests/devloop_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -211,36 +211,41 @@ contains "$uuid_one" "00000000-0000-4000-8000-" "new_uuid fallback format"
ok "pure helpers"

bin_dir="$work/bin"
skills_dir="$work/skills"
DEVLOOP_BIN_DIR="$bin_dir" DEVLOOP_SKILLS_DIR="$skills_dir" "$ROOT/install.sh" >/tmp/devloop-install-test.out
install_home="$work/install-home"
DEVLOOP_BIN_DIR="$bin_dir" HOME="$install_home" "$ROOT/install.sh" >/tmp/devloop-install-test.out
[[ -x "$ROOT/devloop" ]] || fail "devloop is not executable"
[[ -L "$bin_dir/devloop" ]] || fail "installer did not create symlink"
[[ -f "$skills_dir/devloop-spec/SKILL.md" ]] || fail "installer did not install spec skill"
[[ -f "$skills_dir/devloop-spec/references/spec-template.md" ]] || fail "installer did not install spec template reference"
[[ -f "$skills_dir/devloop-review/SKILL.md" ]] || fail "installer did not install review skill"
[[ -f "$skills_dir/devloop-review/.devloop-checksum" ]] || fail "installer did not write checksum"
[[ -f "$install_home/.agents/skills/devloop-spec/SKILL.md" ]] || fail "installer did not install Codex spec skill"
[[ -f "$install_home/.agents/skills/devloop-spec/references/spec-template.md" ]] || fail "installer did not install Codex spec template reference"
[[ -f "$install_home/.agents/skills/devloop-review/SKILL.md" ]] || fail "installer did not install Codex review skill"
[[ -f "$install_home/.agents/skills/devloop-review/.devloop-checksum" ]] || fail "installer did not write Codex checksum"
[[ -f "$install_home/.claude/skills/devloop-spec/SKILL.md" ]] || fail "installer did not install Claude spec skill"
[[ -f "$install_home/.claude/skills/devloop-review/SKILL.md" ]] || fail "installer did not install Claude review skill"
[[ -f "$install_home/.claude/skills/devloop-review/.devloop-checksum" ]] || fail "installer did not write Claude checksum"
"$bin_dir/devloop" --help >/tmp/devloop-help-test.out
contains "$(cat /tmp/devloop-help-test.out)" "Spec-driven code and review loop." "installed help"
ok "installer"

printf '%s\n' "user edit" >> "$skills_dir/devloop-review/SKILL.md"
DEVLOOP_BIN_DIR="$bin_dir" DEVLOOP_SKILLS_DIR="$skills_dir" "$ROOT/install.sh" >/tmp/devloop-install-skip.out 2>&1
printf '%s\n' "user edit" >> "$install_home/.agents/skills/devloop-review/SKILL.md"
DEVLOOP_BIN_DIR="$bin_dir" HOME="$install_home" "$ROOT/install.sh" >/tmp/devloop-install-skip.out 2>&1
contains "$(cat /tmp/devloop-install-skip.out)" "skipping modified skill" "installer modified skill guard"
contains "$(cat /tmp/devloop-install-skip.out)" "try: devloop doctor" "installer guidance after skill skip"
contains "$(cat "$skills_dir/devloop-review/SKILL.md")" "user edit" "installer modified skill preserved"
DEVLOOP_FORCE=1 DEVLOOP_BIN_DIR="$bin_dir" DEVLOOP_SKILLS_DIR="$skills_dir" "$ROOT/install.sh" >/tmp/devloop-install-force.out
if grep -q "user edit" "$skills_dir/devloop-review/SKILL.md"; then fail "installer force did not restore skill"; fi
contains "$(cat "$install_home/.agents/skills/devloop-review/SKILL.md")" "user edit" "installer modified skill preserved"
DEVLOOP_FORCE=1 DEVLOOP_BIN_DIR="$bin_dir" HOME="$install_home" "$ROOT/install.sh" >/tmp/devloop-install-force.out
if grep -q "user edit" "$install_home/.agents/skills/devloop-review/SKILL.md"; then fail "installer force did not restore skill"; fi
ok "installer skill updates"

fake_bin="$work/fake-bin"
mkdir -p "$fake_bin"
printf '#!/usr/bin/env bash\nexit 0\n' > "$fake_bin/codex"
printf '#!/usr/bin/env bash\nexit 0\n' > "$fake_bin/claude"
chmod +x "$fake_bin/codex" "$fake_bin/claude"
doctor_output="$(DEVLOOP_SKILLS_DIR="$skills_dir" PATH="$bin_dir:$fake_bin:$PATH" "$bin_dir/devloop" doctor 2>&1)"
doctor_output="$(HOME="$install_home" PATH="$bin_dir:$fake_bin:$PATH" "$bin_dir/devloop" doctor 2>&1)"
contains "$doctor_output" "devloop doctor: ready" "doctor"
contains "$doctor_output" "[ok] skill devloop-spec" "doctor"
contains "$doctor_output" "Optional UI" "doctor"
contains "$doctor_output" "$install_home/.agents/skills/devloop-spec" "doctor Codex skill"
contains "$doctor_output" "$install_home/.claude/skills/devloop-spec" "doctor Claude skill"
ok "doctor"

agent="$work/spec-agent"
Expand Down