@nocville/npc 0.0.0-stage → 0.2.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.
Files changed (213) hide show
  1. package/README.md +268 -2
  2. package/dist/cjs/cli.js +584 -0
  3. package/dist/cjs/core/api-key.js +69 -0
  4. package/dist/cjs/core/base-npc.js +160 -0
  5. package/dist/cjs/core/config.js +108 -0
  6. package/dist/cjs/core/event-npc.js +14 -0
  7. package/dist/cjs/core/logger.js +39 -0
  8. package/dist/cjs/core/polling-npc.js +65 -0
  9. package/dist/cjs/core/screen.js +28 -0
  10. package/dist/cjs/core/session-filter.js +150 -0
  11. package/dist/cjs/core/text.js +15 -0
  12. package/dist/cjs/core/thresholds.js +48 -0
  13. package/dist/cjs/core/types.js +10 -0
  14. package/dist/cjs/core/user-config.js +92 -0
  15. package/dist/cjs/doctor.js +130 -0
  16. package/dist/cjs/index.js +95 -0
  17. package/dist/cjs/npcs/agent-common/activity.js +291 -0
  18. package/dist/cjs/npcs/agent-common/git-stats.js +71 -0
  19. package/dist/cjs/npcs/agent-common/hook-runner.js +252 -0
  20. package/dist/cjs/npcs/agent-common/hook-settings.js +279 -0
  21. package/dist/cjs/npcs/agent-common/liveness.js +112 -0
  22. package/dist/cjs/npcs/agent-common/lock.js +99 -0
  23. package/dist/cjs/npcs/agent-common/runtime.js +154 -0
  24. package/dist/cjs/npcs/agent-common/session.js +412 -0
  25. package/dist/cjs/npcs/agent-common/shim.js +96 -0
  26. package/dist/cjs/npcs/agent-common/watchdog.js +149 -0
  27. package/dist/cjs/npcs/claude-code/defaults.js +20 -0
  28. package/dist/cjs/npcs/claude-code/hook-handler.js +139 -0
  29. package/dist/cjs/npcs/claude-code/index.js +104 -0
  30. package/dist/cjs/npcs/claude-code/setup.js +133 -0
  31. package/dist/cjs/npcs/claude-code/state-machine.js +130 -0
  32. package/dist/cjs/npcs/codex/defaults.js +32 -0
  33. package/dist/cjs/npcs/codex/hook-handler.js +116 -0
  34. package/dist/cjs/npcs/codex/index.js +92 -0
  35. package/dist/cjs/npcs/codex/rollout.js +246 -0
  36. package/dist/cjs/npcs/codex/setup.js +104 -0
  37. package/dist/cjs/npcs/codex/watcher.js +361 -0
  38. package/dist/cjs/npcs/cursor/defaults.js +15 -0
  39. package/dist/cjs/npcs/cursor/hook-handler.js +65 -0
  40. package/dist/cjs/npcs/cursor/index.js +72 -0
  41. package/dist/cjs/npcs/cursor/normalize.js +54 -0
  42. package/dist/cjs/npcs/cursor/setup.js +98 -0
  43. package/dist/cjs/npcs/gemini/defaults.js +15 -0
  44. package/dist/cjs/npcs/gemini/hook-handler.js +93 -0
  45. package/dist/cjs/npcs/gemini/index.js +69 -0
  46. package/dist/cjs/npcs/gemini/setup.js +98 -0
  47. package/dist/cjs/npcs/github-watcher/defaults.js +11 -0
  48. package/dist/cjs/npcs/github-watcher/format.js +21 -0
  49. package/dist/cjs/npcs/github-watcher/github-api.js +126 -0
  50. package/dist/cjs/npcs/github-watcher/index.js +148 -0
  51. package/dist/cjs/npcs/internet-connectivity/checks.js +92 -0
  52. package/dist/cjs/npcs/internet-connectivity/defaults.js +22 -0
  53. package/dist/cjs/npcs/internet-connectivity/index.js +251 -0
  54. package/dist/cjs/npcs/log-watcher/buffer.js +40 -0
  55. package/dist/cjs/npcs/log-watcher/defaults.js +12 -0
  56. package/dist/cjs/npcs/log-watcher/index.js +161 -0
  57. package/dist/cjs/npcs/log-watcher/tail.js +81 -0
  58. package/dist/cjs/npcs/process-watcher/defaults.js +9 -0
  59. package/dist/cjs/npcs/process-watcher/index.js +140 -0
  60. package/dist/cjs/npcs/process-watcher/process-scanner.js +114 -0
  61. package/dist/cjs/npcs/receptionist/defaults.js +9 -0
  62. package/dist/cjs/npcs/receptionist/index.js +120 -0
  63. package/dist/cjs/npcs/receptionist/weather.js +129 -0
  64. package/dist/cjs/npcs/system-monitor/collectors.js +68 -0
  65. package/dist/cjs/npcs/system-monitor/defaults.js +13 -0
  66. package/dist/cjs/npcs/system-monitor/index.js +105 -0
  67. package/dist/cjs/npcs/web-page-watcher/basic-mode.js +58 -0
  68. package/dist/cjs/npcs/web-page-watcher/capture.js +80 -0
  69. package/dist/cjs/npcs/web-page-watcher/defaults.js +31 -0
  70. package/dist/cjs/npcs/web-page-watcher/index.js +234 -0
  71. package/dist/cjs/npcs/web-page-watcher/pixelate.js +181 -0
  72. package/dist/cjs/package.json +1 -0
  73. package/dist/cli.d.ts +3 -0
  74. package/dist/cli.js +549 -0
  75. package/dist/core/api-key.d.ts +34 -0
  76. package/dist/core/api-key.js +64 -0
  77. package/dist/core/base-npc.d.ts +41 -0
  78. package/dist/core/base-npc.js +156 -0
  79. package/dist/core/config.d.ts +22 -0
  80. package/dist/core/config.js +101 -0
  81. package/dist/core/event-npc.d.ts +8 -0
  82. package/dist/core/event-npc.js +10 -0
  83. package/dist/core/logger.d.ts +11 -0
  84. package/dist/core/logger.js +35 -0
  85. package/dist/core/polling-npc.d.ts +18 -0
  86. package/dist/core/polling-npc.js +61 -0
  87. package/dist/core/screen.d.ts +11 -0
  88. package/dist/core/screen.js +24 -0
  89. package/dist/core/session-filter.d.ts +59 -0
  90. package/dist/core/session-filter.js +143 -0
  91. package/dist/core/text.d.ts +6 -0
  92. package/dist/core/text.js +12 -0
  93. package/dist/core/thresholds.d.ts +10 -0
  94. package/dist/core/thresholds.js +44 -0
  95. package/dist/core/types.d.ts +60 -0
  96. package/dist/core/types.js +7 -0
  97. package/dist/core/user-config.d.ts +43 -0
  98. package/dist/core/user-config.js +85 -0
  99. package/dist/doctor.d.ts +8 -0
  100. package/dist/doctor.js +126 -0
  101. package/dist/index.d.ts +41 -0
  102. package/dist/index.js +26 -0
  103. package/dist/npcs/agent-common/activity.d.ts +42 -0
  104. package/dist/npcs/agent-common/activity.js +280 -0
  105. package/dist/npcs/agent-common/git-stats.d.ts +11 -0
  106. package/dist/npcs/agent-common/git-stats.js +67 -0
  107. package/dist/npcs/agent-common/hook-runner.d.ts +62 -0
  108. package/dist/npcs/agent-common/hook-runner.js +245 -0
  109. package/dist/npcs/agent-common/hook-settings.d.ts +75 -0
  110. package/dist/npcs/agent-common/hook-settings.js +265 -0
  111. package/dist/npcs/agent-common/liveness.d.ts +12 -0
  112. package/dist/npcs/agent-common/liveness.js +105 -0
  113. package/dist/npcs/agent-common/lock.d.ts +6 -0
  114. package/dist/npcs/agent-common/lock.js +95 -0
  115. package/dist/npcs/agent-common/runtime.d.ts +16 -0
  116. package/dist/npcs/agent-common/runtime.js +149 -0
  117. package/dist/npcs/agent-common/session.d.ts +192 -0
  118. package/dist/npcs/agent-common/session.js +390 -0
  119. package/dist/npcs/agent-common/shim.d.ts +4 -0
  120. package/dist/npcs/agent-common/shim.js +90 -0
  121. package/dist/npcs/agent-common/watchdog.d.ts +16 -0
  122. package/dist/npcs/agent-common/watchdog.js +144 -0
  123. package/dist/npcs/claude-code/defaults.d.ts +15 -0
  124. package/dist/npcs/claude-code/defaults.js +17 -0
  125. package/dist/npcs/claude-code/hook-handler.d.ts +40 -0
  126. package/dist/npcs/claude-code/hook-handler.js +131 -0
  127. package/dist/npcs/claude-code/index.d.ts +36 -0
  128. package/dist/npcs/claude-code/index.js +92 -0
  129. package/dist/npcs/claude-code/setup.d.ts +58 -0
  130. package/dist/npcs/claude-code/setup.js +121 -0
  131. package/dist/npcs/claude-code/state-machine.d.ts +28 -0
  132. package/dist/npcs/claude-code/state-machine.js +125 -0
  133. package/dist/npcs/codex/defaults.d.ts +24 -0
  134. package/dist/npcs/codex/defaults.js +28 -0
  135. package/dist/npcs/codex/hook-handler.d.ts +24 -0
  136. package/dist/npcs/codex/hook-handler.js +110 -0
  137. package/dist/npcs/codex/index.d.ts +21 -0
  138. package/dist/npcs/codex/index.js +76 -0
  139. package/dist/npcs/codex/rollout.d.ts +39 -0
  140. package/dist/npcs/codex/rollout.js +242 -0
  141. package/dist/npcs/codex/setup.d.ts +37 -0
  142. package/dist/npcs/codex/setup.js +96 -0
  143. package/dist/npcs/codex/watcher.d.ts +65 -0
  144. package/dist/npcs/codex/watcher.js +356 -0
  145. package/dist/npcs/cursor/defaults.d.ts +9 -0
  146. package/dist/npcs/cursor/defaults.js +11 -0
  147. package/dist/npcs/cursor/hook-handler.d.ts +8 -0
  148. package/dist/npcs/cursor/hook-handler.js +60 -0
  149. package/dist/npcs/cursor/index.d.ts +18 -0
  150. package/dist/npcs/cursor/index.js +57 -0
  151. package/dist/npcs/cursor/normalize.d.ts +6 -0
  152. package/dist/npcs/cursor/normalize.js +49 -0
  153. package/dist/npcs/cursor/setup.d.ts +29 -0
  154. package/dist/npcs/cursor/setup.js +90 -0
  155. package/dist/npcs/gemini/defaults.d.ts +9 -0
  156. package/dist/npcs/gemini/defaults.js +11 -0
  157. package/dist/npcs/gemini/hook-handler.d.ts +10 -0
  158. package/dist/npcs/gemini/hook-handler.js +87 -0
  159. package/dist/npcs/gemini/index.d.ts +17 -0
  160. package/dist/npcs/gemini/index.js +56 -0
  161. package/dist/npcs/gemini/setup.d.ts +29 -0
  162. package/dist/npcs/gemini/setup.js +90 -0
  163. package/dist/npcs/github-watcher/defaults.d.ts +8 -0
  164. package/dist/npcs/github-watcher/defaults.js +8 -0
  165. package/dist/npcs/github-watcher/format.d.ts +3 -0
  166. package/dist/npcs/github-watcher/format.js +18 -0
  167. package/dist/npcs/github-watcher/github-api.d.ts +55 -0
  168. package/dist/npcs/github-watcher/github-api.js +120 -0
  169. package/dist/npcs/github-watcher/index.d.ts +35 -0
  170. package/dist/npcs/github-watcher/index.js +144 -0
  171. package/dist/npcs/internet-connectivity/checks.d.ts +24 -0
  172. package/dist/npcs/internet-connectivity/checks.js +84 -0
  173. package/dist/npcs/internet-connectivity/defaults.d.ts +20 -0
  174. package/dist/npcs/internet-connectivity/defaults.js +19 -0
  175. package/dist/npcs/internet-connectivity/index.d.ts +55 -0
  176. package/dist/npcs/internet-connectivity/index.js +246 -0
  177. package/dist/npcs/log-watcher/buffer.d.ts +18 -0
  178. package/dist/npcs/log-watcher/buffer.js +34 -0
  179. package/dist/npcs/log-watcher/defaults.d.ts +9 -0
  180. package/dist/npcs/log-watcher/defaults.js +9 -0
  181. package/dist/npcs/log-watcher/index.d.ts +45 -0
  182. package/dist/npcs/log-watcher/index.js +157 -0
  183. package/dist/npcs/log-watcher/tail.d.ts +9 -0
  184. package/dist/npcs/log-watcher/tail.js +78 -0
  185. package/dist/npcs/process-watcher/defaults.d.ts +6 -0
  186. package/dist/npcs/process-watcher/defaults.js +6 -0
  187. package/dist/npcs/process-watcher/index.d.ts +26 -0
  188. package/dist/npcs/process-watcher/index.js +136 -0
  189. package/dist/npcs/process-watcher/process-scanner.d.ts +19 -0
  190. package/dist/npcs/process-watcher/process-scanner.js +110 -0
  191. package/dist/npcs/receptionist/defaults.d.ts +6 -0
  192. package/dist/npcs/receptionist/defaults.js +6 -0
  193. package/dist/npcs/receptionist/index.d.ts +29 -0
  194. package/dist/npcs/receptionist/index.js +116 -0
  195. package/dist/npcs/receptionist/weather.d.ts +37 -0
  196. package/dist/npcs/receptionist/weather.js +121 -0
  197. package/dist/npcs/system-monitor/collectors.d.ts +45 -0
  198. package/dist/npcs/system-monitor/collectors.js +60 -0
  199. package/dist/npcs/system-monitor/defaults.d.ts +7 -0
  200. package/dist/npcs/system-monitor/defaults.js +10 -0
  201. package/dist/npcs/system-monitor/index.d.ts +27 -0
  202. package/dist/npcs/system-monitor/index.js +101 -0
  203. package/dist/npcs/web-page-watcher/basic-mode.d.ts +16 -0
  204. package/dist/npcs/web-page-watcher/basic-mode.js +52 -0
  205. package/dist/npcs/web-page-watcher/capture.d.ts +49 -0
  206. package/dist/npcs/web-page-watcher/capture.js +75 -0
  207. package/dist/npcs/web-page-watcher/defaults.d.ts +19 -0
  208. package/dist/npcs/web-page-watcher/defaults.js +27 -0
  209. package/dist/npcs/web-page-watcher/index.d.ts +42 -0
  210. package/dist/npcs/web-page-watcher/index.js +230 -0
  211. package/dist/npcs/web-page-watcher/pixelate.d.ts +36 -0
  212. package/dist/npcs/web-page-watcher/pixelate.js +172 -0
  213. package/package.json +73 -3
package/README.md CHANGED
@@ -1,3 +1,269 @@
1
- # Temporary Holding Version
1
+ # @nocville/npc
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Put your AI coding sessions and your infrastructure in the [Nocville](https://nocville.com)
4
+ office as live NPCs. Each Claude Code, Codex, Gemini CLI or Cursor session becomes a coworker named after its
5
+ project (`Claude · nocville`, `Codex · gridbreak`) who walks to a desk while working, raises
6
+ a hand when it needs you, dozes when idle, and shows what it's doing on its speech bubble and
7
+ desk sign: `Editing src/GameScene.ts`, `Running pnpm test`, `Searching for 'reconnect'`,
8
+ `Needs your approval`, `Thinking…`. It leaves the office when the session ends.
9
+
10
+ Requires Node.js 20+. Your team API key is in your Nocville team settings.
11
+
12
+ ## Quick start: AI coworkers
13
+
14
+ ```bash
15
+ npx @nocville/npc setup
16
+ ```
17
+
18
+ Paste your key at the prompt (input is hidden, so the key never lands in your shell history).
19
+ For scripts, pass `--api-key=<your team key>` or set `NOCVILLE_API_KEY` instead. That's it. The command saves your key to `~/.config/nocville/config.json` (mode 0600), then
20
+ hooks up whichever agents you have installed:
21
+
22
+ - **Claude Code**: adds hooks to `~/.claude/settings.json`. New sessions show up right away;
23
+ restart sessions that are already running.
24
+ - **Codex**: adds hooks to `~/.codex/hooks.json`. Codex won't run a new hook until you've
25
+ approved it, so start Codex once and run **`/hooks`** to trust the Nocville hooks.
26
+
27
+ - **Gemini CLI**: adds eight telemetry hooks to `~/.gemini/settings.json`, preserving your
28
+ settings and existing hooks. Start a new session after setup. Hooks return only `{}`.
29
+
30
+ - **Cursor**: adds native hooks to `~/.cursor/hooks.json`. Existing Nocville Claude hooks
31
+ also detect Cursor when its third-party hook imports are enabled. Both installations share
32
+ one coworker and deduplicate matching events. No permission decisions or model output are sent.
33
+
34
+ Self-hosting or developing locally? Add `--api-url=http://localhost:3601`. To send sessions to
35
+ a map other than your team's default, add `--map="Trading Floor"`.
36
+
37
+ Prefer a global install? Run `npm i -g @nocville/npc`, then `nocville-npc setup --api-key=…`.
38
+ When setup runs through `npx`, it copies itself to `~/.config/nocville/runtime/` so the hooks
39
+ keep working after npm clears its cache.
40
+
41
+ ### Choose which sessions appear
42
+
43
+ By default every agent session publishes to your office. To keep some out, or to show only
44
+ some, edit `~/.config/nocville/sessions.json` (setup creates an empty one):
45
+
46
+ ```json
47
+ {
48
+ "allow": { "projects": [], "sessions": [] },
49
+ "deny": { "projects": ["client-*", "~/work/secret"], "sessions": ["*private*"] }
50
+ }
51
+ ```
52
+
53
+ - **Empty lists** (or no file): every session publishes.
54
+ - **`deny` always wins.** If any `allow` entry is set, **only** matching sessions publish.
55
+ - **`projects`**: a folder name anywhere in the session's working directory (`client-*` also
56
+ covers `~/work/client-acme/api`), or a path containing `/` (`~/work/secret` covers that folder
57
+ and everything inside it).
58
+ - **`sessions`**: a Claude Code session name (set with `/rename`). Unnamed sessions never match.
59
+ - Patterns are case-insensitive; `*` matches anything, `?` one character.
60
+
61
+ The file is read on every update, so edits apply to running sessions without reinstalling: a
62
+ session that becomes filtered out leaves the office on its next update. If the file can't be
63
+ parsed, nothing publishes until it's fixed. `npx @nocville/npc sessions` shows the rules and
64
+ checks the current folder (`--project=<dir>`, `--name=<session name>` to test others).
65
+
66
+ ### Per-agent commands
67
+
68
+ | Claude Code | Codex |
69
+ | ------------------------------------ | ---------------------------------------------- |
70
+ | `nocville-npc claude-code setup` | `nocville-npc codex setup` (then `/hooks`) |
71
+ | `nocville-npc claude-code uninstall` | `nocville-npc codex uninstall` |
72
+ | | `nocville-npc codex watch` (no-hooks fallback) |
73
+
74
+ `nocville-npc gemini setup` and `nocville-npc gemini uninstall` manage Gemini alone.
75
+ Gemini uses a 5000 ms hook timeout; pushes and end-of-turn git summaries are detached.
76
+ Its Node process is identified by the Gemini executable path, not an unrelated Node parent.
77
+
78
+ `nocville-npc cursor setup` and `nocville-npc cursor uninstall` manage native Cursor hooks.
79
+ Native commands use 10-second timeouts. Setup installs only sessionStart/sessionEnd,
80
+ beforeSubmitPrompt, postToolUse/postToolUseFailure, afterFileEdit, stop, subagentStop and
81
+ preCompact; it removes older Nocville permission hooks while preserving other hooks.
82
+ Activity comes from completed tools. Native and imported Cursor invocations return exactly
83
+ `{}` with no decision fields. Payload fields identify imported Cursor; `CURSOR_VERSION`
84
+ alone never reclassifies Claude launched inside its terminal. The launcher suppresses child
85
+ stdout for every agent, including debug logs, and emits only the appropriate neutral response.
86
+ Cursor does not watch the editor process or start a watchdog: an abandoned conversation uses
87
+ the one-hour fallback heartbeat interval (unknown after two hours, despawn ten minutes later).
88
+ Uninstall intentionally leaves `{"version":1}` when no other settings remain.
89
+
90
+ Cursor's [documented tool names](https://cursor.com/docs/hooks#configuration) include Grep,
91
+ Delete, Task and MCP tools. Their summaries use the shared redaction/path handling; MCP names
92
+ are normalized to the shared MCP format. Real Cursor Shell/Read/Write verification with both
93
+ Claude-imported-only and native hooks is still required before a public support claim.
94
+
95
+ `nocville-npc uninstall` removes every Nocville hook. `nocville-npc init` only saves
96
+ credentials. Every setup command takes the key from `--api-key`, then `NOCVILLE_API_KEY`, then
97
+ the saved config, and otherwise prompts for it (in a terminal) or exits with an error. `nocville-npc doctor` checks connectivity and sends a test push.
98
+
99
+ ### What you'll see
100
+
101
+ | Agent state | NPC | Desk sign / bubble |
102
+ | -------------------------------- | ------------------------------------- | ---------------------------------------------- |
103
+ | Prompt submitted | walks to a desk, sits | `Thinking…` |
104
+ | Using a tool | stays at the desk | `Editing apps/web/x.ts`, `Running pnpm test` … |
105
+ | Permission prompt / question | stands up and raises a hand | `Needs your approval: Running rm -rf dist` |
106
+ | Turn finished, or at the prompt | dozes at the desk | `Done — waiting for next task` |
107
+ | Session ends (exit, `/clear`, …) | leaves the office (immediate despawn) | |
108
+
109
+ **Privacy:** prompts, file contents, edit text, and command output are never sent. Commands
110
+ are shortened to their first line, and anything that looks like a secret is redacted first:
111
+ env assignments, `--token=`/`--password` flags, bearer headers, URL credentials, and long
112
+ key-like strings. Paths are shown relative to the project.
113
+
114
+ **Lifecycle and overhead:** normal hooks run asynchronously, update a locked local state file,
115
+ and hand HTTP delivery to a detached process. Same-phase bursts coalesce at 1.5 seconds.
116
+ SessionEnd stays synchronous with a short timeout to deliver the offline update; failures
117
+ remain pending for retry. Hooks never output instructions or permission decisions.
118
+
119
+ A watchdog checks a known agent process every 15 seconds and heartbeats once a minute.
120
+ A killed process leaves the office on the next check. If the machine sleeps or loses power,
121
+ the NPC becomes unknown after three minutes and disappears after thirteen. Hosts where
122
+ process liveness cannot be established retain the legacy one-hour heartbeat fallback.
123
+ Claude subagent activity appears below the parent's current activity and disappears when
124
+ the subagent stops (or after ten minutes without activity).
125
+
126
+ Setup installs a stable `~/.config/nocville/bin/nocville-hook` launcher (respecting
127
+ `XDG_CONFIG_HOME` and `NOCVILLE_CONFIG`) and an immutable runtime copy when needed.
128
+ Re-running setup updates the runtime pointer without changing the hook command or Codex's
129
+ command trust. Old snapshots remain available to running watchdogs. Windows uses the sibling
130
+ `nocville-hook.cmd` launcher, which resolves the saved Node path and runs the same bootstrap.
131
+ Windows setup writes plain commands, never `-EncodedCommand` or an execution-policy override.
132
+ Re-running setup replaces the former encoded commands; uninstall still recognizes them.
133
+
134
+ | Agent | Windows command host and verification |
135
+ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
136
+ | Claude Code | Explicit per-hook `shell: "powershell"`; invokes `& '<shim>.cmd' claude-code hook`. Claude selects PowerShell 7 or falls back to 5.1. CI exercises both. [Claude hook shell reference](https://code.claude.com/docs/en/hooks#windows-powershell-tool). |
137
+ | Gemini | Its hook runner selects PowerShell 7 or 5.1. The saved `cmd.exe /d /s /c --% ...` preserves cmd quoting, with a terminating newline before Gemini's appended exit check. CI exercises both hosts with that check. [Hook reference](https://geminicli.com/docs/hooks/reference/), [runner](https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/hooks/hookRunner.ts), [shell selection](https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/utils/shell-utils.ts). |
138
+ | Cursor | Explicit `cmd.exe /d /s /c` launcher; CI executes the saved string under cmd. The [published hook contract](https://cursor.com/docs/hooks) does not specify the Windows outer shell; real Windows Cursor verification remains required. |
139
+ | Codex | Explicit `cmd.exe /d /s /c` launcher; CI executes the saved string under cmd. The [published hook contract](https://learn.chatgpt.com/docs/hooks) does not identify the Windows outer shell; real Windows Codex verification remains required. |
140
+
141
+ PowerShell's [stop-parsing token](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#the-stop-parsing-token)
142
+ ends at a newline, not a semicolon. Keep Gemini's saved newline: its runner appends PowerShell
143
+ code to the command. No event input is interpolated into shell commands. Windows support remains
144
+ unverified until the Windows CI job and real-agent checks pass; macOS tests cannot establish it.
145
+ **Windows Cursor users:** install native hooks with `nocville-npc cursor setup`; do not rely on
146
+ Cursor importing Claude Code hooks. Claude's saved hook uses PowerShell syntax and an explicit
147
+ `shell` field. If Cursor's import path ignores that field and uses cmd, the imported command can
148
+ fail before the shim runs and produce hook errors. Nocville cannot control Cursor's import path.
149
+
150
+ Windows real-agent owner checklist (required before support claims):
151
+
152
+ - Run the native Windows CI job, including PowerShell 5.1/7 and a spaced `O’Neil` profile path.
153
+ - Verify native Cursor Shell/Read/Write events after `nocville-npc cursor setup`.
154
+ - With Claude hooks also installed, check whether Cursor imports them and honors their `shell`
155
+ field. Record agent versions and any duplicate/error events; verify native Cursor hooks work
156
+ independently. Do not count an imported Claude hook as proof of native Cursor support.
157
+ - Verify the actual Claude, Gemini and Codex hosts using their installed saved commands.
158
+
159
+ Claude's single-quoted path doubles straight and curly apostrophes according to
160
+ [PowerShell quoting rules](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules#including-quote-characters-in-a-string).
161
+
162
+ Both launchers discard child stdout and preserve stderr; all SDK logger levels use stderr.
163
+ If Node is unavailable, the shim drains stdin and returns neutral `{}` for Cursor/Gemini
164
+ and Claude hooks (which may have been imported by Cursor); Codex stays silent. Session records are local, including terminal
165
+ records that prevent late events from reviving an ended NPC; `cleanup` removes old records.
166
+
167
+ ### How the Codex integration works
168
+
169
+ Codex supports Claude-style lifecycle hooks (`SessionStart`, `UserPromptSubmit`,
170
+ `PreToolUse`, `PermissionRequest`, `Stop`, `SessionEnd`, …) configured in
171
+ `~/.codex/hooks.json`. Setup merges a Nocville entry into each event, keeps your existing
172
+ hooks, makes a one-time `hooks.json.nocville-backup`, and is safe to run again.
173
+
174
+ `nocville-npc codex watch` is a deprecated, best-effort fallback for builds without hooks. It
175
+ reads the rollout logs Codex writes to `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`. It
176
+ shows one NPC per interactive session (subagent and reviewer threads are skipped; add
177
+ `--include-exec` to include `codex exec` runs). A session leaves the office after 30 minutes
178
+ without log activity (`--idle-timeout=<minutes>`) or when you press Ctrl-C. If hooks are
179
+ installed for a session (any hook state file, including an old terminal record), the watcher
180
+ leaves that session to them. `doctor` warns when both modes are running.
181
+
182
+ ## Monitoring NPCs
183
+
184
+ Long-running processes that each collect from one source and push status. Their NPC spawns
185
+ on your map automatically and despawns when you press Ctrl-C. Once you've run
186
+ `nocville-npc init`, no `--api-key` is needed.
187
+
188
+ ```bash
189
+ # Common options:
190
+ # --api-key=<key> Team API key (or NOCVILLE_API_KEY, or saved via `init`)
191
+ # --api-url=<url> Default https://nocville.com (or NOCVILLE_API_URL)
192
+ # --service-id=<id> Service ID (or NOCVILLE_SERVICE_ID)
193
+ # --name=<banner name> Friendly NPC name (or NOCVILLE_NAME)
194
+ # --map=<id-or-name> Target map (or NOCVILLE_MAP). Default: the team's default map
195
+ # --debug Verbose logging
196
+ ```
197
+
198
+ | NPC | What it does | Example |
199
+ | ----------------------- | -------------------------------------------------- | ------------------------------------------------------ |
200
+ | `system-monitor` | CPU / memory / disk / load with sparklines | `nocville-npc system-monitor --cpu-warn=75` |
201
+ | `internet-connectivity` | DNS, HTTP and TCP latency checks | `nocville-npc internet-connectivity` |
202
+ | `process-watcher` | Watches named processes, calls out deaths | `nocville-npc process-watcher --processes=nginx,redis` |
203
+ | `log-watcher` | Error bursts in log files, last lines on a monitor | `nocville-npc log-watcher --files=/var/log/app.log` |
204
+ | `receptionist` | Time and weather at the entrance, greets players | `nocville-npc receptionist --location="Berlin"` |
205
+ | `github-watcher` | New commits on a commit board | `nocville-npc github-watcher --repo=owner/name` |
206
+ | `web-page-watcher` | A web page rendered as a pixel-art TV | `nocville-npc web-page-watcher --url=https://…` |
207
+
208
+ Run `nocville-npc --help` to see every flag. A few notes:
209
+
210
+ - **log-watcher**: `--error-pattern="ERROR|FATAL" --window=300 --error-warn=5 --error-crit=20`.
211
+ - **receptionist**: weather comes from the keyless open-meteo API. Set the place with
212
+ `--lat=52.5 --lon=13.4` or `--location=…` (otherwise it uses IP geolocation). Add
213
+ `--fahrenheit` for °F.
214
+ - **github-watcher**: uses ETag polling. Pass `--token=$GITHUB_TOKEN` for a higher rate limit.
215
+ - **web-page-watcher**: the pixel-art TV needs `npm i puppeteer-core` and a local
216
+ Chrome/Chromium (`--chrome-path` or `CHROME_PATH`). Without them it falls back to a
217
+ title/status ticker. Other options: `--mode=auto|screenshot|basic` and `--screen=160x120`.
218
+
219
+ ## Programmatic use
220
+
221
+ ```typescript
222
+ import { SystemMonitorNPC, PollingNPC } from '@nocville/npc';
223
+ import type { CollectedMetrics } from '@nocville/npc';
224
+
225
+ await new SystemMonitorNPC({
226
+ apiUrl: 'https://nocville.com',
227
+ apiKey: 'noc_…',
228
+ serviceId: 'web-1',
229
+ }).start();
230
+
231
+ class MyNPC extends PollingNPC {
232
+ get name() {
233
+ return 'my-npc';
234
+ }
235
+ async collect(): Promise<CollectedMetrics> {
236
+ return { status: 'healthy', mood: 'happy', message: 'All good', metrics: { qps: 42 } };
237
+ }
238
+ }
239
+ ```
240
+
241
+ ## Developing in the monorepo
242
+
243
+ ```bash
244
+ pnpm install
245
+ pnpm --filter @nocville/npc build
246
+ pnpm --filter @nocville/npc test
247
+ node libs/nocville-npc-library/dist/cli.js --help
248
+ SMOKE_STUB=1 pnpm --filter @nocville/npc smoke # every payload shape, no server needed
249
+ ```
250
+
251
+ **Watch an AI-coworker NPC without running an agent.** `simulate` plays a scripted session
252
+ (start → prompt → Read → Edit → `pnpm test` → approval wait → idle → end) through the real
253
+ built hook handlers. It works in a throwaway HOME, so your `~/.claude` and `~/.codex` are
254
+ untouched, and the NPC appears as "Claude · demo" (or "Codex · demo"):
255
+
256
+ ```bash
257
+ pnpm --filter @nocville/npc simulate claude --api-key=<key> --api-url=http://localhost:3601
258
+ pnpm --filter @nocville/npc simulate codex --api-key=<key> --project=gridbreak --fast
259
+ # --hold stay idle after Stop until Ctrl-C, then end the session
260
+ # --fast 5x shorter pauses (CI)
261
+ ```
262
+
263
+ **Real hooks against your local checkout.** This writes real hooks into `~/.claude` and
264
+ `~/.codex` that run your working copy's `dist/cli.js`:
265
+
266
+ ```bash
267
+ node libs/nocville-npc-library/dist/cli.js setup --api-key=<key> --api-url=http://localhost:3601
268
+ node libs/nocville-npc-library/dist/cli.js uninstall # remove them again
269
+ ```