@witchpot/gmc 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/package.json +1 -1
- package/skill/gmc-analysis/CHANGELOG.md +22 -0
- package/skill/gmc-analysis/SKILL.md +69 -2
- package/skill/gmc-analysis.manifest.json +2 -2
- package/src/args.js +30 -1
- package/src/constants.js +23 -2
- package/src/help.js +180 -0
- package/src/requests.js +398 -2
- package/src/requests.test.js +545 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,88 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the Game Market Copilot CLI will be recorded here.
|
|
4
4
|
|
|
5
|
+
## 0.11.0 - 2026-10-27
|
|
6
|
+
|
|
7
|
+
- `gmc games follower-history <appid>`: observed Steam follower series (same window flags as `price-history`). MCP: `game_follower_history`.
|
|
8
|
+
- `gmc games events <appid> [--include rank_series]`: Steam event participations with observed ranks and shelves. MCP: `game_steam_events`.
|
|
9
|
+
- The bundled `gmc-analysis` skill 0.17.0 covers both commands, and the
|
|
10
|
+
optional `positiveReviews` field on `gmc games review-history` points (state
|
|
11
|
+
a rate as `positiveReviews / totalReviews` when it is present).
|
|
12
|
+
|
|
13
|
+
- New `gmc games price-history <appid>` and `gmc games screenshot-labels
|
|
14
|
+
<appid>` commands (NAK-934, Provisional), the same plain-appid shape as
|
|
15
|
+
`gmc games ai-disclosure`. `price-history` returns the observed Steam
|
|
16
|
+
storefront price on every observed day (major currency units, with
|
|
17
|
+
discount, currency and store country) and takes `--range 7d|1m|1y|all`
|
|
18
|
+
(default `all`) or `--from`/`--to` (`YYYY-MM-DD`, real calendar dates,
|
|
19
|
+
`--from` on or before `--to`), never both; a bad window is a usage error
|
|
20
|
+
before any request. Days without an observation
|
|
21
|
+
are absent, never filled, and a price never implies sales.
|
|
22
|
+
`screenshot-labels` returns one set of visual labels per Steam store
|
|
23
|
+
screenshot and takes no flags. The labels are model output, not Steam
|
|
24
|
+
data. `docs/cli-use-cases.md` and `docs/agent-usage.md` cover both.
|
|
25
|
+
|
|
26
|
+
- Not shipped here: the bundled `gmc-analysis` skill does not mention the
|
|
27
|
+
two new commands yet. It is a separately versioned artifact with its own
|
|
28
|
+
manifest gate, so its coverage follows in its own change.
|
|
29
|
+
|
|
30
|
+
- New `gmc roblox saved | save | remove` commands (NAK-723, Provisional),
|
|
31
|
+
mapping the saved-experiences surface 0.10.0 recorded as "not yet
|
|
32
|
+
mapped". `gmc roblox saved --json` lists the workspace's saved
|
|
33
|
+
experiences resolved for one chart, country and cutoff (`--country`,
|
|
34
|
+
`--device`, `--sort`, `--as-of`, `--eligibility-days`); `gmc roblox save
|
|
35
|
+
<universeId>` and `gmc roblox remove <universeId>` change the set and
|
|
36
|
+
take no flags at all, refusing a scope flag rather than ignoring it.
|
|
37
|
+
|
|
38
|
+
Unlike the five read commands these are workspace-scoped: they need an
|
|
39
|
+
API key or CLI credential and answer 401 without one, which is not an
|
|
40
|
+
empty saved list. They charge no credits — saving is workspace metadata,
|
|
41
|
+
not analytics — and the paid-plan gate still applies.
|
|
42
|
+
|
|
43
|
+
Saved membership is scope-independent: the flags on `saved` decide which
|
|
44
|
+
of `in_scope` / `out_of_scope` / `not_tracked` each row is in, never
|
|
45
|
+
which rows are listed. Both writes are idempotent and report the state
|
|
46
|
+
that now holds, so `changed: false` is success rather than a write to
|
|
47
|
+
retry. Universe ids travel as decimal strings into the request body as
|
|
48
|
+
well as the path, and the CLI rejects a spelling the server would reject
|
|
49
|
+
before any request is sent.
|
|
50
|
+
|
|
51
|
+
`docs/cli-use-cases.md` (new case 70) and `docs/agent-usage.md` now cover
|
|
52
|
+
the saved commands alongside the read family.
|
|
53
|
+
|
|
54
|
+
- The 0.10.0 entry below is left exactly as published. Its "not yet
|
|
55
|
+
mapped" line was true of 0.10.0 and is corrected by this entry rather
|
|
56
|
+
than edited in place, matching how 0.8.0 recorded the 0.7.0 registry gap:
|
|
57
|
+
a published entry describes the release users installed, so amending one
|
|
58
|
+
would make the repo disagree with the tarball already on npm.
|
|
59
|
+
|
|
60
|
+
- Not shipped here: the bundled `gmc-analysis` skill still documents the
|
|
61
|
+
Roblox read family only. The skill is a separately versioned artifact
|
|
62
|
+
with its own manifest gate, so its Roblox-saved coverage and version bump
|
|
63
|
+
follow in their own change.
|
|
64
|
+
|
|
65
|
+
## 0.10.0 - 2026-09-22
|
|
66
|
+
|
|
67
|
+
- New `gmc roblox` command family (NAK-715, Provisional): `charts` (one
|
|
68
|
+
recorded ranking cohort as of a cutoff), `experiences` (the tracked
|
|
69
|
+
catalog with filters, sorts and signed-cursor pagination), `experience
|
|
70
|
+
<universeId>` (one experience with per-metric observation times),
|
|
71
|
+
`history <universeId> --granularity hourly|daily` (retained observations
|
|
72
|
+
with explicit gaps), and `coverage` (the configured cohort matrix with
|
|
73
|
+
per-dimension freshness). Universe ids are decimal strings end to end —
|
|
74
|
+
they are never Steam appids, and the commands reject one. The Roblox
|
|
75
|
+
surface went live in production on 2026-09-22 (NAK-725); the commands
|
|
76
|
+
stay Provisional until the surface has been through a broader real-data
|
|
77
|
+
smoke period.
|
|
78
|
+
|
|
79
|
+
- The Roblox read endpoints reject impossible calendar dates in every
|
|
80
|
+
offset spelling (`as_of=2026-02-31T12:00:00+09:00` is now a 400 naming
|
|
81
|
+
the nonexistent date instead of silently answering for March 3), and
|
|
82
|
+
history gap counts always reconcile with the expected point count.
|
|
83
|
+
|
|
84
|
+
- Not yet mapped: the saved-experiences surface (`/api/v1/roblox/saved`,
|
|
85
|
+
NAK-723) is live over REST and MCP; a CLI mapping will follow.
|
|
86
|
+
|
|
5
87
|
## 0.9.0 - 2026-09-16
|
|
6
88
|
|
|
7
89
|
- New `--release-status <released|upcoming|tba>` filter on the games
|
package/package.json
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# gmc-analysis skill changelog
|
|
2
2
|
|
|
3
|
+
## 0.17.0 - 2026-10-08
|
|
4
|
+
|
|
5
|
+
- Review-count history (NAK-934): each `series[]` point can carry the
|
|
6
|
+
optional `positiveReviews`, the positive-review count observed on the same
|
|
7
|
+
row, beside the optional `ratingPercent`. Both are absent, never zero, when
|
|
8
|
+
the row carried no usable value. New guardrail: state a rate as
|
|
9
|
+
`positiveReviews / totalReviews` when the count is present, because
|
|
10
|
+
`ratingPercent` is rounded to one decimal (1,999 of 2,000 reads 100.0);
|
|
11
|
+
without the count, a `100.0` on 2,000 or more reviews is possibly rounded
|
|
12
|
+
and is never reported as "no negative reviews".
|
|
13
|
+
- New "Steam follower history" section: `gmc games follower-history <appid>`
|
|
14
|
+
(MCP: `game_follower_history`), its `--range` or `--from`/`--to` window
|
|
15
|
+
(never both), and the guardrails: observed days only, followers are not
|
|
16
|
+
wishlists or sales, `not_collected` is a collection state, truncation
|
|
17
|
+
drops the oldest points.
|
|
18
|
+
- New "Steam event participation" section: `gmc games events <appid>
|
|
19
|
+
[--include rank_series]` (MCP: `game_steam_events`), with the guardrails:
|
|
20
|
+
ranks and shelves are observed event-page state, not an official ranking;
|
|
21
|
+
an absent block or an empty list is a collection state; the rank series is
|
|
22
|
+
raw and never resampled.
|
|
23
|
+
- MCP-only mapping updated with both tools.
|
|
24
|
+
|
|
3
25
|
## 0.16.0 - 2026-09-16
|
|
4
26
|
|
|
5
27
|
- New "Disclosure text and change history" subsection under "AI disclosure
|
|
@@ -8,7 +8,7 @@ description: >-
|
|
|
8
8
|
or campaigns that GMC can answer — including turning results into
|
|
9
9
|
publish-ready branded chart cards.
|
|
10
10
|
metadata:
|
|
11
|
-
version: 0.
|
|
11
|
+
version: 0.17.0
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
# GMC Analysis
|
|
@@ -59,7 +59,9 @@ applies identically. Mechanics map as follows:
|
|
|
59
59
|
its lifecycle/announcement/discount context (`games review-history`) ->
|
|
60
60
|
`game_review_history`; per-title AI-disclosure text and change history
|
|
61
61
|
(`games ai-disclosure`) -> `game_profile` with `ai_disclosure` in
|
|
62
|
-
`sections
|
|
62
|
+
`sections`; Steam follower history (`games follower-history`) ->
|
|
63
|
+
`game_follower_history`; Steam event participation (`games events`) ->
|
|
64
|
+
`game_steam_events` (`include: ["rank_series"]` for the rank series).
|
|
63
65
|
- Per-family collection status ("has GMC actually collected X for this
|
|
64
66
|
game?") has no CLI command yet and is MCP-only: `game_collection_status`
|
|
65
67
|
(see the dedicated section below).
|
|
@@ -413,6 +415,13 @@ Field paths differ by surface: the CLI and REST wrap the payload, so read
|
|
|
413
415
|
the same fields at the top level. The guardrails below name the CLI path;
|
|
414
416
|
drop the `data.` prefix over MCP.
|
|
415
417
|
|
|
418
|
+
Each `data.series[]` point carries `date` and `totalReviews`, and two
|
|
419
|
+
OPTIONAL fields: `ratingPercent` (the positive-review percentage observed on
|
|
420
|
+
that row, 0-100, rounded to one decimal) and `positiveReviews` (the
|
|
421
|
+
positive-review count observed on that row, 0 to `totalReviews`). Absent
|
|
422
|
+
means the row carried no usable value; neither is ever zero-filled, and a
|
|
423
|
+
returned `0` is a real observation.
|
|
424
|
+
|
|
416
425
|
Interpretation guardrails (honesty rules, not suggestions):
|
|
417
426
|
|
|
418
427
|
- **Scale by date, never by array index.** Collection has real gaps, so two
|
|
@@ -439,9 +448,67 @@ Interpretation guardrails (honesty rules, not suggestions):
|
|
|
439
448
|
day from it. `endDate: null` means no end has been observed yet;
|
|
440
449
|
`discountPercent` is the opening observation, not the deepest or final
|
|
441
450
|
depth.
|
|
451
|
+
- **State a rate from the count when it is there.** `ratingPercent` is
|
|
452
|
+
rounded to one decimal, so 1,999 positive of 2,000 reads `100.0`. When
|
|
453
|
+
`positiveReviews` is present, compute the rate as
|
|
454
|
+
`positiveReviews / totalReviews`. When it is absent and `ratingPercent` is
|
|
455
|
+
`100.0` on `totalReviews` of 2,000 or more, treat the 100 as possibly
|
|
456
|
+
rounded (anything from 99.95% up): say "about 100% (rounded)", never "no
|
|
457
|
+
negative reviews". Below 2,000 reviews a `100.0` is exact, because one
|
|
458
|
+
negative review already reads 99.9 or less.
|
|
442
459
|
- **Temporal comparison only.** "Reviews rose in the week after the update"
|
|
443
460
|
is a claim you can make; "the update caused the rise" is not.
|
|
444
461
|
|
|
462
|
+
## Steam follower history
|
|
463
|
+
|
|
464
|
+
`gmc games follower-history <appid> [--range 7d|1m|1y|all] [--from YYYY-MM-DD] [--to YYYY-MM-DD] --json`
|
|
465
|
+
(MCP: `game_follower_history` with `appid` and either `range` or
|
|
466
|
+
`from`/`to`) returns one game's Steam Community group member count on every
|
|
467
|
+
OBSERVED day: `data.series[]` of `date` and `followerCount`, plus
|
|
468
|
+
`data.availability` and `data.seriesMeta`. It takes the appid alone (no
|
|
469
|
+
`--source`). `--range` defaults to `all`, ending today (UTC); `--from` and
|
|
470
|
+
`--to` are inclusive. `--range` cannot be combined with `--from`/`--to`:
|
|
471
|
+
the CLI rejects that as a usage error before any request, so the failure is
|
|
472
|
+
not transient.
|
|
473
|
+
|
|
474
|
+
Interpretation guardrails:
|
|
475
|
+
|
|
476
|
+
- **Observed days only.** Days without an observation are absent and never
|
|
477
|
+
filled or interpolated. Scale by date, never by array index.
|
|
478
|
+
- **Followers are not wishlists.** They are Steam Community game-group
|
|
479
|
+
members, a public proxy for audience interest. Never report them as a
|
|
480
|
+
wishlist count and never as sales.
|
|
481
|
+
- **`not_collected` is a collection state.** `data.availability:
|
|
482
|
+
not_collected` means followers were never collected, never zero followers.
|
|
483
|
+
- **Truncation drops the oldest points.** When `data.seriesMeta.truncated`
|
|
484
|
+
is true the series still ends at the newest observation; say the early
|
|
485
|
+
history is cut.
|
|
486
|
+
|
|
487
|
+
## Steam event participation
|
|
488
|
+
|
|
489
|
+
`gmc games events <appid> [--include rank_series] --json` (MCP:
|
|
490
|
+
`game_steam_events`, with `include: ["rank_series"]`) returns the
|
|
491
|
+
Steam-run events the game took part in (Next Fest and others), newest
|
|
492
|
+
first: each with its observed best rank, shelf exposure, demo review
|
|
493
|
+
series, store follower series and social account deltas. `--include
|
|
494
|
+
rank_series` adds `rankSeries[]` of `{ snapshotUtc, rank, sourceKey }` per
|
|
495
|
+
event, bounded per event and per response (`rankSeriesTruncated` marks an
|
|
496
|
+
event a cap cut).
|
|
497
|
+
|
|
498
|
+
Interpretation guardrails:
|
|
499
|
+
|
|
500
|
+
- **Observed state, not an official ranking.** Ranks and shelf values are
|
|
501
|
+
event-page state captured by the collector. Report them as observed
|
|
502
|
+
positions, never as Steam's own ranking.
|
|
503
|
+
- **Absence is a collection state.** An absent block means that signal was
|
|
504
|
+
not collected for the event. An empty `data.events` means no observed
|
|
505
|
+
participation, not proof the game never took part.
|
|
506
|
+
- **The rank series is raw.** `rankSeries` holds the observations themselves
|
|
507
|
+
(about every 30 minutes during a festival), never resampled; a gap is a
|
|
508
|
+
real collection gap.
|
|
509
|
+
- **Temporal comparison only.** A follower or review change during an event
|
|
510
|
+
is something you observed beside it, never something the event caused.
|
|
511
|
+
|
|
445
512
|
## Per-family collection status
|
|
446
513
|
|
|
447
514
|
`game_collection_status` (MCP-only, no CLI command) returns per-signal-family
|
package/src/args.js
CHANGED
|
@@ -21,6 +21,12 @@ export function parseArgs(args) {
|
|
|
21
21
|
const command = [];
|
|
22
22
|
const positional = [];
|
|
23
23
|
const options = {};
|
|
24
|
+
// Option names given more than once. Later values still win in `options`
|
|
25
|
+
// (every command's parsing is unchanged); a command that cannot take a list
|
|
26
|
+
// reads this to reject the repetition. Non-enumerable so it never shows up
|
|
27
|
+
// as an option.
|
|
28
|
+
const repeated = new Set();
|
|
29
|
+
Object.defineProperty(options, "repeatedOptions", { value: repeated, enumerable: false });
|
|
24
30
|
|
|
25
31
|
for (let index = 0; index < args.length; index += 1) {
|
|
26
32
|
const arg = args[index];
|
|
@@ -57,6 +63,7 @@ export function parseArgs(args) {
|
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
if (inlineValue == null) index += 1;
|
|
66
|
+
if (Object.prototype.hasOwnProperty.call(options, key)) repeated.add(key);
|
|
60
67
|
options[key] = value;
|
|
61
68
|
continue;
|
|
62
69
|
}
|
|
@@ -90,6 +97,11 @@ function isCommandToken(command, token) {
|
|
|
90
97
|
// unknown command `steam` with a stray positional rather than reaching
|
|
91
98
|
// the tag-trends branch.
|
|
92
99
|
"steam",
|
|
100
|
+
// NAK-715. Same trap as `steam` above: a token missing from this list
|
|
101
|
+
// falls through to positional rather than erroring, so `gmc roblox
|
|
102
|
+
// charts` would parse as the unknown command `roblox` with a stray
|
|
103
|
+
// positional instead of reaching the charts branch.
|
|
104
|
+
"roblox",
|
|
93
105
|
"auth",
|
|
94
106
|
"login",
|
|
95
107
|
"logout",
|
|
@@ -109,7 +121,7 @@ function isCommandToken(command, token) {
|
|
|
109
121
|
return ["install", "print", "status"].includes(token);
|
|
110
122
|
}
|
|
111
123
|
if (command[0] === "games") {
|
|
112
|
-
return ["search", "count", "aggregate", "resolve", "benchmark", "detail", "analysis", "compare", "topics", "language-topics", "review-history", "success-report", "more-like-this", "ai-disclosure"].includes(token);
|
|
124
|
+
return ["search", "count", "aggregate", "resolve", "benchmark", "detail", "analysis", "compare", "topics", "language-topics", "review-history", "success-report", "more-like-this", "ai-disclosure", "price-history", "follower-history", "events", "screenshot-labels"].includes(token);
|
|
113
125
|
}
|
|
114
126
|
if (command[0] === "reviews") {
|
|
115
127
|
return token === "patterns";
|
|
@@ -138,6 +150,23 @@ function isCommandToken(command, token) {
|
|
|
138
150
|
if (command[0] === "steam") {
|
|
139
151
|
return ["tag-trends", "trend-stories"].includes(token);
|
|
140
152
|
}
|
|
153
|
+
if (command[0] === "roblox") {
|
|
154
|
+
// NAK-723 adds saved/save/remove. Same trap the read family documents, and
|
|
155
|
+
// worse for the two writes: a token missing from this list falls through to
|
|
156
|
+
// positional rather than erroring, so `gmc roblox save <universeId>` would
|
|
157
|
+
// parse as the unknown command `roblox` and exit 2 with a message naming
|
|
158
|
+
// the id rather than the subcommand.
|
|
159
|
+
return [
|
|
160
|
+
"charts",
|
|
161
|
+
"experiences",
|
|
162
|
+
"experience",
|
|
163
|
+
"history",
|
|
164
|
+
"coverage",
|
|
165
|
+
"saved",
|
|
166
|
+
"save",
|
|
167
|
+
"remove"
|
|
168
|
+
].includes(token);
|
|
169
|
+
}
|
|
141
170
|
return false;
|
|
142
171
|
}
|
|
143
172
|
|
package/src/constants.js
CHANGED
|
@@ -7,7 +7,7 @@ export const EXIT_CONFIG = 3;
|
|
|
7
7
|
export const EXIT_API = 4;
|
|
8
8
|
|
|
9
9
|
export const CLI_PACKAGE_NAME = "@witchpot/gmc";
|
|
10
|
-
export const CLI_VERSION = "0.
|
|
10
|
+
export const CLI_VERSION = "0.11.0";
|
|
11
11
|
|
|
12
12
|
export const DEFAULT_API_BASE_URL = "https://gamemarketcopilot.com";
|
|
13
13
|
export const DEFAULT_CONFIG_PATH = path.join(homedir(), ".config", "gmc", "config.json");
|
|
@@ -63,7 +63,28 @@ export const PROVISIONAL_BETA_COMMANDS = [
|
|
|
63
63
|
"gmc steam tag-trends --view featured|board|group|table|tag --json",
|
|
64
64
|
// NAK-804. New command; starts Provisional per the closed-beta roadmap's
|
|
65
65
|
// default for anything not yet through broader real-data smoke testing.
|
|
66
|
-
"gmc games ai-disclosure <appid> --json"
|
|
66
|
+
"gmc games ai-disclosure <appid> --json",
|
|
67
|
+
// NAK-934. New commands; Provisional per the same closed-beta default.
|
|
68
|
+
"gmc games price-history <appid> --range 7d|1m|1y|all --json",
|
|
69
|
+
"gmc games follower-history <appid> --range 7d|1m|1y|all --json",
|
|
70
|
+
"gmc games events <appid> --include rank_series --json",
|
|
71
|
+
"gmc games screenshot-labels <appid> --json",
|
|
72
|
+
// NAK-715/NAK-725. Enabled in production on 2026-09-22; stays Provisional
|
|
73
|
+
// per the closed-beta roadmap's default until the surface has been through
|
|
74
|
+
// a broader real-data smoke period (the NAK-804 rule), then graduates.
|
|
75
|
+
"gmc roblox charts --country all --sort top-playing-now --json",
|
|
76
|
+
"gmc roblox experiences --country all --limit 50 --json",
|
|
77
|
+
"gmc roblox experience <universeId> --json",
|
|
78
|
+
"gmc roblox history <universeId> --granularity daily|hourly --json",
|
|
79
|
+
"gmc roblox coverage --json",
|
|
80
|
+
// NAK-723. The workspace's saved Roblox experiences, mapping the REST
|
|
81
|
+
// surface that shipped with the pages. Provisional for the SAME reason the
|
|
82
|
+
// five read commands above are — they are one surface, gated by one flag,
|
|
83
|
+
// and graduating the saved commands ahead of the reads they resolve against
|
|
84
|
+
// would promise a stability the reads have not earned yet.
|
|
85
|
+
"gmc roblox saved --country all --json",
|
|
86
|
+
"gmc roblox save <universeId> --json",
|
|
87
|
+
"gmc roblox remove <universeId> --json"
|
|
67
88
|
];
|
|
68
89
|
|
|
69
90
|
export const COMPARE_FOCUSES = ["overview", "market", "reviews", "marketing", "audience", "showcases"];
|
package/src/help.js
CHANGED
|
@@ -52,6 +52,10 @@ Usage:
|
|
|
52
52
|
gmc games review-history --source steam <externalId> --json
|
|
53
53
|
gmc games more-like-this --source steam <externalId> --country <country> --json
|
|
54
54
|
gmc games ai-disclosure <appid> --json
|
|
55
|
+
gmc games price-history <appid> --range 1y --json
|
|
56
|
+
gmc games follower-history <appid> --range 1y --json
|
|
57
|
+
gmc games events <appid> --json
|
|
58
|
+
gmc games screenshot-labels <appid> --json
|
|
55
59
|
gmc reviews patterns --source steam --tags <tag_slug[,tag_slug...]> --reviews-min <number> --json
|
|
56
60
|
gmc reviews patterns --source steam --tags <tag_slug[,tag_slug...]> --sample-size <number> --preset summary --json
|
|
57
61
|
gmc marketing patterns --source steam --tags <tag_slug[,tag_slug...]> --reviews-min <number> --json
|
|
@@ -70,6 +74,14 @@ Usage:
|
|
|
70
74
|
gmc steam tag-trends --view featured --json
|
|
71
75
|
gmc steam tag-trends --view table --sort release_share_delta --order desc --json
|
|
72
76
|
gmc steam tag-trends --view tag --tag <tag_slug> --json
|
|
77
|
+
gmc roblox charts --country all --sort top-playing-now --json
|
|
78
|
+
gmc roblox experiences --country all --limit 50 --json
|
|
79
|
+
gmc roblox experience <universeId> --json
|
|
80
|
+
gmc roblox history <universeId> --granularity daily --json
|
|
81
|
+
gmc roblox coverage --json
|
|
82
|
+
gmc roblox saved --country all --json
|
|
83
|
+
gmc roblox save <universeId> --json
|
|
84
|
+
gmc roblox remove <universeId> --json
|
|
73
85
|
|
|
74
86
|
Tag taxonomy:
|
|
75
87
|
gmc tags --json List the full tag dictionary (slug, name).
|
|
@@ -177,6 +189,77 @@ AI-disclosure text and history:
|
|
|
177
189
|
Example:
|
|
178
190
|
gmc games ai-disclosure 730 --json
|
|
179
191
|
|
|
192
|
+
Observed price history:
|
|
193
|
+
gmc games price-history <appid> [--range <preset>] [--from <date>] [--to <date>] --json
|
|
194
|
+
One game's Steam storefront price on
|
|
195
|
+
every OBSERVED day: priceInitial,
|
|
196
|
+
priceFinal (major currency units),
|
|
197
|
+
discountPercent, currency, storeCountry.
|
|
198
|
+
--range <preset> 7d | 1m | 1y | all, ending today (UTC).
|
|
199
|
+
Defaults to all.
|
|
200
|
+
--from <YYYY-MM-DD> Inclusive window start.
|
|
201
|
+
--to <YYYY-MM-DD> Inclusive window end; defaults to today.
|
|
202
|
+
--range cannot be combined with --from
|
|
203
|
+
or --to.
|
|
204
|
+
|
|
205
|
+
Collection is not daily for every game, so days without an observation are
|
|
206
|
+
absent and never filled; scale by date, never by array index. priceFinal 0
|
|
207
|
+
with a null priceInitial is a free game; a null priceFinal means no price
|
|
208
|
+
was shown that day. data.availability not_collected means no snapshot was
|
|
209
|
+
ever collected, never a free game. A price says nothing about units sold or
|
|
210
|
+
revenue.
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
gmc games price-history 730 --range 1y --json
|
|
214
|
+
|
|
215
|
+
Steam follower history:
|
|
216
|
+
gmc games follower-history <appid> [--range <preset>] [--from <date>] [--to <date>] --json
|
|
217
|
+
Steam Community game-group members on
|
|
218
|
+
every OBSERVED day: date, followerCount.
|
|
219
|
+
--range <preset> 7d | 1m | 1y | all, ending today (UTC).
|
|
220
|
+
Defaults to all. Cannot be combined with
|
|
221
|
+
--from or --to.
|
|
222
|
+
|
|
223
|
+
Followers are a public proxy for audience interest, never a wishlist count
|
|
224
|
+
and never sales. Days without an observation are absent and never filled.
|
|
225
|
+
data.availability not_collected means followers were never collected.
|
|
226
|
+
|
|
227
|
+
Example:
|
|
228
|
+
gmc games follower-history 730 --range 1y --json
|
|
229
|
+
|
|
230
|
+
Steam event participation:
|
|
231
|
+
gmc games events <appid> [--include rank_series] --json
|
|
232
|
+
Steam-run events the game took part in
|
|
233
|
+
(Next Fest and others), newest first, with
|
|
234
|
+
observed best rank, shelf exposure, demo
|
|
235
|
+
reviews, store followers and social
|
|
236
|
+
deltas.
|
|
237
|
+
--include rank_series Attach the observed ranking series per
|
|
238
|
+
event.
|
|
239
|
+
|
|
240
|
+
Ranks and shelves are observed event-page state, never an official Steam
|
|
241
|
+
ranking. An absent block means the signal was not collected.
|
|
242
|
+
|
|
243
|
+
Example:
|
|
244
|
+
gmc games events 730 --include rank_series --json
|
|
245
|
+
|
|
246
|
+
Screenshot labels:
|
|
247
|
+
gmc games screenshot-labels <appid> --json
|
|
248
|
+
Visual labels for each Steam store
|
|
249
|
+
screenshot of one game (screen type,
|
|
250
|
+
camera, art style, lighting, colour, a
|
|
251
|
+
one-line observation), in store order.
|
|
252
|
+
There are no parameters beyond the appid.
|
|
253
|
+
|
|
254
|
+
The labels are MODEL OUTPUT (data.labels[].labeler), not Steam data and not
|
|
255
|
+
a human review, and they can be wrong. One label set is returned per
|
|
256
|
+
screenshot: the most recently labelled taxonomy version. data.availability
|
|
257
|
+
not_labelled means nothing has been labelled yet, never that the game has
|
|
258
|
+
no screenshots.
|
|
259
|
+
|
|
260
|
+
Example:
|
|
261
|
+
gmc games screenshot-labels 730 --json
|
|
262
|
+
|
|
180
263
|
Steam tag trends:
|
|
181
264
|
gmc steam tag-trends --view <view> --json
|
|
182
265
|
Tag supply trends over one frozen monthly
|
|
@@ -232,6 +315,103 @@ Steam tag trends:
|
|
|
232
315
|
gmc steam tag-trends --view table --sort current_share --limit 50 --json
|
|
233
316
|
gmc steam tag-trends --view tag --tag cozy --json
|
|
234
317
|
|
|
318
|
+
Roblox:
|
|
319
|
+
gmc roblox charts --json One ranking cohort's stored chart
|
|
320
|
+
snapshot as of a cutoff.
|
|
321
|
+
gmc roblox experiences --json Browse the tracked cohort with coverage.
|
|
322
|
+
gmc roblox experience <universeId> --json
|
|
323
|
+
One experience: metadata, metrics,
|
|
324
|
+
artwork.
|
|
325
|
+
gmc roblox history <universeId> --json
|
|
326
|
+
A bounded window of stored metric
|
|
327
|
+
observations.
|
|
328
|
+
gmc roblox coverage --json What the surface has, per dimension.
|
|
329
|
+
gmc roblox saved --json This WORKSPACE's saved experiences,
|
|
330
|
+
resolved for one cohort and cutoff.
|
|
331
|
+
gmc roblox save <universeId> --json
|
|
332
|
+
Save one experience to the workspace.
|
|
333
|
+
gmc roblox remove <universeId> --json
|
|
334
|
+
Remove one from the workspace's saved
|
|
335
|
+
set.
|
|
336
|
+
--country <country> all | US | JP. Selects which CHART was
|
|
337
|
+
read, never a regional metric scope.
|
|
338
|
+
--device <device> computer.
|
|
339
|
+
--sort <sort> top-playing-now | top-trending |
|
|
340
|
+
top-revisited | fun-with-friends.
|
|
341
|
+
--as-of <instant> ISO 8601 cutoff; defaults to now. No
|
|
342
|
+
observation after it is considered.
|
|
343
|
+
--eligibility-days <number> experiences and saved, 1-30 (default 7).
|
|
344
|
+
How far back a chart appearance counts.
|
|
345
|
+
--min-player-count <number> experiences only, on the resolved row.
|
|
346
|
+
--max-rank <number> experiences only, on the resolved row.
|
|
347
|
+
--genre <genre> experiences only, exact match.
|
|
348
|
+
--creator-type <type> experiences only, e.g. Group or User.
|
|
349
|
+
--detail <enriched|chart_only> experiences only. Omitted returns both.
|
|
350
|
+
--order-by <key> rank | player_count | last_chart_seen |
|
|
351
|
+
universe_id (experiences only).
|
|
352
|
+
--order <asc|desc> experiences only.
|
|
353
|
+
--granularity <hourly|daily> history only. Defaults to daily.
|
|
354
|
+
--from <instant> --to <instant> history only. Hourly windows span at most
|
|
355
|
+
90 days, daily at most 365.
|
|
356
|
+
--limit <number> charts 1-250, experiences 1-200.
|
|
357
|
+
--offset <number> charts only.
|
|
358
|
+
--cursor <cursor> experiences only. Continuation token.
|
|
359
|
+
|
|
360
|
+
A Roblox universe id is NOT a Steam appid. Ids travel as decimal strings
|
|
361
|
+
because they exceed the range a JSON number represents exactly, and nothing
|
|
362
|
+
here merges a Roblox experience with a Steam game.
|
|
363
|
+
|
|
364
|
+
Unknown is never zero. A null player count was not measured. price.known
|
|
365
|
+
false means the price is UNKNOWN, not that the experience is free. Zero
|
|
366
|
+
recorded votes gives no positive percentage at all, rather than 0%.
|
|
367
|
+
|
|
368
|
+
Nothing is fabricated to fill a gap. An absent cohort answers availability
|
|
369
|
+
"never_observed" with an empty entries list — a measurement about
|
|
370
|
+
collection, not an error and never another sort's ranking. History reports
|
|
371
|
+
unsampled periods in data.gaps and interpolates nothing, so a gap is not a
|
|
372
|
+
zero. Daily sampledPlayingMin/Max are the extremes of the SAMPLES, not the
|
|
373
|
+
day's peak; compare CCU only across points whose comparable is true.
|
|
374
|
+
Observed counter decreases are reported as-is and are not resets.
|
|
375
|
+
|
|
376
|
+
This is a curated tracked cohort, never all of Roblox. Quote
|
|
377
|
+
coverage.trackedUniverses as the denominator for any share.
|
|
378
|
+
|
|
379
|
+
While the surface is switched off the commands answer 404
|
|
380
|
+
ROBLOX_UNAVAILABLE. That is the documented not-available state, not an
|
|
381
|
+
outage and not worth retrying on a schedule.
|
|
382
|
+
|
|
383
|
+
Saved experiences (workspace-scoped, unlike the five reads above):
|
|
384
|
+
saved, save and remove carry one workspace's own data and need a
|
|
385
|
+
credential — a Game Market Copilot API key or a CLI credential. Without
|
|
386
|
+
one they answer 401, which is not an empty saved list.
|
|
387
|
+
|
|
388
|
+
Saved membership is scope-independent. --country, --device, --sort,
|
|
389
|
+
--as-of and --eligibility-days apply to "saved" alone and decide which of
|
|
390
|
+
three states each row is in, never which rows are listed: in_scope
|
|
391
|
+
carries the observation that scope recorded, out_of_scope is saved and
|
|
392
|
+
tracked with NO reading there (never back-filled from another cohort or
|
|
393
|
+
from after the cutoff, and never reportable as zero or as removed), and
|
|
394
|
+
not_tracked has nothing recorded at all. The two writes take no flags.
|
|
395
|
+
|
|
396
|
+
Both writes are idempotent and report the state that NOW holds, so
|
|
397
|
+
changed: false is success and must not be retried as a failure. Saving
|
|
398
|
+
records interest in a universe id — it never triggers collection, however
|
|
399
|
+
often it is called, and it refuses an id the tracked cohort has never
|
|
400
|
+
recorded (404 NOT_FOUND, a different fact from ROBLOX_UNAVAILABLE).
|
|
401
|
+
"remove" removes the id from this workspace's saved set only; it deletes
|
|
402
|
+
no data and removing something not saved is changed: false, not a 404.
|
|
403
|
+
The set is capped per workspace (savedLimit in every response); past it,
|
|
404
|
+
saving answers 409 CONFLICT.
|
|
405
|
+
|
|
406
|
+
Examples:
|
|
407
|
+
gmc roblox coverage --json
|
|
408
|
+
gmc roblox charts --country JP --sort top-trending --json
|
|
409
|
+
gmc roblox experiences --min-player-count 1000 --order-by player_count --json
|
|
410
|
+
gmc roblox history <universeId> --granularity hourly --json
|
|
411
|
+
gmc roblox saved --country JP --sort top-trending --json
|
|
412
|
+
gmc roblox save <universeId> --json
|
|
413
|
+
gmc roblox remove <universeId> --json
|
|
414
|
+
|
|
235
415
|
Binary aliases:
|
|
236
416
|
gmcp and game-market-copilot run the same CLI. Use them when a shell
|
|
237
417
|
alias shadows gmc (Oh My Zsh's git plugin defines gmc as
|