@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.
Files changed (40) hide show
  1. package/build/cli.js +7 -1
  2. package/build/commands/dev/doctor.js +27 -1
  3. package/build/commands/dev/down.js +22 -10
  4. package/build/commands/dev/status.js +4 -3
  5. package/build/commands/dev/test.js +12 -4
  6. package/build/commands/dev/up.js +90 -50
  7. package/build/commands/frontend/angular.js +48 -46
  8. package/build/commands/frontend/convert-mode.js +41 -39
  9. package/build/commands/frontend/nuxt.js +49 -47
  10. package/build/commands/fullstack/add-api.js +34 -32
  11. package/build/commands/fullstack/add-app.js +25 -23
  12. package/build/commands/fullstack/convert-mode.js +85 -65
  13. package/build/commands/fullstack/init.js +12 -0
  14. package/build/commands/fullstack/update.js +24 -0
  15. package/build/commands/server/add-property.js +42 -40
  16. package/build/commands/server/convert-mode.js +41 -39
  17. package/build/commands/server/create.js +65 -63
  18. package/build/commands/server/module.js +56 -54
  19. package/build/commands/server/object.js +42 -40
  20. package/build/commands/server/permissions.js +60 -58
  21. package/build/commands/ticket/list.js +78 -0
  22. package/build/commands/ticket/start.js +141 -0
  23. package/build/commands/ticket/stop.js +166 -0
  24. package/build/commands/ticket/switch.js +70 -0
  25. package/build/commands/ticket/test.js +80 -0
  26. package/build/commands/ticket/ticket.js +36 -0
  27. package/build/commands/tools/crawl.js +92 -90
  28. package/build/extensions/frontend-helper.js +8 -37
  29. package/build/extensions/server.js +8 -38
  30. package/build/lib/command-help.js +161 -0
  31. package/build/lib/dev-identity.js +18 -0
  32. package/build/lib/dev-patches.js +1 -1
  33. package/build/lib/dev-project.js +14 -0
  34. package/build/lib/dev-state.js +96 -0
  35. package/build/lib/dev-test-session.js +55 -35
  36. package/build/lib/dev-ticket.js +343 -0
  37. package/build/lib/vendor-claude-md.js +227 -0
  38. package/docs/lt-dev-ticket-workflow.html +603 -0
  39. package/docs/lt-dev-ticket-workflow.pdf +0 -0
  40. package/package.json +32 -1
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ /**
3
+ * Single source of truth for the "Vendor-Mode Notice" blocks that the CLI
4
+ * writes into project CLAUDE.md files.
5
+ *
6
+ * Why this exists: when a project runs in vendor mode, Claude Code (and humans)
7
+ * must know — from the project itself, without the lt-dev plugin installed —
8
+ * that the framework lives in a vendored `core/` tree and which command syncs
9
+ * it (`update`) vs. ports local fixes back (`contribute`). The plugin hooks are
10
+ * a proactive safety net, but they only fire when the plugin is installed; the
11
+ * CLAUDE.md block is the plugin-independent channel.
12
+ *
13
+ * Three blocks are generated:
14
+ * - Backend → `projects/api/CLAUDE.md` (marker {@link BACKEND_VENDOR_MARKER})
15
+ * - Frontend → `projects/app/CLAUDE.md` (marker {@link FRONTEND_VENDOR_MARKER})
16
+ * - Root → workspace `CLAUDE.md` (marker {@link ROOT_VENDOR_MARKER})
17
+ *
18
+ * The root block is new: Claude often reads the monorepo root CLAUDE.md first,
19
+ * so it needs a short pointer to the per-subproject vendor docs.
20
+ *
21
+ * Each block starts with its HTML marker comment and ends with a `---`
22
+ * horizontal rule, so {@link upsertVendorBlock} / {@link removeVendorBlock} can
23
+ * find and replace exactly the generated region without touching hand-written
24
+ * content below it.
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.ROOT_VENDOR_MARKER = exports.FRONTEND_VENDOR_MARKER = exports.BACKEND_VENDOR_MARKER = void 0;
28
+ exports.buildBackendVendorBlock = buildBackendVendorBlock;
29
+ exports.buildFrontendVendorBlock = buildFrontendVendorBlock;
30
+ exports.buildRootVendorBlock = buildRootVendorBlock;
31
+ exports.hasVendorBlock = hasVendorBlock;
32
+ exports.healVendorClaudeMd = healVendorClaudeMd;
33
+ exports.insertVendorBlockIfMissing = insertVendorBlockIfMissing;
34
+ exports.removeVendorBlock = removeVendorBlock;
35
+ exports.upsertVendorBlock = upsertVendorBlock;
36
+ exports.BACKEND_VENDOR_MARKER = '<!-- lt-vendor-marker -->';
37
+ exports.FRONTEND_VENDOR_MARKER = '<!-- lt-vendor-marker-frontend -->';
38
+ exports.ROOT_VENDOR_MARKER = '<!-- lt-vendor-marker-root -->';
39
+ /**
40
+ * Vendor-mode notice for the backend api project (`projects/api/CLAUDE.md`).
41
+ */
42
+ function buildBackendVendorBlock() {
43
+ return block([
44
+ exports.BACKEND_VENDOR_MARKER,
45
+ '',
46
+ '# Vendor-Mode Notice',
47
+ '',
48
+ 'This api project runs in **vendor mode**: the `@lenne.tech/nest-server`',
49
+ 'core/ tree has been copied directly into `src/core/` as first-class',
50
+ 'project code. There is **no** `@lenne.tech/nest-server` npm dependency.',
51
+ '',
52
+ '- **Read framework code from `src/core/**`** — not from `node_modules/`.',
53
+ '- **Generated imports use relative paths** to `src/core`, e.g.',
54
+ " `import { CrudService } from '../../../core';`",
55
+ ' The exact depth depends on the file location. `lt server module`',
56
+ ' computes it automatically.',
57
+ '- **Baseline + patch log** live in `src/core/VENDOR.md`. Log any',
58
+ ' substantial local change there so the `nest-server-core-updater`',
59
+ ' agent can classify it at sync time.',
60
+ '- **Update flow:** run `/lt-dev:backend:update-nest-server-core` (the',
61
+ ' agent clones upstream, computes a delta, and presents a review). The',
62
+ ' update also raises npm packages to at least the upstream baseline',
63
+ ' (via `/lt-dev:maintenance:maintain`).',
64
+ '- **Contribute back:** run `/lt-dev:backend:contribute-nest-server-core`',
65
+ ' to propose local fixes as upstream PRs.',
66
+ '- **Freshness check:** `pnpm run check:vendor-freshness` warns (non-',
67
+ ' blockingly) when upstream has a newer release than the baseline.',
68
+ ]);
69
+ }
70
+ /**
71
+ * Vendor-mode notice for the frontend app project (`projects/app/CLAUDE.md`).
72
+ */
73
+ function buildFrontendVendorBlock() {
74
+ return block([
75
+ exports.FRONTEND_VENDOR_MARKER,
76
+ '',
77
+ '# Vendor-Mode Notice (Frontend)',
78
+ '',
79
+ 'This frontend project runs in **vendor mode**: the `@lenne.tech/nuxt-extensions`',
80
+ 'module has been copied directly into `app/core/` as first-class',
81
+ 'project code. There is **no** `@lenne.tech/nuxt-extensions` npm dependency.',
82
+ '',
83
+ '- **Read framework code from `app/core/**`** — not from `node_modules/`.',
84
+ "- **nuxt.config.ts** references `'./app/core/module'` instead of",
85
+ " `'@lenne.tech/nuxt-extensions'`.",
86
+ '- **Baseline + patch log** live in `app/core/VENDOR.md`. Log any',
87
+ ' substantial local change there so the `nuxt-extensions-core-updater`',
88
+ ' agent can classify it at sync time.',
89
+ '- **Update flow:** run `/lt-dev:frontend:update-nuxt-extensions-core`. The',
90
+ ' update also raises npm packages to at least the upstream baseline',
91
+ ' (via `/lt-dev:maintenance:maintain`).',
92
+ '- **Contribute back:** run `/lt-dev:frontend:contribute-nuxt-extensions-core`.',
93
+ '- **Freshness check:** `pnpm run check:vendor-freshness` warns when',
94
+ ' upstream has a newer release than the baseline.',
95
+ ]);
96
+ }
97
+ /**
98
+ * Vendor-mode notice for the monorepo root (`<workspace>/CLAUDE.md`).
99
+ *
100
+ * Tailored to which halves are vendored so Claude sees only the relevant
101
+ * commands. At least one of `backend` / `frontend` must be true; otherwise the
102
+ * caller should {@link removeVendorBlock} instead of writing an empty notice.
103
+ */
104
+ function buildRootVendorBlock(opts) {
105
+ const { backend, frontend } = opts;
106
+ const lines = [
107
+ exports.ROOT_VENDOR_MARKER,
108
+ '',
109
+ '# Vendor-Mode Notice (Monorepo)',
110
+ '',
111
+ 'This workspace runs at least one framework in **vendor mode** — the',
112
+ 'framework source is vendored directly into the project tree instead of',
113
+ 'being an npm dependency. Read framework code from the vendored `core/`',
114
+ 'trees, not from `node_modules/`.',
115
+ '',
116
+ '**Vendored frameworks:**',
117
+ ];
118
+ if (backend) {
119
+ lines.push('- **Backend** (`@lenne.tech/nest-server`): `projects/api/src/core/` —', ' details in `projects/api/CLAUDE.md` and `projects/api/src/core/VENDOR.md`.');
120
+ }
121
+ if (frontend) {
122
+ lines.push('- **Frontend** (`@lenne.tech/nuxt-extensions`): `projects/app/app/core/` —', ' details in `projects/app/CLAUDE.md` and `projects/app/app/core/VENDOR.md`.');
123
+ }
124
+ lines.push('', '**Update** (sync from upstream; also raises npm packages to at least the', 'upstream baseline via `/lt-dev:maintenance:maintain`):');
125
+ if (backend) {
126
+ lines.push('- Backend: `/lt-dev:backend:update-nest-server-core`');
127
+ }
128
+ if (frontend) {
129
+ lines.push('- Frontend: `/lt-dev:frontend:update-nuxt-extensions-core`');
130
+ }
131
+ lines.push('', '**Contribute back** generally-useful core fixes as upstream PRs:');
132
+ if (backend) {
133
+ lines.push('- Backend: `/lt-dev:backend:contribute-nest-server-core`');
134
+ }
135
+ if (frontend) {
136
+ lines.push('- Frontend: `/lt-dev:frontend:contribute-nuxt-extensions-core`');
137
+ }
138
+ lines.push('', 'Project-specific code never goes into a `core/` tree — see each', "subproject's VENDOR.md Modification Policy.");
139
+ return block(lines);
140
+ }
141
+ /** True when `content` already contains the given vendor marker. */
142
+ function hasVendorBlock(content, marker) {
143
+ return content.includes(marker);
144
+ }
145
+ /**
146
+ * Bring every CLAUDE.md in a workspace in line with its current vendor state:
147
+ * upsert the matching notice block where a framework is vendored, remove it
148
+ * where it is not. Idempotent — running it on an already-correct workspace
149
+ * changes nothing.
150
+ *
151
+ * This is what makes `lt fullstack update` able to *heal* pre-existing or
152
+ * drifted vendor projects (e.g. ones scaffolded before the root notice existed,
153
+ * or whose notice fell out of date).
154
+ *
155
+ * @returns the list of CLAUDE.md paths that were actually modified.
156
+ */
157
+ function healVendorClaudeMd(fs, state) {
158
+ const changed = [];
159
+ const apply = (path, marker, desiredBlock) => {
160
+ if (!fs.exists(path)) {
161
+ return;
162
+ }
163
+ const content = fs.read(path) || '';
164
+ const next = desiredBlock ? upsertVendorBlock(content, marker, desiredBlock) : removeVendorBlock(content, marker);
165
+ if (next !== content) {
166
+ fs.write(path, next);
167
+ changed.push(path);
168
+ }
169
+ };
170
+ if (state.apiDir) {
171
+ apply(joinPath(state.apiDir, 'CLAUDE.md'), exports.BACKEND_VENDOR_MARKER, state.backendVendor ? buildBackendVendorBlock() : null);
172
+ }
173
+ if (state.appDir) {
174
+ apply(joinPath(state.appDir, 'CLAUDE.md'), exports.FRONTEND_VENDOR_MARKER, state.frontendVendor ? buildFrontendVendorBlock() : null);
175
+ }
176
+ if (state.workspaceRoot) {
177
+ const anyVendor = state.backendVendor || state.frontendVendor;
178
+ apply(joinPath(state.workspaceRoot, 'CLAUDE.md'), exports.ROOT_VENDOR_MARKER, anyVendor ? buildRootVendorBlock({ backend: state.backendVendor, frontend: state.frontendVendor }) : null);
179
+ }
180
+ return changed;
181
+ }
182
+ /**
183
+ * Insert the block at the very top of the file **only when it is missing**.
184
+ * Used during conversion so a hand-customized existing block is never clobbered.
185
+ */
186
+ function insertVendorBlockIfMissing(content, marker, newBlock) {
187
+ if (content.includes(marker)) {
188
+ return content;
189
+ }
190
+ return newBlock + content;
191
+ }
192
+ /**
193
+ * Remove the generated block (marker through the first `---`) and trim leading
194
+ * blank lines. Used when converting a project back to npm mode.
195
+ */
196
+ function removeVendorBlock(content, marker) {
197
+ if (!content.includes(marker)) {
198
+ return content;
199
+ }
200
+ return content.replace(blockRegex(marker), '').replace(/^\n+/, '');
201
+ }
202
+ /**
203
+ * Insert the block if missing, or replace the existing generated region with the
204
+ * current canonical block (idempotent self-heal). Used by `lt fullstack update`
205
+ * to bring pre-existing / drifted projects up to the current notice.
206
+ */
207
+ function upsertVendorBlock(content, marker, newBlock) {
208
+ if (!content.includes(marker)) {
209
+ return newBlock + content;
210
+ }
211
+ return content.replace(blockRegex(marker), newBlock);
212
+ }
213
+ /** Join block lines into the canonical `marker … --- ` shape (ends with `---\n`). */
214
+ function block(lines) {
215
+ return [...lines, '', '---', ''].join('\n');
216
+ }
217
+ /** Build the regex that matches an existing block from its marker to the first `---`. */
218
+ function blockRegex(marker) {
219
+ return new RegExp(`${escapeRegExp(marker)}[\\s\\S]*?---\\s*\\n?`);
220
+ }
221
+ /** Escape a string for safe use inside a RegExp. */
222
+ function escapeRegExp(value) {
223
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
224
+ }
225
+ function joinPath(dir, file) {
226
+ return dir.endsWith('/') ? `${dir}${file}` : `${dir}/${file}`;
227
+ }