Keep Claude Code running on its own. Track sessions across projects. Publish artifacts automatically.
Two systems in one repo:
- Auto-Submit — automatically accepts Claude Code's suggested prompts when you're away
- Session Orchestrator — logs sessions, syncs to Google Sheets, publishes artifacts to Cloudflare R2
Claude Code stops and waits for you. A lot. Even when it already knows what to do next and shows it as ghost text (the grey prompt you accept with Tab).
This tool watches for those moments. When Claude finishes, it plays a sound. If you don't come back within 4 minutes, it presses Tab to accept the suggested prompt and keeps going.
- Claude Code fires a Stop hook every time it finishes a response
- The hook plays a sound alert (Glass.aiff)
- A 4-minute timer starts in the background
- If you haven't typed anything, the script presses Tab + Enter — accepting Claude's own suggested next step
- The timer resets every time Claude finishes a new response
02:42:55 [ttys000] Stop hook fired → timer started
02:57:55 [ttys000] Timer fired → Tab injected via TIOCSTI ✓
02:58:38 [ttys000] Claude responded → timer reset
03:13:38 [ttys000] Timer fired → Tab injected ✓
02:52:06 [ttys008] Stop hook fired → timer started (separate tab)
03:07:06 [ttys008] Timer fired → Tab injected ✓
Two tabs running independently for hours. Total human input: zero.
Every Claude Code session is logged, tracked, and synced to Google Sheets. Artifacts (files created, URLs referenced) are published to Cloudflare R2 for team access.
Claude Code Stop Hook
↓
session-logger.py (parses transcript, extracts artifacts)
↓
data/events.jsonl (local buffer)
↓
session-daemon.py (background, every 60s)
├── Update session status (active → idle → finished)
├── Upload artifact files to Cloudflare R2
├── Summarize sessions with LLM (optional)
├── Sync to Sessions tab (Session Orchestrator sheet)
└── Sync artifacts to Projects Dashboard
- Session ID, teams, status (active/idle/finished)
- Last seen timestamp
- User/assistant message previews
- Tools used per session
- Artifacts: files created via Write/Edit + URLs from Google Docs, GitHub, Sheets, etc.
- LLM summaries (optional, via OpenAI)
The logger extracts artifacts from Claude Code transcripts:
| Source | What's extracted |
|---|---|
Write / Edit tool calls |
input.file_path — files Claude created or modified |
| Tool results | File paths matching common extensions |
| Assistant/user text | URLs from Google Docs, GitHub, Sheets, Figma, etc. |
Local files are uploaded to Cloudflare R2 and get public URLs. URLs are synced to Google Sheets.
Two spreadsheets are updated:
| Sheet | What | Columns |
|---|---|---|
| Sessions (Session Orchestrator) | Per-session tracking | session_id, teams, status, last_seen, summaries, artifacts |
| Projects Dashboard | Per-project view | Artifacts column added, aggregated by team |
git clone https://github.com/kobzevvv/claude-auto-submit
cd claude-auto-submit
./install.shGrant Accessibility permission to your terminal:
System Settings → Privacy & Security → Accessibility → add Terminal.app (or iTerm2)
- macOS only (TIOCSTI + AppleScript for keystroke injection)
- Claude Code installed
- Terminal.app or iTerm2
gcloudCLI with Application Default Credentials configured- Google Sheets API access (for syncing)
wranglerCLI (for Cloudflare R2 artifact upload)- OpenAI API key (optional, for session summarization)
# Auto-submit
TIMEOUT_SECONDS=240 # Wait before pressing Tab (default: 4 min)
AUTO_SUBMIT=true # Set false for sound-only mode
# Session Orchestrator
SESSION_LOG_ENABLED=true
DAEMON_SYNC_INTERVAL=60 # Sheets sync every N seconds
SHEETS_SPREADSHEET_ID="..." # Session Orchestrator spreadsheet
R2_BUCKET="session-artifacts"
R2_PUBLIC_URL="https://pub-xxx.r2.dev"
# Optional
OPENAI_API_KEY="" # For LLM safety check + session summariesThis is the central configuration for how sessions map to teams and projects. Edit this file to add teams, repos, or change project-to-team associations.
{
"teams": {
"skillset": {
"description": "Skillset — AI recruiting SaaS platform",
"directories": ["skillset-ai", "skillset-landing-pages", "sklst-landing"]
},
"chillai": {
"description": "ChillAI — tools for AI developers",
"directories": ["vibe-sec", "claude-auto-submit", "aichill-content"]
}
},
"cross_team": {
"recruiter-ai-coach": ["skillset", "luda"]
},
"project_artifact_map": {
"Landing Pages": ["skillset"],
"Campaign Management": ["skillset"]
}
}How team resolution works:
- When a Claude Code session stops, the logger checks the working directory
- The directory name is matched against
teams[].directories(exact or prefix match) - If no match,
cross_teamentries are checked - Home directory (
~/) defaults topersonal - Unknown directories get
unknownteam
To add a new project:
- Add the repo directory name to the appropriate team's
directoriesarray - If it spans multiple teams, add to
cross_team - If it appears in the Projects Dashboard, add to
project_artifact_map
./scripts/start-daemon.shThe daemon runs in the background, reads data/events.jsonl, and syncs to Sheets every 60 seconds.
./scripts/stop-daemon.shScan all existing Claude Code transcripts and extract artifacts:
python3 scripts/backfill-artifacts.py --dry-run # Preview
python3 scripts/backfill-artifacts.py # ExecuteUse GPT-4o-mini to rank and filter artifacts per project in the Projects Dashboard:
python3 scripts/rank-artifacts.py --dry-run # Preview
python3 scripts/rank-artifacts.py # Executetail -f data/daemon.logBefore pressing Enter, the tool can run a sanity check using GPT-4o-mini.
Two categories are blocked:
- Requires user action — "paste your API token here", "confirm you've published"
- High-risk irreversible — "deploy to production", "push to main", "drop the database"
Everything else is submitted automatically.
echo 'OPENAI_API_KEY=sk-...' > .envWithout an API key, Tab+Enter fires with no classification.
config/
teams.json — team/project mapping (edit this!)
config.sh — runtime configuration
hooks/
stop.sh — Claude Code Stop hook (auto-submit)
session-log.sh — Claude Code Stop hook (session logging)
scripts/
timer.sh — background timer (one per session)
inject.py — Tab+Enter injection (TIOCSTI → AppleScript)
session-logger.py — parse transcript, extract artifacts, append to JSONL
session-daemon.py — background sync: events → Sheets + R2 upload
start-daemon.sh — launch daemon in background
stop-daemon.sh — stop daemon
backfill-artifacts.py — one-time scan of all transcripts for artifacts
rank-artifacts.py — LLM-powered artifact ranking per project
data/ — runtime data (gitignored)
events.jsonl — local event buffer
daemon.log — daemon output
uploaded.json — R2 upload tracker
state/ — auto-submit runtime state (gitignored)
Each terminal tab running Claude Code gets its own independent timer. Sessions don't interfere with each other.
./uninstall.shRemoves the Stop hook from ~/.claude/settings.json and kills running timers.