navori 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +122 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1653,6 +1653,51 @@ function extractManagedContent(existing, id, commentStyle = "html") {
1653
1653
  const match = findMarker(existing, id, syntax);
1654
1654
  return match ? match.content : null;
1655
1655
  }
1656
+ function locateManagedBlocks(content, syntax) {
1657
+ const openRegex = new RegExp(
1658
+ `${escapeRegex(syntax.openPrefix)}\\s+id="([^"]+)"${syntax.attrsAndTerminatorPattern}`,
1659
+ "g"
1660
+ );
1661
+ const blocks = [];
1662
+ for (const m of content.matchAll(openRegex)) {
1663
+ if (m.index === void 0) continue;
1664
+ const id = m[1];
1665
+ const close = closeMarker(id, syntax);
1666
+ const closeStart = content.indexOf(close, m.index + m[0].length);
1667
+ if (closeStart < 0) continue;
1668
+ blocks.push({ id, openStart: m.index, closeEnd: closeStart + close.length });
1669
+ }
1670
+ return blocks;
1671
+ }
1672
+ function reorderManagedBlocks(content, canonicalOrder, commentStyle = "html") {
1673
+ const syntax = syntaxFor(commentStyle);
1674
+ const blocks = locateManagedBlocks(content, syntax);
1675
+ if (blocks.length < 2) {
1676
+ return { output: content, reordered: false, blockedByInterleaving: false };
1677
+ }
1678
+ const rank = /* @__PURE__ */ new Map();
1679
+ canonicalOrder.forEach((id, i) => {
1680
+ if (!rank.has(id)) rank.set(id, i);
1681
+ });
1682
+ const unknownBase = canonicalOrder.length;
1683
+ const desired = blocks.map((b, i) => ({ b, i, key: rank.has(b.id) ? rank.get(b.id) : unknownBase + i })).sort((a, z8) => a.key - z8.key || a.i - z8.i).map((x) => x.b);
1684
+ if (desired.every((b, i) => b === blocks[i])) {
1685
+ return { output: content, reordered: false, blockedByInterleaving: false };
1686
+ }
1687
+ for (let i = 0; i < blocks.length - 1; i++) {
1688
+ const gap = content.slice(blocks[i].closeEnd, blocks[i + 1].openStart);
1689
+ if (gap.trim() !== "") {
1690
+ return { output: content, reordered: false, blockedByInterleaving: true };
1691
+ }
1692
+ }
1693
+ const first = blocks[0];
1694
+ const last = blocks[blocks.length - 1];
1695
+ const preamble = content.slice(0, first.openStart);
1696
+ const suffix = content.slice(last.closeEnd);
1697
+ const body = desired.map((b) => content.slice(b.openStart, b.closeEnd)).join("\n\n");
1698
+ const pre = preamble === "" ? "" : preamble.replace(/\n*$/, "\n\n");
1699
+ return { output: pre + body + suffix, reordered: true, blockedByInterleaving: false };
1700
+ }
1656
1701
  function resolveCondition(config, path) {
1657
1702
  const segments = path.split(".");
1658
1703
  let cursor = config;
@@ -1885,6 +1930,16 @@ function computeRenderPlan(existing, inputConfig, repoRoot, options = {}) {
1885
1930
  updatesAvailable
1886
1931
  };
1887
1932
  }
1933
+ var CLAUDE_COMPUTED_BLOCK_IDS = [
1934
+ "skills-index",
1935
+ "agentes-disponibles",
1936
+ "contexto-proyecto"
1937
+ ];
1938
+ function canonicalManagedOrder(config, repoRoot) {
1939
+ const plan = computeRenderPlan("", config, repoRoot);
1940
+ const ids = plan.entries.filter((entry) => entry.newContent !== null).map((entry) => entry.asset.id);
1941
+ return [...ids, ...CLAUDE_COMPUTED_BLOCK_IDS];
1942
+ }
1888
1943
 
1889
1944
  // src/engines/claude/settings-detection.ts
1890
1945
  var NAVORI_OWNERSHIP_MARKER = "$navori";
@@ -2491,6 +2546,13 @@ function renderClaudeEngine(cwd, inputConfig, options = {}) {
2491
2546
  } else {
2492
2547
  claudeMdContent = removeManagedSection(claudeMdContent, CONTEXTO_PROYECTO_ID);
2493
2548
  }
2549
+ const reorder = reorderManagedBlocks(claudeMdContent, canonicalManagedOrder(config, repoRoot));
2550
+ claudeMdContent = reorder.output;
2551
+ if (reorder.blockedByInterleaving) {
2552
+ warnings.push(
2553
+ "CLAUDE.md: los bloques managed est\xE1n fuera del orden can\xF3nico, pero hay texto tuyo intercalado entre bloques, as\xED que no los reorden\xE9. Mueve ese texto arriba del primer bloque managed o abajo del \xFAltimo para que navori pueda ordenarlos."
2554
+ );
2555
+ }
2494
2556
  if (claudeMdContent !== claudeMdExisting) {
2495
2557
  pending.push({
2496
2558
  path: claudeMdPath,
@@ -3446,6 +3508,7 @@ var ES = {
3446
3508
  detectionFailedYes: "No detect\xE9 el nombre del proyecto. Corre sin --yes/--recommended para darlo.",
3447
3509
  wroteConfig: (path) => `Escrib\xED ${path}`,
3448
3510
  recPluginsEnabled: (list) => `Plugins recomendados activados: ${list}`,
3511
+ pluginsAlwaysOn: (list) => `Incluidos siempre con navori: ${list} (no hace falta elegirlos)`,
3449
3512
  presetGapNotice: (stack) => `Detect\xE9 un proyecto '${stack}', pero todav\xEDa no hay un preset oficial para ese stack. Se instala el harness completo (agentes, gates, protocolo, SDD) y funciona desde ya; lo \xFAnico que falta son skills espec\xEDficas de '${stack}'. Quedas con el baseline (preset: custom). Para cubrir el hueco: crea tu preset local con 'navori preset init ${stack}', o agrega skills sueltas en project.localSkills.`,
3450
3513
  placeholderNameNotice: (name) => `El name '${name}' parece un placeholder de scaffold (heredado del package.json sin renombrar). Ren\xF3mbralo en package.json o edita "name" en navori.config.json si no es el nombre real del repo.`,
3451
3514
  workspaceDefaultsTitle: (name) => `Defaults del workspace \xB7 ${name}`,
@@ -3553,6 +3616,7 @@ var EN = {
3553
3616
  detectionFailedYes: "Could not detect project name. Run without --yes/--recommended to provide one.",
3554
3617
  wroteConfig: (path) => `Wrote ${path}`,
3555
3618
  recPluginsEnabled: (list) => `Recommended plugins enabled: ${list}`,
3619
+ pluginsAlwaysOn: (list) => `Always included with navori: ${list} (no need to pick them)`,
3556
3620
  presetGapNotice: (stack) => `Detected a '${stack}' project, but there's no official preset for that stack yet. The full harness installs (agents, gates, protocol, SDD) and works right away; the only thing missing are '${stack}'-specific skills. You stay on the baseline (preset: custom). To cover the gap: scaffold your local preset with 'navori preset init ${stack}', or add individual skills via project.localSkills.`,
3557
3621
  placeholderNameNotice: (name) => `The name '${name}' looks like a scaffold placeholder (carried over from an un-renamed package.json). Rename it in package.json or edit "name" in navori.config.json if it isn't the repo's real name.`,
3558
3622
  workspaceDefaultsTitle: (name) => `Workspace defaults \xB7 ${name}`,
@@ -3919,7 +3983,8 @@ var initCommand = defineCommand2({
3919
3983
  }
3920
3984
  const wsPlugins2 = wsDefaults?.plugins ?? {};
3921
3985
  const recommendedPlugins = args.recommended ? buildRecommendedPlugins(cwd) : {};
3922
- const mergedPlugins2 = { ...wsPlugins2, ...recommendedPlugins };
3986
+ const mergedPlugins2 = { ...ALWAYS_ON_PLUGINS, ...wsPlugins2, ...recommendedPlugins };
3987
+ p2.log.info(tr.pluginsAlwaysOn(Object.keys(ALWAYS_ON_PLUGINS).join(", ")));
3923
3988
  if (args.recommended && Object.keys(recommendedPlugins).length > 0) {
3924
3989
  p2.log.info(tr.recPluginsEnabled(Object.keys(recommendedPlugins).join(", ")));
3925
3990
  }
@@ -4236,7 +4301,7 @@ var initCommand = defineCommand2({
4236
4301
  }
4237
4302
  }
4238
4303
  const wsPlugins = wsDefaults?.plugins ?? {};
4239
- const mergedPlugins = { ...wsPlugins, ...pluginsConfig };
4304
+ const mergedPlugins = { ...ALWAYS_ON_PLUGINS, ...wsPlugins, ...pluginsConfig };
4240
4305
  const monorepoBlock = await buildMonorepoBlock(cwd, detected, {
4241
4306
  scanMonorepo: Boolean(args["scan-monorepo"]),
4242
4307
  autoYes: false,
@@ -4298,6 +4363,7 @@ async function chooseAdoptionMode(cwd, infra, projectName, args) {
4298
4363
  const tr = t(args.lang);
4299
4364
  if (args.yes) {
4300
4365
  p2.log.warn(tr.existingInfraYesMode);
4366
+ p2.note(formatInfraSummary(infra, args.lang), tr.filesFoundTitle);
4301
4367
  return "coexist";
4302
4368
  }
4303
4369
  p2.log.warn(tr.existingInfraDetected);
@@ -4326,7 +4392,9 @@ async function chooseAdoptionMode(cwd, infra, projectName, args) {
4326
4392
  return "coexist";
4327
4393
  }
4328
4394
  async function pickPlugins(lang) {
4329
- const ids = listKnownPluginIds();
4395
+ const alwaysOn = Object.keys(ALWAYS_ON_PLUGINS);
4396
+ p2.log.info(t(lang).pluginsAlwaysOn(alwaysOn.join(", ")));
4397
+ const ids = listKnownPluginIds().filter((id) => !(id in ALWAYS_ON_PLUGINS));
4330
4398
  if (ids.length === 0) return [];
4331
4399
  const options = ids.map((id) => {
4332
4400
  const plugin = (() => {
@@ -4405,10 +4473,11 @@ ${summary}`);
4405
4473
  }
4406
4474
  return overrides;
4407
4475
  }
4476
+ var ALWAYS_ON_PLUGINS = {
4477
+ engram: { enabled: true }
4478
+ };
4408
4479
  function buildRecommendedPlugins(cwd) {
4409
- const result = {
4410
- engram: { enabled: true }
4411
- };
4480
+ const result = {};
4412
4481
  if (isGitHubRepo(cwd)) {
4413
4482
  result.gh = { enabled: true };
4414
4483
  }
@@ -4831,21 +4900,45 @@ function scanManagedDrift(cwd, config) {
4831
4900
  }
4832
4901
  return out;
4833
4902
  }
4903
+ function scanManagedOrder(cwd, config) {
4904
+ const claudeMdPath = join13(cwd, "CLAUDE.md");
4905
+ if (!existsSync17(claudeMdPath)) return null;
4906
+ const content = readFileSync14(claudeMdPath, "utf-8");
4907
+ const current = listMarkers(claudeMdPath).map((m) => m.id);
4908
+ if (current.length < 2) return null;
4909
+ const canonical = canonicalManagedOrder(config, cwd);
4910
+ const result = reorderManagedBlocks(content, canonical);
4911
+ if (!result.reordered && !result.blockedByInterleaving) return null;
4912
+ const rank = /* @__PURE__ */ new Map();
4913
+ canonical.forEach((id, i) => {
4914
+ if (!rank.has(id)) rank.set(id, i);
4915
+ });
4916
+ const expected = current.map((id, i) => ({ id, i, key: rank.has(id) ? rank.get(id) : canonical.length + i })).sort((a, z8) => a.key - z8.key || a.i - z8.i).map((x) => x.id);
4917
+ return { current, expected, interleaved: result.blockedByInterleaving };
4918
+ }
4834
4919
  function suggestNextSteps(state) {
4835
4920
  const steps = [];
4836
4921
  if (!state.claudeMdExists) {
4837
- steps.push("Corr\xE9 'navori render --apply' para generar CLAUDE.md + .claude/.");
4922
+ steps.push("Corre 'navori render --apply' para generar CLAUDE.md + .claude/.");
4838
4923
  }
4839
4924
  if (state.missingPlugins.length > 0) {
4840
4925
  steps.push(
4841
- `Resolv\xE9 ${state.missingPlugins.length} plugin(s) faltante(s): instalalos o quitalos del config.`
4926
+ `Resuelve ${state.missingPlugins.length} plugin(s) faltante(s): inst\xE1lalos o qu\xEDtalos del config.`
4842
4927
  );
4843
4928
  }
4844
4929
  if (state.drifts.some((d) => d.kind === "content")) {
4845
- steps.push("Corr\xE9 'navori sync --interactive' para resolver bloques editados a mano.");
4930
+ steps.push("Corre 'navori sync --interactive' para resolver bloques editados a mano.");
4846
4931
  }
4847
4932
  if (state.drifts.some((d) => d.kind === "version")) {
4848
- steps.push("Corr\xE9 'navori render --apply' para traer los bloques a la \xFAltima versi\xF3n.");
4933
+ steps.push("Corre 'navori render --apply' para traer los bloques a la \xFAltima versi\xF3n.");
4934
+ }
4935
+ if (state.orderReport && !state.orderReport.interleaved) {
4936
+ steps.push("Corre 'navori render --apply' para reordenar los bloques de CLAUDE.md al orden can\xF3nico.");
4937
+ }
4938
+ if (state.orderReport?.interleaved) {
4939
+ steps.push(
4940
+ "Mueve el texto que tienes entre bloques managed de CLAUDE.md arriba del primer bloque o abajo del \xFAltimo; luego corre 'navori render --apply' para reordenarlos."
4941
+ );
4849
4942
  }
4850
4943
  if (steps.length === 0) {
4851
4944
  steps.push("Todo al d\xEDa \u2014 sin acciones pendientes.");
@@ -4910,6 +5003,7 @@ var doctorCommand = defineCommand3({
4910
5003
  const markers = listMarkers(claudeMdPath);
4911
5004
  const missingPlugins = collectMissingPlugins(config);
4912
5005
  const drifts = scanManagedDrift(cwd, config);
5006
+ const orderReport = scanManagedOrder(cwd, config);
4913
5007
  const corruptedSettings = scanCorruptedSettings(cwd);
4914
5008
  const missingInvariants = scanMissingInvariants(cwd, config);
4915
5009
  const resolvedPreset = config.preset !== "custom" ? resolvePreset(config.preset, cwd) : null;
@@ -4934,6 +5028,7 @@ var doctorCommand = defineCommand3({
4934
5028
  managedBlocks: markers,
4935
5029
  missingPlugins,
4936
5030
  drifts,
5031
+ orderReport,
4937
5032
  corruptedSettings,
4938
5033
  missingInvariants,
4939
5034
  missingPreset,
@@ -5057,10 +5152,26 @@ ${lines.join("\n")}`
5057
5152
  ${lines.join("\n")}`
5058
5153
  );
5059
5154
  }
5155
+ if (orderReport) {
5156
+ if (orderReport.interleaved) {
5157
+ p3.log.warn(
5158
+ `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
+ orden actual: ${orderReport.current.join(", ")}
5160
+ orden can\xF3nico: ${orderReport.expected.join(", ")}`
5161
+ );
5162
+ } else {
5163
+ p3.log.warn(
5164
+ `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
+ orden actual: ${orderReport.current.join(", ")}
5166
+ orden can\xF3nico: ${orderReport.expected.join(", ")}`
5167
+ );
5168
+ }
5169
+ }
5060
5170
  const nextSteps = suggestNextSteps({
5061
5171
  claudeMdExists: report.checks.claudeMdExists,
5062
5172
  missingPlugins,
5063
- drifts
5173
+ drifts,
5174
+ orderReport
5064
5175
  });
5065
5176
  p3.note(
5066
5177
  nextSteps.map((s) => ` ${color.cyan(sym.bullet)} ${s}`).join("\n"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "navori",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Multi-agent harness + SDD scaffolder for Claude Code and other AI engines",
5
5
  "type": "module",
6
6
  "bin": {