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/assets/core/core-assets/agents/leader.md +5 -3
- package/dist/assets/core/core-assets/hooks/guard-destructive.sh +18 -1
- package/dist/assets/core/core-assets/hooks/quality-gate-pre-commit.sh +14 -1
- package/dist/assets/core/core-assets/managed/orquestacion.md +34 -9
- package/dist/assets/core/core-assets/presets/express/managed/stack.md +20 -0
- package/dist/assets/core/core-assets/presets/express.json +44 -0
- package/dist/assets/core/core-assets/presets/vite-react-ts/managed/stack.md +7 -0
- package/dist/assets/core/core-assets/presets/vite-react-ts.json +23 -0
- package/dist/assets/core/core-assets/skills/loop-back-debug.md +1 -1
- package/dist/assets/plugins/cognitive/scripts/check-cognitive.sh +15 -2
- package/dist/assets/plugins/engram/skills/engram-leader.md +3 -3
- package/dist/assets/plugins/jscpd/scripts/check-jscpd.sh +15 -2
- package/dist/assets/plugins/semgrep/scripts/check-semgrep.sh +15 -2
- package/dist/index.js +998 -580
- package/package.json +2 -2
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
|
|
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:
|
|
212
|
+
engines: tolerantEnumArray(ENGINES, "claude"),
|
|
197
213
|
preset: z2.string().min(1),
|
|
198
|
-
language:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1414
|
-
const abs = resolve5(repoRoot,
|
|
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
|
|
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
|
|
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((
|
|
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
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
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(
|
|
3062
|
+
chmodSync(p17.path, 493);
|
|
2799
3063
|
} catch {
|
|
2800
3064
|
}
|
|
2801
3065
|
}
|
|
2802
|
-
written.push({ path: relative2(cwd,
|
|
3066
|
+
written.push({ path: relative2(cwd, p17.path), status: p17.status });
|
|
2803
3067
|
}
|
|
2804
3068
|
} else {
|
|
2805
|
-
for (const
|
|
2806
|
-
written.push({ path: relative2(cwd,
|
|
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
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
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((
|
|
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:
|
|
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 =
|
|
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
|
-
|
|
3587
|
+
p2.intro(brand("render"));
|
|
3297
3588
|
if (!existsSync13(cwd)) {
|
|
3298
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3618
|
+
p2.log.message(`${dim("Backup:")} ${result.backupPath}`);
|
|
3328
3619
|
}
|
|
3329
3620
|
for (const ws of result.workspaces) {
|
|
3330
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3640
|
+
p2.log.message(`${dim("engine")} ${color.cyan(ee.engine)}`);
|
|
3344
3641
|
for (const w of ee.written) {
|
|
3345
|
-
|
|
3642
|
+
p2.log.message(` ${renderStatusSymbol(w.status)} ${w.path} ${dim("(")}${renderStatusLabel(w.status)}${dim(")")}`);
|
|
3346
3643
|
}
|
|
3347
|
-
for (const s of ee.skipped)
|
|
3348
|
-
for (const warn of ee.warnings)
|
|
3349
|
-
if (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
|
-
|
|
3653
|
+
p2.outro(`${color.yellow("Preview")} ${summary} ${dim("\xB7 corre 'navori render --apply' para escribir")}`);
|
|
3357
3654
|
} else {
|
|
3358
|
-
|
|
3655
|
+
p2.outro(`${dim("Up to date")} ${summary} ${dim("\xB7 nada que aplicar")}`);
|
|
3359
3656
|
}
|
|
3360
3657
|
} else if (anyPending) {
|
|
3361
|
-
|
|
3658
|
+
p2.outro(`${color.green("Done")} ${summary}`);
|
|
3362
3659
|
} else {
|
|
3363
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3746
|
-
out.push({ ...
|
|
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
|
|
3753
|
-
out.push({ ...
|
|
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((
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
4209
|
+
p3.cancel(bootstrapT.dirNotFound(cwd));
|
|
3913
4210
|
process.exit(1);
|
|
3914
4211
|
}
|
|
3915
4212
|
if (existsSync16(configPath)) {
|
|
3916
|
-
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
4247
|
+
p3.cancel(err.message);
|
|
3951
4248
|
process.exit(1);
|
|
3952
4249
|
}
|
|
3953
4250
|
throw err;
|
|
3954
4251
|
}
|
|
3955
4252
|
if (!workspaceConfig) {
|
|
3956
|
-
|
|
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
|
-
|
|
4258
|
+
p3.note(
|
|
3962
4259
|
formatWorkspaceSummary(workspaceConfig, lang),
|
|
3963
4260
|
tr.workspaceDefaultsTitle(workspaceConfig.name)
|
|
3964
4261
|
);
|
|
3965
4262
|
}
|
|
3966
|
-
|
|
4263
|
+
p3.note(formatDetectionSummary(detected, lang), tr.detectedTitle);
|
|
3967
4264
|
if (detected.suggestedPresetGap) {
|
|
3968
|
-
|
|
4265
|
+
p3.log.warn(tr.presetGapNotice(detected.suggestedPresetGap));
|
|
3969
4266
|
}
|
|
3970
4267
|
if (detected.name && isPlaceholderName(detected.name)) {
|
|
3971
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4284
|
+
p3.log.info(tr.pluginsAlwaysOn(Object.keys(ALWAYS_ON_PLUGINS).join(", ")));
|
|
3988
4285
|
if (args.recommended && Object.keys(recommendedPlugins).length > 0) {
|
|
3989
|
-
|
|
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
|
-
|
|
4314
|
+
p3.log.success(tr.wroteConfig(configPath));
|
|
4018
4315
|
if (args.recommended && !detected.qualityGate && fallbackQg) {
|
|
4019
|
-
|
|
4316
|
+
p3.log.info(`Quality gate fallback aplicado: ${fallbackQg.fast}`);
|
|
4020
4317
|
}
|
|
4021
4318
|
if (!fallbackQg) {
|
|
4022
|
-
|
|
4319
|
+
p3.log.warn(tr.qualityGateNotDetected);
|
|
4023
4320
|
}
|
|
4024
4321
|
if (mode === "coexist") {
|
|
4025
|
-
|
|
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
|
-
|
|
4327
|
+
p3.outro(tr.done);
|
|
4031
4328
|
return;
|
|
4032
4329
|
}
|
|
4033
|
-
const accept = await
|
|
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 (
|
|
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
|
|
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 (
|
|
4363
|
+
if (p3.isCancel(toAdjust)) return cancel4(lang);
|
|
4067
4364
|
const adjustments = toAdjust;
|
|
4068
4365
|
if (adjustments.includes("name") || !name) {
|
|
4069
|
-
const value = await
|
|
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 (
|
|
4375
|
+
if (p3.isCancel(value)) return cancel4(lang);
|
|
4079
4376
|
name = value;
|
|
4080
4377
|
}
|
|
4081
4378
|
if (adjustments.includes("language")) {
|
|
4082
|
-
const value = await
|
|
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 (
|
|
4387
|
+
if (p3.isCancel(value)) return cancel4(lang);
|
|
4091
4388
|
language = value;
|
|
4092
4389
|
}
|
|
4093
4390
|
if (adjustments.includes("workspace")) {
|
|
4094
|
-
const value = await
|
|
4391
|
+
const value = await p3.text({
|
|
4095
4392
|
message: tr.workspaceOptional,
|
|
4096
4393
|
placeholder: tr.leaveEmpty
|
|
4097
4394
|
});
|
|
4098
|
-
if (
|
|
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
|
|
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 (
|
|
4406
|
+
if (p3.isCancel(value)) return cancel4(lang);
|
|
4110
4407
|
engines = value;
|
|
4111
4408
|
}
|
|
4112
4409
|
if (adjustments.includes("preset")) {
|
|
4113
|
-
const value = await
|
|
4410
|
+
const value = await p3.text({
|
|
4114
4411
|
message: tr.stackPresetFreeText,
|
|
4115
4412
|
placeholder: preset,
|
|
4116
4413
|
defaultValue: preset
|
|
4117
4414
|
});
|
|
4118
|
-
if (
|
|
4415
|
+
if (p3.isCancel(value)) return cancel4(lang);
|
|
4119
4416
|
preset = value;
|
|
4120
4417
|
}
|
|
4121
4418
|
if (adjustments.includes("branchBase")) {
|
|
4122
|
-
const value = await
|
|
4419
|
+
const value = await p3.text({
|
|
4123
4420
|
message: tr.baseBranch,
|
|
4124
4421
|
placeholder: defaultBranchBase,
|
|
4125
4422
|
defaultValue: defaultBranchBase
|
|
4126
4423
|
});
|
|
4127
|
-
if (
|
|
4424
|
+
if (p3.isCancel(value)) return cancel4(lang);
|
|
4128
4425
|
branchBase = value;
|
|
4129
4426
|
}
|
|
4130
4427
|
if (adjustments.includes("qualityGate")) {
|
|
4131
|
-
const fastVal = await
|
|
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 (
|
|
4137
|
-
const fullVal = await
|
|
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 (
|
|
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
|
-
|
|
4446
|
+
p3.cancel(tr.projectNameRequired);
|
|
4150
4447
|
process.exit(1);
|
|
4151
4448
|
}
|
|
4152
4449
|
let pluginsToEnable = await pickPlugins(lang);
|
|
4153
|
-
if (pluginsToEnable === null) return
|
|
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
|
|
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
|
-
|
|
4173
|
-
const action = await
|
|
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 (
|
|
4479
|
+
if (p3.isCancel(action) || action === "cancel") return cancel4(lang);
|
|
4183
4480
|
if (action === "ok") break;
|
|
4184
|
-
const choice = await
|
|
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 (
|
|
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
|
|
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 (!
|
|
4505
|
+
if (!p3.isCancel(v)) name = v;
|
|
4209
4506
|
break;
|
|
4210
4507
|
}
|
|
4211
4508
|
case "language": {
|
|
4212
|
-
const v = await
|
|
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 (!
|
|
4517
|
+
if (!p3.isCancel(v)) language = v;
|
|
4221
4518
|
break;
|
|
4222
4519
|
}
|
|
4223
4520
|
case "engines": {
|
|
4224
|
-
const v = await
|
|
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 (!
|
|
4527
|
+
if (!p3.isCancel(v)) engines = v;
|
|
4231
4528
|
break;
|
|
4232
4529
|
}
|
|
4233
4530
|
case "preset": {
|
|
4234
|
-
const v = await
|
|
4531
|
+
const v = await p3.text({
|
|
4235
4532
|
message: tr.stackPresetFreeText,
|
|
4236
4533
|
placeholder: preset,
|
|
4237
4534
|
defaultValue: preset
|
|
4238
4535
|
});
|
|
4239
|
-
if (!
|
|
4536
|
+
if (!p3.isCancel(v)) preset = v;
|
|
4240
4537
|
break;
|
|
4241
4538
|
}
|
|
4242
4539
|
case "branchBase": {
|
|
4243
|
-
const v = await
|
|
4540
|
+
const v = await p3.text({
|
|
4244
4541
|
message: tr.baseBranch,
|
|
4245
4542
|
placeholder: branchBase,
|
|
4246
4543
|
defaultValue: branchBase
|
|
4247
4544
|
});
|
|
4248
|
-
if (!
|
|
4545
|
+
if (!p3.isCancel(v)) branchBase = v;
|
|
4249
4546
|
break;
|
|
4250
4547
|
}
|
|
4251
4548
|
case "qualityGate": {
|
|
4252
|
-
const fastVal = await
|
|
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 (
|
|
4258
|
-
const fullVal = await
|
|
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 (
|
|
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
|
|
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
|
-
|
|
4630
|
+
p3.log.success(tr.wroteConfig(configPath));
|
|
4334
4631
|
if (mode === "coexist") {
|
|
4335
|
-
|
|
4632
|
+
p3.outro(tr.doneExistingUntouched);
|
|
4336
4633
|
return;
|
|
4337
4634
|
}
|
|
4338
4635
|
if (args.render === false) {
|
|
4339
|
-
|
|
4636
|
+
p3.outro(tr.doneSkippedRender);
|
|
4340
4637
|
return;
|
|
4341
4638
|
}
|
|
4342
|
-
const shouldRender = await
|
|
4639
|
+
const shouldRender = await p3.confirm({
|
|
4343
4640
|
message: tr.renderNow,
|
|
4344
4641
|
initialValue: true
|
|
4345
4642
|
});
|
|
4346
|
-
if (
|
|
4347
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4366
|
-
|
|
4662
|
+
p3.log.warn(tr.existingInfraYesMode);
|
|
4663
|
+
p3.note(formatInfraSummary(infra, args.lang), tr.filesFoundTitle);
|
|
4367
4664
|
return "coexist";
|
|
4368
4665
|
}
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
const choice = await
|
|
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 (
|
|
4676
|
+
if (p3.isCancel(choice)) return null;
|
|
4380
4677
|
if (choice === "replace") {
|
|
4381
|
-
const confirm11 = await
|
|
4678
|
+
const confirm11 = await p3.confirm({
|
|
4382
4679
|
message: tr.replaceConfirm,
|
|
4383
4680
|
initialValue: false
|
|
4384
4681
|
});
|
|
4385
|
-
if (
|
|
4682
|
+
if (p3.isCancel(confirm11) || !confirm11) return null;
|
|
4386
4683
|
const backup = createMigrationBackup(cwd, projectName ?? "unknown");
|
|
4387
|
-
|
|
4684
|
+
p3.log.success(tr.backedUp(backup.movedPaths.length, backup.path));
|
|
4388
4685
|
removeOriginals(cwd, backup.movedPaths);
|
|
4389
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
4742
|
+
p3.log.message(`${tr.recommendedAssignments}
|
|
4446
4743
|
${summary}`);
|
|
4447
|
-
const accept = await
|
|
4744
|
+
const accept = await p3.confirm({
|
|
4448
4745
|
message: tr.useAssignments,
|
|
4449
4746
|
initialValue: true
|
|
4450
4747
|
});
|
|
4451
|
-
if (
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
4809
|
+
p3.log.success(`Rendered ${result.filePath}${summary}`);
|
|
4513
4810
|
} else {
|
|
4514
|
-
|
|
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
|
-
|
|
4819
|
+
p3.log.message(`${dim("Files written:")}
|
|
4523
4820
|
${lines}${more}`);
|
|
4524
4821
|
}
|
|
4525
4822
|
for (const s of result.engineResult.skipped) {
|
|
4526
|
-
|
|
4823
|
+
p3.log.warn(`Skipped ${s.path}: ${s.reason}`);
|
|
4527
4824
|
}
|
|
4528
4825
|
for (const w of result.engineResult.warnings) {
|
|
4529
|
-
|
|
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
|
|
4834
|
+
const answer = await p3.confirm({
|
|
4538
4835
|
message: t(opts.lang).preCommitHookPrompt,
|
|
4539
4836
|
initialValue: false
|
|
4540
4837
|
});
|
|
4541
|
-
if (
|
|
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
|
-
|
|
4844
|
+
p3.log.success(t(opts.lang).preCommitHookWritten(result.path));
|
|
4548
4845
|
} else {
|
|
4549
|
-
|
|
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
|
|
4577
|
-
|
|
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
|
-
|
|
4629
|
-
const choice = await
|
|
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 (
|
|
4934
|
+
if (p3.isCancel(choice)) return null;
|
|
4638
4935
|
if (choice === "skip") {
|
|
4639
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
|
4976
|
+
const v = await p3.text({
|
|
4680
4977
|
message,
|
|
4681
4978
|
placeholder: prompt.placeholder ?? "",
|
|
4682
4979
|
defaultValue: ""
|
|
4683
4980
|
});
|
|
4684
|
-
if (
|
|
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
|
|
4696
|
-
if (
|
|
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
|
|
4701
|
-
if (
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5045
|
+
p3.log.message(`${dim("Workspaces detectados en el monorepo:")}
|
|
4749
5046
|
${overview}`);
|
|
4750
|
-
const addAll = await
|
|
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 (
|
|
4755
|
-
const useSuggested = await
|
|
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 (
|
|
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
|
|
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 (!
|
|
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
|
|
4793
|
-
import { existsSync as
|
|
4794
|
-
import { join as
|
|
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/
|
|
4797
|
-
import { existsSync as existsSync17,
|
|
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 (!
|
|
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 (
|
|
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 =
|
|
4849
|
-
if (!
|
|
5168
|
+
const absDir = join14(cwd, dir);
|
|
5169
|
+
if (!existsSync18(absDir)) continue;
|
|
4850
5170
|
try {
|
|
4851
|
-
for (const file of
|
|
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 =
|
|
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 =
|
|
4905
|
-
if (!
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
4968
|
-
if (!
|
|
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
|
-
|
|
5334
|
+
p4.cancel(`Directory not found: ${cwd}`);
|
|
4973
5335
|
}
|
|
4974
5336
|
process.exit(1);
|
|
4975
5337
|
}
|
|
4976
|
-
if (!
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
5024
|
-
agentsMdExists:
|
|
5025
|
-
claudeDirExists:
|
|
5026
|
-
progressDirExists:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) => !
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
5191
|
-
if (!
|
|
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 (!
|
|
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 =
|
|
5250
|
-
if (
|
|
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 =
|
|
5257
|
-
if (
|
|
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 =
|
|
5705
|
+
entries = readdirSync9(dir, { withFileTypes: true });
|
|
5264
5706
|
} catch {
|
|
5265
5707
|
return;
|
|
5266
5708
|
}
|
|
5267
5709
|
for (const entry of entries) {
|
|
5268
|
-
const abs =
|
|
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
|
|
5305
|
-
import { existsSync as
|
|
5306
|
-
import { resolve as resolve13, join as
|
|
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
|
-
|
|
5351
|
-
if (!
|
|
5352
|
-
|
|
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 (!
|
|
5356
|
-
|
|
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 =
|
|
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
|
-
|
|
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((
|
|
5815
|
+
const hasOtherChanges = plans.some((p17) => p17.plan.written.length > 0);
|
|
5374
5816
|
if (!hasOtherChanges && conflicts.length === 0) {
|
|
5375
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
5424
|
-
|
|
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
|
-
|
|
5872
|
+
p5.cancel("Aborted");
|
|
5431
5873
|
process.exit(0);
|
|
5432
5874
|
}
|
|
5433
5875
|
resolutions = resolved;
|
|
5434
5876
|
}
|
|
5435
5877
|
} else {
|
|
5436
|
-
const ok = await
|
|
5878
|
+
const ok = await p5.confirm({
|
|
5437
5879
|
message: "Aplicar cambios?",
|
|
5438
5880
|
initialValue: true
|
|
5439
5881
|
});
|
|
5440
|
-
if (
|
|
5441
|
-
|
|
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
|
-
|
|
5898
|
+
p5.log.message(`${dim(`Backup [${t2.label}]:`)} ${applied.backupPath}`);
|
|
5457
5899
|
}
|
|
5458
5900
|
}
|
|
5459
|
-
|
|
5460
|
-
|
|
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 =
|
|
5511
|
-
const existing =
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
6104
|
+
p6.intro(brand("add --suggest"));
|
|
5687
6105
|
} else if (!args.plugin) {
|
|
5688
|
-
|
|
5689
|
-
|
|
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
|
-
|
|
6110
|
+
p6.intro(brand(`add ${accent(args.plugin)}`));
|
|
5693
6111
|
}
|
|
5694
6112
|
if (!existsSync21(cwd)) {
|
|
5695
|
-
|
|
6113
|
+
p6.cancel(`Directory not found: ${cwd}`);
|
|
5696
6114
|
process.exit(1);
|
|
5697
6115
|
}
|
|
5698
6116
|
if (!existsSync21(configPath)) {
|
|
5699
|
-
|
|
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
|
-
|
|
6130
|
+
p6.cancel(`Unknown plugin '${pluginId}'. Known: ${listKnownPluginIds().join(", ") || "(none)"}`);
|
|
5713
6131
|
process.exit(1);
|
|
5714
6132
|
}
|
|
5715
6133
|
if (err instanceof PluginManifestError) {
|
|
5716
|
-
|
|
6134
|
+
p6.cancel(err.message);
|
|
5717
6135
|
process.exit(1);
|
|
5718
6136
|
}
|
|
5719
6137
|
throw err;
|
|
5720
6138
|
}
|
|
5721
|
-
|
|
5722
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6152
|
+
p6.log.success(`Added '${plugin.manifest.id}' to ${configPath}`);
|
|
5735
6153
|
}
|
|
5736
6154
|
const tool = plugin.manifest.externalTool;
|
|
5737
6155
|
if (!tool) {
|
|
5738
|
-
|
|
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
|
-
|
|
5744
|
-
|
|
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
|
-
|
|
5749
|
-
|
|
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
|
-
|
|
5756
|
-
|
|
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
|
|
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 (
|
|
5764
|
-
|
|
5765
|
-
|
|
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 =
|
|
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
|
-
|
|
6197
|
+
p6.outro(dim("Plugin registered but external tool install failed. Install manually."));
|
|
5780
6198
|
return;
|
|
5781
6199
|
}
|
|
5782
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5811
|
-
|
|
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
|
|
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
|
-
|
|
6301
|
+
p7.intro(brand(`workspace init ${accent(name)}`));
|
|
5884
6302
|
let description = args.description ?? "";
|
|
5885
6303
|
if (!args.yes && !description) {
|
|
5886
|
-
const value = await
|
|
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 (
|
|
5891
|
-
|
|
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
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
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
|
-
|
|
6341
|
+
p7.intro(brand("workspace ls"));
|
|
5924
6342
|
if (names.length === 0) {
|
|
5925
|
-
|
|
5926
|
-
|
|
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
|
-
|
|
5943
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6444
|
+
p7.log.warn(`Names placeholder (scaffold sin renombrar):
|
|
6027
6445
|
${lines.join("\n")}`);
|
|
6028
6446
|
}
|
|
6029
6447
|
}
|
|
6030
|
-
|
|
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
|
-
|
|
6064
|
-
|
|
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
|
-
|
|
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
|
|
6489
|
+
const ok = await p7.confirm({
|
|
6072
6490
|
message: `Rename workspace '${from}' to '${to}'?`,
|
|
6073
6491
|
initialValue: false
|
|
6074
6492
|
});
|
|
6075
|
-
if (
|
|
6076
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6107
|
-
|
|
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
|
|
6529
|
+
const ok = await p7.confirm({
|
|
6112
6530
|
message: `Delete workspace '${name}'?`,
|
|
6113
6531
|
initialValue: false
|
|
6114
6532
|
});
|
|
6115
|
-
if (
|
|
6116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
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
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
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
|
-
|
|
6645
|
+
p7.intro(brand(`workspace render ${accent(ws.name)}`));
|
|
6228
6646
|
if (ws.repos.length === 0) {
|
|
6229
|
-
|
|
6230
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6688
|
+
p7.outro(`${color.yellow("Done with errors")} ${dim(summary)}`);
|
|
6271
6689
|
process.exit(1);
|
|
6272
6690
|
}
|
|
6273
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
6893
|
+
p8.intro(brand(`ticket list ${accent(args.workspace)}`));
|
|
6476
6894
|
if (filtered.length === 0) {
|
|
6477
|
-
|
|
6478
|
-
|
|
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
|
-
|
|
6486
|
-
|
|
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
|
-
|
|
6523
|
-
|
|
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
|
-
|
|
6948
|
+
p8.note(readFileSync19(ticket.path, "utf-8"), "Content");
|
|
6531
6949
|
if (referencing.length === 0) {
|
|
6532
|
-
|
|
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
|
-
|
|
6956
|
+
p8.log.message(`Referenced in:
|
|
6539
6957
|
${refLines.join("\n")}`);
|
|
6540
6958
|
}
|
|
6541
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
6981
|
+
const value = await p8.text({
|
|
6564
6982
|
message: "Ticket title",
|
|
6565
6983
|
placeholder: id,
|
|
6566
6984
|
defaultValue: id
|
|
6567
6985
|
});
|
|
6568
|
-
if (
|
|
6569
|
-
|
|
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
|
-
|
|
6997
|
+
p8.cancel(err.message);
|
|
6580
6998
|
process.exit(1);
|
|
6581
6999
|
}
|
|
6582
7000
|
throw err;
|
|
6583
7001
|
}
|
|
6584
|
-
|
|
6585
|
-
|
|
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
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
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
|
-
|
|
7042
|
+
p8.intro(brand(`ticket delete ${accent(args.id)}`));
|
|
6625
7043
|
if (!args.yes) {
|
|
6626
|
-
const ok = await
|
|
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 (
|
|
6631
|
-
|
|
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
|
-
|
|
7055
|
+
p8.outro("Deleted");
|
|
6638
7056
|
} catch (err) {
|
|
6639
7057
|
if (err instanceof TicketError) {
|
|
6640
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 (
|
|
6727
|
-
|
|
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)
|
|
6738
|
-
if (removed.length > 0)
|
|
6739
|
-
if (added.length === 0 && removed.length === 0)
|
|
6740
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
6766
|
-
|
|
7183
|
+
if (p9.isCancel(fastVal)) {
|
|
7184
|
+
p9.cancel("Cancelled");
|
|
6767
7185
|
return;
|
|
6768
7186
|
}
|
|
6769
7187
|
fast = fastVal.trim();
|
|
6770
|
-
const fullVal = await
|
|
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 (
|
|
6776
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6788
|
-
|
|
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
|
-
|
|
7221
|
+
p9.intro(brand("configure language"));
|
|
6804
7222
|
let value = args.value;
|
|
6805
7223
|
if (!value) {
|
|
6806
|
-
const choice = await
|
|
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 (
|
|
6815
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6827
|
-
|
|
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
|
-
|
|
7260
|
+
p9.intro(brand("configure branch-base"));
|
|
6843
7261
|
let value = args.value?.trim();
|
|
6844
7262
|
if (!value) {
|
|
6845
|
-
const input = await
|
|
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 (
|
|
6851
|
-
|
|
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
|
-
|
|
7275
|
+
p9.cancel("Branch name cannot be empty");
|
|
6858
7276
|
return;
|
|
6859
7277
|
}
|
|
6860
7278
|
raw.branchBase = value;
|
|
6861
7279
|
persist(path, raw);
|
|
6862
|
-
|
|
6863
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
6888
|
-
|
|
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
|
-
|
|
7312
|
+
p9.cancel("Branch name cannot be empty");
|
|
6895
7313
|
return;
|
|
6896
7314
|
}
|
|
6897
7315
|
raw.prTarget = value;
|
|
6898
7316
|
persist(path, raw);
|
|
6899
|
-
|
|
7317
|
+
p9.log.success(`prTarget \u2192 ${value}`);
|
|
6900
7318
|
if (value === config.branchBase) {
|
|
6901
|
-
|
|
7319
|
+
p9.log.message(dim(`Same as branchBase \u2014 PRs target ${value} as before.`));
|
|
6902
7320
|
}
|
|
6903
|
-
|
|
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
|
-
|
|
6918
|
-
const selected = await
|
|
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 (
|
|
6925
|
-
|
|
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
|
-
|
|
6931
|
-
|
|
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
|
-
|
|
7366
|
+
p9.intro(brand("configure workspace"));
|
|
6949
7367
|
if (!value) {
|
|
6950
7368
|
const currentWorkspace = raw.workspace;
|
|
6951
7369
|
if (!currentWorkspace) {
|
|
6952
|
-
|
|
7370
|
+
p9.outro("No workspace associated. Nothing to remove.");
|
|
6953
7371
|
return;
|
|
6954
7372
|
}
|
|
6955
7373
|
if (!args.yes) {
|
|
6956
|
-
const ok = await
|
|
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 (
|
|
6961
|
-
|
|
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
|
-
|
|
6968
|
-
|
|
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
|
-
|
|
6974
|
-
|
|
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
|
|
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
|
-
|
|
7502
|
+
p10.intro(brand("update"));
|
|
7085
7503
|
if (!existsSync25(cwd)) {
|
|
7086
|
-
|
|
7504
|
+
p10.cancel(`Directory not found: ${cwd}`);
|
|
7087
7505
|
process.exit(1);
|
|
7088
7506
|
}
|
|
7089
7507
|
if (!existsSync25(configPath)) {
|
|
7090
|
-
|
|
7508
|
+
p10.cancel(`No navori.config.json at ${configPath}. Run 'navori init' first.`);
|
|
7091
7509
|
process.exit(1);
|
|
7092
7510
|
}
|
|
7093
|
-
const config =
|
|
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
|
-
|
|
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
|
-
|
|
7526
|
+
p10.log.info(`Config drift detected (${diffs.length}):
|
|
7109
7527
|
${lines.join("\n")}`);
|
|
7110
7528
|
} else {
|
|
7111
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7542
|
+
p10.log.info(`Managed block updates available (${updates.length}):
|
|
7125
7543
|
${lines.join("\n")}`);
|
|
7126
7544
|
}
|
|
7127
7545
|
if (previewConflicts.length > 0) {
|
|
7128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
7145
|
-
|
|
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
|
-
|
|
7572
|
+
p10.log.success(`Updated ${configPath}`);
|
|
7155
7573
|
}
|
|
7156
7574
|
if (args["config-only"]) {
|
|
7157
|
-
|
|
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
|
-
|
|
7163
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7590
|
+
p10.log.success(`Re-rendered ${written.length} archivo(s) (CLAUDE.md + .claude/)`);
|
|
7173
7591
|
} else {
|
|
7174
|
-
|
|
7592
|
+
p10.log.info("No re-render needed");
|
|
7175
7593
|
}
|
|
7176
|
-
|
|
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
|
|
7183
|
-
import { existsSync as existsSync26, readdirSync as
|
|
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
|
|
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
|
|
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
|
-
|
|
7653
|
+
p11.intro(brand("backup list"));
|
|
7236
7654
|
if (backups.length === 0) {
|
|
7237
|
-
|
|
7238
|
-
|
|
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
|
-
|
|
7255
|
-
|
|
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
|
-
|
|
7690
|
+
p11.intro(brand(`backup restore ${accent(ts)}`));
|
|
7273
7691
|
if (!existsSync26(backupDir)) {
|
|
7274
|
-
|
|
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
|
-
|
|
7697
|
+
p11.cancel(`Backup is empty: ${backupDir}`);
|
|
7280
7698
|
process.exit(1);
|
|
7281
7699
|
}
|
|
7282
|
-
|
|
7283
|
-
for (const f of files)
|
|
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
|
|
7703
|
+
const ok = await p11.confirm({
|
|
7286
7704
|
message: "Existing files will be overwritten. Proceed?",
|
|
7287
7705
|
initialValue: false
|
|
7288
7706
|
});
|
|
7289
|
-
if (
|
|
7290
|
-
|
|
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
|
-
|
|
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
|
|
7327
|
-
import { existsSync as existsSync27, readdirSync as
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
7805
|
+
p12.intro(brand("migrations list"));
|
|
7388
7806
|
if (migrations.length === 0) {
|
|
7389
|
-
|
|
7390
|
-
|
|
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
|
-
|
|
7408
|
-
|
|
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
|
-
|
|
7845
|
+
p12.intro(brand(`migrations restore ${accent(`${ts}/${repoName}`)}`));
|
|
7428
7846
|
if (!existsSync27(migrationDir)) {
|
|
7429
|
-
|
|
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
|
-
|
|
7852
|
+
p12.cancel(`Migration is empty: ${migrationDir}`);
|
|
7435
7853
|
process.exit(1);
|
|
7436
7854
|
}
|
|
7437
|
-
|
|
7438
|
-
for (const f of files.slice(0, 10))
|
|
7439
|
-
if (files.length > 10)
|
|
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
|
|
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 (
|
|
7446
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
7959
|
+
p13.intro(brand(`preset init ${accent(id)}`));
|
|
7542
7960
|
if (id === "custom") {
|
|
7543
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7585
|
-
|
|
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
|
-
|
|
7597
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
8104
|
+
p14.intro(brand("scan"));
|
|
7687
8105
|
const dryRun = runScan({ cwd, yes: false });
|
|
7688
8106
|
if (dryRun.kind === "no-config") {
|
|
7689
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8119
|
+
p14.log.info(
|
|
7702
8120
|
`No encontr\xE9 patrones de workspace en pnpm-workspace.yaml ni en package.json#workspaces.`
|
|
7703
8121
|
);
|
|
7704
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
7725
|
-
|
|
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
|
-
|
|
8150
|
+
p14.cancel(`Resultado inesperado: ${final.kind}`);
|
|
7733
8151
|
process.exit(1);
|
|
7734
8152
|
}
|
|
7735
|
-
|
|
8153
|
+
p14.log.success(
|
|
7736
8154
|
`Agregu\xE9 ${final.added.length} workspace(s) a ${final.configPath}`
|
|
7737
8155
|
);
|
|
7738
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
8189
|
+
const acceptAll = await p14.confirm({
|
|
7772
8190
|
message: `\xBFUsar preset sugerido en cada workspace nuevo?`,
|
|
7773
8191
|
initialValue: true
|
|
7774
8192
|
});
|
|
7775
|
-
if (
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
7813
|
-
|
|
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
|
-
|
|
7826
|
-
|
|
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
|
-
|
|
7859
|
-
|
|
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
|
-
|
|
7872
|
-
|
|
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
|
|
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
|
-
|
|
8311
|
+
p16.intro(brand("bench"));
|
|
7894
8312
|
if (!existsSync31(`${cwd}/navori.config.json`)) {
|
|
7895
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8337
|
+
p16.outro(color.green("Done"));
|
|
7920
8338
|
}
|
|
7921
8339
|
});
|
|
7922
8340
|
|