agent-theatre 0.1.2 → 0.1.4

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 ADDED
@@ -0,0 +1,47 @@
1
+ # agent-theatre
2
+
3
+ **Watch your coding agent work — as a film, not a log.**
4
+
5
+ Agent Theatre turns a Claude Code session into an animated scene. Your repo becomes a stage — folders are districts, files are buildings, test files are beacons — and the agent is a character who walks to the files it reads, builds on the files it edits, runs tests at the beacons and chases gremlins when they fail. Twenty stages, a cast of twelve, every claim checked: **if the agent says it's done but no test has passed since the last edit, a gremlin hides behind it.**
6
+
7
+ Site and playable episodes: **https://agenttheatre.dev**
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ cd your-repo
13
+ npx agent-theatre hooks install # once: adds hooks to ~/.claude/settings.json (backup kept)
14
+ npx agent-theatre # opens the stage for this repo; run Claude Code in another terminal
15
+ ```
16
+
17
+ Node 22+. Works with the Claude Code CLI, desktop app and IDE extensions — anything that runs hooks. Hooks are picked up without restarting Claude Code.
18
+
19
+ ## Commands
20
+
21
+ | Command | What it does |
22
+ |---|---|
23
+ | `agent-theatre` | Start the local server for the current repo and open the stage (live). `--port <n>`, `--no-open`, `--set <id>` |
24
+ | `agent-theatre replay [session]` | Replay a past Claude Code session of this repo; no argument lists them, `--latest` picks the newest |
25
+ | `agent-theatre hooks install` | Add the hooks to `~/.claude/settings.json` (non-destructive, backup at `settings.json.bak-agent-theatre`) |
26
+ | `agent-theatre hooks uninstall` | Remove them, byte-for-byte |
27
+ | `agent-theatre hooks status` | Show whether they are installed |
28
+
29
+ Stage ids: `island orbit metropolis harbour observatory greenhouse workshop arcade kingdom outpost snowfield canopy lagoon volcano cloudtop reef bazaar monastery moonbase lanterns`. The picker suggests the ones that fit your repo.
30
+
31
+ ## Privacy
32
+
33
+ Runs entirely on your machine and listens only on `127.0.0.1`. Events carry repo-relative paths, line counts, a command's first word and a classified intent — never file contents, prompts, command bodies or search queries. A test in the repo plants marker strings in all of those and asserts zero leaks. Paths outside the repo appear as `//ext/<name>`; your home directory as `~/`. The hook always exits 0 and never blocks a tool call.
34
+
35
+ ## VS Code
36
+
37
+ The same stage inside the editor: the `.vsix` is attached to every [GitHub release](https://github.com/shreyassaw5394/agent-theatre/releases).
38
+
39
+ ## Library
40
+
41
+ ```ts
42
+ import { Theatre, scanRepo, repoIdFor, listSessions, installHooks } from "agent-theatre";
43
+ const t = new Theatre({ cwd, repoId: repoIdFor(cwd), files: scanRepo(cwd) });
44
+ await t.listen(0); // t.url → http://127.0.0.1:<port>/
45
+ ```
46
+
47
+ MIT · source: https://github.com/shreyassaw5394/agent-theatre
package/dist/cli.js CHANGED
@@ -9,9 +9,9 @@ import pc from "picocolors";
9
9
 
10
10
  // src/server.ts
11
11
  import { createServer } from "http";
12
- import { existsSync, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
12
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync, realpathSync } from "fs";
13
13
  import { homedir as homedir2 } from "os";
14
- import { join as join2, basename as basename2 } from "path";
14
+ import { join as join2, basename as basename2, resolve, sep } from "path";
15
15
  import { Hono } from "hono";
16
16
  import { getRequestListener } from "@hono/node-server";
17
17
  import { WebSocketServer } from "ws";
@@ -1206,6 +1206,8 @@ var Theatre = class {
1206
1206
  } catch {
1207
1207
  return c.body(null, 204);
1208
1208
  }
1209
+ const cwd = typeof payload?.cwd === "string" ? payload.cwd : "";
1210
+ if (cwd && !isWithin(cwd, this.opts.cwd)) return c.body(null, 204);
1209
1211
  const { events } = hookPayloadToEvents(payload, { home: homedir2() });
1210
1212
  if (events.length) this.ingest(events);
1211
1213
  return c.body(null, 204);
@@ -1306,6 +1308,17 @@ var Theatre = class {
1306
1308
  return basename2(this.opts.cwd);
1307
1309
  }
1308
1310
  };
1311
+ function isWithin(dir, root) {
1312
+ const norm3 = (p) => {
1313
+ try {
1314
+ return realpathSync(p);
1315
+ } catch {
1316
+ return resolve(p);
1317
+ }
1318
+ };
1319
+ const d = norm3(dir), r = norm3(root);
1320
+ return d === r || d.startsWith(r.endsWith(sep) ? r : r + sep);
1321
+ }
1309
1322
 
1310
1323
  // src/scan.ts
1311
1324
  import { readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "fs";