@puzzmo/cli 1.0.50 → 1.0.52

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.
Files changed (50) hide show
  1. package/README.md +12 -2
  2. package/docs/games-changed.md +223 -0
  3. package/lib/commands/changed.d.ts +12 -0
  4. package/lib/commands/changed.js +178 -0
  5. package/lib/commands/changed.js.map +1 -0
  6. package/lib/commands/game/create.js +1 -10
  7. package/lib/commands/game/create.js.map +1 -1
  8. package/lib/commands/upload.d.ts +2 -0
  9. package/lib/commands/upload.js +37 -13
  10. package/lib/commands/upload.js.map +1 -1
  11. package/lib/commands/validate.d.ts +1 -1
  12. package/lib/commands/validate.js +1 -1
  13. package/lib/commands/validate.js.map +1 -1
  14. package/lib/index.js +57 -3
  15. package/lib/index.js.map +1 -1
  16. package/lib/queries/__generated__/cliGameRuntimesQuery.graphql.d.ts +37 -0
  17. package/lib/queries/__generated__/cliGameRuntimesQuery.graphql.js +256 -0
  18. package/lib/queries/__generated__/cliGameRuntimesQuery.graphql.js.map +1 -0
  19. package/lib/queries/gameRuntimes.d.ts +14 -0
  20. package/lib/queries/gameRuntimes.js +52 -0
  21. package/lib/queries/gameRuntimes.js.map +1 -0
  22. package/lib/util/api.d.ts +3 -0
  23. package/lib/util/api.js.map +1 -1
  24. package/lib/util/belay.d.ts +34 -0
  25. package/lib/util/belay.js +22 -0
  26. package/lib/util/belay.js.map +1 -0
  27. package/lib/util/createGame.d.ts +3 -0
  28. package/lib/util/createGame.js +6 -1
  29. package/lib/util/createGame.js.map +1 -1
  30. package/lib/util/discoverGames.d.ts +10 -2
  31. package/lib/util/discoverGames.js +14 -11
  32. package/lib/util/discoverGames.js.map +1 -1
  33. package/lib/util/slugify.d.ts +8 -0
  34. package/lib/util/slugify.js +17 -0
  35. package/lib/util/slugify.js.map +1 -0
  36. package/package.json +1 -1
  37. package/schemas/puzzmo-file-schema.json +14 -2
  38. package/src/commands/changed.ts +239 -0
  39. package/src/commands/game/create.ts +1 -12
  40. package/src/commands/upload.ts +41 -15
  41. package/src/commands/validate.ts +1 -1
  42. package/src/index.ts +63 -3
  43. package/src/queries/__generated__/cliGameRuntimesQuery.graphql.ts +301 -0
  44. package/src/queries/gameRuntimes.ts +64 -0
  45. package/src/util/api.ts +3 -0
  46. package/src/util/belay.ts +56 -0
  47. package/src/util/createGame.ts +9 -1
  48. package/src/util/discoverGames.ts +23 -11
  49. package/src/util/slugify.ts +17 -0
  50. package/templates/minesweeper/README.md +1 -1
@@ -0,0 +1,34 @@
1
+ /** Standard GraphQL error shape */
2
+ export type GraphQLError = {
3
+ message: string;
4
+ locations?: Array<{
5
+ line: number;
6
+ column: number;
7
+ }>;
8
+ path?: Array<string | number>;
9
+ extensions?: Record<string, unknown>;
10
+ };
11
+ /** GraphQL response shape */
12
+ export type GraphQLResponse<TData> = {
13
+ data?: TData;
14
+ errors?: GraphQLError[];
15
+ };
16
+ /** Operation type with variables and response — matches Relay's generated operation types */
17
+ export type OperationType = {
18
+ variables: Record<string, unknown>;
19
+ response: unknown;
20
+ };
21
+ /** Request options for the query function */
22
+ export type RequestOptions<TVariables> = {
23
+ variables: TVariables;
24
+ url: string;
25
+ headers?: HeadersInit;
26
+ signal?: AbortSignal;
27
+ };
28
+ /**
29
+ * A graphql tagged template literal that returns the query string. This lets the Relay
30
+ * compiler pick up and validate the operation (and generate its types) without bundling relay-runtime.
31
+ */
32
+ export declare const graphql: (strings: TemplateStringsArray) => string;
33
+ /** Execute a GraphQL query via fetch, typed with a Relay-generated operation type. */
34
+ export declare const query: <T extends OperationType>(queryString: string, options: RequestOptions<T["variables"]>) => Promise<GraphQLResponse<T["response"]>>;
@@ -0,0 +1,22 @@
1
+ // "belay lite" — a local copy of the parts of @puzzmo-com/belay the CLI needs.
2
+ // The CLI is published to npm and ships unbundled, so it can't depend on the private,
3
+ // source-only belay package; this vendors the tiny query/graphql helpers instead.
4
+ // The `graphql` tag lets the Relay compiler validate these queries and generate types
5
+ // (see the "cli" project in relay.config.json). Keep this in sync with packages/belay.
6
+ /**
7
+ * A graphql tagged template literal that returns the query string. This lets the Relay
8
+ * compiler pick up and validate the operation (and generate its types) without bundling relay-runtime.
9
+ */
10
+ export const graphql = (strings) => strings[0];
11
+ /** Execute a GraphQL query via fetch, typed with a Relay-generated operation type. */
12
+ export const query = async (queryString, options) => {
13
+ const { variables, url, headers, signal } = options;
14
+ const response = await fetch(url, {
15
+ method: "POST",
16
+ headers: { "Content-Type": "application/json", ...headers },
17
+ signal,
18
+ body: JSON.stringify({ query: queryString, variables }),
19
+ });
20
+ return response.json();
21
+ };
22
+ //# sourceMappingURL=belay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"belay.js","sourceRoot":"","sources":["../../src/util/belay.ts"],"names":[],"mappings":"AAAA,iFAA+E;AAC/E,sFAAsF;AACtF,kFAAkF;AAClF,sFAAsF;AACtF,uFAAuF;AA8BvF;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,OAA6B,EAAU,EAAE,CAAC,OAAO,CAAC,CAAC,CAAE,CAAA;AAE7E,sFAAsF;AACtF,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,EACxB,WAAmB,EACnB,OAAuC,EACE,EAAE,CAAC;IAC5C,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAEnD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,OAAO,EAAE;QAC3D,MAAM;QACN,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;KACxD,CAAC,CAAA;IAEF,OAAO,QAAQ,CAAC,IAAI,EAA6C,CAAA;AAAA,CAClE,CAAA"}
@@ -4,6 +4,9 @@ export type CreateUserGameOptions = {
4
4
  /** A teamAccessToken (the same token saved by `puzzmo login`). The mutation uses it to identify the team. */
5
5
  teamAccessToken: string;
6
6
  displayName: string;
7
+ oneliner?: string;
8
+ description?: string;
9
+ highlightColor?: string;
7
10
  };
8
11
  export type CreatedGame = {
9
12
  id: string;
@@ -16,7 +16,12 @@ export const createUserGame = async (options) => {
16
16
  query: createUserGameMutation,
17
17
  variables: {
18
18
  teamAccessToken: options.teamAccessToken,
19
- input: { displayName: options.displayName },
19
+ input: {
20
+ displayName: options.displayName,
21
+ oneliner: options.oneliner,
22
+ description: options.description,
23
+ highlightColor: options.highlightColor,
24
+ },
20
25
  },
21
26
  }),
22
27
  });
@@ -1 +1 @@
1
- {"version":3,"file":"createGame.js","sourceRoot":"","sources":["../../src/util/createGame.ts"],"names":[],"mappings":"AAAA,MAAM,sBAAsB,GAAG;;;;;;;CAO9B,CAAA;AAwBD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,OAA8B,EAAwB,EAAE,CAAC;IAC5F,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,MAAM,UAAU,CAAA;IAEvC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK,EAAE,sBAAsB;YAC7B,SAAS,EAAE;gBACT,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,KAAK,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE;aAC5C;SACF,CAAC;KACH,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;IAE7G,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAA;IAEhE,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC9D,MAAM,IAAI,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAA;IACtD,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IAEpF,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAA;IAC3D,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,EAAE,CAAA;AAAA,CACvF,CAAA"}
1
+ {"version":3,"file":"createGame.js","sourceRoot":"","sources":["../../src/util/createGame.ts"],"names":[],"mappings":"AAAA,MAAM,sBAAsB,GAAG;;;;;;;CAO9B,CAAA;AA2BD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,OAA8B,EAAwB,EAAE,CAAC;IAC5F,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,MAAM,UAAU,CAAA;IAEvC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK,EAAE,sBAAsB;YAC7B,SAAS,EAAE;gBACT,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,KAAK,EAAE;oBACL,WAAW,EAAE,OAAO,CAAC,WAAW;oBAChC,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;oBAChC,cAAc,EAAE,OAAO,CAAC,cAAc;iBACvC;aACF;SACF,CAAC;KACH,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;IAE7G,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAA;IAEhE,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC9D,MAAM,IAAI,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAA;IACtD,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IAEpF,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAA;IAC3D,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,EAAE,CAAA;AAAA,CACvF,CAAA"}
@@ -7,7 +7,10 @@ export type DiscoveredGame = {
7
7
  puzzmoJsonDir: string;
8
8
  /** Validated puzzmo.json contents */
9
9
  puzzmoFile: PuzzmoFile;
10
- /** Absolute path to the dist directory containing the build artifacts */
10
+ /**
11
+ * Absolute path to the dist directory containing the build artifacts (empty string when discovered with requireDist: false and none was
12
+ * found)
13
+ */
11
14
  distDir: string;
12
15
  };
13
16
  /** A puzzmo.json that was found but could not be used (invalid JSON, schema errors, missing dist) */
@@ -21,5 +24,10 @@ export type DiscoveryResult = {
21
24
  games: DiscoveredGame[];
22
25
  errors: DiscoveryError[];
23
26
  };
27
+ /** Options for {@link discoverGames} */
28
+ export type DiscoverOptions = {
29
+ /** Require each game to have a non-empty dist folder (default true). Set false for commands that run before a build. */
30
+ requireDist?: boolean;
31
+ };
24
32
  /** Walks `rootDir` looking for puzzmo.json files, validates each, and resolves their dist directory */
25
- export declare const discoverGames: (rootDir: string) => Promise<DiscoveryResult>;
33
+ export declare const discoverGames: (rootDir: string, options?: DiscoverOptions) => Promise<DiscoveryResult>;
@@ -7,7 +7,8 @@ const ignoredDirs = new Set(["node_modules", ".git", ".turbo", ".next", ".cache"
7
7
  /** Folder names that are searched (in order) when resolving a game's dist directory */
8
8
  const outputDirNames = ["dist", "build", "built", "output"];
9
9
  /** Walks `rootDir` looking for puzzmo.json files, validates each, and resolves their dist directory */
10
- export const discoverGames = async (rootDir) => {
10
+ export const discoverGames = async (rootDir, options = {}) => {
11
+ const { requireDist = true } = options;
11
12
  const root = path.resolve(rootDir);
12
13
  const puzzmoJsonPaths = findPuzzmoJsonFiles(root);
13
14
  const games = [];
@@ -30,17 +31,19 @@ export const discoverGames = async (rootDir) => {
30
31
  const puzzmoFile = validation.data;
31
32
  const puzzmoJsonDir = path.dirname(puzzmoJsonPath);
32
33
  const distDir = resolveDistDir(puzzmoFile, puzzmoJsonDir, root);
33
- if (!distDir) {
34
- fileErrors.push(`Could not find a dist/build folder for ${puzzmoFile.game.slug}. Set "output.dir" in puzzmo.json.`);
35
- }
36
- else if (!hasFiles(distDir)) {
37
- fileErrors.push(`Dist folder is empty: ${distDir}`);
38
- }
39
- if (fileErrors.length) {
40
- errors.push({ puzzmoJsonPath, slug: puzzmoFile.game.slug, errors: fileErrors });
41
- continue;
34
+ if (requireDist) {
35
+ if (!distDir) {
36
+ fileErrors.push(`Could not find a dist/build folder for ${puzzmoFile.game.slug}. Set "output.dir" in puzzmo.json.`);
37
+ }
38
+ else if (!hasFiles(distDir)) {
39
+ fileErrors.push(`Dist folder is empty: ${distDir}`);
40
+ }
41
+ if (fileErrors.length) {
42
+ errors.push({ puzzmoJsonPath, slug: puzzmoFile.game.slug, errors: fileErrors });
43
+ continue;
44
+ }
42
45
  }
43
- games.push({ puzzmoJsonPath, puzzmoJsonDir, puzzmoFile, distDir: distDir });
46
+ games.push({ puzzmoJsonPath, puzzmoJsonDir, puzzmoFile, distDir: distDir ?? "" });
44
47
  }
45
48
  return { games, errors };
46
49
  };
@@ -1 +1 @@
1
- {"version":3,"file":"discoverGames.js","sourceRoot":"","sources":["../../src/util/discoverGames.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAmB,KAAK,IAAI,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAGxF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE5D,iFAAiF;AACjF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAA;AAEtH,uFAAuF;AACvF,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;AA2B3D,uGAAuG;AACvG,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,OAAe,EAA4B,EAAE,CAAC;IAChF,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAClC,MAAM,eAAe,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAA;IAEjD,MAAM,KAAK,GAAqB,EAAE,CAAA;IAClC,MAAM,MAAM,GAAqB,EAAE,CAAA;IAEnC,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;QAC7C,MAAM,UAAU,GAAa,EAAE,CAAA;QAC/B,MAAM,WAAW,GAAiB,EAAE,CAAA;QACpC,MAAM,MAAM,GAAY,UAAU,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAA;QACvH,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;YACtG,MAAM,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,kBAAkB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;YAClF,SAAQ;QACV,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAA;QACnD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAA;YAC7B,MAAM,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;YAChE,SAAQ;QACV,CAAC;QAED,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAA;QAClC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;QAClD,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,EAAE,aAAa,EAAE,IAAI,CAAC,CAAA;QAE/D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,UAAU,CAAC,IAAI,CAAC,0CAA0C,UAAU,CAAC,IAAI,CAAC,IAAI,oCAAoC,CAAC,CAAA;QACrH,CAAC;aAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,UAAU,CAAC,IAAI,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAA;QACrD,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;YACtB,MAAM,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;YAC/E,SAAQ;QACV,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,OAAiB,EAAE,CAAC,CAAA;IACvF,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AAAA,CACzB,CAAA;AAED,qIAAqI;AACrI,MAAM,mBAAmB,GAAG,CAAC,GAAW,EAAY,EAAE,CAAC;IACrD,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IACjC,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,CAAA;IACnB,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,EAAY,CAAA;QACrC,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;QACzC,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;YAAE,SAAQ;QACtD,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QAExB,IAAI,OAAoB,CAAA;QACxB,IAAI,CAAC;YACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QAChE,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ;QACV,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;YAC/C,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;YAC1C,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;gBACzB,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,SAAQ;gBACzC,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,SAAQ;gBACxC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAClB,CAAC;iBAAM,IAAI,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;gBAC3D,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAA;AAAA,CACpB,CAAA;AAED,6FAA6F;AAC7F,MAAM,cAAc,GAAG,CAAC,UAAsB,EAAE,aAAqB,EAAE,QAAgB,EAAiB,EAAE,CAAC;IACzG,IAAI,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACnE,OAAO,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA;IAClD,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAA;IACjC,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAA;IACrD,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;QACpC,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;YAC/C,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE;gBAAE,OAAO,QAAQ,CAAA;YACnF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;YACtC,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE;gBAAE,OAAO,KAAK,CAAA;QAC5E,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AAAA,CACZ,CAAA;AAED,gFAAgF;AAChF,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,IAAY,EAAY,EAAE,CAAC;IACxD,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAClB,IAAI,OAAO,KAAK,IAAI;YAAE,MAAK;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QACpC,IAAI,MAAM,KAAK,OAAO;YAAE,MAAK;QAC7B,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,MAAK;QACpC,OAAO,GAAG,MAAM,CAAA;IAClB,CAAC;IACD,OAAO,IAAI,CAAA;AAAA,CACZ,CAAA;AAED,kIAAkI;AAClI,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,OAAO,GAAgB,IAAI,GAAG,EAAE,EAAW,EAAE,CAAC;IAC3E,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;IACjC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAA;IAClD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAEpB,IAAI,OAAoB,CAAA;IACxB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QAC3C,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAC1C,IAAI,IAAI,KAAK,MAAM;YAAE,OAAO,IAAI,CAAA;QAChC,IAAI,IAAI,KAAK,WAAW,IAAI,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;YAAE,OAAO,IAAI,CAAA;IAClE,CAAC;IACD,OAAO,KAAK,CAAA;AAAA,CACb,CAAA;AAED,uIAAuI;AACvI,MAAM,gBAAgB,GAAG,CAAC,KAAgB,EAAE,QAAgB,EAA+B,EAAE,CAAC;IAC5F,IAAI,KAAK,CAAC,MAAM,EAAE;QAAE,OAAO,MAAM,CAAA;IACjC,IAAI,KAAK,CAAC,WAAW,EAAE;QAAE,OAAO,WAAW,CAAA;IAC3C,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE;QAAE,OAAO,IAAI,CAAA;IACxC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QAClC,IAAI,IAAI,CAAC,MAAM,EAAE;YAAE,OAAO,MAAM,CAAA;QAChC,IAAI,IAAI,CAAC,WAAW,EAAE;YAAE,OAAO,WAAW,CAAA;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,mBAAmB;IACrB,CAAC;IACD,OAAO,IAAI,CAAA;AAAA,CACZ,CAAA;AAED,wHAAwH;AACxH,MAAM,YAAY,GAAG,CAAC,CAAS,EAAiB,EAAE,CAAC;IACjD,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AAAA,CACF,CAAA;AAED,0FAA0F;AAC1F,MAAM,QAAQ,GAAG,CAAC,MAAe,EAAsB,EAAE,CAAC;IACxD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAC3D,MAAM,IAAI,GAAI,MAA6B,CAAC,IAAI,CAAA;IAChD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IACvD,MAAM,IAAI,GAAI,IAA2B,CAAC,IAAI,CAAA;IAC9C,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;AAAA,CACnD,CAAA"}
1
+ {"version":3,"file":"discoverGames.js","sourceRoot":"","sources":["../../src/util/discoverGames.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Converts a string to a URL-friendly slug. Mirrors packages/shared/slugify.ts —
3
+ * the CLI is standalone and does not depend on @puzzmo-com/shared, so the host's
4
+ * slug derivation is replicated here. Keep in sync with the shared version: the
5
+ * server derives a new game's slug from its displayName the same way, and
6
+ * `upload` uses this to refuse creating a game whose puzzmo.json slug wouldn't match.
7
+ */
8
+ export declare const slugify: (text: string) => string;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Converts a string to a URL-friendly slug. Mirrors packages/shared/slugify.ts —
3
+ * the CLI is standalone and does not depend on @puzzmo-com/shared, so the host's
4
+ * slug derivation is replicated here. Keep in sync with the shared version: the
5
+ * server derives a new game's slug from its displayName the same way, and
6
+ * `upload` uses this to refuse creating a game whose puzzmo.json slug wouldn't match.
7
+ */
8
+ export const slugify = (text) => text
9
+ .toString()
10
+ .normalize("NFD")
11
+ .replace(/[\u0300-\u036f]/g, "")
12
+ .toLowerCase()
13
+ .trim()
14
+ .replace(/\s+/g, "-")
15
+ .replace(/[^\w-]+/g, "")
16
+ .replace(/--+/g, "-");
17
+ //# sourceMappingURL=slugify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugify.js","sourceRoot":"","sources":["../../src/util/slugify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE,CAC9C,IAAI;KACD,QAAQ,EAAE;KACV,SAAS,CAAC,KAAK,CAAC;KAChB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;KAC/B,WAAW,EAAE;KACb,IAAI,EAAE;KACN,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;KACpB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;KACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puzzmo/cli",
3
- "version": "1.0.50",
3
+ "version": "1.0.52",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "puzzmo": "./lib/index.js"
@@ -10,7 +10,7 @@
10
10
  "properties": {
11
11
  "displayName": {
12
12
  "type": "string",
13
- "description": "Display name shown in the UI, use | to indicate where a line break should go"
13
+ "description": "Display name shown in the UI, use | to indicate where a line break should go. Synced to the game on every upload; seeds a newly created game."
14
14
  },
15
15
  "slug": {
16
16
  "type": "string",
@@ -19,6 +19,18 @@
19
19
  "teamID": {
20
20
  "type": "string",
21
21
  "description": "The team ID that owns this game"
22
+ },
23
+ "oneliner": {
24
+ "type": "string",
25
+ "description": "Short tagline for the game. Synced to the game on every upload when present; seeds a newly created game."
26
+ },
27
+ "description": {
28
+ "type": "string",
29
+ "description": "Longer description of the game. Synced to the game on every upload when present; seeds a newly created game."
30
+ },
31
+ "highlightColor": {
32
+ "type": "string",
33
+ "description": "Hex highlight color (e.g. \"#FF8800\"). Synced to the game on every upload when present; seeds a newly created game."
22
34
  }
23
35
  },
24
36
  "required": ["displayName", "slug", "teamID"],
@@ -39,7 +51,7 @@
39
51
  },
40
52
  "required": ["dir"],
41
53
  "additionalProperties": false,
42
- "description": "Where the built game artifacts live for this game. Used by `puzzmo upload` and `puzzmo validate` to find the dist folder when running from a repo root. If omitted, the CLI looks for a `dist`, `build`, or `built` folder — first next to puzzmo.json, then walking up to the repo root, optionally with `<slug>` as a sub-folder for shared output dirs."
54
+ "description": "Where the built game artifacts live for this game. Used by `puzzmo games upload` and `puzzmo games validate` to find the dist folder when running from a repo root. If omitted, the CLI looks for a `dist`, `build`, or `built` folder — first next to puzzmo.json, then walking up to the repo root, optionally with `<slug>` as a sub-folder for shared output dirs."
43
55
  }
44
56
  },
45
57
  "required": ["game"],
@@ -0,0 +1,239 @@
1
+ import { execFileSync } from "node:child_process"
2
+ import fs from "node:fs"
3
+ import path from "node:path"
4
+
5
+ import { findTokensForTeam, getTokens, resolveServerForTeam, sourceToURL } from "../util/config.js"
6
+ import { type DiscoveredGame, discoverGames } from "../util/discoverGames.js"
7
+ import { fetchTeamGameVersions, type GameVersions } from "../queries/gameRuntimes.js"
8
+
9
+ /** Which deployed build slot to diff against: the team's latest build, the staged next, or the previous one. */
10
+ export type ChangedAgainst = "latest" | "next" | "previous"
11
+
12
+ export type ChangedOptions = {
13
+ json?: boolean
14
+ list?: boolean
15
+ matrix?: boolean
16
+ ref?: string
17
+ against?: ChangedAgainst
18
+ includeUncommitted?: boolean
19
+ }
20
+
21
+ type GameStatus = "changed" | "unchanged" | "new"
22
+
23
+ type ChangedEntry = {
24
+ slug: string
25
+ displayName: string
26
+ teamID: string
27
+ dir: string
28
+ baseSha: string | null
29
+ ref: string
30
+ status: GameStatus
31
+ changedFiles: number
32
+ }
33
+
34
+ type EvalResult = { entry: ChangedEntry } | { error: string }
35
+
36
+ /** Reports which discovered games changed since their last deployed build, scoped per game folder. */
37
+ export const changed = async (dir: string, options: ChangedOptions = {}) => {
38
+ const { json = false, list = false, matrix = false, against = "latest", includeUncommitted = false } = options
39
+
40
+ if (getTokens().length === 0) {
41
+ console.error("Not logged in. Run `puzzmo login <token>` or set PUZZMO_TOKEN.")
42
+ process.exit(1)
43
+ }
44
+
45
+ const rootDir = path.resolve(dir)
46
+ if (!fs.existsSync(rootDir)) {
47
+ console.error(`Directory not found: ${dir}`)
48
+ process.exit(1)
49
+ }
50
+
51
+ const repoRoot = getRepoRoot(rootDir)
52
+ if (!repoRoot) {
53
+ console.error(`Not a git repository: ${rootDir}`)
54
+ process.exit(1)
55
+ }
56
+
57
+ const ref = options.ref || "HEAD"
58
+ const refSha = revParseShort(ref, repoRoot)
59
+ if (!refSha) {
60
+ console.error(`Could not resolve git ref: ${ref}`)
61
+ process.exit(1)
62
+ }
63
+
64
+ const { games, errors: discoveryErrors } = await discoverGames(rootDir, { requireDist: false })
65
+ if (!games.length && !discoveryErrors.length) {
66
+ console.error(`No puzzmo.json files found under ${rootDir}`)
67
+ process.exit(1)
68
+ }
69
+
70
+ const hardErrors = discoveryErrors.map((err) => `${err.slug ?? path.relative(rootDir, err.puzzmoJsonPath)}: ${err.errors.join("; ")}`)
71
+
72
+ const teamVersions = new Map<string, Promise<Map<string, GameVersions>>>()
73
+ const results = await Promise.all(
74
+ games.map((game) => evaluateGame(game, { repoRoot, ref, refSha, against, includeUncommitted, teamVersions })),
75
+ )
76
+ const report: ChangedEntry[] = []
77
+ for (const result of results) {
78
+ if ("entry" in result) report.push(result.entry)
79
+ else hardErrors.push(result.error)
80
+ }
81
+
82
+ const buildable = report.filter((entry) => entry.status !== "unchanged")
83
+
84
+ if (json) console.log(JSON.stringify(report, null, 2))
85
+ else if (matrix) console.log(JSON.stringify({ include: buildable.map((entry) => ({ dir: entry.dir, slug: entry.slug })) }))
86
+ else if (list) for (const entry of buildable) console.log(entry.dir)
87
+ else if (report.length) printTable(report)
88
+ else if (!hardErrors.length) console.log(`No puzzmo.json files found under ${rootDir}`)
89
+
90
+ if (hardErrors.length) {
91
+ console.error(`\n${hardErrors.length} error${hardErrors.length === 1 ? "" : "s"}:`)
92
+ for (const message of hardErrors) console.error(` ${message}`)
93
+ process.exit(1)
94
+ }
95
+ }
96
+
97
+ type EvalContext = {
98
+ repoRoot: string
99
+ ref: string
100
+ refSha: string
101
+ against: ChangedAgainst
102
+ includeUncommitted: boolean
103
+ // Memoizes the per-team runtime fetch so games sharing a team only hit the API once.
104
+ teamVersions: Map<string, Promise<Map<string, GameVersions>>>
105
+ }
106
+
107
+ /** Resolves one game's deployed SHA and diffs it against the ref. Returns either a report entry or a hard error. */
108
+ const evaluateGame = async (game: DiscoveredGame, ctx: EvalContext): Promise<EvalResult> => {
109
+ const { slug, teamID } = game.puzzmoFile.game
110
+ const dir = toPosix(path.relative(ctx.repoRoot, game.puzzmoJsonDir)) || "."
111
+
112
+ const credential = await resolveServerForTeam(teamID)
113
+ if (!credential) {
114
+ const matches = findTokensForTeam(teamID)
115
+ const message =
116
+ matches.length === 0
117
+ ? `No saved token for team ${teamID}. Run \`puzzmo login <token>\`.`
118
+ : `Token for team ${teamID} is registered against ${matches.map((m) => m.source).join(", ")} but none of those servers are reachable.`
119
+ return { error: `${slug}: ${message}` }
120
+ }
121
+
122
+ let versions: GameVersions | undefined
123
+ try {
124
+ versions = (await getTeamVersions(teamID, credential, ctx)).get(slug)
125
+ } catch (e) {
126
+ return { error: `${slug}: ${e instanceof Error ? e.message : String(e)}` }
127
+ }
128
+
129
+ const baseSha = pickBaseSha(versions ?? null, ctx.against)
130
+
131
+ // Never deployed (or the chosen slot is empty) — nothing to diff against, so it's new and buildable.
132
+ if (!baseSha) return { entry: makeEntry(game, dir, null, ctx.refSha, "new", 0) }
133
+
134
+ // The deployed commit has to exist locally or we can't diff — fail loudly rather than guessing.
135
+ if (!commitExists(baseSha, ctx.repoRoot))
136
+ return {
137
+ error: `${slug}: deployed commit ${baseSha} not found locally. Fetch full git history (e.g. actions/checkout fetch-depth: 0).`,
138
+ }
139
+
140
+ const changedFiles = countChanges(baseSha, ctx.ref, game.puzzmoJsonDir, ctx.includeUncommitted)
141
+ return { entry: makeEntry(game, dir, baseSha, ctx.refSha, changedFiles > 0 ? "changed" : "unchanged", changedFiles) }
142
+ }
143
+
144
+ const makeEntry = (
145
+ game: DiscoveredGame,
146
+ dir: string,
147
+ baseSha: string | null,
148
+ ref: string,
149
+ status: GameStatus,
150
+ changedFiles: number,
151
+ ): ChangedEntry => ({
152
+ slug: game.puzzmoFile.game.slug,
153
+ displayName: game.puzzmoFile.game.displayName,
154
+ teamID: game.puzzmoFile.game.teamID,
155
+ dir,
156
+ baseSha,
157
+ ref,
158
+ status,
159
+ changedFiles,
160
+ })
161
+
162
+ /** Fetches a team's per-game runtime SHAs once, memoizing the in-flight promise so concurrent games share the call. */
163
+ const getTeamVersions = (
164
+ teamID: string,
165
+ credential: { source: string; token: string },
166
+ ctx: EvalContext,
167
+ ): Promise<Map<string, GameVersions>> => {
168
+ let pending = ctx.teamVersions.get(teamID)
169
+ if (!pending) {
170
+ pending = fetchTeamGameVersions(sourceToURL(credential.source), credential.token)
171
+ ctx.teamVersions.set(teamID, pending)
172
+ }
173
+ return pending
174
+ }
175
+
176
+ /** Picks the baseline SHA for the chosen slot. `latest` is the most recent build the team has — the staged next, else the live current. */
177
+ const pickBaseSha = (versions: GameVersions | null, against: ChangedAgainst): string | null => {
178
+ if (!versions) return null
179
+ if (against === "next") return versions.next
180
+ if (against === "previous") return versions.previous
181
+ return versions.next ?? versions.current
182
+ }
183
+
184
+ /** Counts files differing under `dir` between `base` and `ref`, optionally folding in uncommitted working-tree changes. */
185
+ const countChanges = (base: string, ref: string, dir: string, includeUncommitted: boolean): number => {
186
+ const files = new Set(gitLines(["diff", "--name-only", `${base}..${ref}`, "--", "."], dir))
187
+ if (includeUncommitted) {
188
+ for (const file of gitLines(["diff", "--name-only", "--", "."], dir)) files.add(file) // unstaged
189
+ for (const file of gitLines(["diff", "--name-only", "--cached", "--", "."], dir)) files.add(file) // staged
190
+ for (const file of gitLines(["ls-files", "--others", "--exclude-standard", "--", "."], dir)) files.add(file) // untracked
191
+ }
192
+ return files.size
193
+ }
194
+
195
+ /** Prints the human-readable summary table. */
196
+ const printTable = (report: ChangedEntry[]) => {
197
+ const headers = ["GAME", "STATUS", "BASE", "FILES"]
198
+ const rows = report.map((entry) => [entry.slug, entry.status, entry.baseSha ?? "—", String(entry.changedFiles)])
199
+ const widths = headers.map((header, i) => Math.max(header.length, ...rows.map((row) => row[i].length)))
200
+ const format = (cols: string[]) => cols.map((col, i) => col.padEnd(widths[i])).join(" ")
201
+
202
+ console.log(format(headers))
203
+ for (const row of rows) console.log(format(row))
204
+
205
+ const changedCount = report.filter((entry) => entry.status !== "unchanged").length
206
+ console.log(`\n${changedCount} of ${report.length} game${report.length === 1 ? "" : "s"} changed.`)
207
+ }
208
+
209
+ const getRepoRoot = (cwd: string): string | null => tryGit(["rev-parse", "--show-toplevel"], cwd)
210
+
211
+ const revParseShort = (ref: string, cwd: string): string | null => tryGit(["rev-parse", "--short", ref], cwd)
212
+
213
+ /** True if `sha` resolves to a commit object in the repo at `cwd`. */
214
+ const commitExists = (sha: string, cwd: string): boolean => {
215
+ try {
216
+ execFileSync("git", ["rev-parse", "--verify", "--quiet", `${sha}^{commit}`], { cwd, stdio: "ignore" })
217
+ return true
218
+ } catch {
219
+ return false
220
+ }
221
+ }
222
+
223
+ /** Runs a git command and returns its trimmed stdout, or null if it failed (git's own stderr is suppressed). */
224
+ const tryGit = (args: string[], cwd: string): string | null => {
225
+ try {
226
+ return execFileSync("git", args, { encoding: "utf-8", cwd, stdio: ["ignore", "pipe", "ignore"] }).trim()
227
+ } catch {
228
+ return null
229
+ }
230
+ }
231
+
232
+ /** Runs a git command that emits one file path per line and returns the non-empty lines, or [] if it failed. */
233
+ const gitLines = (args: string[], cwd: string): string[] => {
234
+ const out = tryGit(args, cwd)
235
+ if (out === null) return []
236
+ return out.split("\n").filter(Boolean)
237
+ }
238
+
239
+ const toPosix = (p: string): string => p.split(path.sep).join("/")
@@ -11,6 +11,7 @@ import { runCommand, gitCommit } from "../../util/exec.js"
11
11
  import { runSkillsPipelineTUI, runAgentWithBuildLoop } from "../../skills/runner.js"
12
12
  import { login } from "../login.js"
13
13
  import { getDefaultToken } from "../../util/config.js"
14
+ import { slugify } from "../../util/slugify.js"
14
15
  import { detectRepoContext, type RepoType } from "./detectRepo.js"
15
16
 
16
17
  export type Strategy = "import" | "blank" | "prompt"
@@ -27,18 +28,6 @@ export type CreateOptions = {
27
28
  prompt?: string
28
29
  }
29
30
 
30
- /** Converts a string to a URL-friendly slug (matches packages/shared/slugify.ts) */
31
- const slugify = (text: string) =>
32
- text
33
- .toString()
34
- .normalize("NFD")
35
- .replace(/[̀-ͯ]/g, "")
36
- .toLowerCase()
37
- .trim()
38
- .replace(/\s+/g, "-")
39
- .replace(/[^\w-]+/g, "")
40
- .replace(/--+/g, "-")
41
-
42
31
  /** Writes .mcp.json with dev server config */
43
32
  const writeMcpConfig = (dir: string) => {
44
33
  const token = getDefaultToken()
@@ -16,9 +16,12 @@ import {
16
16
  } from "../util/config.js"
17
17
  import { createUserGame } from "../util/createGame.js"
18
18
  import { discoverGames, type DiscoveredGame } from "../util/discoverGames.js"
19
+ import { slugify } from "../util/slugify.js"
19
20
 
20
21
  type UploadOptions = {
21
22
  verbose?: boolean
23
+ /** Create games that don't exist yet without prompting (for CI). */
24
+ createMissing?: boolean
22
25
  }
23
26
 
24
27
  type GameSuccess = {
@@ -45,6 +48,7 @@ type GameResult = GameSuccess | GameFailure
45
48
  /** Uploads game build artifacts to Puzzmo by discovering puzzmo.json files in `dir` */
46
49
  export const upload = async (dir: string, options: UploadOptions = {}) => {
47
50
  const { verbose = false } = options
51
+ const autoCreate = options.createMissing ?? false
48
52
 
49
53
  if (getTokens().length === 0) {
50
54
  console.error("Not logged in. Run `puzzmo login <token>` or set PUZZMO_TOKEN.")
@@ -120,7 +124,7 @@ export const upload = async (dir: string, options: UploadOptions = {}) => {
120
124
  result = await uploadOneGame(game, { credential, apiURL, sha, description, repoURL, verbose })
121
125
  } catch (e) {
122
126
  if (!(e instanceof GameNotFoundError)) throw e
123
- const created = await maybeCreateMissingGame(e, game, { apiURL, teamAccessToken: credential.token })
127
+ const created = await maybeCreateMissingGame(e, game, { apiURL, teamAccessToken: credential.token, autoCreate })
124
128
  if (!created) throw e
125
129
  result = await uploadOneGame(game, { credential, apiURL, sha, description, repoURL, verbose })
126
130
  }
@@ -313,31 +317,53 @@ const hashGames = (games: DiscoveredGame[]): string => {
313
317
 
314
318
  /**
315
319
  * When a game can't be uploaded because it doesn't exist on the user's account,
316
- * offer to create it interactively. Returns true if the game was created and the
317
- * caller can retry the upload. Returns false otherwise (non-interactive shell or
318
- * user declined).
320
+ * create it. With `autoCreate` (CI: --create-missing) it
321
+ * creates without prompting; otherwise it asks for confirmation in an interactive
322
+ * shell. Returns true if the game was created and the caller can retry the upload.
323
+ * Returns false otherwise (non-interactive without autoCreate, or user declined).
319
324
  */
320
325
  const maybeCreateMissingGame = async (
321
326
  err: GameNotFoundError,
322
327
  game: DiscoveredGame,
323
- opts: { apiURL: string; teamAccessToken: string },
328
+ opts: { apiURL: string; teamAccessToken: string; autoCreate: boolean },
324
329
  ): Promise<boolean> => {
325
330
  const slug = err.slug
326
- const displayName = game.puzzmoFile.game.displayName
327
-
328
- if (!process.stdin.isTTY) {
329
- console.error(` Game "${slug}" not found on your account. Re-run interactively to create it.`)
331
+ const { displayName, oneliner, description, highlightColor } = game.puzzmoFile.game
332
+
333
+ // Creation derives the slug from displayName server-side; if it wouldn't match the
334
+ // puzzmo.json slug, creating would mint an orphan game under the wrong slug and the
335
+ // retried upload would still 404. Refuse up front so nothing is created.
336
+ const expectedSlug = slugify(displayName)
337
+ if (expectedSlug !== slug) {
338
+ console.error(
339
+ ` Game "${slug}" not found, and cannot be auto-created: displayName "${displayName}" slugifies to "${expectedSlug}", not "${slug}". ` +
340
+ `Set puzzmo.json game.slug to "${expectedSlug}" (or adjust game.displayName), or create the game manually.`,
341
+ )
330
342
  return false
331
343
  }
332
344
 
333
- const consent = await p.confirm({
334
- message: `Game "${slug}" doesn't exist on your account. Create it now (displayName: "${displayName}")?`,
335
- initialValue: true,
336
- })
337
- if (p.isCancel(consent) || !consent) return false
345
+ if (opts.autoCreate) {
346
+ console.log(` Game "${slug}" not found creating it (displayName: "${displayName}")...`)
347
+ } else if (!process.stdin.isTTY) {
348
+ console.error(` Game "${slug}" not found on your account. Re-run interactively, or pass --create-missing to create it automatically.`)
349
+ return false
350
+ } else {
351
+ const consent = await p.confirm({
352
+ message: `Game "${slug}" doesn't exist on your account. Create it now (displayName: "${displayName}")?`,
353
+ initialValue: true,
354
+ })
355
+ if (p.isCancel(consent) || !consent) return false
356
+ }
338
357
 
339
358
  try {
340
- const created = await createUserGame({ apiURL: opts.apiURL, displayName, teamAccessToken: opts.teamAccessToken })
359
+ const created = await createUserGame({
360
+ apiURL: opts.apiURL,
361
+ displayName,
362
+ oneliner,
363
+ description,
364
+ highlightColor,
365
+ teamAccessToken: opts.teamAccessToken,
366
+ })
341
367
  console.log(` Created game ${created.slug}`)
342
368
  if (created.slug !== slug) {
343
369
  console.warn(` Server picked slug "${created.slug}" but your puzzmo.json uses "${slug}". Update puzzmo.json before re-uploading.`)
@@ -3,7 +3,7 @@ import path from "node:path"
3
3
 
4
4
  import { discoverGames } from "../util/discoverGames.js"
5
5
 
6
- /** CLI command: puzzmo validate [dir] — discovers every puzzmo.json under dir and validates each */
6
+ /** CLI command: puzzmo games validate [dir] — discovers every puzzmo.json under dir and validates each */
7
7
  export const validate = async (dir: string) => {
8
8
  const rootDir = path.resolve(dir)
9
9
  if (!fs.existsSync(rootDir)) {