@rafiqui987/markode 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +10 -1
  2. package/dist/cli.js +194 -88
  3. package/dist/web/assets/{abnfDiagram-WEYOIA3C-C-xz1zhH.js → abnfDiagram-WEYOIA3C-B-3Lcktj.js} +1 -1
  4. package/dist/web/assets/{arc-0NKn9s7n.js → arc-B1vQQwbr.js} +1 -1
  5. package/dist/web/assets/{architecture-WOLXFQ4H-yvNPw04L.js → architecture-WOLXFQ4H-D8_qhlNG.js} +1 -1
  6. package/dist/web/assets/{architectureDiagram-ZHVYKT53-DiNkPsUS.js → architectureDiagram-ZHVYKT53-B4uGFnQg.js} +1 -1
  7. package/dist/web/assets/{blockDiagram-RJAE7QA3-Bm5an-ZR.js → blockDiagram-RJAE7QA3-B1PRI2Fk.js} +1 -1
  8. package/dist/web/assets/{c4Diagram-XDPPPE54-DCIkW1i3.js → c4Diagram-XDPPPE54-BOSiNgn_.js} +1 -1
  9. package/dist/web/assets/channel-ggr2Mduz.js +1 -0
  10. package/dist/web/assets/{chunk-2E7WHVMD-w-cvaztJ.js → chunk-2E7WHVMD-B2TT-9T2.js} +1 -1
  11. package/dist/web/assets/{chunk-4EMNKR2T-Dz1c4ddf.js → chunk-4EMNKR2T-BJStHb31.js} +1 -1
  12. package/dist/web/assets/{chunk-55XCU76K-BkW2QTCZ.js → chunk-55XCU76K-CGSdzfU6.js} +1 -1
  13. package/dist/web/assets/{chunk-7S6XSTFO-SqLAsU6N.js → chunk-7S6XSTFO-D7U3aXUK.js} +1 -1
  14. package/dist/web/assets/{chunk-AQDTSKZB-DwIPdMmQ.js → chunk-AQDTSKZB-C6fkREWV.js} +1 -1
  15. package/dist/web/assets/{chunk-DSNLSHJZ-BNEo5raG.js → chunk-DSNLSHJZ-C0Wri32R.js} +1 -1
  16. package/dist/web/assets/{chunk-HAGFOTED-BokAjJjC.js → chunk-HAGFOTED-dS3yjqSY.js} +1 -1
  17. package/dist/web/assets/{chunk-HNE4Q36P-BlJCzrNG.js → chunk-HNE4Q36P-B0HJeFYe.js} +1 -1
  18. package/dist/web/assets/{chunk-NQOKRVIF-CK1D9VKN.js → chunk-NQOKRVIF-DCR-ZD6p.js} +1 -1
  19. package/dist/web/assets/{chunk-UHW7PFBP-ycMkWOji.js → chunk-UHW7PFBP-DvXVrsgg.js} +1 -1
  20. package/dist/web/assets/{chunk-VQ5W62QI-CNI8j3BI.js → chunk-VQ5W62QI-mU_IBCXT.js} +1 -1
  21. package/dist/web/assets/{chunk-WQQL4CDK-DvQQgYrd.js → chunk-WQQL4CDK-DnyUQt3W.js} +1 -1
  22. package/dist/web/assets/{chunk-XXDRQBXY-cFqAbOh1.js → chunk-XXDRQBXY-DNhISORq.js} +1 -1
  23. package/dist/web/assets/{chunk-YZO67HFG-CkfGL1zX.js → chunk-YZO67HFG-C_oEIdWe.js} +1 -1
  24. package/dist/web/assets/{chunk-ZAUX2LTX-BwdZydyj.js → chunk-ZAUX2LTX-D5aMvPiJ.js} +1 -1
  25. package/dist/web/assets/{chunk-ZCHMQSOR-CXDXBIC6.js → chunk-ZCHMQSOR-B5J6sRpu.js} +1 -1
  26. package/dist/web/assets/{classDiagram-v2-4MYWR3UA-B95ypqg8.js → classDiagram-v2-4MYWR3UA-D4-o3iga.js} +1 -1
  27. package/dist/web/assets/{cose-bilkent-JH36ORCC-C8-ToTGn.js → cose-bilkent-JH36ORCC-CqNUB7G6.js} +1 -1
  28. package/dist/web/assets/{cynefin-EF2NZ3EQ-CHth0Nt9.js → cynefin-EF2NZ3EQ-BLBtXLl2.js} +1 -1
  29. package/dist/web/assets/{cynefinDiagram-WF24UDRP-CMcwwxC8.js → cynefinDiagram-WF24UDRP-BlytpdKL.js} +1 -1
  30. package/dist/web/assets/{dagre-NK4GIZVX-BZzAgRpg.js → dagre-NK4GIZVX-DIUkOqo_.js} +1 -1
  31. package/dist/web/assets/{diagram-CBK3MWTC-DGwxJbVy.js → diagram-CBK3MWTC-4WJqLswq.js} +1 -1
  32. package/dist/web/assets/{diagram-I5XIIV2D-B17CYdAP.js → diagram-I5XIIV2D-s1EUO6xt.js} +1 -1
  33. package/dist/web/assets/{diagram-ODIL2UCW-CYPFYS2M.js → diagram-ODIL2UCW-GvgDR4t8.js} +1 -1
  34. package/dist/web/assets/{diagram-PFBEJUXE-BQMumROl.js → diagram-PFBEJUXE-B6J9SedW.js} +1 -1
  35. package/dist/web/assets/{diagram-RJA3I4VV-vHTuDO1-.js → diagram-RJA3I4VV-CqA7qcNf.js} +1 -1
  36. package/dist/web/assets/{diagram-TYUNFSNR-C_WYz7qL.js → diagram-TYUNFSNR-Cln9POlx.js} +1 -1
  37. package/dist/web/assets/{dist-DFcDEdTm.js → dist--fImHE6g.js} +1 -1
  38. package/dist/web/assets/{dist-BOCA4kQq.js → dist-Bg-QtHy9.js} +1 -1
  39. package/dist/web/assets/{dist-cSM0Lli0.js → dist-BghTt_37.js} +1 -1
  40. package/dist/web/assets/{dist-PwT2nxki.js → dist-CU6eXj3N.js} +1 -1
  41. package/dist/web/assets/{dist-B1oQ1VTT.js → dist-CVF0b6Yz.js} +1 -1
  42. package/dist/web/assets/{dist-B5uXb_Wi.js → dist-Cd1DR3ly.js} +1 -1
  43. package/dist/web/assets/{dist-8k6FSdRu.js → dist-CoioyMmF.js} +1 -1
  44. package/dist/web/assets/{dist-DV0HO3Fe.js → dist-CykDxBaO.js} +1 -1
  45. package/dist/web/assets/{dist-QI7QavRs.js → dist-DpqggjJG.js} +1 -1
  46. package/dist/web/assets/{dist-DZQ_bwip.js → dist-YudOLIZ5.js} +1 -1
  47. package/dist/web/assets/{dist-BT3nK1GA.js → dist-xGXJDvke.js} +1 -1
  48. package/dist/web/assets/{ebnfDiagram-V4P3TTPT-DLMwyX-U.js → ebnfDiagram-V4P3TTPT-BusXM5tX.js} +1 -1
  49. package/dist/web/assets/{elk-QONTBCTQ-DVvxbtF4.js → elk-QONTBCTQ-B9oq7Tkq.js} +1 -1
  50. package/dist/web/assets/{erDiagram-KYQIGQAA-DPXicJ1t.js → erDiagram-KYQIGQAA-CmL9SAnh.js} +1 -1
  51. package/dist/web/assets/{eventmodeling-K75KTNOO-DpHVZP_6.js → eventmodeling-K75KTNOO-R6NmozzN.js} +1 -1
  52. package/dist/web/assets/flowDiagram-IUGLLWBU-CkUCRx3C.js +1 -0
  53. package/dist/web/assets/{ganttDiagram-R7GPNTJY-Ch6NDX5Q.js → ganttDiagram-R7GPNTJY-DRa2V_Qe.js} +1 -1
  54. package/dist/web/assets/{gitGraph-VSP46ZUC-CIhLB663.js → gitGraph-VSP46ZUC-CdFMO2LD.js} +1 -1
  55. package/dist/web/assets/{gitGraphDiagram-PDYL7XHY-Ci_MIbqs.js → gitGraphDiagram-PDYL7XHY-C-xmUbl1.js} +1 -1
  56. package/dist/web/assets/{index-CxwfaWVo.js → index-BFpTDsCp.js} +4 -4
  57. package/dist/web/assets/{info-OHQRW6UA-Dau8xD88.js → info-OHQRW6UA-CnPUMAqS.js} +1 -1
  58. package/dist/web/assets/{infoDiagram-MVDRKAX5-4spgggd7.js → infoDiagram-MVDRKAX5-DxouwrN7.js} +1 -1
  59. package/dist/web/assets/{ishikawaDiagram-HPEHV2GE-DgFczAdB.js → ishikawaDiagram-HPEHV2GE-qrLpOq3_.js} +1 -1
  60. package/dist/web/assets/{journeyDiagram-BOSHW7O7-CJKwCuyG.js → journeyDiagram-BOSHW7O7-BDTfHWHd.js} +1 -1
  61. package/dist/web/assets/{kanban-definition-FNSA4HC5-B_HPa_Q8.js → kanban-definition-FNSA4HC5-CTBgSpp6.js} +1 -1
  62. package/dist/web/assets/{line-Bil8yI0B.js → line-D8R5RRtF.js} +1 -1
  63. package/dist/web/assets/{linear-DJSxHb3I.js → linear-BCL193tG.js} +1 -1
  64. package/dist/web/assets/{mermaid-parser.core-ByEdgqAb.js → mermaid-parser.core-C1XIxySl.js} +3 -3
  65. package/dist/web/assets/{mermaid.core-CXUI3iYT.js → mermaid.core-C-385ykR.js} +4 -4
  66. package/dist/web/assets/{mindmap-definition-6OY57UG3-w7xG6UiS.js → mindmap-definition-6OY57UG3-BnCvQFLY.js} +1 -1
  67. package/dist/web/assets/{packet-JDAUHWVQ-YvTb_dXP.js → packet-JDAUHWVQ-DVCRx-wc.js} +1 -1
  68. package/dist/web/assets/{pegDiagram-ECIUJNBX-D5FI1bVJ.js → pegDiagram-ECIUJNBX-CIDVz_ln.js} +1 -1
  69. package/dist/web/assets/{pie-XZMESJXO-BH2bih-K.js → pie-XZMESJXO-BHXvmR-y.js} +1 -1
  70. package/dist/web/assets/{pieDiagram-GNMPY74T-DUR82NlG.js → pieDiagram-GNMPY74T-Bb_l0cKd.js} +1 -1
  71. package/dist/web/assets/{quadrantDiagram-D57K4MC3-DauAEIyL.js → quadrantDiagram-D57K4MC3-Dqq4AGQt.js} +1 -1
  72. package/dist/web/assets/{radar-ABXABTNO-CdUXfEdf.js → radar-ABXABTNO-Ba5vnnQ7.js} +1 -1
  73. package/dist/web/assets/{railroad-I3PHUGI6-Djt90nhE.js → railroad-I3PHUGI6-CxoMtJ5h.js} +1 -1
  74. package/dist/web/assets/{railroad-abnf-LNEFI6M7-XqY0pGCO.js → railroad-abnf-LNEFI6M7-BXxErwiU.js} +1 -1
  75. package/dist/web/assets/{railroad-ebnf-DWYD2UWJ-BNSOgId4.js → railroad-ebnf-DWYD2UWJ-C_9H5arK.js} +1 -1
  76. package/dist/web/assets/{railroad-peg-JR7BVNR7-DW2TRqOr.js → railroad-peg-JR7BVNR7-50KHJESB.js} +1 -1
  77. package/dist/web/assets/{railroadDiagram-Y72NRBL7-CTDNRHBf.js → railroadDiagram-Y72NRBL7-9w1amWMc.js} +1 -1
  78. package/dist/web/assets/{requirementDiagram-EQYSW5RA-DJ5H8uqE.js → requirementDiagram-EQYSW5RA-D3XygoSz.js} +1 -1
  79. package/dist/web/assets/{sankeyDiagram-SY24JHO5-Xa_3Bd4U.js → sankeyDiagram-SY24JHO5-C68crPY9.js} +1 -1
  80. package/dist/web/assets/{sequenceDiagram-BLEHNHQU-2jNpfI3_.js → sequenceDiagram-BLEHNHQU-BNcJ1wfc.js} +1 -1
  81. package/dist/web/assets/{src-DgNmQVK4.js → src-okGi814h.js} +1 -1
  82. package/dist/web/assets/{stateDiagram-v2-A6IESKWZ-DtxzihUG.js → stateDiagram-v2-A6IESKWZ-DX7rAO1j.js} +1 -1
  83. package/dist/web/assets/{swimlanes-B3ENRKFQ-DJBCOj2u.js → swimlanes-B3ENRKFQ-DGSmz_ab.js} +1 -1
  84. package/dist/web/assets/swimlanesDiagram-AYQRV4AK-D8bRrq1P.js +8 -0
  85. package/dist/web/assets/{timeline-definition-JXYKGLGB-C-ixUm6b.js → timeline-definition-JXYKGLGB-5COU2ouX.js} +1 -1
  86. package/dist/web/assets/{treeView-D4JQ5SDB-yWRFH4mR.js → treeView-D4JQ5SDB-BgrtWP9w.js} +1 -1
  87. package/dist/web/assets/{treemap-SAJKECNS-q3FZ9UjJ.js → treemap-SAJKECNS-CLZcktWl.js} +1 -1
  88. package/dist/web/assets/{usecaseDiagram-WPOLBNNX-Ch9QG7L1.js → usecaseDiagram-WPOLBNNX-BbJ7QjYR.js} +1 -1
  89. package/dist/web/assets/{vennDiagram-Q3Y3IDCW-85m0XMhF.js → vennDiagram-Q3Y3IDCW-IhZTghFG.js} +1 -1
  90. package/dist/web/assets/{wardley-7MLQ67FV-S2NRiIdM.js → wardley-7MLQ67FV-XjDz9vxn.js} +1 -1
  91. package/dist/web/assets/{wardleyDiagram-IVAIDKWA-q8ZLrvVG.js → wardleyDiagram-IVAIDKWA-DTcLR0T-.js} +1 -1
  92. package/dist/web/assets/{xychartDiagram-4URNHDU4-eSUy1iT7.js → xychartDiagram-4URNHDU4-Dja-ugvw.js} +1 -1
  93. package/dist/web/index.html +1 -1
  94. package/package.json +1 -1
  95. package/templates/proposals/SKILL.md +2 -1
  96. package/templates/skill/SKILL.md +18 -1
  97. package/dist/web/assets/channel-BkrEjK89.js +0 -1
  98. package/dist/web/assets/flowDiagram-IUGLLWBU-DrcHkm4d.js +0 -1
  99. package/dist/web/assets/swimlanesDiagram-AYQRV4AK-_cBuwUIk.js +0 -8
package/README.md CHANGED
@@ -369,6 +369,8 @@ missing description, a name that doesn't match its folder…).
369
369
  markode # set up the project if needed, then open the editor
370
370
  markode serve # open the editor without the setup
371
371
  markode serve --port 5000 --no-open
372
+ markode url [<doc.md>] # the editor's link (or a doc's); starts it in the background if needed
373
+ markode stop # stop the editor started in the background
372
374
  markode init # run the setup again: only adds modules (your defaults, if saved)
373
375
  markode init --profile full # no questions: viewer, agent or full
374
376
  markode init --yes # no questions: your defaults, or viewer + AI agent
@@ -390,10 +392,11 @@ Installing it also makes the Claude Code hooks faster than running through `npx`
390
392
 
391
393
  The AI agent module installs the `markode` skill where the project's agents read skills
392
394
  (`.claude/skills/` for Claude Code, `.agents/skills/` for Codex, Copilot, Gemini and others). It tells
393
- the agent how to write Markdown that markode presents well, and gives it two commands:
395
+ the agent how to write Markdown that markode presents well, and gives it three commands:
394
396
 
395
397
  | Command | What the agent does | When |
396
398
  |---|---|---|
399
+ | `/markode` | Starts the editor in the background if it isn't running and gives you its link (`markode url`) | When you want to open markode |
397
400
  | `/markode sync` | Keeps the docs consistent: applies answered proposals, ticks phases from their tasks, checks the projects table and broken links (`markode check`) | Start and end of a session, after editing docs |
398
401
  | `/markode translate` | Translates what is behind; asks before changing any source file | Only when you ask |
399
402
 
@@ -404,6 +407,11 @@ enough. In the main doc it writes only between its markers:
404
407
  - `markode:workflow`: how the project plans its work (the markode method, OpenSpec or Spec Kit);
405
408
  - `markode:proposals`: how the agent writes proposals and answers comments.
406
409
 
410
+ **Links to docs.** When the agent points you to a doc (a proposal it wrote, a doc it changed), it gives
411
+ a link you can click in the terminal: `http://localhost:4319/open?path=docs/proposals/003-auth.md`. The
412
+ markode tab you already have open shows that doc, and the tab the click opened closes itself, so you
413
+ keep a single markode tab. With no markode tab open, the link opens the editor on that doc.
414
+
407
415
  When the agent writes a proposal, it runs `markode wait <proposal>` in the background: it waits
408
416
  without using tokens until you press "Done, send my answer", then hands the agent your choices, open
409
417
  questions and comments. If no session was open, the next one applies the answer first.
@@ -426,6 +434,7 @@ module, in projects that use Claude Code, markode adds three hooks to `.claude/s
426
434
  Without a global install the command is `npx -y --prefer-offline @rafiqui987/markode`.
427
435
 
428
436
  ```bash
437
+ markode url [<doc.md>] # link to the editor or a doc (starts it if needed)
429
438
  markode check [--json] # what /markode sync fixes
430
439
  markode wait <proposal.md> # wait for the user's answer, then print it
431
440
  markode changes [--json] # the user's edits and comments since last told
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
- import fs20 from "fs";
5
- import path20 from "path";
4
+ import fs21 from "fs";
5
+ import path21 from "path";
6
6
 
7
7
  // src/core/changes.ts
8
8
  import crypto from "crypto";
@@ -1832,12 +1832,69 @@ function keepLanguage(root2, rel) {
1832
1832
  return detected;
1833
1833
  }
1834
1834
 
1835
- // src/core/wait.ts
1835
+ // src/core/server.ts
1836
+ import { spawn } from "child_process";
1836
1837
  import fs17 from "fs";
1837
1838
  import path17 from "path";
1839
+ var serverFile = (root2) => path17.join(stateDir(root2), "server.json");
1840
+ var isAlive = (pid) => {
1841
+ try {
1842
+ process.kill(pid, 0);
1843
+ return true;
1844
+ } catch {
1845
+ return false;
1846
+ }
1847
+ };
1848
+ function recordServer(root2, port) {
1849
+ const file = serverFile(root2);
1850
+ fs17.mkdirSync(path17.dirname(file), { recursive: true });
1851
+ assertInside(file);
1852
+ fs17.writeFileSync(file, JSON.stringify({ port, pid: process.pid }));
1853
+ process.once("exit", () => {
1854
+ if (runningServer(root2)?.pid === process.pid) fs17.rmSync(file, { force: true });
1855
+ });
1856
+ for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) process.once(sig, () => process.exit(130));
1857
+ }
1858
+ function runningServer(root2) {
1859
+ try {
1860
+ const s = JSON.parse(fs17.readFileSync(serverFile(root2), "utf8"));
1861
+ return Number.isInteger(s.port) && Number.isInteger(s.pid) && isAlive(s.pid) ? { port: s.port, pid: s.pid } : null;
1862
+ } catch {
1863
+ return null;
1864
+ }
1865
+ }
1866
+ async function responds(port) {
1867
+ try {
1868
+ return (await fetch(`http://127.0.0.1:${port}/api/info`, { signal: AbortSignal.timeout(1e3) })).ok;
1869
+ } catch {
1870
+ return false;
1871
+ }
1872
+ }
1873
+ async function ensureServer(root2, cli2, port) {
1874
+ const running = runningServer(root2);
1875
+ if (running && await responds(running.port)) return running;
1876
+ const child = spawn(process.execPath, [...process.execArgv, cli2, "serve", "--no-open", "--port", String(port)], {
1877
+ cwd: root2,
1878
+ env: { ...process.env, MARKODE_ROOT: root2 },
1879
+ detached: true,
1880
+ stdio: "ignore"
1881
+ });
1882
+ child.unref();
1883
+ for (let i = 0; i < 100; i++) {
1884
+ await new Promise((r) => setTimeout(r, 100));
1885
+ const s = runningServer(root2);
1886
+ if (s && s.pid === child.pid && await responds(s.port)) return s;
1887
+ }
1888
+ throw new Error("The editor did not start; run `markode serve` to see why.");
1889
+ }
1890
+ var editorUrl = (port, doc) => `http://localhost:${port}/${doc ? `open?path=${encodeURIComponent(doc).replace(/%2F/g, "/")}` : ""}`;
1891
+
1892
+ // src/core/wait.ts
1893
+ import fs18 from "fs";
1894
+ import path18 from "path";
1838
1895
  import YAML5 from "yaml";
1839
- var waitsDir = (root2) => path17.join(stateDir(root2), "waits");
1840
- var pidFile = (root2, rel) => path17.join(waitsDir(root2), `${encodeURIComponent(rel)}.pid`);
1896
+ var waitsDir = (root2) => path18.join(stateDir(root2), "waits");
1897
+ var pidFile = (root2, rel) => path18.join(waitsDir(root2), `${encodeURIComponent(rel)}.pid`);
1841
1898
  var frontmatter2 = (text3) => {
1842
1899
  try {
1843
1900
  return YAML5.parse(/^---\r?\n([\s\S]*?)\r?\n---/.exec(text3)?.[1] ?? "", { logLevel: "error" }) ?? {};
@@ -1845,7 +1902,7 @@ var frontmatter2 = (text3) => {
1845
1902
  return {};
1846
1903
  }
1847
1904
  };
1848
- var isAlive = (pid) => {
1905
+ var isAlive2 = (pid) => {
1849
1906
  try {
1850
1907
  process.kill(pid, 0);
1851
1908
  return true;
@@ -1856,27 +1913,27 @@ var isAlive = (pid) => {
1856
1913
  function waitingOn(root2) {
1857
1914
  let names2 = [];
1858
1915
  try {
1859
- names2 = fs17.readdirSync(waitsDir(root2)).filter((f) => f.endsWith(".pid"));
1916
+ names2 = fs18.readdirSync(waitsDir(root2)).filter((f) => f.endsWith(".pid"));
1860
1917
  } catch {
1861
1918
  }
1862
1919
  return names2.flatMap((f) => {
1863
- const pid = Number(fs17.readFileSync(path17.join(waitsDir(root2), f), "utf8"));
1864
- return isAlive(pid) ? [decodeURIComponent(f.slice(0, -4))] : [];
1920
+ const pid = Number(fs18.readFileSync(path18.join(waitsDir(root2), f), "utf8"));
1921
+ return isAlive2(pid) ? [decodeURIComponent(f.slice(0, -4))] : [];
1865
1922
  });
1866
1923
  }
1867
1924
  function waitForAnswer(root2, rel) {
1868
- const abs = path17.join(root2, rel);
1869
- fs17.mkdirSync(waitsDir(root2), { recursive: true });
1925
+ const abs = path18.join(root2, rel);
1926
+ fs18.mkdirSync(waitsDir(root2), { recursive: true });
1870
1927
  assertInside(pidFile(root2, rel));
1871
- fs17.writeFileSync(pidFile(root2, rel), String(process.pid));
1872
- const cleanup = () => fs17.rmSync(pidFile(root2, rel), { force: true });
1928
+ fs18.writeFileSync(pidFile(root2, rel), String(process.pid));
1929
+ const cleanup = () => fs18.rmSync(pidFile(root2, rel), { force: true });
1873
1930
  process.once("exit", cleanup);
1874
1931
  for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) process.once(sig, () => process.exit(130));
1875
1932
  return new Promise((resolve) => {
1876
1933
  const poll = () => {
1877
1934
  let text3;
1878
1935
  try {
1879
- text3 = fs17.readFileSync(abs, "utf8");
1936
+ text3 = fs18.readFileSync(abs, "utf8");
1880
1937
  } catch {
1881
1938
  return resolve("gone");
1882
1939
  }
@@ -1890,7 +1947,7 @@ function waitForAnswer(root2, rel) {
1890
1947
  }
1891
1948
  var COMMENT = /^\s*<!-- (comment|reply) by (.+?), (\d{4}-\d{2}-\d{2})(?:, on "((?:[^"\\]|\\.)*)")?:\s?([\s\S]*?)\s*-->\s*$/;
1892
1949
  function answerSummary(root2, rel) {
1893
- const text3 = fs17.readFileSync(path17.join(root2, rel), "utf8");
1950
+ const text3 = fs18.readFileSync(path18.join(root2, rel), "utf8");
1894
1951
  const fm = frontmatter2(text3);
1895
1952
  const body = text3.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
1896
1953
  const lines = body.split("\n");
@@ -1964,9 +2021,10 @@ function answerSummary(root2, rel) {
1964
2021
  }
1965
2022
 
1966
2023
  // src/server/index.ts
1967
- import fs18 from "fs";
2024
+ import crypto3 from "crypto";
2025
+ import fs19 from "fs";
1968
2026
  import http from "http";
1969
- import path18 from "path";
2027
+ import path19 from "path";
1970
2028
  import { watch } from "chokidar";
1971
2029
  import open from "open";
1972
2030
  import pc from "picocolors";
@@ -2034,7 +2092,7 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2034
2092
  if (typeof rel !== "string" || !rel.trim()) throw new HttpError(400, "Expected a path");
2035
2093
  const abs = safeResolve(root2, rel);
2036
2094
  if (abs === root2) throw new HttpError(400, "Refusing to delete the project root");
2037
- if (!fs18.existsSync(abs)) throw new HttpError(404, "Not found");
2095
+ if (!fs19.existsSync(abs)) throw new HttpError(404, "Not found");
2038
2096
  return abs;
2039
2097
  };
2040
2098
  const translationFile = (lang, rel) => {
@@ -2046,7 +2104,7 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2046
2104
  "GET /api/info": () => {
2047
2105
  const c = config();
2048
2106
  return {
2049
- name: path18.basename(root2),
2107
+ name: path19.basename(root2),
2050
2108
  mainDoc: c.mainDoc ?? null,
2051
2109
  sourceLang: sourceLang(),
2052
2110
  langs: langs().filter((l) => l !== sourceLang()),
@@ -2073,7 +2131,7 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2073
2131
  }
2074
2132
  if (isLang(body.sourceLang) && body.sourceLang !== sourceLang()) {
2075
2133
  if (!c.mainDoc) throw new HttpError(400, "No main document configured; run `markode init`");
2076
- applyRulesToFile(path18.join(root2, c.mainDoc), void 0, body.sourceLang);
2134
+ applyRulesToFile(path19.join(root2, c.mainDoc), void 0, body.sourceLang);
2077
2135
  }
2078
2136
  writeConfig(root2, c);
2079
2137
  return { ...c, langs: c.langs.filter((l) => l !== sourceLang()) };
@@ -2094,20 +2152,20 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2094
2152
  "GET /api/waiting": () => ({ paths: waitingOn(root2) }),
2095
2153
  "GET /api/settings": () => readSettings(),
2096
2154
  "PUT /api/settings": (_, body) => writeSettings(body),
2097
- "GET /api/tree": (url2) => {
2098
- const l = viewLang(url2.searchParams.get("lang"));
2155
+ "GET /api/tree": (url3) => {
2156
+ const l = viewLang(url3.searchParams.get("lang"));
2099
2157
  const sync2 = l ? Object.fromEntries(allStatuses(root2, l).map((e) => [e.path, e.status])) : {};
2100
2158
  const foreign = featureState(root2).translations ? Object.fromEntries(foreignFiles(root2, sourceLang()).map((f) => [f.path, f.detected])) : {};
2101
2159
  return { tree: scanTree(root2), git: gitStatuses(root2), sync: sync2, foreign };
2102
2160
  },
2103
2161
  "POST /api/keep-language": (_, body) => ({ lang: keepLanguage(root2, mdPath(body.path)) }),
2104
- "GET /api/file": (url2) => {
2105
- const rel = mdPath(url2.searchParams.get("path"));
2106
- const l = viewLang(url2.searchParams.get("lang"));
2107
- const source = fs18.readFileSync(safeResolve(root2, rel), "utf8");
2162
+ "GET /api/file": (url3) => {
2163
+ const rel = mdPath(url3.searchParams.get("path"));
2164
+ const l = viewLang(url3.searchParams.get("lang"));
2165
+ const source = fs19.readFileSync(safeResolve(root2, rel), "utf8");
2108
2166
  if (l) {
2109
2167
  const tPath = translationFile(l, rel);
2110
- if (fs18.existsSync(tPath)) return { path: rel, content: fs18.readFileSync(tPath, "utf8"), target: "translation", status: fileStatus(root2, l, rel) };
2168
+ if (fs19.existsSync(tPath)) return { path: rel, content: fs19.readFileSync(tPath, "utf8"), target: "translation", status: fileStatus(root2, l, rel) };
2111
2169
  return { path: rel, content: source, target: "source", status: "missing" };
2112
2170
  }
2113
2171
  return { path: rel, content: source, target: "source", status: null };
@@ -2116,19 +2174,19 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2116
2174
  const rel = mdPath(body.path);
2117
2175
  const l = viewLang(body.lang);
2118
2176
  const file = body.target === "translation" && l ? translationFile(l, rel) : safeResolve(root2, rel);
2119
- fs18.mkdirSync(path18.dirname(file), { recursive: true });
2177
+ fs19.mkdirSync(path19.dirname(file), { recursive: true });
2120
2178
  const content = String(body.content ?? "");
2121
- if (!l && featureState(root2).agent) logSave(root2, rel, fs18.existsSync(file) ? fs18.readFileSync(file, "utf8") : "", content);
2122
- fs18.writeFileSync(file, content);
2179
+ if (!l && featureState(root2).agent) logSave(root2, rel, fs19.existsSync(file) ? fs19.readFileSync(file, "utf8") : "", content);
2180
+ fs19.writeFileSync(file, content);
2123
2181
  return { ok: true, status: l ? fileStatus(root2, l, rel) : null };
2124
2182
  },
2125
2183
  "POST /api/file": (_, body) => {
2126
2184
  const rel = mdPath(body.path);
2127
2185
  const file = safeResolve(root2, rel);
2128
- if (fs18.existsSync(file)) throw new HttpError(409, "File already exists");
2129
- fs18.mkdirSync(path18.dirname(file), { recursive: true });
2130
- const title = path18.basename(rel, ".md").replace(/^\d+[-_]/, "").replace(/[-_]/g, " ");
2131
- fs18.writeFileSync(file, `# ${title.charAt(0).toUpperCase() + title.slice(1)}
2186
+ if (fs19.existsSync(file)) throw new HttpError(409, "File already exists");
2187
+ fs19.mkdirSync(path19.dirname(file), { recursive: true });
2188
+ const title = path19.basename(rel, ".md").replace(/^\d+[-_]/, "").replace(/[-_]/g, " ");
2189
+ fs19.writeFileSync(file, `# ${title.charAt(0).toUpperCase() + title.slice(1)}
2132
2190
 
2133
2191
  `);
2134
2192
  return { ok: true, path: rel };
@@ -2138,20 +2196,20 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2138
2196
  if (typeof body.path !== "string" || !body.path.trim()) throw new HttpError(400, "Expected a path");
2139
2197
  const dir = safeResolve(root2, body.path);
2140
2198
  if (dir === root2) throw new HttpError(400, "Expected a path");
2141
- if (fs18.existsSync(dir)) throw new HttpError(409, "A file or folder with that name already exists");
2142
- fs18.mkdirSync(dir, { recursive: true });
2143
- return { ok: true, path: toPosix(path18.relative(root2, dir)) };
2199
+ if (fs19.existsSync(dir)) throw new HttpError(409, "A file or folder with that name already exists");
2200
+ fs19.mkdirSync(dir, { recursive: true });
2201
+ return { ok: true, path: toPosix(path19.relative(root2, dir)) };
2144
2202
  },
2145
- "GET /api/delete-preview": (url2) => {
2146
- const abs = deletable(url2.searchParams.get("path"));
2147
- if (!fs18.statSync(abs).isDirectory()) return { kind: "file", files: 1, md: 1 };
2148
- const files = fs18.readdirSync(abs, { recursive: true, withFileTypes: true }).filter((e) => e.isFile());
2203
+ "GET /api/delete-preview": (url3) => {
2204
+ const abs = deletable(url3.searchParams.get("path"));
2205
+ if (!fs19.statSync(abs).isDirectory()) return { kind: "file", files: 1, md: 1 };
2206
+ const files = fs19.readdirSync(abs, { recursive: true, withFileTypes: true }).filter((e) => e.isFile());
2149
2207
  return { kind: "dir", files: files.length, md: files.filter((e) => e.name.toLowerCase().endsWith(".md")).length };
2150
2208
  },
2151
- "DELETE /api/path": (url2) => {
2152
- const rel = toPosix(path18.relative(root2, deletable(url2.searchParams.get("path"))));
2153
- fs18.rmSync(safeResolve(root2, rel), { recursive: true });
2154
- for (const l of config().langs) fs18.rmSync(translationFile(l, rel), { recursive: true, force: true });
2209
+ "DELETE /api/path": (url3) => {
2210
+ const rel = toPosix(path19.relative(root2, deletable(url3.searchParams.get("path"))));
2211
+ fs19.rmSync(safeResolve(root2, rel), { recursive: true });
2212
+ for (const l of config().langs) fs19.rmSync(translationFile(l, rel), { recursive: true, force: true });
2155
2213
  return { ok: true };
2156
2214
  },
2157
2215
  "POST /api/ignore": (_, body) => {
@@ -2160,7 +2218,7 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2160
2218
  setIgnored(root2, body.path, body.kind === "dir" ? "dir" : "file", !!body.ignored);
2161
2219
  return { ok: true };
2162
2220
  },
2163
- "GET /api/diff": (url2) => ({ diff: gitDiff(root2, mdPath(url2.searchParams.get("path"))) }),
2221
+ "GET /api/diff": (url3) => ({ diff: gitDiff(root2, mdPath(url3.searchParams.get("path"))) }),
2164
2222
  "GET /api/dashboard": () => {
2165
2223
  const sdd = summarizeSdd(root2);
2166
2224
  const shown = sddPrefixes(sdd);
@@ -2168,12 +2226,12 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2168
2226
  },
2169
2227
  "GET /api/rules": () => {
2170
2228
  const mainDoc = readConfig(root2)?.mainDoc;
2171
- const text3 = mainDoc && fs18.existsSync(path18.join(root2, mainDoc)) ? fs18.readFileSync(path18.join(root2, mainDoc), "utf8") : "";
2229
+ const text3 = mainDoc && fs19.existsSync(path19.join(root2, mainDoc)) ? fs19.readFileSync(path19.join(root2, mainDoc), "utf8") : "";
2172
2230
  return { rules: listRules(), applied: appliedRuleIds(text3), mainDoc: mainDoc ?? null };
2173
2231
  },
2174
2232
  "PUT /api/rules": (_, body) => saveRule(body),
2175
- "DELETE /api/rules": (url2) => {
2176
- deleteRule(url2.searchParams.get("id") ?? "");
2233
+ "DELETE /api/rules": (url3) => {
2234
+ deleteRule(url3.searchParams.get("id") ?? "");
2177
2235
  return { ok: true };
2178
2236
  },
2179
2237
  // Global skills: reads and writes are limited to SKILL.md of discovered skill folders.
@@ -2182,14 +2240,14 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2182
2240
  if (typeof body.name !== "string" || !Array.isArray(body.tags)) throw new HttpError(400, "Expected name and tags");
2183
2241
  return { tags: setSkillTags(body.name, body.tags.map(String)) };
2184
2242
  },
2185
- "GET /api/skills/file": (url2) => {
2186
- const skill = getSkill(root2, url2.searchParams.get("key"));
2187
- return { path: skill.key, content: fs18.readFileSync(path18.join(skill.key, "SKILL.md"), "utf8"), target: "source", status: null };
2243
+ "GET /api/skills/file": (url3) => {
2244
+ const skill = getSkill(root2, url3.searchParams.get("key"));
2245
+ return { path: skill.key, content: fs19.readFileSync(path19.join(skill.key, "SKILL.md"), "utf8"), target: "source", status: null };
2188
2246
  },
2189
2247
  "PUT /api/skills/file": (_, body) => {
2190
2248
  const skill = getSkill(root2, body.key);
2191
2249
  if (skill.managed) throw new HttpError(403, "This skill is managed by the app that installed it");
2192
- fs18.writeFileSync(path18.join(skill.key, "SKILL.md"), String(body.content ?? ""));
2250
+ fs19.writeFileSync(path19.join(skill.key, "SKILL.md"), String(body.content ?? ""));
2193
2251
  return { ok: true, status: null };
2194
2252
  },
2195
2253
  "POST /api/skills/install": (_, body) => {
@@ -2202,7 +2260,7 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2202
2260
  "POST /api/rules/apply": (_, body) => {
2203
2261
  const mainDoc = readConfig(root2)?.mainDoc;
2204
2262
  if (!mainDoc) throw new HttpError(400, "No main document configured; run `markode init`");
2205
- applyRulesToFile(path18.join(root2, mainDoc), body.ids ?? []);
2263
+ applyRulesToFile(path19.join(root2, mainDoc), body.ids ?? []);
2206
2264
  return { ok: true };
2207
2265
  }
2208
2266
  };
@@ -2213,11 +2271,11 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2213
2271
  res.writeHead(403).end("Forbidden");
2214
2272
  return;
2215
2273
  }
2216
- const url2 = new URL(req.url ?? "/", "http://localhost");
2217
- const route = routes[`${req.method} ${url2.pathname}`];
2274
+ const url3 = new URL(req.url ?? "/", "http://localhost");
2275
+ const route = routes[`${req.method} ${url3.pathname}`];
2218
2276
  if (route) {
2219
2277
  try {
2220
- const result = await route(url2, req.method === "GET" || req.method === "DELETE" ? {} : await readBody(req));
2278
+ const result = await route(url3, req.method === "GET" || req.method === "DELETE" ? {} : await readBody(req));
2221
2279
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(result));
2222
2280
  } catch (err) {
2223
2281
  const status = err instanceof HttpError ? err.status : err.code === "ENOENT" ? 404 : 500;
@@ -2225,8 +2283,9 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2225
2283
  }
2226
2284
  return;
2227
2285
  }
2228
- if (req.method === "GET" && url2.pathname === "/api/asset") return serveAsset(url2.searchParams.get("path") ?? "", res);
2229
- serveStatic(url2.pathname, res);
2286
+ if (req.method === "GET" && url3.pathname === "/api/asset") return serveAsset(url3.searchParams.get("path") ?? "", res);
2287
+ if (req.method === "GET" && url3.pathname === "/open") return openDoc(url3.searchParams.get("path") ?? "", res);
2288
+ serveStatic(url3.pathname, res);
2230
2289
  });
2231
2290
  function serveAsset(rel, res) {
2232
2291
  let file;
@@ -2236,13 +2295,33 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2236
2295
  res.writeHead(404).end();
2237
2296
  return;
2238
2297
  }
2239
- const type = IMAGE_MIME[path18.extname(file).toLowerCase()];
2240
- if (!type || !fs18.existsSync(file) || !fs18.statSync(file).isFile()) {
2298
+ const type = IMAGE_MIME[path19.extname(file).toLowerCase()];
2299
+ if (!type || !fs19.existsSync(file) || !fs19.statSync(file).isFile()) {
2241
2300
  res.writeHead(404).end();
2242
2301
  return;
2243
2302
  }
2244
2303
  res.writeHead(200, { "content-type": type, "cache-control": "no-cache", "content-security-policy": "sandbox; frame-ancestors 'none'" });
2245
- fs18.createReadStream(file).pipe(res);
2304
+ fs19.createReadStream(file).pipe(res);
2305
+ }
2306
+ function openDoc(rel, res) {
2307
+ let doc;
2308
+ try {
2309
+ doc = mdPath(rel);
2310
+ } catch {
2311
+ res.writeHead(400).end("Expected a .md path");
2312
+ return;
2313
+ }
2314
+ const hash2 = `/#/${encodeURI(doc)}`;
2315
+ if (!wss.clients.size) {
2316
+ res.writeHead(302, { location: hash2 }).end();
2317
+ return;
2318
+ }
2319
+ broadcast({ type: "open", path: doc });
2320
+ const text3 = OPENED_TEXT[readSettings().uiLang] ?? OPENED_TEXT.en;
2321
+ const page = `<!doctype html><meta charset="utf-8"><title>markode</title><body style="font:15px system-ui;padding:2rem;color-scheme:light dark">
2322
+ <p>${text3.shown}</p><p><a href="${hash2}">${text3.here}</a></p><script>window.close()</script>`;
2323
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", "content-security-policy": "frame-ancestors 'none'; script-src 'sha256-" + CLOSE_HASH + "'" });
2324
+ res.end(page);
2246
2325
  }
2247
2326
  const actualPort = await listen(server, port);
2248
2327
  const wss = new WebSocketServer({ server, path: "/ws", verifyClient: ({ req }) => isLocalRequest(req) });
@@ -2256,27 +2335,34 @@ async function startServer({ root: root2, port, open: openBrowser }) {
2256
2335
  // Unreadable folders (e.g. caches owned by another user) are skipped instead of failing.
2257
2336
  ignorePermissionErrors: true,
2258
2337
  ignored: (p3, stats) => {
2259
- const rel = toPosix(path18.relative(root2, p3));
2338
+ const rel = toPosix(path19.relative(root2, p3));
2260
2339
  if (rel === ".markode") return false;
2261
2340
  if (rel.startsWith(".markode/")) return !rel.startsWith(".markode/i18n") && rel !== ".markode/state.json" && rel !== ".markode/config.json";
2262
2341
  return skips(rel, stats ? stats.isDirectory() : void 0);
2263
2342
  }
2264
2343
  }).on("error", (err) => console.warn(pc.yellow(` file watcher: ${err.message}`))).on("all", (event, file) => {
2265
- const abs = path18.resolve(file);
2344
+ const abs = path19.resolve(file);
2266
2345
  if (!abs.endsWith(".md") && !abs.endsWith(".json") && !abs.endsWith(".markodeignore") && event !== "addDir" && event !== "unlinkDir") return;
2267
- let rel = toPosix(path18.relative(root2, abs));
2268
- const i18n = toPosix(path18.relative(root2, path18.join(stateDir(root2), "i18n")));
2346
+ let rel = toPosix(path19.relative(root2, abs));
2347
+ const i18n = toPosix(path19.relative(root2, path19.join(stateDir(root2), "i18n")));
2269
2348
  if (rel.startsWith(i18n + "/")) rel = rel.split("/").slice(3).join("/");
2270
2349
  broadcast({ type: "fs", event, path: rel });
2271
2350
  });
2272
- const url = `http://localhost:${actualPort}`;
2351
+ recordServer(root2, actualPort);
2352
+ const url2 = `http://localhost:${actualPort}`;
2273
2353
  console.log(`
2274
- ${pc.bold("markode")} ${pc.dim("\u2192")} ${pc.cyan(url)} ${pc.dim(root2)}
2354
+ ${pc.bold("markode")} ${pc.dim("\u2192")} ${pc.cyan(url2)} ${pc.dim(root2)}
2275
2355
  `);
2276
- if (!fs18.existsSync(WEB_DIR)) console.log(pc.yellow(" Web UI not built: run `pnpm build`, or `pnpm dev` and open http://localhost:4320\n"));
2277
- else if (openBrowser) open(url).catch(() => {
2356
+ if (!fs19.existsSync(WEB_DIR)) console.log(pc.yellow(" Web UI not built: run `pnpm build`, or `pnpm dev` and open http://localhost:4320\n"));
2357
+ else if (openBrowser) open(url2).catch(() => {
2278
2358
  });
2279
2359
  }
2360
+ var OPENED_TEXT = {
2361
+ en: { shown: "Opened in your markode tab. You can close this one.", here: "Open it here instead" },
2362
+ es: { shown: "Abierto en tu pesta\xF1a de markode. Puedes cerrar esta.", here: "Abrirlo aqu\xED" },
2363
+ fr: { shown: "Ouvert dans ton onglet markode. Tu peux fermer celui-ci.", here: "L\u2019ouvrir ici" }
2364
+ };
2365
+ var CLOSE_HASH = crypto3.createHash("sha256").update("window.close()").digest("base64");
2280
2366
  function listen(server, port) {
2281
2367
  return new Promise((resolve, reject) => {
2282
2368
  server.once("error", (err) => {
@@ -2287,19 +2373,19 @@ function listen(server, port) {
2287
2373
  });
2288
2374
  }
2289
2375
  function serveStatic(pathname, res) {
2290
- let file = path18.join(WEB_DIR, path18.normalize(pathname).replace(/^(\.\.[/\\])+/, ""));
2291
- if (!file.startsWith(WEB_DIR) || !fs18.existsSync(file) || fs18.statSync(file).isDirectory()) file = path18.join(WEB_DIR, "index.html");
2292
- if (!fs18.existsSync(file)) {
2376
+ let file = path19.join(WEB_DIR, path19.normalize(pathname).replace(/^(\.\.[/\\])+/, ""));
2377
+ if (!file.startsWith(WEB_DIR) || !fs19.existsSync(file) || fs19.statSync(file).isDirectory()) file = path19.join(WEB_DIR, "index.html");
2378
+ if (!fs19.existsSync(file)) {
2293
2379
  res.writeHead(404).end("Web UI not built. Run `pnpm build`.");
2294
2380
  return;
2295
2381
  }
2296
- res.writeHead(200, { "content-type": MIME[path18.extname(file)] ?? "application/octet-stream", "content-security-policy": "frame-ancestors 'none'" });
2297
- fs18.createReadStream(file).pipe(res);
2382
+ res.writeHead(200, { "content-type": MIME[path19.extname(file)] ?? "application/octet-stream", "content-security-policy": "frame-ancestors 'none'" });
2383
+ fs19.createReadStream(file).pipe(res);
2298
2384
  }
2299
2385
 
2300
2386
  // src/cli/init.ts
2301
- import fs19 from "fs";
2302
- import path19 from "path";
2387
+ import fs20 from "fs";
2388
+ import path20 from "path";
2303
2389
  import * as p from "@clack/prompts";
2304
2390
  import pc2 from "picocolors";
2305
2391
  function check(value) {
@@ -2310,12 +2396,12 @@ function check(value) {
2310
2396
  return value;
2311
2397
  }
2312
2398
  async function offerClaudeLink(root2, d) {
2313
- if (fs19.existsSync(path19.join(root2, "CLAUDE.md"))) return;
2399
+ if (fs20.existsSync(path20.join(root2, "CLAUDE.md"))) return;
2314
2400
  const link2 = d ? d.mainDoc === "CLAUDE.md" : check(await p.confirm({ message: "Claude Code reads CLAUDE.md, not AGENTS.md. Add a CLAUDE.md that imports AGENTS.md?", initialValue: true }));
2315
2401
  if (link2 && linkAgentsDoc(root2)) p.log.success("Created CLAUDE.md (imports AGENTS.md)");
2316
2402
  }
2317
2403
  async function chooseMainDoc(root2, d) {
2318
- const existing = MAIN_DOCS.filter((d2) => fs19.existsSync(path19.join(root2, d2)) && !isPointer(fs19.readFileSync(path19.join(root2, d2), "utf8"), d2));
2404
+ const existing = MAIN_DOCS.filter((d2) => fs20.existsSync(path20.join(root2, d2)) && !isPointer(fs20.readFileSync(path20.join(root2, d2), "utf8"), d2));
2319
2405
  if (d) {
2320
2406
  if (existing.length === 1 && existing[0] === "AGENTS.md") await offerClaudeLink(root2, d);
2321
2407
  if (existing.length) return existing.includes(d.mainDoc) ? d.mainDoc : existing[0];
@@ -2432,7 +2518,7 @@ async function chooseAreas(root2) {
2432
2518
  return areas;
2433
2519
  }
2434
2520
  async function chooseKind(root2, dir) {
2435
- const guess = guessKind(path19.join(root2, dir));
2521
+ const guess = guessKind(path20.join(root2, dir));
2436
2522
  if (!guess.unclear) return guess.kind;
2437
2523
  const where = dir ? `${dir}/` : "this project";
2438
2524
  const seen = guess.signals.length ? `found: ${guess.signals.join(", ")}` : "no code yet";
@@ -2445,7 +2531,7 @@ async function chooseKind(root2, dir) {
2445
2531
  );
2446
2532
  }
2447
2533
  async function askProfile(root2, dir, kind) {
2448
- const base = detectProfile(path19.join(root2, dir), kind);
2534
+ const base = detectProfile(path20.join(root2, dir), kind);
2449
2535
  if (kind === "app") return base;
2450
2536
  const where = dir ? ` (${dir}/)` : "";
2451
2537
  if (kind === "library") return { kind, published: check(await p.confirm({ message: `Is the library${where} published (npm, PyPI\u2026)?`, initialValue: base.published })) };
@@ -2522,7 +2608,7 @@ async function chooseWorkflow(root2, mainDoc, previous, quiet) {
2522
2608
  const profile = areas.length ? void 0 : await askProfile(root2, "", await chooseKind(root2, ""));
2523
2609
  const created = scaffoldMethod(root2, mainDoc, areas);
2524
2610
  installMethodSkills(root2);
2525
- applyWorkflowToFile(path19.join(root2, mainDoc), "markode", methodSection(areas, root2, { profile, contracts, existing }));
2611
+ applyWorkflowToFile(path20.join(root2, mainDoc), "markode", methodSection(areas, root2, { profile, contracts, existing }));
2526
2612
  const big = !areas.length && flattenFiles(scanTree(root2)).length > 40;
2527
2613
  const layout = areas.length ? "structured" : !big ? "general" : check(await p.select({ message: PROPOSALS_QUESTION, options: PROPOSAL_OPTIONS }));
2528
2614
  activateProposals(root2, layout, mainDoc);
@@ -2587,10 +2673,10 @@ async function runInit(root2, plan2) {
2587
2673
  let workflow = previous?.workflow ?? "none";
2588
2674
  if (has2("agent")) {
2589
2675
  mainDoc = await chooseMainDoc(root2, d);
2590
- const mainFile = path19.join(root2, mainDoc);
2676
+ const mainFile = path20.join(root2, mainDoc);
2591
2677
  if (has2("dashboard")) workflow = await chooseWorkflow(root2, mainDoc, previous?.workflow, d);
2592
2678
  if (workflow === "openspec" || workflow === "speckit") applyWorkflowToFile(mainFile, workflow);
2593
- const mainText = fs19.readFileSync(mainFile, "utf8");
2679
+ const mainText = fs20.readFileSync(mainFile, "utf8");
2594
2680
  const sourceLang = d ? SOURCE_LANG_MARK.test(mainText) ? sourceLangOf(mainText) : detectedLang : check(await p.select({ message: SOURCE_LANG_QUESTION, options: LANG_OPTIONS, initialValue: SOURCE_LANG_MARK.test(mainText) ? sourceLangOf(mainText) : detectedLang }));
2595
2681
  const rules = listRules();
2596
2682
  const applied = appliedRuleIds(mainText);
@@ -2605,7 +2691,7 @@ async function runInit(root2, plan2) {
2605
2691
  required: false
2606
2692
  })
2607
2693
  );
2608
- for (const dir of targets) applyRulesToFile(contentDoc(path19.join(root2, dir)), selected, sourceLang);
2694
+ for (const dir of targets) applyRulesToFile(contentDoc(path20.join(root2, dir)), selected, sourceLang);
2609
2695
  }
2610
2696
  installAgentSkill(root2);
2611
2697
  const proposals = proposalsState(root2);
@@ -2752,6 +2838,8 @@ Usage
2752
2838
  markode features list the modules and whether they are on in this project
2753
2839
  markode features <module> on|off turn a module on or off (same as Settings \u2192 Features)
2754
2840
  markode serve open the editor without running init
2841
+ markode url [<doc.md>] the editor's link (or a doc's), starting it in the background if it isn't running
2842
+ markode stop stop the editor running in the background for this project
2755
2843
  markode translate choose languages to review the docs in (or: markode --translate)
2756
2844
  markode check [--json] what /markode sync fixes: answered proposals, phases vs tasks, projects table, broken links
2757
2845
  markode wait <proposal> wait until the user answers a proposal in markode, then print the answer
@@ -2785,9 +2873,9 @@ var option = (name) => {
2785
2873
  var command = args[0] && !args[0].startsWith("-") ? args[0] : "start";
2786
2874
  var root = process.env.MARKODE_ROOT ?? process.cwd();
2787
2875
  async function main() {
2788
- if (fs20.existsSync(root)) setProjectRoot(root);
2876
+ if (fs21.existsSync(root)) setProjectRoot(root);
2789
2877
  if (flag("--help") || flag("-h")) return console.log(HELP);
2790
- if (flag("--version") || flag("-v")) return console.log(JSON.parse(fs20.readFileSync(path20.join(PKG_ROOT, "package.json"), "utf8")).version);
2878
+ if (flag("--version") || flag("-v")) return console.log(JSON.parse(fs21.readFileSync(path21.join(PKG_ROOT, "package.json"), "utf8")).version);
2791
2879
  const translation = flag("--status") || flag("--mark") || flag("--keep");
2792
2880
  if ((command === "sync" || command === "translate") && translation) return sync();
2793
2881
  if (command === "translate" || flag("--translate")) return runTranslate(root);
@@ -2795,6 +2883,8 @@ async function main() {
2795
2883
  if (command === "wait") return wait();
2796
2884
  if (command === "link") return link();
2797
2885
  if (command === "changes") return changes();
2886
+ if (command === "url") return url();
2887
+ if (command === "stop") return stop();
2798
2888
  if (command === "features") return features();
2799
2889
  if (command === "setup") {
2800
2890
  await runSetup(false, flag("--custom"));
@@ -2880,6 +2970,22 @@ function link() {
2880
2970
  if (flag("--dry-run")) return console.log(missing.map((m) => `would create ${m.path} \u2192 ${m.pointsTo}`).join("\n"));
2881
2971
  for (const p3 of linkMainDocs(root)) console.log(`created ${p3}`);
2882
2972
  }
2973
+ async function url() {
2974
+ const doc = args[1] && !args[1].startsWith("-") ? args[1] : void 0;
2975
+ if (doc && (!doc.toLowerCase().endsWith(".md") || !fs21.existsSync(path21.join(root, doc)))) {
2976
+ console.error(`Usage: markode url [<doc.md>] (relative to ${root})`);
2977
+ process.exitCode = 1;
2978
+ return;
2979
+ }
2980
+ const { port } = await ensureServer(root, process.argv[1], Number(option("--port") ?? 4319));
2981
+ console.log(editorUrl(port, doc));
2982
+ }
2983
+ function stop() {
2984
+ const running = runningServer(root);
2985
+ if (!running) return console.log("The editor is not running for this project.");
2986
+ process.kill(running.pid);
2987
+ console.log(`Stopped the editor on port ${running.port}.`);
2988
+ }
2883
2989
  function changes() {
2884
2990
  if (flag("--start")) return startSession(root);
2885
2991
  const list = takeChanges(root);
@@ -2894,7 +3000,7 @@ function changes() {
2894
3000
  }
2895
3001
  async function wait() {
2896
3002
  const rel = args[1];
2897
- if (!rel || !fs20.existsSync(path20.join(root, rel))) {
3003
+ if (!rel || !fs21.existsSync(path21.join(root, rel))) {
2898
3004
  console.error(`Usage: markode wait <proposal.md> (relative to ${root})`);
2899
3005
  process.exitCode = 1;
2900
3006
  return;
@@ -1 +1 @@
1
- import{m as e,t}from"./mermaid-parser.core-ByEdgqAb.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-DgNmQVK4.js";import"./chunk-DSNLSHJZ-BNEo5raG.js";import{n as i,r as a,t as o}from"./chunk-2E7WHVMD-w-cvaztJ.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-CXUI3iYT.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
1
+ import{m as e,t}from"./mermaid-parser.core-C1XIxySl.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-okGi814h.js";import"./chunk-DSNLSHJZ-C0Wri32R.js";import{n as i,r as a,t as o}from"./chunk-2E7WHVMD-B2TT-9T2.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-C-385ykR.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
@@ -1 +1 @@
1
- import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-B1oQ1VTT.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
1
+ import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-CVF0b6Yz.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};