Commit graph

10 commits

Author SHA1 Message Date
A
d584cc4d4e
fix: allow nested worktree paths in pre-merge hook regex (#2401) (#2411)
The worktree path regex in pre-merge-check.ts used [^\s/]+ which only
matched a single path segment after /tmp/spawn-worktrees/. This blocked
PR merges from nested worktrees like refactor/fix/issue-N used by the
automated refactoring service.

Fix both the TypeScript regex ([^\s/]+ -> [^\s]+) and the inline bash
grep pattern in settings.json ([a-zA-Z0-9._-]+ -> [a-zA-Z0-9._/-]+).

Closes #2401

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-09 23:02:30 -04:00
A
67443aa4b3
chore: add pre-merge hook to gate gh pr merge/ready on lint + tests (#2173)
Adds a PreToolUse hook for Bash that intercepts `gh pr merge` and
`gh pr ready` commands and runs `biome check src/` + `bun test` before
allowing them. Blocks the command if either check fails.

The hook finds the worktree from the command path or falls back to
git rev-parse --show-toplevel.

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 23:29:26 -08:00
A
446923c447
refactor: extract inline hook commands to TypeScript scripts (#2174)
* refactor: extract inline hook commands to TypeScript scripts in .claude/scripts/

Replace long inline `bash -c '...'` one-liners in .claude/settings.json with
standalone TypeScript scripts that are easier to read, debug, and maintain:

- enforce-worktree.ts: PreToolUse hook ensuring edits happen in worktrees
- validate-file.ts: PostToolUse hook for .sh/.ts file validation
- pre-merge-check.ts: PreToolUse hook running biome + tests before merge

Add .claude/scripts as a bun workspace package (@spawn/hooks).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: replace manual typeguards with valibot schemas in hook scripts

- Extract shared schemas (FilePathInput, CommandInput, parseStdin) to schemas.ts
- Replace inline multi-level typeof/in checks with v.safeParse() calls
- Add valibot dependency to @spawn/hooks package
- Add CLAUDE.md rule: always prefer valibot over manual typeguards, share schemas

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: split CLAUDE.md into modular .claude/rules/ files

Split the 437-line monolithic CLAUDE.md into a lean 89-line project overview
plus 9 focused rules files in .claude/rules/ (auto-loaded by Claude Code):

- culture.md — embrace bold changes, parallelize, verify exhaustively
- shell-scripts.md — curl|bash compat, macOS bash 3.x, ESM only, bun not python
- type-safety.md — no `as` assertions, ALWAYS use valibot (never manual typeguards)
- testing.md — bun:test only, no vitest, no subprocess spawning
- git-workflow.md — worktree-first mandatory workflow
- autonomous-loops.md — discovery/refactor service architecture
- discovery.md — how to fill matrix gaps, add clouds/agents
- documentation.md — never commit docs, use .docs/
- cli-version.md — bump version on every CLI change

The type-safety rule now explicitly mandates valibot schemas over manual
typeguard chains in all cases beyond single-primitive narrowing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(lint): run biome check across all packages in CI

The lint workflow only checked packages/cli/src/. Now it checks all
TypeScript locations in a single biome check command:

- packages/cli/src/ (with GritQL plugins)
- packages/shared/src/ (new biome.json)
- .claude/scripts/ (new biome.json)
- .claude/skills/setup-spa/

Fixed all pre-existing lint/format errors:
- node: protocol on all Node.js built-in imports in hook scripts
- useBlockStatements in packages/shared/src/type-guards.ts
- expand formatting in .claude/skills/setup-spa/main.ts and spa.test.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 23:05:41 -08:00
A
01b23042b8
style: enforce separated type imports via biome (#1870)
* refactor: clean up SPA code — extract helpers, use isString, DRY Slack calls

helpers.ts:
- Extract parseAssistantEvent/parseUserEvent/formatToolHint from parseStreamEvent
- Eliminate redundant second scans for toolName and isError (captured during loop)
- Use isString() from @openrouter/spawn-shared instead of typeof checks
- Split long node:fs import across multiple lines

main.ts:
- Extract postOrUpdate() to DRY the post-vs-update pattern (was 3 copy-paste blocks)
- Add SlackClient type alias (replaces 4x InstanceType<typeof App>["client"])
- Remove unused Mapping import
- Inline REQUIRED_VARS into for loop
- Rename currentMsgTs → msgTs
- Tighten formatting throughout

spa.test.ts:
- Remove unused beforeEach import

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add biome config to SPA, fix all lint/format, add PostToolUse hook

- Add biome.json extending lint/biome.json (2-space indent, 120 line width,
  useBlockStatements, expand:always, etc.)
- Fix all 36 useBlockStatements violations (braceless if/continue/return)
- Fix all format issues (line width, expand, trailing commas)
- Add biome lint+format to PostToolUse hook — runs automatically on any
  .ts file edit when a biome.json is found in the file's directory

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: enforce separated type imports via biome useImportType

Add `style: "separatedType"` to the base biome config's useImportType
rule. This enforces `import type { T }` on its own line instead of
mixing `type` into value imports (`import { type T, foo }`).

Auto-fixed 21 violations across CLI (18) and SPA (3).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-24 00:16:03 -08:00
A
6a5e0c5161
feat: SPA — Spawn's Personal Agent (#1825)
* feat: add Slack issue bot for #proj-spawn

Socket Mode bot that listens for @mentions in a configured Slack channel,
files GitHub issues via `gh` CLI, and syncs thread replies as issue comments.
State persisted to ~/.config/spawn/slack-issues.json.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: rewrite Spawnis to pipe threads into Claude Code sessions

- @mention triggers Claude Code with full thread as prompt
- Subsequent thread replies in tracked threads auto-trigger new runs
- System prompt focuses on GitHub issue management via `gh` CLI
- Streams Claude Code responses back to Slack in real-time
- Bot resolves own user ID at startup to skip self-messages
- Adds slack-manifest.yml for one-click Slack app creation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: lowercase display name to spawnis

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: rename to SPA — Spawn Processes Autonomously

Display name: spa

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: SPA — Spawn's Personal Agent

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: lowercase app name to Spa

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add biome config and fix lint/format to match CLI rules

Adds local biome.json mirroring cli/biome.json rules (minus GritQL
plugins). Fixes all useBlockStatements errors and applies expand:always
formatting to match the project style.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: share biome config via root biome.json + extends

Move shared linter rules, formatter, and JS formatter settings to a
root-level biome.json. Both cli/ and .claude/skills/slack-bot/ extend
from it — CLI adds its GritQL plugins and test overrides, slack-bot
just overrides includes and disables VCS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: move GritQL lint rules to repo root lint/

Move no-type-assertion.grit and no-typeof-string-number.grit from
cli/lint/ to lint/ at the repo root. Both cli/ and slack-bot share
the no-type-assertion rule; cli/ additionally uses no-typeof-string-number.

Plugin paths live in each child biome.json (not root) because biome
resolves plugin paths relative to the consumer config, not the extended
config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: move shared biome.json into lint/

All shared lint config now lives under lint/:
  lint/biome.json
  lint/no-type-assertion.grit
  lint/no-typeof-string-number.grit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add no-banner-comments lint rule, fix slack-bot

GritQL can't match comments (they're trivia in biome's CST), so this
is a Bun script at lint/no-banner-comments.ts that catches decorative
// --------- separator blocks and suggests /** Section */ or #region.

Replace all 9 banner blocks in slack-bot.ts with /** */ headers.

Usage: bun run lint/no-banner-comments.ts [files...]

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: use // #region instead of /** */ section headers

Switch slack-bot.ts to // #region / // #endregion for all section
markers (collapsible in most editors). Update no-banner-comments lint
script to recommend #region as the preferred style.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: replace lint script with PostToolUse hook for banner comments

Move banner comment detection into the existing PostToolUse hook on
Write|Edit in .claude/settings.json. Runs inline on every .ts file
edit — no separate bun script needed. Delete lint/no-banner-comments.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: simplify Slack manifest description

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: rename skill from slack-bot to setup-spa

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use named import for @slack/bolt App class

Bun resolves `import App from "@slack/bolt"` as the App constructor
directly, not a module with a `.default` property. Switch to named
import `{ App }` and remove all `.default` usage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add --verbose flag required by stream-json output format

Claude Code requires --verbose when using --output-format=stream-json
with --print. Also fix systemd PATH to include ~/.local/bin for claude.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: stream all Claude Code events to Slack (tools, results, text)

Replace text-only streaming with full event parsing:
- Tool use: shows 🛠️ *ToolName*
- Tool result: shows truncated output in code block
- Text delta: accumulates as before
- Errors: shows  prefix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: enforce issue title templates in system prompt

Add mandatory bracket prefix format matching the repo's issue templates:
[Bug]:, [CLI]:, [Agent]:, [Cloud]:, [Team]:. Also instructs Claude to
apply matching labels (bug + pending-review, cli + enhancement, etc.).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: reference issue templates at runtime instead of hardcoding

Tell Claude to read .github/ISSUE_TEMPLATE/ for the correct title
prefix, labels, and fields rather than hardcoding them in the prompt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-23 19:52:14 -08:00
A
430390bcf5
feat: enforce worktree-first workflow via PreToolUse hook (#1808)
Replace the "block on main" hook with a stricter worktree check:
edits are only allowed when the target file lives inside a git
worktree, not the main checkout. Also blocks edits on the main
branch even within a worktree. CLAUDE.md updated with the new
worktree-first workflow instructions.

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-23 08:03:48 -08:00
A
aba55b3895
docs: enforce PR workflow for ALL changes, no exceptions (#1793)
* docs: enforce PR workflow for ALL changes, no exceptions

Rewrites the "Draft PR First" section to be unambiguous:
- Every file edit (including CLAUDE.md itself) requires a PR
- Explicit list of change types that are NOT exempt
- Step-by-step workflow: branch → change → commit → draft PR → merge
- Finished PRs must be converted from draft and merged immediately

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* remove unnecessary "don't commit to main" warning

Branch protection already prevents direct commits — no need
to restate it as a rule.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add branch-check-first workflow and stash recovery

Agents must check their branch before editing files. If on main,
branch first. If they already have uncommitted changes on main,
stash → branch → unstash.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add PreToolUse hook to block edits on main branch

- Adds a PreToolUse hook that exits 2 (blocks) any Write/Edit when
  the current branch is main, with a clear error message telling the
  agent to create a branch first
- Updates CLAUDE.md to reference the hook and use cherry-pick
  (not stash) for recovering commits made on main by mistake

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-22 23:22:20 -08:00
Sprite
4ba682434a fix: Correct PostToolUse hook structure in settings.json
The hook configuration had two issues:
- Invalid "dangerouslySkipPermissions" setting (not supported)
- Wrong PostToolUse hook format (missing "hooks" array with "type" field)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 18:46:49 +00:00
L
b6ee6b6ab1
Add guardrails: CLAUDE.md rules, hooks, pre-commit validation (#33)
* feat: add gptme agent to spawn matrix

Add gptme (https://github.com/gptme/gptme) - a personal AI agent in the
terminal with tools for code editing, terminal commands, web browsing,
and more. Natively supports OpenRouter via OPENROUTER_API_KEY.

- Add gptme agent entry to manifest.json with OpenRouter env vars
- Implement sprite/gptme.sh deployment script
- Implement hetzner/gptme.sh deployment script
- Add "missing" matrix entries for remaining 8 clouds
- Update README.md with usage instructions for Sprite and Hetzner

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add Fly.io cloud provider with claude and aider agents

Add Fly.io as a new cloud provider using the Machines REST API for
provisioning and flyctl CLI for SSH access. Docker-based machines
with pay-per-second pricing.

- Create fly/lib/common.sh with Fly.io Machines API integration
- Implement fly/claude.sh for Claude Code deployment
- Implement fly/aider.sh for Aider deployment
- Update README.md with Fly.io usage instructions and env vars

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add gemini, amazonq, cline, gptme to Fly.io

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add openclaw, nanoclaw, goose, codex, interpreter to Fly.io

Implements 5 new agent scripts for the Fly.io cloud provider:
- fly/openclaw.sh: OpenClaw with gateway + TUI, model selection, config
- fly/nanoclaw.sh: NanoClaw WhatsApp agent with .env configuration
- fly/goose.sh: Block's Goose agent with OpenRouter provider
- fly/codex.sh: OpenAI Codex CLI with OpenRouter base URL override
- fly/interpreter.sh: Open Interpreter with OpenRouter base URL override

All scripts follow the Fly.io pattern (flyctl-based, no IP args for
run_server/interactive_session) and use upload_file for env injection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add gptme agent to 8 remaining clouds

Implement gptme agent scripts for digitalocean, vultr, linode, lambda,
aws-lightsail, gcp, e2b, and modal. Each script follows the exact
pattern of that cloud's existing aider.sh, adapted for gptme's install
and launch commands. Updates manifest.json matrix entries from "missing"
to "implemented".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add guardrails from insights: CLAUDE.md rules, hooks, pre-commit

Based on usage insights analysis:

CLAUDE.md:
- Shell script rules: curl|bash compat, macOS bash 3.x compat
- Autonomous loop rules: test after each iteration, never revert fixes
- Git workflow rules: always use feature branches

.claude/settings.json:
- PostToolUse hook validates .sh files on every Write/Edit:
  syntax check, no relative source, no echo -e, no set -u

.githooks/pre-commit:
- Blocks commits with: syntax errors, relative sources, echo -e,
  set -euo, references to deleted functions
- Install: git config core.hooksPath .githooks

README.md:
- Added developer setup section with hook installation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 20:02:19 -08:00
L
7940d43169
Implement Claude Code Agent Teams for continuous improvement (#20)
Rewrites improve.sh to use the experimental Agent Teams feature:
- Lead coordinates in delegate mode (never touches code)
- Teammates work in parallel: Gap Fillers, Agent Scouts, Cloud Scouts
- Shared task list for self-claiming work
- Plan approval required for cloud provider work (lib/common.sh)

CLAUDE.md updated with team role definitions and coordination rules.
.claude/settings.json enables CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS.

Usage:
  ./improve.sh              # one team cycle
  ./improve.sh --loop       # continuous team cycles
  ./improve.sh --single     # old single-agent mode (fallback)

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 13:54:27 -08:00