@lovenyberg/ove 0.5.2 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/deploy/ove.service +16 -0
- package/package.json +1 -1
- package/src/config.test.ts +6 -0
- package/src/config.ts +5 -1
- package/src/flows.test.ts +1 -1
- package/src/handlers.ts +75 -70
- package/.dockerignore +0 -7
- package/.github/workflows/ci.yml +0 -16
- package/.github/workflows/pages.yml +0 -33
- package/.github/workflows/publish.yml +0 -45
- package/Dockerfile +0 -37
- package/bun.lock +0 -503
- package/bunfig.toml +0 -2
- package/docker-compose.yml +0 -15
- package/docs/examples.md +0 -247
- package/docs/favicon.ico +0 -0
- package/docs/index.html +0 -980
- package/docs/logo.png +0 -0
- package/docs/plans/2026-02-21-codex-runner-design.md +0 -51
- package/docs/plans/2026-02-21-codex-runner-plan.md +0 -475
- package/docs/plans/2026-02-22-repo-autodiscovery-design.md +0 -98
- package/docs/plans/2026-02-22-repo-autodiscovery-plan.md +0 -826
- package/docs/plans/2026-02-23-conversation-repo-memory.md +0 -272
- package/docs/plans/2026-02-25-landing-page-harmonization-design.md +0 -89
- package/docs/plans/2026-02-25-landing-page-harmonization-plan.md +0 -604
- package/logo.png +0 -0
- package/public/logo.png +0 -0
package/docs/index.html
DELETED
|
@@ -1,980 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<link rel="icon" href="favicon.ico">
|
|
7
|
-
<title>Ove — Your Grumpy Dev Companion</title>
|
|
8
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
11
|
-
<style>
|
|
12
|
-
:root {
|
|
13
|
-
--bg: #1a1a1a;
|
|
14
|
-
--bg-panel: #161616;
|
|
15
|
-
--bg-item: #1e1e1e;
|
|
16
|
-
--bg-item-hover: #252525;
|
|
17
|
-
--border: #2a2a2a;
|
|
18
|
-
--border-light: #333;
|
|
19
|
-
--text: #e0e0e0;
|
|
20
|
-
--text-dim: #777;
|
|
21
|
-
--text-muted: #555;
|
|
22
|
-
--accent: #8ab4f8;
|
|
23
|
-
--green: #4ade80;
|
|
24
|
-
--red: #f28b82;
|
|
25
|
-
--amber: #fbbf24;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
29
|
-
|
|
30
|
-
html { scroll-behavior: smooth; }
|
|
31
|
-
|
|
32
|
-
body {
|
|
33
|
-
font-family: "Inter", system-ui, -apple-system, sans-serif;
|
|
34
|
-
color: var(--text);
|
|
35
|
-
line-height: 1.7;
|
|
36
|
-
background: var(--bg);
|
|
37
|
-
font-size: 16px;
|
|
38
|
-
-webkit-font-smoothing: antialiased;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/* ─── HEADER ─── */
|
|
42
|
-
header {
|
|
43
|
-
display: flex;
|
|
44
|
-
align-items: center;
|
|
45
|
-
justify-content: space-between;
|
|
46
|
-
padding: 0.5rem 1rem;
|
|
47
|
-
border-bottom: 1px solid var(--border);
|
|
48
|
-
background: var(--bg-panel);
|
|
49
|
-
position: sticky;
|
|
50
|
-
top: 0;
|
|
51
|
-
z-index: 100;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.header-left {
|
|
55
|
-
display: flex;
|
|
56
|
-
align-items: center;
|
|
57
|
-
gap: 1rem;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.header-logo {
|
|
61
|
-
width: 24px;
|
|
62
|
-
height: 24px;
|
|
63
|
-
border-radius: 3px;
|
|
64
|
-
object-fit: cover;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.header-left h1 {
|
|
68
|
-
font-family: "JetBrains Mono", monospace;
|
|
69
|
-
font-size: 0.85rem;
|
|
70
|
-
font-weight: 600;
|
|
71
|
-
letter-spacing: 0.05em;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.nav-link {
|
|
75
|
-
color: var(--text-dim);
|
|
76
|
-
text-decoration: none;
|
|
77
|
-
font-family: "JetBrains Mono", monospace;
|
|
78
|
-
font-size: 0.7rem;
|
|
79
|
-
padding: 0.2rem 0.5rem;
|
|
80
|
-
border: 1px solid var(--border);
|
|
81
|
-
border-radius: 3px;
|
|
82
|
-
transition: all 0.15s;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.nav-link:hover {
|
|
86
|
-
color: var(--text);
|
|
87
|
-
border-color: var(--border-light);
|
|
88
|
-
background: var(--bg-item);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/* ─── HERO ─── */
|
|
92
|
-
.hero {
|
|
93
|
-
display: flex;
|
|
94
|
-
align-items: center;
|
|
95
|
-
justify-content: center;
|
|
96
|
-
padding: 64px 32px;
|
|
97
|
-
border-bottom: 1px solid var(--border);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
.hero-inner {
|
|
101
|
-
display: grid;
|
|
102
|
-
grid-template-columns: auto 1fr;
|
|
103
|
-
gap: 48px;
|
|
104
|
-
align-items: center;
|
|
105
|
-
max-width: 900px;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.hero-portrait {
|
|
109
|
-
width: 140px;
|
|
110
|
-
height: 140px;
|
|
111
|
-
border-radius: 4px;
|
|
112
|
-
border: 1px solid var(--border);
|
|
113
|
-
box-shadow: 0 12px 32px rgba(0,0,0,0.3);
|
|
114
|
-
object-fit: cover;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.hero-text h1 {
|
|
118
|
-
font-family: "JetBrains Mono", monospace;
|
|
119
|
-
font-size: 2.5rem;
|
|
120
|
-
font-weight: 700;
|
|
121
|
-
color: var(--text);
|
|
122
|
-
letter-spacing: -0.02em;
|
|
123
|
-
line-height: 1;
|
|
124
|
-
margin-bottom: 6px;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.hero-subtitle {
|
|
128
|
-
font-family: "JetBrains Mono", monospace;
|
|
129
|
-
font-size: 0.8rem;
|
|
130
|
-
color: var(--text-dim);
|
|
131
|
-
font-weight: 400;
|
|
132
|
-
letter-spacing: 0.08em;
|
|
133
|
-
text-transform: uppercase;
|
|
134
|
-
margin-bottom: 20px;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.hero-quote {
|
|
138
|
-
font-style: italic;
|
|
139
|
-
color: var(--text-dim);
|
|
140
|
-
font-size: 1.05rem;
|
|
141
|
-
line-height: 1.5;
|
|
142
|
-
margin-bottom: 24px;
|
|
143
|
-
max-width: 420px;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
.hero-quote span {
|
|
147
|
-
color: var(--text-muted);
|
|
148
|
-
font-style: normal;
|
|
149
|
-
font-size: 0.8rem;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.btn {
|
|
153
|
-
display: inline-block;
|
|
154
|
-
font-family: "JetBrains Mono", monospace;
|
|
155
|
-
font-size: 0.8rem;
|
|
156
|
-
font-weight: 500;
|
|
157
|
-
text-decoration: none;
|
|
158
|
-
padding: 10px 24px;
|
|
159
|
-
border: 1px solid var(--border-light);
|
|
160
|
-
color: var(--accent);
|
|
161
|
-
background: transparent;
|
|
162
|
-
border-radius: 3px;
|
|
163
|
-
transition: all 0.15s;
|
|
164
|
-
letter-spacing: 0.02em;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
.btn:hover {
|
|
168
|
-
background: var(--accent);
|
|
169
|
-
color: var(--bg);
|
|
170
|
-
border-color: var(--accent);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
.btn-ghost {
|
|
174
|
-
border-color: var(--border);
|
|
175
|
-
color: var(--text-dim);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
.btn-ghost:hover {
|
|
179
|
-
border-color: var(--text-dim);
|
|
180
|
-
color: var(--text);
|
|
181
|
-
background: transparent;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/* ─── INSTALL BAR ─── */
|
|
185
|
-
.install-bar {
|
|
186
|
-
background: var(--bg-panel);
|
|
187
|
-
border-bottom: 1px solid var(--border);
|
|
188
|
-
padding: 16px 32px;
|
|
189
|
-
text-align: center;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.install-bar code {
|
|
193
|
-
font-family: "JetBrains Mono", monospace;
|
|
194
|
-
font-size: 0.82rem;
|
|
195
|
-
color: var(--accent);
|
|
196
|
-
letter-spacing: -0.02em;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
.install-bar code::before {
|
|
200
|
-
content: "$ ";
|
|
201
|
-
color: var(--text-muted);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/* ─── MAIN CONTENT ─── */
|
|
205
|
-
.container {
|
|
206
|
-
max-width: 740px;
|
|
207
|
-
margin: 0 auto;
|
|
208
|
-
padding: 0 32px;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
.section {
|
|
212
|
-
padding: 64px 0;
|
|
213
|
-
border-bottom: 1px solid var(--border);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
.section:last-child {
|
|
217
|
-
border-bottom: none;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/* ─── SECTION HEADERS ─── */
|
|
221
|
-
h2 {
|
|
222
|
-
font-family: "JetBrains Mono", monospace;
|
|
223
|
-
font-size: 1.3rem;
|
|
224
|
-
font-weight: 700;
|
|
225
|
-
color: var(--text);
|
|
226
|
-
margin-bottom: 8px;
|
|
227
|
-
letter-spacing: -0.01em;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.section-note {
|
|
231
|
-
color: var(--text-dim);
|
|
232
|
-
font-style: italic;
|
|
233
|
-
margin-bottom: 32px;
|
|
234
|
-
font-size: 0.9rem;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
h3 {
|
|
238
|
-
font-family: "JetBrains Mono", monospace;
|
|
239
|
-
font-size: 0.9rem;
|
|
240
|
-
font-weight: 500;
|
|
241
|
-
color: var(--accent);
|
|
242
|
-
margin: 32px 0 10px;
|
|
243
|
-
letter-spacing: 0.01em;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
h3:first-child,
|
|
247
|
-
.section-note + h3 {
|
|
248
|
-
margin-top: 0;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/* ─── FEATURES ─── */
|
|
252
|
-
.features {
|
|
253
|
-
display: grid;
|
|
254
|
-
grid-template-columns: 1fr 1fr;
|
|
255
|
-
gap: 1px;
|
|
256
|
-
background: var(--border);
|
|
257
|
-
border: 1px solid var(--border);
|
|
258
|
-
margin-top: 32px;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
.feature {
|
|
262
|
-
padding: 24px 20px;
|
|
263
|
-
background: var(--bg);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
.feature-label {
|
|
267
|
-
font-family: "JetBrains Mono", monospace;
|
|
268
|
-
font-size: 0.65rem;
|
|
269
|
-
text-transform: uppercase;
|
|
270
|
-
letter-spacing: 0.12em;
|
|
271
|
-
color: var(--text-muted);
|
|
272
|
-
margin-bottom: 8px;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
.feature h3 {
|
|
276
|
-
font-family: "JetBrains Mono", monospace;
|
|
277
|
-
font-size: 0.95rem;
|
|
278
|
-
color: var(--text);
|
|
279
|
-
margin: 0 0 8px;
|
|
280
|
-
font-weight: 500;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
.feature p {
|
|
284
|
-
color: var(--text-dim);
|
|
285
|
-
font-size: 0.85rem;
|
|
286
|
-
line-height: 1.55;
|
|
287
|
-
margin-bottom: 10px;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
.feature code {
|
|
291
|
-
font-family: "JetBrains Mono", monospace;
|
|
292
|
-
font-size: 0.75rem;
|
|
293
|
-
color: var(--accent);
|
|
294
|
-
background: var(--bg-item);
|
|
295
|
-
padding: 3px 8px;
|
|
296
|
-
border: 1px solid var(--border);
|
|
297
|
-
display: inline-block;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/* ─── PERSONALITY QUOTES ─── */
|
|
301
|
-
.ove-says {
|
|
302
|
-
border-left: 2px solid var(--accent);
|
|
303
|
-
padding: 14px 0 14px 20px;
|
|
304
|
-
margin: 32px 0;
|
|
305
|
-
font-style: italic;
|
|
306
|
-
color: var(--text-dim);
|
|
307
|
-
font-size: 0.95rem;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
.ove-says .attr {
|
|
311
|
-
display: block;
|
|
312
|
-
font-style: normal;
|
|
313
|
-
color: var(--text-muted);
|
|
314
|
-
font-size: 0.75rem;
|
|
315
|
-
margin-top: 6px;
|
|
316
|
-
font-family: "JetBrains Mono", monospace;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/* ─── CODE BLOCKS ─── */
|
|
320
|
-
pre {
|
|
321
|
-
background: var(--bg-panel);
|
|
322
|
-
border: 1px solid var(--border);
|
|
323
|
-
padding: 16px 18px;
|
|
324
|
-
overflow-x: auto;
|
|
325
|
-
font-size: 0.8rem;
|
|
326
|
-
margin: 14px 0;
|
|
327
|
-
line-height: 1.65;
|
|
328
|
-
position: relative;
|
|
329
|
-
border-radius: 3px;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
pre::before {
|
|
333
|
-
content: attr(data-label);
|
|
334
|
-
position: absolute;
|
|
335
|
-
top: 0;
|
|
336
|
-
right: 0;
|
|
337
|
-
font-family: "JetBrains Mono", monospace;
|
|
338
|
-
font-size: 0.6rem;
|
|
339
|
-
text-transform: uppercase;
|
|
340
|
-
letter-spacing: 0.1em;
|
|
341
|
-
color: var(--text-muted);
|
|
342
|
-
padding: 4px 10px;
|
|
343
|
-
background: var(--bg-item);
|
|
344
|
-
border-left: 1px solid var(--border);
|
|
345
|
-
border-bottom: 1px solid var(--border);
|
|
346
|
-
border-radius: 0 3px 0 3px;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
code {
|
|
350
|
-
font-family: "JetBrains Mono", monospace;
|
|
351
|
-
font-size: 0.85em;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
pre code {
|
|
355
|
-
color: var(--text);
|
|
356
|
-
font-size: 1em;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
p code {
|
|
360
|
-
background: var(--bg-item);
|
|
361
|
-
border: 1px solid var(--border);
|
|
362
|
-
padding: 1px 6px;
|
|
363
|
-
color: var(--accent);
|
|
364
|
-
font-size: 0.82em;
|
|
365
|
-
border-radius: 2px;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/* ─── LISTS ─── */
|
|
369
|
-
.steps {
|
|
370
|
-
margin: 16px 0;
|
|
371
|
-
padding-left: 0;
|
|
372
|
-
list-style: none;
|
|
373
|
-
counter-reset: step;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
.steps li {
|
|
377
|
-
counter-increment: step;
|
|
378
|
-
padding: 8px 0 8px 36px;
|
|
379
|
-
position: relative;
|
|
380
|
-
border-bottom: 1px solid var(--border);
|
|
381
|
-
font-size: 0.95rem;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
.steps li:last-child {
|
|
385
|
-
border-bottom: none;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
.steps li::before {
|
|
389
|
-
content: counter(step);
|
|
390
|
-
position: absolute;
|
|
391
|
-
left: 0;
|
|
392
|
-
font-family: "JetBrains Mono", monospace;
|
|
393
|
-
font-size: 0.75rem;
|
|
394
|
-
color: var(--text-muted);
|
|
395
|
-
width: 22px;
|
|
396
|
-
text-align: right;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/* ─── LINKS ─── */
|
|
400
|
-
a {
|
|
401
|
-
color: var(--accent);
|
|
402
|
-
text-decoration: none;
|
|
403
|
-
border-bottom: 1px solid var(--border);
|
|
404
|
-
transition: border-color 0.15s;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
a:hover {
|
|
408
|
-
border-bottom-color: var(--accent);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
strong {
|
|
412
|
-
color: var(--text);
|
|
413
|
-
font-weight: 500;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
p {
|
|
417
|
-
margin-bottom: 12px;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
p:last-child {
|
|
421
|
-
margin-bottom: 0;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/* ─── COMMANDS TABLE ─── */
|
|
425
|
-
.cmd-grid {
|
|
426
|
-
display: grid;
|
|
427
|
-
grid-template-columns: auto 1fr;
|
|
428
|
-
gap: 0;
|
|
429
|
-
border: 1px solid var(--border);
|
|
430
|
-
font-size: 0.88rem;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
.cmd-grid dt,
|
|
434
|
-
.cmd-grid dd {
|
|
435
|
-
padding: 10px 16px;
|
|
436
|
-
border-bottom: 1px solid var(--border);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
.cmd-grid dt:last-of-type,
|
|
440
|
-
.cmd-grid dd:last-of-type {
|
|
441
|
-
border-bottom: none;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
.cmd-grid dt {
|
|
445
|
-
font-family: "JetBrains Mono", monospace;
|
|
446
|
-
font-size: 0.78rem;
|
|
447
|
-
color: var(--accent);
|
|
448
|
-
background: var(--bg-panel);
|
|
449
|
-
white-space: nowrap;
|
|
450
|
-
border-right: 1px solid var(--border);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
.cmd-grid dd {
|
|
454
|
-
color: var(--text-dim);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
/* ─── HOW IT WORKS ─── */
|
|
458
|
-
.pipeline {
|
|
459
|
-
margin: 24px 0;
|
|
460
|
-
padding-left: 0;
|
|
461
|
-
list-style: none;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
.pipeline li {
|
|
465
|
-
display: flex;
|
|
466
|
-
gap: 16px;
|
|
467
|
-
padding: 14px 0;
|
|
468
|
-
border-bottom: 1px solid var(--border);
|
|
469
|
-
align-items: baseline;
|
|
470
|
-
font-size: 0.95rem;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
.pipeline li:last-child {
|
|
474
|
-
border-bottom: none;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
.pipeline .step-n {
|
|
478
|
-
font-family: "JetBrains Mono", monospace;
|
|
479
|
-
font-size: 0.7rem;
|
|
480
|
-
color: var(--text-muted);
|
|
481
|
-
flex-shrink: 0;
|
|
482
|
-
width: 16px;
|
|
483
|
-
text-align: right;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
.pipeline .step-arrow {
|
|
487
|
-
color: var(--border-light);
|
|
488
|
-
flex-shrink: 0;
|
|
489
|
-
font-size: 0.8rem;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/* ─── FOOTER ─── */
|
|
493
|
-
footer {
|
|
494
|
-
padding: 40px 32px;
|
|
495
|
-
text-align: center;
|
|
496
|
-
color: var(--text-dim);
|
|
497
|
-
font-size: 0.78rem;
|
|
498
|
-
border-top: 1px solid var(--border);
|
|
499
|
-
font-family: "JetBrains Mono", monospace;
|
|
500
|
-
letter-spacing: 0.02em;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
footer a {
|
|
504
|
-
color: var(--text-muted);
|
|
505
|
-
border-bottom: none;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
footer a:hover {
|
|
509
|
-
color: var(--accent);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/* ─── RESPONSIVE ─── */
|
|
513
|
-
@media (max-width: 700px) {
|
|
514
|
-
.hero-inner {
|
|
515
|
-
grid-template-columns: 1fr;
|
|
516
|
-
text-align: center;
|
|
517
|
-
gap: 32px;
|
|
518
|
-
}
|
|
519
|
-
.hero-portrait {
|
|
520
|
-
width: 120px;
|
|
521
|
-
height: 120px;
|
|
522
|
-
margin: 0 auto;
|
|
523
|
-
}
|
|
524
|
-
.hero-quote {
|
|
525
|
-
margin: 0 auto 32px;
|
|
526
|
-
}
|
|
527
|
-
.hero-actions {
|
|
528
|
-
justify-content: center;
|
|
529
|
-
}
|
|
530
|
-
.hero-text h1 {
|
|
531
|
-
font-size: 2rem;
|
|
532
|
-
}
|
|
533
|
-
.features {
|
|
534
|
-
grid-template-columns: 1fr;
|
|
535
|
-
}
|
|
536
|
-
.cmd-grid {
|
|
537
|
-
grid-template-columns: 1fr;
|
|
538
|
-
}
|
|
539
|
-
.cmd-grid dt {
|
|
540
|
-
border-right: none;
|
|
541
|
-
border-bottom: none;
|
|
542
|
-
padding-bottom: 0;
|
|
543
|
-
}
|
|
544
|
-
.cmd-grid dd {
|
|
545
|
-
padding-top: 2px;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
/* ─── ANIMATIONS ─── */
|
|
550
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
551
|
-
.hero-portrait {
|
|
552
|
-
animation: fadeUp 0.8s ease both;
|
|
553
|
-
}
|
|
554
|
-
.hero-text h1 {
|
|
555
|
-
animation: fadeUp 0.7s ease 0.1s both;
|
|
556
|
-
}
|
|
557
|
-
.hero-subtitle {
|
|
558
|
-
animation: fadeUp 0.7s ease 0.2s both;
|
|
559
|
-
}
|
|
560
|
-
.hero-quote {
|
|
561
|
-
animation: fadeUp 0.7s ease 0.3s both;
|
|
562
|
-
}
|
|
563
|
-
.hero-actions {
|
|
564
|
-
animation: fadeUp 0.7s ease 0.4s both;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
@keyframes fadeUp {
|
|
569
|
-
from {
|
|
570
|
-
opacity: 0;
|
|
571
|
-
transform: translateY(16px);
|
|
572
|
-
}
|
|
573
|
-
to {
|
|
574
|
-
opacity: 1;
|
|
575
|
-
transform: translateY(0);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
</style>
|
|
579
|
-
</head>
|
|
580
|
-
<body>
|
|
581
|
-
|
|
582
|
-
<header>
|
|
583
|
-
<div class="header-left">
|
|
584
|
-
<img class="header-logo" src="logo.png" alt="">
|
|
585
|
-
<h1>ove</h1>
|
|
586
|
-
<a class="nav-link" href="#features">features</a>
|
|
587
|
-
<a class="nav-link" href="#getting-started">setup</a>
|
|
588
|
-
<a class="nav-link" href="#commands">commands</a>
|
|
589
|
-
<a class="nav-link" href="#deploy">deploy</a>
|
|
590
|
-
</div>
|
|
591
|
-
<a class="nav-link" href="https://github.com/jacksoncage/ove">github</a>
|
|
592
|
-
</header>
|
|
593
|
-
|
|
594
|
-
<section class="hero">
|
|
595
|
-
<div class="hero-inner">
|
|
596
|
-
<img class="hero-portrait" src="logo.png" alt="Ove, grumpy but reliable">
|
|
597
|
-
<div class="hero-text">
|
|
598
|
-
<h1>Ove</h1>
|
|
599
|
-
<div class="hero-subtitle">Dev companion</div>
|
|
600
|
-
<p class="hero-quote">
|
|
601
|
-
"Just tell me what's wrong. I'll figure it out."
|
|
602
|
-
<span>— Ove, on every conversation</span>
|
|
603
|
-
</p>
|
|
604
|
-
<div class="hero-actions">
|
|
605
|
-
<a class="btn" href="#getting-started">Get started</a>
|
|
606
|
-
<a class="btn btn-ghost" href="https://github.com/jacksoncage/ove">GitHub</a>
|
|
607
|
-
</div>
|
|
608
|
-
</div>
|
|
609
|
-
</div>
|
|
610
|
-
</section>
|
|
611
|
-
|
|
612
|
-
<div class="install-bar">
|
|
613
|
-
<code>npm install -g @lovenyberg/ove</code>
|
|
614
|
-
</div>
|
|
615
|
-
|
|
616
|
-
<div class="container">
|
|
617
|
-
|
|
618
|
-
<section class="section" id="features">
|
|
619
|
-
<h2>What Ove Does</h2>
|
|
620
|
-
<p class="section-note">He grumbles, but he gets it done. Properly.</p>
|
|
621
|
-
|
|
622
|
-
<p>Talk to Ove like you'd talk to a colleague. Ask questions, describe problems, paste error logs, think out loud — he understands natural language. No need to memorize commands. Just chat.</p>
|
|
623
|
-
|
|
624
|
-
<pre data-label="just chat">"the login page throws a 500 after submitting"
|
|
625
|
-
"can you check what's breaking the auth tests on my-app?"
|
|
626
|
-
"how does the payment webhook work?"
|
|
627
|
-
"refactor the user service, it's getting messy"</pre>
|
|
628
|
-
|
|
629
|
-
<p>Ove figures out the intent, picks the right repo, and gets to work. For common tasks, there are also shorthand commands — but they're shortcuts, not requirements.</p>
|
|
630
|
-
|
|
631
|
-
<div class="features">
|
|
632
|
-
<div class="feature">
|
|
633
|
-
<div class="feature-label">01 — Review</div>
|
|
634
|
-
<h3>Code Review</h3>
|
|
635
|
-
<p>Tell Ove to review a PR. He'll go through every line, find the bugs you missed, and leave comments. Thorough as always.</p>
|
|
636
|
-
<code>review PR #42 on my-app</code>
|
|
637
|
-
</div>
|
|
638
|
-
<div class="feature">
|
|
639
|
-
<div class="feature-label">02 — Fix</div>
|
|
640
|
-
<h3>Fix Issues</h3>
|
|
641
|
-
<p>Point Ove at a GitHub issue. He'll read it, explore the code, implement the fix, write tests, and open a PR.</p>
|
|
642
|
-
<code>fix issue #15 on my-app</code>
|
|
643
|
-
</div>
|
|
644
|
-
<div class="feature">
|
|
645
|
-
<div class="feature-label">03 — Validate</div>
|
|
646
|
-
<h3>Validate</h3>
|
|
647
|
-
<p>Run the test suite and linter. Ove reports every failure with the look of a man who knew this would happen.</p>
|
|
648
|
-
<code>validate my-app</code>
|
|
649
|
-
</div>
|
|
650
|
-
<div class="feature">
|
|
651
|
-
<div class="feature-label">04 — Discuss</div>
|
|
652
|
-
<h3>Brainstorm</h3>
|
|
653
|
-
<p>Discuss ideas with Ove. He'll ask the questions you forgot to ask, poke holes in your plan, and suggest what actually works.</p>
|
|
654
|
-
<code>discuss notification service</code>
|
|
655
|
-
</div>
|
|
656
|
-
<div class="feature">
|
|
657
|
-
<div class="feature-label">05 — Schedule</div>
|
|
658
|
-
<h3>Scheduled Tasks</h3>
|
|
659
|
-
<p>Tell Ove to do something on a schedule. He'll parse your natural language, set it up, and run it on time. Every time.</p>
|
|
660
|
-
<code>lint and check every day at 9</code>
|
|
661
|
-
</div>
|
|
662
|
-
<div class="feature">
|
|
663
|
-
<div class="feature-label">06 — Manage</div>
|
|
664
|
-
<h3>Task Management</h3>
|
|
665
|
-
<p>See what's running, what's queued, and kill anything that's gone sideways. Different repos run in parallel, same repo stays serial.</p>
|
|
666
|
-
<code>tasks</code> <code>cancel <id></code>
|
|
667
|
-
</div>
|
|
668
|
-
</div>
|
|
669
|
-
|
|
670
|
-
<div class="ove-says">
|
|
671
|
-
"People today don't even know how a worktree works. In my day we used patches. On paper."
|
|
672
|
-
<span class="attr">— Ove, probably</span>
|
|
673
|
-
</div>
|
|
674
|
-
</section>
|
|
675
|
-
|
|
676
|
-
<section class="section" id="getting-started">
|
|
677
|
-
<h2>Getting Started</h2>
|
|
678
|
-
<p class="section-note">Three ways to run Ove. Pick what fits. No excuses.</p>
|
|
679
|
-
|
|
680
|
-
<h3>Prerequisites</h3>
|
|
681
|
-
<ol class="steps">
|
|
682
|
-
<li><a href="https://docs.anthropic.com/en/docs/claude-code">Claude Code CLI</a> installed and authenticated (default runner)</li>
|
|
683
|
-
<li>Or <a href="https://github.com/openai/codex">OpenAI Codex CLI</a> installed and authenticated (alternative runner)</li>
|
|
684
|
-
<li><a href="https://cli.github.com">GitHub CLI</a> (<code>gh</code>) installed and authenticated</li>
|
|
685
|
-
<li>SSH access to your Git repos</li>
|
|
686
|
-
</ol>
|
|
687
|
-
|
|
688
|
-
<h3>1. Local</h3>
|
|
689
|
-
<p>Install globally, run the interactive setup, start.</p>
|
|
690
|
-
<pre data-label="shell">npm install -g @lovenyberg/ove
|
|
691
|
-
ove init # walks you through config
|
|
692
|
-
ove start</pre>
|
|
693
|
-
<p>Requires <a href="https://bun.sh">Bun</a> runtime on your machine.</p>
|
|
694
|
-
|
|
695
|
-
<h3>2. Docker</h3>
|
|
696
|
-
<p>Generate config locally, then run everything in a container. The image includes Bun, git, and Claude CLI.</p>
|
|
697
|
-
<pre data-label="shell"># Generate config on host first
|
|
698
|
-
ove init
|
|
699
|
-
|
|
700
|
-
# Start the container
|
|
701
|
-
docker compose up -d
|
|
702
|
-
|
|
703
|
-
# Watch logs
|
|
704
|
-
docker compose logs -f</pre>
|
|
705
|
-
<p>Mounts <code>config.json</code>, <code>.env</code>, <code>repos/</code>, and <code>~/.ssh</code> from the host. Set <code>ANTHROPIC_API_KEY</code> in <code>.env</code> or your shell.</p>
|
|
706
|
-
|
|
707
|
-
<h3>3. VM</h3>
|
|
708
|
-
<p>Best for always-on setups. A small VM (2 CPU, 4 GB RAM) is enough. See the <a href="#deploy">full VM guide</a> below.</p>
|
|
709
|
-
<pre data-label="shell">git clone git@github.com:jacksoncage/ove.git && cd ove
|
|
710
|
-
bun install
|
|
711
|
-
ove init
|
|
712
|
-
sudo cp deploy/ove.service /etc/systemd/system/ove.service
|
|
713
|
-
sudo systemctl enable --now ove</pre>
|
|
714
|
-
|
|
715
|
-
<h3>Configure</h3>
|
|
716
|
-
<p><code>ove init</code> creates <code>config.json</code> and <code>.env</code> interactively. It asks for your transports, tokens, repos, and users. You can also edit them directly:</p>
|
|
717
|
-
<pre data-label="config.json">{
|
|
718
|
-
"repos": {
|
|
719
|
-
"my-app": {
|
|
720
|
-
"url": "git@github.com:org/my-app.git",
|
|
721
|
-
"defaultBranch": "main"
|
|
722
|
-
}
|
|
723
|
-
},
|
|
724
|
-
"users": {
|
|
725
|
-
"slack:U0ABC1234": { "name": "alice", "repos": ["my-app"] },
|
|
726
|
-
"telegram:123456789": { "name": "alice", "repos": ["my-app"] },
|
|
727
|
-
"discord:987654321": { "name": "alice", "repos": ["my-app"] },
|
|
728
|
-
"github:alice": { "name": "alice", "repos": ["my-app"] },
|
|
729
|
-
"http:anon": { "name": "alice", "repos": ["my-app"] },
|
|
730
|
-
"cli:local": { "name": "alice", "repos": ["my-app"] }
|
|
731
|
-
},
|
|
732
|
-
"claude": { "maxTurns": 10 },
|
|
733
|
-
"runner": { "name": "claude" },
|
|
734
|
-
"cron": [
|
|
735
|
-
{
|
|
736
|
-
"schedule": "0 9 * * 1-5",
|
|
737
|
-
"repo": "my-app",
|
|
738
|
-
"prompt": "Run lint and tests.",
|
|
739
|
-
"userId": "slack:U0ABC1234"
|
|
740
|
-
}
|
|
741
|
-
]
|
|
742
|
-
}</pre>
|
|
743
|
-
|
|
744
|
-
<p>Set <code>"runner"</code> globally to <code>{"name": "codex"}</code> to use OpenAI Codex, or override per-repo with a <code>"runner"</code> field inside the repo config. Supported runners: <code>claude</code> (default), <code>codex</code>.</p>
|
|
745
|
-
|
|
746
|
-
<p>Static cron jobs go in <code>config.json</code>. Users can also create schedules from chat — just say something like <code>lint and check every day at 9</code>. These are stored in SQLite and managed with <code>list schedules</code> / <code>remove schedule #N</code>.</p>
|
|
747
|
-
|
|
748
|
-
<div class="ove-says">
|
|
749
|
-
"Read the instructions before you start. I wrote them for a reason."
|
|
750
|
-
<span class="attr">— Ove</span>
|
|
751
|
-
</div>
|
|
752
|
-
</section>
|
|
753
|
-
|
|
754
|
-
<section class="section" id="commands">
|
|
755
|
-
<h2>Commands</h2>
|
|
756
|
-
<p class="section-note">Shortcuts for common tasks. You can also just type what you need in plain language.</p>
|
|
757
|
-
|
|
758
|
-
<dl class="cmd-grid">
|
|
759
|
-
<dt>review PR #N on <repo></dt>
|
|
760
|
-
<dd>Code review with inline comments</dd>
|
|
761
|
-
<dt>fix issue #N on <repo></dt>
|
|
762
|
-
<dd>Read issue, implement fix, create PR</dd>
|
|
763
|
-
<dt>simplify <path> in <repo></dt>
|
|
764
|
-
<dd>Reduce complexity, create PR</dd>
|
|
765
|
-
<dt>validate <repo></dt>
|
|
766
|
-
<dd>Run tests and linter</dd>
|
|
767
|
-
<dt>discuss <topic></dt>
|
|
768
|
-
<dd>Brainstorm ideas, no code changes</dd>
|
|
769
|
-
<dt>create project <name></dt>
|
|
770
|
-
<dd>Scaffold a new project</dd>
|
|
771
|
-
<dt><task> every day/weekday at <time></dt>
|
|
772
|
-
<dd>Schedule a recurring task</dd>
|
|
773
|
-
<dt>list schedules</dt>
|
|
774
|
-
<dd>See your scheduled tasks</dd>
|
|
775
|
-
<dt>remove schedule #N</dt>
|
|
776
|
-
<dd>Remove a scheduled task</dd>
|
|
777
|
-
<dt>tasks</dt>
|
|
778
|
-
<dd>List running and pending tasks</dd>
|
|
779
|
-
<dt>cancel <id></dt>
|
|
780
|
-
<dd>Kill a running or pending task</dd>
|
|
781
|
-
<dt>status</dt>
|
|
782
|
-
<dd>Queue stats</dd>
|
|
783
|
-
<dt>history</dt>
|
|
784
|
-
<dd>Recent tasks</dd>
|
|
785
|
-
<dt>clear</dt>
|
|
786
|
-
<dd>Reset conversation</dd>
|
|
787
|
-
<dt>help</dt>
|
|
788
|
-
<dd>Show commands</dd>
|
|
789
|
-
</dl>
|
|
790
|
-
</section>
|
|
791
|
-
|
|
792
|
-
<section class="section">
|
|
793
|
-
<h2>How It Works</h2>
|
|
794
|
-
<p class="section-note">Parallel where it matters. Serial where it must be. Proper.</p>
|
|
795
|
-
|
|
796
|
-
<ol class="pipeline">
|
|
797
|
-
<li><span class="step-n">1</span><span class="step-arrow">›</span> Message arrives via Slack, WhatsApp, Telegram, Discord, CLI, HTTP API, or GitHub comment</li>
|
|
798
|
-
<li><span class="step-n">2</span><span class="step-arrow">›</span> Router parses intent and extracts repo/args</li>
|
|
799
|
-
<li><span class="step-n">3</span><span class="step-arrow">›</span> Task gets queued in SQLite — one per repo at a time, different repos run in parallel</li>
|
|
800
|
-
<li><span class="step-n">4</span><span class="step-arrow">›</span> Worker loop picks up tasks concurrently (up to 5 parallel)</li>
|
|
801
|
-
<li><span class="step-n">5</span><span class="step-arrow">›</span> Each task gets an isolated git worktree</li>
|
|
802
|
-
<li><span class="step-n">6</span><span class="step-arrow">›</span> Runs the configured runner (<code>claude -p</code> or <code>codex exec</code>) with streaming JSON output</li>
|
|
803
|
-
<li><span class="step-n">7</span><span class="step-arrow">›</span> Status updates stream back — chat edits, SSE, or GitHub comment</li>
|
|
804
|
-
<li><span class="step-n">8</span><span class="step-arrow">›</span> Result sent back, worktree cleaned up — use <code>tasks</code> to monitor, <code>cancel <id></code> to stop</li>
|
|
805
|
-
</ol>
|
|
806
|
-
</section>
|
|
807
|
-
|
|
808
|
-
<section class="section" id="skills">
|
|
809
|
-
<h2>Skills</h2>
|
|
810
|
-
<p class="section-note">Teach Ove's Claude instances your conventions. Manually.</p>
|
|
811
|
-
|
|
812
|
-
<p>Ove spawns <a href="https://code.claude.com/docs/en/skills">Claude Code CLI</a> in isolated worktrees. The spawned instances automatically pick up <strong>skills</strong> — reusable instruction sets that follow the <a href="https://agentskills.io">Agent Skills</a> open standard.</p>
|
|
813
|
-
|
|
814
|
-
<p>Skills are configured <strong>manually</strong> on the host machine running Ove:</p>
|
|
815
|
-
|
|
816
|
-
<dl class="cmd-grid">
|
|
817
|
-
<dt>~/.claude/skills/</dt>
|
|
818
|
-
<dd>Personal skills — available across all repos on this machine</dd>
|
|
819
|
-
<dt>.claude/skills/</dt>
|
|
820
|
-
<dd>Per-repo skills — committed to each repo Ove manages</dd>
|
|
821
|
-
<dt>Plugins</dt>
|
|
822
|
-
<dd>Installed via <code>claude plugins add</code> — available where enabled</dd>
|
|
823
|
-
</dl>
|
|
824
|
-
|
|
825
|
-
<p>When Ove runs a task, Claude Code picks up all three levels. Drop a <code>SKILL.md</code> with domain knowledge, coding conventions, review checklists, or deployment workflows — and every task on that repo benefits.</p>
|
|
826
|
-
|
|
827
|
-
<pre data-label="example: .claude/skills/review/SKILL.md">---
|
|
828
|
-
name: review
|
|
829
|
-
description: Review code using our team standards
|
|
830
|
-
---
|
|
831
|
-
|
|
832
|
-
When reviewing code, check for:
|
|
833
|
-
1. Error handling covers all failure modes
|
|
834
|
-
2. Tests cover the happy path and at least one edge case
|
|
835
|
-
3. No secrets or credentials in code</pre>
|
|
836
|
-
|
|
837
|
-
<p>See the <a href="https://code.claude.com/docs/en/skills">Claude Code skills docs</a> for frontmatter options, argument passing, subagent execution, and more.</p>
|
|
838
|
-
|
|
839
|
-
<div class="ove-says">
|
|
840
|
-
"Skills are just instructions in a file. Not magic. But they work."
|
|
841
|
-
<span class="attr">— Ove</span>
|
|
842
|
-
</div>
|
|
843
|
-
</section>
|
|
844
|
-
|
|
845
|
-
<section class="section" id="transports">
|
|
846
|
-
<h2>Transport Setup</h2>
|
|
847
|
-
<p class="section-note">Enable what you need. Ove doesn't judge. Much.</p>
|
|
848
|
-
|
|
849
|
-
<h3>Slack</h3>
|
|
850
|
-
<ol class="steps">
|
|
851
|
-
<li>Create a Slack app at <a href="https://api.slack.com/apps">api.slack.com/apps</a></li>
|
|
852
|
-
<li>Enable <strong>Socket Mode</strong> — generate an App-Level Token (<code>xapp-...</code>)</li>
|
|
853
|
-
<li>Add bot scopes: <code>chat:write</code>, <code>channels:history</code>, <code>groups:history</code>, <code>im:history</code>, <code>mpim:history</code>, <code>app_mentions:read</code></li>
|
|
854
|
-
<li>Enable <strong>Event Subscriptions</strong>: <code>message.im</code>, <code>app_mention</code></li>
|
|
855
|
-
<li><strong>App Home</strong> → enable Messages Tab → "Allow users to send messages"</li>
|
|
856
|
-
<li>Install to workspace — copy Bot Token (<code>xoxb-...</code>)</li>
|
|
857
|
-
<li>Add both tokens to <code>.env</code></li>
|
|
858
|
-
</ol>
|
|
859
|
-
|
|
860
|
-
<h3>Telegram</h3>
|
|
861
|
-
<ol class="steps">
|
|
862
|
-
<li>Message <a href="https://t.me/BotFather">@BotFather</a> → <code>/newbot</code></li>
|
|
863
|
-
<li>Copy the bot token</li>
|
|
864
|
-
<li>Set <code>TELEGRAM_BOT_TOKEN=<token></code> in <code>.env</code></li>
|
|
865
|
-
</ol>
|
|
866
|
-
|
|
867
|
-
<h3>Discord</h3>
|
|
868
|
-
<ol class="steps">
|
|
869
|
-
<li>Create an app at <a href="https://discord.com/developers/applications">discord.com/developers</a></li>
|
|
870
|
-
<li>Bot → enable <strong>Message Content Intent</strong></li>
|
|
871
|
-
<li>Copy the bot token</li>
|
|
872
|
-
<li>Invite to server with <code>bot</code> scope + <code>Send Messages</code>, <code>Read Message History</code></li>
|
|
873
|
-
<li>Set <code>DISCORD_BOT_TOKEN=<token></code> in <code>.env</code></li>
|
|
874
|
-
</ol>
|
|
875
|
-
|
|
876
|
-
<h3>HTTP API + Web UI</h3>
|
|
877
|
-
<ol class="steps">
|
|
878
|
-
<li>Set <code>HTTP_API_PORT=3000</code> and <code>HTTP_API_KEY=<your-secret></code> in <code>.env</code></li>
|
|
879
|
-
<li>Open <code>http://localhost:3000</code> for the Web UI</li>
|
|
880
|
-
<li>Or call the API: <code>curl -X POST http://localhost:3000/api/message -H "X-API-Key: <key>" -d '{"text":"validate my-app"}'</code></li>
|
|
881
|
-
</ol>
|
|
882
|
-
|
|
883
|
-
<h3>GitHub (issue/PR comments)</h3>
|
|
884
|
-
<ol class="steps">
|
|
885
|
-
<li>Set <code>GITHUB_POLL_REPOS=owner/repo1,owner/repo2</code> in <code>.env</code></li>
|
|
886
|
-
<li>Optionally set <code>GITHUB_BOT_NAME=ove</code> (default) and <code>GITHUB_POLL_INTERVAL=30000</code></li>
|
|
887
|
-
<li>Mention <code>@ove</code> in an issue or PR comment to trigger a task</li>
|
|
888
|
-
<li>Ove replies with a comment when the task completes</li>
|
|
889
|
-
</ol>
|
|
890
|
-
|
|
891
|
-
<h3>WhatsApp</h3>
|
|
892
|
-
<ol class="steps">
|
|
893
|
-
<li>Set <code>WHATSAPP_ENABLED=true</code> and <code>WHATSAPP_PHONE=<your-number></code> in <code>.env</code></li>
|
|
894
|
-
<li>Start Ove and enter the pairing code on your phone (WhatsApp → Linked Devices → Link a Device)</li>
|
|
895
|
-
<li>Set <code>WHATSAPP_ALLOWED_CHATS=<phone1>,<phone2></code> to limit which chats Ove listens to. Without this, Ove responds to <strong>all</strong> your outgoing messages in every chat. Use phone numbers (e.g. <code>46701234567</code>) for individual chats or full JIDs (e.g. <code>120363xxx@g.us</code>) for groups.</li>
|
|
896
|
-
</ol>
|
|
897
|
-
</section>
|
|
898
|
-
|
|
899
|
-
<section class="section" id="deploy">
|
|
900
|
-
<h2>Deployment Details</h2>
|
|
901
|
-
<p class="section-note">Pick your method. They all work. Ove doesn't care.</p>
|
|
902
|
-
|
|
903
|
-
<h3>Docker</h3>
|
|
904
|
-
<p>The Dockerfile builds an image with Bun, git, openssh-client, and Claude CLI baked in. Install Codex CLI separately if needed. Just mount your config and go.</p>
|
|
905
|
-
<pre data-label="docker-compose.yml">services:
|
|
906
|
-
ove:
|
|
907
|
-
build: .
|
|
908
|
-
env_file: .env
|
|
909
|
-
environment:
|
|
910
|
-
- ANTHROPIC_API_KEY
|
|
911
|
-
volumes:
|
|
912
|
-
- ./config.json:/app/config.json:ro
|
|
913
|
-
- ./repos:/app/repos
|
|
914
|
-
- ~/.ssh:/root/.ssh:ro
|
|
915
|
-
restart: unless-stopped</pre>
|
|
916
|
-
|
|
917
|
-
<p>Run <code>ove init</code> on the host first to generate <code>config.json</code> and <code>.env</code>, then <code>docker compose up -d</code>. Repos and the SQLite database persist in the <code>repos/</code> volume.</p>
|
|
918
|
-
|
|
919
|
-
<h3>VM</h3>
|
|
920
|
-
<p>Ove runs on a small VM (2 CPU, 4 GB RAM) where Claude Code and GitHub CLI are set up. Ubuntu 22.04+, Debian 12+. Hetzner, DigitalOcean, whatever you have.</p>
|
|
921
|
-
|
|
922
|
-
<h3>1. Install prerequisites</h3>
|
|
923
|
-
<pre data-label="shell"># Bun
|
|
924
|
-
curl -fsSL https://bun.sh/install | bash
|
|
925
|
-
|
|
926
|
-
# Claude Code (default runner)
|
|
927
|
-
npm install -g @anthropic-ai/claude-code
|
|
928
|
-
|
|
929
|
-
# Or Codex CLI (alternative runner)
|
|
930
|
-
# npm install -g @openai/codex
|
|
931
|
-
|
|
932
|
-
# GitHub CLI
|
|
933
|
-
sudo apt install gh
|
|
934
|
-
gh auth login
|
|
935
|
-
|
|
936
|
-
# SSH key for git
|
|
937
|
-
ssh-keygen -t ed25519
|
|
938
|
-
# Add the public key to GitHub</pre>
|
|
939
|
-
|
|
940
|
-
<h3>2. Authenticate Claude Code</h3>
|
|
941
|
-
<pre data-label="shell"># Run once to authenticate
|
|
942
|
-
claude
|
|
943
|
-
|
|
944
|
-
# Verify it works
|
|
945
|
-
claude -p "say hello" --output-format text</pre>
|
|
946
|
-
|
|
947
|
-
<h3>3. Clone and configure</h3>
|
|
948
|
-
<pre data-label="shell">git clone git@github.com:jacksoncage/ove.git
|
|
949
|
-
cd ove
|
|
950
|
-
bun install
|
|
951
|
-
ove init # interactive setup</pre>
|
|
952
|
-
|
|
953
|
-
<h3>4. Run as a systemd service</h3>
|
|
954
|
-
<pre data-label="shell"># Copy the service file
|
|
955
|
-
sudo cp deploy/ove.service /etc/systemd/system/ove.service
|
|
956
|
-
|
|
957
|
-
# Enable and start
|
|
958
|
-
sudo systemctl enable ove
|
|
959
|
-
sudo systemctl start ove
|
|
960
|
-
|
|
961
|
-
# Check logs
|
|
962
|
-
journalctl -u ove -f</pre>
|
|
963
|
-
|
|
964
|
-
<h3>5. Verify</h3>
|
|
965
|
-
<p>Send Ove a message on any configured transport and type <code>help</code>. He should respond. Grumpily.</p>
|
|
966
|
-
|
|
967
|
-
<div class="ove-says">
|
|
968
|
-
"If the service doesn't start, check the logs. That's what they're there for. I'm not going to hold your hand."
|
|
969
|
-
<span class="attr">— Ove, on debugging</span>
|
|
970
|
-
</div>
|
|
971
|
-
</section>
|
|
972
|
-
|
|
973
|
-
</div>
|
|
974
|
-
|
|
975
|
-
<footer>
|
|
976
|
-
Built by <a href="https://www.jacksoncage.se/">Love Billingskog Nyberg</a> · Powered by <a href="https://docs.anthropic.com/en/docs/claude-code">Claude Code</a> & <a href="https://github.com/openai/codex">Codex</a>
|
|
977
|
-
</footer>
|
|
978
|
-
|
|
979
|
-
</body>
|
|
980
|
-
</html>
|