@witchpot/gmc 0.1.7 → 0.1.9
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 +16 -0
- package/README.md +17 -12
- package/package.json +3 -1
- package/scripts/smoke-beta.mjs +35 -9
- package/skill/gmc-analysis/CHANGELOG.md +15 -0
- package/skill/gmc-analysis/SKILL.md +11 -3
- package/skill/gmc-analysis/references/pitfalls.md +26 -2
- package/skill/gmc-analysis/references/quota.md +7 -1
- package/skill/gmc-analysis/references/recipes.md +16 -1
- package/src/args.js +1 -1
- package/src/config.js +5 -5
- package/src/constants.js +1 -1
- package/src/help.js +4 -2
- package/src/main.js +12 -0
- package/src/requests.js +17 -5
- package/src/requests.test.js +43 -6
- package/src/skill.js +2 -1
- package/src/telemetry.js +341 -0
- package/src/telemetry.test.js +121 -0
- package/src/workflows.js +81 -27
- package/src/workflows.test.js +28 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the Game Market Copilot CLI will be recorded here.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 0.1.9 - 2026-07-03
|
|
8
|
+
|
|
9
|
+
- Clarified packaged Agent Skill docs so Claude Code uses `gmc skill install`
|
|
10
|
+
and Codex/AGENTS.md-based agents use
|
|
11
|
+
`gmc skill print --format agents-md >> AGENTS.md`.
|
|
12
|
+
|
|
13
|
+
## 0.1.8 - 2026-06-12
|
|
14
|
+
|
|
15
|
+
- Updated the bundled `gmc-analysis` skill to v0.2.0 with the learnings from
|
|
16
|
+
the first price-band field test: camelCase-query to kebab-case-flag
|
|
17
|
+
mapping note, zero-review rating artifact pitfall, discounted snapshot
|
|
18
|
+
price pitfall, price-question routing to explicit-bucket aggregates, and
|
|
19
|
+
the evidence-pack quota location exception.
|
|
20
|
+
|
|
5
21
|
## 0.1.7 - 2026-06-12
|
|
6
22
|
|
|
7
23
|
- Bundled the `gmc-analysis` Agent Skill (v0.1.0): analysis recipes, pivot
|
package/README.md
CHANGED
|
@@ -6,17 +6,17 @@ showcases.
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
-
Install the
|
|
9
|
+
Install the CLI:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm install -g @witchpot/gmc
|
|
12
|
+
npm install -g @witchpot/gmc
|
|
13
13
|
gmc --version
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
Or run it without a global install:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npx @witchpot/gmc
|
|
19
|
+
npx @witchpot/gmc --version
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
The package installs three equivalent binaries: `gmc`, `gmcp`, and
|
|
@@ -64,8 +64,10 @@ a non-secret prefix.
|
|
|
64
64
|
## Agent Skill
|
|
65
65
|
|
|
66
66
|
The package bundles the `gmc-analysis` Agent Skill — analysis recipes, pivot
|
|
67
|
-
heuristics, quota etiquette, and honesty rules for coding agents.
|
|
68
|
-
|
|
67
|
+
heuristics, quota etiquette, and honesty rules for coding agents. The core
|
|
68
|
+
analysis guidance is shared, but the registration step depends on the agent.
|
|
69
|
+
|
|
70
|
+
For Claude Code, install the packaged skill directory:
|
|
69
71
|
|
|
70
72
|
```bash
|
|
71
73
|
gmc skill install # writes .claude/skills/gmc-analysis (project)
|
|
@@ -73,13 +75,16 @@ gmc skill install --global # or ~/.claude/skills/gmc-analysis
|
|
|
73
75
|
gmc skill status --json # packaged vs installed versions
|
|
74
76
|
```
|
|
75
77
|
|
|
76
|
-
For Codex and other AGENTS.md-based agents, append the self-contained
|
|
77
|
-
|
|
78
|
+
For Codex and other AGENTS.md-based agents, append the self-contained edition
|
|
79
|
+
(references inlined) to the repo brief the agent reads:
|
|
78
80
|
|
|
79
81
|
```bash
|
|
80
82
|
gmc skill print --format agents-md >> AGENTS.md
|
|
81
83
|
```
|
|
82
84
|
|
|
85
|
+
If `AGENTS.md` already contains a generated `gmc-analysis` section, update that
|
|
86
|
+
section instead of appending a duplicate.
|
|
87
|
+
|
|
83
88
|
Re-run install/print after `npm update` to pick up newer skill versions.
|
|
84
89
|
|
|
85
90
|
## First Queries
|
|
@@ -87,11 +92,11 @@ Re-run install/print after `npm update` to pick up newer skill versions.
|
|
|
87
92
|
```bash
|
|
88
93
|
gmc schema --json
|
|
89
94
|
gmc update --json
|
|
90
|
-
gmc games
|
|
91
|
-
gmc games
|
|
92
|
-
gmc
|
|
93
|
-
gmc
|
|
94
|
-
gmc showcases
|
|
95
|
+
gmc games search --source steam --release-year 2025 --reviews-min 1000 --fields title,appid,rating,reviews --json
|
|
96
|
+
gmc games aggregate --source steam --release-year 2025 --metric count,median_reviews --group-by release_year --reviews-min 1000 --json
|
|
97
|
+
gmc games benchmark --source steam --tags <tag_slug[,tag_slug...]> --reviews-min 1000 --preset summary --json
|
|
98
|
+
gmc reviews patterns --source steam --tags <tag_slug[,tag_slug...]> --sample-size 15 --preset summary --json
|
|
99
|
+
gmc showcases patterns --source steam --tags <tag_slug[,tag_slug...]> --json
|
|
95
100
|
```
|
|
96
101
|
|
|
97
102
|
Preserve `meta.requestId`, `meta.entitlements`, `warnings`, and `pagination`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@witchpot/gmc",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Command line tools for Game Market Copilot market research.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
"smoke:beta": "node scripts/smoke-beta.mjs",
|
|
46
46
|
"pack:check": "node scripts/smoke-pack.mjs --dry-run",
|
|
47
47
|
"smoke:pack": "node scripts/smoke-pack.mjs",
|
|
48
|
+
"publish:latest:dry-run": "npm publish --access public --dry-run --cache /tmp/gmc-npm-cache",
|
|
49
|
+
"publish:latest": "npm publish --access public --cache /tmp/gmc-npm-cache",
|
|
48
50
|
"publish:beta:dry-run": "npm publish --tag beta --access public --dry-run --cache /tmp/gmc-npm-cache",
|
|
49
51
|
"publish:beta": "npm publish --tag beta --access public --cache /tmp/gmc-npm-cache"
|
|
50
52
|
}
|
package/scripts/smoke-beta.mjs
CHANGED
|
@@ -33,14 +33,40 @@ const smokeCommands = [
|
|
|
33
33
|
validate: (payload) => requireSuccessEnvelope(payload, ["data.version", "data.cliCommands"])
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
|
-
name: "
|
|
37
|
-
args: [
|
|
38
|
-
|
|
36
|
+
name: "search recent reviewed games",
|
|
37
|
+
args: [
|
|
38
|
+
"games",
|
|
39
|
+
"search",
|
|
40
|
+
"--source",
|
|
41
|
+
"steam",
|
|
42
|
+
"--release-year",
|
|
43
|
+
"2025",
|
|
44
|
+
"--reviews-min",
|
|
45
|
+
"1000",
|
|
46
|
+
"--limit",
|
|
47
|
+
"5",
|
|
48
|
+
"--json"
|
|
49
|
+
],
|
|
50
|
+
validate: (payload) => requireSuccessEnvelope(payload, ["data"])
|
|
39
51
|
},
|
|
40
52
|
{
|
|
41
|
-
name: "
|
|
42
|
-
args: [
|
|
43
|
-
|
|
53
|
+
name: "aggregate recent reviewed games",
|
|
54
|
+
args: [
|
|
55
|
+
"games",
|
|
56
|
+
"aggregate",
|
|
57
|
+
"--source",
|
|
58
|
+
"steam",
|
|
59
|
+
"--release-year",
|
|
60
|
+
"2025",
|
|
61
|
+
"--metric",
|
|
62
|
+
"count,median_reviews",
|
|
63
|
+
"--group-by",
|
|
64
|
+
"release_year",
|
|
65
|
+
"--reviews-min",
|
|
66
|
+
"1000",
|
|
67
|
+
"--json"
|
|
68
|
+
],
|
|
69
|
+
validate: (payload) => requireSuccessEnvelope(payload, ["data"])
|
|
44
70
|
},
|
|
45
71
|
{
|
|
46
72
|
name: "benchmark city builder automation",
|
|
@@ -60,9 +86,9 @@ const smokeCommands = [
|
|
|
60
86
|
validate: (payload) => requireComposedEnvelope(payload, ["data.summary"])
|
|
61
87
|
},
|
|
62
88
|
{
|
|
63
|
-
name: "showcase
|
|
64
|
-
args: ["showcases", "
|
|
65
|
-
validate: (payload) => requireComposedEnvelope(payload, ["data.
|
|
89
|
+
name: "showcase patterns by tag",
|
|
90
|
+
args: ["showcases", "patterns", "--source", "steam", "--tags", "city_builder,automation", "--json"],
|
|
91
|
+
validate: (payload) => requireComposedEnvelope(payload, ["data.summary"])
|
|
66
92
|
}
|
|
67
93
|
];
|
|
68
94
|
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# gmc-analysis skill changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 - 2026-06-12
|
|
4
|
+
|
|
5
|
+
- Folded in the five learnings from the first field test (2025 city
|
|
6
|
+
builders, price-band vs rating/review analysis; 7/7 rubric pass):
|
|
7
|
+
- setup: documented the camelCase-query -> kebab-case-flag mapping and
|
|
8
|
+
that per-command `--help` is currently generic;
|
|
9
|
+
- new pitfall P5: zero-review games aggregate as rating 0 — pair rating
|
|
10
|
+
metrics with `--reviews-min`, derive per-bucket hit rates from counts;
|
|
11
|
+
- new pitfall P6: snapshot prices include active discounts — check the
|
|
12
|
+
`discount` field before price-band conclusions;
|
|
13
|
+
- recipe R4: price/distribution questions route to aggregate with
|
|
14
|
+
explicit buckets first; `cohort pricing` is page-bounded texture only;
|
|
15
|
+
- quota: composed evidence packs carry quota under
|
|
16
|
+
`meta.upstreamRequests[].entitlements`, not at the top level.
|
|
17
|
+
|
|
3
18
|
## 0.1.0 - 2026-06-12
|
|
4
19
|
|
|
5
20
|
- Initial release, seeded from the 2026-06-12 field test (roguelike cohort
|
|
@@ -7,7 +7,7 @@ description: >-
|
|
|
7
7
|
about Steam games, reviews, creators, showcases, or campaigns that gmc can
|
|
8
8
|
answer.
|
|
9
9
|
metadata:
|
|
10
|
-
version: 0.
|
|
10
|
+
version: 0.2.0
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# GMC Analysis
|
|
@@ -26,8 +26,11 @@ gmc auth status --json # anonymous => redacted previews + tiny quota
|
|
|
26
26
|
gmc schema --json # the authoritative command/endpoint reference
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
Never guess flags: `gmc schema --json`
|
|
30
|
-
|
|
29
|
+
Never guess flags: `gmc schema --json` is the source of truth for commands
|
|
30
|
+
and parameters. Endpoint query params are camelCase and the matching CLI
|
|
31
|
+
flag is the kebab-case equivalent (`priceMin` -> `--price-min`,
|
|
32
|
+
`reviewsGt` -> `--reviews-gt`). Per-command `--help` currently prints one
|
|
33
|
+
generic usage list, so trust the schema. Always pass `--json`.
|
|
31
34
|
|
|
32
35
|
## Core workflow for cohort questions
|
|
33
36
|
|
|
@@ -50,6 +53,11 @@ For cohorts beyond one page, and for the full-cohort map-reduce procedure
|
|
|
50
53
|
(pagination, bounded concurrency, `--fields` projection, subagent chunking),
|
|
51
54
|
follow the recipes reference.
|
|
52
55
|
|
|
56
|
+
Search commands are optimized for row retrieval. Do not expect
|
|
57
|
+
`pagination.total` from `gmc games search` or CLI-composed evidence packs
|
|
58
|
+
unless you explicitly pass `--include-total`; for count-only questions use
|
|
59
|
+
`gmc games count` instead.
|
|
60
|
+
|
|
53
61
|
## Hard rules
|
|
54
62
|
|
|
55
63
|
- **Missing data is not absent sentiment.** Report `cohort.usableGames`,
|
|
@@ -42,9 +42,33 @@ the quota is tiny, which can read as "the API has no data".
|
|
|
42
42
|
**Rule**: run `gmc auth status --json` first; if `authMode` is `anonymous`,
|
|
43
43
|
say so in your answer and scope expectations to public preview data.
|
|
44
44
|
|
|
45
|
-
## P5.
|
|
45
|
+
## P5. Zero-review games carry rating 0 in aggregates (2026-06)
|
|
46
|
+
|
|
47
|
+
Unconditional `avg_rating` / `median_rating` in low-traction segments (cheap
|
|
48
|
+
or free price buckets) is crushed by zero-review games aggregating as
|
|
49
|
+
rating 0 (observed: a free bucket with avg_rating 11.1). This misreads as
|
|
50
|
+
"cheap games are hated" when it actually means "most cheap games have no
|
|
51
|
+
reviews".
|
|
52
|
+
|
|
53
|
+
**Pivot**: pair rating metrics with `--reviews-min` (e.g. 10). For "share of
|
|
54
|
+
well-rated games per bucket", run `--metric count` twice over the same
|
|
55
|
+
bucket spec — once unconditional, once with `--reviews-min 10
|
|
56
|
+
--rating-min 80` — and divide.
|
|
57
|
+
|
|
58
|
+
## P6. Snapshot prices include active discounts (2026-06)
|
|
59
|
+
|
|
60
|
+
`price` is the current store price, not the launch/list price; a discounted
|
|
61
|
+
title lands in a lower price bucket (observed: a $34.99 list price counted
|
|
62
|
+
as $23.44 at -33%). Game rows carry a `discount` field.
|
|
63
|
+
|
|
64
|
+
**Rule**: when price-band conclusions matter, check `discount` on the
|
|
65
|
+
exemplar rows and caveat bucket assignments, especially during sale seasons.
|
|
66
|
+
|
|
67
|
+
## P7. Don't paginate by brute force
|
|
46
68
|
|
|
47
69
|
List endpoints cap at `limit=100`. For "how many" questions use
|
|
48
70
|
`gmc games count`; for distributions use `gmc games aggregate` — both are
|
|
49
71
|
single-request server aggregates. Reserve pagination loops for when you
|
|
50
|
-
actually need the rows (recipe R2).
|
|
72
|
+
actually need the rows (recipe R2). `gmc games search` is optimized for rows
|
|
73
|
+
and may omit `pagination.total`; pass `--include-total` only when a row page
|
|
74
|
+
also needs an exact count.
|
|
@@ -9,7 +9,10 @@ indicative — the response metadata is the source of truth.
|
|
|
9
9
|
|
|
10
10
|
1. **Read before you spend.** Every bearer response carries
|
|
11
11
|
`meta.entitlements.quota` = `{ used, limit }`. Check it at the start of a
|
|
12
|
-
session and after each fan-out.
|
|
12
|
+
session and after each fan-out. Exception: CLI-composed evidence packs
|
|
13
|
+
(`cohort ...`, `reviews patterns`, `games benchmark`, ...) expose quota
|
|
14
|
+
per upstream call instead — read the last entry of
|
|
15
|
+
`meta.upstreamRequests[].entitlements.quota`.
|
|
13
16
|
2. **Estimate fan-out cost first.** A cohort sweep costs roughly
|
|
14
17
|
`search pages + (1 per game per section call)`. A 114-game cohort with one
|
|
15
18
|
analysis call each ~= 120 requests. Compare against remaining quota and
|
|
@@ -22,3 +25,6 @@ indicative — the response metadata is the source of truth.
|
|
|
22
25
|
midnight), and tell the user how much of the analysis completed.
|
|
23
26
|
5. **Prefer server aggregates** (`count`, `aggregate`) over row pagination —
|
|
24
27
|
they answer sizing questions in one request.
|
|
28
|
+
6. **Keep totals explicit.** Search commands prioritize fast row pages and
|
|
29
|
+
exact totals are opt-in (`--include-total`). Use `games count` for
|
|
30
|
+
denominators so row collection stays cheap.
|
|
@@ -36,7 +36,9 @@ Cost: ~(pages + N) requests. Check quota headroom first (see quota.md).
|
|
|
36
36
|
1. **Collect the cohort**: page `gmc games search <filters> --coverage
|
|
37
37
|
full,partial --sort reviews_desc --limit 100 --offset <0,100,...>
|
|
38
38
|
--fields appid,title,reviews --json` until `pagination.nextOffset` is
|
|
39
|
-
null.
|
|
39
|
+
null. Run `gmc games count` first when you need the denominator; add
|
|
40
|
+
`--include-total` to search only when the exact total must travel with the
|
|
41
|
+
row page.
|
|
40
42
|
2. **Fan out** `gmc games analysis --source steam <appid> --sections
|
|
41
43
|
reviews --json` per game with bounded concurrency (<= 8 parallel
|
|
42
44
|
processes). Write each response to a temp file; tolerate individual
|
|
@@ -80,6 +82,19 @@ gmc games aggregate --source steam --metric count --group-by coverage <filters>
|
|
|
80
82
|
`data.basis = server_aggregate` means market-wide for the filter set (within
|
|
81
83
|
GMC source coverage) — unlike evidence packs, these are not page-bounded.
|
|
82
84
|
|
|
85
|
+
**Price/distribution questions belong here, not in `cohort pricing`.** Run
|
|
86
|
+
the aggregate with explicit buckets first (e.g.
|
|
87
|
+
`--bucket price:0,5,10,15,20,25,30,40,60`); `gmc cohort pricing` is
|
|
88
|
+
returned-page (top ~20) texture, useful only as a supplement and must be
|
|
89
|
+
labeled as page-bounded evidence.
|
|
90
|
+
|
|
91
|
+
Per-bucket "hit rate" pattern (one request per condition): pair the same
|
|
92
|
+
bucket spec with shared filters — `--reviews-min 500 --metric count` gives
|
|
93
|
+
reach per price band; `--reviews-min 10 --rating-min 80 --metric count`
|
|
94
|
+
gives the well-rated share. Divide by the unconditional bucket counts.
|
|
95
|
+
Never read unconditional rating aggregates in low-traction buckets
|
|
96
|
+
(pitfall P5).
|
|
97
|
+
|
|
83
98
|
## Output contract for every analysis
|
|
84
99
|
|
|
85
100
|
Always include in your final answer: the cohort definition and size, how many
|
package/src/args.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EXIT_USAGE } from "./constants.js";
|
|
2
2
|
import { writeJsonError } from "./output.js";
|
|
3
3
|
|
|
4
|
-
export const BOOLEAN_OPTIONS = new Set(["json", "help", "version", "no-open", "global"]);
|
|
4
|
+
export const BOOLEAN_OPTIONS = new Set(["json", "help", "version", "no-open", "global", "include-total"]);
|
|
5
5
|
|
|
6
6
|
export function parseArgs(args) {
|
|
7
7
|
const command = [];
|
package/src/config.js
CHANGED
|
@@ -187,15 +187,15 @@ export async function handleLocalCommand(command, options, positional, config) {
|
|
|
187
187
|
status: "manual_update_required",
|
|
188
188
|
updateCheck: {
|
|
189
189
|
available: null,
|
|
190
|
-
reason: "The CLI does not query the npm registry yet. Use
|
|
190
|
+
reason: "The CLI does not query the npm registry yet. Use npm install or npx to run the current public package."
|
|
191
191
|
},
|
|
192
192
|
commands: {
|
|
193
|
-
npmGlobal: `npm install -g ${
|
|
194
|
-
npmExec: `npx ${
|
|
193
|
+
npmGlobal: `npm install -g ${CLI_PACKAGE_NAME}`,
|
|
194
|
+
npmExec: `npx ${CLI_PACKAGE_NAME} --version`
|
|
195
195
|
},
|
|
196
196
|
notes: [
|
|
197
|
-
"This command is intentionally non-mutating
|
|
198
|
-
"A future gmc update --apply command can run the package-manager update after
|
|
197
|
+
"This command is intentionally non-mutating.",
|
|
198
|
+
"A future gmc update --apply command can run the package-manager update after registry checks are implemented."
|
|
199
199
|
]
|
|
200
200
|
}
|
|
201
201
|
};
|
package/src/constants.js
CHANGED
|
@@ -8,7 +8,7 @@ export const EXIT_API = 4;
|
|
|
8
8
|
|
|
9
9
|
export const CLI_PACKAGE_NAME = "@witchpot/gmc";
|
|
10
10
|
export const CLI_BETA_PACKAGE_NAME = "@witchpot/gmc";
|
|
11
|
-
export const CLI_VERSION = "0.1.
|
|
11
|
+
export const CLI_VERSION = "0.1.9";
|
|
12
12
|
|
|
13
13
|
export const DEFAULT_API_BASE_URL = "https://gamemarketcopilot.com";
|
|
14
14
|
export const DEFAULT_CONFIG_PATH = path.join(homedir(), ".config", "gmc", "config.json");
|
package/src/help.js
CHANGED
|
@@ -56,10 +56,10 @@ Binary aliases:
|
|
|
56
56
|
alias shadows gmc (Oh My Zsh's git plugin defines gmc as
|
|
57
57
|
'git merge --continue').
|
|
58
58
|
|
|
59
|
-
Supported
|
|
59
|
+
Supported commands:
|
|
60
60
|
${SUPPORTED_BETA_COMMANDS.map((command) => ` ${command}`).join("\n")}
|
|
61
61
|
|
|
62
|
-
Provisional
|
|
62
|
+
Provisional commands:
|
|
63
63
|
${PROVISIONAL_BETA_COMMANDS.map((command) => ` ${command}`).join("\n")}
|
|
64
64
|
|
|
65
65
|
Environment:
|
|
@@ -102,6 +102,8 @@ Sorting:
|
|
|
102
102
|
Pagination:
|
|
103
103
|
--limit <number> 1-100, default 20.
|
|
104
104
|
--offset <number> Offset for the next page.
|
|
105
|
+
--include-total Also fetch an exact total. Prefer games count
|
|
106
|
+
when the user only asks how many games match.
|
|
105
107
|
|
|
106
108
|
Output:
|
|
107
109
|
--fields <list> Optional comma-separated field list.
|
package/src/main.js
CHANGED
|
@@ -12,6 +12,7 @@ import { printGamesSearchHelp, printHelp } from "./help.js";
|
|
|
12
12
|
import { writeJsonError } from "./output.js";
|
|
13
13
|
import { applyOutputProjection } from "./projection.js";
|
|
14
14
|
import { buildRequest } from "./requests.js";
|
|
15
|
+
import { trackCliCommandExecuted } from "./telemetry.js";
|
|
15
16
|
import { executeRequestPlan } from "./workflows.js";
|
|
16
17
|
|
|
17
18
|
export async function main(argv = process.argv.slice(2)) {
|
|
@@ -78,6 +79,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
78
79
|
process.exit(EXIT_CONFIG);
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
const startedAt = Date.now();
|
|
81
83
|
const response = await executeRequestPlan(baseUrl, request, { apiKey });
|
|
82
84
|
const payload = matches(command, ["auth", "status"])
|
|
83
85
|
? enrichAuthStatusOutput(response, config)
|
|
@@ -89,6 +91,16 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
89
91
|
});
|
|
90
92
|
|
|
91
93
|
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
94
|
+
await trackCliCommandExecuted({
|
|
95
|
+
baseUrl,
|
|
96
|
+
apiKey,
|
|
97
|
+
command,
|
|
98
|
+
options,
|
|
99
|
+
positional,
|
|
100
|
+
request,
|
|
101
|
+
response: payload,
|
|
102
|
+
startedAt
|
|
103
|
+
});
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
export function runCliEntryPoint() {
|
package/src/requests.js
CHANGED
|
@@ -88,7 +88,7 @@ export function buildRequest(command, options, positional) {
|
|
|
88
88
|
const query = {
|
|
89
89
|
sort: "reviews_desc",
|
|
90
90
|
limit: sampling?.pageLimit ?? DEFAULT_BENCHMARK_LIMIT,
|
|
91
|
-
...buildGameSearchQuery(options)
|
|
91
|
+
...buildGameSearchQuery(options, { includeTotal: false })
|
|
92
92
|
};
|
|
93
93
|
if (sampling && options.limit == null) query.limit = sampling.pageLimit;
|
|
94
94
|
return {
|
|
@@ -96,6 +96,7 @@ export function buildRequest(command, options, positional) {
|
|
|
96
96
|
source: "steam",
|
|
97
97
|
query,
|
|
98
98
|
sampling,
|
|
99
|
+
countRequest: buildOptionalGameCountRequest(options),
|
|
99
100
|
searchRequest: {
|
|
100
101
|
path: "/api/v1/steam/games",
|
|
101
102
|
query
|
|
@@ -201,7 +202,7 @@ export function buildRequest(command, options, positional) {
|
|
|
201
202
|
const query = {
|
|
202
203
|
sort: "reviews_desc",
|
|
203
204
|
limit: sampling?.pageLimit ?? DEFAULT_REVIEW_PATTERNS_LIMIT,
|
|
204
|
-
...buildGameSearchQuery(options)
|
|
205
|
+
...buildGameSearchQuery(options, { includeTotal: false })
|
|
205
206
|
};
|
|
206
207
|
if (sampling && options.limit == null) query.limit = sampling.pageLimit;
|
|
207
208
|
|
|
@@ -210,6 +211,7 @@ export function buildRequest(command, options, positional) {
|
|
|
210
211
|
source: "steam",
|
|
211
212
|
query,
|
|
212
213
|
sampling,
|
|
214
|
+
countRequest: buildOptionalGameCountRequest(options),
|
|
213
215
|
minGames,
|
|
214
216
|
maxTopicsPerGame,
|
|
215
217
|
sentiment,
|
|
@@ -361,7 +363,7 @@ function buildCohortEvidenceRequest({ workflow, evidenceType, sections, options
|
|
|
361
363
|
const query = {
|
|
362
364
|
sort: "reviews_desc",
|
|
363
365
|
limit: sampling?.pageLimit ?? DEFAULT_COHORT_WORKFLOW_LIMIT,
|
|
364
|
-
...buildGameSearchQuery(options)
|
|
366
|
+
...buildGameSearchQuery(options, { includeTotal: false })
|
|
365
367
|
};
|
|
366
368
|
if (sampling && options.limit == null) query.limit = sampling.pageLimit;
|
|
367
369
|
return {
|
|
@@ -372,6 +374,7 @@ function buildCohortEvidenceRequest({ workflow, evidenceType, sections, options
|
|
|
372
374
|
sections,
|
|
373
375
|
query,
|
|
374
376
|
sampling,
|
|
377
|
+
countRequest: buildOptionalGameCountRequest(options),
|
|
375
378
|
searchRequest: {
|
|
376
379
|
path: "/api/v1/steam/games",
|
|
377
380
|
query
|
|
@@ -410,7 +413,7 @@ function pickQuery(options, mapping) {
|
|
|
410
413
|
return query;
|
|
411
414
|
}
|
|
412
415
|
|
|
413
|
-
function buildGameSearchQuery(options) {
|
|
416
|
+
function buildGameSearchQuery(options, { includeTotal = Boolean(options["include-total"]) } = {}) {
|
|
414
417
|
const query = {
|
|
415
418
|
...buildGameCountQuery(options),
|
|
416
419
|
...pickQuery(options, {
|
|
@@ -418,12 +421,21 @@ function buildGameSearchQuery(options) {
|
|
|
418
421
|
limit: "limit",
|
|
419
422
|
offset: "offset",
|
|
420
423
|
locale: "locale"
|
|
421
|
-
})
|
|
424
|
+
}),
|
|
425
|
+
includeTotal
|
|
422
426
|
};
|
|
423
427
|
|
|
424
428
|
return query;
|
|
425
429
|
}
|
|
426
430
|
|
|
431
|
+
function buildOptionalGameCountRequest(options) {
|
|
432
|
+
if (!options["include-total"]) return undefined;
|
|
433
|
+
return {
|
|
434
|
+
path: "/api/v1/steam/games/count",
|
|
435
|
+
query: buildGameCountQuery(options)
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
427
439
|
function buildGameCountQuery(options) {
|
|
428
440
|
const query = pickQuery(options, {
|
|
429
441
|
query: "q",
|
package/src/requests.test.js
CHANGED
|
@@ -15,11 +15,11 @@ test("games success-report builds the success-report request plan", () => {
|
|
|
15
15
|
"success-report",
|
|
16
16
|
"--source",
|
|
17
17
|
"steam",
|
|
18
|
-
"
|
|
18
|
+
"123456",
|
|
19
19
|
"--json"
|
|
20
20
|
]);
|
|
21
21
|
|
|
22
|
-
assert.equal(request.path, "/api/v1/steam/games/
|
|
22
|
+
assert.equal(request.path, "/api/v1/steam/games/123456/success-report");
|
|
23
23
|
assert.deepEqual(request.query, {});
|
|
24
24
|
});
|
|
25
25
|
|
|
@@ -29,13 +29,13 @@ test("games success-report forwards locale", () => {
|
|
|
29
29
|
"success-report",
|
|
30
30
|
"--source",
|
|
31
31
|
"steam",
|
|
32
|
-
"
|
|
32
|
+
"123456",
|
|
33
33
|
"--locale",
|
|
34
34
|
"ja",
|
|
35
35
|
"--json"
|
|
36
36
|
]);
|
|
37
37
|
|
|
38
|
-
assert.equal(request.path, "/api/v1/steam/games/
|
|
38
|
+
assert.equal(request.path, "/api/v1/steam/games/123456/success-report");
|
|
39
39
|
assert.deepEqual(request.query, { locale: "ja" });
|
|
40
40
|
});
|
|
41
41
|
|
|
@@ -45,11 +45,11 @@ test("games detail request plan stays unchanged", () => {
|
|
|
45
45
|
"detail",
|
|
46
46
|
"--source",
|
|
47
47
|
"steam",
|
|
48
|
-
"
|
|
48
|
+
"123456",
|
|
49
49
|
"--json"
|
|
50
50
|
]);
|
|
51
51
|
|
|
52
|
-
assert.equal(request.path, "/api/v1/steam/games/
|
|
52
|
+
assert.equal(request.path, "/api/v1/steam/games/123456");
|
|
53
53
|
assert.deepEqual(request.query, {});
|
|
54
54
|
});
|
|
55
55
|
|
|
@@ -69,6 +69,21 @@ test("games search forwards coverage as a filter", () => {
|
|
|
69
69
|
assert.equal(request.path, "/api/v1/steam/games");
|
|
70
70
|
assert.equal(request.query.coverage, "full,partial");
|
|
71
71
|
assert.equal(request.query.reviewsMin, "1000");
|
|
72
|
+
assert.equal(request.query.includeTotal, false);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("games search can opt into an exact total", () => {
|
|
76
|
+
const request = buildRequestFromArgv([
|
|
77
|
+
"games",
|
|
78
|
+
"search",
|
|
79
|
+
"--source",
|
|
80
|
+
"steam",
|
|
81
|
+
"--include-total",
|
|
82
|
+
"--json"
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
assert.equal(request.path, "/api/v1/steam/games");
|
|
86
|
+
assert.equal(request.query.includeTotal, true);
|
|
72
87
|
});
|
|
73
88
|
|
|
74
89
|
test("games count forwards coverage for cohort pre-estimation", () => {
|
|
@@ -101,4 +116,26 @@ test("reviews patterns forwards coverage into the cohort search request", () =>
|
|
|
101
116
|
|
|
102
117
|
assert.equal(request.kind, "reviewPatterns");
|
|
103
118
|
assert.equal(request.searchRequest.query.coverage, "full,partial");
|
|
119
|
+
assert.equal(request.searchRequest.query.includeTotal, false);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("sampled workflows keep exact totals in a separate count request", () => {
|
|
123
|
+
const request = buildRequestFromArgv([
|
|
124
|
+
"games",
|
|
125
|
+
"benchmark",
|
|
126
|
+
"--source",
|
|
127
|
+
"steam",
|
|
128
|
+
"--release-year",
|
|
129
|
+
"2025",
|
|
130
|
+
"--include-total",
|
|
131
|
+
"--json"
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
assert.equal(request.kind, "benchmark");
|
|
135
|
+
assert.equal(request.searchRequest.query.includeTotal, false);
|
|
136
|
+
assert.equal(request.countRequest.path, "/api/v1/steam/games/count");
|
|
137
|
+
assert.deepEqual(request.countRequest.query, {
|
|
138
|
+
releaseFrom: "2025-01-01",
|
|
139
|
+
releaseTo: "2025-12-31"
|
|
140
|
+
});
|
|
104
141
|
});
|
package/src/skill.js
CHANGED
|
@@ -51,7 +51,8 @@ export function handleSkillInstall(options) {
|
|
|
51
51
|
scope: options.dir ? "custom" : options.global ? "global" : "project",
|
|
52
52
|
next: [
|
|
53
53
|
"Claude Code picks up project skills from .claude/skills automatically.",
|
|
54
|
-
"For Codex or other AGENTS.md-based agents
|
|
54
|
+
"For Codex or other AGENTS.md-based agents, append the self-contained instructions with: gmc skill print --format agents-md >> AGENTS.md",
|
|
55
|
+
"If AGENTS.md already contains a generated gmc-analysis section, update it instead of appending a duplicate."
|
|
55
56
|
]
|
|
56
57
|
}
|
|
57
58
|
};
|