@happyvertical/smrt-cli 0.40.62 → 0.40.63

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.
package/AGENTS.md CHANGED
@@ -15,10 +15,13 @@ smrt db:rollback # Rollback migrations
15
15
  smrt docs:agents # Generate .agents/smrt-framework.md
16
16
  smrt docs:claude # Deprecated alias writing .claude/smrt-framework.md
17
17
  smrt dev:knowledge-* # Deterministic agent knowledge index/check/diff
18
+ smrt dev:knowledge-index --scope installed # Installed @happyvertical/* deps, versions, AGENTS.md hashes
18
19
  smrt knowledge:review-context --scope package --package <name>
19
- smrt knowledge:architecture-context --scope project|local|package|sdk
20
+ smrt knowledge:architecture-context --scope project|local|package|sdk|installed
20
21
  smrt dev:knowledge-check --format markdown|json
21
- smrt generate:mcp # Generate MCP server
22
+ smrt generate-mcp # Generate MCP server (aliases: generate-mcp-server, mcp)
23
+ smrt generate-types # Generate TypeScript declarations (alias: generate-declarations)
24
+ smrt generate-routes # Generate SvelteKit API routes (aliases: routes, generate:routes)
22
25
  smrt config:export # Export agent config for SSG
23
26
  smrt init # Init new project
24
27
  smrt gnode # Scaffold gnode site
@@ -50,4 +53,13 @@ migrations are manifest-driven through registered objects and project manifests.
50
53
 
51
54
  - **Test mode detection**: checks `NODE_ENV=test`, `VITEST=true`, `global.it`/`describe` — could conflict with other test runners
52
55
  - **External package load failures silenced**: one package failing doesn't prevent others from loading
56
+ - **Generation command names are hyphenated**: `generate-mcp` and `generate-types` have
57
+ no colon alias. Only `generate-routes` and `generate-register` declare one, so
58
+ `smrt generate:mcp` is an unknown command (#2279).
59
+ - **Subcommand `--version` beats the global flag**: `parseCliArgs` in
60
+ `@happyvertical/utils` dispatches to the built-in `version` command for a
61
+ `--version` token anywhere in argv. `parseCliCommandArgs` rescopes that token to
62
+ the named subcommand (`smrt generate-mcp --version 0.1.0`) and only leaves it
63
+ global when it appears before or without a subcommand (#2279). `--help` is
64
+ deliberately untouched.
53
65
  - **Schema history nuance**: `db:status` / `db:history` should distinguish active live drift from superseded failed generated schema repairs instead of treating all failed rows as current blockers
package/README.md CHANGED
@@ -83,8 +83,30 @@ the tracker or writing to the database.
83
83
 
84
84
  | Command | Description |
85
85
  |---------|------------|
86
- | `smrt generate:mcp` | Generate MCP server from registered objects |
87
- | `smrt generate:types` | Generate TypeScript declarations from manifest |
86
+ | `smrt generate-mcp` | Generate MCP server from registered objects (aliases: `generate-mcp-server`, `mcp`) |
87
+ | `smrt generate-types` | Generate TypeScript declarations from manifest (alias: `generate-declarations`) |
88
+ | `smrt generate-routes` | Generate SvelteKit API routes (aliases: `routes`, `generate:routes`) |
89
+ | `smrt generate-register` | Generate `.smrt/register.js` from discovered packages (aliases: `register`, `generate:register`) |
90
+
91
+ Generation commands are hyphenated. `generate-routes` and `generate-register`
92
+ also answer to a colon alias for backward compatibility; `generate-mcp` and
93
+ `generate-types` do not.
94
+
95
+ `smrt generate-mcp` writes `.smrt/mcp-server/index.js` by default and emits
96
+ JavaScript for a `.js` target, so `node .smrt/mcp-server/index.js` runs it
97
+ directly. Ask for a `.ts` target to keep the annotated TypeScript for `tsx` or
98
+ Node's type stripping:
99
+
100
+ ```bash
101
+ smrt generate-mcp --output-path .smrt/mcp-server/index.ts
102
+ ```
103
+
104
+ The generated server is always an ES module, so a `.cjs`/`.cts` output path is
105
+ rejected. It imports `@modelcontextprotocol/server`, `@happyvertical/smrt-core`,
106
+ and `@happyvertical/smrt-config` at runtime (plus `@happyvertical/smrt-jobs` for
107
+ task actions and `@happyvertical/smrt-tenancy` for tenant-scoped objects), so
108
+ those have to be resolvable from the project you run it in — under pnpm's strict
109
+ layout that means declaring them, not relying on the CLI's own dependencies.
88
110
 
89
111
  ### Documentation
90
112
 
@@ -95,8 +117,8 @@ the tracker or writing to the database.
95
117
  | `smrt dev:knowledge-index --format markdown\|json` | Print the deterministic s-m-r-t + SDK knowledge index |
96
118
  | `smrt dev:knowledge-check --format markdown\|json` | Check agent knowledge freshness |
97
119
  | `smrt dev:knowledge-diff --format markdown\|json` | Show changed files and affected package experts |
98
- | `smrt knowledge:review-context --scope project\|local\|package\|sdk --package <name> --format markdown\|json` | Build a model-ready domain review prompt bundle |
99
- | `smrt knowledge:architecture-context --scope project\|local\|package\|sdk --package <name> --format markdown\|json` | Build a model-ready domain architecture prompt bundle |
120
+ | `smrt knowledge:review-context --scope project\|local\|package\|sdk\|installed --package <name> --format markdown\|json` | Build a model-ready domain review prompt bundle |
121
+ | `smrt knowledge:architecture-context --scope project\|local\|package\|sdk\|installed --package <name> --format markdown\|json` | Build a model-ready domain architecture prompt bundle |
100
122
 
101
123
  ### Configuration
102
124
 
@@ -162,7 +184,7 @@ Custom methods on s-m-r-t objects are auto-discovered from manifests. Method par
162
184
  smrt introspect
163
185
 
164
186
  # Generate an MCP server
165
- smrt generate:mcp
187
+ smrt generate-mcp
166
188
 
167
189
  # Scaffold a package playground definition
168
190
  smrt playground init
@@ -3157,7 +3157,7 @@ var generateCommands = {
3157
3157
  },
3158
3158
  modular: {
3159
3159
  type: "boolean",
3160
- description: "Generate modular directory structure (tools/, handlers/, config.ts)",
3160
+ description: "Generate modular directory structure (tools/, handlers/, config)",
3161
3161
  default: false
3162
3162
  },
3163
3163
  debug: {
@@ -5758,7 +5758,7 @@ var devKnowledgeCommands = {
5758
5758
  },
5759
5759
  scope: {
5760
5760
  type: "string",
5761
- description: "Knowledge scope: project, local, package, or sdk",
5761
+ description: "Knowledge scope: project, local, package, sdk, or installed",
5762
5762
  default: "project"
5763
5763
  },
5764
5764
  package: {
@@ -5807,7 +5807,7 @@ var devKnowledgeCommands = {
5807
5807
  },
5808
5808
  scope: {
5809
5809
  type: "string",
5810
- description: "Knowledge scope: project, local, package, or sdk",
5810
+ description: "Knowledge scope: project, local, package, sdk, or installed",
5811
5811
  default: "project"
5812
5812
  },
5813
5813
  package: {
@@ -5852,7 +5852,7 @@ var devKnowledgeCommands = {
5852
5852
  },
5853
5853
  scope: {
5854
5854
  type: "string",
5855
- description: "Knowledge scope: project, local, package, or sdk",
5855
+ description: "Knowledge scope: project, local, package, sdk, or installed",
5856
5856
  default: "project"
5857
5857
  },
5858
5858
  package: {
@@ -5935,7 +5935,7 @@ function contextOptions(defaultFormat) {
5935
5935
  },
5936
5936
  scope: {
5937
5937
  type: "string",
5938
- description: "Knowledge scope: project, local, package, or sdk",
5938
+ description: "Knowledge scope: project, local, package, sdk, or installed",
5939
5939
  default: "project"
5940
5940
  },
5941
5941
  package: {
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from "node:fs";
3
- import { dirname, join } from "node:path";
3
+ import { basename, dirname, join } from "node:path";
4
4
  import { createInterface } from "node:readline";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { ObjectRegistry, getPackageFromQualifiedName } from "@happyvertical/smrt-core";
@@ -48,56 +48,56 @@ var _docsCommands = null;
48
48
  var _playgroundCommands = null;
49
49
  async function getGnodeCommands() {
50
50
  if (!_gnodeCommands) {
51
- const { gnodeCommands } = await import("./commands-CB3xtl7p.js");
51
+ const { gnodeCommands } = await import("./commands-Bkdk2-S5.js");
52
52
  _gnodeCommands = gnodeCommands;
53
53
  }
54
54
  return _gnodeCommands;
55
55
  }
56
56
  async function getGitCommands() {
57
57
  if (!_gitCommands) {
58
- const { gitCommands } = await import("./commands-CB3xtl7p.js");
58
+ const { gitCommands } = await import("./commands-Bkdk2-S5.js");
59
59
  _gitCommands = gitCommands;
60
60
  }
61
61
  return _gitCommands;
62
62
  }
63
63
  async function getGenerateCommands() {
64
64
  if (!_generateCommands) {
65
- const { generateCommands } = await import("./commands-CB3xtl7p.js");
65
+ const { generateCommands } = await import("./commands-Bkdk2-S5.js");
66
66
  _generateCommands = generateCommands;
67
67
  }
68
68
  return _generateCommands;
69
69
  }
70
70
  async function getInitCommands() {
71
71
  if (!_initCommands) {
72
- const { initCommands } = await import("./commands-CB3xtl7p.js");
72
+ const { initCommands } = await import("./commands-Bkdk2-S5.js");
73
73
  _initCommands = initCommands;
74
74
  }
75
75
  return _initCommands;
76
76
  }
77
77
  async function getUtilityCommands() {
78
78
  if (!_utilityCommands) {
79
- const { utilityCommands } = await import("./commands-CB3xtl7p.js");
79
+ const { utilityCommands } = await import("./commands-Bkdk2-S5.js");
80
80
  _utilityCommands = utilityCommands;
81
81
  }
82
82
  return _utilityCommands;
83
83
  }
84
84
  async function getDispatchCommands() {
85
85
  if (!_dispatchCommands) {
86
- const { dispatchCommands } = await import("./commands-CB3xtl7p.js");
86
+ const { dispatchCommands } = await import("./commands-Bkdk2-S5.js");
87
87
  _dispatchCommands = dispatchCommands;
88
88
  }
89
89
  return _dispatchCommands;
90
90
  }
91
91
  async function getDocsCommands() {
92
92
  if (!_docsCommands) {
93
- const { docsCommands } = await import("./commands-CB3xtl7p.js");
93
+ const { docsCommands } = await import("./commands-Bkdk2-S5.js");
94
94
  _docsCommands = docsCommands;
95
95
  }
96
96
  return _docsCommands;
97
97
  }
98
98
  async function getPlaygroundCommands() {
99
99
  if (!_playgroundCommands) {
100
- const { playgroundCommands } = await import("./commands-CB3xtl7p.js");
100
+ const { playgroundCommands } = await import("./commands-Bkdk2-S5.js");
101
101
  _playgroundCommands = playgroundCommands;
102
102
  }
103
103
  return _playgroundCommands;
@@ -122,11 +122,129 @@ function collectRepeatableOptionValues(argv, optionName) {
122
122
  return values;
123
123
  }
124
124
  /**
125
+ * Option name a subcommand may declare that collides with a global CLI flag.
126
+ *
127
+ * `parseCliArgs` dispatches to the built-in `version` command whenever
128
+ * `--version` appears anywhere in argv, so `smrt generate-mcp --version 0.1.0`
129
+ * never reached the subcommand and exited without generating anything (#2279).
130
+ * Only `--version` is rescoped: `--help` after a subcommand still prints help,
131
+ * which is what someone typing it wants.
132
+ */
133
+ var SCOPED_GLOBAL_FLAG = "version";
134
+ /**
135
+ * Private option name the scoped flag is parsed under. Renaming the token is
136
+ * what keeps `parseCliArgs` from intercepting it; the value is mapped back onto
137
+ * the declared name before any handler sees it.
138
+ */
139
+ var SCOPED_GLOBAL_FLAG_ALIAS = "smrt-scoped-version";
140
+ /**
141
+ * Index of the first real argument, mirroring the node/script-path stripping
142
+ * `parseCliArgs` does. The CLI entry point passes `process.argv.slice(2)`, but
143
+ * the shared parser also accepts a full `process.argv`, and this wrapper must
144
+ * read the same tokens the parser will.
145
+ */
146
+ function firstArgumentIndex(argv) {
147
+ let index = 0;
148
+ if (argv.length > index && basename(argv[index]) === "node") index += 1;
149
+ if (argv.length > index && argv[index].endsWith(".js")) index += 1;
150
+ return index;
151
+ }
152
+ /**
153
+ * Resolve the command a plain argv addresses, mirroring the multi-word command
154
+ * matching in `parseCliArgs` (up to three leading words, names or aliases).
155
+ *
156
+ * @returns The matched command, if any, and how many leading argv tokens it consumed
157
+ */
158
+ function matchLeadingCommand(argv, commands, builtInCommands) {
159
+ for (let width = Math.min(3, argv.length); width > 0; width -= 1) {
160
+ const candidateName = argv.slice(0, width).join(" ");
161
+ const found = builtInCommands[candidateName] ?? commands.find((candidate) => candidate.name === candidateName || candidate.aliases?.includes(candidateName));
162
+ if (found) return {
163
+ command: found,
164
+ wordCount: width
165
+ };
166
+ }
167
+ return { wordCount: 0 };
168
+ }
169
+ /**
170
+ * Rewrite a subcommand-scoped `--version` token to a private alias so the
171
+ * shared parser routes it to the subcommand instead of the global version
172
+ * command. Tokens before the subcommand — and a bare `smrt --version` — are
173
+ * left alone, so the global flag still wins when it is used globally.
174
+ *
175
+ * @returns Rewritten argv and the matched command, or `null` when nothing applies
176
+ */
177
+ function rescopeGlobalFlag(argv, commands, builtInCommands) {
178
+ const start = firstArgumentIndex(argv);
179
+ const leading = argv[start];
180
+ if (!leading || leading.startsWith("-")) return null;
181
+ const flag = `--${SCOPED_GLOBAL_FLAG}`;
182
+ const inlinePrefix = `${flag}=`;
183
+ const aliasFlag = `--${SCOPED_GLOBAL_FLAG_ALIAS}`;
184
+ const { command, wordCount } = matchLeadingCommand(argv.slice(start), commands, builtInCommands);
185
+ const firstOptionIndex = start + Math.max(wordCount, 1);
186
+ let rewritten = false;
187
+ const rescoped = [...argv];
188
+ for (let index = firstOptionIndex; index < rescoped.length; index += 1) {
189
+ const token = rescoped[index];
190
+ if (token === "--") break;
191
+ if (token === flag) {
192
+ rescoped[index] = aliasFlag;
193
+ rewritten = true;
194
+ continue;
195
+ }
196
+ if (token.startsWith(inlinePrefix)) {
197
+ rescoped[index] = `${aliasFlag}=${token.slice(inlinePrefix.length)}`;
198
+ rewritten = true;
199
+ }
200
+ }
201
+ return rewritten ? {
202
+ argv: rescoped,
203
+ command
204
+ } : null;
205
+ }
206
+ /**
207
+ * Swap a matched command's declared `version` option for the private alias the
208
+ * rescoped token is parsed under, so its type, default, and short flag still
209
+ * apply. Commands that do not declare the option are returned untouched.
210
+ */
211
+ function declareScopedGlobalFlag(command, commands, builtInCommands) {
212
+ const declaredOptions = command?.options;
213
+ const declared = declaredOptions?.[SCOPED_GLOBAL_FLAG];
214
+ if (!command || !declaredOptions || !declared) return {
215
+ commands,
216
+ builtInCommands
217
+ };
218
+ const { [SCOPED_GLOBAL_FLAG]: _replaced, ...otherOptions } = declaredOptions;
219
+ const rewrittenCommand = {
220
+ ...command,
221
+ options: {
222
+ ...otherOptions,
223
+ [SCOPED_GLOBAL_FLAG_ALIAS]: { ...declared }
224
+ }
225
+ };
226
+ return {
227
+ commands: commands.map((candidate) => candidate === command ? rewrittenCommand : candidate),
228
+ builtInCommands: Object.fromEntries(Object.entries(builtInCommands).map(([name, candidate]) => [name, candidate === command ? rewrittenCommand : candidate]))
229
+ };
230
+ }
231
+ /**
125
232
  * Parse one command while preserving every occurrence of options marked
126
- * `multiple`. Non-repeatable options retain the shared SDK parser behavior.
233
+ * `multiple`, and while letting a subcommand's own options win over the
234
+ * global flags the shared parser intercepts. Everything else retains the
235
+ * shared SDK parser behavior.
127
236
  */
128
237
  function parseCliCommandArgs(argv, commands, builtInCommands = {}) {
129
- const parsed = parseCliArgs(argv, commands, builtInCommands);
238
+ const rescoped = rescopeGlobalFlag(argv, commands, builtInCommands);
239
+ let parsed;
240
+ if (rescoped) {
241
+ const declared = declareScopedGlobalFlag(rescoped.command, commands, builtInCommands);
242
+ parsed = parseCliArgs(rescoped.argv, declared.commands, declared.builtInCommands);
243
+ if (SCOPED_GLOBAL_FLAG_ALIAS in parsed.options) {
244
+ parsed.options[SCOPED_GLOBAL_FLAG] = parsed.options[SCOPED_GLOBAL_FLAG_ALIAS];
245
+ delete parsed.options[SCOPED_GLOBAL_FLAG_ALIAS];
246
+ }
247
+ } else parsed = parseCliArgs(argv, commands, builtInCommands);
130
248
  const command = parsed.command ? builtInCommands[parsed.command] ?? commands.find((candidate) => candidate.name === parsed.command || candidate.aliases?.includes(parsed.command)) : void 0;
131
249
  if (!command?.options) return parsed;
132
250
  for (const [name, option] of Object.entries(command.options)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-cli",
3
- "version": "0.40.62",
3
+ "version": "0.40.63",
4
4
  "description": "Developer CLI for SMRT framework - introspection, testing, and project management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -24,20 +24,20 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "@happyvertical/ai": "^0.85.5",
28
- "@happyvertical/files": "^0.85.5",
29
- "@happyvertical/logger": "^0.85.5",
30
- "@happyvertical/sql": "^0.85.5",
31
- "@happyvertical/utils": "^0.85.5",
27
+ "@happyvertical/ai": "^0.86.1",
28
+ "@happyvertical/files": "^0.86.1",
29
+ "@happyvertical/logger": "^0.86.1",
30
+ "@happyvertical/sql": "^0.86.1",
31
+ "@happyvertical/utils": "^0.86.1",
32
32
  "acorn": "^8.17.0",
33
33
  "fast-glob": "3.3.3",
34
34
  "tar": "^7.5.19",
35
- "@happyvertical/smrt-agents": "0.40.62",
36
- "@happyvertical/smrt-core": "0.40.62",
37
- "@happyvertical/smrt-dev-mcp": "0.40.62",
38
- "@happyvertical/smrt-config": "0.40.62",
39
- "@happyvertical/smrt-playground": "0.40.62",
40
- "@happyvertical/smrt-types": "0.40.62"
35
+ "@happyvertical/smrt-agents": "0.40.63",
36
+ "@happyvertical/smrt-config": "0.40.63",
37
+ "@happyvertical/smrt-core": "0.40.63",
38
+ "@happyvertical/smrt-dev-mcp": "0.40.63",
39
+ "@happyvertical/smrt-types": "0.40.63",
40
+ "@happyvertical/smrt-playground": "0.40.63"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "24.13.2",