Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f70665b
feat(codex): add native plugin bundle
yigitkonur Jul 15, 2026
e9fb3f4
feat(codex): add setup and diagnostics
yigitkonur Jul 15, 2026
a9e19b5
chore(codex): validate plugin releases
yigitkonur Jul 15, 2026
2c28178
docs(codex): document plugin setup
yigitkonur Jul 15, 2026
c634319
fix(codex): harden plugin checks
yigitkonur Jul 15, 2026
16eef7c
fix(codex): harden hook follow-ups
yigitkonur Jul 15, 2026
96138d1
fix(codex): refresh existing marketplace
yigitkonur Jul 15, 2026
7539eb0
fix(codex): harden release validation
yigitkonur Jul 15, 2026
fc34b34
test(codex): isolate missing binary home
yigitkonur Jul 15, 2026
3c572af
fix(codex): parse shell commit commands
yigitkonur Jul 15, 2026
3984fb9
fix(codex): model shell execution status
yigitkonur Jul 15, 2026
c09c866
test(codex): make release checks portable
yigitkonur Jul 15, 2026
833e34c
fix(codex): parse hooks with bash AST
yigitkonur Jul 15, 2026
2df57a7
fix(codex): avoid duplicate checker errors
yigitkonur Jul 15, 2026
053a0cd
fix(codex): handle invalid checker encoding
yigitkonur Jul 15, 2026
3b5bf5a
fix(codex): keep version checks doctor-only
yigitkonur Jul 15, 2026
63c5feb
fix(codex): harden doctor diagnostics
yigitkonur Jul 15, 2026
7966aa8
refactor(codex): drop unproven hook stack and python checker
jeremy Jul 20, 2026
affdc84
feat(harness): give agents a doctor-only diagnostics hook
jeremy Jul 20, 2026
43ab709
feat(codex): humanize setup flow with structured remediation
jeremy Jul 20, 2026
cb13972
docs(codex): defer hooks language; make doctor skill agent-neutral
jeremy Jul 20, 2026
5e614cd
Merge remote-tracking branch 'origin/main' into yigitkonur/feat/nativ…
jeremy Jul 20, 2026
369e77b
fix(codex): actionable remediation without a codex binary
jeremy Jul 20, 2026
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
34 changes: 34 additions & 0 deletions .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "basecamp",
"version": "0.7.2",
"description": "Use Basecamp from Codex to find work, create todos, and keep projects up to date.",
"author": {
"name": "37signals",
"email": "support@37signals.com",
"url": "https://37signals.com"
},
"homepage": "https://basecamp.com/cli",
"repository": "https://github.com/basecamp/basecamp-cli",
"license": "MIT",
"skills": "./skills/",
"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.",
"developerName": "37signals",
"category": "Productivity",
"capabilities": [
"Interactive",
"Read",
"Write"
],
"websiteURL": "https://basecamp.com/cli",
"defaultPrompt": [
"Show my assigned Basecamp work and highlight anything overdue.",
"Create a Basecamp todo from this task and assign it to me.",
"Summarize recent activity in the current Basecamp project."
],
"composerIcon": "./assets/bc5-snowglobe.png",
"logo": "./assets/bc5-snowglobe.png"
}
}
2 changes: 1 addition & 1 deletion .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ project_name: basecamp
before:
hooks:
- sh -c 'mkdir -p completions && go run ./cmd/basecamp completion bash > completions/basecamp.bash && go run ./cmd/basecamp completion zsh > completions/_basecamp && go run ./cmd/basecamp completion fish > completions/basecamp.fish'
- sh -c '{{ if .Prerelease }}echo "Skipping Claude plugin version stamp for prerelease {{ .Version }}"{{ else }}scripts/stamp-plugin-version.sh {{ .Version }}{{ end }}'
- sh -c '{{ if .Prerelease }}echo "Skipping plugin version stamps for prerelease {{ .Version }}"{{ else }}scripts/stamp-plugin-version.sh {{ .Version }} && scripts/stamp-codex-plugin-version.sh {{ .Version }}{{ end }}'

builds:
- id: basecamp
Expand Down
23 changes: 23 additions & 0 deletions .surface
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,7 @@ CMD basecamp search metadata
CMD basecamp search types
CMD basecamp setup
CMD basecamp setup claude
CMD basecamp setup codex
CMD basecamp show
CMD basecamp skill
CMD basecamp skill install
Expand Down Expand Up @@ -11394,6 +11395,27 @@ FLAG basecamp setup claude --stats type=bool
FLAG basecamp setup claude --styled type=bool
FLAG basecamp setup claude --todolist type=string
FLAG basecamp setup claude --verbose type=count
FLAG basecamp setup codex --account type=string
FLAG basecamp setup codex --agent type=bool
FLAG basecamp setup codex --cache-dir type=string
FLAG basecamp setup codex --count type=bool
FLAG basecamp setup codex --help type=bool
FLAG basecamp setup codex --hints type=bool
FLAG basecamp setup codex --ids-only type=bool
FLAG basecamp setup codex --in type=string
FLAG basecamp setup codex --jq type=string
FLAG basecamp setup codex --json type=bool
FLAG basecamp setup codex --markdown type=bool
FLAG basecamp setup codex --md type=bool
FLAG basecamp setup codex --no-hints type=bool
FLAG basecamp setup codex --no-stats type=bool
FLAG basecamp setup codex --profile type=string
FLAG basecamp setup codex --project type=string
FLAG basecamp setup codex --quiet type=bool
FLAG basecamp setup codex --stats type=bool
FLAG basecamp setup codex --styled type=bool
FLAG basecamp setup codex --todolist type=string
FLAG basecamp setup codex --verbose type=count
FLAG basecamp show --account type=string
FLAG basecamp show --agent type=bool
FLAG basecamp show --all-comments type=bool
Expand Down Expand Up @@ -16394,6 +16416,7 @@ SUB basecamp search metadata
SUB basecamp search types
SUB basecamp setup
SUB basecamp setup claude
SUB basecamp setup codex
SUB basecamp show
SUB basecamp skill
SUB basecamp skill install
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ basecamp-cli/
│ └── version/ # Version info
├── e2e/ # BATS integration tests
├── skills/ # Agent skills
└── .claude-plugin/ # Claude Code integration
├── .claude-plugin/ # Claude Code integration
└── .codex-plugin/ # Codex plugin manifest
```

## Basecamp API Reference
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ check-surface-compat: build
.PHONY: check-skill-drift
check-skill-drift:
@scripts/check-skill-drift.sh
@scripts/check-skill-drift.sh skills/basecamp-doctor/SKILL.md

# Verify group commands show help bare (no RunE on parents with subcommands)
.PHONY: check-bare-groups
Expand Down
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- Works standalone or with any AI agent (Claude, Codex, Copilot, Gemini)
- JSON output with breadcrumbs for easy navigation
- OAuth authentication with automatic token refresh
- Includes agent skill and Claude plugin
- Includes agent skills plus native Claude Code and Codex plugins

## Quick Start

Expand Down Expand Up @@ -148,7 +148,21 @@ Both `BASECAMP_OAUTH_CLIENT_ID` and `BASECAMP_OAUTH_CLIENT_SECRET` must be set t

`basecamp` works with any AI agent that can run shell commands.

**Claude Code:** `basecamp setup claude` — installs the plugin with skills, hooks, and agent workflow support.
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.

**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.

Manual Codex installation uses the same marketplace:

```bash
codex plugin marketplace add basecamp/claude-plugins
codex plugin add basecamp@37signals
```

To pick up a newer plugin version later, refresh the marketplace with
`codex plugin marketplace upgrade 37signals` (or re-run `basecamp setup codex`).

**Other agents:** Point your agent at [`skills/basecamp/SKILL.md`](skills/basecamp/SKILL.md) for Basecamp workflow coverage.

Expand Down Expand Up @@ -180,6 +194,7 @@ See [install.md](install.md) for step-by-step setup instructions.
```bash
basecamp doctor # Check CLI health and diagnose issues
basecamp doctor --verbose # Verbose output with details
basecamp doctor --json # Structured checks, including Claude and Codex
```

## Development
Expand Down
3 changes: 2 additions & 1 deletion RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ make release VERSION=0.2.0 DRY_RUN=1
1. Validates semver format, main branch, clean tree, synced with remote
2. Checks for `replace` directives in go.mod
3. Runs `make release-check` (quality checks, vuln scan, replace-check, race-test, surface compat)
4. Updates stable release metadata (`nix/package.nix` and `.claude-plugin/plugin.json`) for stable versions
4. Updates stable release metadata (`nix/package.nix`, `.claude-plugin/plugin.json`, and `.codex-plugin/plugin.json`) for stable versions
5. Creates annotated tag `v$VERSION` and pushes to origin
6. GitHub Actions [release workflow](.github/workflows/release.yml) runs:
- Security scan + full test suite + CLI surface compatibility check
Expand Down Expand Up @@ -58,6 +58,7 @@ manager channels and agent distribution metadata on the latest stable version.
| AUR | Updates the `basecamp-cli` package when `AUR_KEY` is configured. | The stable AUR package remains active. |
| Nix flake | Updates `nix/package.nix` and verifies the flake. | Nix metadata remains on the latest stable version. |
| Claude plugin metadata | Updates `.claude-plugin/plugin.json`. | Plugin metadata remains on the latest stable version. |
| Codex plugin metadata | Updates `.codex-plugin/plugin.json`. | Plugin metadata remains on the latest stable version. |
| Skills distribution | Syncs skills to the distribution repo. | Uses the skill embedded in the prerelease binary. |

The prerelease binary embeds the matching `skills/basecamp/SKILL.md`. Testers can
Expand Down
27 changes: 26 additions & 1 deletion install.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,38 @@ EXECUTE NOW: Start with Step 1. Mark TODO items complete as you go. Stop when `b

**Do not execute this section unless explicitly requested.** The core installation is complete when DONE WHEN passes.

Both agent plugins require the `basecamp` CLI installed above — the plugin invokes it for every Basecamp operation.

### Claude Code

```bash
basecamp setup claude
```

This registers the marketplace and installs the plugin with skills, hooks, and agent workflow support.
This registers the marketplace and installs the plugin with skills 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.

For a manual install:

```bash
codex plugin marketplace add basecamp/claude-plugins
codex plugin add basecamp@37signals
```

To pick up a newer plugin version later, refresh with `codex plugin marketplace upgrade 37signals` (or re-run `basecamp setup codex`).

Verify either agent integration with structured diagnostics:

```bash
basecamp doctor --json
```

### Other Agents

Expand Down
40 changes: 19 additions & 21 deletions internal/commands/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,28 +206,20 @@ func runDoctorChecks(ctx context.Context, app *appctx.App, verbose bool) []Check
checks = append(checks, checkSkillVersion())
}
for _, agent := range harness.DetectedAgents() {
if agent.Checks != nil {
for _, c := range agent.Checks() {
checks = append(checks, Check{
Name: c.Name,
Status: c.Status,
Message: c.Message,
Hint: c.Hint,
})
}
var agentChecks []*harness.StatusCheck
if agent.Diagnostics != nil {
agentChecks = agent.Diagnostics(ctx)
} else if agent.Checks != nil {
agentChecks = agent.Checks()
}
for _, c := range agentChecks {
checks = append(checks, Check{
Name: c.Name,
Status: c.Status,
Message: c.Message,
Hint: c.Hint,
})
}
}

// 13. Claude plugin version (doctor-only, not part of generic agent checks
// which gate setup wizard behavior)
if harness.DetectClaude() {
pvc := harness.CheckClaudePluginVersion()
checks = append(checks, Check{
Name: pvc.Name,
Status: pvc.Status,
Message: pvc.Message,
Hint: pvc.Hint,
})
}

return checks
Expand Down Expand Up @@ -985,6 +977,12 @@ func buildDoctorBreadcrumbs(checks []Check) []output.Breadcrumb {
Cmd: "basecamp skill install",
Description: "Update installed skill",
})
case "Codex Plugin", "Codex Plugin Version":
breadcrumbs = append(breadcrumbs, output.Breadcrumb{
Action: "setup_codex",
Cmd: "basecamp setup codex",
Description: "Install or update the Codex plugin",
})
}
}

Expand Down
46 changes: 46 additions & 0 deletions internal/commands/doctor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"os"
"path/filepath"
"strings"
"testing"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -662,6 +663,51 @@ func TestBuildDoctorBreadcrumbs_SkillVersionWarn(t *testing.T) {
assert.Equal(t, "basecamp skill install", breadcrumbs[0].Cmd)
}

func TestCheckCodexIntegrationIncludesVersion(t *testing.T) {
logPath := installCodexStub(t, codexStubOptions{pluginAlreadyInstalled: true})
app, _ := setupDoctorTestApp(t, "")

checks := runDoctorChecks(context.Background(), app, false)
var names []string
for _, check := range checks {
if check.Name == "Codex Plugin" || check.Name == "Codex Plugin Version" {
names = append(names, check.Name)
}
}
require.Equal(t, []string{"Codex Plugin", "Codex Plugin Version"}, names)
assert.Equal(t, 1, strings.Count(readCodexSetupCalls(t, logPath), "plugin list --available --json"))
}

func TestCheckCodexIntegrationWarnsOnVersionMismatch(t *testing.T) {
installCodexStub(t, codexStubOptions{pluginAlreadyInstalled: true})
app, _ := setupDoctorTestApp(t, "")
original := version.Version
version.Version = "0.8.0"
t.Cleanup(func() { version.Version = original })

checks := runDoctorChecks(context.Background(), app, false)
for _, check := range checks {
if check.Name == "Codex Plugin Version" {
assert.Equal(t, "warn", check.Status)
assert.Contains(t, check.Hint, "basecamp setup codex")
return
}
}
t.Fatal("Codex Plugin Version check not found")
}

func TestBuildDoctorBreadcrumbs_Codex(t *testing.T) {
checks := []Check{
{Name: "Codex Plugin", Status: "fail"},
{Name: "Codex Plugin Version", Status: "warn"},
}

breadcrumbs := buildDoctorBreadcrumbs(checks)

require.Len(t, breadcrumbs, 1)
assert.Equal(t, "basecamp setup codex", breadcrumbs[0].Cmd)
}

func TestCheckLegacyInstall_SkipsKeyringWhenNoKeyring(t *testing.T) {
t.Setenv("BASECAMP_NO_KEYRING", "1")
t.Setenv("XDG_CACHE_HOME", t.TempDir())
Expand Down
Loading
Loading