navori 0.2.7 → 0.2.8

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/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { fileURLToPath as fileURLToPath2 } from "url";
8
8
 
9
9
  // src/commands/init.ts
10
10
  import { defineCommand as defineCommand2 } from "citty";
11
- import * as p2 from "@clack/prompts";
11
+ import * as p3 from "@clack/prompts";
12
12
  import { resolve as resolve11, join as join12, dirname as dirname7, relative as relative3 } from "path";
13
13
  import { existsSync as existsSync16, mkdirSync as mkdirSync6, chmodSync as chmodSync2 } from "fs";
14
14
  import { spawnSync as spawnSync2 } from "child_process";
@@ -98,6 +98,22 @@ var safeRelPath = z.string().min(1).refine(
98
98
  // src/lib/schema.ts
99
99
  var ENGINES = ["claude", "agents-md", "cursor", "copilot"];
100
100
  var MODELS = ["opus", "sonnet", "haiku"];
101
+ var COMMITS = ["conventional", "conventional-es", "free"];
102
+ var LANGUAGES = ["es", "en"];
103
+ function tolerantEnumArray(values, fallback) {
104
+ return z2.preprocess((val) => {
105
+ if (!Array.isArray(val)) return val;
106
+ const known = val.filter((v) => values.includes(v));
107
+ if (known.length === 0 && val.length > 0) return [fallback];
108
+ return known;
109
+ }, z2.array(z2.enum(values)).min(1));
110
+ }
111
+ function tolerantEnum(values, fallback) {
112
+ return z2.preprocess(
113
+ (val) => val === void 0 || values.includes(val) ? val : fallback,
114
+ z2.enum(values).default(fallback)
115
+ );
116
+ }
101
117
  var QualityGateSchema = z2.object({
102
118
  fast: z2.string().min(1),
103
119
  full: z2.string().min(1)
@@ -193,9 +209,9 @@ var NavoriConfigSchema = z2.object({
193
209
  name: z2.string().regex(/^[a-z0-9][a-z0-9-]*$/, "name must be kebab-case"),
194
210
  version: z2.string().default("1.0.0"),
195
211
  workspace: z2.string().optional(),
196
- engines: z2.array(z2.enum(ENGINES)).min(1),
212
+ engines: tolerantEnumArray(ENGINES, "claude"),
197
213
  preset: z2.string().min(1),
198
- language: z2.enum(["es", "en"]).default("es"),
214
+ language: tolerantEnum(LANGUAGES, "es"),
199
215
  branchBase: z2.string().default("main"),
200
216
  /** Target branch for PRs (`gh pr create --base`). When omitted, PRs target
201
217
  * branchBase. Decouples the fork point / protected branch (branchBase) from
@@ -203,7 +219,7 @@ var NavoriConfigSchema = z2.object({
203
219
  * The render derives the effective value (prTarget ?? branchBase) so this
204
220
  * stays out of configs that don't need it. */
205
221
  prTarget: z2.string().optional(),
206
- commits: z2.enum(["conventional", "conventional-es", "free"]).default("conventional-es"),
222
+ commits: tolerantEnum(COMMITS, "conventional-es"),
207
223
  qualityGate: QualityGateSchema.optional(),
208
224
  sdd: SddSchema.optional(),
209
225
  harness: HarnessSchema.optional(),
@@ -260,11 +276,34 @@ function readConfig(path) {
260
276
  result.error.issues
261
277
  );
262
278
  }
279
+ warnDroppedEnums(parsed, result.data);
263
280
  return result.data;
264
281
  }
282
+ function warnDroppedEnums(raw, parsed) {
283
+ if (!raw || typeof raw !== "object") return;
284
+ const r = raw;
285
+ const dropped = [];
286
+ if (Array.isArray(r.engines)) {
287
+ for (const e of r.engines) {
288
+ if (typeof e === "string" && !parsed.engines.includes(e)) {
289
+ dropped.push(`engines: "${e}"`);
290
+ }
291
+ }
292
+ }
293
+ for (const key of ["commits", "language"]) {
294
+ const v = r[key];
295
+ if (typeof v === "string" && v !== parsed[key]) dropped.push(`${key}: "${v}"`);
296
+ }
297
+ if (dropped.length > 0) {
298
+ process.stderr.write(
299
+ `navori: valores de config desconocidos ignorados (\xBFconfig de un navori m\xE1s nuevo? actualiz\xE1 el CLI): ${dropped.join(", ")}
300
+ `
301
+ );
302
+ }
303
+ }
265
304
 
266
305
  // src/lib/detect.ts
267
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
306
+ import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync as readdirSync3 } from "fs";
268
307
  import { join as join5, basename as basename2 } from "path";
269
308
  import { spawnSync } from "child_process";
270
309
 
@@ -600,7 +639,7 @@ function detectClaudeInfra(cwd) {
600
639
  // src/lib/detect.ts
601
640
  function detectProject(cwd) {
602
641
  const pkg = readPackageJson(cwd);
603
- const pyproject = readPyproject(cwd);
642
+ const pyproject = readPyproject(cwd) ?? (pkg ? null : readPythonFallback(cwd));
604
643
  const cargo = readCargoToml(cwd);
605
644
  const fromPackageJson = typeof pkg?.name === "string" ? pkg.name : null;
606
645
  const fromPyproject = typeof pyproject?.name === "string" ? pyproject.name : null;
@@ -705,6 +744,55 @@ function readPyproject(cwd) {
705
744
  return null;
706
745
  }
707
746
  }
747
+ function readPythonFallback(cwd) {
748
+ const deps = [];
749
+ let hasSignal = false;
750
+ const reqPath = join5(cwd, "requirements.txt");
751
+ if (existsSync5(reqPath)) {
752
+ hasSignal = true;
753
+ try {
754
+ for (const line of readFileSync5(reqPath, "utf-8").split("\n")) {
755
+ const t2 = line.trim();
756
+ if (!t2 || t2.startsWith("#") || t2.startsWith("-")) continue;
757
+ const m = t2.match(/^([a-zA-Z0-9_.-]+)/);
758
+ if (m?.[1]) deps.push(m[1].toLowerCase());
759
+ }
760
+ } catch {
761
+ }
762
+ }
763
+ const pipfilePath = join5(cwd, "Pipfile");
764
+ if (existsSync5(pipfilePath)) {
765
+ hasSignal = true;
766
+ try {
767
+ const block = readFileSync5(pipfilePath, "utf-8").match(/\[packages\]([\s\S]*?)(?:\n\[|$)/);
768
+ for (const line of block?.[1]?.split("\n") ?? []) {
769
+ const m = line.match(/^\s*"?([a-zA-Z0-9_.-]+)"?\s*=/);
770
+ if (m?.[1]) deps.push(m[1].toLowerCase());
771
+ }
772
+ } catch {
773
+ }
774
+ }
775
+ const setupPath = join5(cwd, "setup.py");
776
+ if (existsSync5(setupPath)) {
777
+ hasSignal = true;
778
+ try {
779
+ const req = readFileSync5(setupPath, "utf-8").match(/install_requires\s*=\s*\[([\s\S]*?)\]/);
780
+ for (const item of req?.[1]?.match(/["']([^"']+)["']/g) ?? []) {
781
+ const pkgName = item.slice(1, -1).split(/[<>=~!]/)[0]?.trim().toLowerCase();
782
+ if (pkgName) deps.push(pkgName);
783
+ }
784
+ } catch {
785
+ }
786
+ }
787
+ if (!hasSignal) {
788
+ try {
789
+ hasSignal = readdirSync3(cwd).some((f) => f.endsWith(".py"));
790
+ } catch {
791
+ }
792
+ }
793
+ if (!hasSignal) return null;
794
+ return { name: null, deps: Array.from(new Set(deps)) };
795
+ }
708
796
  function readCargoToml(cwd) {
709
797
  const path = join5(cwd, "Cargo.toml");
710
798
  if (!existsSync5(path)) return null;
@@ -949,7 +1037,7 @@ function pickPresetCandidate(stack, monorepo) {
949
1037
  const usesMongoose = stack.deps.includes("mongoose");
950
1038
  if (stack.worker && fw === "express" && !usesMongoose) return "background-worker";
951
1039
  if (stack.worker && fw === null) return "background-worker";
952
- if (fw === "express") return "express-mongoose";
1040
+ if (fw === "express") return usesMongoose ? "express-mongoose" : "express";
953
1041
  return "custom";
954
1042
  }
955
1043
  function guessQualityGate(pkg, pm, stack) {
@@ -1162,7 +1250,7 @@ function loadEnabledPlugins(pluginsConfig) {
1162
1250
  }
1163
1251
 
1164
1252
  // src/lib/migrate.ts
1165
- import { mkdirSync, existsSync as existsSync7, copyFileSync, readdirSync as readdirSync3, statSync as statSync3, rmSync as rmSync2 } from "fs";
1253
+ import { mkdirSync, existsSync as existsSync7, copyFileSync, readdirSync as readdirSync4, statSync as statSync3, rmSync as rmSync2 } from "fs";
1166
1254
  import { join as join6, resolve as resolve4, dirname as dirname3 } from "path";
1167
1255
 
1168
1256
  // src/lib/home.ts
@@ -1203,7 +1291,7 @@ function copyRecursive(src, dest) {
1203
1291
  const stat = statSync3(src);
1204
1292
  if (stat.isDirectory()) {
1205
1293
  mkdirSync(dest, { recursive: true });
1206
- for (const entry of readdirSync3(src)) {
1294
+ for (const entry of readdirSync4(src)) {
1207
1295
  copyRecursive(join6(src, entry), join6(dest, entry));
1208
1296
  }
1209
1297
  } else if (stat.isFile()) {
@@ -1245,7 +1333,7 @@ function migrationsRoot() {
1245
1333
  }
1246
1334
 
1247
1335
  // src/lib/workspace.ts
1248
- import { existsSync as existsSync8, readFileSync as readFileSync7, readdirSync as readdirSync4, mkdirSync as mkdirSync2, statSync as statSync4, copyFileSync as copyFileSync2, rmSync as rmSync3 } from "fs";
1336
+ import { existsSync as existsSync8, readFileSync as readFileSync7, readdirSync as readdirSync5, mkdirSync as mkdirSync2, statSync as statSync4, copyFileSync as copyFileSync2, rmSync as rmSync3 } from "fs";
1249
1337
  import { join as join7 } from "path";
1250
1338
  import { z as z6 } from "zod";
1251
1339
  function workspacesRootLazy() {
@@ -1317,14 +1405,14 @@ function migrateLegacyLayoutIfNeeded(name) {
1317
1405
  }
1318
1406
  function listWorkspaces() {
1319
1407
  if (!existsSync8(workspacesRootLazy())) return [];
1320
- for (const entry of readdirSync4(workspacesRootLazy())) {
1408
+ for (const entry of readdirSync5(workspacesRootLazy())) {
1321
1409
  if (entry.endsWith(".json")) {
1322
1410
  const name = entry.replace(/\.json$/, "");
1323
1411
  migrateLegacyLayoutIfNeeded(name);
1324
1412
  }
1325
1413
  }
1326
1414
  const names = [];
1327
- for (const entry of readdirSync4(workspacesRootLazy())) {
1415
+ for (const entry of readdirSync5(workspacesRootLazy())) {
1328
1416
  const full = join7(workspacesRootLazy(), entry);
1329
1417
  try {
1330
1418
  if (!statSync4(full).isDirectory()) continue;
@@ -1373,16 +1461,37 @@ function writeWorkspace(workspace) {
1373
1461
 
1374
1462
  // src/commands/render.ts
1375
1463
  import { defineCommand } from "citty";
1376
- import * as p from "@clack/prompts";
1464
+ import * as p2 from "@clack/prompts";
1377
1465
  import { existsSync as existsSync13 } from "fs";
1378
1466
  import { resolve as resolve9 } from "path";
1379
1467
 
1468
+ // src/lib/cli-config.ts
1469
+ import * as p from "@clack/prompts";
1470
+ function readConfigOrExit(path) {
1471
+ try {
1472
+ return readConfig(path);
1473
+ } catch (err) {
1474
+ if (err instanceof ConfigError) {
1475
+ p.cancel(err.message);
1476
+ for (const issue of err.issues ?? []) {
1477
+ console.error(` - ${issue.path.join(".") || "(root)"}: ${issue.message}`);
1478
+ }
1479
+ process.exit(1);
1480
+ }
1481
+ if (err instanceof NavoriError) {
1482
+ p.cancel(err.message);
1483
+ process.exit(1);
1484
+ }
1485
+ throw err;
1486
+ }
1487
+ }
1488
+
1380
1489
  // src/engines/claude/index.ts
1381
1490
  import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync11, chmodSync } from "fs";
1382
1491
  import { basename as basename3, dirname as dirname5, join as join9, relative as relative2, resolve as resolve8 } from "path";
1383
1492
 
1384
1493
  // src/lib/backup.ts
1385
- import { mkdirSync as mkdirSync3, copyFileSync as copyFileSync3, existsSync as existsSync9, readdirSync as readdirSync5, rmSync as rmSync4, statSync as statSync5 } from "fs";
1494
+ import { mkdirSync as mkdirSync3, copyFileSync as copyFileSync3, existsSync as existsSync9, readdirSync as readdirSync6, rmSync as rmSync4, statSync as statSync5 } from "fs";
1386
1495
  import { dirname as dirname4, join as join8, relative, resolve as resolve5 } from "path";
1387
1496
  function backupRootLazy() {
1388
1497
  return join8(safeHomedir(), ".navori", "backups");
@@ -1410,8 +1519,8 @@ function createBackup(repoRoot, paths, options = {}) {
1410
1519
  mkdirSync3(dir, { recursive: true });
1411
1520
  const exclude = (options.exclude ?? []).map((e) => e.replace(/\/+$/, ""));
1412
1521
  const copied = [];
1413
- for (const p16 of paths) {
1414
- const abs = resolve5(repoRoot, p16);
1522
+ for (const p17 of paths) {
1523
+ const abs = resolve5(repoRoot, p17);
1415
1524
  if (!existsSync9(abs)) continue;
1416
1525
  copyRecursive2(abs, dir, repoRoot, exclude, copied);
1417
1526
  }
@@ -1422,7 +1531,7 @@ function copyRecursive2(abs, backupDir, repoRoot, exclude, copied) {
1422
1531
  if (isExcluded(rel, exclude)) return;
1423
1532
  const stat = statSync5(abs);
1424
1533
  if (stat.isDirectory()) {
1425
- for (const entry of readdirSync5(abs)) {
1534
+ for (const entry of readdirSync6(abs)) {
1426
1535
  copyRecursive2(join8(abs, entry), backupDir, repoRoot, exclude, copied);
1427
1536
  }
1428
1537
  return;
@@ -1445,7 +1554,7 @@ function purgeOldBackups(retentionDays = DEFAULT_RETENTION_DAYS) {
1445
1554
  if (!existsSync9(root)) return [];
1446
1555
  const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
1447
1556
  const pruned = [];
1448
- for (const entry of readdirSync5(root)) {
1557
+ for (const entry of readdirSync6(root)) {
1449
1558
  const full = join8(root, entry);
1450
1559
  const stat = statSync5(full);
1451
1560
  if (!stat.isDirectory()) continue;
@@ -1722,13 +1831,13 @@ function placeholderFallback(path) {
1722
1831
  // src/lib/render-plan.ts
1723
1832
  var CORE_SOURCE_ID = "@navori/core";
1724
1833
  var CORE_MANAGED_ASSETS = [
1725
- { id: "orquestacion", relPath: "core-assets/managed/orquestacion.md", availableLanguages: ["es"] },
1726
- { id: "idioma-rol", relPath: "core-assets/managed/idioma-rol.md", availableLanguages: ["es"] },
1727
- { id: "formato-respuesta", relPath: "core-assets/managed/formato-respuesta.md", availableLanguages: ["es"] },
1834
+ { id: "orquestacion", relPath: "core-assets/managed/orquestacion.md", availableLanguages: ["es"], rootOnly: true },
1835
+ { id: "idioma-rol", relPath: "core-assets/managed/idioma-rol.md", availableLanguages: ["es"], rootOnly: true },
1836
+ { id: "formato-respuesta", relPath: "core-assets/managed/formato-respuesta.md", availableLanguages: ["es"], rootOnly: true },
1728
1837
  { id: "tipado-fuerte", relPath: "core-assets/managed/tipado-fuerte.md", availableLanguages: ["es"], condition: "project.typedLanguage" },
1729
- { id: "operaciones-seguras", relPath: "core-assets/managed/operaciones-seguras.md", availableLanguages: ["es"] },
1730
- { id: "arranque-sesion", relPath: "core-assets/managed/arranque-sesion.md", availableLanguages: ["es"] },
1731
- { id: "cierre-sesion", relPath: "core-assets/managed/cierre-sesion.md", availableLanguages: ["es"] }
1838
+ { id: "operaciones-seguras", relPath: "core-assets/managed/operaciones-seguras.md", availableLanguages: ["es"], rootOnly: true },
1839
+ { id: "arranque-sesion", relPath: "core-assets/managed/arranque-sesion.md", availableLanguages: ["es"], rootOnly: true },
1840
+ { id: "cierre-sesion", relPath: "core-assets/managed/cierre-sesion.md", availableLanguages: ["es"], rootOnly: true }
1732
1841
  ];
1733
1842
  var CORE_VERSION = readBundledCoreVersion();
1734
1843
  function resolveAssetPath(asset, language = "es") {
@@ -1776,6 +1885,17 @@ function computeRenderPlan(existing, inputConfig, repoRoot, options = {}) {
1776
1885
  if (skipIds.has(asset.id)) {
1777
1886
  continue;
1778
1887
  }
1888
+ if (options.omitRootOnly && asset.rootOnly) {
1889
+ const before = working;
1890
+ working = removeManagedSection(working, asset.id);
1891
+ entries.push({
1892
+ asset,
1893
+ source: "core",
1894
+ status: before === working ? "unchanged" : "removed-condition-false",
1895
+ newContent: null
1896
+ });
1897
+ continue;
1898
+ }
1779
1899
  if (asset.condition) {
1780
1900
  const truthy = resolveCondition(configRecord, asset.condition);
1781
1901
  if (!truthy) {
@@ -1935,8 +2055,8 @@ var CLAUDE_COMPUTED_BLOCK_IDS = [
1935
2055
  "agentes-disponibles",
1936
2056
  "contexto-proyecto"
1937
2057
  ];
1938
- function canonicalManagedOrder(config, repoRoot) {
1939
- const plan = computeRenderPlan("", config, repoRoot);
2058
+ function canonicalManagedOrder(config, repoRoot, omitRootOnly = false) {
2059
+ const plan = computeRenderPlan("", config, repoRoot, { omitRootOnly });
1940
2060
  const ids = plan.entries.filter((entry) => entry.newContent !== null).map((entry) => entry.asset.id);
1941
2061
  return [...ids, ...CLAUDE_COMPUTED_BLOCK_IDS];
1942
2062
  }
@@ -2140,6 +2260,146 @@ function pluginHooksToClaudeShape(entries) {
2140
2260
  return grouped;
2141
2261
  }
2142
2262
 
2263
+ // src/engines/claude/coexist-settings.ts
2264
+ var OWNERSHIP_KEY = "$navori";
2265
+ function isPlainObject2(v) {
2266
+ return typeof v === "object" && v !== null && !Array.isArray(v);
2267
+ }
2268
+ function asStringArray(v) {
2269
+ return Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
2270
+ }
2271
+ function mergeCoexistSettings(existing, navoriSettings) {
2272
+ const result = structuredClone(existing);
2273
+ const prior = readTracking(result);
2274
+ stripTrackedHooks(result, prior.managedHooks);
2275
+ stripFromPermissionList(result, "deny", prior.managedDeny);
2276
+ stripFromPermissionList(result, "ask", prior.managedAsk);
2277
+ const desiredHooks = collectHooks(navoriSettings);
2278
+ const desiredDeny = permissionList(navoriSettings, "deny");
2279
+ const desiredAsk = permissionList(navoriSettings, "ask");
2280
+ injectHooks(result, desiredHooks);
2281
+ injectPermissionList(result, "deny", desiredDeny);
2282
+ injectPermissionList(result, "ask", desiredAsk);
2283
+ pruneEmptyHooks(result);
2284
+ writeTracking(result, {
2285
+ managedHooks: desiredHooks.map((d) => String(d.hook.command)),
2286
+ managedDeny: desiredDeny,
2287
+ managedAsk: desiredAsk
2288
+ });
2289
+ return result;
2290
+ }
2291
+ function readTracking(obj) {
2292
+ const nav = obj[OWNERSHIP_KEY];
2293
+ const t2 = isPlainObject2(nav) ? nav : {};
2294
+ return {
2295
+ managedHooks: asStringArray(t2.managedHooks),
2296
+ managedDeny: asStringArray(t2.managedDeny),
2297
+ managedAsk: asStringArray(t2.managedAsk)
2298
+ };
2299
+ }
2300
+ function writeTracking(obj, t2) {
2301
+ const nav = isPlainObject2(obj[OWNERSHIP_KEY]) ? obj[OWNERSHIP_KEY] : {};
2302
+ nav.managedHooks = t2.managedHooks;
2303
+ nav.managedDeny = t2.managedDeny;
2304
+ nav.managedAsk = t2.managedAsk;
2305
+ nav.note = "Bloques 'managed*' inyectados por navori (coexist). El resto es tuyo. Se re-sincronizan en cada 'navori render'.";
2306
+ obj[OWNERSHIP_KEY] = nav;
2307
+ }
2308
+ function collectHooks(settings) {
2309
+ const out = [];
2310
+ const hooks = settings.hooks;
2311
+ if (!isPlainObject2(hooks)) return out;
2312
+ for (const [event, entries] of Object.entries(hooks)) {
2313
+ if (!Array.isArray(entries)) continue;
2314
+ for (const entry of entries) {
2315
+ if (!isPlainObject2(entry) || !Array.isArray(entry.hooks)) continue;
2316
+ const matcher = typeof entry.matcher === "string" ? entry.matcher : void 0;
2317
+ for (const hook of entry.hooks) {
2318
+ if (isPlainObject2(hook) && typeof hook.command === "string") {
2319
+ out.push({ event, matcher, hook });
2320
+ }
2321
+ }
2322
+ }
2323
+ }
2324
+ return out;
2325
+ }
2326
+ function permissionList(settings, key) {
2327
+ const perms = settings.permissions;
2328
+ if (!isPlainObject2(perms)) return [];
2329
+ return asStringArray(perms[key]);
2330
+ }
2331
+ function stripTrackedHooks(obj, commands) {
2332
+ if (commands.length === 0) return;
2333
+ const hooks = obj.hooks;
2334
+ if (!isPlainObject2(hooks)) return;
2335
+ const set = new Set(commands);
2336
+ for (const [event, entries] of Object.entries(hooks)) {
2337
+ if (!Array.isArray(entries)) continue;
2338
+ const kept = [];
2339
+ for (const entry of entries) {
2340
+ if (isPlainObject2(entry) && Array.isArray(entry.hooks)) {
2341
+ entry.hooks = entry.hooks.filter(
2342
+ (h) => !(isPlainObject2(h) && typeof h.command === "string" && set.has(h.command))
2343
+ );
2344
+ if (entry.hooks.length > 0) kept.push(entry);
2345
+ } else {
2346
+ kept.push(entry);
2347
+ }
2348
+ }
2349
+ hooks[event] = kept;
2350
+ }
2351
+ }
2352
+ function pruneEmptyHooks(obj) {
2353
+ const hooks = obj.hooks;
2354
+ if (!isPlainObject2(hooks)) return;
2355
+ for (const [event, entries] of Object.entries(hooks)) {
2356
+ if (Array.isArray(entries) && entries.length === 0) delete hooks[event];
2357
+ }
2358
+ if (Object.keys(hooks).length === 0) delete obj.hooks;
2359
+ }
2360
+ function stripFromPermissionList(obj, key, values) {
2361
+ if (values.length === 0) return;
2362
+ const perms = obj.permissions;
2363
+ if (!isPlainObject2(perms)) return;
2364
+ const list = perms[key];
2365
+ if (!Array.isArray(list)) return;
2366
+ const set = new Set(values);
2367
+ const filtered = list.filter((v) => !(typeof v === "string" && set.has(v)));
2368
+ if (filtered.length > 0) perms[key] = filtered;
2369
+ else delete perms[key];
2370
+ }
2371
+ function injectHooks(obj, desired) {
2372
+ if (desired.length === 0) return;
2373
+ const hooks = isPlainObject2(obj.hooks) ? obj.hooks : {};
2374
+ obj.hooks = hooks;
2375
+ for (const { event, matcher, hook } of desired) {
2376
+ const arr = Array.isArray(hooks[event]) ? hooks[event] : [];
2377
+ hooks[event] = arr;
2378
+ let group = arr.find(
2379
+ (e) => isPlainObject2(e) && e.matcher === matcher && Array.isArray(e.hooks)
2380
+ );
2381
+ if (!group) {
2382
+ group = matcher !== void 0 ? { matcher, hooks: [] } : { hooks: [] };
2383
+ arr.push(group);
2384
+ }
2385
+ const groupHooks = group.hooks;
2386
+ const cmd = hook.command;
2387
+ if (!groupHooks.some((h) => isPlainObject2(h) && h.command === cmd)) {
2388
+ groupHooks.push(hook);
2389
+ }
2390
+ }
2391
+ }
2392
+ function injectPermissionList(obj, key, values) {
2393
+ if (values.length === 0) return;
2394
+ const perms = isPlainObject2(obj.permissions) ? obj.permissions : {};
2395
+ obj.permissions = perms;
2396
+ const list = Array.isArray(perms[key]) ? perms[key] : [];
2397
+ perms[key] = list;
2398
+ for (const v of values) {
2399
+ if (!list.includes(v)) list.push(v);
2400
+ }
2401
+ }
2402
+
2143
2403
  // src/engines/claude/render-managed-file.ts
2144
2404
  import { readFileSync as readFileSync10 } from "fs";
2145
2405
 
@@ -2472,6 +2732,7 @@ function renderClaudeEngine(cwd, inputConfig, options = {}) {
2472
2732
  const dryRun = options.dryRun === true;
2473
2733
  const force = options.force === true;
2474
2734
  const repoRoot = options.repoRoot ?? cwd;
2735
+ const isWorkspace = options.repoRoot != null && resolve8(options.repoRoot) !== resolve8(cwd);
2475
2736
  const coreAssets = resolve8(getCoreRoot(), "core-assets");
2476
2737
  const skipped = [];
2477
2738
  const warnings = [];
@@ -2481,7 +2742,8 @@ function renderClaudeEngine(cwd, inputConfig, options = {}) {
2481
2742
  const claudeMdExisting = existsSync11(claudeMdPath) ? readFileSync11(claudeMdPath, "utf-8") : "";
2482
2743
  const claudeMdPlan = computeRenderPlan(claudeMdExisting, config, repoRoot, {
2483
2744
  skipIds: options.skipIds,
2484
- forceIds: options.forceIds
2745
+ forceIds: options.forceIds,
2746
+ omitRootOnly: isWorkspace
2485
2747
  });
2486
2748
  inspected += 1;
2487
2749
  const localSkills = config.project?.localSkills ?? [];
@@ -2546,7 +2808,7 @@ function renderClaudeEngine(cwd, inputConfig, options = {}) {
2546
2808
  } else {
2547
2809
  claudeMdContent = removeManagedSection(claudeMdContent, CONTEXTO_PROYECTO_ID);
2548
2810
  }
2549
- const reorder = reorderManagedBlocks(claudeMdContent, canonicalManagedOrder(config, repoRoot));
2811
+ const reorder = reorderManagedBlocks(claudeMdContent, canonicalManagedOrder(config, repoRoot, isWorkspace));
2550
2812
  claudeMdContent = reorder.output;
2551
2813
  if (reorder.blockedByInterleaving) {
2552
2814
  warnings.push(
@@ -2778,8 +3040,9 @@ function renderClaudeEngine(cwd, inputConfig, options = {}) {
2778
3040
  inspected
2779
3041
  };
2780
3042
  }
3043
+ pending.sort((a, b) => Number(a.path === claudeMdPath) - Number(b.path === claudeMdPath));
2781
3044
  if (!dryRun) {
2782
- const hasExistingTarget = pending.some((p16) => existsSync11(p16.path));
3045
+ const hasExistingTarget = pending.some((p17) => existsSync11(p17.path));
2783
3046
  if (hasExistingTarget) {
2784
3047
  const handle = createBackup(cwd, ["CLAUDE.md", ".claude"], {
2785
3048
  exclude: [".claude/settings.local.json", ".claude/progress"]
@@ -2789,21 +3052,22 @@ function renderClaudeEngine(cwd, inputConfig, options = {}) {
2789
3052
  purgeOldBackups();
2790
3053
  }
2791
3054
  }
2792
- for (const p16 of pending) {
2793
- mkdirSync4(dirname5(p16.path), { recursive: true });
2794
- writeFileAtomic(p16.path, p16.content);
2795
- log.debug("wrote", { path: relative2(cwd, p16.path), status: p16.status });
2796
- if (p16.chmodExec) {
3055
+ if (backupPath) log.debug("pre-write backup", { path: relative2(cwd, backupPath) });
3056
+ for (const p17 of pending) {
3057
+ mkdirSync4(dirname5(p17.path), { recursive: true });
3058
+ writeFileAtomic(p17.path, p17.content);
3059
+ log.debug("wrote", { path: relative2(cwd, p17.path), status: p17.status });
3060
+ if (p17.chmodExec) {
2797
3061
  try {
2798
- chmodSync(p16.path, 493);
3062
+ chmodSync(p17.path, 493);
2799
3063
  } catch {
2800
3064
  }
2801
3065
  }
2802
- written.push({ path: relative2(cwd, p16.path), status: p16.status });
3066
+ written.push({ path: relative2(cwd, p17.path), status: p17.status });
2803
3067
  }
2804
3068
  } else {
2805
- for (const p16 of pending) {
2806
- written.push({ path: relative2(cwd, p16.path), status: p16.status });
3069
+ for (const p17 of pending) {
3070
+ written.push({ path: relative2(cwd, p17.path), status: p17.status });
2807
3071
  }
2808
3072
  }
2809
3073
  benchMark("write");
@@ -2847,11 +3111,17 @@ function planSettings(cwd, config, plugins, force = false) {
2847
3111
  if (force) {
2848
3112
  return { kind: "write", path, content: newJson, status: "updated" };
2849
3113
  }
2850
- return {
2851
- kind: "skip",
2852
- path,
2853
- reason: "settings.json existe sin `$navori.managed = true`. Corr\xE9 'navori init' en modo replace o 'navori render --force --apply' para adoptar."
2854
- };
3114
+ if (!isPlainObject2(parsed)) {
3115
+ return {
3116
+ kind: "skip",
3117
+ path,
3118
+ reason: "settings.json no es un objeto JSON \u2014 no se puede fusionar. Corr\xE9 'navori render --force --apply' para regenerar."
3119
+ };
3120
+ }
3121
+ const merged = mergeCoexistSettings(parsed, newSettings);
3122
+ const mergedJson = JSON.stringify(merged, null, 2) + "\n";
3123
+ if (mergedJson === readFileSync11(path, "utf-8")) return { kind: "noop" };
3124
+ return { kind: "write", path, content: mergedJson, status: "updated" };
2855
3125
  }
2856
3126
  const current = readFileSync11(path, "utf-8");
2857
3127
  if (current === newJson) return { kind: "noop" };
@@ -2900,7 +3170,7 @@ function applyBootstrapPlan(plan, _cwd, pending) {
2900
3170
  function applySubBlockInject(input) {
2901
3171
  const targetAbs = join9(input.cwd, input.skill.injectInto);
2902
3172
  let currentContent;
2903
- const pendingEntry = input.pending.find((p16) => p16.path === targetAbs);
3173
+ const pendingEntry = input.pending.find((p17) => p17.path === targetAbs);
2904
3174
  if (pendingEntry) {
2905
3175
  currentContent = pendingEntry.content;
2906
3176
  } else if (existsSync11(targetAbs)) {
@@ -3044,7 +3314,22 @@ function renderAgentsMdEngine(cwd, inputConfig, options = {}) {
3044
3314
  writeFileAtomic(agentsMdPath, output);
3045
3315
  }
3046
3316
  }
3047
- return { written, skipped, warnings: [], backupPath };
3317
+ return { written, skipped, warnings: collectOmissionWarnings(config), backupPath };
3318
+ }
3319
+ function collectOmissionWarnings(config) {
3320
+ const warnings = [
3321
+ "AGENTS.md no replica la infraestructura espec\xEDfica de Claude Code: orquestaci\xF3n de subagentes (Agent tool), hooks (quality-gate/guard-destructive) y reglas de permisos. Config\xFAralos en tu herramienta si las necesitas."
3322
+ ];
3323
+ const enabledPlugins = Object.entries(config.plugins ?? {}).filter(([, s]) => s.enabled === true).map(([id]) => id);
3324
+ if (enabledPlugins.length > 0) {
3325
+ warnings.push(
3326
+ `Bloques de plugins omitidos por asumir infraestructura de Claude Code: ${enabledPlugins.join(", ")}.`
3327
+ );
3328
+ }
3329
+ if (config.models && Object.keys(config.models).length > 0) {
3330
+ warnings.push("La asignaci\xF3n de modelo por agente (config.models) no aplica fuera de Claude Code; se omiti\xF3.");
3331
+ }
3332
+ return warnings;
3048
3333
  }
3049
3334
 
3050
3335
  // src/lib/style.ts
@@ -3163,7 +3448,7 @@ function runRender(cwd, dryRunOrOptions = false, force = false) {
3163
3448
  workspaces: []
3164
3449
  };
3165
3450
  }
3166
- const config = readConfig(configPath);
3451
+ const config = readConfigOrExit(configPath);
3167
3452
  benchMark("loadConfig");
3168
3453
  if (workspaceFilter) {
3169
3454
  const declared = config.monorepo?.workspaces ?? [];
@@ -3230,9 +3515,14 @@ function runRender(cwd, dryRunOrOptions = false, force = false) {
3230
3515
  const renderClaude = engines.includes("claude");
3231
3516
  const engineResult = renderClaude ? renderClaudeEngine(cwd, config, { dryRun, force: forceFlag }) : void 0;
3232
3517
  const workspaces = [];
3518
+ const orphanedWorkspaces = [];
3233
3519
  if (renderClaude) {
3234
3520
  for (const ws of config.monorepo?.workspaces ?? []) {
3235
3521
  const wsCwd = resolve9(cwd, ws.path);
3522
+ if (!existsSync13(wsCwd)) {
3523
+ orphanedWorkspaces.push(ws.path);
3524
+ continue;
3525
+ }
3236
3526
  const wsConfig = effectiveConfigForWorkspace(config, ws);
3237
3527
  const wsResult = renderClaudeEngine(wsCwd, wsConfig, {
3238
3528
  dryRun,
@@ -3263,6 +3553,7 @@ function runRender(cwd, dryRunOrOptions = false, force = false) {
3263
3553
  backupPath: engineResult?.backupPath ?? null,
3264
3554
  engineResult,
3265
3555
  workspaces,
3556
+ orphanedWorkspaces,
3266
3557
  extraEngines
3267
3558
  };
3268
3559
  }
@@ -3293,9 +3584,9 @@ var renderCommand = defineCommand({
3293
3584
  async run({ args }) {
3294
3585
  benchStart();
3295
3586
  const cwd = resolve9(args.cwd ?? process.cwd());
3296
- p.intro(brand("render"));
3587
+ p2.intro(brand("render"));
3297
3588
  if (!existsSync13(cwd)) {
3298
- p.cancel(`Directory not found: ${cwd}`);
3589
+ p2.cancel(`Directory not found: ${cwd}`);
3299
3590
  process.exit(1);
3300
3591
  }
3301
3592
  const apply = Boolean(args.apply);
@@ -3307,60 +3598,66 @@ var renderCommand = defineCommand({
3307
3598
  workspaceFilter
3308
3599
  });
3309
3600
  if (!result.ok) {
3310
- p.cancel(result.reason ?? "Render failed");
3601
+ p2.cancel(result.reason ?? "Render failed");
3311
3602
  process.exit(1);
3312
3603
  }
3313
3604
  const hasWorkspaces = result.workspaces.length > 0;
3314
3605
  if (hasWorkspaces && result.engineResult) {
3315
- p.log.message(`${dim("root")}`);
3606
+ p2.log.message(`${dim("root")}`);
3316
3607
  }
3317
3608
  if (result.engineResult) {
3318
3609
  reportClaudeMd(result.filePath, result.entries, result.written, preview);
3319
3610
  reportEngineFiles(result.engineResult);
3320
3611
  }
3321
3612
  if (result.languageFallbacks.length > 0) {
3322
- p.log.warn(
3613
+ p2.log.warn(
3323
3614
  `Language fallback to Spanish for: ${result.languageFallbacks.join(", ")} (English version not available yet)`
3324
3615
  );
3325
3616
  }
3326
3617
  if (result.backupPath) {
3327
- p.log.message(`${dim("Backup:")} ${result.backupPath}`);
3618
+ p2.log.message(`${dim("Backup:")} ${result.backupPath}`);
3328
3619
  }
3329
3620
  for (const ws of result.workspaces) {
3330
- p.log.message(`${dim("workspace")} ${color.cyan(ws.workspaceName)} ${dim(`(${ws.workspacePath})`)}`);
3621
+ p2.log.message(`${dim("workspace")} ${color.cyan(ws.workspaceName)} ${dim(`(${ws.workspacePath})`)}`);
3331
3622
  reportClaudeMd(ws.filePath, ws.entries, ws.written, preview);
3332
3623
  reportEngineFiles(ws.engineResult);
3333
3624
  if (ws.languageFallbacks.length > 0) {
3334
- p.log.warn(
3625
+ p2.log.warn(
3335
3626
  `[${ws.workspaceName}] Language fallback to Spanish for: ${ws.languageFallbacks.join(", ")} (English version not available yet)`
3336
3627
  );
3337
3628
  }
3338
3629
  if (ws.backupPath) {
3339
- p.log.message(`${dim("Backup:")} ${ws.backupPath}`);
3630
+ p2.log.message(`${dim("Backup:")} ${ws.backupPath}`);
3340
3631
  }
3341
3632
  }
3633
+ if (result.orphanedWorkspaces && result.orphanedWorkspaces.length > 0) {
3634
+ p2.log.warn(
3635
+ `Workspaces declarados en config pero ausentes en disco (${result.orphanedWorkspaces.length}) \u2014 no se renderizaron (evita resucitar dirs borrados). Corr\xE9 'navori scan' o quit\xE1 del config:
3636
+ ` + result.orphanedWorkspaces.map((w) => ` ${color.yellow(sym.update)} ${w}`).join("\n")
3637
+ );
3638
+ }
3342
3639
  for (const ee of result.extraEngines ?? []) {
3343
- p.log.message(`${dim("engine")} ${color.cyan(ee.engine)}`);
3640
+ p2.log.message(`${dim("engine")} ${color.cyan(ee.engine)}`);
3344
3641
  for (const w of ee.written) {
3345
- p.log.message(` ${renderStatusSymbol(w.status)} ${w.path} ${dim("(")}${renderStatusLabel(w.status)}${dim(")")}`);
3642
+ p2.log.message(` ${renderStatusSymbol(w.status)} ${w.path} ${dim("(")}${renderStatusLabel(w.status)}${dim(")")}`);
3346
3643
  }
3347
- for (const s of ee.skipped) p.log.warn(` ${s.path}: ${s.reason}`);
3348
- for (const warn of ee.warnings) p.log.warn(` ${warn}`);
3349
- if (ee.backupPath) p.log.message(` ${dim("Backup:")} ${ee.backupPath}`);
3644
+ for (const s of ee.skipped) p2.log.warn(` ${s.path}: ${s.reason}`);
3645
+ for (const warn of ee.warnings) p2.log.warn(` ${warn}`);
3646
+ if (ee.backupPath) p2.log.message(` ${dim("Backup:")} ${ee.backupPath}`);
3350
3647
  }
3351
3648
  const allEntries = result.entries.concat(...result.workspaces.map((w) => w.entries));
3352
3649
  const anyPending = result.written || result.workspaces.some((w) => w.written) || (result.extraEngines ?? []).some((e) => e.written.length > 0);
3353
3650
  const summary = summarize(allEntries);
3354
3651
  if (preview) {
3355
3652
  if (anyPending) {
3356
- p.outro(`${color.yellow("Preview")} ${summary} ${dim("\xB7 corre 'navori render --apply' para escribir")}`);
3653
+ p2.outro(`${color.yellow("Preview")} ${summary} ${dim("\xB7 corre 'navori render --apply' para escribir")}`);
3357
3654
  } else {
3358
- p.outro(`${dim("Up to date")} ${summary} ${dim("\xB7 nada que aplicar")}`);
3655
+ p2.outro(`${dim("Up to date")} ${summary} ${dim("\xB7 nada que aplicar")}`);
3359
3656
  }
3360
3657
  } else if (anyPending) {
3361
- p.outro(`${color.green("Done")} ${summary}`);
3658
+ p2.outro(`${color.green("Done")} ${summary}`);
3362
3659
  } else {
3363
- p.outro(`${dim("Up to date")} ${summary}`);
3660
+ p2.outro(`${dim("Up to date")} ${summary}`);
3364
3661
  }
3365
3662
  benchReport();
3366
3663
  }
@@ -3388,7 +3685,7 @@ function reportClaudeMd(file, entries, changed, preview) {
3388
3685
  if (preview) lines.push(` ${dim(changed ? "\u2192 preview (se escribir\xEDa)" : "\u2192 sin cambios")}`);
3389
3686
  else if (changed) lines.push(` ${dim("\u2192 written")}`);
3390
3687
  else lines.push(` ${dim("\u2192 no changes")}`);
3391
- p.log.message(lines.join("\n"));
3688
+ p2.log.message(lines.join("\n"));
3392
3689
  }
3393
3690
  function reportEngineFiles(engine) {
3394
3691
  const written = engine.written.filter((w) => w.path !== "CLAUDE.md");
@@ -3417,7 +3714,7 @@ function reportEngineFiles(engine) {
3417
3714
  for (const w of engine.warnings) {
3418
3715
  lines.push(` ${color.yellow("\xB7")} ${dim(w)}`);
3419
3716
  }
3420
- p.log.message(lines.join("\n"));
3717
+ p2.log.message(lines.join("\n"));
3421
3718
  }
3422
3719
 
3423
3720
  // src/lib/i18n.ts
@@ -3742,15 +4039,15 @@ var CORE_PROMPTS_REL = "core-assets/prompts.json";
3742
4039
  function loadPrompts(enabledPlugins) {
3743
4040
  const out = [];
3744
4041
  const warnings = [];
3745
- for (const p16 of loadCorePrompts(warnings)) {
3746
- out.push({ ...p16, source: "core" });
4042
+ for (const p17 of loadCorePrompts(warnings)) {
4043
+ out.push({ ...p17, source: "core" });
3747
4044
  }
3748
4045
  const { loaded } = loadEnabledPlugins(enabledPlugins);
3749
4046
  for (const plugin of loaded) {
3750
4047
  const declared = plugin.manifest.prompts;
3751
4048
  if (!declared || declared.length === 0) continue;
3752
- for (const p16 of declared) {
3753
- out.push({ ...p16, source: plugin.manifest.id });
4049
+ for (const p17 of declared) {
4050
+ out.push({ ...p17, source: plugin.manifest.id });
3754
4051
  }
3755
4052
  }
3756
4053
  return { prompts: out, warnings };
@@ -3770,11 +4067,11 @@ function loadCorePrompts(warnings) {
3770
4067
  warnings.push(`core prompts.json no valida el shape esperado`);
3771
4068
  return [];
3772
4069
  }
3773
- return result.data.prompts.map((p16) => ({ ...p16, source: "core" }));
4070
+ return result.data.prompts.map((p17) => ({ ...p17, source: "core" }));
3774
4071
  }
3775
4072
 
3776
4073
  // src/lib/scan.ts
3777
- import { existsSync as existsSync15, readdirSync as readdirSync6 } from "fs";
4074
+ import { existsSync as existsSync15, readdirSync as readdirSync7 } from "fs";
3778
4075
  import { join as join11 } from "path";
3779
4076
  function scanMonorepoWorkspaces(cwd) {
3780
4077
  const patterns = collectWorkspacePatterns(cwd);
@@ -3807,7 +4104,7 @@ function walk(cwd, accum, remaining) {
3807
4104
  if (!existsSync15(currentAbs)) return [];
3808
4105
  let entries;
3809
4106
  try {
3810
- entries = readdirSync6(currentAbs, { withFileTypes: true });
4107
+ entries = readdirSync7(currentAbs, { withFileTypes: true });
3811
4108
  } catch {
3812
4109
  return [];
3813
4110
  }
@@ -3905,15 +4202,15 @@ var initCommand = defineCommand2({
3905
4202
  const cwd = resolve11(args.cwd ?? process.cwd());
3906
4203
  const configPath = `${cwd}/navori.config.json`;
3907
4204
  const autoYes = Boolean(args.yes || args.recommended);
3908
- p2.intro(brand("init"));
4205
+ p3.intro(brand("init"));
3909
4206
  const bootstrapLang = normalizeLang(args.lang) ?? "es";
3910
4207
  const bootstrapT = t(bootstrapLang);
3911
4208
  if (!existsSync16(cwd)) {
3912
- p2.cancel(bootstrapT.dirNotFound(cwd));
4209
+ p3.cancel(bootstrapT.dirNotFound(cwd));
3913
4210
  process.exit(1);
3914
4211
  }
3915
4212
  if (existsSync16(configPath)) {
3916
- p2.cancel(bootstrapT.configExists(configPath));
4213
+ p3.cancel(bootstrapT.configExists(configPath));
3917
4214
  process.exit(1);
3918
4215
  }
3919
4216
  let lang = bootstrapLang;
@@ -3922,7 +4219,7 @@ var initCommand = defineCommand2({
3922
4219
  if (explicit) {
3923
4220
  lang = explicit;
3924
4221
  } else {
3925
- const picked = await p2.select({
4222
+ const picked = await p3.select({
3926
4223
  message: bootstrapT.pickLanguage,
3927
4224
  options: [
3928
4225
  { value: "es", label: bootstrapT.pickLanguageEs },
@@ -3930,7 +4227,7 @@ var initCommand = defineCommand2({
3930
4227
  ],
3931
4228
  initialValue: "es"
3932
4229
  });
3933
- if (p2.isCancel(picked)) return cancel3(bootstrapLang);
4230
+ if (p3.isCancel(picked)) return cancel4(bootstrapLang);
3934
4231
  lang = picked;
3935
4232
  }
3936
4233
  }
@@ -3940,35 +4237,35 @@ var initCommand = defineCommand2({
3940
4237
  yes: autoYes,
3941
4238
  lang
3942
4239
  });
3943
- if (mode === null) return cancel3(lang);
4240
+ if (mode === null) return cancel4(lang);
3944
4241
  let workspaceConfig = null;
3945
4242
  if (args.workspace) {
3946
4243
  try {
3947
4244
  workspaceConfig = loadWorkspace(args.workspace);
3948
4245
  } catch (err) {
3949
4246
  if (err instanceof WorkspaceError) {
3950
- p2.cancel(err.message);
4247
+ p3.cancel(err.message);
3951
4248
  process.exit(1);
3952
4249
  }
3953
4250
  throw err;
3954
4251
  }
3955
4252
  if (!workspaceConfig) {
3956
- p2.cancel(
4253
+ p3.cancel(
3957
4254
  `Workspace '${args.workspace}' not found. Create it with 'navori workspace init ${args.workspace}'.`
3958
4255
  );
3959
4256
  process.exit(1);
3960
4257
  }
3961
- p2.note(
4258
+ p3.note(
3962
4259
  formatWorkspaceSummary(workspaceConfig, lang),
3963
4260
  tr.workspaceDefaultsTitle(workspaceConfig.name)
3964
4261
  );
3965
4262
  }
3966
- p2.note(formatDetectionSummary(detected, lang), tr.detectedTitle);
4263
+ p3.note(formatDetectionSummary(detected, lang), tr.detectedTitle);
3967
4264
  if (detected.suggestedPresetGap) {
3968
- p2.log.warn(tr.presetGapNotice(detected.suggestedPresetGap));
4265
+ p3.log.warn(tr.presetGapNotice(detected.suggestedPresetGap));
3969
4266
  }
3970
4267
  if (detected.name && isPlaceholderName(detected.name)) {
3971
- p2.log.warn(tr.placeholderNameNotice(detected.name));
4268
+ p3.log.warn(tr.placeholderNameNotice(detected.name));
3972
4269
  }
3973
4270
  const wsDefaults = workspaceConfig?.defaults;
3974
4271
  const defaultEngines = wsDefaults?.engines ?? (detected.existingEngines.length > 0 ? detected.existingEngines : ["claude"]);
@@ -3978,15 +4275,15 @@ var initCommand = defineCommand2({
3978
4275
  const defaultCommits = wsDefaults?.commits;
3979
4276
  if (autoYes) {
3980
4277
  if (!detected.name) {
3981
- p2.cancel(tr.detectionFailedYes);
4278
+ p3.cancel(tr.detectionFailedYes);
3982
4279
  process.exit(1);
3983
4280
  }
3984
4281
  const wsPlugins2 = wsDefaults?.plugins ?? {};
3985
4282
  const recommendedPlugins = args.recommended ? buildRecommendedPlugins(cwd) : {};
3986
4283
  const mergedPlugins2 = { ...ALWAYS_ON_PLUGINS, ...wsPlugins2, ...recommendedPlugins };
3987
- p2.log.info(tr.pluginsAlwaysOn(Object.keys(ALWAYS_ON_PLUGINS).join(", ")));
4284
+ p3.log.info(tr.pluginsAlwaysOn(Object.keys(ALWAYS_ON_PLUGINS).join(", ")));
3988
4285
  if (args.recommended && Object.keys(recommendedPlugins).length > 0) {
3989
- p2.log.info(tr.recPluginsEnabled(Object.keys(recommendedPlugins).join(", ")));
4286
+ p3.log.info(tr.recPluginsEnabled(Object.keys(recommendedPlugins).join(", ")));
3990
4287
  }
3991
4288
  const monorepoBlock2 = await buildMonorepoBlock(cwd, detected, {
3992
4289
  scanMonorepo: Boolean(args["scan-monorepo"]),
@@ -4014,27 +4311,27 @@ var initCommand = defineCommand2({
4014
4311
  project: projectBlock,
4015
4312
  ...monorepoBlock2 ? { monorepo: monorepoBlock2 } : {}
4016
4313
  });
4017
- p2.log.success(tr.wroteConfig(configPath));
4314
+ p3.log.success(tr.wroteConfig(configPath));
4018
4315
  if (args.recommended && !detected.qualityGate && fallbackQg) {
4019
- p2.log.info(`Quality gate fallback aplicado: ${fallbackQg.fast}`);
4316
+ p3.log.info(`Quality gate fallback aplicado: ${fallbackQg.fast}`);
4020
4317
  }
4021
4318
  if (!fallbackQg) {
4022
- p2.log.warn(tr.qualityGateNotDetected);
4319
+ p3.log.warn(tr.qualityGateNotDetected);
4023
4320
  }
4024
4321
  if (mode === "coexist") {
4025
- p2.outro(tr.doneExistingUntouched);
4322
+ p3.outro(tr.doneExistingUntouched);
4026
4323
  return;
4027
4324
  }
4028
4325
  if (args.render !== false) renderInline(cwd);
4029
4326
  await offerPreCommitHook(cwd, { autoYes, force: Boolean(args["pre-commit-hook"]), lang });
4030
- p2.outro(tr.done);
4327
+ p3.outro(tr.done);
4031
4328
  return;
4032
4329
  }
4033
- const accept = await p2.confirm({
4330
+ const accept = await p3.confirm({
4034
4331
  message: detected.name ? tr.useTheseValues : tr.projectNameUndetectedAdjust,
4035
4332
  initialValue: Boolean(detected.name)
4036
4333
  });
4037
- if (p2.isCancel(accept)) return cancel3(lang);
4334
+ if (p3.isCancel(accept)) return cancel4(lang);
4038
4335
  let name = detected.name;
4039
4336
  let engines = defaultEngines;
4040
4337
  let branchBase = defaultBranchBase;
@@ -4043,7 +4340,7 @@ var initCommand = defineCommand2({
4043
4340
  let qualityGate = detected.qualityGate;
4044
4341
  let language = defaultLanguage;
4045
4342
  if (!accept || !detected.name) {
4046
- const toAdjust = await p2.multiselect({
4343
+ const toAdjust = await p3.multiselect({
4047
4344
  message: tr.whatToChange,
4048
4345
  options: [
4049
4346
  {
@@ -4063,10 +4360,10 @@ var initCommand = defineCommand2({
4063
4360
  required: !detected.name,
4064
4361
  initialValues: detected.name ? [] : ["name"]
4065
4362
  });
4066
- if (p2.isCancel(toAdjust)) return cancel3(lang);
4363
+ if (p3.isCancel(toAdjust)) return cancel4(lang);
4067
4364
  const adjustments = toAdjust;
4068
4365
  if (adjustments.includes("name") || !name) {
4069
- const value = await p2.text({
4366
+ const value = await p3.text({
4070
4367
  message: tr.projectNameKebab,
4071
4368
  placeholder: detected.name ?? "my-project",
4072
4369
  defaultValue: detected.name ?? "",
@@ -4075,11 +4372,11 @@ var initCommand = defineCommand2({
4075
4372
  return void 0;
4076
4373
  }
4077
4374
  });
4078
- if (p2.isCancel(value)) return cancel3(lang);
4375
+ if (p3.isCancel(value)) return cancel4(lang);
4079
4376
  name = value;
4080
4377
  }
4081
4378
  if (adjustments.includes("language")) {
4082
- const value = await p2.select({
4379
+ const value = await p3.select({
4083
4380
  message: tr.languageForAssets,
4084
4381
  options: [
4085
4382
  { value: "es", label: tr.assetEsLabel },
@@ -4087,73 +4384,73 @@ var initCommand = defineCommand2({
4087
4384
  ],
4088
4385
  initialValue: defaultLanguage
4089
4386
  });
4090
- if (p2.isCancel(value)) return cancel3(lang);
4387
+ if (p3.isCancel(value)) return cancel4(lang);
4091
4388
  language = value;
4092
4389
  }
4093
4390
  if (adjustments.includes("workspace")) {
4094
- const value = await p2.text({
4391
+ const value = await p3.text({
4095
4392
  message: tr.workspaceOptional,
4096
4393
  placeholder: tr.leaveEmpty
4097
4394
  });
4098
- if (p2.isCancel(value)) return cancel3(lang);
4395
+ if (p3.isCancel(value)) return cancel4(lang);
4099
4396
  const trimmed = value.trim();
4100
4397
  if (trimmed) workspace = trimmed;
4101
4398
  }
4102
4399
  if (adjustments.includes("engines")) {
4103
- const value = await p2.multiselect({
4400
+ const value = await p3.multiselect({
4104
4401
  message: tr.enginesToTarget,
4105
4402
  options: ENGINE_OPTIONS,
4106
4403
  required: true,
4107
4404
  initialValues: defaultEngines
4108
4405
  });
4109
- if (p2.isCancel(value)) return cancel3(lang);
4406
+ if (p3.isCancel(value)) return cancel4(lang);
4110
4407
  engines = value;
4111
4408
  }
4112
4409
  if (adjustments.includes("preset")) {
4113
- const value = await p2.text({
4410
+ const value = await p3.text({
4114
4411
  message: tr.stackPresetFreeText,
4115
4412
  placeholder: preset,
4116
4413
  defaultValue: preset
4117
4414
  });
4118
- if (p2.isCancel(value)) return cancel3(lang);
4415
+ if (p3.isCancel(value)) return cancel4(lang);
4119
4416
  preset = value;
4120
4417
  }
4121
4418
  if (adjustments.includes("branchBase")) {
4122
- const value = await p2.text({
4419
+ const value = await p3.text({
4123
4420
  message: tr.baseBranch,
4124
4421
  placeholder: defaultBranchBase,
4125
4422
  defaultValue: defaultBranchBase
4126
4423
  });
4127
- if (p2.isCancel(value)) return cancel3(lang);
4424
+ if (p3.isCancel(value)) return cancel4(lang);
4128
4425
  branchBase = value;
4129
4426
  }
4130
4427
  if (adjustments.includes("qualityGate")) {
4131
- const fastVal = await p2.text({
4428
+ const fastVal = await p3.text({
4132
4429
  message: tr.qualityGateFast,
4133
4430
  placeholder: qualityGate?.fast ?? "pnpm tsc --noEmit",
4134
4431
  defaultValue: qualityGate?.fast ?? ""
4135
4432
  });
4136
- if (p2.isCancel(fastVal)) return cancel3(lang);
4137
- const fullVal = await p2.text({
4433
+ if (p3.isCancel(fastVal)) return cancel4(lang);
4434
+ const fullVal = await p3.text({
4138
4435
  message: tr.qualityGateFull,
4139
4436
  placeholder: qualityGate?.full ?? fastVal,
4140
4437
  defaultValue: qualityGate?.full ?? fastVal
4141
4438
  });
4142
- if (p2.isCancel(fullVal)) return cancel3(lang);
4439
+ if (p3.isCancel(fullVal)) return cancel4(lang);
4143
4440
  if (fastVal.trim() && fullVal.trim()) {
4144
4441
  qualityGate = { fast: fastVal.trim(), full: fullVal.trim() };
4145
4442
  }
4146
4443
  }
4147
4444
  }
4148
4445
  if (!name) {
4149
- p2.cancel(tr.projectNameRequired);
4446
+ p3.cancel(tr.projectNameRequired);
4150
4447
  process.exit(1);
4151
4448
  }
4152
4449
  let pluginsToEnable = await pickPlugins(lang);
4153
- if (pluginsToEnable === null) return cancel3(lang);
4450
+ if (pluginsToEnable === null) return cancel4(lang);
4154
4451
  let pluginsConfig = buildPluginsConfig(pluginsToEnable);
4155
4452
  let agentAssignments = await pickAgentAssignments(pluginsToEnable, lang);
4156
- if (agentAssignments === null) return cancel3(lang);
4453
+ if (agentAssignments === null) return cancel4(lang);
4157
4454
  while (true) {
4158
4455
  const summary = buildConfigPreview(
4159
4456
  {
@@ -4169,8 +4466,8 @@ var initCommand = defineCommand2({
4169
4466
  },
4170
4467
  lang
4171
4468
  );
4172
- p2.note(summary, tr.previewTitle);
4173
- const action = await p2.select({
4469
+ p3.note(summary, tr.previewTitle);
4470
+ const action = await p3.select({
4174
4471
  message: tr.previewAction,
4175
4472
  options: [
4176
4473
  { value: "ok", label: color.green(tr.saveAndContinue) },
@@ -4179,9 +4476,9 @@ var initCommand = defineCommand2({
4179
4476
  ],
4180
4477
  initialValue: "ok"
4181
4478
  });
4182
- if (p2.isCancel(action) || action === "cancel") return cancel3(lang);
4479
+ if (p3.isCancel(action) || action === "cancel") return cancel4(lang);
4183
4480
  if (action === "ok") break;
4184
- const choice = await p2.select({
4481
+ const choice = await p3.select({
4185
4482
  message: tr.whatToEdit,
4186
4483
  options: [
4187
4484
  { value: "name", label: tr.editField(tr.labelProjectName) },
@@ -4195,21 +4492,21 @@ var initCommand = defineCommand2({
4195
4492
  { value: "__back", label: tr.backToPreview }
4196
4493
  ]
4197
4494
  });
4198
- if (p2.isCancel(choice) || choice === "__back") continue;
4495
+ if (p3.isCancel(choice) || choice === "__back") continue;
4199
4496
  const field = choice;
4200
4497
  switch (field) {
4201
4498
  case "name": {
4202
- const v = await p2.text({
4499
+ const v = await p3.text({
4203
4500
  message: tr.projectNameKebab,
4204
4501
  placeholder: name ?? "my-project",
4205
4502
  defaultValue: name ?? "",
4206
4503
  validate: (val) => /^[a-z0-9][a-z0-9-]*$/.test(val) ? void 0 : tr.mustBeKebab
4207
4504
  });
4208
- if (!p2.isCancel(v)) name = v;
4505
+ if (!p3.isCancel(v)) name = v;
4209
4506
  break;
4210
4507
  }
4211
4508
  case "language": {
4212
- const v = await p2.select({
4509
+ const v = await p3.select({
4213
4510
  message: tr.languageForAssets,
4214
4511
  options: [
4215
4512
  { value: "es", label: tr.assetEsLabel },
@@ -4217,50 +4514,50 @@ var initCommand = defineCommand2({
4217
4514
  ],
4218
4515
  initialValue: language
4219
4516
  });
4220
- if (!p2.isCancel(v)) language = v;
4517
+ if (!p3.isCancel(v)) language = v;
4221
4518
  break;
4222
4519
  }
4223
4520
  case "engines": {
4224
- const v = await p2.multiselect({
4521
+ const v = await p3.multiselect({
4225
4522
  message: tr.enginesToTarget,
4226
4523
  options: ENGINE_OPTIONS,
4227
4524
  required: true,
4228
4525
  initialValues: engines
4229
4526
  });
4230
- if (!p2.isCancel(v)) engines = v;
4527
+ if (!p3.isCancel(v)) engines = v;
4231
4528
  break;
4232
4529
  }
4233
4530
  case "preset": {
4234
- const v = await p2.text({
4531
+ const v = await p3.text({
4235
4532
  message: tr.stackPresetFreeText,
4236
4533
  placeholder: preset,
4237
4534
  defaultValue: preset
4238
4535
  });
4239
- if (!p2.isCancel(v)) preset = v;
4536
+ if (!p3.isCancel(v)) preset = v;
4240
4537
  break;
4241
4538
  }
4242
4539
  case "branchBase": {
4243
- const v = await p2.text({
4540
+ const v = await p3.text({
4244
4541
  message: tr.baseBranch,
4245
4542
  placeholder: branchBase,
4246
4543
  defaultValue: branchBase
4247
4544
  });
4248
- if (!p2.isCancel(v)) branchBase = v;
4545
+ if (!p3.isCancel(v)) branchBase = v;
4249
4546
  break;
4250
4547
  }
4251
4548
  case "qualityGate": {
4252
- const fastVal = await p2.text({
4549
+ const fastVal = await p3.text({
4253
4550
  message: tr.qualityGateFast,
4254
4551
  placeholder: qualityGate?.fast ?? "pnpm tsc --noEmit",
4255
4552
  defaultValue: qualityGate?.fast ?? ""
4256
4553
  });
4257
- if (p2.isCancel(fastVal)) break;
4258
- const fullVal = await p2.text({
4554
+ if (p3.isCancel(fastVal)) break;
4555
+ const fullVal = await p3.text({
4259
4556
  message: tr.qualityGateFull,
4260
4557
  placeholder: qualityGate?.full ?? fastVal,
4261
4558
  defaultValue: qualityGate?.full ?? fastVal
4262
4559
  });
4263
- if (p2.isCancel(fullVal)) break;
4560
+ if (p3.isCancel(fullVal)) break;
4264
4561
  if (fastVal.trim() && fullVal.trim()) {
4265
4562
  qualityGate = {
4266
4563
  fast: fastVal.trim(),
@@ -4295,7 +4592,7 @@ var initCommand = defineCommand2({
4295
4592
  const promptsResult = loadPrompts(pluginsConfig);
4296
4593
  if (promptsResult.prompts.length > 0) {
4297
4594
  const collected = await runProjectPrompts(promptsResult.prompts, lang);
4298
- if (collected === null) return cancel3(lang);
4595
+ if (collected === null) return cancel4(lang);
4299
4596
  if (Object.keys(collected).length > 0) {
4300
4597
  project = collected;
4301
4598
  }
@@ -4330,26 +4627,26 @@ var initCommand = defineCommand2({
4330
4627
  },
4331
4628
  ...monorepoBlock ? { monorepo: monorepoBlock } : {}
4332
4629
  });
4333
- p2.log.success(tr.wroteConfig(configPath));
4630
+ p3.log.success(tr.wroteConfig(configPath));
4334
4631
  if (mode === "coexist") {
4335
- p2.outro(tr.doneExistingUntouched);
4632
+ p3.outro(tr.doneExistingUntouched);
4336
4633
  return;
4337
4634
  }
4338
4635
  if (args.render === false) {
4339
- p2.outro(tr.doneSkippedRender);
4636
+ p3.outro(tr.doneSkippedRender);
4340
4637
  return;
4341
4638
  }
4342
- const shouldRender = await p2.confirm({
4639
+ const shouldRender = await p3.confirm({
4343
4640
  message: tr.renderNow,
4344
4641
  initialValue: true
4345
4642
  });
4346
- if (p2.isCancel(shouldRender) || !shouldRender) {
4347
- p2.outro(tr.doneRunLater);
4643
+ if (p3.isCancel(shouldRender) || !shouldRender) {
4644
+ p3.outro(tr.doneRunLater);
4348
4645
  return;
4349
4646
  }
4350
4647
  renderInline(cwd);
4351
4648
  await offerPreCommitHook(cwd, { autoYes, force: Boolean(args["pre-commit-hook"]), lang });
4352
- p2.outro(tr.harnessReady);
4649
+ p3.outro(tr.harnessReady);
4353
4650
  }
4354
4651
  });
4355
4652
  function normalizeLang(raw) {
@@ -4362,13 +4659,13 @@ async function chooseAdoptionMode(cwd, infra, projectName, args) {
4362
4659
  if (!infra.present) return "fresh";
4363
4660
  const tr = t(args.lang);
4364
4661
  if (args.yes) {
4365
- p2.log.warn(tr.existingInfraYesMode);
4366
- p2.note(formatInfraSummary(infra, args.lang), tr.filesFoundTitle);
4662
+ p3.log.warn(tr.existingInfraYesMode);
4663
+ p3.note(formatInfraSummary(infra, args.lang), tr.filesFoundTitle);
4367
4664
  return "coexist";
4368
4665
  }
4369
- p2.log.warn(tr.existingInfraDetected);
4370
- p2.note(formatInfraSummary(infra, args.lang), tr.filesFoundTitle);
4371
- const choice = await p2.select({
4666
+ p3.log.warn(tr.existingInfraDetected);
4667
+ p3.note(formatInfraSummary(infra, args.lang), tr.filesFoundTitle);
4668
+ const choice = await p3.select({
4372
4669
  message: tr.howToAdopt,
4373
4670
  options: [
4374
4671
  { value: "coexist", label: tr.coexistLabel, hint: tr.coexistHint },
@@ -4376,24 +4673,24 @@ async function chooseAdoptionMode(cwd, infra, projectName, args) {
4376
4673
  ],
4377
4674
  initialValue: "coexist"
4378
4675
  });
4379
- if (p2.isCancel(choice)) return null;
4676
+ if (p3.isCancel(choice)) return null;
4380
4677
  if (choice === "replace") {
4381
- const confirm11 = await p2.confirm({
4678
+ const confirm11 = await p3.confirm({
4382
4679
  message: tr.replaceConfirm,
4383
4680
  initialValue: false
4384
4681
  });
4385
- if (p2.isCancel(confirm11) || !confirm11) return null;
4682
+ if (p3.isCancel(confirm11) || !confirm11) return null;
4386
4683
  const backup = createMigrationBackup(cwd, projectName ?? "unknown");
4387
- p2.log.success(tr.backedUp(backup.movedPaths.length, backup.path));
4684
+ p3.log.success(tr.backedUp(backup.movedPaths.length, backup.path));
4388
4685
  removeOriginals(cwd, backup.movedPaths);
4389
- p2.log.info(tr.removedOriginals(cwd));
4686
+ p3.log.info(tr.removedOriginals(cwd));
4390
4687
  return "replace";
4391
4688
  }
4392
4689
  return "coexist";
4393
4690
  }
4394
4691
  async function pickPlugins(lang) {
4395
4692
  const alwaysOn = Object.keys(ALWAYS_ON_PLUGINS);
4396
- p2.log.info(t(lang).pluginsAlwaysOn(alwaysOn.join(", ")));
4693
+ p3.log.info(t(lang).pluginsAlwaysOn(alwaysOn.join(", ")));
4397
4694
  const ids = listKnownPluginIds().filter((id) => !(id in ALWAYS_ON_PLUGINS));
4398
4695
  if (ids.length === 0) return [];
4399
4696
  const options = ids.map((id) => {
@@ -4410,13 +4707,13 @@ async function pickPlugins(lang) {
4410
4707
  hint: plugin?.manifest.description
4411
4708
  };
4412
4709
  });
4413
- const selected = await p2.multiselect({
4710
+ const selected = await p3.multiselect({
4414
4711
  message: t(lang).pluginsToEnable,
4415
4712
  options,
4416
4713
  required: false,
4417
4714
  initialValues: []
4418
4715
  });
4419
- if (p2.isCancel(selected)) return null;
4716
+ if (p3.isCancel(selected)) return null;
4420
4717
  return selected;
4421
4718
  }
4422
4719
  async function pickAgentAssignments(enabledPlugins, lang) {
@@ -4442,13 +4739,13 @@ async function pickAgentAssignments(enabledPlugins, lang) {
4442
4739
  }
4443
4740
  if (recommendations.length === 0) return {};
4444
4741
  const summary = recommendations.map((r) => ` \xB7 ${r.id} (${r.pluginId}) \u2192 ${r.recommendedAgent}`).join("\n");
4445
- p2.log.message(`${tr.recommendedAssignments}
4742
+ p3.log.message(`${tr.recommendedAssignments}
4446
4743
  ${summary}`);
4447
- const accept = await p2.confirm({
4744
+ const accept = await p3.confirm({
4448
4745
  message: tr.useAssignments,
4449
4746
  initialValue: true
4450
4747
  });
4451
- if (p2.isCancel(accept)) return null;
4748
+ if (p3.isCancel(accept)) return null;
4452
4749
  if (accept) return {};
4453
4750
  const overrides = {};
4454
4751
  const agentOptions = [
@@ -4461,12 +4758,12 @@ ${summary}`);
4461
4758
  { value: "explorer", label: tr.roleExplorer }
4462
4759
  ];
4463
4760
  for (const rec of recommendations) {
4464
- const choice = await p2.select({
4761
+ const choice = await p3.select({
4465
4762
  message: tr.agentFor(rec.id, rec.pluginId),
4466
4763
  options: agentOptions,
4467
4764
  initialValue: rec.recommendedAgent
4468
4765
  });
4469
- if (p2.isCancel(choice)) return null;
4766
+ if (p3.isCancel(choice)) return null;
4470
4767
  if (choice !== rec.recommendedAgent) {
4471
4768
  overrides[rec.id] = choice;
4472
4769
  }
@@ -4494,7 +4791,7 @@ function isGitHubRepo(cwd) {
4494
4791
  function renderInline(cwd) {
4495
4792
  const result = runRender(cwd, false);
4496
4793
  if (!result.ok) {
4497
- p2.log.error(result.reason ?? "Render failed");
4794
+ p3.log.error(result.reason ?? "Render failed");
4498
4795
  return;
4499
4796
  }
4500
4797
  const counts = result.entries.reduce((acc, e) => {
@@ -4509,9 +4806,9 @@ function renderInline(cwd) {
4509
4806
  if (counts.unchanged) parts.push(dim(`${counts.unchanged} unchanged`));
4510
4807
  const summary = parts.length > 0 ? ` ${dim("\u2014")} ${parts.join(dim(", "))}` : "";
4511
4808
  if (result.written) {
4512
- p2.log.success(`Rendered ${result.filePath}${summary}`);
4809
+ p3.log.success(`Rendered ${result.filePath}${summary}`);
4513
4810
  } else {
4514
- p2.log.info(`No render needed${summary}`);
4811
+ p3.log.info(`No render needed${summary}`);
4515
4812
  }
4516
4813
  if (result.engineResult) {
4517
4814
  const written = result.engineResult.written.filter((w) => w.path !== "CLAUDE.md");
@@ -4519,14 +4816,14 @@ function renderInline(cwd) {
4519
4816
  const lines = written.slice(0, 12).map((w) => ` ${dim("+")} ${w.path}`).join("\n");
4520
4817
  const more = written.length > 12 ? `
4521
4818
  ${dim(`\u2026 +${written.length - 12} more`)}` : "";
4522
- p2.log.message(`${dim("Files written:")}
4819
+ p3.log.message(`${dim("Files written:")}
4523
4820
  ${lines}${more}`);
4524
4821
  }
4525
4822
  for (const s of result.engineResult.skipped) {
4526
- p2.log.warn(`Skipped ${s.path}: ${s.reason}`);
4823
+ p3.log.warn(`Skipped ${s.path}: ${s.reason}`);
4527
4824
  }
4528
4825
  for (const w of result.engineResult.warnings) {
4529
- p2.log.warn(w);
4826
+ p3.log.warn(w);
4530
4827
  }
4531
4828
  }
4532
4829
  }
@@ -4534,19 +4831,19 @@ async function offerPreCommitHook(cwd, opts) {
4534
4831
  if (!existsSync16(join12(cwd, ".git"))) return;
4535
4832
  let wanted = opts.force;
4536
4833
  if (!wanted && !opts.autoYes) {
4537
- const answer = await p2.confirm({
4834
+ const answer = await p3.confirm({
4538
4835
  message: t(opts.lang).preCommitHookPrompt,
4539
4836
  initialValue: false
4540
4837
  });
4541
- if (p2.isCancel(answer)) return;
4838
+ if (p3.isCancel(answer)) return;
4542
4839
  wanted = answer;
4543
4840
  }
4544
4841
  if (!wanted) return;
4545
4842
  const result = writePreCommitHook(cwd);
4546
4843
  if (result.ok) {
4547
- p2.log.success(t(opts.lang).preCommitHookWritten(result.path));
4844
+ p3.log.success(t(opts.lang).preCommitHookWritten(result.path));
4548
4845
  } else {
4549
- p2.log.warn(t(opts.lang).preCommitHookExists(result.path));
4846
+ p3.log.warn(t(opts.lang).preCommitHookExists(result.path));
4550
4847
  }
4551
4848
  }
4552
4849
  function writePreCommitHook(cwd) {
@@ -4573,8 +4870,8 @@ function writePreCommitHook(cwd) {
4573
4870
  }
4574
4871
  return { ok: true, path: relPath };
4575
4872
  }
4576
- function cancel3(lang) {
4577
- p2.cancel(t(lang).cancelled);
4873
+ function cancel4(lang) {
4874
+ p3.cancel(t(lang).cancelled);
4578
4875
  process.exit(0);
4579
4876
  }
4580
4877
  function buildPluginsConfig(ids) {
@@ -4625,8 +4922,8 @@ function buildConfigPreview(state, lang) {
4625
4922
  }
4626
4923
  async function runProjectPrompts(prompts, lang) {
4627
4924
  const tr = t(lang);
4628
- p2.note(tr.projectPromptsIntro, "project");
4629
- const choice = await p2.select({
4925
+ p3.note(tr.projectPromptsIntro, "project");
4926
+ const choice = await p3.select({
4630
4927
  message: tr.projectPromptsAsk,
4631
4928
  options: [
4632
4929
  { value: "run", label: tr.projectPromptsRun },
@@ -4634,9 +4931,9 @@ async function runProjectPrompts(prompts, lang) {
4634
4931
  ],
4635
4932
  initialValue: "run"
4636
4933
  });
4637
- if (p2.isCancel(choice)) return null;
4934
+ if (p3.isCancel(choice)) return null;
4638
4935
  if (choice === "skip") {
4639
- p2.log.info(tr.projectPromptsSkipNote);
4936
+ p3.log.info(tr.projectPromptsSkipNote);
4640
4937
  return {};
4641
4938
  }
4642
4939
  const collected = {};
@@ -4646,7 +4943,7 @@ async function runProjectPrompts(prompts, lang) {
4646
4943
  ];
4647
4944
  for (const { header, group } of phases) {
4648
4945
  if (group.length === 0) continue;
4649
- p2.note(header);
4946
+ p3.note(header);
4650
4947
  for (const prompt of group) {
4651
4948
  const subKey = prompt.key.startsWith("project.") ? prompt.key.slice("project.".length) : null;
4652
4949
  if (!subKey) continue;
@@ -4664,24 +4961,24 @@ async function askProjectPrompt(prompt, lang) {
4664
4961
  const message = prompt.optional ? `${question} ${dim(tr.projectPromptsOptional)}` : question;
4665
4962
  switch (prompt.type) {
4666
4963
  case "string": {
4667
- const v = await p2.text({
4964
+ const v = await p3.text({
4668
4965
  message,
4669
4966
  placeholder: prompt.placeholder ?? "",
4670
4967
  // Without defaultValue, @clack/prompts returns the placeholder text on
4671
4968
  // an empty submit — which would persist the hint as a real value.
4672
4969
  defaultValue: ""
4673
4970
  });
4674
- if (p2.isCancel(v)) return null;
4971
+ if (p3.isCancel(v)) return null;
4675
4972
  const trimmed = v.trim();
4676
4973
  return trimmed ? trimmed : void 0;
4677
4974
  }
4678
4975
  case "string-list": {
4679
- const v = await p2.text({
4976
+ const v = await p3.text({
4680
4977
  message,
4681
4978
  placeholder: prompt.placeholder ?? "",
4682
4979
  defaultValue: ""
4683
4980
  });
4684
- if (p2.isCancel(v)) return null;
4981
+ if (p3.isCancel(v)) return null;
4685
4982
  const items = v.split(",").map((s) => s.trim()).filter(Boolean);
4686
4983
  return items.length > 0 ? items : void 0;
4687
4984
  }
@@ -4692,17 +4989,17 @@ async function askProjectPrompt(prompt, lang) {
4692
4989
  }));
4693
4990
  if (opts.length === 0) return void 0;
4694
4991
  if (prompt.optional) opts.push({ value: "__skip__", label: tr.projectPromptSkipOption });
4695
- const v = await p2.select({ message: question, options: opts });
4696
- if (p2.isCancel(v)) return null;
4992
+ const v = await p3.select({ message: question, options: opts });
4993
+ if (p3.isCancel(v)) return null;
4697
4994
  return v === "__skip__" ? void 0 : v;
4698
4995
  }
4699
4996
  case "boolean": {
4700
- const v = await p2.confirm({ message, initialValue: false });
4701
- if (p2.isCancel(v)) return null;
4997
+ const v = await p3.confirm({ message, initialValue: false });
4998
+ if (p3.isCancel(v)) return null;
4702
4999
  return v;
4703
5000
  }
4704
5001
  case "number": {
4705
- const v = await p2.text({
5002
+ const v = await p3.text({
4706
5003
  message,
4707
5004
  placeholder: prompt.placeholder ?? "",
4708
5005
  defaultValue: "",
@@ -4711,7 +5008,7 @@ async function askProjectPrompt(prompt, lang) {
4711
5008
  return /^-?\d+(\.\d+)?$/.test(val.trim()) ? void 0 : "n\xFAmero inv\xE1lido";
4712
5009
  }
4713
5010
  });
4714
- if (p2.isCancel(v)) return null;
5011
+ if (p3.isCancel(v)) return null;
4715
5012
  const trimmed = v.trim();
4716
5013
  return trimmed ? Number(trimmed) : void 0;
4717
5014
  }
@@ -4724,11 +5021,11 @@ async function buildMonorepoBlock(cwd, detected, opts) {
4724
5021
  tool: detected.monorepo.tool,
4725
5022
  workspaces: []
4726
5023
  };
4727
- if (!opts.scanMonorepo) return block;
5024
+ if (!opts.scanMonorepo && opts.autoYes) return block;
4728
5025
  const found = scanMonorepoWorkspaces(cwd);
4729
5026
  if (found.length === 0) {
4730
5027
  if (!opts.autoYes) {
4731
- p2.log.info(
5028
+ p3.log.info(
4732
5029
  "Monorepo detectado pero no se encontraron workspaces en pnpm-workspace.yaml/package.json#workspaces."
4733
5030
  );
4734
5031
  }
@@ -4736,7 +5033,7 @@ async function buildMonorepoBlock(cwd, detected, opts) {
4736
5033
  }
4737
5034
  if (opts.autoYes) {
4738
5035
  block.workspaces = found.map((d) => buildWorkspaceEntry(d, opts.rootPreset, d.suggestedPreset));
4739
- p2.log.info(
5036
+ p3.log.info(
4740
5037
  `Detectados ${found.length} workspace(s) en monorepo: ${found.map((w) => w.path).join(", ")}`
4741
5038
  );
4742
5039
  return block;
@@ -4745,27 +5042,27 @@ async function buildMonorepoBlock(cwd, detected, opts) {
4745
5042
  const fw = w.framework ? dim(` [${w.framework}]`) : "";
4746
5043
  return ` \xB7 ${w.path}${fw} ${dim("\u2192")} ${w.suggestedPreset}`;
4747
5044
  }).join("\n");
4748
- p2.log.message(`${dim("Workspaces detectados en el monorepo:")}
5045
+ p3.log.message(`${dim("Workspaces detectados en el monorepo:")}
4749
5046
  ${overview}`);
4750
- const addAll = await p2.confirm({
5047
+ const addAll = await p3.confirm({
4751
5048
  message: `\xBFAgregar ${found.length} workspace(s) a monorepo.workspaces[]?`,
4752
5049
  initialValue: true
4753
5050
  });
4754
- if (p2.isCancel(addAll) || !addAll) return block;
4755
- const useSuggested = await p2.confirm({
5051
+ if (p3.isCancel(addAll) || !addAll) return block;
5052
+ const useSuggested = await p3.confirm({
4756
5053
  message: "\xBFUsar el preset sugerido en cada workspace?",
4757
5054
  initialValue: true
4758
5055
  });
4759
- if (p2.isCancel(useSuggested)) return block;
5056
+ if (p3.isCancel(useSuggested)) return block;
4760
5057
  for (const d of found) {
4761
5058
  let preset = d.suggestedPreset;
4762
5059
  if (!useSuggested) {
4763
- const value = await p2.text({
5060
+ const value = await p3.text({
4764
5061
  message: `Preset para ${d.path}`,
4765
5062
  placeholder: preset,
4766
5063
  defaultValue: preset
4767
5064
  });
4768
- if (!p2.isCancel(value) && value.trim()) {
5065
+ if (!p3.isCancel(value) && value.trim()) {
4769
5066
  preset = value.trim();
4770
5067
  }
4771
5068
  }
@@ -4789,15 +5086,37 @@ function formatProjectValue(v) {
4789
5086
 
4790
5087
  // src/commands/doctor.ts
4791
5088
  import { defineCommand as defineCommand3 } from "citty";
4792
- import * as p3 from "@clack/prompts";
4793
- import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync8 } from "fs";
4794
- import { join as join14, resolve as resolve12, relative as relative4 } from "path";
5089
+ import * as p4 from "@clack/prompts";
5090
+ import { existsSync as existsSync19, readFileSync as readFileSync15, readdirSync as readdirSync9 } from "fs";
5091
+ import { join as join15, resolve as resolve12, relative as relative4 } from "path";
4795
5092
 
4796
- // src/lib/health.ts
4797
- import { existsSync as existsSync17, readFileSync as readFileSync14, readdirSync as readdirSync7 } from "fs";
5093
+ // src/lib/which.ts
5094
+ import { existsSync as existsSync17, statSync as statSync6 } from "fs";
4798
5095
  import { join as join13 } from "path";
5096
+ function hasBinary(name) {
5097
+ const pathEnv = process.env.PATH ?? "";
5098
+ const sep2 = process.platform === "win32" ? ";" : ":";
5099
+ const dirs = pathEnv.split(sep2).filter(Boolean);
5100
+ const exts = process.platform === "win32" ? (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";") : [""];
5101
+ for (const dir of dirs) {
5102
+ for (const ext of exts) {
5103
+ const candidate = join13(dir, name + ext);
5104
+ if (existsSync17(candidate)) {
5105
+ try {
5106
+ if (statSync6(candidate).isFile()) return true;
5107
+ } catch {
5108
+ }
5109
+ }
5110
+ }
5111
+ }
5112
+ return false;
5113
+ }
5114
+
5115
+ // src/lib/health.ts
5116
+ import { existsSync as existsSync18, readFileSync as readFileSync14, readdirSync as readdirSync8 } from "fs";
5117
+ import { join as join14 } from "path";
4799
5118
  function listMarkers(filePath) {
4800
- if (!existsSync17(filePath)) return [];
5119
+ if (!existsSync18(filePath)) return [];
4801
5120
  const content = readFileSync14(filePath, "utf-8");
4802
5121
  const re = /<!-- navori:managed [^>]*-->/g;
4803
5122
  const result = [];
@@ -4843,12 +5162,13 @@ function scanManagedDrift(cwd, config) {
4843
5162
  }
4844
5163
  }
4845
5164
  const files = [];
4846
- if (existsSync17(join13(cwd, "CLAUDE.md"))) files.push("CLAUDE.md");
5165
+ if (existsSync18(join14(cwd, "CLAUDE.md"))) files.push("CLAUDE.md");
5166
+ if (existsSync18(join14(cwd, "AGENTS.md"))) files.push("AGENTS.md");
4847
5167
  for (const dir of [".claude/agents", ".claude/skills"]) {
4848
- const absDir = join13(cwd, dir);
4849
- if (!existsSync17(absDir)) continue;
5168
+ const absDir = join14(cwd, dir);
5169
+ if (!existsSync18(absDir)) continue;
4850
5170
  try {
4851
- for (const file of readdirSync7(absDir)) {
5171
+ for (const file of readdirSync8(absDir)) {
4852
5172
  if (file.endsWith(".md")) files.push(`${dir}/${file}`);
4853
5173
  }
4854
5174
  } catch {
@@ -4856,7 +5176,7 @@ function scanManagedDrift(cwd, config) {
4856
5176
  }
4857
5177
  }
4858
5178
  for (const rel of files) {
4859
- const abs = join13(cwd, rel);
5179
+ const abs = join14(cwd, rel);
4860
5180
  const fileContent = (() => {
4861
5181
  try {
4862
5182
  return readFileSync14(abs, "utf-8");
@@ -4901,8 +5221,8 @@ function scanManagedDrift(cwd, config) {
4901
5221
  return out;
4902
5222
  }
4903
5223
  function scanManagedOrder(cwd, config) {
4904
- const claudeMdPath = join13(cwd, "CLAUDE.md");
4905
- if (!existsSync17(claudeMdPath)) return null;
5224
+ const claudeMdPath = join14(cwd, "CLAUDE.md");
5225
+ if (!existsSync18(claudeMdPath)) return null;
4906
5226
  const content = readFileSync14(claudeMdPath, "utf-8");
4907
5227
  const current = listMarkers(claudeMdPath).map((m) => m.id);
4908
5228
  if (current.length < 2) return null;
@@ -4914,7 +5234,47 @@ function scanManagedOrder(cwd, config) {
4914
5234
  if (!rank.has(id)) rank.set(id, i);
4915
5235
  });
4916
5236
  const expected = current.map((id, i) => ({ id, i, key: rank.has(id) ? rank.get(id) : canonical.length + i })).sort((a, z8) => a.key - z8.key || a.i - z8.i).map((x) => x.id);
4917
- return { current, expected, interleaved: result.blockedByInterleaving };
5237
+ const lead = expected[0];
5238
+ const leadPos = lead !== void 0 ? current.indexOf(lead) : -1;
5239
+ const misplacedFirst = lead !== void 0 && leadPos > 0 ? { id: lead, currentPos: leadPos + 1, total: current.length } : null;
5240
+ return { current, expected, interleaved: result.blockedByInterleaving, misplacedFirst };
5241
+ }
5242
+ function scanMalformedMarkers(cwd) {
5243
+ const out = [];
5244
+ const files = [];
5245
+ if (existsSync18(join14(cwd, "CLAUDE.md"))) files.push("CLAUDE.md");
5246
+ if (existsSync18(join14(cwd, "AGENTS.md"))) files.push("AGENTS.md");
5247
+ for (const dir of [".claude/agents", ".claude/skills"]) {
5248
+ const absDir = join14(cwd, dir);
5249
+ if (!existsSync18(absDir)) continue;
5250
+ try {
5251
+ for (const file of readdirSync8(absDir)) {
5252
+ if (file.endsWith(".md")) files.push(`${dir}/${file}`);
5253
+ }
5254
+ } catch {
5255
+ continue;
5256
+ }
5257
+ }
5258
+ const prefixes = ["<!-- /navori:managed", "<!-- navori:managed"];
5259
+ for (const rel of files) {
5260
+ let content;
5261
+ try {
5262
+ content = readFileSync14(join14(cwd, rel), "utf-8");
5263
+ } catch {
5264
+ continue;
5265
+ }
5266
+ content.split("\n").forEach((lineText, i) => {
5267
+ for (const prefix of prefixes) {
5268
+ const idx = lineText.indexOf(prefix);
5269
+ if (idx === -1) continue;
5270
+ if (!lineText.slice(idx + prefix.length).includes("-->")) {
5271
+ out.push({ filePath: rel, line: i + 1, snippet: lineText.trim().slice(0, 80) });
5272
+ }
5273
+ break;
5274
+ }
5275
+ });
5276
+ }
5277
+ return out;
4918
5278
  }
4919
5279
  function suggestNextSteps(state) {
4920
5280
  const steps = [];
@@ -4936,8 +5296,10 @@ function suggestNextSteps(state) {
4936
5296
  steps.push("Corre 'navori render --apply' para reordenar los bloques de CLAUDE.md al orden can\xF3nico.");
4937
5297
  }
4938
5298
  if (state.orderReport?.interleaved) {
5299
+ const mf = state.orderReport.misplacedFirst;
5300
+ const lead = mf ? ` (p.ej. '${mf.id}' est\xE1 en posici\xF3n ${mf.currentPos} de ${mf.total} y deber\xEDa ir 1\xBA)` : "";
4939
5301
  steps.push(
4940
- "Mueve el texto que tienes entre bloques managed de CLAUDE.md arriba del primer bloque o abajo del \xFAltimo; luego corre 'navori render --apply' para reordenarlos."
5302
+ `Mueve el texto que tienes entre bloques managed de CLAUDE.md arriba del primer bloque o abajo del \xFAltimo${lead}; luego corre 'navori render --apply' para reordenarlos.`
4941
5303
  );
4942
5304
  }
4943
5305
  if (steps.length === 0) {
@@ -4964,20 +5326,20 @@ var doctorCommand = defineCommand3({
4964
5326
  const cwd = resolve12(args.cwd ?? process.cwd());
4965
5327
  const configPath = `${cwd}/navori.config.json`;
4966
5328
  const claudeMdPath = `${cwd}/CLAUDE.md`;
4967
- if (!args.json) p3.intro(brand("doctor"));
4968
- if (!existsSync18(cwd)) {
5329
+ if (!args.json) p4.intro(brand("doctor"));
5330
+ if (!existsSync19(cwd)) {
4969
5331
  if (args.json) {
4970
5332
  console.log(JSON.stringify({ ok: false, error: "directory-missing", cwd }));
4971
5333
  } else {
4972
- p3.cancel(`Directory not found: ${cwd}`);
5334
+ p4.cancel(`Directory not found: ${cwd}`);
4973
5335
  }
4974
5336
  process.exit(1);
4975
5337
  }
4976
- if (!existsSync18(configPath)) {
5338
+ if (!existsSync19(configPath)) {
4977
5339
  if (args.json) {
4978
5340
  console.log(JSON.stringify({ ok: false, error: "config-missing", configPath }));
4979
5341
  } else {
4980
- p3.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
5342
+ p4.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
4981
5343
  }
4982
5344
  process.exit(1);
4983
5345
  }
@@ -4989,7 +5351,7 @@ var doctorCommand = defineCommand3({
4989
5351
  if (args.json) {
4990
5352
  console.log(JSON.stringify({ ok: false, error: "config-invalid", message: err.message, issues: err.issues }));
4991
5353
  } else {
4992
- p3.cancel(err.message);
5354
+ p4.cancel(err.message);
4993
5355
  if (err.issues) {
4994
5356
  for (const issue of err.issues) {
4995
5357
  console.error(` - ${issue.path.join(".") || "(root)"}: ${issue.message}`);
@@ -5006,6 +5368,9 @@ var doctorCommand = defineCommand3({
5006
5368
  const orderReport = scanManagedOrder(cwd, config);
5007
5369
  const corruptedSettings = scanCorruptedSettings(cwd);
5008
5370
  const missingInvariants = scanMissingInvariants(cwd, config);
5371
+ const malformedMarkers = scanMalformedMarkers(cwd);
5372
+ const missingExternalTools = scanMissingExternalTools(config);
5373
+ const monorepoDrift = scanMonorepoDrift(cwd, config);
5009
5374
  const resolvedPreset = config.preset !== "custom" ? resolvePreset(config.preset, cwd) : null;
5010
5375
  const missingPreset = config.preset !== "custom" && resolvedPreset === null ? config.preset : null;
5011
5376
  const presetOverride = resolvedPreset?.source === "local" && presetExists(config.preset) ? config.preset : null;
@@ -5020,10 +5385,10 @@ var doctorCommand = defineCommand3({
5020
5385
  configPath,
5021
5386
  config,
5022
5387
  checks: {
5023
- claudeMdExists: existsSync18(claudeMdPath),
5024
- agentsMdExists: existsSync18(`${cwd}/AGENTS.md`),
5025
- claudeDirExists: existsSync18(`${cwd}/.claude`),
5026
- progressDirExists: existsSync18(`${cwd}/${config.progress?.dir ?? "progress"}`)
5388
+ claudeMdExists: existsSync19(claudeMdPath),
5389
+ agentsMdExists: existsSync19(`${cwd}/AGENTS.md`),
5390
+ claudeDirExists: existsSync19(`${cwd}/.claude`),
5391
+ progressDirExists: existsSync19(`${cwd}/${config.progress?.dir ?? "progress"}`)
5027
5392
  },
5028
5393
  managedBlocks: markers,
5029
5394
  missingPlugins,
@@ -5031,6 +5396,9 @@ var doctorCommand = defineCommand3({
5031
5396
  orderReport,
5032
5397
  corruptedSettings,
5033
5398
  missingInvariants,
5399
+ malformedMarkers,
5400
+ missingExternalTools,
5401
+ monorepoDrift,
5034
5402
  missingPreset,
5035
5403
  presetOverride,
5036
5404
  missingPresetFiles,
@@ -5044,7 +5412,7 @@ var doctorCommand = defineCommand3({
5044
5412
  if (Boolean(args.strict) && drifts.length > 0) process.exit(1);
5045
5413
  return;
5046
5414
  }
5047
- p3.note(
5415
+ p4.note(
5048
5416
  kv([
5049
5417
  ["name", accent(config.name)],
5050
5418
  ["version", config.version],
@@ -5057,7 +5425,7 @@ var doctorCommand = defineCommand3({
5057
5425
  ]),
5058
5426
  `Config \xB7 ${dim(configPath)}`
5059
5427
  );
5060
- p3.note(
5428
+ p4.note(
5061
5429
  [
5062
5430
  ` ${check(report.checks.claudeMdExists)} CLAUDE.md`,
5063
5431
  ` ${check(report.checks.agentsMdExists)} AGENTS.md`,
@@ -5072,7 +5440,7 @@ var doctorCommand = defineCommand3({
5072
5440
  const src = m.source ?? dim("(unknown source)");
5073
5441
  return ` ${color.cyan(sym.bullet)} ${accent(m.id)} ${dim("\u2190")} ${src}${ver}`;
5074
5442
  });
5075
- p3.note(lines.join("\n"), `Managed blocks in CLAUDE.md \xB7 ${markers.length}`);
5443
+ p4.note(lines.join("\n"), `Managed blocks in CLAUDE.md \xB7 ${markers.length}`);
5076
5444
  }
5077
5445
  const assignments = collectAssignments(config);
5078
5446
  if (assignments.length > 0) {
@@ -5080,25 +5448,25 @@ var doctorCommand = defineCommand3({
5080
5448
  const override = a.override ? ` ${dim("(overridden)")}` : "";
5081
5449
  return ` ${color.cyan(sym.bullet)} ${accent(a.id)} ${dim("\u2192")} ${a.agent}${override}`;
5082
5450
  });
5083
- p3.note(lines.join("\n"), `Skill \u2192 agent assignments \xB7 ${assignments.length}`);
5451
+ p4.note(lines.join("\n"), `Skill \u2192 agent assignments \xB7 ${assignments.length}`);
5084
5452
  }
5085
5453
  if (missingPlugins.length > 0) {
5086
5454
  const lines = missingPlugins.map((m) => ` ${color.red(sym.fail)} ${m.id} ${dim(`\u2014 ${m.reason}`)}`);
5087
- p3.log.warn(`Plugins declared in config but not loadable (${missingPlugins.length}):
5455
+ p4.log.warn(`Plugins declared in config but not loadable (${missingPlugins.length}):
5088
5456
  ${lines.join("\n")}`);
5089
5457
  }
5090
5458
  if (missingPreset !== null) {
5091
- p3.log.warn(
5459
+ p4.log.warn(
5092
5460
  `Preset '${missingPreset}' declarado en config pero no existe (ni local en .navori/presets/${missingPreset}/ ni bundled) \u2014 el render cae al baseline (sin los extras del preset). Corre 'navori preset init ${missingPreset}', 'navori configure', o usa un preset v\xE1lido / 'custom'.`
5093
5461
  );
5094
5462
  }
5095
5463
  if (presetOverride) {
5096
- p3.log.warn(
5464
+ p4.log.warn(
5097
5465
  `Preset local '${presetOverride}' (.navori/presets/${presetOverride}/) sombrea el preset oficial del mismo nombre \u2014 se usa el local. Ren\xF3mbralo si el override no es intencional.`
5098
5466
  );
5099
5467
  }
5100
5468
  if (placeholderName) {
5101
- p3.log.warn(
5469
+ p4.log.warn(
5102
5470
  `El name '${placeholderName}' parece un placeholder de scaffold (probablemente heredado del package.json sin renombrar). Edita "name" en navori.config.json si no es el nombre real del repo.`
5103
5471
  );
5104
5472
  }
@@ -5106,19 +5474,19 @@ ${lines.join("\n")}`);
5106
5474
  const lines = missingPresetFiles.map(
5107
5475
  (f) => ` ${color.red(sym.fail)} ${accent(f.id)} ${dim(`\u2014 falta ${f.path}`)}`
5108
5476
  );
5109
- p3.log.warn(
5477
+ p4.log.warn(
5110
5478
  `Extras del preset '${config.preset}' sin archivo (${missingPresetFiles.length}) \u2014 el render fallar\xE1 al leerlos; cr\xE9alos o qu\xEDtalos del manifest:
5111
5479
  ${lines.join("\n")}`
5112
5480
  );
5113
5481
  }
5114
5482
  const missingLocalSkills = (config.project?.localSkills ?? []).filter(
5115
- (name) => !existsSync18(join14(cwd, ".claude/skills", `${name}.md`))
5483
+ (name) => !existsSync19(join15(cwd, ".claude/skills", `${name}.md`))
5116
5484
  );
5117
5485
  if (missingLocalSkills.length > 0) {
5118
5486
  const lines = missingLocalSkills.map(
5119
5487
  (n) => ` ${color.red(sym.fail)} ${accent(n)} ${dim(`\u2014 falta .claude/skills/${n}.md`)}`
5120
5488
  );
5121
- p3.log.warn(
5489
+ p4.log.warn(
5122
5490
  `Skills project-local declarados sin archivo (${missingLocalSkills.length}) \u2014 crea el .md o quita el id de project.localSkills:
5123
5491
  ${lines.join("\n")}`
5124
5492
  );
@@ -5131,14 +5499,14 @@ ${lines.join("\n")}`
5131
5499
  return ` ${color.yellow(sym.update)} ${accent(`${d.filePath}:${d.markerId}`)} ${dim(`${d.fromVersion} \u2192 ${d.toVersion}`)} ${dim(`(${d.source})`)}`;
5132
5500
  });
5133
5501
  const hint = drifts.some((d) => d.kind === "content") ? "corre 'navori sync' para resolver conflicts; 'navori render --apply' para actualizar versiones" : "corre 'navori render --apply' o 'navori sync'";
5134
- p3.log.warn(`Drift detectado (${drifts.length}) \u2014 ${hint}:
5502
+ p4.log.warn(`Drift detectado (${drifts.length}) \u2014 ${hint}:
5135
5503
  ${lines.join("\n")}`);
5136
5504
  }
5137
5505
  if (corruptedSettings.length > 0) {
5138
5506
  const lines = corruptedSettings.map(
5139
5507
  (c) => ` ${color.red(sym.fail)} ${accent(c.path)} ${dim(`\u2014 JSON inv\xE1lido: ${c.error}`)}`
5140
5508
  );
5141
- p3.log.error(
5509
+ p4.log.error(
5142
5510
  `Settings.json corrupto (${corruptedSettings.length}) \u2014 corre 'navori render --force --apply' para regenerar desde el bundle (el archivo actual se respalda):
5143
5511
  ${lines.join("\n")}`
5144
5512
  );
@@ -5147,23 +5515,62 @@ ${lines.join("\n")}`
5147
5515
  const lines = missingInvariants.map(
5148
5516
  (m) => ` ${color.red(sym.fail)} ${accent(m.invariant)} ${dim(`\u2014 declarado por ${m.source}`)}`
5149
5517
  );
5150
- p3.log.error(
5518
+ p4.log.error(
5151
5519
  `Invariantes ausentes en el output (${missingInvariants.length}) \u2014 una regla load-bearing desapareci\xF3; corre 'navori render --apply' o revisa el template:
5152
5520
  ${lines.join("\n")}`
5153
5521
  );
5154
5522
  }
5523
+ if (malformedMarkers.length > 0) {
5524
+ const lines = malformedMarkers.map(
5525
+ (m) => ` ${color.yellow(sym.update)} ${accent(`${m.filePath}:${m.line}`)} ${dim(`\u2014 ${m.snippet}`)}`
5526
+ );
5527
+ p4.log.warn(
5528
+ `Markers managed malformados (${malformedMarkers.length}) \u2014 a esta(s) l\xEDnea(s) les falta el cierre '-->', as\xED que navori ya no las reconoce; el pr\xF3ximo render appendear\xEDa un bloque duplicado y dejar\xEDa la l\xEDnea rota. Restaura el '-->' (o borra la l\xEDnea) a mano:
5529
+ ${lines.join("\n")}`
5530
+ );
5531
+ }
5532
+ if (missingExternalTools.length > 0) {
5533
+ const lines = missingExternalTools.map((t2) => {
5534
+ const how = t2.install ? `${t2.install}${t2.postInstall ? ` && ${t2.postInstall}` : ""}` : "instala la herramienta y reinicia Claude Code";
5535
+ return ` ${color.yellow(sym.update)} ${accent(t2.pluginId)} ${dim(`\u2014 falta '${t2.binary}' en PATH; ${how}`)}`;
5536
+ });
5537
+ p4.log.warn(
5538
+ `Plugins habilitados con herramienta externa no instalada (${missingExternalTools.length}) \u2014 su protocolo/scan referencia algo que no est\xE1 disponible en esta m\xE1quina:
5539
+ ${lines.join("\n")}`
5540
+ );
5541
+ }
5542
+ if (monorepoDrift) {
5543
+ const lines = [];
5544
+ if (monorepoDrift.emptyDeclared) {
5545
+ lines.push(
5546
+ ` ${color.yellow(sym.update)} monorepo declarado pero workspaces[] vac\xEDo \u2014 corr\xE9 'navori scan' para poblarlo`
5547
+ );
5548
+ }
5549
+ for (const path of monorepoDrift.added) {
5550
+ lines.push(` ${color.yellow(sym.update)} ${path} ${dim("\u2014 en disco, falta en config (corr\xE9 'navori scan')")}`);
5551
+ }
5552
+ for (const path of monorepoDrift.orphan) {
5553
+ lines.push(` ${color.yellow(sym.update)} ${path} ${dim("\u2014 en config, ausente en disco (quitalo del config)")}`);
5554
+ }
5555
+ if (lines.length > 0) {
5556
+ p4.log.warn(`Monorepo desincronizado con el disco (${lines.length}):
5557
+ ${lines.join("\n")}`);
5558
+ }
5559
+ }
5155
5560
  if (orderReport) {
5561
+ const spotlight = orderReport.misplacedFirst ? `
5562
+ \u2192 '${orderReport.misplacedFirst.id}' (centro de gravedad) est\xE1 en posici\xF3n ${orderReport.misplacedFirst.currentPos} de ${orderReport.misplacedFirst.total}, deber\xEDa ir 1\xBA.` : "";
5156
5563
  if (orderReport.interleaved) {
5157
- p3.log.warn(
5564
+ p4.log.warn(
5158
5565
  `Bloques managed de CLAUDE.md fuera del orden can\xF3nico \u2014 NO se pueden reordenar autom\xE1ticamente porque hay texto tuyo entre bloques. Mueve ese texto arriba del primer bloque managed o abajo del \xFAltimo; luego corre 'navori render --apply'.
5159
5566
  orden actual: ${orderReport.current.join(", ")}
5160
- orden can\xF3nico: ${orderReport.expected.join(", ")}`
5567
+ orden can\xF3nico: ${orderReport.expected.join(", ")}${spotlight}`
5161
5568
  );
5162
5569
  } else {
5163
- p3.log.warn(
5570
+ p4.log.warn(
5164
5571
  `Bloques managed de CLAUDE.md fuera del orden can\xF3nico \u2014 corre 'navori render --apply' o 'navori sync' para reordenarlos (el primer bloque marca el centro de gravedad del harness).
5165
5572
  orden actual: ${orderReport.current.join(", ")}
5166
- orden can\xF3nico: ${orderReport.expected.join(", ")}`
5573
+ orden can\xF3nico: ${orderReport.expected.join(", ")}${spotlight}`
5167
5574
  );
5168
5575
  }
5169
5576
  }
@@ -5173,13 +5580,13 @@ ${lines.join("\n")}`
5173
5580
  drifts,
5174
5581
  orderReport
5175
5582
  });
5176
- p3.note(
5583
+ p4.note(
5177
5584
  nextSteps.map((s) => ` ${color.cyan(sym.bullet)} ${s}`).join("\n"),
5178
5585
  "Pr\xF3ximos pasos"
5179
5586
  );
5180
5587
  const hasIssues = missingPlugins.length > 0 || corruptedSettings.length > 0 || missingInvariants.length > 0 || missingPreset !== null || missingPresetFiles.length > 0;
5181
5588
  const strictFail = Boolean(args.strict) && drifts.length > 0;
5182
- p3.outro(
5589
+ p4.outro(
5183
5590
  hasIssues ? color.red("Issues found") : strictFail ? color.yellow("Drift detected (--strict)") : color.green("OK")
5184
5591
  );
5185
5592
  if (hasIssues) process.exit(2);
@@ -5187,8 +5594,8 @@ ${lines.join("\n")}`
5187
5594
  }
5188
5595
  });
5189
5596
  function scanCorruptedSettings(cwd) {
5190
- const path = join14(cwd, ".claude/settings.json");
5191
- if (!existsSync18(path)) return [];
5597
+ const path = join15(cwd, ".claude/settings.json");
5598
+ if (!existsSync19(path)) return [];
5192
5599
  try {
5193
5600
  JSON.parse(readFileSync15(path, "utf-8"));
5194
5601
  return [];
@@ -5210,10 +5617,45 @@ function scanMissingPresetFiles(cwd, config) {
5210
5617
  const missing = [];
5211
5618
  for (const e of [...managed, ...agents, ...skills, ...hooks]) {
5212
5619
  const abs = resolve12(loaded.assetRoot, e.relPath);
5213
- if (!existsSync18(abs)) missing.push({ id: e.id, path: relative4(cwd, abs) });
5620
+ if (!existsSync19(abs)) missing.push({ id: e.id, path: relative4(cwd, abs) });
5621
+ }
5622
+ return missing;
5623
+ }
5624
+ function scanMissingExternalTools(config) {
5625
+ const missing = [];
5626
+ const platform = process.platform;
5627
+ for (const [id, settings] of Object.entries(config.plugins ?? {})) {
5628
+ if (settings.enabled !== true) continue;
5629
+ try {
5630
+ const tool = loadPlugin(id).manifest.externalTool;
5631
+ if (!tool?.checkBinary || hasBinary(tool.checkBinary)) continue;
5632
+ missing.push({
5633
+ pluginId: id,
5634
+ binary: tool.checkBinary,
5635
+ install: tool.install?.[platform] ?? null,
5636
+ postInstall: tool.postInstall ?? null
5637
+ });
5638
+ } catch {
5639
+ }
5214
5640
  }
5215
5641
  return missing;
5216
5642
  }
5643
+ function scanMonorepoDrift(cwd, config) {
5644
+ if (!config.monorepo) return null;
5645
+ const configured = config.monorepo.workspaces ?? [];
5646
+ let detected;
5647
+ try {
5648
+ detected = scanMonorepoWorkspaces(cwd);
5649
+ } catch {
5650
+ return null;
5651
+ }
5652
+ const diff = diffWorkspaces(detected, configured);
5653
+ return {
5654
+ added: diff.added.map((d) => d.path),
5655
+ orphan: diff.orphan.map((o) => o.path),
5656
+ emptyDeclared: configured.length === 0 && detected.length > 0
5657
+ };
5658
+ }
5217
5659
  function scanMissingInvariants(cwd, config) {
5218
5660
  const sources = [];
5219
5661
  try {
@@ -5246,26 +5688,26 @@ function scanMissingInvariants(cwd, config) {
5246
5688
  }
5247
5689
  function readRenderedText(cwd) {
5248
5690
  const parts = [];
5249
- const claudeMd = join14(cwd, "CLAUDE.md");
5250
- if (existsSync18(claudeMd)) {
5691
+ const claudeMd = join15(cwd, "CLAUDE.md");
5692
+ if (existsSync19(claudeMd)) {
5251
5693
  try {
5252
5694
  parts.push(readFileSync15(claudeMd, "utf-8"));
5253
5695
  } catch {
5254
5696
  }
5255
5697
  }
5256
- const claudeDir = join14(cwd, ".claude");
5257
- if (existsSync18(claudeDir)) collectText(claudeDir, parts);
5698
+ const claudeDir = join15(cwd, ".claude");
5699
+ if (existsSync19(claudeDir)) collectText(claudeDir, parts);
5258
5700
  return parts.join("\n");
5259
5701
  }
5260
5702
  function collectText(dir, parts) {
5261
5703
  let entries;
5262
5704
  try {
5263
- entries = readdirSync8(dir, { withFileTypes: true });
5705
+ entries = readdirSync9(dir, { withFileTypes: true });
5264
5706
  } catch {
5265
5707
  return;
5266
5708
  }
5267
5709
  for (const entry of entries) {
5268
- const abs = join14(dir, entry.name);
5710
+ const abs = join15(dir, entry.name);
5269
5711
  if (entry.isDirectory()) {
5270
5712
  collectText(abs, parts);
5271
5713
  } else if (entry.isFile() && TEXT_EXTENSIONS.some((e) => entry.name.endsWith(e))) {
@@ -5301,9 +5743,9 @@ function collectAssignments(config) {
5301
5743
 
5302
5744
  // src/commands/sync.ts
5303
5745
  import { defineCommand as defineCommand4 } from "citty";
5304
- import * as p4 from "@clack/prompts";
5305
- import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
5306
- import { resolve as resolve13, join as join15 } from "path";
5746
+ import * as p5 from "@clack/prompts";
5747
+ import { existsSync as existsSync20, readFileSync as readFileSync16 } from "fs";
5748
+ import { resolve as resolve13, join as join16 } from "path";
5307
5749
 
5308
5750
  // src/lib/diff.ts
5309
5751
  function formatLineDiff(current, proposed, _options = {}) {
@@ -5347,20 +5789,20 @@ var syncCommand = defineCommand4({
5347
5789
  async run({ args }) {
5348
5790
  const cwd = resolve13(args.cwd ?? process.cwd());
5349
5791
  const configPath = `${cwd}/navori.config.json`;
5350
- p4.intro(brand("sync"));
5351
- if (!existsSync19(cwd)) {
5352
- p4.cancel(`Directory not found: ${cwd}`);
5792
+ p5.intro(brand("sync"));
5793
+ if (!existsSync20(cwd)) {
5794
+ p5.cancel(`Directory not found: ${cwd}`);
5353
5795
  process.exit(1);
5354
5796
  }
5355
- if (!existsSync19(configPath)) {
5356
- p4.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
5797
+ if (!existsSync20(configPath)) {
5798
+ p5.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
5357
5799
  process.exit(1);
5358
5800
  }
5359
- const config = readConfig(configPath);
5801
+ const config = readConfigOrExit(configPath);
5360
5802
  const workspaceFilter = args.workspace ?? null;
5361
5803
  const targetsResult = resolveSyncTargets(cwd, config, workspaceFilter);
5362
5804
  if (!targetsResult.ok) {
5363
- p4.cancel(targetsResult.reason);
5805
+ p5.cancel(targetsResult.reason);
5364
5806
  process.exit(1);
5365
5807
  }
5366
5808
  const targets = targetsResult.targets;
@@ -5370,24 +5812,24 @@ var syncCommand = defineCommand4({
5370
5812
  }));
5371
5813
  reportPlans(plans);
5372
5814
  const conflicts = collectAllConflicts(plans);
5373
- const hasOtherChanges = plans.some((p16) => p16.plan.written.length > 0);
5815
+ const hasOtherChanges = plans.some((p17) => p17.plan.written.length > 0);
5374
5816
  if (!hasOtherChanges && conflicts.length === 0) {
5375
- p4.outro("Up to date \u2014 no changes");
5817
+ p5.outro("Up to date \u2014 no changes");
5376
5818
  return;
5377
5819
  }
5378
5820
  if (args["dry-run"]) {
5379
- const pending = plans.reduce((acc, p16) => acc + p16.plan.written.length, 0);
5821
+ const pending = plans.reduce((acc, p17) => acc + p17.plan.written.length, 0);
5380
5822
  const summary = [
5381
5823
  conflicts.length > 0 ? `${conflicts.length} conflict(s)` : null,
5382
5824
  hasOtherChanges ? `${pending} pending` : null
5383
5825
  ].filter(Boolean).join(", ");
5384
- p4.outro(`Dry-run complete${summary ? ` \u2014 ${summary}` : ""}`);
5826
+ p5.outro(`Dry-run complete${summary ? ` \u2014 ${summary}` : ""}`);
5385
5827
  return;
5386
5828
  }
5387
5829
  const autoApply = Boolean(args.yes || args.apply);
5388
5830
  if (args.yes && conflicts.length > 0) {
5389
5831
  const lines = conflicts.map((c) => ` - ${c.path}: ${c.reason}`).join("\n");
5390
- p4.cancel(
5832
+ p5.cancel(
5391
5833
  `${conflicts.length} conflict(s) detected with --yes. Resu\xE9lvelos a mano o corre 'sync --apply' sin --yes para el flujo interactivo.
5392
5834
  ${lines}`
5393
5835
  );
@@ -5398,18 +5840,18 @@ ${lines}`
5398
5840
  if (conflicts.length > 0 && Boolean(args.interactive)) {
5399
5841
  const resolved = await resolveConflictsInteractively(plans);
5400
5842
  if (resolved === null) {
5401
- p4.cancel("Aborted");
5843
+ p5.cancel("Aborted");
5402
5844
  process.exit(0);
5403
5845
  }
5404
5846
  resolutions = resolved;
5405
5847
  const fileConflicts = conflicts.filter((c) => !c.path.includes("CLAUDE.md"));
5406
5848
  if (fileConflicts.length > 0) {
5407
- p4.log.warn(
5849
+ p5.log.warn(
5408
5850
  `${fileConflicts.length} conflicto(s) en archivos .claude/ se mantienen \u2014 la resoluci\xF3n interactiva cubre CLAUDE.md; resuelve los de .claude/ a mano y vuelve a correr sync.`
5409
5851
  );
5410
5852
  }
5411
5853
  } else if (conflicts.length > 0) {
5412
- const choice = await p4.select({
5854
+ const choice = await p5.select({
5413
5855
  message: `Encontr\xE9 ${conflicts.length} conflict(s). \xBFQu\xE9 hago?`,
5414
5856
  options: [
5415
5857
  {
@@ -5420,25 +5862,25 @@ ${lines}`
5420
5862
  { value: "abort", label: "Abortar \u2014 no escribir nada" }
5421
5863
  ]
5422
5864
  });
5423
- if (p4.isCancel(choice) || choice === "abort") {
5424
- p4.cancel("Aborted");
5865
+ if (p5.isCancel(choice) || choice === "abort") {
5866
+ p5.cancel("Aborted");
5425
5867
  process.exit(0);
5426
5868
  }
5427
5869
  if (choice === "interactive") {
5428
5870
  const resolved = await resolveConflictsInteractively(plans);
5429
5871
  if (resolved === null) {
5430
- p4.cancel("Aborted");
5872
+ p5.cancel("Aborted");
5431
5873
  process.exit(0);
5432
5874
  }
5433
5875
  resolutions = resolved;
5434
5876
  }
5435
5877
  } else {
5436
- const ok = await p4.confirm({
5878
+ const ok = await p5.confirm({
5437
5879
  message: "Aplicar cambios?",
5438
5880
  initialValue: true
5439
5881
  });
5440
- if (p4.isCancel(ok) || !ok) {
5441
- p4.cancel("Aborted");
5882
+ if (p5.isCancel(ok) || !ok) {
5883
+ p5.cancel("Aborted");
5442
5884
  process.exit(0);
5443
5885
  }
5444
5886
  }
@@ -5453,11 +5895,11 @@ ${lines}`
5453
5895
  });
5454
5896
  writtenTotal += applied.written.length;
5455
5897
  if (applied.backupPath) {
5456
- p4.log.message(`${dim(`Backup [${t2.label}]:`)} ${applied.backupPath}`);
5898
+ p5.log.message(`${dim(`Backup [${t2.label}]:`)} ${applied.backupPath}`);
5457
5899
  }
5458
5900
  }
5459
- p4.log.success(`Wrote ${writtenTotal} file(s)`);
5460
- p4.outro(`${color.green("Done")} ${summarize2(writtenTotal, conflicts.length)}`);
5901
+ p5.log.success(`Wrote ${writtenTotal} file(s)`);
5902
+ p5.outro(`${color.green("Done")} ${summarize2(writtenTotal, conflicts.length)}`);
5461
5903
  }
5462
5904
  });
5463
5905
  function resolveSyncTargets(cwd, config, workspaceFilter) {
@@ -5507,26 +5949,26 @@ async function resolveConflictsInteractively(plans) {
5507
5949
  (e) => e.status === "user-modified-skipped"
5508
5950
  );
5509
5951
  if (cmConflicts.length === 0) continue;
5510
- const claudeMdPath = join15(tp.target.cwd, "CLAUDE.md");
5511
- const existing = existsSync19(claudeMdPath) ? readFileSync16(claudeMdPath, "utf-8") : "";
5952
+ const claudeMdPath = join16(tp.target.cwd, "CLAUDE.md");
5953
+ const existing = existsSync20(claudeMdPath) ? readFileSync16(claudeMdPath, "utf-8") : "";
5512
5954
  const skipIds = /* @__PURE__ */ new Set();
5513
5955
  const forceIds = /* @__PURE__ */ new Set();
5514
5956
  for (const e of cmConflicts) {
5515
5957
  const actual = extractManagedContent(existing, e.asset.id) ?? "";
5516
5958
  const proposed = e.newContent ?? "";
5517
- p4.log.message(
5959
+ p5.log.message(
5518
5960
  `${color.yellow("Conflict")} [${tp.target.label}] CLAUDE.md:${accent(e.asset.id)}
5519
5961
  ${dim("(- your edit, + rendered)")}
5520
5962
  ${formatLineDiff(actual, proposed)}`
5521
5963
  );
5522
- const choice = await p4.select({
5964
+ const choice = await p5.select({
5523
5965
  message: `${e.asset.id}: keep your edit or accept the new version?`,
5524
5966
  options: [
5525
5967
  { value: "keep", label: "Keep mine \u2014 skip, your edit stays" },
5526
5968
  { value: "accept", label: "Accept new \u2014 overwrite with the rendered version" }
5527
5969
  ]
5528
5970
  });
5529
- if (p4.isCancel(choice)) return null;
5971
+ if (p5.isCancel(choice)) return null;
5530
5972
  if (choice === "accept") forceIds.add(e.asset.id);
5531
5973
  else skipIds.add(e.asset.id);
5532
5974
  }
@@ -5588,7 +6030,7 @@ function reportTargetPlan({ target, plan }) {
5588
6030
  lines.push(` ${color.cyan(sym.update)} ${u.id} ${dim(`${u.fromVersion} \u2192 ${u.toVersion}`)}`);
5589
6031
  }
5590
6032
  }
5591
- p4.log.message(lines.join("\n"));
6033
+ p5.log.message(lines.join("\n"));
5592
6034
  }
5593
6035
  function summarize2(writtenCount, conflictCount) {
5594
6036
  const parts = [];
@@ -5599,34 +6041,10 @@ function summarize2(writtenCount, conflictCount) {
5599
6041
 
5600
6042
  // src/commands/add.ts
5601
6043
  import { defineCommand as defineCommand5 } from "citty";
5602
- import * as p5 from "@clack/prompts";
6044
+ import * as p6 from "@clack/prompts";
5603
6045
  import { existsSync as existsSync21, readFileSync as readFileSync17 } from "fs";
5604
6046
  import { resolve as resolve14 } from "path";
5605
6047
  import { spawnSync as spawnSync3 } from "child_process";
5606
-
5607
- // src/lib/which.ts
5608
- import { existsSync as existsSync20, statSync as statSync6 } from "fs";
5609
- import { join as join16 } from "path";
5610
- function hasBinary(name) {
5611
- const pathEnv = process.env.PATH ?? "";
5612
- const sep2 = process.platform === "win32" ? ";" : ":";
5613
- const dirs = pathEnv.split(sep2).filter(Boolean);
5614
- const exts = process.platform === "win32" ? (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";") : [""];
5615
- for (const dir of dirs) {
5616
- for (const ext of exts) {
5617
- const candidate = join16(dir, name + ext);
5618
- if (existsSync20(candidate)) {
5619
- try {
5620
- if (statSync6(candidate).isFile()) return true;
5621
- } catch {
5622
- }
5623
- }
5624
- }
5625
- }
5626
- return false;
5627
- }
5628
-
5629
- // src/commands/add.ts
5630
6048
  function currentPlatform() {
5631
6049
  if (process.platform === "darwin") return "darwin";
5632
6050
  if (process.platform === "linux") return "linux";
@@ -5683,20 +6101,20 @@ var addCommand = defineCommand5({
5683
6101
  const cwd = resolve14(args.cwd ?? process.cwd());
5684
6102
  const configPath = `${cwd}/navori.config.json`;
5685
6103
  if (args.suggest) {
5686
- p5.intro(brand("add --suggest"));
6104
+ p6.intro(brand("add --suggest"));
5687
6105
  } else if (!args.plugin) {
5688
- p5.intro(brand("add"));
5689
- p5.cancel("Pasa un plugin id (ej. 'navori add engram') o usa --suggest para ver recomendaciones.");
6106
+ p6.intro(brand("add"));
6107
+ p6.cancel("Pasa un plugin id (ej. 'navori add engram') o usa --suggest para ver recomendaciones.");
5690
6108
  process.exit(1);
5691
6109
  } else {
5692
- p5.intro(brand(`add ${accent(args.plugin)}`));
6110
+ p6.intro(brand(`add ${accent(args.plugin)}`));
5693
6111
  }
5694
6112
  if (!existsSync21(cwd)) {
5695
- p5.cancel(`Directory not found: ${cwd}`);
6113
+ p6.cancel(`Directory not found: ${cwd}`);
5696
6114
  process.exit(1);
5697
6115
  }
5698
6116
  if (!existsSync21(configPath)) {
5699
- p5.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
6117
+ p6.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
5700
6118
  process.exit(1);
5701
6119
  }
5702
6120
  if (args.suggest) {
@@ -5709,21 +6127,21 @@ var addCommand = defineCommand5({
5709
6127
  plugin = loadPlugin(pluginId);
5710
6128
  } catch (err) {
5711
6129
  if (err instanceof PluginNotFoundError) {
5712
- p5.cancel(`Unknown plugin '${pluginId}'. Known: ${listKnownPluginIds().join(", ") || "(none)"}`);
6130
+ p6.cancel(`Unknown plugin '${pluginId}'. Known: ${listKnownPluginIds().join(", ") || "(none)"}`);
5713
6131
  process.exit(1);
5714
6132
  }
5715
6133
  if (err instanceof PluginManifestError) {
5716
- p5.cancel(err.message);
6134
+ p6.cancel(err.message);
5717
6135
  process.exit(1);
5718
6136
  }
5719
6137
  throw err;
5720
6138
  }
5721
- p5.log.info(`${plugin.manifest.name} v${plugin.manifest.version}`);
5722
- p5.log.message(plugin.manifest.description);
6139
+ p6.log.info(`${plugin.manifest.name} v${plugin.manifest.version}`);
6140
+ p6.log.message(plugin.manifest.description);
5723
6141
  const config = readConfig(configPath);
5724
6142
  const already = config.plugins?.[plugin.manifest.id]?.enabled === true;
5725
6143
  if (already) {
5726
- p5.log.warn(`'${plugin.manifest.id}' is already enabled in this config`);
6144
+ p6.log.warn(`'${plugin.manifest.id}' is already enabled in this config`);
5727
6145
  } else {
5728
6146
  const updatedPlugins = {
5729
6147
  ...config.plugins ?? {},
@@ -5731,41 +6149,41 @@ var addCommand = defineCommand5({
5731
6149
  };
5732
6150
  const raw = JSON.parse(readFileSync17(configPath, "utf-8"));
5733
6151
  writeConfig(configPath, { ...raw, plugins: updatedPlugins });
5734
- p5.log.success(`Added '${plugin.manifest.id}' to ${configPath}`);
6152
+ p6.log.success(`Added '${plugin.manifest.id}' to ${configPath}`);
5735
6153
  }
5736
6154
  const tool = plugin.manifest.externalTool;
5737
6155
  if (!tool) {
5738
- p5.outro("Done \u2014 run 'navori render --apply' to apply");
6156
+ p6.outro("Done \u2014 run 'navori render --apply' to apply");
5739
6157
  return;
5740
6158
  }
5741
6159
  const installed = tool.checkBinary ? hasBinary(tool.checkBinary) : true;
5742
6160
  if (installed) {
5743
- p5.log.success(`External tool '${tool.name}' is already installed`);
5744
- p5.outro("Done \u2014 run 'navori render --apply' to apply");
6161
+ p6.log.success(`External tool '${tool.name}' is already installed`);
6162
+ p6.outro("Done \u2014 run 'navori render --apply' to apply");
5745
6163
  return;
5746
6164
  }
5747
6165
  if (args["skip-install"]) {
5748
- p5.log.warn(`External tool '${tool.name}' is not installed. Skip-install requested.`);
5749
- p5.outro("Done \u2014 install manually later");
6166
+ p6.log.warn(`External tool '${tool.name}' is not installed. Skip-install requested.`);
6167
+ p6.outro("Done \u2014 install manually later");
5750
6168
  return;
5751
6169
  }
5752
6170
  const platform = currentPlatform();
5753
6171
  const installCmd = tool.install?.[platform];
5754
6172
  if (!installCmd) {
5755
- p5.log.warn(`No install command for platform '${platform}'. Install '${tool.name}' manually.`);
5756
- p5.outro("Done");
6173
+ p6.log.warn(`No install command for platform '${platform}'. Install '${tool.name}' manually.`);
6174
+ p6.outro("Done");
5757
6175
  return;
5758
6176
  }
5759
- const shouldInstall = args.yes ? true : await p5.confirm({
6177
+ const shouldInstall = args.yes ? true : await p6.confirm({
5760
6178
  message: `Install '${tool.name}'? Will run: ${installCmd}`,
5761
6179
  initialValue: false
5762
6180
  });
5763
- if (p5.isCancel(shouldInstall) || !shouldInstall) {
5764
- p5.log.warn(`External tool '${tool.name}' not installed. Hooks will skip silently.`);
5765
- p5.outro("Done");
6181
+ if (p6.isCancel(shouldInstall) || !shouldInstall) {
6182
+ p6.log.warn(`External tool '${tool.name}' not installed. Hooks will skip silently.`);
6183
+ p6.outro("Done");
5766
6184
  return;
5767
6185
  }
5768
- const spin = p5.spinner();
6186
+ const spin = p6.spinner();
5769
6187
  try {
5770
6188
  spin.start(`Installing ${accent(tool.name)} \u2014 ${dim(installCmd)}`);
5771
6189
  runShellCommand(installCmd);
@@ -5776,10 +6194,10 @@ var addCommand = defineCommand5({
5776
6194
  spin.stop(`${color.green("\u2713")} Installed ${accent(tool.name)}`);
5777
6195
  } catch (err) {
5778
6196
  spin.stop(`${color.red("\u2717")} Install failed: ${err.message}`, 1);
5779
- p5.outro(dim("Plugin registered but external tool install failed. Install manually."));
6197
+ p6.outro(dim("Plugin registered but external tool install failed. Install manually."));
5780
6198
  return;
5781
6199
  }
5782
- p5.outro(`${color.green("Done")} ${dim("\u2014 run 'navori render --apply' to apply")}`);
6200
+ p6.outro(`${color.green("Done")} ${dim("\u2014 run 'navori render --apply' to apply")}`);
5783
6201
  }
5784
6202
  });
5785
6203
  function printSuggestions(cwd, configPath) {
@@ -5802,18 +6220,18 @@ function printSuggestions(cwd, configPath) {
5802
6220
  );
5803
6221
  }
5804
6222
  if (lines.length === 0) {
5805
- p5.outro(
6223
+ p6.outro(
5806
6224
  `${color.green("Nada que sugerir")} ${dim("\u2014 el preset matchea el stack y engram ya est\xE1 habilitado.")}`
5807
6225
  );
5808
6226
  return;
5809
6227
  }
5810
- p5.note(lines.join("\n"), "Sugerencias");
5811
- p5.outro(dim("Sugerencias, no aplicadas \u2014 corre 'navori add <id>' o 'navori configure'."));
6228
+ p6.note(lines.join("\n"), "Sugerencias");
6229
+ p6.outro(dim("Sugerencias, no aplicadas \u2014 corre 'navori add <id>' o 'navori configure'."));
5812
6230
  }
5813
6231
 
5814
6232
  // src/commands/workspace.ts
5815
6233
  import { defineCommand as defineCommand6 } from "citty";
5816
- import * as p6 from "@clack/prompts";
6234
+ import * as p7 from "@clack/prompts";
5817
6235
  import { existsSync as existsSync22 } from "fs";
5818
6236
  import { join as join17 } from "path";
5819
6237
 
@@ -5880,15 +6298,15 @@ var initSubCommand = defineCommand6({
5880
6298
  console.error(`Workspace '${name}' already exists at ${path}`);
5881
6299
  process.exit(1);
5882
6300
  }
5883
- p6.intro(brand(`workspace init ${accent(name)}`));
6301
+ p7.intro(brand(`workspace init ${accent(name)}`));
5884
6302
  let description = args.description ?? "";
5885
6303
  if (!args.yes && !description) {
5886
- const value = await p6.text({
6304
+ const value = await p7.text({
5887
6305
  message: "Workspace description (optional)",
5888
6306
  placeholder: "e.g. Bonum coaching platform \u2014 multi-repo"
5889
6307
  });
5890
- if (p6.isCancel(value)) {
5891
- p6.cancel("Cancelled");
6308
+ if (p7.isCancel(value)) {
6309
+ p7.cancel("Cancelled");
5892
6310
  process.exit(0);
5893
6311
  }
5894
6312
  description = value.trim();
@@ -5901,9 +6319,9 @@ var initSubCommand = defineCommand6({
5901
6319
  repos: []
5902
6320
  };
5903
6321
  const written = writeWorkspace(workspace);
5904
- p6.log.success(`Wrote ${written}`);
5905
- p6.log.message(`Tickets directory: ${workspaceDirectory(name)}/tickets/`);
5906
- p6.outro(`Run 'navori workspace show ${name}' to inspect, or add it to a repo with 'navori init --workspace ${name}'.`);
6322
+ p7.log.success(`Wrote ${written}`);
6323
+ p7.log.message(`Tickets directory: ${workspaceDirectory(name)}/tickets/`);
6324
+ p7.outro(`Run 'navori workspace show ${name}' to inspect, or add it to a repo with 'navori init --workspace ${name}'.`);
5907
6325
  }
5908
6326
  });
5909
6327
  var lsSubCommand = defineCommand6({
@@ -5920,10 +6338,10 @@ var lsSubCommand = defineCommand6({
5920
6338
  console.log(JSON.stringify(names, null, 2));
5921
6339
  return;
5922
6340
  }
5923
- p6.intro(brand("workspace ls"));
6341
+ p7.intro(brand("workspace ls"));
5924
6342
  if (names.length === 0) {
5925
- p6.log.info("No workspaces found. Create one with 'navori workspace init <name>'.");
5926
- p6.outro(dim("Done"));
6343
+ p7.log.info("No workspaces found. Create one with 'navori workspace init <name>'.");
6344
+ p7.outro(dim("Done"));
5927
6345
  return;
5928
6346
  }
5929
6347
  const lines = [];
@@ -5939,8 +6357,8 @@ var lsSubCommand = defineCommand6({
5939
6357
  lines.push(` ${color.red(sym.fail)} ${name} ${dim("(invalid manifest)")}`);
5940
6358
  }
5941
6359
  }
5942
- p6.log.message(lines.join("\n"));
5943
- p6.outro(dim(`${names.length} workspace${names.length === 1 ? "" : "s"}`));
6360
+ p7.log.message(lines.join("\n"));
6361
+ p7.outro(dim(`${names.length} workspace${names.length === 1 ? "" : "s"}`));
5944
6362
  }
5945
6363
  });
5946
6364
  var showSubCommand = defineCommand6({
@@ -5980,7 +6398,7 @@ Or list known workspaces: navori workspace ls
5980
6398
  console.log(JSON.stringify(workspace, null, 2));
5981
6399
  return;
5982
6400
  }
5983
- p6.intro(brand(`workspace show ${accent(workspace.name)}`));
6401
+ p7.intro(brand(`workspace show ${accent(workspace.name)}`));
5984
6402
  const rows = [];
5985
6403
  if (workspace.description) rows.push(["description", workspace.description]);
5986
6404
  rows.push(["path", workspacePath(name)]);
@@ -5988,7 +6406,7 @@ Or list known workspaces: navori workspace ls
5988
6406
  rows.push(["ticketsDir", workspace.ticketsDir]);
5989
6407
  rows.push(["defaults", JSON.stringify(workspace.defaults)]);
5990
6408
  rows.push(["repos", String(workspace.repos.length)]);
5991
- p6.log.message(kv(rows));
6409
+ p7.log.message(kv(rows));
5992
6410
  if (workspace.repos.length > 0) {
5993
6411
  const configNames = /* @__PURE__ */ new Map();
5994
6412
  for (const repo of workspace.repos) {
@@ -6001,7 +6419,7 @@ Or list known workspaces: navori workspace ls
6001
6419
  const cfgLabel = cfgName && cfgName !== repo.name ? dim(` (name: ${cfgName})`) : "";
6002
6420
  return ` ${color.cyan(sym.bullet)} ${accent(repo.name)}${stack}${cfgLabel} ${dim(repo.path)}${desc}`;
6003
6421
  });
6004
- p6.log.message(`Repos:
6422
+ p7.log.message(`Repos:
6005
6423
  ${repoLines.join("\n")}`);
6006
6424
  const byConfigName = /* @__PURE__ */ new Map();
6007
6425
  const placeholders = [];
@@ -6016,18 +6434,18 @@ ${repoLines.join("\n")}`);
6016
6434
  const lines = collisions.map(
6017
6435
  ([name2, repos]) => ` ${color.red(sym.fail)} ${accent(name2)} ${dim("\u2190")} ${repos.join(", ")}`
6018
6436
  );
6019
- p6.log.warn(
6437
+ p7.log.warn(
6020
6438
  `Colisi\xF3n de name entre repos (mismo config.name) \u2014 ren\xF3mbralos en su package.json / navori.config.json para que cada repo tenga identidad \xFAnica:
6021
6439
  ${lines.join("\n")}`
6022
6440
  );
6023
6441
  }
6024
6442
  if (placeholders.length > 0) {
6025
6443
  const lines = placeholders.map((pl) => ` ${color.yellow("!")} ${accent(pl.repo)} ${dim("\u2192")} '${pl.name}'`);
6026
- p6.log.warn(`Names placeholder (scaffold sin renombrar):
6444
+ p7.log.warn(`Names placeholder (scaffold sin renombrar):
6027
6445
  ${lines.join("\n")}`);
6028
6446
  }
6029
6447
  }
6030
- p6.outro(dim("Done"));
6448
+ p7.outro(dim("Done"));
6031
6449
  }
6032
6450
  });
6033
6451
  var renameSubCommand = defineCommand6({
@@ -6060,20 +6478,20 @@ var renameSubCommand = defineCommand6({
6060
6478
  console.error(`Workspace '${to}' already exists. Choose a different name or delete it first.`);
6061
6479
  process.exit(1);
6062
6480
  }
6063
- p6.intro(brand(`workspace rename ${accent(from)} ${dim("\u2192")} ${accent(to)}`));
6064
- p6.log.message(
6481
+ p7.intro(brand(`workspace rename ${accent(from)} ${dim("\u2192")} ${accent(to)}`));
6482
+ p7.log.message(
6065
6483
  `Will rename the workspace directory and update the manifest's 'name' field. ${ws.repos.length} repo registration(s) and any tickets will be preserved.`
6066
6484
  );
6067
- p6.log.warn(
6485
+ p7.log.warn(
6068
6486
  `Repos that have 'workspace: ${from}' in their navori.config.json must be updated manually: cd to each repo and run 'navori configure workspace ${to}'.`
6069
6487
  );
6070
6488
  if (!args.yes) {
6071
- const ok = await p6.confirm({
6489
+ const ok = await p7.confirm({
6072
6490
  message: `Rename workspace '${from}' to '${to}'?`,
6073
6491
  initialValue: false
6074
6492
  });
6075
- if (p6.isCancel(ok) || !ok) {
6076
- p6.cancel("Aborted");
6493
+ if (p7.isCancel(ok) || !ok) {
6494
+ p7.cancel("Aborted");
6077
6495
  return;
6078
6496
  }
6079
6497
  }
@@ -6083,7 +6501,7 @@ var renameSubCommand = defineCommand6({
6083
6501
  renameSync3(oldDir, newDir);
6084
6502
  const renamed = { ...ws, name: to };
6085
6503
  writeWorkspace(renamed);
6086
- p6.outro(`Renamed. New path: ${newDir}`);
6504
+ p7.outro(`Renamed. New path: ${newDir}`);
6087
6505
  }
6088
6506
  });
6089
6507
  var deleteSubCommand = defineCommand6({
@@ -6103,17 +6521,17 @@ var deleteSubCommand = defineCommand6({
6103
6521
  process.exit(1);
6104
6522
  }
6105
6523
  const dir = workspaceDirectory(name);
6106
- p6.intro(brand(`workspace delete ${accent(name)}`));
6107
- p6.log.warn(
6524
+ p7.intro(brand(`workspace delete ${accent(name)}`));
6525
+ p7.log.warn(
6108
6526
  `Will move ${dir} to ~/.navori/.trash/. Includes ${ws.repos.length} repo registration(s) and any tickets in that workspace.`
6109
6527
  );
6110
6528
  if (!args.yes) {
6111
- const ok = await p6.confirm({
6529
+ const ok = await p7.confirm({
6112
6530
  message: `Delete workspace '${name}'?`,
6113
6531
  initialValue: false
6114
6532
  });
6115
- if (p6.isCancel(ok) || !ok) {
6116
- p6.cancel("Aborted");
6533
+ if (p7.isCancel(ok) || !ok) {
6534
+ p7.cancel("Aborted");
6117
6535
  return;
6118
6536
  }
6119
6537
  }
@@ -6125,7 +6543,7 @@ var deleteSubCommand = defineCommand6({
6125
6543
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
6126
6544
  const dest = joinPath(trashRoot, `${name}-${ts}`);
6127
6545
  if (existsSync32(dir)) renameSync3(dir, dest);
6128
- p6.outro(`Moved to ${dest}. Restore manually if needed.`);
6546
+ p7.outro(`Moved to ${dest}. Restore manually if needed.`);
6129
6547
  }
6130
6548
  });
6131
6549
  var addRepoSubCommand = defineCommand6({
@@ -6157,9 +6575,9 @@ var addRepoSubCommand = defineCommand6({
6157
6575
  ...args.description ? { description: args.description } : {}
6158
6576
  });
6159
6577
  const written = writeWorkspace(ws);
6160
- p6.intro(brand(`workspace add-repo ${accent(ws.name)}`));
6161
- p6.log.success(`Registered '${accent(args.name)}' (${dim(written)})`);
6162
- p6.outro(dim("Done"));
6578
+ p7.intro(brand(`workspace add-repo ${accent(ws.name)}`));
6579
+ p7.log.success(`Registered '${accent(args.name)}' (${dim(written)})`);
6580
+ p7.outro(dim("Done"));
6163
6581
  }
6164
6582
  });
6165
6583
  var setDefaultSubCommand = defineCommand6({
@@ -6193,10 +6611,10 @@ var setDefaultSubCommand = defineCommand6({
6193
6611
  }
6194
6612
  ws.defaults = result.defaults;
6195
6613
  const written = writeWorkspace(ws);
6196
- p6.intro(brand(`workspace set-default ${accent(ws.name)}`));
6197
- p6.log.success(`Set ${accent(args.key)} ${dim(`(${written})`)}`);
6198
- p6.log.message(kv([["defaults", JSON.stringify(ws.defaults)]]));
6199
- p6.outro(dim("Done"));
6614
+ p7.intro(brand(`workspace set-default ${accent(ws.name)}`));
6615
+ p7.log.success(`Set ${accent(args.key)} ${dim(`(${written})`)}`);
6616
+ p7.log.message(kv([["defaults", JSON.stringify(ws.defaults)]]));
6617
+ p7.outro(dim("Done"));
6200
6618
  }
6201
6619
  });
6202
6620
  var renderSubCommand = defineCommand6({
@@ -6224,10 +6642,10 @@ var renderSubCommand = defineCommand6({
6224
6642
  const apply = Boolean(args.apply);
6225
6643
  const preview = !apply;
6226
6644
  const force = Boolean(args.force);
6227
- p6.intro(brand(`workspace render ${accent(ws.name)}`));
6645
+ p7.intro(brand(`workspace render ${accent(ws.name)}`));
6228
6646
  if (ws.repos.length === 0) {
6229
- p6.log.info("No repos registered. Add one with 'navori workspace add-repo'.");
6230
- p6.outro(dim("Done"));
6647
+ p7.log.info("No repos registered. Add one with 'navori workspace add-repo'.");
6648
+ p7.outro(dim("Done"));
6231
6649
  return;
6232
6650
  }
6233
6651
  const rows = [];
@@ -6261,16 +6679,16 @@ var renderSubCommand = defineCommand6({
6261
6679
  const detail = r.detail ? dim(` ${r.detail}`) : "";
6262
6680
  return ` ${marker[r.status]} ${accent(r.name)} ${dim(r.status)}${detail}`;
6263
6681
  });
6264
- p6.log.message(lines.join("\n"));
6682
+ p7.log.message(lines.join("\n"));
6265
6683
  const failed = rows.filter((r) => r.status === "error" || r.status === "missing").length;
6266
6684
  const pending = rows.filter((r) => r.status === "written" || r.status === "would-write").length;
6267
6685
  const ok = rows.length - failed;
6268
6686
  const summary = `${ok}/${rows.length} ok \xB7 ${pending} ${preview ? "would change" : "changed"} \xB7 ${failed} failed`;
6269
6687
  if (failed > 0) {
6270
- p6.outro(`${color.yellow("Done with errors")} ${dim(summary)}`);
6688
+ p7.outro(`${color.yellow("Done with errors")} ${dim(summary)}`);
6271
6689
  process.exit(1);
6272
6690
  }
6273
- p6.outro(`${preview ? color.yellow("Preview") : color.green("Done")} ${dim(summary)}`);
6691
+ p7.outro(`${preview ? color.yellow("Preview") : color.green("Done")} ${dim(summary)}`);
6274
6692
  }
6275
6693
  });
6276
6694
  function summarizeEntries(entries) {
@@ -6305,11 +6723,11 @@ var workspaceCommand = defineCommand6({
6305
6723
 
6306
6724
  // src/commands/ticket.ts
6307
6725
  import { defineCommand as defineCommand7 } from "citty";
6308
- import * as p7 from "@clack/prompts";
6726
+ import * as p8 from "@clack/prompts";
6309
6727
  import { readFileSync as readFileSync19 } from "fs";
6310
6728
 
6311
6729
  // src/lib/tickets.ts
6312
- import { existsSync as existsSync23, readFileSync as readFileSync18, readdirSync as readdirSync9, statSync as statSync7, mkdirSync as mkdirSync7, renameSync as renameSync2, rmSync as rmSync5 } from "fs";
6730
+ import { existsSync as existsSync23, readFileSync as readFileSync18, readdirSync as readdirSync10, statSync as statSync7, mkdirSync as mkdirSync7, renameSync as renameSync2, rmSync as rmSync5 } from "fs";
6313
6731
  import { join as join18, resolve as resolve15 } from "path";
6314
6732
  var TicketError = class extends NavoriError {
6315
6733
  constructor(message) {
@@ -6340,7 +6758,7 @@ function listTickets(workspaceName) {
6340
6758
  const out = [];
6341
6759
  const collect = (folder, state) => {
6342
6760
  if (!existsSync23(folder)) return;
6343
- for (const entry of readdirSync9(folder)) {
6761
+ for (const entry of readdirSync10(folder)) {
6344
6762
  if (!entry.endsWith(".md")) continue;
6345
6763
  const full = join18(folder, entry);
6346
6764
  try {
@@ -6472,18 +6890,18 @@ var listSubCommand = defineCommand7({
6472
6890
  console.log(JSON.stringify(filtered, null, 2));
6473
6891
  return;
6474
6892
  }
6475
- p7.intro(brand(`ticket list ${accent(args.workspace)}`));
6893
+ p8.intro(brand(`ticket list ${accent(args.workspace)}`));
6476
6894
  if (filtered.length === 0) {
6477
- p7.log.info(`No tickets. Create one with 'navori ticket new ${args.workspace} <id>'.`);
6478
- p7.outro(dim("Done"));
6895
+ p8.log.info(`No tickets. Create one with 'navori ticket new ${args.workspace} <id>'.`);
6896
+ p8.outro(dim("Done"));
6479
6897
  return;
6480
6898
  }
6481
6899
  const lines = filtered.map((t2) => {
6482
6900
  const badge = t2.state === "archive" ? color.magenta(" [archive]") : "";
6483
6901
  return ` ${color.cyan(sym.bullet)} ${accent(t2.id)}${badge} ${t2.title}`;
6484
6902
  });
6485
- p7.log.message(lines.join("\n"));
6486
- p7.outro(dim(`${filtered.length} ticket${filtered.length === 1 ? "" : "s"}`));
6903
+ p8.log.message(lines.join("\n"));
6904
+ p8.outro(dim(`${filtered.length} ticket${filtered.length === 1 ? "" : "s"}`));
6487
6905
  }
6488
6906
  });
6489
6907
  var showSubCommand2 = defineCommand7({
@@ -6519,26 +6937,26 @@ Create it with: navori ticket new ${args.workspace} ${args.id}
6519
6937
  console.log(JSON.stringify({ ticket, referencing, content }, null, 2));
6520
6938
  return;
6521
6939
  }
6522
- p7.intro(brand(`ticket show ${accent(ticket.id)}`));
6523
- p7.log.message(
6940
+ p8.intro(brand(`ticket show ${accent(ticket.id)}`));
6941
+ p8.log.message(
6524
6942
  kv([
6525
6943
  ["title", ticket.title],
6526
6944
  ["state", ticket.state],
6527
6945
  ["path", ticket.path]
6528
6946
  ])
6529
6947
  );
6530
- p7.note(readFileSync19(ticket.path, "utf-8"), "Content");
6948
+ p8.note(readFileSync19(ticket.path, "utf-8"), "Content");
6531
6949
  if (referencing.length === 0) {
6532
- p7.log.message(dim("Referenced in: (no repo's progress/current.md mentions this ticket)"));
6950
+ p8.log.message(dim("Referenced in: (no repo's progress/current.md mentions this ticket)"));
6533
6951
  } else {
6534
6952
  const refLines = referencing.flatMap((ref) => [
6535
6953
  ` ${color.cyan(sym.bullet)} ${ref.path}`,
6536
6954
  ...ref.matches.map((match) => ` ${dim(">")} ${dim(match)}`)
6537
6955
  ]);
6538
- p7.log.message(`Referenced in:
6956
+ p8.log.message(`Referenced in:
6539
6957
  ${refLines.join("\n")}`);
6540
6958
  }
6541
- p7.outro(dim("Done"));
6959
+ p8.outro(dim("Done"));
6542
6960
  }
6543
6961
  });
6544
6962
  var newSubCommand = defineCommand7({
@@ -6552,21 +6970,21 @@ var newSubCommand = defineCommand7({
6552
6970
  title: { type: "string", description: "Ticket title (default: id)" }
6553
6971
  },
6554
6972
  async run({ args }) {
6555
- p7.intro(brand(`ticket new ${accent(args.id)}`));
6973
+ p8.intro(brand(`ticket new ${accent(args.id)}`));
6556
6974
  const id = args.id;
6557
6975
  if (!/^[A-Za-z0-9][A-Za-z0-9-_]*$/.test(id)) {
6558
- p7.cancel(`Invalid ticket id '${id}'. Use letters, digits, hyphens, underscores (must start alphanumeric).`);
6976
+ p8.cancel(`Invalid ticket id '${id}'. Use letters, digits, hyphens, underscores (must start alphanumeric).`);
6559
6977
  process.exit(1);
6560
6978
  }
6561
6979
  let title = args.title;
6562
6980
  if (!title) {
6563
- const value = await p7.text({
6981
+ const value = await p8.text({
6564
6982
  message: "Ticket title",
6565
6983
  placeholder: id,
6566
6984
  defaultValue: id
6567
6985
  });
6568
- if (p7.isCancel(value)) {
6569
- p7.cancel("Cancelled");
6986
+ if (p8.isCancel(value)) {
6987
+ p8.cancel("Cancelled");
6570
6988
  process.exit(0);
6571
6989
  }
6572
6990
  title = value;
@@ -6576,13 +6994,13 @@ var newSubCommand = defineCommand7({
6576
6994
  ticket = createTicket(args.workspace, args.id, title);
6577
6995
  } catch (err) {
6578
6996
  if (err instanceof TicketError) {
6579
- p7.cancel(err.message);
6997
+ p8.cancel(err.message);
6580
6998
  process.exit(1);
6581
6999
  }
6582
7000
  throw err;
6583
7001
  }
6584
- p7.log.success(`Wrote ${ticket.path}`);
6585
- p7.outro(`Reference it from a repo's progress/current.md with:
7002
+ p8.log.success(`Wrote ${ticket.path}`);
7003
+ p8.outro(`Reference it from a repo's progress/current.md with:
6586
7004
  ticket: ${args.id}`);
6587
7005
  }
6588
7006
  });
@@ -6598,9 +7016,9 @@ var archiveSubCommand = defineCommand7({
6598
7016
  run({ args }) {
6599
7017
  try {
6600
7018
  const result = archiveTicket(args.workspace, args.id);
6601
- p7.intro(brand(`ticket archive ${accent(args.id)}`));
6602
- p7.log.success(`Archived \u2192 ${dim(result.path)}`);
6603
- p7.outro(dim("Done"));
7019
+ p8.intro(brand(`ticket archive ${accent(args.id)}`));
7020
+ p8.log.success(`Archived \u2192 ${dim(result.path)}`);
7021
+ p8.outro(dim("Done"));
6604
7022
  } catch (err) {
6605
7023
  if (err instanceof TicketError) {
6606
7024
  console.error(err.message);
@@ -6621,23 +7039,23 @@ var deleteSubCommand2 = defineCommand7({
6621
7039
  yes: { type: "boolean", description: "Skip confirmation" }
6622
7040
  },
6623
7041
  async run({ args }) {
6624
- p7.intro(brand(`ticket delete ${accent(args.id)}`));
7042
+ p8.intro(brand(`ticket delete ${accent(args.id)}`));
6625
7043
  if (!args.yes) {
6626
- const ok = await p7.confirm({
7044
+ const ok = await p8.confirm({
6627
7045
  message: `Permanently delete ticket '${args.id}' from workspace '${args.workspace}'?`,
6628
7046
  initialValue: false
6629
7047
  });
6630
- if (p7.isCancel(ok) || !ok) {
6631
- p7.cancel("Aborted");
7048
+ if (p8.isCancel(ok) || !ok) {
7049
+ p8.cancel("Aborted");
6632
7050
  return;
6633
7051
  }
6634
7052
  }
6635
7053
  try {
6636
7054
  deleteTicket(args.workspace, args.id);
6637
- p7.outro("Deleted");
7055
+ p8.outro("Deleted");
6638
7056
  } catch (err) {
6639
7057
  if (err instanceof TicketError) {
6640
- p7.cancel(err.message);
7058
+ p8.cancel(err.message);
6641
7059
  process.exit(1);
6642
7060
  }
6643
7061
  throw err;
@@ -6660,7 +7078,7 @@ var ticketCommand = defineCommand7({
6660
7078
 
6661
7079
  // src/commands/configure.ts
6662
7080
  import { defineCommand as defineCommand8 } from "citty";
6663
- import * as p8 from "@clack/prompts";
7081
+ import * as p9 from "@clack/prompts";
6664
7082
  import { existsSync as existsSync24, readFileSync as readFileSync20 } from "fs";
6665
7083
  import { resolve as resolve16 } from "path";
6666
7084
  var ENGINE_OPTIONS2 = [
@@ -6678,7 +7096,7 @@ function loadOrExit(cwd) {
6678
7096
  if (!existsSync24(cwd)) fail(`Directory not found: ${cwd}`);
6679
7097
  const configPath = resolve16(cwd, "navori.config.json");
6680
7098
  if (!existsSync24(configPath)) fail(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
6681
- const config = readConfig(configPath);
7099
+ const config = readConfigOrExit(configPath);
6682
7100
  const raw = JSON.parse(readFileSync20(configPath, "utf-8"));
6683
7101
  return { config, path: configPath, raw };
6684
7102
  }
@@ -6698,7 +7116,7 @@ var pluginsSubCommand = defineCommand8({
6698
7116
  async run({ args }) {
6699
7117
  const cwd = resolve16(args.cwd ?? process.cwd());
6700
7118
  const { config, path, raw } = loadOrExit(cwd);
6701
- p8.intro(brand("configure plugins"));
7119
+ p9.intro(brand("configure plugins"));
6702
7120
  const allIds = listKnownPluginIds();
6703
7121
  const current = config.plugins ?? {};
6704
7122
  const enabledNow = new Set(
@@ -6717,14 +7135,14 @@ var pluginsSubCommand = defineCommand8({
6717
7135
  hint: plugin.manifest.description
6718
7136
  };
6719
7137
  }).filter((o) => o !== null);
6720
- const selected = await p8.multiselect({
7138
+ const selected = await p9.multiselect({
6721
7139
  message: "Plugins enabled in this repo",
6722
7140
  options,
6723
7141
  required: false,
6724
7142
  initialValues: [...enabledNow]
6725
7143
  });
6726
- if (p8.isCancel(selected)) {
6727
- p8.cancel("Cancelled");
7144
+ if (p9.isCancel(selected)) {
7145
+ p9.cancel("Cancelled");
6728
7146
  return;
6729
7147
  }
6730
7148
  const selectedSet = new Set(selected);
@@ -6734,10 +7152,10 @@ var pluginsSubCommand = defineCommand8({
6734
7152
  persist(path, raw);
6735
7153
  const added = [...selectedSet].filter((id) => !enabledNow.has(id));
6736
7154
  const removed = [...enabledNow].filter((id) => !selectedSet.has(id));
6737
- if (added.length > 0) p8.log.success(`Enabled: ${added.join(", ")}`);
6738
- if (removed.length > 0) p8.log.warn(`Disabled: ${removed.join(", ")}`);
6739
- if (added.length === 0 && removed.length === 0) p8.log.info("No changes");
6740
- p8.outro("Run 'navori render --apply' or 'navori sync' to apply.");
7155
+ if (added.length > 0) p9.log.success(`Enabled: ${added.join(", ")}`);
7156
+ if (removed.length > 0) p9.log.warn(`Disabled: ${removed.join(", ")}`);
7157
+ if (added.length === 0 && removed.length === 0) p9.log.info("No changes");
7158
+ p9.outro("Run 'navori render --apply' or 'navori sync' to apply.");
6741
7159
  }
6742
7160
  });
6743
7161
  var qualityGateSubCommand = defineCommand8({
@@ -6753,39 +7171,39 @@ var qualityGateSubCommand = defineCommand8({
6753
7171
  async run({ args }) {
6754
7172
  const cwd = resolve16(args.cwd ?? process.cwd());
6755
7173
  const { config, path, raw } = loadOrExit(cwd);
6756
- p8.intro(brand("configure quality-gate"));
7174
+ p9.intro(brand("configure quality-gate"));
6757
7175
  let fast = args.fast;
6758
7176
  let full = args.full;
6759
7177
  if (!fast || !full) {
6760
- const fastVal = await p8.text({
7178
+ const fastVal = await p9.text({
6761
7179
  message: "Fast gate command (runs on Stop hook)",
6762
7180
  placeholder: config.qualityGate?.fast ?? "pnpm tsc --noEmit",
6763
7181
  defaultValue: config.qualityGate?.fast ?? ""
6764
7182
  });
6765
- if (p8.isCancel(fastVal)) {
6766
- p8.cancel("Cancelled");
7183
+ if (p9.isCancel(fastVal)) {
7184
+ p9.cancel("Cancelled");
6767
7185
  return;
6768
7186
  }
6769
7187
  fast = fastVal.trim();
6770
- const fullVal = await p8.text({
7188
+ const fullVal = await p9.text({
6771
7189
  message: "Full gate command (runs before close session)",
6772
7190
  placeholder: config.qualityGate?.full ?? fast,
6773
7191
  defaultValue: config.qualityGate?.full ?? fast
6774
7192
  });
6775
- if (p8.isCancel(fullVal)) {
6776
- p8.cancel("Cancelled");
7193
+ if (p9.isCancel(fullVal)) {
7194
+ p9.cancel("Cancelled");
6777
7195
  return;
6778
7196
  }
6779
7197
  full = fullVal.trim();
6780
7198
  }
6781
7199
  if (!fast || !full) {
6782
- p8.cancel("Both fast and full commands are required");
7200
+ p9.cancel("Both fast and full commands are required");
6783
7201
  return;
6784
7202
  }
6785
7203
  raw.qualityGate = { fast, full };
6786
7204
  persist(path, raw);
6787
- p8.log.success(`qualityGate updated`);
6788
- p8.outro("Done");
7205
+ p9.log.success(`qualityGate updated`);
7206
+ p9.outro("Done");
6789
7207
  }
6790
7208
  });
6791
7209
  var languageSubCommand = defineCommand8({
@@ -6800,10 +7218,10 @@ var languageSubCommand = defineCommand8({
6800
7218
  async run({ args }) {
6801
7219
  const cwd = resolve16(args.cwd ?? process.cwd());
6802
7220
  const { config, path, raw } = loadOrExit(cwd);
6803
- p8.intro(brand("configure language"));
7221
+ p9.intro(brand("configure language"));
6804
7222
  let value = args.value;
6805
7223
  if (!value) {
6806
- const choice = await p8.select({
7224
+ const choice = await p9.select({
6807
7225
  message: "Language for managed Core assets",
6808
7226
  options: [
6809
7227
  { value: "es", label: "Espa\xF1ol (default \u2014 full coverage)" },
@@ -6811,20 +7229,20 @@ var languageSubCommand = defineCommand8({
6811
7229
  ],
6812
7230
  initialValue: config.language
6813
7231
  });
6814
- if (p8.isCancel(choice)) {
6815
- p8.cancel("Cancelled");
7232
+ if (p9.isCancel(choice)) {
7233
+ p9.cancel("Cancelled");
6816
7234
  return;
6817
7235
  }
6818
7236
  value = choice;
6819
7237
  }
6820
7238
  if (value !== "es" && value !== "en") {
6821
- p8.cancel(`Invalid language '${value}'. Must be 'es' or 'en'.`);
7239
+ p9.cancel(`Invalid language '${value}'. Must be 'es' or 'en'.`);
6822
7240
  return;
6823
7241
  }
6824
7242
  raw.language = value;
6825
7243
  persist(path, raw);
6826
- p8.log.success(`language \u2192 ${value}`);
6827
- p8.outro("Run 'navori render --apply' to re-render managed blocks in the new language.");
7244
+ p9.log.success(`language \u2192 ${value}`);
7245
+ p9.outro("Run 'navori render --apply' to re-render managed blocks in the new language.");
6828
7246
  }
6829
7247
  });
6830
7248
  var branchBaseSubCommand = defineCommand8({
@@ -6839,28 +7257,28 @@ var branchBaseSubCommand = defineCommand8({
6839
7257
  async run({ args }) {
6840
7258
  const cwd = resolve16(args.cwd ?? process.cwd());
6841
7259
  const { config, path, raw } = loadOrExit(cwd);
6842
- p8.intro(brand("configure branch-base"));
7260
+ p9.intro(brand("configure branch-base"));
6843
7261
  let value = args.value?.trim();
6844
7262
  if (!value) {
6845
- const input = await p8.text({
7263
+ const input = await p9.text({
6846
7264
  message: "Base branch that gates (semgrep / jscpd / cognitive) diff against",
6847
7265
  placeholder: config.branchBase,
6848
7266
  defaultValue: config.branchBase
6849
7267
  });
6850
- if (p8.isCancel(input)) {
6851
- p8.cancel("Cancelled");
7268
+ if (p9.isCancel(input)) {
7269
+ p9.cancel("Cancelled");
6852
7270
  return;
6853
7271
  }
6854
7272
  value = input.trim();
6855
7273
  }
6856
7274
  if (!value) {
6857
- p8.cancel("Branch name cannot be empty");
7275
+ p9.cancel("Branch name cannot be empty");
6858
7276
  return;
6859
7277
  }
6860
7278
  raw.branchBase = value;
6861
7279
  persist(path, raw);
6862
- p8.log.success(`branchBase \u2192 ${value}`);
6863
- p8.outro("Run 'navori render --apply' to update the gate scripts.");
7280
+ p9.log.success(`branchBase \u2192 ${value}`);
7281
+ p9.outro("Run 'navori render --apply' to update the gate scripts.");
6864
7282
  }
6865
7283
  });
6866
7284
  var prTargetSubCommand = defineCommand8({
@@ -6875,32 +7293,32 @@ var prTargetSubCommand = defineCommand8({
6875
7293
  async run({ args }) {
6876
7294
  const cwd = resolve16(args.cwd ?? process.cwd());
6877
7295
  const { config, path, raw } = loadOrExit(cwd);
6878
- p8.intro(brand("configure pr-target"));
7296
+ p9.intro(brand("configure pr-target"));
6879
7297
  let value = args.value?.trim();
6880
7298
  if (!value) {
6881
7299
  const fallback = config.prTarget ?? config.branchBase;
6882
- const input = await p8.text({
7300
+ const input = await p9.text({
6883
7301
  message: "Branch PRs open against (gh pr create --base)",
6884
7302
  placeholder: fallback,
6885
7303
  defaultValue: fallback
6886
7304
  });
6887
- if (p8.isCancel(input)) {
6888
- p8.cancel("Cancelled");
7305
+ if (p9.isCancel(input)) {
7306
+ p9.cancel("Cancelled");
6889
7307
  return;
6890
7308
  }
6891
7309
  value = input.trim();
6892
7310
  }
6893
7311
  if (!value) {
6894
- p8.cancel("Branch name cannot be empty");
7312
+ p9.cancel("Branch name cannot be empty");
6895
7313
  return;
6896
7314
  }
6897
7315
  raw.prTarget = value;
6898
7316
  persist(path, raw);
6899
- p8.log.success(`prTarget \u2192 ${value}`);
7317
+ p9.log.success(`prTarget \u2192 ${value}`);
6900
7318
  if (value === config.branchBase) {
6901
- p8.log.message(dim(`Same as branchBase \u2014 PRs target ${value} as before.`));
7319
+ p9.log.message(dim(`Same as branchBase \u2014 PRs target ${value} as before.`));
6902
7320
  }
6903
- p8.outro("Run 'navori render --apply' to update the PR skills.");
7321
+ p9.outro("Run 'navori render --apply' to update the PR skills.");
6904
7322
  }
6905
7323
  });
6906
7324
  var enginesSubCommand = defineCommand8({
@@ -6914,21 +7332,21 @@ var enginesSubCommand = defineCommand8({
6914
7332
  async run({ args }) {
6915
7333
  const cwd = resolve16(args.cwd ?? process.cwd());
6916
7334
  const { config, path, raw } = loadOrExit(cwd);
6917
- p8.intro(brand("configure engines"));
6918
- const selected = await p8.multiselect({
7335
+ p9.intro(brand("configure engines"));
7336
+ const selected = await p9.multiselect({
6919
7337
  message: "Engines to target",
6920
7338
  options: ENGINE_OPTIONS2,
6921
7339
  required: true,
6922
7340
  initialValues: config.engines
6923
7341
  });
6924
- if (p8.isCancel(selected)) {
6925
- p8.cancel("Cancelled");
7342
+ if (p9.isCancel(selected)) {
7343
+ p9.cancel("Cancelled");
6926
7344
  return;
6927
7345
  }
6928
7346
  raw.engines = selected;
6929
7347
  persist(path, raw);
6930
- p8.log.success(`engines \u2192 ${selected.join(", ")}`);
6931
- p8.outro("Done");
7348
+ p9.log.success(`engines \u2192 ${selected.join(", ")}`);
7349
+ p9.outro("Done");
6932
7350
  }
6933
7351
  });
6934
7352
  var workspaceSubCommand = defineCommand8({
@@ -6945,33 +7363,33 @@ var workspaceSubCommand = defineCommand8({
6945
7363
  const cwd = resolve16(args.cwd ?? process.cwd());
6946
7364
  const { path, raw } = loadOrExit(cwd);
6947
7365
  const value = args.value?.trim();
6948
- p8.intro(brand("configure workspace"));
7366
+ p9.intro(brand("configure workspace"));
6949
7367
  if (!value) {
6950
7368
  const currentWorkspace = raw.workspace;
6951
7369
  if (!currentWorkspace) {
6952
- p8.outro("No workspace associated. Nothing to remove.");
7370
+ p9.outro("No workspace associated. Nothing to remove.");
6953
7371
  return;
6954
7372
  }
6955
7373
  if (!args.yes) {
6956
- const ok = await p8.confirm({
7374
+ const ok = await p9.confirm({
6957
7375
  message: `Remove workspace association '${currentWorkspace}'? Plugins inherited from the workspace defaults will no longer be applied on next render.`,
6958
7376
  initialValue: false
6959
7377
  });
6960
- if (p8.isCancel(ok) || !ok) {
6961
- p8.cancel("Aborted");
7378
+ if (p9.isCancel(ok) || !ok) {
7379
+ p9.cancel("Aborted");
6962
7380
  return;
6963
7381
  }
6964
7382
  }
6965
7383
  delete raw.workspace;
6966
7384
  persist(path, raw);
6967
- p8.log.success("Workspace association removed");
6968
- p8.outro("Run 'navori render --apply' to apply.");
7385
+ p9.log.success("Workspace association removed");
7386
+ p9.outro("Run 'navori render --apply' to apply.");
6969
7387
  return;
6970
7388
  }
6971
7389
  raw.workspace = value;
6972
7390
  persist(path, raw);
6973
- p8.log.success(`workspace \u2192 ${value}`);
6974
- p8.outro("Run 'navori render --apply' to apply.");
7391
+ p9.log.success(`workspace \u2192 ${value}`);
7392
+ p9.outro("Run 'navori render --apply' to apply.");
6975
7393
  }
6976
7394
  });
6977
7395
  var configureCommand = defineCommand8({
@@ -6992,7 +7410,7 @@ var configureCommand = defineCommand8({
6992
7410
 
6993
7411
  // src/commands/update.ts
6994
7412
  import { defineCommand as defineCommand9 } from "citty";
6995
- import * as p9 from "@clack/prompts";
7413
+ import * as p10 from "@clack/prompts";
6996
7414
  import { existsSync as existsSync25, readFileSync as readFileSync21 } from "fs";
6997
7415
  import { resolve as resolve17 } from "path";
6998
7416
  function sameSet(a, b) {
@@ -7081,16 +7499,16 @@ var updateCommand = defineCommand9({
7081
7499
  async run({ args }) {
7082
7500
  const cwd = resolve17(args.cwd ?? process.cwd());
7083
7501
  const configPath = `${cwd}/navori.config.json`;
7084
- p9.intro(brand("update"));
7502
+ p10.intro(brand("update"));
7085
7503
  if (!existsSync25(cwd)) {
7086
- p9.cancel(`Directory not found: ${cwd}`);
7504
+ p10.cancel(`Directory not found: ${cwd}`);
7087
7505
  process.exit(1);
7088
7506
  }
7089
7507
  if (!existsSync25(configPath)) {
7090
- p9.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
7508
+ p10.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
7091
7509
  process.exit(1);
7092
7510
  }
7093
- const config = readConfig(configPath);
7511
+ const config = readConfigOrExit(configPath);
7094
7512
  const detected = detectProject(cwd);
7095
7513
  const diffs = diffConfig(config, detected);
7096
7514
  const preview = runRender(cwd, true);
@@ -7098,51 +7516,51 @@ var updateCommand = defineCommand9({
7098
7516
  const previewConflicts = preview.engineResult?.skipped ?? [];
7099
7517
  const updates = preview.updatesAvailable ?? [];
7100
7518
  if (diffs.length === 0 && previewWrites.length === 0 && previewConflicts.length === 0) {
7101
- p9.outro("Up to date \u2014 nothing to update");
7519
+ p10.outro("Up to date \u2014 nothing to update");
7102
7520
  return;
7103
7521
  }
7104
7522
  if (diffs.length > 0) {
7105
7523
  const lines = diffs.map(
7106
7524
  (d) => ` ${color.yellow(sym.updated)} ${accent(d.field)}${dim(":")} ${color.red(d.before)} ${dim("\u2192")} ${color.green(d.after)}`
7107
7525
  );
7108
- p9.log.info(`Config drift detected (${diffs.length}):
7526
+ p10.log.info(`Config drift detected (${diffs.length}):
7109
7527
  ${lines.join("\n")}`);
7110
7528
  } else {
7111
- p9.log.info("Config is in sync with the repo");
7529
+ p10.log.info("Config is in sync with the repo");
7112
7530
  }
7113
7531
  if (previewWrites.length > 0) {
7114
7532
  const shown = previewWrites.slice(0, 12).map((w) => ` ${color.cyan(sym.update)} ${w.path} ${dim(`(${w.status})`)}`);
7115
7533
  const more = previewWrites.length > 12 ? `
7116
7534
  ${dim(`\u2026 +${previewWrites.length - 12} m\xE1s`)}` : "";
7117
- p9.log.info(`Archivos que se actualizar\xEDan (${previewWrites.length}):
7535
+ p10.log.info(`Archivos que se actualizar\xEDan (${previewWrites.length}):
7118
7536
  ${shown.join("\n")}${more}`);
7119
7537
  }
7120
7538
  if (updates.length > 0) {
7121
7539
  const lines = updates.map(
7122
7540
  (u) => ` ${color.cyan(sym.update)} ${u.id} ${dim(`(${u.source} ${u.fromVersion} \u2192 ${u.toVersion})`)}`
7123
7541
  );
7124
- p9.log.info(`Managed block updates available (${updates.length}):
7542
+ p10.log.info(`Managed block updates available (${updates.length}):
7125
7543
  ${lines.join("\n")}`);
7126
7544
  }
7127
7545
  if (previewConflicts.length > 0) {
7128
- p9.log.warn(`${previewConflicts.length} archivo(s) con ediciones tuyas \u2014 'navori sync' los resuelve interactivamente`);
7546
+ p10.log.warn(`${previewConflicts.length} archivo(s) con ediciones tuyas \u2014 'navori sync' los resuelve interactivamente`);
7129
7547
  }
7130
7548
  if (args["dry-run"]) {
7131
7549
  if (diffs.some((d) => d.field === "project.libraries")) {
7132
- p9.log.message(
7550
+ p10.log.message(
7133
7551
  dim("Nota: aplicar el diff de project.libraries materializa las library skills (el preview de arriba refleja el config actual).")
7134
7552
  );
7135
7553
  }
7136
- p9.outro("Dry-run complete (no files written)");
7554
+ p10.outro("Dry-run complete (no files written)");
7137
7555
  return;
7138
7556
  }
7139
7557
  if (!args.yes && diffs.length > 0) {
7140
- const ok = await p9.confirm({
7558
+ const ok = await p10.confirm({
7141
7559
  message: `Apply ${diffs.length} config update${diffs.length === 1 ? "" : "s"} + re-render?`,
7142
7560
  initialValue: true
7143
7561
  });
7144
- if (p9.isCancel(ok) || !ok) {
7145
- p9.cancel("Aborted");
7562
+ if (p10.isCancel(ok) || !ok) {
7563
+ p10.cancel("Aborted");
7146
7564
  return;
7147
7565
  }
7148
7566
  }
@@ -7151,42 +7569,42 @@ ${lines.join("\n")}`);
7151
7569
  delete raw.$schema;
7152
7570
  applyDiffs(raw, detected, diffs);
7153
7571
  writeConfig(configPath, raw);
7154
- p9.log.success(`Updated ${configPath}`);
7572
+ p10.log.success(`Updated ${configPath}`);
7155
7573
  }
7156
7574
  if (args["config-only"]) {
7157
- p9.outro("Config updated. Corre 'navori sync' para refrescar CLAUDE.md + .claude/.");
7575
+ p10.outro("Config updated. Corre 'navori sync' para refrescar CLAUDE.md + .claude/.");
7158
7576
  return;
7159
7577
  }
7160
7578
  const result = runRender(cwd, false);
7161
7579
  if (!result.ok) {
7162
- p9.log.error(result.reason ?? "Render failed");
7163
- p9.outro("Done (config actualizado, pero el render fall\xF3)");
7580
+ p10.log.error(result.reason ?? "Render failed");
7581
+ p10.outro("Done (config actualizado, pero el render fall\xF3)");
7164
7582
  return;
7165
7583
  }
7166
7584
  const written = result.engineResult?.written ?? [];
7167
7585
  const skipped = result.engineResult?.skipped ?? [];
7168
7586
  if (skipped.length > 0) {
7169
- p9.log.warn(`${skipped.length} archivo(s) con ediciones tuyas no se tocaron \u2014 'navori sync' para resolver`);
7587
+ p10.log.warn(`${skipped.length} archivo(s) con ediciones tuyas no se tocaron \u2014 'navori sync' para resolver`);
7170
7588
  }
7171
7589
  if (written.length > 0) {
7172
- p9.log.success(`Re-rendered ${written.length} archivo(s) (CLAUDE.md + .claude/)`);
7590
+ p10.log.success(`Re-rendered ${written.length} archivo(s) (CLAUDE.md + .claude/)`);
7173
7591
  } else {
7174
- p9.log.info("No re-render needed");
7592
+ p10.log.info("No re-render needed");
7175
7593
  }
7176
- p9.outro("Done");
7594
+ p10.outro("Done");
7177
7595
  }
7178
7596
  });
7179
7597
 
7180
7598
  // src/commands/backup.ts
7181
7599
  import { defineCommand as defineCommand10 } from "citty";
7182
- import * as p10 from "@clack/prompts";
7183
- import { existsSync as existsSync26, readdirSync as readdirSync10, statSync as statSync8, copyFileSync as copyFileSync4, mkdirSync as mkdirSync8 } from "fs";
7600
+ import * as p11 from "@clack/prompts";
7601
+ import { existsSync as existsSync26, readdirSync as readdirSync11, statSync as statSync8, copyFileSync as copyFileSync4, mkdirSync as mkdirSync8 } from "fs";
7184
7602
  import { join as join19, relative as relative5, resolve as resolve18, dirname as dirname8 } from "path";
7185
7603
  function listBackups() {
7186
7604
  const root = backupRoot();
7187
7605
  if (!existsSync26(root)) return [];
7188
7606
  const entries = [];
7189
- for (const name of readdirSync10(root)) {
7607
+ for (const name of readdirSync11(root)) {
7190
7608
  const full = join19(root, name);
7191
7609
  try {
7192
7610
  const stat = statSync8(full);
@@ -7201,7 +7619,7 @@ function listBackups() {
7201
7619
  }
7202
7620
  function collectFiles(root, dir) {
7203
7621
  const out = [];
7204
- for (const entry of readdirSync10(dir)) {
7622
+ for (const entry of readdirSync11(dir)) {
7205
7623
  const full = join19(dir, entry);
7206
7624
  try {
7207
7625
  const stat = statSync8(full);
@@ -7232,10 +7650,10 @@ var listSubCommand2 = defineCommand10({
7232
7650
  console.log(JSON.stringify({ backups: truncated, totalAvailable: backups.length }, null, 2));
7233
7651
  return;
7234
7652
  }
7235
- p10.intro(brand("backup list"));
7653
+ p11.intro(brand("backup list"));
7236
7654
  if (backups.length === 0) {
7237
- p10.log.info("No backups found. They are created automatically before each 'sync' or 'render' that modifies files.");
7238
- p10.outro(dim("Done"));
7655
+ p11.log.info("No backups found. They are created automatically before each 'sync' or 'render' that modifies files.");
7656
+ p11.outro(dim("Done"));
7239
7657
  return;
7240
7658
  }
7241
7659
  const lines = [];
@@ -7251,8 +7669,8 @@ var listSubCommand2 = defineCommand10({
7251
7669
  if (backups.length > truncated.length) {
7252
7670
  lines.push(dim(` ... ${backups.length - truncated.length} more (use --limit to show)`));
7253
7671
  }
7254
- p10.log.message(lines.join("\n"));
7255
- p10.outro(dim("Done"));
7672
+ p11.log.message(lines.join("\n"));
7673
+ p11.outro(dim("Done"));
7256
7674
  }
7257
7675
  });
7258
7676
  var restoreSubCommand = defineCommand10({
@@ -7269,25 +7687,25 @@ var restoreSubCommand = defineCommand10({
7269
7687
  const ts = args.timestamp;
7270
7688
  const cwd = resolve18(args.cwd ?? process.cwd());
7271
7689
  const backupDir = join19(backupRoot(), ts);
7272
- p10.intro(brand(`backup restore ${accent(ts)}`));
7690
+ p11.intro(brand(`backup restore ${accent(ts)}`));
7273
7691
  if (!existsSync26(backupDir)) {
7274
- p10.cancel(`Backup not found: ${backupDir}`);
7692
+ p11.cancel(`Backup not found: ${backupDir}`);
7275
7693
  process.exit(1);
7276
7694
  }
7277
7695
  const files = collectFiles(backupDir, backupDir);
7278
7696
  if (files.length === 0) {
7279
- p10.cancel(`Backup is empty: ${backupDir}`);
7697
+ p11.cancel(`Backup is empty: ${backupDir}`);
7280
7698
  process.exit(1);
7281
7699
  }
7282
- p10.log.message(`Will restore ${files.length} file(s) from ${backupDir} into ${cwd}:`);
7283
- for (const f of files) p10.log.message(` \xB7 ${f}`);
7700
+ p11.log.message(`Will restore ${files.length} file(s) from ${backupDir} into ${cwd}:`);
7701
+ for (const f of files) p11.log.message(` \xB7 ${f}`);
7284
7702
  if (!args.yes) {
7285
- const ok = await p10.confirm({
7703
+ const ok = await p11.confirm({
7286
7704
  message: "Existing files will be overwritten. Proceed?",
7287
7705
  initialValue: false
7288
7706
  });
7289
- if (p10.isCancel(ok) || !ok) {
7290
- p10.cancel("Aborted");
7707
+ if (p11.isCancel(ok) || !ok) {
7708
+ p11.cancel("Aborted");
7291
7709
  return;
7292
7710
  }
7293
7711
  }
@@ -7297,7 +7715,7 @@ var restoreSubCommand = defineCommand10({
7297
7715
  mkdirSync8(dirname8(dest), { recursive: true });
7298
7716
  copyFileSync4(src, dest);
7299
7717
  }
7300
- p10.outro(`Restored ${files.length} file(s)`);
7718
+ p11.outro(`Restored ${files.length} file(s)`);
7301
7719
  }
7302
7720
  });
7303
7721
  function humanAge(mtimeMs) {
@@ -7323,19 +7741,19 @@ var backupCommand = defineCommand10({
7323
7741
 
7324
7742
  // src/commands/migrations.ts
7325
7743
  import { defineCommand as defineCommand11 } from "citty";
7326
- import * as p11 from "@clack/prompts";
7327
- import { existsSync as existsSync27, readdirSync as readdirSync11, statSync as statSync9, copyFileSync as copyFileSync5, mkdirSync as mkdirSync9 } from "fs";
7744
+ import * as p12 from "@clack/prompts";
7745
+ import { existsSync as existsSync27, readdirSync as readdirSync12, statSync as statSync9, copyFileSync as copyFileSync5, mkdirSync as mkdirSync9 } from "fs";
7328
7746
  import { join as join20, relative as relative6, resolve as resolve19, dirname as dirname9 } from "path";
7329
7747
  function listMigrations() {
7330
7748
  const root = migrationsRoot();
7331
7749
  if (!existsSync27(root)) return [];
7332
7750
  const entries = [];
7333
- for (const ts of readdirSync11(root)) {
7751
+ for (const ts of readdirSync12(root)) {
7334
7752
  const tsDir = join20(root, ts);
7335
7753
  try {
7336
7754
  const stat = statSync9(tsDir);
7337
7755
  if (!stat.isDirectory()) continue;
7338
- for (const repoName of readdirSync11(tsDir)) {
7756
+ for (const repoName of readdirSync12(tsDir)) {
7339
7757
  const repoDir = join20(tsDir, repoName);
7340
7758
  try {
7341
7759
  const repoStat = statSync9(repoDir);
@@ -7353,7 +7771,7 @@ function listMigrations() {
7353
7771
  }
7354
7772
  function collectFiles2(root, dir) {
7355
7773
  const out = [];
7356
- for (const entry of readdirSync11(dir)) {
7774
+ for (const entry of readdirSync12(dir)) {
7357
7775
  const full = join20(dir, entry);
7358
7776
  try {
7359
7777
  const stat = statSync9(full);
@@ -7384,10 +7802,10 @@ var listSubCommand3 = defineCommand11({
7384
7802
  console.log(JSON.stringify({ migrations: truncated, totalAvailable: migrations.length }, null, 2));
7385
7803
  return;
7386
7804
  }
7387
- p11.intro(brand("migrations list"));
7805
+ p12.intro(brand("migrations list"));
7388
7806
  if (migrations.length === 0) {
7389
- p11.log.info("No migrations found. They are created when 'init' adopts navori in replace mode (the interactive wizard) on a repo with existing Claude infrastructure.");
7390
- p11.outro(dim("Done"));
7807
+ p12.log.info("No migrations found. They are created when 'init' adopts navori in replace mode (the interactive wizard) on a repo with existing Claude infrastructure.");
7808
+ p12.outro(dim("Done"));
7391
7809
  return;
7392
7810
  }
7393
7811
  const lines = [];
@@ -7404,8 +7822,8 @@ var listSubCommand3 = defineCommand11({
7404
7822
  if (migrations.length > truncated.length) {
7405
7823
  lines.push(dim(` ... ${migrations.length - truncated.length} more (use --limit to show)`));
7406
7824
  }
7407
- p11.log.message(lines.join("\n"));
7408
- p11.outro(dim("Done"));
7825
+ p12.log.message(lines.join("\n"));
7826
+ p12.outro(dim("Done"));
7409
7827
  }
7410
7828
  });
7411
7829
  var restoreSubCommand2 = defineCommand11({
@@ -7424,26 +7842,26 @@ var restoreSubCommand2 = defineCommand11({
7424
7842
  const repoName = args.repo;
7425
7843
  const cwd = resolve19(args.cwd ?? process.cwd());
7426
7844
  const migrationDir = join20(migrationsRoot(), ts, repoName);
7427
- p11.intro(brand(`migrations restore ${accent(`${ts}/${repoName}`)}`));
7845
+ p12.intro(brand(`migrations restore ${accent(`${ts}/${repoName}`)}`));
7428
7846
  if (!existsSync27(migrationDir)) {
7429
- p11.cancel(`Migration not found: ${migrationDir}`);
7847
+ p12.cancel(`Migration not found: ${migrationDir}`);
7430
7848
  process.exit(1);
7431
7849
  }
7432
7850
  const files = collectFiles2(migrationDir, migrationDir);
7433
7851
  if (files.length === 0) {
7434
- p11.cancel(`Migration is empty: ${migrationDir}`);
7852
+ p12.cancel(`Migration is empty: ${migrationDir}`);
7435
7853
  process.exit(1);
7436
7854
  }
7437
- p11.log.message(`Will restore ${files.length} file(s) from ${migrationDir} into ${cwd}:`);
7438
- for (const f of files.slice(0, 10)) p11.log.message(` \xB7 ${f}`);
7439
- if (files.length > 10) p11.log.message(` ... ${files.length - 10} more`);
7855
+ p12.log.message(`Will restore ${files.length} file(s) from ${migrationDir} into ${cwd}:`);
7856
+ for (const f of files.slice(0, 10)) p12.log.message(` \xB7 ${f}`);
7857
+ if (files.length > 10) p12.log.message(` ... ${files.length - 10} more`);
7440
7858
  if (!args.yes) {
7441
- const ok = await p11.confirm({
7859
+ const ok = await p12.confirm({
7442
7860
  message: "Existing files will be OVERWRITTEN by the migration's snapshot. Proceed?",
7443
7861
  initialValue: false
7444
7862
  });
7445
- if (p11.isCancel(ok) || !ok) {
7446
- p11.cancel("Aborted");
7863
+ if (p12.isCancel(ok) || !ok) {
7864
+ p12.cancel("Aborted");
7447
7865
  return;
7448
7866
  }
7449
7867
  }
@@ -7453,7 +7871,7 @@ var restoreSubCommand2 = defineCommand11({
7453
7871
  mkdirSync9(dirname9(dest), { recursive: true });
7454
7872
  copyFileSync5(src, dest);
7455
7873
  }
7456
- p11.outro(`Restored ${files.length} file(s)`);
7874
+ p12.outro(`Restored ${files.length} file(s)`);
7457
7875
  }
7458
7876
  });
7459
7877
  var migrationsCommand = defineCommand11({
@@ -7480,7 +7898,7 @@ var migrationsCommand = defineCommand11({
7480
7898
 
7481
7899
  // src/commands/preset.ts
7482
7900
  import { defineCommand as defineCommand12 } from "citty";
7483
- import * as p12 from "@clack/prompts";
7901
+ import * as p13 from "@clack/prompts";
7484
7902
  import { existsSync as existsSync28, mkdirSync as mkdirSync10 } from "fs";
7485
7903
  import { join as join21, resolve as resolve20 } from "path";
7486
7904
  var PRESET_ID_RE = /^[a-z0-9][a-z0-9-]*$/;
@@ -7538,20 +7956,20 @@ var initSubCommand2 = defineCommand12({
7538
7956
  run({ args }) {
7539
7957
  const id = String(args.id);
7540
7958
  const cwd = resolve20(args.cwd ?? process.cwd());
7541
- p12.intro(brand(`preset init ${accent(id)}`));
7959
+ p13.intro(brand(`preset init ${accent(id)}`));
7542
7960
  if (id === "custom") {
7543
- p12.cancel("'custom' es un id reservado (es el baseline sin extras). Elige otro nombre.");
7961
+ p13.cancel("'custom' es un id reservado (es el baseline sin extras). Elige otro nombre.");
7544
7962
  process.exit(1);
7545
7963
  }
7546
7964
  if (!PRESET_ID_RE.test(id)) {
7547
- p12.cancel(
7965
+ p13.cancel(
7548
7966
  `Id inv\xE1lido '${id}': usa kebab-case \u2014 min\xFAsculas, n\xFAmeros y guiones, empezando con alfanum\xE9rico.`
7549
7967
  );
7550
7968
  process.exit(1);
7551
7969
  }
7552
7970
  const presetDir = resolve20(cwd, ".navori/presets", id);
7553
7971
  if (existsSync28(presetDir)) {
7554
- p12.cancel(
7972
+ p13.cancel(
7555
7973
  `Ya existe .navori/presets/${id}/ \u2014 b\xF3rralo o usa otro id si quieres regenerarlo.`
7556
7974
  );
7557
7975
  process.exit(1);
@@ -7581,8 +7999,8 @@ var initSubCommand2 = defineCommand12({
7581
7999
  writeFileAtomic(join21(presetDir, `${id}.json`), JSON.stringify(manifest, null, 2) + "\n");
7582
8000
  writeFileAtomic(join21(presetDir, "managed", "stack.md"), stackTemplate(id));
7583
8001
  writeFileAtomic(join21(presetDir, "skills", `${skillId}.md`), skillTemplate(skillId));
7584
- p12.log.success(`Creado .navori/presets/${id}/`);
7585
- p12.log.message(
8002
+ p13.log.success(`Creado .navori/presets/${id}/`);
8003
+ p13.log.message(
7586
8004
  [
7587
8005
  ` ${dim("\xB7")} ${id}.json`,
7588
8006
  ` ${dim("\xB7")} managed/stack.md`,
@@ -7593,13 +8011,13 @@ var initSubCommand2 = defineCommand12({
7593
8011
  if (existsSync28(configPath)) {
7594
8012
  const config = readConfig(configPath);
7595
8013
  writeConfig(configPath, { ...config, preset: id });
7596
- p12.log.success(`navori.config.json \u2192 preset: ${accent(id)}`);
7597
- p12.outro(`Listo. Edita la plantilla y corre ${accent("navori render --apply")} para materializarla.`);
8014
+ p13.log.success(`navori.config.json \u2192 preset: ${accent(id)}`);
8015
+ p13.outro(`Listo. Edita la plantilla y corre ${accent("navori render --apply")} para materializarla.`);
7598
8016
  } else {
7599
- p12.log.warn(
8017
+ p13.log.warn(
7600
8018
  `No hay navori.config.json en ${cwd}. Corre ${accent("navori init")} y elige el preset '${id}' para activarlo.`
7601
8019
  );
7602
- p12.outro("Preset local scaffoldeado. Inicializa navori para activarlo.");
8020
+ p13.outro("Preset local scaffoldeado. Inicializa navori para activarlo.");
7603
8021
  }
7604
8022
  }
7605
8023
  });
@@ -7615,7 +8033,7 @@ var presetCommand = defineCommand12({
7615
8033
 
7616
8034
  // src/commands/scan.ts
7617
8035
  import { defineCommand as defineCommand13 } from "citty";
7618
- import * as p13 from "@clack/prompts";
8036
+ import * as p14 from "@clack/prompts";
7619
8037
  import { resolve as resolve21 } from "path";
7620
8038
  import { existsSync as existsSync29 } from "fs";
7621
8039
  function runScan(opts) {
@@ -7683,59 +8101,59 @@ var scanCommand = defineCommand13({
7683
8101
  },
7684
8102
  async run({ args }) {
7685
8103
  const cwd = resolve21(args.cwd ?? process.cwd());
7686
- p13.intro(brand("scan"));
8104
+ p14.intro(brand("scan"));
7687
8105
  const dryRun = runScan({ cwd, yes: false });
7688
8106
  if (dryRun.kind === "no-config") {
7689
- p13.cancel(
8107
+ p14.cancel(
7690
8108
  `No encontr\xE9 navori.config.json en ${dryRun.configPath}. Corre 'navori init' primero.`
7691
8109
  );
7692
8110
  process.exit(1);
7693
8111
  }
7694
8112
  if (dryRun.kind === "not-monorepo") {
7695
- p13.cancel(
8113
+ p14.cancel(
7696
8114
  `${dryRun.configPath} no declara 'monorepo'. Edita el config para agregar { monorepo: { enabled: true, tool: '...' } } y vuelve a correr scan.`
7697
8115
  );
7698
8116
  process.exit(1);
7699
8117
  }
7700
8118
  if (dryRun.kind === "no-patterns") {
7701
- p13.log.info(
8119
+ p14.log.info(
7702
8120
  `No encontr\xE9 patrones de workspace en pnpm-workspace.yaml ni en package.json#workspaces.`
7703
8121
  );
7704
- p13.outro(dim("Nada que escanear"));
8122
+ p14.outro(dim("Nada que escanear"));
7705
8123
  return;
7706
8124
  }
7707
8125
  showSummary(dryRun);
7708
8126
  if (dryRun.added.length === 0) {
7709
8127
  if (dryRun.orphan.length > 0) {
7710
- p13.log.warn(
8128
+ p14.log.warn(
7711
8129
  `${dryRun.orphan.length} workspace(s) en config ya no existen en disco. Edita navori.config.json para removerlos.`
7712
8130
  );
7713
8131
  }
7714
- p13.outro(dim("Config al d\xEDa"));
8132
+ p14.outro(dim("Config al d\xEDa"));
7715
8133
  return;
7716
8134
  }
7717
8135
  let overrides = {};
7718
8136
  if (args.yes) {
7719
8137
  } else {
7720
- const ok = await p13.confirm({
8138
+ const ok = await p14.confirm({
7721
8139
  message: `\xBFAgregar ${dryRun.added.length} workspace(s) a navori.config.json?`,
7722
8140
  initialValue: true
7723
8141
  });
7724
- if (p13.isCancel(ok) || !ok) {
7725
- p13.cancel("Cancelado");
8142
+ if (p14.isCancel(ok) || !ok) {
8143
+ p14.cancel("Cancelado");
7726
8144
  return;
7727
8145
  }
7728
8146
  overrides = await collectPresetOverrides(dryRun.added);
7729
8147
  }
7730
8148
  const final = runScan({ cwd, yes: true, presetOverrides: overrides });
7731
8149
  if (final.kind !== "ok") {
7732
- p13.cancel(`Resultado inesperado: ${final.kind}`);
8150
+ p14.cancel(`Resultado inesperado: ${final.kind}`);
7733
8151
  process.exit(1);
7734
8152
  }
7735
- p13.log.success(
8153
+ p14.log.success(
7736
8154
  `Agregu\xE9 ${final.added.length} workspace(s) a ${final.configPath}`
7737
8155
  );
7738
- p13.outro(dim("Corre 'navori render --apply' para generar CLAUDE.md + .claude/ por workspace"));
8156
+ p14.outro(dim("Corre 'navori render --apply' para generar CLAUDE.md + .claude/ por workspace"));
7739
8157
  }
7740
8158
  });
7741
8159
  function showSummary(outcome) {
@@ -7752,36 +8170,36 @@ function showSummary(outcome) {
7752
8170
  if (outcome.orphan.length > 0) {
7753
8171
  rows.push(["orphan", color.yellow(String(outcome.orphan.length))]);
7754
8172
  }
7755
- p13.note(kv(rows), "summary");
8173
+ p14.note(kv(rows), "summary");
7756
8174
  if (outcome.added.length > 0) {
7757
8175
  const lines = outcome.diff.added.map((w) => {
7758
8176
  const fw = w.framework ? dim(` [${w.framework}]`) : "";
7759
8177
  return ` ${color.green("+")} ${w.path}${fw} ${dim("\u2192")} ${w.suggestedPreset}`;
7760
8178
  }).join("\n");
7761
- p13.log.message(`${dim("Workspaces nuevos:")}
8179
+ p14.log.message(`${dim("Workspaces nuevos:")}
7762
8180
  ${lines}`);
7763
8181
  }
7764
8182
  if (outcome.orphan.length > 0) {
7765
8183
  const lines = outcome.orphan.map((w) => ` ${color.yellow("?")} ${w.path}`).join("\n");
7766
- p13.log.message(`${dim("Hu\xE9rfanos (en config, no existen en disco):")}
8184
+ p14.log.message(`${dim("Hu\xE9rfanos (en config, no existen en disco):")}
7767
8185
  ${lines}`);
7768
8186
  }
7769
8187
  }
7770
8188
  async function collectPresetOverrides(added) {
7771
- const acceptAll = await p13.confirm({
8189
+ const acceptAll = await p14.confirm({
7772
8190
  message: `\xBFUsar preset sugerido en cada workspace nuevo?`,
7773
8191
  initialValue: true
7774
8192
  });
7775
- if (p13.isCancel(acceptAll)) return {};
8193
+ if (p14.isCancel(acceptAll)) return {};
7776
8194
  if (acceptAll) return {};
7777
8195
  const overrides = {};
7778
8196
  for (const ws of added) {
7779
- const value = await p13.text({
8197
+ const value = await p14.text({
7780
8198
  message: `Preset para ${ws.path}`,
7781
8199
  placeholder: ws.preset ?? "(heredar del root)",
7782
8200
  defaultValue: ws.preset ?? ""
7783
8201
  });
7784
- if (p13.isCancel(value)) return overrides;
8202
+ if (p14.isCancel(value)) return overrides;
7785
8203
  const trimmed = value.trim();
7786
8204
  if (trimmed) overrides[ws.path] = trimmed;
7787
8205
  }
@@ -7790,7 +8208,7 @@ async function collectPresetOverrides(added) {
7790
8208
 
7791
8209
  // src/commands/status.ts
7792
8210
  import { defineCommand as defineCommand14 } from "citty";
7793
- import * as p14 from "@clack/prompts";
8211
+ import * as p15 from "@clack/prompts";
7794
8212
  import { existsSync as existsSync30 } from "fs";
7795
8213
  import { resolve as resolve22 } from "path";
7796
8214
  var statusCommand = defineCommand14({
@@ -7809,8 +8227,8 @@ var statusCommand = defineCommand14({
7809
8227
  if (args.json) {
7810
8228
  console.log(JSON.stringify({ ok: false, error: "config-missing", configPath }));
7811
8229
  } else {
7812
- p14.intro(brand("status"));
7813
- p14.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
8230
+ p15.intro(brand("status"));
8231
+ p15.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
7814
8232
  }
7815
8233
  process.exit(1);
7816
8234
  }
@@ -7822,8 +8240,8 @@ var statusCommand = defineCommand14({
7822
8240
  if (args.json) {
7823
8241
  console.log(JSON.stringify({ ok: false, error: "config-invalid", message: err.message }));
7824
8242
  } else {
7825
- p14.intro(brand("status"));
7826
- p14.cancel(err.message);
8243
+ p15.intro(brand("status"));
8244
+ p15.cancel(err.message);
7827
8245
  }
7828
8246
  process.exit(1);
7829
8247
  }
@@ -7855,8 +8273,8 @@ var statusCommand = defineCommand14({
7855
8273
  );
7856
8274
  return;
7857
8275
  }
7858
- p14.intro(brand("status"));
7859
- p14.note(
8276
+ p15.intro(brand("status"));
8277
+ p15.note(
7860
8278
  kv([
7861
8279
  ["name", accent(config.name)],
7862
8280
  ["version", config.version],
@@ -7868,14 +8286,14 @@ var statusCommand = defineCommand14({
7868
8286
  ]),
7869
8287
  `Status \xB7 ${dim(cwd)}`
7870
8288
  );
7871
- p14.note(nextSteps.map((s) => ` ${color.cyan(sym.bullet)} ${s}`).join("\n"), "Pr\xF3ximos pasos");
7872
- p14.outro(missingPlugins.length > 0 ? color.red("Issues found") : color.green("OK"));
8289
+ p15.note(nextSteps.map((s) => ` ${color.cyan(sym.bullet)} ${s}`).join("\n"), "Pr\xF3ximos pasos");
8290
+ p15.outro(missingPlugins.length > 0 ? color.red("Issues found") : color.green("OK"));
7873
8291
  }
7874
8292
  });
7875
8293
 
7876
8294
  // src/commands/bench.ts
7877
8295
  import { defineCommand as defineCommand15 } from "citty";
7878
- import * as p15 from "@clack/prompts";
8296
+ import * as p16 from "@clack/prompts";
7879
8297
  import { performance as performance2 } from "perf_hooks";
7880
8298
  import { existsSync as existsSync31 } from "fs";
7881
8299
  import { resolve as resolve23 } from "path";
@@ -7890,9 +8308,9 @@ var benchCommand = defineCommand15({
7890
8308
  },
7891
8309
  async run({ args }) {
7892
8310
  const cwd = resolve23(args.cwd ?? process.cwd());
7893
- p15.intro(brand("bench"));
8311
+ p16.intro(brand("bench"));
7894
8312
  if (!existsSync31(`${cwd}/navori.config.json`)) {
7895
- p15.cancel(`No navori.config.json at ${cwd}. Run 'navori init' first.`);
8313
+ p16.cancel(`No navori.config.json at ${cwd}. Run 'navori init' first.`);
7896
8314
  process.exit(1);
7897
8315
  }
7898
8316
  const runs = Math.max(1, Math.floor(Number(args.runs) || 20));
@@ -7906,7 +8324,7 @@ var benchCommand = defineCommand15({
7906
8324
  samples.sort((a, b) => a - b);
7907
8325
  const at = (q) => samples[Math.min(samples.length - 1, Math.max(0, Math.ceil(q / 100 * samples.length) - 1))];
7908
8326
  const fmt = (ms) => `${ms.toFixed(1)}ms`;
7909
- p15.note(
8327
+ p16.note(
7910
8328
  [
7911
8329
  `runs ${runs}`,
7912
8330
  `min ${fmt(samples[0])}`,
@@ -7916,7 +8334,7 @@ var benchCommand = defineCommand15({
7916
8334
  ].join("\n"),
7917
8335
  `render (dry-run) \xB7 ${dim(cwd)}`
7918
8336
  );
7919
- p15.outro(color.green("Done"));
8337
+ p16.outro(color.green("Done"));
7920
8338
  }
7921
8339
  });
7922
8340