How the CLI fits together
At runtime, the flow is:
[User command] -> Cobra root in internal/cli/App
- validates args/flags (usage + enum/timestamp checks)
- loads credential context (config/env)
- authenticatedClient() chooses auth path:
- API key first (if present), otherwise OAuth token with youtube.readonly
- executes request via one client:
- internal/youtube for Data API calls
- internal/analytics for Analytics API calls
- pagination/filtering in youtube.ListResult
- rendering through internal/output (table/json/jsonl/tsv)
- exits via mapped code in cmd/oytc/main.go
Core layers
| Package | Role | Notes |
|---|---|---|
| internal/cli | Command surface + orchestration | Builds the full tree (analytics/search/channel/video/...); validates args, adds paging/format flags, maps exit errors. |
| internal/config | Auth storage + precedence | OAuth + API key in auth.json, with env var override precedence for API key. |
| internal/youtube | HTTP client | Header-based auth (X-Goog-Api-Key or Bearer), key/token fallback rules, retries, API error parsing. |
| internal/analytics | Analytics normalizer | Builds report queries and converts row arrays into key/value maps. |
| internal/oauth | OAuth flow | PKCE loopback flow, token exchange/refresh/revoke. |
| internal/output | Formatting | table/json/jsonl/tsv output with optional column selection. |
| internal/update | Self-updater | GitHub release resolution, checksum verification, atomic replacement. |
| internal/skill | Bundled skill install | Atomic write of embedded skill package under ~/.agents/skills/oytc. |
Command diagram (end-to-end)
oytc
├─ authentication
│ ├─ login [--oauth]
│ ├─ status [--check]
│ └─ logout
├─ analytics
│ ├─ overview [--by day|month]
│ ├─ report --metrics --dimensions
│ ├─ video <VIDEO_ID>
│ ├─ traffic-sources
│ └─ demographics
├─ search
├─ channel
│ ├─ get <REFERENCE>
│ ├─ activities <CHANNEL>
│ ├─ sections [CHANNEL|--id]
│ └─ uploads <CHANNEL>
├─ video
│ ├─ get <VIDEO_ID>
│ ├─ stats <VIDEO_ID>
│ ├─ popular
│ └─ trainability <VIDEO_ID>
├─ playlist
│ ├─ get <PLAYLIST_ID>
│ ├─ list --channel
│ └─ items <PLAYLIST_ID>
├─ comment
│ ├─ get <COMMENT_ID>
│ ├─ replies <PARENT_COMMENT_ID>
│ └─ threads (--video|--channel|--id)
├─ subscription
│ └─ list (--channel|--id)
├─ live-chat
│ ├─ list (--video|--chat-id)
│ └─ stream (--video|--chat-id)
├─ category
│ └─ list (--region|--id)
├─ language
│ └─ list
├─ region
│ └─ list
├─ version
├─ update [--check|--version]
└─ skills
└─ install
Additional helper command group: completion (bash | fish | zsh | powershell)
Real-life smoke test matrix
Passing commands (exit code 0)
| Command | Category | Result | Notes |
|---|---|---|---|
| analytics overview | analytics | 0 | Ran with OAuth token and query defaults. |
| analytics video | analytics | 0 | Returns normalized metrics table. |
| analytics traffic-sources | analytics | 0 | OAuth-required path works. |
| analytics demographics | analytics | 0 | OAuth-required path works. |
| analytics report | analytics | 0 | Raw report mode works. |
| search | public data | 0 | Search limit/page logic worked. |
| channel get | resources | 0 | Handle resolution successful. |
| channel activities | resources | 0 | Public activity listing worked. |
| channel sections | resources | 0 | Both channel and --id variants tested. |
| channel uploads | resources | 0 | Playlist resolution to uploads succeeded. |
| video get | resources | 0 | Video fetch by ID worked. |
| video stats | resources | 0 | Counter-focused part selection worked. |
| video popular | resources | 0 | MostPopular query worked. |
| video trainability | resources | 0 | No key needed for this endpoint. |
| playlist get | resources | 0 | Batch and envelope format good. |
| playlist list | resources | 0 | Channel playlist listing worked. |
| playlist items | resources | 0 | Playlist item paging path worked. |
| comment get | comments | 0 | Top-level comment lookup worked. |
| comment replies | comments | 0 | Replies listing worked. |
| comment threads --video | comments | 0 | Thread filtering by video worked. |
| comment threads --channel | comments | 0 | Thread filtering by channel worked. |
| comment threads --id | comments | 0 | Direct thread lookup worked. |
| category list --region | metadata | 0 | US region listing worked. |
| category list --id | metadata | 0 | ID-based lookup worked. |
| language list | metadata | 0 | Language listing worked. |
| region list | metadata | 0 | Region listing worked. |
| status --check | auth | 0 | API key and OAuth validity confirmed. |
| version | meta | 0 | Version/commit/build output rendered. |
| update --check | maintenance | 0 | Latest release check only, no install. |
| upgrade --check | maintenance | 0 | Alias dispatch confirmed. |
| skills install (decline) | maintenance | 0 | Prompt path completed without changing files. |
| completion bash/zsh/fish/powershell | ui | 0 | All shells return scripts/help text. |
| login (empty input) | auth | 2 (expected) | Validation path tested safely with empty key. |
| logout --help | auth | 0 | Help text only. |
| skill (alias) | maintenance | 0 | Alias path to skills confirmed. |
Environment/account-dependent or expected-failure checks
| Command | Exit | Observed reason | Why this is expected |
|---|---|---|---|
| subscription list --channel | 1 (exit status 4) | subscriptionForbidden | Current token/account cannot access this channel's public subscriptions in this environment. |
| subscription list --id dummy | 1 (exit status 3) | authError: Invalid Credentials | Likely auth scope/credential profile mismatch for this endpoint on this account. |
| live-chat list --video NIDrPjeZ-jQ | 1 (exit status 4) | Video has no active public live chat | Depends on live stream state. |
| live-chat stream --video NIDrPjeZ-jQ | 1 (exit status 4) | Video has no active public live chat | Same dependency on live state. |
Notes on behavior and architecture fit
The environment currently has OAuth scope yt-analytics.readonly only; most public Data API commands still run via API key while analytics commands correctly require OAuth.
Exit code semantics
| Code | Meaning |
|---|---|
| 0 | Success |
| 2 | Usage/validation error |
| 3 | Missing/invalid auth |
| 4 | Resource unavailable / forbidden / not found |
| 5 | Quota or rate limit |
| 6 | Operational/runtime (network/config/retry exhaust) |
What this means operationally
The codebase is internally consistent: command discovery/readiness, validation, and request flow all work in practice. Real-life smoke checks show healthy request/response behavior across all major command families. The remaining non-green cases are not code defects in this CLI; they are normal service or account-bound effects (subscription permissions, live stream state, auth scope).
Architecture is sound and currently operational with live APIs; continue expanding the command coverage by testing with a YouTube account authorized for subscription visibility and with an actively live chat target.