@lenne.tech/cli 1.31.0 → 1.32.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.
@@ -124,9 +124,10 @@ const UpCommand = {
124
124
  return 'dev up: slug in use by another checkout';
125
125
  }
126
126
  }
127
- // Sanft auto-migrate sichere Operationen (ohne Code-Modifikation):
128
- // CLAUDE.md-URL-Block einfügen + .gitignore ergänzen.
129
- // Code-Patches (config.env.ts, nuxt.config.ts) bleiben explizit `lt dev init`.
127
+ // Auto-establish every prerequisite so the user never has to run `lt dev
128
+ // init` first: CLAUDE.md URL block (base only) + `.gitignore` + the code
129
+ // patches that make config.env.ts / nuxt.config.ts / playwright.config.ts
130
+ // honour the env `lt dev` injects (PORT, URLs).
130
131
  {
131
132
  // NEVER patch the git-tracked CLAUDE.md for a ticket worktree: it would
132
133
  // differ per worktree and risk committing ticket-specific URLs. The lt-dev
@@ -149,20 +150,34 @@ const UpCommand = {
149
150
  info(colors.dim('added `.lt-dev/` to .gitignore'));
150
151
  }
151
152
  }
152
- // Warnung bei Legacy-Code (hardcoded ports) kein Auto-Patch.
153
+ // Self-heal legacy hardcoded ports instead of just warning. An unmigrated
154
+ // project hardcodes `port: 3000`/`3001` and ignores the injected `PORT`, so
155
+ // it binds the framework defaults and misses Caddy → the (ticket) URLs don't
156
+ // route and collide with parallel stacks. `autoPatch` is idempotent (no-op on
157
+ // an already-env-aware config) and only ever touches config.env.ts /
158
+ // nuxt.config.ts / playwright.config.ts — never CLAUDE.md — so it is safe in a
159
+ // ticket worktree. In a worktree these are uncommitted patches; `lt ticket
160
+ // stop` recognises a pristine lt-dev patch and tears down without `--force`.
153
161
  {
154
- const legacyFiles = [];
162
+ const filesToPatch = [];
155
163
  if (layout.apiDir) {
156
- const f = (0, dev_project_1.apiNeedsPortPatch)(layout.apiDir);
157
- if (f)
158
- legacyFiles.push(f);
164
+ const apiCfg = (0, path_1.join)(layout.apiDir, 'src', 'config.env.ts');
165
+ if ((0, fs_1.existsSync)(apiCfg))
166
+ filesToPatch.push(apiCfg);
159
167
  }
160
- if (layout.appDir)
161
- legacyFiles.push(...(0, dev_project_1.appNeedsPortPatch)(layout.appDir));
162
- if (legacyFiles.length > 0) {
163
- warning('Legacy hardcoded ports detected — Caddy will proxy correctly only after running `lt dev init`:');
164
- legacyFiles.forEach((f) => info(colors.dim(` - ${f}`)));
165
- info(colors.dim(' (Continuing — env-aware files will work; legacy files may bind on 3000/3001 and miss Caddy.)'));
168
+ if (layout.appDir) {
169
+ for (const rel of ['nuxt.config.ts', 'playwright.config.ts']) {
170
+ const f = (0, path_1.join)(layout.appDir, rel);
171
+ if ((0, fs_1.existsSync)(f))
172
+ filesToPatch.push(f);
173
+ }
174
+ }
175
+ const patched = filesToPatch.map((f) => (0, dev_patches_1.autoPatch)(f)).filter((r) => r.patched);
176
+ if (patched.length > 0) {
177
+ patched.forEach((r) => success(`patched ${r.replacements}× in ${r.file} (env-aware ports for lt dev)`));
178
+ if (ticket) {
179
+ info(colors.dim(' (worktree config self-healed — auto-discarded on `lt ticket stop`, or commit it to migrate the project)'));
180
+ }
166
181
  }
167
182
  }
168
183
  // Load any existing session up-front. The "is it already running?" decision
@@ -116,13 +116,15 @@ const StopCommand = {
116
116
  }
117
117
  }
118
118
  // 3. Remove the worktree (branch is kept). Auto-force when the ONLY dirty
119
- // files are framework-generated (e.g. `nuxt dev` rewrites the tracked
120
- // `.nuxtrc` on boot), which would otherwise block the remove — but NEVER
119
+ // files are auto-discardable — framework-generated (e.g. `nuxt dev`
120
+ // rewrites the tracked `.nuxtrc` on boot) OR pristine lt-dev self-heal
121
+ // patches (config.env.ts/nuxt.config.ts/playwright.config.ts that
122
+ // `lt dev up` env-aware'd) — which would otherwise block the remove. NEVER
121
123
  // discard real source edits (those keep the non-forced remove, which
122
124
  // errors with a hint so unsaved work is never lost).
123
- const force = parameters.options.force === true || (0, dev_ticket_1.worktreeDirtyOnlyGenerated)(wt.path);
125
+ const force = parameters.options.force === true || (0, dev_ticket_1.worktreeDirtyOnlyAutoDiscardable)(wt.path);
124
126
  if (force && parameters.options.force !== true) {
125
- info(colors.dim(' (worktree had only generated files dirty, e.g. .nuxtrc — removing)'));
127
+ info(colors.dim(' (worktree had only generated / lt-dev-patched files dirty — removing)'));
126
128
  }
127
129
  try {
128
130
  (0, dev_ticket_1.worktreeRemove)(mainRepoRoot, wt.path, force);
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildIdentity = buildIdentity;
4
4
  exports.buildTestIdentity = buildTestIdentity;
5
5
  exports.buildTicketIdentity = buildTicketIdentity;
6
+ exports.isUnmodifiedTemplateName = isUnmodifiedTemplateName;
6
7
  exports.projectSlug = projectSlug;
7
8
  exports.slugify = slugify;
8
9
  /**
@@ -19,6 +20,7 @@ exports.slugify = slugify;
19
20
  * The internal port behind each subdomain is opaque — Caddy proxies
20
21
  * arbitrary local ports. Developers and Claude only ever see the URL.
21
22
  */
23
+ const child_process_1 = require("child_process");
22
24
  const fs_1 = require("fs");
23
25
  const path_1 = require("path");
24
26
  /**
@@ -104,13 +106,44 @@ function buildTicketIdentity(base, id) {
104
106
  return buildTestIdentity(base, `-${id}`);
105
107
  }
106
108
  /**
107
- * Read the bare project name from package.json (scope stripped).
108
- * Falls back to directory basename if no package.json or no `name`.
109
+ * package.json `name` values that are unchanged starter-template defaults.
110
+ *
111
+ * A project scaffolded by cloning a template (`git clone lenneTech/lt-monorepo
112
+ * my-project`) instead of running `lt fullstack init`, or one that predates the
113
+ * rename-on-init logic, keeps the template's default `name`. That value is not
114
+ * project-identifying, so {@link projectSlug} ignores it and falls back to the
115
+ * project directory name; `renameUnmodifiedTemplatePackage` (in package-name.ts)
116
+ * rewrites the field on the next `lt dev init`.
117
+ */
118
+ const UNMODIFIED_TEMPLATE_NAMES = new Set(['lt-monorepo']);
119
+ /**
120
+ * True when `name` matches a known unmodified starter-template default.
121
+ *
122
+ * Such names (e.g. `lt-monorepo`) are NOT project-identifying — see
123
+ * {@link UNMODIFIED_TEMPLATE_NAMES} — so {@link projectSlug} ignores them and
124
+ * derives the slug from the project directory instead.
125
+ */
126
+ function isUnmodifiedTemplateName(name) {
127
+ return typeof name === 'string' && UNMODIFIED_TEMPLATE_NAMES.has(name);
128
+ }
129
+ /**
130
+ * Read the bare project name from package.json (scope stripped) and slugify it.
131
+ *
132
+ * Falls back to the PROJECT directory name when there is no usable name — i.e.
133
+ * no package.json, no `name`, or an unmodified starter-template default (e.g.
134
+ * `lt-monorepo`, left over from a project scaffolded before rename-on-init
135
+ * existed). The fallback is anchored on the MAIN git worktree, so a linked
136
+ * `lt ticket` worktree (`imo-2314/`) inherits the base project name (`imo`)
137
+ * rather than slugging to its own folder — otherwise {@link buildTicketIdentity}
138
+ * would double-suffix it to `imo-2314-2314`.
109
139
  */
110
140
  function projectSlug(root) {
141
+ var _a;
111
142
  const fromPkg = readPackageName(root);
112
- const raw = fromPkg || (0, path_1.basename)(root);
113
- return slugify(raw);
143
+ if (fromPkg && !isUnmodifiedTemplateName(fromPkg)) {
144
+ return slugify(fromPkg);
145
+ }
146
+ return slugify((0, path_1.basename)((_a = mainWorktreeDir(root)) !== null && _a !== void 0 ? _a : root));
114
147
  }
115
148
  /** Lowercase, alphanumerics + dashes only, trimmed dashes. */
116
149
  function slugify(input) {
@@ -119,6 +152,28 @@ function slugify(input) {
119
152
  .replace(/[^a-z0-9]+/g, '-')
120
153
  .replace(/^-+|-+$/g, '');
121
154
  }
155
+ /**
156
+ * Best-effort directory of the MAIN git worktree containing `root`.
157
+ *
158
+ * For a linked worktree (e.g. an `lt ticket` worktree `imo-2314/`) this resolves
159
+ * to the primary checkout (`imo/`), NOT the worktree folder: `--git-common-dir`
160
+ * is the shared `.git`, identical for every worktree, and its parent is the main
161
+ * repo root. Used only by {@link projectSlug}'s fallback so every worktree
162
+ * inherits the same base project name. Returns null when `root` is outside a git
163
+ * repo or git is unavailable.
164
+ */
165
+ function mainWorktreeDir(root) {
166
+ try {
167
+ const commonDir = (0, child_process_1.execFileSync)('git', ['-C', root, 'rev-parse', '--path-format=absolute', '--git-common-dir'], {
168
+ encoding: 'utf8',
169
+ stdio: ['ignore', 'pipe', 'ignore'],
170
+ }).trim();
171
+ return commonDir ? (0, path_1.dirname)(commonDir) : null;
172
+ }
173
+ catch (_a) {
174
+ return null;
175
+ }
176
+ }
122
177
  /** Read `name` from package.json, scope-stripped (e.g. `@lenne.tech/foo` → `foo`). */
123
178
  function readPackageName(dir) {
124
179
  const pkgPath = (0, path_1.join)(dir, 'package.json');
@@ -47,6 +47,7 @@ const caddy_1 = require("./caddy");
47
47
  const dev_env_1 = require("./dev-env");
48
48
  const dev_env_bridge_1 = require("./dev-env-bridge");
49
49
  const dev_identity_1 = require("./dev-identity");
50
+ const dev_patches_1 = require("./dev-patches");
50
51
  const dev_process_1 = require("./dev-process");
51
52
  const dev_project_1 = require("./dev-project");
52
53
  const dev_state_1 = require("./dev-state");
@@ -96,6 +97,31 @@ function bringUpTestSession(layout_1, baseIdentity_1, log_1) {
96
97
  const { dbName, testIdentity } = resolveTestSession(layout, baseIdentity, shardIndex, devDbName);
97
98
  // Always start from a clean slate — reclaim a stale/crashed test session.
98
99
  yield tearDownTestSession(layout, baseIdentity, log, { shardIndex, silent: true });
100
+ // Self-heal legacy hardcoded ports BEFORE the build below — the test API runs
101
+ // COMPILED (`node dist/...`), so config.env.ts must already honour the injected
102
+ // `PORT` or the compiled bundle binds 3000 and misses Caddy. Belt-and-suspenders
103
+ // for a project that never ran `lt dev up` first; idempotent and only ever
104
+ // touches the three configs (never CLAUDE.md), so it is ticket-safe. Mirrors the
105
+ // self-heal in `lt dev up` (see commands/dev/up.ts).
106
+ {
107
+ const filesToPatch = [];
108
+ if (layout.apiDir) {
109
+ const apiCfg = (0, path_1.join)(layout.apiDir, 'src', 'config.env.ts');
110
+ if ((0, fs_1.existsSync)(apiCfg))
111
+ filesToPatch.push(apiCfg);
112
+ }
113
+ if (layout.appDir) {
114
+ for (const rel of ['nuxt.config.ts', 'playwright.config.ts']) {
115
+ const f = (0, path_1.join)(layout.appDir, rel);
116
+ if ((0, fs_1.existsSync)(f))
117
+ filesToPatch.push(f);
118
+ }
119
+ }
120
+ for (const r of filesToPatch.map((f) => (0, dev_patches_1.autoPatch)(f))) {
121
+ if (r.patched)
122
+ log.info(`patched ${r.replacements}× in ${r.file} (env-aware ports for lt dev test)`);
123
+ }
124
+ }
99
125
  // Allocate internal ports AND reserve them in the registry ATOMICALLY, under a
100
126
  // cross-process lock — so two parallel `lt dev test` runs (different ticket
101
127
  // worktrees) can never read the registry, pick the same free ports, and both
@@ -13,6 +13,7 @@ exports.pnpmInstall = pnpmInstall;
13
13
  exports.readTicketMarker = readTicketMarker;
14
14
  exports.resolveDevIdentity = resolveDevIdentity;
15
15
  exports.worktreeAdd = worktreeAdd;
16
+ exports.worktreeDirtyOnlyAutoDiscardable = worktreeDirtyOnlyAutoDiscardable;
16
17
  exports.worktreeDirtyOnlyGenerated = worktreeDirtyOnlyGenerated;
17
18
  exports.worktreePathFor = worktreePathFor;
18
19
  exports.worktreeRemove = worktreeRemove;
@@ -39,8 +40,10 @@ exports.writeTicketMarker = writeTicketMarker;
39
40
  */
40
41
  const child_process_1 = require("child_process");
41
42
  const fs_1 = require("fs");
43
+ const os_1 = require("os");
42
44
  const path_1 = require("path");
43
45
  const dev_identity_1 = require("./dev-identity");
46
+ const dev_patches_1 = require("./dev-patches");
44
47
  const dev_project_1 = require("./dev-project");
45
48
  const dev_state_1 = require("./dev-state");
46
49
  /** Marker file (under `.lt-dev/`) that tags a worktree with its ticket id. */
@@ -261,6 +264,19 @@ function worktreeAdd(repoDir, worktreePath, branch, baseRef) {
261
264
  }
262
265
  /** Framework-generated / ephemeral paths a dev/build run dirties (never real work). */
263
266
  const GENERATED_PATHS = /(^|\/)(\.nuxtrc|\.nuxt|\.nitro|\.output|dist|\.turbo|\.cache|\.eslintcache)(\/|$)|\.tsbuildinfo$/;
267
+ /** The three git-tracked configs `lt dev up` self-heals to be env-aware. */
268
+ const LT_DEV_MANAGED_CONFIG = /(?:^|\/)(?:config\.env\.ts|nuxt\.config\.ts|playwright\.config\.ts)$/;
269
+ /**
270
+ * True when a worktree has uncommitted changes AND every one is auto-discardable
271
+ * (framework-generated OR a pristine lt-dev self-heal patch). Lets `lt ticket
272
+ * stop` force-remove a provisioning-only worktree — e.g. an unmigrated project
273
+ * whose configs `lt dev up` env-aware'd — without `--force` and without ever
274
+ * discarding real developer work.
275
+ */
276
+ function worktreeDirtyOnlyAutoDiscardable(worktreePath) {
277
+ const { autoDiscardable, realDirty } = classifyWorktreeDirt(worktreePath);
278
+ return realDirty.length === 0 && autoDiscardable.length > 0;
279
+ }
264
280
  /**
265
281
  * True ONLY when a worktree has uncommitted changes AND every one is a
266
282
  * framework-generated / ephemeral file (`.nuxtrc`, `.nuxt`, `.output`, …).
@@ -268,14 +284,7 @@ const GENERATED_PATHS = /(^|\/)(\.nuxtrc|\.nuxt|\.nitro|\.output|dist|\.turbo|\.
268
284
  * `git worktree remove`; this lets `lt ticket stop` auto-clean those safely.
269
285
  */
270
286
  function worktreeDirtyOnlyGenerated(worktreePath) {
271
- let out = '';
272
- try {
273
- out = git(worktreePath, ['status', '--porcelain', '--untracked-files=all']);
274
- }
275
- catch (_a) {
276
- return false;
277
- }
278
- const lines = out.split(/\r?\n/).filter((l) => l.trim());
287
+ const lines = gitStatusPorcelain(worktreePath);
279
288
  if (lines.length === 0)
280
289
  return false; // clean → no force needed
281
290
  return lines.every((line) => GENERATED_PATHS.test(porcelainPath(line)));
@@ -301,25 +310,19 @@ function worktreeRemove(repoDir, worktreePath, force = false) {
301
310
  * the branch not on any remote (the branch is kept on stop, but local-only).
302
311
  */
303
312
  function worktreeSafetyReport(worktreePath) {
304
- let status = '';
305
- try {
306
- status = git(worktreePath, ['status', '--porcelain', '--untracked-files=all']);
307
- }
308
- catch (_a) {
309
- return { dirtySource: [], unpushed: 0 };
310
- }
311
- const dirtySource = status
312
- .split(/\r?\n/)
313
- .filter((l) => l.trim())
314
- .filter((l) => !GENERATED_PATHS.test(porcelainPath(l)));
313
+ // Only REAL developer work counts as dirtySource — framework-generated files
314
+ // AND pristine lt-dev self-heal patches (config.env.ts/nuxt.config.ts/
315
+ // playwright.config.ts that `lt dev up` env-aware'd) are auto-discardable, so
316
+ // `lt ticket stop` never refuses over them.
317
+ const { realDirty } = classifyWorktreeDirt(worktreePath);
315
318
  let unpushed = 0;
316
319
  try {
317
320
  unpushed = Number(git(worktreePath, ['rev-list', '--count', 'HEAD', '--not', '--remotes'])) || 0;
318
321
  }
319
- catch (_b) {
322
+ catch (_a) {
320
323
  /* no remotes / detached HEAD → cannot determine; treat as 0 */
321
324
  }
322
- return { dirtySource, unpushed };
325
+ return { dirtySource: realDirty, unpushed };
323
326
  }
324
327
  /** Write the ticket marker that tags a worktree (created by `lt ticket start`). */
325
328
  function writeTicketMarker(root, id) {
@@ -327,6 +330,24 @@ function writeTicketMarker(root, id) {
327
330
  (0, fs_1.mkdirSync)(dir, { recursive: true });
328
331
  (0, fs_1.writeFileSync)((0, path_1.join)(dir, TICKET_MARKER), `${id}\n`, 'utf8');
329
332
  }
333
+ /**
334
+ * Split a worktree's uncommitted changes into "auto-discardable" (framework-
335
+ * generated files + pristine lt-dev self-heal patches) and "real" developer work.
336
+ * `lt ticket stop` may force-remove a worktree whose changes are ALL
337
+ * auto-discardable; anything in `realDirty` blocks removal (work could be lost).
338
+ */
339
+ function classifyWorktreeDirt(worktreePath) {
340
+ const autoDiscardable = [];
341
+ const realDirty = [];
342
+ for (const line of gitStatusPorcelain(worktreePath)) {
343
+ const p = porcelainPath(line);
344
+ if (GENERATED_PATHS.test(p) || isPristineLtDevPatch(worktreePath, p))
345
+ autoDiscardable.push(p);
346
+ else
347
+ realDirty.push(p);
348
+ }
349
+ return { autoDiscardable, realDirty };
350
+ }
330
351
  function finalizeWorktree(partial) {
331
352
  var _a, _b;
332
353
  const path = (_a = partial.path) !== null && _a !== void 0 ? _a : '';
@@ -336,6 +357,73 @@ function finalizeWorktree(partial) {
336
357
  function git(cwd, args) {
337
358
  return (0, child_process_1.execFileSync)('git', args, { cwd, encoding: 'utf8' }).trim();
338
359
  }
360
+ /**
361
+ * `git status --porcelain` lines, each kept VERBATIM (not trimmed).
362
+ *
363
+ * Crucial: a tracked-but-modified file's porcelain prefix begins with a space
364
+ * (` M path`), so trimming the blob (as the generic `git()` helper does) would
365
+ * eat that leading space and shift `porcelainPath`'s `slice(3)` by one,
366
+ * corrupting the path (`projects/…` → `rojects/…`). Returns [] on error.
367
+ */
368
+ function gitStatusPorcelain(cwd) {
369
+ let out = '';
370
+ try {
371
+ out = (0, child_process_1.execFileSync)('git', ['-C', cwd, 'status', '--porcelain', '--untracked-files=all'], { encoding: 'utf8' });
372
+ }
373
+ catch (_a) {
374
+ return [];
375
+ }
376
+ return out.split(/\r?\n/).filter((l) => l.trim() !== '');
377
+ }
378
+ /**
379
+ * True when the dirty tracked file at `relPath` (relative to the worktree root)
380
+ * differs from its committed (HEAD) version by EXACTLY the lt-dev self-heal
381
+ * patch — i.e. `lt dev up` env-aware'd a legacy config and the developer made no
382
+ * other edit. Verified by re-deriving: apply the same `autoPatch` to the HEAD
383
+ * blob and compare to the working-tree content. Any extra developer edit makes
384
+ * the two differ → treated as real work (never auto-discarded).
385
+ *
386
+ * Only the three lt-dev-managed configs qualify; everything else returns false.
387
+ */
388
+ function isPristineLtDevPatch(worktreePath, relPath) {
389
+ if (!LT_DEV_MANAGED_CONFIG.test(relPath))
390
+ return false;
391
+ let head;
392
+ try {
393
+ // NOT the trimming `git()` helper — the trailing newline must survive so the
394
+ // comparison against the (untrimmed) working-tree content is exact.
395
+ head = (0, child_process_1.execFileSync)('git', ['-C', worktreePath, 'show', `HEAD:${relPath}`], { encoding: 'utf8' });
396
+ }
397
+ catch (_a) {
398
+ return false; // not tracked at HEAD (e.g. a brand-new file) → never auto-discard
399
+ }
400
+ let current;
401
+ try {
402
+ current = (0, fs_1.readFileSync)((0, path_1.join)(worktreePath, relPath), 'utf8');
403
+ }
404
+ catch (_b) {
405
+ return false;
406
+ }
407
+ // Re-derive what `lt dev up`'s autoPatch produced from the HEAD blob. autoPatch
408
+ // dispatches by filename suffix, so the temp file must keep the basename.
409
+ const tmp = (0, path_1.join)((0, os_1.tmpdir)(), `lt-dev-verify-${process.pid}-${(0, path_1.basename)(relPath)}`);
410
+ try {
411
+ (0, fs_1.writeFileSync)(tmp, head, 'utf8');
412
+ (0, dev_patches_1.autoPatch)(tmp);
413
+ return (0, fs_1.readFileSync)(tmp, 'utf8') === current;
414
+ }
415
+ catch (_c) {
416
+ return false;
417
+ }
418
+ finally {
419
+ try {
420
+ (0, fs_1.unlinkSync)(tmp);
421
+ }
422
+ catch (_d) {
423
+ /* best-effort cleanup */
424
+ }
425
+ }
426
+ }
339
427
  /** Path from a `git status --porcelain` line ("XY <path>" / "XY <old> -> <new>"). */
340
428
  function porcelainPath(line) {
341
429
  var _a;
@@ -1,29 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isUnmodifiedTemplateName = isUnmodifiedTemplateName;
3
+ exports.isUnmodifiedTemplateName = void 0;
4
4
  exports.renameUnmodifiedTemplatePackage = renameUnmodifiedTemplatePackage;
5
5
  exports.setPackageName = setPackageName;
6
6
  const path_1 = require("path");
7
7
  const dev_identity_1 = require("./dev-identity");
8
- /**
9
- * package.json `name` values that are unchanged starter-template defaults.
10
- *
11
- * When a user clones a template manually (`git clone lenneTech/lt-monorepo
12
- * my-project`) instead of running `lt fullstack init`, the `name` field
13
- * stays at the template's default. That field is what
14
- * `dev-identity#projectSlug` reads to derive `<slug>.localhost`, so every
15
- * cloned project would collide on `https://lt-monorepo.localhost`.
16
- *
17
- * `lt fullstack init` rewrites this field already (see `setPackageName`);
18
- * the detection here is the safety net for projects that bypassed init.
19
- */
20
- const UNMODIFIED_TEMPLATE_NAMES = new Set(['lt-monorepo']);
21
- /**
22
- * True when `name` matches a known unmodified starter template default.
23
- */
24
- function isUnmodifiedTemplateName(name) {
25
- return typeof name === 'string' && UNMODIFIED_TEMPLATE_NAMES.has(name);
26
- }
8
+ // `isUnmodifiedTemplateName` and the template-name detection now live in
9
+ // `dev-identity.ts` (the slug owner — `projectSlug` needs the same check to
10
+ // ignore an unmodified `lt-monorepo` name). Re-exported here so existing
11
+ // importers / tests of the package-name surface keep working unchanged.
12
+ var dev_identity_2 = require("./dev-identity");
13
+ Object.defineProperty(exports, "isUnmodifiedTemplateName", { enumerable: true, get: function () { return dev_identity_2.isUnmodifiedTemplateName; } });
27
14
  /**
28
15
  * If the package.json at `<projectRoot>/package.json` still carries an
29
16
  * unmodified starter-template `name` (e.g. `lt-monorepo` from a raw
@@ -47,7 +34,7 @@ function renameUnmodifiedTemplatePackage(options) {
47
34
  if (!pkg || typeof pkg !== 'object' || Array.isArray(pkg))
48
35
  return null;
49
36
  const currentName = typeof pkg.name === 'string' ? pkg.name : null;
50
- if (!isUnmodifiedTemplateName(currentName))
37
+ if (!(0, dev_identity_1.isUnmodifiedTemplateName)(currentName))
51
38
  return null;
52
39
  // Slugify the directory basename: npm names must be lowercase and
53
40
  // URL-safe, and this keeps the rewritten value consistent with what
@@ -56,7 +43,7 @@ function renameUnmodifiedTemplatePackage(options) {
56
43
  // back). Anything else would produce a slug mismatch between
57
44
  // package.json and `<slug>.localhost`.
58
45
  const derived = (0, dev_identity_1.slugify)((0, path_1.basename)(projectRoot));
59
- if (!derived || isUnmodifiedTemplateName(derived))
46
+ if (!derived || (0, dev_identity_1.isUnmodifiedTemplateName)(derived))
60
47
  return null;
61
48
  const written = setPackageName({ filesystem, name: derived, packageJsonPath });
62
49
  return written ? derived : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/cli",
3
- "version": "1.31.0",
3
+ "version": "1.32.0",
4
4
  "description": "lenne.Tech CLI: lt",
5
5
  "keywords": [
6
6
  "lenne.Tech",
@@ -20,7 +20,8 @@
20
20
  "scripts": {
21
21
  "c": "npm run check",
22
22
  "cf": "npm run check:fix",
23
- "check": "npm install && npm run format && npm run build && npm run check:start",
23
+ "check": "bash scripts/check.sh",
24
+ "check:audit": "bash scripts/check.sh --audit-only",
24
25
  "check:fix": "npm install && npm audit fix && npm run format && npm run lint:fix && npm run build && npm run check:start",
25
26
  "check:start": "bash scripts/check-cli-start.sh",
26
27
  "postinstall": "node bin/postinstall.js 2>/dev/null || true",
@@ -68,7 +69,7 @@
68
69
  "glob": "13.0.6",
69
70
  "gluegun": "5.2.2",
70
71
  "js-sha256": "0.11.1",
71
- "js-yaml": "4.1.1",
72
+ "js-yaml": "4.2.0",
72
73
  "jsdom": "29.1.1",
73
74
  "lodash": "4.18.1",
74
75
  "open": "11.0.0",
@@ -100,10 +101,16 @@
100
101
  },
101
102
  "//overrides": {
102
103
  "brace-expansion@5.0.2 - 5.0.5": "Security fix: GHSA-jxxr-4gwj-5jf2 (large numeric range defeats max DoS protection) in brace-expansion 5.0.2-5.0.5 - transitive via minimatch under glob, @ts-morph/common, @typescript-eslint/typescript-estree. Remove once those parents resolve minimatch to a brace-expansion >=5.0.6.",
103
- "semver@*": "Force latest semver 7.x across all sub-deps; gluegun@5.2.2 pins semver@7.7.0 which is stale - remove once gluegun updates its dep."
104
+ "semver@*": "Force latest semver 7.x across all sub-deps; gluegun@5.2.2 pins semver@7.7.0 which is stale - remove once gluegun updates its dep.",
105
+ "js-yaml": "Security fix: GHSA-h67p-54hq-rp68 (quadratic-complexity DoS in merge-key handling) in js-yaml <=4.1.1. Forces 4.2.0 everywhere, incl. the 3.14.2 pulled transitively by @istanbuljs/load-nyc-config under babel-plugin-istanbul (jest coverage); that loader only runs js-yaml when reading a YAML nyc config, which this project does not use. Remove once the jest toolchain resolves js-yaml >=4.2.0 itself.",
106
+ "form-data": "Security fix: GHSA-hmw2-7cc7-3qxx (CRLF injection via unescaped multipart field names) in form-data 4.0.0-4.0.5 - transitive via axios. Remove once axios pins form-data >=4.0.6.",
107
+ "@babel/core": "Security fix: GHSA-4x5r-pxfx-6jf8 (arbitrary file read via sourceMappingURL) in @babel/core <=7.29.0 - transitive via the jest toolchain. Remove once jest resolves @babel/core >=7.29.6."
104
108
  },
105
109
  "overrides": {
110
+ "@babel/core": "7.29.7",
106
111
  "brace-expansion@5.0.2 - 5.0.5": "5.0.6",
112
+ "form-data": "4.0.6",
113
+ "js-yaml": "4.2.0",
107
114
  "semver@*": "7.8.1"
108
115
  },
109
116
  "jest": {