@stacksjs/buddy 0.70.257 → 0.70.259

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 (113) hide show
  1. package/dist/cli.d.ts +11 -1
  2. package/dist/cli.js +5 -164
  3. package/dist/commands/about.js +1 -35
  4. package/dist/commands/add.js +1 -25
  5. package/dist/commands/ai-context.js +4 -27
  6. package/dist/commands/auth.js +1 -68
  7. package/dist/commands/build.js +1 -221
  8. package/dist/commands/cd.js +8 -39
  9. package/dist/commands/changelog.js +1 -32
  10. package/dist/commands/clean.js +1 -44
  11. package/dist/commands/cloud.js +2 -519
  12. package/dist/commands/commit.js +1 -16
  13. package/dist/commands/completion.js +6 -42
  14. package/dist/commands/config-migrate.js +6 -94
  15. package/dist/commands/configure.js +2 -45
  16. package/dist/commands/create.js +1 -162
  17. package/dist/commands/deploy.d.ts +10 -4
  18. package/dist/commands/deploy.js +13 -1664
  19. package/dist/commands/desktop-apple.js +12 -399
  20. package/dist/commands/dev.js +6 -1083
  21. package/dist/commands/dns.js +3 -103
  22. package/dist/commands/docs/buddy-commands.js +10 -132
  23. package/dist/commands/docs/buddy-commands.test.js +1 -38
  24. package/dist/commands/docs/framework-repo.js +1 -14
  25. package/dist/commands/docs/framework-repo.test.js +1 -68
  26. package/dist/commands/docs/generated-artifacts.js +3 -70
  27. package/dist/commands/docs/generated-artifacts.test.js +1 -19
  28. package/dist/commands/docs/links.js +2 -85
  29. package/dist/commands/docs/links.test.js +4 -66
  30. package/dist/commands/docs.js +1 -24
  31. package/dist/commands/doctor.js +2 -478
  32. package/dist/commands/domains.js +2 -152
  33. package/dist/commands/email.js +31 -503
  34. package/dist/commands/env.js +1 -312
  35. package/dist/commands/extension.js +1 -239
  36. package/dist/commands/features.js +9 -345
  37. package/dist/commands/fresh.js +1 -46
  38. package/dist/commands/generate.js +2 -152
  39. package/dist/commands/http.js +1 -33
  40. package/dist/commands/index.js +1 -57
  41. package/dist/commands/install.js +1 -22
  42. package/dist/commands/key.js +1 -23
  43. package/dist/commands/link.js +2 -147
  44. package/dist/commands/lint.js +2 -46
  45. package/dist/commands/list.js +1 -140
  46. package/dist/commands/mail.js +15 -1477
  47. package/dist/commands/maintenance.js +1 -141
  48. package/dist/commands/make.js +1 -392
  49. package/dist/commands/migrate-project.js +1 -49
  50. package/dist/commands/migrate.js +27 -625
  51. package/dist/commands/outdated.js +1 -19
  52. package/dist/commands/package.js +1 -17
  53. package/dist/commands/phone.js +17 -181
  54. package/dist/commands/ports.js +2 -118
  55. package/dist/commands/prepublish.js +1 -17
  56. package/dist/commands/projects.js +1 -29
  57. package/dist/commands/protocol/craft-evidence.js +4 -98
  58. package/dist/commands/protocol/craft-evidence.test.js +1 -20
  59. package/dist/commands/protocol/desktop-lifecycle-report.js +3 -57
  60. package/dist/commands/protocol/desktop-lifecycle-report.test.js +1 -32
  61. package/dist/commands/protocol/desktop-support.js +2 -32
  62. package/dist/commands/protocol/desktop-workflow.test.js +3 -35
  63. package/dist/commands/protocol/driver-registry.js +2 -71
  64. package/dist/commands/protocol/pantry-evidence.js +4 -144
  65. package/dist/commands/protocol/pantry-evidence.test.js +1 -29
  66. package/dist/commands/protocol/release-manifest.js +3 -102
  67. package/dist/commands/protocol/release-manifest.test.js +2 -34
  68. package/dist/commands/protocol/run-conformance.js +4 -346
  69. package/dist/commands/protocol/run-conformance.test.js +1 -52
  70. package/dist/commands/protocol/run-driver-contracts.js +4 -83
  71. package/dist/commands/protocol/run-driver-contracts.test.js +1 -18
  72. package/dist/commands/protocol/source-manifest.js +3 -113
  73. package/dist/commands/protocol/sync-suite.js +3 -136
  74. package/dist/commands/protocol/sync-suite.test.js +1 -44
  75. package/dist/commands/protocol.js +1 -60
  76. package/dist/commands/publish.js +8 -500
  77. package/dist/commands/queue.js +1 -248
  78. package/dist/commands/release.js +1 -42
  79. package/dist/commands/route.js +1 -21
  80. package/dist/commands/run-tool.js +1 -9
  81. package/dist/commands/saas.js +1 -25
  82. package/dist/commands/schedule.js +1 -61
  83. package/dist/commands/search.js +1 -84
  84. package/dist/commands/seed.js +1 -47
  85. package/dist/commands/serve.js +1 -55
  86. package/dist/commands/setup-ai.js +2 -121
  87. package/dist/commands/setup.js +2 -305
  88. package/dist/commands/share.js +1 -214
  89. package/dist/commands/sms.js +32 -319
  90. package/dist/commands/stacks.js +1 -72
  91. package/dist/commands/telemetry.js +1 -74
  92. package/dist/commands/test.js +1 -130
  93. package/dist/commands/tinker.js +1 -37
  94. package/dist/commands/types.js +1 -18
  95. package/dist/commands/upgrade.js +1 -98
  96. package/dist/commands/user.js +3 -84
  97. package/dist/commands/version.js +1 -14
  98. package/dist/config.js +1 -231
  99. package/dist/custom-cli.js +1 -23
  100. package/dist/database-preflight.js +1 -88
  101. package/dist/global-options.js +1 -4
  102. package/dist/index.js +1 -1
  103. package/dist/lazy-commands.js +1 -241
  104. package/dist/migrators/index.js +2 -62
  105. package/dist/migrators/laravel/index.js +1 -148
  106. package/dist/migrators/laravel/migrations.js +4 -231
  107. package/dist/migrators/laravel/models.js +8 -111
  108. package/dist/migrators/rails/index.js +1 -11
  109. package/dist/production-server.js +1 -257
  110. package/dist/project-setup.js +1 -36
  111. package/dist/serve-entry.js +1 -5
  112. package/dist/version-info.js +1 -5
  113. package/package.json +54 -47
@@ -1,33 +1,4 @@
1
- import process from "node:process";
2
- import { log, onUnknownSubcommand } from "@stacksjs/cli";
3
- export function completion(buddy) {
4
- const descriptions = {
5
- completion: "Generate shell completion scripts",
6
- shell: "Shell type (bash, zsh, fish)"
7
- };
8
- buddy.command("completion [shell]", descriptions.completion).option("-s, --shell [shell]", descriptions.shell).example("buddy completion bash").example("buddy completion zsh").example("buddy completion fish").example("buddy completion bash > /usr/local/etc/bash_completion.d/buddy").action(async (shell, options) => {
9
- log.debug("Running `buddy completion` ...", options);
10
- const targetShell = shell || options.shell || "bash", commandNames = (buddy.commands || []).map((cmd) => cmd.name).filter((name) => Boolean(name));
11
- switch (targetShell) {
12
- case "bash":
13
- console.log(generateBashCompletion(commandNames));
14
- break;
15
- case "zsh":
16
- console.log(generateZshCompletion(commandNames));
17
- break;
18
- case "fish":
19
- console.log(generateFishCompletion(commandNames));
20
- break;
21
- default:
22
- console.error(`Unknown shell: ${targetShell}`);
23
- console.error("Supported shells: bash, zsh, fish");
24
- process.exit(1);
25
- }
26
- });
27
- onUnknownSubcommand(buddy, "completion");
28
- }
29
- function generateBashCompletion(commands) {
30
- return `# buddy completion for bash
1
+ import process from"node:process";import{log,onUnknownSubcommand}from"@stacksjs/cli";export function completion(buddy){const descriptions={completion:"Generate shell completion scripts",shell:"Shell type (bash, zsh, fish)"};buddy.command("completion [shell]",descriptions.completion).option("-s, --shell [shell]",descriptions.shell).example("buddy completion bash").example("buddy completion zsh").example("buddy completion fish").example("buddy completion bash > /usr/local/etc/bash_completion.d/buddy").action(async(shell,options)=>{log.debug("Running `buddy completion` ...",options);const targetShell=shell||options.shell||"bash",commandNames=(buddy.commands||[]).map((cmd)=>cmd.name).filter((name)=>Boolean(name));switch(targetShell){case"bash":console.log(generateBashCompletion(commandNames));break;case"zsh":console.log(generateZshCompletion(commandNames));break;case"fish":console.log(generateFishCompletion(commandNames));break;default:console.error(`Unknown shell: ${targetShell}`);console.error("Supported shells: bash, zsh, fish");process.exit(1)}});onUnknownSubcommand(buddy,"completion")}function generateBashCompletion(commands){return`# buddy completion for bash
31
2
 
32
3
  _buddy_completion() {
33
4
  local cur prev commands
@@ -60,17 +31,14 @@ complete -F _buddy_completion buddy
60
31
  # - macOS: /usr/local/etc/bash_completion.d/buddy
61
32
  #
62
33
  # Then reload your shell or run: source ~/.bashrc
63
- `;
64
- }
65
- function generateZshCompletion(commands) {
66
- return `#compdef buddy
34
+ `}function generateZshCompletion(commands){return`#compdef buddy
67
35
 
68
36
  # buddy completion for zsh
69
37
 
70
38
  _buddy() {
71
39
  local -a commands
72
40
  commands=(
73
- ${commands.map((cmd) => ` '${cmd}:${cmd} command'`).join(`
41
+ ${commands.map((cmd)=>` '${cmd}:${cmd} command'`).join(`
74
42
  `)}
75
43
  )
76
44
 
@@ -111,16 +79,13 @@ _buddy "$@"
111
79
  # Then add to your ~/.zshrc:
112
80
  # fpath=(~/.zsh/completions $fpath)
113
81
  # autoload -Uz compinit && compinit
114
- `;
115
- }
116
- function generateFishCompletion(commands) {
117
- return `# buddy completion for fish
82
+ `}function generateFishCompletion(commands){return`# buddy completion for fish
118
83
 
119
84
  # Remove old completions
120
85
  complete -c buddy -e
121
86
 
122
87
  # Command completions
123
- ${commands.map((cmd) => `complete -c buddy -n "__fish_use_subcommand" -a "${cmd}" -d "${cmd} command"`).join(`
88
+ ${commands.map((cmd)=>`complete -c buddy -n "__fish_use_subcommand" -a "${cmd}" -d "${cmd} command"`).join(`
124
89
  `)}
125
90
 
126
91
  # Global flag completions
@@ -139,5 +104,4 @@ complete -c buddy -s f -l force -d "Skip confirmation prompts"
139
104
  # ~/.config/fish/completions/buddy.fish
140
105
  #
141
106
  # Fish will automatically load it on startup
142
- `;
143
- }
107
+ `}
@@ -1,94 +1,6 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import process from "node:process";
3
- import { italic, log } from "@stacksjs/cli";
4
- import { projectPath } from "@stacksjs/path";
5
- const CODEMODS = [
6
- {
7
- id: "0.65-cache-driver-rename",
8
- description: "Rename `cache.driver` to `cache.default` (introduced in 0.65).",
9
- files: ["cache.ts"],
10
- shouldApply: (c) => /\bdriver\s*:/.test(c) && !c.includes("default:"),
11
- apply: (c) => c.replace(/(\bcache\b[\s\S]*?)\bdriver(\s*:)/m, "$1default$2")
12
- },
13
- {
14
- id: "0.68-search-engine-name",
15
- description: "Rename `searchEngine.driver` to `searchEngine.default` (matches other configs).",
16
- files: ["search-engine.ts"],
17
- shouldApply: (c) => /\bdriver\s*:/.test(c),
18
- apply: (c) => c.replace(/\bdriver(\s*:)/g, "default$1")
19
- },
20
- {
21
- id: "0.70-ports-string-to-number",
22
- description: 'Convert string port values like `"3000"` to numbers (TypeScript types now require number).',
23
- files: ["ports.ts"],
24
- shouldApply: (c) => /:\s*['"]\d+['"]/.test(c),
25
- apply: (c) => c.replace(/:\s*['"](\d+)['"]/g, ": $1")
26
- }
27
- ];
28
- export async function runConfigMigrations(opts) {
29
- const changed = [], skipped = [], codemods = opts.only ? CODEMODS.filter((m) => m.id === opts.only) : CODEMODS;
30
- if (codemods.length === 0) {
31
- log.warn(`No matching codemods. Available: ${CODEMODS.map((m) => m.id).join(", ")}`);
32
- return { changed, skipped };
33
- }
34
- for (const cm of codemods) {
35
- log.info(`Codemod ${italic(cm.id)} \u2014 ${cm.description}`);
36
- for (const rel of cm.files) {
37
- const full = projectPath(`config/${rel}`);
38
- if (!existsSync(full)) {
39
- skipped.push(`${full} (missing)`);
40
- continue;
41
- }
42
- const before = readFileSync(full, "utf-8");
43
- if (!cm.shouldApply(before)) {
44
- skipped.push(`${full} (no changes)`);
45
- continue;
46
- }
47
- const after = cm.apply(before);
48
- if (after === before) {
49
- skipped.push(`${full} (no changes)`);
50
- continue;
51
- }
52
- if (opts.dryRun) {
53
- log.info(`[dry-run] would update ${full}`);
54
- printDiff(before, after);
55
- } else {
56
- writeFileSync(full, after);
57
- log.success(`updated ${full}`);
58
- }
59
- changed.push(full);
60
- }
61
- }
62
- return { changed, skipped };
63
- }
64
- function printDiff(before, after) {
65
- const beforeLines = before.split(`
66
- `), afterLines = after.split(`
67
- `), max = Math.max(beforeLines.length, afterLines.length);
68
- for (let i = 0;i < max; i++) {
69
- const b = beforeLines[i], a = afterLines[i];
70
- if (b === a)
71
- continue;
72
- if (b !== void 0)
73
- process.stdout.write(` - ${b}
74
- `);
75
- if (a !== void 0)
76
- process.stdout.write(` + ${a}
77
- `);
78
- }
79
- process.stdout.write(`
80
- `);
81
- }
82
- export function configMigrate(buddy) {
83
- buddy.command("config:migrate", "Apply config-shape codemods for the latest Stacks version").option("--dry-run", "Preview changes without writing", { default: !1 }).option("--only [id]", "Run a single codemod by id", { default: "" }).action(async (options) => {
84
- log.info("Running `buddy config:migrate` ...");
85
- const { changed, skipped } = await runConfigMigrations({
86
- dryRun: Boolean(options.dryRun),
87
- only: options.only?.length ? options.only : void 0
88
- });
89
- log.info(`Changed: ${changed.length}, Skipped: ${skipped.length}`);
90
- if (skipped.length > 0)
91
- for (const s of skipped)
92
- log.debug(` skipped: ${s}`);
93
- });
94
- }
1
+ import{existsSync,readFileSync,writeFileSync}from"node:fs";import process from"node:process";import{italic,log}from"@stacksjs/cli";import{projectPath}from"@stacksjs/path";const CODEMODS=[{id:"0.65-cache-driver-rename",description:"Rename `cache.driver` to `cache.default` (introduced in 0.65).",files:["cache.ts"],shouldApply:(c)=>/\bdriver\s*:/.test(c)&&!c.includes("default:"),apply:(c)=>c.replace(/(\bcache\b[\s\S]*?)\bdriver(\s*:)/m,"$1default$2")},{id:"0.68-search-engine-name",description:"Rename `searchEngine.driver` to `searchEngine.default` (matches other configs).",files:["search-engine.ts"],shouldApply:(c)=>/\bdriver\s*:/.test(c),apply:(c)=>c.replace(/\bdriver(\s*:)/g,"default$1")},{id:"0.70-ports-string-to-number",description:'Convert string port values like `"3000"` to numbers (TypeScript types now require number).',files:["ports.ts"],shouldApply:(c)=>/:\s*['"]\d+['"]/.test(c),apply:(c)=>c.replace(/:\s*['"](\d+)['"]/g,": $1")}];export async function runConfigMigrations(opts){const changed=[],skipped=[],codemods=opts.only?CODEMODS.filter((m)=>m.id===opts.only):CODEMODS;if(codemods.length===0){log.warn(`No matching codemods. Available: ${CODEMODS.map((m)=>m.id).join(", ")}`);return{changed,skipped}}for(const cm of codemods){log.info(`Codemod ${italic(cm.id)} \u2014 ${cm.description}`);for(const rel of cm.files){const full=projectPath(`config/${rel}`);if(!existsSync(full)){skipped.push(`${full} (missing)`);continue}const before=readFileSync(full,"utf-8");if(!cm.shouldApply(before)){skipped.push(`${full} (no changes)`);continue}const after=cm.apply(before);if(after===before){skipped.push(`${full} (no changes)`);continue}if(opts.dryRun){log.info(`[dry-run] would update ${full}`);printDiff(before,after)}else{writeFileSync(full,after);log.success(`updated ${full}`)}changed.push(full)}}return{changed,skipped}}function printDiff(before,after){const beforeLines=before.split(`
2
+ `),afterLines=after.split(`
3
+ `),max=Math.max(beforeLines.length,afterLines.length);for(let i=0;i<max;i++){const b=beforeLines[i],a=afterLines[i];if(b===a)continue;if(b!==void 0)process.stdout.write(` - ${b}
4
+ `);if(a!==void 0)process.stdout.write(` + ${a}
5
+ `)}process.stdout.write(`
6
+ `)}export function configMigrate(buddy){buddy.command("config:migrate","Apply config-shape codemods for the latest Stacks version").option("--dry-run","Preview changes without writing",{default:!1}).option("--only [id]","Run a single codemod by id",{default:""}).action(async(options)=>{log.info("Running `buddy config:migrate` ...");const{changed,skipped}=await runConfigMigrations({dryRun:Boolean(options.dryRun),only:options.only?.length?options.only:void 0});log.info(`Changed: ${changed.length}, Skipped: ${skipped.length}`);if(skipped.length>0)for(const s of skipped)log.debug(` skipped: ${s}`)})}
@@ -1,48 +1,5 @@
1
- import process from "node:process";
2
- import { log, onUnknownSubcommand, outro, runCommand } from "@stacksjs/cli";
3
- import { path as p } from "@stacksjs/path";
4
- import { ExitCode } from "@stacksjs/types";
5
- export function configure(buddy) {
6
- const descriptions = {
7
- configure: "Configure options",
8
- aws: "Configure the AWS connection",
9
- project: "Target a specific project",
10
- profile: "The AWS profile to use",
11
- verbose: "Enable verbose output"
12
- };
13
- buddy.command("configure", descriptions.configure).option("--aws", descriptions.aws, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
14
- log.debug("Running `buddy configure` ...", options);
15
- if (options?.aws) {
16
- await configureAws(options);
17
- process.exit(ExitCode.Success);
18
- }
19
- log.info("Not implemented yet. Please use the --aws flag to configure AWS.");
20
- process.exit(ExitCode.Success);
21
- });
22
- buddy.command("configure:aws", descriptions.aws).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--profile", descriptions.profile, {
23
- default: process.env.AWS_PROFILE
24
- }).option("--verbose", descriptions.verbose, { default: !1 }).option("--access-key-id", "The AWS access key").option("--secret-access-key", "The AWS secret access key").option("--region", "The AWS region").option("--output", "The AWS output format").option("--quiet", "Suppress output").action(async (options) => {
25
- log.debug("Running `buddy configure:aws` ...", options);
26
- await configureAws(options);
27
- process.exit(ExitCode.Success);
28
- });
29
- onUnknownSubcommand(buddy, "configure");
30
- }
31
- async function configureAws(options) {
32
- const startTime = performance.now(), awsAccessKeyId = options?.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID, awsSecretAccessKey = options?.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY, defaultRegion = "us-east-1", defaultOutputFormat = options?.output ?? "json", profile = process.env.AWS_PROFILE ?? options?.profile, command = profile ? `aws configure --profile ${profile}` : "aws configure", input = `${awsAccessKeyId}
1
+ import process from"node:process";import{log,onUnknownSubcommand,outro,runCommand}from"@stacksjs/cli";import{path as p}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";export function configure(buddy){const descriptions={configure:"Configure options",aws:"Configure the AWS connection",project:"Target a specific project",profile:"The AWS profile to use",verbose:"Enable verbose output"};buddy.command("configure",descriptions.configure).option("--aws",descriptions.aws,{default:!1}).option("-p, --project [project]",descriptions.project,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy configure` ...",options);if(options?.aws){await configureAws(options);process.exit(ExitCode.Success)}log.info("Not implemented yet. Please use the --aws flag to configure AWS.");process.exit(ExitCode.Success)});buddy.command("configure:aws",descriptions.aws).option("-p, --project [project]",descriptions.project,{default:!1}).option("--profile",descriptions.profile,{default:process.env.AWS_PROFILE}).option("--verbose",descriptions.verbose,{default:!1}).option("--access-key-id","The AWS access key").option("--secret-access-key","The AWS secret access key").option("--region","The AWS region").option("--output","The AWS output format").option("--quiet","Suppress output").action(async(options)=>{log.debug("Running `buddy configure:aws` ...",options);await configureAws(options);process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"configure")}async function configureAws(options){const startTime=performance.now(),awsAccessKeyId=options?.accessKeyId??process.env.AWS_ACCESS_KEY_ID,awsSecretAccessKey=options?.secretAccessKey??process.env.AWS_SECRET_ACCESS_KEY,defaultRegion="us-east-1",defaultOutputFormat=options?.output??"json",profile=process.env.AWS_PROFILE??options?.profile,command=profile?`aws configure --profile ${profile}`:"aws configure",input=`${awsAccessKeyId}
33
2
  ${awsSecretAccessKey}
34
3
  ${defaultRegion}
35
4
  ${defaultOutputFormat}
36
- `, result = await runCommand(command, {
37
- cwd: p.projectPath(),
38
- stdin: "pipe",
39
- input
40
- });
41
- if (result.isErr) {
42
- await outro("While running the cloud command, there was an issue", { startTime, useSeconds: !0 }, result.error);
43
- process.exit(ExitCode.FatalError);
44
- }
45
- if (options?.quiet)
46
- return;
47
- await outro("Exited", { startTime, useSeconds: !0 });
48
- }
5
+ `,result=await runCommand(command,{cwd:p.projectPath(),stdin:"pipe",input});if(result.isErr){await outro("While running the cloud command, there was an issue",{startTime,useSeconds:!0},result.error);process.exit(ExitCode.FatalError)}if(options?.quiet)return;await outro("Exited",{startTime,useSeconds:!0})}
@@ -1,162 +1 @@
1
- import { chmodSync, existsSync, readdirSync } from "node:fs";
2
- import process from "node:process";
3
- import { runAction } from "@stacksjs/actions";
4
- import { bold, cyan, dim, intro, log, onUnknownSubcommand, runCommand } from "@stacksjs/cli";
5
- import { Action } from "@stacksjs/enums";
6
- import { resolve } from "@stacksjs/path";
7
- import { isFolder } from "@stacksjs/storage";
8
- import { ExitCode } from "@stacksjs/types";
9
- import { uninstallAllFeatures } from "./features";
10
- import { ensurePantryDependencies, ensurePantryInstalled } from "./setup";
11
- export function create(buddy) {
12
- const descriptions = {
13
- name: "The name of the project",
14
- command: "Create a new Stacks project",
15
- ui: "Are you building a UI?",
16
- components: "Are you building UI components?",
17
- webComponents: "Automagically built optimized custom elements/web components?",
18
- views: "How about views?",
19
- functions: "Are you developing functions/composables?",
20
- api: "Are you building an API?",
21
- database: "Do you need a database?",
22
- notifications: "Do you need notifications? e.g. email, SMS, push or chat notifications",
23
- cache: "Do you need caching?",
24
- email: "Do you need email?",
25
- project: "Target a specific project",
26
- minimal: "Skip optional feature bundles (cms, commerce, dashboard, marketing, monitoring, realtime, queue) \u2014 bare-bones API/SPA starter that can re-add them later via `./buddy <feature>:install`.",
27
- withCore: "Keep the framework vendored in `storage/framework/core` as a Bun workspace, for working ON Stacks. Apps that only work WITH Stacks want the default, which resolves every @stacksjs/* package from npm.",
28
- verbose: "Enable verbose output"
29
- };
30
- buddy.command("new [name]", descriptions.command).alias("create [name]").option("-n, --name [name]", descriptions.name, { default: !1 }).option("-u, --ui", descriptions.ui, { default: !0 }).option("-c, --components", descriptions.components, { default: !0 }).option("-w, --web-components", descriptions.webComponents, { default: !0 }).option("-p, --views", descriptions.views, { default: !0 }).option("-f, --functions", descriptions.functions, { default: !0 }).option("-a, --api", descriptions.api, { default: !0 }).option("-d, --database", descriptions.database, { default: !0 }).option("-ca, --cache", descriptions.cache, { default: !1 }).option("-e, --email", descriptions.email, { default: !1 }).option("-P, --project [project]", descriptions.project, { default: !1 }).option("-m, --minimal", descriptions.minimal, { default: !1 }).option("--with-core", descriptions.withCore, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (name, options) => {
31
- log.debug("Running `buddy new <name>` ...", options);
32
- const startTime = await intro("buddy new");
33
- name = name ?? options.name;
34
- const path = resolve(process.cwd(), name);
35
- isFolderCheck(path);
36
- await onlineCheck();
37
- const result = await download(name, path, options);
38
- if (result.isErr) {
39
- log.error(result.error);
40
- process.exit(ExitCode.FatalError);
41
- }
42
- ensureExecutableScripts(path);
43
- await ensureEnv(path, options);
44
- await install(path, options);
45
- if (!options.withCore)
46
- await unvendorCore(path, options);
47
- if (options.minimal)
48
- await stripFeatures(path);
49
- if (startTime) {
50
- const time = performance.now() - startTime;
51
- log.success(dim(`[${time.toFixed(2)}ms] Completed`));
52
- }
53
- log.info(bold("Welcome to the Stacks Framework! \u269B\uFE0F"));
54
- log.info(`Get started: ${cyan(`cd ${name}`)} and then ${cyan("./buddy dev")}`);
55
- log.info(`Run ${cyan("./buddy doctor")} anytime to check your setup`);
56
- log.info("To learn more, visit https://stacksjs.com");
57
- process.exit(ExitCode.Success);
58
- });
59
- onUnknownSubcommand(buddy, "new");
60
- }
61
- function isFolderCheck(path) {
62
- if (!isFolder(path))
63
- return;
64
- if (readdirSync(path).filter((entry) => entry !== ".git").length === 0)
65
- return;
66
- log.error(`Path ${path} already exists`);
67
- process.exit(ExitCode.FatalError);
68
- }
69
- async function onlineCheck() {
70
- if (await isOnline())
71
- return;
72
- log.info("It appears you are disconnected from the internet.");
73
- log.info("Creating a new project requires a brief internet connection to download the template and install dependencies.");
74
- process.exit(ExitCode.FatalError);
75
- }
76
- async function isOnline() {
77
- try {
78
- return (await fetch("https://github.com", {
79
- method: "HEAD",
80
- signal: AbortSignal.timeout(3000)
81
- })).ok;
82
- } catch {
83
- return !1;
84
- }
85
- }
86
- async function download(name, path, _options) {
87
- log.info("Setting up your stack.");
88
- try {
89
- const { downloadTemplate } = await import("@stacksjs/gitit");
90
- await downloadTemplate("gh:stacksjs/stacks", { dir: name, force: !0 });
91
- log.success(`Successfully scaffolded your project at ${cyan(path)}`);
92
- return { isErr: !1 };
93
- } catch (error) {
94
- return { isErr: !0, error: error instanceof Error ? error.message : String(error) };
95
- }
96
- }
97
- function ensureExecutableScripts(path) {
98
- for (const script of ["buddy", "bootstrap"])
99
- try {
100
- chmodSync(resolve(path, script), 493);
101
- } catch {}
102
- }
103
- async function ensureEnv(path, _options) {
104
- log.info("Ensuring your environment is ready...");
105
- await ensurePantryInstalled();
106
- await ensurePantryDependencies(path);
107
- log.success("Environment is ready");
108
- }
109
- async function install(path, options) {
110
- log.info("Installing & setting up Stacks");
111
- log.info("Copying .env.example \u2192 .env");
112
- let result = await runCommand("cp .env.example .env", { ...options, cwd: path });
113
- if (result?.isErr) {
114
- log.error(result.error);
115
- process.exit(ExitCode.FatalError);
116
- }
117
- log.info("Removing template-encrypted env files...");
118
- const { rm } = await import("node:fs/promises");
119
- for (const stale of [".env.development", ".env.staging", ".env.production", ".env.keys"])
120
- await rm(`${path}/${stale}`, { force: !0 });
121
- log.info("Generating application key...");
122
- const keyResult = await runAction(Action.KeyGenerate, { ...options, cwd: path });
123
- if (keyResult.isErr) {
124
- log.error(keyResult.error);
125
- process.exit(ExitCode.FatalError);
126
- }
127
- if (existsSync(resolve(path, ".git")))
128
- log.info("Existing git repository detected, skipping git init");
129
- else {
130
- log.info("Initializing git repository...");
131
- result = await runCommand("git init", { ...options, cwd: path });
132
- if (result.isErr) {
133
- log.error(result.error);
134
- process.exit(ExitCode.FatalError);
135
- }
136
- }
137
- log.success("Installed & set-up \uD83D\uDE80");
138
- }
139
- async function unvendorCore(path, options) {
140
- log.info("Resolving the framework from npm (pass --with-core to keep it vendored)...");
141
- const result = await runCommand("./buddy unpublish:core --all --force", { ...options, cwd: path });
142
- if (result.isErr) {
143
- log.error(result.error);
144
- process.exit(ExitCode.FatalError);
145
- }
146
- log.success("Framework resolved from npm");
147
- }
148
- async function stripFeatures(path) {
149
- log.info("Stripping optional feature bundles (--minimal)...");
150
- const results = await uninstallAllFeatures({ root: path });
151
- let strippedAny = !1;
152
- for (const { feature, configOutcome, filesRemoved } of results)
153
- if (configOutcome === "flipped" || filesRemoved.length > 0) {
154
- strippedAny = !0;
155
- const fileSummary = filesRemoved.length > 0 ? ` (${filesRemoved.length} path${filesRemoved.length === 1 ? "" : "s"})` : "";
156
- log.info(` - ${feature}${fileSummary}`);
157
- }
158
- if (!strippedAny)
159
- log.info(" \u2192 no feature scaffolding present; nothing to strip.");
160
- else
161
- log.success("Minimal skeleton ready \u2014 run `./buddy <feature>:install` to add features back.");
162
- }
1
+ import{chmodSync,existsSync,readdirSync}from"node:fs";import process from"node:process";import{runAction}from"@stacksjs/actions";import{bold,cyan,dim,intro,log,onUnknownSubcommand,runCommand}from"@stacksjs/cli";import{Action}from"@stacksjs/enums";import{resolve}from"@stacksjs/path";import{isFolder}from"@stacksjs/storage";import{ExitCode}from"@stacksjs/types";import{uninstallAllFeatures}from"./features";import{ensurePantryDependencies,ensurePantryInstalled}from"./setup";export function create(buddy){const descriptions={name:"The name of the project",command:"Create a new Stacks project",ui:"Are you building a UI?",components:"Are you building UI components?",webComponents:"Automagically built optimized custom elements/web components?",views:"How about views?",functions:"Are you developing functions/composables?",api:"Are you building an API?",database:"Do you need a database?",notifications:"Do you need notifications? e.g. email, SMS, push or chat notifications",cache:"Do you need caching?",email:"Do you need email?",project:"Target a specific project",minimal:"Skip optional feature bundles (cms, commerce, dashboard, marketing, monitoring, realtime, queue) \u2014 bare-bones API/SPA starter that can re-add them later via `./buddy <feature>:install`.",withCore:"Keep the framework vendored in `storage/framework/core` as a Bun workspace, for working ON Stacks. Apps that only work WITH Stacks want the default, which resolves every @stacksjs/* package from npm.",verbose:"Enable verbose output"};buddy.command("new [name]",descriptions.command).alias("create [name]").option("-n, --name [name]",descriptions.name,{default:!1}).option("-u, --ui",descriptions.ui,{default:!0}).option("-c, --components",descriptions.components,{default:!0}).option("-w, --web-components",descriptions.webComponents,{default:!0}).option("-p, --views",descriptions.views,{default:!0}).option("-f, --functions",descriptions.functions,{default:!0}).option("-a, --api",descriptions.api,{default:!0}).option("-d, --database",descriptions.database,{default:!0}).option("-ca, --cache",descriptions.cache,{default:!1}).option("-e, --email",descriptions.email,{default:!1}).option("-P, --project [project]",descriptions.project,{default:!1}).option("-m, --minimal",descriptions.minimal,{default:!1}).option("--with-core",descriptions.withCore,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(name,options)=>{log.debug("Running `buddy new <name>` ...",options);const startTime=await intro("buddy new");name=name??options.name;const path=resolve(process.cwd(),name);isFolderCheck(path);await onlineCheck();const result=await download(name,path,options);if(result.isErr){log.error(result.error);process.exit(ExitCode.FatalError)}ensureExecutableScripts(path);await ensureEnv(path,options);await install(path,options);if(!options.withCore)await unvendorCore(path,options);if(options.minimal)await stripFeatures(path);if(startTime){const time=performance.now()-startTime;log.success(dim(`[${time.toFixed(2)}ms] Completed`))}log.info(bold("Welcome to the Stacks Framework! \u269B\uFE0F"));log.info(`Get started: ${cyan(`cd ${name}`)} and then ${cyan("./buddy dev")}`);log.info(`Run ${cyan("./buddy doctor")} anytime to check your setup`);log.info("To learn more, visit https://stacksjs.com");process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"new")}function isFolderCheck(path){if(!isFolder(path))return;if(readdirSync(path).filter((entry)=>entry!==".git").length===0)return;log.error(`Path ${path} already exists`);process.exit(ExitCode.FatalError)}async function onlineCheck(){if(await isOnline())return;log.info("It appears you are disconnected from the internet.");log.info("Creating a new project requires a brief internet connection to download the template and install dependencies.");process.exit(ExitCode.FatalError)}async function isOnline(){try{return(await fetch("https://github.com",{method:"HEAD",signal:AbortSignal.timeout(3000)})).ok}catch{return!1}}async function download(name,path,_options){log.info("Setting up your stack.");try{const{downloadTemplate}=await import("@stacksjs/gitit");await downloadTemplate("gh:stacksjs/stacks",{dir:name,force:!0});log.success(`Successfully scaffolded your project at ${cyan(path)}`);return{isErr:!1}}catch(error){return{isErr:!0,error:error instanceof Error?error.message:String(error)}}}function ensureExecutableScripts(path){for(const script of["buddy","bootstrap"])try{chmodSync(resolve(path,script),493)}catch{}}async function ensureEnv(path,_options){log.info("Ensuring your environment is ready...");await ensurePantryInstalled();await ensurePantryDependencies(path);log.success("Environment is ready")}async function install(path,options){log.info("Installing & setting up Stacks");log.info("Copying .env.example \u2192 .env");let result=await runCommand("cp .env.example .env",{...options,cwd:path});if(result?.isErr){log.error(result.error);process.exit(ExitCode.FatalError)}log.info("Removing template-encrypted env files...");const{rm}=await import("node:fs/promises");for(const stale of[".env.development",".env.staging",".env.production",".env.keys"])await rm(`${path}/${stale}`,{force:!0});log.info("Generating application key...");const keyResult=await runAction(Action.KeyGenerate,{...options,cwd:path});if(keyResult.isErr){log.error(keyResult.error);process.exit(ExitCode.FatalError)}if(existsSync(resolve(path,".git")))log.info("Existing git repository detected, skipping git init");else{log.info("Initializing git repository...");result=await runCommand("git init",{...options,cwd:path});if(result.isErr){log.error(result.error);process.exit(ExitCode.FatalError)}}log.success("Installed & set-up \uD83D\uDE80")}async function unvendorCore(path,options){log.info("Resolving the framework from npm (pass --with-core to keep it vendored)...");const result=await runCommand("./buddy unpublish:core --all --force",{...options,cwd:path});if(result.isErr){log.error(result.error);process.exit(ExitCode.FatalError)}log.success("Framework resolved from npm")}async function stripFeatures(path){log.info("Stripping optional feature bundles (--minimal)...");const results=await uninstallAllFeatures({root:path});let strippedAny=!1;for(const{feature,configOutcome,filesRemoved}of results)if(configOutcome==="flipped"||filesRemoved.length>0){strippedAny=!0;const fileSummary=filesRemoved.length>0?` (${filesRemoved.length} path${filesRemoved.length===1?"":"s"})`:"";log.info(` - ${feature}${fileSummary}`)}if(!strippedAny)log.info(" \u2192 no feature scaffolding present; nothing to strip.");else log.success("Minimal skeleton ready \u2014 run `./buddy <feature>:install` to add features back.")}
@@ -122,10 +122,16 @@ export declare function applyPersistentStatePaths(sites: Record<string, any>, sl
122
122
  */
123
123
  export declare function applyEnvironmentToSites(sites: Record<string, any>, environment: string, config: any): Record<string, any>;
124
124
  /**
125
- * Load the ts-cloud driver used by Buddy. Published installs use the declared
126
- * package dependency. Framework and ts-cloud development can point at a built
127
- * local module with TS_CLOUD_MODULE, avoiding stale nested dependency copies
128
- * while still exercising the exact Buddy deploy path.
125
+ * Resolve the app's direct ts-cloud dependency before Buddy's own dependency.
126
+ * Package managers may retain a stale nested copy below Buddy even after the
127
+ * app explicitly updates ts-cloud; resolving from the project manifest keeps
128
+ * the deploy engine selected by the application authoritative.
129
+ */
130
+ export declare function resolveProjectTsCloudModule(projectRoot?: unknown): string | undefined;
131
+ /**
132
+ * Load the ts-cloud driver used by Buddy. An explicit TS_CLOUD_MODULE remains
133
+ * available for framework development; published apps otherwise use their
134
+ * direct dependency before falling back to Buddy's declared dependency.
129
135
  */
130
136
  export declare function loadTsCloudDeployApi(): Promise<typeof import('@stacksjs/ts-cloud')>;
131
137
  /**