@monoes/monomindcli 2.13.0 → 2.14.1

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 (251) hide show
  1. package/.claude/skills/monodesign/scripts/context.mjs +1 -1
  2. package/.claude/skills/monodesign/scripts/detector/detect-antipatterns-browser.js +2 -2
  3. package/.claude/skills/monodesign/scripts/detector/rules/checks.mjs +2 -2
  4. package/.claude/skills/monodesign/scripts/live-browser.js +1 -1
  5. package/bin/cli.js +21 -1
  6. package/bin/mcp-server.js +21 -1
  7. package/dist/src/capabilities/cap-documents.d.ts.map +1 -1
  8. package/dist/src/capabilities/cap-documents.js +23 -0
  9. package/dist/src/capabilities/cap-documents.js.map +1 -1
  10. package/dist/src/commands/doc-filters.d.ts +19 -0
  11. package/dist/src/commands/doc-filters.d.ts.map +1 -0
  12. package/dist/src/commands/doc-filters.js +58 -0
  13. package/dist/src/commands/doc-filters.js.map +1 -0
  14. package/dist/src/commands/doc-library.d.ts +21 -0
  15. package/dist/src/commands/doc-library.d.ts.map +1 -0
  16. package/dist/src/commands/doc-library.js +390 -0
  17. package/dist/src/commands/doc-library.js.map +1 -0
  18. package/dist/src/commands/doc-list.d.ts +9 -0
  19. package/dist/src/commands/doc-list.d.ts.map +1 -0
  20. package/dist/src/commands/doc-list.js +99 -0
  21. package/dist/src/commands/doc-list.js.map +1 -0
  22. package/dist/src/commands/doc.d.ts.map +1 -1
  23. package/dist/src/commands/doc.js +49 -37
  24. package/dist/src/commands/doc.js.map +1 -1
  25. package/dist/src/commands/doctor-mcp-probe.d.ts +48 -0
  26. package/dist/src/commands/doctor-mcp-probe.d.ts.map +1 -0
  27. package/dist/src/commands/doctor-mcp-probe.js +205 -0
  28. package/dist/src/commands/doctor-mcp-probe.js.map +1 -0
  29. package/dist/src/commands/doctor-project-checks.d.ts +12 -1
  30. package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
  31. package/dist/src/commands/doctor-project-checks.js +55 -4
  32. package/dist/src/commands/doctor-project-checks.js.map +1 -1
  33. package/dist/src/commands/doctor.d.ts.map +1 -1
  34. package/dist/src/commands/doctor.js +10 -1
  35. package/dist/src/commands/doctor.js.map +1 -1
  36. package/dist/src/commands/init.d.ts.map +1 -1
  37. package/dist/src/commands/init.js +17 -0
  38. package/dist/src/commands/init.js.map +1 -1
  39. package/dist/src/commands/mcp.d.ts.map +1 -1
  40. package/dist/src/commands/mcp.js +4 -3
  41. package/dist/src/commands/mcp.js.map +1 -1
  42. package/dist/src/commands/org-observe.d.ts.map +1 -1
  43. package/dist/src/commands/org-observe.js +14 -0
  44. package/dist/src/commands/org-observe.js.map +1 -1
  45. package/dist/src/commands/org.d.ts.map +1 -1
  46. package/dist/src/commands/org.js +12 -2
  47. package/dist/src/commands/org.js.map +1 -1
  48. package/dist/src/init/claudemd-generator.d.ts.map +1 -1
  49. package/dist/src/init/claudemd-generator.js +2 -1
  50. package/dist/src/init/claudemd-generator.js.map +1 -1
  51. package/dist/src/init/mcp-generator.d.ts.map +1 -1
  52. package/dist/src/init/mcp-generator.js +3 -3
  53. package/dist/src/init/mcp-generator.js.map +1 -1
  54. package/dist/src/init/types.d.ts +6 -0
  55. package/dist/src/init/types.d.ts.map +1 -1
  56. package/dist/src/init/types.js.map +1 -1
  57. package/dist/src/init/write-runtime-config.d.ts.map +1 -1
  58. package/dist/src/init/write-runtime-config.js +12 -2
  59. package/dist/src/init/write-runtime-config.js.map +1 -1
  60. package/dist/src/knowledge/capture-envelope.d.ts +80 -0
  61. package/dist/src/knowledge/capture-envelope.d.ts.map +1 -0
  62. package/dist/src/knowledge/capture-envelope.js +165 -0
  63. package/dist/src/knowledge/capture-envelope.js.map +1 -0
  64. package/dist/src/knowledge/capture-text.d.ts +23 -0
  65. package/dist/src/knowledge/capture-text.d.ts.map +1 -0
  66. package/dist/src/knowledge/capture-text.js +49 -0
  67. package/dist/src/knowledge/capture-text.js.map +1 -0
  68. package/dist/src/knowledge/citation.d.ts +121 -0
  69. package/dist/src/knowledge/citation.d.ts.map +1 -0
  70. package/dist/src/knowledge/citation.js +252 -0
  71. package/dist/src/knowledge/citation.js.map +1 -0
  72. package/dist/src/knowledge/document-pipeline.d.ts +60 -0
  73. package/dist/src/knowledge/document-pipeline.d.ts.map +1 -1
  74. package/dist/src/knowledge/document-pipeline.js +188 -4
  75. package/dist/src/knowledge/document-pipeline.js.map +1 -1
  76. package/dist/src/knowledge/html-extract.d.ts +43 -0
  77. package/dist/src/knowledge/html-extract.d.ts.map +1 -0
  78. package/dist/src/knowledge/html-extract.js +286 -0
  79. package/dist/src/knowledge/html-extract.js.map +1 -0
  80. package/dist/src/knowledge/html-tags.d.ts +30 -0
  81. package/dist/src/knowledge/html-tags.d.ts.map +1 -0
  82. package/dist/src/knowledge/html-tags.js +229 -0
  83. package/dist/src/knowledge/html-tags.js.map +1 -0
  84. package/dist/src/knowledge/library.d.ts +106 -0
  85. package/dist/src/knowledge/library.d.ts.map +1 -0
  86. package/dist/src/knowledge/library.js +195 -0
  87. package/dist/src/knowledge/library.js.map +1 -0
  88. package/dist/src/knowledge/mhtml.d.ts +60 -0
  89. package/dist/src/knowledge/mhtml.d.ts.map +1 -0
  90. package/dist/src/knowledge/mhtml.js +154 -0
  91. package/dist/src/knowledge/mhtml.js.map +1 -0
  92. package/dist/src/knowledge/related.d.ts +58 -0
  93. package/dist/src/knowledge/related.d.ts.map +1 -0
  94. package/dist/src/knowledge/related.js +222 -0
  95. package/dist/src/knowledge/related.js.map +1 -0
  96. package/dist/src/knowledge/section-diff.d.ts +48 -0
  97. package/dist/src/knowledge/section-diff.d.ts.map +1 -0
  98. package/dist/src/knowledge/section-diff.js +151 -0
  99. package/dist/src/knowledge/section-diff.js.map +1 -0
  100. package/dist/src/knowledge/watch.d.ts +92 -0
  101. package/dist/src/knowledge/watch.d.ts.map +1 -0
  102. package/dist/src/knowledge/watch.js +221 -0
  103. package/dist/src/knowledge/watch.js.map +1 -0
  104. package/dist/src/mcp-server.d.ts.map +1 -1
  105. package/dist/src/mcp-server.js +44 -87
  106. package/dist/src/mcp-server.js.map +1 -1
  107. package/dist/src/mcp-tools/browser-instrument-tools.d.ts +35 -0
  108. package/dist/src/mcp-tools/browser-instrument-tools.d.ts.map +1 -0
  109. package/dist/src/mcp-tools/browser-instrument-tools.js +359 -0
  110. package/dist/src/mcp-tools/browser-instrument-tools.js.map +1 -0
  111. package/dist/src/mcp-tools/browser-metrics.d.ts +42 -0
  112. package/dist/src/mcp-tools/browser-metrics.d.ts.map +1 -0
  113. package/dist/src/mcp-tools/browser-metrics.js +91 -0
  114. package/dist/src/mcp-tools/browser-metrics.js.map +1 -0
  115. package/dist/src/mcp-tools/browser-profile-tools.d.ts +16 -0
  116. package/dist/src/mcp-tools/browser-profile-tools.d.ts.map +1 -0
  117. package/dist/src/mcp-tools/browser-profile-tools.js +324 -0
  118. package/dist/src/mcp-tools/browser-profile-tools.js.map +1 -0
  119. package/dist/src/mcp-tools/browser-session.d.ts +68 -0
  120. package/dist/src/mcp-tools/browser-session.d.ts.map +1 -0
  121. package/dist/src/mcp-tools/browser-session.js +224 -0
  122. package/dist/src/mcp-tools/browser-session.js.map +1 -0
  123. package/dist/src/mcp-tools/browser-tools.d.ts.map +1 -1
  124. package/dist/src/mcp-tools/browser-tools.js +10 -176
  125. package/dist/src/mcp-tools/browser-tools.js.map +1 -1
  126. package/dist/src/mcp-tools/capture-resource-read.d.ts +115 -0
  127. package/dist/src/mcp-tools/capture-resource-read.d.ts.map +1 -0
  128. package/dist/src/mcp-tools/capture-resource-read.js +296 -0
  129. package/dist/src/mcp-tools/capture-resource-read.js.map +1 -0
  130. package/dist/src/mcp-tools/capture-resource-tools.d.ts +22 -0
  131. package/dist/src/mcp-tools/capture-resource-tools.d.ts.map +1 -0
  132. package/dist/src/mcp-tools/capture-resource-tools.js +182 -0
  133. package/dist/src/mcp-tools/capture-resource-tools.js.map +1 -0
  134. package/dist/src/mcp-tools/capture-resources.d.ts +142 -0
  135. package/dist/src/mcp-tools/capture-resources.d.ts.map +1 -0
  136. package/dist/src/mcp-tools/capture-resources.js +289 -0
  137. package/dist/src/mcp-tools/capture-resources.js.map +1 -0
  138. package/dist/src/mcp-tools/index.d.ts +3 -0
  139. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  140. package/dist/src/mcp-tools/index.js +6 -0
  141. package/dist/src/mcp-tools/index.js.map +1 -1
  142. package/dist/src/mcp-tools/knowledge-tools.d.ts.map +1 -1
  143. package/dist/src/mcp-tools/knowledge-tools.js +9 -1
  144. package/dist/src/mcp-tools/knowledge-tools.js.map +1 -1
  145. package/dist/src/mcp-tools/resource-router.d.ts +86 -0
  146. package/dist/src/mcp-tools/resource-router.d.ts.map +1 -0
  147. package/dist/src/mcp-tools/resource-router.js +181 -0
  148. package/dist/src/mcp-tools/resource-router.js.map +1 -0
  149. package/dist/src/memory/memory-bridge.js +1 -1
  150. package/dist/src/memory/memory-bridge.js.map +1 -1
  151. package/dist/src/orgrt/agent-runner.d.ts +38 -0
  152. package/dist/src/orgrt/agent-runner.d.ts.map +1 -1
  153. package/dist/src/orgrt/agent-runner.js +47 -0
  154. package/dist/src/orgrt/agent-runner.js.map +1 -1
  155. package/dist/src/orgrt/checkpoint.d.ts +12 -1
  156. package/dist/src/orgrt/checkpoint.d.ts.map +1 -1
  157. package/dist/src/orgrt/checkpoint.js +15 -6
  158. package/dist/src/orgrt/checkpoint.js.map +1 -1
  159. package/dist/src/orgrt/completion-gate.d.ts +74 -0
  160. package/dist/src/orgrt/completion-gate.d.ts.map +1 -1
  161. package/dist/src/orgrt/completion-gate.js +45 -0
  162. package/dist/src/orgrt/completion-gate.js.map +1 -1
  163. package/dist/src/orgrt/cost-tier.d.ts +144 -0
  164. package/dist/src/orgrt/cost-tier.d.ts.map +1 -0
  165. package/dist/src/orgrt/cost-tier.js +183 -0
  166. package/dist/src/orgrt/cost-tier.js.map +1 -0
  167. package/dist/src/orgrt/cross-org.d.ts.map +1 -1
  168. package/dist/src/orgrt/cross-org.js +14 -0
  169. package/dist/src/orgrt/cross-org.js.map +1 -1
  170. package/dist/src/orgrt/daemon.d.ts +12 -1
  171. package/dist/src/orgrt/daemon.d.ts.map +1 -1
  172. package/dist/src/orgrt/daemon.js +216 -49
  173. package/dist/src/orgrt/daemon.js.map +1 -1
  174. package/dist/src/orgrt/decisions.d.ts +14 -2
  175. package/dist/src/orgrt/decisions.d.ts.map +1 -1
  176. package/dist/src/orgrt/decisions.js +216 -10
  177. package/dist/src/orgrt/decisions.js.map +1 -1
  178. package/dist/src/orgrt/file-roots.d.ts +7 -0
  179. package/dist/src/orgrt/file-roots.d.ts.map +1 -1
  180. package/dist/src/orgrt/file-roots.js +38 -1
  181. package/dist/src/orgrt/file-roots.js.map +1 -1
  182. package/dist/src/orgrt/forwarder.d.ts.map +1 -1
  183. package/dist/src/orgrt/forwarder.js +16 -2
  184. package/dist/src/orgrt/forwarder.js.map +1 -1
  185. package/dist/src/orgrt/idle-deadline.d.ts +74 -1
  186. package/dist/src/orgrt/idle-deadline.d.ts.map +1 -1
  187. package/dist/src/orgrt/idle-deadline.js +52 -2
  188. package/dist/src/orgrt/idle-deadline.js.map +1 -1
  189. package/dist/src/orgrt/loadouts.d.ts +38 -0
  190. package/dist/src/orgrt/loadouts.d.ts.map +1 -0
  191. package/dist/src/orgrt/loadouts.js +129 -0
  192. package/dist/src/orgrt/loadouts.js.map +1 -0
  193. package/dist/src/orgrt/mailbox.d.ts +17 -1
  194. package/dist/src/orgrt/mailbox.d.ts.map +1 -1
  195. package/dist/src/orgrt/mailbox.js +50 -3
  196. package/dist/src/orgrt/mailbox.js.map +1 -1
  197. package/dist/src/orgrt/policy.d.ts +42 -2
  198. package/dist/src/orgrt/policy.d.ts.map +1 -1
  199. package/dist/src/orgrt/policy.js +53 -8
  200. package/dist/src/orgrt/policy.js.map +1 -1
  201. package/dist/src/orgrt/prompt-vars.d.ts +11 -0
  202. package/dist/src/orgrt/prompt-vars.d.ts.map +1 -0
  203. package/dist/src/orgrt/prompt-vars.js +49 -0
  204. package/dist/src/orgrt/prompt-vars.js.map +1 -0
  205. package/dist/src/orgrt/questions.d.ts +22 -1
  206. package/dist/src/orgrt/questions.d.ts.map +1 -1
  207. package/dist/src/orgrt/questions.js +24 -3
  208. package/dist/src/orgrt/questions.js.map +1 -1
  209. package/dist/src/orgrt/review-packet.d.ts +29 -0
  210. package/dist/src/orgrt/review-packet.d.ts.map +1 -0
  211. package/dist/src/orgrt/review-packet.js +67 -0
  212. package/dist/src/orgrt/review-packet.js.map +1 -0
  213. package/dist/src/orgrt/role-sandbox.d.ts.map +1 -1
  214. package/dist/src/orgrt/role-sandbox.js +16 -3
  215. package/dist/src/orgrt/role-sandbox.js.map +1 -1
  216. package/dist/src/orgrt/session-ledger.d.ts +68 -0
  217. package/dist/src/orgrt/session-ledger.d.ts.map +1 -0
  218. package/dist/src/orgrt/session-ledger.js +128 -0
  219. package/dist/src/orgrt/session-ledger.js.map +1 -0
  220. package/dist/src/orgrt/session.d.ts +45 -5
  221. package/dist/src/orgrt/session.d.ts.map +1 -1
  222. package/dist/src/orgrt/session.js +397 -27
  223. package/dist/src/orgrt/session.js.map +1 -1
  224. package/dist/src/orgrt/task-dag.d.ts +34 -1
  225. package/dist/src/orgrt/task-dag.d.ts.map +1 -1
  226. package/dist/src/orgrt/task-dag.js +58 -7
  227. package/dist/src/orgrt/task-dag.js.map +1 -1
  228. package/dist/src/orgrt/tool-spill.d.ts +94 -0
  229. package/dist/src/orgrt/tool-spill.d.ts.map +1 -0
  230. package/dist/src/orgrt/tool-spill.js +180 -0
  231. package/dist/src/orgrt/tool-spill.js.map +1 -0
  232. package/dist/src/orgrt/types.d.ts +138 -0
  233. package/dist/src/orgrt/types.d.ts.map +1 -1
  234. package/dist/src/orgrt/types.js +148 -0
  235. package/dist/src/orgrt/types.js.map +1 -1
  236. package/dist/src/platform-adapters/renderers/mcp.d.ts +20 -2
  237. package/dist/src/platform-adapters/renderers/mcp.d.ts.map +1 -1
  238. package/dist/src/platform-adapters/renderers/mcp.js +25 -5
  239. package/dist/src/platform-adapters/renderers/mcp.js.map +1 -1
  240. package/dist/src/protocol-capabilities.d.ts +2 -1
  241. package/dist/src/protocol-capabilities.d.ts.map +1 -1
  242. package/dist/src/protocol-capabilities.js +2 -1
  243. package/dist/src/protocol-capabilities.js.map +1 -1
  244. package/dist/src/ui/dashboard.html +450 -164
  245. package/dist/src/ui/org-hil.mjs +276 -0
  246. package/dist/src/ui/org-runtime.mjs +350 -0
  247. package/dist/src/ui/orgs.html +15 -4
  248. package/dist/src/ui/routes-org.mjs +290 -541
  249. package/dist/src/ui/server.mjs +25 -5
  250. package/dist/tsconfig.tsbuildinfo +1 -1
  251. package/package.json +9 -8
@@ -0,0 +1,154 @@
1
+ /**
2
+ * MHTML / MHT container parsing (RCL-01).
3
+ *
4
+ * An MHTML archive is an RFC 2557 `multipart/related` MIME message: the page
5
+ * plus every subresource it referenced, each part carrying its own transfer
6
+ * encoding and charset. Chrome's `Page.captureSnapshot` writes them
7
+ * quoted-printable; Internet Explorer and most "save as web archive" tools
8
+ * write base64.
9
+ *
10
+ * Only the ROOT frame is prose. Picking the first `text/html` part instead is
11
+ * how an ad iframe ends up indexed as the article, so the root is resolved
12
+ * from the container's `Snapshot-Content-Location` / `Content-Location` when
13
+ * one is present.
14
+ *
15
+ * Read the file as `latin1` (or pass a Buffer): byte fidelity matters before
16
+ * transfer decoding, and the real charset is only known per part.
17
+ *
18
+ * @module v1/cli/knowledge/mhtml
19
+ */
20
+ /** Decode a quoted-printable body to bytes: soft line breaks vanish, `=XX`
21
+ * becomes one byte, everything else is its own latin1 byte. */
22
+ export function decodeQuotedPrintable(input) {
23
+ const out = [];
24
+ for (let i = 0; i < input.length; i++) {
25
+ const ch = input[i];
26
+ if (ch !== '=') {
27
+ out.push(input.charCodeAt(i) & 0xff);
28
+ continue;
29
+ }
30
+ if (input[i + 1] === '\r' && input[i + 2] === '\n') {
31
+ i += 2; // soft line break
32
+ continue;
33
+ }
34
+ if (input[i + 1] === '\n') {
35
+ i += 1; // soft line break, LF-only file
36
+ continue;
37
+ }
38
+ const hex = input.slice(i + 1, i + 3);
39
+ if (/^[0-9a-fA-F]{2}$/.test(hex)) {
40
+ out.push(Number.parseInt(hex, 16));
41
+ i += 2;
42
+ continue;
43
+ }
44
+ out.push(0x3d); // a lone `=` — keep it
45
+ }
46
+ return Buffer.from(out);
47
+ }
48
+ function decodeBody(raw, encoding) {
49
+ switch (encoding) {
50
+ case 'quoted-printable':
51
+ return decodeQuotedPrintable(raw);
52
+ case 'base64':
53
+ return Buffer.from(raw.replace(/\s+/g, ''), 'base64');
54
+ default:
55
+ return Buffer.from(raw, 'latin1');
56
+ }
57
+ }
58
+ /** Decode bytes under a MIME charset, falling back to UTF-8 (then latin1)
59
+ * rather than failing an ingest over an exotic label. */
60
+ export function decodeCharset(bytes, charset) {
61
+ const label = (charset ?? 'utf-8').trim().toLowerCase();
62
+ for (const candidate of [label, 'utf-8']) {
63
+ try {
64
+ return new TextDecoder(candidate, { fatal: false }).decode(bytes);
65
+ }
66
+ catch {
67
+ /* unknown label — try the next */
68
+ }
69
+ }
70
+ return bytes.toString('latin1');
71
+ }
72
+ /** Split a MIME chunk into unfolded headers and the body after the first
73
+ * blank line. Continuation lines (leading space/tab) join their header. */
74
+ function splitHeaders(chunk) {
75
+ const sep = /\r?\n\r?\n/.exec(chunk);
76
+ const headerText = sep ? chunk.slice(0, sep.index) : chunk;
77
+ const body = sep ? chunk.slice(sep.index + sep[0].length) : '';
78
+ const headers = {};
79
+ let current = '';
80
+ for (const rawLine of headerText.split(/\r?\n/)) {
81
+ if (/^[ \t]/.test(rawLine) && current) {
82
+ headers[current] += ` ${rawLine.trim()}`;
83
+ continue;
84
+ }
85
+ const colon = rawLine.indexOf(':');
86
+ if (colon === -1)
87
+ continue;
88
+ current = rawLine.slice(0, colon).trim().toLowerCase();
89
+ headers[current] = rawLine.slice(colon + 1).trim();
90
+ }
91
+ return { headers, body };
92
+ }
93
+ function param(headerValue, name) {
94
+ if (!headerValue)
95
+ return null;
96
+ const m = new RegExp(`${name}\\s*=\\s*("[^"]*"|'[^']*'|[^;\\s]+)`, 'i').exec(headerValue);
97
+ return m ? m[1].replace(/^["']|["']$/g, '') : null;
98
+ }
99
+ function toPart(chunk) {
100
+ const { headers, body } = splitHeaders(chunk);
101
+ const contentType = (headers['content-type'] ?? 'text/html').split(';')[0].trim().toLowerCase();
102
+ const encoding = (headers['content-transfer-encoding'] ?? 'binary').trim().toLowerCase();
103
+ return {
104
+ contentType,
105
+ charset: param(headers['content-type'], 'charset')?.toLowerCase() ?? null,
106
+ location: headers['content-location'] ?? null,
107
+ encoding,
108
+ body: decodeBody(body, encoding),
109
+ headers,
110
+ };
111
+ }
112
+ /**
113
+ * Parse an MHTML container. Anything that is not a well-formed multipart
114
+ * message is treated as a single part, so a plain `.html` file renamed to
115
+ * `.mhtml` still ingests.
116
+ */
117
+ export function parseMhtml(raw) {
118
+ const text = typeof raw === 'string' ? raw : raw.toString('latin1');
119
+ const { headers, body } = splitHeaders(text);
120
+ const snapshotLocation = headers['snapshot-content-location'] ?? headers['content-location'] ?? null;
121
+ const subject = headers.subject ?? null;
122
+ const boundary = param(headers['content-type'], 'boundary');
123
+ let parts;
124
+ if (!boundary) {
125
+ // No boundary: either a single-part message or a bare HTML file. Feeding
126
+ // the whole document through `toPart` covers both — an HTML file has no
127
+ // header block, so everything lands in the body.
128
+ parts = [toPart(/^[a-z-]+:/i.test(text) ? text : `Content-Type: text/html\r\n\r\n${text}`)];
129
+ }
130
+ else {
131
+ const delimiter = `--${boundary}`;
132
+ parts = body
133
+ .split(delimiter)
134
+ .slice(1) // text before the first delimiter is the MIME preamble
135
+ .filter((chunk) => !chunk.startsWith('--')) // the closing `--boundary--`
136
+ .map((chunk) => chunk.replace(/^\r?\n/, ''))
137
+ .map(toPart);
138
+ }
139
+ const html = parts.filter((p) => p.contentType === 'text/html' || p.contentType === 'application/xhtml+xml');
140
+ const root = (snapshotLocation ? html.find((p) => p.location === snapshotLocation) : undefined) ??
141
+ html[0] ??
142
+ null;
143
+ return { root, parts, snapshotLocation, subject };
144
+ }
145
+ /** Parse a container and return its root frame decoded to a string. */
146
+ export function mhtmlToHtml(raw) {
147
+ const parsed = parseMhtml(raw);
148
+ return {
149
+ html: parsed.root ? decodeCharset(parsed.root.body, parsed.root.charset) : '',
150
+ location: parsed.root?.location ?? parsed.snapshotLocation,
151
+ subject: parsed.subject,
152
+ };
153
+ }
154
+ //# sourceMappingURL=mhtml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mhtml.js","sourceRoot":"","sources":["../../../src/knowledge/mhtml.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Related-at-save (RCL-03) — "3 things in your brain relate to this".
3
+ *
4
+ * The extension calls this on EVERY capture, while the user watches. That
5
+ * single fact sets every design decision here:
6
+ *
7
+ * - it never throws. An empty store, a missing embedder, a URL that was
8
+ * never captured — all of them are an empty list, because a capture must
9
+ * not fail because the sidebar had nothing to say;
10
+ * - it is bounded. The vector pass races a timeout (default 2s) and the
11
+ * cheap signals — same URL, same site, read straight off the metadata log —
12
+ * are computed first, so a slow or absent embedder degrades to a fast
13
+ * answer instead of no answer;
14
+ * - the target itself is never in its own results, by path AND by canonical
15
+ * URL, so a re-capture does not "relate" to the version it replaced.
16
+ *
17
+ * Three signals, combined: exact URL (the same page, captured before),
18
+ * same site, and vector similarity over the chunks already embedded.
19
+ *
20
+ * @module v1/cli/knowledge/related
21
+ */
22
+ export type RelatedReason = 'same-url' | 'same-site' | 'similar';
23
+ export interface RelatedDocument {
24
+ filePath: string;
25
+ scope: string;
26
+ title: string;
27
+ url?: string;
28
+ site?: string;
29
+ /** 0–1. Exact-URL matches lead; similarity and site agreement compound. */
30
+ score: number;
31
+ reasons: RelatedReason[];
32
+ capturedAt?: string;
33
+ /** The most similar passage, when similarity is one of the reasons. */
34
+ excerpt?: string;
35
+ chunkIndex?: number;
36
+ anchor?: string;
37
+ }
38
+ export interface RelatedOptions {
39
+ rootDir?: string;
40
+ scope?: string;
41
+ limit?: number;
42
+ /** Budget for the vector pass. The metadata signals ignore it: they are
43
+ * synchronous file reads. */
44
+ timeoutMs?: number;
45
+ minScore?: number;
46
+ store?: 'project' | 'global' | 'all';
47
+ /** Text to compare against, when the caller already has it (a capture that
48
+ * has not been ingested yet). */
49
+ text?: string;
50
+ }
51
+ /**
52
+ * The documents most related to `target` (a URL, a docId, or an indexed path).
53
+ *
54
+ * Safe to call on every capture: bounded, and empty rather than thrown on any
55
+ * failure.
56
+ */
57
+ export declare function relatedDocuments(target: string, opts?: RelatedOptions): Promise<RelatedDocument[]>;
58
+ //# sourceMappingURL=related.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"related.d.ts","sourceRoot":"","sources":["../../../src/knowledge/related.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AASH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;kCAC8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;IACrC;sCACkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAwFD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,eAAe,EAAE,CAAC,CAmH5B"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Related-at-save (RCL-03) — "3 things in your brain relate to this".
3
+ *
4
+ * The extension calls this on EVERY capture, while the user watches. That
5
+ * single fact sets every design decision here:
6
+ *
7
+ * - it never throws. An empty store, a missing embedder, a URL that was
8
+ * never captured — all of them are an empty list, because a capture must
9
+ * not fail because the sidebar had nothing to say;
10
+ * - it is bounded. The vector pass races a timeout (default 2s) and the
11
+ * cheap signals — same URL, same site, read straight off the metadata log —
12
+ * are computed first, so a slow or absent embedder degrades to a fast
13
+ * answer instead of no answer;
14
+ * - the target itself is never in its own results, by path AND by canonical
15
+ * URL, so a re-capture does not "relate" to the version it replaced.
16
+ *
17
+ * Three signals, combined: exact URL (the same page, captured before),
18
+ * same site, and vector similarity over the chunks already embedded.
19
+ *
20
+ * @module v1/cli/knowledge/related
21
+ */
22
+ import * as fs from 'node:fs';
23
+ import * as path from 'node:path';
24
+ import { captureIdentityUrl, readCaptureProvenance } from './capture-envelope.js';
25
+ import { passageQuote } from './citation.js';
26
+ import { documentCapturedAt, documentSite } from './library.js';
27
+ const DEFAULT_LIMIT = 3;
28
+ const DEFAULT_TIMEOUT_MS = 2000;
29
+ const DEFAULT_MIN_SCORE = 0.25;
30
+ const QUERY_CHARS = 1200;
31
+ const SAME_URL_SCORE = 1;
32
+ /** Ceiling for everything that is not literally the same page. */
33
+ const NEAR_SCORE_CAP = 0.95;
34
+ const SAME_SITE_SCORE = 0.45;
35
+ const BOTH_SIGNALS_BONUS = 0.1;
36
+ const EXCERPT_CHARS = 200;
37
+ function stripFragment(url) {
38
+ const hash = url.indexOf('#');
39
+ return (hash === -1 ? url : url.slice(0, hash)).trim();
40
+ }
41
+ function looksLikeUrl(target) {
42
+ return /^https?:\/\//i.test(target.trim());
43
+ }
44
+ function hostOf(url) {
45
+ if (!url)
46
+ return undefined;
47
+ try {
48
+ return new URL(url).hostname.toLowerCase().replace(/^www\./, '');
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ }
54
+ /** Words a URL alone gives us — the fallback query when there is no text. */
55
+ function urlWords(url) {
56
+ try {
57
+ const u = new URL(url);
58
+ return [u.hostname.replace(/^www\./, ''), ...u.pathname.split(/[/\-_.]+/)]
59
+ .filter((w) => w && !/^\d+$/.test(w) && w.length > 1)
60
+ .join(' ');
61
+ }
62
+ catch {
63
+ return url;
64
+ }
65
+ }
66
+ async function queryText(record, target, provided) {
67
+ if (provided?.trim())
68
+ return provided.slice(0, QUERY_CHARS);
69
+ const parts = [];
70
+ if (record?.provenance?.title)
71
+ parts.push(record.provenance.title);
72
+ if (record && fs.existsSync(record.filePath)) {
73
+ try {
74
+ const { extractText } = await import('../capabilities/cap-documents.js');
75
+ const stat = fs.statSync(record.filePath);
76
+ const text = await extractText({
77
+ path: record.filePath,
78
+ absolutePath: record.filePath,
79
+ extension: path.extname(record.filePath).toLowerCase(),
80
+ size: stat.size,
81
+ modified: stat.mtime,
82
+ created: stat.birthtime,
83
+ });
84
+ if (text)
85
+ parts.push(text.slice(0, QUERY_CHARS));
86
+ }
87
+ catch {
88
+ /* fall through to the URL words */
89
+ }
90
+ }
91
+ if (!parts.length)
92
+ parts.push(urlWords(looksLikeUrl(target) ? target : path.basename(target)));
93
+ return parts.join('\n').slice(0, QUERY_CHARS);
94
+ }
95
+ /** Resolve the vector pass or give up on time — whichever comes first. */
96
+ function withTimeout(work, ms, fallback) {
97
+ return new Promise((resolve) => {
98
+ const timer = setTimeout(() => resolve(fallback), ms);
99
+ work
100
+ .then((value) => {
101
+ clearTimeout(timer);
102
+ resolve(value);
103
+ })
104
+ .catch(() => {
105
+ clearTimeout(timer);
106
+ resolve(fallback);
107
+ });
108
+ });
109
+ }
110
+ /**
111
+ * The documents most related to `target` (a URL, a docId, or an indexed path).
112
+ *
113
+ * Safe to call on every capture: bounded, and empty rather than thrown on any
114
+ * failure.
115
+ */
116
+ export async function relatedDocuments(target, opts = {}) {
117
+ try {
118
+ const pipeline = await import('./document-pipeline.js');
119
+ const scope = opts.scope;
120
+ const rootDir = opts.rootDir ?? pipeline.getKnowledgeRoot(scope ?? 'shared');
121
+ const limit = opts.limit ?? DEFAULT_LIMIT;
122
+ const self = pipeline.findDocumentRecord(rootDir, target, scope);
123
+ // A capture that has been written but not yet ingested is the case this
124
+ // story exists for: the extension has the directory on disk and wants to
125
+ // know what it relates to BEFORE the ingest finishes. Its identity comes
126
+ // from the envelope's own meta.json.
127
+ const pending = !self && !looksLikeUrl(target) ? readCaptureProvenance(target) : null;
128
+ const selfUrl = self?.canonicalUrl ??
129
+ (looksLikeUrl(target) ? stripFragment(target) : captureIdentityUrl(pending));
130
+ const selfSite = hostOf(selfUrl);
131
+ const selfPaths = new Set([self?.filePath, looksLikeUrl(target) ? undefined : path.resolve(target)].filter((p) => !!p));
132
+ // Excluding by URL is right when the target IS that page — an indexed
133
+ // document, or a caller asking about the URL itself. It is wrong for a
134
+ // capture still sitting in the inbox: there the earlier capture of the
135
+ // same page is the single most related thing in the brain.
136
+ const excludeByUrl = !!self || looksLikeUrl(target);
137
+ const isSelf = (doc) => selfPaths.has(doc.filePath) || (excludeByUrl && !!selfUrl && doc.canonicalUrl === selfUrl);
138
+ const docs = pipeline.listDocuments(rootDir, scope).filter((d) => !isSelf(d));
139
+ const byPath = new Map(docs.map((d) => [d.filePath, d]));
140
+ const scored = new Map();
141
+ const put = (doc, reason, score, extra = {}) => {
142
+ const existing = scored.get(doc.filePath);
143
+ if (existing) {
144
+ if (!existing.reasons.includes(reason))
145
+ existing.reasons.push(reason);
146
+ // Agreeing signals compound, but only the same page itself reaches 1 —
147
+ // otherwise a same-site neighbour that also embeds well would tie with
148
+ // the earlier capture of this very page and win on sort order.
149
+ const cap = existing.reasons.includes('same-url') ? SAME_URL_SCORE : NEAR_SCORE_CAP;
150
+ existing.score = Math.min(cap, Math.max(existing.score, score) + BOTH_SIGNALS_BONUS);
151
+ Object.assign(existing, extra);
152
+ return;
153
+ }
154
+ const url = doc.canonicalUrl ?? doc.provenance?.canonicalUrl ?? doc.provenance?.url;
155
+ scored.set(doc.filePath, {
156
+ filePath: doc.filePath,
157
+ scope: doc.scope,
158
+ title: doc.provenance?.title ?? path.basename(doc.filePath),
159
+ ...(url ? { url } : {}),
160
+ ...(documentSite(doc) ? { site: documentSite(doc) } : {}),
161
+ score,
162
+ reasons: [reason],
163
+ ...(documentCapturedAt(doc) ? { capturedAt: documentCapturedAt(doc) } : {}),
164
+ ...extra,
165
+ });
166
+ };
167
+ // 1 + 2 — the cheap signals, straight off the metadata log.
168
+ for (const doc of docs) {
169
+ const url = doc.canonicalUrl ?? doc.provenance?.canonicalUrl;
170
+ if (selfUrl && url && stripFragment(url) === selfUrl)
171
+ put(doc, 'same-url', SAME_URL_SCORE);
172
+ else if (selfSite && documentSite(doc) === selfSite)
173
+ put(doc, 'same-site', SAME_SITE_SCORE);
174
+ }
175
+ // 3 — similarity, bounded. A store with no embeddings comes back empty
176
+ // here and the metadata signals still stand.
177
+ // For a not-yet-ingested capture the text to compare against is the file
178
+ // the caller pointed at, read through the same extraction the ingest uses.
179
+ const queryRecord = self ?? (pending ? { filePath: path.resolve(target), provenance: pending } : undefined);
180
+ const query = await queryText(queryRecord, target, opts.text);
181
+ if (query.trim()) {
182
+ const hits = await withTimeout(pipeline.searchKnowledge(query, {
183
+ scope: scope ?? 'shared',
184
+ rootDir,
185
+ limit: Math.max(limit * 6, 12),
186
+ minScore: opts.minScore ?? DEFAULT_MIN_SCORE,
187
+ store: opts.store ?? 'all',
188
+ }), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS, []);
189
+ const best = new Map();
190
+ for (const hit of hits) {
191
+ if (!hit.filePath || selfPaths.has(hit.filePath))
192
+ continue;
193
+ const current = best.get(hit.filePath);
194
+ if (!current || hit.similarity > current.similarity)
195
+ best.set(hit.filePath, hit);
196
+ }
197
+ for (const [filePath, hit] of best) {
198
+ const doc = byPath.get(filePath);
199
+ if (!doc || isSelf(doc))
200
+ continue;
201
+ put(doc, 'similar', Math.min(1, hit.similarity), {
202
+ excerpt: passageQuote(hit.text ?? '', EXCERPT_CHARS),
203
+ chunkIndex: hit.chunkIndex,
204
+ ...(hit.anchor ? { anchor: hit.anchor } : {}),
205
+ });
206
+ }
207
+ }
208
+ return [...scored.values()]
209
+ .sort((a, b) => b.score - a.score || (b.capturedAt ?? '').localeCompare(a.capturedAt ?? ''))
210
+ .slice(0, limit);
211
+ }
212
+ catch (err) {
213
+ // Called on every capture: an unexpected failure is "nothing related",
214
+ // never a broken save. It is still a bug when it is one of ours, so it is
215
+ // visible under DEBUG rather than only in an empty sidebar.
216
+ if (process.env.DEBUG || process.env.MONOMIND_DEBUG) {
217
+ console.error(`[relatedDocuments] ${target}:`, err);
218
+ }
219
+ return [];
220
+ }
221
+ }
222
+ //# sourceMappingURL=related.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"related.js","sourceRoot":"","sources":["../../../src/knowledge/related.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Semantic diff between two versions of a captured page (RCL-08).
3
+ *
4
+ * A character diff of a re-captured page is noise: the byline date moved, an
5
+ * ad slot re-rendered, the markdown re-wrapped. What a watcher actually wants
6
+ * to be told is WHICH SECTIONS changed and roughly how much — "Pricing gained
7
+ * a paragraph, Changelog got three new entries, everything else is the same".
8
+ *
9
+ * So the unit here is the markdown section (heading + body), keyed by its
10
+ * full heading path, and the measure is words and sentences rather than
11
+ * characters. Sentences are also what gives a change its sample line: the
12
+ * first sentence that is in the new version and was not in the old one is the
13
+ * most useful single thing to print.
14
+ *
15
+ * No dependency, no LLM: heading paths and sentence sets are enough to say
16
+ * what moved.
17
+ *
18
+ * @module v1/cli/knowledge/section-diff
19
+ */
20
+ export interface Section {
21
+ /** Full heading path, e.g. `Pricing > Enterprise`. `(intro)` for text
22
+ * before the first heading. */
23
+ key: string;
24
+ heading: string;
25
+ level: number;
26
+ body: string;
27
+ }
28
+ export interface SectionChange {
29
+ key: string;
30
+ addedWords: number;
31
+ removedWords: number;
32
+ /** First sentence present in the new version and absent from the old. */
33
+ sample?: string;
34
+ }
35
+ export interface SectionDiff {
36
+ added: string[];
37
+ removed: string[];
38
+ changed: SectionChange[];
39
+ unchanged: number;
40
+ /** One line, ready to print or to put in a notification. */
41
+ summary: string;
42
+ }
43
+ /** Split markdown into sections keyed by heading path. Duplicate paths get a
44
+ * `#2` suffix so two "Notes" sections never collapse into one. */
45
+ export declare function splitSections(text: string): Section[];
46
+ /** What changed between two versions of the same page. */
47
+ export declare function diffSections(oldText: string, newText: string): SectionDiff;
48
+ //# sourceMappingURL=section-diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"section-diff.d.ts","sourceRoot":"","sources":["../../../src/knowledge/section-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,OAAO;IACtB;oCACgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;CACjB;AAKD;mEACmE;AACnE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAmCrD;AA2DD,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,CAgC1E"}