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
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"interface": {
"displayName": "Basecamp",
"shortDescription": "Work with Basecamp from Codex.",
"longDescription": "Find assignments, create and update Basecamp work, and diagnose the integration. Requires the Basecamp CLI (basecamp) installed and on your PATH.",
"longDescription": "Find assignments, create and update Basecamp work, diagnose the integration, and connect Git commits to Basecamp todos. Requires the Basecamp CLI (basecamp) installed and on your PATH.",
"developerName": "37signals",
"category": "Productivity",
"capabilities": [
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ basecamp-cli/
│ └── version/ # Version info
├── e2e/ # BATS integration tests
├── skills/ # Agent skills
├── hooks/ # Agent lifecycle hooks (both agents)
├── .claude-plugin/ # Claude Code integration
└── .codex-plugin/ # Codex plugin manifest
```
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ Both `BASECAMP_OAUTH_CLIENT_ID` and `BASECAMP_OAUTH_CLIENT_SECRET` must be set t

Both plugins require the `basecamp` CLI installed and on your PATH.

**Claude Code:** `basecamp setup claude` — installs the plugin with skills and agent workflow support.
**Claude Code:** `basecamp setup claude` — installs the plugin with skills, hooks, and agent workflow support.

**Codex:** `basecamp setup codex` — registers the 37signals marketplace and installs the native plugin with Basecamp skills and diagnostics. Start a new Codex thread afterward to load the skills.
**Codex:** `basecamp setup codex` — registers the 37signals marketplace and installs the native plugin with Basecamp skills, diagnostics, and opt-in hooks. In Codex, review and trust the plugin hooks with `/hooks`, then start a new thread to load the skills and hooks.

Manual Codex installation uses the same marketplace:

Expand Down
54 changes: 54 additions & 0 deletions hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"description": "Basecamp agent hooks: session context and commit-reference nudges.",
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume|clear|compact",
"hooks": [
{
"type": "command",
"command": "basecamp agent-hook session-start",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Don't merge hooks before the CLI command ships

If this lands while v0.7.2 is still the published CLI, this hook will fail for users who install the plugin from the marketplace and then trust hooks: the marketplace entry points at basecamp/basecamp-cli's default branch, but the latest v0.7.2 release (f087e6e, checked July 22, 2026) does not contain the hidden agent-hook command, which was added later in 340b502. Please either hold this until a CLI release that includes agent-hook is available, or wrap the hook command with a command/version-exists fallback so stable CLI installs stay silent instead of reporting hook failures.

Useful? React with 👍 / 👎.

"timeout": 5,
"statusMessage": "Checking Basecamp status"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "basecamp agent-hook pre-commit-snapshot",
"timeout": 5
}
Comment on lines +21 to +25
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "basecamp agent-hook post-commit",
"timeout": 5
}
Comment on lines +33 to +37
]
}
],
"PostToolUseFailure": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "basecamp agent-hook post-commit",
"timeout": 5
}
Comment on lines +45 to +49
]
}
]
}
}
4 changes: 2 additions & 2 deletions install.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@ Both agent plugins require the `basecamp` CLI installed above — the plugin inv
basecamp setup claude
```

This registers the marketplace and installs the plugin with skills and agent workflow support.
This registers the marketplace and installs the plugin with skills, hooks, and agent workflow support.

### Codex

```bash
basecamp setup codex
```

This installs the shared Basecamp skill, registers the 37signals Codex marketplace, and installs the native plugin. Start a new Codex thread after setup to load the skills.
This installs the shared Basecamp skill, registers the 37signals Codex marketplace, and installs the native plugin. After setup, review and trust the plugin hooks with `/hooks` (Codex lists untrusted hooks but does not run them until trusted), then start a new Codex thread to load the skills and hooks.

For a manual install:

Expand Down
5 changes: 4 additions & 1 deletion internal/commands/wizard_codex.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ func runCodexSetup(cmd *cobra.Command, styles *tui.Styles) error {
fmt.Fprintln(w, styles.RenderStatus(true, "37signals marketplace ready"))
fmt.Fprintln(w, styles.RenderStatus(true, "Codex plugin installed and enabled"))
fmt.Fprintln(w)
fmt.Fprintln(w, styles.Muted.Render(" Start a new Codex thread to load the Basecamp skills."))
// Trust must come first: an untrusted SessionStart hook is silently
// skipped, so a thread started before trusting gets no hook context.
fmt.Fprintln(w, styles.Muted.Render(" Review and trust the plugin hooks with /hooks."))
fmt.Fprintln(w, styles.Muted.Render(" Then start a new Codex thread to load the Basecamp skills."))
return nil
}

Expand Down
11 changes: 8 additions & 3 deletions internal/commands/wizard_codex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,14 @@ func TestRunCodexSetupInteractiveExplainsNextSteps(t *testing.T) {

assert.Contains(t, output.String(), "Registering 37signals marketplace")
assert.Contains(t, output.String(), "Installing basecamp plugin")
assert.Contains(t, output.String(), "Start a new Codex thread")
// No hooks ship yet — setup must not point users at /hooks trust.
assert.NotContains(t, output.String(), "/hooks")
// Codex lists untrusted hooks but does not run them until trusted, and
// an untrusted SessionStart is skipped — so the trust step must come
// before the new-thread step.
trustAt := strings.Index(output.String(), "trust the plugin hooks with /hooks")
newThreadAt := strings.Index(output.String(), "start a new Codex thread")
require.GreaterOrEqual(t, trustAt, 0)
require.GreaterOrEqual(t, newThreadAt, 0)
assert.Less(t, trustAt, newThreadAt, "trust guidance must precede the new-thread step")
}

func TestRunCodexSetupInteractiveFailureWarnsAndContinues(t *testing.T) {
Expand Down
Loading