happenin 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin.js +100 -62
  2. package/package.json +1 -1
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`]),ee=new Set([`UserPromptSubmit`,`UserPromptExpansion`]),te=new Set([`PreToolUse`,`PermissionRequest`,`SubagentStart`,`SubagentStop`,`TaskCreated`,`TaskCompleted`,`Stop`,`WorktreeCreate`,`WorktreeRemove`]),ne=(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 ee.has(t)?JSON.stringify({continue:!0}):te.has(t)?JSON.stringify({decision:`approve`}):void 0},v=e=>typeof e==`object`&&!!e&&!Array.isArray(e),re=e=>e instanceof Error?e.message:String(e),ie=()=>c.env.HOME||u(),ae=()=>{let e=c.argv[1];return e&&s.isAbsolute(e)?e:`happenin`},oe=e=>{t(e,{recursive:!0})},y=t=>e(t),b=e=>{let t=n(e,`utf8`);return JSON.parse(t)},se=(e,t)=>{let n=`${e}.${Date.now()}.tmp`;oe(s.dirname(e)),o(n,JSON.stringify(t,null,2)+`
3
- `,`utf8`),i(n,e)},ce=(e,t)=>{if(oe(t),!y(e))return;let r=s.join(t,`${s.basename(e)}.${Date.now()}.bak`);return o(r,n(e,`utf8`),`utf8`),r},le=e=>{if(!y(e))return{version:1,hooks:{}};let t=b(e);if(!v(t))throw Error(`Invalid JSON object in ${e}`);let n=v(t.hooks)?t.hooks:{};return{...t,version:typeof t.version==`number`?t.version:1,hooks:n}},ue=e=>{if(!y(e))return{hooks:{}};let t=b(e);if(!v(t))throw Error(`Invalid JSON object in ${e}`);let n=v(t.hooks)?t.hooks:{};return{...t,hooks:n}},de=(e,t,n)=>{e[t]=[...Array.isArray(e[t])?e[t]:[],{command:`${n} record cursor`}]},fe=(e,t,n)=>{e[t]=[...Array.isArray(e[t])?e[t]:[],{matcher:``,hooks:[{type:`command`,command:`${n} record claude ${t}`}]}]},pe=e=>{let t=ie(),n=s.join(t,`.cursor`,`hooks.json`),r=s.join(t,`.happenin`,`backups`,`cursor`),i=ce(n,r),a=le(n);for(let t of m)de(a.hooks,t,e);return se(n,a),{target:n,backup:i}},me=e=>{let t=ie(),n=s.join(t,`.claude`,`settings.json`),r=s.join(t,`.happenin`,`backups`,`claude`),i=ce(n,r),a=ue(n);for(let t of h)fe(a.hooks,t,e);return se(n,a),{target:n,backup:i}},he=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 ge(e={}){let{cursor:t=!0,claude:n=!0}=e,r=ae(),i=[];t&&i.push(pe(r)),n&&i.push(me(r));for(let{target:e,backup:t}of i)c.stdout.write(`${e} written
4
- `),t&&c.stdout.write(` backup: ${t}\n`)}async function _e(e=[]){try{let{cursor:t,claude:n}=he(e);await ge({cursor:t,claude:n})}catch(e){c.stderr.write(`install failed: ${re(e)}\n`),c.exitCode=1}}const ve=[`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 x(e){let t=e.prepare(`PRAGMA user_version`).get();return Number(t?.user_version??0)}function ye(e){let t=x(e);if(!(t>=1)){if(e.exec(`BEGIN IMMEDIATE;`),t=x(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}}}const be=e=>{let t=x(e);if(!(t>=2)){if(e.exec(`BEGIN IMMEDIATE;`),t=x(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,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`]),ee=new Set([`UserPromptSubmit`,`UserPromptExpansion`]),te=new Set([`PreToolUse`,`PermissionRequest`,`SubagentStart`,`SubagentStop`,`TaskCreated`,`TaskCompleted`,`Stop`,`WorktreeCreate`,`WorktreeRemove`]),ne=(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 ee.has(t)?JSON.stringify({continue:!0}):te.has(t)?JSON.stringify({decision:`approve`}):void 0},v=e=>typeof e==`object`&&!!e&&!Array.isArray(e),re=e=>e instanceof Error?e.message:String(e),y=()=>c.env.HOME||u(),ie=()=>{let e=c.argv[1];return e&&s.isAbsolute(e)?e:`happenin`},ae=e=>{t(e,{recursive:!0})},b=t=>e(t),oe=e=>{let t=n(e,`utf8`);return JSON.parse(t)},x=(e,t)=>{let n=`${e}.${Date.now()}.tmp`;ae(s.dirname(e)),o(n,JSON.stringify(t,null,2)+`
3
+ `,`utf8`),i(n,e)},se=(e,t)=>{if(ae(t),!b(e))return;let r=s.join(t,`${s.basename(e)}.${Date.now()}.bak`);return o(r,n(e,`utf8`),`utf8`),r},ce=e=>{if(!b(e))return{version:1,hooks:{}};let t=oe(e);if(!v(t))throw Error(`Invalid JSON object in ${e}`);let n=v(t.hooks)?t.hooks:{};return{...t,version:typeof t.version==`number`?t.version:1,hooks:n}},le=e=>{if(!b(e))return{hooks:{}};let t=oe(e);if(!v(t))throw Error(`Invalid JSON object in ${e}`);let n=v(t.hooks)?t.hooks:{};return{...t,hooks:n}},ue=(e,t,n)=>{e[t]=[...Array.isArray(e[t])?e[t]:[],{command:`${n} record cursor`}]},de=(e,t,n)=>{e[t]=[...Array.isArray(e[t])?e[t]:[],{matcher:``,hooks:[{type:`command`,command:`${n} record claude ${t}`}]}]},fe=e=>{let t=y(),n=s.join(t,`.cursor`,`hooks.json`),r=s.join(t,`.happenin`,`backups`,`cursor`),i=se(n,r),a=ce(n);for(let t of m)ue(a.hooks,t,e);return x(n,a),{target:n,backup:i}},pe=e=>{let t=y(),n=s.join(t,`.claude`,`settings.json`),r=s.join(t,`.happenin`,`backups`,`claude`),i=se(n,r),a=le(n);for(let t of h)de(a.hooks,t,e);return x(n,a),{target:n,backup:i}},me=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 he(e={}){let{cursor:t=!0,claude:n=!0}=e,r=ie(),i=[];t&&i.push(fe(r)),n&&i.push(pe(r));for(let{target:e,backup:t}of i)c.stdout.write(`${e} written
4
+ `),t&&c.stdout.write(` backup: ${t}\n`)}async function ge(e=[]){try{let{cursor:t,claude:n}=me(e);await he({cursor:t,claude:n})}catch(e){c.stderr.write(`install failed: ${re(e)}\n`),c.exitCode=1}}const _e=[`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 S(e){let t=e.prepare(`PRAGMA user_version`).get();return Number(t?.user_version??0)}function ve(e){let t=S(e);if(!(t>=1)){if(e.exec(`BEGIN IMMEDIATE;`),t=S(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}}}const ye=e=>{let t=S(e);if(!(t>=2)){if(e.exec(`BEGIN IMMEDIATE;`),t=S(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}}},xe=e=>{let t=x(e);if(t<1)throw Error(`Database schema must be migrated before backfilling subagent metadata`);if(e.exec(`BEGIN IMMEDIATE;`),t=x(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}}},be=e=>{let t=S(e);if(t<1)throw Error(`Database schema must be migrated before backfilling subagent metadata`);if(e.exec(`BEGIN IMMEDIATE;`),t=S(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}},S=()=>c.env.HAPPENIN_DB?c.env.HAPPENIN_DB:s.join(u(),`.happenin`,`happenin.db`),C=(e,n=5e3)=>{let r=e??S();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}},C=()=>c.env.HAPPENIN_DB?c.env.HAPPENIN_DB:s.join(u(),`.happenin`,`happenin.db`),w=(e,n=5e3)=>{let r=e??C();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 (
68
68
  id INTEGER PRIMARY KEY AUTOINCREMENT,
69
69
  source TEXT NOT NULL,
70
70
  client TEXT,
@@ -86,7 +86,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
86
86
  mtime INTEGER NOT NULL,
87
87
  imported_at INTEGER NOT NULL
88
88
  );
89
- `),ye(i),be(i);for(let e of ve)i.exec(e);return i}catch(e){try{i.close()}catch{}throw e}},Se=[`happenedAt`,`timestamp`,`happened_at`,`time`,`ts`,`createdAt`,`created_at`];function w(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 Se){let n=w(Reflect.get(t,e));if(n!==void 0)return n}}const T=(e,t)=>{let n=e.prepare(`
89
+ `),ve(i),ye(i);for(let e of _e)i.exec(e);return i}catch(e){try{i.close()}catch{}throw e}},xe=[`happenedAt`,`timestamp`,`happened_at`,`time`,`ts`,`createdAt`,`created_at`];function T(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 xe){let n=T(Reflect.get(t,e));if(n!==void 0)return n}}const E=(e,t)=>{let n=e.prepare(`
90
90
  INSERT INTO events (
91
91
  source,
92
92
  client,
@@ -103,7 +103,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
103
103
  subagent_type,
104
104
  transcript_path
105
105
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
106
- `),r=Date.now(),i=w(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.payload,t.sourcePath??null,t.subagentId??null,t.subagentType??null,t.transcriptPath??null)},E=`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 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.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.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(`${E} >= ?`),r.push(new Date(t-we(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=`
106
+ `),r=Date.now(),i=T(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.payload,t.sourcePath??null,t.subagentId??null,t.subagentType??null,t.transcriptPath??null)},D=`COALESCE(strftime('%Y-%m-%dT%H:%M:%fZ', happened_at), strftime('%Y-%m-%dT%H:%M:%fZ', received_at / 1000.0, 'unixepoch'))`;function Ce(e){return(e===`7d`?168:e===`30d`?720:24)*60*60*1e3}function O(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.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(`${D} >= ?`),r.push(new Date(t-Ce(e.range)).toISOString())),{clause:n.length>0?`WHERE ${n.join(` AND `)}`:``,params:r}}const k=(e,t)=>{let{clause:n,params:r}=O(t),i=t.limit??100,a=t.offset??0,o=`
107
107
  SELECT
108
108
  id,
109
109
  source,
@@ -121,7 +121,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
121
121
  subagent_type AS subagentType,
122
122
  transcript_path AS transcriptPath
123
123
  FROM events
124
- ${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},Te=(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)}},Ee=(e,t,n=Date.now())=>{let{clause:r,params:i}=D(t,n),a=t.limit??100,o=t.offset??0,s=`
124
+ ${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}=O(t),i=`SELECT COUNT(*) AS count FROM events ${n}`,a=e.prepare(i).get(...r);return a?Number(a.count):0},we=(e,t)=>{let{clause:n,params:r}=O(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 j(e){let t=Number(e.firstReceivedAt),n=Number(e.lastReceivedAt),r=JSON.parse(e.projectPaths),i=JSON.parse(e.toolNames),a=e.firstAt?Date.parse(e.firstAt):NaN,o=e.lastAt?Date.parse(e.lastAt):NaN,s=!Number.isNaN(a)&&!Number.isNaN(o)&&o>=a?o-a:n-t;return{sessionId:e.sessionId,subagentId:e.subagentId,subagentType:e.subagentType,firstAt:e.firstAt,lastAt:e.lastAt,firstReceivedAt:t,lastReceivedAt:n,durationMs:s,eventCount:Number(e.eventCount),projectPath:r[0]??null,projectPaths:r,tools:i,failureCount:Number(e.failureCount)}}const Te=(e,t,n=Date.now())=>{let{clause:r,params:i}=O(t,n),a=t.limit??100,o=t.offset??0,s=`
125
125
  SELECT
126
126
  session_id AS sessionId,
127
127
  COUNT(*) AS eventCount,
@@ -137,13 +137,30 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
137
137
  GROUP BY session_id
138
138
  ORDER BY lastReceivedAt DESC, sessionId
139
139
  LIMIT ?${o>0?` OFFSET ?`:``}
140
- `;return i.push(a),o>0&&i.push(o),e.prepare(s).all(...i).map(e=>{let t=Number(e.firstReceivedAt),n=Number(e.lastReceivedAt),r=JSON.parse(e.projectPaths),i=JSON.parse(e.toolNames),a=e.firstAt?Date.parse(e.firstAt):NaN,o=e.lastAt?Date.parse(e.lastAt):NaN,s=!Number.isNaN(a)&&!Number.isNaN(o)&&o>=a?o-a:n-t;return{sessionId:e.sessionId,firstAt:e.firstAt,lastAt:e.lastAt,firstReceivedAt:t,lastReceivedAt:n,durationMs:s,eventCount:Number(e.eventCount),projectPath:r[0]??null,projectPaths:r,tools:i,failureCount:Number(e.failureCount)}})},De=(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)};function Oe(e){return e===`day`?`strftime('%Y-%m-%dT00:00:00.000Z', ${E})`:`strftime('%Y-%m-%dT%H:00:00.000Z', ${E})`}function ke(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 Ae(e){return e===`day`?1440*60*1e3:3600*1e3}const je=(e,t={},n=24,r=`hour`,i=Date.now())=>{if(n<=0){let n=Oe(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=Ae(r),o=ke(r,i),s=ke(r,i-n*60*60*1e3),c=Math.max(1,Math.ceil((o.getTime()-s.getTime())/a)+1),l=s.toISOString(),u=Oe(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},A=e=>{let t=e.prepare(`SELECT id FROM events ORDER BY id DESC LIMIT 1`).get();return t?Number(t.id):0},Me=(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 Ne(e,t,n){return!(t.status!==void 0&&Me(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 j=(e,t={},n=Date.now())=>{let r=Ee(e,{...t,limit:2**53-1,offset:0},n).filter(e=>Ne(e,t,n)),i=t.offset??0,a=t.limit;return i===0&&a===void 0?r:a===void 0?r.slice(i):r.slice(i,i+a)},Pe=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();return{sources:t.map(e=>e.source),events:n.map(e=>e.event),tools:r.map(e=>e.tool)}},M=(e,t,n)=>{e.prepare(`
140
+ `;return i.push(a),o>0&&i.push(o),e.prepare(s).all(...i).map(j)},Ee=(e,t={},n=10,r=Date.now())=>{let{clause:i,params:a}=O(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)};function De(e){return e===`day`?`strftime('%Y-%m-%dT00:00:00.000Z', ${D})`:`strftime('%Y-%m-%dT%H:00:00.000Z', ${D})`}function Oe(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 ke(e){return e===`day`?1440*60*1e3:3600*1e3}const Ae=(e,t={},n=24,r=`hour`,i=Date.now())=>{if(n<=0){let n=De(r),{clause:a,params:o}=O({...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=ke(r),o=Oe(r,i),s=Oe(r,i-n*60*60*1e3),c=Math.max(1,Math.ceil((o.getTime()-s.getTime())/a)+1),l=s.toISOString(),u=De(r),{clause:d,params:f}=O({...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},je=e=>{let t=e.prepare(`SELECT id FROM events ORDER BY id DESC LIMIT 1`).get();return t?Number(t.id):0},Me=(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 Ne(e,t,n){return!(t.status!==void 0&&Me(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 Pe=(e,t,n={})=>{let r=[...new Set(t.filter(e=>e!==null))];if(r.length===0)return[];let{clause:i,params:a}=O({...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=`
141
+ SELECT
142
+ session_id AS sessionId,
143
+ subagent_id AS subagentId,
144
+ MAX(subagent_type) AS subagentType,
145
+ COUNT(*) AS eventCount,
146
+ MIN(received_at) AS firstReceivedAt,
147
+ MAX(received_at) AS lastReceivedAt,
148
+ MIN(happened_at) AS firstAt,
149
+ MAX(happened_at) AS lastAt,
150
+ COALESCE(json_group_array(DISTINCT project_path ORDER BY project_path) FILTER (WHERE project_path IS NOT NULL AND project_path <> ''), '[]') AS projectPaths,
151
+ COALESCE(json_group_array(DISTINCT tool_name ORDER BY tool_name) FILTER (WHERE tool_name IS NOT NULL AND tool_name <> ''), '[]') AS toolNames,
152
+ SUM(CASE WHEN event LIKE '%Failure%' THEN 1 ELSE 0 END) AS failureCount
153
+ FROM events
154
+ WHERE subagent_id IS NOT NULL AND subagent_id <> '' AND session_id IN (${n.map(()=>`?`).join(`,`)})${o}
155
+ GROUP BY session_id, subagent_id
156
+ ORDER BY lastReceivedAt DESC, subagent_id
157
+ `,c=e.prepare(i);s.push(...c.all(...n,...a))}return s.map(j)},M=(e,t={},n=Date.now())=>{let r=Te(e,{...t,limit:2**53-1,offset:0},n).filter(e=>Ne(e,t,n)),i=Pe(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)},Fe=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();return{sources:t.map(e=>e.source),events:n.map(e=>e.event),tools:r.map(e=>e.tool)}},N=(e,t,n)=>{e.prepare(`
141
158
  INSERT INTO imports (path, mtime, imported_at)
142
159
  VALUES (?, ?, ?)
143
160
  ON CONFLICT(path) DO UPDATE SET
144
161
  mtime = excluded.mtime,
145
162
  imported_at = excluded.imported_at
146
- `).run(t,n,Date.now())},Fe=(e,t)=>{let n=e.prepare(`SELECT mtime FROM imports WHERE path = ?`).get(t);return n?Number(n.mtime):void 0};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 Ie(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 Le(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=Ce(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.filePath??r.file_path??r.path),u=N(r.toolName??r.tool_name??r.tool),d=N(r.client)??(i===`cursor`?`cursor`:`claude_code`),f=N(r.subagent_id)??void 0,p=N(r.subagent_type)??void 0,m=N(r.transcript_path)??void 0,h={source:i,client:d,event:a,sessionId:o,happenedAt:s,projectPath:c,filePath:l,toolName:u,payload:t,subagentId:f,subagentType:p,transcriptPath:m},g=ne(i,a??``),_;try{if(_=C(n,500),!h.subagentId){let e=N(r.tool_use_id);if(e){let t=_.prepare(`SELECT subagent_id FROM events WHERE event = 'subagentStart' AND subagent_id = ? LIMIT 1`).get(e);t&&(h.subagentId=t.subagent_id)}}T(_,h)}catch(e){if(Ie(e))console.warn(`happenin: dropping event due to database lock`,e);else throw e}finally{try{_?.close()}catch{}}return g}async function Re(e){let t=[];for await(let e of c.stdin)t.push(Buffer.isBuffer(e)?e:Buffer.from(e,`utf8`));let n=Le(e,Buffer.concat(t).toString(`utf8`).trim());n!==void 0&&c.stdout.write(`${n}\n`)}const F=`claude-transcript`,I=`cursor-transcript`,ze=`cursor`,L=e=>{try{return r(e,{withFileTypes:!0})}catch{return[]}},Be=e=>{try{return n(e,`utf8`)}catch{return}},Ve=e=>{try{return Math.floor(a(e).mtimeMs)}catch{return}},R=(e,t)=>{let n=Ve(t);if(n===void 0||Fe(e,t)===n)return;let r=Be(t);if(r!==void 0)return{content:r,mtime:n}};function z(e){let t=[];for(let n of L(e)){if(n.isSymbolicLink())continue;let r=s.join(e,n.name);n.isDirectory()?t.push(...z(r)):n.isFile()&&n.name.endsWith(`.jsonl`)&&n.name!==`store.db`&&t.push(r)}return t}function He(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 Ue(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(F,t);for(let{obj:n,line:r}of o)T(e,{source:F,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});M(e,t,i)}async function We(e){let t=s.resolve(u(),`.claude/projects`);for(let n of z(t))await Ue(e,n)}async function Ge(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`&&T(e,{source:I,client:ze,event:`prompt`,sessionId:n,payload:JSON.stringify({prompt:r}),sourcePath:t});M(e,t,a)}async function Ke(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),T(e,{source:I,client:ze,event:`session_meta`,sessionId:n,payload:i,sourcePath:t}),M(e,t,a)}async function qe(e,t){let n=s.basename(t);await Ge(e,s.join(t,`prompt_history.json`),n),await Ke(e,s.join(t,`meta.json`),n)}async function Je(e){let t=s.resolve(u(),`.cursor/chats`);for(let n of He(t))await qe(e,n)}async function B(e){let t=C(S());try{xe(t),await Ye(t)}finally{t.close()}}async function Ye(e){await We(e),await Je(e)}function V(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function H(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`)}function U(e,t=24){return e.length<=t?e:`${e.slice(0,t-1)}…`}function W(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 G(e){if(e==null||e===``)return`-`;let t=new Date(e);return Number.isNaN(t.getTime())?`-`:t.toLocaleString()}function Xe(e){let t=e.totalSessions===0?`-`:`${Math.round(e.successRate)}%`;return`<div class="metric-grid">
163
+ `).run(t,n,Date.now())},Ie=(e,t)=>{let n=e.prepare(`SELECT mtime FROM imports WHERE path = ?`).get(t);return n?Number(n.mtime):void 0};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 Le(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 Re(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=Se(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.filePath??r.file_path??r.path),u=P(r.toolName??r.tool_name??r.tool),d=P(r.client)??(i===`cursor`?`cursor`:`claude_code`),f=P(r.subagent_id)??void 0,p=P(r.subagent_type)??void 0,m=P(r.transcript_path)??void 0,h={source:i,client:d,event:a,sessionId:o,happenedAt:s,projectPath:c,filePath:l,toolName:u,payload:t,subagentId:f,subagentType:p,transcriptPath:m},g=ne(i,a??``),_;try{if(_=w(n,500),!h.subagentId){let e=P(r.tool_use_id);if(e){let t=_.prepare(`SELECT subagent_id FROM events WHERE event = 'subagentStart' AND subagent_id = ? LIMIT 1`).get(e);t&&(h.subagentId=t.subagent_id)}}E(_,h)}catch(e){if(Le(e))console.warn(`happenin: dropping event due to database lock`,e);else throw e}finally{try{_?.close()}catch{}}return g}async function ze(e){let t=[];for await(let e of c.stdin)t.push(Buffer.isBuffer(e)?e:Buffer.from(e,`utf8`));let n=Re(e,Buffer.concat(t).toString(`utf8`).trim());n!==void 0&&c.stdout.write(`${n}\n`)}const Be=`claude-transcript`,I=`cursor-transcript`,L=`cursor`,R=e=>{try{return r(e,{withFileTypes:!0})}catch{return[]}},Ve=e=>{try{return n(e,`utf8`)}catch{return}},He=e=>{try{return Math.floor(a(e).mtimeMs)}catch{return}},z=(e,t)=>{let n=He(t);if(n===void 0||Ie(e,t)===n)return;let r=Ve(t);if(r!==void 0)return{content:r,mtime:n}};function B(e){let t=[];for(let n of R(e)){if(n.isSymbolicLink())continue;let r=s.join(e,n.name);n.isDirectory()?t.push(...B(r)):n.isFile()&&n.name.endsWith(`.jsonl`)&&n.name!==`store.db`&&t.push(r)}return t}function Ue(e){let t=[];for(let n of R(e)){if(!n.isDirectory()||n.isSymbolicLink())continue;let r=s.join(e,n.name);for(let e of R(r))e.isDirectory()&&!e.isSymbolicLink()&&t.push(s.join(r,e.name))}return t}async function We(e,t){let n=z(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(Be,t);for(let{obj:n,line:r}of o)E(e,{source:Be,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});N(e,t,i)}async function Ge(e){let t=s.resolve(u(),`.claude/projects`);for(let n of B(t))await We(e,n)}async function Ke(e,t,n){let r=z(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`&&E(e,{source:I,client:L,event:`prompt`,sessionId:n,payload:JSON.stringify({prompt:r}),sourcePath:t});N(e,t,a)}async function qe(e,t,n){let r=z(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),E(e,{source:I,client:L,event:`session_meta`,sessionId:n,payload:i,sourcePath:t}),N(e,t,a)}async function Je(e,t){let n=s.basename(t);await Ke(e,s.join(t,`prompt_history.json`),n),await qe(e,s.join(t,`meta.json`),n)}async function Ye(e){let t=s.resolve(u(),`.cursor/chats`);for(let n of Ue(t))await Je(e,n)}async function V(e){let t=w(C());try{be(t),await Xe(t)}finally{t.close()}}async function Xe(e){await Ge(e),await Ye(e)}function H(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function U(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`)}function Ze(e,t=24){return e.length<=t?e:`${e.slice(0,t-1)}…`}function Qe(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 W(e){if(e==null||e===``)return`-`;let t=new Date(e);return Number.isNaN(t.getTime())?`-`:t.toLocaleString()}function $e(e){let t=e.totalSessions===0?`-`:`${Math.round(e.successRate)}%`;return`<div class="metric-grid">
147
164
  <div class="metric-card metric-sessions">
148
165
  <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>
149
166
  <div class="metric-label">Total Sessions</div>
@@ -157,24 +174,26 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
157
174
  <div class="metric-card metric-duration">
158
175
  <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>
159
176
  <div class="metric-label">Average Duration</div>
160
- <div class="metric-value">${W(e.averageDurationMs)}</div>
177
+ <div class="metric-value">${Qe(e.averageDurationMs)}</div>
161
178
  </div>
162
179
  <div class="metric-card metric-success">
163
180
  <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>
164
181
  <div class="metric-label">Success Rate</div>
165
182
  <div class="metric-value">${t}</div>
166
183
  </div>
167
- </div>`}function Ze(e,t){return t===`day`?e.slice(5,10):e.slice(11,16)}function Qe(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:Ze(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">${V(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 $e(e){if(e.length===0)return`<div class="chart-panel"><h2 class="chart-title">Most Used Tools</h2><div class="chart-empty">No data</div></div>`;let t=Math.max(...e.map(e=>e.count));return`<div class="chart-panel"><h2 class="chart-title">Most Used Tools</h2><div class="tool-chart">${e.map(e=>{let n=t>0?e.count/t*100:0,r=e.tool;return`<div class="tool-row"><span class="tool-name"${r.length>20?` title="${H(r)}"`:``}>${V(r.length>20?`${r.slice(0,19)}…`:r)}</span><div class="tool-bar-bg"><div class="tool-bar" style="width: ${n}%"></div></div><span class="tool-count">${e.count}</span></div>`}).join(``)}</div></div>`}function et(e,t){let n=new URLSearchParams({session:t});return e?.source&&n.set(`source`,e.source),e?.event&&n.set(`event`,e.event),e?.q&&n.set(`q`,e.q),e?.range&&n.set(`range`,e.range),e?.status&&n.set(`status`,e.status),e?.tool&&n.set(`tool`,e.tool),e?.minDuration!==void 0&&n.set(`minDuration`,String(e.minDuration)),e?.maxDuration!==void 0&&n.set(`maxDuration`,String(e.maxDuration)),e?.limit!==void 0&&n.set(`limit`,String(e.limit)),e?.offset!==void 0&&n.set(`offset`,String(e.offset)),`/fragments/detail?${n.toString()}`}function tt(e,t=Date.now(),n,r){return e.length===0?`<h2 class="session-list-title">Recent Sessions</h2><ul class="session-list sessions-table"><li class="empty">No sessions found.</li></ul>`:`<h2 class="session-list-title">Recent Sessions</h2>
168
- <ul class="session-list sessions-table">${e.map(e=>{let i=Me(e,t),a=e.sessionId??`no session`,o=V(U(a,28)),s=V(G(e.firstAt??e.firstReceivedAt)),c=V(W(e.durationMs)),l=n!==void 0&&a===n?` active`:``,u=e.sessionId?` hx-get="${H(et(r,e.sessionId))}" hx-target="#dashboard-content" hx-swap="innerHTML"`:``;return`<li class="${`session-item${l}${e.sessionId?``:` session-item-static`}`}" data-session="${H(a)}"${u}>
169
- <div class="session-main">
170
- <div class="session-row">
171
- <span class="session-id" title="${H(a)}">${o}</span>
172
- <span class="status-badge status-${i}">${i}</span>
173
- </div>
174
- <div class="session-meta"><span>${c}</span><span>${e.eventCount} events</span><span>${s}</span></div>
175
- </div>
176
- <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>
177
- </li>`}).join(``)}</ul>`}function nt(e){if(e)try{return JSON.parse(e)}catch{return}}function rt(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,sourcePath:e.sourcePath,subagentId:e.subagentId,subagentType:e.subagentType,transcriptPath:e.transcriptPath,payload:nt(e.payload??``)??e.payload}}function it(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 K(e){return`<span class="ts">${V(G(e.happenedAt??e.receivedAt))}</span><span class="source">${V(e.source??``)}</span><span class="event">${V(e.event??``)}</span><span class="tool">${V(e.toolName??``)}</span>`}function at(e){let t=V(G(e.happenedAt??e.receivedAt)),n=V(e.source??``),r=V(e.client??``),i=V(e.event??``),a=V(e.toolName??``),o=V(e.subagentId??``),s=V(e.subagentType??``),c=V(e.projectPath??``),l=V(e.filePath??``),u=o?`<div><span>Subagent</span><span>${s||`-`} (${o})</span></div>`:``,d=l?`<div><span>File</span><span>${l}</span></div>`:``;return`<div class="detail-attrs">
184
+ </div>`}function et(e,t){return t===`day`?e.slice(5,10):e.slice(11,16)}function tt(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:et(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">${H(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 nt(e){if(e.length===0)return`<div class="chart-panel"><h2 class="chart-title">Most Used Tools</h2><div class="chart-empty">No data</div></div>`;let t=Math.max(...e.map(e=>e.count));return`<div class="chart-panel"><h2 class="chart-title">Most Used Tools</h2><div class="tool-chart">${e.map(e=>{let n=t>0?e.count/t*100:0,r=e.tool;return`<div class="tool-row"><span class="tool-name"${r.length>20?` title="${U(r)}"`:``}>${H(r.length>20?`${r.slice(0,19)}…`:r)}</span><div class="tool-bar-bg"><div class="tool-bar" style="width: ${n}%"></div></div><span class="tool-count">${e.count}</span></div>`}).join(``)}</div></div>`}function rt(e,t,n){let r=new URLSearchParams({session:t});return 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?.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 G(e,t,n,r,i,a=!1,o=``,s=``){let c=Me(e,t),l=a?e.subagentId??`no subagent`:e.sessionId??`no session`,u=H(Ze(l,28)),d=H(W(e.firstAt??e.firstReceivedAt)),f=H(Qe(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="${U(rt(i,m,h))}" hx-target="#dashboard-content" hx-swap="innerHTML"`:``,ee=m&&g?` hx-get="${U(rt(i,m))}" hx-target="#dashboard-content" hx-swap="innerHTML"`:``,te=a?` session-subagent`:``,ne=!a&&e.children?.length&&e.sessionId===n?` expanded`:``,v=!a&&e.children?.length?` session-parent`:``,re=!a&&!e.sessionId?` session-item-static`:``,y=a?` data-session="${U(m)}" data-subagent="${U(e.subagentId??``)}"`:` data-session="${U(e.sessionId??``)}"`,ie=a&&e.subagentType?` <span class="subagent-type-badge">${H(e.subagentType)}</span>`:``;return`<li class="session-item${v}${ne}${te}${p}${re}"${y}${_}>
185
+ ${o}
186
+ <div class="session-main"${ee}>
187
+ <div class="session-row">
188
+ <span class="session-id" title="${U(l)}">${u}${ie}</span>
189
+ <span class="status-badge status-${c}">${c}</span>
190
+ </div>
191
+ <div class="session-meta"><span>${f}</span><span>${e.eventCount} events</span><span>${d}</span></div>
192
+ </div>
193
+ <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>
194
+ ${s}
195
+ </li>`}function it(e,t=Date.now(),n,r,i){return e.length===0?`<h2 class="session-list-title">Recent Sessions</h2><ul class="session-list sessions-table"><li class="empty">No sessions found.</li></ul>`:`<h2 class="session-list-title">Recent Sessions</h2>
196
+ <ul class="session-list sessions-table">${e.map(e=>e.children?.length?G(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=>G(e,t,n,r,i,!0)).join(``)}</ul>`):G(e,t,n,r,i)).join(``)}</ul>`}function at(e){if(e)try{return JSON.parse(e)}catch{return}}function ot(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,sourcePath:e.sourcePath,subagentId:e.subagentId,subagentType:e.subagentType,transcriptPath:e.transcriptPath,payload:at(e.payload??``)??e.payload}}function st(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 ct(e){return`<span class="ts">${H(W(e.happenedAt??e.receivedAt))}</span><span class="source">${H(e.source??``)}</span><span class="event">${H(e.event??``)}</span><span class="tool">${H(e.toolName??``)}</span>`}function lt(e){let t=H(W(e.happenedAt??e.receivedAt)),n=H(e.source??``),r=H(e.client??``),i=H(e.event??``),a=H(e.toolName??``),o=H(e.subagentId??``),s=H(e.subagentType??``),c=H(e.projectPath??``),l=H(e.filePath??``),u=o?`<div><span>Subagent</span><span>${s||`-`} (${o})</span></div>`:``,d=l?`<div><span>File</span><span>${l}</span></div>`:``;return`<div class="detail-attrs">
178
197
  <div><span>Time</span><span>${t}</span></div>
179
198
  <div><span>Source</span><span>${n}</span></div>
180
199
  <div><span>Client</span><span>${r||`-`}</span></div>
@@ -183,53 +202,53 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
183
202
  <div><span>Project</span><span>${c||`-`}</span></div>
184
203
  ${u}
185
204
  ${d}
186
- </div>`}function ot(e){return`<div class="detail-json">
205
+ </div>`}function ut(e){return`<div class="detail-json">
187
206
  <details class="json-toggle">
188
207
  <summary>JSON</summary>
189
- <pre>${V(JSON.stringify(rt(e),null,2))}</pre>
208
+ <pre>${H(JSON.stringify(ot(e),null,2))}</pre>
190
209
  </details>
191
210
  <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>
192
- </div>`}function st(e){return`<details class="detail-event" data-event-id="${e.id}" open>
193
- <summary>${K(e)}</summary>
194
- <div class="detail-body">${at(e)}
195
- ${ot(e)}
211
+ </div>`}function dt(e){return`<details class="detail-event" data-event-id="${e.id}" open>
212
+ <summary>${ct(e)}</summary>
213
+ <div class="detail-body">${lt(e)}
214
+ ${ut(e)}
196
215
  </div>
197
- </details>`}function ct(e,t){let n=e.parent,r=e.children.map(st).join(``),i=`<span class="subagent-badge">${V(n.subagentType||`subagent`)}${` · ${V(U(t,18))}`} · ${e.children.length}</span>`;return`<details class="detail-event detail-subagent" data-event-id="${n.id}" open>
198
- <summary>${K(n)}${i}</summary>
216
+ </details>`}function ft(e,t){let n=e.parent,r=e.children.map(dt).join(``),i=`<span class="subagent-badge">${H(n.subagentType||`subagent`)}${` · ${H(Ze(t,18))}`} · ${e.children.length}</span>`;return`<details class="detail-event detail-subagent" data-event-id="${n.id}" open>
217
+ <summary>${ct(n)}${i}</summary>
199
218
  <div class="detail-body">
200
- ${at(n)}
201
- ${ot(n)}
219
+ ${lt(n)}
220
+ ${ut(n)}
202
221
  <div class="detail-children">${r}</div>
203
222
  </div>
204
- </details>`}function lt(e){return it(e).map(e=>e.subagentId?ct(e,e.subagentId):[e.parent,...e.children].map(st).join(``)).join(``)}function ut(e,t,n){if(!e)return`<div class="detail-header"><h2 class="detail-title">Session Details</h2></div><div class="empty">No events.</div>`;if(t.length===0)return`<div class="detail-header"><h2 class="detail-title">Session Details - ${V(e)}</h2></div><div class="empty">No events for this session.</div>`;let r=lt(t),i=JSON.stringify(t,null,2),a=n!==void 0&&n>t.length?`<div class="detail-truncated">Showing the ${t.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="${H(e)}">
223
+ </details>`}function pt(e){return st(e).map(e=>e.subagentId?ft(e,e.subagentId):[e.parent,...e.children].map(dt).join(``)).join(``)}function mt(e,t,n,r){if(!e)return`<div class="detail-header"><h2 class="detail-title">Session Details</h2></div><div class="empty">No events.</div>`;let i=r?t.filter(e=>e.subagentId===r):t,a=r?`${e} · ${r}`:e;if(i.length===0)return`<div class="detail-header"><h2 class="detail-title">Session Details - ${H(a)}</h2></div><div class="empty">No events for this subagent.</div>`;let o=pt(i),s=JSON.stringify(i,null,2),c=n!==void 0&&n>i.length?`<div class="detail-truncated">Showing the ${i.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="${U(e)}" data-subagent="${U(r??``)}">
205
224
  <div class="detail-header">
206
225
  <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>Back</span></button>
207
- <h2 class="detail-title">Session Details - ${V(e)}</h2>
208
- <span class="detail-count">${n??t.length} events</span>
226
+ <h2 class="detail-title">Session Details - ${H(a)}</h2>
227
+ <span class="detail-count">${n??i.length} events</span>
209
228
  </div>
210
- ${a}
229
+ ${c}
211
230
  <div class="detail-toolbar">
212
231
  <input type="search" class="detail-search" placeholder="Search attributes..." oninput="filterSessionDetails(this.value)">
213
232
  <button type="button" onclick="toggleSessionDetails(false)">Collapse All</button>
214
233
  <button type="button" onclick="toggleSessionDetails(true)">Expand All</button>
215
234
  </div>
216
- <div class="detail-events">${r}</div>
235
+ <div class="detail-events">${o}</div>
217
236
  <div class="detail-actions">
218
237
  <button type="button" onclick="copySessionJson()">Copy JSON</button>
219
238
  </div>
220
- <pre id="session-json" class="session-json">${V(i)}</pre>
221
- </section>`}function dt(e){if(!e)return;let t=Number(e);return Number.isFinite(t)&&t>=0?t:void 0}function ft(e){if(!e)return;let t=Number(e);return Number.isInteger(t)&&t>=0?t:void 0}function pt(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 q(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(`q`)||void 0,o=e.searchParams.get(`status`)||void 0,s=e.searchParams.get(`tool`)||void 0,c=e.searchParams.get(`minDuration`),l=e.searchParams.get(`maxDuration`),u=e.searchParams.get(`range`)||`24h`,d=[`24h`,`7d`,`30d`,`all`].includes(u)?u:`24h`,f=e.searchParams.get(`limit`),p=e.searchParams.get(`offset`),m;if(t){let e=parseInt(t,10);Number.isNaN(e)||(m=e)}return{since:m,source:n,event:r,sessionId:i,q:a,status:o===`active`||o===`completed`||o===`failed`?o:void 0,tool:s,minDuration:dt(c),maxDuration:dt(l),range:d,limit:ft(f),offset:ft(p)}}function J(e,t,n){let r=new URLSearchParams;return e.source&&r.set(`source`,e.source),e.event&&r.set(`event`,e.event),e.sessionId&&r.set(`session`,e.sessionId),e.q&&r.set(`q`,e.q),e.since!==void 0&&r.set(`since`,String(e.since)),e.range&&r.set(`range`,e.range),e.status&&r.set(`status`,e.status),e.tool&&r.set(`tool`,e.tool),e.minDuration!==void 0&&r.set(`minDuration`,String(e.minDuration)),e.maxDuration!==void 0&&r.set(`maxDuration`,String(e.maxDuration)),r.set(`limit`,String(n)),r.set(`offset`,String(t)),`/fragments/sessions?${r.toString()}`}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;return`<div class="pager">${o>=0?`<a href="${J(e,o,n)}" hx-get="${J(e,o,n)}" 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="${J(e,s,n)}" hx-get="${J(e,s,n)}" hx-target="#dashboard-content" hx-swap="innerHTML">Next</a>`:`<button type="button" disabled>Next</button>`}</div>`}function ht(e,t,n,r){let i=t.limit&&t.limit>0?t.limit:25,a=t.offset??0;return`<aside class="session-sidebar">
239
+ <pre id="session-json" class="session-json">${H(s)}</pre>
240
+ </section>`}function ht(e){if(!e)return;let t=Number(e);return Number.isFinite(t)&&t>=0?t:void 0}function gt(e){if(!e)return;let t=Number(e);return Number.isInteger(t)&&t>=0?t:void 0}function _t(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 K(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(`minDuration`),u=e.searchParams.get(`maxDuration`),d=e.searchParams.get(`range`)||`24h`,f=[`24h`,`7d`,`30d`,`all`].includes(d)?d:`24h`,p=e.searchParams.get(`limit`),m=e.searchParams.get(`offset`),h;if(t){let e=parseInt(t,10);Number.isNaN(e)||(h=e)}return{since:h,source:n,event:r,sessionId:i,subagentId:a,q:o,status:s===`active`||s===`completed`||s===`failed`?s:void 0,tool:c,minDuration:ht(l),maxDuration:ht(u),range:f,limit:gt(p),offset:gt(m)}}function q(e,t,n){let r=new URLSearchParams;return e.source&&r.set(`source`,e.source),e.event&&r.set(`event`,e.event),e.sessionId&&r.set(`session`,e.sessionId),e.q&&r.set(`q`,e.q),e.since!==void 0&&r.set(`since`,String(e.since)),e.range&&r.set(`range`,e.range),e.status&&r.set(`status`,e.status),e.tool&&r.set(`tool`,e.tool),e.minDuration!==void 0&&r.set(`minDuration`,String(e.minDuration)),e.maxDuration!==void 0&&r.set(`maxDuration`,String(e.maxDuration)),r.set(`limit`,String(n)),r.set(`offset`,String(t)),`/fragments/sessions?${r.toString()}`}function vt(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;return`<div class="pager">${o>=0?`<a href="${q(e,o,n)}" hx-get="${q(e,o,n)}" 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="${q(e,s,n)}" hx-get="${q(e,s,n)}" hx-target="#dashboard-content" hx-swap="innerHTML">Next</a>`:`<button type="button" disabled>Next</button>`}</div>`}function J(e,t,n,r,i){let a=t.limit&&t.limit>0?t.limit:25,o=t.offset??0;return`<aside class="session-sidebar">
222
241
  <div class="session-list-wrapper">
223
- ${tt(e.slice(a,a+i),n,r,t)}
224
- ${mt(t,e.length)}
242
+ ${it(e.slice(o,o+a),n,r,i,t)}
243
+ ${vt(t,e.length)}
225
244
  </div>
226
- </aside>`}function gt(e,t){let n=Date.now(),{hours:r,groupBy:i}=pt(t.range),a=j(e,{...t,limit:void 0,offset:void 0},n),o=a.map(e=>e.sessionId),s=[],c=[];if(o.length>0){let a={...t,sessionIds:o};s=je(e,a,r,i,n),c=De(e,a,10,n)}let l=a.length,u=a.reduce((e,t)=>e+t.eventCount,0),d=l>0?a.reduce((e,t)=>e+t.durationMs,0)/l:0,f=a.filter(e=>e.failureCount===0).length,p={totalSessions:l,totalEvents:u,averageDurationMs:d,successRate:l>0?f/l*100:0};return`${ht(a,t,n)}
245
+ </aside>`}function yt(e,t){let n=Date.now(),{hours:r,groupBy:i}=_t(t.range),a=M(e,{...t,subagentId:void 0,limit:void 0,offset:void 0},n),o=a.map(e=>e.sessionId),s=[],c=[];if(o.length>0){let a={...t,subagentId:void 0,sessionIds:o};s=Ae(e,a,r,i,n),c=Ee(e,a,10,n)}let l=a.length,u=a.reduce((e,t)=>e+t.eventCount,0),d=l>0?a.reduce((e,t)=>e+t.durationMs,0)/l:0,f=a.filter(e=>e.failureCount===0).length,p={totalSessions:l,totalEvents:u,averageDurationMs:d,successRate:l>0?f/l*100:0};return`${J(a,t,n)}
227
246
  <div class="main-content">
228
- <div class="main-metrics">${Xe(p)}</div>
229
- <div class="top-charts">${Qe(s,i)}${$e(c)}</div>
230
- </div>`}function _t(e,t,n,r){let i=gt(r,q(n));t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(i)}function vt(e,t){let n=Date.now(),r=ht(j(e,{...t,sessionId:void 0,limit:void 0,offset:void 0},n),t,n,t.sessionId);if(!t.sessionId)return`${r}
231
- <div class="main-content"><div class="detail-area"><div class="empty">No events.</div></div></div>`;let i=O(e,{...t,range:void 0,sessionId:t.sessionId,sessionIdExact:!0,limit:1e3,offset:0}),a=k(e,{...t,range:void 0,sessionId:t.sessionId,sessionIdExact:!0});return`${r}
232
- <div class="main-content">${ut(t.sessionId,i,a)}</div>`}function yt(e,t,n,r){let i=vt(r,q(n));t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(i)}function bt(e,t,n,r){let i=O(r,q(n));t.writeHead(200,{"Content-Type":`application/json`}),t.end(JSON.stringify(i))}const xt=[{value:`24h`,label:`Last 24h`},{value:`7d`,label:`Last 7 days`},{value:`30d`,label:`Last 30 days`},{value:`all`,label:`All time`}];function St(e){let t=V(e.q??``),n=e.range??`24h`;return`<header class="app-header">
247
+ <div class="main-metrics">${$e(p)}</div>
248
+ <div class="top-charts">${tt(s,i)}${nt(c)}</div>
249
+ </div>`}function bt(e,t,n,r){let i=yt(r,K(n));t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(i)}function xt(e,t){let n=Date.now(),r=J(M(e,{...t,sessionId:void 0,subagentId:void 0,limit:void 0,offset:void 0},n),t,n,t.sessionId,t.subagentId);if(!t.sessionId)return`${r}
250
+ <div class="main-content"><div class="detail-area"><div class="empty">No events.</div></div></div>`;let i=k(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});return`${r}
251
+ <div class="main-content">${mt(t.sessionId,i,a,t.subagentId)}</div>`}function St(e,t,n,r){let i=xt(r,K(n));t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(i)}function Ct(e,t,n,r){let i=k(r,K(n));t.writeHead(200,{"Content-Type":`application/json`}),t.end(JSON.stringify(i))}const wt=[{value:`24h`,label:`Last 24h`},{value:`7d`,label:`Last 7 days`},{value:`30d`,label:`Last 30 days`},{value:`all`,label:`All time`}];function Tt(e){let t=H(e.q??``),n=e.range??`24h`;return`<header class="app-header">
233
252
  <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>
234
253
  <h1 class="page-title">Session Overview Analytics</h1>
235
254
  <div class="header-controls">
@@ -237,17 +256,17 @@ ${mt(t,e.length)}
237
256
  <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>
238
257
  <input type="text" name="q" class="search" placeholder="session or payload" value="${t}" autocomplete="off">
239
258
  </div>
240
- <label>range <select name="range">${xt.map(e=>`<option value="${H(e.value)}"${e.value===n?` selected`:``}>${V(e.label)}</option>`).join(``)}</select></label>
259
+ <label>range <select name="range">${wt.map(e=>`<option value="${U(e.value)}"${e.value===n?` selected`:``}>${H(e.label)}</option>`).join(``)}</select></label>
241
260
  <button type="button" class="theme" data-theme-toggle title="Toggle light/dark theme" aria-label="toggle theme">🌙</button>
242
261
  </div>
243
- </header>`}const Ct=[`active`,`completed`,`failed`];function Y(e,t,n,r=`all`){let i=n??``,a=H(i);return`<select name="${e}"><option value=""${i===``?` selected`:``}>${r}</option>${t.map(e=>{let t=H(e);return`<option value="${t}"${t===a?` selected`:``}>${V(e)}</option>`}).join(``)}</select>`}function wt(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);return`<div class="filter-bar">
244
- <label>status ${Y(`status`,Ct,n)}</label>
262
+ </header>`}const Et=[`active`,`completed`,`failed`];function Y(e,t,n,r=`all`){let i=n??``,a=U(i);return`<select name="${e}"><option value=""${i===``?` selected`:``}>${r}</option>${t.map(e=>{let t=U(e);return`<option value="${t}"${t===a?` selected`:``}>${H(e)}</option>`}).join(``)}</select>`}function Dt(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);return`<div class="filter-bar">
263
+ <label>status ${Y(`status`,Et,n)}</label>
245
264
  <label>source ${Y(`source`,e.sources,r)}</label>
246
265
  <label>tool ${Y(`tool`,e.tools,i)}</label>
247
266
  <label>event ${Y(`event`,e.events,a)}</label>
248
- <label>min duration (m) <input type="number" name="minDuration" min="0" step="any" placeholder="min" value="${V(o)}"></label>
249
- <label>max duration (m) <input type="number" name="maxDuration" min="0" step="any" placeholder="max" value="${V(s)}"></label>
250
- </div>`}function Tt(e,t){let n=Pe(e),r=gt(e,t);return`<!DOCTYPE html>
267
+ <label>min duration (m) <input type="number" name="minDuration" min="0" step="any" placeholder="min" value="${H(o)}"></label>
268
+ <label>max duration (m) <input type="number" name="maxDuration" min="0" step="any" placeholder="max" value="${H(s)}"></label>
269
+ </div>`}function Ot(e,t){let n=Fe(e),r=yt(e,t);return`<!DOCTYPE html>
251
270
  <html lang="en" hx-ext="sse">
252
271
  <head>
253
272
  <meta charset="utf-8">
@@ -432,8 +451,12 @@ ${mt(t,e.length)}
432
451
  const detail = content.querySelector('.session-detail-view');
433
452
  document.body.classList.toggle('session-detail-open', !!detail);
434
453
  const sessionId = detail ? detail.getAttribute('data-session') : null;
454
+ const subagentId = detail ? detail.getAttribute('data-subagent') || null : null;
435
455
  for (const item of content.querySelectorAll('.session-item')) {
436
- item.classList.toggle('active', !!sessionId && item.dataset.session === sessionId);
456
+ const sessionMatch = !!sessionId && item.dataset.session === sessionId;
457
+ const itemSubagent = item.dataset.subagent || null;
458
+ const subagentMatch = itemSubagent === null || itemSubagent === subagentId;
459
+ item.classList.toggle('active', sessionMatch && subagentMatch);
437
460
  }
438
461
  const list = content.querySelector('.session-list');
439
462
  if (list) {
@@ -561,6 +584,21 @@ button:hover { background: var(--surface-5); }
561
584
  .session-chevron { color: var(--text-3); background: none; border: none; padding: 0.2rem; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
562
585
  .session-chevron:hover { color: var(--accent); background: none; }
563
586
  .session-list .empty { padding: 2rem 1rem; text-align: center; color: var(--text-4); }
587
+ .session-item { flex-wrap: wrap; }
588
+ .session-item.session-parent { padding-bottom: 0; cursor: default; }
589
+ .session-item.session-parent:hover { background: none; }
590
+ .session-item.session-parent > .session-main { cursor: pointer; }
591
+ .session-item.session-parent > .session-main:hover { background: var(--surface-3); }
592
+ .session-toggle { background: none; border: none; color: var(--text-3); font-size: 0.8rem; padding: 0; margin-right: 0.5rem; cursor: pointer; width: 1rem; text-align: center; }
593
+ .session-toggle:hover { color: var(--accent); }
594
+ .session-children { display: none; width: 100%; list-style: none; padding: 0; margin: 0; align-self: flex-start; }
595
+ .session-children .session-item { padding-left: 2.75rem; background: none; border-bottom: 1px solid var(--border-2); border-left: 3px solid var(--border-3); }
596
+ .session-children .session-item:hover { background: var(--surface-3); }
597
+ .session-children .session-item.active { background: var(--surface-3); border-left-color: var(--accent-2); box-shadow: inset 3px 0 0 var(--accent-2); }
598
+ .session-children .session-subagent .session-id { color: var(--accent-2); }
599
+ .session-children .session-subagent .session-chevron { display: none; }
600
+ .session-item.expanded > .session-children { display: block; }
601
+ .subagent-type-badge { font-size: 0.65rem; color: var(--text-3); margin-left: 0.35rem; padding: 0.08rem 0.3rem; background: var(--surface-2); border-radius: 0.2rem; }
564
602
  .main-content { display: grid; grid-template-rows: auto 1fr; gap: 1.25rem; min-height: 0; overflow: hidden; }
565
603
  .main-content:has(.session-detail-view) { grid-template-rows: 1fr; }
566
604
  .main-metrics .metric-grid { grid-template-columns: repeat(4, 1fr); gap: 1rem; }
@@ -653,7 +691,7 @@ button:hover { background: var(--surface-5); }
653
691
  <div class="app">
654
692
  <div class="sticky-bar">
655
693
  <form data-filter-form hx-get="/fragments/sessions" hx-target="#dashboard-content" hx-swap="innerHTML" hx-trigger="change, input changed delay:300ms from:.search">
656
- ${St(t)}${wt(n,t)}
694
+ ${Tt(t)}${Dt(n,t)}
657
695
  </form>
658
696
  </div>
659
697
  <div id="dashboard-content" class="dashboard-layout" sse-connect="/events/stream" hx-get="/fragments/sessions" hx-target="#dashboard-content" hx-swap="innerHTML" hx-trigger="sse:message">
@@ -661,12 +699,12 @@ ${r}
661
699
  </div>
662
700
  </div>
663
701
  </body>
664
- </html>`}function Et(e,t,n,r){let i=q(n);t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(Tt(r,i))}let X;const Z=new Set;function Dt(e,t){t.writeHead(200,{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`}),t.setTimeout(0),t.write(`retry: 500
702
+ </html>`}function kt(e,t,n,r){let i=K(n);t.writeHead(200,{"Content-Type":`text/html; charset=utf-8`}),t.end(Ot(r,i))}let X;const Z=new Set;function At(e,t){t.writeHead(200,{"Content-Type":`text/event-stream`,"Cache-Control":`no-cache`,Connection:`keep-alive`}),t.setTimeout(0),t.write(`retry: 500
665
703
 
666
- `);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)?A(X):i,timer:null};a.timer=setInterval(()=>{try{let e=A(X);e>a.lastId&&(t.write(`event: message
704
+ `);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)?je(X):i,timer:null};a.timer=setInterval(()=>{try{let e=je(X);e>a.lastId&&(t.write(`event: message
667
705
  data: ping
668
706
 
669
- `),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 Ot(e,t){let n=new URL(e.url??`/`,`http://localhost`);try{if(e.method===`GET`){if(n.pathname===`/`){Et(e,t,n,X);return}if(n.pathname===`/fragments/sessions`){_t(e,t,n,X);return}if(n.pathname===`/fragments/detail`){yt(e,t,n,X);return}if(n.pathname===`/events`){bt(e,t,n,X);return}if(n.pathname===`/events/stream`){Dt(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`))}}async function kt(e,t,n=!1){let r=f.createServer((e,t)=>{Ot(e,t)});await new Promise((i,a)=>{let o=e=>{function s(t){r.off(`error`,s),r.off(`listening`,l),t.code===`EADDRINUSE`&&e<65535?o(e+1):a(t)}function l(){r.off(`error`,s);let a=`http://localhost:${e}`;n||console.log(`Dashboard: ${a}`),t&&c.platform===`darwin`&&p(`open`,[a],e=>{e&&console.error(`open failed:`,e.message)}),i()}r.once(`error`,s),r.once(`listening`,l),r.listen(e,`127.0.0.1`)};o(e)})}async function At(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 B(),X=C(S()),await kt(t,n,r)}const jt=[`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 Mt=[`24h`,`7d`,`30d`,`all`];function Nt(){c.stdout.write(`Usage: happenin query [options]
707
+ `),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 jt(e,t){let n=new URL(e.url??`/`,`http://localhost`);try{if(e.method===`GET`){if(n.pathname===`/`){kt(e,t,n,X);return}if(n.pathname===`/fragments/sessions`){bt(e,t,n,X);return}if(n.pathname===`/fragments/detail`){St(e,t,n,X);return}if(n.pathname===`/events`){Ct(e,t,n,X);return}if(n.pathname===`/events/stream`){At(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`))}}async function Mt(e,t,n=!1){let r=f.createServer((e,t)=>{jt(e,t)});await new Promise((i,a)=>{let o=e=>{function s(t){r.off(`error`,s),r.off(`listening`,l),t.code===`EADDRINUSE`&&e<65535?o(e+1):a(t)}function l(){r.off(`error`,s);let a=`http://localhost:${e}`;n||console.log(`Dashboard: ${a}`),t&&c.platform===`darwin`&&p(`open`,[a],e=>{e&&console.error(`open failed:`,e.message)}),i()}r.once(`error`,s),r.once(`listening`,l),r.listen(e,`127.0.0.1`)};o(e)})}async function Nt(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 V(),X=w(C()),await Mt(t,n,r)}const Pt=[`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 Ft=[`24h`,`7d`,`30d`,`all`];function It(){c.stdout.write(`Usage: happenin query [options]
670
708
 
671
709
  Options:
672
710
  --source <source> filter by source (cursor, claude, ...)
@@ -681,7 +719,7 @@ Options:
681
719
  --format <format> output format: json (default), jsonl, summary
682
720
  --db <path> database path (default: HAPPENIN_DB or ~/.happenin/happenin.db)
683
721
  -h, --help show this help
684
- `)}function Pt(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`:Mt.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`:jt.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`:Mt.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`:jt.includes(a)&&(n=a);break;case`db`:r=a;break}}}}return{options:{range:`24h`,...t},format:n,dbPath:r,help:i}}async function Ft(e,t,n,r=!1){let{options:i,format:a,dbPath:o,help:s}=Pt(e);if(s){t();return}r||(delete i.status,delete i.minDuration,delete i.maxDuration);let c=C(o);try{await n(c,i,a)}finally{c.close()}}async function It(e){await Ft(e,Nt,(e,t,n)=>{if(n===`summary`){let n=Te(e,t);c.stdout.write(`${JSON.stringify(n,null,2)}\n`);return}let r={total:k(e,t),rows:O(e,t).map(rt)};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 Lt(){c.stdout.write(`Usage: happenin sessions [options]
722
+ `)}function Lt(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`:Ft.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`:Pt.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`:Ft.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`:Pt.includes(a)&&(n=a);break;case`db`:r=a;break}}}}return{options:{range:`24h`,...t},format:n,dbPath:r,help:i}}async function Rt(e,t,n,r=!1){let{options:i,format:a,dbPath:o,help:s}=Lt(e);if(s){t();return}r||(delete i.status,delete i.minDuration,delete i.maxDuration);let c=w(o);try{await n(c,i,a)}finally{c.close()}}async function zt(e){await Rt(e,It,(e,t,n)=>{if(n===`summary`){let n=we(e,t);c.stdout.write(`${JSON.stringify(n,null,2)}\n`);return}let r={total:A(e,t),rows:k(e,t).map(ot)};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 Bt(){c.stdout.write(`Usage: happenin sessions [options]
685
723
 
686
724
  Options:
687
725
  --source <source> filter by source (cursor, claude, ...)
@@ -699,8 +737,8 @@ Options:
699
737
  --format <format> output format: json (default), jsonl, summary
700
738
  --db <path> database path (default: HAPPENIN_DB or ~/.happenin/happenin.db)
701
739
  -h, --help show this help
702
- `)}function Rt(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(`,`)||`-`;return`${n}\t${r}\t${i}\t${t}\t${e.eventCount} events\tproject:${a}\ttools:${o}\tfailures:${e.failureCount}`}).join(`
703
- `)}async function zt(e){await Ft(e,Lt,(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(`${Rt(r)}\n`);return}c.stdout.write(`${JSON.stringify(r,null,2)}\n`)},!0)}function Bt(){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 Vt=Bt(),Ht=s.join(Vt,`assets`,`help.md`),Ut=s.join(Vt,`package.json`);function Wt(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 Gt(){let e;try{e=n(Ht,`utf8`)}catch{e=`# happenin
740
+ `)}function Vt(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(`,`)||`-`;return`${n}\t${r}\t${i}\t${t}\t${e.eventCount} events\tproject:${a}\ttools:${o}\tfailures:${e.failureCount}`}).join(`
741
+ `)}async function Ht(e){await Rt(e,Bt,(e,t,n)=>{let r=M(e,t);if(n===`jsonl`){for(let e of r)c.stdout.write(`${JSON.stringify(e)}\n`);return}if(n===`summary`){c.stdout.write(`${Vt(r)}\n`);return}c.stdout.write(`${JSON.stringify(r,null,2)}\n`)},!0)}function Ut(){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 Wt=Ut(),Gt=s.join(Wt,`assets`,`help.md`),Kt=s.join(Wt,`package.json`);function qt(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 Jt(){let e;try{e=n(Gt,`utf8`)}catch{e=`# happenin
704
742
 
705
743
  Run \`happenin <command>\`.
706
- `}c.stdout.write(`\n${Wt(e)}\n`)}function Kt(){let e=`0.0.0`;try{let t=n(Ut,`utf8`);e=JSON.parse(t).version??e}catch{}c.stdout.write(`${e}\n`)}async function qt(){let[e,...t]=c.argv.slice(2);if(!e||e===`--help`||e===`-h`){Gt();return}if(e===`--version`||e===`-v`){Kt();return}if(e===`install`){await _e(t);return}if(e===`record`){await Re(t);return}if(e===`import`){await B(t);return}if(e===`dashboard`){await At(t);return}if(e===`query`){await It(t);return}if(e===`sessions`){await zt(t);return}c.stderr.write(`Unknown command: ${e}\n`),Gt(),c.exitCode=1}await qt();export{};
744
+ `}c.stdout.write(`\n${qt(e)}\n`)}function Yt(){let e=`0.0.0`;try{let t=n(Kt,`utf8`);e=JSON.parse(t).version??e}catch{}c.stdout.write(`${e}\n`)}async function Xt(){let[e,...t]=c.argv.slice(2);if(!e||e===`--help`||e===`-h`){Jt();return}if(e===`--version`||e===`-v`){Yt();return}if(e===`install`){await ge(t);return}if(e===`record`){await ze(t);return}if(e===`import`){await V(t);return}if(e===`dashboard`){await Nt(t);return}if(e===`query`){await zt(t);return}if(e===`sessions`){await Ht(t);return}c.stderr.write(`Unknown command: ${e}\n`),Jt(),c.exitCode=1}await Xt();export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happenin",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "A macOS 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",