@nac3/forge-cli 1.19.0 → 1.19.2

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 (53) hide show
  1. package/dist/chat/panel.d.ts.map +1 -1
  2. package/dist/chat/panel.js +251 -3
  3. package/dist/chat/panel.js.map +1 -1
  4. package/dist/chat/server.d.ts.map +1 -1
  5. package/dist/chat/server.js +152 -22
  6. package/dist/chat/server.js.map +1 -1
  7. package/dist/chat/tools/session_verbs.d.ts +10 -0
  8. package/dist/chat/tools/session_verbs.d.ts.map +1 -1
  9. package/dist/chat/tools/session_verbs.js +22 -12
  10. package/dist/chat/tools/session_verbs.js.map +1 -1
  11. package/dist/chat/tools/urazuke.d.ts +4 -0
  12. package/dist/chat/tools/urazuke.d.ts.map +1 -1
  13. package/dist/chat/tools/urazuke.js +21 -0
  14. package/dist/chat/tools/urazuke.js.map +1 -1
  15. package/dist/chat/tools.d.ts.map +1 -1
  16. package/dist/chat/tools.js +5 -0
  17. package/dist/chat/tools.js.map +1 -1
  18. package/dist/chat/urazuke_section.d.ts +1 -0
  19. package/dist/chat/urazuke_section.d.ts.map +1 -1
  20. package/dist/chat/urazuke_section.js +1 -0
  21. package/dist/chat/urazuke_section.js.map +1 -1
  22. package/dist/commands/urazuke.d.ts.map +1 -1
  23. package/dist/commands/urazuke.js +29 -0
  24. package/dist/commands/urazuke.js.map +1 -1
  25. package/dist/mcp/bridge_registry.d.ts +1 -1
  26. package/dist/nac3/dispatch_log.d.ts.map +1 -1
  27. package/dist/nac3/dispatch_log.js +16 -11
  28. package/dist/nac3/dispatch_log.js.map +1 -1
  29. package/dist/nac3/internal_manifest.d.ts.map +1 -1
  30. package/dist/nac3/internal_manifest.js +24 -0
  31. package/dist/nac3/internal_manifest.js.map +1 -1
  32. package/dist/urazuke/announce.d.ts +21 -0
  33. package/dist/urazuke/announce.d.ts.map +1 -0
  34. package/dist/urazuke/announce.js +40 -0
  35. package/dist/urazuke/announce.js.map +1 -0
  36. package/dist/urazuke/current.d.ts +33 -1
  37. package/dist/urazuke/current.d.ts.map +1 -1
  38. package/dist/urazuke/current.js +50 -7
  39. package/dist/urazuke/current.js.map +1 -1
  40. package/dist/urazuke/gate.d.ts +5 -0
  41. package/dist/urazuke/gate.d.ts.map +1 -1
  42. package/dist/urazuke/gate.js +7 -5
  43. package/dist/urazuke/gate.js.map +1 -1
  44. package/dist/urazuke/section.d.ts.map +1 -1
  45. package/dist/urazuke/section.js +1 -0
  46. package/dist/urazuke/section.js.map +1 -1
  47. package/dist/urazuke/start.d.ts +64 -0
  48. package/dist/urazuke/start.d.ts.map +1 -0
  49. package/dist/urazuke/start.js +91 -0
  50. package/dist/urazuke/start.js.map +1 -0
  51. package/dist/version.d.ts +1 -1
  52. package/dist/version.js +1 -1
  53. package/package.json +1 -1
@@ -1777,6 +1777,89 @@ async function route(req, res, ctx) {
1777
1777
  }
1778
1778
  return;
1779
1779
  }
1780
+ /* One project document, for the links of the Urazuke card modal. Gated to
1781
+ * the project root: a path that escapes it is refused. */
1782
+ if (req.method === 'GET' && url.pathname === '/api/forge/project-doc') {
1783
+ const rel = (url.searchParams.get('path') || '').trim();
1784
+ if (!rel) {
1785
+ sendJson(res, 400, { ok: false, error: 'path required' });
1786
+ return;
1787
+ }
1788
+ try {
1789
+ const nodePath = await import('node:path');
1790
+ const fsp = await import('node:fs/promises');
1791
+ const abs = nodePath.resolve(ctx.projectRoot, rel);
1792
+ const inside = abs === ctx.projectRoot || abs.startsWith(ctx.projectRoot + nodePath.sep);
1793
+ if (!inside) {
1794
+ sendJson(res, 403, { ok: false, error: 'path outside the project' });
1795
+ return;
1796
+ }
1797
+ const st = await fsp.stat(abs);
1798
+ if (!st.isFile()) {
1799
+ sendJson(res, 404, { ok: false, error: 'not a file' });
1800
+ return;
1801
+ }
1802
+ if (st.size > 2 * 1024 * 1024) {
1803
+ sendJson(res, 413, { ok: false, error: 'file too big for the panel (2 MB)' });
1804
+ return;
1805
+ }
1806
+ const content = await fsp.readFile(abs, 'utf8');
1807
+ const ext = nodePath.extname(abs).replace('.', '').toLowerCase();
1808
+ sendJson(res, 200, { ok: true, path: nodePath.relative(ctx.projectRoot, abs).split(nodePath.sep).join('/'), content, language: ext || null });
1809
+ }
1810
+ catch (e) {
1811
+ sendJson(res, 404, { ok: false, error: e instanceof Error ? e.message : String(e) });
1812
+ }
1813
+ return;
1814
+ }
1815
+ /* One pending item in full, for the click on a Workflow task. */
1816
+ if (req.method === 'GET' && url.pathname === '/api/forge/pending-item') {
1817
+ const id = (url.searchParams.get('id') || '').trim();
1818
+ if (!id) {
1819
+ sendJson(res, 400, { ok: false, error: 'id required' });
1820
+ return;
1821
+ }
1822
+ try {
1823
+ const { findPending } = await import('../pending/store.js');
1824
+ const item = await findPending(ctx.projectSlug || '', id);
1825
+ if (!item) {
1826
+ sendJson(res, 404, { ok: false, error: 'pending ' + id + ' not found' });
1827
+ return;
1828
+ }
1829
+ sendJson(res, 200, { ok: true, item });
1830
+ }
1831
+ catch (e) {
1832
+ sendJson(res, 500, { ok: false, error: e instanceof Error ? e.message : String(e) });
1833
+ }
1834
+ return;
1835
+ }
1836
+ /* URAZUKE start (2026-10-19): the panel button. */
1837
+ if (req.method === 'POST' && url.pathname === '/api/forge/urazuke-start') {
1838
+ let body = {};
1839
+ try {
1840
+ body = JSON.parse(await readBody(req) || '{}');
1841
+ }
1842
+ catch {
1843
+ body = {};
1844
+ }
1845
+ try {
1846
+ const { startUrazuke } = await import('../urazuke/start.js');
1847
+ const r = await startUrazuke(ctx.projectSlug || '', ctx.projectRoot, { force: body.force === true });
1848
+ if (r.ok) {
1849
+ try {
1850
+ const { urazukeCurrent } = await import('../urazuke/current.js');
1851
+ const { announceStepIfChanged } = await import('../urazuke/announce.js');
1852
+ await announceStepIfChanged(ctx.projectSlug || '', await urazukeCurrent(ctx.projectSlug || '', ctx.projectRoot));
1853
+ }
1854
+ catch { /* best effort */ }
1855
+ }
1856
+ sendJson(res, r.ok ? 200 : 409, { ...r });
1857
+ }
1858
+ catch (e) {
1859
+ sendJson(res, 500, { ok: false, error: e instanceof Error ? e.message : String(e) });
1860
+ }
1861
+ return;
1862
+ }
1780
1863
  if (req.method === 'POST' && url.pathname === '/api/forge/urazuke-prompts') {
1781
1864
  let body;
1782
1865
  try {
@@ -4687,39 +4770,65 @@ async function handleStartupTabs(_req, res, ctx, url) {
4687
4770
  }
4688
4771
  }
4689
4772
  catch { /* sin pendings */ }
4773
+ /* Una sola ventana canonica (Pablo, 2026-10-19): cuando Urazuke esta
4774
+ * encendido y el proyecto corre la escalera, la pestana Urazuke reemplaza
4775
+ * a la pestana Workflow (dibuja lo mismo y mas); cuando esta apagado, la
4776
+ * pestana Urazuke se oculta. `hidden` le dice al panel que pestanas
4777
+ * abiertas tiene que cerrar sin recordarlas como cerradas por el usuario. */
4778
+ const hidden = [];
4779
+ let ladderActive = false;
4780
+ try {
4781
+ const { urazukeEnabled } = await import('../urazuke/enabled.js');
4782
+ const { findUrazukeInstance } = await import('../urazuke/current.js');
4783
+ ladderActive = !!(ctx.projectSlug && await urazukeEnabled() && await findUrazukeInstance(ctx.projectSlug));
4784
+ }
4785
+ catch {
4786
+ ladderActive = false;
4787
+ }
4788
+ let urazukeOn = false;
4789
+ try {
4790
+ urazukeOn = await (await import('../urazuke/enabled.js')).urazukeEnabled();
4791
+ }
4792
+ catch {
4793
+ urazukeOn = false;
4794
+ }
4795
+ if (ladderActive)
4796
+ hidden.push('startup_workflow');
4797
+ else if (!urazukeOn)
4798
+ hidden.push('startup_urazuke');
4690
4799
  /* 4: workflow como GRAFO de nodos (las 6 fases del flujo yujin
4691
4800
  * con sus pasos), coloreadas por estado (done/pendiente) cuando
4692
4801
  * hay workflow state. Cumple el pedido de "workflow como grafo
4693
4802
  * de nodos y conectores con detalle al click". */
4694
- try {
4695
- /* PND-094 -- vista jerarquica de FASES + tareas (no grafo): cada
4696
- * fase con su avance + sus tareas debajo con su estado. */
4697
- /* PND-094 ext -- multi-workflow: the coexisting WorkflowInstances
4698
- * with their tasks (pendings) grouped by stage. Falls back to the
4699
- * legacy single-workflow view when there are no instances yet. */
4700
- const { computeWorkflowsView } = await import('../workflow/multi_view.js');
4701
- const cards = await computeWorkflowsView(ctx.projectSlug ?? '', 'open');
4702
- if (cards.length > 0) {
4703
- tabs.push({ key: 'startup_workflow', title: t('startup.tab.workflow'), kind: 'workflow', content: JSON.stringify({ workflows: cards }) });
4803
+ if (!ladderActive)
4804
+ try {
4805
+ /* PND-094 -- vista jerarquica de FASES + tareas (no grafo): cada
4806
+ * fase con su avance + sus tareas debajo con su estado. */
4807
+ /* PND-094 ext -- multi-workflow: the coexisting WorkflowInstances
4808
+ * with their tasks (pendings) grouped by stage. Falls back to the
4809
+ * legacy single-workflow view when there are no instances yet. */
4810
+ const { computeWorkflowsView } = await import('../workflow/multi_view.js');
4811
+ const cards = await computeWorkflowsView(ctx.projectSlug ?? '', 'open');
4812
+ if (cards.length > 0) {
4813
+ tabs.push({ key: 'startup_workflow', title: t('startup.tab.workflow'), kind: 'workflow', content: JSON.stringify({ workflows: cards }) });
4814
+ }
4815
+ else {
4816
+ const { readWorkflow } = await import('../workflow/state.js');
4817
+ const { computeWorkflowView } = await import('../workflow/view.js');
4818
+ const view = computeWorkflowView(await readWorkflow(ctx.projectRoot));
4819
+ tabs.push({ key: 'startup_workflow', title: t('startup.tab.workflow'), kind: 'workflow', content: JSON.stringify(view) });
4820
+ }
4704
4821
  }
4705
- else {
4706
- const { readWorkflow } = await import('../workflow/state.js');
4707
- const { computeWorkflowView } = await import('../workflow/view.js');
4708
- const view = computeWorkflowView(await readWorkflow(ctx.projectRoot));
4709
- tabs.push({ key: 'startup_workflow', title: t('startup.tab.workflow'), kind: 'workflow', content: JSON.stringify(view) });
4822
+ catch {
4823
+ tabs.push({ key: 'startup_workflow', title: t('startup.tab.workflow'), kind: 'md', content: '# Workflow\n\nSin datos.\n' });
4710
4824
  }
4711
- }
4712
- catch {
4713
- tabs.push({ key: 'startup_workflow', title: t('startup.tab.workflow'), kind: 'md', content: '# Workflow\n\nSin datos.\n' });
4714
- }
4715
4825
  /* 4b: URAZUKE (2026-10-19) -- la escalera con el estado mecanico de cada
4716
4826
  * peldano y el peldano en curso con su prompt, sus insumos y sus chequeos.
4717
4827
  * Solo cuando el proyecto corre un workflow urazuke / urazuke-legacy. */
4718
4828
  try {
4719
4829
  const { findUrazukeInstance, urazukeLadder, urazukeCurrent } = await import('../urazuke/current.js');
4720
4830
  const slugU = ctx.projectSlug ?? '';
4721
- const { urazukeEnabled } = await import('../urazuke/enabled.js');
4722
- if (slugU && await urazukeEnabled() && await findUrazukeInstance(slugU)) {
4831
+ if (slugU && ladderActive && await findUrazukeInstance(slugU)) {
4723
4832
  const ladder = await urazukeLadder(slugU, ctx.projectRoot);
4724
4833
  const cur = await urazukeCurrent(slugU, ctx.projectRoot);
4725
4834
  const current = cur ? {
@@ -4733,6 +4842,12 @@ async function handleStartupTabs(_req, res, ctx, url) {
4733
4842
  } : null;
4734
4843
  tabs.push({ key: 'startup_urazuke', title: t('startup.tab.urazuke'), kind: 'urazuke', content: JSON.stringify({ ladder, current }) });
4735
4844
  }
4845
+ else if (slugU && urazukeOn) {
4846
+ /* No ladder yet: the tab offers the door in (yujin.urazuke.start). */
4847
+ const { urazukeStartHint } = await import('../urazuke/start.js');
4848
+ const start = await urazukeStartHint(slugU, ctx.projectRoot);
4849
+ tabs.push({ key: 'startup_urazuke', title: t('startup.tab.urazuke'), kind: 'urazuke', content: JSON.stringify({ ladder: null, current: null, start }) });
4850
+ }
4736
4851
  }
4737
4852
  catch { /* sin escalera: la pestana no aparece */ }
4738
4853
  /* 5: PND-007 -- Tareas de fondo (se llena en vivo client-side). */
@@ -4751,7 +4866,7 @@ async function handleStartupTabs(_req, res, ctx, url) {
4751
4866
  kind: 'files',
4752
4867
  content: '{}',
4753
4868
  });
4754
- sendJson(res, 200, { ok: true, enabled: true, tabs });
4869
+ sendJson(res, 200, { ok: true, enabled: true, tabs, hidden });
4755
4870
  }
4756
4871
  /** Formato de id de pending para display (PND-NNN). */
4757
4872
  function formatPendId(id) {
@@ -6196,6 +6311,13 @@ async function handleChat(req, res, ctx) {
6196
6311
  const r = await buildUrazukeSection({ project_slug: ctx.projectSlug || '', project_root: ctx.projectRoot, char_budget: 6000 });
6197
6312
  if (r.rendered_text)
6198
6313
  urazukeSection = r.rendered_text;
6314
+ /* The owner sees in the chat which step runs and what is expected of
6315
+ * them (never the prompt): one system line per step change. */
6316
+ try {
6317
+ const { announceStepIfChanged } = await import('../urazuke/announce.js');
6318
+ await announceStepIfChanged(ctx.projectSlug || '', r.current);
6319
+ }
6320
+ catch { /* best effort */ }
6199
6321
  }
6200
6322
  catch (_) { /* swallow */ }
6201
6323
  /* alpha.59z.15 HITO 6.3 -- prefetch global learnings from
@@ -6229,6 +6351,14 @@ async function handleChat(req, res, ctx) {
6229
6351
  try {
6230
6352
  const { readLimitsConfig } = await import('../core/limits_config.js');
6231
6353
  multiagentEnabled = (await readLimitsConfig()).multiagent.enabled === true;
6354
+ /* 2026-10-19: on the Claude Plan without another provider's key the
6355
+ * children cannot run; hide the verbs instead of letting the brain
6356
+ * discover it by four dead sub-sessions. */
6357
+ if (multiagentEnabled) {
6358
+ const { hasUsableChildBrain } = await import('./tools/session_verbs.js');
6359
+ if (!(await hasUsableChildBrain()))
6360
+ multiagentEnabled = false;
6361
+ }
6232
6362
  }
6233
6363
  catch (_) { /* config failure -> stay OFF (safe default) */ }
6234
6364
  /* alpha.59z.37 NAC-3 v3.x backend tools -- prefetch the