explorbot 0.4.9 → 0.5.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.
- package/README.md +4 -1
- package/bin/mdq.ts +18 -0
- package/boat/api-tester/src/ai/chief.ts +72 -0
- package/boat/api-tester/src/api-client.ts +37 -0
- package/boat/prima/src/prima.ts +41 -2
- package/dist/bin/mdq.js +19 -0
- package/dist/boat/api-tester/src/ai/chief.js +69 -0
- package/dist/boat/api-tester/src/api-client.js +26 -0
- package/dist/boat/prima/src/prima.js +42 -2
- package/dist/package.json +3 -2
- package/dist/src/action.js +4 -2
- package/dist/src/ai/agent.d.ts +3 -1
- package/dist/src/ai/judge-provider.d.ts +17 -0
- package/dist/src/ai/judge-provider.js +56 -0
- package/dist/src/ai/judge-tool.d.ts +2 -0
- package/dist/src/ai/judge-tool.js +33 -0
- package/dist/src/ai/judge.d.ts +28 -0
- package/dist/src/ai/judge.js +71 -0
- package/dist/src/ai/navigator.d.ts +3 -1
- package/dist/src/ai/navigator.js +24 -28
- package/dist/src/ai/pilot.d.ts +4 -0
- package/dist/src/ai/pilot.js +67 -9
- package/dist/src/ai/planner.js +9 -6
- package/dist/src/ai/provider.d.ts +4 -1
- package/dist/src/ai/provider.js +52 -7
- package/dist/src/ai/rerunner.js +7 -0
- package/dist/src/ai/researcher/deep-analysis.js +2 -2
- package/dist/src/ai/researcher/locators.js +2 -2
- package/dist/src/ai/researcher/pagination.js +1 -1
- package/dist/src/ai/researcher/research-result.js +2 -2
- package/dist/src/ai/researcher.js +1 -1
- package/dist/src/ai/task-agent.d.ts +2 -0
- package/dist/src/ai/task-agent.js +3 -1
- package/dist/src/ai/tester.js +19 -15
- package/dist/src/ai/tools.d.ts +4 -3
- package/dist/src/ai/tools.js +35 -7
- package/dist/src/api/request-result.js +2 -1
- package/dist/src/api/xhr-capture.js +2 -1
- package/dist/src/command-handler.d.ts +1 -0
- package/dist/src/command-handler.js +24 -3
- package/dist/src/commands/base-command.d.ts +5 -0
- package/dist/src/commands/base-command.js +3 -0
- package/dist/src/commands/explore-command.d.ts +2 -1
- package/dist/src/commands/explore-command.js +12 -1
- package/dist/src/commands/freesail-command.js +8 -2
- package/dist/src/commands/init-command.js +1 -1
- package/dist/src/commands/navigate-command.d.ts +2 -1
- package/dist/src/commands/navigate-command.js +6 -0
- package/dist/src/commands/plan-load-command.d.ts +2 -1
- package/dist/src/commands/plan-load-command.js +4 -0
- package/dist/src/commands/plans-command.d.ts +3 -9
- package/dist/src/commands/plans-command.js +11 -21
- package/dist/src/commands/rerun-command.d.ts +2 -1
- package/dist/src/commands/rerun-command.js +5 -1
- package/dist/src/commands/research-command.d.ts +2 -1
- package/dist/src/commands/research-command.js +6 -0
- package/dist/src/commands/test-command.d.ts +2 -1
- package/dist/src/commands/test-command.js +4 -1
- package/dist/src/components/Autocomplete.js +26 -12
- package/dist/src/components/InputReadline.js +10 -1
- package/dist/src/config.d.ts +6 -0
- package/dist/src/experience-tracker.js +4 -3
- package/dist/src/explorbot.d.ts +3 -0
- package/dist/src/explorbot.js +8 -0
- package/dist/src/explorer.js +2 -3
- package/dist/src/knowledge-tracker.js +1 -1
- package/dist/src/reporter.js +8 -4
- package/dist/src/state-manager.d.ts +2 -0
- package/dist/src/state-manager.js +16 -0
- package/dist/src/test-plan.d.ts +11 -0
- package/dist/src/test-plan.js +54 -2
- package/dist/src/utils/aria-ref.js +1 -1
- package/dist/src/utils/logger.js +9 -3
- package/dist/src/utils/markdown-query.d.ts +1 -48
- package/dist/src/utils/markdown-query.js +1 -444
- package/dist/src/utils/mdq/cli.d.ts +6 -0
- package/dist/src/utils/mdq/cli.js +122 -0
- package/dist/src/utils/mdq/edit.d.ts +24 -0
- package/dist/src/utils/mdq/edit.js +147 -0
- package/dist/src/utils/mdq/query.d.ts +118 -0
- package/dist/src/utils/mdq/query.js +451 -0
- package/dist/src/utils/step-analyzer.d.ts +3 -0
- package/dist/src/utils/step-analyzer.js +7 -0
- package/dist/src/utils/strings.d.ts +1 -0
- package/dist/src/utils/strings.js +7 -0
- package/dist/src/utils/test-files.d.ts +1 -0
- package/dist/src/utils/test-files.js +5 -2
- package/dist/src/utils/url-matcher.d.ts +1 -0
- package/dist/src/utils/url-matcher.js +7 -0
- package/docs/api-testing/planning.md +1 -1
- package/docs/superpowers/plans/2026-09-15-mdq-package.md +130 -94
- package/docs/superpowers/specs/2026-09-18-judge-decision-model-design.md +79 -0
- package/package.json +3 -2
- package/src/action.ts +4 -2
- package/src/ai/agent.ts +3 -1
- package/src/ai/judge-provider.ts +62 -0
- package/src/ai/judge-tool.ts +35 -0
- package/src/ai/judge.ts +75 -0
- package/src/ai/navigator.ts +24 -27
- package/src/ai/pilot.ts +68 -9
- package/src/ai/planner.ts +9 -6
- package/src/ai/provider.ts +51 -7
- package/src/ai/rerunner.ts +4 -0
- package/src/ai/researcher/deep-analysis.ts +2 -2
- package/src/ai/researcher/locators.ts +2 -2
- package/src/ai/researcher/pagination.ts +1 -1
- package/src/ai/researcher/research-result.ts +2 -2
- package/src/ai/researcher.ts +1 -1
- package/src/ai/task-agent.ts +4 -1
- package/src/ai/tester.ts +19 -16
- package/src/ai/tools.ts +42 -7
- package/src/api/request-result.ts +2 -1
- package/src/api/xhr-capture.ts +2 -1
- package/src/command-handler.ts +28 -3
- package/src/commands/base-command.ts +9 -0
- package/src/commands/explore-command.ts +15 -2
- package/src/commands/freesail-command.ts +9 -2
- package/src/commands/init-command.ts +1 -1
- package/src/commands/navigate-command.ts +8 -1
- package/src/commands/plan-load-command.ts +6 -1
- package/src/commands/plans-command.ts +13 -29
- package/src/commands/rerun-command.ts +7 -2
- package/src/commands/research-command.ts +8 -1
- package/src/commands/test-command.ts +6 -2
- package/src/components/Autocomplete.tsx +39 -10
- package/src/components/InputReadline.tsx +10 -1
- package/src/config.ts +1 -0
- package/src/experience-tracker.ts +4 -3
- package/src/explorbot.ts +8 -0
- package/src/explorer.ts +2 -2
- package/src/knowledge-tracker.ts +1 -1
- package/src/reporter.ts +8 -4
- package/src/state-manager.ts +16 -0
- package/src/test-plan.ts +67 -2
- package/src/utils/aria-ref.ts +1 -1
- package/src/utils/logger.ts +7 -2
- package/src/utils/markdown-query.ts +1 -519
- package/src/utils/mdq/cli.ts +118 -0
- package/src/utils/mdq/edit.ts +158 -0
- package/src/utils/mdq/query.ts +556 -0
- package/src/utils/step-analyzer.ts +8 -0
- package/src/utils/strings.ts +7 -0
- package/src/utils/test-files.ts +5 -2
- package/src/utils/url-matcher.ts +7 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { MdqError, mdq } from "./query.js";
|
|
4
|
+
const EDIT_FLAGS = ['remove', 'replace', 'insertBefore', 'insertAfter', 'prepend', 'append', 'addRow', 'addItem', 'set'];
|
|
5
|
+
export async function runMdq(argv, readStdin) {
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name('mdq')
|
|
9
|
+
.description('query and edit markdown')
|
|
10
|
+
.argument('[selector]', 'markdown selector')
|
|
11
|
+
.argument('[file]', 'file to read; stdin when omitted')
|
|
12
|
+
.option('-j, --json', 'output rows as JSON')
|
|
13
|
+
.option('-c, --count', 'print the number of matches')
|
|
14
|
+
.option('-t, --text', 'print unwrapped text')
|
|
15
|
+
.option('--frontmatter', 'print frontmatter as JSON')
|
|
16
|
+
.option('-i, --in-place', 'write the result back to the file')
|
|
17
|
+
.option('--remove', 'delete matched blocks')
|
|
18
|
+
.option('--replace <markdown>', 'replace matched blocks')
|
|
19
|
+
.option('--insert-before <markdown>', 'insert before each match')
|
|
20
|
+
.option('--insert-after <markdown>', 'insert after each match')
|
|
21
|
+
.option('--prepend <markdown>', 'insert at the start of each match')
|
|
22
|
+
.option('--append <markdown>', 'insert at the end of each match')
|
|
23
|
+
.option('--add-row <json>', 'append a table row')
|
|
24
|
+
.option('--add-item <text>', 'append a list item')
|
|
25
|
+
.option('--set <key=value>', 'set an entry; omit the value to delete it')
|
|
26
|
+
.exitOverride()
|
|
27
|
+
.configureOutput({ writeOut: () => { }, writeErr: () => { } });
|
|
28
|
+
try {
|
|
29
|
+
program.parse(argv, { from: 'user' });
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
return { output: String(error.message), code: 2 };
|
|
33
|
+
}
|
|
34
|
+
const options = program.opts();
|
|
35
|
+
let [selector, file] = program.args;
|
|
36
|
+
if (options.frontmatter && selector && !file) {
|
|
37
|
+
file = selector;
|
|
38
|
+
selector = '';
|
|
39
|
+
}
|
|
40
|
+
let source = '';
|
|
41
|
+
if (!file)
|
|
42
|
+
source = await readStdin();
|
|
43
|
+
if (file) {
|
|
44
|
+
try {
|
|
45
|
+
source = readFileSync(file, 'utf8');
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { output: `Cannot read ${file}`, code: 2 };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
return await apply(mdq(source), selector, options, file);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error instanceof MdqError)
|
|
56
|
+
return { output: error.message, code: 2 };
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function apply(doc, selector, options, file) {
|
|
61
|
+
if (options.frontmatter)
|
|
62
|
+
return { output: `${JSON.stringify(doc.frontmatter(), null, 2)}\n`, code: 0 };
|
|
63
|
+
if (!selector)
|
|
64
|
+
return { output: 'A selector is required', code: 2 };
|
|
65
|
+
const chosen = EDIT_FLAGS.filter((flag) => options[flag] !== undefined);
|
|
66
|
+
if (chosen.length > 1)
|
|
67
|
+
return { output: 'Only one edit at a time', code: 2 };
|
|
68
|
+
const selection = doc.query(selector);
|
|
69
|
+
if (chosen.length === 0)
|
|
70
|
+
return read(selection, options);
|
|
71
|
+
if (!selection.exists())
|
|
72
|
+
return finish(String(doc), 1, options, file);
|
|
73
|
+
return finish(String(edit(selection, chosen[0], options)), 0, options, file);
|
|
74
|
+
}
|
|
75
|
+
function read(selection, options) {
|
|
76
|
+
let code = 1;
|
|
77
|
+
if (selection.exists())
|
|
78
|
+
code = 0;
|
|
79
|
+
if (options.count)
|
|
80
|
+
return { output: `${selection.count()}\n`, code: 0 };
|
|
81
|
+
if (options.json)
|
|
82
|
+
return { output: `${JSON.stringify(selection.rows(), null, 2)}\n`, code };
|
|
83
|
+
if (options.text)
|
|
84
|
+
return {
|
|
85
|
+
output: selection
|
|
86
|
+
.nodes()
|
|
87
|
+
.map((node) => node.text)
|
|
88
|
+
.join('\n\n'),
|
|
89
|
+
code,
|
|
90
|
+
};
|
|
91
|
+
return { output: selection.text(), code };
|
|
92
|
+
}
|
|
93
|
+
function edit(selection, flag, options) {
|
|
94
|
+
if (flag === 'remove')
|
|
95
|
+
return selection.remove();
|
|
96
|
+
if (flag === 'replace')
|
|
97
|
+
return selection.replace(options.replace);
|
|
98
|
+
if (flag === 'insertBefore')
|
|
99
|
+
return selection.insertBefore(options.insertBefore);
|
|
100
|
+
if (flag === 'insertAfter')
|
|
101
|
+
return selection.insertAfter(options.insertAfter);
|
|
102
|
+
if (flag === 'prepend')
|
|
103
|
+
return selection.prepend(options.prepend);
|
|
104
|
+
if (flag === 'append')
|
|
105
|
+
return selection.append(options.append);
|
|
106
|
+
if (flag === 'addItem')
|
|
107
|
+
return selection.addItem(options.addItem);
|
|
108
|
+
if (flag === 'addRow')
|
|
109
|
+
return selection.addRow(JSON.parse(options.addRow));
|
|
110
|
+
const separator = options.set.indexOf('=');
|
|
111
|
+
if (separator < 0)
|
|
112
|
+
return selection.setEntry(options.set, null);
|
|
113
|
+
return selection.setEntry(options.set.slice(0, separator), options.set.slice(separator + 1) || null);
|
|
114
|
+
}
|
|
115
|
+
function finish(output, code, options, file) {
|
|
116
|
+
if (!options.inPlace)
|
|
117
|
+
return { output, code };
|
|
118
|
+
if (!file)
|
|
119
|
+
return { output: '--in-place needs a file', code: 2 };
|
|
120
|
+
writeFileSync(file, output);
|
|
121
|
+
return { output: '', code };
|
|
122
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { MatchedRange } from './query.js';
|
|
2
|
+
export declare class MarkdownEditor {
|
|
3
|
+
source: string;
|
|
4
|
+
constructor(source: string);
|
|
5
|
+
static splitFrontmatter(source: string): FrontmatterSplit;
|
|
6
|
+
static blockEnd(range: MatchedRange): number;
|
|
7
|
+
static table(headers: string[], rows: string[][], align: (string | null)[]): string;
|
|
8
|
+
static item(listRaw: string, text: string): string;
|
|
9
|
+
static entries(text: string, key: string, value: string | null, isBlockquote: boolean): string;
|
|
10
|
+
remove(ranges: MatchedRange[]): string;
|
|
11
|
+
replace(ranges: MatchedRange[], render: (range: MatchedRange, index: number) => string): string;
|
|
12
|
+
insert(offsets: number[], markdown: string): string;
|
|
13
|
+
frontmatter(): Record<string, unknown>;
|
|
14
|
+
setFrontmatter(key: string, value: unknown): string;
|
|
15
|
+
static spliceIn(source: string, offset: number, markdown: string): string;
|
|
16
|
+
static dashes(alignment: string | null, width: number): string;
|
|
17
|
+
static entryKey(line: string): string | null;
|
|
18
|
+
dedupe(ranges: MatchedRange[]): MatchedRange[];
|
|
19
|
+
}
|
|
20
|
+
export interface FrontmatterSplit {
|
|
21
|
+
raw: string;
|
|
22
|
+
body: string;
|
|
23
|
+
offset: number;
|
|
24
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import YAML from 'yaml';
|
|
2
|
+
export class MarkdownEditor {
|
|
3
|
+
source;
|
|
4
|
+
constructor(source) {
|
|
5
|
+
this.source = source;
|
|
6
|
+
}
|
|
7
|
+
static splitFrontmatter(source) {
|
|
8
|
+
if (!source.startsWith('---'))
|
|
9
|
+
return { raw: '', body: source, offset: 0 };
|
|
10
|
+
const match = source.match(/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/);
|
|
11
|
+
if (!match)
|
|
12
|
+
return { raw: '', body: source, offset: 0 };
|
|
13
|
+
return { raw: match[1], body: source.slice(match[0].length), offset: match[0].length };
|
|
14
|
+
}
|
|
15
|
+
static blockEnd(range) {
|
|
16
|
+
if (range.trailing)
|
|
17
|
+
return range.trailing.start + range.trailing.length;
|
|
18
|
+
return range.start + range.length;
|
|
19
|
+
}
|
|
20
|
+
static table(headers, rows, align) {
|
|
21
|
+
const widths = headers.map((header, index) => Math.max(header.length, 3, ...rows.map((row) => (row[index] || '').length)));
|
|
22
|
+
const line = (cells) => `| ${cells.map((cell, index) => (cell || '').padEnd(widths[index])).join(' | ')} |`;
|
|
23
|
+
const divider = `| ${widths.map((width, index) => MarkdownEditor.dashes(align[index], width)).join(' | ')} |`;
|
|
24
|
+
return `${[line(headers), divider, ...rows.map(line)].join('\n')}\n`;
|
|
25
|
+
}
|
|
26
|
+
static item(listRaw, text) {
|
|
27
|
+
const lines = listRaw.split('\n').filter((line) => line.trim());
|
|
28
|
+
const last = lines[lines.length - 1] || '- x';
|
|
29
|
+
const ordered = last.match(/^(\s*)(\d+)([.)])\s/);
|
|
30
|
+
if (ordered)
|
|
31
|
+
return `${ordered[1]}${Number.parseInt(ordered[2], 10) + 1}${ordered[3]} ${text}`;
|
|
32
|
+
const bullet = last.match(/^(\s*)([-*+])\s/);
|
|
33
|
+
if (!bullet)
|
|
34
|
+
return `- ${text}`;
|
|
35
|
+
return `${bullet[1]}${bullet[2]} ${text}`;
|
|
36
|
+
}
|
|
37
|
+
static entries(text, key, value, isBlockquote) {
|
|
38
|
+
const lines = text
|
|
39
|
+
.split('\n')
|
|
40
|
+
.map((line) => line.trim())
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
const index = lines.findIndex((line) => MarkdownEditor.entryKey(line) === key.toLowerCase());
|
|
43
|
+
if (index < 0 && value)
|
|
44
|
+
lines.push(`${key}: ${value}`);
|
|
45
|
+
if (index >= 0 && value)
|
|
46
|
+
lines[index] = `${key}: ${value}`;
|
|
47
|
+
if (index >= 0 && !value)
|
|
48
|
+
lines.splice(index, 1);
|
|
49
|
+
if (!isBlockquote)
|
|
50
|
+
return lines.join('\n');
|
|
51
|
+
return lines.map((line) => `> ${line}`).join('\n');
|
|
52
|
+
}
|
|
53
|
+
remove(ranges) {
|
|
54
|
+
const ordered = this.dedupe(ranges);
|
|
55
|
+
let result = this.source;
|
|
56
|
+
for (let i = ordered.length - 1; i >= 0; i--) {
|
|
57
|
+
const range = ordered[i];
|
|
58
|
+
const head = result.slice(0, range.start);
|
|
59
|
+
const tail = result.slice(MarkdownEditor.blockEnd(range));
|
|
60
|
+
if (tail) {
|
|
61
|
+
result = head + tail;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!head) {
|
|
65
|
+
result = '';
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
result = `${head.trimEnd()}\n`;
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
replace(ranges, render) {
|
|
73
|
+
const ordered = this.dedupe(ranges);
|
|
74
|
+
const rendered = ordered.map(render);
|
|
75
|
+
let result = this.source;
|
|
76
|
+
for (let i = ordered.length - 1; i >= 0; i--) {
|
|
77
|
+
const range = ordered[i];
|
|
78
|
+
result = result.slice(0, range.start) + rendered[i] + result.slice(range.start + range.length);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
insert(offsets, markdown) {
|
|
83
|
+
const ordered = [...offsets].sort((a, b) => a - b);
|
|
84
|
+
let result = this.source;
|
|
85
|
+
for (let i = ordered.length - 1; i >= 0; i--) {
|
|
86
|
+
result = MarkdownEditor.spliceIn(result, ordered[i], markdown);
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
frontmatter() {
|
|
91
|
+
const { raw } = MarkdownEditor.splitFrontmatter(this.source);
|
|
92
|
+
if (!raw)
|
|
93
|
+
return {};
|
|
94
|
+
return YAML.parseDocument(raw).toJS() || {};
|
|
95
|
+
}
|
|
96
|
+
setFrontmatter(key, value) {
|
|
97
|
+
const { raw, body, offset } = MarkdownEditor.splitFrontmatter(this.source);
|
|
98
|
+
let document = new YAML.Document({});
|
|
99
|
+
if (raw)
|
|
100
|
+
document = YAML.parseDocument(raw);
|
|
101
|
+
if (value === null)
|
|
102
|
+
document.delete(key);
|
|
103
|
+
if (value !== null)
|
|
104
|
+
document.set(key, value);
|
|
105
|
+
const rendered = document.toString().trimEnd();
|
|
106
|
+
if (!offset)
|
|
107
|
+
return `---\n${rendered}\n---\n\n${this.source}`;
|
|
108
|
+
return `---\n${rendered}\n---\n${body}`;
|
|
109
|
+
}
|
|
110
|
+
static spliceIn(source, offset, markdown) {
|
|
111
|
+
const block = `${markdown.trimEnd()}\n`;
|
|
112
|
+
const before = source.slice(0, offset).replace(/\n+$/, '');
|
|
113
|
+
const after = source.slice(offset).replace(/^\n+/, '');
|
|
114
|
+
if (!before)
|
|
115
|
+
return `${block}\n${after}`;
|
|
116
|
+
if (!after)
|
|
117
|
+
return `${before}\n\n${block}`;
|
|
118
|
+
return `${before}\n\n${block}\n${after}`;
|
|
119
|
+
}
|
|
120
|
+
static dashes(alignment, width) {
|
|
121
|
+
if (alignment === 'center')
|
|
122
|
+
return `:${'-'.repeat(Math.max(width - 2, 1))}:`;
|
|
123
|
+
if (alignment === 'left')
|
|
124
|
+
return `:${'-'.repeat(Math.max(width - 1, 1))}`;
|
|
125
|
+
if (alignment === 'right')
|
|
126
|
+
return `${'-'.repeat(Math.max(width - 1, 1))}:`;
|
|
127
|
+
return '-'.repeat(width);
|
|
128
|
+
}
|
|
129
|
+
static entryKey(line) {
|
|
130
|
+
const separator = line.indexOf(':');
|
|
131
|
+
if (separator < 1)
|
|
132
|
+
return null;
|
|
133
|
+
return line.slice(0, separator).trim().toLowerCase();
|
|
134
|
+
}
|
|
135
|
+
dedupe(ranges) {
|
|
136
|
+
const sorted = [...ranges].sort((a, b) => a.start - b.start);
|
|
137
|
+
const kept = [];
|
|
138
|
+
let lastEnd = -1;
|
|
139
|
+
for (const range of sorted) {
|
|
140
|
+
if (range.start < lastEnd)
|
|
141
|
+
continue;
|
|
142
|
+
kept.push(range);
|
|
143
|
+
lastEnd = range.start + range.length;
|
|
144
|
+
}
|
|
145
|
+
return kept;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { type Token } from 'marked';
|
|
2
|
+
import { MarkdownEditor } from './edit.js';
|
|
3
|
+
export { MarkdownEditor };
|
|
4
|
+
export declare class MarkdownQuery {
|
|
5
|
+
source: string;
|
|
6
|
+
matches: MatchedRange[];
|
|
7
|
+
constructor(source: string, matches?: MatchedRange[]);
|
|
8
|
+
query(selector: string, matcher?: Matcher): MarkdownQuery;
|
|
9
|
+
section(matcher?: Matcher, options?: SelectorOptions): MarkdownQuery;
|
|
10
|
+
heading(matcher?: Matcher, options?: SelectorOptions): MarkdownQuery;
|
|
11
|
+
paragraph(matcher?: Matcher): MarkdownQuery;
|
|
12
|
+
table(matcher?: Matcher): MarkdownQuery;
|
|
13
|
+
list(matcher?: Matcher): MarkdownQuery;
|
|
14
|
+
item(matcher?: Matcher): MarkdownQuery;
|
|
15
|
+
code(matcher?: Matcher): MarkdownQuery;
|
|
16
|
+
blockquote(matcher?: Matcher): MarkdownQuery;
|
|
17
|
+
comment(matcher?: Matcher): MarkdownQuery;
|
|
18
|
+
html(matcher?: Matcher): MarkdownQuery;
|
|
19
|
+
hr(): MarkdownQuery;
|
|
20
|
+
text(): string;
|
|
21
|
+
nodes(): NodeInfo[];
|
|
22
|
+
rows(): Record<string, string>[];
|
|
23
|
+
entries(): Record<string, string>;
|
|
24
|
+
frontmatter(): Record<string, unknown>;
|
|
25
|
+
count(): number;
|
|
26
|
+
exists(): boolean;
|
|
27
|
+
at(index: number): MarkdownQuery;
|
|
28
|
+
slice(from?: number, to?: number): MarkdownQuery;
|
|
29
|
+
first(): MarkdownQuery;
|
|
30
|
+
last(): MarkdownQuery;
|
|
31
|
+
each(): MarkdownQuery[];
|
|
32
|
+
preceding(): MarkdownQuery;
|
|
33
|
+
following(): MarkdownQuery;
|
|
34
|
+
replace(content: Markdown): MarkdownQuery;
|
|
35
|
+
replaceEach(replacer: (match: MarkdownQuery, index: number) => Markdown): MarkdownQuery;
|
|
36
|
+
remove(): MarkdownQuery;
|
|
37
|
+
insertBefore(markdown: Markdown): MarkdownQuery;
|
|
38
|
+
insertAfter(markdown: Markdown): MarkdownQuery;
|
|
39
|
+
prepend(markdown: Markdown): MarkdownQuery;
|
|
40
|
+
append(markdown: Markdown): MarkdownQuery;
|
|
41
|
+
addRow(row: Record<string, string>): MarkdownQuery;
|
|
42
|
+
addItem(text: string): MarkdownQuery;
|
|
43
|
+
setEntry(key: string, value: string | null): MarkdownQuery;
|
|
44
|
+
setFrontmatter(key: string, value: unknown): MarkdownQuery;
|
|
45
|
+
toString(): string;
|
|
46
|
+
valueOf(): string;
|
|
47
|
+
/** Compatibility aliases. Each delegates to the canonical name above; prefer those. */
|
|
48
|
+
get(): string;
|
|
49
|
+
meta(): NodeInfo[];
|
|
50
|
+
toJson(): Record<string, string>[];
|
|
51
|
+
keyValue(): Record<string, string>;
|
|
52
|
+
setKeyValue(key: string, value: string | null): MarkdownQuery;
|
|
53
|
+
before(): MarkdownQuery;
|
|
54
|
+
after(): MarkdownQuery;
|
|
55
|
+
editor(): MarkdownEditor;
|
|
56
|
+
run(candidates: MatchedRange[], segments: QuerySegment[]): MatchedRange[];
|
|
57
|
+
sectionsOf(candidates: MatchedRange[], segment: QuerySegment): MatchedRange[];
|
|
58
|
+
itemsOf(candidates: MatchedRange[]): MatchedRange[];
|
|
59
|
+
expandSections(matches: MatchedRange[]): MatchedRange[];
|
|
60
|
+
narrow(matches: MatchedRange[], segment: QuerySegment): MatchedRange[];
|
|
61
|
+
filterByText(matches: MatchedRange[], segment: QuerySegment): MatchedRange[];
|
|
62
|
+
matchText(text: string, matcher: TextMatcher): boolean;
|
|
63
|
+
valueMatcher(matcher: Matcher): TextMatcher;
|
|
64
|
+
tokenText(token: Token): string;
|
|
65
|
+
isComment(token: Token): boolean;
|
|
66
|
+
insertAll(offsetOf: (range: MatchedRange) => number, markdown: Markdown): MarkdownQuery;
|
|
67
|
+
containerStart(range: MatchedRange): number;
|
|
68
|
+
containerEnd(range: MatchedRange): number;
|
|
69
|
+
}
|
|
70
|
+
export declare class MdqError extends Error {
|
|
71
|
+
}
|
|
72
|
+
export declare class MdqSelectorError extends MdqError {
|
|
73
|
+
index: number;
|
|
74
|
+
constructor(message: string, index: number);
|
|
75
|
+
}
|
|
76
|
+
export declare class MdqOperationError extends MdqError {
|
|
77
|
+
constructor(message: string);
|
|
78
|
+
}
|
|
79
|
+
export declare function mdq(source: Markdown): MarkdownQuery;
|
|
80
|
+
export declare function parseQuery(input: string): QuerySegment[];
|
|
81
|
+
export declare function buildTokenIndex(source: string): MatchedRange[];
|
|
82
|
+
export type Markdown = string | MarkdownQuery;
|
|
83
|
+
export type Matcher = string | RegExp | ((text: string) => boolean);
|
|
84
|
+
export type SelectorType = 'comment' | 'html' | 'section' | 'section1' | 'section2' | 'section3' | 'section4' | 'section5' | 'section6' | 'table' | 'heading' | 'paragraph' | 'list' | 'item' | 'code' | 'blockquote' | 'hr' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
85
|
+
export interface SelectorOptions {
|
|
86
|
+
depth?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
87
|
+
}
|
|
88
|
+
export interface TextMatcher {
|
|
89
|
+
mode: 'exact' | 'contains' | 'regex' | 'predicate';
|
|
90
|
+
value: string;
|
|
91
|
+
negated: boolean;
|
|
92
|
+
flags?: string;
|
|
93
|
+
predicate?: (text: string) => boolean;
|
|
94
|
+
}
|
|
95
|
+
export interface QuerySegment {
|
|
96
|
+
selector: SelectorType;
|
|
97
|
+
textMatch?: TextMatcher;
|
|
98
|
+
index: number | null;
|
|
99
|
+
slice: {
|
|
100
|
+
from?: number;
|
|
101
|
+
to?: number;
|
|
102
|
+
} | null;
|
|
103
|
+
}
|
|
104
|
+
export interface MatchedRange {
|
|
105
|
+
token: Token;
|
|
106
|
+
start: number;
|
|
107
|
+
length: number;
|
|
108
|
+
trailing?: {
|
|
109
|
+
start: number;
|
|
110
|
+
length: number;
|
|
111
|
+
};
|
|
112
|
+
innerTokens?: MatchedRange[];
|
|
113
|
+
}
|
|
114
|
+
export interface NodeInfo {
|
|
115
|
+
type: string;
|
|
116
|
+
depth: number | null;
|
|
117
|
+
text: string;
|
|
118
|
+
}
|