@pixel-point/toolcraft 0.0.12 → 0.0.14

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 (37) hide show
  1. package/README.md +4 -2
  2. package/package.json +1 -1
  3. package/src/cli.mjs +18 -45
  4. package/src/cli.test.mjs +141 -6
  5. package/src/command-runner.mjs +71 -0
  6. package/src/dependency-install.mjs +87 -0
  7. package/src/generate.mjs +32 -4
  8. package/src/generate.test.mjs +116 -15
  9. package/src/package-json.mjs +11 -2
  10. package/src/package-json.test.mjs +27 -0
  11. package/src/package-manager.mjs +123 -0
  12. package/src/package-manager.test.mjs +80 -0
  13. package/templates/runtime/contracts/component-contracts.test.ts +1 -1
  14. package/templates/runtime/contracts/component-contracts.ts +1 -1
  15. package/templates/starter/AGENTS.md +4 -3
  16. package/templates/starter/docs/toolcraft/README.md +15 -0
  17. package/templates/starter/docs/toolcraft/acceptance-testing.md +2 -0
  18. package/templates/starter/docs/toolcraft/assembly-workflow.md +35 -171
  19. package/templates/starter/docs/toolcraft/component-rules.md +12 -188
  20. package/templates/starter/docs/toolcraft/core/control-selection.md +93 -0
  21. package/templates/starter/docs/toolcraft/core/layout.md +104 -0
  22. package/templates/starter/docs/toolcraft/core/media-upload.md +85 -0
  23. package/templates/starter/docs/toolcraft/core/performance.md +83 -0
  24. package/templates/starter/docs/toolcraft/core/reference-study.md +115 -0
  25. package/templates/starter/docs/toolcraft/core/runtime-boundary.md +53 -0
  26. package/templates/starter/docs/toolcraft/core/setup-export.md +86 -0
  27. package/templates/starter/docs/toolcraft/core/timeline-animation.md +67 -0
  28. package/templates/starter/docs/toolcraft/custom-controls.md +2 -0
  29. package/templates/starter/docs/toolcraft/performance.md +2 -0
  30. package/templates/starter/docs/toolcraft/renderer-technique.md +2 -0
  31. package/templates/starter/docs/toolcraft/schema-reference.md +117 -367
  32. package/templates/starter/docs/toolcraft/workflow.md +12 -10
  33. package/templates/starter/package.json +1 -0
  34. package/templates/starter/scripts/check-toolcraft-docs.mjs +28 -6
  35. package/templates/starter/scripts/run-vite-on-free-port.mjs +25 -6
  36. package/templates/starter/src/app/starter-acceptance.test.ts +24 -15
  37. package/templates/starter/src/app/starter-performance.test.ts +17 -7
package/README.md CHANGED
@@ -19,7 +19,7 @@ tools for client work.
19
19
  npx @pixel-point/toolcraft create
20
20
  ```
21
21
 
22
- The create command uses the current directory when no target directory is passed, prompts for missing project values in an interactive terminal, generates the app, runs `pnpm install`, then prints the command to start the dev server.
22
+ The create command uses the current directory when no target directory is passed, prompts for missing project values in an interactive terminal, generates the app, runs dependency installation with the package manager that launched the CLI, then prints the command to start the dev server.
23
23
 
24
24
  After dependencies are installed, Toolcraft installs the required workflow skills
25
25
  in a batch through the `skills` CLI. The skill installer uses the same agent,
@@ -30,7 +30,7 @@ Example:
30
30
  ```bash
31
31
  npx @pixel-point/toolcraft create my-ascii-tool
32
32
  cd my-ascii-tool
33
- pnpm dev
33
+ npm run dev
34
34
  ```
35
35
 
36
36
  Then open the generated folder in Codex, Claude Code, Cursor, or another AI
@@ -46,6 +46,8 @@ Scripted usage:
46
46
  npx @pixel-point/toolcraft create my-toolcraft-app --name my-toolcraft-app --yes --force
47
47
  ```
48
48
 
49
+ Toolcraft detects `npm` and `pnpm` from the package manager user agent. For example, `npx @pixel-point/toolcraft create` generates npm-flavored setup commands, while `pnpm dlx @pixel-point/toolcraft create` generates pnpm-flavored setup commands.
50
+
49
51
  Install Toolcraft skills to specific agents or locations:
50
52
 
51
53
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixel-point/toolcraft",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "license": "SEE LICENSE IN LICENSE.md",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -4,7 +4,6 @@ import path from "node:path";
4
4
  import readline from "node:readline/promises";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
- import spawn from "cross-spawn";
8
7
  import {
9
8
  cancel,
10
9
  confirm,
@@ -16,8 +15,14 @@ import {
16
15
  text,
17
16
  } from "@clack/prompts";
18
17
 
18
+ import { runCommand } from "./command-runner.mjs";
19
19
  import { pathExists } from "./copy-recursive.mjs";
20
+ import { installGeneratedAppDependencies } from "./dependency-install.mjs";
20
21
  import { generateToolcraft } from "./generate.mjs";
22
+ import {
23
+ createRunScriptCommand,
24
+ detectPackageManager,
25
+ } from "./package-manager.mjs";
21
26
  import { sanitizePackageName } from "./package-json.mjs";
22
27
 
23
28
  const DEFAULT_PROJECT_NAME = "my-toolcraft-app";
@@ -355,31 +360,6 @@ export async function resolveCreateOptions(parsedOptions, context = {}) {
355
360
  };
356
361
  }
357
362
 
358
- function runCommand(command, args, options = {}, context = {}) {
359
- if (context.runCommand) {
360
- return context.runCommand(command, args, options);
361
- }
362
-
363
- return new Promise((resolve, reject) => {
364
- const child = spawn(command, args, {
365
- cwd: options.cwd,
366
- env: options.env,
367
- shell: options.shell ?? false,
368
- stdio: options.stdio ?? "inherit",
369
- });
370
-
371
- child.on("error", reject);
372
- child.on("exit", (exitCode) => {
373
- if (exitCode === 0) {
374
- resolve();
375
- return;
376
- }
377
-
378
- reject(new Error(`${command} ${args.join(" ")} failed with exit code ${exitCode}.`));
379
- });
380
- });
381
- }
382
-
383
363
  function getSkillsCliPath(context = {}) {
384
364
  if (context.skillsCliPath) {
385
365
  return context.skillsCliPath;
@@ -459,7 +439,7 @@ function writeFinalSummary(stdout, result, createOptions) {
459
439
  : result.relativeTargetDir;
460
440
  writeLine(stdout, ` cd ${displayTargetDir}`);
461
441
  }
462
- writeLine(stdout, " pnpm dev");
442
+ writeLine(stdout, ` ${createRunScriptCommand(createOptions.packageManager, "dev")}`);
463
443
  }
464
444
 
465
445
  export async function runCreateCommand(parsedOptions, context = {}) {
@@ -471,6 +451,7 @@ export async function runCreateCommand(parsedOptions, context = {}) {
471
451
  intro("Create Toolcraft app");
472
452
  }
473
453
 
454
+ const packageManager = detectPackageManager(context.env ?? process.env);
474
455
  const createOptions = await resolveCreateOptions(parsedOptions, context);
475
456
 
476
457
  const result = await runWithSpinner(context, "Creating Toolcraft app...", "Created app", () =>
@@ -478,27 +459,19 @@ export async function runCreateCommand(parsedOptions, context = {}) {
478
459
  cwd,
479
460
  force: createOptions.force,
480
461
  name: createOptions.name,
462
+ packageManager,
481
463
  targetDir: createOptions.targetDir,
482
464
  }),
483
465
  );
484
466
 
485
467
  if (createOptions.install) {
486
- writeLine(stdout, "");
487
- writeLine(stdout, "Installing dependencies with pnpm...");
488
- await runCommand(
489
- "pnpm",
490
- ["install"],
491
- {
492
- cwd: result.targetDir,
493
- env: {
494
- ...process.env,
495
- ...(context.env ?? {}),
496
- },
497
- shell: false,
498
- stdio: "inherit",
499
- },
468
+ await installGeneratedAppDependencies({
500
469
  context,
501
- );
470
+ packageManager,
471
+ result,
472
+ skillsRequested: createOptions.skills,
473
+ stdout,
474
+ });
502
475
  }
503
476
 
504
477
  if (createOptions.skills) {
@@ -510,12 +483,12 @@ export async function runCreateCommand(parsedOptions, context = {}) {
510
483
  if (usingClack) {
511
484
  const nextSteps = [
512
485
  ...(result.targetDir === cwd ? [] : [`cd ${result.relativeTargetDir}`]),
513
- "pnpm dev",
486
+ createRunScriptCommand(packageManager, "dev"),
514
487
  ].join("\n");
515
488
  note(nextSteps, "Next steps");
516
489
  outro(`Created ${result.packageName}`);
517
490
  } else {
518
- writeFinalSummary(stdout, result, { ...createOptions, cwd });
491
+ writeFinalSummary(stdout, result, { ...createOptions, cwd, packageManager });
519
492
  }
520
493
 
521
494
  return result;
@@ -550,7 +523,7 @@ Options:
550
523
  --copy Copy skills instead of using the skills CLI default link behavior.
551
524
  --all Install Toolcraft skills to all supported agents without skills prompts.
552
525
  --no-skills Skip Toolcraft skills installation.
553
- --no-install Skip automatic pnpm install. Intended for local CLI tests and automation.
526
+ --no-install Skip automatic dependency installation. Intended for local CLI tests and automation.
554
527
  --help, -h Show this help message.`;
555
528
  }
556
529
 
package/src/cli.test.mjs CHANGED
@@ -206,7 +206,7 @@ describe("runToolcraftCli", () => {
206
206
  assert.ok(await fs.stat(path.join(tempRoot, "src/app/app-schema.ts")));
207
207
  assert.match(stdout.text, new RegExp(`Created ${path.basename(tempRoot).toLowerCase()}`));
208
208
  assert.doesNotMatch(stdout.text, /cd /);
209
- assert.match(stdout.text, /pnpm dev/);
209
+ assert.match(stdout.text, /npm run dev/);
210
210
  assert.equal(stderr.text, "");
211
211
  });
212
212
 
@@ -239,12 +239,12 @@ describe("runToolcraftCli", () => {
239
239
  assert.ok(await fs.stat(path.join(tempRoot, "generated-app/src/app/app-schema.ts")));
240
240
  assert.match(stdout.text, /Created generated-app/);
241
241
  assert.match(stdout.text, /cd generated-app/);
242
- assert.match(stdout.text, /pnpm dev/);
242
+ assert.match(stdout.text, /npm run dev/);
243
243
  assert.doesNotMatch(stdout.text, /pnpm verify:final/);
244
244
  assert.equal(stderr.text, "");
245
245
  });
246
246
 
247
- it("runs pnpm install and Toolcraft skills install by default after creating the app", async () => {
247
+ it("runs npm install and Toolcraft skills install when launched through npx", async () => {
248
248
  const tempRoot = await createTempRoot();
249
249
  const stdout = createWritableCapture();
250
250
  const installs = [];
@@ -253,7 +253,9 @@ describe("runToolcraftCli", () => {
253
253
 
254
254
  await runToolcraftCli(["create", "install-app", "--name", "Install App", "--yes", "--force"], {
255
255
  cwd: tempRoot,
256
- env: {},
256
+ env: {
257
+ npm_config_user_agent: "npm/10.9.4 node/v24.4.1 darwin arm64",
258
+ },
257
259
  stdout,
258
260
  skillsCliPath,
259
261
  throwOnError: true,
@@ -266,7 +268,7 @@ describe("runToolcraftCli", () => {
266
268
  assert.deepEqual(installs, [
267
269
  {
268
270
  args: ["install"],
269
- command: "pnpm",
271
+ command: "npm",
270
272
  cwd: path.join(tempRoot, "install-app"),
271
273
  shell: false,
272
274
  },
@@ -277,11 +279,144 @@ describe("runToolcraftCli", () => {
277
279
  shell: false,
278
280
  },
279
281
  ]);
280
- assert.match(stdout.text, /Installing dependencies with pnpm/);
282
+ assert.match(stdout.text, /Installing dependencies with npm/);
281
283
  assert.match(stdout.text, /Installing Toolcraft skills/);
284
+ assert.match(stdout.text, /npm run dev/);
285
+
286
+ const packageJson = JSON.parse(
287
+ await fs.readFile(path.join(tempRoot, "install-app/package.json"), "utf8"),
288
+ );
289
+ assert.equal(
290
+ packageJson.scripts["verify:final"],
291
+ "npm run ai:check && npm run test && npm run build && npm run test:browser",
292
+ );
293
+
294
+ const playwrightConfigSource = await fs.readFile(
295
+ path.join(tempRoot, "install-app/playwright.config.ts"),
296
+ "utf8",
297
+ );
298
+ assert.match(playwrightConfigSource, /npm exec -- vite dev/);
299
+ });
300
+
301
+ it("runs pnpm install when launched through pnpm", async () => {
302
+ const tempRoot = await createTempRoot();
303
+ const stdout = createWritableCapture();
304
+ const installs = [];
305
+
306
+ await runToolcraftCli(
307
+ ["create", "pnpm-app", "--name", "Pnpm App", "--yes", "--force", "--no-skills"],
308
+ {
309
+ cwd: tempRoot,
310
+ env: {
311
+ npm_config_user_agent: "pnpm/10.0.0 npm/? node/v24.4.1 darwin arm64",
312
+ },
313
+ stdout,
314
+ throwOnError: true,
315
+ async runCommand(command, args, options) {
316
+ installs.push({ args, command, cwd: options.cwd, shell: options.shell });
317
+ },
318
+ },
319
+ );
320
+
321
+ assert.deepEqual(installs, [
322
+ {
323
+ args: ["install"],
324
+ command: "pnpm",
325
+ cwd: path.join(tempRoot, "pnpm-app"),
326
+ shell: false,
327
+ },
328
+ ]);
329
+ assert.match(stdout.text, /Installing dependencies with pnpm/);
282
330
  assert.match(stdout.text, /pnpm dev/);
283
331
  });
284
332
 
333
+ it("reports recovery steps when dependency installation command is missing", async () => {
334
+ const tempRoot = await createTempRoot();
335
+ const stdout = createWritableCapture();
336
+ const stderr = createWritableCapture();
337
+ const commands = [];
338
+ let exitCode;
339
+
340
+ const result = await runToolcraftCli(
341
+ ["create", "missing-npm-app", "--name", "Missing Npm App", "--yes", "--force"],
342
+ {
343
+ cwd: tempRoot,
344
+ env: {
345
+ npm_config_user_agent: "npm/10.9.4 node/v24.4.1 darwin arm64",
346
+ },
347
+ stderr,
348
+ stdout,
349
+ setExitCode(value) {
350
+ exitCode = value;
351
+ },
352
+ async runCommand(command, args, options) {
353
+ commands.push({ args, command, cwd: options.cwd });
354
+ const error = new Error("spawn npm ENOENT");
355
+ error.code = "ENOENT";
356
+ throw error;
357
+ },
358
+ },
359
+ );
360
+
361
+ assert.equal(result.ok, false);
362
+ assert.equal(exitCode, 1);
363
+ assert.deepEqual(commands, [
364
+ {
365
+ args: ["install"],
366
+ command: "npm",
367
+ cwd: path.join(tempRoot, "missing-npm-app"),
368
+ },
369
+ ]);
370
+ assert.ok(await fs.stat(path.join(tempRoot, "missing-npm-app/src/app/app-schema.ts")));
371
+ assert.match(stderr.text, /Toolcraft app created at .*missing-npm-app, but setup did not finish/);
372
+ assert.match(stderr.text, /npm was not found in PATH/);
373
+ assert.match(stderr.text, /cd .*missing-npm-app/);
374
+ assert.match(stderr.text, /npm install/);
375
+ assert.match(stderr.text, /Toolcraft skills were not installed/);
376
+ assert.match(stderr.text, /npm run dev/);
377
+ assert.doesNotMatch(stdout.text, /Installing Toolcraft skills/);
378
+ });
379
+
380
+ it("does not mention skipped skills when dependency installation fails with --no-skills", async () => {
381
+ const tempRoot = await createTempRoot();
382
+ const stdout = createWritableCapture();
383
+ const stderr = createWritableCapture();
384
+ let exitCode;
385
+
386
+ const result = await runToolcraftCli(
387
+ [
388
+ "create",
389
+ "missing-npm-no-skills-app",
390
+ "--name",
391
+ "Missing Npm No Skills App",
392
+ "--yes",
393
+ "--force",
394
+ "--no-skills",
395
+ ],
396
+ {
397
+ cwd: tempRoot,
398
+ env: {
399
+ npm_config_user_agent: "npm/10.9.4 node/v24.4.1 darwin arm64",
400
+ },
401
+ stderr,
402
+ stdout,
403
+ setExitCode(value) {
404
+ exitCode = value;
405
+ },
406
+ async runCommand() {
407
+ const error = new Error("spawn npm ENOENT");
408
+ error.code = "ENOENT";
409
+ throw error;
410
+ },
411
+ },
412
+ );
413
+
414
+ assert.equal(result.ok, false);
415
+ assert.equal(exitCode, 1);
416
+ assert.match(stderr.text, /npm was not found in PATH/);
417
+ assert.doesNotMatch(stderr.text, /Toolcraft skills were not installed/);
418
+ });
419
+
285
420
  it("forwards skills add compatible options", async () => {
286
421
  const tempRoot = await createTempRoot();
287
422
  const skillsCommands = [];
@@ -0,0 +1,71 @@
1
+ import spawn from "cross-spawn";
2
+
3
+ export class CommandRunError extends Error {
4
+ constructor(command, args, options, details = {}) {
5
+ super(details.message);
6
+ this.name = "CommandRunError";
7
+ this.args = args;
8
+ this.code = details.code;
9
+ this.command = command;
10
+ this.cwd = options.cwd;
11
+ this.exitCode = details.exitCode;
12
+ this.signal = details.signal;
13
+ }
14
+ }
15
+
16
+ export function getErrorCode(error) {
17
+ return error && typeof error === "object" ? error.code : undefined;
18
+ }
19
+
20
+ export function runCommand(command, args, options = {}, context = {}) {
21
+ if (context.runCommand) {
22
+ return context.runCommand(command, args, options);
23
+ }
24
+
25
+ return new Promise((resolve, reject) => {
26
+ let settled = false;
27
+ const settle = (callback) => {
28
+ if (settled) {
29
+ return;
30
+ }
31
+
32
+ settled = true;
33
+ callback();
34
+ };
35
+
36
+ const child = spawn(command, args, {
37
+ cwd: options.cwd,
38
+ env: options.env,
39
+ shell: options.shell ?? false,
40
+ stdio: options.stdio ?? "inherit",
41
+ });
42
+
43
+ child.on("error", (error) => {
44
+ settle(() =>
45
+ reject(
46
+ new CommandRunError(command, args, options, {
47
+ code: error.code,
48
+ message:
49
+ error.code === "ENOENT" ? `Command not found: ${command}` : error.message,
50
+ }),
51
+ ),
52
+ );
53
+ });
54
+ child.on("close", (exitCode, signal) => {
55
+ settle(() => {
56
+ if (exitCode === 0) {
57
+ resolve();
58
+ return;
59
+ }
60
+
61
+ reject(
62
+ new CommandRunError(command, args, options, {
63
+ exitCode,
64
+ message: `${command} ${args.join(" ")} failed with exit code ${exitCode}.`,
65
+ signal,
66
+ }),
67
+ );
68
+ });
69
+ });
70
+ });
71
+ }
@@ -0,0 +1,87 @@
1
+ import { getErrorCode, runCommand } from "./command-runner.mjs";
2
+ import {
3
+ createInstallCommand,
4
+ createInstallHelp,
5
+ createRunScriptCommand,
6
+ normalizePackageManager,
7
+ } from "./package-manager.mjs";
8
+
9
+ function writeLine(stream, message = "") {
10
+ stream.write(`${message}\n`);
11
+ }
12
+
13
+ export function createDependencyInstallError(
14
+ error,
15
+ result,
16
+ packageManager,
17
+ { skillsRequested = true } = {},
18
+ ) {
19
+ const normalizedPackageManager = normalizePackageManager(packageManager);
20
+ const installCommand = `${normalizedPackageManager} install`;
21
+ const devCommand = createRunScriptCommand(normalizedPackageManager, "dev");
22
+ const commandMissing = getErrorCode(error) === "ENOENT";
23
+ const lines = [`Toolcraft app created at ${result.targetDir}, but setup did not finish.`, ""];
24
+
25
+ if (commandMissing) {
26
+ lines.push(
27
+ `Dependency installation failed because ${normalizedPackageManager} was not found in PATH.`,
28
+ "",
29
+ `Install ${normalizedPackageManager}:`,
30
+ "",
31
+ ...createInstallHelp(normalizedPackageManager),
32
+ );
33
+ } else {
34
+ lines.push(
35
+ "Dependency installation failed while running:",
36
+ "",
37
+ ` ${installCommand}`,
38
+ "",
39
+ `Original error: ${error instanceof Error ? error.message : String(error)}`,
40
+ );
41
+ }
42
+
43
+ lines.push("", "Then finish setup:", "", ` cd ${result.targetDir}`, ` ${installCommand}`);
44
+
45
+ if (skillsRequested) {
46
+ lines.push("", "Toolcraft skills were not installed because dependency installation failed.");
47
+ }
48
+
49
+ lines.push("", "After setup, start the app with:", "", ` ${devCommand}`);
50
+
51
+ return new Error(lines.join("\n"));
52
+ }
53
+
54
+ export async function installGeneratedAppDependencies({
55
+ context = {},
56
+ packageManager,
57
+ result,
58
+ skillsRequested = true,
59
+ stdout,
60
+ }) {
61
+ const normalizedPackageManager = normalizePackageManager(packageManager);
62
+ const installCommand = createInstallCommand(normalizedPackageManager);
63
+
64
+ writeLine(stdout, "");
65
+ writeLine(stdout, `Installing dependencies with ${normalizedPackageManager}...`);
66
+
67
+ try {
68
+ await runCommand(
69
+ installCommand.command,
70
+ installCommand.args,
71
+ {
72
+ cwd: result.targetDir,
73
+ env: {
74
+ ...process.env,
75
+ ...(context.env ?? {}),
76
+ },
77
+ shell: false,
78
+ stdio: "inherit",
79
+ },
80
+ context,
81
+ );
82
+ } catch (error) {
83
+ throw createDependencyInstallError(error, result, normalizedPackageManager, {
84
+ skillsRequested,
85
+ });
86
+ }
87
+ }
package/src/generate.mjs CHANGED
@@ -11,6 +11,11 @@ import {
11
11
  pathExists,
12
12
  removeDirectory,
13
13
  } from "./copy-recursive.mjs";
14
+ import {
15
+ DEFAULT_PACKAGE_MANAGER,
16
+ normalizePackageManager,
17
+ replaceGeneratedCommandReferences,
18
+ } from "./package-manager.mjs";
14
19
  import {
15
20
  createGeneratedPackageJson,
16
21
  createGeneratedTsConfig,
@@ -50,8 +55,29 @@ function createRelativePath(fromDir, toDir) {
50
55
  return relativePath === "" ? "." : relativePath;
51
56
  }
52
57
 
53
- function rewriteGeneratedAppText(source) {
54
- return rewriteGeneratedText(source)
58
+ function shouldReplacePackageManagerCommands(filePath, targetDir) {
59
+ const relativePath = path.relative(targetDir, filePath).split(path.sep).join("/");
60
+
61
+ return (
62
+ relativePath === "AGENTS.md" ||
63
+ relativePath === "playwright.config.ts" ||
64
+ relativePath === "scripts/check-toolcraft-docs.mjs" ||
65
+ /^docs\/toolcraft\/.+\.md$/.test(relativePath)
66
+ );
67
+ }
68
+
69
+ function rewriteGeneratedAppText(
70
+ source,
71
+ filePath,
72
+ { packageManager = DEFAULT_PACKAGE_MANAGER, targetDir } = {},
73
+ ) {
74
+ const rewrittenSource = rewriteGeneratedText(source);
75
+ const commandSource =
76
+ targetDir && shouldReplacePackageManagerCommands(filePath, targetDir)
77
+ ? replaceGeneratedCommandReferences(rewrittenSource, packageManager)
78
+ : rewrittenSource;
79
+
80
+ return commandSource
55
81
  .replaceAll("starterAcceptance", "appAcceptance")
56
82
  .replaceAll("starter-acceptance", "app-acceptance")
57
83
  .replaceAll("starterProductReadiness", "appProductReadiness")
@@ -218,6 +244,7 @@ export function getDefaultSourcePaths(repoRoot = REPO_ROOT) {
218
244
  export async function generateToolcraft(options = {}) {
219
245
  const cwd = options.cwd ?? process.cwd();
220
246
  const targetDir = path.resolve(cwd, options.targetDir ?? ".");
247
+ const packageManager = normalizePackageManager(options.packageManager ?? DEFAULT_PACKAGE_MANAGER);
221
248
  const sourcePaths = options.sourcePaths ?? getDefaultSourcePaths(options.repoRoot ?? REPO_ROOT);
222
249
 
223
250
  await assertDirectory(sourcePaths.starterDir, "Starter app");
@@ -226,6 +253,7 @@ export async function generateToolcraft(options = {}) {
226
253
  const starterPackageJson = await readJson(path.join(sourcePaths.starterDir, "package.json"));
227
254
  const packageJson = createGeneratedPackageJson({
228
255
  name: options.name ?? path.basename(targetDir),
256
+ packageManager,
229
257
  starterPackageJson,
230
258
  });
231
259
  const projectTitle = normalizeProjectTitle(packageJson.name);
@@ -243,8 +271,8 @@ export async function generateToolcraft(options = {}) {
243
271
  await copyDirectory(sourcePaths.toolcraftSrc, path.join(toolcraftRoot, "runtime"));
244
272
  await removeToolcraftTestFiles(toolcraftRoot);
245
273
 
246
- const changedFiles = await rewriteTextFiles(targetDir, (source) =>
247
- rewriteGeneratedAppText(source),
274
+ const changedFiles = await rewriteTextFiles(targetDir, (source, filePath) =>
275
+ rewriteGeneratedAppText(source, filePath, { packageManager, targetDir }),
248
276
  );
249
277
  await writeGeneratedProjectTitle(targetDir, projectTitle);
250
278
  await writeToolcraftIntegrityManifest(toolcraftRoot);