Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: basecamp/basecamp-cli
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.2.1
Choose a base ref
...
head repository: basecamp/basecamp-cli
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.2.2
Choose a head ref
  • 10 commits
  • 54 files changed
  • 1 contributor

Commits on Mar 3, 2026

  1. Correct skills/ subdir sync

    jeremy committed Mar 3, 2026
    Configuration menu
    Copy the full SHA
    1c011d7 View commit details
    Browse the repository at this point in the history
  2. Render todos as markdown task lists (#178)

    * Add task list markdown renderer with grouped output
    
    Introduce MarkdownListView schema type with style and group_by fields.
    When a schema declares style: tasklist, RenderListMarkdown renders
    - [ ] / - [x] checkbox items instead of GFM pipe tables. Items are
    grouped by a dot-path field (e.g. bucket.name) with headings suppressed
    for single groups and "Other" for items missing the group field.
    
    Adds PresentOption/WithGroupBy for dynamic group-by override, and
    removes empty-slice bail in both presentStyled and presentMarkdown
    so empty results are handled consistently by the renderers (both
    paths now emit *No results* for empty data).
    
    * Add DisplayData and WithGroupBy to output envelope
    
    DisplayData (json:"-") provides alternate data for styled/markdown
    rendering while keeping Data untouched for JSON serialization. This
    lets commands preserve wrapper structs for machine output while
    presenting unwrapped slices through the schema-aware presenter.
    
    WithGroupBy threads a presenter option through the envelope to
    override the schema's default group_by field at render time.
    
    * Render reports assigned as markdown task lists
    
    Wire up reports assigned to use WithEntity("todo") + WithDisplayData
    for schema-aware rendering. JSON output preserves the full wrapper
    struct; styled/markdown output presents the unwrapped todos through
    the task list renderer. --group-by date maps to due_on grouping.
    
    Adds output-layer tests for DisplayData contract (JSON uses Data,
    markdown/styled use DisplayData) and presenter tests for task list
    rendering, grouping, override, empty states, and Other heading.
    
    * Fix date label and people name extraction in task list renderer
    
    Only use "due:" prefix for `due_on` fields; other date columns use their
    field label. Extract people names directly from the raw array value
    instead of splitting the formatted comma-joined string, which broke names
    containing commas (e.g. "Park, Joon-seo").
    jeremy authored Mar 3, 2026
    Configuration menu
    Copy the full SHA
    c516df5 View commit details
    Browse the repository at this point in the history
  3. Fix AI labeler removing and re-adding the same label (#179)

    Check current labels before modifying. Only remove classification labels
    that differ from the chosen one and are actually present; only add the
    chosen label if it's not already there. Eliminates no-op label churn on
    synchronize events.
    jeremy authored Mar 3, 2026
    Configuration menu
    Copy the full SHA
    6b9ef89 View commit details
    Browse the repository at this point in the history
  4. Fix 86 CodeQL findings across Go, Python, and CI workflows (#177)

    * Sanitize paths from env vars and OS APIs to fix CodeQL go/path-injection
    
    Apply filepath.Clean() at source functions where environment variables
    (XDG_CONFIG_HOME, XDG_CACHE_HOME, HOME) and OS APIs (UserHomeDir,
    UserCacheDir, UserConfigDir) produce tainted values. This breaks
    CodeQL's taint chain for ~69 downstream path-injection findings.
    
    Uses "fallback first, then clean non-empty" pattern to avoid
    filepath.Clean("") returning "." which would redirect to CWD.
    Falls back to os.TempDir() when home directory is unavailable.
    
    * Strip newlines from auth token output to fix CodeQL log-injection
    
    * Validate editor path with LookPath to fix CodeQL command-injection
    
    exec.LookPath() is recognized by CodeQL as a sanitizer for the
    command-injection finding on $EDITOR-derived exec.Command calls.
    
    * Guard float-to-int conversion with 2^53 precision bound to fix CodeQL integer-overflow
    
    float64 can only represent consecutive integers exactly up to 2^53.
    Check range before casting to int64 to avoid undefined behavior for
    large floats. Adds boundary tests for precision edge cases.
    
    * Add workflow permissions and pin action tags for CodeQL security findings
    
    - Add top-level permissions: contents: read to test.yml (7 findings)
    - Pin golangci-lint-action and actions/cache to commit hashes in release.yml
    
    * Add is_dir() guards in doc_parity.py before glob/rglob calls
    jeremy authored Mar 3, 2026
    Configuration menu
    Copy the full SHA
    0257161 View commit details
    Browse the repository at this point in the history
  5. Add config trust model for authority keys (#181)

    * Add TrustStore for opt-in config file trust
    
    Introduce TrustStore type persisted at ~/.config/basecamp/trusted-configs.json.
    Path-based trust (not content-based) allows authority keys from local/repo
    configs when explicitly approved via the trust store.
    
    Canonical paths (EvalSymlinks + Abs) prevent symlink bypasses. When a file
    is deleted, parent-directory symlink resolution preserves canonical form so
    stale entries can still be revoked.
    
    * Wire trust store into config loading
    
    loadFromFile now accepts a *TrustStore parameter. Authority keys (base_url,
    default_profile, profiles) from local/repo sources are accepted when the
    file is trusted, rejected otherwise.
    
    Warnings include the exact file path with shell-safe quoting so users can
    copy-paste the remediation command. Export RepoConfigPath for reuse by the
    trust subcommand.
    
    * Add config trust/untrust subcommands
    
    basecamp config trust [path] — approve a local/repo config for authority keys
    basecamp config untrust [path] — revoke trust (works even if file was deleted)
    basecamp config trust --list — show all trusted paths
    
    config set now warns when writing authority keys to untrusted local config.
    Trust and untrust use separate path resolution: trust requires the file to
    exist, untrust accepts explicit nonexistent paths for stale entry cleanup.
    
    * Harden canonicalizePath and use POSIX shell quoting in warnings
    
    canonicalizePath now only falls back to the absolute path on
    fs.ErrNotExist; other EvalSymlinks errors (permission denied, etc.)
    return "" to fail closed on the security boundary.
    
    Warning messages use POSIX single-quote escaping instead of Go %q
    double-quoting, preventing shell metacharacter expansion when users
    copy-paste the suggested trust command.
    jeremy authored Mar 3, 2026
    Configuration menu
    Copy the full SHA
    701669a View commit details
    Browse the repository at this point in the history
  6. Restore error guards on AI labeler edit calls (#180)

    The label snapshot can go stale between gh pr view and the subsequent
    edits if another workflow or human changes labels concurrently. Keep
    the 2>/dev/null || true guards so a race doesn't fail the job.
    jeremy authored Mar 3, 2026
    Configuration menu
    Copy the full SHA
    f5f188f View commit details
    Browse the repository at this point in the history
  7. Convert HTML content to markdown in presenter output (#184)

    * Detect bc-attachment tags as HTML in IsHTML
    
    Pure bc-attachment payloads (Basecamp mentions, file references) without
    wrapping <div>/<p> tags bypassed HTML detection because reSafeTag did not
    include bc-attachment. This caused raw tags to leak through formatText()
    and every downstream renderer.
    
    * Convert HTML to markdown in presenter output
    
    The Basecamp API returns Trix HTML in content, description, and body
    fields. The TUI already converts via richtext.HTMLToMarkdown → glamour,
    but the CLI presenter rendered strings verbatim, leaking <div>, <br>,
    <bc-attachment>, <blockquote>, etc. into every output mode.
    
    - formatText() now detects HTML and converts to markdown at the
      universal formatting point, so all output modes benefit
    - singleLine() helper collapses multi-line converted content for
      compact renderers (list rows, task items, table cells, metadata)
    - RenderHeadline() normalizes HTML headlines and strips emphasis
      markers that would nest with the bold/primary rendering context
    
    * Address review feedback on HTML-to-markdown conversion
    
    - singleLine: add fast-path when no newlines to avoid slice allocation
      on the common case (every cell/value in list rendering)
    - RenderHeadline: replace blanket strings.ReplaceAll("**","") with regex
      that unwraps **...** pairs, preserving literal ** in content like 2**10
    - Narrow doc comment to describe bold-only unwrapping, not general emphasis
    jeremy authored Mar 3, 2026
    Configuration menu
    Copy the full SHA
    88114fa View commit details
    Browse the repository at this point in the history

Commits on Mar 4, 2026

  1. Add macOS code signing and notarization (#185)

    * Add macOS code signing and notarization via GoReleaser
    
    Use GoReleaser v2's native notarize.macos support (powered by embedded
    quill) to sign and notarize darwin binaries on the existing Ubuntu runner.
    
    The enabled gate requires all five MACOS_* secrets to be present,
    cleanly skipping for forks and local builds. Explicit ids: [basecamp]
    prevents accidental scope expansion if new build targets are added.
    
    * Wire macOS signing secrets and preflight check into release workflow
    
    Pass MACOS_SIGN_P12, MACOS_SIGN_PASSWORD, MACOS_NOTARY_KEY,
    MACOS_NOTARY_KEY_ID, and MACOS_NOTARY_ISSUER_ID to GoReleaser.
    
    A preflight step on the canonical repo fails fast if any secret is
    missing, preventing silent publication of unsigned macOS binaries.
    Forks skip the check via github.repository guard.
    
    Bump release job timeout from 15m to 45m for notarization wait headroom.
    
    * Fix cosign verification in install script to use bundle format
    
    The release workflow produces checksums.txt.bundle (cosign v2 bundle),
    not the separate .sig/.pem files the installer was trying to download.
    Switch to --bundle flag to match actual release assets.
    
    * Use non-empty check instead of isEnvSet for notarize guard
    
    GitHub Actions sets missing secrets as empty strings, so isEnvSet
    returns true even on forks without secrets configured. Switch to
    Go template truthiness (empty string is falsy) so the notarize
    block is skipped when secrets are absent.
    
    * Document macOS signing secrets and notarization in RELEASING.md
    
    * Move macOS signing secrets to release environment
    
    Add `environment: release` to the release job so signing credentials
    are only accessible to that specific job context. Isolates the
    Developer ID cert and App Store Connect API key from other workflows.
    jeremy authored Mar 4, 2026
    Configuration menu
    Copy the full SHA
    5512797 View commit details
    Browse the repository at this point in the history
  2. Support custom OAuth redirect_uri (#186)

    * Support custom OAuth redirect_uri via env var and programmatic override
    
    Users with their own OAuth client credentials registered on Launchpad
    need the CLI's redirect_uri to match their app's registered callback.
    The CLI previously hardcoded http://127.0.0.1:8976/callback with no
    way to override it.
    
    Add resolveOAuthCallback() with precedence chain:
      LoginOptions.RedirectURI > BASECAMP_OAUTH_REDIRECT_URI env var >
      CallbackAddr-derived > hardcoded default
    
    Validate all redirect URIs (including default) against RFC 8252
    loopback rules: http scheme, loopback host, explicit port, no
    userinfo/query/fragment.
    
    Rename credential env vars to BASECAMP_OAUTH_CLIENT_ID/SECRET with
    strict pairing (both required when either is set).
    
    BC3 DCR clients registered with a custom redirect URI are not
    persisted to client.json, preventing stale credentials on subsequent
    runs without the override.
    
    Closes #183
    
    * Document custom OAuth credential env vars in README
    
    * Address review: derive customRedirect inside registerBC3Client, fix test isolation
    
    - Remove `customRedirect bool` parameter from `registerBC3Client`; derive
      it from `opts.RedirectURI != defaultRedirectURI` inside the function
    - Add `XDG_CONFIG_HOME` override in `TestRegisterBC3Client_UsesResolvedRedirectURI`
      to prevent writing to the developer's real config dir
    - Use full env var names in README sentence
    jeremy authored Mar 4, 2026
    Configuration menu
    Copy the full SHA
    8990b3d View commit details
    Browse the repository at this point in the history
  3. Add --subscribe / --no-subscribe to recording creation commands (#187)

    * Bump SDK to v0.2.2
    
    Picks up Subscriptions field on create request types
    (CreateMessageRequest, CreateDocumentRequest, CreateScheduleEntryRequest).
    
    * Add --subscribe / --no-subscribe to recording creation commands
    
    Controls who gets subscribed and notified when creating messages, docs,
    and schedule entries. Critical for bot/agent use cases that need silent
    creation.
    
      --no-subscribe        subscribe nobody (no notifications)
      --subscribe "X,Y"     subscribe specific people (names, emails, IDs, "me")
    
    Mutually exclusive. Neither flag preserves server default (everyone).
    Explicit --subscribe with no resolvable people is a hard error, including
    --subscribe "" (via cmd.Flags().Changed detection).
    
    Available on: message, messages create, docs create, schedule create.
    
    Closes #182
    
    * Address PR review feedback
    
    - Wrap ParseInt error in resolvePersonIDs for better diagnostics
    - Add happy-path tests verifying --no-subscribe sends empty subscriptions
      array and default omits the field entirely
    - Update --no-subscribe help text to "Don't subscribe anyone else" since
      the server always auto-subscribes the creator
    jeremy authored Mar 4, 2026
    Configuration menu
    Copy the full SHA
    fea17f4 View commit details
    Browse the repository at this point in the history
Loading