@zhijiewang/openharness 2.1.0 → 2.3.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 (233) hide show
  1. package/README.md +4 -4
  2. package/dist/DeferredTool.js +3 -1
  3. package/dist/Tool.d.ts +1 -1
  4. package/dist/agents/roles.js +58 -62
  5. package/dist/commands/cybergotchi.d.ts +1 -1
  6. package/dist/commands/cybergotchi.js +30 -30
  7. package/dist/commands/index.js +288 -132
  8. package/dist/components/App.d.ts +1 -1
  9. package/dist/components/App.js +6 -6
  10. package/dist/components/CompanionFooter.d.ts +1 -1
  11. package/dist/components/CompanionFooter.js +6 -8
  12. package/dist/components/CybergotchiBubble.js +5 -5
  13. package/dist/components/CybergotchiPanel.d.ts +1 -1
  14. package/dist/components/CybergotchiPanel.js +7 -7
  15. package/dist/components/CybergotchiPanelConnected.js +2 -2
  16. package/dist/components/CybergotchiSetup.js +26 -24
  17. package/dist/components/CybergotchiSprite.d.ts +1 -1
  18. package/dist/components/CybergotchiSprite.js +8 -12
  19. package/dist/components/DiffView.d.ts +1 -1
  20. package/dist/components/DiffView.js +10 -10
  21. package/dist/components/ErrorBoundary.d.ts +1 -1
  22. package/dist/components/ErrorBoundary.js +1 -1
  23. package/dist/components/InitWizard.js +65 -33
  24. package/dist/components/Markdown.js +2 -4
  25. package/dist/components/Messages.js +4 -4
  26. package/dist/components/PermissionPrompt.d.ts +1 -1
  27. package/dist/components/PermissionPrompt.js +15 -17
  28. package/dist/components/REPL.d.ts +1 -1
  29. package/dist/components/REPL.js +74 -49
  30. package/dist/components/Spinner.js +2 -2
  31. package/dist/components/TextInput.js +35 -29
  32. package/dist/components/ToolCallDisplay.js +3 -5
  33. package/dist/cybergotchi/bones.d.ts +1 -1
  34. package/dist/cybergotchi/bones.js +8 -8
  35. package/dist/cybergotchi/config.d.ts +2 -2
  36. package/dist/cybergotchi/config.js +13 -13
  37. package/dist/cybergotchi/events.d.ts +5 -5
  38. package/dist/cybergotchi/events.js +7 -7
  39. package/dist/cybergotchi/needs.d.ts +2 -2
  40. package/dist/cybergotchi/needs.js +7 -9
  41. package/dist/cybergotchi/personality.d.ts +2 -2
  42. package/dist/cybergotchi/personality.js +2 -2
  43. package/dist/cybergotchi/species.d.ts +1 -1
  44. package/dist/cybergotchi/species.js +145 -217
  45. package/dist/cybergotchi/speech.d.ts +2 -2
  46. package/dist/cybergotchi/speech.js +43 -43
  47. package/dist/cybergotchi/types.d.ts +4 -4
  48. package/dist/cybergotchi/types.js +26 -26
  49. package/dist/cybergotchi/useCybergotchi.d.ts +1 -1
  50. package/dist/cybergotchi/useCybergotchi.js +29 -25
  51. package/dist/git/index.js +11 -9
  52. package/dist/harness/checkpoints.js +29 -21
  53. package/dist/harness/config.d.ts +3 -3
  54. package/dist/harness/config.js +15 -9
  55. package/dist/harness/context-warning.d.ts +1 -1
  56. package/dist/harness/context-warning.js +1 -1
  57. package/dist/harness/cost.js +1 -1
  58. package/dist/harness/credentials.js +13 -13
  59. package/dist/harness/hooks.js +7 -5
  60. package/dist/harness/keybindings.js +20 -18
  61. package/dist/harness/marketplace.d.ts +3 -3
  62. package/dist/harness/marketplace.js +55 -42
  63. package/dist/harness/memory.d.ts +23 -5
  64. package/dist/harness/memory.js +142 -41
  65. package/dist/harness/onboarding.js +30 -10
  66. package/dist/harness/plugins.d.ts +9 -1
  67. package/dist/harness/plugins.js +54 -30
  68. package/dist/harness/rules.js +12 -7
  69. package/dist/harness/sandbox.js +15 -15
  70. package/dist/harness/session-db.d.ts +55 -0
  71. package/dist/harness/session-db.js +165 -0
  72. package/dist/harness/session.d.ts +1 -1
  73. package/dist/harness/session.js +34 -15
  74. package/dist/harness/store.d.ts +3 -3
  75. package/dist/harness/store.js +6 -4
  76. package/dist/harness/submit-handler.d.ts +4 -4
  77. package/dist/harness/submit-handler.js +25 -23
  78. package/dist/harness/telemetry.d.ts +1 -1
  79. package/dist/harness/telemetry.js +23 -19
  80. package/dist/harness/traces.d.ts +2 -2
  81. package/dist/harness/traces.js +39 -33
  82. package/dist/harness/verification.d.ts +1 -1
  83. package/dist/harness/verification.js +50 -44
  84. package/dist/lsp/client.js +44 -40
  85. package/dist/main.js +98 -59
  86. package/dist/mcp/DeferredMcpTool.d.ts +4 -4
  87. package/dist/mcp/DeferredMcpTool.js +9 -5
  88. package/dist/mcp/McpTool.d.ts +4 -4
  89. package/dist/mcp/McpTool.js +8 -4
  90. package/dist/mcp/client.d.ts +2 -2
  91. package/dist/mcp/client.js +21 -21
  92. package/dist/mcp/loader.d.ts +1 -1
  93. package/dist/mcp/loader.js +17 -12
  94. package/dist/mcp/registry.d.ts +3 -3
  95. package/dist/mcp/registry.js +97 -97
  96. package/dist/mcp/schema.d.ts +1 -1
  97. package/dist/mcp/schema.js +16 -16
  98. package/dist/mcp/server.d.ts +1 -1
  99. package/dist/mcp/server.js +21 -21
  100. package/dist/mcp/types.d.ts +3 -3
  101. package/dist/providers/anthropic.d.ts +2 -2
  102. package/dist/providers/anthropic.js +10 -9
  103. package/dist/providers/base.d.ts +1 -1
  104. package/dist/providers/index.js +10 -3
  105. package/dist/providers/llamacpp.d.ts +2 -2
  106. package/dist/providers/llamacpp.js +1 -3
  107. package/dist/providers/ollama.d.ts +2 -2
  108. package/dist/providers/ollama.js +3 -4
  109. package/dist/providers/openai.d.ts +2 -2
  110. package/dist/providers/openai.js +3 -5
  111. package/dist/providers/openrouter.d.ts +2 -2
  112. package/dist/providers/router.d.ts +1 -1
  113. package/dist/providers/router.js +7 -7
  114. package/dist/query/compress.d.ts +2 -2
  115. package/dist/query/compress.js +22 -21
  116. package/dist/query/context-manager.d.ts +1 -1
  117. package/dist/query/context-manager.js +5 -5
  118. package/dist/query/errors.js +1 -1
  119. package/dist/query/index.d.ts +1 -1
  120. package/dist/query/index.js +30 -22
  121. package/dist/query/tools.js +15 -12
  122. package/dist/query/types.d.ts +1 -1
  123. package/dist/query.d.ts +1 -1
  124. package/dist/query.js +1 -1
  125. package/dist/remote/auth.d.ts +2 -2
  126. package/dist/remote/auth.js +8 -8
  127. package/dist/remote/server.d.ts +3 -3
  128. package/dist/remote/server.js +60 -60
  129. package/dist/renderer/cells.js +9 -9
  130. package/dist/renderer/colors.js +24 -6
  131. package/dist/renderer/diff.d.ts +2 -2
  132. package/dist/renderer/diff.js +27 -19
  133. package/dist/renderer/differ.d.ts +1 -1
  134. package/dist/renderer/differ.js +9 -9
  135. package/dist/renderer/image.js +19 -19
  136. package/dist/renderer/index.d.ts +6 -6
  137. package/dist/renderer/index.js +163 -93
  138. package/dist/renderer/input.js +66 -48
  139. package/dist/renderer/layout.d.ts +6 -6
  140. package/dist/renderer/layout.js +163 -124
  141. package/dist/renderer/markdown.d.ts +2 -2
  142. package/dist/renderer/markdown.js +173 -54
  143. package/dist/renderer/session-browser.d.ts +2 -2
  144. package/dist/renderer/session-browser.js +19 -21
  145. package/dist/repl.d.ts +5 -5
  146. package/dist/repl.js +300 -198
  147. package/dist/sdk/index.d.ts +5 -5
  148. package/dist/sdk/index.js +32 -26
  149. package/dist/services/AgentDispatcher.d.ts +3 -3
  150. package/dist/services/AgentDispatcher.js +33 -29
  151. package/dist/services/CronExecutor.d.ts +4 -4
  152. package/dist/services/CronExecutor.js +12 -8
  153. package/dist/services/EvaluatorLoop.d.ts +3 -3
  154. package/dist/services/EvaluatorLoop.js +29 -21
  155. package/dist/services/MetaHarness.d.ts +1 -1
  156. package/dist/services/MetaHarness.js +34 -32
  157. package/dist/services/PipelineExecutor.d.ts +1 -1
  158. package/dist/services/PipelineExecutor.js +23 -25
  159. package/dist/services/SkillExtractor.d.ts +43 -0
  160. package/dist/services/SkillExtractor.js +143 -0
  161. package/dist/services/StreamingToolExecutor.d.ts +2 -2
  162. package/dist/services/StreamingToolExecutor.js +11 -7
  163. package/dist/services/a2a.d.ts +8 -8
  164. package/dist/services/a2a.js +44 -34
  165. package/dist/services/agent-messaging.d.ts +33 -15
  166. package/dist/services/agent-messaging.js +65 -13
  167. package/dist/services/cron.js +16 -16
  168. package/dist/tools/AgentTool/index.d.ts +5 -2
  169. package/dist/tools/AgentTool/index.js +35 -15
  170. package/dist/tools/AskUserTool/index.js +1 -1
  171. package/dist/tools/BashTool/index.d.ts +2 -2
  172. package/dist/tools/BashTool/index.js +18 -10
  173. package/dist/tools/CronTool/index.d.ts +2 -2
  174. package/dist/tools/CronTool/index.js +30 -12
  175. package/dist/tools/DiagnosticsTool/index.js +28 -22
  176. package/dist/tools/EnterPlanModeTool/index.js +93 -14
  177. package/dist/tools/EnterWorktreeTool/index.js +7 -3
  178. package/dist/tools/ExitPlanModeTool/index.d.ts +22 -1
  179. package/dist/tools/ExitPlanModeTool/index.js +20 -5
  180. package/dist/tools/ExitWorktreeTool/index.js +11 -4
  181. package/dist/tools/FileEditTool/index.js +3 -5
  182. package/dist/tools/FileReadTool/index.js +16 -10
  183. package/dist/tools/FileWriteTool/index.js +2 -2
  184. package/dist/tools/GlobTool/index.js +5 -9
  185. package/dist/tools/GrepTool/index.d.ts +2 -2
  186. package/dist/tools/GrepTool/index.js +14 -9
  187. package/dist/tools/ImageReadTool/index.js +2 -2
  188. package/dist/tools/KillProcessTool/index.js +11 -7
  189. package/dist/tools/LSTool/index.js +3 -3
  190. package/dist/tools/MemoryTool/index.d.ts +11 -11
  191. package/dist/tools/MemoryTool/index.js +28 -14
  192. package/dist/tools/MonitorTool/index.js +24 -19
  193. package/dist/tools/MultiEditTool/index.js +9 -5
  194. package/dist/tools/NotebookEditTool/index.js +3 -3
  195. package/dist/tools/ParallelAgentTool/index.d.ts +4 -4
  196. package/dist/tools/ParallelAgentTool/index.js +12 -6
  197. package/dist/tools/PipelineTool/index.d.ts +4 -4
  198. package/dist/tools/PipelineTool/index.js +3 -3
  199. package/dist/tools/PowerShellTool/index.js +10 -6
  200. package/dist/tools/RemoteTriggerTool/index.js +8 -4
  201. package/dist/tools/ScheduleWakeupTool/index.d.ts +42 -0
  202. package/dist/tools/ScheduleWakeupTool/index.js +115 -0
  203. package/dist/tools/SendMessageTool/index.js +25 -7
  204. package/dist/tools/SessionSearchTool/index.d.ts +15 -0
  205. package/dist/tools/SessionSearchTool/index.js +36 -0
  206. package/dist/tools/SkillTool/index.d.ts +3 -0
  207. package/dist/tools/SkillTool/index.js +39 -9
  208. package/dist/tools/TaskCreateTool/index.d.ts +2 -2
  209. package/dist/tools/TaskCreateTool/index.js +2 -2
  210. package/dist/tools/TaskGetTool/index.js +2 -2
  211. package/dist/tools/TaskListTool/index.js +3 -5
  212. package/dist/tools/TaskOutputTool/index.js +2 -2
  213. package/dist/tools/TaskStopTool/index.js +3 -3
  214. package/dist/tools/TaskUpdateTool/index.d.ts +4 -4
  215. package/dist/tools/TaskUpdateTool/index.js +2 -2
  216. package/dist/tools/ToolSearchTool/index.js +9 -6
  217. package/dist/tools/WebFetchTool/index.js +1 -1
  218. package/dist/tools/WebSearchTool/index.js +2 -6
  219. package/dist/tools.js +31 -30
  220. package/dist/types/permissions.js +15 -9
  221. package/dist/utils/bash-safety.d.ts +1 -1
  222. package/dist/utils/bash-safety.js +64 -54
  223. package/dist/utils/diff-algorithm.d.ts +3 -3
  224. package/dist/utils/diff-algorithm.js +7 -7
  225. package/dist/utils/fs.js +3 -3
  226. package/dist/utils/safe-env.js +1 -1
  227. package/dist/utils/theme-data.d.ts +1 -1
  228. package/dist/utils/theme-data.js +1 -1
  229. package/dist/utils/theme.d.ts +1 -1
  230. package/dist/utils/theme.js +1 -1
  231. package/dist/utils/tool-summary.d.ts +1 -1
  232. package/dist/utils/tool-summary.js +27 -9
  233. package/package.json +10 -3
@@ -1,7 +1,7 @@
1
1
  import type { Provider } from "../providers/base.js";
2
2
  import type { Tools } from "../Tool.js";
3
- import type { PermissionMode } from "../types/permissions.js";
4
3
  import type { Message } from "../types/message.js";
4
+ import type { PermissionMode } from "../types/permissions.js";
5
5
  type AppProps = {
6
6
  provider: Provider;
7
7
  tools: Tools;
@@ -1,13 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useMemo } from "react";
3
- import { loadRulesAsPrompt } from "../harness/rules.js";
4
- import { detectProject, projectContextToPrompt } from "../harness/onboarding.js";
5
- import { loadCompanionRuntime, getCompanionSystemPrompt } from "../cybergotchi/config.js";
3
+ import { getCompanionSystemPrompt, loadCompanionRuntime } from "../cybergotchi/config.js";
6
4
  import { readOhConfig } from "../harness/config.js";
7
- import { setToolPermissionRules } from "../types/permissions.js";
8
5
  import { loadMemories, memoriesToPrompt } from "../harness/memory.js";
6
+ import { detectProject, projectContextToPrompt } from "../harness/onboarding.js";
9
7
  import { discoverSkills, skillsToPrompt } from "../harness/plugins.js";
10
- import { ThemeProvider, darkTheme } from "../utils/theme.js";
8
+ import { loadRulesAsPrompt } from "../harness/rules.js";
9
+ import { setToolPermissionRules } from "../types/permissions.js";
10
+ import { darkTheme, ThemeProvider } from "../utils/theme.js";
11
11
  import { ErrorBoundary } from "./ErrorBoundary.js";
12
12
  import REPL from "./REPL.js";
13
13
  const DEFAULT_SYSTEM_PROMPT = `You are OpenHarness, an AI coding assistant running in the user's terminal.
@@ -75,7 +75,7 @@ export default function App({ provider, tools, permissionMode, systemPrompt, mod
75
75
  parts.push(getCompanionSystemPrompt(companionRuntime));
76
76
  }
77
77
  return parts.join("\n\n");
78
- }, [systemPrompt]);
78
+ }, [systemPrompt, permissionMode]);
79
79
  return (_jsx(ThemeProvider, { value: darkTheme, children: _jsx(ErrorBoundary, { children: _jsx(REPL, { provider: provider, tools: tools, permissionMode: permissionMode, systemPrompt: fullSystemPrompt, model: model, initialMessages: initialMessages, resumeSessionId: resumeSessionId }) }) }));
80
80
  }
81
81
  export { DEFAULT_SYSTEM_PROMPT };
@@ -1,4 +1,4 @@
1
- import type { CompanionBones, CompanionConfig, CompanionState } from '../cybergotchi/types.js';
1
+ import type { CompanionBones, CompanionConfig, CompanionState } from "../cybergotchi/types.js";
2
2
  interface Props {
3
3
  bones: CompanionBones;
4
4
  config: CompanionConfig;
@@ -1,13 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
3
- import { RARITY_COLORS, RARITY_STARS } from '../cybergotchi/types.js';
4
- import CybergotchiSprite from './CybergotchiSprite.js';
5
- import CybergotchiBubble from './CybergotchiBubble.js';
2
+ import { Box, Text } from "ink";
3
+ import { RARITY_COLORS, RARITY_STARS } from "../cybergotchi/types.js";
4
+ import CybergotchiBubble from "./CybergotchiBubble.js";
5
+ import CybergotchiSprite from "./CybergotchiSprite.js";
6
6
  export default function CompanionFooter({ bones, config, state }) {
7
7
  const rarityColor = RARITY_COLORS[bones.rarity];
8
- const stagePrefix = config.evolutionStage === 2 ? ''
9
- : config.evolutionStage === 1 ? '✦ '
10
- : '';
11
- return (_jsxs(Box, { flexDirection: "column", alignItems: "flex-end", children: [state.speech && (_jsx(CybergotchiBubble, { speech: state.speech, name: config.soul.name, maxWidth: 16 })), _jsx(CybergotchiSprite, { bones: bones, config: config, state: state }), _jsxs(Text, { color: rarityColor, dimColor: true, children: [stagePrefix, config.soul.name, " ", RARITY_STARS[bones.rarity]] })] }));
8
+ const stagePrefix = config.evolutionStage === 2 ? "" : config.evolutionStage === 1 ? "✦ " : "";
9
+ return (_jsxs(Box, { flexDirection: "column", alignItems: "flex-end", children: [state.speech && _jsx(CybergotchiBubble, { speech: state.speech, name: config.soul.name, maxWidth: 16 }), _jsx(CybergotchiSprite, { bones: bones, config: config, state: state }), _jsxs(Text, { color: rarityColor, dimColor: true, children: [stagePrefix, config.soul.name, " ", RARITY_STARS[bones.rarity]] })] }));
12
10
  }
13
11
  //# sourceMappingURL=CompanionFooter.js.map
@@ -1,9 +1,9 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
2
+ import { Box, Text } from "ink";
3
3
  function wrapText(text, width) {
4
- const words = text.split(' ');
4
+ const words = text.split(" ");
5
5
  const lines = [];
6
- let current = '';
6
+ let current = "";
7
7
  for (const word of words) {
8
8
  if (current.length + word.length + (current ? 1 : 0) <= width) {
9
9
  current = current ? `${current} ${word}` : word;
@@ -20,7 +20,7 @@ function wrapText(text, width) {
20
20
  }
21
21
  export default function CybergotchiBubble({ speech, name, maxWidth = 18 }) {
22
22
  const lines = wrapText(speech, maxWidth);
23
- const boxWidth = Math.min(maxWidth, Math.max(...lines.map(l => l.length)));
24
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [name, ":"] }), _jsx(Text, { color: "white", children: '╭' + ''.repeat(boxWidth + 2) + '╮' }), lines.map((line, i) => (_jsxs(Text, { color: "white", children: ['', line.padEnd(boxWidth), ''] }, i))), _jsx(Text, { color: "white", children: '╰' + ''.repeat(boxWidth + 2) + '╯' })] }));
23
+ const boxWidth = Math.min(maxWidth, Math.max(...lines.map((l) => l.length)));
24
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [name, ":"] }), _jsx(Text, { color: "white", children: `╭${"".repeat(boxWidth + 2)}╮` }), lines.map((line, i) => (_jsxs(Text, { color: "white", children: ["", line.padEnd(boxWidth), ""] }, i))), _jsx(Text, { color: "white", children: `╰${"".repeat(boxWidth + 2)}╯` })] }));
25
25
  }
26
26
  //# sourceMappingURL=CybergotchiBubble.js.map
@@ -1,4 +1,4 @@
1
- import type { CompanionBones, CompanionConfig, CompanionState } from '../cybergotchi/types.js';
1
+ import type { CompanionBones, CompanionConfig, CompanionState } from "../cybergotchi/types.js";
2
2
  interface Props {
3
3
  bones: CompanionBones;
4
4
  config: CompanionConfig;
@@ -1,17 +1,17 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
3
- import { RARITY_COLORS, RARITY_STARS } from '../cybergotchi/types.js';
4
- import CybergotchiSprite from './CybergotchiSprite.js';
5
- import CybergotchiBubble from './CybergotchiBubble.js';
2
+ import { Box, Text } from "ink";
3
+ import { RARITY_COLORS, RARITY_STARS } from "../cybergotchi/types.js";
4
+ import CybergotchiBubble from "./CybergotchiBubble.js";
5
+ import CybergotchiSprite from "./CybergotchiSprite.js";
6
6
  function NeedsBar({ icon, value }) {
7
7
  const filled = Math.round(value / 10);
8
8
  const empty = 10 - filled;
9
- const color = value < 20 ? 'red' : value < 40 ? 'yellow' : 'green';
10
- return (_jsxs(Text, { children: [icon, ' ', _jsxs(Text, { color: color, children: [''.repeat(filled), ''.repeat(empty)] }), ' ', _jsx(Text, { dimColor: true, children: String(Math.round(value)).padStart(3) })] }));
9
+ const color = value < 20 ? "red" : value < 40 ? "yellow" : "green";
10
+ return (_jsxs(Text, { children: [icon, " ", _jsxs(Text, { color: color, children: ["".repeat(filled), "".repeat(empty)] }), " ", _jsx(Text, { dimColor: true, children: String(Math.round(value)).padStart(3) })] }));
11
11
  }
12
12
  export default function CybergotchiPanel({ bones, config, state }) {
13
13
  const streak = config.currentStreak;
14
14
  const rarityColor = RARITY_COLORS[bones.rarity];
15
- return (_jsxs(Box, { flexDirection: "column", width: 22, marginLeft: 1, borderStyle: "single", borderColor: rarityColor, paddingX: 1, children: [_jsxs(Text, { color: rarityColor, dimColor: true, children: [config.evolutionStage === 2 ? '' : config.evolutionStage === 1 ? '' : '', config.soul.name, " ", RARITY_STARS[bones.rarity]] }), state.speech && (_jsx(CybergotchiBubble, { speech: state.speech, name: config.soul.name })), _jsx(CybergotchiSprite, { bones: bones, config: config, state: state }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(NeedsBar, { icon: "\uD83C\uDF56", value: config.needs.hunger }), _jsx(NeedsBar, { icon: "\u26A1", value: config.needs.energy }), _jsx(NeedsBar, { icon: "\uD83D\uDC9B", value: config.needs.happiness }), streak >= 3 && (_jsxs(Text, { color: "yellow", children: ['🔥 ', streak, " streak"] }))] })] }));
15
+ return (_jsxs(Box, { flexDirection: "column", width: 22, marginLeft: 1, borderStyle: "single", borderColor: rarityColor, paddingX: 1, children: [_jsxs(Text, { color: rarityColor, dimColor: true, children: [config.evolutionStage === 2 ? "" : config.evolutionStage === 1 ? "" : "", config.soul.name, " ", RARITY_STARS[bones.rarity]] }), state.speech && _jsx(CybergotchiBubble, { speech: state.speech, name: config.soul.name }), _jsx(CybergotchiSprite, { bones: bones, config: config, state: state }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(NeedsBar, { icon: "\uD83C\uDF56", value: config.needs.hunger }), _jsx(NeedsBar, { icon: "\u26A1", value: config.needs.energy }), _jsx(NeedsBar, { icon: "\uD83D\uDC9B", value: config.needs.happiness }), streak >= 3 && (_jsxs(Text, { color: "yellow", children: ["🔥 ", streak, " streak"] }))] })] }));
16
16
  }
17
17
  //# sourceMappingURL=CybergotchiPanel.js.map
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useCybergotchi } from '../cybergotchi/useCybergotchi.js';
3
- import CompanionFooter from './CompanionFooter.js';
2
+ import { useCybergotchi } from "../cybergotchi/useCybergotchi.js";
3
+ import CompanionFooter from "./CompanionFooter.js";
4
4
  /**
5
5
  * Self-contained wrapper that owns the useCybergotchi hook.
6
6
  * Isolates all 500ms animation re-renders to this subtree only,
@@ -1,52 +1,54 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useMemo } from 'react';
3
- import { Box, Text, useInput } from 'ink';
4
- import TextInputLib from 'ink-text-input';
5
- import { HAT_ART, RARITY_COLORS, RARITY_STARS, RARITY_HATS } from '../cybergotchi/types.js';
6
- import { SPECIES } from '../cybergotchi/species.js';
7
- import { createCompanionConfig, saveCompanionConfig } from '../cybergotchi/config.js';
8
- import { roll, getDefaultSeed } from '../cybergotchi/bones.js';
9
- import CybergotchiSprite from './CybergotchiSprite.js';
2
+ import { Box, Text, useInput } from "ink";
3
+ import TextInputLib from "ink-text-input";
4
+ import { useMemo, useState } from "react";
5
+ import { getDefaultSeed, roll } from "../cybergotchi/bones.js";
6
+ import { createCompanionConfig, saveCompanionConfig } from "../cybergotchi/config.js";
7
+ import { SPECIES } from "../cybergotchi/species.js";
8
+ import { HAT_ART, RARITY_COLORS, RARITY_HATS, RARITY_STARS } from "../cybergotchi/types.js";
9
+ import CybergotchiSprite from "./CybergotchiSprite.js";
10
10
  export default function CybergotchiSetup({ onComplete, onSkip }) {
11
- const [step, setStep] = useState('seed');
11
+ const [step, setStep] = useState("seed");
12
12
  const [seed, setSeed] = useState(getDefaultSeed());
13
- const [name, setName] = useState('');
13
+ const [name, setName] = useState("");
14
14
  const [hatIdx, setHatIdx] = useState(0);
15
15
  // Compute bones from current seed
16
16
  const bones = useMemo(() => roll(seed), [seed]);
17
- const species = SPECIES.find(s => s.name === bones.species);
17
+ const species = SPECIES.find((s) => s.name === bones.species);
18
18
  const availableHats = RARITY_HATS[bones.rarity];
19
19
  // Build a preview config for the sprite
20
- const previewConfig = useMemo(() => createCompanionConfig(seed, name || species.label, '', availableHats[hatIdx] ?? 'none'), [seed, name, species.label, hatIdx, availableHats]);
21
- const previewState = { emotion: 'idle', frame: 0, speech: null, speechTtl: 0 };
22
- useInput((input, key) => {
20
+ const previewConfig = useMemo(() => createCompanionConfig(seed, name || species.label, "", availableHats[hatIdx] ?? "none"), [seed, name, species.label, hatIdx, availableHats]);
21
+ const previewState = { emotion: "idle", frame: 0, speech: null, speechTtl: 0 };
22
+ useInput((_input, key) => {
23
23
  if (key.escape) {
24
24
  onSkip();
25
25
  return;
26
26
  }
27
- if (step === 'seed') {
27
+ if (step === "seed") {
28
28
  // Seed is edited via TextInput; Enter advances
29
29
  }
30
- else if (step === 'reveal') {
30
+ else if (step === "reveal") {
31
31
  if (key.return)
32
- setStep('name');
32
+ setStep("name");
33
33
  }
34
- else if (step === 'name') {
34
+ else if (step === "name") {
35
35
  // Name is edited via TextInput; Enter advances
36
36
  }
37
- else if (step === 'hat') {
37
+ else if (step === "hat") {
38
38
  if (key.upArrow)
39
- setHatIdx(i => (i - 1 + availableHats.length) % availableHats.length);
39
+ setHatIdx((i) => (i - 1 + availableHats.length) % availableHats.length);
40
40
  if (key.downArrow)
41
- setHatIdx(i => (i + 1) % availableHats.length);
41
+ setHatIdx((i) => (i + 1) % availableHats.length);
42
42
  if (key.return) {
43
- const cfg = createCompanionConfig(seed, name || species.label, '', // personality — filled by LLM later
44
- availableHats[hatIdx] ?? 'none');
43
+ const cfg = createCompanionConfig(seed, name || species.label, "", // personality — filled by LLM later
44
+ availableHats[hatIdx] ?? "none");
45
45
  saveCompanionConfig(cfg);
46
46
  onComplete();
47
47
  }
48
48
  }
49
49
  });
50
- return (_jsxs(Box, { flexDirection: "row", gap: 2, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Text, { bold: true, color: "magenta", children: "\u25C6 Companion Setup" }), _jsx(Text, { dimColor: true, children: "Esc to skip" }), _jsx(Text, { children: ' ' }), step === 'seed' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Enter a seed phrase:" }), _jsx(Text, { dimColor: true, children: "This determines your species, rarity, and stats." }), _jsx(Text, { dimColor: true, children: "Same seed = same companion. Press Enter to continue." }), _jsx(Text, { children: ' ' }), _jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: '' }), _jsx(TextInputLib, { value: seed, onChange: setSeed, onSubmit: () => setStep('reveal'), placeholder: getDefaultSeed() })] })] })), step === 'reveal' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Your companion has been determined!" }), _jsx(Text, { children: ' ' }), _jsxs(Text, { children: ["Species: ", _jsx(Text, { color: RARITY_COLORS[bones.rarity], bold: true, children: species.label })] }), _jsxs(Text, { children: ["Rarity: ", _jsxs(Text, { color: RARITY_COLORS[bones.rarity], children: [bones.rarity, " ", RARITY_STARS[bones.rarity]] })] }), bones.isShiny && _jsx(Text, { color: "yellow", bold: true, children: "\u2728 SHINY! \u2728" }), _jsx(Text, { children: ' ' }), _jsx(Text, { dimColor: true, children: "Stats:" }), _jsxs(Text, { children: [" DEBUGGING: ", bones.baseStats.DEBUGGING] }), _jsxs(Text, { children: [" PATIENCE: ", bones.baseStats.PATIENCE] }), _jsxs(Text, { children: [" CHAOS: ", bones.baseStats.CHAOS] }), _jsxs(Text, { children: [" WISDOM: ", bones.baseStats.WISDOM] }), _jsxs(Text, { children: [" SNARK: ", bones.baseStats.SNARK] }), _jsx(Text, { children: ' ' }), _jsx(Text, { dimColor: true, children: species.traitHint }), _jsx(Text, { children: ' ' }), _jsx(Text, { dimColor: true, children: "Press Enter to continue" })] })), step === 'name' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Name your companion:" }), _jsx(Text, { dimColor: true, children: "Enter to continue" }), _jsx(Text, { children: ' ' }), _jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: '' }), _jsx(TextInputLib, { value: name, onChange: setName, onSubmit: () => { setStep('hat'); }, placeholder: species.label })] })] })), step === 'hat' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Choose a hat:" }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 to browse \u00B7 Enter to finish" }), bones.rarity === 'common' && (_jsx(Text, { dimColor: true, children: "More hats unlock at higher rarities!" })), _jsx(Text, { children: ' ' }), availableHats.map((h, i) => (_jsxs(Text, { color: i === hatIdx ? 'cyan' : undefined, children: [i === hatIdx ? '▶ ' : ' ', h, h !== 'none' && HAT_ART[h] ? ` ${HAT_ART[h]}` : ''] }, h)))] }))] }), _jsxs(Box, { flexDirection: "column", width: 16, borderStyle: "single", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { color: "cyan", dimColor: true, children: "Preview" }), _jsx(CybergotchiSprite, { bones: bones, config: previewConfig, state: previewState }), _jsxs(Text, { color: RARITY_COLORS[bones.rarity], dimColor: true, children: [name || species.label, " ", RARITY_STARS[bones.rarity]] })] })] }));
50
+ return (_jsxs(Box, { flexDirection: "row", gap: 2, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Text, { bold: true, color: "magenta", children: "\u25C6 Companion Setup" }), _jsx(Text, { dimColor: true, children: "Esc to skip" }), _jsx(Text, { children: " " }), step === "seed" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Enter a seed phrase:" }), _jsx(Text, { dimColor: true, children: "This determines your species, rarity, and stats." }), _jsx(Text, { dimColor: true, children: "Same seed = same companion. Press Enter to continue." }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "" }), _jsx(TextInputLib, { value: seed, onChange: setSeed, onSubmit: () => setStep("reveal"), placeholder: getDefaultSeed() })] })] })), step === "reveal" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Your companion has been determined!" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Species:", " ", _jsx(Text, { color: RARITY_COLORS[bones.rarity], bold: true, children: species.label })] }), _jsxs(Text, { children: ["Rarity:", " ", _jsxs(Text, { color: RARITY_COLORS[bones.rarity], children: [bones.rarity, " ", RARITY_STARS[bones.rarity]] })] }), bones.isShiny && (_jsx(Text, { color: "yellow", bold: true, children: "\u2728 SHINY! \u2728" })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Stats:" }), _jsxs(Text, { children: [" DEBUGGING: ", bones.baseStats.DEBUGGING] }), _jsxs(Text, { children: [" PATIENCE: ", bones.baseStats.PATIENCE] }), _jsxs(Text, { children: [" CHAOS: ", bones.baseStats.CHAOS] }), _jsxs(Text, { children: [" WISDOM: ", bones.baseStats.WISDOM] }), _jsxs(Text, { children: [" SNARK: ", bones.baseStats.SNARK] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: species.traitHint }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Press Enter to continue" })] })), step === "name" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Name your companion:" }), _jsx(Text, { dimColor: true, children: "Enter to continue" }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "" }), _jsx(TextInputLib, { value: name, onChange: setName, onSubmit: () => {
51
+ setStep("hat");
52
+ }, placeholder: species.label })] })] })), step === "hat" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Choose a hat:" }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 to browse \u00B7 Enter to finish" }), bones.rarity === "common" && _jsx(Text, { dimColor: true, children: "More hats unlock at higher rarities!" }), _jsx(Text, { children: " " }), availableHats.map((h, i) => (_jsxs(Text, { color: i === hatIdx ? "cyan" : undefined, children: [i === hatIdx ? "▶ " : " ", h, h !== "none" && HAT_ART[h] ? ` ${HAT_ART[h]}` : ""] }, h)))] }))] }), _jsxs(Box, { flexDirection: "column", width: 16, borderStyle: "single", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { color: "cyan", dimColor: true, children: "Preview" }), _jsx(CybergotchiSprite, { bones: bones, config: previewConfig, state: previewState }), _jsxs(Text, { color: RARITY_COLORS[bones.rarity], dimColor: true, children: [name || species.label, " ", RARITY_STARS[bones.rarity]] })] })] }));
51
53
  }
52
54
  //# sourceMappingURL=CybergotchiSetup.js.map
@@ -1,4 +1,4 @@
1
- import type { CompanionBones, CompanionConfig, CompanionState } from '../cybergotchi/types.js';
1
+ import type { CompanionBones, CompanionConfig, CompanionState } from "../cybergotchi/types.js";
2
2
  interface Props {
3
3
  bones: CompanionBones;
4
4
  config: CompanionConfig;
@@ -1,25 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
3
- import { EYE_STYLES, HAT_ART, RARITY_COLORS } from '../cybergotchi/types.js';
4
- import { getSpecies } from '../cybergotchi/species.js';
2
+ import { Box, Text } from "ink";
3
+ import { getSpecies } from "../cybergotchi/species.js";
4
+ import { EYE_STYLES, HAT_ART, RARITY_COLORS } from "../cybergotchi/types.js";
5
5
  export default function CybergotchiSprite({ bones, config, state }) {
6
6
  const species = getSpecies(bones.species);
7
7
  const frames = species.frames[state.emotion];
8
8
  const frame = frames[state.frame % frames.length] ?? frames[0];
9
- const eyes = EYE_STYLES[bones.eyeStyle % EYE_STYLES.length] ?? 'o o';
9
+ const eyes = EYE_STYLES[bones.eyeStyle % EYE_STYLES.length] ?? "o o";
10
10
  // Inject eyes into frame lines
11
- const lines = frame.map(line => line.replace('{E}', eyes));
11
+ const lines = frame.map((line) => line.replace("{E}", eyes));
12
12
  // Hat — stage 2 forces crown if no hat set
13
- const hatKey = config.evolutionStage === 2 && config.soul.hat === 'none' ? 'crown' : config.soul.hat;
13
+ const hatKey = config.evolutionStage === 2 && config.soul.hat === "none" ? "crown" : config.soul.hat;
14
14
  const hat = HAT_ART[hatKey];
15
15
  // Color based on rarity (overridden by evolution stage for higher stages)
16
- const spriteColor = config.evolutionStage === 2
17
- ? 'yellow'
18
- : config.evolutionStage === 1
19
- ? 'magenta'
20
- : RARITY_COLORS[bones.rarity];
16
+ const spriteColor = config.evolutionStage === 2 ? "yellow" : config.evolutionStage === 1 ? "magenta" : RARITY_COLORS[bones.rarity];
21
17
  // Shiny: cycle colors for a shimmer effect
22
- const shinyColors = ['red', 'yellow', 'green', 'cyan', 'blue', 'magenta'];
18
+ const shinyColors = ["red", "yellow", "green", "cyan", "blue", "magenta"];
23
19
  const shinyColor = bones.isShiny ? shinyColors[state.frame % shinyColors.length] : undefined;
24
20
  const color = shinyColor ?? spriteColor;
25
21
  return (_jsxs(Box, { flexDirection: "column", children: [hat && _jsx(Text, { color: "yellow", children: hat }), lines.map((line, i) => (_jsx(Text, { color: color, children: line }, i)))] }));
@@ -1,4 +1,4 @@
1
- import { computeDiff, filterWithContext } from '../utils/diff-algorithm.js';
1
+ import { computeDiff, filterWithContext } from "../utils/diff-algorithm.js";
2
2
  interface Props {
3
3
  oldContent: string;
4
4
  newContent: string;
@@ -1,20 +1,20 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
3
- import { computeDiff, filterWithContext } from '../utils/diff-algorithm.js';
2
+ import { Box, Text } from "ink";
3
+ import { computeDiff, filterWithContext } from "../utils/diff-algorithm.js";
4
4
  export default function DiffView({ oldContent, newContent, filePath, maxLines = 30 }) {
5
5
  const rawDiff = computeDiff(oldContent, newContent);
6
6
  const filtered = filterWithContext(rawDiff);
7
7
  const display = filtered.slice(0, maxLines);
8
8
  const truncated = filtered.length > maxLines;
9
- const adds = rawDiff.filter(d => d.type === 'add').length;
10
- const removes = rawDiff.filter(d => d.type === 'remove').length;
11
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ['─── ', filePath, ' ───'] }), _jsxs(Text, { children: [_jsx(Text, { color: "green", children: `+${adds}` }), ' ', _jsx(Text, { color: "red", children: `-${removes}` })] }), display.map((d, i) => {
12
- if (d.type === 'separator') {
13
- return _jsx(Text, { dimColor: true, children: ' ...' }, i);
9
+ const adds = rawDiff.filter((d) => d.type === "add").length;
10
+ const removes = rawDiff.filter((d) => d.type === "remove").length;
11
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["─── ", filePath, " ───"] }), _jsxs(Text, { children: [_jsx(Text, { color: "green", children: `+${adds}` }), " ", _jsx(Text, { color: "red", children: `-${removes}` })] }), display.map((d, i) => {
12
+ if (d.type === "separator") {
13
+ return (_jsx(Text, { dimColor: true, children: " ..." }, i));
14
14
  }
15
- const prefix = d.type === 'add' ? '+ ' : d.type === 'remove' ? '- ' : ' ';
16
- const color = d.type === 'add' ? 'green' : d.type === 'remove' ? 'red' : undefined;
17
- return _jsxs(Text, { color: color, children: [prefix, d.line] }, i);
15
+ const prefix = d.type === "add" ? "+ " : d.type === "remove" ? "- " : " ";
16
+ const color = d.type === "add" ? "green" : d.type === "remove" ? "red" : undefined;
17
+ return (_jsxs(Text, { color: color, children: [prefix, d.line] }, i));
18
18
  }), truncated && _jsx(Text, { dimColor: true, children: ` ... (${filtered.length - maxLines} more lines)` })] }));
19
19
  }
20
20
  export { computeDiff, filterWithContext };
@@ -11,7 +11,7 @@ type State = {
11
11
  export declare class ErrorBoundary extends React.Component<Props, State> {
12
12
  state: State;
13
13
  static getDerivedStateFromError(error: Error): State;
14
- render(): string | number | boolean | Iterable<React.ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
14
+ render(): string | number | boolean | import("react/jsx-runtime").JSX.Element | Iterable<React.ReactNode> | null | undefined;
15
15
  }
16
16
  export {};
17
17
  //# sourceMappingURL=ErrorBoundary.d.ts.map
@@ -2,8 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * React error boundary for graceful crash handling.
4
4
  */
5
- import React from "react";
6
5
  import { Box, Text } from "ink";
6
+ import React from "react";
7
7
  export class ErrorBoundary extends React.Component {
8
8
  state = { error: null };
9
9
  static getDerivedStateFromError(error) {
@@ -11,18 +11,42 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  * 6. Cybergotchi hatch (Y/n)
12
12
  * 7. Summary + write .oh/config.yaml
13
13
  */
14
- import { useState, useCallback } from "react";
15
14
  import { Box, Text, useInput } from "ink";
16
15
  import TextInput from "ink-text-input";
16
+ import { useCallback, useState } from "react";
17
17
  import { writeOhConfig } from "../harness/config.js";
18
18
  import CybergotchiSetup from "./CybergotchiSetup.js";
19
19
  const PROVIDERS = [
20
- { key: "ollama", label: "Ollama (local, free)", defaultModel: "llama3", needsApiKey: false, defaultBaseUrl: "http://localhost:11434" },
20
+ {
21
+ key: "ollama",
22
+ label: "Ollama (local, free)",
23
+ defaultModel: "llama3",
24
+ needsApiKey: false,
25
+ defaultBaseUrl: "http://localhost:11434",
26
+ },
21
27
  { key: "openai", label: "OpenAI", defaultModel: "gpt-4o", needsApiKey: true },
22
28
  { key: "anthropic", label: "Anthropic (Claude)", defaultModel: "claude-sonnet-4-6", needsApiKey: true },
23
- { key: "openrouter", label: "OpenRouter", defaultModel: "openai/gpt-4o", needsApiKey: true, defaultBaseUrl: "https://openrouter.ai/api/v1" },
24
- { key: "llamacpp", label: "llama.cpp / GGUF (local, no Ollama needed)", defaultModel: "", needsApiKey: false, defaultBaseUrl: "http://localhost:8080" },
25
- { key: "lmstudio", label: "LM Studio (local, OpenAI-compatible)", defaultModel: "", needsApiKey: false, defaultBaseUrl: "http://localhost:1234" },
29
+ {
30
+ key: "openrouter",
31
+ label: "OpenRouter",
32
+ defaultModel: "openai/gpt-4o",
33
+ needsApiKey: true,
34
+ defaultBaseUrl: "https://openrouter.ai/api/v1",
35
+ },
36
+ {
37
+ key: "llamacpp",
38
+ label: "llama.cpp / GGUF (local, no Ollama needed)",
39
+ defaultModel: "",
40
+ needsApiKey: false,
41
+ defaultBaseUrl: "http://localhost:8080",
42
+ },
43
+ {
44
+ key: "lmstudio",
45
+ label: "LM Studio (local, OpenAI-compatible)",
46
+ defaultModel: "",
47
+ needsApiKey: false,
48
+ defaultBaseUrl: "http://localhost:1234",
49
+ },
26
50
  { key: "custom", label: "Custom (OpenAI-compatible)", defaultModel: "", needsApiKey: true },
27
51
  ];
28
52
  const PERMISSION_MODES = [
@@ -33,22 +57,22 @@ const PERMISSION_MODES = [
33
57
  /** Auto-detect provider from environment variables */
34
58
  function detectProviderFromEnv() {
35
59
  if (process.env.ANTHROPIC_API_KEY)
36
- return PROVIDERS.findIndex(p => p.key === "anthropic");
60
+ return PROVIDERS.findIndex((p) => p.key === "anthropic");
37
61
  if (process.env.OPENAI_API_KEY)
38
- return PROVIDERS.findIndex(p => p.key === "openai");
62
+ return PROVIDERS.findIndex((p) => p.key === "openai");
39
63
  if (process.env.OPENROUTER_API_KEY)
40
- return PROVIDERS.findIndex(p => p.key === "openrouter");
64
+ return PROVIDERS.findIndex((p) => p.key === "openrouter");
41
65
  return 0; // Default to Ollama
42
66
  }
43
67
  /** Get the detected API key for a provider */
44
68
  function getEnvApiKey(providerKey) {
45
69
  const envMap = {
46
- anthropic: 'ANTHROPIC_API_KEY',
47
- openai: 'OPENAI_API_KEY',
48
- openrouter: 'OPENROUTER_API_KEY',
70
+ anthropic: "ANTHROPIC_API_KEY",
71
+ openai: "OPENAI_API_KEY",
72
+ openrouter: "OPENROUTER_API_KEY",
49
73
  };
50
74
  const envVar = envMap[providerKey];
51
- return envVar ? (process.env[envVar] ?? '') : '';
75
+ return envVar ? (process.env[envVar] ?? "") : "";
52
76
  }
53
77
  export default function InitWizard({ onDone }) {
54
78
  const detectedIdx = detectProviderFromEnv();
@@ -72,9 +96,9 @@ export default function InitWizard({ onDone }) {
72
96
  useInput(useCallback((input, key) => {
73
97
  if (step === "provider") {
74
98
  if (key.upArrow)
75
- setProviderIdx(i => Math.max(0, i - 1));
99
+ setProviderIdx((i) => Math.max(0, i - 1));
76
100
  if (key.downArrow)
77
- setProviderIdx(i => Math.min(PROVIDERS.length - 1, i + 1));
101
+ setProviderIdx((i) => Math.min(PROVIDERS.length - 1, i + 1));
78
102
  if (key.return) {
79
103
  setBaseUrl(provider.defaultBaseUrl ?? "");
80
104
  setModel(provider.defaultModel);
@@ -98,12 +122,12 @@ export default function InitWizard({ onDone }) {
98
122
  }
99
123
  if (step === "permission") {
100
124
  if (key.upArrow)
101
- setPermIdx(i => Math.max(0, i - 1));
125
+ setPermIdx((i) => Math.max(0, i - 1));
102
126
  if (key.downArrow)
103
- setPermIdx(i => Math.min(PERMISSION_MODES.length - 1, i + 1));
127
+ setPermIdx((i) => Math.min(PERMISSION_MODES.length - 1, i + 1));
104
128
  if (key.return) {
105
129
  // Suggest popular MCP servers
106
- setSuggestedMcp(['github', 'memory', 'fetch', 'sequential-thinking', 'brave-search']);
130
+ setSuggestedMcp(["github", "memory", "fetch", "sequential-thinking", "brave-search"]);
107
131
  setMcpIdx(0);
108
132
  setSelectedMcp(new Set());
109
133
  setStep("mcp");
@@ -111,14 +135,14 @@ export default function InitWizard({ onDone }) {
111
135
  }
112
136
  if (step === "mcp") {
113
137
  if (key.upArrow)
114
- setMcpIdx(i => Math.max(0, i - 1));
138
+ setMcpIdx((i) => Math.max(0, i - 1));
115
139
  if (key.downArrow)
116
- setMcpIdx(i => Math.min(suggestedMcp.length - 1, i + 1));
140
+ setMcpIdx((i) => Math.min(suggestedMcp.length - 1, i + 1));
117
141
  if (input === " ") {
118
142
  // Toggle selection
119
143
  const name = suggestedMcp[mcpIdx];
120
144
  if (name) {
121
- setSelectedMcp(prev => {
145
+ setSelectedMcp((prev) => {
122
146
  const next = new Set(prev);
123
147
  if (next.has(name))
124
148
  next.delete(name);
@@ -135,9 +159,9 @@ export default function InitWizard({ onDone }) {
135
159
  }
136
160
  if (step === "model" && availableModels.length > 0) {
137
161
  if (key.upArrow)
138
- setModelIdx(i => Math.max(0, i - 1));
162
+ setModelIdx((i) => Math.max(0, i - 1));
139
163
  if (key.downArrow)
140
- setModelIdx(i => Math.min(availableModels.length - 1, i + 1));
164
+ setModelIdx((i) => Math.min(availableModels.length - 1, i + 1));
141
165
  if (key.return) {
142
166
  setModel(availableModels[modelIdx] ?? model);
143
167
  setStep("permission");
@@ -185,18 +209,20 @@ export default function InitWizard({ onDone }) {
185
209
  let mcpServers;
186
210
  if (selectedMcp.size > 0) {
187
211
  try {
188
- const { MCP_REGISTRY } = require('../mcp/registry.js');
212
+ const { MCP_REGISTRY } = require("../mcp/registry.js");
189
213
  mcpServers = [...selectedMcp]
190
- .map(name => MCP_REGISTRY.find((e) => e.name === name))
214
+ .map((name) => MCP_REGISTRY.find((e) => e.name === name))
191
215
  .filter(Boolean)
192
216
  .map((e) => ({
193
217
  name: e.name,
194
- command: 'npx',
195
- args: ['-y', e.package, ...(e.args ?? [])],
218
+ command: "npx",
219
+ args: ["-y", e.package, ...(e.args ?? [])],
196
220
  ...(e.envVars?.length ? { env: Object.fromEntries(e.envVars.map((v) => [v, `YOUR_${v}`])) } : {}),
197
221
  }));
198
222
  }
199
- catch { /* ignore */ }
223
+ catch {
224
+ /* ignore */
225
+ }
200
226
  }
201
227
  writeOhConfig({
202
228
  provider: provider.key,
@@ -211,12 +237,18 @@ export default function InitWizard({ onDone }) {
211
237
  }, [provider, model, availableModels, modelIdx, permIdx, apiKey, baseUrl, selectedMcp]);
212
238
  // ── Render ──
213
239
  if (showSetup) {
214
- return _jsx(CybergotchiSetup, { onComplete: () => { setShowSetup(false); writeFinal(); }, onSkip: () => { setShowSetup(false); writeFinal(); } });
240
+ return (_jsx(CybergotchiSetup, { onComplete: () => {
241
+ setShowSetup(false);
242
+ writeFinal();
243
+ }, onSkip: () => {
244
+ setShowSetup(false);
245
+ writeFinal();
246
+ } }));
215
247
  }
216
248
  if (step === "done") {
217
249
  return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { color: "green", children: "\u2713 OpenHarness configured!" }), _jsx(Text, { dimColor: true, children: "Config saved to .oh/config.yaml" }), _jsx(Text, { dimColor: true, children: "Run: oh" })] }));
218
250
  }
219
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "OpenHarness Setup" }), _jsx(Text, { children: " " }), step === "provider" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Select provider:", detectedIdx > 0 ? _jsx(Text, { dimColor: true, children: " (auto-detected from env)" }) : ''] }), PROVIDERS.map((p, i) => (_jsxs(Text, { color: i === providerIdx ? "cyan" : undefined, children: [i === providerIdx ? "▶ " : " ", p.label] }, p.key))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "apikey" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["API key for ", _jsx(Text, { color: "cyan", children: provider.label }), ":"] }), _jsx(TextInput, { value: apiKey, onChange: setApiKey, mask: "*", onSubmit: (val) => {
251
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "OpenHarness Setup" }), _jsx(Text, { children: " " }), step === "provider" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Select provider:", detectedIdx > 0 ? _jsx(Text, { dimColor: true, children: " (auto-detected from env)" }) : ""] }), PROVIDERS.map((p, i) => (_jsxs(Text, { color: i === providerIdx ? "cyan" : undefined, children: [i === providerIdx ? "▶ " : " ", p.label] }, p.key))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "apikey" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["API key for ", _jsx(Text, { color: "cyan", children: provider.label }), ":"] }), _jsx(TextInput, { value: apiKey, onChange: setApiKey, mask: "*", onSubmit: (val) => {
220
252
  if (!val.trim())
221
253
  return;
222
254
  if (provider.key === "custom") {
@@ -229,14 +261,14 @@ export default function InitWizard({ onDone }) {
229
261
  } })] })), step === "baseurl" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Base URL ", _jsx(Text, { dimColor: true, children: "(e.g. http://localhost:8080/v1)" }), ":"] }), _jsx(TextInput, { value: baseUrl, onChange: setBaseUrl, onSubmit: (val) => {
230
262
  runTest(provider, apiKey, val);
231
263
  setStep("testing");
232
- } })] })), step === "testing" && provider.key === "llamacpp" && testStatus !== "ok" && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [_jsx(Text, { color: "cyan", children: "llama.cpp setup" }), _jsx(Text, { dimColor: true, children: "To use llama.cpp, start llama-server first:" }), _jsx(Text, { dimColor: true, children: " llama-server --model ./your-model.gguf --port 8080 --alias my-model" }), _jsx(Text, { dimColor: true, children: "Then enter \"my-model\" as the model name below." })] })), step === "testing" && provider.key === "lmstudio" && testStatus !== "ok" && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [_jsx(Text, { color: "cyan", children: "LM Studio setup" }), _jsx(Text, { dimColor: true, children: "Enable the local server in LM Studio:" }), _jsx(Text, { dimColor: true, children: " Settings \u2192 Local Server \u2192 Start Server (port 1234)" }), _jsx(Text, { dimColor: true, children: "Then load a model and set the model name below." })] })), step === "testing" && (_jsxs(Box, { flexDirection: "column", children: [testStatus === "testing" && _jsxs(Text, { color: "yellow", children: ["\u27F3 Testing connection to ", provider.label, "..."] }), testStatus === "ok" && _jsx(Text, { color: "green", children: "\u2713 Connected!" }), testStatus === "fail" && provider.key !== "llamacpp" && provider.key !== "lmstudio" && (_jsxs(Text, { color: "red", children: ["\u2717 Failed: ", _jsx(Text, { dimColor: true, children: testError })] })), testStatus === "fail" && provider.key === "lmstudio" && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "red", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "red", children: "\u2717 Could not connect to LM Studio." }), _jsx(Text, { dimColor: true, children: testError }), _jsx(Text, { children: " " }), _jsx(Text, { color: "yellow", children: "Make sure LM Studio local server is running:" }), _jsx(Text, { dimColor: true, children: " Settings \u2192 Local Server \u2192 Start Server (port 1234)" })] })), testStatus === "fail" && provider.key === "llamacpp" && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "red", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "red", children: "\u2717 Could not connect to llama-server." }), _jsx(Text, { dimColor: true, children: testError }), _jsx(Text, { children: " " }), _jsx(Text, { color: "yellow", children: "Make sure llama-server is running:" }), _jsx(Text, { dimColor: true, children: " llama-server --model ./your-model.gguf --port 8080 --alias my-model" })] }))] })), step === "model" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Select model:" }), availableModels.length > 0 ? (() => {
264
+ } })] })), step === "testing" && provider.key === "llamacpp" && testStatus !== "ok" && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [_jsx(Text, { color: "cyan", children: "llama.cpp setup" }), _jsx(Text, { dimColor: true, children: "To use llama.cpp, start llama-server first:" }), _jsx(Text, { dimColor: true, children: " llama-server --model ./your-model.gguf --port 8080 --alias my-model" }), _jsx(Text, { dimColor: true, children: "Then enter \"my-model\" as the model name below." })] })), step === "testing" && provider.key === "lmstudio" && testStatus !== "ok" && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [_jsx(Text, { color: "cyan", children: "LM Studio setup" }), _jsx(Text, { dimColor: true, children: "Enable the local server in LM Studio:" }), _jsx(Text, { dimColor: true, children: " Settings \u2192 Local Server \u2192 Start Server (port 1234)" }), _jsx(Text, { dimColor: true, children: "Then load a model and set the model name below." })] })), step === "testing" && (_jsxs(Box, { flexDirection: "column", children: [testStatus === "testing" && _jsxs(Text, { color: "yellow", children: ["\u27F3 Testing connection to ", provider.label, "..."] }), testStatus === "ok" && _jsx(Text, { color: "green", children: "\u2713 Connected!" }), testStatus === "fail" && provider.key !== "llamacpp" && provider.key !== "lmstudio" && (_jsxs(Text, { color: "red", children: ["\u2717 Failed: ", _jsx(Text, { dimColor: true, children: testError })] })), testStatus === "fail" && provider.key === "lmstudio" && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "red", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "red", children: "\u2717 Could not connect to LM Studio." }), _jsx(Text, { dimColor: true, children: testError }), _jsx(Text, { children: " " }), _jsx(Text, { color: "yellow", children: "Make sure LM Studio local server is running:" }), _jsx(Text, { dimColor: true, children: " Settings \u2192 Local Server \u2192 Start Server (port 1234)" })] })), testStatus === "fail" && provider.key === "llamacpp" && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "red", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "red", children: "\u2717 Could not connect to llama-server." }), _jsx(Text, { dimColor: true, children: testError }), _jsx(Text, { children: " " }), _jsx(Text, { color: "yellow", children: "Make sure llama-server is running:" }), _jsx(Text, { dimColor: true, children: " llama-server --model ./your-model.gguf --port 8080 --alias my-model" })] }))] })), step === "model" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Select model:" }), availableModels.length > 0 ? ((() => {
233
265
  const WINDOW = 8;
234
266
  const start = Math.max(0, Math.min(modelIdx - Math.floor(WINDOW / 2), availableModels.length - WINDOW));
235
267
  const visible = availableModels.slice(start, start + WINDOW);
236
- return (_jsxs(Box, { flexDirection: "column", children: [start > 0 && _jsxs(Text, { dimColor: true, children: [" \u2191 ", start, " more"] }), visible.map((m, vi) => {
268
+ return (_jsxs(Box, { flexDirection: "column", children: [start > 0 && _jsxs(Text, { dimColor: true, children: [" \u2191 ", start, " more"] }), visible.map((m, vi) => {
237
269
  const gi = start + vi;
238
270
  return (_jsxs(Text, { color: gi === modelIdx ? "cyan" : undefined, children: [gi === modelIdx ? "▶ " : " ", m] }, m));
239
- }), start + WINDOW < availableModels.length && (_jsxs(Text, { dimColor: true, children: [" \u2193 ", availableModels.length - start - WINDOW, " more"] }))] }));
240
- })() : (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Could not fetch model list. Enter model name:" }), _jsx(TextInput, { value: model, onChange: setModel, onSubmit: () => setStep("permission") })] })), availableModels.length > 0 && _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "permission" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Permission mode:" }), PERMISSION_MODES.map((p, i) => (_jsxs(Text, { color: i === permIdx ? "cyan" : undefined, children: [i === permIdx ? "▶ " : " ", p.label] }, p.key))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "mcp" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Add MCP servers? ", _jsx(Text, { dimColor: true, children: "(Space to toggle, Enter to confirm, S to skip)" })] }), _jsx(Text, { children: " " }), suggestedMcp.map((name, i) => (_jsxs(Text, { color: i === mcpIdx ? "cyan" : undefined, children: [i === mcpIdx ? "▶ " : " ", selectedMcp.has(name) ? "[✓] " : "[ ] ", name] }, name))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Space toggle Enter confirm S skip" })] })), step === "gotchi" && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: ["Hatch a cybergotchi companion? ", _jsx(Text, { dimColor: true, children: "(Y/n)" })] }) }))] }));
271
+ }), start + WINDOW < availableModels.length && (_jsxs(Text, { dimColor: true, children: [" \u2193 ", availableModels.length - start - WINDOW, " more"] }))] }));
272
+ })()) : (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Could not fetch model list. Enter model name:" }), _jsx(TextInput, { value: model, onChange: setModel, onSubmit: () => setStep("permission") })] })), availableModels.length > 0 && _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "permission" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Permission mode:" }), PERMISSION_MODES.map((p, i) => (_jsxs(Text, { color: i === permIdx ? "cyan" : undefined, children: [i === permIdx ? "▶ " : " ", p.label] }, p.key))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "mcp" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Add MCP servers? ", _jsx(Text, { dimColor: true, children: "(Space to toggle, Enter to confirm, S to skip)" })] }), _jsx(Text, { children: " " }), suggestedMcp.map((name, i) => (_jsxs(Text, { color: i === mcpIdx ? "cyan" : undefined, children: [i === mcpIdx ? "▶ " : " ", selectedMcp.has(name) ? "[✓] " : "[ ] ", name] }, name))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Space toggle Enter confirm S skip" })] })), step === "gotchi" && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: ["Hatch a cybergotchi companion? ", _jsx(Text, { dimColor: true, children: "(Y/n)" })] }) }))] }));
241
273
  }
242
274
  //# sourceMappingURL=InitWizard.js.map
@@ -69,9 +69,7 @@ function TokenView({ token, theme, }) {
69
69
  }
70
70
  }
71
71
  const pad = (s, w) => s + " ".repeat(Math.max(0, w - s.length));
72
- const headerLine = t.header
73
- .map((h, c) => pad(cleanInline(h.text), widths[c]))
74
- .join(" | ");
72
+ const headerLine = t.header.map((h, c) => pad(cleanInline(h.text), widths[c])).join(" | ");
75
73
  const sepLine = widths.map((w) => "─".repeat(w)).join("─┼─");
76
74
  return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: theme.text, children: headerLine }), _jsx(Text, { color: theme.dim, children: sepLine }), t.rows.map((row, ri) => (_jsx(Text, { color: theme.text, children: row.map((cell, c) => pad(cleanInline(cell.text), widths[c])).join(" | ") }, ri)))] }));
77
75
  }
@@ -85,7 +83,7 @@ function TokenView({ token, theme, }) {
85
83
  return null;
86
84
  default: {
87
85
  const t = token;
88
- return t.text ? (_jsx(Text, { color: theme.text, children: cleanInline(t.text) })) : null;
86
+ return t.text ? _jsx(Text, { color: theme.text, children: cleanInline(t.text) }) : null;
89
87
  }
90
88
  }
91
89
  }