@lenne.tech/cli 1.10.0 → 1.11.0

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.
@@ -18,7 +18,7 @@ const NewCommand = {
18
18
  hidden: false,
19
19
  name: 'init',
20
20
  run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
21
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
21
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
22
22
  // Retrieve the tools we need
23
23
  const { config, filesystem, frontendHelper, git, helper, parameters, patching, print: { error, info, spin, success }, prompt: { ask, confirm }, server, strings: { kebabCase }, system, } = toolbox;
24
24
  // Start timer
@@ -45,7 +45,7 @@ const NewCommand = {
45
45
  const configFrontendLink = (_v = (_u = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _u === void 0 ? void 0 : _u.fullstack) === null || _v === void 0 ? void 0 : _v.frontendLink;
46
46
  const configFrameworkMode = (_x = (_w = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _w === void 0 ? void 0 : _w.fullstack) === null || _x === void 0 ? void 0 : _x.frameworkMode;
47
47
  // Parse CLI arguments
48
- const { 'api-branch': cliApiBranch, 'api-copy': cliApiCopy, 'api-link': cliApiLink, 'api-mode': cliApiMode, 'dry-run': cliDryRun, 'framework-mode': cliFrameworkMode, 'framework-upstream-branch': cliFrameworkUpstreamBranch, frontend: cliFrontend, 'frontend-branch': cliFrontendBranch, 'frontend-copy': cliFrontendCopy, 'frontend-link': cliFrontendLink, git: cliGit, 'git-link': cliGitLink, name: cliName, } = parameters.options;
48
+ const { 'api-branch': cliApiBranch, 'api-copy': cliApiCopy, 'api-link': cliApiLink, 'api-mode': cliApiMode, 'dry-run': cliDryRun, 'framework-mode': cliFrameworkMode, 'framework-upstream-branch': cliFrameworkUpstreamBranch, frontend: cliFrontend, 'frontend-branch': cliFrontendBranch, 'frontend-copy': cliFrontendCopy, 'frontend-framework-mode': cliFrontendFrameworkMode, 'frontend-link': cliFrontendLink, git: cliGit, 'git-link': cliGitLink, name: cliName, } = parameters.options;
49
49
  const dryRun = cliDryRun === true || cliDryRun === 'true';
50
50
  const frameworkUpstreamBranch = typeof cliFrameworkUpstreamBranch === 'string' && cliFrameworkUpstreamBranch.length > 0
51
51
  ? cliFrameworkUpstreamBranch
@@ -181,6 +181,27 @@ const NewCommand = {
181
181
  });
182
182
  frameworkMode = frameworkModeChoice.frameworkMode.startsWith('vendor') ? 'vendor' : 'npm';
183
183
  }
184
+ // ── Frontend framework mode ─────────────────────────────────────────
185
+ const configFrontendFrameworkMode = (_0 = (_z = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _z === void 0 ? void 0 : _z.fullstack) === null || _0 === void 0 ? void 0 : _0.frontendFrameworkMode;
186
+ let frontendFrameworkMode;
187
+ if (cliFrontendFrameworkMode === 'npm' || cliFrontendFrameworkMode === 'vendor') {
188
+ frontendFrameworkMode = cliFrontendFrameworkMode;
189
+ }
190
+ else if (cliFrontendFrameworkMode) {
191
+ error(`Invalid --frontend-framework-mode value "${cliFrontendFrameworkMode}". Use "npm" or "vendor".`);
192
+ return;
193
+ }
194
+ else if (configFrontendFrameworkMode === 'npm' || configFrontendFrameworkMode === 'vendor') {
195
+ frontendFrameworkMode = configFrontendFrameworkMode;
196
+ info(`Using frontend framework mode from lt.config: ${frontendFrameworkMode}`);
197
+ }
198
+ else if (noConfirm) {
199
+ frontendFrameworkMode = 'npm';
200
+ }
201
+ else {
202
+ // Default to npm without asking (unless user sets it explicitly)
203
+ frontendFrameworkMode = 'npm';
204
+ }
184
205
  // Determine remote push settings with priority: CLI > config > interactive
185
206
  // Git is always initialized; the question is whether to push to a remote
186
207
  let pushToRemote = false;
@@ -251,6 +272,7 @@ const NewCommand = {
251
272
  info(` frontend: ${frontend}`);
252
273
  info(` apiMode: ${apiMode}`);
253
274
  info(` frameworkMode: ${frameworkMode}`);
275
+ info(` frontendFrameworkMode: ${frontendFrameworkMode}`);
254
276
  if (frameworkUpstreamBranch) {
255
277
  info(` frameworkUpstreamBranch: ${frameworkUpstreamBranch}`);
256
278
  }
@@ -282,6 +304,11 @@ const NewCommand = {
282
304
  info(` 3. clone nest-server-starter → ${projectDir}/projects/api`);
283
305
  info(` 4. run processApiMode(${apiMode})`);
284
306
  }
307
+ if (frontendFrameworkMode === 'vendor') {
308
+ info(` M1. clone @lenne.tech/nuxt-extensions → /tmp`);
309
+ info(` M2. vendor app/core/ (module.ts + runtime/) + codemod consumer imports`);
310
+ info(` M3. rewrite nuxt.config.ts module entry`);
311
+ }
285
312
  info(' N. pnpm install + initial git commit');
286
313
  info('');
287
314
  return `fullstack init dry-run (${frameworkMode} / ${apiMode})`;
@@ -364,6 +391,21 @@ const NewCommand = {
364
391
  if (frontendResult.method !== 'link') {
365
392
  frontendHelper.patchFrontendEnv(frontendDest, projectDir);
366
393
  }
394
+ // ── Frontend vendoring (if requested) ───────────────────────────────
395
+ if (isNuxt && frontendFrameworkMode === 'vendor' && frontendResult.method !== 'link') {
396
+ const vendorSpinner = spin('Converting frontend to vendor mode...');
397
+ try {
398
+ yield frontendHelper.convertAppCloneToVendored({
399
+ dest: frontendDest,
400
+ projectName: name,
401
+ });
402
+ vendorSpinner.succeed('Frontend converted to vendor mode (app/core/)');
403
+ }
404
+ catch (err) {
405
+ vendorSpinner.fail(`Frontend vendor conversion failed: ${err.message}`);
406
+ toolbox.print.warning('Continuing with npm mode for frontend.');
407
+ }
408
+ }
367
409
  // Remove gitkeep file
368
410
  filesystem.remove(`${projectDir}/projects/.gitkeep`);
369
411
  // Integrate files
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const path_1 = require("path");
13
13
  const framework_detection_1 = require("../../lib/framework-detection");
14
+ const frontend_framework_detection_1 = require("../../lib/frontend-framework-detection");
14
15
  /**
15
16
  * Update a fullstack workspace — mode-aware.
16
17
  *
@@ -120,10 +121,57 @@ const NewCommand = {
120
121
  success(' The nest-server-updater agent auto-detects vendor projects');
121
122
  success(' and delegates to nest-server-core-updater when VENDOR.md is present.');
122
123
  }
124
+ // ── Frontend mode-aware instructions ──────────────────────────────
123
125
  info('');
124
126
  info(colors.dim('─'.repeat(60)));
125
127
  info('');
126
- return `fullstack update (${mode} mode)`;
128
+ // Detect frontend project
129
+ const appCandidates = [
130
+ (0, path_1.join)(cwd, 'projects', 'app'),
131
+ (0, path_1.join)(cwd, 'packages', 'app'),
132
+ ].filter((p) => Boolean(p));
133
+ let appDir;
134
+ for (const candidate of appCandidates) {
135
+ if (filesystem.exists((0, path_1.join)(candidate, 'nuxt.config.ts')) || filesystem.exists((0, path_1.join)(candidate, 'package.json'))) {
136
+ appDir = candidate;
137
+ break;
138
+ }
139
+ }
140
+ if (appDir) {
141
+ const frontendMode = (0, frontend_framework_detection_1.detectFrontendFrameworkMode)(appDir);
142
+ const frontendVendored = (0, frontend_framework_detection_1.isVendoredAppProject)(appDir);
143
+ info(` App project: ${appDir}`);
144
+ info(` Frontend framework mode: ${frontendMode}${frontendVendored ? ' (app/core/VENDOR.md present)' : ''}`);
145
+ info('');
146
+ if (frontendMode === 'vendor') {
147
+ info(colors.bold('Frontend vendor-mode update flow:'));
148
+ info('');
149
+ info(' The nuxt-extensions module lives directly in this project at');
150
+ info(' app/core/');
151
+ info(' and is managed as first-class project code.');
152
+ info('');
153
+ info(colors.bold(' Recommended update commands:'));
154
+ info('');
155
+ info(' /lt-dev:frontend:update-nuxt-extensions-core');
156
+ info('');
157
+ }
158
+ else {
159
+ info(colors.bold('Frontend npm-mode update flow:'));
160
+ info('');
161
+ info(' /lt-dev:fullstack:update --skip-backend');
162
+ info('');
163
+ info(' (or manually: pnpm update @lenne.tech/nuxt-extensions)');
164
+ info('');
165
+ }
166
+ }
167
+ info('');
168
+ info(colors.bold('For a comprehensive update of everything, use:'));
169
+ info('');
170
+ info(' /lt-dev:fullstack:update-all');
171
+ info('');
172
+ info(colors.dim('─'.repeat(60)));
173
+ info('');
174
+ return `fullstack update (backend: ${mode}, frontend: ${appDir ? (0, frontend_framework_detection_1.detectFrontendFrameworkMode)(appDir) : 'not found'})`;
127
175
  }),
128
176
  };
129
177
  exports.default = NewCommand;
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const framework_detection_1 = require("../../lib/framework-detection");
13
+ /**
14
+ * Convert an existing API project between npm mode and vendor mode.
15
+ *
16
+ * Usage:
17
+ * lt server convert-mode --to vendor [--upstream-branch 11.24.2]
18
+ * lt server convert-mode --to npm [--version 11.24.2]
19
+ */
20
+ const ConvertModeCommand = {
21
+ description: 'Convert API project between npm and vendor framework modes',
22
+ hidden: false,
23
+ name: 'convert-mode',
24
+ run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
25
+ const { filesystem, parameters, print: { error, info, spin, success, warning }, prompt: { confirm }, server, } = toolbox;
26
+ // Handle --help-json flag
27
+ if (toolbox.tools.helpJson({
28
+ description: 'Convert API project between npm and vendor framework modes',
29
+ name: 'convert-mode',
30
+ options: [
31
+ {
32
+ description: 'Target mode',
33
+ flag: '--to',
34
+ required: true,
35
+ type: 'string',
36
+ values: ['vendor', 'npm'],
37
+ },
38
+ {
39
+ description: 'Upstream branch/tag to vendor from (only with --to vendor)',
40
+ flag: '--upstream-branch',
41
+ required: false,
42
+ type: 'string',
43
+ },
44
+ {
45
+ description: 'nest-server version to install (only with --to npm, default: from VENDOR.md baseline)',
46
+ flag: '--version',
47
+ required: false,
48
+ type: 'string',
49
+ },
50
+ {
51
+ default: false,
52
+ description: 'Skip confirmation prompt',
53
+ flag: '--noConfirm',
54
+ required: false,
55
+ type: 'boolean',
56
+ },
57
+ {
58
+ default: false,
59
+ description: 'Show the resolved plan without making any changes',
60
+ flag: '--dry-run',
61
+ required: false,
62
+ type: 'boolean',
63
+ },
64
+ ],
65
+ })) {
66
+ return;
67
+ }
68
+ const targetMode = parameters.options.to;
69
+ if (!targetMode || !['npm', 'vendor'].includes(targetMode)) {
70
+ error('Missing or invalid --to flag. Use: --to vendor or --to npm');
71
+ return;
72
+ }
73
+ // Find the API project root
74
+ const cwd = filesystem.cwd();
75
+ const projectDir = (0, framework_detection_1.findProjectDir)(cwd);
76
+ if (!projectDir) {
77
+ error('Could not find a package.json in the current directory or any parent. Are you inside an API project?');
78
+ return;
79
+ }
80
+ // Detect current mode
81
+ const currentMode = (0, framework_detection_1.detectFrameworkMode)(projectDir);
82
+ info(`Detected current mode: ${currentMode}`);
83
+ if (currentMode === targetMode) {
84
+ warning(`Project is already in ${targetMode} mode. Nothing to do.`);
85
+ return;
86
+ }
87
+ // Dry-run: print the resolved plan and exit without any disk changes.
88
+ const dryRun = parameters.options['dry-run'] === true || parameters.options['dry-run'] === 'true';
89
+ if (dryRun) {
90
+ info('');
91
+ info('Dry-run plan:');
92
+ info(` projectDir: ${projectDir}`);
93
+ info(` currentMode: ${currentMode}`);
94
+ info(` targetMode: ${targetMode}`);
95
+ if (targetMode === 'vendor') {
96
+ const upstreamBranch = parameters.options['upstream-branch'];
97
+ info(` upstreamBranch: ${upstreamBranch || '(auto-detect from package.json)'}`);
98
+ info('');
99
+ info('Would execute:');
100
+ info(' 1. Clone @lenne.tech/nest-server → /tmp/lt-vendor-nest-server-*');
101
+ info(' 2. Copy src/core/, src/index.ts, src/core.module.ts, test/, templates/, types/ → src/core/');
102
+ info(' 3. Apply flatten-fix (index.ts, core.module.ts, test.helper.ts)');
103
+ info(' 4. Rewrite consumer imports: @lenne.tech/nest-server → relative paths');
104
+ info(' 5. Merge upstream deps dynamically into package.json');
105
+ info(' 6. Rewrite migrate scripts to use local bin/migrate.js');
106
+ info(' 7. Add check:vendor-freshness script');
107
+ info(' 8. Create src/core/VENDOR.md with baseline metadata');
108
+ info(' 9. Prepend vendor-mode notice to CLAUDE.md');
109
+ }
110
+ else {
111
+ const targetVersion = parameters.options.version;
112
+ info(` targetVersion: ${targetVersion || '(from VENDOR.md baseline)'}`);
113
+ info('');
114
+ info('Would execute:');
115
+ info(' 1. Read baseline version from src/core/VENDOR.md');
116
+ info(' 2. Warn if local patches exist in VENDOR.md');
117
+ info(' 3. Rewrite consumer imports: relative → @lenne.tech/nest-server');
118
+ info(' 4. Delete src/core/');
119
+ info(' 5. Restore @lenne.tech/nest-server in package.json');
120
+ info(' 6. Restore migrate scripts to node_modules paths');
121
+ info(' 7. Remove vendor artifacts and CLAUDE.md marker');
122
+ }
123
+ info('');
124
+ return `server convert-mode dry-run (${currentMode} → ${targetMode})`;
125
+ }
126
+ // Confirm
127
+ const noConfirm = parameters.options.noConfirm;
128
+ if (!noConfirm) {
129
+ const proceed = yield confirm(`Convert project from ${currentMode} mode to ${targetMode} mode?\n` +
130
+ ` Project: ${projectDir}\n` +
131
+ ` This will modify package.json, imports, tsconfig, and CLAUDE.md.`);
132
+ if (!proceed) {
133
+ info('Aborted.');
134
+ return;
135
+ }
136
+ }
137
+ // Execute conversion
138
+ if (targetMode === 'vendor') {
139
+ // npm → vendor
140
+ const upstreamBranch = parameters.options['upstream-branch'];
141
+ // Auto-detect version from current @lenne.tech/nest-server dep
142
+ let branch = upstreamBranch;
143
+ if (!branch) {
144
+ try {
145
+ const pkg = filesystem.read(`${projectDir}/package.json`, 'json');
146
+ const deps = Object.assign(Object.assign({}, ((pkg === null || pkg === void 0 ? void 0 : pkg.dependencies) || {})), ((pkg === null || pkg === void 0 ? void 0 : pkg.devDependencies) || {}));
147
+ const version = deps['@lenne.tech/nest-server'];
148
+ if (version) {
149
+ // Strip semver range chars (^, ~, >=, etc.) to get the bare version
150
+ branch = version.replace(/^[^0-9]*/, '');
151
+ info(`Auto-detected @lenne.tech/nest-server version: ${branch}`);
152
+ }
153
+ }
154
+ catch (_a) {
155
+ // Will prompt or use HEAD
156
+ }
157
+ }
158
+ const spinner = spin('Converting to vendor mode...');
159
+ try {
160
+ yield server.convertToVendorMode({
161
+ dest: projectDir,
162
+ upstreamBranch: branch,
163
+ });
164
+ spinner.succeed('Converted to vendor mode successfully.');
165
+ success('\nNext steps:');
166
+ info(' 1. Run: pnpm install');
167
+ info(' 2. Run: pnpm exec tsc --noEmit');
168
+ info(' 3. Run: pnpm test');
169
+ info(' 4. Commit the changes');
170
+ }
171
+ catch (err) {
172
+ spinner.fail(`Conversion failed: ${err.message}`);
173
+ }
174
+ }
175
+ else {
176
+ // vendor → npm
177
+ const targetVersion = parameters.options.version;
178
+ const spinner = spin('Converting to npm mode...');
179
+ try {
180
+ yield server.convertToNpmMode({
181
+ dest: projectDir,
182
+ targetVersion,
183
+ });
184
+ spinner.succeed('Converted to npm mode successfully.');
185
+ success('\nNext steps:');
186
+ info(' 1. Run: pnpm install');
187
+ info(' 2. Run: pnpm exec tsc --noEmit');
188
+ info(' 3. Run: pnpm test');
189
+ info(' 4. Commit the changes');
190
+ }
191
+ catch (err) {
192
+ spinner.fail(`Conversion failed: ${err.message}`);
193
+ }
194
+ }
195
+ }),
196
+ };
197
+ exports.default = ConvertModeCommand;
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const path_1 = require("path");
13
13
  const framework_detection_1 = require("../lib/framework-detection");
14
+ const frontend_framework_detection_1 = require("../lib/frontend-framework-detection");
14
15
  /**
15
16
  * Show project status and context
16
17
  */
@@ -29,11 +30,13 @@ const StatusCommand = {
29
30
  const projectInfo = {
30
31
  configFiles: [],
31
32
  frameworkMode: null,
33
+ frontendFrameworkMode: null,
32
34
  gitBranch: null,
33
35
  gitRoot: null,
34
36
  hasGit: false,
35
37
  hasLtConfig: false,
36
38
  hasPackageJson: false,
39
+ monorepoSubprojects: [],
37
40
  nodeVersion: null,
38
41
  npmVersion: null,
39
42
  packageName: null,
@@ -70,6 +73,10 @@ const StatusCommand = {
70
73
  }
71
74
  else if (deps['nuxt']) {
72
75
  projectInfo.projectType = 'nuxt';
76
+ // Detect frontend framework mode if nuxt-extensions is present
77
+ if (deps['@lenne.tech/nuxt-extensions'] || (0, frontend_framework_detection_1.isVendoredAppProject)(cwd)) {
78
+ projectInfo.frontendFrameworkMode = (0, frontend_framework_detection_1.detectFrontendFrameworkMode)(cwd);
79
+ }
73
80
  }
74
81
  else if (deps['@angular/core']) {
75
82
  projectInfo.projectType = 'angular';
@@ -91,6 +98,52 @@ const StatusCommand = {
91
98
  // Ignore parse errors
92
99
  }
93
100
  }
101
+ // Monorepo subproject detection: scan projects/api and projects/app for
102
+ // framework modes so that `lt status` at the monorepo root surfaces
103
+ // backend + frontend framework consumption modes even when the root
104
+ // itself is not a Nest/Nuxt project.
105
+ const monorepoCandidates = [
106
+ { kind: 'backend', path: (0, path_1.join)(cwd, 'projects', 'api') },
107
+ { kind: 'backend', path: (0, path_1.join)(cwd, 'packages', 'api') },
108
+ { kind: 'frontend', path: (0, path_1.join)(cwd, 'projects', 'app') },
109
+ { kind: 'frontend', path: (0, path_1.join)(cwd, 'packages', 'app') },
110
+ ];
111
+ for (const candidate of monorepoCandidates) {
112
+ if (!filesystem.exists((0, path_1.join)(candidate.path, 'package.json')))
113
+ continue;
114
+ if (candidate.kind === 'backend') {
115
+ try {
116
+ const subPkg = JSON.parse(filesystem.read((0, path_1.join)(candidate.path, 'package.json')) || '{}');
117
+ const subDeps = Object.assign(Object.assign({}, subPkg.dependencies), subPkg.devDependencies);
118
+ if (subDeps['@lenne.tech/nest-server'] || (0, framework_detection_1.isVendoredProject)(candidate.path)) {
119
+ projectInfo.monorepoSubprojects.push({
120
+ frameworkMode: (0, framework_detection_1.detectFrameworkMode)(candidate.path),
121
+ kind: 'backend',
122
+ path: candidate.path,
123
+ });
124
+ }
125
+ }
126
+ catch (_d) {
127
+ // ignore
128
+ }
129
+ }
130
+ else {
131
+ try {
132
+ const subPkg = JSON.parse(filesystem.read((0, path_1.join)(candidate.path, 'package.json')) || '{}');
133
+ const subDeps = Object.assign(Object.assign({}, subPkg.dependencies), subPkg.devDependencies);
134
+ if (subDeps['@lenne.tech/nuxt-extensions'] || (0, frontend_framework_detection_1.isVendoredAppProject)(candidate.path)) {
135
+ projectInfo.monorepoSubprojects.push({
136
+ frameworkMode: (0, frontend_framework_detection_1.detectFrontendFrameworkMode)(candidate.path),
137
+ kind: 'frontend',
138
+ path: candidate.path,
139
+ });
140
+ }
141
+ }
142
+ catch (_e) {
143
+ // ignore
144
+ }
145
+ }
146
+ }
94
147
  // Check for git
95
148
  try {
96
149
  const gitRoot = yield system.run('git rev-parse --show-toplevel 2>/dev/null');
@@ -101,7 +154,7 @@ const StatusCommand = {
101
154
  projectInfo.gitBranch = (branch === null || branch === void 0 ? void 0 : branch.trim()) || null;
102
155
  }
103
156
  }
104
- catch (_d) {
157
+ catch (_f) {
105
158
  // Not a git repository
106
159
  }
107
160
  // Get Node/npm versions
@@ -109,7 +162,7 @@ const StatusCommand = {
109
162
  projectInfo.nodeVersion = ((_a = (yield system.run('node --version 2>/dev/null'))) === null || _a === void 0 ? void 0 : _a.trim()) || null;
110
163
  projectInfo.npmVersion = ((_b = (yield system.run('npm --version 2>/dev/null'))) === null || _b === void 0 ? void 0 : _b.trim()) || null;
111
164
  }
112
- catch (_e) {
165
+ catch (_g) {
113
166
  // Ignore errors
114
167
  }
115
168
  // Display project info
@@ -132,6 +185,32 @@ const StatusCommand = {
132
185
  : 'npm (@lenne.tech/nest-server dependency)';
133
186
  info(` Framework: ${modeLabel}`);
134
187
  }
188
+ if (projectInfo.frontendFrameworkMode) {
189
+ const frontendModeLabel = projectInfo.frontendFrameworkMode === 'vendor'
190
+ ? 'vendor (app/core/, VENDOR.md)'
191
+ : 'npm (@lenne.tech/nuxt-extensions dependency)';
192
+ info(` Frontend Framework: ${frontendModeLabel}`);
193
+ }
194
+ }
195
+ // Show monorepo subprojects if we detected any (typically at monorepo root)
196
+ if (projectInfo.monorepoSubprojects.length > 0) {
197
+ info('');
198
+ info(colors.bold('Monorepo Subprojects:'));
199
+ for (const sub of projectInfo.monorepoSubprojects) {
200
+ const relPath = sub.path.replace(`${cwd}/`, '');
201
+ if (sub.kind === 'backend') {
202
+ const label = sub.frameworkMode === 'vendor'
203
+ ? 'vendor (src/core/, VENDOR.md)'
204
+ : 'npm (@lenne.tech/nest-server dependency)';
205
+ info(` Backend: ${relPath} → ${label}`);
206
+ }
207
+ else {
208
+ const label = sub.frameworkMode === 'vendor'
209
+ ? 'vendor (app/core/, VENDOR.md)'
210
+ : 'npm (@lenne.tech/nuxt-extensions dependency)';
211
+ info(` Frontend: ${relPath} → ${label}`);
212
+ }
213
+ }
135
214
  }
136
215
  if (projectInfo.hasGit) {
137
216
  info('');
@@ -0,0 +1,4 @@
1
+ {
2
+ "$comment": "Upstream @lenne.tech/nuxt-extensions devDependencies that must be promoted to regular dependencies in vendor mode. Currently empty because nuxt-extensions has only @nuxt/kit as a direct dependency (which is handled explicitly during vendoring).",
3
+ "runtimeHelpers": []
4
+ }