@lenne.tech/cli 1.32.1 → 1.34.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.
- package/build/commands/codex/codex.js +23 -0
- package/build/commands/codex/plugins.js +103 -0
- package/build/commands/codex/shortcuts.js +110 -0
- package/build/commands/dev/test.js +10 -4
- package/build/commands/dev/up.js +9 -3
- package/build/commands/frontend/nuxt.js +4 -7
- package/build/commands/fullstack/init.js +26 -11
- package/build/commands/fullstack/update.js +13 -0
- package/build/commands/ticket/start.js +6 -4
- package/build/extensions/frontend-helper.js +49 -59
- package/build/extensions/server.js +220 -32
- package/build/lib/check-freshness-hooks.js +53 -0
- package/build/lib/codex-cli.js +56 -0
- package/build/lib/codex-plugin-utils.js +102 -0
- package/build/lib/dev-package-manager.js +89 -0
- package/build/lib/dev-patches.js +4 -1
- package/build/lib/dev-test-session.js +30 -16
- package/build/lib/dev-ticket.js +12 -6
- package/build/lib/heal-check-wrapper.js +59 -0
- package/build/lib/hoist-workspace-pnpm-config.js +105 -92
- package/build/lib/vendor-claude-md.js +7 -2
- package/build/templates/check/check.mjs +512 -0
- package/package.json +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
/**
|
|
13
|
+
* Codex commands
|
|
14
|
+
*/
|
|
15
|
+
module.exports = {
|
|
16
|
+
alias: ['cx'],
|
|
17
|
+
description: 'Codex commands',
|
|
18
|
+
name: 'codex',
|
|
19
|
+
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
|
+
yield toolbox.helper.showMenu('codex');
|
|
21
|
+
return 'codex';
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
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 fs_1 = require("fs");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
const codex_cli_1 = require("../../lib/codex-cli");
|
|
15
|
+
const codex_plugin_utils_1 = require("../../lib/codex-plugin-utils");
|
|
16
|
+
const CODEX_PLUGIN_NAME = 'lt-dev';
|
|
17
|
+
/**
|
|
18
|
+
* Install/update lenne.tech Codex plugin, custom agents, and prompt wrappers.
|
|
19
|
+
*/
|
|
20
|
+
const PluginsCommand = {
|
|
21
|
+
alias: ['p'],
|
|
22
|
+
description: 'Install Codex plugins',
|
|
23
|
+
name: 'plugins',
|
|
24
|
+
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
+
const { helper, print: { error, info, spin, success, warning }, system, } = toolbox;
|
|
26
|
+
const timer = system.startTimer();
|
|
27
|
+
const marketplaceRoot = String(toolbox.parameters.options.path || codex_cli_1.DEFAULT_CODEX_MARKETPLACE_ROOT);
|
|
28
|
+
const pluginRoot = (0, path_1.join)(marketplaceRoot, 'plugins', CODEX_PLUGIN_NAME);
|
|
29
|
+
const marketplaceName = (0, codex_plugin_utils_1.readCodexMarketplaceName)(marketplaceRoot);
|
|
30
|
+
if (!marketplaceName) {
|
|
31
|
+
error(`Codex marketplace not found at ${marketplaceRoot}`);
|
|
32
|
+
info('');
|
|
33
|
+
info('Expected: .agents/plugins/marketplace.json');
|
|
34
|
+
info('Run this from a checkout that contains the generated codex marketplace, or pass --path=<marketplace-root>.');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
if (!(0, fs_1.existsSync)(pluginRoot)) {
|
|
38
|
+
error(`Codex plugin not found: ${pluginRoot}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const cli = (0, codex_cli_1.findCodexCli)();
|
|
42
|
+
if (!cli) {
|
|
43
|
+
error('Codex CLI not found. Please install Codex first.');
|
|
44
|
+
info('');
|
|
45
|
+
info('Installation: https://developers.openai.com/codex');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
const listSpinner = spin('Checking Codex marketplaces');
|
|
49
|
+
const listResult = (0, codex_cli_1.runCodexCommand)(cli, ['plugin', 'marketplace', 'list', '--json']);
|
|
50
|
+
const marketplaceConfigured = listResult.success &&
|
|
51
|
+
(listResult.output.includes(`"name": "${marketplaceName}"`) || listResult.output.includes(marketplaceRoot));
|
|
52
|
+
if (marketplaceConfigured) {
|
|
53
|
+
listSpinner.succeed(`Marketplace ${marketplaceName} already configured`);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
listSpinner.text = `Adding marketplace ${marketplaceName}`;
|
|
57
|
+
const addResult = (0, codex_cli_1.runCodexCommand)(cli, ['plugin', 'marketplace', 'add', marketplaceRoot, '--json']);
|
|
58
|
+
if (addResult.success || addResult.output.includes('already')) {
|
|
59
|
+
listSpinner.succeed(`Marketplace ${marketplaceName} added`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
listSpinner.fail(`Failed to add marketplace ${marketplaceName}`);
|
|
63
|
+
error(addResult.output);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const installSpinner = spin(`Installing/updating ${CODEX_PLUGIN_NAME}`);
|
|
68
|
+
const installResult = (0, codex_cli_1.runCodexCommand)(cli, ['plugin', 'add', `${CODEX_PLUGIN_NAME}@${marketplaceName}`, '--json']);
|
|
69
|
+
if (installResult.success || installResult.output.includes('already')) {
|
|
70
|
+
installSpinner.succeed(`${CODEX_PLUGIN_NAME} installed`);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
installSpinner.fail(`Failed to install ${CODEX_PLUGIN_NAME}`);
|
|
74
|
+
error(installResult.output);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
const agentSpinner = spin('Installing Codex custom agents');
|
|
78
|
+
const agentCount = (0, codex_plugin_utils_1.installCodexAgents)(pluginRoot);
|
|
79
|
+
agentSpinner.succeed(`${agentCount} custom agents installed`);
|
|
80
|
+
const promptSpinner = spin('Installing Codex prompt wrappers');
|
|
81
|
+
const promptCount = (0, codex_plugin_utils_1.installCodexPrompts)(pluginRoot);
|
|
82
|
+
promptSpinner.succeed(`${promptCount} prompt wrappers installed`);
|
|
83
|
+
const contents = (0, codex_plugin_utils_1.readLocalCodexPluginContents)(pluginRoot);
|
|
84
|
+
info('');
|
|
85
|
+
success(`Codex setup completed in ${helper.msToMinutesAndSeconds(timer())}m.`);
|
|
86
|
+
info('');
|
|
87
|
+
info('Installed:');
|
|
88
|
+
info(` Plugin: ${CODEX_PLUGIN_NAME}@${marketplaceName}`);
|
|
89
|
+
info(` Skills (${contents.skills.length}): ${contents.skills.join(', ')}`);
|
|
90
|
+
info(` MCP Servers (${contents.mcpServers.length}): ${contents.mcpServers.join(', ')}`);
|
|
91
|
+
info(` Hooks: ${contents.hooks}`);
|
|
92
|
+
info(` Custom agents: ${agentCount}`);
|
|
93
|
+
info(` Prompt wrappers: ${promptCount}`);
|
|
94
|
+
info('');
|
|
95
|
+
warning('Restart Codex or start a new thread so new skills, agents, prompts, and MCP tools are loaded.');
|
|
96
|
+
info('Use former lt-dev commands via /prompts:lt-dev-... or by asking Codex to use the lt-dev command router.');
|
|
97
|
+
if (!toolbox.parameters.options.fromGluegunMenu) {
|
|
98
|
+
process.exit(0);
|
|
99
|
+
}
|
|
100
|
+
return 'codex plugins installed';
|
|
101
|
+
}),
|
|
102
|
+
};
|
|
103
|
+
exports.default = PluginsCommand;
|
|
@@ -0,0 +1,110 @@
|
|
|
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 shell_config_1 = require("../../lib/shell-config");
|
|
13
|
+
const CODEX_SHORTCUTS = [
|
|
14
|
+
{
|
|
15
|
+
alias: 'x',
|
|
16
|
+
command: 'codex --sandbox workspace-write --ask-for-approval on-request',
|
|
17
|
+
description: 'Start new Codex session',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
alias: 'xr',
|
|
21
|
+
command: 'codex resume',
|
|
22
|
+
description: 'Select and resume previous Codex session',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
alias: 'xf',
|
|
26
|
+
command: 'LT_PLUGIN_HOOKS_SKIP=1 codex --sandbox workspace-write --ask-for-approval on-request',
|
|
27
|
+
description: 'Start Codex in fast mode (skip lenne.tech plugin detect hooks)',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
alias: 'xp',
|
|
31
|
+
command: 'lt codex plugins',
|
|
32
|
+
description: 'Install/update lenne.tech Codex plugin setup',
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
const ShortcutsCommand = {
|
|
36
|
+
alias: ['s'],
|
|
37
|
+
description: 'Install Codex shell shortcuts',
|
|
38
|
+
name: 'shortcuts',
|
|
39
|
+
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
+
const { print: { error, info, success }, } = toolbox;
|
|
41
|
+
const shellConfig = (0, shell_config_1.getPreferredShellConfig)();
|
|
42
|
+
if (!shellConfig) {
|
|
43
|
+
error('Could not detect shell configuration file.');
|
|
44
|
+
info('Supported shells: zsh, bash');
|
|
45
|
+
if (!toolbox.parameters.options.fromGluegunMenu) {
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
return 'shortcuts: no shell config found';
|
|
49
|
+
}
|
|
50
|
+
info(`Shell: ${shellConfig.shell}`);
|
|
51
|
+
info(`Config: ${shellConfig.path}`);
|
|
52
|
+
info('');
|
|
53
|
+
const existingAliases = [];
|
|
54
|
+
const missingAliases = [];
|
|
55
|
+
for (const shortcut of CODEX_SHORTCUTS) {
|
|
56
|
+
if ((0, shell_config_1.checkAliasInFile)(shellConfig.path, shortcut.alias)) {
|
|
57
|
+
existingAliases.push(shortcut);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
missingAliases.push(shortcut);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (existingAliases.length > 0) {
|
|
64
|
+
info('Already installed:');
|
|
65
|
+
for (const { alias, description } of existingAliases) {
|
|
66
|
+
info(` ${alias} - ${description}`);
|
|
67
|
+
}
|
|
68
|
+
info('');
|
|
69
|
+
}
|
|
70
|
+
if (missingAliases.length === 0) {
|
|
71
|
+
success('All Codex shortcuts are already installed!');
|
|
72
|
+
info('');
|
|
73
|
+
info('Available shortcuts:');
|
|
74
|
+
for (const { alias, command, description } of CODEX_SHORTCUTS) {
|
|
75
|
+
info(` ${alias} - ${description}`);
|
|
76
|
+
info(` ${command}`);
|
|
77
|
+
}
|
|
78
|
+
if (!toolbox.parameters.options.fromGluegunMenu) {
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
return 'shortcuts: already installed';
|
|
82
|
+
}
|
|
83
|
+
const added = (0, shell_config_1.addAliasBlockToShellConfig)(shellConfig.path, missingAliases, 'Codex shortcuts - Added by lenne.tech CLI');
|
|
84
|
+
if (added) {
|
|
85
|
+
info('');
|
|
86
|
+
success(`Added ${missingAliases.length} shortcut${missingAliases.length > 1 ? 's' : ''} to ${shellConfig.path}`);
|
|
87
|
+
info('');
|
|
88
|
+
info(`Run: source ${shellConfig.path}`);
|
|
89
|
+
info('Or restart your terminal to apply changes.');
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
error(`Failed to write to ${shellConfig.path}`);
|
|
93
|
+
info('');
|
|
94
|
+
info('To add manually, add these lines to your shell config:');
|
|
95
|
+
info('');
|
|
96
|
+
for (const { alias, command } of missingAliases) {
|
|
97
|
+
info(`alias ${alias}='${command}'`);
|
|
98
|
+
}
|
|
99
|
+
if (!toolbox.parameters.options.fromGluegunMenu) {
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
return 'shortcuts: write failed';
|
|
103
|
+
}
|
|
104
|
+
if (!toolbox.parameters.options.fromGluegunMenu) {
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
return `shortcuts: ${missingAliases.length} added`;
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
exports.default = ShortcutsCommand;
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const fs_1 = require("fs");
|
|
13
13
|
const caddy_1 = require("../../lib/caddy");
|
|
14
14
|
const dev_env_bridge_1 = require("../../lib/dev-env-bridge");
|
|
15
|
+
const dev_package_manager_1 = require("../../lib/dev-package-manager");
|
|
15
16
|
const dev_process_1 = require("../../lib/dev-process");
|
|
16
17
|
const dev_project_1 = require("../../lib/dev-project");
|
|
17
18
|
const dev_test_session_1 = require("../../lib/dev-test-session");
|
|
@@ -75,7 +76,6 @@ const TestCommand = {
|
|
|
75
76
|
const keep = Boolean(parameters.options.keep) || parameters.options.teardown === false;
|
|
76
77
|
const debug = Boolean(parameters.options.debug);
|
|
77
78
|
const forwarded = parameters.array || [];
|
|
78
|
-
const pnpmBin = process.env.LT_PNPM_BIN || 'pnpm';
|
|
79
79
|
// `--shard N` → run the suite split across N fully-isolated stacks in
|
|
80
80
|
// parallel. A bare `--shard` defaults to 2 — the stable sweet spot for a
|
|
81
81
|
// heavy built-SSR suite (N>=3 over-subscribes the perf cores → flaky; see
|
|
@@ -102,7 +102,8 @@ const TestCommand = {
|
|
|
102
102
|
return 'dev test: no api';
|
|
103
103
|
}
|
|
104
104
|
info(colors.bold(`Running API tests for "${identity.slug}" (isolated DB)`));
|
|
105
|
-
const
|
|
105
|
+
const apiPm = (0, dev_package_manager_1.pickPackageManager)(layout.apiDir);
|
|
106
|
+
const code = yield (0, dev_process_1.runChildInherit)(apiPm.bin, apiPm.runScript('test:e2e', forwarded), {
|
|
106
107
|
cwd: layout.apiDir,
|
|
107
108
|
env: process.env,
|
|
108
109
|
});
|
|
@@ -174,10 +175,11 @@ const TestCommand = {
|
|
|
174
175
|
try {
|
|
175
176
|
info('');
|
|
176
177
|
info(colors.bold(`Running isolated Playwright E2E for "${identity.slug}" sharded across ${shardTotal} stacks`));
|
|
178
|
+
const shardPm = (0, dev_package_manager_1.pickPackageManager)(layout.appDir);
|
|
177
179
|
shardExit = yield (0, dev_test_session_1.runShardedTestSession)(layout, identity, log, {
|
|
178
180
|
devDbName,
|
|
179
181
|
forwarded,
|
|
180
|
-
|
|
182
|
+
pm: shardPm,
|
|
181
183
|
total: shardTotal,
|
|
182
184
|
});
|
|
183
185
|
}
|
|
@@ -233,7 +235,11 @@ const TestCommand = {
|
|
|
233
235
|
info(colors.bold(`Running isolated Playwright E2E for "${identity.slug}"`));
|
|
234
236
|
info(colors.dim(` app: ${ctx.appUrl} db: ${ctx.dbName}`));
|
|
235
237
|
info('');
|
|
236
|
-
|
|
238
|
+
const appPm = (0, dev_package_manager_1.pickPackageManager)(layout.appDir);
|
|
239
|
+
exitCode = yield (0, dev_process_1.runChildInherit)(appPm.bin, appPm.runScript('test:e2e', forwarded), {
|
|
240
|
+
cwd: layout.appDir,
|
|
241
|
+
env,
|
|
242
|
+
});
|
|
237
243
|
}
|
|
238
244
|
catch (e) {
|
|
239
245
|
error(`Failed to run isolated E2E: ${e.message}`);
|
package/build/commands/dev/up.js
CHANGED
|
@@ -14,6 +14,7 @@ const path_1 = require("path");
|
|
|
14
14
|
const caddy_1 = require("../../lib/caddy");
|
|
15
15
|
const dev_env_1 = require("../../lib/dev-env");
|
|
16
16
|
const dev_env_bridge_1 = require("../../lib/dev-env-bridge");
|
|
17
|
+
const dev_package_manager_1 = require("../../lib/dev-package-manager");
|
|
17
18
|
const dev_patches_1 = require("../../lib/dev-patches");
|
|
18
19
|
const dev_process_1 = require("../../lib/dev-process");
|
|
19
20
|
const dev_project_1 = require("../../lib/dev-project");
|
|
@@ -326,7 +327,6 @@ const UpCommand = {
|
|
|
326
327
|
dbName,
|
|
327
328
|
identity,
|
|
328
329
|
});
|
|
329
|
-
const pnpmBin = process.env.LT_PNPM_BIN || 'pnpm';
|
|
330
330
|
const pids = {};
|
|
331
331
|
const rotationNotes = [];
|
|
332
332
|
const started = [];
|
|
@@ -350,7 +350,12 @@ const UpCommand = {
|
|
|
350
350
|
}
|
|
351
351
|
else {
|
|
352
352
|
yield reclaimPort(existingSession === null || existingSession === void 0 ? void 0 : existingSession.pids.api, apiPort, apiHealth !== null && apiHealth !== void 0 ? apiHealth : 'dead');
|
|
353
|
-
|
|
353
|
+
// Per-component PM detection: a monorepo may have an npm api and a
|
|
354
|
+
// pnpm app, and the legacy hard-coded `pnpm start` would silently
|
|
355
|
+
// regenerate a foreign lockfile + crash on un-approved build
|
|
356
|
+
// scripts when run against an npm-only project.
|
|
357
|
+
const apiPm = (0, dev_package_manager_1.pickPackageManager)(layout.apiDir);
|
|
358
|
+
const apiResult = (0, dev_process_1.spawnDetached)(apiPm.bin, apiPm.runScript('start'), {
|
|
354
359
|
cwd: layout.apiDir,
|
|
355
360
|
env: devEnv.api.env,
|
|
356
361
|
logFile: (0, path_1.join)(layout.root, '.lt-dev', 'api.log'),
|
|
@@ -371,7 +376,8 @@ const UpCommand = {
|
|
|
371
376
|
}
|
|
372
377
|
else {
|
|
373
378
|
yield reclaimPort(existingSession === null || existingSession === void 0 ? void 0 : existingSession.pids.app, appPort, appHealth !== null && appHealth !== void 0 ? appHealth : 'dead');
|
|
374
|
-
const
|
|
379
|
+
const appPm = (0, dev_package_manager_1.pickPackageManager)(layout.appDir);
|
|
380
|
+
const appResult = (0, dev_process_1.spawnDetached)(appPm.bin, appPm.runScript('dev'), {
|
|
375
381
|
cwd: layout.appDir,
|
|
376
382
|
env: devEnv.app.env,
|
|
377
383
|
logFile: (0, path_1.join)(layout.root, '.lt-dev', 'app.log'),
|
|
@@ -15,7 +15,7 @@ const workspace_integration_1 = require("../../lib/workspace-integration");
|
|
|
15
15
|
* Create a new nuxt workspace
|
|
16
16
|
*
|
|
17
17
|
* Standalone counterpart to `lt fullstack init` / `lt fullstack add-app`
|
|
18
|
-
* for Nuxt: clones nuxt-base-starter
|
|
18
|
+
* for Nuxt: clones nuxt-base-starter into
|
|
19
19
|
* a brand-new directory. Mirrors the same surface area as add-app where
|
|
20
20
|
* applicable so behaviour is consistent across the four flows.
|
|
21
21
|
*/
|
|
@@ -170,7 +170,7 @@ const NewCommand = {
|
|
|
170
170
|
info('Dry-run plan:');
|
|
171
171
|
info(` name: ${projName}`);
|
|
172
172
|
info(` projectDir: ${projectDir}`);
|
|
173
|
-
info(` branch: ${branch || '(default
|
|
173
|
+
info(` branch: ${branch || '(default branch)'}`);
|
|
174
174
|
info(` copy: ${copyPath || '(none)'}`);
|
|
175
175
|
info(` link: ${linkPath || '(none)'}`);
|
|
176
176
|
info(` frontendFrameworkMode: ${frontendFrameworkMode}`);
|
|
@@ -183,11 +183,8 @@ const NewCommand = {
|
|
|
183
183
|
else if (copyPath) {
|
|
184
184
|
info(` 1. copy ${copyPath} → ./${projectDir}`);
|
|
185
185
|
}
|
|
186
|
-
else if (branch) {
|
|
187
|
-
info(` 1. clone nuxt-base-starter (branch: ${branch}) → ./${projectDir}`);
|
|
188
|
-
}
|
|
189
186
|
else {
|
|
190
|
-
info(` 1.
|
|
187
|
+
info(` 1. clone nuxt-base-starter (${branch ? `branch: ${branch}` : 'default branch'}) → ./${projectDir}`);
|
|
191
188
|
}
|
|
192
189
|
if (frontendFrameworkMode === 'vendor') {
|
|
193
190
|
info(` 2. clone @lenne.tech/nuxt-extensions → /tmp`);
|
|
@@ -206,7 +203,7 @@ const NewCommand = {
|
|
|
206
203
|
branch,
|
|
207
204
|
copyPath,
|
|
208
205
|
linkPath,
|
|
209
|
-
skipInstall:
|
|
206
|
+
skipInstall: false, // clone-based setup installs here (create-nuxt-base no longer used)
|
|
210
207
|
});
|
|
211
208
|
if (!result.success) {
|
|
212
209
|
baseSpinner.fail(`Failed to setup nuxt workspace: ${result.path}`);
|
|
@@ -33,7 +33,7 @@ const NewCommand = {
|
|
|
33
33
|
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
34
34
|
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;
|
|
35
35
|
// Retrieve the tools we need
|
|
36
|
-
const { config, filesystem, frontendHelper, git, helper, parameters, patching, print: { colors, error, info, spin, success }, prompt: { ask, confirm }, server, strings: { kebabCase }, system, template, } = toolbox;
|
|
36
|
+
const { config, filesystem, frontendHelper, git, helper, parameters, patching, print: { colors, error, info, spin, success, warning }, prompt: { ask, confirm }, server, strings: { kebabCase }, system, template, } = toolbox;
|
|
37
37
|
// Start timer
|
|
38
38
|
const timer = system.startTimer();
|
|
39
39
|
// Info
|
|
@@ -209,7 +209,9 @@ const NewCommand = {
|
|
|
209
209
|
// `/lt-dev:backend:update-nest-server-core`; local patches are logged
|
|
210
210
|
// in src/core/VENDOR.md.
|
|
211
211
|
//
|
|
212
|
-
// Default is
|
|
212
|
+
// Default is 'vendor' — the lt CLI integrates the framework core directly
|
|
213
|
+
// (clones the starter, vendors core into src/core/), which is the
|
|
214
|
+
// recommended setup; pass `--framework-mode npm` for the classic npm dep.
|
|
213
215
|
let frameworkMode;
|
|
214
216
|
if (experimental) {
|
|
215
217
|
frameworkMode = 'npm';
|
|
@@ -226,16 +228,16 @@ const NewCommand = {
|
|
|
226
228
|
info(`Using framework mode from lt.config: ${frameworkMode}`);
|
|
227
229
|
}
|
|
228
230
|
else if (noConfirm) {
|
|
229
|
-
frameworkMode = '
|
|
230
|
-
info('Using default framework mode:
|
|
231
|
+
frameworkMode = 'vendor';
|
|
232
|
+
info('Using default framework mode: vendor (noConfirm mode)');
|
|
231
233
|
}
|
|
232
234
|
else {
|
|
233
235
|
const frameworkModeChoice = yield ask({
|
|
234
236
|
choices: [
|
|
235
237
|
'npm - @lenne.tech/nest-server as npm dependency (classic, stable)',
|
|
236
|
-
'vendor - framework core vendored into projects/api/src/core/ (
|
|
238
|
+
'vendor - framework core vendored into projects/api/src/core/ (default, allows local patches)',
|
|
237
239
|
],
|
|
238
|
-
initial:
|
|
240
|
+
initial: 1,
|
|
239
241
|
message: 'Framework consumption mode?',
|
|
240
242
|
name: 'frameworkMode',
|
|
241
243
|
type: 'select',
|
|
@@ -257,11 +259,11 @@ const NewCommand = {
|
|
|
257
259
|
info(`Using frontend framework mode from lt.config: ${frontendFrameworkMode}`);
|
|
258
260
|
}
|
|
259
261
|
else if (noConfirm) {
|
|
260
|
-
frontendFrameworkMode = '
|
|
262
|
+
frontendFrameworkMode = 'vendor';
|
|
261
263
|
}
|
|
262
264
|
else {
|
|
263
|
-
// Default to
|
|
264
|
-
frontendFrameworkMode = '
|
|
265
|
+
// Default to vendor without asking (unless user sets it explicitly)
|
|
266
|
+
frontendFrameworkMode = 'vendor';
|
|
265
267
|
}
|
|
266
268
|
// Determine remote push settings with priority: CLI > config > interactive
|
|
267
269
|
// Git is always initialized; the question is whether to push to a remote
|
|
@@ -273,8 +275,13 @@ const NewCommand = {
|
|
|
273
275
|
if (pushToRemote) {
|
|
274
276
|
gitLink = cliGitLink || configGitLink;
|
|
275
277
|
if (!gitLink) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
// Git is always initialized locally (dev branch + initial commit);
|
|
279
|
+
// --git only controls the remote push. Without a link there is
|
|
280
|
+
// nothing to push to, so degrade to a local-only init instead of
|
|
281
|
+
// aborting the whole scaffold — consistent with the config and
|
|
282
|
+
// interactive branches below.
|
|
283
|
+
warning('--git true without --git-link: initializing local git only (no remote push).');
|
|
284
|
+
pushToRemote = false;
|
|
278
285
|
}
|
|
279
286
|
}
|
|
280
287
|
}
|
|
@@ -314,6 +321,14 @@ const NewCommand = {
|
|
|
314
321
|
}
|
|
315
322
|
}
|
|
316
323
|
}
|
|
324
|
+
else if (noConfirm) {
|
|
325
|
+
// Default in noConfirm mode: git true. Git is always initialized locally
|
|
326
|
+
// (dev branch + initial commit); push to a remote only when a gitLink is
|
|
327
|
+
// configured, otherwise stay local-only (nothing to push to).
|
|
328
|
+
gitLink = configGitLink;
|
|
329
|
+
pushToRemote = !!gitLink;
|
|
330
|
+
info(`Using default git: true${gitLink ? ' (push to configured gitLink)' : ' (local only — no --git-link configured)'}`);
|
|
331
|
+
}
|
|
317
332
|
// Determine branches and copy/link paths with priority: CLI > config
|
|
318
333
|
const apiBranch = cliApiBranch || configApiBranch;
|
|
319
334
|
// Under `--next`, default the nuxt-base-starter ref to the `next`
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const path_1 = require("path");
|
|
13
13
|
const framework_detection_1 = require("../../lib/framework-detection");
|
|
14
14
|
const frontend_framework_detection_1 = require("../../lib/frontend-framework-detection");
|
|
15
|
+
const heal_check_wrapper_1 = require("../../lib/heal-check-wrapper");
|
|
15
16
|
const vendor_claude_md_1 = require("../../lib/vendor-claude-md");
|
|
16
17
|
/**
|
|
17
18
|
* Update a fullstack workspace — mode-aware.
|
|
@@ -185,6 +186,18 @@ const NewCommand = {
|
|
|
185
186
|
info(` ${changedPath}`);
|
|
186
187
|
}
|
|
187
188
|
}
|
|
189
|
+
// ── Self-heal: install/refresh the report-driven `check` wrapper ──────
|
|
190
|
+
//
|
|
191
|
+
// `lt fullstack init` ships `scripts/check.mjs` via the template clone, but
|
|
192
|
+
// pre-existing projects predate it. This idempotently installs the bundled
|
|
193
|
+
// wrapper (and rewrites the root `check`/`check:raw`) so a migrated project
|
|
194
|
+
// gets the quiet, report-driven check too. No-op once already wired.
|
|
195
|
+
const checkAsset = (0, path_1.join)(__dirname, '..', '..', 'templates', 'check', 'check.mjs');
|
|
196
|
+
const changedCheck = (0, heal_check_wrapper_1.healCheckWrapper)(cwd, checkAsset);
|
|
197
|
+
if (changedCheck.length > 0) {
|
|
198
|
+
info('');
|
|
199
|
+
success(` Installed/updated the check wrapper: ${changedCheck.join(', ')}`);
|
|
200
|
+
}
|
|
188
201
|
info('');
|
|
189
202
|
info(colors.bold('For a comprehensive update of everything, use:'));
|
|
190
203
|
info('');
|
|
@@ -97,14 +97,16 @@ const StartCommand = {
|
|
|
97
97
|
}
|
|
98
98
|
// 2. tag the worktree with its ticket id (makes lt dev * ticket-aware).
|
|
99
99
|
(0, dev_ticket_1.writeTicketMarker)(worktreePath, id);
|
|
100
|
-
// 3. install deps
|
|
100
|
+
// 3. install deps. Auto-detects pnpm/yarn/npm from the worktree's
|
|
101
|
+
// lockfile so an npm-only repo doesn't get a foreign pnpm-lock
|
|
102
|
+
// injected on every ticket start.
|
|
101
103
|
if (parameters.options.install !== false) {
|
|
102
|
-
info(colors.dim('Installing dependencies
|
|
104
|
+
info(colors.dim('Installing dependencies …'));
|
|
103
105
|
try {
|
|
104
|
-
(0, dev_ticket_1.
|
|
106
|
+
(0, dev_ticket_1.installWorktreeDeps)(worktreePath);
|
|
105
107
|
}
|
|
106
108
|
catch (e) {
|
|
107
|
-
warning(`
|
|
109
|
+
warning(`install failed (${e.message}) — continuing; run it manually in the worktree.`);
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
// 4. bring the isolated stack up (re-invokes THIS lt build so the marker is
|