Skip to content
Open
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
15 changes: 15 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"hooks": {
"SessionEnd": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "bash agentsview/scripts/export-session.sh"
}
]
}
]
}
}
4 changes: 4 additions & 0 deletions .claude/skills/fullsend/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ description: |
Also use when asked to browse, view, or search fullsend runs in AgentsView,
download run transcripts, start or stop the AgentsView viewer, or check
agent run history.
Also use when asked to share, export, push, or pull Claude Code sessions,
configure session sharing, or view shared team sessions.
Also use when asked to upgrade fullsend, sync scaffold files with a new
version, update the CLI, bump fullsend, or check what version we're on.
---
Expand Down Expand Up @@ -100,6 +102,7 @@ To add a variable, create an env file and wire it via `host_files` in the harnes
| `comment <#issue> <message> [--repo]` | Post a comment on an issue or PR |
| `label <#issue> <add\|remove> <label> [--repo]` | Add or remove a label on an issue or PR |
| `runs [fetch\|up\|down]` | Browse fullsend runs in AgentsView — fetch transcripts, start/stop viewer |
| `sessions [push\|pull\|view]` | Share team Claude Code sessions via git repo — export, sync, browse |
| `upgrade [version]` | Upgrade CLI, scaffold files, and dispatch workflows to a new fullsend release |
| `help [topic]` | Onboarding companion — agent pipeline, local deployment overview, upstream docs |
| `custom-agents` | Guide for building custom standalone agents (scaffold, dispatch, security) |
Expand All @@ -124,6 +127,7 @@ Parse the first word after `/fullsend` as the subcommand.
| `comment` | `references/comment.md` |
| `label` | `references/label.md` |
| `runs` | `references/runs.md` |
| `sessions` | `references/sessions.md` |
| `upgrade` | `references/upgrade.md` |
| `help` | `references/help.md` |
| `custom-agents` | `references/custom-agents.md` |
Expand Down
146 changes: 146 additions & 0 deletions .claude/skills/fullsend/references/sessions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# sessions

Share and browse team Claude Code session transcripts via a shared git repo.

## Overview

Exports regular Claude Code sessions (not just fullsend runs) to a shared git repo, making them viewable in AgentsView alongside fullsend agent runs. Sessions are auto-exported on session end via a `SessionEnd` hook, committed locally, and pushed on demand.

## Prerequisites

- `~/.config/fullsend/sessions.env` exists with `FULLSEND_SESSIONS_REPO` set to a valid git repo path
- `jq` installed
- The sessions repo must be initialized (`git init`) before first use

## Usage

```
/fullsend sessions # status — config check, session count, last sync
/fullsend sessions push # push local commits to remote
/fullsend sessions pull # pull team sessions from remote
/fullsend sessions view # start AgentsView with shared sessions
```

## Setup

If sessions are not yet configured, guide the user through setup:

1. Create or clone the shared sessions repo:
```bash
# New repo
mkdir -p ~/src/team-sessions && cd ~/src/team-sessions && git init

# Or clone existing
git clone git@github.com:org/team-sessions.git ~/src/team-sessions
```

2. Create the config file:
```bash
mkdir -p ~/.config/fullsend
echo 'FULLSEND_SESSIONS_REPO=/Users/me/src/team-sessions' > ~/.config/fullsend/sessions.env
chmod 600 ~/.config/fullsend/sessions.env
```

3. Verify the hook is active — `.claude/settings.json` in the project repo should have the `SessionEnd` hook configured. This is already committed in rhdh-fullsend.

## Procedure

### Status check (no subcommand)

Check and report:

1. **Config**: Does `~/.config/fullsend/sessions.env` exist? Is `FULLSEND_SESSIONS_REPO` set and pointing to a valid directory?
```bash
if [ -f ~/.config/fullsend/sessions.env ]; then
. ~/.config/fullsend/sessions.env
echo "Sessions repo: ${FULLSEND_SESSIONS_REPO:-not set}"
[ -d "${FULLSEND_SESSIONS_REPO:-}" ] && echo "Status: exists" || echo "Status: directory not found"
else
echo "Not configured — run /fullsend sessions for setup instructions"
fi
```

2. **Session count**: How many `.jsonl` files are in the sessions repo?
```bash
find "$FULLSEND_SESSIONS_REPO" -name '*.jsonl' | wc -l
```

3. **Project breakdown**: Which projects have sessions?
```bash
ls -d "$FULLSEND_SESSIONS_REPO"/*/ 2>/dev/null | xargs -I{} sh -c 'echo "$(basename {}): $(find {} -name "*.jsonl" | wc -l) sessions"'
```

4. **Git status**: Any unpushed commits?
```bash
git -C "$FULLSEND_SESSIONS_REPO" log --oneline '@{upstream}..HEAD' 2>/dev/null | wc -l
```

If not configured, print the setup instructions from the Setup section above.

### push

Push local session commits to the remote:

```bash
. ~/.config/fullsend/sessions.env
git -C "$FULLSEND_SESSIONS_REPO" push
```

Report how many commits were pushed. If no remote is configured, tell the user to add one:
```bash
git -C "$FULLSEND_SESSIONS_REPO" remote add origin <url>
```

### pull

Pull team sessions from the remote:

```bash
. ~/.config/fullsend/sessions.env
git -C "$FULLSEND_SESSIONS_REPO" pull --rebase
```

Report new sessions received (count of new `.jsonl` files).

### view

Start AgentsView with the sessions repo as the data source:

```bash
cd agentsview && make sessions
```

This sets `AGENTSVIEW_RUNS` to the sessions repo path and starts the container.

## Architecture

```
Claude Code session ends
▼ SessionEnd hook (.claude/settings.json)
▼ export-session.sh (reads stdin JSON)
├── Sources config from ~/.config/fullsend/sessions.env
├── Copies transcript with metadata header
└── git add + git commit (local only)
<sessions-repo>/<user>_<project>/<session-id>.jsonl
├── /fullsend sessions push → git push
├── /fullsend sessions pull → git pull --rebase
└── /fullsend sessions view → make sessions → AgentsView
```

Session directory layout:
```
team-sessions/
marcel-hild_rhdh-fullsend/
abc-123-def.jsonl
xyz-789-ghi.jsonl
marcel-hild_rhdh-plugins/
...
teammate_their-project/
...
```
9 changes: 8 additions & 1 deletion agentsview/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

COMPOSE := $(if $(shell command -v podman 2>/dev/null),podman compose,docker compose)

.PHONY: fetch up down local viewer help
.PHONY: fetch up down local sessions viewer help

define start-viewer
$(COMPOSE) -f docker-compose.fullsend.yaml up -d
Expand All @@ -24,6 +24,12 @@ local:
./scripts/import-local-run.sh $(if $(DIR),"$(DIR)",)
AGENTSVIEW_RUNS=./runs-local $(start-viewer)

# Browse shared team sessions in AgentsView
sessions:
@SESSIONS_REPO=$$(. ~/.config/fullsend/sessions.env 2>/dev/null && echo $$FULLSEND_SESSIONS_REPO); \
if [ -z "$$SESSIONS_REPO" ]; then echo "error: FULLSEND_SESSIONS_REPO not set in ~/.config/fullsend/sessions.env" >&2; exit 1; fi; \
AGENTSVIEW_RUNS=$$SESSIONS_REPO $(start-viewer)

# Start AgentsView without fetching (use after manual imports)
viewer:
$(start-viewer)
Expand All @@ -38,6 +44,7 @@ help:
@echo " make fetch - Download fullsend runs from GitHub Actions"
@echo " make up - Fetch + start AgentsView (all remote runs)"
@echo " make local - Import local run(s) + start AgentsView (auto-discovers)"
@echo " make sessions - Browse shared team sessions in AgentsView"
@echo " make viewer - Start AgentsView without fetching"
@echo " make down - Stop AgentsView"
@echo ""
Expand Down
97 changes: 97 additions & 0 deletions agentsview/scripts/export-session.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
# Export a Claude Code session transcript to the shared sessions repo.
#
# Called by the SessionEnd hook — receives session metadata as JSON on stdin.
#
# Usage (hook):
# bash agentsview/scripts/export-session.sh # stdin: JSON with transcript_path, session_id, cwd
#
# Usage (manual test):
# echo '{"transcript_path":"/path/to.jsonl","session_id":"abc-123","cwd":"/Users/me/myproject"}' \
# | bash agentsview/scripts/export-session.sh
#
# Prerequisites: jq, git
#
# Configuration:
# ~/.config/fullsend/sessions.env must define FULLSEND_SESSIONS_REPO=/path/to/repo
#
# Directory layout produced (matches AgentsView Claude discovery):
# <sessions-repo>/<user>_<project>/<session-id>.jsonl

set -euo pipefail

CONFIG_FILE="${HOME}/.config/fullsend/sessions.env"

# --- Load config -----------------------------------------------------------

if [ ! -f "$CONFIG_FILE" ]; then
exit 0
fi

# shellcheck source=/dev/null
. "$CONFIG_FILE"

if [ -z "${FULLSEND_SESSIONS_REPO:-}" ]; then
exit 0
fi

if [ ! -d "$FULLSEND_SESSIONS_REPO" ]; then
exit 0
fi

# --- Parse stdin JSON -------------------------------------------------------

INPUT="$(cat)"

TRANSCRIPT_PATH="$(echo "$INPUT" | jq -r '.transcript_path // empty')"
SESSION_ID="$(echo "$INPUT" | jq -r '.session_id // empty')"
SESSION_CWD="$(echo "$INPUT" | jq -r '.cwd // empty')"

if [ -z "$TRANSCRIPT_PATH" ] || [ -z "$SESSION_ID" ]; then
exit 0
fi

if [ ! -f "$TRANSCRIPT_PATH" ] || [ ! -s "$TRANSCRIPT_PATH" ]; then
exit 0
fi

# --- Derive names -----------------------------------------------------------

USERNAME="$(git config user.name 2>/dev/null | tr ' ' '-' | tr '[:upper:]' '[:lower:]' || echo "${USER:-unknown}")"
PROJECT="$(basename "${SESSION_CWD:-unknown}")"

PROJECT_DIR="${FULLSEND_SESSIONS_REPO}/${USERNAME}_${PROJECT}"
DEST_FILE="${PROJECT_DIR}/${SESSION_ID}.jsonl"

if [ -f "$DEST_FILE" ]; then
exit 0
fi

# --- Build metadata line ----------------------------------------------------

TIMESTAMP="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

META_LINE="$(jq -nc \
--arg project "$PROJECT" \
--arg user "$USERNAME" \
--arg ts "$TIMESTAMP" \
--arg cwd "/sessions/${USERNAME}_${PROJECT}" \
'{
type: "user",
timestamp: $ts,
message: { content: ("[Session: \($project)] by \($user)\nProject: \($cwd)") },
cwd: $cwd
}'
)"

# --- Copy transcript with metadata -----------------------------------------

mkdir -p "$PROJECT_DIR"

{ echo "$META_LINE"; cat "$TRANSCRIPT_PATH"; } > "$DEST_FILE"

# --- Commit in sessions repo ------------------------------------------------

git -C "$FULLSEND_SESSIONS_REPO" add "${USERNAME}_${PROJECT}/${SESSION_ID}.jsonl"
git -C "$FULLSEND_SESSIONS_REPO" commit -q -m "feat: add session ${USERNAME}/${PROJECT}/${SESSION_ID}" \
2>/dev/null || true