@witchpot/gmc 0.2.1 → 0.3.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 CHANGED
@@ -4,6 +4,28 @@ All notable changes to the Game Market Copilot CLI will be recorded here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.3.0 - 2026-07-05
8
+
9
+ - Added `gmc tags` for tag taxonomy discovery: `gmc tags` lists the full
10
+ slug/name dictionary and `gmc tags --q <term> [--limit <number>]` searches
11
+ it. The endpoint is anonymous and unmetered, so agents can resolve a genre
12
+ or keyword to valid tag slugs before spending quota on `--tags` filters.
13
+ - Split shared, pure composition and analytics helpers (resolve/cohort/
14
+ showcase composers, deadline filtering) out of the CLI workflow layer and
15
+ into the new `@witchpot/gmc-core` package, now a published dependency of
16
+ this CLI. No command-facing behavior change.
17
+ - This release rides on server-side additions already live in the API:
18
+ - Tag filters on games search/count/aggregate/benchmark, review patterns,
19
+ and the marketing/creators/cohort/showcases workflows now validate and
20
+ canonicalize `--tags` slugs, rejecting unknown ones with a 400
21
+ `INVALID_TAG` error carrying `details.unknown_tags` and
22
+ `details.suggestions`. `gmc tags` is the recommended preflight for
23
+ resolving valid slugs before hitting these filters.
24
+ - The `market_aggregate` MCP tool gained `segment_filter`, which narrows
25
+ the base filter (AND) and reports `segment.matched`/`segment.ratio`
26
+ against the base cohort at no extra credit cost (not yet combinable
27
+ with `group_by`).
28
+
7
29
  ## 0.2.1 - 2026-07-04
8
30
 
9
31
  - Updated `gmc update` to recommend the public `latest` npm dist-tag instead
package/README.md CHANGED
@@ -91,6 +91,7 @@ Re-run install/print after `npm update` to pick up newer skill versions.
91
91
 
92
92
  ```bash
93
93
  gmc schema --json
94
+ gmc tags --q roguelike --json
94
95
  gmc update --json
95
96
  gmc games search --source steam --release-year 2025 --reviews-min 1000 --fields title,appid,rating,reviews --json
96
97
  gmc games aggregate --source steam --release-year 2025 --metric count,median_reviews --group-by release_year --reviews-min 1000 --json
@@ -107,6 +108,7 @@ when using output in agent answers.
107
108
  - `gmc status`
108
109
  - `gmc auth status`
109
110
  - `gmc schema`
111
+ - `gmc tags`
110
112
  - `gmc games search`
111
113
  - `gmc games count`
112
114
  - `gmc games aggregate`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@witchpot/gmc",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "Command line tools for Game Market Copilot market research.",
6
6
  "license": "UNLICENSED",
@@ -27,6 +27,9 @@
27
27
  "engines": {
28
28
  "node": ">=20.9.0"
29
29
  },
30
+ "dependencies": {
31
+ "@witchpot/gmc-core": "^0.1.0"
32
+ },
30
33
  "files": [
31
34
  "bin",
32
35
  "src",
package/src/args.js CHANGED
@@ -49,6 +49,7 @@ function isCommandToken(command, token) {
49
49
  return [
50
50
  "status",
51
51
  "schema",
52
+ "tags",
52
53
  "games",
53
54
  "reviews",
54
55
  "campaigns",
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.2.1";
10
+ export const CLI_VERSION = "0.3.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");
@@ -16,6 +16,8 @@ export const SUPPORTED_BETA_COMMANDS = [
16
16
  "gmc status --json",
17
17
  "gmc auth status --json",
18
18
  "gmc schema --json",
19
+ "gmc tags --json",
20
+ "gmc tags --q <term> --json",
19
21
  "gmc --version",
20
22
  "gmc update --json",
21
23
  "gmc skill install --json",
@@ -53,14 +55,7 @@ export const PROVISIONAL_BETA_COMMANDS = [
53
55
  ];
54
56
 
55
57
  export const COMPARE_FOCUSES = ["overview", "market", "reviews", "marketing", "audience", "showcases"];
56
- export const COMPARE_FOCUS_SECTIONS = {
57
- overview: [],
58
- market: [],
59
- reviews: ["reviews"],
60
- marketing: ["marketing", "creators", "media"],
61
- audience: ["reviews", "coverage", "creators"],
62
- showcases: ["showcases"]
63
- };
58
+ export { COMPARE_FOCUS_SECTIONS } from "@witchpot/gmc-core";
64
59
 
65
60
  export const DEFAULT_BENCHMARK_LIMIT = "20";
66
61
  export const DEFAULT_CAMPAIGN_SIGNALS_LIMIT = "30";
package/src/help.js CHANGED
@@ -25,6 +25,8 @@ Usage:
25
25
  gmc skill print --format agents-md
26
26
  gmc skill status --json
27
27
  gmc schema --json
28
+ gmc tags --json
29
+ gmc tags --q <term> --limit <number> --json
28
30
  gmc lists --json
29
31
  gmc lists create --name <name> --kind manual --json
30
32
  gmc lists games <id> --json
@@ -56,6 +58,19 @@ Usage:
56
58
  gmc showcases fit --source steam <externalId> --deadline-before <YYYY-MM-DD> --json
57
59
  gmc showcases patterns --source steam --tags <tag_slug[,tag_slug...]> --json
58
60
 
61
+ Tag taxonomy:
62
+ gmc tags --json List the full tag dictionary (slug, name).
63
+ gmc tags --q <term> --json Search tags by free-text term.
64
+ gmc tags --q <term> --limit <number> --json
65
+ Cap the number of matches, 1-500 (default 50).
66
+
67
+ Commands that filter by --tags (games search/count/aggregate/benchmark,
68
+ reviews patterns, marketing/creators/cohort/showcases workflows) reject
69
+ unknown tag slugs with a 400 INVALID_TAG error and a
70
+ details.unknown_tags / details.suggestions payload. Run gmc tags --q
71
+ <term> first to resolve a genre or keyword to valid slugs before
72
+ building a --tags filter.
73
+
59
74
  Binary aliases:
60
75
  gmcp and game-market-copilot run the same CLI. Use them when a shell
61
76
  alias shadows gmc (Oh My Zsh's git plugin defines gmc as
package/src/requests.js CHANGED
@@ -14,6 +14,9 @@ import {
14
14
  } from "./constants.js";
15
15
  import { matches } from "./args.js";
16
16
  import { writeJsonError } from "./output.js";
17
+ import { filterShowcasesByDeadline } from "@witchpot/gmc-core";
18
+
19
+ export { filterShowcasesByDeadline };
17
20
 
18
21
  export function buildRequest(command, options, positional) {
19
22
  if (matches(command, ["status"])) {
@@ -37,6 +40,17 @@ export function buildRequest(command, options, positional) {
37
40
  };
38
41
  }
39
42
 
43
+ if (matches(command, ["tags"])) {
44
+ const q = options.q ?? options.query;
45
+ return {
46
+ path: "/api/v1/tags",
47
+ query: {
48
+ ...(q != null ? { q } : {}),
49
+ ...pickQuery(options, { limit: "limit" })
50
+ }
51
+ };
52
+ }
53
+
40
54
  if (matches(command, ["lists"])) {
41
55
  return {
42
56
  path: "/api/v1/lists",
@@ -886,18 +900,3 @@ function normalizeTagSlugCsv(value) {
886
900
  .filter(Boolean)
887
901
  .join(",");
888
902
  }
889
-
890
- export function filterShowcasesByDeadline(payload, deadlineBefore) {
891
- if (!payload || !Array.isArray(payload.data)) return payload;
892
- return {
893
- ...payload,
894
- data: payload.data.filter((showcase) => {
895
- if (!showcase || typeof showcase !== "object" || !showcase.deadline) return false;
896
- return String(showcase.deadline) <= deadlineBefore;
897
- }),
898
- warnings: [
899
- ...(payload.warnings ?? []),
900
- "deadline-before was applied client-side by the gmc CLI"
901
- ]
902
- };
903
- }
@@ -39,6 +39,41 @@ test("games success-report forwards locale", () => {
39
39
  assert.deepEqual(request.query, { locale: "ja" });
40
40
  });
41
41
 
42
+ test("tags builds the full-dictionary request plan", () => {
43
+ const request = buildRequestFromArgv(["tags", "--json"]);
44
+
45
+ assert.equal(request.path, "/api/v1/tags");
46
+ assert.deepEqual(request.query, {});
47
+ });
48
+
49
+ test("tags forwards --q as the q query param", () => {
50
+ const request = buildRequestFromArgv(["tags", "--q", "roguelike", "--json"]);
51
+
52
+ assert.equal(request.path, "/api/v1/tags");
53
+ assert.deepEqual(request.query, { q: "roguelike" });
54
+ });
55
+
56
+ test("tags accepts --query as an alias for --q", () => {
57
+ const request = buildRequestFromArgv(["tags", "--query", "roguelike", "--json"]);
58
+
59
+ assert.equal(request.path, "/api/v1/tags");
60
+ assert.deepEqual(request.query, { q: "roguelike" });
61
+ });
62
+
63
+ test("tags forwards --limit alongside --q", () => {
64
+ const request = buildRequestFromArgv([
65
+ "tags",
66
+ "--q",
67
+ "deck",
68
+ "--limit",
69
+ "10",
70
+ "--json"
71
+ ]);
72
+
73
+ assert.equal(request.path, "/api/v1/tags");
74
+ assert.deepEqual(request.query, { q: "deck", limit: "10" });
75
+ });
76
+
42
77
  test("games detail request plan stays unchanged", () => {
43
78
  const request = buildRequestFromArgv([
44
79
  "games",