Skip to content
This repository was archived by the owner on Oct 8, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,43 @@ prism usage
Each provider is fetched independently, so an unavailable login does not hide
usage from the other providers.

Add `--json` to print machine-readable output instead of the table. The document
has `generated_at` (RFC 3339, UTC) and a `providers` array in the same order as
the table, where each entry has `provider`, `display_name`, `accounts`, and
`error`. A provider that could not be fetched has an empty `accounts` array and
an `error` object; every other provider has `error: null`. The same flag works
for a single provider, such as `prism chatgpt usage --json`, which prints one
provider object:

```json
{
"provider": "chatgpt",
"display_name": "ChatGPT",
"accounts": [
{
"id": "cred_example",
"name": "[email protected]",
"plan": "pro",
"observed_at": "2026-09-17T08:30:00Z",
"status": "fresh",
"limits": [
{
"name": "default",
"window": "7d",
"used_percent": 12.5,
"remaining_percent": 87.5,
"limit_reached": false,
"reset_at": "2026-09-22T00:00:00Z",
"window_seconds": 604800
}
],
"error": null
}
],
"error": null
}
```

ChatGPT accounts also show available earned reset credits and each credit's
expiry time when the provider returns that detail.

Expand Down
18 changes: 15 additions & 3 deletions internal/cli/cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,25 @@ func runCursorCommand(ctx context.Context, args []string, stdout io.Writer, stde
if err != nil {
return err
}
if len(remaining) != 0 {
return errors.New("usage: prism cursor usage [--account <name-or-email>]")
asJSON := false
unexpected := make([]string, 0, len(remaining))
for _, argument := range remaining {
if argument == "--json" {
asJSON = true
continue
}
unexpected = append(unexpected, argument)
}
if len(unexpected) != 0 {
return errors.New("usage: prism cursor usage [--account <name-or-email>] [--json]")
}
usage, err := fetchAllCursorUsage(ctx, account)
if err != nil {
return err
}
if asJSON {
return printUsageJSON(stdout, usageJSONProviderFor("cursor", usage, nil))
}
printUsage(stdout, usage)
return nil
}
Expand Down Expand Up @@ -398,7 +410,7 @@ func printCursorHelp(output io.Writer) {
prism cursor auth list|remove <name-or-email>
prism cursor status
prism cursor models
prism cursor usage [--account <name-or-email>]
prism cursor usage [--account <name-or-email>] [--json]
prism cursor [--account <name-or-email>] [Cursor Agent arguments...]

Install and run the official Cursor Agent without replacing ~/.local/bin/agent.
Expand Down
9 changes: 5 additions & 4 deletions internal/cli/gemini.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ func isGeminiCLIInvocation([]string) bool {

func runGeminiCommand(ctx context.Context, args []string, stdout io.Writer, stderr io.Writer) error {
if len(args) > 0 && args[0] == "usage" {
if len(args) != 1 {
return errors.New("usage: prism gemini usage")
asJSON := len(args) == 2 && args[1] == "--json"
if len(args) != 1 && !asJSON {
return errors.New("usage: prism gemini usage [--json]")
}
return runGeminiUsage(ctx, stdout, stderr)
return runGeminiUsage(ctx, stdout, stderr, asJSON)
}
if len(args) > 0 && args[0] == "auth" {
return errors.New("Antigravity CLI signs in automatically on the first 'prism gemini' run; it has no auth subcommand")
Expand Down Expand Up @@ -174,7 +175,7 @@ func withDefaultGeminiModel(args []string) []string {
func printGeminiHelp(output io.Writer) {
_, _ = fmt.Fprintln(output, `Usage:
prism gemini [Antigravity CLI arguments...]
prism gemini usage
prism gemini usage [--json]

Runs the official Antigravity CLI (agy) inside the Prism command surface.
The signed-in Google subscription remains managed by agy; Prism never copies
Expand Down
5 changes: 4 additions & 1 deletion internal/cli/gemini_usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@ type antigravityUsageEnvelope struct {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: With --json, fetch failures return before JSON encoding, so callers receive no provider object or structured usage_unavailable error.

Assessment: 🟠 Major Β· πŸ” Occurrence: Sometimes Β· 🏷️ Api mismatch

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent πŸ€–
This is a comment left during a code review.

**Path:** internal/cli/gemini_usage.go
**Line:** 34:34
**Comment:**
	*Api Mismatch: With `--json`, fetch failures return before JSON encoding, so callers receive no provider object or structured `usage_unavailable` error.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
πŸ‘ | πŸ‘Ž

var fetchGeminiUsage = fetchAntigravityUsage

func runGeminiUsage(ctx context.Context, stdout io.Writer, stderr io.Writer) error {
func runGeminiUsage(ctx context.Context, stdout io.Writer, stderr io.Writer, asJSON bool) error {
usage, err := fetchGeminiUsage(ctx)
if err != nil {
return err
}
if asJSON {
return printUsageJSON(stdout, usageJSONProviderFor("gemini", usage, nil))
}
printUsage(stdout, usage)
return nil
}
Expand Down
64 changes: 44 additions & 20 deletions internal/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type commonOptions struct {
confirm bool
providerAccountID string
ownerID string
json bool
help bool
}

Expand Down Expand Up @@ -145,6 +146,9 @@ func Run(
if err != nil {
return err
}
if options.json {
return printUsageJSON(stdout, usageJSONProviderFor(providerName, usage, nil))
}
Comment on lines +149 to +151

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: When the provider fetch fails, this JSON branch is never reached because the preceding error return emits no machine-readable provider object.

Assessment: 🟠 Major Β· πŸ” Occurrence: Sometimes Β· 🏷️ Api mismatch

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent πŸ€–
This is a comment left during a code review.

**Path:** internal/cli/run.go
**Line:** 149:151
**Comment:**
	*Api Mismatch: When the provider fetch fails, this JSON branch is never reached because the preceding error return emits no machine-readable provider object.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
πŸ‘ | πŸ‘Ž

printUsage(stdout, usage)
return nil
}
Expand Down Expand Up @@ -223,14 +227,14 @@ func runCombinedUsage(ctx context.Context, args []string, output io.Writer) erro
return err
}
if options.help {
fmt.Fprintln(output, "Usage:\n prism usage [--profile <name>]")
fmt.Fprintln(output, "Usage:\n prism usage [--profile <name>] [--json]")
return nil
}
if len(positionals) != 0 {
return fmt.Errorf("unexpected argument %q", positionals[0])
}
if options.name != "" || options.providerAccountID != "" || options.ownerID != "" {
return errors.New("usage accepts only --profile")
return errors.New("usage accepts only --profile and --json")
}

type usageResult struct {
Expand Down Expand Up @@ -269,39 +273,57 @@ func runCombinedUsage(ctx context.Context, args []string, output io.Writer) erro
}()

results := []struct {
name string
result usageResult
provider string
result usageResult
}{
{name: "ChatGPT", result: <-chatGPTResults},
{name: "Claude", result: <-anthropicResults},
{name: "Copilot", result: <-copilotResults},
{name: "OpenCode", result: <-openCodeResults},
{name: "Cursor", result: <-cursorResults},
{name: "Gemini", result: <-geminiResults},
{provider: "chatgpt", result: <-chatGPTResults},
{provider: "anthropic", result: <-anthropicResults},
{provider: "copilot", result: <-copilotResults},
{provider: "opencode-go", result: <-openCodeResults},
{provider: "cursor", result: <-cursorResults},
{provider: "gemini", result: <-geminiResults},
}
succeeded := 0
var failures []string
providers := make([]usageTableProvider, 0, len(results))
document := usageJSONDocument{
GeneratedAt: time.Now().UTC().Format(time.RFC3339),
Providers: make([]usageJSONProvider, 0, len(results)),
}
for _, provider := range results {
displayName := usageProviderDisplayName(provider.provider)
providers = append(providers, usageTableProvider{
name: provider.name,
name: displayName,
usage: provider.result.usage,
err: provider.result.err,
})
document.Providers = append(
document.Providers,
usageJSONProviderFor(provider.provider, provider.result.usage, provider.result.err),
)
if provider.result.err != nil {
failures = append(failures, provider.name)
failures = append(failures, displayName)
continue
}
succeeded++
}
printUsageTableAt(output, providers, time.Now(), true)
if options.json {
if err := printUsageJSON(output, document); err != nil {
return err
}
} else {
printUsageTableAt(output, providers, time.Now(), true)
}
if succeeded == 0 {
return fmt.Errorf("usage is unavailable for %s", joinNames(failures))
}
return nil
}

func validateCommand(provider string, command string, positionals []string, options commonOptions) error {
if options.json && command != "usage" {
return errors.New("--json is accepted only by 'prism usage' and 'prism <provider> usage'")
}
switch command {
case "usage":
if provider != "chatgpt" && provider != "anthropic" && provider != "copilot" && provider != "opencode-go" && provider != "gemini" {
Expand All @@ -311,7 +333,7 @@ func validateCommand(provider string, command string, positionals []string, opti
return fmt.Errorf("unexpected argument %q", positionals[0])
}
if options.name != "" || options.providerAccountID != "" || options.ownerID != "" {
return errors.New("usage accepts only --profile")
return errors.New("usage accepts only --profile and --json")
}
case "reset":
if provider != "chatgpt" {
Expand Down Expand Up @@ -854,6 +876,8 @@ func parseCommonOptions(args []string) (commonOptions, []string, error) {
options.creditID = strings.TrimPrefix(argument, "--credit-id=")
case argument == "--confirm":
options.confirm = true
case argument == "--json":
options.json = true
Comment on lines +879 to +880

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: --json is accepted by the shared parser, but prism claude login --json bypasses validateCommand and proceeds with login instead of rejecting the flag.

Assessment: 🟠 Major Β· πŸ” Occurrence: Sometimes Β· 🏷️ Api mismatch

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent πŸ€–
This is a comment left during a code review.

**Path:** internal/cli/run.go
**Line:** 879:880
**Comment:**
	*Api Mismatch: `--json` is accepted by the shared parser, but `prism claude login --json` bypasses `validateCommand` and proceeds with login instead of rejecting the flag.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
πŸ‘ | πŸ‘Ž

case argument == "--provider-account-id":
index++
if index >= len(args) || args[index] == "" {
Expand Down Expand Up @@ -899,11 +923,11 @@ Usage:
prism claude [--account <alias-or-id>] [claude arguments...]
prism codex enable|disable|status
prism cursor [--account <name-or-email>] [cursor arguments...]
prism usage [--profile <name>]
prism chatgpt usage [--profile <name>]
prism usage [--profile <name>] [--json]
prism chatgpt usage [--profile <name>] [--json]
prism chatgpt reset --account <name-or-email> [--credit-id <id>] --confirm [--profile <name>]
prism copilot usage [--profile <name>]
prism opencode-go usage
prism copilot usage [--profile <name>] [--json]
prism opencode-go usage [--json]
prism chatgpt auth login [--profile <name>]
prism anthropic auth login [--profile <name>]
prism claude login [--profile <name>]
Expand Down Expand Up @@ -946,8 +970,8 @@ proxy Antigravity OAuth credentials.`)

func printProviderUsageHelp(output io.Writer, provider string) {
if provider == "chatgpt" {
fmt.Fprintln(output, "Usage:\n prism chatgpt usage [--profile <name>]\n prism chatgpt reset --account <name-or-email> [--credit-id <id>] --confirm [--profile <name>]")
fmt.Fprintln(output, "Usage:\n prism chatgpt usage [--profile <name>] [--json]\n prism chatgpt reset --account <name-or-email> [--credit-id <id>] --confirm [--profile <name>]")
return
}
fmt.Fprintf(output, "Usage:\n prism %s usage [--profile <name>]\n", provider)
fmt.Fprintf(output, "Usage:\n prism %s usage [--profile <name>] [--json]\n", provider)
}
Loading
Loading