@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.
- package/README.md +256 -160
- package/dist/Tool.d.ts +3 -1
- package/dist/Tool.d.ts.map +1 -1
- package/dist/Tool.js +1 -1
- package/dist/Tool.js.map +1 -1
- package/dist/commands/cybergotchi.d.ts +3 -0
- package/dist/commands/cybergotchi.d.ts.map +1 -0
- package/dist/commands/cybergotchi.js +75 -0
- package/dist/commands/cybergotchi.js.map +1 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +45 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/components/CybergotchiBubble.d.ts +7 -0
- package/dist/components/CybergotchiBubble.d.ts.map +1 -0
- package/dist/components/CybergotchiBubble.js +27 -0
- package/dist/components/CybergotchiBubble.js.map +1 -0
- package/dist/components/CybergotchiPanel.d.ts +8 -0
- package/dist/components/CybergotchiPanel.d.ts.map +1 -0
- package/dist/components/CybergotchiPanel.js +15 -0
- package/dist/components/CybergotchiPanel.js.map +1 -0
- package/dist/components/CybergotchiSetup.d.ts +7 -0
- package/dist/components/CybergotchiSetup.d.ts.map +1 -0
- package/dist/components/CybergotchiSetup.js +80 -0
- package/dist/components/CybergotchiSetup.js.map +1 -0
- package/dist/components/CybergotchiSprite.d.ts +8 -0
- package/dist/components/CybergotchiSprite.d.ts.map +1 -0
- package/dist/components/CybergotchiSprite.js +20 -0
- package/dist/components/CybergotchiSprite.js.map +1 -0
- package/dist/components/InitWizard.d.ts +18 -0
- package/dist/components/InitWizard.d.ts.map +1 -0
- package/dist/components/InitWizard.js +151 -0
- package/dist/components/InitWizard.js.map +1 -0
- package/dist/components/REPL.d.ts.map +1 -1
- package/dist/components/REPL.js +91 -3
- package/dist/components/REPL.js.map +1 -1
- package/dist/components/ToolCallDisplay.d.ts +1 -0
- package/dist/components/ToolCallDisplay.d.ts.map +1 -1
- package/dist/components/ToolCallDisplay.js +8 -2
- package/dist/components/ToolCallDisplay.js.map +1 -1
- package/dist/cybergotchi/config.d.ts +5 -0
- package/dist/cybergotchi/config.d.ts.map +1 -0
- package/dist/cybergotchi/config.js +48 -0
- package/dist/cybergotchi/config.js.map +1 -0
- package/dist/cybergotchi/events.d.ts +15 -0
- package/dist/cybergotchi/events.d.ts.map +1 -0
- package/dist/cybergotchi/events.js +14 -0
- package/dist/cybergotchi/events.js.map +1 -0
- package/dist/cybergotchi/needs.d.ts +10 -0
- package/dist/cybergotchi/needs.d.ts.map +1 -0
- package/dist/cybergotchi/needs.js +83 -0
- package/dist/cybergotchi/needs.js.map +1 -0
- package/dist/cybergotchi/species.d.ts +11 -0
- package/dist/cybergotchi/species.d.ts.map +1 -0
- package/dist/cybergotchi/species.js +356 -0
- package/dist/cybergotchi/species.js.map +1 -0
- package/dist/cybergotchi/speech.d.ts +4 -0
- package/dist/cybergotchi/speech.d.ts.map +1 -0
- package/dist/cybergotchi/speech.js +80 -0
- package/dist/cybergotchi/speech.js.map +1 -0
- package/dist/cybergotchi/types.d.ts +39 -0
- package/dist/cybergotchi/types.d.ts.map +1 -0
- package/dist/cybergotchi/types.js +29 -0
- package/dist/cybergotchi/types.js.map +1 -0
- package/dist/cybergotchi/useCybergotchi.d.ts +10 -0
- package/dist/cybergotchi/useCybergotchi.d.ts.map +1 -0
- package/dist/cybergotchi/useCybergotchi.js +99 -0
- package/dist/cybergotchi/useCybergotchi.js.map +1 -0
- package/dist/harness/config.d.ts +14 -0
- package/dist/harness/config.d.ts.map +1 -0
- package/dist/harness/config.js +24 -0
- package/dist/harness/config.js.map +1 -0
- package/dist/harness/cost.d.ts +4 -0
- package/dist/harness/cost.d.ts.map +1 -1
- package/dist/harness/cost.js +20 -0
- package/dist/harness/cost.js.map +1 -1
- package/dist/main.js +26 -16
- package/dist/main.js.map +1 -1
- package/dist/mcp/McpTool.d.ts +19 -0
- package/dist/mcp/McpTool.d.ts.map +1 -0
- package/dist/mcp/McpTool.js +40 -0
- package/dist/mcp/McpTool.js.map +1 -0
- package/dist/mcp/client.d.ts +15 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +78 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/loader.d.ts +8 -0
- package/dist/mcp/loader.d.ts.map +1 -0
- package/dist/mcp/loader.js +52 -0
- package/dist/mcp/loader.js.map +1 -0
- package/dist/mcp/types.d.ts +35 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +3 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +17 -6
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/index.d.ts +4 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +16 -5
- package/dist/providers/openai.js.map +1 -1
- package/dist/query.d.ts +1 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +17 -3
- package/dist/query.js.map +1 -1
- package/dist/services/StreamingToolExecutor.d.ts +6 -2
- package/dist/services/StreamingToolExecutor.d.ts.map +1 -1
- package/dist/services/StreamingToolExecutor.js +11 -4
- package/dist/services/StreamingToolExecutor.js.map +1 -1
- package/dist/tools/BashTool/index.d.ts.map +1 -1
- package/dist/tools/BashTool/index.js +10 -2
- package/dist/tools/BashTool/index.js.map +1 -1
- package/dist/tools/ImageReadTool/index.d.ts +13 -0
- package/dist/tools/ImageReadTool/index.d.ts.map +1 -0
- package/dist/tools/ImageReadTool/index.js +59 -0
- package/dist/tools/ImageReadTool/index.js.map +1 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +2 -0
- package/dist/tools.js.map +1 -1
- package/dist/types/events.d.ts +6 -1
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/message.d.ts +1 -1
- package/dist/types/message.js +1 -1
- package/dist/types/permissions.d.ts +2 -2
- package/dist/types/permissions.js +2 -2
- package/dist/utils/theme.d.ts +1 -1
- package/dist/utils/theme.js +1 -1
- package/package.json +9 -3
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -27
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -17
- package/.github/pull_request_template.md +0 -24
- package/.github/workflows/ci.yml +0 -31
- package/CHANGELOG.md +0 -18
- package/CODE_OF_CONDUCT.md +0 -43
- package/CONTRIBUTING.md +0 -55
- package/SECURITY.md +0 -21
- package/data/models.json +0 -74
- package/data/prompts/system.md +0 -25
- package/data/skills/code-review.md +0 -19
- package/data/skills/commit.md +0 -17
- package/data/skills/debug.md +0 -24
- package/data/skills/tdd.md +0 -22
- package/dist/components/StatusBar.d.ts +0 -7
- package/dist/components/StatusBar.d.ts.map +0 -1
- package/dist/components/StatusBar.js +0 -6
- package/dist/components/StatusBar.js.map +0 -1
- package/dist/utils/retry.d.ts +0 -10
- package/dist/utils/retry.d.ts.map +0 -1
- package/dist/utils/retry.js +0 -23
- package/dist/utils/retry.js.map +0 -1
- package/dist/utils/tokens.d.ts +0 -18
- package/dist/utils/tokens.d.ts.map +0 -1
- package/dist/utils/tokens.js +0 -57
- 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
|
-
|
|
15
|
-
|
|
16
|
-

|
|
18
|
-

|
|
19
|
-

|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
oh
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
## Tools
|
|
66
|
-
|
|
67
|
-
| Tool | Risk | Description |
|
|
68
|
-
|------|------|-------------|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
+

|
|
17
|
+

|
|
18
|
+

|
|
19
|
+

|
|
20
|
+

|
|
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 —
|
|
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;
|
package/dist/Tool.d.ts.map
CHANGED
|
@@ -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;
|
|
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
package/dist/Tool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tool.js","sourceRoot":"","sources":["../src/Tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
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 @@
|
|
|
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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"}
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/commands/index.js
CHANGED
|
@@ -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
|
});
|