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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
site/
.pixi/
.claude/worktrees/
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,35 @@ and this project adheres to

## [Unreleased]

### Added

- A second Claude Code skill, `git-workflow`, installed alongside
`bodhi-compute` by `--install-claude`. Where `bodhi-compute` is about the
cluster, this one is about the repository open in the session: semantic
versioning with annotated `vX.Y.Z` tags, Conventional Commit messages, one
worktree per branch under `.claude/worktrees/`, landing work through a pull
request rather than committing to `main`, and running the repo's own CI
gates before pushing. It is deliberately general — it names no project, and
defers to a repository that documents something stricter of its own.

Sessions are where this work actually happens, and an agent that starts with
no standing guidance re-derives the conventions every time, or guesses. The
skill rides the rails `bodhi-compute` already established, so it reaches
every session on every node with no per-session setup.

### Changed

- The installer no longer names the skills it ships. `--install-claude` copies
every `skills/*/SKILL.md` found beside the script, and `make install`,
`make install-system` and `make nodes` install the whole `skills/` tree
rather than one path each. Adding a skill is now dropping a directory into
`skills/`, with no install target, fan-out recipe, or copy loop to update in
step — the previous shape hardcoded `skills/bodhi-compute` in six places,
and any one of them missed would have shipped a partial set to the nodes.

`make nodes-check` reports assets present based on the `skills/` directory
rather than `skills/bodhi-compute`, so it stays true as the set grows.

## [0.3.0] - 2026-08-26

### Changed
Expand Down
37 changes: 23 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ MANDIR ?= ~/.local/share/man/man1
# $XDG_DATA_DIRS (which includes /usr/local/share) for
# bash-completion/completions/<command>.
COMPDIR ?= ~/.local/share/bash-completion/completions
# Claude Code assets (skill + hooks) ship beside the script so that
# Claude Code assets (skills + hooks) ship beside the script so that
# `sinteractive --install-claude` works from an installed copy and not only
# from a git checkout. Mirrors the repo layout, so the lookup in the script
# has one code path for both.
SHAREDIR ?= ~/.local/share/sinteractive

# Every skill under skills/ ships, discovered rather than listed: adding one
# is a matter of dropping a directory in, with no install target to update.
# The paths are relative and mirror the repo layout, so `install -D` into a
# destination root reproduces skills/<name>/SKILL.md underneath it.
SKILLS := $(wildcard skills/*/SKILL.md)

# When run as root, install system-wide: sinteractive to /usr/local/bin and
# its man page to /usr/local/share/man.
UID := $(shell id -u)
Expand All @@ -31,26 +37,29 @@ install-user:
cp man/sinteractive.1 $(MANDIR)/sinteractive.1
mkdir -p $(COMPDIR)
cp completions/sinteractive.bash $(COMPDIR)/sinteractive
mkdir -p $(SHAREDIR)/claude/hooks $(SHAREDIR)/skills/bodhi-compute
mkdir -p $(SHAREDIR)/claude/hooks
install -m 0755 claude/hooks/*.sh $(SHAREDIR)/claude/hooks/
install -m 0644 claude/settings-snippet.json $(SHAREDIR)/claude/
install -m 0644 skills/bodhi-compute/SKILL.md $(SHAREDIR)/skills/bodhi-compute/
for s in $(SKILLS); do install -D -m 0644 $$s $(SHAREDIR)/$$s; done

install-system:
install -m 0755 sinteractive /usr/local/bin/sinteractive
install -D -m 0644 man/sinteractive.1 /usr/local/share/man/man1/sinteractive.1
install -D -m 0644 completions/sinteractive.bash /usr/local/share/bash-completion/completions/sinteractive
install -d -m 0755 /usr/local/share/sinteractive/claude/hooks /usr/local/share/sinteractive/skills/bodhi-compute
install -d -m 0755 /usr/local/share/sinteractive/claude/hooks
install -m 0755 claude/hooks/*.sh /usr/local/share/sinteractive/claude/hooks/
install -m 0644 claude/settings-snippet.json /usr/local/share/sinteractive/claude/
install -m 0644 skills/bodhi-compute/SKILL.md /usr/local/share/sinteractive/skills/bodhi-compute/
for s in $(SKILLS); do install -D -m 0644 $$s /usr/local/share/sinteractive/$$s; done

# ---------------------------------------------------------------------------
# Claude Code integration. Two parts:
#
# - the bodhi-compute skill, which teaches an agent cluster etiquette: the
# login node and an sinteractive session are both orchestration shells,
# and real work goes into its own allocation;
# - skills, which teach an agent how work is done here. bodhi-compute
# covers cluster etiquette: the login node and an sinteractive session are
# both orchestration shells, and real work goes into its own allocation.
# git-workflow covers the git conventions — semantic versioning,
# Conventional Commits, a worktree per branch, landing through a pull
# request — and is about the repository in the session, not the cluster;
# - two hooks for an agent running INSIDE a session, which tell it at
# startup where it is and how big the allocation is, and warn it when the
# session is running out of walltime.
Expand Down Expand Up @@ -170,26 +179,26 @@ nodes: require-root
&& mv /usr/local/bin/sinteractive.new /usr/local/bin/sinteractive \
&& install -D -m 0644 $(CURDIR)/man/sinteractive.1 /usr/local/share/man/man1/sinteractive.1 \
&& install -D -m 0644 $(CURDIR)/completions/sinteractive.bash /usr/local/share/bash-completion/completions/sinteractive \
&& install -d -m 0755 /usr/local/share/sinteractive/claude/hooks /usr/local/share/sinteractive/skills/bodhi-compute \
&& install -d -m 0755 /usr/local/share/sinteractive/claude/hooks \
&& install -m 0755 $(CURDIR)/claude/hooks/*.sh /usr/local/share/sinteractive/claude/hooks/ \
&& install -m 0644 $(CURDIR)/claude/settings-snippet.json /usr/local/share/sinteractive/claude/ \
&& install -m 0644 $(CURDIR)/skills/bodhi-compute/SKILL.md /usr/local/share/sinteractive/skills/bodhi-compute/ \
&& for s in $(SKILLS); do install -D -m 0644 $(CURDIR)/$$s /usr/local/share/sinteractive/$$s || exit 1; done \
&& echo ok'; \
else \
for n in $(NODES); do \
printf '==> %s: ' "$$n"; \
tar cf - sinteractive man/sinteractive.1 completions/sinteractive.bash \
claude/hooks claude/settings-snippet.json skills/bodhi-compute/SKILL.md \
claude/hooks claude/settings-snippet.json $(SKILLS) \
| ssh $(SSH_USER)@$$n \
'set -e; d=$$(mktemp -d); trap "rm -rf $$d" EXIT; tar xf - -C "$$d"; \
install -m 0755 "$$d/sinteractive" /usr/local/bin/sinteractive.new; \
mv /usr/local/bin/sinteractive.new /usr/local/bin/sinteractive; \
install -D -m 0644 "$$d/man/sinteractive.1" /usr/local/share/man/man1/sinteractive.1; \
install -D -m 0644 "$$d/completions/sinteractive.bash" /usr/local/share/bash-completion/completions/sinteractive; \
install -d -m 0755 /usr/local/share/sinteractive/claude/hooks /usr/local/share/sinteractive/skills/bodhi-compute; \
install -d -m 0755 /usr/local/share/sinteractive/claude/hooks; \
install -m 0755 "$$d"/claude/hooks/*.sh /usr/local/share/sinteractive/claude/hooks/; \
install -m 0644 "$$d/claude/settings-snippet.json" /usr/local/share/sinteractive/claude/; \
install -m 0644 "$$d/skills/bodhi-compute/SKILL.md" /usr/local/share/sinteractive/skills/bodhi-compute/; \
cd "$$d" && for s in skills/*/SKILL.md; do install -D -m 0644 "$$s" "/usr/local/share/sinteractive/$$s"; done; \
echo ok' \
|| echo "FAILED"; \
done; \
Expand All @@ -211,7 +220,7 @@ nodes-check:
v=$$(sed -n "s/^VERSION=.\(.*\)./\1/p" /usr/local/bin/sinteractive 2>/dev/null | head -1); \
[ -e /usr/local/bin/sinteractive ] || v=missing; \
[ -n "$$v" ] || v=unknown; \
a=no; [ -d /usr/local/share/sinteractive/skills/bodhi-compute ] && a=yes; \
a=no; [ -d /usr/local/share/sinteractive/skills ] && a=yes; \
t=$$(/usr/local/bin/tmux -V 2>/dev/null) || t="tmux missing"; \
echo "sinteractive=$$v assets=$$a $$t"' 2>/dev/null \
|| echo "unreachable"; \
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ sinteractive [OPTIONS] [SBATCH_ARGS...]
| `--ensure NAME` | Reuse the session named NAME, or launch it if absent (implies `--detach`) | |
| `--cancel TARGET` | Cancel a session by JOBID or NAME | |
| `--agent-context` | Brief a coding agent on the session it is running inside | |
| `--install-claude` | Install the Claude Code skill and hooks, and register them | |
| `--install-claude` | Install the Claude Code skills and hooks, and register them | |
| `-l`, `--list` | List running sinteractive sessions | |
| `-h`, `--help` | Show help message | |

Expand Down Expand Up @@ -279,10 +279,12 @@ and new panes, but shells already running keep their original
`SINTERACTIVE_NAME`.

> [!TIP]
> This repo ships a [Claude Code skill](https://code.claude.com/docs/en/skills)
> plus two hooks, for agents that run **inside** a session. The skill teaches
> cluster etiquette; the hooks brief the agent on which session it is in at
> startup, and warn it when the session is running out of wall time.
> This repo ships two [Claude Code skills](https://code.claude.com/docs/en/skills)
> plus two hooks, for agents that run **inside** a session. `bodhi-compute`
> teaches cluster etiquette; `git-workflow` teaches the git conventions —
> semantic versioning, Conventional Commits, a worktree per branch, landing
> through a pull request. The hooks brief the agent on which session it is in
> at startup, and warn it when the session is running out of wall time.
>
> ```bash
> sinteractive --install-claude # from any installed copy
Expand Down
26 changes: 19 additions & 7 deletions docs/scripting.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,18 @@ and new panes, but shells already running keep their original

## Claude Code integration

Install the skill and hooks:
Install the skills and hooks:

```bash
sinteractive --install-claude # from any installed copy
make claude-install # equivalent, from a checkout
```

Both write `~/.claude/skills/bodhi-compute` and `~/.claude/hooks/`, then
register the hooks in `~/.claude/settings.json`. That file is yours and
Both write every skill under `~/.claude/skills/` and the hooks to
`~/.claude/hooks/`, then register the hooks in `~/.claude/settings.json`.
Skills are discovered from what ships beside the script rather than named in
the installer, so a new one arrives with an upgrade and needs no new flag.
That settings file is yours and
usually already has hooks in it, so the merge is done by `jq` and only by
`jq` — string surgery on it in bash could silently disable every setting in
the file. What the merge guarantees:
Expand Down Expand Up @@ -145,10 +148,19 @@ need them too, since running `--install-claude` from inside a session runs
the node's copy of the script. Point `SINTERACTIVE_SHARE` at a checkout to
override, and `make nodes-check` to see which nodes actually have them.

**The [skill](https://code.claude.com/docs/en/skills)** teaches agents cluster
etiquette: neither the login node nor an sinteractive session is a compute
target, real work goes into an allocation sized for it, reuse sessions rather
than piling them up, check the time budget before long jobs, and clean up.
**Two [skills](https://code.claude.com/docs/en/skills)** teach agents how work
is done here.

`bodhi-compute` covers cluster etiquette: neither the login node nor an
sinteractive session is a compute target, real work goes into an allocation
sized for it, reuse sessions rather than piling them up, check the time budget
before long jobs, and clean up.

`git-workflow` covers the git conventions, and is about the repository open in
the session rather than the cluster: semantic versioning with annotated
`vX.Y.Z` tags, Conventional Commit messages, one worktree per branch under
`.claude/worktrees/`, landing work through a pull request rather than
committing to `main`, and running the repo's own CI gates before pushing.

**`sinteractive --agent-context`** prints a briefing on the current session —
job, node, partition, allocation size, walltime remaining, and the rules
Expand Down
2 changes: 1 addition & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ sinteractive [OPTIONS] [SBATCH_ARGS...]
| `--ensure NAME` | Reuse the session named NAME, or launch it if absent (implies `--detach`) | |
| `--cancel TARGET` | Cancel a session by JOBID or NAME | |
| `--agent-context` | Brief a coding agent on the session it is running inside | |
| `--install-claude` | Install the Claude Code skill and hooks, and register them | |
| `--install-claude` | Install the Claude Code skills and hooks, and register them | |
| `-l`, `--list` | List running sinteractive sessions | |
| `-h`, `--help` | Show help message | |
| `-V`, `--version` | Show version | |
Expand Down
17 changes: 11 additions & 6 deletions man/sinteractive.1
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ hook (see
but useful by hand to see exactly what an agent was told.
.TP
.B \-\-install\-claude
Install the Claude Code skill and hooks into
Install the Claude Code skills and hooks into
.I ~/.claude
(or
.BR $CLAUDE_CONFIG_DIR ),
Expand Down Expand Up @@ -486,9 +486,14 @@ run from inside one create their own allocations rather than steps of the
session's job.
.SS Claude Code hooks
.B \-\-agent\-context
prints this briefing for the current session. A skill and two hooks wire it
up \(em one briefing the agent at session start, one warning it when the
session is running out of wall time \(em installed with
prints this briefing for the current session. Two skills and two hooks wire
it up \(em
.I bodhi\-compute
for cluster etiquette and
.I git\-workflow
for the git conventions, plus one hook briefing the agent at session start
and one warning it when the session is running out of wall time \(em
installed with
.B sinteractive \-\-install\-claude
from any installed copy, or
.B make claude\-install
Expand Down Expand Up @@ -619,8 +624,8 @@ status-bar countdown keeps running from the last known end time.
.I ~/.claude
Written by
.BR \-\-install\-claude :
the skill under
.IR skills/bodhi\-compute/ ,
the skills under
.IR skills/ ,
the hooks under
.IR hooks/ ,
and their registration merged into
Expand Down
32 changes: 24 additions & 8 deletions sinteractive
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Options:
--cancel TARGET Cancel a session by JOBID or NAME
--refresh [TARGET] Re-check the time budget now (after scontrol)
--agent-context Brief a coding agent on the session it is running in
--install-claude Install the Claude Code skill and hooks, and register them
--install-claude Install the Claude Code skills and hooks, and register them
-l, --list List your running sessions
-h, --help Show this help message
-V, --version Show version
Expand Down Expand Up @@ -1131,7 +1131,7 @@ function pane_border_format_string {
# with its own start. ASCII only: the slice below is bash substring
# expansion, which counts bytes under a C locale, and a multibyte character
# cut in half renders as garbage.
local msg='Claude Code: run sinteractive --install-claude to enable the skill and hooks | '
local msg='Claude Code: run sinteractive --install-claude to enable the skills and hooks | '
((offset %= ${#msg}))
local window="${msg}${msg}"
printf '%s' "#[align=centre]#[fg=yellow,bold] ${window:offset:HINT_WIDTH} #[default]"
Expand Down Expand Up @@ -1172,6 +1172,9 @@ function find_claude_assets {

local root
for root in "${candidates[@]}"; do
# bodhi-compute is the probe rather than skills/ itself: an empty skills/
# directory left behind by a half-finished install would otherwise pass,
# and this one has shipped since the assets existed.
if [[ -d "${root}/claude/hooks" && -d "${root}/skills/bodhi-compute" ]]; then
printf '%s' "$root"
return 0
Expand Down Expand Up @@ -1289,7 +1292,7 @@ function register_claude_hooks {
return 0
}

# Install the Claude Code skill and hooks into the user's ~/.claude, from
# Install the Claude Code skills and hooks into the user's ~/.claude, from
# wherever this copy of sinteractive keeps its assets. Skills and hooks are
# per-user, so this is the same work whoever runs it.
#
Expand All @@ -1312,17 +1315,30 @@ function install_claude {
fi

local claude_dir="${CLAUDE_CONFIG_DIR:-${HOME}/.claude}"
mkdir -p "${claude_dir}/skills/bodhi-compute" "${claude_dir}/hooks" || exit 1

cp "${assets}/skills/bodhi-compute/SKILL.md" \
"${claude_dir}/skills/bodhi-compute/SKILL.md" || exit 1
mkdir -p "${claude_dir}/hooks" || exit 1

# Every skill shipped beside the script, rather than a list to keep in sync
# here: adding one is then a matter of dropping a directory into skills/.
local skill name names=()
for skill in "${assets}"/skills/*/SKILL.md; do
[[ -e "$skill" ]] || continue
name=$(basename "$(dirname "$skill")")
mkdir -p "${claude_dir}/skills/${name}" || exit 1
cp "$skill" "${claude_dir}/skills/${name}/SKILL.md" || exit 1
names+=("$name")
done
local hook
for hook in "${assets}"/claude/hooks/*.sh; do
[[ -e "$hook" ]] || continue
install -m 0755 "$hook" "${claude_dir}/hooks/" || exit 1
done

echo "Installed the bodhi-compute skill and hooks into ${claude_dir}"
# Name what landed rather than counting it: the point of the line is to say
# which skills this copy of sinteractive carries, which is what differs
# between a current install and a stale one.
local list=''
((${#names[@]})) && printf -v list ', %s' "${names[@]}"
echo "Installed the Claude Code hooks and skills (${list:2}) into ${claude_dir}"
echo " from ${assets}"
echo ''

Expand Down
Loading
Loading