chore: add Forgejo PR workflow
This commit is contained in:
parent
1ffdb937ce
commit
cbf70665f8
9 changed files with 498 additions and 0 deletions
54
.claude/hooks/log-request.sh
Executable file
54
.claude/hooks/log-request.sh
Executable file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Claude Code — UserPromptSubmit hook
|
||||||
|
#
|
||||||
|
# Logs each new user prompt to .claude/request-log.jsonl (git-tracked).
|
||||||
|
# Runs async so it never blocks Claude's response.
|
||||||
|
#
|
||||||
|
# Input (stdin): JSON with fields: session_id, cwd, prompt, is_continuation
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
INPUT=$(cat)
|
||||||
|
|
||||||
|
PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""')
|
||||||
|
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // ""')
|
||||||
|
CWD=$(echo "$INPUT" | jq -r '.cwd // "$PWD"')
|
||||||
|
IS_CONTINUATION=$(echo "$INPUT" | jq -r '.is_continuation // false')
|
||||||
|
|
||||||
|
LOG_FILE="${CWD}/.claude/request-log.jsonl"
|
||||||
|
|
||||||
|
# Skip: continuation turns, trivially short prompts, no log file dir
|
||||||
|
if [ "$IS_CONTINUATION" = "true" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${#PROMPT}" -lt 5 ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "${CWD}/.claude" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
REQ_ID="req_$(date +%Y%m%d_%H%M%S)_$(LC_ALL=C tr -dc 'a-z0-9' < /dev/urandom | head -c 5)"
|
||||||
|
BRANCH=$(git -C "$CWD" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
||||||
|
|
||||||
|
jq -n \
|
||||||
|
--arg id "$REQ_ID" \
|
||||||
|
--arg ts "$TIMESTAMP" \
|
||||||
|
--arg prompt "$PROMPT" \
|
||||||
|
--arg session "$SESSION_ID" \
|
||||||
|
--arg branch "$BRANCH" \
|
||||||
|
'{
|
||||||
|
id: $id,
|
||||||
|
timestamp: $ts,
|
||||||
|
prompt: $prompt,
|
||||||
|
session: $session,
|
||||||
|
branch: $branch,
|
||||||
|
pr_number: null,
|
||||||
|
pr_url: null,
|
||||||
|
status: "pending"
|
||||||
|
}' >> "$LOG_FILE"
|
||||||
|
|
||||||
|
exit 0
|
||||||
39
.claude/hooks/session-start.sh
Executable file
39
.claude/hooks/session-start.sh
Executable file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Claude Code — SessionStart hook
|
||||||
|
#
|
||||||
|
# Injects recent request history into Claude's context at session start.
|
||||||
|
# stdout is appended to Claude's context window.
|
||||||
|
#
|
||||||
|
# Input (stdin): JSON with fields: session_id, cwd, session_start_source
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
INPUT=$(cat)
|
||||||
|
CWD=$(echo "$INPUT" | jq -r '.cwd // "$PWD"')
|
||||||
|
|
||||||
|
LOG_FILE="${CWD}/.claude/request-log.jsonl"
|
||||||
|
|
||||||
|
if [ ! -f "$LOG_FILE" ] || [ ! -s "$LOG_FILE" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOTAL=$(wc -l < "$LOG_FILE" | tr -d ' ')
|
||||||
|
OPEN=$(jq -r 'select(.status == "open")' "$LOG_FILE" 2>/dev/null | jq -s 'length')
|
||||||
|
|
||||||
|
echo "=== Forgejo Workflow: Session Context ==="
|
||||||
|
echo "Request log: .claude/request-log.jsonl ($TOTAL total, $OPEN open PRs)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Last 5 entries
|
||||||
|
echo "Recent requests:"
|
||||||
|
tail -5 "$LOG_FILE" | jq -r '
|
||||||
|
" [\(.timestamp[0:16])] \(.prompt | gsub("\n"; " ") | .[0:72])" +
|
||||||
|
"\n branch: \(.branch)" +
|
||||||
|
(if .pr_number then " | PR #\(.pr_number) (\(.status))" else " | no PR yet" end)
|
||||||
|
' 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Run '.claude/scripts/show-history.sh' for full history."
|
||||||
|
echo "========================================="
|
||||||
|
|
||||||
|
exit 0
|
||||||
0
.claude/request-log.jsonl
Normal file
0
.claude/request-log.jsonl
Normal file
100
.claude/scripts/create-pr.sh
Executable file
100
.claude/scripts/create-pr.sh
Executable file
|
|
@ -0,0 +1,100 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Create a PR on Forgejo via tea and record it in the request log.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# .claude/scripts/create-pr.sh <title> [body] [base]
|
||||||
|
#
|
||||||
|
# Environment:
|
||||||
|
# FORGEJO_BASE_BRANCH — override default base branch (default: main)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TITLE="${1:-}"
|
||||||
|
BODY="${2:-}"
|
||||||
|
BASE="${3:-${FORGEJO_BASE_BRANCH:-main}}"
|
||||||
|
LOG_FILE=".claude/request-log.jsonl"
|
||||||
|
|
||||||
|
if [ -z "$TITLE" ]; then
|
||||||
|
echo "Usage: create-pr.sh <title> [body] [base-branch]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v tea &>/dev/null; then
|
||||||
|
cat >&2 <<'MSG'
|
||||||
|
Error: 'tea' CLI not found.
|
||||||
|
|
||||||
|
Install options:
|
||||||
|
nix develop path:~/.claude/workflows/forgejo # recommended
|
||||||
|
brew install gitea/tap/tea # macOS
|
||||||
|
go install gitea.com/gitea/tea@latest # Go
|
||||||
|
|
||||||
|
Then configure: tea login add
|
||||||
|
MSG
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
|
||||||
|
if [ "$CURRENT_BRANCH" = "$BASE" ]; then
|
||||||
|
echo "Error: currently on base branch '$BASE'. Create a feature branch first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Creating PR: '$TITLE'"
|
||||||
|
echo " head: $CURRENT_BRANCH → base: $BASE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Build tea args
|
||||||
|
TEA_ARGS=(pr create
|
||||||
|
--title "$TITLE"
|
||||||
|
--head "$CURRENT_BRANCH"
|
||||||
|
--base "$BASE"
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ -n "$BODY" ]; then
|
||||||
|
TEA_ARGS+=(--description "$BODY")
|
||||||
|
fi
|
||||||
|
|
||||||
|
PR_OUTPUT=$(tea "${TEA_ARGS[@]}" 2>&1)
|
||||||
|
echo "$PR_OUTPUT"
|
||||||
|
|
||||||
|
# Extract URL — tea prints something like: "Created pull request #42"
|
||||||
|
# and/or a URL line
|
||||||
|
PR_URL=$(echo "$PR_OUTPUT" | grep -oE 'https?://[^[:space:]]+/pulls/[0-9]+' | head -1 || true)
|
||||||
|
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$' || true)
|
||||||
|
|
||||||
|
if [ -z "$PR_NUMBER" ]; then
|
||||||
|
# Fallback: parse "Created pull request #N"
|
||||||
|
PR_NUMBER=$(echo "$PR_OUTPUT" | grep -oE '#[0-9]+' | grep -oE '[0-9]+' | head -1 || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update most recent pending entry in the log
|
||||||
|
if [ -n "$PR_NUMBER" ] && [ -f "$LOG_FILE" ] && [ -s "$LOG_FILE" ]; then
|
||||||
|
TMPFILE=$(mktemp)
|
||||||
|
|
||||||
|
# Find and update the last entry that matches current branch & is pending
|
||||||
|
awk -v branch="$CURRENT_BRANCH" -v pr_num="$PR_NUMBER" -v pr_url="$PR_URL" '
|
||||||
|
BEGIN { updated = 0 }
|
||||||
|
{
|
||||||
|
lines[NR] = $0
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
# Walk backwards to find last matching entry
|
||||||
|
for (i = NR; i >= 1; i--) {
|
||||||
|
if (!updated && lines[i] ~ ("\"branch\":\"" branch "\"") && lines[i] ~ "\"status\":\"pending\"") {
|
||||||
|
# Use a simple substitution — jq would be cleaner but we avoid a subshell per line
|
||||||
|
cmd = "echo " lines[i] " | jq --arg pn \"" pr_num "\" --arg pu \"" pr_url "\" \".pr_number = ($pn | tonumber) | .pr_url = $pu | .status = \\\"open\\\"\""
|
||||||
|
cmd | getline updated_line
|
||||||
|
close(cmd)
|
||||||
|
lines[i] = updated_line
|
||||||
|
updated = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 1; i <= NR; i++) print lines[i]
|
||||||
|
}
|
||||||
|
' "$LOG_FILE" > "$TMPFILE"
|
||||||
|
|
||||||
|
mv "$TMPFILE" "$LOG_FILE"
|
||||||
|
echo ""
|
||||||
|
echo "Request log updated with PR #$PR_NUMBER"
|
||||||
|
fi
|
||||||
84
.claude/scripts/show-history.sh
Executable file
84
.claude/scripts/show-history.sh
Executable file
|
|
@ -0,0 +1,84 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Display the Claude request history for this project.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# .claude/scripts/show-history.sh [--filter open|merged|pending] [--last N] [--json]
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
LOG_FILE=".claude/request-log.jsonl"
|
||||||
|
FILTER=""
|
||||||
|
LAST=0
|
||||||
|
JSON_MODE=false
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--filter) FILTER="$2"; shift 2 ;;
|
||||||
|
--last) LAST="$2"; shift 2 ;;
|
||||||
|
--json) JSON_MODE=true; shift ;;
|
||||||
|
-*) echo "Unknown flag: $1" >&2; exit 1 ;;
|
||||||
|
*) LOG_FILE="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -f "$LOG_FILE" ]; then
|
||||||
|
echo "No request log found at $LOG_FILE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq &>/dev/null; then
|
||||||
|
echo "Error: jq not found. Run: nix develop path:~/.claude/workflows/forgejo" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOTAL=$(wc -l < "$LOG_FILE" | tr -d ' ')
|
||||||
|
|
||||||
|
if [ "$TOTAL" -eq 0 ]; then
|
||||||
|
echo "No requests logged yet."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build jq filter
|
||||||
|
JQ_SELECT='.'
|
||||||
|
if [ -n "$FILTER" ]; then
|
||||||
|
JQ_SELECT="select(.status == \"$FILTER\")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
CONTENT=$(jq -r "$JQ_SELECT" "$LOG_FILE" 2>/dev/null | jq -s '.')
|
||||||
|
|
||||||
|
if [ "$LAST" -gt 0 ]; then
|
||||||
|
CONTENT=$(echo "$CONTENT" | jq ".[-${LAST}:]")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$JSON_MODE" = true ]; then
|
||||||
|
echo "$CONTENT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
COUNT=$(echo "$CONTENT" | jq 'length')
|
||||||
|
|
||||||
|
echo "━━━ Request History ($LOG_FILE) ━━━"
|
||||||
|
echo "Total: $TOTAL entries | Showing: $COUNT${FILTER:+ ($FILTER)}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "$CONTENT" | jq -r '.[] |
|
||||||
|
"[\(.timestamp[0:16])] \(.id)",
|
||||||
|
" Prompt: \(.prompt | gsub("\n";" ") | .[0:80])\(if (.prompt | length) > 80 then "…" else "" end)",
|
||||||
|
" Branch: \(.branch)",
|
||||||
|
(if .pr_number then
|
||||||
|
" PR: #\(.pr_number) [\(.status)] \(.pr_url // "")"
|
||||||
|
else
|
||||||
|
" PR: none yet (status: \(.status))"
|
||||||
|
end),
|
||||||
|
""
|
||||||
|
'
|
||||||
|
|
||||||
|
# Summary stats
|
||||||
|
OPEN=$(jq -r 'select(.status == "open")' "$LOG_FILE" | jq -s 'length')
|
||||||
|
MERGED=$(jq -r 'select(.status == "merged")' "$LOG_FILE" | jq -s 'length')
|
||||||
|
PENDING=$(jq -r 'select(.status == "pending")' "$LOG_FILE" | jq -s 'length')
|
||||||
|
|
||||||
|
echo "━━━ Summary ━━━"
|
||||||
|
echo " Open PRs: $OPEN"
|
||||||
|
echo " Merged: $MERGED"
|
||||||
|
echo " No PR yet: $PENDING"
|
||||||
66
.claude/scripts/update-entry.sh
Executable file
66
.claude/scripts/update-entry.sh
Executable file
|
|
@ -0,0 +1,66 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Update a specific log entry (e.g. mark a PR as merged).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# .claude/scripts/update-entry.sh <req-id> --status merged
|
||||||
|
# .claude/scripts/update-entry.sh <req-id> --pr-number 42 --pr-url https://...
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REQ_ID="${1:-}"
|
||||||
|
LOG_FILE=".claude/request-log.jsonl"
|
||||||
|
NEW_STATUS=""
|
||||||
|
NEW_PR_NUMBER=""
|
||||||
|
NEW_PR_URL=""
|
||||||
|
|
||||||
|
shift || true
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--status) NEW_STATUS="$2"; shift 2 ;;
|
||||||
|
--pr-number) NEW_PR_NUMBER="$2"; shift 2 ;;
|
||||||
|
--pr-url) NEW_PR_URL="$2"; shift 2 ;;
|
||||||
|
*) echo "Unknown arg: $1" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$REQ_ID" ]; then
|
||||||
|
echo "Usage: update-entry.sh <req-id> [--status STATUS] [--pr-number N] [--pr-url URL]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$LOG_FILE" ]; then
|
||||||
|
echo "Log file not found: $LOG_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TMPFILE=$(mktemp)
|
||||||
|
|
||||||
|
JQ_ARGS=()
|
||||||
|
JQ_FILTER='.'
|
||||||
|
|
||||||
|
if [ -n "$NEW_STATUS" ]; then
|
||||||
|
JQ_ARGS+=(--arg status "$NEW_STATUS")
|
||||||
|
JQ_FILTER="$JQ_FILTER | .status = \$status"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$NEW_PR_NUMBER" ]; then
|
||||||
|
JQ_ARGS+=(--argjson pr_num "$NEW_PR_NUMBER")
|
||||||
|
JQ_FILTER="$JQ_FILTER | .pr_number = \$pr_num"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$NEW_PR_URL" ]; then
|
||||||
|
JQ_ARGS+=(--arg pr_url "$NEW_PR_URL")
|
||||||
|
JQ_FILTER="$JQ_FILTER | .pr_url = \$pr_url"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r line; do
|
||||||
|
ID=$(echo "$line" | jq -r '.id // ""')
|
||||||
|
if [ "$ID" = "$REQ_ID" ]; then
|
||||||
|
echo "$line" | jq "${JQ_ARGS[@]}" "$JQ_FILTER"
|
||||||
|
else
|
||||||
|
echo "$line"
|
||||||
|
fi
|
||||||
|
done < "$LOG_FILE" > "$TMPFILE"
|
||||||
|
|
||||||
|
mv "$TMPFILE" "$LOG_FILE"
|
||||||
|
echo "Updated entry $REQ_ID"
|
||||||
29
.claude/settings.json
Normal file
29
.claude/settings.json
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"UserPromptSubmit": [
|
||||||
|
{
|
||||||
|
"matcher": "",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/log-request.sh",
|
||||||
|
"timeout": 10,
|
||||||
|
"async": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"SessionStart": [
|
||||||
|
{
|
||||||
|
"matcher": "",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-start.sh",
|
||||||
|
"timeout": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -9,3 +9,9 @@ ios/
|
||||||
*.local
|
*.local
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.claude/worktrees/
|
.claude/worktrees/
|
||||||
|
e2e/test-results/
|
||||||
|
e2e/report/
|
||||||
|
e2e/.features-gen/
|
||||||
|
|
||||||
|
# Claude Code local settings (secrets, personal config)
|
||||||
|
.claude/settings.local.json
|
||||||
|
|
|
||||||
120
CLAUDE.md
120
CLAUDE.md
|
|
@ -798,3 +798,123 @@ function getItemsInContainer(containerId: string): Item[] {
|
||||||
return allItems.filter(i => i.storageContainerId === containerId);
|
return allItems.filter(i => i.storageContainerId === containerId);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Forgejo PR Workflow
|
||||||
|
|
||||||
|
Every substantive session is tracked in `.claude/request-log.jsonl` and backed by a Forgejo PR.
|
||||||
|
|
||||||
|
### When to create a branch + PR
|
||||||
|
|
||||||
|
Create a branch and PR for:
|
||||||
|
- New features (`feat/`)
|
||||||
|
- Bug fixes (`fix/`)
|
||||||
|
- Refactors (`refactor/`)
|
||||||
|
- Dependency or config changes (`chore/`)
|
||||||
|
|
||||||
|
Skip branching/PR for:
|
||||||
|
- Questions, explanations, code reading
|
||||||
|
- Trivial one-liner fixes that are best committed directly to the current branch
|
||||||
|
|
||||||
|
### Branch naming
|
||||||
|
|
||||||
|
```
|
||||||
|
feat/YYYYMMDD-short-kebab-description
|
||||||
|
fix/YYYYMMDD-short-kebab-description
|
||||||
|
chore/YYYYMMDD-short-kebab-description
|
||||||
|
refactor/YYYYMMDD-short-kebab-description
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the current branch first:
|
||||||
|
```bash
|
||||||
|
git rev-parse --abbrev-ref HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
If already on a feature branch from this session, continue on it. Only create a new branch for a new distinct task.
|
||||||
|
|
||||||
|
### Workflow for each task
|
||||||
|
|
||||||
|
1. **Create branch** (if needed):
|
||||||
|
```bash
|
||||||
|
git checkout -b feat/$(date +%Y%m%d)-short-description
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Do the work** — write code, run tests, iterate.
|
||||||
|
|
||||||
|
3. **Commit** with conventional commit messages:
|
||||||
|
```bash
|
||||||
|
git add <files>
|
||||||
|
git commit -m "feat: short description"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Push**:
|
||||||
|
```bash
|
||||||
|
git push -u origin HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Create PR**:
|
||||||
|
```bash
|
||||||
|
bash .claude/scripts/create-pr.sh \
|
||||||
|
"feat: short description" \
|
||||||
|
"$(cat <<'EOF'
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- What was done and why
|
||||||
|
|
||||||
|
## Test plan
|
||||||
|
|
||||||
|
- [ ] Manual test steps
|
||||||
|
|
||||||
|
🤖 Generated with Claude Code
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
```
|
||||||
|
|
||||||
|
This also updates `.claude/request-log.jsonl` with the PR number.
|
||||||
|
|
||||||
|
6. **Commit the updated request log** (as part of the PR or separately):
|
||||||
|
```bash
|
||||||
|
git add .claude/request-log.jsonl
|
||||||
|
git commit -m "chore: log request #<req-id>"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Show history
|
||||||
|
|
||||||
|
When the user asks for history, recent work, or what's been done:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash .claude/scripts/show-history.sh
|
||||||
|
bash .claude/scripts/show-history.sh --filter open
|
||||||
|
bash .claude/scripts/show-history.sh --last 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### tea CLI quick reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tea pr list # list open PRs
|
||||||
|
tea pr view <number> # view a PR
|
||||||
|
tea pr merge <number> # merge a PR
|
||||||
|
tea issue create # create an issue
|
||||||
|
tea repo info # show repo details
|
||||||
|
tea login list # show configured Forgejo instances
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request log format
|
||||||
|
|
||||||
|
`.claude/request-log.jsonl` — one JSON object per line, git-tracked:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "req_20260226_143012_a3f9b",
|
||||||
|
"timestamp": "2026-02-26T14:30:12Z",
|
||||||
|
"prompt": "Add dark mode support",
|
||||||
|
"session": "abc123",
|
||||||
|
"branch": "feat/20260226-dark-mode",
|
||||||
|
"pr_number": 42,
|
||||||
|
"pr_url": "https://forge.example.com/user/repo/pulls/42",
|
||||||
|
"status": "open"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Possible statuses: `pending` (no PR yet), `open`, `merged`, `closed`.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue