Skip to content

Commit 04d5b6c

Browse files
authored
Merge pull request #2 from robzolkos/feature/register-helper
Add register helper command for OAuth app setup
2 parents d28d2c7 + 50836a9 commit 04d5b6c

3 files changed

Lines changed: 111 additions & 4 deletions

File tree

README.md

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,35 @@ make build
3030

3131
## Setup
3232

33-
1. Create a Basecamp OAuth app at https://launchpad.37signals.com/integrations
34-
2. Run `basecamp init` to configure credentials
35-
3. Run `basecamp auth` to authenticate
33+
### Prerequisites
3634

37-
Configuration files (XDG Base Directory):
35+
During OAuth authentication, Basecamp redirects to your computer on **port 3002**. Your machine must be accessible via a URL for this to work. Use a service like:
36+
37+
- [Tailscale](https://tailscale.com/) - Recommended for persistent access
38+
- [ngrok](https://ngrok.com/) - Quick setup for temporary access
39+
- Any reverse proxy that exposes localhost:3002
40+
41+
### Registration
42+
43+
1. Start your tunnel service and note the public URL (e.g., `https://myhost.tailscale.ts.net`)
44+
45+
2. Run the registration helper to generate your OAuth app values:
46+
47+
```bash
48+
basecamp register
49+
```
50+
51+
This will ask for your application details and output the exact values to enter in the Basecamp registration form, including the correct redirect URI.
52+
53+
3. Visit https://launchpad.37signals.com/integrations and register your app using the generated values
54+
55+
4. Run `basecamp init` to configure your credentials (Client ID, Client Secret, and the same Redirect URI)
56+
57+
5. Run `basecamp auth` to authenticate (ensure your tunnel is running on port 3002)
58+
59+
### Configuration Files
60+
61+
Configuration follows XDG Base Directory specification:
3862
- `~/.config/basecamp/config.json` - client credentials
3963
- `~/.local/share/basecamp/token.json` - OAuth token
4064

@@ -350,6 +374,14 @@ basecamp card 44444444 # just need card_id
350374

351375
The CLI searches current directory and parent directories for `.basecamp.yml`.
352376

377+
## Agent Skills
378+
379+
Install the Basecamp skill for AI coding agents (Claude Code, OpenCode, and others):
380+
381+
```bash
382+
npx skills add robzolkos/basecamp-cli
383+
```
384+
353385
## Output
354386

355387
All commands output JSON for easy parsing with `jq`:

internal/commands/register.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package commands
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"os"
7+
"strings"
8+
)
9+
10+
type RegisterCmd struct{}
11+
12+
func (c *RegisterCmd) Run(args []string) error {
13+
fmt.Fprintln(os.Stderr, "Basecamp OAuth App Registration Helper")
14+
fmt.Fprintln(os.Stderr, strings.Repeat("=", 40))
15+
fmt.Fprintln(os.Stderr)
16+
fmt.Fprintln(os.Stderr, "This helper will generate the values you need to register")
17+
fmt.Fprintln(os.Stderr, "your Basecamp OAuth application.")
18+
fmt.Fprintln(os.Stderr)
19+
20+
reader := bufio.NewReader(os.Stdin)
21+
22+
appName := prompt(reader, "Application name", "My Basecamp CLI")
23+
companyName := prompt(reader, "Company/Organization name", "")
24+
websiteURL := prompt(reader, "Website URL", "https://github.com/robzolkos/basecamp-cli")
25+
accessibleURL := prompt(reader, "URL where this computer is accessible (e.g., https://myhost.tailscale.ts.net)", "")
26+
27+
// Build redirect URI from accessible URL
28+
redirectURI := buildRedirectURI(accessibleURL)
29+
30+
fmt.Fprintln(os.Stderr)
31+
fmt.Fprintln(os.Stderr, strings.Repeat("=", 60))
32+
fmt.Fprintln(os.Stderr, "REGISTRATION INSTRUCTIONS")
33+
fmt.Fprintln(os.Stderr, strings.Repeat("=", 60))
34+
fmt.Fprintln(os.Stderr)
35+
fmt.Fprintln(os.Stderr, "1. Visit: https://launchpad.37signals.com/integrations")
36+
fmt.Fprintln(os.Stderr)
37+
fmt.Fprintln(os.Stderr, "2. Click 'Register another application'")
38+
fmt.Fprintln(os.Stderr)
39+
fmt.Fprintln(os.Stderr, "3. Fill out the form with these values:")
40+
fmt.Fprintln(os.Stderr)
41+
fmt.Fprintf(os.Stderr, " Name of your application: %s\n", appName)
42+
fmt.Fprintf(os.Stderr, " Your company/organization: %s\n", companyName)
43+
fmt.Fprintf(os.Stderr, " Website URL: %s\n", websiteURL)
44+
fmt.Fprintf(os.Stderr, " Redirect URI: %s\n", redirectURI)
45+
fmt.Fprintln(os.Stderr)
46+
fmt.Fprintln(os.Stderr, "4. After registering, copy your Client ID and Client Secret")
47+
fmt.Fprintln(os.Stderr)
48+
fmt.Fprintln(os.Stderr, "5. Run 'basecamp init' and enter the credentials when prompted")
49+
fmt.Fprintln(os.Stderr, " (use the same Redirect URI shown above)")
50+
fmt.Fprintln(os.Stderr)
51+
fmt.Fprintln(os.Stderr, "6. Run 'basecamp auth' to authenticate")
52+
fmt.Fprintln(os.Stderr, strings.Repeat("=", 60))
53+
54+
return PrintJSON(map[string]string{
55+
"application_name": appName,
56+
"company_name": companyName,
57+
"website_url": websiteURL,
58+
"redirect_uri": redirectURI,
59+
"registration_url": "https://launchpad.37signals.com/integrations",
60+
})
61+
}
62+
63+
func buildRedirectURI(accessibleURL string) string {
64+
if accessibleURL == "" {
65+
return "http://localhost:3002/callback"
66+
}
67+
68+
// Remove trailing slash if present
69+
accessibleURL = strings.TrimSuffix(accessibleURL, "/")
70+
71+
// Add port and callback path
72+
return accessibleURL + ":3002/callback"
73+
}

internal/commands/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type Command interface {
1414
}
1515

1616
var commands = map[string]func() Command{
17+
"register": func() Command { return &RegisterCmd{} },
1718
"init": func() Command { return &InitCmd{} },
1819
"auth": func() Command { return &AuthCmd{} },
1920
"projects": func() Command { return &ProjectsCmd{} },
@@ -115,6 +116,7 @@ func printHelp(version string) {
115116
Usage: basecamp <command> [arguments] [flags]
116117
117118
Commands:
119+
register Generate OAuth app registration values
118120
init Configure credentials
119121
auth Authenticate with OAuth
120122
projects List all projects

0 commit comments

Comments
 (0)