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,158 @@
1
+ import YAML from 'yaml';
2
+ import type { MatchedRange } from './query.ts';
3
+
4
+ export class MarkdownEditor {
5
+ private source: string;
6
+
7
+ constructor(source: string) {
8
+ this.source = source;
9
+ }
10
+
11
+ static splitFrontmatter(source: string): FrontmatterSplit {
12
+ if (!source.startsWith('---')) return { raw: '', body: source, offset: 0 };
13
+ const match = source.match(/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/);
14
+ if (!match) return { raw: '', body: source, offset: 0 };
15
+ return { raw: match[1], body: source.slice(match[0].length), offset: match[0].length };
16
+ }
17
+
18
+ static blockEnd(range: MatchedRange): number {
19
+ if (range.trailing) return range.trailing.start + range.trailing.length;
20
+ return range.start + range.length;
21
+ }
22
+
23
+ static table(headers: string[], rows: string[][], align: (string | null)[]): string {
24
+ const widths = headers.map((header, index) => Math.max(header.length, 3, ...rows.map((row) => (row[index] || '').length)));
25
+ const line = (cells: string[]) => `| ${cells.map((cell, index) => (cell || '').padEnd(widths[index])).join(' | ')} |`;
26
+ const divider = `| ${widths.map((width, index) => MarkdownEditor.dashes(align[index], width)).join(' | ')} |`;
27
+ return `${[line(headers), divider, ...rows.map(line)].join('\n')}\n`;
28
+ }
29
+
30
+ static item(listRaw: string, text: string): string {
31
+ const lines = listRaw.split('\n').filter((line) => line.trim());
32
+ const last = lines[lines.length - 1] || '- x';
33
+ const ordered = last.match(/^(\s*)(\d+)([.)])\s/);
34
+ if (ordered) return `${ordered[1]}${Number.parseInt(ordered[2], 10) + 1}${ordered[3]} ${text}`;
35
+ const bullet = last.match(/^(\s*)([-*+])\s/);
36
+ if (!bullet) return `- ${text}`;
37
+ return `${bullet[1]}${bullet[2]} ${text}`;
38
+ }
39
+
40
+ static entries(text: string, key: string, value: string | null, isBlockquote: boolean): string {
41
+ const lines = text
42
+ .split('\n')
43
+ .map((line) => line.trim())
44
+ .filter(Boolean);
45
+
46
+ const index = lines.findIndex((line) => MarkdownEditor.entryKey(line) === key.toLowerCase());
47
+ if (index < 0 && value) lines.push(`${key}: ${value}`);
48
+ if (index >= 0 && value) lines[index] = `${key}: ${value}`;
49
+ if (index >= 0 && !value) lines.splice(index, 1);
50
+
51
+ if (!isBlockquote) return lines.join('\n');
52
+ return lines.map((line) => `> ${line}`).join('\n');
53
+ }
54
+
55
+ remove(ranges: MatchedRange[]): string {
56
+ const ordered = this.dedupe(ranges);
57
+ let result = this.source;
58
+
59
+ for (let i = ordered.length - 1; i >= 0; i--) {
60
+ const range = ordered[i];
61
+ const head = result.slice(0, range.start);
62
+ const tail = result.slice(MarkdownEditor.blockEnd(range));
63
+ if (tail) {
64
+ result = head + tail;
65
+ continue;
66
+ }
67
+ if (!head) {
68
+ result = '';
69
+ continue;
70
+ }
71
+ result = `${head.trimEnd()}\n`;
72
+ }
73
+
74
+ return result;
75
+ }
76
+
77
+ replace(ranges: MatchedRange[], render: (range: MatchedRange, index: number) => string): string {
78
+ const ordered = this.dedupe(ranges);
79
+ const rendered = ordered.map(render);
80
+ let result = this.source;
81
+
82
+ for (let i = ordered.length - 1; i >= 0; i--) {
83
+ const range = ordered[i];
84
+ result = result.slice(0, range.start) + rendered[i] + result.slice(range.start + range.length);
85
+ }
86
+
87
+ return result;
88
+ }
89
+
90
+ insert(offsets: number[], markdown: string): string {
91
+ const ordered = [...offsets].sort((a, b) => a - b);
92
+ let result = this.source;
93
+ for (let i = ordered.length - 1; i >= 0; i--) {
94
+ result = MarkdownEditor.spliceIn(result, ordered[i], markdown);
95
+ }
96
+ return result;
97
+ }
98
+
99
+ frontmatter(): Record<string, unknown> {
100
+ const { raw } = MarkdownEditor.splitFrontmatter(this.source);
101
+ if (!raw) return {};
102
+ return (YAML.parseDocument(raw).toJS() as Record<string, unknown>) || {};
103
+ }
104
+
105
+ setFrontmatter(key: string, value: unknown): string {
106
+ const { raw, body, offset } = MarkdownEditor.splitFrontmatter(this.source);
107
+ let document = new YAML.Document({});
108
+ if (raw) document = YAML.parseDocument(raw);
109
+ if (value === null) document.delete(key);
110
+ if (value !== null) document.set(key, value);
111
+
112
+ const rendered = document.toString().trimEnd();
113
+ if (!offset) return `---\n${rendered}\n---\n\n${this.source}`;
114
+ return `---\n${rendered}\n---\n${body}`;
115
+ }
116
+
117
+ private static spliceIn(source: string, offset: number, markdown: string): string {
118
+ const block = `${markdown.trimEnd()}\n`;
119
+ const before = source.slice(0, offset).replace(/\n+$/, '');
120
+ const after = source.slice(offset).replace(/^\n+/, '');
121
+ if (!before) return `${block}\n${after}`;
122
+ if (!after) return `${before}\n\n${block}`;
123
+ return `${before}\n\n${block}\n${after}`;
124
+ }
125
+
126
+ private static dashes(alignment: string | null, width: number): string {
127
+ if (alignment === 'center') return `:${'-'.repeat(Math.max(width - 2, 1))}:`;
128
+ if (alignment === 'left') return `:${'-'.repeat(Math.max(width - 1, 1))}`;
129
+ if (alignment === 'right') return `${'-'.repeat(Math.max(width - 1, 1))}:`;
130
+ return '-'.repeat(width);
131
+ }
132
+
133
+ private static entryKey(line: string): string | null {
134
+ const separator = line.indexOf(':');
135
+ if (separator < 1) return null;
136
+ return line.slice(0, separator).trim().toLowerCase();
137
+ }
138
+
139
+ private dedupe(ranges: MatchedRange[]): MatchedRange[] {
140
+ const sorted = [...ranges].sort((a, b) => a.start - b.start);
141
+ const kept: MatchedRange[] = [];
142
+ let lastEnd = -1;
143
+
144
+ for (const range of sorted) {
145
+ if (range.start < lastEnd) continue;
146
+ kept.push(range);
147
+ lastEnd = range.start + range.length;
148
+ }
149
+
150
+ return kept;
151
+ }
152
+ }
153
+
154
+ export interface FrontmatterSplit {
155
+ raw: string;
156
+ body: string;
157
+ offset: number;
158
+ }