happenin 0.4.0 → 0.5.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.
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "happenin",
3
+ "description": "Record Cursor and Claude Code agent events in a local dashboard.",
4
+ "owner": {
5
+ "name": "Yogev Boaron Ben-Har"
6
+ },
7
+ "plugins": [
8
+ {
9
+ "name": "happenin",
10
+ "source": "./plugins/claude",
11
+ "description": "Record local Claude Code events for the happenin dashboard."
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "happenin",
3
+ "owner": { "name": "Yogev Boaron Ben-Har" },
4
+ "plugins": [
5
+ {
6
+ "name": "happenin",
7
+ "source": "./plugins/cursor",
8
+ "description": "Record local Cursor agent events for the happenin dashboard."
9
+ }
10
+ ]
11
+ }
package/README.md CHANGED
@@ -12,12 +12,12 @@ A CLI that records Cursor and Claude Code agent events to a local SQLite databas
12
12
  ## Try it
13
13
 
14
14
  ```bash
15
- npx -y happenin install # add record hooks to Cursor and Claude Code
15
+ npx -y happenin install # show plugin setup for Cursor and Claude Code
16
16
  npx -y happenin import # import existing transcripts
17
17
  npx -y happenin dashboard # open the live dashboard
18
18
  ```
19
19
 
20
- No global install needed: hooks installed this way run through `npx`. For a permanent setup, use the [global install](#install).
20
+ No global install needed: the packaged hooks run through `npx`. For a permanent setup, use the [global install](#install).
21
21
 
22
22
  ## Why
23
23
 
@@ -52,7 +52,7 @@ npm run build
52
52
  ## Quick start
53
53
 
54
54
  ```bash
55
- # Install hooks into Cursor and Claude Code (backs up existing configs)
55
+ # Show the plugin setup for Cursor and Claude Code
56
56
  happenin install
57
57
 
58
58
  # Import existing transcripts
@@ -68,7 +68,9 @@ The dashboard opens at `http://localhost:8765`. New events appear automatically.
68
68
 
69
69
  ### `happenin install [--cursor] [--claude]`
70
70
 
71
- Backs up and appends `happenin record` hooks to `~/.cursor/hooks.json` and `~/.claude/settings.json`. Backups are written to `~/.happenin/backups/`. Re-running `install` replaces the previous `happenin` hooks instead of duplicating them, and hooks installed through `npx -y happenin install` are written as `npx -y happenin` commands so they keep working without a global install.
71
+ Prints the supported plugin setup for Cursor and Claude Code. The packaged plugins call `npx -y happenin record`, so they work with both the trial and global-install flows without rewriting `~/.cursor/hooks.json` or `~/.claude/settings.json`. Use `--cursor` or `--claude` to show one client only.
72
+
73
+ Migrating from a pre-plugin install? Remove the old `happenin record` entries from `~/.cursor/hooks.json` and `~/.claude/settings.json` by hand (one-time step). Keep every unrelated hook.
72
74
 
73
75
  ```bash
74
76
  happenin install --cursor
@@ -187,18 +189,22 @@ npm install
187
189
  npm run build
188
190
  ```
189
191
 
192
+ Every pull request must reference an existing issue. If no issue exists, open one first, then link it in the pull request body with `Closes #<issue>`. CI enforces this on every pull request; bot PRs are skipped, and maintainers can bypass with the `no-issue` label.
193
+
190
194
  Before opening a pull request, run the full check suite:
191
195
 
192
196
  ```bash
193
- npm run build
194
- npm run typecheck
195
- npm run format
196
- npm run lint
197
- npm run duplicates:ci
198
- npm run knip:ci
199
- npm run test:ci
197
+ nub run build
198
+ nub run typecheck
199
+ nub run format
200
+ nub run lint
201
+ nub run duplicates:ci
202
+ nub run knip:ci
203
+ nub run test:ci
200
204
  ```
201
205
 
206
+ The same scripts also work with `npm run <script>`.
207
+
202
208
  See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) for details.
203
209
 
204
210
  ## Documentation
package/assets/help.md CHANGED
@@ -6,15 +6,11 @@ A CLI that records Cursor and Claude Code agent events to a local SQLite databas
6
6
 
7
7
  ### `happenin install [--cursor] [--claude]`
8
8
 
9
- Backs up and appends `happenin record` hooks to your Cursor and Claude Code configuration files.
10
- Re-running install replaces previous happenin hooks instead of duplicating them. When happenin
11
- runs through npx, hooks are written as `npx -y happenin` commands so they keep working without a
12
- global install.
9
+ Shows how to install the packaged Cursor and Claude Code plugins without changing user config files.
10
+ The plugin hooks use `npx -y happenin`, so they work with the trial and global-install flows.
13
11
 
14
- - `--cursor` — install Cursor hooks only.
15
- - `--claude` — install Claude Code hooks only.
16
-
17
- By default both are installed. Backups are written to `~/.happenin/backups/`.
12
+ - `--cursor` — show Cursor setup only.
13
+ - `--claude` — show Claude Code setup only.
18
14
 
19
15
  ### `happenin record <source> [event]`
20
16
 
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameSync as i,statSync as a,writeFileSync as o}from"node:fs";import s from"node:path";import c from"node:process";import{fileURLToPath as l}from"node:url";import{homedir as u}from"node:os";import{DatabaseSync as d}from"node:sqlite";import f from"node:http";import{execFile as p}from"node:child_process";const m=[`sessionStart`,`sessionEnd`,`beforeSubmitPrompt`,`preToolUse`,`postToolUse`,`postToolUseFailure`,`subagentStart`,`subagentStop`,`beforeShellExecution`,`afterShellExecution`,`beforeMCPExecution`,`afterMCPExecution`,`beforeReadFile`,`afterFileEdit`,`afterAgentResponse`,`afterAgentThought`,`preCompact`,`stop`],h=`SessionStart.SessionEnd.Setup.UserPromptSubmit.UserPromptExpansion.PreToolUse.PermissionRequest.PermissionDenied.PostToolUse.PostToolUseFailure.PostToolBatch.Notification.MessageDisplay.SubagentStart.SubagentStop.TaskCreated.TaskCompleted.Stop.StopFailure.TeammateIdle.InstructionsLoaded.ConfigChange.CwdChanged.DirectoryAdded.FileChanged.WorktreeCreate.WorktreeRemove.PreCompact.PostCompact.Elicitation.ElicitationResult`.split(`.`),g=new Set([`beforeSubmitPrompt`]),_=new Set([`preToolUse`,`beforeShellExecution`,`beforeMCPExecution`,`beforeReadFile`,`subagentStart`]),v=new Set([`UserPromptSubmit`,`UserPromptExpansion`]),y=new Set([`PreToolUse`,`PermissionRequest`,`SubagentStart`,`SubagentStop`,`TaskCreated`,`TaskCompleted`,`Stop`,`WorktreeCreate`,`WorktreeRemove`]),b=(e,t)=>{if(e===`cursor`)return g.has(t)?JSON.stringify({continue:!0}):_.has(t)?JSON.stringify({permission:`allow`}):void 0;if(e===`claude`)return v.has(t)?JSON.stringify({continue:!0}):y.has(t)?JSON.stringify({decision:`approve`}):void 0},x=()=>c.env.HOME||u(),S=e=>typeof e==`object`&&!!e&&!Array.isArray(e),ee=e=>e instanceof Error?e.message:String(e),te=()=>{let e=c.argv[1];if(e&&s.isAbsolute(e)){if(e.split(s.sep).includes(`_npx`))return`npx -y happenin`;if(c.platform!==`win32`)return e}return`happenin`},ne=(e,t)=>e.includes(t),re=e=>{t(e,{recursive:!0})},C=t=>e(t),ie=e=>{let t=n(e,`utf8`);return JSON.parse(t)},ae=(e,t)=>{let n=`${e}.${Date.now()}.tmp`;re(s.dirname(e)),o(n,JSON.stringify(t,null,2)+`
3
- `,`utf8`),i(n,e)},oe=(e,t)=>{if(re(t),!C(e))return;let r=s.join(t,`${s.basename(e)}.${Date.now()}.bak`);return o(r,n(e,`utf8`),`utf8`),r},se=e=>{if(!C(e))return{version:1,hooks:{}};let t=ie(e);if(!S(t))throw Error(`Invalid JSON object in ${e}`);let n=S(t.hooks)?t.hooks:{};return{...t,version:typeof t.version==`number`?t.version:1,hooks:n}},ce=e=>{if(!C(e))return{hooks:{}};let t=ie(e);if(!S(t))throw Error(`Invalid JSON object in ${e}`);let n=S(t.hooks)?t.hooks:{};return{...t,hooks:n}},le=(e,t,n)=>{e[t]=[...(Array.isArray(e[t])?e[t]:[]).filter(e=>!S(e)||typeof e.command!=`string`||!ne(e.command,`happenin record cursor`)),{command:`${n} record cursor`}]},ue=(e,t,n)=>{let r=`happenin record claude ${t}`;e[t]=[...(Array.isArray(e[t])?e[t]:[]).map(e=>S(e)&&Array.isArray(e.hooks)?{...e,hooks:e.hooks.filter(e=>!S(e)||typeof e.command!=`string`||!ne(e.command,r))}:e).filter(e=>!(S(e)&&Array.isArray(e.hooks)&&e.hooks.length===0)),{matcher:``,hooks:[{type:`command`,command:`${n} record claude ${t}`}]}]},de=e=>{let t=x(),n=s.join(t,`.cursor`,`hooks.json`),r=s.join(t,`.happenin`,`backups`,`cursor`),i=oe(n,r),a=se(n);for(let t of m)le(a.hooks,t,e);return ae(n,a),{target:n,backup:i}},fe=e=>{let t=x(),n=s.join(t,`.claude`,`settings.json`),r=s.join(t,`.happenin`,`backups`,`claude`),i=oe(n,r),a=ce(n);for(let t of h)ue(a.hooks,t,e);return ae(n,a),{target:n,backup:i}},pe=e=>{let t=!1,n=!1;for(let r of e)r===`--cursor`&&(t=!0),r===`--claude`&&(n=!0);return!t&&!n&&(t=!0,n=!0),{cursor:t,claude:n}};async function me(e={}){let{cursor:t=!0,claude:n=!0}=e,r=te(),i=[];t&&i.push(de(r)),n&&i.push(fe(r));for(let{target:e,backup:t}of i)c.stdout.write(`${e} written
4
- `),t&&c.stdout.write(` backup: ${t}\n`)}async function he(e=[]){try{let{cursor:t,claude:n}=pe(e);await me({cursor:t,claude:n})}catch(e){c.stderr.write(`install failed: ${ee(e)}\n`),c.exitCode=1}}const ge=[`CREATE INDEX IF NOT EXISTS idx_events_source ON events(source);`,`CREATE INDEX IF NOT EXISTS idx_events_event ON events(event);`,`CREATE INDEX IF NOT EXISTS idx_events_session_id ON events(session_id);`,`CREATE INDEX IF NOT EXISTS idx_events_happened_at ON events(happened_at);`,`CREATE INDEX IF NOT EXISTS idx_events_received_at ON events(received_at);`,`CREATE INDEX IF NOT EXISTS idx_events_source_path ON events(source_path);`,`CREATE INDEX IF NOT EXISTS idx_events_subagent_id ON events(subagent_id);`];function w(e){let t=e.prepare(`PRAGMA user_version`).get();return Number(t?.user_version??0)}function _e(e){let t=w(e);if(!(t>=1)){if(e.exec(`BEGIN IMMEDIATE;`),t=w(e),t>=1){e.exec(`ROLLBACK;`);return}try{let t=e.prepare(`PRAGMA table_info(events)`).all(),n=new Set(t.map(e=>e.name)),r=t=>{n.has(t)||e.exec(`ALTER TABLE events ADD COLUMN ${t} TEXT;`)};r(`subagent_id`),r(`subagent_type`),r(`transcript_path`),e.exec(`PRAGMA user_version = 1;`),e.exec(`COMMIT;`)}catch(t){try{e.exec(`ROLLBACK;`)}catch{}throw t}}}function ve(e){let t=w(e);if(!(t>=3)){if(e.exec(`BEGIN IMMEDIATE;`),t=w(e),t>=3){e.exec(`ROLLBACK;`);return}try{e.prepare(`PRAGMA table_info(events)`).all().some(e=>e.name===`skill_name`)||e.exec(`ALTER TABLE events ADD COLUMN skill_name TEXT;`),e.exec(`PRAGMA user_version = 3;`),e.exec(`COMMIT;`)}catch(t){try{e.exec(`ROLLBACK;`)}catch{}throw t}}}const ye=e=>{let t=w(e);if(!(t>=2)){if(e.exec(`BEGIN IMMEDIATE;`),t=w(e),t>=2){e.exec(`ROLLBACK;`);return}try{let t=e.prepare(`SELECT id, payload FROM events WHERE happened_at IS NULL AND json_valid(payload) = 1`).all(),n=e.prepare(`UPDATE events SET happened_at = ? WHERE id = ?`);for(let e of t){let t=Ce(JSON.parse(e.payload));t!==void 0&&n.run(t,e.id)}e.exec(`
2
+ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,statSync as i}from"node:fs";import a from"node:path";import o from"node:process";import{fileURLToPath as s}from"node:url";import{DatabaseSync as c}from"node:sqlite";import{homedir as l}from"node:os";import u from"node:http";import{execFile as d}from"node:child_process";const f=e=>e instanceof Error?e.message:String(e),p=e=>{let t=!1,n=!1;for(let r of e)r===`--cursor`&&(t=!0),r===`--claude`&&(n=!0);return!t&&!n&&(t=!0,n=!0),{cursor:t,claude:n}},m={cursor:`Cursor: open Customize, import https://github.com/YogliB/happenin as a marketplace, then install happenin.`,claude:`Claude Code: run /plugin marketplace add YogliB/happenin, then /plugin install happenin@happenin.`},h=e=>{o.stdout.write(`happenin hooks are packaged as plugins; no user config files were changed.
3
+ `),e.cursor&&o.stdout.write(`${m.cursor}\n`),e.claude&&o.stdout.write(`${m.claude}\n`),o.stdout.write(`Already installed hooks with an older happenin? See the migration guide in README.md.
4
+ `)};async function g(e=[]){try{h(p(e))}catch(e){o.stderr.write(`install failed: ${f(e)}\n`),o.exitCode=1}}const _=new Set([`beforeSubmitPrompt`]),v=new Set([`preToolUse`,`beforeShellExecution`,`beforeMCPExecution`,`beforeReadFile`,`subagentStart`]),y=new Set([`UserPromptSubmit`,`UserPromptExpansion`]),b=new Set([`PreToolUse`,`PermissionRequest`,`SubagentStart`,`SubagentStop`,`TaskCreated`,`TaskCompleted`,`Stop`,`WorktreeCreate`,`WorktreeRemove`]),x=(e,t)=>{if(e===`cursor`)return _.has(t)?JSON.stringify({continue:!0}):v.has(t)?JSON.stringify({permission:`allow`}):void 0;if(e===`claude`)return y.has(t)?JSON.stringify({continue:!0}):b.has(t)?JSON.stringify({decision:`approve`}):void 0},S=()=>o.env.HOME||l(),ee=[`CREATE INDEX IF NOT EXISTS idx_events_source ON events(source);`,`CREATE INDEX IF NOT EXISTS idx_events_event ON events(event);`,`CREATE INDEX IF NOT EXISTS idx_events_session_id ON events(session_id);`,`CREATE INDEX IF NOT EXISTS idx_events_happened_at ON events(happened_at);`,`CREATE INDEX IF NOT EXISTS idx_events_received_at ON events(received_at);`,`CREATE INDEX IF NOT EXISTS idx_events_source_path ON events(source_path);`,`CREATE INDEX IF NOT EXISTS idx_events_subagent_id ON events(subagent_id);`];function C(e){let t=e.prepare(`PRAGMA user_version`).get();return Number(t?.user_version??0)}function te(e){let t=C(e);if(!(t>=1)){if(e.exec(`BEGIN IMMEDIATE;`),t=C(e),t>=1){e.exec(`ROLLBACK;`);return}try{let t=e.prepare(`PRAGMA table_info(events)`).all(),n=new Set(t.map(e=>e.name)),r=t=>{n.has(t)||e.exec(`ALTER TABLE events ADD COLUMN ${t} TEXT;`)};r(`subagent_id`),r(`subagent_type`),r(`transcript_path`),e.exec(`PRAGMA user_version = 1;`),e.exec(`COMMIT;`)}catch(t){try{e.exec(`ROLLBACK;`)}catch{}throw t}}}function ne(e){let t=C(e);if(!(t>=3)){if(e.exec(`BEGIN IMMEDIATE;`),t=C(e),t>=3){e.exec(`ROLLBACK;`);return}try{e.prepare(`PRAGMA table_info(events)`).all().some(e=>e.name===`skill_name`)||e.exec(`ALTER TABLE events ADD COLUMN skill_name TEXT;`),e.exec(`PRAGMA user_version = 3;`),e.exec(`COMMIT;`)}catch(t){try{e.exec(`ROLLBACK;`)}catch{}throw t}}}const re=e=>{let t=C(e);if(!(t>=2)){if(e.exec(`BEGIN IMMEDIATE;`),t=C(e),t>=2){e.exec(`ROLLBACK;`);return}try{let t=e.prepare(`SELECT id, payload FROM events WHERE happened_at IS NULL AND json_valid(payload) = 1`).all(),n=e.prepare(`UPDATE events SET happened_at = ? WHERE id = ?`);for(let e of t){let t=se(JSON.parse(e.payload));t!==void 0&&n.run(t,e.id)}e.exec(`
5
5
  UPDATE events
6
6
  SET happened_at = strftime('%Y-%m-%dT%H:%M:%fZ', received_at / 1000.0, 'unixepoch')
7
7
  WHERE happened_at IS NULL;
@@ -24,7 +24,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
24
24
  WHERE
25
25
  project_path IS NULL
26
26
  AND json_valid(payload) = 1;
27
- `),e.exec(`PRAGMA user_version = 2;`),e.exec(`COMMIT;`)}catch(t){try{e.exec(`ROLLBACK;`)}catch{}throw t}}},be=e=>{let t=w(e);if(t<1)throw Error(`Database schema must be migrated before backfilling subagent metadata`);if(e.exec(`BEGIN IMMEDIATE;`),t=w(e),t<1){e.exec(`ROLLBACK;`);return}try{e.exec(`
27
+ `),e.exec(`PRAGMA user_version = 2;`),e.exec(`COMMIT;`)}catch(t){try{e.exec(`ROLLBACK;`)}catch{}throw t}}},ie=e=>{let t=C(e);if(t<1)throw Error(`Database schema must be migrated before backfilling subagent metadata`);if(e.exec(`BEGIN IMMEDIATE;`),t=C(e),t<1){e.exec(`ROLLBACK;`);return}try{e.exec(`
28
28
  UPDATE events
29
29
  SET
30
30
  subagent_id = NULLIF(trim(json_extract(payload, '$.subagent_id')), ''),
@@ -64,7 +64,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
64
64
  WHERE s.event = 'subagentStart'
65
65
  AND s.subagent_id = json_extract(events.payload, '$.tool_use_id')
66
66
  );
67
- `),e.exec(`COMMIT;`)}catch(t){try{e.exec(`ROLLBACK;`)}catch{}throw t}},T=()=>c.env.HAPPENIN_DB?c.env.HAPPENIN_DB:s.join(x(),`.happenin`,`happenin.db`),E=(e,n=5e3)=>{let r=e??T();t(s.dirname(r),{recursive:!0});let i=new d(r);try{i.exec(`PRAGMA busy_timeout = ${n};`),i.exec(`PRAGMA journal_mode = WAL;`),i.exec(`CREATE TABLE IF NOT EXISTS events (
67
+ `),e.exec(`COMMIT;`)}catch(t){try{e.exec(`ROLLBACK;`)}catch{}throw t}},w=()=>o.env.HAPPENIN_DB?o.env.HAPPENIN_DB:a.join(S(),`.happenin`,`happenin.db`),T=(e,n=5e3)=>{let r=e??w();t(a.dirname(r),{recursive:!0});let i=new c(r);try{i.exec(`PRAGMA busy_timeout = ${n};`),i.exec(`PRAGMA journal_mode = WAL;`),i.exec(`CREATE TABLE IF NOT EXISTS events (
68
68
  id INTEGER PRIMARY KEY AUTOINCREMENT,
69
69
  source TEXT NOT NULL,
70
70
  client TEXT,
@@ -87,7 +87,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
87
87
  mtime INTEGER NOT NULL,
88
88
  imported_at INTEGER NOT NULL
89
89
  );
90
- `),_e(i),ye(i),ve(i);for(let e of ge)i.exec(e);return i}catch(e){try{i.close()}catch{}throw e}},xe=[`happenedAt`,`timestamp`,`happened_at`,`time`,`ts`,`createdAt`,`created_at`];function Se(e){if(e!=null){if(typeof e==`number`){if(!Number.isFinite(e))return;try{return new Date(e).toISOString()}catch{return}}if(typeof e==`string`){let t=e.trim();if(t.length===0)return;if(/^\d+$/.test(t)){let e=Number(t);if(!Number.isFinite(e))return;try{return new Date(e).toISOString()}catch{return}}return Number.isNaN(Date.parse(t))?void 0:t}}}function Ce(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e;for(let e of xe){let n=Se(Reflect.get(t,e));if(n!==void 0)return n}}const D=(e,t)=>{let n=e.prepare(`
90
+ `),te(i),re(i),ne(i);for(let e of ee)i.exec(e);return i}catch(e){try{i.close()}catch{}throw e}},ae=[`happenedAt`,`timestamp`,`happened_at`,`time`,`ts`,`createdAt`,`created_at`];function oe(e){if(e!=null){if(typeof e==`number`){if(!Number.isFinite(e))return;try{return new Date(e).toISOString()}catch{return}}if(typeof e==`string`){let t=e.trim();if(t.length===0)return;if(/^\d+$/.test(t)){let e=Number(t);if(!Number.isFinite(e))return;try{return new Date(e).toISOString()}catch{return}}return Number.isNaN(Date.parse(t))?void 0:t}}}function se(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e;for(let e of ae){let n=oe(Reflect.get(t,e));if(n!==void 0)return n}}const E=(e,t)=>{let n=e.prepare(`
91
91
  INSERT INTO events (
92
92
  source,
93
93
  client,
@@ -105,7 +105,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
105
105
  subagent_type,
106
106
  transcript_path
107
107
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
108
- `),r=Date.now(),i=Se(t.happenedAt)??new Date(r).toISOString();n.run(t.source,t.client??null,t.event??null,t.sessionId??null,i,r,t.projectPath??null,t.filePath??null,t.toolName??null,t.skillName??null,t.payload,t.sourcePath??null,t.subagentId??null,t.subagentType??null,t.transcriptPath??null)},O=`COALESCE(strftime('%Y-%m-%dT%H:%M:%fZ', happened_at), strftime('%Y-%m-%dT%H:%M:%fZ', received_at / 1000.0, 'unixepoch'))`;function we(e){return(e===`7d`?168:e===`30d`?720:24)*60*60*1e3}function Te(e){return e.replace(/\\/g,`\\\\`).replace(/%/g,`\\%`).replace(/_/g,`\\_`)}function Ee(e){return`mcp\\_\\_${Te(e)}\\_\\_%`}function k(e,t=Date.now()){let n=[],r=[];if(e.since!==void 0&&(n.push(`id > ?`),r.push(e.since)),e.source!==void 0&&(n.push(`source = ?`),r.push(e.source)),e.event!==void 0&&(n.push(`event = ?`),r.push(e.event)),e.tool!==void 0&&e.tool!==``&&(n.push(`tool_name = ?`),r.push(e.tool)),e.skill!==void 0&&e.skill!==``&&(n.push(`skill_name = ?`),r.push(e.skill)),e.file!==void 0&&e.file!==``&&(n.push(`file_path = ?`),r.push(e.file)),e.skills!==void 0&&e.skills.length>0){let t=e.skills.map(()=>`?`).join(`,`);n.push(`skill_name IN (${t})`);for(let t of e.skills)r.push(t)}if(e.projectPaths!==void 0&&e.projectPaths.length>0){let t=e.projectPaths.map(()=>`?`).join(`,`);n.push(`project_path IN (${t})`);for(let t of e.projectPaths)r.push(t)}if(e.mcpServers!==void 0&&e.mcpServers.length>0){let t=e.mcpServers.map(()=>`tool_name LIKE ? ESCAPE '\\'`);n.push(t.length===1?t[0]:`(${t.join(` OR `)})`);for(let t of e.mcpServers)r.push(Ee(t))}if(e.sessionId!==void 0&&e.sessionId!==``&&(e.sessionIdExact?(n.push(`session_id = ?`),r.push(e.sessionId)):(n.push(`session_id LIKE ?`),r.push(`%${e.sessionId}%`))),e.sessionIds!==void 0&&e.sessionIds.length>0){let t=e.sessionIds.filter(e=>e!==null),i=e.sessionIds.some(e=>e===null),a=[];if(t.length>0){let e=t.map(()=>`?`).join(`,`);a.push(`session_id IN (${e})`);for(let e of t)r.push(e)}i&&a.push(`session_id IS NULL`),n.push(a.length===1?a[0]:`(${a.join(` OR `)})`)}return e.subagentId!==void 0&&e.subagentId!==``&&(n.push(`subagent_id = ?`),r.push(e.subagentId)),e.q!==void 0&&e.q!==``&&(n.push(`(payload LIKE ? OR session_id LIKE ?)`),r.push(`%${e.q}%`),r.push(`%${e.q}%`)),e.range!==void 0&&e.range!==`all`&&(n.push(`${O} >= ?`),r.push(new Date(t-we(e.range)).toISOString())),{clause:n.length>0?`WHERE ${n.join(` AND `)}`:``,params:r}}const De=(e,t)=>{let{clause:n,params:r}=k(t),i=t.limit??100,a=t.offset??0,o=`
108
+ `),r=Date.now(),i=oe(t.happenedAt)??new Date(r).toISOString();n.run(t.source,t.client??null,t.event??null,t.sessionId??null,i,r,t.projectPath??null,t.filePath??null,t.toolName??null,t.skillName??null,t.payload,t.sourcePath??null,t.subagentId??null,t.subagentType??null,t.transcriptPath??null)},ce=`COALESCE(strftime('%Y-%m-%dT%H:%M:%fZ', happened_at), strftime('%Y-%m-%dT%H:%M:%fZ', received_at / 1000.0, 'unixepoch'))`;function le(e){return(e===`7d`?168:e===`30d`?720:24)*60*60*1e3}function ue(e){return e.replace(/\\/g,`\\\\`).replace(/%/g,`\\%`).replace(/_/g,`\\_`)}function de(e){return`mcp\\_\\_${ue(e)}\\_\\_%`}function D(e,t=Date.now()){let n=[],r=[];if(e.since!==void 0&&(n.push(`id > ?`),r.push(e.since)),e.source!==void 0&&(n.push(`source = ?`),r.push(e.source)),e.event!==void 0&&(n.push(`event = ?`),r.push(e.event)),e.tool!==void 0&&e.tool!==``&&(n.push(`tool_name = ?`),r.push(e.tool)),e.skill!==void 0&&e.skill!==``&&(n.push(`skill_name = ?`),r.push(e.skill)),e.file!==void 0&&e.file!==``&&(n.push(`file_path = ?`),r.push(e.file)),e.skills!==void 0&&e.skills.length>0){let t=e.skills.map(()=>`?`).join(`,`);n.push(`skill_name IN (${t})`);for(let t of e.skills)r.push(t)}if(e.projectPaths!==void 0&&e.projectPaths.length>0){let t=e.projectPaths.map(()=>`?`).join(`,`);n.push(`project_path IN (${t})`);for(let t of e.projectPaths)r.push(t)}if(e.mcpServers!==void 0&&e.mcpServers.length>0){let t=e.mcpServers.map(()=>`tool_name LIKE ? ESCAPE '\\'`);n.push(t.length===1?t[0]:`(${t.join(` OR `)})`);for(let t of e.mcpServers)r.push(de(t))}if(e.sessionId!==void 0&&e.sessionId!==``&&(e.sessionIdExact?(n.push(`session_id = ?`),r.push(e.sessionId)):(n.push(`session_id LIKE ?`),r.push(`%${e.sessionId}%`))),e.sessionIds!==void 0&&e.sessionIds.length>0){let t=e.sessionIds.filter(e=>e!==null),i=e.sessionIds.some(e=>e===null),a=[];if(t.length>0){let e=t.map(()=>`?`).join(`,`);a.push(`session_id IN (${e})`);for(let e of t)r.push(e)}i&&a.push(`session_id IS NULL`),n.push(a.length===1?a[0]:`(${a.join(` OR `)})`)}return e.subagentId!==void 0&&e.subagentId!==``&&(n.push(`subagent_id = ?`),r.push(e.subagentId)),e.q!==void 0&&e.q!==``&&(n.push(`(payload LIKE ? OR session_id LIKE ?)`),r.push(`%${e.q}%`),r.push(`%${e.q}%`)),e.range!==void 0&&e.range!==`all`&&(n.push(`${ce} >= ?`),r.push(new Date(t-le(e.range)).toISOString())),{clause:n.length>0?`WHERE ${n.join(` AND `)}`:``,params:r}}const O=(e,t)=>{let{clause:n,params:r}=D(t),i=t.limit??100,a=t.offset??0,o=`
109
109
  SELECT
110
110
  id,
111
111
  source,
@@ -124,7 +124,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
124
124
  subagent_type AS subagentType,
125
125
  transcript_path AS transcriptPath
126
126
  FROM events
127
- ${n} ORDER BY id DESC LIMIT ?${a>0?` OFFSET ?`:``}`;return r.push(i),a>0&&r.push(a),e.prepare(o).all(...r)},A=(e,t)=>{let{clause:n,params:r}=k(t),i=`SELECT COUNT(*) AS count FROM events ${n}`,a=e.prepare(i).get(...r);return a?Number(a.count):0},Oe=(e,t)=>{let{clause:n,params:r}=k(t);return{total:A(e,t),bySource:e.prepare(`SELECT source, COUNT(*) AS count FROM events ${n} GROUP BY source ORDER BY count DESC`).all(...r),byEvent:e.prepare(`SELECT event, COUNT(*) AS count FROM events ${n} GROUP BY event ORDER BY count DESC`).all(...r),bySession:e.prepare(`SELECT session_id AS sessionId, COUNT(*) AS count, MIN(happened_at) AS firstAt, MAX(happened_at) AS lastAt FROM events ${n} GROUP BY session_id ORDER BY count DESC`).all(...r)}};function ke(e){let t=Number(e.firstReceivedAt),n=Number(e.lastReceivedAt),r=JSON.parse(e.projectPaths),i=JSON.parse(e.toolNames),a=JSON.parse(e.skillNames),o=JSON.parse(e.filePaths),s=e.firstAt?Date.parse(e.firstAt):NaN,c=e.lastAt?Date.parse(e.lastAt):NaN,l=!Number.isNaN(s)&&!Number.isNaN(c)&&c>=s?c-s:n-t;return{sessionId:e.sessionId,subagentId:e.subagentId,subagentType:e.subagentType,firstAt:e.firstAt,lastAt:e.lastAt,firstReceivedAt:t,lastReceivedAt:n,durationMs:l,eventCount:Number(e.eventCount),projectPath:r[0]??null,projectPaths:r,tools:i,skills:a,files:o,failureCount:Number(e.failureCount)}}const Ae=(e,t,n=Date.now())=>{let{clause:r,params:i}=k(t,n),a=t.limit??100,o=t.offset??0,s=`
127
+ ${n} ORDER BY id DESC LIMIT ?${a>0?` OFFSET ?`:``}`;return r.push(i),a>0&&r.push(a),e.prepare(o).all(...r)},k=(e,t)=>{let{clause:n,params:r}=D(t),i=`SELECT COUNT(*) AS count FROM events ${n}`,a=e.prepare(i).get(...r);return a?Number(a.count):0},fe=(e,t)=>{let{clause:n,params:r}=D(t);return{total:k(e,t),bySource:e.prepare(`SELECT source, COUNT(*) AS count FROM events ${n} GROUP BY source ORDER BY count DESC`).all(...r),byEvent:e.prepare(`SELECT event, COUNT(*) AS count FROM events ${n} GROUP BY event ORDER BY count DESC`).all(...r),bySession:e.prepare(`SELECT session_id AS sessionId, COUNT(*) AS count, MIN(happened_at) AS firstAt, MAX(happened_at) AS lastAt FROM events ${n} GROUP BY session_id ORDER BY count DESC`).all(...r)}};function pe(e){let t=Number(e.firstReceivedAt),n=Number(e.lastReceivedAt),r=JSON.parse(e.projectPaths),i=JSON.parse(e.toolNames),a=JSON.parse(e.skillNames),o=JSON.parse(e.filePaths),s=e.firstAt?Date.parse(e.firstAt):NaN,c=e.lastAt?Date.parse(e.lastAt):NaN,l=!Number.isNaN(s)&&!Number.isNaN(c)&&c>=s?c-s:n-t;return{sessionId:e.sessionId,subagentId:e.subagentId,subagentType:e.subagentType,firstAt:e.firstAt,lastAt:e.lastAt,firstReceivedAt:t,lastReceivedAt:n,durationMs:l,eventCount:Number(e.eventCount),projectPath:r[0]??null,projectPaths:r,tools:i,skills:a,files:o,failureCount:Number(e.failureCount)}}const me=(e,t,n=Date.now())=>{let{clause:r,params:i}=D(t,n),a=t.limit??100,o=t.offset??0,s=`
128
128
  SELECT
129
129
  session_id AS sessionId,
130
130
  COUNT(*) AS eventCount,
@@ -142,7 +142,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
142
142
  GROUP BY session_id
143
143
  ORDER BY lastReceivedAt DESC, sessionId
144
144
  LIMIT ?${o>0?` OFFSET ?`:``}
145
- `;return i.push(a),o>0&&i.push(o),e.prepare(s).all(...i).map(ke)},je=(e,t={},n=10,r=Date.now())=>{let{clause:i,params:a}=k(t,r),o=`tool_name IS NOT NULL AND tool_name <> ''`,s=`SELECT tool_name AS tool, COUNT(*) AS count FROM events ${i?`${i} AND ${o}`:`WHERE ${o}`} GROUP BY tool_name ORDER BY count DESC, tool_name ASC LIMIT ?`;return e.prepare(s).all(...a,n)},Me=(e,t={},n=10,r=Date.now())=>{let{clause:i,params:a}=k(t,r),o=`skill_name IS NOT NULL AND skill_name <> ''`,s=`SELECT skill_name AS skill, COUNT(*) AS count FROM events ${i?`${i} AND ${o}`:`WHERE ${o}`} GROUP BY skill_name ORDER BY count DESC, skill_name ASC LIMIT ?`;return e.prepare(s).all(...a,n)},Ne=(e,t={},n=10,r=Date.now())=>{let{clause:i,params:a}=k(t,r),o=[`file_path IS NOT NULL`,`file_path <> ''`,`LOWER(file_path) LIKE '%.md'`];t.mdDir&&o.push(`project_path = ?`);let s=o.join(` AND `),c=`SELECT file_path AS file, COUNT(*) AS count FROM events ${i?`${i} AND ${s}`:`WHERE ${s}`} GROUP BY file_path ORDER BY count DESC, file_path ASC LIMIT ?`,l=e.prepare(c),u=t.mdDir?[...a,t.mdDir,n]:[...a,n];return l.all(...u)},Pe=`
145
+ `;return i.push(a),o>0&&i.push(o),e.prepare(s).all(...i).map(pe)},he=(e,t={},n=10,r=Date.now())=>{let{clause:i,params:a}=D(t,r),o=`tool_name IS NOT NULL AND tool_name <> ''`,s=`SELECT tool_name AS tool, COUNT(*) AS count FROM events ${i?`${i} AND ${o}`:`WHERE ${o}`} GROUP BY tool_name ORDER BY count DESC, tool_name ASC LIMIT ?`;return e.prepare(s).all(...a,n)},ge=(e,t={},n=10,r=Date.now())=>{let{clause:i,params:a}=D(t,r),o=`skill_name IS NOT NULL AND skill_name <> ''`,s=`SELECT skill_name AS skill, COUNT(*) AS count FROM events ${i?`${i} AND ${o}`:`WHERE ${o}`} GROUP BY skill_name ORDER BY count DESC, skill_name ASC LIMIT ?`;return e.prepare(s).all(...a,n)},_e=(e,t={},n=10,r=Date.now())=>{let{clause:i,params:a}=D(t,r),o=[`file_path IS NOT NULL`,`file_path <> ''`,`LOWER(file_path) LIKE '%.md'`];t.mdDir&&o.push(`project_path = ?`);let s=o.join(` AND `),c=`SELECT file_path AS file, COUNT(*) AS count FROM events ${i?`${i} AND ${s}`:`WHERE ${s}`} GROUP BY file_path ORDER BY count DESC, file_path ASC LIMIT ?`,l=e.prepare(c),u=t.mdDir?[...a,t.mdDir,n]:[...a,n];return l.all(...u)},ve=`
146
146
  CASE
147
147
  WHEN tool_name LIKE 'mcp\\_\\_%' ESCAPE '\\' THEN 'mcpServers'
148
148
  WHEN file_path IS NOT NULL AND file_path <> '' AND LOWER(file_path) LIKE '%.md' THEN 'mdFiles'
@@ -151,9 +151,9 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
151
151
  WHEN event IN ('user', 'assistant', 'prompt') THEN 'actualValue'
152
152
  ELSE 'bloatware'
153
153
  END
154
- `;function Fe(){return{bytes:{mcpServers:0,mdFiles:0,bloatware:0,actualValue:0},tokens:{mcpServers:0,mdFiles:0,bloatware:0,actualValue:0}}}const Ie=(e,t={},n=Date.now())=>{let{clause:r,params:i}=k(t,n),a=`
154
+ `;function ye(){return{bytes:{mcpServers:0,mdFiles:0,bloatware:0,actualValue:0},tokens:{mcpServers:0,mdFiles:0,bloatware:0,actualValue:0}}}const be=(e,t={},n=Date.now())=>{let{clause:r,params:i}=D(t,n),a=`
155
155
  SELECT
156
- ${Pe} AS bucket,
156
+ ${ve} AS bucket,
157
157
  SUM(LENGTH(payload)) AS bytes,
158
158
  SUM(
159
159
  CASE WHEN json_valid(payload) THEN
@@ -164,8 +164,8 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
164
164
  ) AS tokens
165
165
  FROM events
166
166
  ${r}
167
- GROUP BY ${Pe}
168
- `,o=e.prepare(a).all(...i),s=Fe();for(let e of o)s.bytes[e.bucket]=Number(e.bytes),s.tokens[e.bucket]=Number(e.tokens);return s};function Le(e){return e===`day`?`strftime('%Y-%m-%dT00:00:00.000Z', ${O})`:`strftime('%Y-%m-%dT%H:00:00.000Z', ${O})`}function Re(e,t=Date.now()){let n=new Date(t);return e===`day`?n.setUTCHours(0,0,0,0):(n.setUTCMinutes(0,0,0),n.setUTCMilliseconds(0)),n}function ze(e){return e===`day`?1440*60*1e3:3600*1e3}const Be=(e,t={},n=24,r=`hour`,i=Date.now())=>{if(n<=0){let n=Le(r),{clause:a,params:o}=k({...t,range:void 0},i),s=`SELECT ${n} AS bucket, COUNT(*) AS count FROM events ${a} GROUP BY bucket ORDER BY bucket`;return e.prepare(s).all(...o)}let a=ze(r),o=Re(r,i),s=Re(r,i-n*60*60*1e3),c=Math.max(1,Math.ceil((o.getTime()-s.getTime())/a)+1),l=s.toISOString(),u=Le(r),{clause:d,params:f}=k({...t,range:void 0},i),p=`${u} >= ?`,m=`SELECT ${u} AS bucket, COUNT(*) AS count FROM events ${d?`${d} AND ${p}`:`WHERE ${p}`} GROUP BY bucket ORDER BY bucket`,h=e.prepare(m).all(...f,l),g=[],_=new Map;for(let e of h)_.set(e.bucket,e.count);for(let e=0;e<c;e++){let e=r===`day`?`${s.toISOString().slice(0,10)}T00:00:00.000Z`:s.toISOString();g.push({bucket:e,count:_.get(e)??0}),s.setTime(s.getTime()+a)}return g},Ve=e=>{let t=e.prepare(`SELECT id FROM events ORDER BY id DESC LIMIT 1`).get();return t?Number(t.id):0},He=(e,t)=>{if(e.failureCount>0)return`failed`;let n=e.lastAt?Date.parse(e.lastAt):NaN;return t-(Number.isNaN(n)?e.lastReceivedAt:Math.min(n,e.lastReceivedAt))<=300*1e3?`active`:`completed`};function Ue(e,t,n){return!(t.status!==void 0&&He(e,n)!==t.status||t.minDuration!==void 0&&e.durationMs<t.minDuration*60*1e3||t.maxDuration!==void 0&&e.durationMs>t.maxDuration*60*1e3)}const We=(e,t,n={})=>{let r=[...new Set(t.filter(e=>e!==null))];if(r.length===0)return[];let{clause:i,params:a}=k({...n,sessionId:void 0,sessionIds:void 0,subagentId:void 0}),o=i?` ${i.replace(/^WHERE/,`AND`)}`:``,s=[];for(let t=0;t<r.length;t+=500){let n=r.slice(t,t+500),i=`
167
+ GROUP BY ${ve}
168
+ `,o=e.prepare(a).all(...i),s=ye();for(let e of o)s.bytes[e.bucket]=Number(e.bytes),s.tokens[e.bucket]=Number(e.tokens);return s};function xe(e){return e===`day`?`strftime('%Y-%m-%dT00:00:00.000Z', ${ce})`:`strftime('%Y-%m-%dT%H:00:00.000Z', ${ce})`}function Se(e,t=Date.now()){let n=new Date(t);return e===`day`?n.setUTCHours(0,0,0,0):(n.setUTCMinutes(0,0,0),n.setUTCMilliseconds(0)),n}function Ce(e){return e===`day`?1440*60*1e3:3600*1e3}const we=(e,t={},n=24,r=`hour`,i=Date.now())=>{if(n<=0){let n=xe(r),{clause:a,params:o}=D({...t,range:void 0},i),s=`SELECT ${n} AS bucket, COUNT(*) AS count FROM events ${a} GROUP BY bucket ORDER BY bucket`;return e.prepare(s).all(...o)}let a=Ce(r),o=Se(r,i),s=Se(r,i-n*60*60*1e3),c=Math.max(1,Math.ceil((o.getTime()-s.getTime())/a)+1),l=s.toISOString(),u=xe(r),{clause:d,params:f}=D({...t,range:void 0},i),p=`${u} >= ?`,m=`SELECT ${u} AS bucket, COUNT(*) AS count FROM events ${d?`${d} AND ${p}`:`WHERE ${p}`} GROUP BY bucket ORDER BY bucket`,h=e.prepare(m).all(...f,l),g=[],_=new Map;for(let e of h)_.set(e.bucket,e.count);for(let e=0;e<c;e++){let e=r===`day`?`${s.toISOString().slice(0,10)}T00:00:00.000Z`:s.toISOString();g.push({bucket:e,count:_.get(e)??0}),s.setTime(s.getTime()+a)}return g},Te=e=>{let t=e.prepare(`SELECT id FROM events ORDER BY id DESC LIMIT 1`).get();return t?Number(t.id):0},Ee=(e,t)=>{if(e.failureCount>0)return`failed`;let n=e.lastAt?Date.parse(e.lastAt):NaN;return t-(Number.isNaN(n)?e.lastReceivedAt:Math.min(n,e.lastReceivedAt))<=300*1e3?`active`:`completed`};function De(e,t,n){return!(t.status!==void 0&&Ee(e,n)!==t.status||t.minDuration!==void 0&&e.durationMs<t.minDuration*60*1e3||t.maxDuration!==void 0&&e.durationMs>t.maxDuration*60*1e3)}const Oe=(e,t,n={})=>{let r=[...new Set(t.filter(e=>e!==null))];if(r.length===0)return[];let{clause:i,params:a}=D({...n,sessionId:void 0,sessionIds:void 0,subagentId:void 0}),o=i?` ${i.replace(/^WHERE/,`AND`)}`:``,s=[];for(let t=0;t<r.length;t+=500){let n=r.slice(t,t+500),i=`
169
169
  SELECT
170
170
  session_id AS sessionId,
171
171
  subagent_id AS subagentId,
@@ -184,7 +184,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
184
184
  WHERE subagent_id IS NOT NULL AND subagent_id <> '' AND session_id IN (${n.map(()=>`?`).join(`,`)})${o}
185
185
  GROUP BY session_id, subagent_id
186
186
  ORDER BY lastReceivedAt DESC, subagent_id
187
- `,c=e.prepare(i);s.push(...c.all(...n,...a))}return s.map(ke)},j=(e,t={},n=Date.now())=>{let r=Ae(e,{...t,limit:2**53-1,offset:0},n).filter(e=>Ue(e,t,n)),i=We(e,r.map(e=>e.sessionId),t),a=new Map;for(let e of i){let t=a.get(e.sessionId)??[];t.push(e),a.set(e.sessionId,t)}let o=r.map(e=>({...e,children:a.get(e.sessionId)})),s=t.offset??0,c=t.limit;return s===0&&c===void 0?o:c===void 0?o.slice(s):o.slice(s,s+c)},Ge=e=>{let t=e.prepare(`SELECT DISTINCT source FROM events WHERE source IS NOT NULL AND source <> '' ORDER BY source`).all(),n=e.prepare(`SELECT DISTINCT event FROM events WHERE event IS NOT NULL AND event <> '' ORDER BY event`).all(),r=e.prepare(`SELECT DISTINCT tool_name AS tool FROM events WHERE tool_name IS NOT NULL AND tool_name <> '' ORDER BY tool_name`).all(),i=e.prepare(`SELECT DISTINCT project_path AS dir FROM events WHERE project_path IS NOT NULL AND project_path <> '' AND project_path NOT LIKE '/private/%' ORDER BY project_path`).all(),a=e.prepare(`SELECT DISTINCT skill_name AS skill FROM events WHERE skill_name IS NOT NULL AND skill_name <> '' ORDER BY skill_name`).all(),o=e.prepare(`
187
+ `,c=e.prepare(i);s.push(...c.all(...n,...a))}return s.map(pe)},A=(e,t={},n=Date.now())=>{let r=me(e,{...t,limit:2**53-1,offset:0},n).filter(e=>De(e,t,n)),i=Oe(e,r.map(e=>e.sessionId),t),a=new Map;for(let e of i){let t=a.get(e.sessionId)??[];t.push(e),a.set(e.sessionId,t)}let o=r.map(e=>({...e,children:a.get(e.sessionId)})),s=t.offset??0,c=t.limit;return s===0&&c===void 0?o:c===void 0?o.slice(s):o.slice(s,s+c)},ke=e=>{let t=e.prepare(`SELECT DISTINCT source FROM events WHERE source IS NOT NULL AND source <> '' ORDER BY source`).all(),n=e.prepare(`SELECT DISTINCT event FROM events WHERE event IS NOT NULL AND event <> '' ORDER BY event`).all(),r=e.prepare(`SELECT DISTINCT tool_name AS tool FROM events WHERE tool_name IS NOT NULL AND tool_name <> '' ORDER BY tool_name`).all(),i=e.prepare(`SELECT DISTINCT project_path AS dir FROM events WHERE project_path IS NOT NULL AND project_path <> '' AND project_path NOT LIKE '/private/%' ORDER BY project_path`).all(),a=e.prepare(`SELECT DISTINCT skill_name AS skill FROM events WHERE skill_name IS NOT NULL AND skill_name <> '' ORDER BY skill_name`).all(),o=e.prepare(`
188
188
  SELECT DISTINCT server FROM (
189
189
  SELECT substr(tool_name, 6, instr(substr(tool_name, 6), '__') - 1) AS server
190
190
  FROM events
@@ -193,13 +193,13 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
193
193
  )
194
194
  WHERE server <> ''
195
195
  ORDER BY server
196
- `).all();return{sources:t.map(e=>e.source),events:n.map(e=>e.event),tools:r.map(e=>e.tool),directories:i.map(e=>e.dir),skills:a.map(e=>e.skill),mcpServers:o.map(e=>e.server)}},M=(e,t,n)=>{e.prepare(`
196
+ `).all();return{sources:t.map(e=>e.source),events:n.map(e=>e.event),tools:r.map(e=>e.tool),directories:i.map(e=>e.dir),skills:a.map(e=>e.skill),mcpServers:o.map(e=>e.server)}},j=(e,t,n)=>{e.prepare(`
197
197
  INSERT INTO imports (path, mtime, imported_at)
198
198
  VALUES (?, ?, ?)
199
199
  ON CONFLICT(path) DO UPDATE SET
200
200
  mtime = excluded.mtime,
201
201
  imported_at = excluded.imported_at
202
- `).run(t,n,Date.now())},Ke=(e,t)=>{let n=e.prepare(`SELECT mtime FROM imports WHERE path = ?`).get(t);return n?Number(n.mtime):void 0},qe=e=>{e.exec(`DELETE FROM imports`)},Je=new Set([`Read`,`Edit`,`Write`,`MultiEdit`,`NotebookEdit`]);function N(e){return typeof e==`string`&&e.length>0?e:void 0}function Ye(e,t){if(!e||!Je.has(e)||typeof t!=`object`||!t)return;let n=t;return N(n.file_path)??N(n.notebook_path)??N(n.path)}function Xe(e,t){if(!(e!==`Skill`||typeof t!=`object`||!t))return N(t.skill)}function P(e){if(e==null||typeof e==`object`)return;let t=String(e).trim();return t.length>0?t:void 0}function F(e){let t=P(e);if(t!==void 0)return t;if(Array.isArray(e))for(let t of e){let e=P(t);if(e!==void 0)return e}}function Ze(e){if(typeof e!=`object`||!e)return!1;let t=e,n=t.errcode??0;return t.code===`ERR_SQLITE_ERROR`&&((n&255)==5||(n&255)==6)}function Qe(e,t,n){if(t.length===0)return;let r;try{r=JSON.parse(t)}catch{return}let i=e[0];if(i!==`cursor`&&i!==`claude`)return;let a;a=i===`cursor`?P(r.hook_event_name):e[1];let o=i===`cursor`&&a===`subagentStart`?P(r.parent_conversation_id)??P(r.conversation_id)??P(r.sessionId)??P(r.session_id):P(r.sessionId)??P(r.session_id),s=Ce(r),c=P(r.projectPath)??P(r.cwd)??P(r.project_path)??F(r.workspaceRoot)??F(r.workspaceRoots)??F(r.workspace_roots)??F(r.workspace_root)??P(r.workspace_path),l=P(r.toolName??r.tool_name??r.tool),u=P(r.filePath??r.file_path??r.path)??Ye(l,r.tool_input),d=Xe(l,r.tool_input),f=P(r.client)??(i===`cursor`?`cursor`:`claude_code`),p=P(r.subagent_id)??void 0,m=P(r.subagent_type)??void 0,h=P(r.transcript_path)??void 0,g={source:i,client:f,event:a,sessionId:o,happenedAt:s,projectPath:c,filePath:u,toolName:l,skillName:d,payload:t,subagentId:p,subagentType:m,transcriptPath:h},_=b(i,a??``),v;try{if(v=E(n,500),!g.subagentId){let e=P(r.tool_use_id);if(e){let t=v.prepare(`SELECT subagent_id FROM events WHERE event = 'subagentStart' AND subagent_id = ? LIMIT 1`).get(e);t&&(g.subagentId=t.subagent_id)}}D(v,g)}catch(e){if(Ze(e))console.warn(`happenin: dropping event due to database lock`,e);else throw e}finally{try{v?.close()}catch{}}return _}async function $e(e){let t=[];for await(let e of c.stdin)t.push(Buffer.isBuffer(e)?e:Buffer.from(e,`utf8`));let n=Qe(e,Buffer.concat(t).toString(`utf8`).trim());n!==void 0&&c.stdout.write(`${n}\n`)}const et=`claude-transcript`,I=`cursor-transcript`,tt=`cursor`;function nt(e){if(e.type!==`assistant`||typeof e.message!=`object`||e.message===null)return[];let t=e.message.content;if(!Array.isArray(t))return[];let n=[];for(let e of t)if(typeof e==`object`&&e&&e.type===`tool_use`&&typeof e.name==`string`){let t=e.name,r=e.input;n.push({name:t,filePath:Ye(t,r),skillName:Xe(t,r)})}return n}const L=e=>{try{return r(e,{withFileTypes:!0})}catch{return[]}},rt=e=>{try{return n(e,`utf8`)}catch{return}},it=e=>{try{return Math.floor(a(e).mtimeMs)}catch{return}},R=(e,t)=>{let n=it(t);if(n===void 0||Ke(e,t)===n)return;let r=rt(t);if(r!==void 0)return{content:r,mtime:n}};function at(e){let t=[];for(let n of L(e)){if(n.isSymbolicLink())continue;let r=s.join(e,n.name);n.isDirectory()?t.push(...at(r)):n.isFile()&&n.name.endsWith(`.jsonl`)&&n.name!==`store.db`&&t.push(r)}return t}function ot(e){let t=[];for(let n of L(e)){if(!n.isDirectory()||n.isSymbolicLink())continue;let r=s.join(e,n.name);for(let e of L(r))e.isDirectory()&&!e.isSymbolicLink()&&t.push(s.join(r,e.name))}return t}async function st(e,t){let n=R(e,t);if(n===void 0)return;let{content:r,mtime:i}=n,a=s.basename(t,`.jsonl`),o=[];for(let e of r.split(/\r?\n/)){if(e.trim().length===0)continue;let t;try{t=JSON.parse(e)}catch{continue}typeof t==`object`&&t&&o.push({obj:t,line:e})}e.prepare(`DELETE FROM events WHERE source = ? AND source_path = ?`).run(et,t);for(let{obj:n,line:r}of o){let i={source:et,client:`claude_code`,event:typeof n.type==`string`?n.type:`transcript`,sessionId:typeof n.sessionId==`string`?n.sessionId:a,happenedAt:typeof n.timestamp==`string`?n.timestamp:typeof n.timestamp==`number`?String(n.timestamp):void 0,projectPath:typeof n.cwd==`string`?n.cwd:void 0,payload:r,sourcePath:t},o=nt(n);if(o.length===0){D(e,i);continue}for(let t of o)D(e,{...i,toolName:t.name,filePath:t.filePath,skillName:t.skillName})}M(e,t,i)}async function ct(e){let t=s.resolve(x(),`.claude/projects`);for(let n of at(t))await st(e,n)}async function lt(e,t,n){let r=R(e,t);if(r===void 0)return;let{content:i,mtime:a}=r,o=[],s;try{s=JSON.parse(i)}catch{return}Array.isArray(s)&&(o=s),e.prepare(`DELETE FROM events WHERE source = ? AND source_path = ?`).run(I,t);for(let r of o)typeof r==`string`&&D(e,{source:I,client:tt,event:`prompt`,sessionId:n,payload:JSON.stringify({prompt:r}),sourcePath:t});M(e,t,a)}async function ut(e,t,n){let r=R(e,t);if(r===void 0)return;let{content:i,mtime:a}=r;e.prepare(`DELETE FROM events WHERE source = ? AND source_path = ?`).run(I,t),D(e,{source:I,client:tt,event:`session_meta`,sessionId:n,payload:i,sourcePath:t}),M(e,t,a)}async function dt(e,t){let n=s.basename(t);await lt(e,s.join(t,`prompt_history.json`),n),await ut(e,s.join(t,`meta.json`),n)}async function ft(e){let t=s.resolve(x(),`.cursor/chats`);for(let n of ot(t))await dt(e,n)}async function pt(e){let t=E(T());try{be(t),e?.includes(`--force`)&&qe(t),await mt(t)}finally{t.close()}}async function mt(e){await ct(e),await ft(e)}function z(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function B(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`)}function V(e,t=24){return e.length<=t?e:`${e.slice(0,t-1)}…`}function ht(e){if(e.length===0)return``;let t=e[0];for(let n of e.slice(1)){let e=0;for(;e<t.length&&e<n.length&&t.charAt(e)===n.charAt(e);)e++;if(t=t.slice(0,e),t===``)return``}let n=process.platform===`win32`?Math.max(t.lastIndexOf(`/`),t.lastIndexOf(`\\`)):t.lastIndexOf(`/`);return n>0?t.slice(0,n+1):``}function gt(e,t){return e.split(process.platform===`win32`?/[\\/]/:/\//).filter(e=>e.length>0).slice(-t).join(`/`)}function _t(e){let t=Math.max(0,Math.round(e/1e3)),n=Math.floor(t/60),r=t%60;return n>0?`${n}m ${r}s`:`${r}s`}function H(e){if(e==null||e===``)return`-`;let t=new Date(e);return Number.isNaN(t.getTime())?`-`:t.toLocaleString()}function U(e,t,n){if(n!==void 0&&n.length!==0)for(let r of n)e.append(t,r)}function vt(e,t={}){let n={...e,...t},r=new URLSearchParams;return n.source&&r.set(`source`,n.source),n.event&&r.set(`event`,n.event),n.sessionId&&r.set(`session`,n.sessionId),n.q&&r.set(`q`,n.q),n.since!==void 0&&r.set(`since`,String(n.since)),n.range&&r.set(`range`,n.range),n.status&&r.set(`status`,n.status),n.tool&&r.set(`tool`,n.tool),n.skill&&r.set(`skill`,n.skill),n.file&&r.set(`file`,n.file),n.mdDir&&r.set(`mdDir`,n.mdDir),U(r,`dirs`,n.projectPaths),U(r,`skills`,n.skills),U(r,`mcp`,n.mcpServers),n.view&&r.set(`view`,n.view),n.minDuration!==void 0&&r.set(`minDuration`,String(n.minDuration)),n.maxDuration!==void 0&&r.set(`maxDuration`,String(n.maxDuration)),n.limit!==void 0&&r.set(`limit`,String(n.limit)),n.offset!==void 0&&r.set(`offset`,String(n.offset)),r.toString()}function W(e,t={}){return`/fragments/sessions?${vt(e,t)}`}function yt(e){let t=e.totalSessions===0?`-`:`${Math.round(e.successRate)}%`;return`<div class="metric-grid">
202
+ `).run(t,n,Date.now())},Ae=(e,t)=>{let n=e.prepare(`SELECT mtime FROM imports WHERE path = ?`).get(t);return n?Number(n.mtime):void 0},je=e=>{e.exec(`DELETE FROM imports`)},Me=new Set([`Read`,`Edit`,`Write`,`MultiEdit`,`NotebookEdit`]);function M(e){return typeof e==`string`&&e.length>0?e:void 0}function Ne(e,t){if(!e||!Me.has(e)||typeof t!=`object`||!t)return;let n=t;return M(n.file_path)??M(n.notebook_path)??M(n.path)}function Pe(e,t){if(!(e!==`Skill`||typeof t!=`object`||!t))return M(t.skill)}function N(e){if(e==null||typeof e==`object`)return;let t=String(e).trim();return t.length>0?t:void 0}function P(e){let t=N(e);if(t!==void 0)return t;if(Array.isArray(e))for(let t of e){let e=N(t);if(e!==void 0)return e}}function Fe(e){if(typeof e!=`object`||!e)return!1;let t=e,n=t.errcode??0;return t.code===`ERR_SQLITE_ERROR`&&((n&255)==5||(n&255)==6)}function Ie(e,t,n){if(t.length===0)return;let r;try{r=JSON.parse(t)}catch{return}let i=e[0];if(i!==`cursor`&&i!==`claude`)return;let a;a=i===`cursor`?N(r.hook_event_name):e[1];let o=i===`cursor`&&a===`subagentStart`?N(r.parent_conversation_id)??N(r.conversation_id)??N(r.sessionId)??N(r.session_id):N(r.sessionId)??N(r.session_id),s=se(r),c=N(r.projectPath)??N(r.cwd)??N(r.project_path)??P(r.workspaceRoot)??P(r.workspaceRoots)??P(r.workspace_roots)??P(r.workspace_root)??N(r.workspace_path),l=N(r.toolName??r.tool_name??r.tool),u=N(r.filePath??r.file_path??r.path)??Ne(l,r.tool_input),d=Pe(l,r.tool_input),f=N(r.client)??(i===`cursor`?`cursor`:`claude_code`),p=N(r.subagent_id)??void 0,m=N(r.subagent_type)??void 0,h=N(r.transcript_path)??void 0,g={source:i,client:f,event:a,sessionId:o,happenedAt:s,projectPath:c,filePath:u,toolName:l,skillName:d,payload:t,subagentId:p,subagentType:m,transcriptPath:h},_=x(i,a??``),v;try{if(v=T(n,500),!g.subagentId){let e=N(r.tool_use_id);if(e){let t=v.prepare(`SELECT subagent_id FROM events WHERE event = 'subagentStart' AND subagent_id = ? LIMIT 1`).get(e);t&&(g.subagentId=t.subagent_id)}}E(v,g)}catch(e){if(Fe(e))console.warn(`happenin: dropping event due to database lock`,e);else throw e}finally{try{v?.close()}catch{}}return _}async function Le(e){let t=[];for await(let e of o.stdin)t.push(Buffer.isBuffer(e)?e:Buffer.from(e,`utf8`));let n=Ie(e,Buffer.concat(t).toString(`utf8`).trim());n!==void 0&&o.stdout.write(`${n}\n`)}const Re=`claude-transcript`,F=`cursor-transcript`,ze=`cursor`;function Be(e){if(e.type!==`assistant`||typeof e.message!=`object`||e.message===null)return[];let t=e.message.content;if(!Array.isArray(t))return[];let n=[];for(let e of t)if(typeof e==`object`&&e&&e.type===`tool_use`&&typeof e.name==`string`){let t=e.name,r=e.input;n.push({name:t,filePath:Ne(t,r),skillName:Pe(t,r)})}return n}const I=e=>{try{return r(e,{withFileTypes:!0})}catch{return[]}},Ve=e=>{try{return n(e,`utf8`)}catch{return}},He=e=>{try{return Math.floor(i(e).mtimeMs)}catch{return}},L=(e,t)=>{let n=He(t);if(n===void 0||Ae(e,t)===n)return;let r=Ve(t);if(r!==void 0)return{content:r,mtime:n}};function Ue(e){let t=[];for(let n of I(e)){if(n.isSymbolicLink())continue;let r=a.join(e,n.name);n.isDirectory()?t.push(...Ue(r)):n.isFile()&&n.name.endsWith(`.jsonl`)&&n.name!==`store.db`&&t.push(r)}return t}function We(e){let t=[];for(let n of I(e)){if(!n.isDirectory()||n.isSymbolicLink())continue;let r=a.join(e,n.name);for(let e of I(r))e.isDirectory()&&!e.isSymbolicLink()&&t.push(a.join(r,e.name))}return t}async function Ge(e,t){let n=L(e,t);if(n===void 0)return;let{content:r,mtime:i}=n,o=a.basename(t,`.jsonl`),s=[];for(let e of r.split(/\r?\n/)){if(e.trim().length===0)continue;let t;try{t=JSON.parse(e)}catch{continue}typeof t==`object`&&t&&s.push({obj:t,line:e})}e.prepare(`DELETE FROM events WHERE source = ? AND source_path = ?`).run(Re,t);for(let{obj:n,line:r}of s){let i={source:Re,client:`claude_code`,event:typeof n.type==`string`?n.type:`transcript`,sessionId:typeof n.sessionId==`string`?n.sessionId:o,happenedAt:typeof n.timestamp==`string`?n.timestamp:typeof n.timestamp==`number`?String(n.timestamp):void 0,projectPath:typeof n.cwd==`string`?n.cwd:void 0,payload:r,sourcePath:t},a=Be(n);if(a.length===0){E(e,i);continue}for(let t of a)E(e,{...i,toolName:t.name,filePath:t.filePath,skillName:t.skillName})}j(e,t,i)}async function Ke(e){let t=a.resolve(S(),`.claude/projects`);for(let n of Ue(t))await Ge(e,n)}async function qe(e,t,n){let r=L(e,t);if(r===void 0)return;let{content:i,mtime:a}=r,o=[],s;try{s=JSON.parse(i)}catch{return}Array.isArray(s)&&(o=s),e.prepare(`DELETE FROM events WHERE source = ? AND source_path = ?`).run(F,t);for(let r of o)typeof r==`string`&&E(e,{source:F,client:ze,event:`prompt`,sessionId:n,payload:JSON.stringify({prompt:r}),sourcePath:t});j(e,t,a)}async function Je(e,t,n){let r=L(e,t);if(r===void 0)return;let{content:i,mtime:a}=r;e.prepare(`DELETE FROM events WHERE source = ? AND source_path = ?`).run(F,t),E(e,{source:F,client:ze,event:`session_meta`,sessionId:n,payload:i,sourcePath:t}),j(e,t,a)}async function Ye(e,t){let n=a.basename(t);await qe(e,a.join(t,`prompt_history.json`),n),await Je(e,a.join(t,`meta.json`),n)}async function Xe(e){let t=a.resolve(S(),`.cursor/chats`);for(let n of We(t))await Ye(e,n)}async function Ze(e){let t=T(w());try{ie(t),e?.includes(`--force`)&&je(t),await Qe(t)}finally{t.close()}}async function Qe(e){await Ke(e),await Xe(e)}function R(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function z(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`)}function B(e,t=24){return e.length<=t?e:`${e.slice(0,t-1)}…`}function $e(e){if(e.length===0)return``;let t=e[0];for(let n of e.slice(1)){let e=0;for(;e<t.length&&e<n.length&&t.charAt(e)===n.charAt(e);)e++;if(t=t.slice(0,e),t===``)return``}let n=process.platform===`win32`?Math.max(t.lastIndexOf(`/`),t.lastIndexOf(`\\`)):t.lastIndexOf(`/`);return n>0?t.slice(0,n+1):``}function et(e,t){return e.split(process.platform===`win32`?/[\\/]/:/\//).filter(e=>e.length>0).slice(-t).join(`/`)}function tt(e){let t=Math.max(0,Math.round(e/1e3)),n=Math.floor(t/60),r=t%60;return n>0?`${n}m ${r}s`:`${r}s`}function V(e){if(e==null||e===``)return`-`;let t=new Date(e);return Number.isNaN(t.getTime())?`-`:t.toLocaleString()}function H(e,t,n){if(n!==void 0&&n.length!==0)for(let r of n)e.append(t,r)}function nt(e,t={}){let n={...e,...t},r=new URLSearchParams;return n.source&&r.set(`source`,n.source),n.event&&r.set(`event`,n.event),n.sessionId&&r.set(`session`,n.sessionId),n.q&&r.set(`q`,n.q),n.since!==void 0&&r.set(`since`,String(n.since)),n.range&&r.set(`range`,n.range),n.status&&r.set(`status`,n.status),n.tool&&r.set(`tool`,n.tool),n.skill&&r.set(`skill`,n.skill),n.file&&r.set(`file`,n.file),n.mdDir&&r.set(`mdDir`,n.mdDir),H(r,`dirs`,n.projectPaths),H(r,`skills`,n.skills),H(r,`mcp`,n.mcpServers),n.view&&r.set(`view`,n.view),n.minDuration!==void 0&&r.set(`minDuration`,String(n.minDuration)),n.maxDuration!==void 0&&r.set(`maxDuration`,String(n.maxDuration)),n.limit!==void 0&&r.set(`limit`,String(n.limit)),n.offset!==void 0&&r.set(`offset`,String(n.offset)),r.toString()}function U(e,t={}){return`/fragments/sessions?${nt(e,t)}`}function rt(e){let t=e.totalSessions===0?`-`:`${Math.round(e.successRate)}%`;return`<div class="metric-grid">
203
203
  <div class="metric-card metric-sessions">
204
204
  <div class="metric-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 18 9 12 13 16 21 6"/><circle cx="21" cy="6" r="2"/></svg></div>
205
205
  <div class="metric-label">Total Sessions</div>
@@ -213,39 +213,39 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
213
213
  <div class="metric-card metric-duration">
214
214
  <div class="metric-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></div>
215
215
  <div class="metric-label">Average Duration</div>
216
- <div class="metric-value">${_t(e.averageDurationMs)}</div>
216
+ <div class="metric-value">${tt(e.averageDurationMs)}</div>
217
217
  </div>
218
218
  <div class="metric-card metric-success">
219
219
  <div class="metric-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><polyline points="9 12 12 15 16 10"/></svg></div>
220
220
  <div class="metric-label">Success Rate</div>
221
221
  <div class="metric-value">${t}</div>
222
222
  </div>
223
- </div>`}function bt(e,t){return t===`day`?e.slice(5,10):e.slice(11,16)}function xt(e,t=`hour`){if(e.length===0)return`<div class="chart-panel"><h2 class="chart-title">Event Frequency Over Time</h2><div class="chart-empty">No data</div></div>`;let n=Math.max(0,...e.map(e=>e.count)),r=e.length>1?552/(e.length-1):0,i=n>0?172/n:1,a=e.map((e,n)=>({x:44+n*r,y:216-e.count*i,count:e.count,label:bt(e.bucket,t)})),o=a.map(e=>`${e.x},${e.y}`).join(` `),s=`M ${a[0].x} 216 ${a.map(e=>`L ${e.x} ${e.y}`).join(` `)} L ${a[a.length-1].x} 216 Z`,c=e.length<=12?1:Math.ceil(e.length/8),l=a.filter((e,t)=>t%c===0).map(e=>`<text x="${e.x}" y="248" text-anchor="middle">${z(e.label)}</text>`).join(``),u=[0,.5,1].map(e=>{let t=n>0?Math.round(n*e):0;return`<text x="36" y="${216-172*e+4}" text-anchor="end" class="y-label">${t}</text>`}).join(``);return`<div class="chart-panel"><h2 class="chart-title">Event Frequency Over Time</h2>${`<svg class="event-chart" viewBox="0 0 640 260" preserveAspectRatio="xMidYMid meet"><defs><linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="var(--accent)" stop-opacity="0.5"/><stop offset="100%" stop-color="var(--accent-2)" stop-opacity="0.05"/></linearGradient></defs>${[.25,.5,.75].map(e=>{let t=216-172*e;return`<line class="grid" x1="44" y1="${t}" x2="596" y2="${t}"/>`}).join(``)}<line class="axis" x1="44" y1="44" x2="44" y2="216"/><line class="axis" x1="44" y1="216" x2="596" y2="216"/>${u}<path class="area" fill="url(#areaGradient)" d="${s}"/><polyline points="${o}"/>${l}</svg>`}${`<div class="chart-max">max ${n.toLocaleString()}</div>`}</div>`}function St(e,t,n){return e.length<=n?e:t===`start`?`…${e.slice(-(n-1))}`:`${e.slice(0,n-1)}…`}function G(e,t,n={}){let{truncate:r=`end`,labelLimit:i=20,wideLabel:a=!1}=n,o=a?`chart-panel chart-panel--wide-label`:`chart-panel`;if(t.length===0)return`<div class="${o}"><h2 class="chart-title">${z(e)}</h2><div class="chart-empty">No data</div></div>`;let s=Math.max(...t.map(e=>e.count)),c=t.map(e=>{let t=s>0?e.count/s*100:0,n=z(St(e.label,r,i)),a=`<span class="tool-name" title="${B(e.title??e.label)}">${n}</span><div class="tool-bar-bg"><div class="tool-bar" style="width: ${t}%"></div></div><span class="tool-count">${e.count}</span>`;return e.href?`<a class="tool-row" href="${B(e.href)}" hx-get="${B(e.href)}" hx-target="#dashboard-content" hx-swap="innerHTML">${a}</a>`:`<div class="tool-row">${a}</div>`}).join(``);return`<div class="${o}"><h2 class="chart-title">${z(e)}</h2><div class="tool-chart">${c}</div></div>`}function Ct(e,t){return G(`Most Used Tools`,e.map(e=>({label:e.tool,count:e.count,href:t&&W(t,{tool:e.tool,view:`list`,offset:0})})),{labelLimit:36,wideLabel:!0})}function wt(e,t){return G(`Most Used Skills`,e.map(e=>({label:e.skill,count:e.count,href:t&&W(t,{skill:e.skill,view:`list`,offset:0})})),{labelLimit:36,wideLabel:!0})}function Tt(e,t){return G(`Top Markdown Files`,e.map(e=>({label:gt(e.file,2),title:e.file,count:e.count,href:t&&W(t,{file:e.file,view:`list`,offset:0})})),{truncate:`start`,labelLimit:40,wideLabel:!0})}const K=[{key:`actualValue`,label:`Actual value`,color:`var(--success)`},{key:`mdFiles`,label:`Markdown files`,color:`var(--accent-2)`},{key:`mcpServers`,label:`MCP servers`,color:`var(--accent)`},{key:`bloatware`,label:`Bloatware`,color:`var(--warning)`}];function Et(e){return e>=1e6?`${(e/1e6).toFixed(1)}MB`:e>=1e3?`${(e/1e3).toFixed(1)}KB`:`${e}B`}function Dt(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:`${e}`}function Ot(e,t,n,r){let i=K.reduce((e,{key:n})=>e+t[n],0);if(i===0)return`<div class="context-bar-block">
224
- <h3 class="context-bar-title">${z(e)}</h3>
225
- <div class="chart-empty">${z(r)}</div>
226
- </div>`;let a=K.filter(({key:e})=>t[e]>0).map(({key:e,label:r,color:a})=>{let o=t[e],s=o/i*100;return`<div class="context-segment" style="width: ${s}%; background: ${a}" title="${z(`${r}: ${n(o)} (${s.toFixed(1)}%)`)}"></div>`}).join(``),o=K.map(({key:e,label:r,color:a})=>{let o=t[e],s=o/i*100;return`<div class="context-legend-row">
223
+ </div>`}function it(e,t){return t===`day`?e.slice(5,10):e.slice(11,16)}function at(e,t=`hour`){if(e.length===0)return`<div class="chart-panel"><h2 class="chart-title">Event Frequency Over Time</h2><div class="chart-empty">No data</div></div>`;let n=Math.max(0,...e.map(e=>e.count)),r=e.length>1?552/(e.length-1):0,i=n>0?172/n:1,a=e.map((e,n)=>({x:44+n*r,y:216-e.count*i,count:e.count,label:it(e.bucket,t)})),o=a.map(e=>`${e.x},${e.y}`).join(` `),s=`M ${a[0].x} 216 ${a.map(e=>`L ${e.x} ${e.y}`).join(` `)} L ${a[a.length-1].x} 216 Z`,c=e.length<=12?1:Math.ceil(e.length/8),l=a.filter((e,t)=>t%c===0).map(e=>`<text x="${e.x}" y="248" text-anchor="middle">${R(e.label)}</text>`).join(``),u=[0,.5,1].map(e=>{let t=n>0?Math.round(n*e):0;return`<text x="36" y="${216-172*e+4}" text-anchor="end" class="y-label">${t}</text>`}).join(``);return`<div class="chart-panel"><h2 class="chart-title">Event Frequency Over Time</h2>${`<svg class="event-chart" viewBox="0 0 640 260" preserveAspectRatio="xMidYMid meet"><defs><linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="var(--accent)" stop-opacity="0.5"/><stop offset="100%" stop-color="var(--accent-2)" stop-opacity="0.05"/></linearGradient></defs>${[.25,.5,.75].map(e=>{let t=216-172*e;return`<line class="grid" x1="44" y1="${t}" x2="596" y2="${t}"/>`}).join(``)}<line class="axis" x1="44" y1="44" x2="44" y2="216"/><line class="axis" x1="44" y1="216" x2="596" y2="216"/>${u}<path class="area" fill="url(#areaGradient)" d="${s}"/><polyline points="${o}"/>${l}</svg>`}${`<div class="chart-max">max ${n.toLocaleString()}</div>`}</div>`}function ot(e,t,n){return e.length<=n?e:t===`start`?`…${e.slice(-(n-1))}`:`${e.slice(0,n-1)}…`}function W(e,t,n={}){let{truncate:r=`end`,labelLimit:i=20,wideLabel:a=!1}=n,o=a?`chart-panel chart-panel--wide-label`:`chart-panel`;if(t.length===0)return`<div class="${o}"><h2 class="chart-title">${R(e)}</h2><div class="chart-empty">No data</div></div>`;let s=Math.max(...t.map(e=>e.count)),c=t.map(e=>{let t=s>0?e.count/s*100:0,n=R(ot(e.label,r,i)),a=`<span class="tool-name" title="${z(e.title??e.label)}">${n}</span><div class="tool-bar-bg"><div class="tool-bar" style="width: ${t}%"></div></div><span class="tool-count">${e.count}</span>`;return e.href?`<a class="tool-row" href="${z(e.href)}" hx-get="${z(e.href)}" hx-target="#dashboard-content" hx-swap="innerHTML">${a}</a>`:`<div class="tool-row">${a}</div>`}).join(``);return`<div class="${o}"><h2 class="chart-title">${R(e)}</h2><div class="tool-chart">${c}</div></div>`}function st(e,t){return W(`Most Used Tools`,e.map(e=>({label:e.tool,count:e.count,href:t&&U(t,{tool:e.tool,view:`list`,offset:0})})),{labelLimit:36,wideLabel:!0})}function ct(e,t){return W(`Most Used Skills`,e.map(e=>({label:e.skill,count:e.count,href:t&&U(t,{skill:e.skill,view:`list`,offset:0})})),{labelLimit:36,wideLabel:!0})}function lt(e,t){return W(`Top Markdown Files`,e.map(e=>({label:et(e.file,2),title:e.file,count:e.count,href:t&&U(t,{file:e.file,view:`list`,offset:0})})),{truncate:`start`,labelLimit:40,wideLabel:!0})}const G=[{key:`actualValue`,label:`Actual value`,color:`var(--success)`},{key:`mdFiles`,label:`Markdown files`,color:`var(--accent-2)`},{key:`mcpServers`,label:`MCP servers`,color:`var(--accent)`},{key:`bloatware`,label:`Bloatware`,color:`var(--warning)`}];function ut(e){return e>=1e6?`${(e/1e6).toFixed(1)}MB`:e>=1e3?`${(e/1e3).toFixed(1)}KB`:`${e}B`}function dt(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:`${e}`}function ft(e,t,n,r){let i=G.reduce((e,{key:n})=>e+t[n],0);if(i===0)return`<div class="context-bar-block">
224
+ <h3 class="context-bar-title">${R(e)}</h3>
225
+ <div class="chart-empty">${R(r)}</div>
226
+ </div>`;let a=G.filter(({key:e})=>t[e]>0).map(({key:e,label:r,color:a})=>{let o=t[e],s=o/i*100;return`<div class="context-segment" style="width: ${s}%; background: ${a}" title="${R(`${r}: ${n(o)} (${s.toFixed(1)}%)`)}"></div>`}).join(``),o=G.map(({key:e,label:r,color:a})=>{let o=t[e],s=o/i*100;return`<div class="context-legend-row">
227
227
  <span class="context-legend-dot" style="background: ${a}"></span>
228
- <span class="context-legend-label">${z(r)}</span>
229
- <span class="context-legend-value">${z(n(o))} · ${s.toFixed(1)}%</span>
228
+ <span class="context-legend-label">${R(r)}</span>
229
+ <span class="context-legend-value">${R(n(o))} · ${s.toFixed(1)}%</span>
230
230
  </div>`}).join(``);return`<div class="context-bar-block">
231
- <h3 class="context-bar-title">${z(e)}</h3>
231
+ <h3 class="context-bar-title">${R(e)}</h3>
232
232
  <div class="context-bar">${a}</div>
233
233
  <div class="context-legend">${o}</div>
234
- </div>`}function kt(e){return`<div class="chart-panel context-breakdown">
234
+ </div>`}function pt(e){return`<div class="chart-panel context-breakdown">
235
235
  <h2 class="chart-title">Context Breakdown</h2>
236
- <div class="context-bars">${Ot(`By payload size (all sources)`,e.bytes,Et,`No data`)}${Ot(`By tokens (imported Claude sessions)`,e.tokens,Dt,"Run `happenin import` to see token-based numbers")}</div>
237
- </div>`}function At(e,t,n){let r=new URLSearchParams({session:t});if(n&&r.set(`subagent`,n),e?.source&&r.set(`source`,e.source),e?.event&&r.set(`event`,e.event),e?.q&&r.set(`q`,e.q),e?.range&&r.set(`range`,e.range),e?.status&&r.set(`status`,e.status),e?.tool&&r.set(`tool`,e.tool),e?.projectPaths&&e.projectPaths.length>0)for(let t of e.projectPaths)r.append(`dirs`,t);if(e?.skills&&e.skills.length>0)for(let t of e.skills)r.append(`skills`,t);if(e?.mcpServers&&e.mcpServers.length>0)for(let t of e.mcpServers)r.append(`mcp`,t);return e?.minDuration!==void 0&&r.set(`minDuration`,String(e.minDuration)),e?.maxDuration!==void 0&&r.set(`maxDuration`,String(e.maxDuration)),e?.limit!==void 0&&r.set(`limit`,String(e.limit)),e?.offset!==void 0&&r.set(`offset`,String(e.offset)),`/fragments/detail?${r.toString()}`}function q(e,t,n,r,i,a=!1,o=``,s=``){let c=He(e,t),l=a?e.subagentId??`no subagent`:e.sessionId??`no session`,u=z(V(l,28)),d=z(H(e.firstAt??e.firstReceivedAt)),f=z(_t(e.durationMs)),p=(a?e.subagentId===r&&e.sessionId===n:e.sessionId===n)?` active`:``,m=e.sessionId??``,h=a?e.subagentId:void 0,g=!a&&!!e.children?.length,_=m&&!g?` hx-get="${B(At(i,m,h))}" hx-target="#dashboard-content" hx-swap="innerHTML"`:``,v=m&&g?` hx-get="${B(At(i,m))}" hx-target="#dashboard-content" hx-swap="innerHTML"`:``,y=a?` session-subagent`:``,b=!a&&e.children?.length&&e.sessionId===n?` expanded`:``,x=!a&&e.children?.length?` session-parent`:``,S=!a&&!e.sessionId?` session-item-static`:``,ee=a?` data-session="${B(m)}" data-subagent="${B(e.subagentId??``)}"`:` data-session="${B(e.sessionId??``)}"`,te=a&&e.subagentType?` <span class="subagent-type-badge">${z(e.subagentType)}</span>`:``;return`<li class="session-item${x}${b}${y}${p}${S}"${ee}${_}>
236
+ <div class="context-bars">${ft(`By payload size (all sources)`,e.bytes,ut,`No data`)}${ft(`By tokens (imported Claude sessions)`,e.tokens,dt,"Run `happenin import` to see token-based numbers")}</div>
237
+ </div>`}function mt(e,t,n){let r=new URLSearchParams({session:t});if(n&&r.set(`subagent`,n),e?.source&&r.set(`source`,e.source),e?.event&&r.set(`event`,e.event),e?.q&&r.set(`q`,e.q),e?.range&&r.set(`range`,e.range),e?.status&&r.set(`status`,e.status),e?.tool&&r.set(`tool`,e.tool),e?.projectPaths&&e.projectPaths.length>0)for(let t of e.projectPaths)r.append(`dirs`,t);if(e?.skills&&e.skills.length>0)for(let t of e.skills)r.append(`skills`,t);if(e?.mcpServers&&e.mcpServers.length>0)for(let t of e.mcpServers)r.append(`mcp`,t);return e?.minDuration!==void 0&&r.set(`minDuration`,String(e.minDuration)),e?.maxDuration!==void 0&&r.set(`maxDuration`,String(e.maxDuration)),e?.limit!==void 0&&r.set(`limit`,String(e.limit)),e?.offset!==void 0&&r.set(`offset`,String(e.offset)),`/fragments/detail?${r.toString()}`}function K(e,t,n,r,i,a=!1,o=``,s=``){let c=Ee(e,t),l=a?e.subagentId??`no subagent`:e.sessionId??`no session`,u=R(B(l,28)),d=R(V(e.firstAt??e.firstReceivedAt)),f=R(tt(e.durationMs)),p=(a?e.subagentId===r&&e.sessionId===n:e.sessionId===n)?` active`:``,m=e.sessionId??``,h=a?e.subagentId:void 0,g=!a&&!!e.children?.length,_=m&&!g?` hx-get="${z(mt(i,m,h))}" hx-target="#dashboard-content" hx-swap="innerHTML"`:``,v=m&&g?` hx-get="${z(mt(i,m))}" hx-target="#dashboard-content" hx-swap="innerHTML"`:``,y=a?` session-subagent`:``,b=!a&&e.children?.length&&e.sessionId===n?` expanded`:``,x=!a&&e.children?.length?` session-parent`:``,S=!a&&!e.sessionId?` session-item-static`:``,ee=a?` data-session="${z(m)}" data-subagent="${z(e.subagentId??``)}"`:` data-session="${z(e.sessionId??``)}"`,C=a&&e.subagentType?` <span class="subagent-type-badge">${R(e.subagentType)}</span>`:``;return`<li class="session-item${x}${b}${y}${p}${S}"${ee}${_}>
238
238
  ${o}
239
239
  <div class="session-main"${v}>
240
240
  <div class="session-row">
241
- <span class="session-id" title="${B(l)}">${u}${te}</span>
241
+ <span class="session-id" title="${z(l)}">${u}${C}</span>
242
242
  <span class="status-badge status-${c}">${c}</span>
243
243
  </div>
244
244
  <div class="session-meta"><span>${f}</span><span>${e.eventCount} events</span><span>${d}</span></div>
245
245
  </div>
246
246
  <span class="session-chevron" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg></span>
247
247
  ${s}
248
- </li>`}function jt(e,t=Date.now(),n,r,i){return e.length===0?`<ul class="session-list sessions-table"><li class="empty">No sessions found.</li></ul>`:`<ul class="session-list sessions-table">${e.map(e=>e.children?.length?q(e,t,n,r,i,!1,`<button type="button" class="session-toggle" aria-label="toggle subagents" onclick="event.stopPropagation(); const li = this.closest('.session-item'); li.classList.toggle('expanded'); this.textContent = li.classList.contains('expanded') ? '▾' : '▸'">${e.sessionId===n?`▾`:`▸`}</button>`,`<ul class="session-children">${e.children.map(e=>q(e,t,n,r,i,!0)).join(``)}</ul>`):q(e,t,n,r,i)).join(``)}</ul>`}function Mt(e){if(e)try{return JSON.parse(e)}catch{return}}function Nt(e){return{id:e.id,source:e.source,client:e.client,event:e.event,sessionId:e.sessionId,happenedAt:e.happenedAt,receivedAt:e.receivedAt,projectPath:e.projectPath,filePath:e.filePath,toolName:e.toolName,skillName:e.skillName,sourcePath:e.sourcePath,subagentId:e.subagentId,subagentType:e.subagentType,transcriptPath:e.transcriptPath,payload:Mt(e.payload??``)??e.payload}}function Pt(e=`Back`){return`<button type="button" class="detail-back" onclick="backToDashboard()" aria-label="back to dashboard"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg><span>${e}</span></button>`}function Ft(e){let t=new Map;for(let n of e){let e=n.subagentId??null;t.has(e)||t.set(e,[]),t.get(e).push(n)}let n=[];for(let[e,r]of t){let t=r.toSorted((e,t)=>t.id-e.id),i=t.findIndex(e=>e.event===`subagentStart`),a=i>=0?t.at(i):t.at(0),o=t.filter(e=>e!==a);n.push({subagentId:e,parent:a,children:o,maxId:t.at(0).id})}return n.toSorted((e,t)=>t.maxId-e.maxId)}function It(e){return`<span class="ts">${z(H(e.happenedAt??e.receivedAt))}</span><span class="source">${z(e.source??``)}</span><span class="event">${z(e.event??``)}</span><span class="tool">${z(e.toolName??``)}</span>`}function Lt(e){let t=z(H(e.happenedAt??e.receivedAt)),n=z(e.source??``),r=z(e.client??``),i=z(e.event??``),a=z(e.toolName??``),o=z(e.subagentId??``),s=z(e.subagentType??``),c=z(e.projectPath??``),l=z(e.filePath??``),u=z(e.skillName??``),d=o?`<div><span>Subagent</span><span>${s||`-`} (${o})</span></div>`:``,f=l?`<div><span>File</span><span>${l}</span></div>`:``,p=u?`<div><span>Skill</span><span>${u}</span></div>`:``;return`<div class="detail-attrs">
248
+ </li>`}function ht(e,t=Date.now(),n,r,i){return e.length===0?`<ul class="session-list sessions-table"><li class="empty">No sessions found.</li></ul>`:`<ul class="session-list sessions-table">${e.map(e=>e.children?.length?K(e,t,n,r,i,!1,`<button type="button" class="session-toggle" aria-label="toggle subagents" onclick="event.stopPropagation(); const li = this.closest('.session-item'); li.classList.toggle('expanded'); this.textContent = li.classList.contains('expanded') ? '▾' : '▸'">${e.sessionId===n?`▾`:`▸`}</button>`,`<ul class="session-children">${e.children.map(e=>K(e,t,n,r,i,!0)).join(``)}</ul>`):K(e,t,n,r,i)).join(``)}</ul>`}function gt(e){if(e)try{return JSON.parse(e)}catch{return}}function _t(e){return{id:e.id,source:e.source,client:e.client,event:e.event,sessionId:e.sessionId,happenedAt:e.happenedAt,receivedAt:e.receivedAt,projectPath:e.projectPath,filePath:e.filePath,toolName:e.toolName,skillName:e.skillName,sourcePath:e.sourcePath,subagentId:e.subagentId,subagentType:e.subagentType,transcriptPath:e.transcriptPath,payload:gt(e.payload??``)??e.payload}}function vt(e=`Back`){return`<button type="button" class="detail-back" onclick="backToDashboard()" aria-label="back to dashboard"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg><span>${e}</span></button>`}function yt(e){let t=new Map;for(let n of e){let e=n.subagentId??null;t.has(e)||t.set(e,[]),t.get(e).push(n)}let n=[];for(let[e,r]of t){let t=r.toSorted((e,t)=>t.id-e.id),i=t.findIndex(e=>e.event===`subagentStart`),a=i>=0?t.at(i):t.at(0),o=t.filter(e=>e!==a);n.push({subagentId:e,parent:a,children:o,maxId:t.at(0).id})}return n.toSorted((e,t)=>t.maxId-e.maxId)}function bt(e){return`<span class="ts">${R(V(e.happenedAt??e.receivedAt))}</span><span class="source">${R(e.source??``)}</span><span class="event">${R(e.event??``)}</span><span class="tool">${R(e.toolName??``)}</span>`}function xt(e){let t=R(V(e.happenedAt??e.receivedAt)),n=R(e.source??``),r=R(e.client??``),i=R(e.event??``),a=R(e.toolName??``),o=R(e.subagentId??``),s=R(e.subagentType??``),c=R(e.projectPath??``),l=R(e.filePath??``),u=R(e.skillName??``),d=o?`<div><span>Subagent</span><span>${s||`-`} (${o})</span></div>`:``,f=l?`<div><span>File</span><span>${l}</span></div>`:``,p=u?`<div><span>Skill</span><span>${u}</span></div>`:``;return`<div class="detail-attrs">
249
249
  <div><span>Time</span><span>${t}</span></div>
250
250
  <div><span>Source</span><span>${n}</span></div>
251
251
  <div><span>Client</span><span>${r||`-`}</span></div>
@@ -255,32 +255,32 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
255
255
  ${d}
256
256
  ${f}
257
257
  ${p}
258
- </div>`}function Rt(e){return`<div class="detail-json">
258
+ </div>`}function St(e){return`<div class="detail-json">
259
259
  <details class="json-toggle">
260
260
  <summary>JSON</summary>
261
- <pre>${z(JSON.stringify(Nt(e),null,2))}</pre>
261
+ <pre>${R(JSON.stringify(_t(e),null,2))}</pre>
262
262
  </details>
263
263
  <button type="button" class="json-copy" onclick="copyEventJson(this)" title="Copy JSON" aria-label="Copy JSON"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
264
- </div>`}function J(e,t){if(t.length===0)return``;let n=t.map(e=>`<span class="chip" title="${B(e)}">${z(V(e,40))}</span>`).join(``);return`<div class="summary-group"><span class="summary-label">${e} (${t.length})</span><div class="summary-chips">${n}</div></div>`}function zt(e){if(!e)return``;let t=[J(`Tools`,e.tools),J(`Skills`,e.skills),J(`Files`,e.files)].join(``);return t?`<div class="detail-summary">${t}</div>`:``}function Bt(e){return`<details class="detail-event" data-event-id="${e.id}" open>
265
- <summary>${It(e)}</summary>
266
- <div class="detail-body">${Lt(e)}
267
- ${Rt(e)}
264
+ </div>`}function q(e,t){if(t.length===0)return``;let n=t.map(e=>`<span class="chip" title="${z(e)}">${R(B(e,40))}</span>`).join(``);return`<div class="summary-group"><span class="summary-label">${e} (${t.length})</span><div class="summary-chips">${n}</div></div>`}function Ct(e){if(!e)return``;let t=[q(`Tools`,e.tools),q(`Skills`,e.skills),q(`Files`,e.files)].join(``);return t?`<div class="detail-summary">${t}</div>`:``}function wt(e){return`<details class="detail-event" data-event-id="${e.id}" open>
265
+ <summary>${bt(e)}</summary>
266
+ <div class="detail-body">${xt(e)}
267
+ ${St(e)}
268
268
  </div>
269
- </details>`}function Vt(e,t){let n=e.parent,r=e.children.map(Bt).join(``),i=`<span class="subagent-badge">${z(n.subagentType||`subagent`)}${` · ${z(V(t,18))}`} · ${e.children.length}</span>`;return`<details class="detail-event detail-subagent" data-event-id="${n.id}" open>
270
- <summary>${It(n)}${i}</summary>
269
+ </details>`}function Tt(e,t){let n=e.parent,r=e.children.map(wt).join(``),i=`<span class="subagent-badge">${R(n.subagentType||`subagent`)}${` · ${R(B(t,18))}`} · ${e.children.length}</span>`;return`<details class="detail-event detail-subagent" data-event-id="${n.id}" open>
270
+ <summary>${bt(n)}${i}</summary>
271
271
  <div class="detail-body">
272
- ${Lt(n)}
273
- ${Rt(n)}
272
+ ${xt(n)}
273
+ ${St(n)}
274
274
  <div class="detail-children">${r}</div>
275
275
  </div>
276
- </details>`}function Ht(e){return Ft(e).map(e=>e.subagentId?Vt(e,e.subagentId):[e.parent,...e.children].map(Bt).join(``)).join(``)}function Ut(e,t,n,r,i,a){if(!e)return`<div class="detail-header"><h2 class="detail-title">Session Details</h2></div><div class="empty">No events.</div>`;let o=r?t.filter(e=>e.subagentId===r):t,s=r?`${e} · ${r}`:e;if(o.length===0)return`<div class="detail-header"><h2 class="detail-title">Session Details - ${z(s)}</h2></div><div class="empty">No events for this subagent.</div>`;let c=Ht(o),l=JSON.stringify(o,null,2),u=n!==void 0&&n>o.length?`<div class="detail-truncated">Showing the ${o.length} most recent of ${n} events — older events are hidden and the JSON copy is partial.</div>`:``;return`<section class="session-detail-view" data-session="${B(e)}" data-subagent="${B(r??``)}">
276
+ </details>`}function Et(e){return yt(e).map(e=>e.subagentId?Tt(e,e.subagentId):[e.parent,...e.children].map(wt).join(``)).join(``)}function Dt(e,t,n,r,i,a){if(!e)return`<div class="detail-header"><h2 class="detail-title">Session Details</h2></div><div class="empty">No events.</div>`;let o=r?t.filter(e=>e.subagentId===r):t,s=r?`${e} · ${r}`:e;if(o.length===0)return`<div class="detail-header"><h2 class="detail-title">Session Details - ${R(s)}</h2></div><div class="empty">No events for this subagent.</div>`;let c=Et(o),l=JSON.stringify(o,null,2),u=n!==void 0&&n>o.length?`<div class="detail-truncated">Showing the ${o.length} most recent of ${n} events — older events are hidden and the JSON copy is partial.</div>`:``;return`<section class="session-detail-view" data-session="${z(e)}" data-subagent="${z(r??``)}">
277
277
  <div class="detail-header">
278
- ${Pt()}
279
- <h2 class="detail-title">Session Details - ${z(s)}</h2>
278
+ ${vt()}
279
+ <h2 class="detail-title">Session Details - ${R(s)}</h2>
280
280
  <span class="detail-count">${n??o.length} events</span>
281
281
  </div>
282
- ${zt(i)}
283
- ${a?kt(a):``}
282
+ ${Ct(i)}
283
+ ${a?pt(a):``}
284
284
  ${u}
285
285
  <div class="detail-toolbar">
286
286
  <input type="search" class="detail-search" placeholder="Search attributes..." oninput="filterSessionDetails(this.value)">
@@ -291,22 +291,22 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
291
291
  <div class="detail-actions">
292
292
  <button type="button" onclick="copySessionJson()">Copy JSON</button>
293
293
  </div>
294
- <pre id="session-json" class="session-json">${z(l)}</pre>
295
- </section>`}const Wt={bytes:{mcpServers:0,mdFiles:0,bloatware:0,actualValue:0},tokens:{mcpServers:0,mdFiles:0,bloatware:0,actualValue:0}};function Gt(e){if(!e)return;let t=Number(e);return Number.isFinite(t)&&t>=0?t:void 0}function Kt(e){if(!e)return;let t=Number(e);return Number.isInteger(t)&&t>=0?t:void 0}function qt(e){return e===`7d`?{hours:168,groupBy:`day`}:e===`30d`?{hours:720,groupBy:`day`}:e===`all`?{hours:0,groupBy:`day`}:{hours:24,groupBy:`hour`}}function Y(e){let t=e.searchParams.get(`since`),n=e.searchParams.get(`source`)||void 0,r=e.searchParams.get(`event`)||void 0,i=e.searchParams.get(`session`)||void 0,a=e.searchParams.get(`subagent`)||void 0,o=e.searchParams.get(`q`)||void 0,s=e.searchParams.get(`status`)||void 0,c=e.searchParams.get(`tool`)||void 0,l=e.searchParams.get(`skill`)||void 0,u=e.searchParams.get(`file`)||void 0,d=e.searchParams.get(`mdDir`)||void 0,f=e.searchParams.getAll(`dirs`).filter(e=>e!==``),p=e.searchParams.getAll(`skills`).filter(e=>e!==``),m=e.searchParams.getAll(`mcp`).filter(e=>e!==``),h=(e.searchParams.get(`view`)||void 0)===`list`?`list`:void 0,g=e.searchParams.get(`minDuration`),_=e.searchParams.get(`maxDuration`),v=e.searchParams.get(`range`)||`24h`,y=[`24h`,`7d`,`30d`,`all`].includes(v)?v:`24h`,b=e.searchParams.get(`limit`),x=e.searchParams.get(`offset`),S;if(t){let e=parseInt(t,10);Number.isNaN(e)||(S=e)}return{since:S,source:n,event:r,sessionId:i,subagentId:a,q:o,status:s===`active`||s===`completed`||s===`failed`?s:void 0,tool:c,skill:l,file:u,mdDir:d,view:h,projectPaths:f.length>0?f:void 0,skills:p.length>0?p:void 0,mcpServers:m.length>0?m:void 0,minDuration:Gt(g),maxDuration:Gt(_),range:y,limit:Kt(b),offset:Kt(x)}}function Jt(e,t){let n=e.limit&&e.limit>0?e.limit:25,r=e.offset??0;if(t<=n)return``;let i=Math.floor(r/n)+1,a=Math.ceil(t/n),o=r-n,s=r+n,c=W(e,{offset:Math.max(o,0),limit:n}),l=W(e,{offset:s,limit:n});return`<div class="pager">${o>=0?`<a href="${c}" hx-get="${c}" hx-target="#dashboard-content" hx-swap="innerHTML">Previous</a>`:`<button type="button" disabled>Previous</button>`}<span class="pager-info">Page ${i} of ${a}</span>${s<t?`<a href="${l}" hx-get="${l}" hx-target="#dashboard-content" hx-swap="innerHTML">Next</a>`:`<button type="button" disabled>Next</button>`}</div>`}function Yt(e){if(e.skill)return{kind:`skill`,value:e.skill};if(e.file)return{kind:`file`,value:e.file};if(e.tool)return{kind:`tool`,value:e.tool}}function Xt(e,t,n){let r=j(e,{...t,subagentId:void 0,limit:void 0,offset:void 0},n),i=t.limit&&t.limit>0?t.limit:25,a=t.offset??0,o=r.slice(a,a+i),s=Yt(t),c=W(t,{tool:void 0,skill:void 0,file:void 0,offset:0}),l=s?`<div class="sessions-list-filter">Sessions using ${z(s.kind)}: <strong>${z(s.value)}</strong> <a href="${c}" hx-get="${c}" hx-target="#dashboard-content" hx-swap="innerHTML">Clear</a></div>`:``;return`<div class="main-content sessions-list-view">
294
+ <pre id="session-json" class="session-json">${R(l)}</pre>
295
+ </section>`}const Ot={bytes:{mcpServers:0,mdFiles:0,bloatware:0,actualValue:0},tokens:{mcpServers:0,mdFiles:0,bloatware:0,actualValue:0}};function kt(e){if(!e)return;let t=Number(e);return Number.isFinite(t)&&t>=0?t:void 0}function At(e){if(!e)return;let t=Number(e);return Number.isInteger(t)&&t>=0?t:void 0}function jt(e){return e===`7d`?{hours:168,groupBy:`day`}:e===`30d`?{hours:720,groupBy:`day`}:e===`all`?{hours:0,groupBy:`day`}:{hours:24,groupBy:`hour`}}function J(e){let t=e.searchParams.get(`since`),n=e.searchParams.get(`source`)||void 0,r=e.searchParams.get(`event`)||void 0,i=e.searchParams.get(`session`)||void 0,a=e.searchParams.get(`subagent`)||void 0,o=e.searchParams.get(`q`)||void 0,s=e.searchParams.get(`status`)||void 0,c=e.searchParams.get(`tool`)||void 0,l=e.searchParams.get(`skill`)||void 0,u=e.searchParams.get(`file`)||void 0,d=e.searchParams.get(`mdDir`)||void 0,f=e.searchParams.getAll(`dirs`).filter(e=>e!==``),p=e.searchParams.getAll(`skills`).filter(e=>e!==``),m=e.searchParams.getAll(`mcp`).filter(e=>e!==``),h=(e.searchParams.get(`view`)||void 0)===`list`?`list`:void 0,g=e.searchParams.get(`minDuration`),_=e.searchParams.get(`maxDuration`),v=e.searchParams.get(`range`)||`24h`,y=[`24h`,`7d`,`30d`,`all`].includes(v)?v:`24h`,b=e.searchParams.get(`limit`),x=e.searchParams.get(`offset`),S;if(t){let e=parseInt(t,10);Number.isNaN(e)||(S=e)}return{since:S,source:n,event:r,sessionId:i,subagentId:a,q:o,status:s===`active`||s===`completed`||s===`failed`?s:void 0,tool:c,skill:l,file:u,mdDir:d,view:h,projectPaths:f.length>0?f:void 0,skills:p.length>0?p:void 0,mcpServers:m.length>0?m:void 0,minDuration:kt(g),maxDuration:kt(_),range:y,limit:At(b),offset:At(x)}}function Mt(e,t){let n=e.limit&&e.limit>0?e.limit:25,r=e.offset??0;if(t<=n)return``;let i=Math.floor(r/n)+1,a=Math.ceil(t/n),o=r-n,s=r+n,c=U(e,{offset:Math.max(o,0),limit:n}),l=U(e,{offset:s,limit:n});return`<div class="pager">${o>=0?`<a href="${c}" hx-get="${c}" hx-target="#dashboard-content" hx-swap="innerHTML">Previous</a>`:`<button type="button" disabled>Previous</button>`}<span class="pager-info">Page ${i} of ${a}</span>${s<t?`<a href="${l}" hx-get="${l}" hx-target="#dashboard-content" hx-swap="innerHTML">Next</a>`:`<button type="button" disabled>Next</button>`}</div>`}function Nt(e){if(e.skill)return{kind:`skill`,value:e.skill};if(e.file)return{kind:`file`,value:e.file};if(e.tool)return{kind:`tool`,value:e.tool}}function Pt(e,t,n){let r=A(e,{...t,subagentId:void 0,limit:void 0,offset:void 0},n),i=t.limit&&t.limit>0?t.limit:25,a=t.offset??0,o=r.slice(a,a+i),s=Nt(t),c=U(t,{tool:void 0,skill:void 0,file:void 0,offset:0}),l=s?`<div class="sessions-list-filter">Sessions using ${R(s.kind)}: <strong>${R(s.value)}</strong> <a href="${c}" hx-get="${c}" hx-target="#dashboard-content" hx-swap="innerHTML">Clear</a></div>`:``;return`<div class="main-content sessions-list-view">
296
296
  <div class="detail-header">
297
- ${Pt()}
297
+ ${vt()}
298
298
  <h2 class="session-list-title">Sessions <span class="session-collapse-count">${r.length}</span></h2>
299
299
  </div>
300
300
  ${l}
301
301
  <div class="session-list-wrapper">
302
- ${jt(o,n,void 0,void 0,t)}
303
- ${Jt(t,r.length)}
302
+ ${ht(o,n,void 0,void 0,t)}
303
+ ${Mt(t,r.length)}
304
304
  </div>
305
- </div>`}function Zt(e,t){let n=Date.now();if(t.view===`list`)return Xt(e,t,n);let{hours:r,groupBy:i}=qt(t.range),a=j(e,{...t,subagentId:void 0,limit:void 0,offset:void 0},n),o=a.map(e=>e.sessionId),s=[],c=[],l=[],u=[],d=Wt;if(o.length>0){let a={...t,subagentId:void 0,sessionIds:o};s=Be(e,a,r,i,n),c=je(e,a,10,n),l=Me(e,a,10,n),u=Ne(e,a,10,n),d=Ie(e,a,n)}let f=a.length,p=a.reduce((e,t)=>e+t.eventCount,0),m=f>0?a.reduce((e,t)=>e+t.durationMs,0)/f:0,h=a.filter(e=>e.failureCount===0).length;return`<div class="main-content">
306
- <div class="main-metrics">${yt({totalSessions:f,totalEvents:p,averageDurationMs:m,successRate:f>0?h/f*100:0})}</div>
307
- <div class="top-charts">${xt(s,i)}${Ct(c,t)}${wt(l,t)}${Tt(u,t)}</div>
308
- ${kt(d)}
309
- </div>`}function Qt(e){return e.headers[`hx-trigger`]===`dashboard-content`}function $t(e,t){let n={"Content-Type":`text/html; charset=utf-8`};return Qt(e)||(n[`HX-Push-Url`]=`/${t.search}`),n}function en(e,t,n,r){let i=Zt(r,Y(n));t.writeHead(200,$t(e,n)),t.end(i)}function tn(e,t){let n=Date.now();if(!t.sessionId)return`<div class="main-content"><div class="detail-area"><div class="empty">No events.</div></div></div>`;let r=j(e,{...t,sessionId:void 0,subagentId:void 0,limit:void 0,offset:void 0},n),i=De(e,{...t,range:void 0,sessionId:t.sessionId,sessionIdExact:!0,subagentId:t.subagentId,limit:1e3,offset:0}),a=A(e,{...t,range:void 0,sessionId:t.sessionId,sessionIdExact:!0,subagentId:t.subagentId}),o=Ie(e,{...t,range:void 0,sessionId:t.sessionId,sessionIdExact:!0,subagentId:t.subagentId}),s=r.find(e=>e.sessionId===t.sessionId),c=t.subagentId?s?.children?.find(e=>e.subagentId===t.subagentId):s;return`<div class="main-content">${Ut(t.sessionId,i,a,t.subagentId,c,o)}</div>`}function nn(e,t,n,r){let i=tn(r,Y(n));t.writeHead(200,$t(e,n)),t.end(i)}function rn(e,t,n,r){let i=De(r,Y(n));t.writeHead(200,{"Content-Type":`application/json`}),t.end(JSON.stringify(i))}const an=[{value:`24h`,label:`Last 24h`},{value:`7d`,label:`Last 7 days`},{value:`30d`,label:`Last 30 days`},{value:`all`,label:`All time`}];function on(e){let t=z(e.q??``),n=e.range??`24h`;return`<header class="app-header">
305
+ </div>`}function Ft(e,t){let n=Date.now();if(t.view===`list`)return Pt(e,t,n);let{hours:r,groupBy:i}=jt(t.range),a=A(e,{...t,subagentId:void 0,limit:void 0,offset:void 0},n),o=a.map(e=>e.sessionId),s=[],c=[],l=[],u=[],d=Ot;if(o.length>0){let a={...t,subagentId:void 0,sessionIds:o};s=we(e,a,r,i,n),c=he(e,a,10,n),l=ge(e,a,10,n),u=_e(e,a,10,n),d=be(e,a,n)}let f=a.length,p=a.reduce((e,t)=>e+t.eventCount,0),m=f>0?a.reduce((e,t)=>e+t.durationMs,0)/f:0,h=a.filter(e=>e.failureCount===0).length;return`<div class="main-content">
306
+ <div class="main-metrics">${rt({totalSessions:f,totalEvents:p,averageDurationMs:m,successRate:f>0?h/f*100:0})}</div>
307
+ <div class="top-charts">${at(s,i)}${st(c,t)}${ct(l,t)}${lt(u,t)}</div>
308
+ ${pt(d)}
309
+ </div>`}function It(e){return e.headers[`hx-trigger`]===`dashboard-content`}function Lt(e,t){let n={"Content-Type":`text/html; charset=utf-8`};return It(e)||(n[`HX-Push-Url`]=`/${t.search}`),n}function Rt(e,t,n,r){let i=Ft(r,J(n));t.writeHead(200,Lt(e,n)),t.end(i)}function zt(e,t){let n=Date.now();if(!t.sessionId)return`<div class="main-content"><div class="detail-area"><div class="empty">No events.</div></div></div>`;let r=A(e,{...t,sessionId:void 0,subagentId:void 0,limit:void 0,offset:void 0},n),i=O(e,{...t,range:void 0,sessionId:t.sessionId,sessionIdExact:!0,subagentId:t.subagentId,limit:1e3,offset:0}),a=k(e,{...t,range:void 0,sessionId:t.sessionId,sessionIdExact:!0,subagentId:t.subagentId}),o=be(e,{...t,range:void 0,sessionId:t.sessionId,sessionIdExact:!0,subagentId:t.subagentId}),s=r.find(e=>e.sessionId===t.sessionId),c=t.subagentId?s?.children?.find(e=>e.subagentId===t.subagentId):s;return`<div class="main-content">${Dt(t.sessionId,i,a,t.subagentId,c,o)}</div>`}function Bt(e,t,n,r){let i=zt(r,J(n));t.writeHead(200,Lt(e,n)),t.end(i)}function Vt(e,t,n,r){let i=O(r,J(n));t.writeHead(200,{"Content-Type":`application/json`}),t.end(JSON.stringify(i))}const Ht=[{value:`24h`,label:`Last 24h`},{value:`7d`,label:`Last 7 days`},{value:`30d`,label:`Last 30 days`},{value:`all`,label:`All time`}];function Ut(e){let t=R(e.q??``),n=e.range??`24h`;return`<header class="app-header">
310
310
  <div class="brand"><svg class="brand-icon" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="4"/><path d="M8 7v10"/><path d="M16 7v10"/><path d="M8 12h8"/></svg> happenin</div>
311
311
  <h1 class="page-title">Session Overview Analytics</h1>
312
312
  <div class="header-controls">
@@ -314,12 +314,12 @@ ${kt(d)}
314
314
  <svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
315
315
  <input type="text" name="q" class="search" placeholder="session or payload" value="${t}" autocomplete="off">
316
316
  </div>
317
- <label>range <select name="range">${an.map(e=>`<option value="${B(e.value)}"${e.value===n?` selected`:``}>${z(e.label)}</option>`).join(``)}</select></label>
317
+ <label>range <select name="range">${Ht.map(e=>`<option value="${z(e.value)}"${e.value===n?` selected`:``}>${R(e.label)}</option>`).join(``)}</select></label>
318
318
  <button type="button" class="theme" data-theme-toggle title="Toggle light/dark theme" aria-label="toggle theme">🌙</button>
319
319
  </div>
320
- </header>`}const sn=[`active`,`completed`,`failed`];function X(e,t,n,r=`all`){let i=n??``,a=B(i);return`<select name="${e}"><option value=""${i===``?` selected`:``}>${r}</option>${t.map(e=>{let t=B(e);return`<option value="${t}"${t===a?` selected`:``}>${z(e)}</option>`}).join(``)}</select>`}function cn(e,t,n){let r=ht(t),i=n??``,a=B(i),o=t.map(e=>{let t=B(e);return`<option value="${t}"${t===a?` selected`:``}>${z(e.slice(r.length))}</option>`}).join(``);return`<select name="${e}"><option value=""${i===``?` selected`:``}>all directories</option>${o}</select>`}function ln(e,t,n,r){let i=new Set(n),a=t.map(({value:e,label:t})=>{let n=B(e),r=i.has(e)?` selected`:``;return`<option value="${n}" data-label="${B(t)}"${r}>${z(t)}</option>`}).join(``);return`<div class="ms" data-ms-all="${B(r)}">
320
+ </header>`}const Wt=[`active`,`completed`,`failed`];function Y(e,t,n,r=`all`){let i=n??``,a=z(i);return`<select name="${e}"><option value=""${i===``?` selected`:``}>${r}</option>${t.map(e=>{let t=z(e);return`<option value="${t}"${t===a?` selected`:``}>${R(e)}</option>`}).join(``)}</select>`}function Gt(e,t,n){let r=$e(t),i=n??``,a=z(i),o=t.map(e=>{let t=z(e);return`<option value="${t}"${t===a?` selected`:``}>${R(e.slice(r.length))}</option>`}).join(``);return`<select name="${e}"><option value=""${i===``?` selected`:``}>all directories</option>${o}</select>`}function Kt(e,t,n,r){let i=new Set(n),a=t.map(({value:e,label:t})=>{let n=z(e),r=i.has(e)?` selected`:``;return`<option value="${n}" data-label="${z(t)}"${r}>${R(t)}</option>`}).join(``);return`<div class="ms" data-ms-all="${z(r)}">
321
321
  <select name="${e}" multiple class="ms-native">${a}</select>
322
- </div>`}function un(e,t,n){let r=ht(t);return ln(e,t.map(e=>({value:e,label:e.slice(r.length)})),n,`All directories`)}function dn(e,t,n,r){return ln(e,t.map(e=>({value:e,label:e})),n,r)}function fn(e,t){let n=t.status,r=t.source,i=t.tool,a=t.event,o=t.minDuration===void 0?``:String(t.minDuration),s=t.maxDuration===void 0?``:String(t.maxDuration),c=t.mdDir,l=t.projectPaths??[],u=t.skills??[],d=t.mcpServers??[],f=t.view===`list`;return`<div class="toolbar-row">
322
+ </div>`}function qt(e,t,n){let r=$e(t);return Kt(e,t.map(e=>({value:e,label:e.slice(r.length)})),n,`All directories`)}function Jt(e,t,n,r){return Kt(e,t.map(e=>({value:e,label:e})),n,r)}function Yt(e,t){let n=t.status,r=t.source,i=t.tool,a=t.event,o=t.minDuration===void 0?``:String(t.minDuration),s=t.maxDuration===void 0?``:String(t.maxDuration),c=t.mdDir,l=t.projectPaths??[],u=t.skills??[],d=t.mcpServers??[],f=t.view===`list`;return`<div class="toolbar-row">
323
323
  <button type="button" id="filters-toggle" class="toggle-pill" aria-expanded="false" aria-controls="filter-panel" onclick="toggleFilters()">
324
324
  <svg class="toggle-chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg><span>Filters</span><span class="toggle-badge" id="filters-badge" hidden></span>
325
325
  </button>
@@ -329,17 +329,17 @@ ${kt(d)}
329
329
  </div>
330
330
  </div>
331
331
  <div class="filter-panel" id="filter-panel">
332
- <div class="field"><label>Status</label>${X(`status`,sn,n)}</div>
333
- <div class="field"><label>Source</label>${X(`source`,e.sources,r)}</div>
334
- <div class="field"><label>Tool</label>${X(`tool`,e.tools,i)}</div>
335
- <div class="field"><label>Event</label>${X(`event`,e.events,a)}</div>
336
- <div class="field field-wide"><label>Directories</label>${un(`dirs`,e.directories,l)}</div>
337
- <div class="field field-wide"><label>Skills</label>${dn(`skills`,e.skills,u,`All skills`)}</div>
338
- <div class="field field-wide"><label>Integrations (MCP)</label>${dn(`mcp`,e.mcpServers,d,`All integrations`)}</div>
339
- <div class="field"><label>Md files directory</label>${cn(`mdDir`,e.directories,c)}</div>
340
- <div class="field"><label>Min duration (m)</label><input type="number" name="minDuration" min="0" step="any" placeholder="min" value="${z(o)}"></div>
341
- <div class="field"><label>Max duration (m)</label><input type="number" name="maxDuration" min="0" step="any" placeholder="max" value="${z(s)}"></div>
342
- </div>`}function pn(e,t){let n=Ge(e),r=t.sessionId?tn(e,t):Zt(e,t),i=on(t),a=fn(n,t);return`<!DOCTYPE html>
332
+ <div class="field"><label>Status</label>${Y(`status`,Wt,n)}</div>
333
+ <div class="field"><label>Source</label>${Y(`source`,e.sources,r)}</div>
334
+ <div class="field"><label>Tool</label>${Y(`tool`,e.tools,i)}</div>
335
+ <div class="field"><label>Event</label>${Y(`event`,e.events,a)}</div>
336
+ <div class="field field-wide"><label>Directories</label>${qt(`dirs`,e.directories,l)}</div>
337
+ <div class="field field-wide"><label>Skills</label>${Jt(`skills`,e.skills,u,`All skills`)}</div>
338
+ <div class="field field-wide"><label>Integrations (MCP)</label>${Jt(`mcp`,e.mcpServers,d,`All integrations`)}</div>
339
+ <div class="field"><label>Md files directory</label>${Gt(`mdDir`,e.directories,c)}</div>
340
+ <div class="field"><label>Min duration (m)</label><input type="number" name="minDuration" min="0" step="any" placeholder="min" value="${R(o)}"></div>
341
+ <div class="field"><label>Max duration (m)</label><input type="number" name="maxDuration" min="0" step="any" placeholder="max" value="${R(s)}"></div>
342
+ </div>`}function Xt(e,t){let n=ke(e),r=t.sessionId?zt(e,t):Ft(e,t),i=Ut(t),a=Yt(n,t);return`<!DOCTYPE html>
343
343
  <html lang="en" hx-ext="sse">
344
344
  <head>
345
345
  <meta charset="utf-8">
@@ -1108,12 +1108,12 @@ ${r}
1108
1108
  </div>
1109
1109
  </div>
1110
1110
  </body>
1111
- </html>`}function mn(e,t,n,r){let i=Y(n);t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(pn(r,i))}let Z;const hn=new Set;function gn(e,t){t.writeHead(200,{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`}),t.setTimeout(0),t.write(`retry: 500
1111
+ </html>`}function Zt(e,t,n,r){let i=J(n);t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(Xt(r,i))}let X;const Z=new Set;function Qt(e,t){t.writeHead(200,{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`}),t.setTimeout(0),t.write(`retry: 500
1112
1112
 
1113
- `);let n=e.headers[`last-event-id`],r=Array.isArray(n)?n[0]:n,i=r?parseInt(r,10):NaN,a={res:t,lastId:Number.isNaN(i)?Ve(Z):i,timer:null};a.timer=setInterval(()=>{try{let e=Ve(Z);e>a.lastId&&(t.write(`event: message
1113
+ `);let n=e.headers[`last-event-id`],r=Array.isArray(n)?n[0]:n,i=r?parseInt(r,10):NaN,a={res:t,lastId:Number.isNaN(i)?Te(X):i,timer:null};a.timer=setInterval(()=>{try{let e=Te(X);e>a.lastId&&(t.write(`event: message
1114
1114
  data: ping
1115
1115
 
1116
- `),a.lastId=e)}catch(e){console.error(`SSE poll error:`,e),clearInterval(a.timer),hn.delete(a)}},300),hn.add(a),e.on(`close`,()=>{clearInterval(a.timer),hn.delete(a)})}function _n(e,t){let n=new URL(e.url??`/`,`http://localhost`);try{if(e.method===`GET`){if(n.pathname===`/`){mn(e,t,n,Z);return}if(n.pathname===`/fragments/sessions`){en(e,t,n,Z);return}if(n.pathname===`/fragments/detail`){nn(e,t,n,Z);return}if(n.pathname===`/events`){rn(e,t,n,Z);return}if(n.pathname===`/events/stream`){gn(e,t);return}}t.writeHead(404,{"Content-Type":`text/plain`}),t.end(`Not found`)}catch(e){console.error(`Dashboard request error:`,e),t.headersSent?t.writableEnded||t.destroy?.():(t.writeHead(500,{"Content-Type":`text/plain`}),t.end(`Internal server error`))}}function vn(e){p(c.platform===`win32`?`cmd`:c.platform===`darwin`?`open`:`xdg-open`,c.platform===`win32`?[`/c`,`start`,``,e]:[e],e=>{e&&console.error(`open failed:`,e.message,`(use --no-open to suppress)`)})}async function yn(e,t,n=!1){let r=f.createServer((e,t)=>{_n(e,t)});await new Promise((i,a)=>{let o=e=>{function s(t){r.off(`error`,s),r.off(`listening`,c),t.code===`EADDRINUSE`&&e<65535?o(e+1):a(t)}function c(){r.off(`error`,s);let a=`http://localhost:${e}`;n||console.log(`Dashboard: ${a}`),t&&vn(a),i()}r.once(`error`,s),r.once(`listening`,c),r.listen(e,`127.0.0.1`)};o(e)})}async function bn(e){let t=8765,n=!0,r=!1,i=!1;for(let a of e){if(i){let e=parseInt(a,10);(Number.isNaN(e)||e<1||e>65535)&&(console.error(`Invalid --port`),c.exit(1)),t=e,i=!1;continue}if(a===`--port`){i=!0;continue}if(a.startsWith(`--port=`)){let e=parseInt(a.slice(7),10);(Number.isNaN(e)||e<1||e>65535)&&(console.error(`Invalid --port`),c.exit(1)),t=e}else a===`--no-open`?n=!1:a===`--silent`&&(r=!0,n=!1)}i&&(console.error(`Missing value for --port`),c.exit(1)),await pt(),Z=E(T()),await yn(t,n,r)}const xn=[`json`,`jsonl`,`summary`];function Q(e){let t=e.trim();if(t===``)return;let n=Number(t);if(!(!Number.isFinite(n)||!Number.isInteger(n)||n<0))return n}function $(e){let t=e.trim();if(t===``)return;let n=Number(t);if(!(!Number.isFinite(n)||n<0))return n}const Sn=[`24h`,`7d`,`30d`,`all`];function Cn(){c.stdout.write(`Usage: happenin query [options]
1116
+ `),a.lastId=e)}catch(e){console.error(`SSE poll error:`,e),clearInterval(a.timer),Z.delete(a)}},300),Z.add(a),e.on(`close`,()=>{clearInterval(a.timer),Z.delete(a)})}function $t(e,t){let n=new URL(e.url??`/`,`http://localhost`);try{if(e.method===`GET`){if(n.pathname===`/`){Zt(e,t,n,X);return}if(n.pathname===`/fragments/sessions`){Rt(e,t,n,X);return}if(n.pathname===`/fragments/detail`){Bt(e,t,n,X);return}if(n.pathname===`/events`){Vt(e,t,n,X);return}if(n.pathname===`/events/stream`){Qt(e,t);return}}t.writeHead(404,{"Content-Type":`text/plain`}),t.end(`Not found`)}catch(e){console.error(`Dashboard request error:`,e),t.headersSent?t.writableEnded||t.destroy?.():(t.writeHead(500,{"Content-Type":`text/plain`}),t.end(`Internal server error`))}}function en(e){d(o.platform===`win32`?`cmd`:o.platform===`darwin`?`open`:`xdg-open`,o.platform===`win32`?[`/c`,`start`,``,e]:[e],e=>{e&&console.error(`open failed:`,e.message,`(use --no-open to suppress)`)})}async function tn(e,t,n=!1){let r=u.createServer((e,t)=>{$t(e,t)});await new Promise((i,a)=>{let o=e=>{function s(t){r.off(`error`,s),r.off(`listening`,c),t.code===`EADDRINUSE`&&e<65535?o(e+1):a(t)}function c(){r.off(`error`,s);let a=`http://localhost:${e}`;n||console.log(`Dashboard: ${a}`),t&&en(a),i()}r.once(`error`,s),r.once(`listening`,c),r.listen(e,`127.0.0.1`)};o(e)})}async function nn(e){let t=8765,n=!0,r=!1,i=!1;for(let a of e){if(i){let e=parseInt(a,10);(Number.isNaN(e)||e<1||e>65535)&&(console.error(`Invalid --port`),o.exit(1)),t=e,i=!1;continue}if(a===`--port`){i=!0;continue}if(a.startsWith(`--port=`)){let e=parseInt(a.slice(7),10);(Number.isNaN(e)||e<1||e>65535)&&(console.error(`Invalid --port`),o.exit(1)),t=e}else a===`--no-open`?n=!1:a===`--silent`&&(r=!0,n=!1)}i&&(console.error(`Missing value for --port`),o.exit(1)),await Ze(),X=T(w()),await tn(t,n,r)}const rn=[`json`,`jsonl`,`summary`];function Q(e){let t=e.trim();if(t===``)return;let n=Number(t);if(!(!Number.isFinite(n)||!Number.isInteger(n)||n<0))return n}function $(e){let t=e.trim();if(t===``)return;let n=Number(t);if(!(!Number.isFinite(n)||n<0))return n}const an=[`24h`,`7d`,`30d`,`all`];function on(){o.stdout.write(`Usage: happenin query [options]
1117
1117
 
1118
1118
  Options:
1119
1119
  --source <source> filter by source (cursor, claude, ...)
@@ -1128,7 +1128,7 @@ Options:
1128
1128
  --format <format> output format: json (default), jsonl, summary
1129
1129
  --db <path> database path (default: HAPPENIN_DB or ~/.happenin/happenin.db)
1130
1130
  -h, --help show this help
1131
- `)}function wn(e){let t={},n=`json`,r,i=!1,a;for(let o of e){if(a){switch(a){case`source`:t.source=o;break;case`event`:t.event=o;break;case`session`:t.sessionId=o;break;case`q`:t.q=o;break;case`since`:{let e=Q(o);e!==void 0&&(t.since=e);break}case`range`:Sn.includes(o)&&(t.range=o);break;case`status`:(o===`active`||o===`completed`||o===`failed`)&&(t.status=o);break;case`tool`:t.tool=o;break;case`minDuration`:{let e=$(o);e!==void 0&&(t.minDuration=e);break}case`maxDuration`:{let e=$(o);e!==void 0&&(t.maxDuration=e);break}case`limit`:{let e=Q(o);e!==void 0&&(t.limit=e);break}case`offset`:{let e=Q(o);e!==void 0&&(t.offset=e);break}case`format`:xn.includes(o)&&(n=o);break;case`db`:r=o;break}a=void 0;continue}if(o===`-h`||o===`--help`){i=!0;continue}if(o===`--source`||o===`--event`||o===`--session`||o===`--q`||o===`--since`||o===`--range`||o===`--status`||o===`--tool`||o===`--minDuration`||o===`--maxDuration`||o===`--limit`||o===`--offset`||o===`--format`||o===`--db`){a=o.slice(2);continue}if(o.startsWith(`--`)){let e=o.indexOf(`=`);if(e!==-1){let i=o.slice(2,e),a=o.slice(e+1);switch(i){case`source`:t.source=a;break;case`event`:t.event=a;break;case`session`:t.sessionId=a;break;case`q`:t.q=a;break;case`since`:{let e=Q(a);e!==void 0&&(t.since=e);break}case`range`:Sn.includes(a)&&(t.range=a);break;case`status`:(a===`active`||a===`completed`||a===`failed`)&&(t.status=a);break;case`tool`:t.tool=a;break;case`minDuration`:{let e=$(a);e!==void 0&&(t.minDuration=e);break}case`maxDuration`:{let e=$(a);e!==void 0&&(t.maxDuration=e);break}case`limit`:{let e=Q(a);e!==void 0&&(t.limit=e);break}case`offset`:{let e=Q(a);e!==void 0&&(t.offset=e);break}case`format`:xn.includes(a)&&(n=a);break;case`db`:r=a;break}}}}return{options:{range:`24h`,...t},format:n,dbPath:r,help:i}}async function Tn(e,t,n,r=!1){let{options:i,format:a,dbPath:o,help:s}=wn(e);if(s){t();return}r||(delete i.status,delete i.minDuration,delete i.maxDuration);let c=E(o);try{await n(c,i,a)}finally{c.close()}}async function En(e){await Tn(e,Cn,(e,t,n)=>{if(n===`summary`){let n=Oe(e,t);c.stdout.write(`${JSON.stringify(n,null,2)}\n`);return}let r={total:A(e,t),rows:De(e,t).map(Nt)};if(n===`jsonl`)for(let e of r.rows)c.stdout.write(`${JSON.stringify(e)}\n`);else c.stdout.write(`${JSON.stringify(r,null,2)}\n`)})}function Dn(){c.stdout.write(`Usage: happenin sessions [options]
1131
+ `)}function sn(e){let t={},n=`json`,r,i=!1,a;for(let o of e){if(a){switch(a){case`source`:t.source=o;break;case`event`:t.event=o;break;case`session`:t.sessionId=o;break;case`q`:t.q=o;break;case`since`:{let e=Q(o);e!==void 0&&(t.since=e);break}case`range`:an.includes(o)&&(t.range=o);break;case`status`:(o===`active`||o===`completed`||o===`failed`)&&(t.status=o);break;case`tool`:t.tool=o;break;case`minDuration`:{let e=$(o);e!==void 0&&(t.minDuration=e);break}case`maxDuration`:{let e=$(o);e!==void 0&&(t.maxDuration=e);break}case`limit`:{let e=Q(o);e!==void 0&&(t.limit=e);break}case`offset`:{let e=Q(o);e!==void 0&&(t.offset=e);break}case`format`:rn.includes(o)&&(n=o);break;case`db`:r=o;break}a=void 0;continue}if(o===`-h`||o===`--help`){i=!0;continue}if(o===`--source`||o===`--event`||o===`--session`||o===`--q`||o===`--since`||o===`--range`||o===`--status`||o===`--tool`||o===`--minDuration`||o===`--maxDuration`||o===`--limit`||o===`--offset`||o===`--format`||o===`--db`){a=o.slice(2);continue}if(o.startsWith(`--`)){let e=o.indexOf(`=`);if(e!==-1){let i=o.slice(2,e),a=o.slice(e+1);switch(i){case`source`:t.source=a;break;case`event`:t.event=a;break;case`session`:t.sessionId=a;break;case`q`:t.q=a;break;case`since`:{let e=Q(a);e!==void 0&&(t.since=e);break}case`range`:an.includes(a)&&(t.range=a);break;case`status`:(a===`active`||a===`completed`||a===`failed`)&&(t.status=a);break;case`tool`:t.tool=a;break;case`minDuration`:{let e=$(a);e!==void 0&&(t.minDuration=e);break}case`maxDuration`:{let e=$(a);e!==void 0&&(t.maxDuration=e);break}case`limit`:{let e=Q(a);e!==void 0&&(t.limit=e);break}case`offset`:{let e=Q(a);e!==void 0&&(t.offset=e);break}case`format`:rn.includes(a)&&(n=a);break;case`db`:r=a;break}}}}return{options:{range:`24h`,...t},format:n,dbPath:r,help:i}}async function cn(e,t,n,r=!1){let{options:i,format:a,dbPath:o,help:s}=sn(e);if(s){t();return}r||(delete i.status,delete i.minDuration,delete i.maxDuration);let c=T(o);try{await n(c,i,a)}finally{c.close()}}async function ln(e){await cn(e,on,(e,t,n)=>{if(n===`summary`){let n=fe(e,t);o.stdout.write(`${JSON.stringify(n,null,2)}\n`);return}let r={total:k(e,t),rows:O(e,t).map(_t)};if(n===`jsonl`)for(let e of r.rows)o.stdout.write(`${JSON.stringify(e)}\n`);else o.stdout.write(`${JSON.stringify(r,null,2)}\n`)})}function un(){o.stdout.write(`Usage: happenin sessions [options]
1132
1132
 
1133
1133
  Options:
1134
1134
  --source <source> filter by source (cursor, claude, ...)
@@ -1146,8 +1146,8 @@ Options:
1146
1146
  --format <format> output format: json (default), jsonl, summary
1147
1147
  --db <path> database path (default: HAPPENIN_DB or ~/.happenin/happenin.db)
1148
1148
  -h, --help show this help
1149
- `)}function On(e){return e.map(e=>{let t=`${Math.floor(e.durationMs/1e3)}s`,n=e.sessionId??`no-session`,r=e.firstAt??`?`,i=e.lastAt??`?`,a=e.projectPath??`-`,o=e.tools.join(`,`)||`-`,s=e.skills.join(`,`)||`-`,c=e.files.join(`,`)||`-`;return`${n}\t${r}\t${i}\t${t}\t${e.eventCount} events\tproject:${a}\ttools:${o}\tskills:${s}\tfiles:${c}\tfailures:${e.failureCount}`}).join(`
1150
- `)}async function kn(e){await Tn(e,Dn,(e,t,n)=>{let r=j(e,t);if(n===`jsonl`){for(let e of r)c.stdout.write(`${JSON.stringify(e)}\n`);return}if(n===`summary`){c.stdout.write(`${On(r)}\n`);return}c.stdout.write(`${JSON.stringify(r,null,2)}\n`)},!0)}function An(){let t=s.dirname(l(import.meta.url)),n=t;for(;;){let t=s.dirname(n);if(t===n)break;if(e(s.join(n,`package.json`)))return n;n=t}return t}const jn=An(),Mn=s.join(jn,`assets`,`help.md`),Nn=s.join(jn,`package.json`);function Pn(e){let t=!!c.stdout.isTTY&&!c.env.NO_COLOR,n=t?`\x1B[1m`:``,r=t?`\x1B[2m`:``,i=t?`\x1B[36m`:``,a=t?`\x1B[0m`:``;return e.replace(/^# (.+)$/gm,`${n}$1${a}`).replace(/^## (.+)$/gm,`${n}$1${a}`).replace(/^### `(.+)`$/gm,`${n}$1${a}`).replace(/^### (.+)$/gm,`${n}$1${a}`).replace(/^#### (.+)$/gm,`${r}$1${a}`).replace(/^- /gm,` • `).replace(/`([^`]+)`/g,`${i}$1${a}`)}function Fn(){let e;try{e=n(Mn,`utf8`)}catch{e=`# happenin
1149
+ `)}function dn(e){return e.map(e=>{let t=`${Math.floor(e.durationMs/1e3)}s`,n=e.sessionId??`no-session`,r=e.firstAt??`?`,i=e.lastAt??`?`,a=e.projectPath??`-`,o=e.tools.join(`,`)||`-`,s=e.skills.join(`,`)||`-`,c=e.files.join(`,`)||`-`;return`${n}\t${r}\t${i}\t${t}\t${e.eventCount} events\tproject:${a}\ttools:${o}\tskills:${s}\tfiles:${c}\tfailures:${e.failureCount}`}).join(`
1150
+ `)}async function fn(e){await cn(e,un,(e,t,n)=>{let r=A(e,t);if(n===`jsonl`){for(let e of r)o.stdout.write(`${JSON.stringify(e)}\n`);return}if(n===`summary`){o.stdout.write(`${dn(r)}\n`);return}o.stdout.write(`${JSON.stringify(r,null,2)}\n`)},!0)}function pn(){let t=a.dirname(s(import.meta.url)),n=t;for(;;){let t=a.dirname(n);if(t===n)break;if(e(a.join(n,`package.json`)))return n;n=t}return t}const mn=pn(),hn=a.join(mn,`assets`,`help.md`),gn=a.join(mn,`package.json`);function _n(e){let t=!!o.stdout.isTTY&&!o.env.NO_COLOR,n=t?`\x1B[1m`:``,r=t?`\x1B[2m`:``,i=t?`\x1B[36m`:``,a=t?`\x1B[0m`:``;return e.replace(/^# (.+)$/gm,`${n}$1${a}`).replace(/^## (.+)$/gm,`${n}$1${a}`).replace(/^### `(.+)`$/gm,`${n}$1${a}`).replace(/^### (.+)$/gm,`${n}$1${a}`).replace(/^#### (.+)$/gm,`${r}$1${a}`).replace(/^- /gm,` • `).replace(/`([^`]+)`/g,`${i}$1${a}`)}function vn(){let e;try{e=n(hn,`utf8`)}catch{e=`# happenin
1151
1151
 
1152
1152
  Run \`happenin <command>\`.
1153
- `}c.stdout.write(`\n${Pn(e)}\n`)}function In(){let e=`0.0.0`;try{let t=n(Nn,`utf8`);e=JSON.parse(t).version??e}catch{}c.stdout.write(`${e}\n`)}async function Ln(){let[e,...t]=c.argv.slice(2);if(!e||e===`--help`||e===`-h`){Fn();return}if(e===`--version`||e===`-v`){In();return}if(e===`install`){await he(t);return}if(e===`record`){await $e(t);return}if(e===`import`){await pt(t);return}if(e===`dashboard`){await bn(t);return}if(e===`query`){await En(t);return}if(e===`sessions`){await kn(t);return}c.stderr.write(`Unknown command: ${e}\n`),Fn(),c.exitCode=1}await Ln();export{};
1153
+ `}o.stdout.write(`\n${_n(e)}\n`)}function yn(){let e=`0.0.0`;try{let t=n(gn,`utf8`);e=JSON.parse(t).version??e}catch{}o.stdout.write(`${e}\n`)}async function bn(){let[e,...t]=o.argv.slice(2);if(!e||e===`--help`||e===`-h`){vn();return}if(e===`--version`||e===`-v`){yn();return}if(e===`install`){await g(t);return}if(e===`record`){await Le(t);return}if(e===`import`){await Ze(t);return}if(e===`dashboard`){await nn(t);return}if(e===`query`){await ln(t);return}if(e===`sessions`){await fn(t);return}o.stderr.write(`Unknown command: ${e}\n`),vn(),o.exitCode=1}await bn();export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happenin",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "A CLI that records Cursor and Claude Code agent events to a local SQLite database and serves a live browser dashboard.",
5
5
  "keywords": [
6
6
  "agents",
@@ -32,6 +32,9 @@
32
32
  "files": [
33
33
  "assets",
34
34
  "dist",
35
+ ".claude-plugin",
36
+ ".cursor-plugin",
37
+ "plugins",
35
38
  "LICENSE.md",
36
39
  "README.md"
37
40
  ],
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "happenin",
3
+ "version": "0.4.0",
4
+ "description": "Record local Claude Code events for the happenin dashboard.",
5
+ "author": { "name": "Yogev Boaron Ben-Har" },
6
+ "repository": "https://github.com/YogliB/happenin",
7
+ "license": "MIT",
8
+ "keywords": ["hooks", "observability", "sessions"]
9
+ }
@@ -0,0 +1,345 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "npx -y happenin record claude SessionStart"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "SessionEnd": [
15
+ {
16
+ "matcher": "",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "npx -y happenin record claude SessionEnd"
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "Setup": [
26
+ {
27
+ "matcher": "",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "npx -y happenin record claude Setup"
32
+ }
33
+ ]
34
+ }
35
+ ],
36
+ "UserPromptSubmit": [
37
+ {
38
+ "matcher": "",
39
+ "hooks": [
40
+ {
41
+ "type": "command",
42
+ "command": "npx -y happenin record claude UserPromptSubmit"
43
+ }
44
+ ]
45
+ }
46
+ ],
47
+ "UserPromptExpansion": [
48
+ {
49
+ "matcher": "",
50
+ "hooks": [
51
+ {
52
+ "type": "command",
53
+ "command": "npx -y happenin record claude UserPromptExpansion"
54
+ }
55
+ ]
56
+ }
57
+ ],
58
+ "PreToolUse": [
59
+ {
60
+ "matcher": "",
61
+ "hooks": [
62
+ {
63
+ "type": "command",
64
+ "command": "npx -y happenin record claude PreToolUse"
65
+ }
66
+ ]
67
+ }
68
+ ],
69
+ "PermissionRequest": [
70
+ {
71
+ "matcher": "",
72
+ "hooks": [
73
+ {
74
+ "type": "command",
75
+ "command": "npx -y happenin record claude PermissionRequest"
76
+ }
77
+ ]
78
+ }
79
+ ],
80
+ "PermissionDenied": [
81
+ {
82
+ "matcher": "",
83
+ "hooks": [
84
+ {
85
+ "type": "command",
86
+ "command": "npx -y happenin record claude PermissionDenied"
87
+ }
88
+ ]
89
+ }
90
+ ],
91
+ "PostToolUse": [
92
+ {
93
+ "matcher": "",
94
+ "hooks": [
95
+ {
96
+ "type": "command",
97
+ "command": "npx -y happenin record claude PostToolUse"
98
+ }
99
+ ]
100
+ }
101
+ ],
102
+ "PostToolUseFailure": [
103
+ {
104
+ "matcher": "",
105
+ "hooks": [
106
+ {
107
+ "type": "command",
108
+ "command": "npx -y happenin record claude PostToolUseFailure"
109
+ }
110
+ ]
111
+ }
112
+ ],
113
+ "PostToolBatch": [
114
+ {
115
+ "matcher": "",
116
+ "hooks": [
117
+ {
118
+ "type": "command",
119
+ "command": "npx -y happenin record claude PostToolBatch"
120
+ }
121
+ ]
122
+ }
123
+ ],
124
+ "Notification": [
125
+ {
126
+ "matcher": "",
127
+ "hooks": [
128
+ {
129
+ "type": "command",
130
+ "command": "npx -y happenin record claude Notification"
131
+ }
132
+ ]
133
+ }
134
+ ],
135
+ "MessageDisplay": [
136
+ {
137
+ "matcher": "",
138
+ "hooks": [
139
+ {
140
+ "type": "command",
141
+ "command": "npx -y happenin record claude MessageDisplay"
142
+ }
143
+ ]
144
+ }
145
+ ],
146
+ "SubagentStart": [
147
+ {
148
+ "matcher": "",
149
+ "hooks": [
150
+ {
151
+ "type": "command",
152
+ "command": "npx -y happenin record claude SubagentStart"
153
+ }
154
+ ]
155
+ }
156
+ ],
157
+ "SubagentStop": [
158
+ {
159
+ "matcher": "",
160
+ "hooks": [
161
+ {
162
+ "type": "command",
163
+ "command": "npx -y happenin record claude SubagentStop"
164
+ }
165
+ ]
166
+ }
167
+ ],
168
+ "TaskCreated": [
169
+ {
170
+ "matcher": "",
171
+ "hooks": [
172
+ {
173
+ "type": "command",
174
+ "command": "npx -y happenin record claude TaskCreated"
175
+ }
176
+ ]
177
+ }
178
+ ],
179
+ "TaskCompleted": [
180
+ {
181
+ "matcher": "",
182
+ "hooks": [
183
+ {
184
+ "type": "command",
185
+ "command": "npx -y happenin record claude TaskCompleted"
186
+ }
187
+ ]
188
+ }
189
+ ],
190
+ "Stop": [
191
+ {
192
+ "matcher": "",
193
+ "hooks": [
194
+ {
195
+ "type": "command",
196
+ "command": "npx -y happenin record claude Stop"
197
+ }
198
+ ]
199
+ }
200
+ ],
201
+ "StopFailure": [
202
+ {
203
+ "matcher": "",
204
+ "hooks": [
205
+ {
206
+ "type": "command",
207
+ "command": "npx -y happenin record claude StopFailure"
208
+ }
209
+ ]
210
+ }
211
+ ],
212
+ "TeammateIdle": [
213
+ {
214
+ "matcher": "",
215
+ "hooks": [
216
+ {
217
+ "type": "command",
218
+ "command": "npx -y happenin record claude TeammateIdle"
219
+ }
220
+ ]
221
+ }
222
+ ],
223
+ "InstructionsLoaded": [
224
+ {
225
+ "matcher": "",
226
+ "hooks": [
227
+ {
228
+ "type": "command",
229
+ "command": "npx -y happenin record claude InstructionsLoaded"
230
+ }
231
+ ]
232
+ }
233
+ ],
234
+ "ConfigChange": [
235
+ {
236
+ "matcher": "",
237
+ "hooks": [
238
+ {
239
+ "type": "command",
240
+ "command": "npx -y happenin record claude ConfigChange"
241
+ }
242
+ ]
243
+ }
244
+ ],
245
+ "CwdChanged": [
246
+ {
247
+ "matcher": "",
248
+ "hooks": [
249
+ {
250
+ "type": "command",
251
+ "command": "npx -y happenin record claude CwdChanged"
252
+ }
253
+ ]
254
+ }
255
+ ],
256
+ "DirectoryAdded": [
257
+ {
258
+ "matcher": "",
259
+ "hooks": [
260
+ {
261
+ "type": "command",
262
+ "command": "npx -y happenin record claude DirectoryAdded"
263
+ }
264
+ ]
265
+ }
266
+ ],
267
+ "FileChanged": [
268
+ {
269
+ "matcher": "",
270
+ "hooks": [
271
+ {
272
+ "type": "command",
273
+ "command": "npx -y happenin record claude FileChanged"
274
+ }
275
+ ]
276
+ }
277
+ ],
278
+ "WorktreeCreate": [
279
+ {
280
+ "matcher": "",
281
+ "hooks": [
282
+ {
283
+ "type": "command",
284
+ "command": "npx -y happenin record claude WorktreeCreate"
285
+ }
286
+ ]
287
+ }
288
+ ],
289
+ "WorktreeRemove": [
290
+ {
291
+ "matcher": "",
292
+ "hooks": [
293
+ {
294
+ "type": "command",
295
+ "command": "npx -y happenin record claude WorktreeRemove"
296
+ }
297
+ ]
298
+ }
299
+ ],
300
+ "PreCompact": [
301
+ {
302
+ "matcher": "",
303
+ "hooks": [
304
+ {
305
+ "type": "command",
306
+ "command": "npx -y happenin record claude PreCompact"
307
+ }
308
+ ]
309
+ }
310
+ ],
311
+ "PostCompact": [
312
+ {
313
+ "matcher": "",
314
+ "hooks": [
315
+ {
316
+ "type": "command",
317
+ "command": "npx -y happenin record claude PostCompact"
318
+ }
319
+ ]
320
+ }
321
+ ],
322
+ "Elicitation": [
323
+ {
324
+ "matcher": "",
325
+ "hooks": [
326
+ {
327
+ "type": "command",
328
+ "command": "npx -y happenin record claude Elicitation"
329
+ }
330
+ ]
331
+ }
332
+ ],
333
+ "ElicitationResult": [
334
+ {
335
+ "matcher": "",
336
+ "hooks": [
337
+ {
338
+ "type": "command",
339
+ "command": "npx -y happenin record claude ElicitationResult"
340
+ }
341
+ ]
342
+ }
343
+ ]
344
+ }
345
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "happenin",
3
+ "displayName": "happenin",
4
+ "version": "0.4.0",
5
+ "description": "Record local Cursor agent events for the happenin dashboard.",
6
+ "author": { "name": "Yogev Boaron Ben-Har" },
7
+ "repository": "https://github.com/YogliB/happenin",
8
+ "license": "MIT",
9
+ "keywords": ["hooks", "observability", "sessions"]
10
+ }
@@ -0,0 +1,95 @@
1
+ {
2
+ "version": 1,
3
+ "hooks": {
4
+ "sessionStart": [
5
+ {
6
+ "command": "npx -y happenin record cursor"
7
+ }
8
+ ],
9
+ "sessionEnd": [
10
+ {
11
+ "command": "npx -y happenin record cursor"
12
+ }
13
+ ],
14
+ "beforeSubmitPrompt": [
15
+ {
16
+ "command": "npx -y happenin record cursor"
17
+ }
18
+ ],
19
+ "preToolUse": [
20
+ {
21
+ "command": "npx -y happenin record cursor"
22
+ }
23
+ ],
24
+ "postToolUse": [
25
+ {
26
+ "command": "npx -y happenin record cursor"
27
+ }
28
+ ],
29
+ "postToolUseFailure": [
30
+ {
31
+ "command": "npx -y happenin record cursor"
32
+ }
33
+ ],
34
+ "subagentStart": [
35
+ {
36
+ "command": "npx -y happenin record cursor"
37
+ }
38
+ ],
39
+ "subagentStop": [
40
+ {
41
+ "command": "npx -y happenin record cursor"
42
+ }
43
+ ],
44
+ "beforeShellExecution": [
45
+ {
46
+ "command": "npx -y happenin record cursor"
47
+ }
48
+ ],
49
+ "afterShellExecution": [
50
+ {
51
+ "command": "npx -y happenin record cursor"
52
+ }
53
+ ],
54
+ "beforeMCPExecution": [
55
+ {
56
+ "command": "npx -y happenin record cursor"
57
+ }
58
+ ],
59
+ "afterMCPExecution": [
60
+ {
61
+ "command": "npx -y happenin record cursor"
62
+ }
63
+ ],
64
+ "beforeReadFile": [
65
+ {
66
+ "command": "npx -y happenin record cursor"
67
+ }
68
+ ],
69
+ "afterFileEdit": [
70
+ {
71
+ "command": "npx -y happenin record cursor"
72
+ }
73
+ ],
74
+ "afterAgentResponse": [
75
+ {
76
+ "command": "npx -y happenin record cursor"
77
+ }
78
+ ],
79
+ "afterAgentThought": [
80
+ {
81
+ "command": "npx -y happenin record cursor"
82
+ }
83
+ ],
84
+ "preCompact": [
85
+ {
86
+ "command": "npx -y happenin record cursor"
87
+ }
88
+ ],
89
+ "stop": [
90
+ {
91
+ "command": "npx -y happenin record cursor"
92
+ }
93
+ ]
94
+ }
95
+ }