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.
Files changed (128) hide show
  1. package/README.md +4 -1
  2. package/bin/mdq.ts +18 -0
  3. package/boat/api-tester/src/ai/chief.ts +72 -0
  4. package/boat/api-tester/src/api-client.ts +37 -0
  5. package/boat/prima/src/prima.ts +41 -2
  6. package/dist/bin/mdq.js +19 -0
  7. package/dist/boat/api-tester/src/ai/chief.js +69 -0
  8. package/dist/boat/api-tester/src/api-client.js +26 -0
  9. package/dist/boat/prima/src/prima.js +42 -2
  10. package/dist/package.json +3 -2
  11. package/dist/src/ai/agent.d.ts +3 -1
  12. package/dist/src/ai/judge-provider.d.ts +17 -0
  13. package/dist/src/ai/judge-provider.js +56 -0
  14. package/dist/src/ai/judge-tool.d.ts +2 -0
  15. package/dist/src/ai/judge-tool.js +33 -0
  16. package/dist/src/ai/judge.d.ts +28 -0
  17. package/dist/src/ai/judge.js +71 -0
  18. package/dist/src/ai/navigator.d.ts +3 -0
  19. package/dist/src/ai/navigator.js +13 -4
  20. package/dist/src/ai/pilot.d.ts +4 -0
  21. package/dist/src/ai/pilot.js +57 -5
  22. package/dist/src/ai/planner.js +9 -6
  23. package/dist/src/ai/provider.d.ts +4 -1
  24. package/dist/src/ai/provider.js +52 -7
  25. package/dist/src/ai/researcher/deep-analysis.js +2 -2
  26. package/dist/src/ai/researcher/locators.js +2 -2
  27. package/dist/src/ai/researcher/pagination.js +1 -1
  28. package/dist/src/ai/researcher/research-result.js +2 -2
  29. package/dist/src/ai/researcher.js +1 -1
  30. package/dist/src/ai/task-agent.d.ts +2 -0
  31. package/dist/src/ai/task-agent.js +3 -1
  32. package/dist/src/ai/tester.js +19 -15
  33. package/dist/src/ai/tools.d.ts +4 -3
  34. package/dist/src/ai/tools.js +35 -7
  35. package/dist/src/api/request-result.js +2 -1
  36. package/dist/src/command-handler.d.ts +1 -0
  37. package/dist/src/command-handler.js +24 -3
  38. package/dist/src/commands/base-command.d.ts +5 -0
  39. package/dist/src/commands/base-command.js +3 -0
  40. package/dist/src/commands/explore-command.d.ts +2 -1
  41. package/dist/src/commands/explore-command.js +12 -1
  42. package/dist/src/commands/freesail-command.js +8 -2
  43. package/dist/src/commands/init-command.js +1 -1
  44. package/dist/src/commands/navigate-command.d.ts +2 -1
  45. package/dist/src/commands/navigate-command.js +6 -0
  46. package/dist/src/commands/plan-load-command.d.ts +2 -1
  47. package/dist/src/commands/plan-load-command.js +4 -0
  48. package/dist/src/commands/plans-command.d.ts +3 -9
  49. package/dist/src/commands/plans-command.js +11 -21
  50. package/dist/src/commands/rerun-command.d.ts +2 -1
  51. package/dist/src/commands/rerun-command.js +5 -1
  52. package/dist/src/commands/research-command.d.ts +2 -1
  53. package/dist/src/commands/research-command.js +6 -0
  54. package/dist/src/commands/test-command.d.ts +2 -1
  55. package/dist/src/commands/test-command.js +4 -1
  56. package/dist/src/components/Autocomplete.js +26 -12
  57. package/dist/src/components/InputReadline.js +10 -1
  58. package/dist/src/config.d.ts +6 -0
  59. package/dist/src/experience-tracker.js +4 -3
  60. package/dist/src/explorbot.d.ts +3 -0
  61. package/dist/src/explorbot.js +8 -0
  62. package/dist/src/knowledge-tracker.js +1 -1
  63. package/dist/src/state-manager.d.ts +2 -0
  64. package/dist/src/state-manager.js +16 -0
  65. package/dist/src/test-plan.d.ts +11 -0
  66. package/dist/src/test-plan.js +54 -2
  67. package/dist/src/utils/aria-ref.js +1 -1
  68. package/dist/src/utils/logger.js +8 -2
  69. package/dist/src/utils/markdown-query.d.ts +1 -48
  70. package/dist/src/utils/markdown-query.js +1 -444
  71. package/dist/src/utils/mdq/cli.d.ts +6 -0
  72. package/dist/src/utils/mdq/cli.js +122 -0
  73. package/dist/src/utils/mdq/edit.d.ts +24 -0
  74. package/dist/src/utils/mdq/edit.js +147 -0
  75. package/dist/src/utils/mdq/query.d.ts +118 -0
  76. package/dist/src/utils/mdq/query.js +451 -0
  77. package/dist/src/utils/strings.d.ts +1 -0
  78. package/dist/src/utils/strings.js +7 -0
  79. package/dist/src/utils/test-files.d.ts +1 -0
  80. package/dist/src/utils/test-files.js +5 -2
  81. package/docs/api-testing/planning.md +1 -1
  82. package/docs/superpowers/plans/2026-09-15-mdq-package.md +130 -94
  83. package/docs/superpowers/specs/2026-09-18-judge-decision-model-design.md +79 -0
  84. package/package.json +3 -2
  85. package/src/ai/agent.ts +3 -1
  86. package/src/ai/judge-provider.ts +62 -0
  87. package/src/ai/judge-tool.ts +35 -0
  88. package/src/ai/judge.ts +75 -0
  89. package/src/ai/navigator.ts +15 -4
  90. package/src/ai/pilot.ts +58 -5
  91. package/src/ai/planner.ts +9 -6
  92. package/src/ai/provider.ts +51 -7
  93. package/src/ai/researcher/deep-analysis.ts +2 -2
  94. package/src/ai/researcher/locators.ts +2 -2
  95. package/src/ai/researcher/pagination.ts +1 -1
  96. package/src/ai/researcher/research-result.ts +2 -2
  97. package/src/ai/researcher.ts +1 -1
  98. package/src/ai/task-agent.ts +4 -1
  99. package/src/ai/tester.ts +19 -16
  100. package/src/ai/tools.ts +42 -7
  101. package/src/api/request-result.ts +2 -1
  102. package/src/command-handler.ts +28 -3
  103. package/src/commands/base-command.ts +9 -0
  104. package/src/commands/explore-command.ts +15 -2
  105. package/src/commands/freesail-command.ts +9 -2
  106. package/src/commands/init-command.ts +1 -1
  107. package/src/commands/navigate-command.ts +8 -1
  108. package/src/commands/plan-load-command.ts +6 -1
  109. package/src/commands/plans-command.ts +13 -29
  110. package/src/commands/rerun-command.ts +7 -2
  111. package/src/commands/research-command.ts +8 -1
  112. package/src/commands/test-command.ts +6 -2
  113. package/src/components/Autocomplete.tsx +39 -10
  114. package/src/components/InputReadline.tsx +10 -1
  115. package/src/config.ts +1 -0
  116. package/src/experience-tracker.ts +4 -3
  117. package/src/explorbot.ts +8 -0
  118. package/src/knowledge-tracker.ts +1 -1
  119. package/src/state-manager.ts +16 -0
  120. package/src/test-plan.ts +67 -2
  121. package/src/utils/aria-ref.ts +1 -1
  122. package/src/utils/logger.ts +6 -1
  123. package/src/utils/markdown-query.ts +1 -519
  124. package/src/utils/mdq/cli.ts +118 -0
  125. package/src/utils/mdq/edit.ts +158 -0
  126. package/src/utils/mdq/query.ts +556 -0
  127. package/src/utils/strings.ts +7 -0
  128. 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
+ }