@lenne.tech/cli 1.27.0 → 1.29.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/cli.js +7 -1
- package/build/commands/dev/doctor.js +27 -1
- package/build/commands/dev/down.js +22 -10
- package/build/commands/dev/status.js +4 -3
- package/build/commands/dev/test.js +12 -4
- package/build/commands/dev/up.js +90 -50
- package/build/commands/frontend/angular.js +48 -46
- package/build/commands/frontend/convert-mode.js +41 -39
- package/build/commands/frontend/nuxt.js +49 -47
- package/build/commands/fullstack/add-api.js +34 -32
- package/build/commands/fullstack/add-app.js +25 -23
- package/build/commands/fullstack/convert-mode.js +85 -65
- package/build/commands/fullstack/init.js +12 -0
- package/build/commands/fullstack/update.js +24 -0
- package/build/commands/server/add-property.js +42 -40
- package/build/commands/server/convert-mode.js +41 -39
- package/build/commands/server/create.js +65 -63
- package/build/commands/server/module.js +56 -54
- package/build/commands/server/object.js +42 -40
- package/build/commands/server/permissions.js +60 -58
- package/build/commands/ticket/list.js +78 -0
- package/build/commands/ticket/start.js +141 -0
- package/build/commands/ticket/stop.js +166 -0
- package/build/commands/ticket/switch.js +70 -0
- package/build/commands/ticket/test.js +80 -0
- package/build/commands/ticket/ticket.js +36 -0
- package/build/commands/tools/crawl.js +92 -90
- package/build/extensions/frontend-helper.js +8 -37
- package/build/extensions/server.js +8 -38
- package/build/lib/command-help.js +161 -0
- package/build/lib/dev-identity.js +18 -0
- package/build/lib/dev-patches.js +1 -1
- package/build/lib/dev-project.js +14 -0
- package/build/lib/dev-state.js +96 -0
- package/build/lib/dev-test-session.js +55 -35
- package/build/lib/dev-ticket.js +343 -0
- package/build/lib/vendor-claude-md.js +227 -0
- package/docs/lt-dev-ticket-workflow.html +603 -0
- package/docs/lt-dev-ticket-workflow.pdf +0 -0
- package/package.json +32 -1
|
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.help = void 0;
|
|
12
13
|
const path_1 = require("path");
|
|
13
14
|
const crawler_1 = require("../../lib/crawler");
|
|
14
15
|
/**
|
|
@@ -18,6 +19,96 @@ const crawler_1 = require("../../lib/crawler");
|
|
|
18
19
|
* shares the defuddle + Turndown extraction pipeline but runs headless
|
|
19
20
|
* from Node and follows links / sitemaps automatically.
|
|
20
21
|
*/
|
|
22
|
+
exports.help = {
|
|
23
|
+
aliases: ['cr'],
|
|
24
|
+
description: 'Crawl a website into Markdown files (for Claude Code knowledge bases)',
|
|
25
|
+
name: 'crawl',
|
|
26
|
+
options: [
|
|
27
|
+
{
|
|
28
|
+
description: 'Start URL (absolute http/https URL)',
|
|
29
|
+
flag: '--url',
|
|
30
|
+
required: true,
|
|
31
|
+
type: 'string',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
default: '.',
|
|
35
|
+
description: 'Output directory (created if missing)',
|
|
36
|
+
flag: '--out',
|
|
37
|
+
type: 'string',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
default: 0,
|
|
41
|
+
description: 'Link depth. 0 = only start page; 1 = + direct links; N = up to N hops; "all" (or -1) = follow every same-origin link until --max-pages is reached',
|
|
42
|
+
flag: '--depth',
|
|
43
|
+
type: 'number|all',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
default: true,
|
|
47
|
+
description: 'Download images and inline them with local paths',
|
|
48
|
+
flag: '--images',
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
default: true,
|
|
53
|
+
description: 'Also seed queue from <origin>/sitemap.xml',
|
|
54
|
+
flag: '--sitemap',
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
default: 4,
|
|
59
|
+
description: 'Parallel HTTP requests',
|
|
60
|
+
flag: '--concurrency',
|
|
61
|
+
type: 'number',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
default: 200,
|
|
65
|
+
description: 'Maximum number of pages to crawl (safety cap)',
|
|
66
|
+
flag: '--max-pages',
|
|
67
|
+
type: 'number',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
description: 'CSS selector for the main content container',
|
|
71
|
+
flag: '--selector',
|
|
72
|
+
type: 'string',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
default: 20000,
|
|
76
|
+
description: 'HTTP request timeout in ms',
|
|
77
|
+
flag: '--timeout',
|
|
78
|
+
type: 'number',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
default: false,
|
|
82
|
+
description: 'Shortcut for --depth all (follows every same-origin link until --max-pages)',
|
|
83
|
+
flag: '--all',
|
|
84
|
+
type: 'boolean',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
default: true,
|
|
88
|
+
description: "Render pages through a headless browser before extracting (for SPAs like Vue/Nuxt/React/Angular). Uses playwright-core with system Chrome / Edge, falling back to Playwright's bundled chromium. Disable with --no-render for plain HTTP fetches.",
|
|
89
|
+
flag: '--render',
|
|
90
|
+
type: 'boolean',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
default: false,
|
|
94
|
+
description: 'If --render cannot find any browser, auto-install Playwright chromium (one-time ~170 MB download).',
|
|
95
|
+
flag: '--install-browser',
|
|
96
|
+
type: 'boolean',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
default: true,
|
|
100
|
+
description: 'After a multi-page crawl, remove any .md or image files inside <outDir>/pages and <outDir>/images that were not written by this run. Disable with --no-prune to preserve old files.',
|
|
101
|
+
flag: '--prune',
|
|
102
|
+
type: 'boolean',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
default: false,
|
|
106
|
+
description: 'Skip confirmation prompts',
|
|
107
|
+
flag: '--noConfirm',
|
|
108
|
+
type: 'boolean',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
21
112
|
const NewCommand = {
|
|
22
113
|
alias: ['cr'],
|
|
23
114
|
description: 'Crawl site to Markdown',
|
|
@@ -26,96 +117,7 @@ const NewCommand = {
|
|
|
26
117
|
run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
118
|
var _a, _b, _c, _d, _e;
|
|
28
119
|
const { config, filesystem, helper, parameters, print: { error, info, spin, success, warning }, prompt: { confirm }, tools, } = toolbox;
|
|
29
|
-
if (tools.helpJson({
|
|
30
|
-
aliases: ['cr'],
|
|
31
|
-
description: 'Crawl a website into Markdown files (for Claude Code knowledge bases)',
|
|
32
|
-
name: 'crawl',
|
|
33
|
-
options: [
|
|
34
|
-
{
|
|
35
|
-
description: 'Start URL (absolute http/https URL)',
|
|
36
|
-
flag: '--url',
|
|
37
|
-
required: true,
|
|
38
|
-
type: 'string',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
default: '.',
|
|
42
|
-
description: 'Output directory (created if missing)',
|
|
43
|
-
flag: '--out',
|
|
44
|
-
type: 'string',
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
default: 0,
|
|
48
|
-
description: 'Link depth. 0 = only start page; 1 = + direct links; N = up to N hops; "all" (or -1) = follow every same-origin link until --max-pages is reached',
|
|
49
|
-
flag: '--depth',
|
|
50
|
-
type: 'number|all',
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
default: true,
|
|
54
|
-
description: 'Download images and inline them with local paths',
|
|
55
|
-
flag: '--images',
|
|
56
|
-
type: 'boolean',
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
default: true,
|
|
60
|
-
description: 'Also seed queue from <origin>/sitemap.xml',
|
|
61
|
-
flag: '--sitemap',
|
|
62
|
-
type: 'boolean',
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
default: 4,
|
|
66
|
-
description: 'Parallel HTTP requests',
|
|
67
|
-
flag: '--concurrency',
|
|
68
|
-
type: 'number',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
default: 200,
|
|
72
|
-
description: 'Maximum number of pages to crawl (safety cap)',
|
|
73
|
-
flag: '--max-pages',
|
|
74
|
-
type: 'number',
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
description: 'CSS selector for the main content container',
|
|
78
|
-
flag: '--selector',
|
|
79
|
-
type: 'string',
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
default: 20000,
|
|
83
|
-
description: 'HTTP request timeout in ms',
|
|
84
|
-
flag: '--timeout',
|
|
85
|
-
type: 'number',
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
default: false,
|
|
89
|
-
description: 'Shortcut for --depth all (follows every same-origin link until --max-pages)',
|
|
90
|
-
flag: '--all',
|
|
91
|
-
type: 'boolean',
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
default: true,
|
|
95
|
-
description: "Render pages through a headless browser before extracting (for SPAs like Vue/Nuxt/React/Angular). Uses playwright-core with system Chrome / Edge, falling back to Playwright's bundled chromium. Disable with --no-render for plain HTTP fetches.",
|
|
96
|
-
flag: '--render',
|
|
97
|
-
type: 'boolean',
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
default: false,
|
|
101
|
-
description: 'If --render cannot find any browser, auto-install Playwright chromium (one-time ~170 MB download).',
|
|
102
|
-
flag: '--install-browser',
|
|
103
|
-
type: 'boolean',
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
default: true,
|
|
107
|
-
description: 'After a multi-page crawl, remove any .md or image files inside <outDir>/pages and <outDir>/images that were not written by this run. Disable with --no-prune to preserve old files.',
|
|
108
|
-
flag: '--prune',
|
|
109
|
-
type: 'boolean',
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
default: false,
|
|
113
|
-
description: 'Skip confirmation prompts',
|
|
114
|
-
flag: '--noConfirm',
|
|
115
|
-
type: 'boolean',
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
})) {
|
|
120
|
+
if (tools.helpJson(exports.help)) {
|
|
119
121
|
return 'crawl';
|
|
120
122
|
}
|
|
121
123
|
tools.nonInteractiveHint('lt tools crawl <url> --out <dir> --depth 1 --noConfirm');
|
|
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.FrontendHelper = void 0;
|
|
13
13
|
const markdown_table_1 = require("../lib/markdown-table");
|
|
14
|
+
const vendor_claude_md_1 = require("../lib/vendor-claude-md");
|
|
14
15
|
/**
|
|
15
16
|
* Frontend helper functions for project scaffolding
|
|
16
17
|
* Provides reusable methods for setting up Nuxt and Angular frontends
|
|
@@ -390,17 +391,10 @@ class FrontendHelper {
|
|
|
390
391
|
// ── 6. Clean CLAUDE.md vendor marker ─────────────────────────────────
|
|
391
392
|
const claudeMdPath = path.join(dest, 'CLAUDE.md');
|
|
392
393
|
if (filesystem.exists(claudeMdPath)) {
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
const startIdx = content.indexOf(markerStart);
|
|
398
|
-
const endIdx = content.indexOf(markerEnd, startIdx);
|
|
399
|
-
if (endIdx > startIdx) {
|
|
400
|
-
content = content.slice(0, startIdx) + content.slice(endIdx + markerEnd.length);
|
|
401
|
-
content = content.replace(/^\n+/, '');
|
|
402
|
-
filesystem.write(claudeMdPath, content);
|
|
403
|
-
}
|
|
394
|
+
const content = filesystem.read(claudeMdPath) || '';
|
|
395
|
+
const cleaned = (0, vendor_claude_md_1.removeVendorBlock)(content, vendor_claude_md_1.FRONTEND_VENDOR_MARKER);
|
|
396
|
+
if (cleaned !== content) {
|
|
397
|
+
filesystem.write(claudeMdPath, cleaned);
|
|
404
398
|
}
|
|
405
399
|
}
|
|
406
400
|
// ── Post-conversion verification ─────────────────────────────────────
|
|
@@ -615,32 +609,9 @@ class FrontendHelper {
|
|
|
615
609
|
const claudeMdPath = path.join(dest, 'CLAUDE.md');
|
|
616
610
|
if (filesystem.exists(claudeMdPath)) {
|
|
617
611
|
const existing = filesystem.read(claudeMdPath) || '';
|
|
618
|
-
const
|
|
619
|
-
if (
|
|
620
|
-
|
|
621
|
-
marker,
|
|
622
|
-
'',
|
|
623
|
-
'# Vendor-Mode Notice (Frontend)',
|
|
624
|
-
'',
|
|
625
|
-
'This frontend project runs in **vendor mode**: the `@lenne.tech/nuxt-extensions`',
|
|
626
|
-
'module has been copied directly into `app/core/` as first-class',
|
|
627
|
-
'project code. There is **no** `@lenne.tech/nuxt-extensions` npm dependency.',
|
|
628
|
-
'',
|
|
629
|
-
'- **Read framework code from `app/core/**`** — not from `node_modules/`.',
|
|
630
|
-
"- **nuxt.config.ts** references `'./app/core/module'` instead of",
|
|
631
|
-
" `'@lenne.tech/nuxt-extensions'`.",
|
|
632
|
-
'- **Baseline + patch log** live in `app/core/VENDOR.md`. Log any',
|
|
633
|
-
' substantial local change there so the `nuxt-extensions-core-updater`',
|
|
634
|
-
' agent can classify it at sync time.',
|
|
635
|
-
'- **Update flow:** run `/lt-dev:frontend:update-nuxt-extensions-core`.',
|
|
636
|
-
'- **Contribute back:** run `/lt-dev:frontend:contribute-nuxt-extensions-core`.',
|
|
637
|
-
'- **Freshness check:** `pnpm run check:vendor-freshness` warns when',
|
|
638
|
-
' upstream has a newer release than the baseline.',
|
|
639
|
-
'',
|
|
640
|
-
'---',
|
|
641
|
-
'',
|
|
642
|
-
].join('\n');
|
|
643
|
-
filesystem.write(claudeMdPath, vendorBlock + existing);
|
|
612
|
+
const patched = (0, vendor_claude_md_1.insertVendorBlockIfMissing)(existing, vendor_claude_md_1.FRONTEND_VENDOR_MARKER, (0, vendor_claude_md_1.buildFrontendVendorBlock)());
|
|
613
|
+
if (patched !== existing) {
|
|
614
|
+
filesystem.write(claudeMdPath, patched);
|
|
644
615
|
}
|
|
645
616
|
}
|
|
646
617
|
// Merge upstream CLAUDE.md sections
|
|
@@ -47,6 +47,7 @@ const crypto = __importStar(require("crypto"));
|
|
|
47
47
|
const path_1 = require("path");
|
|
48
48
|
const ts = __importStar(require("typescript"));
|
|
49
49
|
const markdown_table_1 = require("../lib/markdown-table");
|
|
50
|
+
const vendor_claude_md_1 = require("../lib/vendor-claude-md");
|
|
50
51
|
/**
|
|
51
52
|
* Server helper functions
|
|
52
53
|
*/
|
|
@@ -1585,36 +1586,9 @@ class Server {
|
|
|
1585
1586
|
const apiClaudeMdPath = `${dest}/CLAUDE.md`;
|
|
1586
1587
|
if (filesystem.exists(apiClaudeMdPath)) {
|
|
1587
1588
|
const existing = filesystem.read(apiClaudeMdPath) || '';
|
|
1588
|
-
const
|
|
1589
|
-
if (
|
|
1590
|
-
|
|
1591
|
-
marker,
|
|
1592
|
-
'',
|
|
1593
|
-
'# Vendor-Mode Notice',
|
|
1594
|
-
'',
|
|
1595
|
-
'This api project runs in **vendor mode**: the `@lenne.tech/nest-server`',
|
|
1596
|
-
'core/ tree has been copied directly into `src/core/` as first-class',
|
|
1597
|
-
'project code. There is **no** `@lenne.tech/nest-server` npm dependency.',
|
|
1598
|
-
'',
|
|
1599
|
-
'- **Read framework code from `src/core/**`** — not from `node_modules/`.',
|
|
1600
|
-
'- **Generated imports use relative paths** to `src/core`, e.g.',
|
|
1601
|
-
" `import { CrudService } from '../../../core';`",
|
|
1602
|
-
' The exact depth depends on the file location. `lt server module`',
|
|
1603
|
-
' computes it automatically.',
|
|
1604
|
-
'- **Baseline + patch log** live in `src/core/VENDOR.md`. Log any',
|
|
1605
|
-
' substantial local change there so the `nest-server-core-updater`',
|
|
1606
|
-
' agent can classify it at sync time.',
|
|
1607
|
-
'- **Update flow:** run `/lt-dev:backend:update-nest-server-core` (the',
|
|
1608
|
-
' agent clones upstream, computes a delta, and presents a review).',
|
|
1609
|
-
'- **Contribute back:** run `/lt-dev:backend:contribute-nest-server-core`',
|
|
1610
|
-
' to propose local fixes as upstream PRs.',
|
|
1611
|
-
'- **Freshness check:** `pnpm run check:vendor-freshness` warns (non-',
|
|
1612
|
-
' blockingly) when upstream has a newer release than the baseline.',
|
|
1613
|
-
'',
|
|
1614
|
-
'---',
|
|
1615
|
-
'',
|
|
1616
|
-
].join('\n');
|
|
1617
|
-
filesystem.write(apiClaudeMdPath, vendorBlock + existing);
|
|
1589
|
+
const patched = (0, vendor_claude_md_1.insertVendorBlockIfMissing)(existing, vendor_claude_md_1.BACKEND_VENDOR_MARKER, (0, vendor_claude_md_1.buildBackendVendorBlock)());
|
|
1590
|
+
if (patched !== existing) {
|
|
1591
|
+
filesystem.write(apiClaudeMdPath, patched);
|
|
1618
1592
|
}
|
|
1619
1593
|
}
|
|
1620
1594
|
// ── 9c. Merge nest-server CLAUDE.md sections into project CLAUDE.md ──
|
|
@@ -2307,14 +2281,10 @@ class Server {
|
|
|
2307
2281
|
// ── 6. Clean CLAUDE.md vendor marker ────────────────────────────────
|
|
2308
2282
|
const claudeMdPath = `${dest}/CLAUDE.md`;
|
|
2309
2283
|
if (filesystem.exists(claudeMdPath)) {
|
|
2310
|
-
|
|
2311
|
-
const
|
|
2312
|
-
if (content
|
|
2313
|
-
|
|
2314
|
-
content = content.replace(new RegExp(`${marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?---\\s*\\n?`, ''), '');
|
|
2315
|
-
// Remove leading whitespace/newlines
|
|
2316
|
-
content = content.replace(/^\n+/, '');
|
|
2317
|
-
filesystem.write(claudeMdPath, content);
|
|
2284
|
+
const content = filesystem.read(claudeMdPath) || '';
|
|
2285
|
+
const cleaned = (0, vendor_claude_md_1.removeVendorBlock)(content, vendor_claude_md_1.BACKEND_VENDOR_MARKER);
|
|
2286
|
+
if (cleaned !== content) {
|
|
2287
|
+
filesystem.write(claudeMdPath, cleaned);
|
|
2318
2288
|
}
|
|
2319
2289
|
}
|
|
2320
2290
|
// ── 7. Restore tsconfig excludes ────────────────────────────────────
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generic `--help` / `-h` support for **every** lt command.
|
|
4
|
+
*
|
|
5
|
+
* Problem this solves: gluegun's built-in `.help()` only handles the top-level
|
|
6
|
+
* `lt --help` (the command list). For a subcommand, `lt fullstack convert-mode
|
|
7
|
+
* --help` simply *runs* the command — so a user who only wanted to read about a
|
|
8
|
+
* command accidentally triggers it. {@link installHelpInterceptor} wraps every
|
|
9
|
+
* loaded command so that, when help is requested, the command prints rich help
|
|
10
|
+
* and returns **without executing**.
|
|
11
|
+
*
|
|
12
|
+
* Two levels of detail:
|
|
13
|
+
* 1. Generic (always available) — usage, aliases and description from the
|
|
14
|
+
* command metadata gluegun already has.
|
|
15
|
+
* 2. Rich (opt-in) — a command module may `export const help: CommandHelp`
|
|
16
|
+
* describing options, features, examples and configuration. The interceptor
|
|
17
|
+
* loads it from `command.file` without running the command.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.installHelpInterceptor = installHelpInterceptor;
|
|
21
|
+
exports.isHelpRequested = isHelpRequested;
|
|
22
|
+
exports.loadCommandHelp = loadCommandHelp;
|
|
23
|
+
exports.renderCommandHelp = renderCommandHelp;
|
|
24
|
+
/**
|
|
25
|
+
* Wrap every command's `run` so that `--help` / `-h` prints help and returns
|
|
26
|
+
* without executing. Call once after `build().create()`, before `cli.run()`.
|
|
27
|
+
*
|
|
28
|
+
* Idempotent per command (guarded by `__helpWrapped`), so a command is never
|
|
29
|
+
* double-wrapped if this runs more than once in the same process (e.g. tests).
|
|
30
|
+
*/
|
|
31
|
+
function installHelpInterceptor(commands, defaultCommand) {
|
|
32
|
+
for (const command of commands || []) {
|
|
33
|
+
if (typeof command.run !== 'function' || command.__helpWrapped) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// Leave the top-level/default command and gluegun's preloaded builtins
|
|
37
|
+
// (`help`, `version` — they have `file === null`) to gluegun's own
|
|
38
|
+
// `.help()`, so that `lt --help` keeps printing the brand banner + full
|
|
39
|
+
// command list. Real file-backed subcommands (incl. `lt config help`) are
|
|
40
|
+
// still wrapped.
|
|
41
|
+
if (command === defaultCommand || !command.file || !(command.commandPath && command.commandPath.length)) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const originalRun = command.run;
|
|
45
|
+
command.run = (toolbox) => {
|
|
46
|
+
if ((toolbox === null || toolbox === void 0 ? void 0 : toolbox.print) && isHelpRequested(toolbox.parameters)) {
|
|
47
|
+
renderCommandHelp(toolbox.print, command, loadCommandHelp(command.file));
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return originalRun(toolbox);
|
|
51
|
+
};
|
|
52
|
+
command.__helpWrapped = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* True when the invocation asks for help (`--help` or `-h`) — but NOT for
|
|
57
|
+
* `--help-json` (handled separately by `tools.helpJson`).
|
|
58
|
+
*/
|
|
59
|
+
function isHelpRequested(parameters) {
|
|
60
|
+
const options = (parameters === null || parameters === void 0 ? void 0 : parameters.options) || {};
|
|
61
|
+
if (options['help-json'] === true || options.helpJson === true) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return options.help === true || options.h === true;
|
|
65
|
+
}
|
|
66
|
+
/** Best-effort load of a command's rich `help` export from its file. */
|
|
67
|
+
function loadCommandHelp(file) {
|
|
68
|
+
if (!file) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const mod = require(file);
|
|
73
|
+
const help = (mod && (mod.help || (mod.default && mod.default.help)));
|
|
74
|
+
return help && typeof help === 'object' ? help : undefined;
|
|
75
|
+
}
|
|
76
|
+
catch (_a) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Render human-readable help for a command to `print`. Uses the rich `help`
|
|
82
|
+
* definition when available, otherwise a useful generic fallback. Never runs
|
|
83
|
+
* the command.
|
|
84
|
+
*/
|
|
85
|
+
function renderCommandHelp(print, command, help) {
|
|
86
|
+
var _a, _b, _c;
|
|
87
|
+
const { bold, cyan, dim, yellow } = print.colors;
|
|
88
|
+
const usage = usagePath(command);
|
|
89
|
+
const description = (help === null || help === void 0 ? void 0 : help.description) || command.description || '(no description)';
|
|
90
|
+
print.info('');
|
|
91
|
+
print.info(`${bold(usage)} — ${description}`);
|
|
92
|
+
const aliases = aliasList(command, help);
|
|
93
|
+
if (aliases.length) {
|
|
94
|
+
print.info(dim(`Aliases: ${aliases.join(', ')}`));
|
|
95
|
+
}
|
|
96
|
+
if ((_a = help === null || help === void 0 ? void 0 : help.features) === null || _a === void 0 ? void 0 : _a.length) {
|
|
97
|
+
print.info('');
|
|
98
|
+
print.info(bold('What it does:'));
|
|
99
|
+
for (const feature of help.features) {
|
|
100
|
+
print.info(` • ${feature}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
print.info('');
|
|
104
|
+
print.info(bold('Usage:'));
|
|
105
|
+
print.info(` ${usage} [options]`);
|
|
106
|
+
if ((_b = help === null || help === void 0 ? void 0 : help.examples) === null || _b === void 0 ? void 0 : _b.length) {
|
|
107
|
+
print.info('');
|
|
108
|
+
print.info(bold('Examples:'));
|
|
109
|
+
for (const example of help.examples) {
|
|
110
|
+
print.info(` ${cyan(example.startsWith('lt ') ? example : `lt ${example}`)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
print.info('');
|
|
114
|
+
print.info(bold('Options:'));
|
|
115
|
+
const options = [...((help === null || help === void 0 ? void 0 : help.options) || [])];
|
|
116
|
+
for (const option of options) {
|
|
117
|
+
const meta = [];
|
|
118
|
+
if (option.required) {
|
|
119
|
+
meta.push('required');
|
|
120
|
+
}
|
|
121
|
+
if ((_c = option.values) === null || _c === void 0 ? void 0 : _c.length) {
|
|
122
|
+
meta.push(option.values.join('|'));
|
|
123
|
+
}
|
|
124
|
+
else if (option.type) {
|
|
125
|
+
meta.push(option.type);
|
|
126
|
+
}
|
|
127
|
+
if (option.default !== undefined) {
|
|
128
|
+
meta.push(`default: ${String(option.default)}`);
|
|
129
|
+
}
|
|
130
|
+
const metaText = meta.length ? dim(` (${meta.join(', ')})`) : '';
|
|
131
|
+
print.info(` ${option.flag.padEnd(28)} ${option.description}${metaText}`);
|
|
132
|
+
}
|
|
133
|
+
// Always-present global flags
|
|
134
|
+
print.info(` ${'--help, -h'.padEnd(28)} Show this help and exit (does not run the command)`);
|
|
135
|
+
print.info(` ${'--help-json'.padEnd(28)} Machine-readable help as JSON (where provided)`);
|
|
136
|
+
if (!options.some((o) => o.flag === '--noConfirm')) {
|
|
137
|
+
print.info(` ${'--noConfirm'.padEnd(28)} Skip confirmation prompts (where supported)`);
|
|
138
|
+
}
|
|
139
|
+
if (help === null || help === void 0 ? void 0 : help.configuration) {
|
|
140
|
+
print.info('');
|
|
141
|
+
print.info(bold('Configuration (lt.config.json / lt.config.yaml):'));
|
|
142
|
+
for (const line of help.configuration.split('\n')) {
|
|
143
|
+
print.info(` ${line}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!help) {
|
|
147
|
+
print.info('');
|
|
148
|
+
print.info(dim('Tip: see docs/commands.md and docs/lt.config.md for full reference.'));
|
|
149
|
+
}
|
|
150
|
+
print.info('');
|
|
151
|
+
print.info(yellow('This is help output — the command was NOT executed.'));
|
|
152
|
+
print.info('');
|
|
153
|
+
}
|
|
154
|
+
function aliasList(command, help) {
|
|
155
|
+
return (help === null || help === void 0 ? void 0 : help.aliases) || command.aliases || command.alias || [];
|
|
156
|
+
}
|
|
157
|
+
/** Resolve the user-facing invocation, e.g. `lt fullstack convert-mode`. */
|
|
158
|
+
function usagePath(command) {
|
|
159
|
+
const path = command.commandPath && command.commandPath.length ? command.commandPath : [command.name || ''];
|
|
160
|
+
return `lt ${path.filter(Boolean).join(' ')}`.trim();
|
|
161
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildIdentity = buildIdentity;
|
|
4
4
|
exports.buildTestIdentity = buildTestIdentity;
|
|
5
|
+
exports.buildTicketIdentity = buildTicketIdentity;
|
|
5
6
|
exports.projectSlug = projectSlug;
|
|
6
7
|
exports.slugify = slugify;
|
|
7
8
|
/**
|
|
@@ -85,6 +86,23 @@ function buildTestIdentity(base, suffix = '-test') {
|
|
|
85
86
|
}
|
|
86
87
|
return { root: base.root, slug, subdomains };
|
|
87
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Derive a per-TICKET identity from a base identity (used by `lt ticket` /
|
|
91
|
+
* `lt dev up --ticket`). Suffixes the slug + every subdomain hostname with the
|
|
92
|
+
* ticket id, so each ticket worktree runs on its OWN URLs / ports / Caddy block
|
|
93
|
+
* / DB — fully parallel to and isolated from every other ticket and the base
|
|
94
|
+
* dev session.
|
|
95
|
+
*
|
|
96
|
+
* svl.localhost → svl-2200.localhost
|
|
97
|
+
* api.svl.localhost → api.svl-2200.localhost
|
|
98
|
+
*
|
|
99
|
+
* Mechanically identical to {@link buildTestIdentity} (a named wrapper for
|
|
100
|
+
* readability + intent at the call sites). `id` is already a clean slug (see
|
|
101
|
+
* `deriveTicketId` in dev-ticket.ts).
|
|
102
|
+
*/
|
|
103
|
+
function buildTicketIdentity(base, id) {
|
|
104
|
+
return buildTestIdentity(base, `-${id}`);
|
|
105
|
+
}
|
|
88
106
|
/**
|
|
89
107
|
* Read the bare project name from package.json (scope stripped).
|
|
90
108
|
* Falls back to directory basename if no package.json or no `name`.
|
package/build/lib/dev-patches.js
CHANGED
|
@@ -264,7 +264,7 @@ function patchPlaywrightConfig(file) {
|
|
|
264
264
|
if (!/const SHARDED\b/.test(after) && /export default defineConfig/.test(after)) {
|
|
265
265
|
const shardConst = '// `lt dev test --shard N` saturates the CPU (N built SSR servers + N Chromium),\n' +
|
|
266
266
|
'// slowing every navigation. Relax timeouts ONLY under that load — the CLI sets\n' +
|
|
267
|
-
|
|
267
|
+
'// LT_DEV_TEST_SHARDS — so serial + CI keep their tight, fast-failing defaults.\n' +
|
|
268
268
|
"const SHARDED = Number(process.env.LT_DEV_TEST_SHARDS || '0') > 1;\n\n";
|
|
269
269
|
after = after.replace(/(export default defineConfig)/, `${shardConst}$1`);
|
|
270
270
|
count++;
|
package/build/lib/dev-project.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.apiNeedsPortPatch = apiNeedsPortPatch;
|
|
|
4
4
|
exports.appNeedsPortPatch = appNeedsPortPatch;
|
|
5
5
|
exports.deriveDbName = deriveDbName;
|
|
6
6
|
exports.deriveTestDbName = deriveTestDbName;
|
|
7
|
+
exports.deriveTicketDbName = deriveTicketDbName;
|
|
7
8
|
exports.resolveLayout = resolveLayout;
|
|
8
9
|
const fs_1 = require("fs");
|
|
9
10
|
const path_1 = require("path");
|
|
@@ -68,6 +69,19 @@ function deriveTestDbName(devDbName) {
|
|
|
68
69
|
const base = devDbName.replace(/-(local|dev)$/i, '');
|
|
69
70
|
return `${base}-test`;
|
|
70
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Derive the per-TICKET database name from the project's dev DB name, so each
|
|
74
|
+
* ticket worktree reads/writes its OWN database and tickets never collide.
|
|
75
|
+
*
|
|
76
|
+
* svl-sports-system-local + "2200" → svl-sports-system-2200
|
|
77
|
+
*
|
|
78
|
+
* The ticket's isolated `lt dev test` stack then derives its test DB from this
|
|
79
|
+
* via {@link deriveTestDbName} → `svl-sports-system-2200-test`.
|
|
80
|
+
*/
|
|
81
|
+
function deriveTicketDbName(devDbName, ticketId) {
|
|
82
|
+
const base = devDbName.replace(/-(local|dev)$/i, '');
|
|
83
|
+
return `${base}-${ticketId}`;
|
|
84
|
+
}
|
|
71
85
|
/**
|
|
72
86
|
* Resolve layout starting from `cwd`. Walks up to find a workspace if
|
|
73
87
|
* cwd is inside `projects/api/` or `projects/app/`.
|