@stacksjs/buddy 0.70.258 → 0.70.260

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 (112) 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.js +13 -1676
  18. package/dist/commands/desktop-apple.js +12 -399
  19. package/dist/commands/dev.js +6 -1083
  20. package/dist/commands/dns.js +3 -103
  21. package/dist/commands/docs/buddy-commands.js +10 -132
  22. package/dist/commands/docs/buddy-commands.test.js +1 -38
  23. package/dist/commands/docs/framework-repo.js +1 -14
  24. package/dist/commands/docs/framework-repo.test.js +1 -68
  25. package/dist/commands/docs/generated-artifacts.js +3 -70
  26. package/dist/commands/docs/generated-artifacts.test.js +1 -19
  27. package/dist/commands/docs/links.js +2 -85
  28. package/dist/commands/docs/links.test.js +4 -66
  29. package/dist/commands/docs.js +1 -24
  30. package/dist/commands/doctor.js +2 -478
  31. package/dist/commands/domains.js +2 -152
  32. package/dist/commands/email.js +31 -503
  33. package/dist/commands/env.js +1 -312
  34. package/dist/commands/extension.js +1 -239
  35. package/dist/commands/features.js +9 -345
  36. package/dist/commands/fresh.js +1 -46
  37. package/dist/commands/generate.js +2 -152
  38. package/dist/commands/http.js +1 -33
  39. package/dist/commands/index.js +1 -57
  40. package/dist/commands/install.js +1 -22
  41. package/dist/commands/key.js +1 -23
  42. package/dist/commands/link.js +2 -147
  43. package/dist/commands/lint.js +2 -84
  44. package/dist/commands/list.js +1 -140
  45. package/dist/commands/mail.js +15 -1477
  46. package/dist/commands/maintenance.js +1 -141
  47. package/dist/commands/make.js +1 -392
  48. package/dist/commands/migrate-project.js +1 -49
  49. package/dist/commands/migrate.js +27 -625
  50. package/dist/commands/outdated.js +1 -19
  51. package/dist/commands/package.js +1 -17
  52. package/dist/commands/phone.js +17 -181
  53. package/dist/commands/ports.js +2 -118
  54. package/dist/commands/prepublish.js +1 -17
  55. package/dist/commands/projects.js +1 -29
  56. package/dist/commands/protocol/craft-evidence.js +4 -98
  57. package/dist/commands/protocol/craft-evidence.test.js +1 -20
  58. package/dist/commands/protocol/desktop-lifecycle-report.js +3 -57
  59. package/dist/commands/protocol/desktop-lifecycle-report.test.js +1 -32
  60. package/dist/commands/protocol/desktop-support.js +2 -32
  61. package/dist/commands/protocol/desktop-workflow.test.js +3 -35
  62. package/dist/commands/protocol/driver-registry.js +2 -71
  63. package/dist/commands/protocol/pantry-evidence.js +4 -144
  64. package/dist/commands/protocol/pantry-evidence.test.js +1 -29
  65. package/dist/commands/protocol/release-manifest.js +3 -102
  66. package/dist/commands/protocol/release-manifest.test.js +2 -34
  67. package/dist/commands/protocol/run-conformance.js +4 -346
  68. package/dist/commands/protocol/run-conformance.test.js +1 -52
  69. package/dist/commands/protocol/run-driver-contracts.js +4 -83
  70. package/dist/commands/protocol/run-driver-contracts.test.js +1 -18
  71. package/dist/commands/protocol/source-manifest.js +3 -113
  72. package/dist/commands/protocol/sync-suite.js +3 -136
  73. package/dist/commands/protocol/sync-suite.test.js +1 -44
  74. package/dist/commands/protocol.js +1 -60
  75. package/dist/commands/publish.js +8 -500
  76. package/dist/commands/queue.js +1 -248
  77. package/dist/commands/release.js +1 -42
  78. package/dist/commands/route.js +1 -21
  79. package/dist/commands/run-tool.js +1 -9
  80. package/dist/commands/saas.js +1 -25
  81. package/dist/commands/schedule.js +1 -61
  82. package/dist/commands/search.js +1 -84
  83. package/dist/commands/seed.js +1 -47
  84. package/dist/commands/serve.js +1 -55
  85. package/dist/commands/setup-ai.js +2 -121
  86. package/dist/commands/setup.js +2 -305
  87. package/dist/commands/share.js +1 -214
  88. package/dist/commands/sms.js +32 -319
  89. package/dist/commands/stacks.js +1 -72
  90. package/dist/commands/telemetry.js +1 -74
  91. package/dist/commands/test.js +1 -130
  92. package/dist/commands/tinker.js +1 -37
  93. package/dist/commands/types.js +1 -18
  94. package/dist/commands/upgrade.js +1 -98
  95. package/dist/commands/user.js +3 -84
  96. package/dist/commands/version.js +1 -14
  97. package/dist/config.js +1 -231
  98. package/dist/custom-cli.js +1 -23
  99. package/dist/database-preflight.js +1 -88
  100. package/dist/global-options.js +1 -4
  101. package/dist/index.js +1 -1
  102. package/dist/lazy-commands.js +1 -241
  103. package/dist/migrators/index.js +2 -62
  104. package/dist/migrators/laravel/index.js +1 -148
  105. package/dist/migrators/laravel/migrations.js +4 -231
  106. package/dist/migrators/laravel/models.js +8 -111
  107. package/dist/migrators/rails/index.js +1 -11
  108. package/dist/production-server.js +1 -257
  109. package/dist/project-setup.js +1 -36
  110. package/dist/serve-entry.js +1 -5
  111. package/dist/version-info.js +1 -5
  112. package/package.json +53 -46
@@ -1,33 +1 @@
1
- import process from "node:process";
2
- import { log } from "@stacksjs/cli";
3
- import { config } from "@stacksjs/config";
4
- import { HttxClient } from "@stacksjs/httx";
5
- import { ExitCode } from "@stacksjs/types";
6
- export function http(buddy) {
7
- const descriptions = {
8
- http: "Send an HTTP request to a domain",
9
- project: "Target a specific project",
10
- verbose: "Enable verbose output"
11
- };
12
- buddy.command("http [domain]", descriptions.http).option("-p, --project [project]", descriptions.project, { default: !1 }).option("-v, --verbose", descriptions.verbose, { default: !1 }).action(async (domain, options) => {
13
- log.debug("Running `buddy http [domain]` ...", options);
14
- const url = domain || config.app.url;
15
- if (!url)
16
- throw Error("No domain configured. Pass a domain or set config.app.url.");
17
- const client = new HttxClient({ verbose: options.verbose });
18
- log.info(`GET ${url}`);
19
- (await client.request(url.startsWith("http") ? url : `https://${url}`, {
20
- method: "GET"
21
- })).match({
22
- ok: (response) => {
23
- log.info(`${response.status} ${response.statusText} (${response.timings.duration.toFixed(0)}ms)`);
24
- console.log(typeof response.data === "string" ? response.data : JSON.stringify(response.data, null, 2));
25
- process.exit(ExitCode.Success);
26
- },
27
- err: (error) => {
28
- log.error(`Request failed: ${error.message}`);
29
- process.exit(ExitCode.FatalError);
30
- }
31
- });
32
- });
33
- }
1
+ import process from"node:process";import{log}from"@stacksjs/cli";import{config}from"@stacksjs/config";import{HttxClient}from"@stacksjs/httx";import{ExitCode}from"@stacksjs/types";export function http(buddy){const descriptions={http:"Send an HTTP request to a domain",project:"Target a specific project",verbose:"Enable verbose output"};buddy.command("http [domain]",descriptions.http).option("-p, --project [project]",descriptions.project,{default:!1}).option("-v, --verbose",descriptions.verbose,{default:!1}).action(async(domain,options)=>{log.debug("Running `buddy http [domain]` ...",options);const url=domain||config.app.url;if(!url)throw Error("No domain configured. Pass a domain or set config.app.url.");const client=new HttxClient({verbose:options.verbose});log.info(`GET ${url}`);(await client.request(url.startsWith("http")?url:`https://${url}`,{method:"GET"})).match({ok:(response)=>{log.info(`${response.status} ${response.statusText} (${response.timings.duration.toFixed(0)}ms)`);console.log(typeof response.data==="string"?response.data:JSON.stringify(response.data,null,2));process.exit(ExitCode.Success)},err:(error)=>{log.error(`Request failed: ${error.message}`);process.exit(ExitCode.FatalError)}})})}
@@ -1,57 +1 @@
1
- export * from "./about";
2
- export * from "./auth";
3
- export * from "./build";
4
- export * from "./cd";
5
- export * from "./changelog";
6
- export * from "./clean";
7
- export * from "./cloud";
8
- export * from "./commit";
9
- export * from "./completion";
10
- export * from "./config-migrate";
11
- export * from "./configure";
12
- export * from "./create";
13
- export * from "./deploy";
14
- export * from "./dev";
15
- export * from "./dns";
16
- export * from "./docs";
17
- export * from "./doctor";
18
- export * from "./domains";
19
- export * from "./email";
20
- export * from "./env";
21
- export * from "./features";
22
- export * from "./fresh";
23
- export * from "./generate";
24
- export * from "./http";
25
- export * from "./install";
26
- export * from "./key";
27
- export * from "./lint";
28
- export * from "./list";
29
- export * from "./mail";
30
- export * from "./make";
31
- export * from "./migrate";
32
- export * from "./migrate-project";
33
- export * from "./outdated";
34
- export * from "./phone";
35
- export * from "./ports";
36
- export * from "./link";
37
- export * from "./user";
38
- export * from "./prepublish";
39
- export * from "./projects";
40
- export * from "./protocol";
41
- export * from "./publish";
42
- export * from "./queue";
43
- export * from "./release";
44
- export * from "./route";
45
- export * from "./saas";
46
- export * from "./schedule";
47
- export * from "./search";
48
- export * from "./seed";
49
- export * from "./serve";
50
- export * from "./setup";
51
- export * from "./sms";
52
- export * from "./telemetry";
53
- export * from "./test";
54
- export * from "./tinker";
55
- export * from "./types";
56
- export * from "./upgrade";
57
- export * from "./version";
1
+ export*from"./about";export*from"./auth";export*from"./build";export*from"./cd";export*from"./changelog";export*from"./clean";export*from"./cloud";export*from"./commit";export*from"./completion";export*from"./config-migrate";export*from"./configure";export*from"./create";export*from"./deploy";export*from"./dev";export*from"./dns";export*from"./docs";export*from"./doctor";export*from"./domains";export*from"./email";export*from"./env";export*from"./features";export*from"./fresh";export*from"./generate";export*from"./http";export*from"./install";export*from"./key";export*from"./lint";export*from"./list";export*from"./mail";export*from"./make";export*from"./migrate";export*from"./migrate-project";export*from"./outdated";export*from"./phone";export*from"./ports";export*from"./link";export*from"./user";export*from"./prepublish";export*from"./projects";export*from"./protocol";export*from"./publish";export*from"./queue";export*from"./release";export*from"./route";export*from"./saas";export*from"./schedule";export*from"./search";export*from"./seed";export*from"./serve";export*from"./setup";export*from"./sms";export*from"./telemetry";export*from"./test";export*from"./tinker";export*from"./types";export*from"./upgrade";export*from"./version";
@@ -1,22 +1 @@
1
- import process from "node:process";
2
- import { log, onUnknownSubcommand, runCommand } from "@stacksjs/cli";
3
- import { path as p } from "@stacksjs/path";
4
- import { ExitCode } from "@stacksjs/types";
5
- export function install(buddy) {
6
- const descriptions = {
7
- install: "Install your dependencies",
8
- project: "Target a specific project",
9
- verbose: "Enable verbose output"
10
- };
11
- buddy.command("install", descriptions.install).option("-p, --project [project]", descriptions.project, { default: !1 }).option("-v, --verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
12
- log.debug("Running `buddy install` ...", options);
13
- if ((await runCommand("bun install", {
14
- ...options,
15
- cwd: p.projectPath()
16
- })).isErr) {
17
- log.error("bun install failed");
18
- process.exit(ExitCode.FatalError);
19
- }
20
- });
21
- onUnknownSubcommand(buddy, "install");
22
- }
1
+ import process from"node:process";import{log,onUnknownSubcommand,runCommand}from"@stacksjs/cli";import{path as p}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";export function install(buddy){const descriptions={install:"Install your dependencies",project:"Target a specific project",verbose:"Enable verbose output"};buddy.command("install",descriptions.install).option("-p, --project [project]",descriptions.project,{default:!1}).option("-v, --verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy install` ...",options);if((await runCommand("bun install",{...options,cwd:p.projectPath()})).isErr){log.error("bun install failed");process.exit(ExitCode.FatalError)}});onUnknownSubcommand(buddy,"install")}
@@ -1,23 +1 @@
1
- import process from "node:process";
2
- import { runAction } from "@stacksjs/actions";
3
- import { intro, log, onUnknownSubcommand, outro } from "@stacksjs/cli";
4
- import { Action } from "@stacksjs/enums";
5
- import { ExitCode } from "@stacksjs/types";
6
- export function key(buddy) {
7
- const descriptions = {
8
- command: "Generate & set the application key.",
9
- project: "Target a specific project",
10
- verbose: "Enable verbose output"
11
- };
12
- buddy.command("key:generate", descriptions.command).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
13
- log.debug("Running `buddy key:generate` ...", options);
14
- await intro("buddy key:generate");
15
- const result = await runAction(Action.KeyGenerate, options);
16
- if (result.isErr) {
17
- log.error("Failed to set random application key.", result.error);
18
- process.exit(ExitCode.FatalError);
19
- }
20
- await outro("Random application key set.");
21
- });
22
- onUnknownSubcommand(buddy, "key");
23
- }
1
+ import process from"node:process";import{runAction}from"@stacksjs/actions";import{intro,log,onUnknownSubcommand,outro}from"@stacksjs/cli";import{Action}from"@stacksjs/enums";import{ExitCode}from"@stacksjs/types";export function key(buddy){const descriptions={command:"Generate & set the application key.",project:"Target a specific project",verbose:"Enable verbose output"};buddy.command("key:generate",descriptions.command).option("-p, --project [project]",descriptions.project,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy key:generate` ...",options);await intro("buddy key:generate");const result=await runAction(Action.KeyGenerate,options);if(result.isErr){log.error("Failed to set random application key.",result.error);process.exit(ExitCode.FatalError)}await outro("Random application key set.")});onUnknownSubcommand(buddy,"key")}
@@ -1,147 +1,2 @@
1
- import { existsSync, lstatSync, readdirSync, realpathSync } from "node:fs";
2
- import fs from "node:fs";
3
- import { homedir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import process from "node:process";
6
- import { italic, log } from "@stacksjs/cli";
7
- import { ExitCode } from "@stacksjs/types";
8
- function recordPath() {
9
- return join(process.cwd(), "storage/framework/runtime/linked-core.json");
10
- }
11
- function readRecord() {
12
- try {
13
- return JSON.parse(fs.readFileSync(recordPath(), "utf-8"));
14
- } catch {
15
- return null;
16
- }
17
- }
18
- function writeRecord(record) {
19
- fs.mkdirSync(join(process.cwd(), "storage/framework/runtime"), { recursive: !0 });
20
- fs.writeFileSync(recordPath(), `${JSON.stringify(record, null, 2)}
21
- `);
22
- }
23
- function resolveFrameworkPath(explicit) {
24
- const candidates = [
25
- explicit,
26
- process.env.STACKS_FRAMEWORK_PATH,
27
- resolve(process.cwd(), "../stacks"),
28
- join(homedir(), "Code/stacks")
29
- ].filter(Boolean);
30
- for (const candidate of candidates) {
31
- const full = resolve(candidate);
32
- if (existsSync(join(full, "storage/framework/core")))
33
- return full;
34
- }
35
- return null;
36
- }
37
- function corePackages(framework) {
38
- const coreDir = join(framework, "storage/framework/core"), found = new Map;
39
- for (const entry of readdirSync(coreDir, { withFileTypes: !0 })) {
40
- if (!entry.isDirectory())
41
- continue;
42
- const dir = join(coreDir, entry.name), manifest = join(dir, "package.json");
43
- if (!existsSync(manifest))
44
- continue;
45
- try {
46
- const { name } = JSON.parse(fs.readFileSync(manifest, "utf-8"));
47
- if (name?.startsWith("@stacksjs/"))
48
- found.set(name, dir);
49
- } catch {}
50
- }
51
- return found;
52
- }
53
- function normalize(name) {
54
- return `@stacksjs/${name.replace(/^@stacksjs\//, "").replace(/^core\//, "")}`;
55
- }
56
- function isSymlink(path) {
57
- try {
58
- return lstatSync(path).isSymbolicLink();
59
- } catch {
60
- return !1;
61
- }
62
- }
63
- export function link(buddy) {
64
- buddy.command("link:core [...packages]", "Point this app's @stacksjs/* at a local framework checkout").option("--path <path>", "Framework checkout to link against").option("--all", "Link every core package the app has installed", { default: !1 }).example("buddy link:core auth router").example("buddy link:core --all --path ../stacks").action(async (packages, options) => {
65
- const framework = resolveFrameworkPath(options?.path);
66
- if (!framework) {
67
- log.error("No framework checkout found.");
68
- log.info("Pass one with `--path <dir>` or set STACKS_FRAMEWORK_PATH.");
69
- process.exit(ExitCode.FatalError);
70
- }
71
- const available = corePackages(framework), modulesDir = join(process.cwd(), "node_modules"), wanted = (packages?.length ?? 0) > 0 ? packages.map(normalize) : [...available.keys()].filter((name) => existsSync(join(modulesDir, name)) || options?.all);
72
- if (wanted.length === 0) {
73
- log.info("Nothing to link: no installed @stacksjs/* packages found in this project.");
74
- await log.flush();
75
- process.exit(ExitCode.Success);
76
- }
77
- const linked = [], unbuilt = [];
78
- for (const name of wanted) {
79
- const source = available.get(name);
80
- if (!source) {
81
- log.warn(`${name} is not in ${italic(framework)} \u2014 skipped.`);
82
- continue;
83
- }
84
- const target = join(modulesDir, name);
85
- if (isSymlink(target) && realpathSync(target) === realpathSync(source)) {
86
- linked.push(name);
87
- continue;
88
- }
89
- fs.rmSync(target, { recursive: !0, force: !0 });
90
- fs.mkdirSync(join(target, ".."), { recursive: !0 });
91
- fs.symlinkSync(source, target, "dir");
92
- linked.push(name);
93
- if (!existsSync(join(source, "dist")))
94
- unbuilt.push(name);
95
- }
96
- writeRecord({ framework, packages: linked });
97
- log.success(`Linked ${linked.length} package${linked.length === 1 ? "" : "s"} to ${italic(framework)}`);
98
- if (unbuilt.length > 0) {
99
- log.warn(`Not built yet: ${unbuilt.join(", ")}`);
100
- log.info("An app imports the build output, so each linked package needs `bun build.ts` in its directory.");
101
- }
102
- log.info("Undo with `buddy unlink:core`.");
103
- await log.flush();
104
- process.exit(ExitCode.Success);
105
- });
106
- buddy.command("unlink:core [...packages]", "Go back to the installed @stacksjs/* packages").option("--all", "Unlink everything that was linked", { default: !1 }).example("buddy unlink:core").action(async (packages, _options) => {
107
- const record = readRecord(), modulesDir = join(process.cwd(), "node_modules"), wanted = (packages?.length ?? 0) > 0 ? packages.map(normalize) : record?.packages ?? readdirSync(join(modulesDir, "@stacksjs"), { withFileTypes: !0 }).filter((entry) => entry.isSymbolicLink()).map((entry) => `@stacksjs/${entry.name}`);
108
- let removed = 0;
109
- const unlinked = new Set;
110
- for (const name of wanted) {
111
- const target = join(modulesDir, name);
112
- if (!isSymlink(target))
113
- continue;
114
- fs.rmSync(target, { force: !0 });
115
- unlinked.add(name);
116
- removed++;
117
- }
118
- const survivors = (record?.packages ?? []).filter((name) => !unlinked.has(name));
119
- if (survivors.length > 0)
120
- writeRecord({ framework: record.framework, packages: survivors });
121
- else
122
- fs.rmSync(recordPath(), { force: !0 });
123
- if (removed === 0) {
124
- log.info("Nothing was linked.");
125
- await log.flush();
126
- process.exit(ExitCode.Success);
127
- }
128
- log.info(`Unlinked ${removed} package${removed === 1 ? "" : "s"}; reinstalling the published copies...`);
129
- if (await Bun.spawn(["bun", "install", "--force"], { cwd: process.cwd(), stdout: "inherit", stderr: "inherit" }).exited !== 0) {
130
- log.error("`bun install` failed. The links are gone; re-run the install once the failure is resolved.");
131
- process.exit(ExitCode.FatalError);
132
- }
133
- for (const name of survivors) {
134
- const source = join(record.framework, "storage/framework/core", name.replace("@stacksjs/", "")), target = join(modulesDir, name);
135
- if (!existsSync(source))
136
- continue;
137
- fs.rmSync(target, { recursive: !0, force: !0 });
138
- fs.symlinkSync(source, target, "dir");
139
- }
140
- if (survivors.length > 0)
141
- log.success(`Unlinked ${removed}; ${survivors.length} package${survivors.length === 1 ? "" : "s"} still linked.`);
142
- else
143
- log.success("This project is back on the published packages.");
144
- await log.flush();
145
- process.exit(ExitCode.Success);
146
- });
147
- }
1
+ import{existsSync,lstatSync,readdirSync,realpathSync}from"node:fs";import fs from"node:fs";import{homedir}from"node:os";import{join,resolve}from"node:path";import process from"node:process";import{italic,log}from"@stacksjs/cli";import{ExitCode}from"@stacksjs/types";function recordPath(){return join(process.cwd(),"storage/framework/runtime/linked-core.json")}function readRecord(){try{return JSON.parse(fs.readFileSync(recordPath(),"utf-8"))}catch{return null}}function writeRecord(record){fs.mkdirSync(join(process.cwd(),"storage/framework/runtime"),{recursive:!0});fs.writeFileSync(recordPath(),`${JSON.stringify(record,null,2)}
2
+ `)}function resolveFrameworkPath(explicit){const candidates=[explicit,process.env.STACKS_FRAMEWORK_PATH,resolve(process.cwd(),"../stacks"),join(homedir(),"Code/stacks")].filter(Boolean);for(const candidate of candidates){const full=resolve(candidate);if(existsSync(join(full,"storage/framework/core")))return full}return null}function corePackages(framework){const coreDir=join(framework,"storage/framework/core"),found=new Map;for(const entry of readdirSync(coreDir,{withFileTypes:!0})){if(!entry.isDirectory())continue;const dir=join(coreDir,entry.name),manifest=join(dir,"package.json");if(!existsSync(manifest))continue;try{const{name}=JSON.parse(fs.readFileSync(manifest,"utf-8"));if(name?.startsWith("@stacksjs/"))found.set(name,dir)}catch{}}return found}function normalize(name){return`@stacksjs/${name.replace(/^@stacksjs\//,"").replace(/^core\//,"")}`}function isSymlink(path){try{return lstatSync(path).isSymbolicLink()}catch{return!1}}export function link(buddy){buddy.command("link:core [...packages]","Point this app's @stacksjs/* at a local framework checkout").option("--path <path>","Framework checkout to link against").option("--all","Link every core package the app has installed",{default:!1}).example("buddy link:core auth router").example("buddy link:core --all --path ../stacks").action(async(packages,options)=>{const framework=resolveFrameworkPath(options?.path);if(!framework){log.error("No framework checkout found.");log.info("Pass one with `--path <dir>` or set STACKS_FRAMEWORK_PATH.");process.exit(ExitCode.FatalError)}const available=corePackages(framework),modulesDir=join(process.cwd(),"node_modules"),wanted=(packages?.length??0)>0?packages.map(normalize):[...available.keys()].filter((name)=>existsSync(join(modulesDir,name))||options?.all);if(wanted.length===0){log.info("Nothing to link: no installed @stacksjs/* packages found in this project.");await log.flush();process.exit(ExitCode.Success)}const linked=[],unbuilt=[];for(const name of wanted){const source=available.get(name);if(!source){log.warn(`${name} is not in ${italic(framework)} \u2014 skipped.`);continue}const target=join(modulesDir,name);if(isSymlink(target)&&realpathSync(target)===realpathSync(source)){linked.push(name);continue}fs.rmSync(target,{recursive:!0,force:!0});fs.mkdirSync(join(target,".."),{recursive:!0});fs.symlinkSync(source,target,"dir");linked.push(name);if(!existsSync(join(source,"dist")))unbuilt.push(name)}writeRecord({framework,packages:linked});log.success(`Linked ${linked.length} package${linked.length===1?"":"s"} to ${italic(framework)}`);if(unbuilt.length>0){log.warn(`Not built yet: ${unbuilt.join(", ")}`);log.info("An app imports the build output, so each linked package needs `bun build.ts` in its directory.")}log.info("Undo with `buddy unlink:core`.");await log.flush();process.exit(ExitCode.Success)});buddy.command("unlink:core [...packages]","Go back to the installed @stacksjs/* packages").option("--all","Unlink everything that was linked",{default:!1}).example("buddy unlink:core").action(async(packages,_options)=>{const record=readRecord(),modulesDir=join(process.cwd(),"node_modules"),wanted=(packages?.length??0)>0?packages.map(normalize):record?.packages??readdirSync(join(modulesDir,"@stacksjs"),{withFileTypes:!0}).filter((entry)=>entry.isSymbolicLink()).map((entry)=>`@stacksjs/${entry.name}`);let removed=0;const unlinked=new Set;for(const name of wanted){const target=join(modulesDir,name);if(!isSymlink(target))continue;fs.rmSync(target,{force:!0});unlinked.add(name);removed++}const survivors=(record?.packages??[]).filter((name)=>!unlinked.has(name));if(survivors.length>0)writeRecord({framework:record.framework,packages:survivors});else fs.rmSync(recordPath(),{force:!0});if(removed===0){log.info("Nothing was linked.");await log.flush();process.exit(ExitCode.Success)}log.info(`Unlinked ${removed} package${removed===1?"":"s"}; reinstalling the published copies...`);if(await Bun.spawn(["bun","install","--force"],{cwd:process.cwd(),stdout:"inherit",stderr:"inherit"}).exited!==0){log.error("`bun install` failed. The links are gone; re-run the install once the failure is resolved.");process.exit(ExitCode.FatalError)}for(const name of survivors){const source=join(record.framework,"storage/framework/core",name.replace("@stacksjs/","")),target=join(modulesDir,name);if(!existsSync(source))continue;fs.rmSync(target,{recursive:!0,force:!0});fs.symlinkSync(source,target,"dir")}if(survivors.length>0)log.success(`Unlinked ${removed}; ${survivors.length} package${survivors.length===1?"":"s"} still linked.`);else log.success("This project is back on the published packages.");await log.flush();process.exit(ExitCode.Success)})}
@@ -1,84 +1,2 @@
1
- import process from "node:process";
2
- import { intro, log, onUnknownSubcommand, outro } from "@stacksjs/cli";
3
- import { ExitCode } from "@stacksjs/types";
4
- async function runStyleAction(entry, label, options) {
5
- const actions = await import("@stacksjs/actions"), { ok } = await actions[entry](options);
6
- if (!ok) {
7
- log.error(`${label} reported failure`);
8
- process.exit(ExitCode.FatalError);
9
- }
10
- }
11
- async function runStxChecks(startTime) {
12
- const { runStxLint } = await import("@stacksjs/actions"), report = await runStxLint(), out = [""];
13
- for (const r of report.results)
14
- if (r.status === "fail") {
15
- out.push(` FAIL ${r.label}`);
16
- out.push(` ${r.count} found, baseline ${r.baseline}${r.why ? ` (${r.why})` : ""}`);
17
- for (const line of r.detail.slice(0, 8))
18
- out.push(` ${line}`);
19
- } else if (r.status === "loosened")
20
- out.push(` DROP ${r.label}: ${r.count} < baseline ${r.baseline} \u2014 lower it in config/lint.ts`);
21
- else
22
- out.push(` ok ${r.label}${r.baseline > 0 ? ` (${r.count}, held)` : ""}`);
23
- if (report.distMissing)
24
- out.push("", " note: no build output found \u2014 the dist checks did not run. Run `./buddy build` first.");
25
- if (report.loosened > 0) {
26
- out.push("", " Current counts, for config/lint.ts:");
27
- for (const [id, count] of Object.entries(report.counts).sort(([a], [b]) => a.localeCompare(b)))
28
- out.push(` '${id}': ${count},`);
29
- }
30
- console.log(out.join(`
31
- `));
32
- if (report.failed > 0) {
33
- log.error(`${report.failed} stx check(s) failed.`);
34
- await outro("stx checks failed", { startTime, useSeconds: !0 });
35
- process.exit(ExitCode.FatalError);
36
- }
37
- if (report.loosened > 0) {
38
- log.warn(`No regressions, but ${report.loosened} baseline(s) are now stale.`);
39
- await outro("stx baselines stale", { startTime, useSeconds: !0 });
40
- process.exit(ExitCode.FatalError);
41
- }
42
- await outro("All stx checks pass", { startTime, useSeconds: !0 });
43
- }
44
- export function lint(buddy) {
45
- const descriptions = {
46
- lint: "Automagically lints your project codebase",
47
- lintFix: "Automagically fixes all lint errors",
48
- format: "Format your project codebase",
49
- formatCheck: "Check formatting without making changes",
50
- project: "Target a specific project",
51
- stx: "Run the stx conformance checks instead of code style",
52
- verbose: "Enable verbose output"
53
- };
54
- buddy.command("lint", descriptions.lint).option("-f, --fix", descriptions.lintFix, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--stx", descriptions.stx, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
55
- log.debug("Running `buddy lint` ...", options);
56
- const startTime = await intro("buddy lint");
57
- if (options.stx) {
58
- await runStxChecks(startTime);
59
- return;
60
- }
61
- await runStyleAction(options.fix ? "lintFix" : "lintProject", "lint");
62
- await outro("Linted your project", { startTime, useSeconds: !0 });
63
- });
64
- buddy.command("lint:fix", descriptions.lintFix).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
65
- log.debug("Running `buddy lint:fix` ...", options);
66
- const startTime = await intro("buddy lint:fix");
67
- log.info("Fixing lint errors...");
68
- await runStyleAction("lintFix", "lint:fix");
69
- await outro("Fixed lint errors", { startTime, useSeconds: !0 });
70
- });
71
- buddy.command("format", descriptions.format).option("-w, --write", "Write changes to files", { default: !1 }).option("-c, --check", descriptions.formatCheck, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
72
- log.debug("Running `buddy format` ...", options);
73
- const startTime = await intro("buddy format");
74
- await runStyleAction("formatProject", "format", options.check ? { check: !0 } : { write: !0 });
75
- await outro("Formatted your project", { startTime, useSeconds: !0 });
76
- });
77
- buddy.command("format:check", descriptions.formatCheck).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
78
- log.debug("Running `buddy format:check` ...", options);
79
- const startTime = await intro("buddy format:check");
80
- await runStyleAction("formatProject", "format:check", { check: !0 });
81
- await outro("Format check complete", { startTime, useSeconds: !0 });
82
- });
83
- onUnknownSubcommand(buddy, "lint");
84
- }
1
+ import process from"node:process";import{intro,log,onUnknownSubcommand,outro}from"@stacksjs/cli";import{ExitCode}from"@stacksjs/types";async function runStyleAction(entry,label,options){const actions=await import("@stacksjs/actions"),{ok}=await actions[entry](options);if(!ok){log.error(`${label} reported failure`);process.exit(ExitCode.FatalError)}}async function runStxChecks(startTime){const{runStxLint}=await import("@stacksjs/actions"),report=await runStxLint(),out=[""];for(const r of report.results)if(r.status==="fail"){out.push(` FAIL ${r.label}`);out.push(` ${r.count} found, baseline ${r.baseline}${r.why?` (${r.why})`:""}`);for(const line of r.detail.slice(0,8))out.push(` ${line}`)}else if(r.status==="loosened")out.push(` DROP ${r.label}: ${r.count} < baseline ${r.baseline} \u2014 lower it in config/lint.ts`);else out.push(` ok ${r.label}${r.baseline>0?` (${r.count}, held)`:""}`);if(report.distMissing)out.push(""," note: no build output found \u2014 the dist checks did not run. Run `./buddy build` first.");if(report.loosened>0){out.push(""," Current counts, for config/lint.ts:");for(const[id,count]of Object.entries(report.counts).sort(([a],[b])=>a.localeCompare(b)))out.push(` '${id}': ${count},`)}console.log(out.join(`
2
+ `));if(report.failed>0){log.error(`${report.failed} stx check(s) failed.`);await outro("stx checks failed",{startTime,useSeconds:!0});process.exit(ExitCode.FatalError)}if(report.loosened>0){log.warn(`No regressions, but ${report.loosened} baseline(s) are now stale.`);await outro("stx baselines stale",{startTime,useSeconds:!0});process.exit(ExitCode.FatalError)}await outro("All stx checks pass",{startTime,useSeconds:!0})}export function lint(buddy){const descriptions={lint:"Automagically lints your project codebase",lintFix:"Automagically fixes all lint errors",format:"Format your project codebase",formatCheck:"Check formatting without making changes",project:"Target a specific project",stx:"Run the stx conformance checks instead of code style",verbose:"Enable verbose output"};buddy.command("lint",descriptions.lint).option("-f, --fix",descriptions.lintFix,{default:!1}).option("-p, --project [project]",descriptions.project,{default:!1}).option("--stx",descriptions.stx,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy lint` ...",options);const startTime=await intro("buddy lint");if(options.stx){await runStxChecks(startTime);return}await runStyleAction(options.fix?"lintFix":"lintProject","lint");await outro("Linted your project",{startTime,useSeconds:!0})});buddy.command("lint:fix",descriptions.lintFix).option("-p, --project [project]",descriptions.project,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy lint:fix` ...",options);const startTime=await intro("buddy lint:fix");log.info("Fixing lint errors...");await runStyleAction("lintFix","lint:fix");await outro("Fixed lint errors",{startTime,useSeconds:!0})});buddy.command("format",descriptions.format).option("-w, --write","Write changes to files",{default:!1}).option("-c, --check",descriptions.formatCheck,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy format` ...",options);const startTime=await intro("buddy format");await runStyleAction("formatProject","format",options.check?{check:!0}:{write:!0});await outro("Formatted your project",{startTime,useSeconds:!0})});buddy.command("format:check",descriptions.formatCheck).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy format:check` ...",options);const startTime=await intro("buddy format:check");await runStyleAction("formatProject","format:check",{check:!0});await outro("Format check complete",{startTime,useSeconds:!0})});onUnknownSubcommand(buddy,"lint")}
@@ -1,140 +1 @@
1
- import process from "node:process";
2
- import { log } from "@stacksjs/logging";
3
- import { onUnknownSubcommand } from "@stacksjs/cli";
4
- export function list(buddy) {
5
- const descriptions = {
6
- list: "List all available Buddy commands",
7
- project: "Target a specific project",
8
- verbose: "Enable verbose output",
9
- filter: "Filter commands by name or group",
10
- namespace: "Filter commands by namespace (e.g., make, env, db)",
11
- grouped: "Group commands by category",
12
- format: "Output format (text, json)",
13
- json: "Output the complete command inventory as JSON"
14
- };
15
- buddy.command("list", descriptions.list).option("-p, --project [project]", descriptions.project, { default: !1 }).option("-f, --filter [filter]", descriptions.filter).option("-n, --namespace [namespace]", descriptions.namespace).option("-g, --grouped", descriptions.grouped, { default: !0 }).option("--format [format]", descriptions.format, { default: "text" }).option("-J, --json", descriptions.json, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).example("buddy list").example("buddy list --filter=make").example("buddy list --namespace=make").example("buddy list --namespace=env").example("buddy list --no-grouped").example("buddy list --format=json").example("buddy list --json").action(async (options) => {
16
- log.debug("Running `buddy list` ...", options);
17
- const { bold, dim, green } = await import("@stacksjs/cli"), commands = buddy.commands || [];
18
- let filteredCommands = commands;
19
- if (options.filter) {
20
- const filterStr = String(options.filter).toLowerCase();
21
- filteredCommands = commands.filter((cmd) => {
22
- const name = canonicalCommandName(cmd), desc = cmd.description || "";
23
- return name.toLowerCase().includes(filterStr) || desc.toLowerCase().includes(filterStr);
24
- });
25
- }
26
- if (options.namespace) {
27
- const namespaceStr = String(options.namespace).toLowerCase();
28
- filteredCommands = filteredCommands.filter((cmd) => {
29
- return (cmd.namespace || "").toLowerCase() === namespaceStr;
30
- });
31
- }
32
- if (filteredCommands.length === 0) {
33
- if (usesJsonOutput(options))
34
- console.log(JSON.stringify({ commands: [], total: 0 }, null, 2));
35
- else {
36
- console.log(dim("No commands found"));
37
- if (options.filter)
38
- console.log(dim(`Try removing the filter: ${options.filter}`));
39
- }
40
- return;
41
- }
42
- if (usesJsonOutput(options)) {
43
- const jsonOutput = filteredCommands.map(commandInventoryEntry).sort((a, b) => a.name.localeCompare(b.name));
44
- console.log(JSON.stringify({ commands: jsonOutput, total: jsonOutput.length }, null, 2));
45
- return;
46
- }
47
- if (options.grouped) {
48
- const groups = new Map;
49
- for (const cmd of filteredCommands) {
50
- const name = canonicalCommandName(cmd);
51
- if (!name)
52
- continue;
53
- let group = "General";
54
- if (name.includes(":"))
55
- group = name.split(":")[0] ?? "General";
56
- else if (["dev", "build", "test", "lint"].includes(name))
57
- group = "Development";
58
- else if (["deploy", "release", "publish"].includes(name))
59
- group = "Deployment";
60
- else if (["migrate", "seed", "fresh"].includes(name))
61
- group = "Database";
62
- else if (["doctor", "about", "version"].includes(name))
63
- group = "Info";
64
- if (!groups.has(group))
65
- groups.set(group, []);
66
- groups.get(group).push(cmd);
67
- }
68
- console.log("");
69
- console.log(bold(green("Available Commands:")));
70
- console.log("");
71
- const sortedGroups = Array.from(groups.entries()).sort((a, b) => {
72
- const order = ["Development", "General", "Info"], aIndex = order.indexOf(a[0]), bIndex = order.indexOf(b[0]);
73
- if (aIndex >= 0 && bIndex >= 0)
74
- return aIndex - bIndex;
75
- if (aIndex >= 0)
76
- return -1;
77
- if (bIndex >= 0)
78
- return 1;
79
- return a[0].localeCompare(b[0]);
80
- });
81
- for (const [group, cmds] of sortedGroups) {
82
- console.log(bold(`${group}:`));
83
- cmds.sort((a, b) => canonicalCommandName(a).localeCompare(canonicalCommandName(b)));
84
- for (const cmd of cmds) {
85
- const name = canonicalCommandName(cmd).padEnd(20), desc = cmd.description || "";
86
- console.log(` ${green(name)} ${dim(desc)}`);
87
- }
88
- console.log("");
89
- }
90
- } else {
91
- console.log("");
92
- console.log(bold(green("Available Commands:")));
93
- console.log("");
94
- filteredCommands.sort((a, b) => canonicalCommandName(a).localeCompare(canonicalCommandName(b)));
95
- for (const cmd of filteredCommands) {
96
- const name = canonicalCommandName(cmd).padEnd(20), desc = cmd.description || "";
97
- console.log(` ${green(name)} ${dim(desc)}`);
98
- }
99
- console.log("");
100
- }
101
- console.log(dim(`Total: ${filteredCommands.length} commands`));
102
- console.log("");
103
- });
104
- onUnknownSubcommand(buddy, "list");
105
- }
106
- export function commandInventoryEntry(command) {
107
- const entry = {
108
- name: canonicalCommandName(command),
109
- description: command.description,
110
- aliases: [...command.aliasNames],
111
- usage: command.usageLine,
112
- arguments: command.args.map((argument) => ({
113
- name: argument.value,
114
- required: argument.required,
115
- variadic: argument.variadic
116
- })),
117
- options: command.options.map((option) => ({
118
- name: option.name,
119
- flags: declaredOptionFlags(option.rawName),
120
- description: option.description,
121
- required: option.required === !0,
122
- boolean: option.isBoolean === !0,
123
- negated: option.negated,
124
- ...option.config.default === void 0 ? {} : { default: option.config.default }
125
- })),
126
- examples: command.examples.map((example) => typeof example === "string" ? example : example("buddy"))
127
- };
128
- if (command.namespace)
129
- entry.namespace = command.namespace;
130
- return entry;
131
- }
132
- function canonicalCommandName(command) {
133
- return command.namespace ? `${command.namespace}:${command.name}` : command.name;
134
- }
135
- function declaredOptionFlags(rawName) {
136
- return [...rawName.matchAll(/-{1,2}([\w-]+)/g)].map((match) => match[1]);
137
- }
138
- function usesJsonOutput(options) {
139
- return options.json === !0 || options.format === "json";
140
- }
1
+ import process from"node:process";import{log}from"@stacksjs/logging";import{onUnknownSubcommand}from"@stacksjs/cli";export function list(buddy){const descriptions={list:"List all available Buddy commands",project:"Target a specific project",verbose:"Enable verbose output",filter:"Filter commands by name or group",namespace:"Filter commands by namespace (e.g., make, env, db)",grouped:"Group commands by category",format:"Output format (text, json)",json:"Output the complete command inventory as JSON"};buddy.command("list",descriptions.list).option("-p, --project [project]",descriptions.project,{default:!1}).option("-f, --filter [filter]",descriptions.filter).option("-n, --namespace [namespace]",descriptions.namespace).option("-g, --grouped",descriptions.grouped,{default:!0}).option("--format [format]",descriptions.format,{default:"text"}).option("-J, --json",descriptions.json,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).example("buddy list").example("buddy list --filter=make").example("buddy list --namespace=make").example("buddy list --namespace=env").example("buddy list --no-grouped").example("buddy list --format=json").example("buddy list --json").action(async(options)=>{log.debug("Running `buddy list` ...",options);const{bold,dim,green}=await import("@stacksjs/cli"),commands=buddy.commands||[];let filteredCommands=commands;if(options.filter){const filterStr=String(options.filter).toLowerCase();filteredCommands=commands.filter((cmd)=>{const name=canonicalCommandName(cmd),desc=cmd.description||"";return name.toLowerCase().includes(filterStr)||desc.toLowerCase().includes(filterStr)})}if(options.namespace){const namespaceStr=String(options.namespace).toLowerCase();filteredCommands=filteredCommands.filter((cmd)=>{return(cmd.namespace||"").toLowerCase()===namespaceStr})}if(filteredCommands.length===0){if(usesJsonOutput(options))console.log(JSON.stringify({commands:[],total:0},null,2));else{console.log(dim("No commands found"));if(options.filter)console.log(dim(`Try removing the filter: ${options.filter}`))}return}if(usesJsonOutput(options)){const jsonOutput=filteredCommands.map(commandInventoryEntry).sort((a,b)=>a.name.localeCompare(b.name));console.log(JSON.stringify({commands:jsonOutput,total:jsonOutput.length},null,2));return}if(options.grouped){const groups=new Map;for(const cmd of filteredCommands){const name=canonicalCommandName(cmd);if(!name)continue;let group="General";if(name.includes(":"))group=name.split(":")[0]??"General";else if(["dev","build","test","lint"].includes(name))group="Development";else if(["deploy","release","publish"].includes(name))group="Deployment";else if(["migrate","seed","fresh"].includes(name))group="Database";else if(["doctor","about","version"].includes(name))group="Info";if(!groups.has(group))groups.set(group,[]);groups.get(group).push(cmd)}console.log("");console.log(bold(green("Available Commands:")));console.log("");const sortedGroups=Array.from(groups.entries()).sort((a,b)=>{const order=["Development","General","Info"],aIndex=order.indexOf(a[0]),bIndex=order.indexOf(b[0]);if(aIndex>=0&&bIndex>=0)return aIndex-bIndex;if(aIndex>=0)return-1;if(bIndex>=0)return 1;return a[0].localeCompare(b[0])});for(const[group,cmds]of sortedGroups){console.log(bold(`${group}:`));cmds.sort((a,b)=>canonicalCommandName(a).localeCompare(canonicalCommandName(b)));for(const cmd of cmds){const name=canonicalCommandName(cmd).padEnd(20),desc=cmd.description||"";console.log(` ${green(name)} ${dim(desc)}`)}console.log("")}}else{console.log("");console.log(bold(green("Available Commands:")));console.log("");filteredCommands.sort((a,b)=>canonicalCommandName(a).localeCompare(canonicalCommandName(b)));for(const cmd of filteredCommands){const name=canonicalCommandName(cmd).padEnd(20),desc=cmd.description||"";console.log(` ${green(name)} ${dim(desc)}`)}console.log("")}console.log(dim(`Total: ${filteredCommands.length} commands`));console.log("")});onUnknownSubcommand(buddy,"list")}export function commandInventoryEntry(command){const entry={name:canonicalCommandName(command),description:command.description,aliases:[...command.aliasNames],usage:command.usageLine,arguments:command.args.map((argument)=>({name:argument.value,required:argument.required,variadic:argument.variadic})),options:command.options.map((option)=>({name:option.name,flags:declaredOptionFlags(option.rawName),description:option.description,required:option.required===!0,boolean:option.isBoolean===!0,negated:option.negated,...option.config.default===void 0?{}:{default:option.config.default}})),examples:command.examples.map((example)=>typeof example==="string"?example:example("buddy"))};if(command.namespace)entry.namespace=command.namespace;return entry}function canonicalCommandName(command){return command.namespace?`${command.namespace}:${command.name}`:command.name}function declaredOptionFlags(rawName){return[...rawName.matchAll(/-{1,2}([\w-]+)/g)].map((match)=>match[1])}function usesJsonOutput(options){return options.json===!0||options.format==="json"}