explorbot 0.1.32-beta.1 → 0.2.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 (222) hide show
  1. package/README.md +35 -19
  2. package/bin/explorbot-cli.ts +33 -10
  3. package/boat/api-tester/src/config.ts +45 -3
  4. package/boat/doc-collector/src/ai/documentarian.ts +26 -28
  5. package/boat/doc-collector/src/ai/tools.ts +74 -16
  6. package/boat/doc-collector/src/cli.ts +1 -6
  7. package/boat/doc-collector/src/docbot.ts +30 -9
  8. package/boat/doc-collector/src/docs-renderer.ts +19 -24
  9. package/boat/doc-collector/src/screenshots.ts +34 -2
  10. package/boat/doc-collector/src/state-diagram.ts +281 -0
  11. package/dist/bin/explorbot-cli.js +29 -9
  12. package/dist/boat/api-tester/src/config.js +43 -4
  13. package/dist/boat/doc-collector/src/ai/documentarian.js +19 -7
  14. package/dist/boat/doc-collector/src/ai/tools.js +53 -16
  15. package/dist/boat/doc-collector/src/cli.js +1 -5
  16. package/dist/boat/doc-collector/src/docbot.js +27 -8
  17. package/dist/boat/doc-collector/src/docs-renderer.js +18 -3
  18. package/dist/boat/doc-collector/src/screenshots.js +30 -1
  19. package/dist/boat/doc-collector/src/state-diagram.js +200 -0
  20. package/dist/models.json +30 -0
  21. package/dist/package.json +23 -3
  22. package/dist/src/action-result.d.ts +8 -10
  23. package/dist/src/action-result.js +72 -164
  24. package/dist/src/action.d.ts +13 -12
  25. package/dist/src/action.js +37 -113
  26. package/dist/src/ai/agent.d.ts +17 -0
  27. package/dist/src/ai/captain/idle-mode.js +1 -1
  28. package/dist/src/ai/captain/test-mode.js +1 -1
  29. package/dist/src/ai/captain/web-mode.js +18 -29
  30. package/dist/src/ai/captain.d.ts +1 -3
  31. package/dist/src/ai/captain.js +17 -45
  32. package/dist/src/ai/conversation.d.ts +2 -5
  33. package/dist/src/ai/conversation.js +9 -28
  34. package/dist/src/ai/driller.d.ts +2 -17
  35. package/dist/src/ai/driller.js +26 -50
  36. package/dist/src/ai/experience-compactor.d.ts +2 -2
  37. package/dist/src/ai/experience-compactor.js +16 -33
  38. package/dist/src/ai/fisherman-tools.js +1 -1
  39. package/dist/src/ai/fisherman.d.ts +0 -1
  40. package/dist/src/ai/fisherman.js +0 -3
  41. package/dist/src/ai/historian/codeceptjs.d.ts +2 -0
  42. package/dist/src/ai/historian/codeceptjs.js +4 -3
  43. package/dist/src/ai/historian/playwright.d.ts +4 -1
  44. package/dist/src/ai/historian/playwright.js +4 -3
  45. package/dist/src/ai/historian/screencast.d.ts +2 -1
  46. package/dist/src/ai/historian/screencast.js +2 -2
  47. package/dist/src/ai/historian/utils.d.ts +0 -1
  48. package/dist/src/ai/historian/utils.js +0 -1
  49. package/dist/src/ai/historian.d.ts +10 -8
  50. package/dist/src/ai/historian.js +1 -5
  51. package/dist/src/ai/navigator.d.ts +8 -6
  52. package/dist/src/ai/navigator.js +34 -65
  53. package/dist/src/ai/pilot.d.ts +13 -8
  54. package/dist/src/ai/pilot.js +53 -59
  55. package/dist/src/ai/planner.d.ts +5 -4
  56. package/dist/src/ai/planner.js +66 -52
  57. package/dist/src/ai/provider.d.ts +4 -1
  58. package/dist/src/ai/provider.js +77 -137
  59. package/dist/src/ai/quartermaster.d.ts +22 -22
  60. package/dist/src/ai/quartermaster.js +8 -14
  61. package/dist/src/ai/rerunner.d.ts +3 -13
  62. package/dist/src/ai/rerunner.js +23 -44
  63. package/dist/src/ai/researcher/cache.js +11 -18
  64. package/dist/src/ai/researcher/coordinates.js +4 -3
  65. package/dist/src/ai/researcher/deep-analysis.d.ts +2 -0
  66. package/dist/src/ai/researcher/deep-analysis.js +8 -11
  67. package/dist/src/ai/researcher/locators.d.ts +9 -2
  68. package/dist/src/ai/researcher/locators.js +65 -8
  69. package/dist/src/ai/researcher/parser.js +1 -1
  70. package/dist/src/ai/researcher/research-result.d.ts +0 -1
  71. package/dist/src/ai/researcher/research-result.js +0 -19
  72. package/dist/src/ai/researcher/sections.d.ts +2 -0
  73. package/dist/src/ai/researcher/sections.js +7 -3
  74. package/dist/src/ai/researcher.js +40 -106
  75. package/dist/src/ai/rules.d.ts +1 -0
  76. package/dist/src/ai/rules.js +22 -15
  77. package/dist/src/ai/session-analyst.js +8 -5
  78. package/dist/src/ai/task-agent.d.ts +19 -6
  79. package/dist/src/ai/task-agent.js +37 -33
  80. package/dist/src/ai/tester.d.ts +6 -14
  81. package/dist/src/ai/tester.js +46 -111
  82. package/dist/src/ai/tools.d.ts +26 -14
  83. package/dist/src/ai/tools.js +138 -132
  84. package/dist/src/command-handler.js +1 -1
  85. package/dist/src/commands/compact-command.d.ts +1 -1
  86. package/dist/src/commands/compact-command.js +6 -6
  87. package/dist/src/commands/context-aria-command.js +1 -1
  88. package/dist/src/commands/context-command.js +8 -6
  89. package/dist/src/commands/context-data-command.js +2 -2
  90. package/dist/src/commands/context-experience-command.js +2 -2
  91. package/dist/src/commands/context-html-command.js +2 -2
  92. package/dist/src/commands/context-knowledge-command.js +2 -2
  93. package/dist/src/commands/drill-command.js +1 -1
  94. package/dist/src/commands/experience-command.js +1 -1
  95. package/dist/src/commands/explore-command.js +4 -3
  96. package/dist/src/commands/freesail-command.js +2 -2
  97. package/dist/src/commands/knows-command.js +1 -1
  98. package/dist/src/commands/learn-command.js +5 -3
  99. package/dist/src/commands/path-command.js +1 -1
  100. package/dist/src/commands/research-command.js +1 -1
  101. package/dist/src/commands/test-command.js +1 -1
  102. package/dist/src/components/AddKnowledge.d.ts +2 -0
  103. package/dist/src/components/AddKnowledge.js +3 -7
  104. package/dist/src/components/App.js +3 -3
  105. package/dist/src/config.d.ts +19 -0
  106. package/dist/src/config.js +156 -10
  107. package/dist/src/experience-tracker.d.ts +11 -6
  108. package/dist/src/experience-tracker.js +82 -155
  109. package/dist/src/explorbot.d.ts +21 -13
  110. package/dist/src/explorbot.js +84 -80
  111. package/dist/src/explorer.d.ts +66 -102
  112. package/dist/src/explorer.js +332 -623
  113. package/dist/src/knowledge-tracker.d.ts +2 -2
  114. package/dist/src/knowledge-tracker.js +38 -45
  115. package/dist/src/state-manager.d.ts +10 -30
  116. package/dist/src/state-manager.js +11 -129
  117. package/dist/src/utils/aria.d.ts +6 -1
  118. package/dist/src/utils/aria.js +4 -1
  119. package/dist/src/utils/cache.d.ts +15 -0
  120. package/dist/src/utils/cache.js +34 -0
  121. package/dist/src/utils/context-formatter.d.ts +6 -0
  122. package/dist/src/utils/context-formatter.js +15 -27
  123. package/dist/src/utils/hooks-runner.js +2 -4
  124. package/dist/src/utils/html.d.ts +5 -0
  125. package/dist/src/utils/html.js +71 -0
  126. package/dist/src/utils/markdown-files.d.ts +10 -0
  127. package/dist/src/utils/markdown-files.js +21 -0
  128. package/dist/src/utils/markdown-query.d.ts +6 -0
  129. package/dist/src/utils/markdown-query.js +14 -1
  130. package/dist/src/utils/page-readiness.d.ts +1 -0
  131. package/dist/src/utils/page-readiness.js +1 -1
  132. package/dist/src/utils/secrets.js +2 -2
  133. package/dist/src/utils/strings.d.ts +2 -0
  134. package/dist/src/utils/strings.js +10 -0
  135. package/dist/src/utils/test-files.js +1 -1
  136. package/dist/src/utils/web-annotate.d.ts +5 -0
  137. package/dist/src/utils/web-annotate.js +58 -0
  138. package/dist/src/utils/web-eidx.d.ts +2 -0
  139. package/dist/src/utils/web-eidx.js +20 -0
  140. package/dist/src/utils/web-element.d.ts +3 -1
  141. package/dist/src/utils/web-element.js +32 -0
  142. package/dist/src/utils/web-sandbox.d.ts +4 -0
  143. package/dist/src/utils/web-sandbox.js +37 -0
  144. package/models.json +30 -0
  145. package/package.json +23 -3
  146. package/src/action-result.ts +70 -173
  147. package/src/action.ts +38 -132
  148. package/src/ai/agent.ts +20 -0
  149. package/src/ai/captain/idle-mode.ts +1 -1
  150. package/src/ai/captain/test-mode.ts +1 -1
  151. package/src/ai/captain/web-mode.ts +18 -30
  152. package/src/ai/captain.ts +17 -42
  153. package/src/ai/conversation.ts +9 -32
  154. package/src/ai/driller.ts +53 -83
  155. package/src/ai/experience-compactor.ts +16 -35
  156. package/src/ai/fisherman-tools.ts +1 -1
  157. package/src/ai/fisherman.ts +0 -4
  158. package/src/ai/historian/codeceptjs.ts +5 -3
  159. package/src/ai/historian/playwright.ts +7 -4
  160. package/src/ai/historian/screencast.ts +4 -3
  161. package/src/ai/historian/utils.ts +0 -1
  162. package/src/ai/historian.ts +6 -8
  163. package/src/ai/navigator.ts +39 -70
  164. package/src/ai/pilot.ts +59 -61
  165. package/src/ai/planner.ts +76 -62
  166. package/src/ai/provider.ts +85 -146
  167. package/src/ai/quartermaster.ts +39 -45
  168. package/src/ai/rerunner.ts +27 -50
  169. package/src/ai/researcher/cache.ts +10 -16
  170. package/src/ai/researcher/coordinates.ts +4 -3
  171. package/src/ai/researcher/deep-analysis.ts +10 -11
  172. package/src/ai/researcher/locators.ts +66 -9
  173. package/src/ai/researcher/parser.ts +1 -1
  174. package/src/ai/researcher/research-result.ts +0 -18
  175. package/src/ai/researcher/sections.ts +9 -3
  176. package/src/ai/researcher.ts +42 -119
  177. package/src/ai/rules.ts +24 -13
  178. package/src/ai/session-analyst.ts +8 -5
  179. package/src/ai/task-agent.ts +45 -38
  180. package/src/ai/tester.ts +51 -129
  181. package/src/ai/tools.ts +145 -153
  182. package/src/command-handler.ts +1 -1
  183. package/src/commands/compact-command.ts +7 -8
  184. package/src/commands/context-aria-command.ts +1 -1
  185. package/src/commands/context-command.ts +8 -6
  186. package/src/commands/context-data-command.ts +2 -2
  187. package/src/commands/context-experience-command.ts +2 -2
  188. package/src/commands/context-html-command.ts +2 -2
  189. package/src/commands/context-knowledge-command.ts +2 -2
  190. package/src/commands/drill-command.ts +1 -1
  191. package/src/commands/experience-command.ts +1 -1
  192. package/src/commands/explore-command.ts +4 -3
  193. package/src/commands/freesail-command.ts +2 -2
  194. package/src/commands/knows-command.ts +1 -1
  195. package/src/commands/learn-command.ts +5 -3
  196. package/src/commands/path-command.ts +1 -1
  197. package/src/commands/research-command.ts +1 -1
  198. package/src/commands/test-command.ts +1 -1
  199. package/src/components/AddKnowledge.tsx +5 -7
  200. package/src/components/App.tsx +3 -3
  201. package/src/config.ts +185 -11
  202. package/src/experience-tracker.ts +89 -152
  203. package/src/explorbot.ts +88 -83
  204. package/src/explorer.ts +402 -693
  205. package/src/knowledge-tracker.ts +42 -49
  206. package/src/state-manager.ts +20 -155
  207. package/src/utils/aria.ts +11 -2
  208. package/src/utils/cache.ts +40 -0
  209. package/src/utils/context-formatter.ts +12 -15
  210. package/src/utils/hooks-runner.ts +2 -4
  211. package/src/utils/html.ts +79 -0
  212. package/src/utils/markdown-files.ts +30 -0
  213. package/src/utils/markdown-query.ts +15 -1
  214. package/src/utils/page-readiness.ts +1 -1
  215. package/src/utils/secrets.ts +2 -3
  216. package/src/utils/strings.ts +12 -0
  217. package/src/utils/test-files.ts +1 -1
  218. package/src/utils/web-annotate.ts +64 -0
  219. package/src/utils/web-eidx.ts +21 -0
  220. package/src/utils/web-element.ts +34 -0
  221. package/src/utils/web-sandbox.ts +43 -0
  222. package/dist/boat/api-tester/example/apibot.config.js +0 -30
@@ -178,7 +178,7 @@ export class CommandHandler implements InputManager {
178
178
  }
179
179
 
180
180
  private async executeBrowserCommand(input: string): Promise<void> {
181
- const action = this.explorBot.getExplorer().createAction();
181
+ const action = this.explorBot.getExplorer().action();
182
182
  await action.execute(input);
183
183
  }
184
184
 
@@ -1,12 +1,11 @@
1
1
  import { basename } from 'node:path';
2
2
  import chalk from 'chalk';
3
- import type { ExperienceFile } from '../ai/experience-compactor.js';
3
+ import { COMPACT_MAX_LENGTH } from '../ai/experience-compactor.js';
4
+ import type { ExperienceFile } from '../experience-tracker.js';
4
5
  import type { ExperienceTracker } from '../experience-tracker.js';
5
6
  import { tag } from '../utils/logger.js';
6
7
  import { BaseCommand, type Suggestion } from './base-command.js';
7
8
 
8
- const COMPACT_THRESHOLD = 5000;
9
-
10
9
  export class CompactCommand extends BaseCommand {
11
10
  name = 'compact';
12
11
  description = 'Compact stored experience files; optionally filtered by filename or URL substring';
@@ -22,7 +21,7 @@ export class CompactCommand extends BaseCommand {
22
21
  const dryRun = !!opts.dryRun;
23
22
  const merge = opts.merge !== false;
24
23
 
25
- const tracker = this.explorBot.getExperienceTracker();
24
+ const tracker = this.explorBot.experienceTracker();
26
25
  const files = this.resolveTarget(tracker, target);
27
26
 
28
27
  if (files === null) {
@@ -62,7 +61,7 @@ export class CompactCommand extends BaseCommand {
62
61
  private buildDryRunSuggestions(compactor: ReturnType<typeof this.explorBot.agentExperienceCompactor>, files: ExperienceFile[], target?: string): Suggestion[] {
63
62
  const wouldTouch = files.filter((f) => {
64
63
  const stripped = compactor.stripNonUsefulEntries(f.content);
65
- return stripped !== f.content || stripped.length >= COMPACT_THRESHOLD || compactor.isRecent(f);
64
+ return stripped !== f.content || stripped.length >= COMPACT_MAX_LENGTH || compactor.isRecent(f);
66
65
  });
67
66
  if (wouldTouch.length === 0) return [];
68
67
  const argsPart = target ? ` ${target}` : '';
@@ -73,7 +72,7 @@ export class CompactCommand extends BaseCommand {
73
72
  private async runFullSweep(compactor: ReturnType<typeof this.explorBot.agentExperienceCompactor>, merge: boolean): Promise<{ merged: number; compacted: number }> {
74
73
  if (!merge) {
75
74
  tag('info').log('Compacting all experience files (merge skipped)…');
76
- const compacted = await compactor.compactFiles(this.explorBot.getExperienceTracker().getAllExperience());
75
+ const compacted = await compactor.compactFiles(this.explorBot.experienceTracker().getAllExperience());
77
76
  return { merged: 0, compacted };
78
77
  }
79
78
 
@@ -119,7 +118,7 @@ export class CompactCommand extends BaseCommand {
119
118
  const charsRemoved = chars - strippedChars;
120
119
  const willStrip = stripped !== file.content;
121
120
  const willReview = compactor.isRecent(file);
122
- const willAi = strippedChars >= COMPACT_THRESHOLD;
121
+ const willAi = strippedChars >= COMPACT_MAX_LENGTH;
123
122
 
124
123
  if (willStrip) wouldStrip++;
125
124
  if (willReview) wouldAiReview++;
@@ -148,7 +147,7 @@ export class CompactCommand extends BaseCommand {
148
147
  lines.push(` ${chalk.dim('Files matched:')} ${chalk.bold(String(files.length))}`);
149
148
  lines.push(` ${chalk.dim('Would strip:')} ${chalk.bold(String(wouldStrip))} ${chalk.dim('(remove legacy / FAILED / Visual click, rename to FLOW/ACTION)')}`);
150
149
  lines.push(` ${chalk.dim('Would ai-review:')} ${chalk.bold(String(wouldAiReview))} ${chalk.dim('(recent files ≤30d — quality/reusability check)')}`);
151
- lines.push(` ${chalk.dim('Would ai-compact:')} ${chalk.bold(String(wouldAiCompact))} ${chalk.dim(`(over ${COMPACT_THRESHOLD} chars after strip)`)}`);
150
+ lines.push(` ${chalk.dim('Would ai-compact:')} ${chalk.bold(String(wouldAiCompact))} ${chalk.dim(`(over ${COMPACT_MAX_LENGTH} chars after strip)`)}`);
152
151
  lines.push(` ${chalk.dim('Total chars:')} ${chalk.bold(String(totalChars))}`);
153
152
 
154
153
  tag('info').log(lines.join('\n'));
@@ -6,7 +6,7 @@ export class ContextAriaCommand extends BaseCommand {
6
6
  description = 'Print full ARIA snapshot for current page';
7
7
 
8
8
  async execute(_args: string): Promise<void> {
9
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
9
+ const state = this.explorBot.stateManager().getCurrentState();
10
10
 
11
11
  if (!state) {
12
12
  throw new Error('No active page to snapshot');
@@ -1,9 +1,11 @@
1
1
  import { ActionResult } from '../action-result.js';
2
2
  import { Researcher } from '../ai/researcher.js';
3
+ import { visuallyAnnotateContainers } from '../ai/researcher/coordinates.js';
3
4
  import { outputPath } from '../config.js';
4
5
  import { type ContextData, type ContextMode, formatContextSummary } from '../utils/context-formatter.js';
5
6
  import { tag } from '../utils/logger.js';
6
7
  import { extractValidContainers } from '../utils/research-parser.js';
8
+ import { annotatePageElements } from '../utils/web-annotate.js';
7
9
  import { BaseCommand, type Suggestion } from './base-command.js';
8
10
 
9
11
  export class ContextCommand extends BaseCommand {
@@ -25,7 +27,7 @@ export class ContextCommand extends BaseCommand {
25
27
 
26
28
  async execute(args: string): Promise<void> {
27
29
  const explorer = this.explorBot.getExplorer();
28
- const state = explorer.getStateManager().getCurrentState();
30
+ const state = this.explorBot.stateManager().getCurrentState();
29
31
 
30
32
  if (!state) {
31
33
  throw new Error('No active page to show context for');
@@ -34,17 +36,17 @@ export class ContextCommand extends BaseCommand {
34
36
  const { opts } = this.parseArgs(args);
35
37
  const isVisual = !!(opts.visual || opts.screenshot);
36
38
 
37
- await explorer.annotateElements();
39
+ await explorer.withPage(annotatePageElements);
38
40
 
39
41
  if (isVisual) {
40
42
  const cachedResearch = Researcher.getCachedResearch(state);
41
43
  const containers = cachedResearch ? extractValidContainers(cachedResearch) : [];
42
- await explorer.visuallyAnnotateElements({ containers });
44
+ await explorer.withPage((page) => visuallyAnnotateContainers(page, containers));
43
45
  }
44
46
 
45
- const actionResult = await explorer.createAction().capturePageState({ includeScreenshot: isVisual });
46
- const experienceTracker = explorer.getStateManager().getExperienceTracker();
47
- const knowledgeTracker = this.explorBot.getKnowledgeTracker();
47
+ const actionResult = await explorer.capture({ screenshot: isVisual });
48
+ const experienceTracker = this.explorBot.experienceTracker();
49
+ const knowledgeTracker = this.explorBot.knowledgeTracker();
48
50
 
49
51
  let mode: ContextMode = 'compact';
50
52
  if (opts.full) {
@@ -8,7 +8,7 @@ export class ContextDataCommand extends BaseCommand {
8
8
 
9
9
  async execute(_args: string): Promise<void> {
10
10
  const explorer = this.explorBot.getExplorer();
11
- const state = explorer.getStateManager().getCurrentState();
11
+ const state = this.explorBot.stateManager().getCurrentState();
12
12
 
13
13
  if (!state) {
14
14
  throw new Error('No active page to extract data from');
@@ -18,7 +18,7 @@ export class ContextDataCommand extends BaseCommand {
18
18
 
19
19
  if (!actionResult.html || actionResult.html.trim().length < 100) {
20
20
  tag('info').log('Capturing fresh page content...');
21
- const freshResult = await explorer.createAction().capturePageState();
21
+ const freshResult = await explorer.capture();
22
22
  const table = await this.explorBot.agentResearcher().extractData(freshResult);
23
23
  tag('multiline').log(table);
24
24
  return;
@@ -10,14 +10,14 @@ export class ContextExperienceCommand extends BaseCommand {
10
10
 
11
11
  async execute(_args: string): Promise<void> {
12
12
  const explorer = this.explorBot.getExplorer();
13
- const state = explorer.getStateManager().getCurrentState();
13
+ const state = this.explorBot.stateManager().getCurrentState();
14
14
 
15
15
  if (!state) {
16
16
  throw new Error('No active page');
17
17
  }
18
18
 
19
19
  const actionResult = ActionResult.fromState(state);
20
- const experienceTracker = explorer.getStateManager().getExperienceTracker();
20
+ const experienceTracker = this.explorBot.experienceTracker();
21
21
  const experience = experienceTracker.getRelevantExperience(actionResult);
22
22
 
23
23
  if (experience.length === 0) {
@@ -8,7 +8,7 @@ export class ContextHtmlCommand extends BaseCommand {
8
8
 
9
9
  async execute(_args: string): Promise<void> {
10
10
  const explorer = this.explorBot.getExplorer();
11
- const manager = explorer.getStateManager();
11
+ const manager = this.explorBot.stateManager();
12
12
  const state = manager.getCurrentState();
13
13
 
14
14
  if (!state) {
@@ -19,7 +19,7 @@ export class ContextHtmlCommand extends BaseCommand {
19
19
 
20
20
  if (!actionResult.html || actionResult.html.trim().length < 100) {
21
21
  tag('info').log('Capturing fresh page content...');
22
- actionResult = await explorer.createAction().capturePageState();
22
+ actionResult = await explorer.capture();
23
23
  }
24
24
 
25
25
  const html = await actionResult.combinedHtml();
@@ -10,14 +10,14 @@ export class ContextKnowledgeCommand extends BaseCommand {
10
10
 
11
11
  async execute(_args: string): Promise<void> {
12
12
  const explorer = this.explorBot.getExplorer();
13
- const state = explorer.getStateManager().getCurrentState();
13
+ const state = this.explorBot.stateManager().getCurrentState();
14
14
 
15
15
  if (!state) {
16
16
  throw new Error('No active page');
17
17
  }
18
18
 
19
19
  const actionResult = ActionResult.fromState(state);
20
- const knowledgeTracker = this.explorBot.getKnowledgeTracker();
20
+ const knowledgeTracker = this.explorBot.knowledgeTracker();
21
21
  const knowledge = knowledgeTracker.getRelevantKnowledge(actionResult);
22
22
 
23
23
  if (knowledge.length === 0) {
@@ -13,7 +13,7 @@ export class DrillCommand extends BaseCommand {
13
13
  const knowledgePath = this.parseKnowledgeArg(args);
14
14
  const maxComponents = this.parseMaxArg(args);
15
15
 
16
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
16
+ const state = this.explorBot.stateManager().getCurrentState();
17
17
  if (!state) {
18
18
  throw new Error('No active page to drill');
19
19
  }
@@ -20,7 +20,7 @@ export class ExperienceCommand extends BaseCommand {
20
20
  }
21
21
  const recency = opts.recent ? 'recent' : opts.old ? 'old' : undefined;
22
22
 
23
- const tracker = this.explorBot.getExperienceTracker();
23
+ const tracker = this.explorBot.experienceTracker();
24
24
  const [first, second] = remaining;
25
25
 
26
26
  const combinedRef = first?.match(/^([A-Z]+)[.\-]?(\d+)$/i);
@@ -4,6 +4,7 @@ import { outputPath } from '../config.js';
4
4
  import { normalizeUrl } from '../state-manager.js';
5
5
  import { Stats } from '../stats.js';
6
6
  import { type Plan, type Test, TestResult } from '../test-plan.js';
7
+ import { browserErrorMessage } from '../utils/browser-errors.ts';
7
8
  import { getCliName } from '../utils/cli-name.ts';
8
9
  import { ErrorPageError, getStateErrorPageError } from '../utils/error-page.ts';
9
10
  import { tag } from '../utils/logger.js';
@@ -38,7 +39,7 @@ export class ExploreCommand extends BaseCommand {
38
39
  private priorityFilter?: Set<string>;
39
40
 
40
41
  private getCurrentPageUrl(): string | undefined {
41
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
42
+ const state = this.explorBot.stateManager().getCurrentState();
42
43
  return state?.fullUrl || state?.url;
43
44
  }
44
45
 
@@ -56,7 +57,7 @@ export class ExploreCommand extends BaseCommand {
56
57
  Stats.mode ??= 'explore';
57
58
  Stats.focus ??= feature;
58
59
  const mainUrl = this.getCurrentPageUrl();
59
- const error = getStateErrorPageError(this.explorBot.getExplorer().getStateManager().getCurrentState());
60
+ const error = getStateErrorPageError(this.explorBot.stateManager().getCurrentState());
60
61
  if (error) {
61
62
  tag('warning').log(error.message);
62
63
  return;
@@ -74,7 +75,7 @@ export class ExploreCommand extends BaseCommand {
74
75
  this.printResults();
75
76
  return;
76
77
  }
77
- if (mainUrl) await this.explorBot.visit(mainUrl);
78
+ if (mainUrl) await this.explorBot.visit(mainUrl).catch((err) => tag('warning').log(`Could not return to ${mainUrl}: ${browserErrorMessage(err)}`));
78
79
  const savedPath = this.explorBot.savePlans(this.completedPlans);
79
80
  this.printResults();
80
81
  await this.explorBot.printSessionAnalysis();
@@ -35,7 +35,7 @@ export class FreesailCommand extends BaseCommand {
35
35
  async (ctx) => {
36
36
  if (maxTests != null && testsRun >= maxTests) ctx.stop();
37
37
 
38
- const stateManager = this.explorBot.getExplorer().getStateManager();
38
+ const stateManager = this.explorBot.stateManager();
39
39
  const state = stateManager.getCurrentState();
40
40
 
41
41
  if (state && !Researcher.getCachedResearch(state)) {
@@ -70,7 +70,7 @@ export class FreesailCommand extends BaseCommand {
70
70
  }
71
71
 
72
72
  tag('info').log(`Navigating to: ${suggestion.target} - ${suggestion.reason}`);
73
- await this.explorBot.openFreshTab();
73
+ await this.explorBot.openTab();
74
74
  await this.explorBot.visit(suggestion.target);
75
75
  this.explorBot.clearPlan();
76
76
  },
@@ -23,7 +23,7 @@ export class KnowsCommand extends BaseCommand {
23
23
 
24
24
  async execute(args: string, limit = 0): Promise<void> {
25
25
  const url = args.trim();
26
- const tracker = this.explorBot.getKnowledgeTracker();
26
+ const tracker = this.explorBot.knowledgeTracker();
27
27
 
28
28
  if (!url) {
29
29
  const allKnowledge = tracker.listAllKnowledge();
@@ -13,12 +13,14 @@ export class LearnCommand extends BaseCommand {
13
13
 
14
14
  if (!note) {
15
15
  const AddKnowledge = (await import('../components/AddKnowledge.js')).default;
16
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
16
+ const explorer = this.explorBot.getExplorer();
17
+ const state = this.explorBot.stateManager().getCurrentState();
17
18
  const initialUrl = state?.url || '';
18
19
 
19
20
  const { unmount } = render(
20
21
  React.createElement(AddKnowledge, {
21
22
  initialUrl,
23
+ knowledgeTracker: this.explorBot.knowledgeTracker(),
22
24
  onComplete: () => unmount(),
23
25
  onCancel: () => unmount(),
24
26
  }),
@@ -31,14 +33,14 @@ export class LearnCommand extends BaseCommand {
31
33
  }
32
34
 
33
35
  const explorer = this.explorBot.getExplorer();
34
- const state = explorer.getStateManager().getCurrentState();
36
+ const state = this.explorBot.stateManager().getCurrentState();
35
37
 
36
38
  if (!state) {
37
39
  throw new Error('No active page to attach knowledge');
38
40
  }
39
41
 
40
42
  const targetUrl = state.url || state.fullUrl || '/';
41
- explorer.getKnowledgeTracker().addKnowledge(targetUrl, note);
43
+ this.explorBot.knowledgeTracker().addKnowledge(targetUrl, note);
42
44
  tag('success').log('Knowledge saved for current page');
43
45
  }
44
46
  }
@@ -16,7 +16,7 @@ export class PathCommand extends BaseCommand {
16
16
  async execute(args: string): Promise<void> {
17
17
  const { opts } = this.parseArgs(args);
18
18
  const showLinks = !!opts.links;
19
- const stateManager = this.explorBot.getExplorer().getStateManager();
19
+ const stateManager = this.explorBot.stateManager();
20
20
  const history = stateManager.getStateHistory();
21
21
 
22
22
  if (history.length === 0) {
@@ -27,7 +27,7 @@ export class ResearchCommand extends BaseCommand {
27
27
  await this.explorBot.agentNavigator().visit(target);
28
28
  }
29
29
 
30
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
30
+ const state = this.explorBot.stateManager().getCurrentState();
31
31
  if (!state) {
32
32
  throw new Error('No active page to research');
33
33
  }
@@ -48,7 +48,7 @@ export class TestCommand extends BaseCommand {
48
48
  if (matching.length > 0) {
49
49
  toExecute.push(...matching);
50
50
  } else {
51
- const state = this.explorBot.getExplorer().getStateManager().getCurrentState();
51
+ const state = this.explorBot.stateManager().getCurrentState();
52
52
  if (!state) {
53
53
  throw new Error('No page loaded. Please navigate to a page first.');
54
54
  }
@@ -1,15 +1,16 @@
1
1
  import { Box, Text, useInput } from 'ink';
2
2
  import React, { useEffect, useState } from 'react';
3
- import { KnowledgeTracker } from '../knowledge-tracker.js';
3
+ import type { KnowledgeTracker } from '../knowledge-tracker.js';
4
4
  import InputReadline from './InputReadline.js';
5
5
 
6
6
  interface AddKnowledgeProps {
7
7
  initialUrl?: string;
8
+ knowledgeTracker: KnowledgeTracker;
8
9
  onComplete?: () => void;
9
10
  onCancel?: () => void;
10
11
  }
11
12
 
12
- const AddKnowledge: React.FC<AddKnowledgeProps> = ({ initialUrl = '', onComplete, onCancel }) => {
13
+ const AddKnowledge: React.FC<AddKnowledgeProps> = ({ initialUrl = '', knowledgeTracker, onComplete, onCancel }) => {
13
14
  const [urlPattern, setUrlPattern] = useState(initialUrl);
14
15
  const [description, setDescription] = useState('');
15
16
  const [activeField, setActiveField] = useState<'url' | 'description'>(initialUrl ? 'description' : 'url');
@@ -20,18 +21,16 @@ const AddKnowledge: React.FC<AddKnowledgeProps> = ({ initialUrl = '', onComplete
20
21
 
21
22
  useEffect(() => {
22
23
  try {
23
- const knowledgeTracker = new KnowledgeTracker();
24
24
  const urls = knowledgeTracker.getExistingUrls();
25
25
  setSuggestedUrls(urls);
26
26
  } catch (error) {
27
27
  console.error('Failed to load suggestions:', error);
28
28
  }
29
- }, []);
29
+ }, [knowledgeTracker]);
30
30
 
31
31
  useEffect(() => {
32
32
  if (urlPattern.trim()) {
33
33
  try {
34
- const knowledgeTracker = new KnowledgeTracker();
35
34
  const knowledge = knowledgeTracker.getKnowledgeForUrl(urlPattern);
36
35
  setExistingKnowledge(knowledge);
37
36
  } catch (error) {
@@ -41,7 +40,7 @@ const AddKnowledge: React.FC<AddKnowledgeProps> = ({ initialUrl = '', onComplete
41
40
  } else {
42
41
  setExistingKnowledge([]);
43
42
  }
44
- }, [urlPattern]);
43
+ }, [urlPattern, knowledgeTracker]);
45
44
 
46
45
  const handleSave = () => {
47
46
  if (!urlPattern.trim() || !description.trim()) {
@@ -49,7 +48,6 @@ const AddKnowledge: React.FC<AddKnowledgeProps> = ({ initialUrl = '', onComplete
49
48
  }
50
49
 
51
50
  try {
52
- const knowledgeTracker = new KnowledgeTracker();
53
51
  const result = knowledgeTracker.addKnowledge(urlPattern.trim(), description.trim());
54
52
  const action = result.isNewFile ? 'Created' : 'Updated';
55
53
  console.log(`\nKnowledge ${action} in: ${result.filename}`);
@@ -68,7 +68,7 @@ export function App({ explorBot, initialShowInput = false, exitOnEmptyInput = fa
68
68
  });
69
69
  });
70
70
 
71
- const manager = explorBot.getExplorer().getStateManager();
71
+ const manager = explorBot.stateManager();
72
72
 
73
73
  unsubscribe = manager.onStateChange((transition: StateTransition) => {
74
74
  if (mounted) {
@@ -142,7 +142,7 @@ export function App({ explorBot, initialShowInput = false, exitOnEmptyInput = fa
142
142
  if (!mounted) return;
143
143
  setChecklistData({
144
144
  config: explorBot.getConfig(),
145
- knowledgeTracker: explorBot.getKnowledgeTracker(),
145
+ knowledgeTracker: explorBot.knowledgeTracker(),
146
146
  });
147
147
  setShowWelcome(true);
148
148
  setShowInput(true);
@@ -334,7 +334,7 @@ export function App({ explorBot, initialShowInput = false, exitOnEmptyInput = fa
334
334
  {showSessionTimer && <SessionTimer startedAt={sessionStartedAtRef.current} />}
335
335
  </Box>
336
336
 
337
- {showWelcome && <WelcomeCommands hasKnowledge={explorBot.getKnowledgeTracker().listAllKnowledge().length > 0} />}
337
+ {showWelcome && <WelcomeCommands hasKnowledge={explorBot.knowledgeTracker().listAllKnowledge().length > 0} />}
338
338
  {showInput && <Box height={1} />}
339
339
  {interruptPrompt && showInput && (
340
340
  <Box paddingX={1}>