@objectstack/cli 2.0.6 → 2.0.7

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/cli@2.0.6 build /home/runner/work/spec/spec/packages/cli
2
+ > @objectstack/cli@2.0.7 build /home/runner/work/spec/spec/packages/cli
3
3
  > tsup
4
4
 
5
5
  CLI Building entry: src/bin.ts
@@ -15,10 +15,10 @@
15
15
  ESM Build start
16
16
  CLI Cleaning output folder
17
17
  ESM Build start
18
- ESM dist/index.js 58.94 KB
19
- ESM ⚡️ Build success in 158ms
20
- ESM dist/bin.js 61.55 KB
21
- ESM ⚡️ Build success in 186ms
18
+ ESM dist/index.js 71.41 KB
19
+ ESM ⚡️ Build success in 166ms
20
+ ESM dist/bin.js 74.36 KB
21
+ ESM ⚡️ Build success in 178ms
22
22
  DTS Build start
23
- DTS ⚡️ Build success in 7538ms
24
- DTS dist/index.d.ts 2.93 KB
23
+ DTS ⚡️ Build success in 9654ms
24
+ DTS dist/index.d.ts 3.42 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @objectstack/cli
2
2
 
3
+ ## 2.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @objectstack/spec@2.0.7
9
+ - @objectstack/core@2.0.7
10
+ - @objectstack/objectql@2.0.7
11
+ - @objectstack/driver-memory@2.0.7
12
+ - @objectstack/plugin-hono-server@2.0.7
13
+ - @objectstack/rest@2.0.7
14
+ - @objectstack/runtime@2.0.7
15
+
3
16
  ## 2.0.6
4
17
 
5
18
  ### Patch Changes
package/README.md CHANGED
@@ -58,6 +58,15 @@ os compile
58
58
 
59
59
  Available generate types: `object`, `view`, `action`, `flow`, `agent`, `dashboard`, `app`
60
60
 
61
+ ### Plugin Management
62
+
63
+ | Command | Description |
64
+ |---------|-------------|
65
+ | `os plugin list [config]` | List plugins defined in configuration (alias: `os plugin ls`) |
66
+ | `os plugin info <name> [config]` | Show detailed plugin information |
67
+ | `os plugin add <package>` | Add a plugin import and entry to config |
68
+ | `os plugin remove <name>` | Remove a plugin from config (alias: `os plugin rm`) |
69
+
61
70
  ### Quality
62
71
 
63
72
  | Command | Description |
@@ -118,6 +127,81 @@ export default defineStack({
118
127
  - `-d, --dir <directory>` — Override target directory
119
128
  - `--dry-run` — Preview without writing files
120
129
 
130
+ ### `os plugin list`
131
+
132
+ - `--json` — Output as JSON
133
+
134
+ ### `os plugin add`
135
+
136
+ - `-d, --dev` — Add as a dev-only plugin
137
+ - `-c, --config <path>` — Configuration file path
138
+
139
+ ### `os plugin remove`
140
+
141
+ - `-c, --config <path>` — Configuration file path
142
+
143
+ ## Plugin CLI Extensions
144
+
145
+ Plugins can extend the CLI with custom commands via the `contributes.commands` manifest field. The CLI automatically discovers and loads these commands at startup.
146
+
147
+ ### How to Create a CLI Plugin
148
+
149
+ **1. Declare commands in the plugin manifest:**
150
+
151
+ ```typescript
152
+ export default defineStack({
153
+ manifest: {
154
+ id: 'com.acme.marketplace',
155
+ version: '1.0.0',
156
+ type: 'plugin',
157
+ name: 'Marketplace Plugin',
158
+ contributes: {
159
+ commands: [
160
+ {
161
+ name: 'marketplace',
162
+ description: 'Manage marketplace applications',
163
+ module: './dist/cli.js',
164
+ },
165
+ ],
166
+ },
167
+ },
168
+ });
169
+ ```
170
+
171
+ **2. Export Commander.js commands from the module:**
172
+
173
+ ```typescript
174
+ // src/cli.ts
175
+ import { Command } from 'commander';
176
+
177
+ const marketplaceCommand = new Command('marketplace')
178
+ .description('Manage marketplace applications')
179
+ .addCommand(
180
+ new Command('search')
181
+ .argument('<query>')
182
+ .action(async (query) => { /* ... */ })
183
+ )
184
+ .addCommand(
185
+ new Command('install')
186
+ .argument('<app>')
187
+ .action(async (app) => { /* ... */ })
188
+ );
189
+
190
+ // Named export (recommended)
191
+ export const commands = [marketplaceCommand];
192
+ // Also supports: export default Command | Command[]
193
+ ```
194
+
195
+ **3. Register the plugin in the host project and use:**
196
+
197
+ ```bash
198
+ os plugin add @acme/plugin-marketplace
199
+ os marketplace search "crm"
200
+ os marketplace install com.acme.crm
201
+ ```
202
+
203
+ For a complete guide, see the [Plugin CLI Extensions](/docs/guides/plugins#cli-command-extensions) section in the Plugins guide.
204
+
121
205
  ### `os info`
122
206
 
123
207
  - `--json` — Output as JSON
@@ -133,9 +217,10 @@ os init # 1. Create project
133
217
  os generate object customer # 2. Add a Customer object
134
218
  os generate object order # 3. Add an Order object
135
219
  os generate view customer # 4. Add a list view
136
- os validate # 5. Validate everything
137
- os dev # 6. Start dev server
138
- os compile # 7. Build for production
220
+ os plugin add @objectstack/plugin-auth # 5. Add auth plugin
221
+ os validate # 6. Validate everything
222
+ os dev # 7. Start dev server
223
+ os compile # 8. Build for production
139
224
  ```
140
225
 
141
226
  ## License
package/dist/bin.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/bin.ts
4
4
  import { createRequire as createRequire2 } from "module";
5
- import { Command as Command12 } from "commander";
6
- import chalk13 from "chalk";
5
+ import { Command as Command13 } from "commander";
6
+ import chalk15 from "chalk";
7
7
 
8
8
  // src/commands/compile.ts
9
9
  import { Command } from "commander";
@@ -69,10 +69,10 @@ function formatZodErrors(error) {
69
69
  console.log(chalk.bold.red(`
70
70
  ${section}:`));
71
71
  for (const issue of sectionIssues) {
72
- const path11 = issue.path?.join(".") || "";
72
+ const path12 = issue.path?.join(".") || "";
73
73
  const code = issue.code || "";
74
74
  const msg = issue.message || "";
75
- console.log(chalk.red(` \u2717 ${path11}`));
75
+ console.log(chalk.red(` \u2717 ${path12}`));
76
76
  console.log(chalk.dim(` ${code}: ${msg}`));
77
77
  if (issue.expected) {
78
78
  console.log(chalk.dim(` expected: ${chalk.green(issue.expected)}`));
@@ -1874,33 +1874,366 @@ var generateCommand = new Command11("generate").alias("g").description("Generate
1874
1874
  }
1875
1875
  });
1876
1876
 
1877
+ // src/commands/plugin.ts
1878
+ import { Command as Command12 } from "commander";
1879
+ import chalk13 from "chalk";
1880
+ import fs11 from "fs";
1881
+ import path11 from "path";
1882
+ function resolvePluginName(plugin) {
1883
+ if (typeof plugin === "string") return plugin;
1884
+ if (plugin && typeof plugin === "object") {
1885
+ const p = plugin;
1886
+ if (typeof p.name === "string") return p.name;
1887
+ if (p.constructor && p.constructor.name !== "Object") return p.constructor.name;
1888
+ }
1889
+ return "unknown";
1890
+ }
1891
+ function resolvePluginVersion(plugin) {
1892
+ if (plugin && typeof plugin === "object") {
1893
+ const p = plugin;
1894
+ if (typeof p.version === "string") return p.version;
1895
+ }
1896
+ return "-";
1897
+ }
1898
+ function resolvePluginType(plugin) {
1899
+ if (plugin && typeof plugin === "object") {
1900
+ const p = plugin;
1901
+ if (typeof p.type === "string") return p.type;
1902
+ }
1903
+ return "standard";
1904
+ }
1905
+ function readConfigText(configPath) {
1906
+ return fs11.readFileSync(configPath, "utf-8");
1907
+ }
1908
+ function addPluginToConfig(configPath, packageName) {
1909
+ let content = readConfigText(configPath);
1910
+ const shortName = packageName.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
1911
+ const varName = shortName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + "Plugin";
1912
+ const importLine = `import ${varName} from '${packageName}';
1913
+ `;
1914
+ if (content.includes(packageName)) {
1915
+ throw new Error(`Plugin '${packageName}' is already referenced in the config`);
1916
+ }
1917
+ const importRegex = /^import .+$/gm;
1918
+ let lastImportEnd = 0;
1919
+ let match;
1920
+ while ((match = importRegex.exec(content)) !== null) {
1921
+ lastImportEnd = match.index + match[0].length;
1922
+ }
1923
+ if (lastImportEnd > 0) {
1924
+ content = content.slice(0, lastImportEnd) + "\n" + importLine + content.slice(lastImportEnd);
1925
+ } else {
1926
+ content = importLine + "\n" + content;
1927
+ }
1928
+ if (/plugins\s*:\s*\[/.test(content)) {
1929
+ let replaced = false;
1930
+ content = content.replace(
1931
+ /(plugins\s*:\s*\[)/,
1932
+ (match2) => {
1933
+ if (replaced) return match2;
1934
+ replaced = true;
1935
+ return `${match2}
1936
+ ${varName},`;
1937
+ }
1938
+ );
1939
+ } else {
1940
+ content = content.replace(
1941
+ /(defineStack\(\{[\s\S]*?)(}\s*\))/,
1942
+ `$1 plugins: [
1943
+ ${varName},
1944
+ ],
1945
+ $2`
1946
+ );
1947
+ }
1948
+ fs11.writeFileSync(configPath, content);
1949
+ }
1950
+ function removePluginFromConfig(configPath, pluginName) {
1951
+ let content = readConfigText(configPath);
1952
+ const importRegex = new RegExp(`^import .+['"]${escapeRegex(pluginName)}['"]\\s*;?\\s*$\\n?`, "gm");
1953
+ const hadImport = importRegex.test(content);
1954
+ importRegex.lastIndex = 0;
1955
+ content = content.replace(importRegex, "");
1956
+ const shortName = pluginName.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
1957
+ const varName = shortName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + "Plugin";
1958
+ if (!hadImport) {
1959
+ const varImportRegex = new RegExp(`^import .* ${escapeRegex(varName)} .+$\\n?`, "gm");
1960
+ content = content.replace(varImportRegex, "");
1961
+ }
1962
+ const entryPatterns = [
1963
+ new RegExp(`\\s*${escapeRegex(varName)},?\\n?`, "g"),
1964
+ new RegExp(`\\s*['"]${escapeRegex(pluginName)}['"],?\\n?`, "g")
1965
+ ];
1966
+ for (const pattern of entryPatterns) {
1967
+ content = content.replace(pattern, "\n");
1968
+ }
1969
+ content = content.replace(/plugins\s*:\s*\[\s*\],?\n?/g, "");
1970
+ fs11.writeFileSync(configPath, content);
1971
+ }
1972
+ function escapeRegex(str) {
1973
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1974
+ }
1975
+ var listCommand = new Command12("list").alias("ls").description("List plugins defined in the configuration").argument("[config]", "Configuration file path").option("--json", "Output as JSON").action(async (configSource, options) => {
1976
+ try {
1977
+ const { config } = await loadConfig(configSource);
1978
+ const plugins = config.plugins || [];
1979
+ const devPlugins = config.devPlugins || [];
1980
+ if (options?.json) {
1981
+ const data = {
1982
+ plugins: plugins.map((p) => ({
1983
+ name: resolvePluginName(p),
1984
+ version: resolvePluginVersion(p),
1985
+ type: resolvePluginType(p),
1986
+ dev: false
1987
+ })),
1988
+ devPlugins: devPlugins.map((p) => ({
1989
+ name: resolvePluginName(p),
1990
+ version: resolvePluginVersion(p),
1991
+ type: resolvePluginType(p),
1992
+ dev: true
1993
+ }))
1994
+ };
1995
+ console.log(JSON.stringify(data, null, 2));
1996
+ return;
1997
+ }
1998
+ printHeader("Plugins");
1999
+ if (plugins.length === 0 && devPlugins.length === 0) {
2000
+ printInfo("No plugins configured");
2001
+ console.log("");
2002
+ console.log(chalk13.dim(" Hint: Add plugins to your objectstack.config.ts"));
2003
+ console.log(chalk13.dim(" Or run: os plugin add <package-name>"));
2004
+ console.log("");
2005
+ return;
2006
+ }
2007
+ if (plugins.length > 0) {
2008
+ console.log(chalk13.bold(`
2009
+ Plugins (${plugins.length}):`));
2010
+ for (const plugin of plugins) {
2011
+ const name = resolvePluginName(plugin);
2012
+ const version = resolvePluginVersion(plugin);
2013
+ const type = resolvePluginType(plugin);
2014
+ console.log(
2015
+ ` ${chalk13.cyan("\u25CF")} ${chalk13.white(name)}` + (version !== "-" ? chalk13.dim(` v${version}`) : "") + (type !== "standard" ? chalk13.dim(` [${type}]`) : "")
2016
+ );
2017
+ }
2018
+ }
2019
+ if (devPlugins.length > 0) {
2020
+ console.log(chalk13.bold(`
2021
+ Dev Plugins (${devPlugins.length}):`));
2022
+ for (const plugin of devPlugins) {
2023
+ const name = resolvePluginName(plugin);
2024
+ const version = resolvePluginVersion(plugin);
2025
+ console.log(
2026
+ ` ${chalk13.yellow("\u25CF")} ${chalk13.white(name)}` + (version !== "-" ? chalk13.dim(` v${version}`) : "") + chalk13.dim(" [dev]")
2027
+ );
2028
+ }
2029
+ }
2030
+ console.log("");
2031
+ } catch (error) {
2032
+ printError(error.message || String(error));
2033
+ process.exit(1);
2034
+ }
2035
+ });
2036
+ var infoSubCommand = new Command12("info").description("Show detailed information about a plugin").argument("<name>", "Plugin name or package name").argument("[config]", "Configuration file path").action(async (name, configSource) => {
2037
+ try {
2038
+ const { config } = await loadConfig(configSource);
2039
+ const allPlugins = [
2040
+ ...config.plugins || [],
2041
+ ...config.devPlugins || []
2042
+ ];
2043
+ const found = allPlugins.find((p) => {
2044
+ const pName = resolvePluginName(p);
2045
+ return pName === name || pName.includes(name);
2046
+ });
2047
+ if (!found) {
2048
+ printError(`Plugin '${name}' not found in configuration`);
2049
+ console.log("");
2050
+ console.log(chalk13.dim(" Available plugins:"));
2051
+ for (const p of allPlugins) {
2052
+ console.log(chalk13.dim(` - ${resolvePluginName(p)}`));
2053
+ }
2054
+ console.log("");
2055
+ process.exit(1);
2056
+ }
2057
+ printHeader(`Plugin: ${resolvePluginName(found)}`);
2058
+ printKV("Name", resolvePluginName(found));
2059
+ printKV("Version", resolvePluginVersion(found));
2060
+ printKV("Type", resolvePluginType(found));
2061
+ const isDev = (config.devPlugins || []).includes(found);
2062
+ printKV("Environment", isDev ? "development" : "production");
2063
+ if (found && typeof found === "object") {
2064
+ const p = found;
2065
+ if (typeof p.description === "string") {
2066
+ printKV("Description", p.description);
2067
+ }
2068
+ if (Array.isArray(p.dependencies) && p.dependencies.length > 0) {
2069
+ printKV("Dependencies", p.dependencies.join(", "));
2070
+ }
2071
+ if (typeof p.init === "function") {
2072
+ printInfo("This is a runtime plugin instance (has init function)");
2073
+ }
2074
+ }
2075
+ if (typeof found === "string") {
2076
+ printInfo("This is a string reference (will be imported at runtime)");
2077
+ }
2078
+ console.log("");
2079
+ } catch (error) {
2080
+ printError(error.message || String(error));
2081
+ process.exit(1);
2082
+ }
2083
+ });
2084
+ var addCommand = new Command12("add").description("Add a plugin to objectstack.config.ts").argument("<package>", "Plugin package name (e.g. @objectstack/plugin-auth)").option("-d, --dev", "Add as a dev-only plugin").option("-c, --config <path>", "Configuration file path").action(async (packageName, options) => {
2085
+ try {
2086
+ const configPath = resolveConfigPath(options?.config);
2087
+ printHeader("Add Plugin");
2088
+ console.log(` ${chalk13.dim("Package:")} ${chalk13.white(packageName)}`);
2089
+ console.log(` ${chalk13.dim("Config:")} ${chalk13.white(path11.relative(process.cwd(), configPath))}`);
2090
+ console.log("");
2091
+ addPluginToConfig(configPath, packageName);
2092
+ printSuccess(`Added ${chalk13.cyan(packageName)} to config`);
2093
+ console.log("");
2094
+ console.log(chalk13.dim(" Next steps:"));
2095
+ console.log(chalk13.dim(` 1. Install the package: pnpm add ${packageName}`));
2096
+ console.log(chalk13.dim(" 2. Run: os validate"));
2097
+ console.log("");
2098
+ } catch (error) {
2099
+ printError(error.message || String(error));
2100
+ process.exit(1);
2101
+ }
2102
+ });
2103
+ var removeCommand = new Command12("remove").alias("rm").description("Remove a plugin from objectstack.config.ts").argument("<name>", "Plugin name or package name to remove").option("-c, --config <path>", "Configuration file path").action(async (pluginName, options) => {
2104
+ try {
2105
+ const configPath = resolveConfigPath(options?.config);
2106
+ printHeader("Remove Plugin");
2107
+ console.log(` ${chalk13.dim("Plugin:")} ${chalk13.white(pluginName)}`);
2108
+ console.log(` ${chalk13.dim("Config:")} ${chalk13.white(path11.relative(process.cwd(), configPath))}`);
2109
+ console.log("");
2110
+ removePluginFromConfig(configPath, pluginName);
2111
+ printSuccess(`Removed ${chalk13.cyan(pluginName)} from config`);
2112
+ console.log("");
2113
+ console.log(chalk13.dim(" Tip: Run `pnpm remove " + pluginName + "` to uninstall the package"));
2114
+ console.log("");
2115
+ } catch (error) {
2116
+ printError(error.message || String(error));
2117
+ process.exit(1);
2118
+ }
2119
+ });
2120
+ var pluginCommand = new Command12("plugin").description("Manage plugins (list, info, add, remove)").addCommand(listCommand).addCommand(infoSubCommand).addCommand(addCommand).addCommand(removeCommand);
2121
+
2122
+ // src/utils/plugin-commands.ts
2123
+ import chalk14 from "chalk";
2124
+ async function loadPluginCommands(program2) {
2125
+ let config;
2126
+ try {
2127
+ const loaded = await loadConfig();
2128
+ config = loaded.config;
2129
+ } catch {
2130
+ return;
2131
+ }
2132
+ const plugins = [
2133
+ ...config.plugins || [],
2134
+ ...config.devPlugins || []
2135
+ ];
2136
+ const contributions = [];
2137
+ for (const plugin of plugins) {
2138
+ if (!plugin || typeof plugin !== "object") continue;
2139
+ const p = plugin;
2140
+ const manifest = p.manifest;
2141
+ const contributes = manifest?.contributes ?? p.contributes;
2142
+ if (!contributes) continue;
2143
+ const commands = contributes.commands;
2144
+ if (!Array.isArray(commands)) continue;
2145
+ const pluginName = resolvePluginName2(p);
2146
+ for (const cmd of commands) {
2147
+ if (!cmd || typeof cmd.name !== "string") continue;
2148
+ contributions.push({
2149
+ name: cmd.name,
2150
+ description: typeof cmd.description === "string" ? cmd.description : void 0,
2151
+ module: typeof cmd.module === "string" ? cmd.module : void 0,
2152
+ pluginName
2153
+ });
2154
+ }
2155
+ }
2156
+ if (contributions.length === 0) return;
2157
+ for (const contribution of contributions) {
2158
+ try {
2159
+ const commands = await importPluginCommands(contribution);
2160
+ for (const cmd of commands) {
2161
+ program2.addCommand(cmd);
2162
+ }
2163
+ } catch (error) {
2164
+ if (process.env.DEBUG) {
2165
+ const message = error instanceof Error ? error.message : String(error);
2166
+ console.error(
2167
+ chalk14.yellow(` \u26A0 Failed to load CLI command '${contribution.name}' from plugin '${contribution.pluginName}': ${message}`)
2168
+ );
2169
+ }
2170
+ }
2171
+ }
2172
+ }
2173
+ async function importPluginCommands(contribution) {
2174
+ const moduleId = contribution.module ? `${contribution.pluginName}/${contribution.module.replace(/^\.\//, "")}` : contribution.pluginName;
2175
+ const mod = await import(moduleId);
2176
+ if (Array.isArray(mod.commands)) {
2177
+ return mod.commands.filter(isCommandInstance);
2178
+ }
2179
+ const defaultExport = mod.default;
2180
+ if (defaultExport) {
2181
+ if (Array.isArray(defaultExport)) {
2182
+ return defaultExport.filter(isCommandInstance);
2183
+ }
2184
+ if (isCommandInstance(defaultExport)) {
2185
+ return [defaultExport];
2186
+ }
2187
+ }
2188
+ const commands = [];
2189
+ for (const key of Object.keys(mod)) {
2190
+ if (isCommandInstance(mod[key])) {
2191
+ commands.push(mod[key]);
2192
+ }
2193
+ }
2194
+ return commands;
2195
+ }
2196
+ function isCommandInstance(value) {
2197
+ if (value === null || typeof value !== "object") return false;
2198
+ const obj = value;
2199
+ return typeof obj.name === "function" && typeof obj.description === "function" && typeof obj.action === "function" && typeof obj.parse === "function";
2200
+ }
2201
+ function resolvePluginName2(plugin) {
2202
+ if (typeof plugin.name === "string") return plugin.name;
2203
+ const manifest = plugin.manifest;
2204
+ if (manifest && typeof manifest.name === "string") return manifest.name;
2205
+ if (plugin.constructor && plugin.constructor.name !== "Object") return plugin.constructor.name;
2206
+ return "unknown";
2207
+ }
2208
+
1877
2209
  // src/bin.ts
1878
2210
  var require2 = createRequire2(import.meta.url);
1879
2211
  var pkg = require2("../package.json");
1880
2212
  process.on("unhandledRejection", (err) => {
1881
- console.error(chalk13.red(`
2213
+ console.error(chalk15.red(`
1882
2214
  \u2717 Unhandled error: ${err?.message || err}`));
1883
2215
  if (err?.stack && process.env.DEBUG) {
1884
- console.error(chalk13.dim(err.stack));
2216
+ console.error(chalk15.dim(err.stack));
1885
2217
  }
1886
2218
  process.exit(1);
1887
2219
  });
1888
- var program = new Command12();
2220
+ var program = new Command13();
1889
2221
  program.name("objectstack").description("ObjectStack CLI \u2014 Build metadata-driven apps with the ObjectStack Protocol").version(pkg.version, "-v, --version").configureHelp({
1890
2222
  sortSubcommands: false
1891
2223
  }).addHelpText("before", `
1892
- ${chalk13.bold.cyan("\u25C6 ObjectStack CLI")} ${chalk13.dim(`v${pkg.version}`)}
2224
+ ${chalk15.bold.cyan("\u25C6 ObjectStack CLI")} ${chalk15.dim(`v${pkg.version}`)}
1893
2225
  `).addHelpText("after", `
1894
- ${chalk13.bold("Workflow:")}
1895
- ${chalk13.dim("$")} os init ${chalk13.dim("# Create a new project")}
1896
- ${chalk13.dim("$")} os generate object task ${chalk13.dim("# Add metadata")}
1897
- ${chalk13.dim("$")} os validate ${chalk13.dim("# Check configuration")}
1898
- ${chalk13.dim("$")} os dev ${chalk13.dim("# Start dev server")}
1899
- ${chalk13.dim("$")} os studio ${chalk13.dim("# Dev server + Studio UI")}
1900
- ${chalk13.dim("$")} os compile ${chalk13.dim("# Build for production")}
1901
-
1902
- ${chalk13.dim("Aliases: objectstack | os")}
1903
- ${chalk13.dim("Docs: https://objectstack.dev")}
2226
+ ${chalk15.bold("Workflow:")}
2227
+ ${chalk15.dim("$")} os init ${chalk15.dim("# Create a new project")}
2228
+ ${chalk15.dim("$")} os generate object task ${chalk15.dim("# Add metadata")}
2229
+ ${chalk15.dim("$")} os plugin add <package> ${chalk15.dim("# Add a plugin")}
2230
+ ${chalk15.dim("$")} os validate ${chalk15.dim("# Check configuration")}
2231
+ ${chalk15.dim("$")} os dev ${chalk15.dim("# Start dev server")}
2232
+ ${chalk15.dim("$")} os studio ${chalk15.dim("# Dev server + Studio UI")}
2233
+ ${chalk15.dim("$")} os compile ${chalk15.dim("# Build for production")}
2234
+
2235
+ ${chalk15.dim("Aliases: objectstack | os")}
2236
+ ${chalk15.dim("Docs: https://objectstack.dev")}
1904
2237
  `);
1905
2238
  program.addCommand(initCommand);
1906
2239
  program.addCommand(devCommand);
@@ -1911,6 +2244,15 @@ program.addCommand(validateCommand);
1911
2244
  program.addCommand(infoCommand);
1912
2245
  program.addCommand(generateCommand);
1913
2246
  program.addCommand(createCommand);
2247
+ program.addCommand(pluginCommand);
1914
2248
  program.addCommand(testCommand);
1915
2249
  program.addCommand(doctorCommand);
1916
- program.parse(process.argv);
2250
+ loadPluginCommands(program).then(() => {
2251
+ program.parse(process.argv);
2252
+ }).catch((err) => {
2253
+ if (process.env.DEBUG) {
2254
+ console.error(chalk15.yellow(`
2255
+ \u26A0 Plugin command loading failed: ${err?.message || err}`));
2256
+ }
2257
+ program.parse(process.argv);
2258
+ });
package/dist/index.d.ts CHANGED
@@ -90,6 +90,8 @@ declare const templates: {
90
90
  };
91
91
  declare const createCommand: Command;
92
92
 
93
+ declare const pluginCommand: Command;
94
+
93
95
  declare const devCommand: Command;
94
96
 
95
97
  declare const serveCommand: Command;
@@ -98,4 +100,15 @@ declare const testCommand: Command;
98
100
 
99
101
  declare const doctorCommand: Command;
100
102
 
101
- export { compileCommand, createCommand, devCommand, doctorCommand, generateCommand, infoCommand, initCommand, serveCommand, templates, testCommand, validateCommand };
103
+ /**
104
+ * Discover CLI command contributions from installed plugins.
105
+ *
106
+ * Scans the project's `objectstack.config.ts` for plugins that declare
107
+ * `contributes.commands` in their manifest, then dynamically imports
108
+ * those plugin modules to register Commander.js commands.
109
+ *
110
+ * @param program - The root Commander.js program to register commands on
111
+ */
112
+ declare function loadPluginCommands(program: Command): Promise<void>;
113
+
114
+ export { compileCommand, createCommand, devCommand, doctorCommand, generateCommand, infoCommand, initCommand, loadPluginCommands, pluginCommand, serveCommand, templates, testCommand, validateCommand };