explorbot 0.4.10 → 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/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 -0
- package/dist/src/ai/navigator.js +13 -4
- package/dist/src/ai/pilot.d.ts +4 -0
- package/dist/src/ai/pilot.js +57 -5
- 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/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/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/knowledge-tracker.js +1 -1
- 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 +8 -2
- 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/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/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/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 +15 -4
- package/src/ai/pilot.ts +58 -5
- package/src/ai/planner.ts +9 -6
- package/src/ai/provider.ts +51 -7
- 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/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/knowledge-tracker.ts +1 -1
- 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 +6 -1
- 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/strings.ts +7 -0
- package/src/utils/test-files.ts +5 -2
|
@@ -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
|
+
}
|