@lenne.tech/cli 1.9.6 → 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.
Files changed (38) hide show
  1. package/README.md +88 -3
  2. package/build/commands/config/validate.js +2 -0
  3. package/build/commands/frontend/convert-mode.js +198 -0
  4. package/build/commands/fullstack/convert-mode.js +368 -0
  5. package/build/commands/fullstack/init.js +150 -4
  6. package/build/commands/fullstack/update.js +177 -0
  7. package/build/commands/server/add-property.js +29 -2
  8. package/build/commands/server/convert-mode.js +197 -0
  9. package/build/commands/server/create.js +41 -3
  10. package/build/commands/server/module.js +58 -25
  11. package/build/commands/server/object.js +26 -5
  12. package/build/commands/server/permissions.js +20 -6
  13. package/build/commands/server/test.js +7 -1
  14. package/build/commands/status.js +94 -3
  15. package/build/config/vendor-frontend-runtime-deps.json +4 -0
  16. package/build/config/vendor-runtime-deps.json +9 -0
  17. package/build/extensions/api-mode.js +19 -3
  18. package/build/extensions/frontend-helper.js +652 -0
  19. package/build/extensions/server.js +1475 -3
  20. package/build/lib/framework-detection.js +167 -0
  21. package/build/lib/frontend-framework-detection.js +129 -0
  22. package/build/templates/nest-server-module/inputs/template-create.input.ts.ejs +1 -1
  23. package/build/templates/nest-server-module/inputs/template.input.ts.ejs +1 -1
  24. package/build/templates/nest-server-module/outputs/template-fac-result.output.ts.ejs +1 -1
  25. package/build/templates/nest-server-module/template.controller.ts.ejs +1 -1
  26. package/build/templates/nest-server-module/template.model.ts.ejs +1 -1
  27. package/build/templates/nest-server-module/template.module.ts.ejs +1 -1
  28. package/build/templates/nest-server-module/template.resolver.ts.ejs +1 -1
  29. package/build/templates/nest-server-module/template.service.ts.ejs +1 -1
  30. package/build/templates/nest-server-object/template-create.input.ts.ejs +1 -1
  31. package/build/templates/nest-server-object/template.input.ts.ejs +1 -1
  32. package/build/templates/nest-server-object/template.object.ts.ejs +1 -1
  33. package/build/templates/nest-server-tests/tests.e2e-spec.ts.ejs +1 -1
  34. package/docs/LT-ECOSYSTEM-GUIDE.md +973 -0
  35. package/docs/VENDOR-MODE-WORKFLOW.md +471 -0
  36. package/docs/commands.md +196 -0
  37. package/docs/lt.config.md +9 -7
  38. package/package.json +17 -8
@@ -0,0 +1,177 @@
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 path_1 = require("path");
13
+ const framework_detection_1 = require("../../lib/framework-detection");
14
+ const frontend_framework_detection_1 = require("../../lib/frontend-framework-detection");
15
+ /**
16
+ * Update a fullstack workspace — mode-aware.
17
+ *
18
+ * lenne.tech fullstack projects currently run in one of two framework
19
+ * consumption modes:
20
+ *
21
+ * - npm mode: `@lenne.tech/nest-server` is an npm dependency. Updates
22
+ * happen via `pnpm update @lenne.tech/nest-server` plus
23
+ * the migration guides, orchestrated by the
24
+ * `lt-dev:nest-server-updater` Claude Code agent.
25
+ *
26
+ * - vendor mode: The framework `core/` tree is vendored into
27
+ * `projects/api/src/core/`. Updates happen via the
28
+ * `lt-dev:nest-server-core-updater` Claude Code agent,
29
+ * which clones the upstream repo, computes a delta,
30
+ * applies the approved hunks, and re-runs the flatten-fix.
31
+ *
32
+ * Detection is based on the presence of `src/core/VENDOR.md` in the api
33
+ * project. This command prints the right instructions for the caller's
34
+ * project; actual update orchestration lives in the Claude Code agents,
35
+ * not in the CLI.
36
+ */
37
+ const NewCommand = {
38
+ alias: ['up', 'upd'],
39
+ description: 'Show the mode-specific update instructions for this fullstack workspace',
40
+ hidden: false,
41
+ name: 'update',
42
+ run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
43
+ const { filesystem, print: { colors, info, success, warning }, } = toolbox;
44
+ info('');
45
+ info(colors.bold('Fullstack Update'));
46
+ info(colors.dim('─'.repeat(60)));
47
+ // Walk from cwd DOWN into projects/api if it exists, otherwise assume
48
+ // the caller is already inside an api project. Users can also point at
49
+ // a specific directory via `--api <path>`.
50
+ const cwd = filesystem.cwd();
51
+ const candidates = [
52
+ toolbox.parameters.options.api ? String(toolbox.parameters.options.api) : null,
53
+ (0, path_1.join)(cwd, 'projects', 'api'),
54
+ (0, path_1.join)(cwd, 'packages', 'api'),
55
+ cwd,
56
+ ].filter((p) => Boolean(p));
57
+ let apiDir;
58
+ for (const candidate of candidates) {
59
+ if (filesystem.exists((0, path_1.join)(candidate, 'package.json'))) {
60
+ apiDir = candidate;
61
+ break;
62
+ }
63
+ }
64
+ if (!apiDir) {
65
+ warning(' Could not locate an api project (no package.json found in cwd or projects/api/).');
66
+ info('');
67
+ info(' Pass --api <path> to point at the api project explicitly.');
68
+ info('');
69
+ return;
70
+ }
71
+ const mode = (0, framework_detection_1.detectFrameworkMode)(apiDir);
72
+ const vendored = (0, framework_detection_1.isVendoredProject)(apiDir);
73
+ info(` API project: ${apiDir}`);
74
+ info(` Framework mode: ${mode}${vendored ? ' (src/core/VENDOR.md present)' : ''}`);
75
+ info('');
76
+ if (mode === 'vendor') {
77
+ info(colors.bold('Vendor-mode update flow:'));
78
+ info('');
79
+ info(' The framework core/ tree lives directly in this project at');
80
+ info(' src/core/');
81
+ info(' and is managed as first-class project code. Local patches are');
82
+ info(' allowed and tracked in src/core/VENDOR.md.');
83
+ info('');
84
+ info(colors.bold(' Recommended update commands:'));
85
+ info('');
86
+ info(' 1. Refresh the upstream baseline + check for new versions');
87
+ info(` ${colors.cyan('(run from the api project)')}`);
88
+ info('');
89
+ info(' /lt-dev:backend:update-nest-server-core');
90
+ info('');
91
+ info(' 2. After the updater completes, run a freshness check:');
92
+ info('');
93
+ info(' pnpm run check:vendor-freshness');
94
+ info('');
95
+ info(' 3. If local changes have become generally useful, propose');
96
+ info(' them as upstream PRs via:');
97
+ info('');
98
+ info(' /lt-dev:backend:contribute-nest-server-core');
99
+ info('');
100
+ success(' All of these operate on src/core/ in-place; no npm dep bump.');
101
+ }
102
+ else {
103
+ info(colors.bold('npm-mode update flow:'));
104
+ info('');
105
+ info(' The framework lives in node_modules/@lenne.tech/nest-server as');
106
+ info(' a pinned npm dependency.');
107
+ info('');
108
+ info(colors.bold(' Recommended update commands:'));
109
+ info('');
110
+ info(' 1. Run the nest-server-updater agent:');
111
+ info('');
112
+ info(' /lt-dev:backend:update-nest-server');
113
+ info('');
114
+ info(' (or manually: pnpm update @lenne.tech/nest-server');
115
+ info(' and walk the migration guides)');
116
+ info('');
117
+ info(' 2. After upgrade, run the full check suite:');
118
+ info('');
119
+ info(' pnpm run check');
120
+ info('');
121
+ success(' The nest-server-updater agent auto-detects vendor projects');
122
+ success(' and delegates to nest-server-core-updater when VENDOR.md is present.');
123
+ }
124
+ // ── Frontend mode-aware instructions ──────────────────────────────
125
+ info('');
126
+ info(colors.dim('─'.repeat(60)));
127
+ info('');
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'})`;
175
+ }),
176
+ };
177
+ exports.default = NewCommand;
@@ -14,6 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const path_1 = require("path");
16
16
  const ts_morph_1 = require("ts-morph");
17
+ const framework_detection_1 = require("../../lib/framework-detection");
17
18
  const module_1 = __importDefault(require("./module"));
18
19
  const object_1 = __importDefault(require("./object"));
19
20
  /**
@@ -427,8 +428,34 @@ const NewCommand = {
427
428
  const mappingPairs = Object.entries(mappings).map(([k, v]) => `${k}: ${v}`);
428
429
  returnStatement.replaceWithText(`return mapClasses(input, { ${mappingPairs.join(', ')} }, this);`);
429
430
  }
430
- // Ensure mapClasses is imported
431
- const existingImports = moduleFile.getImportDeclaration('@lenne.tech/nest-server');
431
+ // Ensure mapClasses is imported. The import specifier differs by
432
+ // framework-consumption mode — in npm projects it's
433
+ // '@lenne.tech/nest-server'; in vendored projects it's a relative
434
+ // path to src/core whose depth depends on the model file location.
435
+ // We search for BOTH forms so this works regardless of how the file
436
+ // was originally generated.
437
+ const vendoredSpec = (0, framework_detection_1.isVendoredProject)(path)
438
+ ? (0, framework_detection_1.getFrameworkImportSpecifier)(path, modelPath)
439
+ : null;
440
+ let existingImports = moduleFile.getImportDeclaration('@lenne.tech/nest-server');
441
+ if (!existingImports && vendoredSpec) {
442
+ existingImports = moduleFile.getImportDeclaration(vendoredSpec);
443
+ }
444
+ if (!existingImports) {
445
+ // Final fallback: scan all imports and match any that resolves to
446
+ // the framework path (covers hand-edited files or unusual depths).
447
+ const allImports = moduleFile.getImportDeclarations();
448
+ existingImports = allImports.find((imp) => {
449
+ const spec = imp.getModuleSpecifierValue();
450
+ if (spec === '@lenne.tech/nest-server')
451
+ return true;
452
+ if (!vendoredSpec)
453
+ return false;
454
+ // Accept any relative path ending with '/core' or '../core' —
455
+ // covers variations across file depths.
456
+ return /(^|\/)core(\/.*)?$/.test(spec) && spec.startsWith('.');
457
+ });
458
+ }
432
459
  if (existingImports) {
433
460
  const namedImports = existingImports.getNamedImports();
434
461
  const hasMapClasses = namedImports.some((ni) => ni.getName() === 'mapClasses');
@@ -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;
@@ -18,7 +18,7 @@ const NewCommand = {
18
18
  hidden: false,
19
19
  name: 'create',
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, _z;
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, _1, _2;
22
22
  // Retrieve the tools we need
23
23
  const { config, filesystem, git, helper, meta, parameters, print: { error, info, spin, success }, prompt: { ask, confirm }, server, strings: { kebabCase }, system, } = toolbox;
24
24
  // Handle --help-json flag
@@ -62,6 +62,7 @@ const NewCommand = {
62
62
  const configCopy = (_q = (_p = (_o = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _o === void 0 ? void 0 : _o.server) === null || _p === void 0 ? void 0 : _p.create) === null || _q === void 0 ? void 0 : _q.copy;
63
63
  const configLink = (_t = (_s = (_r = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _r === void 0 ? void 0 : _r.server) === null || _s === void 0 ? void 0 : _s.create) === null || _t === void 0 ? void 0 : _t.link;
64
64
  const configApiMode = (_w = (_v = (_u = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _u === void 0 ? void 0 : _u.server) === null || _v === void 0 ? void 0 : _v.create) === null || _w === void 0 ? void 0 : _w.apiMode;
65
+ const configFrameworkMode = (_z = (_y = (_x = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _x === void 0 ? void 0 : _x.server) === null || _y === void 0 ? void 0 : _y.create) === null || _z === void 0 ? void 0 : _z.frameworkMode;
65
66
  // Load global defaults
66
67
  const globalAuthor = config.getGlobalDefault(ltConfig, 'author');
67
68
  const globalApiMode = config.getGlobalDefault(ltConfig, 'apiMode');
@@ -74,10 +75,12 @@ const NewCommand = {
74
75
  const cliCopy = parameters.options.copy || parameters.options.c;
75
76
  const cliLink = parameters.options.link;
76
77
  const cliApiMode = parameters.options['api-mode'] || parameters.options.apiMode;
78
+ const cliFrameworkMode = parameters.options['framework-mode'];
79
+ const cliFrameworkUpstreamBranch = parameters.options['framework-upstream-branch'];
77
80
  // Determine noConfirm with priority: CLI > config > global > default (false)
78
81
  const noConfirm = config.getNoConfirm({
79
82
  cliValue: cliNoConfirm,
80
- commandConfig: (_y = (_x = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _x === void 0 ? void 0 : _x.server) === null || _y === void 0 ? void 0 : _y.create,
83
+ commandConfig: (_1 = (_0 = ltConfig === null || ltConfig === void 0 ? void 0 : ltConfig.commands) === null || _0 === void 0 ? void 0 : _0.server) === null || _1 === void 0 ? void 0 : _1.create,
81
84
  config: ltConfig,
82
85
  });
83
86
  // Start timer
@@ -178,6 +181,39 @@ const NewCommand = {
178
181
  ]);
179
182
  apiMode = apiModeChoice.apiMode.split(' - ')[0];
180
183
  }
184
+ // Determine framework consumption mode — same resolution cascade as
185
+ // lt fullstack init: CLI flag > lt.config > interactive (default npm).
186
+ let frameworkMode;
187
+ if (cliFrameworkMode === 'npm' || cliFrameworkMode === 'vendor') {
188
+ frameworkMode = cliFrameworkMode;
189
+ }
190
+ else if (cliFrameworkMode) {
191
+ error(`Invalid --framework-mode value "${cliFrameworkMode}". Use "npm" or "vendor".`);
192
+ return;
193
+ }
194
+ else if (configFrameworkMode === 'npm' || configFrameworkMode === 'vendor') {
195
+ frameworkMode = configFrameworkMode;
196
+ info(`Using framework mode from lt.config: ${frameworkMode}`);
197
+ }
198
+ else if (noConfirm) {
199
+ frameworkMode = 'npm';
200
+ }
201
+ else {
202
+ const frameworkModeChoice = yield ask({
203
+ choices: [
204
+ 'npm - @lenne.tech/nest-server as npm dependency (classic, stable)',
205
+ 'vendor - framework core vendored into src/core/ (pilot, allows local patches)',
206
+ ],
207
+ initial: 0,
208
+ message: 'Framework consumption mode?',
209
+ name: 'frameworkMode',
210
+ type: 'select',
211
+ });
212
+ frameworkMode = frameworkModeChoice.frameworkMode.startsWith('vendor') ? 'vendor' : 'npm';
213
+ }
214
+ const frameworkUpstreamBranch = typeof cliFrameworkUpstreamBranch === 'string' && cliFrameworkUpstreamBranch.length > 0
215
+ ? cliFrameworkUpstreamBranch
216
+ : undefined;
181
217
  // Setup server using Server extension
182
218
  const setupSpinner = spin(`Setting up server${linkPath ? ' (link)' : copyPath ? ' (copy)' : branch ? ` (branch: ${branch})` : ''}`);
183
219
  const result = yield server.setupServer(`./${projectDir}`, {
@@ -186,6 +222,8 @@ const NewCommand = {
186
222
  branch,
187
223
  copyPath,
188
224
  description,
225
+ frameworkMode,
226
+ frameworkUpstreamBranch,
189
227
  linkPath,
190
228
  name,
191
229
  projectDir,
@@ -213,7 +251,7 @@ const NewCommand = {
213
251
  }
214
252
  // Git initialization (after npm install which is done in setupServer)
215
253
  if (git) {
216
- const inGit = (_z = (yield system.run('git rev-parse --is-inside-work-tree'))) === null || _z === void 0 ? void 0 : _z.trim();
254
+ const inGit = (_2 = (yield system.run('git rev-parse --is-inside-work-tree'))) === null || _2 === void 0 ? void 0 : _2.trim();
217
255
  if (inGit !== 'true') {
218
256
  // Determine initGit with priority: CLI > config > interactive
219
257
  let initializeGit;