Skip to content

feat(google-business-profile): add google-business-profile#1436

Open
rajyaligar wants to merge 2 commits into
mvanhorn:mainfrom
rajyaligar:feat/google-business-profile
Open

feat(google-business-profile): add google-business-profile#1436
rajyaligar wants to merge 2 commits into
mvanhorn:mainfrom
rajyaligar:feat/google-business-profile

Conversation

@rajyaligar

Copy link
Copy Markdown

google-business-profile

Adds the Google Business Profile printed CLI and MCP server to the published library catalog. The CLI wraps Google Business Profile account management, business information, performance, verifications, Q&A, place actions, notifications, and lodging APIs with OAuth2 auth and agent-safe local archive/search workflows.

API: google-business-profile | Category: marketing | Press version: 4.20.1

Spec: library/marketing/google-business-profile/spec.json (sha256:6109d74b822c5879c4b87bf8664d81239e3e6046f2fbf4d859430bc646ff00ab)

CLI Shape

Manage google-business-profile resources via the google-business-profile API.

Highlights (not in the official API docs):
  • workflow archive   Build a local SQLite archive of Google Business Profile resources so agents can inspect accounts and locations without re-hitting the API on every question.
  • workflow status   Show local archive freshness and sync state across resource tables so operators know whether offline answers are still trustworthy.
  • search   Search locally synced Business Profile data with FTS5 when a native API search path is absent or live auth is unavailable.
  • analytics   Run grouped analytics over synced data to count resources and surface distribution patterns without exporting to spreadsheets first.
  • tail   Poll the API and emit NDJSON change events so operators can watch location-related activity in near real time.

Add --agent to any command for JSON output + non-interactive mode.
Run 'google-business-profile-pp-cli doctor' to verify auth and connectivity.

Usage:
  google-business-profile-pp-cli [command]

Available Commands:
  agent-context                          Emit structured JSON describing this CLI for agents
  analytics                              Run analytics queries on locally synced data
  api                                    Browse all API endpoints by interface name
  attributes                             Returns the list of attributes that would be available for a location with the given primary category and country.
  auth                                   Manage authentication for Google Business Profile
  completion                             Generate the autocompletion script for the specified shell
  doctor                                 Check CLI health
  feedback                               Record feedback about this CLI (local by default; upstream opt-in)
  help                                   Help about any command
  import                                 Import data from JSONL file via API create/upsert calls
  locations                              Moves a location from an account that the user owns to another account that the same user administers.
  place-action-type-metadata             Returns the list of available place action types for a location or country.
  profile                                Named sets of flags saved for reuse
  search                                 Full-text search across synced data or live API
  sync                                   Sync API data to local SQLite for offline search and analysis
  tail                                   Stream live changes by polling the API at regular intervals
  verification-tokens                    Generate a token for the provided location data to verify the location.
  version                                Print version
  which                                  Find the command that implements a capability
  workflow                               Compound workflows that combine multiple API operations

What This CLI Does

  • Authenticates with Google OAuth2 using the documented business.manage scope.
  • Lists and manages Google Business Profile accounts and locations through generated endpoint commands.
  • Exposes MCP tools for agent workflows.
  • Adds local SQLite archive, search, analytics, and tailing workflows for lower-token agent operations.
  • Includes live Phase 5 proof against the approved Google Business Profile API project.

Manuscripts

  • Research: library/marketing/google-business-profile/.manuscripts/20260616-220217/research/
  • Proofs: library/marketing/google-business-profile/.manuscripts/20260616-220217/proofs/

Validation Results

Check Result Evidence
Manifest PASS cli-printing-press publish validate --dir library/marketing/google-business-profile --json
Phase 5 PASS phase5-acceptance.json, full live dogfood: 123 passed, 0 failed, 162 skipped
go mod tidy PASS go mod tidy
go vet PASS cli-printing-press publish validate
go build PASS cli-printing-press publish validate
go test PASS go test ./...
--help PASS cli-printing-press publish validate
--version PASS cli-printing-press publish validate
verify-skill PASS cli-printing-press publish validate
govulncheck PASS cli-printing-press publish validate
Manuscripts PASS Research and proofs included under .manuscripts/20260616-220217/
Publish package verifier PASS python3 .github/scripts/verify-publish-package/verify_publish_package.py --base-ref origin/main

Publication Path

Generated through the local Printing Press library at ~/printing-press/library/google-business-profile, then packaged into the published library via cli-printing-press publish package --category marketing.

Novel Commands

  • workflow archive
  • workflow status
  • search
  • analytics
  • tail

Gaps

None blocking. The live-dogfood examples were patched for Google endpoints requiring real query parameters, and the duplicate root login alias was removed so the canonical auth login browser flow is not treated as a non-interactive API command by Phase 5.

@rajyaligar rajyaligar force-pushed the feat/google-business-profile branch from 445e1b5 to c951784 Compare July 4, 2026 02:20
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

@greptileai review

Auto-nudge from greptile-policy-gate.yml because no Greptile Review check appeared for 445e1b5f0b4ce5189fc152aef8f3d1a98031d2e1 after 185s. This usually means the PR is over Greptile auto-review size cap; manual triggers bypass it.

@rajyaligar

Copy link
Copy Markdown
Author

@greptile-apps review

@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a new Google Business Profile CLI and MCP server to the published library catalog, covering account management, business information, performance metrics, verifications, Q&A, place actions, notifications, and lodging APIs with OAuth2 auth. Issues flagged in earlier rounds (wrong binary name in Use, --follow=false silently ignored, unbounded server.Shutdown, and isTerminal returning true on error) are all addressed in this head commit.

  • OAuth2 auth flow (auth.go): browser-launched PKCE-style flow, state validation, 5-second shutdown grace, and token persistence look correct.
  • Local SQLite store (store/store.go): WAL mode, migration locking, FTS5 triggers, and SQL parameterisation are sound; ListField validates identifiers before interpolation.
  • Analytics group-by (analytics.go): runGroupBy passes 0 to db.List, which hard-caps the fetch at 200 rows, so groupBy results are silently sampled rather than reflecting all synced data.

Confidence Score: 4/5

Safe to merge with one correctness fix — the analytics group-by command silently samples only 200 records regardless of dataset size.

The analytics runGroupBy function calls db.List(resourceType, 0), which the store normalises to a hard 200-row cap. Any synced dataset larger than 200 rows will produce wrong group counts and rankings with no user-visible warning. All other core paths — auth, sync, tail, search, client, store migrations — look correct.

library/marketing/google-business-profile/internal/cli/analytics.go — the runGroupBy function needs the record-fetch limit raised or removed.

Important Files Changed

Filename Overview
library/marketing/google-business-profile/internal/cli/analytics.go runGroupBy hard-caps the record fetch at 200, producing silently incorrect analytics for larger synced datasets.
library/marketing/google-business-profile/internal/cli/tail.go --follow=false now correctly exits after the initial poll; previously addressed issue is resolved.
library/marketing/google-business-profile/internal/cli/auth.go OAuth2 login flow now uses a 5-second shutdown timeout; state is validated; credentials resolved correctly.
library/marketing/google-business-profile/internal/cli/helpers.go isTerminal now returns false on Stat errors; wide pagination helpers, table/card renderers, and provenance wrapping all look correct.
library/marketing/google-business-profile/internal/cli/root.go Use field is now the correct short binary name; root command structure and flag registration look correct.
library/marketing/google-business-profile/internal/store/store.go SQL queries use parameterised bindings throughout; ListField validates field names against validIdentifierRE before interpolation; WAL + migration locking is sound.
library/marketing/google-business-profile/internal/cli/search.go Search command correctly routes between live API and local FTS5; envelope unwrapping is safe.
library/marketing/google-business-profile/internal/cli/sync.go Sync command has correct incremental/full path, access-denial warning logic, and user param scoping.
library/marketing/google-business-profile/internal/client/client.go HTTP client uses correct binary name for cache directory; auth header re-derived on each redirect hop.
library/marketing/google-business-profile/internal/cli/channel_workflow.go Workflow archive/status commands use correct binary name for dbPath default; sync resource delegation looks sound.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User invokes CLI] --> B{Command}
    B --> AUTH[auth login]
    B --> SYNC[sync]
    B --> SEARCH[search]
    B --> ANALYTICS[analytics]
    B --> TAIL[tail]
    B --> WORKFLOW[workflow archive/status]

    AUTH --> OAUTH[OAuth2 browser flow\nstate validation + 5s shutdown]
    OAUTH --> TOKEN[Save tokens to config]

    SYNC --> API[Google Business Profile API]
    API --> SQLITE[(Local SQLite\nWAL + FTS5)]

    SEARCH --> AUTO{data-source?}
    AUTO -->|auto/live| API
    AUTO -->|local| SQLITE
    SQLITE --> FTS[FTS5 full-text search]

    ANALYTICS --> SQLITE
    SQLITE -->|db.List limit=200 BUG| GROUPBY[group-by & count]
    GROUPBY --> OUTPUT[NDJSON / table output]

    TAIL --> POLL{follow?}
    POLL -->|true| LOOP[ticker loop → SIGINT]
    POLL -->|false| SINGLE[single poll → exit]

    WORKFLOW --> SYNC
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[User invokes CLI] --> B{Command}
    B --> AUTH[auth login]
    B --> SYNC[sync]
    B --> SEARCH[search]
    B --> ANALYTICS[analytics]
    B --> TAIL[tail]
    B --> WORKFLOW[workflow archive/status]

    AUTH --> OAUTH[OAuth2 browser flow\nstate validation + 5s shutdown]
    OAUTH --> TOKEN[Save tokens to config]

    SYNC --> API[Google Business Profile API]
    API --> SQLITE[(Local SQLite\nWAL + FTS5)]

    SEARCH --> AUTO{data-source?}
    AUTO -->|auto/live| API
    AUTO -->|local| SQLITE
    SQLITE --> FTS[FTS5 full-text search]

    ANALYTICS --> SQLITE
    SQLITE -->|db.List limit=200 BUG| GROUPBY[group-by & count]
    GROUPBY --> OUTPUT[NDJSON / table output]

    TAIL --> POLL{follow?}
    POLL -->|true| LOOP[ticker loop → SIGINT]
    POLL -->|false| SINGLE[single poll → exit]

    WORKFLOW --> SYNC
Loading

Reviews (3): Last reviewed commit: "fix(google-business-profile): address PR..." | Re-trigger Greptile

Comment thread library/marketing/google-business-profile/internal/cli/root.go Outdated
Comment thread library/marketing/google-business-profile/internal/cli/tail.go
@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds the Google Business Profile CLI and MCP server to the library catalog, wrapping the Google Business Profile APIs (accounts, locations, verifications, Q&A, performance, lodging, notifications, place actions) with OAuth2 auth and agent-safe local SQLite archive/search workflows.

  • Core novel commands (sync, search, analytics, tail, workflow archive/status) provide offline FTS5 search, incremental syncing with cursor resumption, and NDJSON polling — all gated behind a concurrent worker pool and a schema-versioned SQLite store.
  • Auth uses a standard OAuth2 loopback callback flow with token refresh, credential masking in logs/URLs, and placeholder-credential detection.
  • tail has a broken --follow=false flag: the follow variable is declared and the flag registered, but the event loop never reads it, so a caller expecting a single-shot poll gets an infinitely-running process instead.

Confidence Score: 3/5

Safe to merge for all commands except tail, which has a functional contract broken by its --follow=false flag doing nothing.

The bulk of the code — sync, search, auth, client, store, and analytics — is well-structured with no logic errors found. The tail command exposes a documented single-poll mode (--follow=false) that silently has no effect: the event loop never checks the variable, so every tail invocation runs indefinitely. Any agent or script relying on this for a bounded poll will hang. Two additional quality findings exist in isTerminal (wrong fallback on Stat() error) and server.Shutdown (unbounded context), but neither affects the primary data workflows.

internal/cli/tail.go — the --follow flag is wired up but never consulted in the run loop.

Important Files Changed

Filename Overview
library/marketing/google-business-profile/internal/cli/tail.go Introduces the tail command for polling the API; the --follow=false single-poll flag is registered but never consulted by the event loop, so the command always runs indefinitely.
library/marketing/google-business-profile/internal/cli/auth.go Implements OAuth2 browser-based login flow with state, token exchange, and refresh; server.Shutdown is called with an unbounded context.Background() which could block indefinitely on a stuck connection.
library/marketing/google-business-profile/internal/cli/helpers.go Large shared helper file covering output formatting, pagination, provenance, and terminal detection; isTerminal returns true on Stat() error, incorrectly routing output to human table format on bad file descriptors.
library/marketing/google-business-profile/internal/client/client.go HTTP client with adaptive rate limiting, token refresh, cross-host redirect auth stripping, credential masking, and binary response wrapping; logic is solid.
library/marketing/google-business-profile/internal/cli/sync.go Concurrent SQLite sync with worker pool, incremental cursor, --strict/exit-policy logic, and dogfood safety cap; no issues found.
library/marketing/google-business-profile/internal/cli/search.go FTS5 local search with live API fallback, result deduplication, and provenance envelope; logic is correct.
library/marketing/google-business-profile/internal/store/store.go SQLite store with WAL mode, schema versioning, write-mutex serialization, and FTS5 search indexes; design is sound.
library/marketing/google-business-profile/internal/config/config.go TOML config with env-var overrides, OAuth token persistence, agentcookie marker, and token clearing; correctly zeroes all credential fields on logout.
library/marketing/google-business-profile/internal/cli/root.go Cobra root command wiring with persistent flags, --agent shorthand, --data-source validation, and profile application; no issues.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant CLI
    participant GoogleOAuth
    participant GoogleAPI
    participant SQLiteDB

    User->>CLI: auth login --client-id --client-secret
    CLI->>GoogleOAuth: redirect browser to authorization URL
    GoogleOAuth-->>CLI: OAuth code via loopback /callback
    CLI->>GoogleOAuth: POST token exchange
    GoogleOAuth-->>CLI: access_token + refresh_token
    CLI->>SQLiteDB: SaveTokens()

    User->>CLI: sync
    CLI->>GoogleAPI: GET /v1/accounts (paginated)
    GoogleAPI-->>CLI: accounts[]
    CLI->>SQLiteDB: UpsertBatch()

    User->>CLI: search "query"
    CLI->>GoogleAPI: GET /v1/chains:search (if live)
    GoogleAPI-->>CLI: results
    alt API unreachable / local mode
        CLI->>SQLiteDB: FTS5 search
        SQLiteDB-->>CLI: local results
    end
    CLI-->>User: NDJSON results + provenance

    User->>CLI: tail accounts --interval 10s
    loop every interval
        CLI->>GoogleAPI: GET /accounts
        GoogleAPI-->>CLI: data
        CLI-->>User: NDJSON event stream
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant CLI
    participant GoogleOAuth
    participant GoogleAPI
    participant SQLiteDB

    User->>CLI: auth login --client-id --client-secret
    CLI->>GoogleOAuth: redirect browser to authorization URL
    GoogleOAuth-->>CLI: OAuth code via loopback /callback
    CLI->>GoogleOAuth: POST token exchange
    GoogleOAuth-->>CLI: access_token + refresh_token
    CLI->>SQLiteDB: SaveTokens()

    User->>CLI: sync
    CLI->>GoogleAPI: GET /v1/accounts (paginated)
    GoogleAPI-->>CLI: accounts[]
    CLI->>SQLiteDB: UpsertBatch()

    User->>CLI: search "query"
    CLI->>GoogleAPI: GET /v1/chains:search (if live)
    GoogleAPI-->>CLI: results
    alt API unreachable / local mode
        CLI->>SQLiteDB: FTS5 search
        SQLiteDB-->>CLI: local results
    end
    CLI-->>User: NDJSON results + provenance

    User->>CLI: tail accounts --interval 10s
    loop every interval
        CLI->>GoogleAPI: GET /accounts
        GoogleAPI-->>CLI: data
        CLI-->>User: NDJSON event stream
    end
Loading

Fix All in Codex Fix All in Claude Code Fix All in Cursor Fix All in Conductor

Reviews (2): Last reviewed commit: "feat(google-business-profile): add googl..." | Re-trigger Greptile

Comment thread library/marketing/google-business-profile/internal/cli/tail.go
Comment thread library/marketing/google-business-profile/internal/cli/auth.go Outdated
Comment thread library/marketing/google-business-profile/internal/cli/helpers.go
- use shipped CLI name in runtime help, hints, and storage paths

- honor tail --follow=false single-poll mode

- bound OAuth callback shutdown and harden terminal detection
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

@greptileai review

Auto-nudge from greptile-policy-gate.yml because no Greptile Review check appeared for f8f6e6fd0b5fe37bf14873d56a0986954f4d0a90 after 184s. This usually means the PR is over Greptile auto-review size cap; manual triggers bypass it.

@rajyaligar

Copy link
Copy Markdown
Author

@tmchow PR #1436 is green now. Greptile feedback is resolved, all checks pass, and merge state is clean. Can you merge when you get a chance?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant