Skip to content

Commit e2e5b7c

Browse files
authored
Update seed plugin to match post-cutback pattern (#8)
* Update seed plugin to match post-cutback pattern Drop agents/ (navigator duplicated skill, context-linker was inert). Make plugin.json metadata-only. Replace session-start.sh with lean liveness check. Add hooks.json, templatized post-commit-check.sh, and /doctor command. Add skills symlink step to seed-cli.md. * Address review feedback on seed plugin hooks - Add mkdir -p before skills symlink in seed-cli.md bootstrap step - Guard post-commit-check.sh against missing jq (exit 0 gracefully) - Append || true to grep -v to prevent errexit on single-line output * Add marketplace harness and setup claude to seed templates Seed templates now include: - internal/harness/claude.go.tmpl skeleton for plugin detection and marketplace install utilities - setup claude subcommand pattern in setup.go.tmpl - Doctor check 9 wired to harness.CheckClaudePlugin() - AGENTS.md.tmpl updated with harness/ in repo structure * Add 3A.6/3A.7 rubric criteria for marketplace plugin pattern - 3A.6: setup claude installs plugin via marketplace - 3A.7: Plugin registered in basecamp/claude-plugins marketplace - Fix 3A.3 wording (agents → commands) - Update scoring totals (T3: 11→13, total: 82→84) - Add audit checklists in rubric-audit SKILL.md - Add harness + marketplace steps to seed-cli.md - Add 3A.6/3A.7 implementation patterns to close-gap.md * Address Copilot review feedback on seed plugin hooks - Move jq check before auth status call in session-start.sh - Assign CLI name to quoted variable to prevent word-splitting - Escape commit message with printf %q in post-commit-check.sh
1 parent 3ec412a commit e2e5b7c

15 files changed

Lines changed: 308 additions & 156 deletions

File tree

RUBRIC.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,11 @@ Flag and subcommand removals are breaking changes caught by CI. Agents can depen
131131
|---|-----------|------|-----|-----------|
132132
| 3A.1 | `SKILL.md` embedded via `go:embed` | Yes || `skills/SKILL.md` |
133133
| 3A.2 | `<cli> skill` prints embedded skill | Yes || `internal/commands/skill.go` |
134-
| 3A.3 | `.claude-plugin/` with plugin.json, hooks, agents | Yes || `.claude-plugin/` |
134+
| 3A.3 | `.claude-plugin/` with plugin.json, hooks, commands | Yes || `.claude-plugin/` |
135135
| 3A.4 | SessionStart hook emits CLI context | Yes || `.claude-plugin/hooks/` |
136136
| 3A.5 | Skill synced to `basecamp/skills` on release | Yes || `scripts/sync-skills.sh` |
137+
| 3A.6 | `setup claude` installs plugin via marketplace | Yes || `internal/harness/claude.go`, `internal/commands/setup.go` |
138+
| 3A.7 | Plugin registered in `basecamp/claude-plugins` marketplace | No || `basecamp/claude-plugins` marketplace.json |
137139

138140
### 3B. Pagination
139141

@@ -219,9 +221,9 @@ For the **TUI tool profile**, score only the applicable tiers (1D, 4A, 4B, 4D) a
219221
|------|-------|-----|
220222
| T1: Agent Contract | /26 | 26 |
221223
| T2: Reliability | /16 | 16 |
222-
| T3: Agent Integration | /11 | 11 |
224+
| T3: Agent Integration | /13 | 13 |
223225
| T4: Distribution | /29 | 29 |
224-
| **Total** | **/82** | **82** |
226+
| **Total** | **/84** | **84** |
225227

226228
### Detailed Results
227229

@@ -274,6 +276,8 @@ For the **TUI tool profile**, score only the applicable tiers (1D, 4A, 4B, 4D) a
274276
| 3A.3 | `.claude-plugin/` | | | |
275277
| 3A.4 | SessionStart hook | | | |
276278
| 3A.5 | Skill synced on release | | | |
279+
| 3A.6 | `setup claude` | | | |
280+
| 3A.7 | Marketplace registration | | | |
277281
| 3B.1 | `--limit N` | | | |
278282
| 3B.2 | `--all` | | | |
279283
| 3B.3 | Truncation notice | | | |

prompts/close-gap.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,16 @@ Replace file-only credential storage with `credstore.NewStore()`. Set ServiceNam
4848

4949
### Adding surface stability (2A.2-3)
5050
Use the `surface` package to generate snapshots. Commit the baseline. Add the `surface-compat` GitHub Action to CI.
51+
52+
### Adding setup claude (3A.6)
53+
Create `internal/harness/claude.go` with `ClaudeMarketplaceSource` and `ClaudePluginName`
54+
constants, plus `DetectClaude`, `FindClaudeBinary`, `IsPluginNeeded`, and `CheckClaudePlugin`
55+
functions. Add a `setup claude` subcommand that runs marketplace add (best-effort) then
56+
plugin install, with verify-after-install. Wire into the main setup wizard and add
57+
breadcrumb suggestions via `harness.IsPluginNeeded()`.
58+
Reference: github.com/basecamp/basecamp-cli/internal/harness/claude.go and wizard.go.
59+
60+
### Marketplace registration (3A.7)
61+
Manual, external follow-up. Add a plugin entry to `basecamp/claude-plugins`
62+
marketplace.json with source pointing at `basecamp/<app>-cli`. This is a one-time
63+
step in the marketplace repo, not automatable from within the CLI repo.

prompts/seed-cli.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ You are creating a new Go CLI for a 37signals product using the seed templates.
1818
│ ├── auth/
1919
│ ├── commands/
2020
│ ├── config/
21+
│ ├── harness/
2122
│ └── output/
2223
├── e2e/
2324
├── skills/
@@ -83,9 +84,11 @@ You are creating a new Go CLI for a 37signals product using the seed templates.
8384
- `seed/internal/commands/doctor.go.tmpl``internal/commands/doctor.go`
8485
- `seed/internal/commands/setup.go.tmpl``internal/commands/setup.go`
8586
- `seed/internal/commands/skill.go.tmpl``internal/commands/skill.go`
87+
- `seed/internal/harness/claude.go.tmpl``internal/harness/claude.go` (fill in app name)
8688

8789
**Skills & plugin:**
8890
- `seed/.claude-plugin/``.claude-plugin/` (customize)
91+
- `mkdir -p .claude-plugin/skills && ln -s ../../skills/<app> .claude-plugin/skills/<app>` (create skills symlink)
8992
- `seed/skills/app/SKILL.md.tmpl``skills/<app>/SKILL.md` (customize)
9093
- `seed/skills/embed.go.tmpl``skills/embed.go`
9194

@@ -157,6 +160,12 @@ After the repo is pushed to GitHub:
157160
pip install pre-commit && pre-commit install --install-hooks
158161
```
159162

163+
5. **Claude plugin marketplace** — register in `basecamp/claude-plugins`:
164+
- Clone `basecamp/claude-plugins`
165+
- Add entry to `.claude-plugin/marketplace.json` plugins array:
166+
`{"name": "<app>", "description": "...", "source": {"source": "github", "repo": "basecamp/<app>-cli"}, "category": "productivity"}`
167+
- PR and merge
168+
160169
## Auth Model Configuration
161170

162171
### OAuth + PKCE

seed/.claude-plugin/agents/context-linker.md

Lines changed: 0 additions & 40 deletions
This file was deleted.

seed/.claude-plugin/agents/navigator.md

Lines changed: 0 additions & 48 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
name: {{.Name}}-doctor
3+
description: Check {{.Name}} plugin health — CLI, auth, API connectivity, project context.
4+
invocable: true
5+
---
6+
7+
# /{{.Name}}-doctor
8+
9+
Run the {{.Name}} CLI health check and report results.
10+
11+
```bash
12+
{{.Name}} doctor --json
13+
```
14+
15+
Interpret the output:
16+
- **pass**: Working correctly
17+
- **warn**: Non-critical issue (e.g., shell completion not installed)
18+
- **skip**: Check not run (e.g., unauthenticated or not applicable)
19+
- **fail**: Broken — needs attention
20+
21+
For any failures, follow the `hint` field in the check output. Common fixes:
22+
- Authentication failed → `{{.Name}} auth login`
23+
- API unreachable → check network / VPN
24+
- Plugin not installed → `{{.Name}} setup claude`
25+
26+
Report results concisely: list failures and warnings with their hints. If everything passes, say so.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"hooks": {
3+
"SessionStart": [
4+
{
5+
"hooks": [
6+
{
7+
"type": "command",
8+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh",
9+
"timeout": 5
10+
}
11+
]
12+
}
13+
],
14+
"PostToolUse": [
15+
{
16+
"matcher": "Bash",
17+
"hooks": [
18+
{
19+
"type": "command",
20+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/post-commit-check.sh",
21+
"timeout": 5
22+
}
23+
]
24+
}
25+
]
26+
}
27+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env bash
2+
# post-commit-check.sh - Check for {{.Name}} item references after git commits
3+
#
4+
# This hook runs after Bash tool use and checks if a git commit was made
5+
# that references a {{.Name}} item ({{.Upper}}-12345, item-12345, etc.)
6+
7+
set -euo pipefail
8+
9+
# Require jq for JSON parsing
10+
if ! command -v jq &>/dev/null; then
11+
exit 0
12+
fi
13+
14+
# Read tool input from stdin (JSON with tool_name, tool_input, tool_output)
15+
input=$(cat)
16+
17+
# Extract tool input (the bash command that was run)
18+
tool_input=$(echo "$input" | jq -r '.tool_input.command // empty' 2>/dev/null)
19+
20+
# Only process git commit commands
21+
if [[ ! "$tool_input" =~ ^git\ commit ]]; then
22+
exit 0
23+
fi
24+
25+
# Check if commit succeeded by looking for output patterns
26+
tool_output=$(echo "$input" | jq -r '.tool_output // empty' 2>/dev/null)
27+
28+
# Skip if commit failed — detect error indicators before checking for success.
29+
# Only match lines that look like git/hook errors, not commit subject lines
30+
# (e.g. "[branch abc1234] Fix failed login" should not trigger this guard).
31+
# We strip the "[branch hash] subject" success line before scanning for errors.
32+
filtered_output=$(echo "$tool_output" | grep -v '^\[.*[a-f0-9]\{7,\}\]' || true)
33+
if echo "$filtered_output" | grep -qiE '(^|[[:space:]])(error|fatal|aborted|rejected)[[:space:]:]|hook[[:space:]].*[[:space:]]failed|pre-commit[[:space:]].*[[:space:]]failed|^error:'; then
34+
exit 0
35+
fi
36+
37+
# Verify commit actually succeeded - look for commit hash pattern or "create mode"
38+
if [[ ! "$tool_output" =~ \[.*[a-f0-9]{7,}\] ]] && [[ ! "$tool_output" =~ "create mode" ]]; then
39+
exit 0
40+
fi
41+
42+
# Look for item references in the commit message or branch name
43+
branch=$(git branch --show-current 2>/dev/null || true)
44+
last_commit_msg=$(git log -1 --format=%s 2>/dev/null || true)
45+
46+
# Patterns: {{.Upper}}-12345, item-12345, {{.Name}}-12345
47+
todo_patterns='{{.Upper}}-[0-9]+|item-[0-9]+|{{.Name}}-[0-9]+'
48+
49+
found_in_branch=$(echo "$branch" | grep -oEi "$todo_patterns" | head -1 || true)
50+
found_in_msg=$(echo "$last_commit_msg" | grep -oEi "$todo_patterns" | head -1 || true)
51+
52+
if [[ -n "$found_in_branch" ]] || [[ -n "$found_in_msg" ]]; then
53+
ref="${found_in_msg:-$found_in_branch}"
54+
# Extract just the number
55+
item_id=$(echo "$ref" | grep -oE '[0-9]+')
56+
57+
short_sha=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
58+
comment="Commit ${short_sha}: ${last_commit_msg}"
59+
escaped_comment=$(printf '%q' "$comment")
60+
61+
cat << EOF
62+
<hook-output>
63+
Detected {{.Name}} item reference: $ref
64+
65+
To link this commit to {{.Name}}:
66+
{{.Name}} comment ${escaped_comment} --on $item_id
67+
68+
Or complete the item:
69+
{{.Name}} done $item_id
70+
</hook-output>
71+
EOF
72+
fi

0 commit comments

Comments
 (0)