@nocville/npc 0.2.1 → 0.2.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jeff Marshall
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -7,7 +7,7 @@ a hand when it needs you, dozes when idle, and shows what it's doing on its spee
7
7
  desk sign: `Editing src/GameScene.ts`, `Running pnpm test`, `Searching for 'reconnect'`,
8
8
  `Needs your approval`, `Thinking…`. It leaves the office when the session ends.
9
9
 
10
- Requires Node.js 20+. Your team API key is in your Nocville team settings.
10
+ Requires Node.js 20+. Get an agent key in your Nocville office under **Connect AI coworkers**.
11
11
 
12
12
  ## Quick start: AI coworkers
13
13
 
@@ -16,23 +16,23 @@ npx @nocville/npc setup
16
16
  ```
17
17
 
18
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:
19
+ For scripts, pass `--api-key=<your key>` or set `NOCVILLE_API_KEY` instead. That's it. The
20
+ command saves your key to `~/.config/nocville/config.json` (readable only by you), then hooks
21
+ up whichever agents you have installed:
21
22
 
22
23
  - **Claude Code**: adds hooks to `~/.claude/settings.json`. New sessions show up right away;
23
24
  restart sessions that are already running.
24
25
  - **Codex**: adds hooks to `~/.codex/hooks.json`. Codex won't run a new hook until you've
25
26
  approved it, so start Codex once and run **`/hooks`** to trust the Nocville hooks.
27
+ - **Gemini CLI**: adds hooks to `~/.gemini/settings.json`, keeping your settings and existing
28
+ hooks. Start a new session after setup.
29
+ - **Cursor**: adds hooks to `~/.cursor/hooks.json`. Start a new chat after setup.
26
30
 
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 `{}`.
31
+ Your existing hooks are left alone, and Nocville's hooks never answer permission prompts or
32
+ change what your agent does; they only report activity.
29
33
 
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"`.
34
+ Running your own Nocville server? Add `--api-url=https://your-server`. To send sessions to a
35
+ map other than your team's default, add `--map="Trading Floor"`.
36
36
 
37
37
  Prefer a global install? Run `npm i -g @nocville/npc`, then `nocville-npc setup --api-key=…`.
38
38
  When setup runs through `npx`, it copies itself to `~/.config/nocville/runtime/` so the hooks
@@ -71,26 +71,8 @@ checks the current folder (`--project=<dir>`, `--name=<session name>` to test ot
71
71
  | `nocville-npc claude-code uninstall` | `nocville-npc codex uninstall` |
72
72
  | | `nocville-npc codex watch` (no-hooks fallback) |
73
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.
74
+ `nocville-npc gemini setup|uninstall` and `nocville-npc cursor setup|uninstall` manage
75
+ Gemini CLI and Cursor on their own. For Cursor, start a new chat after setup.
94
76
 
95
77
  `nocville-npc uninstall` removes every Nocville hook. `nocville-npc init` only saves
96
78
  credentials. Every setup command takes the key from `--api-key`, then `NOCVILLE_API_KEY`, then
@@ -111,58 +93,22 @@ are shortened to their first line, and anything that looks like a secret is reda
111
93
  env assignments, `--token=`/`--password` flags, bearer headers, URL credentials, and long
112
94
  key-like strings. Paths are shown relative to the project.
113
95
 
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.
96
+ **Overhead:** hooks return immediately and send updates from a background process, so your
97
+ agent never waits on the network. Rapid bursts of activity are combined into one update.
98
+
99
+ If an agent is killed, its coworker leaves the office within about 15 seconds. If your machine
100
+ sleeps or goes offline, the coworker greys out after a few minutes and leaves after about
101
+ thirteen. Claude subagent activity appears below the parent's current activity.
102
+
103
+ ### Windows
104
+
105
+ Setup works the same way on Windows; it writes a small `nocville-hook.cmd` launcher to
106
+ `~/.config/nocville/bin/` and points each agent's hooks at it. **Cursor users on Windows:**
107
+ install Cursor's own hooks with `nocville-npc cursor setup` rather than relying on Cursor
108
+ importing your Claude Code hooks.
109
+
110
+ Re-running `setup` (for example after upgrading with `npx @nocville/npc@latest setup`) keeps
111
+ your hooks in place and switches them to the new version; running sessions are not interrupted.
166
112
 
167
113
  ### How the Codex integration works
168
114
 
@@ -175,9 +121,8 @@ hooks, makes a one-time `hooks.json.nocville-backup`, and is safe to run again.
175
121
  reads the rollout logs Codex writes to `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`. It
176
122
  shows one NPC per interactive session (subagent and reviewer threads are skipped; add
177
123
  `--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.
124
+ without log activity (`--idle-timeout=<minutes>`) or when you press Ctrl-C. Sessions that
125
+ hooks already report are left to the hooks.
181
126
 
182
127
  ## Monitoring NPCs
183
128
 
@@ -224,7 +169,7 @@ import type { CollectedMetrics } from '@nocville/npc';
224
169
 
225
170
  await new SystemMonitorNPC({
226
171
  apiUrl: 'https://nocville.com',
227
- apiKey: 'noc_…',
172
+ apiKey: 'nvk_…',
228
173
  serviceId: 'web-1',
229
174
  }).start();
230
175
 
@@ -238,32 +183,7 @@ class MyNPC extends PollingNPC {
238
183
  }
239
184
  ```
240
185
 
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
- ```
186
+ ## Help
250
187
 
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
- ```
188
+ `nocville-npc --help` lists every command and flag, and `nocville-npc doctor` checks that your
189
+ key and connection work. More at [nocville.com](https://nocville.com).
@@ -113,7 +113,9 @@ function projectMatches(pattern, projectDir) {
113
113
  const re = globRegExp(pattern, false);
114
114
  return dir.split('/').some((segment) => segment && re.test(segment));
115
115
  }
116
- const expanded = normalizePath(pattern.startsWith('~') ? (0, node_os_1.homedir)() + pattern.slice(1) : pattern);
116
+ // A trailing `/*` or `/**` means "this folder and everything in it": subfolders already
117
+ // match through their ancestors, so drop it and the folder itself matches too.
118
+ const expanded = normalizePath((pattern.startsWith('~') ? (0, node_os_1.homedir)() + pattern.slice(1) : pattern).replace(/(\/\*{1,2})+$/, ''));
117
119
  const re = globRegExp(expanded, true);
118
120
  // The directory itself or any ancestor: a rule for a folder covers its subfolders.
119
121
  for (let p = dir; p; p = p.includes('/') ? p.slice(0, p.lastIndexOf('/')) : '') {
@@ -8,8 +8,8 @@
8
8
  * Patterns are case-insensitive globs (`*` any run of characters, `?` one character).
9
9
  * A `projects` pattern without a slash matches any folder name on the session's working
10
10
  * directory path (so `client-*` also covers `~/work/client-acme/api`); with a slash (or a
11
- * leading `~`) it matches that directory or anything inside it (`~/work/secret`,
12
- * `~/work/*`). A `sessions` pattern matches the agent's session name (Claude Code's
11
+ * leading `~`) it matches that directory or anything inside it; `~/work/secret`,
12
+ * `~/work/secret/*` and `~/work/secret/**` are equivalent). A `sessions` pattern matches the agent's session name (Claude Code's
13
13
  * session title); sessions without a name never match it.
14
14
  *
15
15
  * Read on every push, so edits apply to running sessions without reinstalling hooks.
@@ -106,7 +106,9 @@ function projectMatches(pattern, projectDir) {
106
106
  const re = globRegExp(pattern, false);
107
107
  return dir.split('/').some((segment) => segment && re.test(segment));
108
108
  }
109
- const expanded = normalizePath(pattern.startsWith('~') ? homedir() + pattern.slice(1) : pattern);
109
+ // A trailing `/*` or `/**` means "this folder and everything in it": subfolders already
110
+ // match through their ancestors, so drop it and the folder itself matches too.
111
+ const expanded = normalizePath((pattern.startsWith('~') ? homedir() + pattern.slice(1) : pattern).replace(/(\/\*{1,2})+$/, ''));
110
112
  const re = globRegExp(expanded, true);
111
113
  // The directory itself or any ancestor: a rule for a folder covers its subfolders.
112
114
  for (let p = dir; p; p = p.includes('/') ? p.slice(0, p.lastIndexOf('/')) : '') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocville/npc",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Put your Claude Code and Codex sessions (and your servers) in the Nocville office as live NPCs",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -24,7 +24,7 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "pngjs": "^7.0.0",
27
- "@nocville/node": "^0.2.1"
27
+ "@nocville/node": "^0.2.2"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "puppeteer-core": "*"
@@ -59,11 +59,6 @@
59
59
  ],
60
60
  "license": "MIT",
61
61
  "homepage": "https://nocville.com",
62
- "repository": {
63
- "type": "git",
64
- "url": "git+https://github.com/marshmansf/nocville.git",
65
- "directory": "libs/nocville-npc-library"
66
- },
67
62
  "scripts": {
68
63
  "build": "tsc && tsc -p tsconfig.cjs.json && node -e \"require('fs').writeFileSync('dist/cjs/package.json', JSON.stringify({ type: 'commonjs' }))\" && node -e \"require('fs').chmodSync('dist/cli.js', 0o755)\"",
69
64
  "clean": "rm -rf dist",