hivecode-mcp 0.12.2 → 0.13.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 (62) hide show
  1. package/HIVE_RULES.md +11 -40
  2. package/LICENSE +21 -21
  3. package/README.md +421 -167
  4. package/core.js +91 -91
  5. package/docs/ALWAYS_ON.md +310 -0
  6. package/docs/GITHUB_WORKFLOW.md +334 -0
  7. package/docs/HIVE_RULES.md +41 -41
  8. package/docs/MCP.md +157 -118
  9. package/docs/QUICKSTART.md +266 -0
  10. package/docs/SELF_HOSTING.md +256 -256
  11. package/docs/SLACK_SETUP.md +450 -0
  12. package/docs/TROUBLESHOOTING.md +87 -0
  13. package/hive-activity.js +336 -310
  14. package/hive-await.js +207 -0
  15. package/hive-branch.js +686 -0
  16. package/hive-busy.js +88 -88
  17. package/hive-coord.js +110 -110
  18. package/hive-gate.js +135 -135
  19. package/hive-github.js +1904 -0
  20. package/hive-hook-install.js +179 -103
  21. package/hive-instructions.js +74 -66
  22. package/hive-listen.js +81 -0
  23. package/hive-mailbox.js +520 -386
  24. package/hive-mcp-http.js +580 -0
  25. package/hive-mcp.js +579 -137
  26. package/hive-mux-proto.js +58 -58
  27. package/hive-mux.js +350 -350
  28. package/hive-notify.js +233 -0
  29. package/hive-obligations.js +247 -247
  30. package/hive-pr-staleness.js +189 -189
  31. package/hive-pr.js +350 -0
  32. package/hive-privacy.js +327 -288
  33. package/hive-receipts.js +233 -233
  34. package/hive-relay-health.js +230 -225
  35. package/hive-room-key.js +209 -0
  36. package/hive-secrets.js +89 -0
  37. package/hive-session.js +114 -0
  38. package/hive-setup.js +558 -445
  39. package/hive-slack-agent.js +396 -0
  40. package/hive-slack.js +1809 -0
  41. package/hive-stdio.js +172 -168
  42. package/hive-stop-hook.js +293 -241
  43. package/hive-throttle.js +118 -118
  44. package/hive-time.js +71 -0
  45. package/hive-update.js +58 -58
  46. package/hive-version.js +110 -59
  47. package/hive-webhook.js +54 -0
  48. package/icr-merge.js +58 -58
  49. package/icr.js +851 -745
  50. package/lang-json.js +211 -211
  51. package/lang-ruby.js +230 -230
  52. package/lang-toml.js +136 -136
  53. package/lang-yaml.js +183 -183
  54. package/limits.js +413 -394
  55. package/package.json +189 -164
  56. package/reconcile.cjs +614 -477
  57. package/slack-app-manifest-agent.json +61 -0
  58. package/slack-app-manifest-inbound.json +36 -0
  59. package/slack-app-manifest.json +27 -0
  60. package/substrate.js +252 -252
  61. package/sync.js +1956 -177
  62. package/grm-link.cjs +0 -141
package/core.js CHANGED
@@ -1,91 +1,91 @@
1
- // Shared pure logic, used by the sync clients and the agents — and unit-tested
2
- // in test.js. Keeping these here (instead of copy-pasted) means a fix in one
3
- // place fixes everywhere, and the tests exercise the REAL code.
4
-
5
- import reconcilePkg from './reconcile.cjs'
6
-
7
- export const {
8
- applyDiff,
9
- hasConflictMarkers,
10
- stripConflictMarkers,
11
- merge3,
12
- summarizeChange,
13
- changedRange,
14
- symbolsIn,
15
- icrMerge3,
16
- createReconciler,
17
- reconcile
18
- } = reconcilePkg
19
-
20
- // Optimistic concurrency: is a write based on `expected` still safe to apply?
21
- // Returns the new version on success, or { stale, current } if the region moved.
22
- export function safeBump(versions, region, expected) {
23
- const current = versions.get(region)
24
- if (current !== expected) return { stale: true, current }
25
- versions.set(region, (current ?? 0) + 1)
26
- return { ok: true, version: (current ?? 0) + 1 }
27
- }
28
-
29
- // Lock helpers (pessimistic concurrency). A lock is "alive" only if unexpired.
30
- export function lockHeldByOther(locks, file, me, now) {
31
- const l = locks.get(file)
32
- return l && l.owner !== me && l.exp > now ? l : null
33
- }
34
-
35
- // Deadlock-safe multi-file ordering: always acquire in a stable sorted order so
36
- // two agents can never form a circular wait (A holds 1 wants 2, B holds 2 wants 1).
37
- export function lockOrder(files) {
38
- return [...files].sort()
39
- }
40
-
41
- // Richer negotiation: when a lock holder gets a request, decide how to respond.
42
- // Returns one of:
43
- // grant — no conflict, hand over (after finishing)
44
- // counter — we both touch the same thing; propose taking turns
45
- // deny — the request is destructive while I'm still mid-edit; refuse for now
46
- // `holder` = { intent, done }, `request` = { from, summary }.
47
- export function negotiate(holder, request) {
48
- const h = (holder.intent || '').toLowerCase()
49
- const r = (request.summary || '').toLowerCase()
50
- if (/\b(delete|drop|remove|rewrite|overwrite)\b/.test(r) && !holder.done) {
51
- return { decision: 'deny', reason: 'that change is destructive and I am still mid-edit' }
52
- }
53
- const overlap = sharedWord(h, r)
54
- if (overlap && !holder.done) {
55
- return { decision: 'counter', reason: `we both touch "${overlap}"`, counter: 'let me finish, then it is yours' }
56
- }
57
- return { decision: 'grant', reason: 'no conflict' }
58
- }
59
-
60
- // Smallest shared "significant" word (len > 3) between two intent strings.
61
- function sharedWord(a, b) {
62
- const words = (s) => new Set(s.split(/[^a-z0-9]+/i).filter((w) => w.length > 3))
63
- const wb = words(b)
64
- for (const w of words(a)) if (wb.has(w)) return w
65
- return null
66
- }
67
-
68
- // --- Patch-apply-or-rework (for two writers in the SAME file) ---
69
- // 3-way line merge: given the `base` an agent read, the `mine` it produced, and
70
- // the `current` on the board now, decide what to write.
71
- // - current unchanged -> take mine
72
- // - I changed nothing -> take current
73
- // - changes on disjoint lines -> merge both (no rework)
74
- // - changes overlap the same lines -> { conflict } -> agent must re-do on current
75
- export function mergeEdit(base, mine, current) {
76
- if (current === base) return { ok: true, text: mine }
77
- if (mine === base) return { ok: true, text: current }
78
- if (mine === current) return { ok: true, text: mine }
79
- const b = base.split('\n')
80
- const mr = changedRange(b, mine.split('\n'))
81
- const tr = changedRange(b, current.split('\n'))
82
- if (mr.endBase <= tr.startBase || tr.endBase <= mr.startBase) {
83
- const out = b.slice()
84
- for (const e of [mr, tr].sort((x, y) => y.startBase - x.startBase)) {
85
- out.splice(e.startBase, e.endBase - e.startBase, ...e.newLines)
86
- }
87
- return { ok: true, text: out.join('\n') }
88
- }
89
- return { conflict: true } // same lines changed two ways -> re-reason on `current`
90
- }
91
-
1
+ // Shared pure logic, used by the sync clients and the agents — and unit-tested
2
+ // in test.js. Keeping these here (instead of copy-pasted) means a fix in one
3
+ // place fixes everywhere, and the tests exercise the REAL code.
4
+
5
+ import reconcilePkg from './reconcile.cjs'
6
+
7
+ export const {
8
+ applyDiff,
9
+ hasConflictMarkers,
10
+ stripConflictMarkers,
11
+ merge3,
12
+ summarizeChange,
13
+ changedRange,
14
+ symbolsIn,
15
+ icrMerge3,
16
+ createReconciler,
17
+ reconcile
18
+ } = reconcilePkg
19
+
20
+ // Optimistic concurrency: is a write based on `expected` still safe to apply?
21
+ // Returns the new version on success, or { stale, current } if the region moved.
22
+ export function safeBump(versions, region, expected) {
23
+ const current = versions.get(region)
24
+ if (current !== expected) return { stale: true, current }
25
+ versions.set(region, (current ?? 0) + 1)
26
+ return { ok: true, version: (current ?? 0) + 1 }
27
+ }
28
+
29
+ // Lock helpers (pessimistic concurrency). A lock is "alive" only if unexpired.
30
+ export function lockHeldByOther(locks, file, me, now) {
31
+ const l = locks.get(file)
32
+ return l && l.owner !== me && l.exp > now ? l : null
33
+ }
34
+
35
+ // Deadlock-safe multi-file ordering: always acquire in a stable sorted order so
36
+ // two agents can never form a circular wait (A holds 1 wants 2, B holds 2 wants 1).
37
+ export function lockOrder(files) {
38
+ return [...files].sort()
39
+ }
40
+
41
+ // Richer negotiation: when a lock holder gets a request, decide how to respond.
42
+ // Returns one of:
43
+ // grant — no conflict, hand over (after finishing)
44
+ // counter — we both touch the same thing; propose taking turns
45
+ // deny — the request is destructive while I'm still mid-edit; refuse for now
46
+ // `holder` = { intent, done }, `request` = { from, summary }.
47
+ export function negotiate(holder, request) {
48
+ const h = (holder.intent || '').toLowerCase()
49
+ const r = (request.summary || '').toLowerCase()
50
+ if (/\b(delete|drop|remove|rewrite|overwrite)\b/.test(r) && !holder.done) {
51
+ return { decision: 'deny', reason: 'that change is destructive and I am still mid-edit' }
52
+ }
53
+ const overlap = sharedWord(h, r)
54
+ if (overlap && !holder.done) {
55
+ return { decision: 'counter', reason: `we both touch "${overlap}"`, counter: 'let me finish, then it is yours' }
56
+ }
57
+ return { decision: 'grant', reason: 'no conflict' }
58
+ }
59
+
60
+ // Smallest shared "significant" word (len > 3) between two intent strings.
61
+ function sharedWord(a, b) {
62
+ const words = (s) => new Set(s.split(/[^a-z0-9]+/i).filter((w) => w.length > 3))
63
+ const wb = words(b)
64
+ for (const w of words(a)) if (wb.has(w)) return w
65
+ return null
66
+ }
67
+
68
+ // --- Patch-apply-or-rework (for two writers in the SAME file) ---
69
+ // 3-way line merge: given the `base` an agent read, the `mine` it produced, and
70
+ // the `current` on the board now, decide what to write.
71
+ // - current unchanged -> take mine
72
+ // - I changed nothing -> take current
73
+ // - changes on disjoint lines -> merge both (no rework)
74
+ // - changes overlap the same lines -> { conflict } -> agent must re-do on current
75
+ export function mergeEdit(base, mine, current) {
76
+ if (current === base) return { ok: true, text: mine }
77
+ if (mine === base) return { ok: true, text: current }
78
+ if (mine === current) return { ok: true, text: mine }
79
+ const b = base.split('\n')
80
+ const mr = changedRange(b, mine.split('\n'))
81
+ const tr = changedRange(b, current.split('\n'))
82
+ if (mr.endBase <= tr.startBase || tr.endBase <= mr.startBase) {
83
+ const out = b.slice()
84
+ for (const e of [mr, tr].sort((x, y) => y.startBase - x.startBase)) {
85
+ out.splice(e.startBase, e.endBase - e.startBase, ...e.newLines)
86
+ }
87
+ return { ok: true, text: out.join('\n') }
88
+ }
89
+ return { conflict: true } // same lines changed two ways -> re-reason on `current`
90
+ }
91
+
@@ -0,0 +1,310 @@
1
+ # Always on: agents that work while your laptop is closed
2
+
3
+ The GitHub watcher (`npx -y hivecode-mcp@latest github start`) does its work on the machine it runs
4
+ on. When that is a laptop and the lid closes, everything pauses: no issue is picked up, and a run in
5
+ progress stops. On wake, the watcher pushes each deadline back by the time it was asleep, so nothing
6
+ is killed for time it never had. But nothing happens overnight either.
7
+
8
+ For "your agents keep working after you go home", run the watcher on a machine that stays on.
9
+
10
+ ## What the machine needs
11
+
12
+ - Node.js 18 or newer, git, and the [GitHub CLI](https://cli.github.com).
13
+ - An agent CLI that runs headless, such as [Claude Code](https://docs.claude.com/en/docs/claude-code).
14
+ - A clone of your repository.
15
+ - `npx -y hivecode-mcp@latest setup`, run once, so the agent has its Hivecode tools.
16
+
17
+ Logins on a machine with no browser:
18
+ - **GitHub:** `gh auth login --with-token < token.txt`, using a fine-grained token with access to
19
+ this repository only. It needs read and write on contents, issues and pull requests, and read on
20
+ actions and checks.
21
+ - **The agent:** follow its own instructions for a server. Claude Code takes an API key
22
+ (`ANTHROPIC_API_KEY`) from the environment.
23
+ - **Slack (optional):** if `--slack-channel` was saved on your test start, `SLACK_BOT_TOKEN` must be
24
+ available in the environment (and `SLACK_APP_TOKEN` if using `--slack-allow`).
25
+
26
+ ---
27
+
28
+ ## The exit-code contract
29
+
30
+ The watcher (`github start`) reports its exit reason via standardized process exit codes:
31
+
32
+ | Exit code | Meaning | Service action |
33
+ |---|---|---|
34
+ | `0` | Clean stop (Ctrl+C, SIGTERM, normal exit) | Do not restart. |
35
+ | `1` | Transient failure (boot-time network glitch, `Q603 could not reach GitHub`, transient API drop, unexpected crash) | **Restart.** |
36
+ | `2` | Permanent refusal (`Q600` gh not logged in, `Q601` push denied, `Q602` agent not on PATH, `Q605` lock held, `Q633` missing bot token, invalid CLI flags) | **Do not restart.** |
37
+
38
+ > **Note:** The 0/1/2 exit-code contract was introduced in `hivecode-mcp@0.13.0`. Older versions (such as `0.12.4`) exited `1` for all errors, causing systemd's `RestartPreventExitStatus=2` to be bypassed and falling back on `StartLimit` rate-limiting alone.
39
+
40
+ Configuring your service to distinguish exit `1` from exit `2` prevents infinite restart loops when a lock is held or credentials are missing.
41
+
42
+ ---
43
+
44
+ ## Linux (systemd service)
45
+
46
+ First, run the watcher once by hand with your options in the clone to verify it and save settings:
47
+
48
+ ```bash
49
+ npx -y hivecode-mcp@latest github start --agent "claude -p --output-format json --allowedTools Bash Edit Write Read Glob Grep mcp__hivecode" --test "npm test"
50
+ ```
51
+
52
+ Stop it with Ctrl+C. The options are now saved in the clone's `.git/hivecode/github.json` file.
53
+
54
+ ### 1. Store secrets in an EnvironmentFile (never in the unit file)
55
+
56
+ Never put API tokens directly into a `.service` file: unit files are typically readable by any user on
57
+ the system and leak into `systemctl show`.
58
+
59
+ Create an environment file readable only by the service user:
60
+
61
+ ```bash
62
+ sudo mkdir -p /etc/hivecode
63
+ sudo touch /etc/hivecode/watcher.env
64
+ sudo chown hive:hive /etc/hivecode/watcher.env
65
+ sudo chmod 600 /etc/hivecode/watcher.env
66
+ ```
67
+
68
+ Add your credentials to `/etc/hivecode/watcher.env`:
69
+
70
+ ```ini
71
+ ANTHROPIC_API_KEY=sk-ant-api03-...
72
+ # Optional: if using Slack threads
73
+ SLACK_BOT_TOKEN=xoxb-...
74
+ SLACK_APP_TOKEN=xapp-...
75
+ ```
76
+
77
+ ### 2. Discover binary paths
78
+
79
+ systemd runs with a minimal `PATH` that does not include user-installed tools (`nvm`, `fnm`, or
80
+ `~/.local/bin`). Run this as the service user to find the exact locations of `npx`, `claude`, and `gh`:
81
+
82
+ ```bash
83
+ sudo -u hive -i command -v npx claude gh
84
+ ```
85
+
86
+ Copy the directory paths containing these binaries into the `Environment="PATH=..."` line below.
87
+
88
+ ### 3. Create the systemd unit
89
+
90
+ Write `/etc/systemd/system/hivecode-watcher.service`. Pin the version of `hivecode-mcp` you tested:
91
+
92
+ ```ini
93
+ [Unit]
94
+ Description=Hivecode GitHub watcher
95
+ After=network-online.target
96
+ Wants=network-online.target
97
+ StartLimitIntervalSec=300
98
+ StartLimitBurst=5
99
+
100
+ [Service]
101
+ User=hive
102
+ WorkingDirectory=/home/hive/your-repo
103
+ EnvironmentFile=/etc/hivecode/watcher.env
104
+ Environment="PATH=/home/hive/.nvm/versions/node/v20.18.0/bin:/home/hive/.local/bin:/usr/local/bin:/usr/bin:/bin"
105
+ ExecStart=/home/hive/.nvm/versions/node/v20.18.0/bin/npx -y hivecode-mcp@<the version you tested> github start
106
+ Restart=on-failure
107
+ RestartSec=30
108
+ RestartPreventExitStatus=2
109
+
110
+ [Install]
111
+ WantedBy=multi-user.target
112
+ ```
113
+
114
+ Key settings:
115
+ - `Restart=on-failure` with `RestartPreventExitStatus=2`: restarts if the network drops (exit 1), but halts immediately on permanent refusals like missing tokens or lock collisions (exit 2).
116
+ - `StartLimitIntervalSec=300` and `StartLimitBurst=5`: rate-limits unexpected crash loops to 5 attempts per 5 minutes.
117
+
118
+ Enable and inspect:
119
+
120
+ ```bash
121
+ sudo systemctl enable --now hivecode-watcher
122
+ journalctl -u hivecode-watcher -f
123
+ ```
124
+
125
+ ---
126
+
127
+ ## macOS (launchd LaunchAgent)
128
+
129
+ Run the watcher as a user LaunchAgent so it has access to user keychain credentials, git logins, and developer tools.
130
+
131
+ ### 1. Store secrets in an environment file (never in the plist)
132
+
133
+ `launchd` runs as a system process outside your interactive shell, so it does **not** source `~/.zshrc` or `~/.bash_profile`. Putting API keys directly into the `.plist` file leaves them in plaintext in `~/Library/LaunchAgents`.
134
+
135
+ Instead, create an environment file owned by you with mode `0600`:
136
+
137
+ ```bash
138
+ mkdir -p ~/.config/hivecode
139
+ touch ~/.config/hivecode/watcher.env
140
+ chmod 600 ~/.config/hivecode/watcher.env
141
+ ```
142
+
143
+ Add your keys to `~/.config/hivecode/watcher.env`:
144
+
145
+ ```ini
146
+ ANTHROPIC_API_KEY=sk-ant-api03-...
147
+ SLACK_BOT_TOKEN=xoxb-...
148
+ ```
149
+
150
+ *(Alternatively, macOS Keychain can supply secrets dynamically: `security find-generic-password -w -s "anthropic-api-key"`.)*
151
+
152
+ ### 2. Create the wrapper script
153
+
154
+ Create `~/.config/hivecode/run-watcher.sh` (`chmod 700 ~/.config/hivecode/run-watcher.sh`):
155
+
156
+ ```bash
157
+ #!/bin/bash
158
+ set -a
159
+ [ -f "$HOME/.config/hivecode/watcher.env" ] && . "$HOME/.config/hivecode/watcher.env"
160
+ set +a
161
+ exec /usr/local/bin/npx -y hivecode-mcp@<the version you tested> github start
162
+ ```
163
+
164
+ ### 3. Create the LaunchAgent plist
165
+
166
+ Create `~/Library/LaunchAgents/com.hivecode.watcher.plist`:
167
+
168
+ ```xml
169
+ <?xml version="1.0" encoding="UTF-8"?>
170
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
171
+ <plist version="1.0">
172
+ <dict>
173
+ <key>Label</key>
174
+ <string>com.hivecode.watcher</string>
175
+ <key>WorkingDirectory</key>
176
+ <string>/Users/youruser/your-repo</string>
177
+ <key>ProgramArguments</key>
178
+ <array>
179
+ <string>/bin/bash</string>
180
+ <string>/Users/youruser/.config/hivecode/run-watcher.sh</string>
181
+ </array>
182
+ <key>EnvironmentVariables</key>
183
+ <dict>
184
+ <key>PATH</key>
185
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
186
+ </dict>
187
+ <key>KeepAlive</key>
188
+ <dict>
189
+ <key>SuccessfulExit</key>
190
+ <false/>
191
+ </dict>
192
+ <key>ThrottleInterval</key>
193
+ <integer>60</integer>
194
+ <key>StandardOutPath</key>
195
+ <string>/Users/youruser/Library/Logs/hivecode-watcher.log</string>
196
+ <key>StandardErrorPath</key>
197
+ <string>/Users/youruser/Library/Logs/hivecode-watcher.err</string>
198
+ </dict>
199
+ </plist>
200
+ ```
201
+
202
+ > **Note on launchd restart policy:** Unlike systemd, `launchd` does not have a filter like `RestartPreventExitStatus`; `KeepAlive` with `SuccessfulExit: false` restarts on any non-zero exit code. Set `ThrottleInterval` to at least `60` seconds so a permanent error does not burn CPU, and check `~/Library/Logs/hivecode-watcher.err` if the service stops making progress.
203
+
204
+ Load and monitor:
205
+
206
+ ```bash
207
+ launchctl load ~/Library/LaunchAgents/com.hivecode.watcher.plist
208
+ tail -f ~/Library/Logs/hivecode-watcher.err
209
+ ```
210
+
211
+ To stop: `launchctl unload ~/Library/LaunchAgents/com.hivecode.watcher.plist`.
212
+
213
+ ---
214
+
215
+ ## Windows (Task Scheduler at Logon)
216
+
217
+ Run the watcher as a scheduled task at user logon. Do not run it as an isolated Windows Service (Session 0), which lacks user credential managers and interactive desktop configurations.
218
+
219
+ ### 1. Set user environment variables (never put keys in scripts)
220
+
221
+ Do not put API tokens inside `.cmd` or `.ps1` files. Set them as per-user environment variables in Windows Settings (System > About > Advanced system settings > Environment Variables), or via PowerShell:
222
+
223
+ ```powershell
224
+ [Environment]::SetEnvironmentVariable("ANTHROPIC_API_KEY", "sk-ant-api03-...", "User")
225
+ ```
226
+
227
+ The logon task inherits these variables automatically. The wrapper script contains no secrets.
228
+
229
+ ### 2. Create the runner wrapper
230
+
231
+ Because Task Scheduler's native retry triggers do not inspect process exit codes, create a wrapper script that enforces the rule: **halt only on clean stop (0) and permanent refusal (2); retry on everything else**.
232
+
233
+ Save as `%USERPROFILE%\.hivecode\run-watcher.cmd`:
234
+
235
+ ```bat
236
+ @echo off
237
+ setlocal
238
+ cd /d "C:\path\to\your-repo"
239
+
240
+ :loop
241
+ call npx -y hivecode-mcp@<the version you tested> github start >> "%USERPROFILE%\.hivecode\watcher.log" 2>&1
242
+ set "CODE=%ERRORLEVEL%"
243
+
244
+ if %CODE% EQU 0 (
245
+ echo [%DATE% %TIME%] Watcher stopped cleanly (exit 0). >> "%USERPROFILE%\.hivecode\watcher.log"
246
+ exit /b 0
247
+ )
248
+
249
+ if %CODE% EQU 2 (
250
+ echo [%DATE% %TIME%] Watcher permanent refusal (exit 2). Halting service. Check logs above. >> "%USERPROFILE%\.hivecode\watcher.log"
251
+ exit /b 2
252
+ )
253
+
254
+ echo [%DATE% %TIME%] Watcher exited with code %CODE%. Retrying in 30s... >> "%USERPROFILE%\.hivecode\watcher.log"
255
+ timeout /t 30 /nobreak >nul
256
+ goto loop
257
+ ```
258
+
259
+ ### 3. Register the task (requires Administrator elevation)
260
+
261
+ > **Elevation required:** Open Command Prompt or PowerShell with **Run as administrator**. Registering a task via `schtasks` modifies `%SystemRoot%\System32\Tasks`; running from a standard (unelevated) prompt fails with `ERROR: Access is denied.`
262
+
263
+ ```cmd
264
+ schtasks /Create /TN "HivecodeWatcher" /TR "cmd /c %USERPROFILE%\.hivecode\run-watcher.cmd" /SC ONLOGON /RL LIMITED
265
+ ```
266
+
267
+ Inspect output with:
268
+
269
+ ```cmd
270
+ type %USERPROFILE%\.hivecode\watcher.log
271
+ ```
272
+
273
+ ---
274
+
275
+ ## Power and sleep settings
276
+
277
+ The watcher pauses when the operating system enters sleep. Ensure sleep is disabled when plugged in:
278
+
279
+ - **Linux:** Server installations typically do not suspend. On desktop Linux, mask sleep targets if needed:
280
+ ```bash
281
+ sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
282
+ ```
283
+ - **macOS:** In **System Settings > Energy Saver** (or **Displays**), enable **Prevent automatic sleeping when the display is off**. Alternatively, run `caffeinate -s`.
284
+ - **Windows:** In **Settings > System > Power & battery**, set **When plugged in, put my device to sleep after** to **Never**.
285
+
286
+ ---
287
+
288
+ ## Status check
289
+
290
+ Regardless of operating system, you can inspect watcher state from inside the repository clone at any time:
291
+
292
+ ```bash
293
+ npx -y hivecode-mcp@latest github status
294
+ ```
295
+
296
+ ---
297
+
298
+ ## What can go wrong
299
+
300
+ | what goes wrong | what happens |
301
+ |---|---|
302
+ | Transient boot network failure (exit 1) | The service waits 30 seconds and retries automatically. |
303
+ | Two copies start (exit 2) | The second copy refuses immediately (`Q605`: one watcher per clone) and does not restart. |
304
+ | Missing API keys or tokens (exit 2) | The watcher halts with `Q600` or `Q633` and logs the remediation fix. |
305
+ | Agent not on PATH (exit 2) | The watcher halts with `Q602` without looping. Check binary directories with `command -v`. |
306
+ | `@latest` changes under a running service | Pin a version in the service file (e.g. `hivecode-mcp@<the version you tested>`), as above, and upgrade on purpose. |
307
+ | The GitHub token expires | The watcher stops asking and logs `Q600` in the service log. |
308
+ | The agent's credits run out | Each issue fails once, waits 15 minutes, fails again, and transitions to `hivecode:needs-human` with the agent's explanation. |
309
+ | The disk fills | Finished issue rooms are removed automatically; only the last failed run's directory is preserved for inspection. |
310
+ | Service user privileges | An agent can execute commands as the service user. Use a dedicated user (`hive`) with access only to the project repository. |