@monoes/monomindcli 2.14.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 (124) 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/knowledge/capture-envelope.d.ts +80 -0
  26. package/dist/src/knowledge/capture-envelope.d.ts.map +1 -0
  27. package/dist/src/knowledge/capture-envelope.js +165 -0
  28. package/dist/src/knowledge/capture-envelope.js.map +1 -0
  29. package/dist/src/knowledge/capture-text.d.ts +23 -0
  30. package/dist/src/knowledge/capture-text.d.ts.map +1 -0
  31. package/dist/src/knowledge/capture-text.js +49 -0
  32. package/dist/src/knowledge/capture-text.js.map +1 -0
  33. package/dist/src/knowledge/citation.d.ts +121 -0
  34. package/dist/src/knowledge/citation.d.ts.map +1 -0
  35. package/dist/src/knowledge/citation.js +252 -0
  36. package/dist/src/knowledge/citation.js.map +1 -0
  37. package/dist/src/knowledge/document-pipeline.d.ts +60 -0
  38. package/dist/src/knowledge/document-pipeline.d.ts.map +1 -1
  39. package/dist/src/knowledge/document-pipeline.js +188 -4
  40. package/dist/src/knowledge/document-pipeline.js.map +1 -1
  41. package/dist/src/knowledge/html-extract.d.ts +43 -0
  42. package/dist/src/knowledge/html-extract.d.ts.map +1 -0
  43. package/dist/src/knowledge/html-extract.js +286 -0
  44. package/dist/src/knowledge/html-extract.js.map +1 -0
  45. package/dist/src/knowledge/html-tags.d.ts +30 -0
  46. package/dist/src/knowledge/html-tags.d.ts.map +1 -0
  47. package/dist/src/knowledge/html-tags.js +229 -0
  48. package/dist/src/knowledge/html-tags.js.map +1 -0
  49. package/dist/src/knowledge/library.d.ts +106 -0
  50. package/dist/src/knowledge/library.d.ts.map +1 -0
  51. package/dist/src/knowledge/library.js +195 -0
  52. package/dist/src/knowledge/library.js.map +1 -0
  53. package/dist/src/knowledge/mhtml.d.ts +60 -0
  54. package/dist/src/knowledge/mhtml.d.ts.map +1 -0
  55. package/dist/src/knowledge/mhtml.js +154 -0
  56. package/dist/src/knowledge/mhtml.js.map +1 -0
  57. package/dist/src/knowledge/related.d.ts +58 -0
  58. package/dist/src/knowledge/related.d.ts.map +1 -0
  59. package/dist/src/knowledge/related.js +222 -0
  60. package/dist/src/knowledge/related.js.map +1 -0
  61. package/dist/src/knowledge/section-diff.d.ts +48 -0
  62. package/dist/src/knowledge/section-diff.d.ts.map +1 -0
  63. package/dist/src/knowledge/section-diff.js +151 -0
  64. package/dist/src/knowledge/section-diff.js.map +1 -0
  65. package/dist/src/knowledge/watch.d.ts +92 -0
  66. package/dist/src/knowledge/watch.d.ts.map +1 -0
  67. package/dist/src/knowledge/watch.js +221 -0
  68. package/dist/src/knowledge/watch.js.map +1 -0
  69. package/dist/src/mcp-server.d.ts.map +1 -1
  70. package/dist/src/mcp-server.js +44 -87
  71. package/dist/src/mcp-server.js.map +1 -1
  72. package/dist/src/mcp-tools/browser-instrument-tools.d.ts +35 -0
  73. package/dist/src/mcp-tools/browser-instrument-tools.d.ts.map +1 -0
  74. package/dist/src/mcp-tools/browser-instrument-tools.js +359 -0
  75. package/dist/src/mcp-tools/browser-instrument-tools.js.map +1 -0
  76. package/dist/src/mcp-tools/browser-metrics.d.ts +42 -0
  77. package/dist/src/mcp-tools/browser-metrics.d.ts.map +1 -0
  78. package/dist/src/mcp-tools/browser-metrics.js +91 -0
  79. package/dist/src/mcp-tools/browser-metrics.js.map +1 -0
  80. package/dist/src/mcp-tools/browser-profile-tools.d.ts +16 -0
  81. package/dist/src/mcp-tools/browser-profile-tools.d.ts.map +1 -0
  82. package/dist/src/mcp-tools/browser-profile-tools.js +324 -0
  83. package/dist/src/mcp-tools/browser-profile-tools.js.map +1 -0
  84. package/dist/src/mcp-tools/browser-session.d.ts +68 -0
  85. package/dist/src/mcp-tools/browser-session.d.ts.map +1 -0
  86. package/dist/src/mcp-tools/browser-session.js +224 -0
  87. package/dist/src/mcp-tools/browser-session.js.map +1 -0
  88. package/dist/src/mcp-tools/browser-tools.d.ts.map +1 -1
  89. package/dist/src/mcp-tools/browser-tools.js +10 -176
  90. package/dist/src/mcp-tools/browser-tools.js.map +1 -1
  91. package/dist/src/mcp-tools/capture-resource-read.d.ts +115 -0
  92. package/dist/src/mcp-tools/capture-resource-read.d.ts.map +1 -0
  93. package/dist/src/mcp-tools/capture-resource-read.js +296 -0
  94. package/dist/src/mcp-tools/capture-resource-read.js.map +1 -0
  95. package/dist/src/mcp-tools/capture-resource-tools.d.ts +22 -0
  96. package/dist/src/mcp-tools/capture-resource-tools.d.ts.map +1 -0
  97. package/dist/src/mcp-tools/capture-resource-tools.js +182 -0
  98. package/dist/src/mcp-tools/capture-resource-tools.js.map +1 -0
  99. package/dist/src/mcp-tools/capture-resources.d.ts +142 -0
  100. package/dist/src/mcp-tools/capture-resources.d.ts.map +1 -0
  101. package/dist/src/mcp-tools/capture-resources.js +289 -0
  102. package/dist/src/mcp-tools/capture-resources.js.map +1 -0
  103. package/dist/src/mcp-tools/index.d.ts +3 -0
  104. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  105. package/dist/src/mcp-tools/index.js +6 -0
  106. package/dist/src/mcp-tools/index.js.map +1 -1
  107. package/dist/src/mcp-tools/knowledge-tools.d.ts.map +1 -1
  108. package/dist/src/mcp-tools/knowledge-tools.js +9 -1
  109. package/dist/src/mcp-tools/knowledge-tools.js.map +1 -1
  110. package/dist/src/mcp-tools/resource-router.d.ts +86 -0
  111. package/dist/src/mcp-tools/resource-router.d.ts.map +1 -0
  112. package/dist/src/mcp-tools/resource-router.js +181 -0
  113. package/dist/src/mcp-tools/resource-router.js.map +1 -0
  114. package/dist/src/memory/memory-bridge.js +1 -1
  115. package/dist/src/memory/memory-bridge.js.map +1 -1
  116. package/dist/src/orgrt/session-ledger.d.ts +7 -0
  117. package/dist/src/orgrt/session-ledger.d.ts.map +1 -1
  118. package/dist/src/orgrt/session-ledger.js +15 -0
  119. package/dist/src/orgrt/session-ledger.js.map +1 -1
  120. package/dist/src/orgrt/session.d.ts.map +1 -1
  121. package/dist/src/orgrt/session.js +21 -8
  122. package/dist/src/orgrt/session.js.map +1 -1
  123. package/dist/tsconfig.tsbuildinfo +1 -1
  124. package/package.json +6 -6
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Paragraph-level citation (RCL-10).
3
+ *
4
+ * A search hit already knows WHICH document it came from. A citation has to
5
+ * know WHERE IN IT: the character span of the chunk against the extracted
6
+ * text, an anchor that survives being written down, and a way back to the
7
+ * live page at that passage.
8
+ *
9
+ * Three pieces, deliberately separate:
10
+ *
11
+ * - `span:<start>-<end>` — a tag written next to every chunk at ingest, so a
12
+ * search result carries offsets without re-reading the document.
13
+ * - the ANCHOR `<hash12>#<start>-<end>` — offsets bound to the content hash
14
+ * of the exact version they were measured against. Quoting an offset range
15
+ * without the version it belongs to is how citations rot: the document is
16
+ * re-captured, the text shifts, and the quote silently points at other
17
+ * words. A stale anchor here is DETECTED (`stale: true`), never served as
18
+ * if it were still true.
19
+ * - a W3C text fragment (`#:~:text=`) — the link a human clicks, which lands
20
+ * on the sentence even though the site never had an id there.
21
+ *
22
+ * Everything above `resolveCitation` is pure and synchronous so the pipeline
23
+ * can use it while ingesting. `resolveCitation` reaches back into the
24
+ * document-pipeline through a DYNAMIC import: the pipeline imports this
25
+ * module statically, and a static import back would close the cycle.
26
+ *
27
+ * @module v1/cli/knowledge/citation
28
+ */
29
+ import type { CaptureProvenance } from './capture-envelope.js';
30
+ export declare const SPAN_TAG_PREFIX = "span:";
31
+ export interface ChunkSpan {
32
+ chunkIndex: number;
33
+ startChar: number;
34
+ endChar: number;
35
+ }
36
+ /** The tag written beside a chunk at ingest: `span:<start>-<end>`. */
37
+ export declare function spanTag(startChar: number, endChar: number): string;
38
+ /** Offsets from a chunk's tag list, or null when it predates span tags. */
39
+ export declare function parseSpanTag(tags: readonly string[] | undefined): {
40
+ startChar: number;
41
+ endChar: number;
42
+ } | null;
43
+ /** `<hash12>#<start>-<end>` — offsets bound to the version they were measured against. */
44
+ export declare function citationAnchor(contentHash: string, startChar: number, endChar: number): string;
45
+ export declare function parseCitationAnchor(anchor: string): {
46
+ hashPrefix: string;
47
+ startChar: number;
48
+ endChar: number;
49
+ } | null;
50
+ /** True when the anchor was cut against this document version. */
51
+ export declare function anchorMatchesHash(anchor: string, contentHash: string): boolean;
52
+ /**
53
+ * The prose of a passage: § context prefixes gone, markdown syntax gone,
54
+ * whitespace collapsed.
55
+ *
56
+ * `skipHeadings` drops heading lines as well, which is what a text fragment
57
+ * wants — a heading is usually its own element on the page, so starting a
58
+ * range there matches less reliably than starting at the paragraph. It falls
59
+ * back to including them when the passage is nothing but headings.
60
+ */
61
+ export declare function passageProse(passage: string, skipHeadings?: boolean): string;
62
+ /** The head of a passage as a quotable sentence or two — the words a reader
63
+ * would see on the page, not the markdown we stored. */
64
+ export declare function passageQuote(passage: string, maxChars?: number): string;
65
+ /**
66
+ * A W3C scroll-to-text fragment for this passage.
67
+ *
68
+ * Long passages use the `textStart,textEnd` form rather than the whole
69
+ * paragraph: the spec matches across the range, and a shorter pair survives
70
+ * the site re-flowing its markup between capture and click.
71
+ */
72
+ export declare function textFragment(passage: string): string;
73
+ /** The captured page's URL, pointed at the passage. Returns undefined when
74
+ * there is no URL — a citation into a local file has no link to give. */
75
+ export declare function citationUrl(url: string | undefined, passage: string): string | undefined;
76
+ export interface Citation {
77
+ /** `<scope>:<filePath>` — the pipeline's own document id. */
78
+ docId: string;
79
+ filePath: string;
80
+ scope: string;
81
+ chunkIndex: number;
82
+ startChar: number;
83
+ endChar: number;
84
+ /** Hash of the text the offsets were cut against, i.e. the file as it is NOW. */
85
+ contentHash: string;
86
+ anchor: string;
87
+ /** The cited text itself, straight out of the extracted document. */
88
+ passage: string;
89
+ /** The head of the passage, collapsed — what an agent puts in quotes. */
90
+ quote: string;
91
+ /** Present only when the anchor asked for a version this file no longer is. */
92
+ stale?: boolean;
93
+ version?: number;
94
+ title?: string;
95
+ url?: string;
96
+ /** `url` with a text fragment appended: the link that lands on the passage. */
97
+ citeUrl?: string;
98
+ capturedAt?: string;
99
+ source?: string;
100
+ provenance?: CaptureProvenance;
101
+ }
102
+ export interface ResolveCitationOptions {
103
+ rootDir?: string;
104
+ scope?: string;
105
+ /** Chunk index, as reported on a search hit. */
106
+ chunkIndex?: number;
107
+ /** An anchor string from a previous citation — resolved by offsets, and
108
+ * flagged `stale` when it names a version this document no longer is. */
109
+ anchor?: string;
110
+ }
111
+ /**
112
+ * Resolve a citation back to the passage it came from.
113
+ *
114
+ * `target` is whatever the caller has written down: an indexed file path, a
115
+ * capture's `canonicalUrl`, or the `<scope>:<path>` docId. Throws with a
116
+ * message meant for a human when the document is not indexed, its file is
117
+ * gone, or the chunk does not exist — a citation that cannot be checked must
118
+ * fail loudly, not resolve to an empty string.
119
+ */
120
+ export declare function resolveCitation(target: string, opts?: ResolveCitationOptions): Promise<Citation>;
121
+ //# sourceMappingURL=citation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"citation.d.ts","sourceRoot":"","sources":["../../../src/knowledge/citation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Paragraph-level citation (RCL-10).
3
+ *
4
+ * A search hit already knows WHICH document it came from. A citation has to
5
+ * know WHERE IN IT: the character span of the chunk against the extracted
6
+ * text, an anchor that survives being written down, and a way back to the
7
+ * live page at that passage.
8
+ *
9
+ * Three pieces, deliberately separate:
10
+ *
11
+ * - `span:<start>-<end>` — a tag written next to every chunk at ingest, so a
12
+ * search result carries offsets without re-reading the document.
13
+ * - the ANCHOR `<hash12>#<start>-<end>` — offsets bound to the content hash
14
+ * of the exact version they were measured against. Quoting an offset range
15
+ * without the version it belongs to is how citations rot: the document is
16
+ * re-captured, the text shifts, and the quote silently points at other
17
+ * words. A stale anchor here is DETECTED (`stale: true`), never served as
18
+ * if it were still true.
19
+ * - a W3C text fragment (`#:~:text=`) — the link a human clicks, which lands
20
+ * on the sentence even though the site never had an id there.
21
+ *
22
+ * Everything above `resolveCitation` is pure and synchronous so the pipeline
23
+ * can use it while ingesting. `resolveCitation` reaches back into the
24
+ * document-pipeline through a DYNAMIC import: the pipeline imports this
25
+ * module statically, and a static import back would close the cycle.
26
+ *
27
+ * @module v1/cli/knowledge/citation
28
+ */
29
+ import * as crypto from 'node:crypto';
30
+ import * as fs from 'node:fs';
31
+ import * as path from 'node:path';
32
+ export const SPAN_TAG_PREFIX = 'span:';
33
+ /** The tag written beside a chunk at ingest: `span:<start>-<end>`. */
34
+ export function spanTag(startChar, endChar) {
35
+ return `${SPAN_TAG_PREFIX}${startChar}-${endChar}`;
36
+ }
37
+ /** Offsets from a chunk's tag list, or null when it predates span tags. */
38
+ export function parseSpanTag(tags) {
39
+ for (const tag of tags ?? []) {
40
+ if (typeof tag !== 'string' || !tag.startsWith(SPAN_TAG_PREFIX))
41
+ continue;
42
+ const m = /^span:(\d+)-(\d+)$/.exec(tag);
43
+ if (!m)
44
+ continue;
45
+ const startChar = Number(m[1]);
46
+ const endChar = Number(m[2]);
47
+ if (endChar < startChar)
48
+ continue;
49
+ return { startChar, endChar };
50
+ }
51
+ return null;
52
+ }
53
+ const ANCHOR_HASH_CHARS = 12;
54
+ /** `<hash12>#<start>-<end>` — offsets bound to the version they were measured against. */
55
+ export function citationAnchor(contentHash, startChar, endChar) {
56
+ return `${(contentHash || '').slice(0, ANCHOR_HASH_CHARS)}#${startChar}-${endChar}`;
57
+ }
58
+ export function parseCitationAnchor(anchor) {
59
+ const m = /^([0-9a-f]{4,64})#(\d+)-(\d+)$/.exec((anchor || '').trim());
60
+ if (!m)
61
+ return null;
62
+ const startChar = Number(m[2]);
63
+ const endChar = Number(m[3]);
64
+ if (endChar < startChar)
65
+ return null;
66
+ return { hashPrefix: m[1], startChar, endChar };
67
+ }
68
+ /** True when the anchor was cut against this document version. */
69
+ export function anchorMatchesHash(anchor, contentHash) {
70
+ const parsed = parseCitationAnchor(anchor);
71
+ if (!parsed)
72
+ return false;
73
+ return (contentHash || '').startsWith(parsed.hashPrefix);
74
+ }
75
+ // ── Quotes and links ───────────────────────────────────────────────
76
+ const QUOTE_MAX_CHARS = 320;
77
+ const CONTEXT_PREFIX_RE = /^(§ [^\n]*\n)+/;
78
+ const HEADING_LINE_RE = /^\s{0,3}#{1,6}\s+/;
79
+ /**
80
+ * One line of markdown as the words a reader sees.
81
+ *
82
+ * This matters more than it looks: a text fragment built from `# Pricing ##
83
+ * Starter Nine dollars` matches NOTHING on the live page, because the page
84
+ * never contained a `#`. The link is only worth having if it is made of the
85
+ * prose the site actually renders.
86
+ */
87
+ function stripMarkdown(line) {
88
+ return line
89
+ .replace(HEADING_LINE_RE, '')
90
+ .replace(/^\s{0,3}>\s?/, '')
91
+ .replace(/^\s{0,3}([-*+]|\d+\.)\s+/, '')
92
+ .replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1')
93
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
94
+ .replace(/`{1,3}([^`]*)`{1,3}/g, '$1')
95
+ .replace(/(\*\*|__)(.*?)\1/g, '$2')
96
+ .replace(/(\*|_)(?=\S)(.*?)(?<=\S)\1/g, '$2')
97
+ .trim();
98
+ }
99
+ /**
100
+ * The prose of a passage: § context prefixes gone, markdown syntax gone,
101
+ * whitespace collapsed.
102
+ *
103
+ * `skipHeadings` drops heading lines as well, which is what a text fragment
104
+ * wants — a heading is usually its own element on the page, so starting a
105
+ * range there matches less reliably than starting at the paragraph. It falls
106
+ * back to including them when the passage is nothing but headings.
107
+ */
108
+ export function passageProse(passage, skipHeadings = false) {
109
+ const lines = String(passage ?? '')
110
+ .replace(CONTEXT_PREFIX_RE, '')
111
+ .split('\n');
112
+ const keep = (only) => lines
113
+ .filter((l) => !only || !HEADING_LINE_RE.test(l))
114
+ .map(stripMarkdown)
115
+ .filter(Boolean)
116
+ .join(' ')
117
+ .replace(/\s+/g, ' ')
118
+ .trim();
119
+ const body = keep(skipHeadings);
120
+ return body || keep(false);
121
+ }
122
+ /** The head of a passage as a quotable sentence or two — the words a reader
123
+ * would see on the page, not the markdown we stored. */
124
+ export function passageQuote(passage, maxChars = QUOTE_MAX_CHARS) {
125
+ const text = passageProse(passage);
126
+ if (text.length <= maxChars)
127
+ return text;
128
+ const window = text.slice(0, maxChars);
129
+ const stop = Math.max(window.lastIndexOf('. '), window.lastIndexOf('? '), window.lastIndexOf('! '));
130
+ return stop > maxChars * 0.4 ? window.slice(0, stop + 1) : `${window.trimEnd()}…`;
131
+ }
132
+ /** `,` and `-` are syntax inside a text fragment, so they cannot be left raw. */
133
+ function encodeFragmentPart(text) {
134
+ return encodeURIComponent(text).replace(/-/g, '%2D').replace(/,/g, '%2C');
135
+ }
136
+ const FRAGMENT_WORDS = 8;
137
+ /**
138
+ * A W3C scroll-to-text fragment for this passage.
139
+ *
140
+ * Long passages use the `textStart,textEnd` form rather than the whole
141
+ * paragraph: the spec matches across the range, and a shorter pair survives
142
+ * the site re-flowing its markup between capture and click.
143
+ */
144
+ export function textFragment(passage) {
145
+ const text = passageProse(passage, true).slice(0, 2000);
146
+ if (!text)
147
+ return '';
148
+ const words = text.split(' ').filter(Boolean);
149
+ if (words.length <= FRAGMENT_WORDS * 2)
150
+ return `:~:text=${encodeFragmentPart(text)}`;
151
+ const start = words.slice(0, FRAGMENT_WORDS).join(' ');
152
+ const end = words.slice(-FRAGMENT_WORDS).join(' ');
153
+ return `:~:text=${encodeFragmentPart(start)},${encodeFragmentPart(end)}`;
154
+ }
155
+ /** The captured page's URL, pointed at the passage. Returns undefined when
156
+ * there is no URL — a citation into a local file has no link to give. */
157
+ export function citationUrl(url, passage) {
158
+ if (!url)
159
+ return undefined;
160
+ const fragment = textFragment(passage);
161
+ if (!fragment)
162
+ return url;
163
+ const hash = url.indexOf('#');
164
+ const base = hash === -1 ? url : url.slice(0, hash);
165
+ return `${base}#${fragment}`;
166
+ }
167
+ function fileEntry(absolutePath) {
168
+ const stat = fs.statSync(absolutePath);
169
+ return {
170
+ path: absolutePath,
171
+ absolutePath,
172
+ extension: path.extname(absolutePath).toLowerCase(),
173
+ size: stat.size,
174
+ modified: stat.mtime,
175
+ created: stat.birthtime,
176
+ };
177
+ }
178
+ /**
179
+ * Resolve a citation back to the passage it came from.
180
+ *
181
+ * `target` is whatever the caller has written down: an indexed file path, a
182
+ * capture's `canonicalUrl`, or the `<scope>:<path>` docId. Throws with a
183
+ * message meant for a human when the document is not indexed, its file is
184
+ * gone, or the chunk does not exist — a citation that cannot be checked must
185
+ * fail loudly, not resolve to an empty string.
186
+ */
187
+ export async function resolveCitation(target, opts = {}) {
188
+ const pipeline = await import('./document-pipeline.js');
189
+ const { extractText } = await import('../capabilities/cap-documents.js');
190
+ const rootDir = opts.rootDir ?? pipeline.getKnowledgeRoot(opts.scope);
191
+ const scope = opts.scope;
192
+ const record = pipeline.findDocumentRecord(rootDir, target, scope);
193
+ if (!record)
194
+ throw new Error(`not indexed: ${target}`);
195
+ if (!fs.existsSync(record.filePath)) {
196
+ throw new Error(`source file is gone: ${record.filePath} (re-ingest the capture to cite it)`);
197
+ }
198
+ const text = await extractText(fileEntry(record.filePath));
199
+ if (!text)
200
+ throw new Error(`no text could be extracted from ${record.filePath}`);
201
+ const hash = crypto.createHash('sha256').update(text).digest('hex');
202
+ let startChar;
203
+ let endChar;
204
+ let chunkIndex;
205
+ const parsedAnchor = opts.anchor ? parseCitationAnchor(opts.anchor) : null;
206
+ if (opts.anchor && !parsedAnchor)
207
+ throw new Error(`malformed citation anchor: ${opts.anchor}`);
208
+ if (parsedAnchor) {
209
+ startChar = Math.min(parsedAnchor.startChar, text.length);
210
+ endChar = Math.min(parsedAnchor.endChar, text.length);
211
+ const spans = await pipeline.chunkSpans(text);
212
+ chunkIndex = spans.findIndex((s) => s.startChar === startChar);
213
+ if (chunkIndex === -1) {
214
+ chunkIndex = spans.findIndex((s) => s.startChar <= startChar && startChar < s.endChar);
215
+ }
216
+ }
217
+ else {
218
+ chunkIndex = opts.chunkIndex ?? 0;
219
+ const spans = await pipeline.chunkSpans(text);
220
+ const span = spans[chunkIndex];
221
+ if (!span) {
222
+ throw new Error(`chunk ${chunkIndex} is out of range: ${path.basename(record.filePath)} has ${spans.length} chunk(s)`);
223
+ }
224
+ startChar = span.startChar;
225
+ endChar = span.endChar;
226
+ }
227
+ const passage = text.slice(startChar, endChar);
228
+ const url = record.canonicalUrl ?? record.provenance?.canonicalUrl ?? record.provenance?.url;
229
+ const stale = parsedAnchor
230
+ ? !anchorMatchesHash(opts.anchor, hash)
231
+ : hash !== record.contentHash;
232
+ return {
233
+ docId: `${record.scope}:${record.filePath}`,
234
+ filePath: record.filePath,
235
+ scope: record.scope,
236
+ chunkIndex: chunkIndex === -1 ? 0 : chunkIndex,
237
+ startChar,
238
+ endChar,
239
+ contentHash: hash,
240
+ anchor: citationAnchor(hash, startChar, endChar),
241
+ passage,
242
+ quote: passageQuote(passage),
243
+ ...(stale ? { stale: true } : {}),
244
+ ...(record.version ? { version: record.version } : {}),
245
+ ...(record.provenance?.title ? { title: record.provenance.title } : {}),
246
+ ...(url ? { url, citeUrl: citationUrl(url, passage) } : {}),
247
+ ...(record.provenance?.capturedAt ? { capturedAt: record.provenance.capturedAt } : {}),
248
+ ...(record.provenance?.source ? { source: record.provenance.source } : {}),
249
+ ...(record.provenance ? { provenance: record.provenance } : {}),
250
+ };
251
+ }
252
+ //# sourceMappingURL=citation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"citation.js","sourceRoot":"","sources":["../../../src/knowledge/citation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AAQvC,sEAAsE;AACtE,MAAM,UAAU,OAAO,CAAC,SAAiB,EAAE,OAAe;IACxD,OAAO,GAAG,eAAe,GAAG,SAAS,IAAI,OAAO,EAAE,CAAC;AACrD,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,YAAY,CAAC,IAAmC;IAI9D,KAAK,MAAM,GAAG,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;QAC7B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC;YAAE,SAAS;QAC1E,MAAM,CAAC,GAAG,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,OAAO,GAAG,SAAS;YAAE,SAAS;QAClC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;IAChC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,0FAA0F;AAC1F,MAAM,UAAU,cAAc,CAAC,WAAmB,EAAE,SAAiB,EAAE,OAAe;IACpF,OAAO,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,IAAI,SAAS,IAAI,OAAO,EAAE,CAAC;AACtF,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,MAAc;IAEd,MAAM,CAAC,GAAG,gCAAgC,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACvE,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,OAAO,GAAG,SAAS;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAClD,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,WAAmB;IACnE,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAC3D,CAAC;AAED,sEAAsE;AAEtE,MAAM,eAAe,GAAG,GAAG,CAAC;AAE5B,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAC3C,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAE5C;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI;SACR,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC;SAC5B,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;SAC3B,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC;SACvC,OAAO,CAAC,yBAAyB,EAAE,IAAI,CAAC;SACxC,OAAO,CAAC,wBAAwB,EAAE,IAAI,CAAC;SACvC,OAAO,CAAC,sBAAsB,EAAE,IAAI,CAAC;SACrC,OAAO,CAAC,mBAAmB,EAAE,IAAI,CAAC;SAClC,OAAO,CAAC,6BAA6B,EAAE,IAAI,CAAC;SAC5C,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,YAAY,GAAG,KAAK;IAChE,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;SAChC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;SAC9B,KAAK,CAAC,IAAI,CAAC,CAAC;IACf,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,EAAE,CAC7B,KAAK;SACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SAChD,GAAG,CAAC,aAAa,CAAC;SAClB,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC;SACT,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;IACZ,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,OAAO,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED;yDACyD;AACzD,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,QAAQ,GAAG,eAAe;IACtE,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CACnB,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EACxB,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EACxB,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CACzB,CAAC;IACF,OAAO,IAAI,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC;AACpF,CAAC;AAED,iFAAiF;AACjF,SAAS,kBAAkB,CAAC,IAAY;IACtC,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,cAAc,GAAG,CAAC,CAAC;AAEzB;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,KAAK,CAAC,MAAM,IAAI,cAAc,GAAG,CAAC;QAAE,OAAO,WAAW,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;IACrF,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnD,OAAO,WAAW,kBAAkB,CAAC,KAAK,CAAC,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED;0EAC0E;AAC1E,MAAM,UAAU,WAAW,CAAC,GAAuB,EAAE,OAAe;IAClE,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,CAAC,QAAQ;QAAE,OAAO,GAAG,CAAC;IAC1B,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACpD,OAAO,GAAG,IAAI,IAAI,QAAQ,EAAE,CAAC;AAC/B,CAAC;AAyCD,SAAS,SAAS,CAAC,YAAoB;IACrC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IACvC,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,YAAY;QACZ,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE;QACnD,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,KAAK;QACpB,OAAO,EAAE,IAAI,CAAC,SAAS;KACxB,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,IAAI,GAA2B,EAAE;IAEjC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IACxD,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,kCAAkC,CAAC,CAAC;IACzE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAEzB,MAAM,MAAM,GAAG,QAAQ,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IACnE,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,MAAM,EAAE,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,QAAQ,qCAAqC,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3D,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjF,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEpE,IAAI,SAAiB,CAAC;IACtB,IAAI,OAAe,CAAC;IACpB,IAAI,UAAkB,CAAC;IACvB,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3E,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAE/F,IAAI,YAAY,EAAE,CAAC;QACjB,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1D,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC9C,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;QAC/D,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;YACtB,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,SAAS,IAAI,SAAS,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;SAAM,CAAC;QACN,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CACb,SAAS,UAAU,qBAAqB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,KAAK,CAAC,MAAM,WAAW,CACtG,CAAC;QACJ,CAAC;QACD,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAC3B,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IACzB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,UAAU,EAAE,YAAY,IAAI,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC;IAC7F,MAAM,KAAK,GAAG,YAAY;QACxB,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAgB,EAAE,IAAI,CAAC;QACjD,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,WAAW,CAAC;IAEhC,OAAO;QACL,KAAK,EAAE,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,QAAQ,EAAE;QAC3C,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,UAAU,EAAE,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;QAC9C,SAAS;QACT,OAAO;QACP,WAAW,EAAE,IAAI;QACjB,MAAM,EAAE,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC;QAChD,OAAO;QACP,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC;QAC5B,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtF,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChE,CAAC;AACJ,CAAC"}
@@ -4,6 +4,8 @@
4
4
  *
5
5
  * @module v1/cli/knowledge/document-pipeline
6
6
  */
7
+ import { type CaptureProvenance } from './capture-envelope.js';
8
+ import { type ChunkSpan } from './citation.js';
7
9
  export interface IngestResult {
8
10
  filePath: string;
9
11
  chunksIndexed: number;
@@ -15,6 +17,16 @@ export interface IngestResult {
15
17
  * none), and re-ingesting repairs it. Absent means the ingest was complete —
16
18
  * a caller must not read `chunksIndexed > 0` alone as success. */
17
19
  partial?: boolean;
20
+ /** RCL-06: the document was already indexed at this exact extracted-text
21
+ * hash, so nothing was written. Reported as "unchanged" rather than as a
22
+ * skip with an error, because nothing went wrong. */
23
+ unchanged?: boolean;
24
+ /** RCL-06: 1 for a first ingest, incremented for each stored revision. */
25
+ version?: number;
26
+ /** RCL-06: contentHash of the version this one replaced, when there was one. */
27
+ supersedes?: string;
28
+ /** RCL-07: provenance read from the capture envelope's `meta.json`. */
29
+ provenance?: CaptureProvenance;
18
30
  }
19
31
  export interface BatchIngestResult {
20
32
  filesProcessed: number;
@@ -35,6 +47,16 @@ export interface KnowledgeExcerpt {
35
47
  * re-ingested (its contentHash is no longer the file's current one). Only
36
48
  * ever set when the caller opted into `includeSuperseded`. */
37
49
  superseded?: boolean;
50
+ /** RCL-07: capture provenance, when the document came from a capture
51
+ * envelope — this is what lets a result cite the page it came from
52
+ * instead of a path under `~/.monomind/inbox`. */
53
+ provenance?: CaptureProvenance;
54
+ /** RCL-10: the chunk's character span against the extracted text. Absent
55
+ * for chunks stored before span tags existed — re-ingest to get them. */
56
+ startChar?: number;
57
+ endChar?: number;
58
+ /** RCL-10: `<hash12>#<start>-<end>`, resolvable with `monomind doc cite`. */
59
+ anchor?: string;
38
60
  }
39
61
  export interface DocumentMeta {
40
62
  filePath: string;
@@ -43,6 +65,16 @@ export interface DocumentMeta {
43
65
  indexedAt: string;
44
66
  scope: string;
45
67
  size: number;
68
+ /** RCL-06: identity URL for a captured page (`canonicalUrl`, else `url`,
69
+ * fragment stripped). Absent for ordinary files on disk. */
70
+ canonicalUrl?: string;
71
+ /** RCL-06: 1 for a first ingest, +1 per stored revision. */
72
+ version?: number;
73
+ /** RCL-06: contentHash of the version this record replaced. Walk it back
74
+ * through `listDocumentVersions` to reach the older ones. */
75
+ supersedes?: string;
76
+ /** RCL-07: the capture envelope's `meta.json`, normalized. */
77
+ provenance?: CaptureProvenance;
46
78
  }
47
79
  export declare function ingestDocument(filePath: string, scope?: string, rootDir?: string, _metadataCache?: DocumentMeta[]): Promise<IngestResult>;
48
80
  export declare function ingestDirectory(dirPath: string, scope?: string, opts?: {
@@ -85,6 +117,34 @@ export declare function searchKnowledge(query: string, opts?: {
85
117
  skipRerank?: boolean;
86
118
  }): Promise<KnowledgeExcerpt[]>;
87
119
  export declare function listDocuments(rootDir?: string, scope?: string): DocumentMeta[];
120
+ /**
121
+ * Every recorded version of one document, oldest first (RCL-06).
122
+ *
123
+ * `target` is either an indexed file path or a capture's `canonicalUrl`. Read
124
+ * from the append-only log rather than the last-wins view, which is how a
125
+ * superseded version stays addressable after its record has been replaced or
126
+ * tombstoned.
127
+ *
128
+ * Best-effort by design: `readMetadata` compacts the log once it passes 1MB
129
+ * and keeps only live records, so history older than a compaction is gone.
130
+ * The `supersedes` pointer on each record is the durable part.
131
+ */
132
+ export declare function listDocumentVersions(rootDir?: string, target?: string, scope?: string): DocumentMeta[];
133
+ /** The store root for a scope: the global brain for `global`, else the project. */
134
+ export declare function getKnowledgeRoot(scope?: string, rootDir?: string): string;
135
+ /** Chunk spans for a text, using the SAME chunker the ingest used — this is
136
+ * what makes a chunk index resolvable back to a character range without
137
+ * storing the text twice. */
138
+ export declare function chunkSpans(text: string): Promise<ChunkSpan[]>;
139
+ /**
140
+ * The indexed record for whatever a caller wrote down: a file path, a
141
+ * capture's `canonicalUrl` (fragment ignored), or the `<scope>:<path>` docId.
142
+ *
143
+ * Live records win. A superseded version is only reached through the
144
+ * append-only log, and only when nothing live matches — otherwise citing a
145
+ * re-captured page would resolve against the version it replaced.
146
+ */
147
+ export declare function findDocumentRecord(rootDir?: string, target?: string, scope?: string): DocumentMeta | undefined;
88
148
  export declare function removeDocument(filePath: string, scope?: string, rootDir?: string): Promise<void>;
89
149
  /**
90
150
  * True for macOS AppleDouble sidecars (`._name`).
@@ -1 +1 @@
1
- {"version":3,"file":"document-pipeline.d.ts","sourceRoot":"","sources":["../../../src/knowledge/document-pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"document-pipeline.d.ts","sourceRoot":"","sources":["../../../src/knowledge/document-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,OAAO,EACL,KAAK,iBAAiB,EAMvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,SAAS,EAAyC,MAAM,eAAe,CAAC;AAyRtF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;uEAGmE;IACnE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;0DAEsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,6FAA6F;IAC7F,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd;;mEAE+D;IAC/D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;uDAEmD;IACnD,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B;8EAC0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb;iEAC6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;kEAC8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAuJD,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAW,EAChB,OAAO,SAAmB,EAC1B,cAAc,CAAC,EAAE,YAAY,EAAE,GAC9B,OAAO,CAAC,YAAY,CAAC,CA+OvB;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,SAAW,EAChB,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GAC5F,OAAO,CAAC,iBAAiB,CAAC,CAyD5B;AAwBD,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAI9D;AAED;;;;;;;2EAO2E;AAC3E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EACjB,eAAe,UAAgB,GAC9B,OAAO,CAIT;AAQD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAGjF;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IACL,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;IACrC;sFACkF;IAClF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mDAAmD;IACnD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GACA,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAkH7B;AAID,wBAAgB,aAAa,CAAC,OAAO,SAAmB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CAGxF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,SAAmB,EAC1B,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,YAAY,EAAE,CAUhB;AAID,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,SAAW,EAAE,OAAO,SAAmB,GAAG,MAAM,CAErF;AAED;;8BAE8B;AAC9B,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAOnE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,SAAmB,EAC1B,MAAM,SAAK,EACX,KAAK,CAAC,EAAE,MAAM,GACb,YAAY,GAAG,SAAS,CA0B1B;AAED,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAW,EAChB,OAAO,SAAmB,GACzB,OAAO,CAAC,IAAI,CAAC,CAIf;AAID;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,cAAc,CAClC,OAAO,SAAmB,EAC1B,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACzC,OAAO,CAAC,eAAe,CAAC,CAwC1B;AAID,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,OAAO,SAAmB,EAC1B,KAAK,SAAW,GACf,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA8DlD;AAID,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,SAAW,EAChB,OAAO,SAAmB,GACzB,OAAO,CAAC,iBAAiB,CAAC,CA4B5B"}