@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 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 beta CLI:
9
+ Install the CLI:
10
10
 
11
11
  ```bash
12
- npm install -g @witchpot/gmc@beta
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@beta --version
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. Install it
68
- so Claude Code (and similar tools) load it automatically:
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
- edition (references inlined) to your project brief:
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 resolve --source steam --query Timberborn --json
91
- gmc games detail --source steam 1062090 --json
92
- gmc campaigns signals --source steam 1062090 --json
93
- gmc games benchmark --source steam --tags city_builder,automation --reviews-min 1000 --preset summary --json
94
- gmc showcases fit --source steam 1062090 --json
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.7",
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
  }
@@ -33,14 +33,40 @@ const smokeCommands = [
33
33
  validate: (payload) => requireSuccessEnvelope(payload, ["data.version", "data.cliCommands"])
34
34
  },
35
35
  {
36
- name: "resolve Timberborn",
37
- args: ["games", "resolve", "--source", "steam", "--query", "Timberborn", "--json"],
38
- validate: (payload) => requireComposedEnvelope(payload, ["data.candidates", "data.resolution"])
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: "detail Timberborn",
42
- args: ["games", "detail", "--source", "steam", "1062090", "--json"],
43
- validate: (payload) => requireSuccessEnvelope(payload, ["data.title"])
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 fit Timberborn",
64
- args: ["showcases", "fit", "--source", "steam", "1062090", "--json"],
65
- validate: (payload) => requireComposedEnvelope(payload, ["data.matches", "data.summary"])
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.1.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` and `gmc <command> --help` are the
30
- source of truth for commands and parameters. Always pass `--json`.
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. Don't paginate by brute force
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 the install command for the closed-beta package once it is published."
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 ${CLI_BETA_PACKAGE_NAME}@beta`,
194
- npmExec: `npx ${CLI_BETA_PACKAGE_NAME}@beta --version`
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 in the closed-beta contract.",
198
- "A future gmc update --apply command can run the package-manager update after the npm distribution channel is finalized."
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.7";
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 beta commands:
59
+ Supported commands:
60
60
  ${SUPPORTED_BETA_COMMANDS.map((command) => ` ${command}`).join("\n")}
61
61
 
62
- Provisional beta commands:
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",
@@ -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
- "1062090",
18
+ "123456",
19
19
  "--json"
20
20
  ]);
21
21
 
22
- assert.equal(request.path, "/api/v1/steam/games/1062090/success-report");
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
- "1062090",
32
+ "123456",
33
33
  "--locale",
34
34
  "ja",
35
35
  "--json"
36
36
  ]);
37
37
 
38
- assert.equal(request.path, "/api/v1/steam/games/1062090/success-report");
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
- "1062090",
48
+ "123456",
49
49
  "--json"
50
50
  ]);
51
51
 
52
- assert.equal(request.path, "/api/v1/steam/games/1062090");
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 run: gmc skill print --format agents-md >> AGENTS.md"
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
  };