@puzzmo/cli 1.0.22 → 1.0.24

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.
@@ -8,6 +8,7 @@ import { runCommand, gitCommit } from "../../util/exec.js";
8
8
  import { runSkillsPipelineTUI } from "../../skills/runner.js";
9
9
  import { login } from "../login.js";
10
10
  import { getToken } from "../../util/config.js";
11
+ import { detectRepoContext } from "./detectRepo.js";
11
12
  /** Parses CLI args into CreateOptions */
12
13
  const parseArgs = (args) => {
13
14
  const opts = {};
@@ -33,32 +34,79 @@ const parseArgs = (args) => {
33
34
  }
34
35
  return opts;
35
36
  };
36
- /** Converts a string to a URL-friendly slug */
37
- const slugify = (str) => str
37
+ /** Converts a string to a URL-friendly slug (matches packages/shared/slugify.ts) */
38
+ const slugify = (text) => text
39
+ .toString()
40
+ .normalize("NFD")
41
+ .replace(/[\u0300-\u036f]/g, "")
38
42
  .toLowerCase()
39
- .replace(/[^a-z0-9]+/g, "-")
40
- .replace(/^-+|-+$/g, "");
43
+ .trim()
44
+ .replace(/\s+/g, "-")
45
+ .replace(/[^\w-]+/g, "")
46
+ .replace(/--+/g, "-");
47
+ /** Writes .mcp.json with dev server config */
48
+ const writeMcpConfig = (dir) => {
49
+ const token = getToken();
50
+ const mcpConfig = {
51
+ mcpServers: {
52
+ "dev.puzzmo.com": {
53
+ type: "http",
54
+ // url: "https://dev.puzzmo.com/api/mcp",
55
+ url: "https://dev-dj9e.onrender.com/api/mcp",
56
+ headers: token ? { Authorization: `Bearer ${token}` } : undefined,
57
+ },
58
+ },
59
+ };
60
+ fs.writeFileSync(path.join(dir, ".mcp.json"), JSON.stringify(mcpConfig, null, 2) + "\n");
61
+ };
62
+ /** Sets up a standalone new repo for the game */
63
+ const setupNewRepo = (gameDir) => {
64
+ fs.writeFileSync(path.join(gameDir, ".gitignore"), ["node_modules", "dist", ".DS_Store", ".yarn", ".pnp.*", ".puzzmo", ""].join("\n"));
65
+ writeMcpConfig(gameDir);
66
+ if (!fs.existsSync(path.join(gameDir, ".git"))) {
67
+ runCommand("git init", { cwd: gameDir });
68
+ runCommand("git add -A", { cwd: gameDir });
69
+ gitCommit("Initial game import", { cwd: gameDir });
70
+ }
71
+ };
72
+ /** Sets up the game inside an existing monorepo */
73
+ const setupMonorepoGame = (tmpDir, slug, repoRoot, parentFolder) => {
74
+ const parentDir = path.join(repoRoot, parentFolder);
75
+ if (!fs.existsSync(parentDir))
76
+ fs.mkdirSync(parentDir, { recursive: true });
77
+ const gameDir = path.join(parentDir, slug);
78
+ if (fs.existsSync(gameDir))
79
+ fs.rmSync(gameDir, { recursive: true });
80
+ fs.renameSync(tmpDir, gameDir);
81
+ // Write .mcp.json at repo root if not already present
82
+ const mcpPath = path.join(repoRoot, ".mcp.json");
83
+ if (!fs.existsSync(mcpPath))
84
+ writeMcpConfig(repoRoot);
85
+ runCommand("git add -A", { cwd: repoRoot });
86
+ gitCommit(`New game: ${slug}`, { cwd: repoRoot });
87
+ return gameDir;
88
+ };
41
89
  /** Main game create wizard */
42
90
  export const gameCreate = async (args) => {
43
91
  const opts = parseArgs(args);
44
92
  const require = createRequire(import.meta.url);
45
93
  const { version } = require("../../../package.json");
46
- p.intro(`Puzzmo Game Importer v${version}`);
47
- // Step 1: Mode selection
94
+ p.intro(`Puzzmo Game Creator v${version}`);
95
+ // Step 1: Detect repo context and choose mode
96
+ const repo = detectRepoContext();
97
+ const isMonorepo = repo.inGitRepo && repo.hasWorkspaces && repo.workspaceFolders.length > 0;
98
+ const modeOptions = [
99
+ { value: "new-repo", label: "Create game in a new repo" },
100
+ { value: "add-to-repo", label: "Add a game to this repo" },
101
+ ];
102
+ if (isMonorepo)
103
+ modeOptions.reverse();
48
104
  const mode = await p.select({
49
105
  message: "How would you like to create your game?",
50
- options: [
51
- { value: "import", label: "Import an HTML game from a web page" },
52
- { value: "new-repo", label: "Create game in a new repo", hint: "coming soon" },
53
- { value: "add-to-repo", label: "Add a game to this repo", hint: "coming soon" },
54
- ],
106
+ options: modeOptions,
55
107
  });
56
108
  if (p.isCancel(mode))
57
109
  process.exit(0);
58
- if (mode !== "import") {
59
- p.log.warn("This mode is not yet supported.");
60
- process.exit(0);
61
- }
62
110
  // Step 2: Source URL
63
111
  let url = opts.url;
64
112
  if (!url) {
@@ -74,7 +122,7 @@ export const gameCreate = async (args) => {
74
122
  s.start(`Downloading ${url}`);
75
123
  const { title } = await downloadPage(url, tmpDir);
76
124
  s.stop("Download complete.");
77
- // Step 4: Game name (default to HTML title if available)
125
+ // Step 4: Game name
78
126
  const defaultName = opts.name || title;
79
127
  const name = (await p.text({
80
128
  message: "Game name",
@@ -83,39 +131,49 @@ export const gameCreate = async (args) => {
83
131
  }));
84
132
  if (p.isCancel(name))
85
133
  process.exit(0);
86
- // Step 5: Move downloaded files to slugified directory
87
134
  const slug = slugify(name);
88
- const gameDir = path.resolve(slug);
89
- if (fs.existsSync(gameDir))
90
- fs.rmSync(gameDir, { recursive: true });
91
- fs.renameSync(tmpDir, gameDir);
92
- // Step 6: Login if token provided
135
+ // Step 5: Login if token provided
93
136
  if (opts.accessToken) {
94
137
  p.log.step("Logging in...");
95
138
  login(opts.accessToken);
96
139
  }
97
- // Step 7: Write .gitignore
98
- fs.writeFileSync(path.join(gameDir, ".gitignore"), ["node_modules", "dist", ".DS_Store", ".yarn", ".pnp.*", ".puzzmo", ""].join("\n"));
99
- // Step 8: Write .mcp.json with dev server config
100
- const token = getToken();
101
- const mcpConfig = {
102
- mcpServers: {
103
- "dev.puzzmo.com": {
104
- type: "http",
105
- // url: "https://dev.puzzmo.com/api/mcp",
106
- url: "https://dev-dj9e.onrender.com/api/mcp",
107
- headers: token ? { Authorization: `Bearer ${token}` } : undefined,
108
- },
109
- },
110
- };
111
- fs.writeFileSync(path.join(gameDir, ".mcp.json"), JSON.stringify(mcpConfig, null, 2) + "\n");
112
- // Step 8: Initialize git
113
- if (!fs.existsSync(path.join(gameDir, ".git"))) {
114
- runCommand("git init", { cwd: gameDir });
115
- runCommand("git add -A", { cwd: gameDir });
116
- gitCommit("Initial game import", { cwd: gameDir });
140
+ // Step 6: Diamond — set up repo or monorepo subdir
141
+ let gameDir;
142
+ if (mode === "new-repo") {
143
+ gameDir = path.resolve(slug);
144
+ if (fs.existsSync(gameDir))
145
+ fs.rmSync(gameDir, { recursive: true });
146
+ fs.renameSync(tmpDir, gameDir);
147
+ setupNewRepo(gameDir);
148
+ }
149
+ else {
150
+ if (!repo.repoRoot) {
151
+ p.log.error("Could not detect repository root. Are you inside a git repo?");
152
+ process.exit(1);
153
+ }
154
+ // Determine which workspace folder to place the game in
155
+ let parentFolder;
156
+ const hasGames = repo.workspaceFolders.includes("games");
157
+ if (hasGames) {
158
+ parentFolder = "games";
159
+ }
160
+ else if (repo.workspaceFolders.length === 0) {
161
+ parentFolder = "games";
162
+ }
163
+ else if (repo.workspaceFolders.length === 1) {
164
+ parentFolder = repo.workspaceFolders[0];
165
+ }
166
+ else {
167
+ parentFolder = (await p.select({
168
+ message: "Which folder should the game be added to?",
169
+ options: repo.workspaceFolders.map((f) => ({ value: f, label: f })),
170
+ }));
171
+ if (p.isCancel(parentFolder))
172
+ process.exit(0);
173
+ }
174
+ gameDir = setupMonorepoGame(tmpDir, slug, repo.repoRoot, parentFolder);
117
175
  }
118
- // Step 9: Detect agent and run skills pipeline
176
+ // Step 7: Detect agent and run skills pipeline
119
177
  const agents = detectAgent();
120
178
  const agentChoices = [
121
179
  ...agents.map((a) => ({ value: a.binary, label: `${a.displayName} (${a.path})` })),
@@ -141,7 +199,8 @@ export const gameCreate = async (args) => {
141
199
  // Done
142
200
  const pm = opts.pm || "npm";
143
201
  const runCmd = pm === "npm" ? "npx" : pm === "yarn" ? "yarn dlx" : "pnpm dlx";
144
- p.note([`cd ${slug}`, `${runCmd} vite # Start development server`, `${runCmd} vite build # Build for production`].join("\n"), "Next steps");
145
- p.outro(`Done! Your game is in ./${slug}/`);
202
+ const relativePath = path.relative(process.cwd(), gameDir);
203
+ p.note([`cd ${relativePath}`, `${runCmd} vite # Start development server`, `${runCmd} vite build # Build for production`].join("\n"), "Next steps");
204
+ p.outro(`Done! Your game is in ./${relativePath}/`);
146
205
  };
147
206
  //# sourceMappingURL=create.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/commands/game/create.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/commands/game/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export type RepoContext = {
2
+ inGitRepo: boolean;
3
+ repoRoot: string | null;
4
+ hasWorkspaces: boolean;
5
+ /** Top-level workspace folders derived from globs (e.g. ["apps", "packages", "games"]) */
6
+ workspaceFolders: string[];
7
+ };
8
+ /** Detects whether we're inside a git repo and if it's a monorepo with workspace folders */
9
+ export declare const detectRepoContext: () => RepoContext;
@@ -0,0 +1,80 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { execSync } from "node:child_process";
4
+ /** Extracts top-level folder names from workspace glob patterns like "apps/*" or "packages/**" */
5
+ const foldersFromGlobs = (globs) => {
6
+ const folders = new Set();
7
+ for (const glob of globs) {
8
+ const topLevel = glob.split("/")[0];
9
+ if (topLevel && !topLevel.includes("*"))
10
+ folders.add(topLevel);
11
+ }
12
+ return [...folders];
13
+ };
14
+ /** Reads workspace globs from package.json (npm/yarn) */
15
+ const readPackageJsonWorkspaces = (repoRoot) => {
16
+ try {
17
+ const pkgPath = path.join(repoRoot, "package.json");
18
+ if (!fs.existsSync(pkgPath))
19
+ return null;
20
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
21
+ const ws = pkg.workspaces;
22
+ if (Array.isArray(ws))
23
+ return ws;
24
+ if (typeof ws === "object" && ws !== null)
25
+ return ws.packages ?? [];
26
+ }
27
+ catch {
28
+ // invalid JSON
29
+ }
30
+ return null;
31
+ };
32
+ /** Reads workspace globs from pnpm-workspace.yaml */
33
+ const readPnpmWorkspaces = (repoRoot) => {
34
+ try {
35
+ const yamlPath = path.join(repoRoot, "pnpm-workspace.yaml");
36
+ if (!fs.existsSync(yamlPath))
37
+ return null;
38
+ const content = fs.readFileSync(yamlPath, "utf-8");
39
+ // Simple YAML parsing for the packages array — avoids a yaml dependency
40
+ const packages = [];
41
+ let inPackages = false;
42
+ for (const line of content.split("\n")) {
43
+ if (/^packages\s*:/.test(line)) {
44
+ inPackages = true;
45
+ continue;
46
+ }
47
+ if (inPackages) {
48
+ const match = line.match(/^\s+-\s+['"]?([^'"#]+)['"]?/);
49
+ if (match)
50
+ packages.push(match[1].trim());
51
+ else if (/^\S/.test(line))
52
+ break;
53
+ }
54
+ }
55
+ return packages.length > 0 ? packages : null;
56
+ }
57
+ catch {
58
+ // missing or unreadable
59
+ }
60
+ return null;
61
+ };
62
+ /** Detects whether we're inside a git repo and if it's a monorepo with workspace folders */
63
+ export const detectRepoContext = () => {
64
+ let repoRoot = null;
65
+ try {
66
+ const result = execSync("git rev-parse --show-toplevel", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
67
+ repoRoot = result.trim();
68
+ }
69
+ catch {
70
+ return { inGitRepo: false, repoRoot: null, hasWorkspaces: false, workspaceFolders: [] };
71
+ }
72
+ const globs = readPackageJsonWorkspaces(repoRoot) ?? readPnpmWorkspaces(repoRoot);
73
+ const hasWorkspaces = globs !== null;
74
+ const workspaceFolders = globs ? foldersFromGlobs(globs) : [];
75
+ // Also recognize a games/ directory even without formal workspaces config
76
+ if (!workspaceFolders.includes("games") && fs.existsSync(path.join(repoRoot, "games")))
77
+ workspaceFolders.push("games");
78
+ return { inGitRepo: true, repoRoot, hasWorkspaces: hasWorkspaces || workspaceFolders.length > 0, workspaceFolders };
79
+ };
80
+ //# sourceMappingURL=detectRepo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detectRepo.js","sourceRoot":"","sources":["../../../src/commands/game/detectRepo.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puzzmo/cli",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "puzzmo": "./lib/index.js"
@@ -9,6 +9,7 @@ import { runCommand, gitCommit } from "../../util/exec.js"
9
9
  import { runSkillsPipelineTUI } from "../../skills/runner.js"
10
10
  import { login } from "../login.js"
11
11
  import { getToken } from "../../util/config.js"
12
+ import { detectRepoContext } from "./detectRepo.js"
12
13
 
13
14
  type CreateOptions = {
14
15
  name?: string
@@ -42,12 +43,64 @@ const parseArgs = (args: string[]): CreateOptions => {
42
43
  return opts
43
44
  }
44
45
 
45
- /** Converts a string to a URL-friendly slug */
46
- const slugify = (str: string): string =>
47
- str
46
+ /** Converts a string to a URL-friendly slug (matches packages/shared/slugify.ts) */
47
+ const slugify = (text: string) =>
48
+ text
49
+ .toString()
50
+ .normalize("NFD")
51
+ .replace(/[\u0300-\u036f]/g, "")
48
52
  .toLowerCase()
49
- .replace(/[^a-z0-9]+/g, "-")
50
- .replace(/^-+|-+$/g, "")
53
+ .trim()
54
+ .replace(/\s+/g, "-")
55
+ .replace(/[^\w-]+/g, "")
56
+ .replace(/--+/g, "-")
57
+
58
+ /** Writes .mcp.json with dev server config */
59
+ const writeMcpConfig = (dir: string) => {
60
+ const token = getToken()
61
+ const mcpConfig = {
62
+ mcpServers: {
63
+ "dev.puzzmo.com": {
64
+ type: "http",
65
+ // url: "https://dev.puzzmo.com/api/mcp",
66
+ url: "https://dev-dj9e.onrender.com/api/mcp",
67
+ headers: token ? { Authorization: `Bearer ${token}` } : undefined,
68
+ },
69
+ },
70
+ }
71
+ fs.writeFileSync(path.join(dir, ".mcp.json"), JSON.stringify(mcpConfig, null, 2) + "\n")
72
+ }
73
+
74
+ /** Sets up a standalone new repo for the game */
75
+ const setupNewRepo = (gameDir: string) => {
76
+ fs.writeFileSync(path.join(gameDir, ".gitignore"), ["node_modules", "dist", ".DS_Store", ".yarn", ".pnp.*", ".puzzmo", ""].join("\n"))
77
+ writeMcpConfig(gameDir)
78
+
79
+ if (!fs.existsSync(path.join(gameDir, ".git"))) {
80
+ runCommand("git init", { cwd: gameDir })
81
+ runCommand("git add -A", { cwd: gameDir })
82
+ gitCommit("Initial game import", { cwd: gameDir })
83
+ }
84
+ }
85
+
86
+ /** Sets up the game inside an existing monorepo */
87
+ const setupMonorepoGame = (tmpDir: string, slug: string, repoRoot: string, parentFolder: string): string => {
88
+ const parentDir = path.join(repoRoot, parentFolder)
89
+ if (!fs.existsSync(parentDir)) fs.mkdirSync(parentDir, { recursive: true })
90
+
91
+ const gameDir = path.join(parentDir, slug)
92
+ if (fs.existsSync(gameDir)) fs.rmSync(gameDir, { recursive: true })
93
+ fs.renameSync(tmpDir, gameDir)
94
+
95
+ // Write .mcp.json at repo root if not already present
96
+ const mcpPath = path.join(repoRoot, ".mcp.json")
97
+ if (!fs.existsSync(mcpPath)) writeMcpConfig(repoRoot)
98
+
99
+ runCommand("git add -A", { cwd: repoRoot })
100
+ gitCommit(`New game: ${slug}`, { cwd: repoRoot })
101
+
102
+ return gameDir
103
+ }
51
104
 
52
105
  /** Main game create wizard */
53
106
  export const gameCreate = async (args: string[]) => {
@@ -55,25 +108,25 @@ export const gameCreate = async (args: string[]) => {
55
108
 
56
109
  const require = createRequire(import.meta.url)
57
110
  const { version } = require("../../../package.json")
58
- p.intro(`Puzzmo Game Importer v${version}`)
111
+ p.intro(`Puzzmo Game Creator v${version}`)
112
+
113
+ // Step 1: Detect repo context and choose mode
114
+ const repo = detectRepoContext()
115
+ const isMonorepo = repo.inGitRepo && repo.hasWorkspaces && repo.workspaceFolders.length > 0
116
+
117
+ const modeOptions = [
118
+ { value: "new-repo" as const, label: "Create game in a new repo" },
119
+ { value: "add-to-repo" as const, label: "Add a game to this repo" },
120
+ ]
121
+ if (isMonorepo) modeOptions.reverse()
59
122
 
60
- // Step 1: Mode selection
61
123
  const mode = await p.select({
62
124
  message: "How would you like to create your game?",
63
- options: [
64
- { value: "import", label: "Import an HTML game from a web page" },
65
- { value: "new-repo", label: "Create game in a new repo", hint: "coming soon" },
66
- { value: "add-to-repo", label: "Add a game to this repo", hint: "coming soon" },
67
- ],
125
+ options: modeOptions,
68
126
  })
69
127
 
70
128
  if (p.isCancel(mode)) process.exit(0)
71
129
 
72
- if (mode !== "import") {
73
- p.log.warn("This mode is not yet supported.")
74
- process.exit(0)
75
- }
76
-
77
130
  // Step 2: Source URL
78
131
  let url = opts.url
79
132
  if (!url) {
@@ -89,7 +142,7 @@ export const gameCreate = async (args: string[]) => {
89
142
  const { title } = await downloadPage(url, tmpDir)
90
143
  s.stop("Download complete.")
91
144
 
92
- // Step 4: Game name (default to HTML title if available)
145
+ // Step 4: Game name
93
146
  const defaultName = opts.name || title
94
147
  const name = (await p.text({
95
148
  message: "Game name",
@@ -98,44 +151,50 @@ export const gameCreate = async (args: string[]) => {
98
151
  })) as string
99
152
  if (p.isCancel(name)) process.exit(0)
100
153
 
101
- // Step 5: Move downloaded files to slugified directory
102
154
  const slug = slugify(name)
103
- const gameDir = path.resolve(slug)
104
- if (fs.existsSync(gameDir)) fs.rmSync(gameDir, { recursive: true })
105
- fs.renameSync(tmpDir, gameDir)
106
155
 
107
- // Step 6: Login if token provided
156
+ // Step 5: Login if token provided
108
157
  if (opts.accessToken) {
109
158
  p.log.step("Logging in...")
110
159
  login(opts.accessToken)
111
160
  }
112
161
 
113
- // Step 7: Write .gitignore
114
- fs.writeFileSync(path.join(gameDir, ".gitignore"), ["node_modules", "dist", ".DS_Store", ".yarn", ".pnp.*", ".puzzmo", ""].join("\n"))
162
+ // Step 6: Diamond — set up repo or monorepo subdir
163
+ let gameDir: string
115
164
 
116
- // Step 8: Write .mcp.json with dev server config
117
- const token = getToken()
118
- const mcpConfig = {
119
- mcpServers: {
120
- "dev.puzzmo.com": {
121
- type: "http",
122
- // url: "https://dev.puzzmo.com/api/mcp",
123
- url: "https://dev-dj9e.onrender.com/api/mcp",
124
- headers: token ? { Authorization: `Bearer ${token}` } : undefined,
125
- },
126
- },
127
- }
165
+ if (mode === "new-repo") {
166
+ gameDir = path.resolve(slug)
167
+ if (fs.existsSync(gameDir)) fs.rmSync(gameDir, { recursive: true })
168
+ fs.renameSync(tmpDir, gameDir)
169
+ setupNewRepo(gameDir)
170
+ } else {
171
+ if (!repo.repoRoot) {
172
+ p.log.error("Could not detect repository root. Are you inside a git repo?")
173
+ process.exit(1)
174
+ }
128
175
 
129
- fs.writeFileSync(path.join(gameDir, ".mcp.json"), JSON.stringify(mcpConfig, null, 2) + "\n")
176
+ // Determine which workspace folder to place the game in
177
+ let parentFolder: string
178
+ const hasGames = repo.workspaceFolders.includes("games")
179
+
180
+ if (hasGames) {
181
+ parentFolder = "games"
182
+ } else if (repo.workspaceFolders.length === 0) {
183
+ parentFolder = "games"
184
+ } else if (repo.workspaceFolders.length === 1) {
185
+ parentFolder = repo.workspaceFolders[0]
186
+ } else {
187
+ parentFolder = (await p.select({
188
+ message: "Which folder should the game be added to?",
189
+ options: repo.workspaceFolders.map((f) => ({ value: f, label: f })),
190
+ })) as string
191
+ if (p.isCancel(parentFolder)) process.exit(0)
192
+ }
130
193
 
131
- // Step 8: Initialize git
132
- if (!fs.existsSync(path.join(gameDir, ".git"))) {
133
- runCommand("git init", { cwd: gameDir })
134
- runCommand("git add -A", { cwd: gameDir })
135
- gitCommit("Initial game import", { cwd: gameDir })
194
+ gameDir = setupMonorepoGame(tmpDir, slug, repo.repoRoot, parentFolder)
136
195
  }
137
196
 
138
- // Step 9: Detect agent and run skills pipeline
197
+ // Step 7: Detect agent and run skills pipeline
139
198
  const agents = detectAgent()
140
199
  const agentChoices = [
141
200
  ...agents.map((a) => ({ value: a.binary, label: `${a.displayName} (${a.path})` })),
@@ -161,11 +220,12 @@ export const gameCreate = async (args: string[]) => {
161
220
  // Done
162
221
  const pm = opts.pm || "npm"
163
222
  const runCmd = pm === "npm" ? "npx" : pm === "yarn" ? "yarn dlx" : "pnpm dlx"
223
+ const relativePath = path.relative(process.cwd(), gameDir)
164
224
 
165
225
  p.note(
166
- [`cd ${slug}`, `${runCmd} vite # Start development server`, `${runCmd} vite build # Build for production`].join("\n"),
226
+ [`cd ${relativePath}`, `${runCmd} vite # Start development server`, `${runCmd} vite build # Build for production`].join("\n"),
167
227
  "Next steps",
168
228
  )
169
229
 
170
- p.outro(`Done! Your game is in ./${slug}/`)
230
+ p.outro(`Done! Your game is in ./${relativePath}/`)
171
231
  }
@@ -0,0 +1,84 @@
1
+ import fs from "node:fs"
2
+ import path from "node:path"
3
+ import { execSync } from "node:child_process"
4
+
5
+ export type RepoContext = {
6
+ inGitRepo: boolean
7
+ repoRoot: string | null
8
+ hasWorkspaces: boolean
9
+ /** Top-level workspace folders derived from globs (e.g. ["apps", "packages", "games"]) */
10
+ workspaceFolders: string[]
11
+ }
12
+
13
+ /** Extracts top-level folder names from workspace glob patterns like "apps/*" or "packages/**" */
14
+ const foldersFromGlobs = (globs: string[]): string[] => {
15
+ const folders = new Set<string>()
16
+ for (const glob of globs) {
17
+ const topLevel = glob.split("/")[0]
18
+ if (topLevel && !topLevel.includes("*")) folders.add(topLevel)
19
+ }
20
+ return [...folders]
21
+ }
22
+
23
+ /** Reads workspace globs from package.json (npm/yarn) */
24
+ const readPackageJsonWorkspaces = (repoRoot: string): string[] | null => {
25
+ try {
26
+ const pkgPath = path.join(repoRoot, "package.json")
27
+ if (!fs.existsSync(pkgPath)) return null
28
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
29
+ const ws = pkg.workspaces
30
+ if (Array.isArray(ws)) return ws
31
+ if (typeof ws === "object" && ws !== null) return ws.packages ?? []
32
+ } catch {
33
+ // invalid JSON
34
+ }
35
+ return null
36
+ }
37
+
38
+ /** Reads workspace globs from pnpm-workspace.yaml */
39
+ const readPnpmWorkspaces = (repoRoot: string): string[] | null => {
40
+ try {
41
+ const yamlPath = path.join(repoRoot, "pnpm-workspace.yaml")
42
+ if (!fs.existsSync(yamlPath)) return null
43
+ const content = fs.readFileSync(yamlPath, "utf-8")
44
+ // Simple YAML parsing for the packages array — avoids a yaml dependency
45
+ const packages: string[] = []
46
+ let inPackages = false
47
+ for (const line of content.split("\n")) {
48
+ if (/^packages\s*:/.test(line)) {
49
+ inPackages = true
50
+ continue
51
+ }
52
+ if (inPackages) {
53
+ const match = line.match(/^\s+-\s+['"]?([^'"#]+)['"]?/)
54
+ if (match) packages.push(match[1].trim())
55
+ else if (/^\S/.test(line)) break
56
+ }
57
+ }
58
+ return packages.length > 0 ? packages : null
59
+ } catch {
60
+ // missing or unreadable
61
+ }
62
+ return null
63
+ }
64
+
65
+ /** Detects whether we're inside a git repo and if it's a monorepo with workspace folders */
66
+ export const detectRepoContext = (): RepoContext => {
67
+ let repoRoot: string | null = null
68
+
69
+ try {
70
+ const result = execSync("git rev-parse --show-toplevel", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] })
71
+ repoRoot = result.trim()
72
+ } catch {
73
+ return { inGitRepo: false, repoRoot: null, hasWorkspaces: false, workspaceFolders: [] }
74
+ }
75
+
76
+ const globs = readPackageJsonWorkspaces(repoRoot) ?? readPnpmWorkspaces(repoRoot)
77
+ const hasWorkspaces = globs !== null
78
+ const workspaceFolders = globs ? foldersFromGlobs(globs) : []
79
+
80
+ // Also recognize a games/ directory even without formal workspaces config
81
+ if (!workspaceFolders.includes("games") && fs.existsSync(path.join(repoRoot, "games"))) workspaceFolders.push("games")
82
+
83
+ return { inGitRepo: true, repoRoot, hasWorkspaces: hasWorkspaces || workspaceFolders.length > 0, workspaceFolders }
84
+ }