@zhijiewang/openharness 0.2.0 → 0.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 (157) hide show
  1. package/README.md +256 -160
  2. package/dist/Tool.d.ts +3 -1
  3. package/dist/Tool.d.ts.map +1 -1
  4. package/dist/Tool.js +1 -1
  5. package/dist/Tool.js.map +1 -1
  6. package/dist/commands/cybergotchi.d.ts +3 -0
  7. package/dist/commands/cybergotchi.d.ts.map +1 -0
  8. package/dist/commands/cybergotchi.js +75 -0
  9. package/dist/commands/cybergotchi.js.map +1 -0
  10. package/dist/commands/index.d.ts +2 -0
  11. package/dist/commands/index.d.ts.map +1 -1
  12. package/dist/commands/index.js +45 -0
  13. package/dist/commands/index.js.map +1 -1
  14. package/dist/components/CybergotchiBubble.d.ts +7 -0
  15. package/dist/components/CybergotchiBubble.d.ts.map +1 -0
  16. package/dist/components/CybergotchiBubble.js +27 -0
  17. package/dist/components/CybergotchiBubble.js.map +1 -0
  18. package/dist/components/CybergotchiPanel.d.ts +8 -0
  19. package/dist/components/CybergotchiPanel.d.ts.map +1 -0
  20. package/dist/components/CybergotchiPanel.js +15 -0
  21. package/dist/components/CybergotchiPanel.js.map +1 -0
  22. package/dist/components/CybergotchiSetup.d.ts +7 -0
  23. package/dist/components/CybergotchiSetup.d.ts.map +1 -0
  24. package/dist/components/CybergotchiSetup.js +80 -0
  25. package/dist/components/CybergotchiSetup.js.map +1 -0
  26. package/dist/components/CybergotchiSprite.d.ts +8 -0
  27. package/dist/components/CybergotchiSprite.d.ts.map +1 -0
  28. package/dist/components/CybergotchiSprite.js +20 -0
  29. package/dist/components/CybergotchiSprite.js.map +1 -0
  30. package/dist/components/InitWizard.d.ts +18 -0
  31. package/dist/components/InitWizard.d.ts.map +1 -0
  32. package/dist/components/InitWizard.js +151 -0
  33. package/dist/components/InitWizard.js.map +1 -0
  34. package/dist/components/REPL.d.ts.map +1 -1
  35. package/dist/components/REPL.js +91 -3
  36. package/dist/components/REPL.js.map +1 -1
  37. package/dist/components/ToolCallDisplay.d.ts +1 -0
  38. package/dist/components/ToolCallDisplay.d.ts.map +1 -1
  39. package/dist/components/ToolCallDisplay.js +8 -2
  40. package/dist/components/ToolCallDisplay.js.map +1 -1
  41. package/dist/cybergotchi/config.d.ts +5 -0
  42. package/dist/cybergotchi/config.d.ts.map +1 -0
  43. package/dist/cybergotchi/config.js +48 -0
  44. package/dist/cybergotchi/config.js.map +1 -0
  45. package/dist/cybergotchi/events.d.ts +15 -0
  46. package/dist/cybergotchi/events.d.ts.map +1 -0
  47. package/dist/cybergotchi/events.js +14 -0
  48. package/dist/cybergotchi/events.js.map +1 -0
  49. package/dist/cybergotchi/needs.d.ts +10 -0
  50. package/dist/cybergotchi/needs.d.ts.map +1 -0
  51. package/dist/cybergotchi/needs.js +83 -0
  52. package/dist/cybergotchi/needs.js.map +1 -0
  53. package/dist/cybergotchi/species.d.ts +11 -0
  54. package/dist/cybergotchi/species.d.ts.map +1 -0
  55. package/dist/cybergotchi/species.js +356 -0
  56. package/dist/cybergotchi/species.js.map +1 -0
  57. package/dist/cybergotchi/speech.d.ts +4 -0
  58. package/dist/cybergotchi/speech.d.ts.map +1 -0
  59. package/dist/cybergotchi/speech.js +80 -0
  60. package/dist/cybergotchi/speech.js.map +1 -0
  61. package/dist/cybergotchi/types.d.ts +39 -0
  62. package/dist/cybergotchi/types.d.ts.map +1 -0
  63. package/dist/cybergotchi/types.js +29 -0
  64. package/dist/cybergotchi/types.js.map +1 -0
  65. package/dist/cybergotchi/useCybergotchi.d.ts +10 -0
  66. package/dist/cybergotchi/useCybergotchi.d.ts.map +1 -0
  67. package/dist/cybergotchi/useCybergotchi.js +99 -0
  68. package/dist/cybergotchi/useCybergotchi.js.map +1 -0
  69. package/dist/harness/config.d.ts +14 -0
  70. package/dist/harness/config.d.ts.map +1 -0
  71. package/dist/harness/config.js +24 -0
  72. package/dist/harness/config.js.map +1 -0
  73. package/dist/harness/cost.d.ts +4 -0
  74. package/dist/harness/cost.d.ts.map +1 -1
  75. package/dist/harness/cost.js +20 -0
  76. package/dist/harness/cost.js.map +1 -1
  77. package/dist/main.js +26 -16
  78. package/dist/main.js.map +1 -1
  79. package/dist/mcp/McpTool.d.ts +19 -0
  80. package/dist/mcp/McpTool.d.ts.map +1 -0
  81. package/dist/mcp/McpTool.js +40 -0
  82. package/dist/mcp/McpTool.js.map +1 -0
  83. package/dist/mcp/client.d.ts +15 -0
  84. package/dist/mcp/client.d.ts.map +1 -0
  85. package/dist/mcp/client.js +78 -0
  86. package/dist/mcp/client.js.map +1 -0
  87. package/dist/mcp/loader.d.ts +8 -0
  88. package/dist/mcp/loader.d.ts.map +1 -0
  89. package/dist/mcp/loader.js +52 -0
  90. package/dist/mcp/loader.js.map +1 -0
  91. package/dist/mcp/types.d.ts +35 -0
  92. package/dist/mcp/types.d.ts.map +1 -0
  93. package/dist/mcp/types.js +3 -0
  94. package/dist/mcp/types.js.map +1 -0
  95. package/dist/providers/anthropic.d.ts.map +1 -1
  96. package/dist/providers/anthropic.js +17 -6
  97. package/dist/providers/anthropic.js.map +1 -1
  98. package/dist/providers/index.d.ts +4 -2
  99. package/dist/providers/index.d.ts.map +1 -1
  100. package/dist/providers/index.js +3 -1
  101. package/dist/providers/index.js.map +1 -1
  102. package/dist/providers/openai.d.ts.map +1 -1
  103. package/dist/providers/openai.js +16 -5
  104. package/dist/providers/openai.js.map +1 -1
  105. package/dist/query.d.ts +1 -1
  106. package/dist/query.d.ts.map +1 -1
  107. package/dist/query.js +17 -3
  108. package/dist/query.js.map +1 -1
  109. package/dist/services/StreamingToolExecutor.d.ts +6 -2
  110. package/dist/services/StreamingToolExecutor.d.ts.map +1 -1
  111. package/dist/services/StreamingToolExecutor.js +11 -4
  112. package/dist/services/StreamingToolExecutor.js.map +1 -1
  113. package/dist/tools/BashTool/index.d.ts.map +1 -1
  114. package/dist/tools/BashTool/index.js +10 -2
  115. package/dist/tools/BashTool/index.js.map +1 -1
  116. package/dist/tools/ImageReadTool/index.d.ts +13 -0
  117. package/dist/tools/ImageReadTool/index.d.ts.map +1 -0
  118. package/dist/tools/ImageReadTool/index.js +59 -0
  119. package/dist/tools/ImageReadTool/index.js.map +1 -0
  120. package/dist/tools.d.ts.map +1 -1
  121. package/dist/tools.js +2 -0
  122. package/dist/tools.js.map +1 -1
  123. package/dist/types/events.d.ts +6 -1
  124. package/dist/types/events.d.ts.map +1 -1
  125. package/dist/types/message.d.ts +1 -1
  126. package/dist/types/message.js +1 -1
  127. package/dist/types/permissions.d.ts +2 -2
  128. package/dist/types/permissions.js +2 -2
  129. package/dist/utils/theme.d.ts +1 -1
  130. package/dist/utils/theme.js +1 -1
  131. package/package.json +9 -3
  132. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -27
  133. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -17
  134. package/.github/pull_request_template.md +0 -24
  135. package/.github/workflows/ci.yml +0 -31
  136. package/CHANGELOG.md +0 -18
  137. package/CODE_OF_CONDUCT.md +0 -43
  138. package/CONTRIBUTING.md +0 -55
  139. package/SECURITY.md +0 -21
  140. package/data/models.json +0 -74
  141. package/data/prompts/system.md +0 -25
  142. package/data/skills/code-review.md +0 -19
  143. package/data/skills/commit.md +0 -17
  144. package/data/skills/debug.md +0 -24
  145. package/data/skills/tdd.md +0 -22
  146. package/dist/components/StatusBar.d.ts +0 -7
  147. package/dist/components/StatusBar.d.ts.map +0 -1
  148. package/dist/components/StatusBar.js +0 -6
  149. package/dist/components/StatusBar.js.map +0 -1
  150. package/dist/utils/retry.d.ts +0 -10
  151. package/dist/utils/retry.d.ts.map +0 -1
  152. package/dist/utils/retry.js +0 -23
  153. package/dist/utils/retry.js.map +0 -1
  154. package/dist/utils/tokens.d.ts +0 -18
  155. package/dist/utils/tokens.d.ts.map +0 -1
  156. package/dist/utils/tokens.js +0 -57
  157. package/dist/utils/tokens.js.map +0 -1
package/README.md CHANGED
@@ -1,160 +1,256 @@
1
- # OpenHarness
2
-
3
- ```
4
- ___
5
- / \
6
- ( ) ___ ___ ___ _ _ _ _ _ ___ _ _ ___ ___ ___
7
- `~w~` / _ \| _ \| __| \| | || | /_\ | _ \ \| | __/ __/ __|
8
- (( )) | (_) | _/| _|| .` | __ |/ _ \| / .` | _|\__ \__ \
9
- ))(( \___/|_| |___|_|\_|_||_/_/ \_\_|_\_|\_|___|___/___/
10
- (( ))
11
- `--`
12
- ```
13
-
14
- Open-source terminal coding agent. Build your own Claude Code with any LLM.
15
-
16
- ![Status: Alpha](https://img.shields.io/badge/status-alpha-orange)
17
- ![Node.js 18+](https://img.shields.io/badge/node-18%2B-green)
18
- ![TypeScript](https://img.shields.io/badge/typescript-strict-blue)
19
- ![License: MIT](https://img.shields.io/badge/license-MIT-green)
20
-
21
- ---
22
-
23
- <video src="https://github.com/user-attachments/assets/ed19a2cc-14d3-4db3-aa5b-3dc07c444498" controls width="100%"></video>
24
-
25
- ---
26
-
27
- ## Quick Start
28
-
29
- ```bash
30
- npm install -g @zhijiewang/openharness
31
- oh
32
- ```
33
-
34
- That's it. Just type `oh` to start chatting with your local Ollama model.
35
-
36
- ```bash
37
- oh # auto-detect Ollama, start chatting
38
- oh --model ollama/qwen2.5:7b # specific model
39
- oh --model gpt-4o # use OpenAI (needs OPENAI_API_KEY)
40
- oh --trust # auto-approve all tool calls
41
- ```
42
-
43
- <!-- ![Demo](assets/demo.gif) -->
44
-
45
- ## Install
46
-
47
- Requires **Node.js 18+**.
48
-
49
- ```bash
50
- # From npm
51
- npm install -g @zhijiewang/openharness
52
-
53
- # From source
54
- git clone https://github.com/zhijiewong/openharness.git
55
- cd openharness
56
- npm install
57
- npm install -g .
58
- oh
59
- ```
60
-
61
- ## Why OpenHarness?
62
-
63
- Claude Code is powerful but locked to Anthropic. OpenHarness gives you the same architecture -- React+Ink terminal UI, async generator agent loop, Zod tool schemas, permission gates -- but works with **any LLM**. Local models via Ollama (free, offline, private), or cloud APIs (OpenAI, Anthropic, OpenRouter, DeepSeek, Groq, and any OpenAI-compatible endpoint).
64
-
65
- ## Tools
66
-
67
- | Tool | Risk | Description |
68
- |------|------|-------------|
69
- | Read | low | Read files with line ranges |
70
- | Edit | medium | Search-and-replace edits |
71
- | Write | medium | Create or overwrite files |
72
- | Bash | high | Shell commands with timeout |
73
- | Glob | low | Find files by pattern |
74
- | Grep | low | Regex content search |
75
- | WebFetch | medium | Fetch URL content |
76
-
77
- Low-risk tools auto-approve. Medium and high risk require confirmation in `ask` mode.
78
-
79
- ## Commands
80
-
81
- ```bash
82
- oh # start chatting (default command)
83
- oh --model MODEL # use a specific model
84
- oh --trust # auto-approve all tools
85
- oh --deny # block all non-read tools
86
- oh --resume ID # resume a saved session
87
- oh models # list models and pricing
88
- oh tools # list tools and risk levels
89
- oh init # set up .oh/ for current project
90
- oh sessions # list saved sessions
91
- oh rules # show project rules
92
- oh --version # show version
93
- ```
94
-
95
- ## Providers
96
-
97
- ```bash
98
- # Local (free, no API key)
99
- oh --model ollama/llama3
100
- oh --model ollama/qwen2.5:7b-instruct
101
-
102
- # Cloud (set API key as env var)
103
- OPENAI_API_KEY=sk-... oh --model gpt-4o
104
- ANTHROPIC_API_KEY=sk-ant-... oh --model claude-sonnet-4-6
105
- OPENROUTER_API_KEY=sk-or-... oh --model openrouter/deepseek-chat
106
- ```
107
-
108
- ## Project Rules
109
-
110
- Create `.oh/RULES.md` in any repo (or run `oh init`):
111
-
112
- ```markdown
113
- - Always run tests after changes
114
- - Use strict TypeScript
115
- - Never commit to main directly
116
- ```
117
-
118
- Rules load automatically into every session.
119
-
120
- ## Tech Stack
121
-
122
- | | OpenHarness | Claude Code |
123
- |---|---|---|
124
- | Language | TypeScript (strict) | TypeScript (strict) |
125
- | Runtime | Node.js 18+ | Bun |
126
- | Terminal UI | React + Ink | React + custom Ink fork |
127
- | Tool schemas | Zod | Zod |
128
- | Agent loop | async generators | async generators |
129
- | Providers | Any (5 built-in) | Anthropic only |
130
- | License | MIT | Proprietary |
131
-
132
- ## Development
133
-
134
- ```bash
135
- git clone https://github.com/zhijiewong/openharness.git
136
- cd openharness
137
- npm install
138
- npx tsx src/main.tsx # run in dev mode
139
- npx tsc --noEmit # type check
140
- ```
141
-
142
- ### Adding a provider
143
-
144
- Create `src/providers/yourprovider.ts` implementing the `Provider` interface, add a case in `src/providers/index.ts`.
145
-
146
- ### Adding a tool
147
-
148
- Create `src/tools/YourTool/index.ts` implementing the `Tool` interface with a Zod input schema, register it in `src/tools.ts`.
149
-
150
- ## Contributing
151
-
152
- See [CONTRIBUTING.md](CONTRIBUTING.md).
153
-
154
- ## License
155
-
156
- MIT
157
-
158
- ---
159
-
160
- This project is not affiliated with Anthropic.
1
+ # OpenHarness
2
+
3
+ ```
4
+ ___
5
+ / \
6
+ ( ) ___ ___ ___ _ _ _ _ _ ___ _ _ ___ ___ ___
7
+ `~w~` / _ \| _ \| __| \| | || | /_\ | _ \ \| | __/ __/ __|
8
+ (( )) | (_) | _/| _|| .` | __ |/ _ \| / .` | _|\__ \__ \
9
+ ))(( \___/|_| |___|_|\_|_||_/_/ \_\_|_\_|\_|___|___/___/
10
+ (( ))
11
+ `--`
12
+ ```
13
+
14
+ AI coding agent in your terminal. Works with any LLM -- free local models or cloud APIs.
15
+
16
+ ![npm](https://img.shields.io/npm/v/@zhijiewang/openharness)
17
+ ![Node.js 18+](https://img.shields.io/badge/node-18%2B-green)
18
+ ![TypeScript](https://img.shields.io/badge/typescript-strict-blue)
19
+ ![License: MIT](https://img.shields.io/badge/license-MIT-green)
20
+ ![Status: Alpha](https://img.shields.io/badge/status-alpha-orange)
21
+
22
+ ---
23
+
24
+ <video src="https://github.com/user-attachments/assets/ed19a2cc-14d3-4db3-aa5b-3dc07c444498" controls width="100%"></video>
25
+
26
+ *OpenHarness reading files, running commands, and editing code — powered by a local Ollama model.*
27
+
28
+ ---
29
+
30
+ ## Quick Start
31
+
32
+ ```bash
33
+ npm install -g @zhijiewang/openharness
34
+ oh
35
+ ```
36
+
37
+ That's it. OpenHarness auto-detects Ollama and starts chatting. No API key needed.
38
+
39
+ ```bash
40
+ oh init # interactive setup wizard (provider + cybergotchi)
41
+ oh # auto-detect local model
42
+ oh --model ollama/qwen2.5:7b # specific model
43
+ oh --model gpt-4o # cloud model (needs OPENAI_API_KEY)
44
+ oh --trust # auto-approve all tool calls
45
+ oh run "fix the tests" --json # headless mode for CI/CD
46
+ ```
47
+
48
+ ## Why OpenHarness?
49
+
50
+ Most AI coding agents are locked to one provider or cost $20+/month. OpenHarness works with any LLM -- run it free with Ollama on your own machine, or connect to any cloud API. Every AI edit is git-committed and reversible with `/undo`.
51
+
52
+ | | OpenHarness | Claude Code | Aider | OpenCode |
53
+ |---|---|---|---|---|
54
+ | Any LLM | Yes (Ollama, OpenAI, Anthropic, OpenRouter, any OpenAI-compatible) | Anthropic only | Yes | Yes |
55
+ | Free local models | Ollama native | No | Yes | Yes |
56
+ | Tools | 18 with permission gates | 40+ | File-focused | 20+ |
57
+ | Git integration | Auto-commit + /undo | Yes | Deep git | Basic |
58
+ | Slash commands | 16 built-in | 80+ | Some | Some |
59
+ | Headless/CI mode | `oh run --json` | Yes | Yes | Yes |
60
+ | Terminal UI | React + Ink | React + Ink | Basic | BubbleTea |
61
+ | Language | TypeScript | TypeScript | Python | Go |
62
+ | License | MIT | Proprietary | Apache 2.0 | MIT |
63
+ | Price | Free (BYOK) | $20+/month | Free (BYOK) | Free (BYOK) |
64
+
65
+ ## Tools (18)
66
+
67
+ | Tool | Risk | Description |
68
+ |------|------|-------------|
69
+ | Bash | high | Execute shell commands with live streaming output |
70
+ | Read | low | Read files with line ranges |
71
+ | ImageRead | low | Read images/PDFs for multimodal analysis |
72
+ | Write | medium | Create or overwrite files |
73
+ | Edit | medium | Search-and-replace edits |
74
+ | Glob | low | Find files by pattern |
75
+ | Grep | low | Regex content search |
76
+ | WebFetch | medium | Fetch URL content (SSRF-protected) |
77
+ | WebSearch | medium | Search the web |
78
+ | TaskCreate | low | Create structured tasks |
79
+ | TaskUpdate | low | Update task status |
80
+ | TaskList | low | List all tasks |
81
+ | AskUser | low | Ask user a question with options |
82
+ | Skill | low | Invoke a skill from .oh/skills/ |
83
+ | Agent | medium | Spawn a sub-agent for delegation |
84
+ | EnterPlanMode | low | Enter structured planning mode |
85
+ | ExitPlanMode | low | Exit planning mode |
86
+ | NotebookEdit | medium | Edit Jupyter notebooks |
87
+
88
+ Low-risk read-only tools auto-approve. Medium and high risk tools require confirmation in `ask` mode. Use `--trust` to skip all prompts.
89
+
90
+ ## Slash Commands (18)
91
+
92
+ Type these during a chat session:
93
+
94
+ | Command | Description |
95
+ |---------|-------------|
96
+ | `/help` | Show all available commands |
97
+ | `/clear` | Clear conversation history |
98
+ | `/cost` | Show session cost and token usage |
99
+ | `/status` | Show model, mode, git branch, MCP servers |
100
+ | `/diff` | Show uncommitted git changes |
101
+ | `/undo` | Undo last AI commit |
102
+ | `/commit [msg]` | Create a git commit |
103
+ | `/log` | Show recent git commits |
104
+ | `/history [n]` | List recent sessions; `/history search <term>` to search |
105
+ | `/files` | List files in context |
106
+ | `/model <name>` | Switch model mid-session |
107
+ | `/compact` | Compress conversation to free context |
108
+ | `/export` | Export conversation to markdown |
109
+ | `/plan` | Enter plan mode |
110
+ | `/review` | Review recent code changes |
111
+ | `/config` | Show configuration |
112
+ | `/memory` | View memories |
113
+ | `/cybergotchi` | Feed, pet, rest, status, rename, or reset your companion |
114
+
115
+ ## Cybergotchi
116
+
117
+ OpenHarness ships with a Tamagotchi-style companion that lives in the side panel. It reacts to your session in real time — celebrating streaks, complaining when tools fail, and getting hungry if you ignore it.
118
+
119
+ **Hatch one:**
120
+ ```
121
+ oh init # wizard includes cybergotchi setup
122
+ /cybergotchi # or hatch mid-session
123
+ ```
124
+
125
+ **Commands:**
126
+ ```
127
+ /cybergotchi feed # +30 hunger
128
+ /cybergotchi pet # +20 happiness
129
+ /cybergotchi rest # +40 energy
130
+ /cybergotchi status # show needs + lifetime stats
131
+ /cybergotchi rename # give it a new name
132
+ /cybergotchi reset # start over with a new species
133
+ ```
134
+
135
+ **Needs** decay over time (hunger fastest, happiness slowest). Feed and pet your gotchi to keep it happy.
136
+
137
+ **Evolution** — your gotchi evolves based on lifetime milestones:
138
+ - Stage 1 (✦ magenta): 10 sessions or 50 commits
139
+ - Stage 2 (★ yellow + crown): 100 tasks completed or a 25-tool streak
140
+
141
+ **18 species** to choose from: duck, cat, owl, penguin, rabbit, turtle, snail, octopus, axolotl, cactus, mushroom, chonk, capybara, goose, and more.
142
+
143
+ ## MCP Servers
144
+
145
+ Connect any MCP (Model Context Protocol) server by editing `.oh/config.yaml`:
146
+
147
+ ```yaml
148
+ provider: anthropic
149
+ model: claude-sonnet-4-6
150
+ permissionMode: ask
151
+ mcpServers:
152
+ - name: filesystem
153
+ command: npx
154
+ args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
155
+ - name: github
156
+ command: npx
157
+ args: ["-y", "@modelcontextprotocol/server-github"]
158
+ env:
159
+ GITHUB_PERSONAL_ACCESS_TOKEN: ghp_...
160
+ ```
161
+
162
+ MCP tools appear alongside built-in tools. `/status` shows connected servers.
163
+
164
+ ## Git Integration
165
+
166
+ OpenHarness auto-commits AI edits in git repos:
167
+
168
+ ```
169
+ oh: Edit src/app.ts # auto-committed with "oh:" prefix
170
+ oh: Write tests/app.test.ts
171
+ ```
172
+
173
+ - Every AI file change is committed automatically
174
+ - `/undo` reverts the last AI commit (only OH commits, never yours)
175
+ - `/diff` shows what changed
176
+ - Your dirty files are safe — committed separately before AI edits
177
+
178
+ ## Headless Mode
179
+
180
+ Run a single prompt without interactive UI — perfect for CI/CD:
181
+
182
+ ```bash
183
+ oh run "fix the failing tests" --model ollama/llama3 --trust
184
+ oh run "add error handling to api.ts" --json # JSON output
185
+ oh run "explain this codebase" --model gpt-4o
186
+ ```
187
+
188
+ Exit code 0 on success, 1 on failure.
189
+
190
+ ## Providers
191
+
192
+ ```bash
193
+ # Local (free, no API key needed)
194
+ oh --model ollama/llama3
195
+ oh --model ollama/qwen2.5:7b-instruct
196
+
197
+ # Cloud
198
+ OPENAI_API_KEY=sk-... oh --model gpt-4o
199
+ ANTHROPIC_API_KEY=sk-ant-... oh --model claude-sonnet-4-6
200
+ OPENROUTER_API_KEY=sk-or-... oh --model openrouter/deepseek-chat
201
+
202
+ # Any OpenAI-compatible endpoint
203
+ oh --model deepseek/deepseek-chat
204
+ ```
205
+
206
+ ## Project Rules
207
+
208
+ Create `.oh/RULES.md` in any repo (or run `oh init`):
209
+
210
+ ```markdown
211
+ - Always run tests after changes
212
+ - Use strict TypeScript
213
+ - Never commit to main directly
214
+ ```
215
+
216
+ Rules load automatically into every session.
217
+
218
+ ## Install
219
+
220
+ Requires **Node.js 18+**.
221
+
222
+ ```bash
223
+ # From npm
224
+ npm install -g @zhijiewang/openharness
225
+
226
+ # From source
227
+ git clone https://github.com/zhijiewong/openharness.git
228
+ cd openharness
229
+ npm install && npm install -g .
230
+ ```
231
+
232
+ ## Development
233
+
234
+ ```bash
235
+ npm install
236
+ npx tsx src/main.tsx # run in dev mode
237
+ npx tsc --noEmit # type check
238
+ npm test # run tests
239
+ ```
240
+
241
+ ### Adding a tool
242
+
243
+ Create `src/tools/YourTool/index.ts` implementing the `Tool` interface with a Zod input schema, register it in `src/tools.ts`.
244
+
245
+ ### Adding a provider
246
+
247
+ Create `src/providers/yourprovider.ts` implementing the `Provider` interface, add a case in `src/providers/index.ts`.
248
+
249
+ ## Contributing
250
+
251
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
252
+
253
+ ## License
254
+
255
+ MIT
256
+
package/dist/Tool.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Tool interface — mirrors Claude Code's Tool.ts pattern.
2
+ * Tool interface — defines how tools are registered, validated, and executed.
3
3
  * Every tool implements this interface with Zod input validation.
4
4
  */
5
5
  import type { z } from "zod";
@@ -11,6 +11,8 @@ export type ToolResult = {
11
11
  export type ToolContext = {
12
12
  workingDir: string;
13
13
  abortSignal?: AbortSignal;
14
+ callId?: string;
15
+ onOutputChunk?: (callId: string, chunk: string) => void;
14
16
  };
15
17
  export type Tool<Input extends z.ZodType = z.ZodType> = {
16
18
  readonly name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Tool.d.ts","sourceRoot":"","sources":["../src/Tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B,8DAA8D;IAC9D,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;IAE3C,8DAA8D;IAC9D,iBAAiB,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;IAElD,wBAAwB;IACxB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEvE,mDAAmD;IACnD,MAAM,IAAI,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC;AAE3B;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG;IAC3C,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,CAAC;CACtE,CASA;AA4CD;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAE3E"}
1
+ {"version":3,"file":"Tool.d.ts","sourceRoot":"","sources":["../src/Tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzD,CAAC;AAEF,MAAM,MAAM,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B,8DAA8D;IAC9D,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;IAE3C,8DAA8D;IAC9D,iBAAiB,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;IAElD,wBAAwB;IACxB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEvE,mDAAmD;IACnD,MAAM,IAAI,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC;AAE3B;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG;IAC3C,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,CAAC;CACtE,CASA;AA4CD;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAE3E"}
package/dist/Tool.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Tool interface — mirrors Claude Code's Tool.ts pattern.
2
+ * Tool interface — defines how tools are registered, validated, and executed.
3
3
  * Every tool implements this interface with Zod input validation.
4
4
  */
5
5
  /**
package/dist/Tool.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Tool.js","sourceRoot":"","sources":["../src/Tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoCH;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAU;IAIxC,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE;YACR,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,MAAM,EAAE;YAC1B,UAAU,EAAE,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;SAC9C;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,MAAiB;IACxC,wDAAwD;IACxD,MAAM,GAAG,GAAI,MAAc,CAAC,IAAI,CAAC;IAEjC,IAAI,GAAG,EAAE,QAAQ,KAAK,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAI,MAA2B,CAAC,KAAK,CAAC;QACjD,MAAM,UAAU,GAA4B,EAAE,CAAC;QAC/C,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,MAAM,KAAK,GAAG,KAAkB,CAAC;YACjC,MAAM,QAAQ,GAAI,KAAa,CAAC,IAAI,CAAC;YAErC,IAAI,QAAQ,EAAE,QAAQ,KAAK,aAAa,EAAE,CAAC;gBACzC,UAAU,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YACxD,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;gBACzC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;YAED,6BAA6B;YAC7B,IAAK,KAAa,CAAC,WAAW,EAAE,CAAC;gBAC9B,UAAU,CAAC,GAAG,CAAS,CAAC,WAAW,GAAI,KAAa,CAAC,WAAW,CAAC;YACpE,CAAC;QACH,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IAClD,CAAC;IAED,IAAI,GAAG,EAAE,QAAQ,KAAK,WAAW;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC7D,IAAI,GAAG,EAAE,QAAQ,KAAK,WAAW;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC7D,IAAI,GAAG,EAAE,QAAQ,KAAK,YAAY;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC/D,IAAI,GAAG,EAAE,QAAQ,KAAK,UAAU;QAC9B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;IAE7D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,WAAW;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAY,EAAE,IAAY;IACvD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"Tool.js","sourceRoot":"","sources":["../src/Tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAsCH;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAU;IAIxC,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE;YACR,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,MAAM,EAAE;YAC1B,UAAU,EAAE,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;SAC9C;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,MAAiB;IACxC,wDAAwD;IACxD,MAAM,GAAG,GAAI,MAAc,CAAC,IAAI,CAAC;IAEjC,IAAI,GAAG,EAAE,QAAQ,KAAK,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAI,MAA2B,CAAC,KAAK,CAAC;QACjD,MAAM,UAAU,GAA4B,EAAE,CAAC;QAC/C,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,MAAM,KAAK,GAAG,KAAkB,CAAC;YACjC,MAAM,QAAQ,GAAI,KAAa,CAAC,IAAI,CAAC;YAErC,IAAI,QAAQ,EAAE,QAAQ,KAAK,aAAa,EAAE,CAAC;gBACzC,UAAU,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YACxD,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;gBACzC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;YAED,6BAA6B;YAC7B,IAAK,KAAa,CAAC,WAAW,EAAE,CAAC;gBAC9B,UAAU,CAAC,GAAG,CAAS,CAAC,WAAW,GAAI,KAAa,CAAC,WAAW,CAAC;YACpE,CAAC;QACH,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IAClD,CAAC;IAED,IAAI,GAAG,EAAE,QAAQ,KAAK,WAAW;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC7D,IAAI,GAAG,EAAE,QAAQ,KAAK,WAAW;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC7D,IAAI,GAAG,EAAE,QAAQ,KAAK,YAAY;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC/D,IAAI,GAAG,EAAE,QAAQ,KAAK,UAAU;QAC9B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;IAE7D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,WAAW;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAY,EAAE,IAAY;IACvD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CommandResult } from './index.js';
2
+ export declare function handleCybergotchiCommand(args: string): CommandResult;
3
+ //# sourceMappingURL=cybergotchi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cybergotchi.d.ts","sourceRoot":"","sources":["../../src/commands/cybergotchi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAShD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CA2EpE"}
@@ -0,0 +1,75 @@
1
+ import { loadCybergotchiConfig, saveCybergotchiConfig } from '../cybergotchi/config.js';
2
+ import { adjustNeed } from '../cybergotchi/needs.js';
3
+ function needsBar(value) {
4
+ const filled = Math.round(value / 10);
5
+ return '█'.repeat(filled) + '░'.repeat(10 - filled) + ' ' + String(Math.round(value)).padStart(3);
6
+ }
7
+ export function handleCybergotchiCommand(args) {
8
+ const config = loadCybergotchiConfig();
9
+ if (!config) {
10
+ return { output: '', handled: true, openCybergotchiSetup: true };
11
+ }
12
+ const sub = args.trim().toLowerCase();
13
+ if (sub === 'feed') {
14
+ adjustNeed(config, 'hunger', 30);
15
+ saveCybergotchiConfig(config);
16
+ return { output: `${config.name} munches happily! 🍖 Hunger: ${Math.round(config.needs.hunger)}`, handled: true };
17
+ }
18
+ if (sub === 'pet') {
19
+ adjustNeed(config, 'happiness', 20);
20
+ saveCybergotchiConfig(config);
21
+ return { output: `${config.name} purrs with joy! 💛 Happiness: ${Math.round(config.needs.happiness)}`, handled: true };
22
+ }
23
+ if (sub === 'rest') {
24
+ adjustNeed(config, 'energy', 40);
25
+ saveCybergotchiConfig(config);
26
+ return { output: `${config.name} takes a nap... ⚡ Energy: ${Math.round(config.needs.energy)}`, handled: true };
27
+ }
28
+ if (sub === 'status') {
29
+ const { hunger, energy, happiness } = config.needs;
30
+ const { totalSessions, totalCommits, totalErrors, totalTasksCompleted, longestStreak } = config.lifetime;
31
+ const lines = [
32
+ `${config.name} (${config.species})`,
33
+ '',
34
+ `🍖 Hunger ${needsBar(hunger)}`,
35
+ `⚡ Energy ${needsBar(energy)}`,
36
+ `💛 Happiness ${needsBar(happiness)}`,
37
+ `🔥 Streak ${config.currentStreak} (best: ${longestStreak})`,
38
+ '',
39
+ 'Lifetime:',
40
+ ` Sessions: ${totalSessions}`,
41
+ ` Commits: ${totalCommits}`,
42
+ ` Errors: ${totalErrors}`,
43
+ ` Tasks: ${totalTasksCompleted}`,
44
+ ];
45
+ return { output: lines.join('\n'), handled: true };
46
+ }
47
+ if (sub.startsWith('rename ')) {
48
+ const newName = args.trim().slice(7).trim();
49
+ if (!newName)
50
+ return { output: 'Usage: /cybergotchi rename <name>', handled: true };
51
+ config.name = newName;
52
+ saveCybergotchiConfig(config);
53
+ return { output: `Renamed to ${newName}!`, handled: true };
54
+ }
55
+ if (sub === 'reset') {
56
+ return { output: '', handled: true, openCybergotchiSetup: true };
57
+ }
58
+ // Default: show summary
59
+ const statLines = Object.entries(config.stats)
60
+ .map(([k, v]) => ` ${k.padEnd(12)} ${'█'.repeat(Math.round(v / 10))}${'░'.repeat(10 - Math.round(v / 10))} ${v}`)
61
+ .join('\n');
62
+ return {
63
+ output: [
64
+ `${config.name} (${config.species}) | hat: ${config.hat}`,
65
+ `🍖 ${Math.round(config.needs.hunger)} ⚡ ${Math.round(config.needs.energy)} 💛 ${Math.round(config.needs.happiness)} 🔥 ${config.currentStreak}`,
66
+ '',
67
+ 'Personality stats:',
68
+ statLines,
69
+ '',
70
+ 'Commands: feed · pet · rest · status · rename <name> · reset',
71
+ ].join('\n'),
72
+ handled: true,
73
+ };
74
+ }
75
+ //# sourceMappingURL=cybergotchi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cybergotchi.js","sourceRoot":"","sources":["../../src/commands/cybergotchi.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,8 @@ export type CommandResult = {
16
16
  newModel?: string;
17
17
  /** If set, replace messages with compacted version */
18
18
  compactedMessages?: Message[];
19
+ /** If true, open the cybergotchi setup UI */
20
+ openCybergotchiSetup?: boolean;
19
21
  };
20
22
  export type CommandContext = {
21
23
  messages: Message[];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,MAAM,aAAa,GAAG;IAC1B,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,6BAA6B;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,OAAO,EAAE,CAAC;CAC/B,CAAC;AAIF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AA4KF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,aAAa,GAAG,IAAI,CAiBhG;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAOnD,MAAM,MAAM,aAAa,GAAG;IAC1B,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,6BAA6B;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,OAAO,EAAE,CAAC;IAC9B,6CAA6C;IAC7C,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAIF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAwNF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,aAAa,GAAG,IAAI,CAiBhG;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C"}
@@ -7,6 +7,11 @@
7
7
  import { writeFileSync, mkdirSync } from "node:fs";
8
8
  import { dirname } from "node:path";
9
9
  import { isGitRepo, gitDiff, gitUndo, gitCommit, gitLog, gitBranch } from "../git/index.js";
10
+ import { handleCybergotchiCommand } from "./cybergotchi.js";
11
+ import { connectedMcpServers } from "../mcp/loader.js";
12
+ import { listSessions, loadSession } from "../harness/session.js";
13
+ import { homedir } from "node:os";
14
+ import { join } from "node:path";
10
15
  const commands = new Map();
11
16
  function register(name, description, handler) {
12
17
  commands.set(name, { description, handler });
@@ -42,6 +47,10 @@ register("status", "Show session status", (_args, ctx) => {
42
47
  if (isGitRepo()) {
43
48
  lines.push(`Git branch: ${gitBranch()}`);
44
49
  }
50
+ const mcp = connectedMcpServers();
51
+ if (mcp.length > 0) {
52
+ lines.push(`MCP servers: ${mcp.join(', ')}`);
53
+ }
45
54
  return { output: lines.join("\n"), handled: true };
46
55
  });
47
56
  register("diff", "Show uncommitted git changes", () => {
@@ -75,6 +84,39 @@ register("log", "Show recent git commits", () => {
75
84
  }
76
85
  return { output: gitLog(10) || "No commits yet.", handled: true };
77
86
  });
87
+ register("history", "List recent sessions or search across them", (args) => {
88
+ const parts = args.trim().split(/\s+/);
89
+ const sessionDir = join(homedir(), ".oh", "sessions");
90
+ if (parts[0] === "search" && parts[1]) {
91
+ const term = parts.slice(1).join(" ").toLowerCase();
92
+ const sessions = listSessions(sessionDir);
93
+ const matches = [];
94
+ for (const s of sessions) {
95
+ try {
96
+ const full = loadSession(s.id, sessionDir);
97
+ const hit = full.messages.find(m => typeof m.content === "string" && m.content.toLowerCase().includes(term));
98
+ if (hit) {
99
+ const date = new Date(s.updatedAt).toLocaleDateString();
100
+ matches.push(` ${s.id} ${date} ${s.model || "?"}`);
101
+ }
102
+ }
103
+ catch { /* skip */ }
104
+ }
105
+ if (matches.length === 0)
106
+ return { output: `No sessions matching "${term}".`, handled: true };
107
+ return { output: `Sessions matching "${term}":\n${matches.join("\n")}`, handled: true };
108
+ }
109
+ const n = parseInt(parts[0] ?? "10", 10) || 10;
110
+ const sessions = listSessions(sessionDir).slice(0, n);
111
+ if (sessions.length === 0)
112
+ return { output: "No saved sessions.", handled: true };
113
+ const lines = sessions.map(s => {
114
+ const date = new Date(s.updatedAt).toLocaleDateString();
115
+ const cost = s.cost > 0 ? ` $${s.cost.toFixed(4)}` : "";
116
+ return ` ${s.id} ${date} ${String(s.messages).padStart(3)} msgs ${(s.model || "?").slice(0, 24)}${cost}`;
117
+ });
118
+ return { output: `Recent sessions (use /resume <id> to continue):\n${lines.join("\n")}`, handled: true };
119
+ });
78
120
  register("files", "List files in context", (_args, ctx) => {
79
121
  const files = new Set();
80
122
  for (const msg of ctx.messages) {
@@ -147,6 +189,9 @@ register("config", "Show current configuration", () => {
147
189
  register("memory", "View memories", () => {
148
190
  return { output: "Use: oh memory (in a new terminal)", handled: true };
149
191
  });
192
+ register("cybergotchi", "Manage your cybergotchi — feed · pet · rest · status · rename · reset", (args) => {
193
+ return handleCybergotchiCommand(args);
194
+ });
150
195
  register("plan", "Enter plan mode", () => {
151
196
  return { output: "Plan mode: describe what you want to build. I'll create a plan before implementing.", handled: false };
152
197
  });