happenin 0.2.0 → 0.4.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.
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),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(`
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(`
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=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(`
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(`
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}},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 (
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 (
68
68
  id INTEGER PRIMARY KEY AUTOINCREMENT,
69
69
  source TEXT NOT NULL,
70
70
  client TEXT,
@@ -75,6 +75,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
75
75
  project_path TEXT,
76
76
  file_path TEXT,
77
77
  tool_name TEXT,
78
+ skill_name TEXT,
78
79
  payload TEXT NOT NULL,
79
80
  source_path TEXT,
80
81
  subagent_id TEXT,
@@ -86,7 +87,7 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
86
87
  mtime INTEGER NOT NULL,
87
88
  imported_at INTEGER NOT NULL
88
89
  );
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
+ `),_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
91
  INSERT INTO events (
91
92
  source,
92
93
  client,
@@ -97,13 +98,14 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
97
98
  project_path,
98
99
  file_path,
99
100
  tool_name,
101
+ skill_name,
100
102
  payload,
101
103
  source_path,
102
104
  subagent_id,
103
105
  subagent_type,
104
106
  transcript_path
105
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
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
+ ) 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=`
107
109
  SELECT
108
110
  id,
109
111
  source,
@@ -115,13 +117,14 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
115
117
  project_path AS projectPath,
116
118
  file_path AS filePath,
117
119
  tool_name AS toolName,
120
+ skill_name AS skillName,
118
121
  payload,
119
122
  source_path AS sourcePath,
120
123
  subagent_id AS subagentId,
121
124
  subagent_type AS subagentType,
122
125
  transcript_path AS transcriptPath
123
126
  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)},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=`
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=`
125
128
  SELECT
126
129
  session_id AS sessionId,
127
130
  COUNT(*) AS eventCount,
@@ -131,13 +134,38 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
131
134
  MAX(happened_at) AS lastAt,
132
135
  COALESCE(json_group_array(DISTINCT project_path ORDER BY project_path) FILTER (WHERE project_path IS NOT NULL AND project_path <> ''), '[]') AS projectPaths,
133
136
  COALESCE(json_group_array(DISTINCT tool_name ORDER BY tool_name) FILTER (WHERE tool_name IS NOT NULL AND tool_name <> ''), '[]') AS toolNames,
137
+ COALESCE(json_group_array(DISTINCT skill_name ORDER BY skill_name) FILTER (WHERE skill_name IS NOT NULL AND skill_name <> ''), '[]') AS skillNames,
138
+ COALESCE(json_group_array(DISTINCT file_path ORDER BY file_path) FILTER (WHERE file_path IS NOT NULL AND file_path <> ''), '[]') AS filePaths,
134
139
  SUM(CASE WHEN event LIKE '%Failure%' THEN 1 ELSE 0 END) AS failureCount
135
140
  FROM events
136
141
  ${r}
137
142
  GROUP BY session_id
138
143
  ORDER BY lastReceivedAt DESC, sessionId
139
144
  LIMIT ?${o>0?` OFFSET ?`:``}
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=`
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=`
146
+ CASE
147
+ WHEN tool_name LIKE 'mcp\\_\\_%' ESCAPE '\\' THEN 'mcpServers'
148
+ WHEN file_path IS NOT NULL AND file_path <> '' AND LOWER(file_path) LIKE '%.md' THEN 'mdFiles'
149
+ WHEN skill_name IS NOT NULL AND skill_name <> '' THEN 'bloatware'
150
+ WHEN tool_name IS NOT NULL AND tool_name <> '' THEN 'actualValue'
151
+ WHEN event IN ('user', 'assistant', 'prompt') THEN 'actualValue'
152
+ ELSE 'bloatware'
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=`
155
+ SELECT
156
+ ${Pe} AS bucket,
157
+ SUM(LENGTH(payload)) AS bytes,
158
+ SUM(
159
+ CASE WHEN json_valid(payload) THEN
160
+ COALESCE(json_extract(payload, '\$.message.usage.input_tokens'), 0) +
161
+ COALESCE(json_extract(payload, '\$.message.usage.cache_creation_input_tokens'), 0) +
162
+ COALESCE(json_extract(payload, '\$.message.usage.cache_read_input_tokens'), 0)
163
+ ELSE 0 END
164
+ ) AS tokens
165
+ FROM events
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=`
141
169
  SELECT
142
170
  session_id AS sessionId,
143
171
  subagent_id AS subagentId,
@@ -149,18 +177,29 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
149
177
  MAX(happened_at) AS lastAt,
150
178
  COALESCE(json_group_array(DISTINCT project_path ORDER BY project_path) FILTER (WHERE project_path IS NOT NULL AND project_path <> ''), '[]') AS projectPaths,
151
179
  COALESCE(json_group_array(DISTINCT tool_name ORDER BY tool_name) FILTER (WHERE tool_name IS NOT NULL AND tool_name <> ''), '[]') AS toolNames,
180
+ COALESCE(json_group_array(DISTINCT skill_name ORDER BY skill_name) FILTER (WHERE skill_name IS NOT NULL AND skill_name <> ''), '[]') AS skillNames,
181
+ COALESCE(json_group_array(DISTINCT file_path ORDER BY file_path) FILTER (WHERE file_path IS NOT NULL AND file_path <> ''), '[]') AS filePaths,
152
182
  SUM(CASE WHEN event LIKE '%Failure%' THEN 1 ELSE 0 END) AS failureCount
153
183
  FROM events
154
184
  WHERE subagent_id IS NOT NULL AND subagent_id <> '' AND session_id IN (${n.map(()=>`?`).join(`,`)})${o}
155
185
  GROUP BY session_id, subagent_id
156
186
  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(`
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(`
188
+ SELECT DISTINCT server FROM (
189
+ SELECT substr(tool_name, 6, instr(substr(tool_name, 6), '__') - 1) AS server
190
+ FROM events
191
+ WHERE tool_name LIKE 'mcp\\_\\_%' ESCAPE '\\'
192
+ AND instr(substr(tool_name, 6), '__') > 0
193
+ )
194
+ WHERE server <> ''
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(`
158
197
  INSERT INTO imports (path, mtime, imported_at)
159
198
  VALUES (?, ?, ?)
160
199
  ON CONFLICT(path) DO UPDATE SET
161
200
  mtime = excluded.mtime,
162
201
  imported_at = excluded.imported_at
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">
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">
164
203
  <div class="metric-card metric-sessions">
165
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>
166
205
  <div class="metric-label">Total Sessions</div>
@@ -174,81 +213,100 @@ import{existsSync as e,mkdirSync as t,readFileSync as n,readdirSync as r,renameS
174
213
  <div class="metric-card metric-duration">
175
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>
176
215
  <div class="metric-label">Average Duration</div>
177
- <div class="metric-value">${Qe(e.averageDurationMs)}</div>
216
+ <div class="metric-value">${_t(e.averageDurationMs)}</div>
178
217
  </div>
179
218
  <div class="metric-card metric-success">
180
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>
181
220
  <div class="metric-label">Success Rate</div>
182
221
  <div class="metric-value">${t}</div>
183
222
  </div>
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}${_}>
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">
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>
230
+ </div>`}).join(``);return`<div class="context-bar-block">
231
+ <h3 class="context-bar-title">${z(e)}</h3>
232
+ <div class="context-bar">${a}</div>
233
+ <div class="context-legend">${o}</div>
234
+ </div>`}function kt(e){return`<div class="chart-panel context-breakdown">
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}${_}>
185
238
  ${o}
186
- <div class="session-main"${ee}>
239
+ <div class="session-main"${v}>
187
240
  <div class="session-row">
188
- <span class="session-id" title="${U(l)}">${u}${ie}</span>
241
+ <span class="session-id" title="${B(l)}">${u}${te}</span>
189
242
  <span class="status-badge status-${c}">${c}</span>
190
243
  </div>
191
244
  <div class="session-meta"><span>${f}</span><span>${e.eventCount} events</span><span>${d}</span></div>
192
245
  </div>
193
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>
194
247
  ${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">
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">
197
249
  <div><span>Time</span><span>${t}</span></div>
198
250
  <div><span>Source</span><span>${n}</span></div>
199
251
  <div><span>Client</span><span>${r||`-`}</span></div>
200
252
  <div><span>Event</span><span>${i}</span></div>
201
253
  <div><span>Tool</span><span>${a||`-`}</span></div>
202
254
  <div><span>Project</span><span>${c||`-`}</span></div>
203
- ${u}
204
255
  ${d}
205
- </div>`}function ut(e){return`<div class="detail-json">
256
+ ${f}
257
+ ${p}
258
+ </div>`}function Rt(e){return`<div class="detail-json">
206
259
  <details class="json-toggle">
207
260
  <summary>JSON</summary>
208
- <pre>${H(JSON.stringify(ot(e),null,2))}</pre>
261
+ <pre>${z(JSON.stringify(Nt(e),null,2))}</pre>
209
262
  </details>
210
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>
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)}
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)}
215
268
  </div>
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>
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>
218
271
  <div class="detail-body">
219
- ${lt(n)}
220
- ${ut(n)}
272
+ ${Lt(n)}
273
+ ${Rt(n)}
221
274
  <div class="detail-children">${r}</div>
222
275
  </div>
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??``)}">
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??``)}">
224
277
  <div class="detail-header">
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>
226
- <h2 class="detail-title">Session Details - ${H(a)}</h2>
227
- <span class="detail-count">${n??i.length} events</span>
278
+ ${Pt()}
279
+ <h2 class="detail-title">Session Details - ${z(s)}</h2>
280
+ <span class="detail-count">${n??o.length} events</span>
228
281
  </div>
229
- ${c}
282
+ ${zt(i)}
283
+ ${a?kt(a):``}
284
+ ${u}
230
285
  <div class="detail-toolbar">
231
286
  <input type="search" class="detail-search" placeholder="Search attributes..." oninput="filterSessionDetails(this.value)">
232
287
  <button type="button" onclick="toggleSessionDetails(false)">Collapse All</button>
233
288
  <button type="button" onclick="toggleSessionDetails(true)">Expand All</button>
234
289
  </div>
235
- <div class="detail-events">${o}</div>
290
+ <div class="detail-events">${c}</div>
236
291
  <div class="detail-actions">
237
292
  <button type="button" onclick="copySessionJson()">Copy JSON</button>
238
293
  </div>
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">
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">
296
+ <div class="detail-header">
297
+ ${Pt()}
298
+ <h2 class="session-list-title">Sessions <span class="session-collapse-count">${r.length}</span></h2>
299
+ </div>
300
+ ${l}
241
301
  <div class="session-list-wrapper">
242
- ${it(e.slice(o,o+a),n,r,i,t)}
243
- ${vt(t,e.length)}
302
+ ${jt(o,n,void 0,void 0,t)}
303
+ ${Jt(t,r.length)}
244
304
  </div>
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)}
246
- <div class="main-content">
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">
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">
252
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>
253
311
  <h1 class="page-title">Session Overview Analytics</h1>
254
312
  <div class="header-controls">
@@ -256,17 +314,32 @@ ${vt(t,e.length)}
256
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>
257
315
  <input type="text" name="q" class="search" placeholder="session or payload" value="${t}" autocomplete="off">
258
316
  </div>
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>
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>
260
318
  <button type="button" class="theme" data-theme-toggle title="Toggle light/dark theme" aria-label="toggle theme">🌙</button>
261
319
  </div>
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>
264
- <label>source ${Y(`source`,e.sources,r)}</label>
265
- <label>tool ${Y(`tool`,e.tools,i)}</label>
266
- <label>event ${Y(`event`,e.events,a)}</label>
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>
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)}">
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">
323
+ <button type="button" id="filters-toggle" class="toggle-pill" aria-expanded="false" aria-controls="filter-panel" onclick="toggleFilters()">
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
+ </button>
326
+ <div class="view-tabs" role="tablist">
327
+ <button type="button" id="tab-overview" class="view-tab" role="tab" aria-selected="${f?`false`:`true`}" onclick="switchView('overview')">Overview</button>
328
+ <button type="button" id="tab-sessions" class="view-tab" role="tab" aria-selected="${f?`true`:`false`}" onclick="switchView('list')">Sessions<span class="toggle-badge" id="sessions-tab-count" hidden></span></button>
329
+ </div>
330
+ </div>
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>
270
343
  <html lang="en" hx-ext="sse">
271
344
  <head>
272
345
  <meta charset="utf-8">
@@ -364,7 +437,14 @@ ${vt(t,e.length)}
364
437
  if (!form) return false;
365
438
  for (const [name, value] of Object.entries(values)) {
366
439
  const field = form.elements[name];
367
- if (field && value != null && value !== '') {
440
+ if (!field) continue;
441
+ if (Array.isArray(value)) {
442
+ if (field.multiple && field.options) {
443
+ for (const option of field.options) {
444
+ option.selected = value.includes(option.value);
445
+ }
446
+ }
447
+ } else if (value != null && value !== '') {
368
448
  field.value = String(value);
369
449
  }
370
450
  }
@@ -373,23 +453,270 @@ ${vt(t,e.length)}
373
453
  return false;
374
454
  }
375
455
 
456
+ function buildMultiSelect(wrapper) {
457
+ const select = wrapper.querySelector('select.ms-native');
458
+ if (!select) return;
459
+ const allLabel = wrapper.getAttribute('data-ms-all') || 'All';
460
+ const noun = allLabel.replace(/^All /i, '').toLowerCase();
461
+
462
+ const pills = document.createElement('div');
463
+ pills.className = 'ms-pills';
464
+ const searchWrap = document.createElement('div');
465
+ searchWrap.className = 'ms-search-wrap';
466
+ const input = document.createElement('input');
467
+ input.type = 'text';
468
+ input.className = 'ms-search';
469
+ input.placeholder = 'Search ' + noun + '...';
470
+ input.autocomplete = 'off';
471
+ const dropdown = document.createElement('div');
472
+ dropdown.className = 'ms-dropdown';
473
+ dropdown.hidden = true;
474
+ searchWrap.appendChild(input);
475
+ searchWrap.appendChild(dropdown);
476
+ wrapper.insertBefore(pills, select);
477
+ wrapper.insertBefore(searchWrap, select);
478
+ wrapper.classList.add('enhanced');
479
+
480
+ function optionsList() {
481
+ return Array.from(select.options);
482
+ }
483
+
484
+ function renderPills() {
485
+ pills.innerHTML = '';
486
+ const selected = optionsList().filter((o) => o.selected);
487
+ if (selected.length === 0) {
488
+ const span = document.createElement('span');
489
+ span.className = 'ms-pill ms-pill--all';
490
+ span.textContent = allLabel;
491
+ pills.appendChild(span);
492
+ return;
493
+ }
494
+ for (const opt of selected) {
495
+ const label = opt.dataset.label || opt.textContent;
496
+ const pill = document.createElement('span');
497
+ pill.className = 'ms-pill';
498
+ const text = document.createElement('span');
499
+ text.textContent = label;
500
+ text.title = label;
501
+ const remove = document.createElement('button');
502
+ remove.type = 'button';
503
+ remove.setAttribute('aria-label', 'Remove ' + label);
504
+ remove.textContent = String.fromCharCode(215);
505
+ remove.addEventListener('click', (e) => {
506
+ e.stopPropagation();
507
+ opt.selected = false;
508
+ select.dispatchEvent(new Event('change', { bubbles: true }));
509
+ renderPills();
510
+ renderDropdown();
511
+ });
512
+ pill.appendChild(text);
513
+ pill.appendChild(remove);
514
+ pills.appendChild(pill);
515
+ }
516
+ }
517
+
518
+ function renderDropdown() {
519
+ const term = input.value.trim().toLowerCase();
520
+ const unselected = optionsList().filter((o) => !o.selected);
521
+ const matches = term
522
+ ? unselected.filter((o) => (o.dataset.label || o.textContent).toLowerCase().includes(term))
523
+ : unselected;
524
+ dropdown.innerHTML = '';
525
+ if (matches.length === 0) {
526
+ const empty = document.createElement('div');
527
+ empty.className = 'ms-empty';
528
+ empty.textContent = unselected.length === 0 ? 'All options selected' : 'No matches';
529
+ dropdown.appendChild(empty);
530
+ return;
531
+ }
532
+ for (const opt of matches.slice(0, 50)) {
533
+ const item = document.createElement('div');
534
+ item.className = 'ms-option';
535
+ item.textContent = opt.dataset.label || opt.textContent;
536
+ item.addEventListener('mousedown', (e) => {
537
+ e.preventDefault();
538
+ opt.selected = true;
539
+ select.dispatchEvent(new Event('change', { bubbles: true }));
540
+ input.value = '';
541
+ renderPills();
542
+ renderDropdown();
543
+ });
544
+ dropdown.appendChild(item);
545
+ }
546
+ }
547
+
548
+ input.addEventListener('focus', () => {
549
+ renderDropdown();
550
+ dropdown.hidden = false;
551
+ });
552
+ input.addEventListener('input', () => {
553
+ renderDropdown();
554
+ dropdown.hidden = false;
555
+ });
556
+ input.addEventListener('blur', () => {
557
+ setTimeout(() => {
558
+ dropdown.hidden = true;
559
+ }, 150);
560
+ });
561
+ input.addEventListener('keydown', (e) => {
562
+ if (e.key === 'Escape') {
563
+ dropdown.hidden = true;
564
+ input.blur();
565
+ }
566
+ if (e.key === 'Enter') {
567
+ e.preventDefault();
568
+ const first = dropdown.querySelector('.ms-option');
569
+ if (first) first.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));
570
+ }
571
+ });
572
+
573
+ select.msRefresh = () => {
574
+ renderPills();
575
+ renderDropdown();
576
+ };
577
+ renderPills();
578
+ renderDropdown();
579
+ }
580
+
581
+ function initMultiSelects() {
582
+ for (const wrapper of document.querySelectorAll('.ms')) {
583
+ buildMultiSelect(wrapper);
584
+ }
585
+ }
586
+
587
+ function refreshMultiSelects() {
588
+ for (const select of document.querySelectorAll('select.ms-native')) {
589
+ if (select.msRefresh) select.msRefresh();
590
+ }
591
+ }
592
+
593
+ function getViewField() {
594
+ const form = document.querySelector('[data-filter-form]');
595
+ return form ? form.elements['view'] : null;
596
+ }
597
+
598
+ function getActiveView() {
599
+ const field = getViewField();
600
+ return field && field.value === 'list' ? 'list' : 'overview';
601
+ }
602
+
603
+ function updateViewTabs() {
604
+ const view = getActiveView();
605
+ const overviewTab = document.getElementById('tab-overview');
606
+ const sessionsTab = document.getElementById('tab-sessions');
607
+ if (overviewTab) overviewTab.setAttribute('aria-selected', String(view === 'overview'));
608
+ if (sessionsTab) sessionsTab.setAttribute('aria-selected', String(view === 'list'));
609
+ }
610
+
611
+ function requestSessionsFragment() {
612
+ const form = document.querySelector('[data-filter-form]');
613
+ const content = document.getElementById('dashboard-content');
614
+ if (!form || !content || typeof htmx === 'undefined') return;
615
+ const params = new URLSearchParams();
616
+ for (const [key, value] of collectFormEntries(form)) {
617
+ params.append(key, value);
618
+ }
619
+ const url = '/fragments/sessions' + (params.size > 0 ? '?' + params.toString() : '');
620
+ content.setAttribute('hx-get', url);
621
+ htmx.ajax('GET', url, { target: content, swap: 'innerHTML' });
622
+ }
623
+
624
+ function switchView(view) {
625
+ const form = document.querySelector('[data-filter-form]');
626
+ const field = getViewField();
627
+ if (field) field.value = view === 'list' ? 'list' : '';
628
+ updateViewTabs();
629
+ if (form) {
630
+ saveFilters(form);
631
+ updateFiltersBadge();
632
+ }
633
+ requestSessionsFragment();
634
+ }
635
+
636
+ function syncViewCounts() {
637
+ const content = document.getElementById('dashboard-content');
638
+ const badge = document.getElementById('sessions-tab-count');
639
+ if (!content || !badge) return;
640
+ const countEl =
641
+ content.querySelector('.metric-sessions .metric-value') ||
642
+ content.querySelector('.session-collapse-count');
643
+ if (countEl) {
644
+ badge.textContent = countEl.textContent;
645
+ badge.hidden = false;
646
+ } else {
647
+ badge.hidden = true;
648
+ }
649
+ }
650
+
651
+ function countActiveFilters() {
652
+ const panel = document.getElementById('filter-panel');
653
+ if (!panel) return 0;
654
+ let count = 0;
655
+ for (const el of panel.querySelectorAll('select, input')) {
656
+ if (el.multiple) count += el.selectedOptions.length;
657
+ else if (el.value) count += 1;
658
+ }
659
+ return count;
660
+ }
661
+
662
+ function updateFiltersBadge() {
663
+ const badge = document.getElementById('filters-badge');
664
+ if (!badge) return;
665
+ const count = countActiveFilters();
666
+ badge.textContent = String(count);
667
+ badge.hidden = count === 0;
668
+ }
669
+
670
+ const FILTERS_OPEN_KEY = 'happenin-filters-open';
671
+
672
+ function toggleFilters() {
673
+ const btn = document.getElementById('filters-toggle');
674
+ const panel = document.getElementById('filter-panel');
675
+ if (!btn || !panel) return;
676
+ const next = btn.getAttribute('aria-expanded') !== 'true';
677
+ btn.setAttribute('aria-expanded', String(next));
678
+ panel.classList.toggle('open', next);
679
+ setItem(FILTERS_OPEN_KEY, next ? '1' : '');
680
+ }
681
+
682
+ function initFilterPanel() {
683
+ const btn = document.getElementById('filters-toggle');
684
+ const panel = document.getElementById('filter-panel');
685
+ if (!btn || !panel) return;
686
+ const open = getItem(FILTERS_OPEN_KEY) === '1';
687
+ btn.setAttribute('aria-expanded', String(open));
688
+ panel.classList.toggle('open', open);
689
+ updateFiltersBadge();
690
+ }
691
+
692
+ function collectFormEntries(form) {
693
+ const data = new FormData(form);
694
+ const entries = [];
695
+ for (const [key, value] of data.entries()) {
696
+ if (value) entries.push([key, String(value)]);
697
+ }
698
+ return entries;
699
+ }
700
+
376
701
  function updateContentUrl() {
377
702
  const form = document.querySelector('[data-filter-form]');
378
703
  const content = document.getElementById('dashboard-content');
379
704
  if (!form || !content) return;
380
- const data = new FormData(form);
381
705
  const params = new URLSearchParams();
382
- for (const [key, value] of data.entries()) {
383
- if (value) params.set(key, String(value));
706
+ for (const [key, value] of collectFormEntries(form)) {
707
+ params.append(key, value);
384
708
  }
385
709
  content.setAttribute('hx-get', '/fragments/sessions' + (params.size > 0 ? '?' + params.toString() : ''));
386
710
  }
387
711
 
388
712
  function saveFilters(form) {
389
- const data = new FormData(form);
390
713
  const filters = {};
391
- for (const [key, value] of data.entries()) {
392
- if (value) filters[key] = String(value);
714
+ for (const [key, value] of collectFormEntries(form)) {
715
+ if (key in filters) {
716
+ filters[key] = Array.isArray(filters[key]) ? [...filters[key], value] : [filters[key], value];
717
+ } else {
718
+ filters[key] = value;
719
+ }
393
720
  }
394
721
  setItem(FILTER_KEY, JSON.stringify(filters));
395
722
  }
@@ -398,15 +725,23 @@ ${vt(t,e.length)}
398
725
  const form = document.querySelector('[data-filter-form]');
399
726
  const content = document.getElementById('dashboard-content');
400
727
  if (!form || !content) return;
401
- const hadSaved = loadFilters();
402
- form.addEventListener('change', () => saveFilters(form));
728
+ initMultiSelects();
729
+ const hadSaved = window.location.search ? false : loadFilters();
730
+ refreshMultiSelects();
731
+ form.addEventListener('change', () => {
732
+ saveFilters(form);
733
+ updateFiltersBadge();
734
+ updateViewTabs();
735
+ refreshMultiSelects();
736
+ });
403
737
  form.addEventListener('input', (event) => {
404
738
  if (event.target.classList.contains('search')) saveFilters(form);
405
739
  });
406
740
  updateContentUrl();
741
+ initFilterPanel();
407
742
  if (hadSaved) {
408
743
  setTimeout(() => {
409
- if (typeof htmx !== 'undefined') htmx.trigger(form, 'change');
744
+ requestSessionsFragment();
410
745
  }, 0);
411
746
  }
412
747
  }
@@ -441,8 +776,7 @@ ${vt(t,e.length)}
441
776
  }
442
777
 
443
778
  function backToDashboard() {
444
- const form = document.querySelector('[data-filter-form]');
445
- if (form && typeof htmx !== 'undefined') htmx.trigger(form, 'change');
779
+ requestSessionsFragment();
446
780
  }
447
781
 
448
782
  function syncDetailState() {
@@ -450,28 +784,21 @@ ${vt(t,e.length)}
450
784
  if (!content) return;
451
785
  const detail = content.querySelector('.session-detail-view');
452
786
  document.body.classList.toggle('session-detail-open', !!detail);
453
- const sessionId = detail ? detail.getAttribute('data-session') : null;
454
- const subagentId = detail ? detail.getAttribute('data-subagent') || null : null;
455
- for (const item of content.querySelectorAll('.session-item')) {
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);
460
- }
461
- const list = content.querySelector('.session-list');
462
- if (list) {
463
- list.scrollTop = sidebarScroll;
464
- const active = content.querySelector('.session-item.active');
465
- if (active) active.scrollIntoView({ block: 'nearest' });
466
- }
467
787
  }
468
788
 
469
- let sidebarScroll = 0;
789
+ function syncSessionsListState() {
790
+ const content = document.getElementById('dashboard-content');
791
+ if (!content) return;
792
+ const isList = !!content.querySelector('.sessions-list-view');
793
+ document.body.classList.toggle('sessions-list-open', isList);
794
+ }
795
+
470
796
  document.addEventListener('htmx:beforeSwap', (e) => {
471
- const list = document.querySelector('.session-list');
472
- if (list) sidebarScroll = list.scrollTop;
473
797
  const elt = e.detail.requestConfig ? e.detail.requestConfig.elt : null;
474
- if (elt && elt.id === 'dashboard-content' && document.body.classList.contains('session-detail-open')) {
798
+ const blocked =
799
+ document.body.classList.contains('session-detail-open') ||
800
+ document.body.classList.contains('sessions-list-open');
801
+ if (elt && elt.id === 'dashboard-content' && blocked) {
475
802
  e.detail.shouldSwap = false;
476
803
  }
477
804
  });
@@ -481,20 +808,34 @@ ${vt(t,e.length)}
481
808
  window.copySessionJson = copySessionJson;
482
809
  window.copyEventJson = copyEventJson;
483
810
  window.backToDashboard = backToDashboard;
811
+ window.switchView = switchView;
812
+ window.toggleFilters = toggleFilters;
484
813
 
485
814
  document.addEventListener('htmx:afterSettle', () => {
486
815
  updateContentUrl();
487
816
  syncDetailState();
817
+ syncSessionsListState();
818
+ updateViewTabs();
819
+ syncViewCounts();
488
820
  });
489
821
 
822
+ function initHistoryState() {
823
+ syncDetailState();
824
+ syncSessionsListState();
825
+ updateViewTabs();
826
+ syncViewCounts();
827
+ }
828
+
490
829
  applyTheme(getTheme());
491
830
 
492
831
  if (document.readyState === 'loading') {
493
832
  document.addEventListener('DOMContentLoaded', initTheme);
494
833
  document.addEventListener('DOMContentLoaded', initFilters);
834
+ document.addEventListener('DOMContentLoaded', initHistoryState);
495
835
  } else {
496
836
  initTheme();
497
837
  initFilters();
838
+ initHistoryState();
498
839
  }
499
840
  })();
500
841
  <\/script>
@@ -518,6 +859,8 @@ ${vt(t,e.length)}
518
859
  --success: #22c55e;
519
860
  --warning: #eab308;
520
861
  --danger: #ef4444;
862
+ --shadow: 0 1px 2px rgba(0,0,0,0.3), 0 4px 12px rgba(0,0,0,0.22);
863
+ --shadow-sm: 0 1px 2px rgba(0,0,0,0.28);
521
864
  }
522
865
  [data-theme="light"] {
523
866
  color-scheme: light;
@@ -538,34 +881,78 @@ ${vt(t,e.length)}
538
881
  --success: #16a34a;
539
882
  --warning: #ca8a04;
540
883
  --danger: #dc2626;
884
+ --shadow: 0 1px 2px rgba(15,23,42,0.04), 0 4px 12px rgba(15,23,42,0.06);
885
+ --shadow-sm: 0 1px 2px rgba(15,23,42,0.05);
541
886
  }
542
887
  * { box-sizing: border-box; }
543
- html, body { height: 100%; }
888
+ html, body { min-height: 100%; }
544
889
  body { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: var(--bg); color: var(--text); margin: 0; padding: 0; line-height: 1.5; }
545
- .app { display: flex; flex-direction: column; height: 100%; max-width: 1400px; margin: 0 auto; padding: 1.5rem; gap: 1.25rem; }
546
- .sticky-bar { flex: 0 0 auto; display: flex; flex-direction: column; gap: 1rem; background: var(--bg); position: sticky; top: 0; z-index: 10; }
547
- .app-header { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; padding: 0.75rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 0.5rem; }
548
- .brand { font-weight: 800; font-size: 1.25rem; display: flex; align-items: center; gap: 0.5rem; color: var(--text); }
890
+ .app { display: flex; flex-direction: column; min-height: 100%; max-width: 1400px; margin: 0 auto; padding: 1.75rem; gap: 1.5rem; }
891
+ .sticky-bar { flex: 0 0 auto; display: flex; flex-direction: column; gap: 0.85rem; background: var(--bg); position: sticky; top: 0; z-index: 10; padding-bottom: 0.25rem; }
892
+ .app-header { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; padding: 0.85rem 1.1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 0.85rem; box-shadow: var(--shadow-sm); }
893
+ .brand { font-weight: 800; font-size: 1.3rem; display: flex; align-items: center; gap: 0.5rem; color: var(--text); background: linear-gradient(135deg, var(--text), var(--text) 40%, var(--accent)); -webkit-background-clip: text; background-clip: text; }
549
894
  .brand-icon { color: var(--accent); }
550
- .page-title { font-size: 1.1rem; font-weight: 600; margin: 0; color: var(--text); }
895
+ .page-title { font-size: 1.05rem; font-weight: 600; margin: 0; color: var(--text-2); }
551
896
  .header-controls { display: flex; gap: 0.75rem; align-items: end; }
552
897
  .search-wrap { position: relative; display: flex; align-items: center; }
553
898
  .search-icon { position: absolute; left: 0.6rem; color: var(--text-4); pointer-events: none; }
554
899
  input.search { padding-left: 2rem; min-width: 14rem; }
555
- .filter-bar { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: end; padding: 0.75rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 0.5rem; }
556
- form { margin: 0; display: flex; flex-direction: column; gap: 1rem; }
900
+ form { margin: 0; display: flex; flex-direction: column; gap: 0.85rem; }
557
901
  label { display: flex; flex-direction: column; font-size: 0.75rem; gap: 0.25rem; color: var(--text-2); }
558
- input, select, button { background: var(--surface-2); color: var(--text); border: 1px solid var(--border); border-radius: 0.4rem; padding: 0.45rem 0.7rem; font-size: 0.85rem; transition: border-color 0.15s, background 0.15s; }
902
+ input, select, button { background: var(--surface-2); color: var(--text); border: 1px solid var(--border); border-radius: 0.4rem; padding: 0.45rem 0.7rem; font-size: 0.85rem; transition: border-color 0.15s, background 0.15s, transform 0.1s; }
559
903
  input:focus, select:focus, button:focus { outline: none; border-color: var(--accent); }
560
904
  select { min-width: 7.5rem; cursor: pointer; }
905
+ select[multiple] { min-width: 10rem; padding: 0.3rem; cursor: default; }
906
+ select[multiple] option { padding: 0.15rem 0.4rem; border-radius: 0.25rem; }
907
+ select[multiple] option:checked { background: var(--accent); color: var(--bg); }
561
908
  button { cursor: pointer; background: var(--surface-4); font-weight: 500; }
562
909
  button:hover { background: var(--surface-5); }
910
+ button:active { transform: scale(0.97); }
563
911
  .theme { display: inline-flex; align-items: center; justify-content: center; font-size: 1.25rem; padding: 0.35rem 0.6rem; line-height: 1; }
564
- .dashboard-layout { flex: 1 1 auto; display: grid; grid-template-columns: 22rem 1fr; gap: 1.25rem; min-height: 0; overflow: hidden; }
565
- .session-sidebar { display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--border); border-radius: 0.5rem; overflow: hidden; min-height: 0; }
566
- .session-list-wrapper { flex: 1 1 auto; display: flex; flex-direction: column; overflow: hidden; min-height: 0; }
567
- .session-list-title { padding: 0.75rem 1rem; margin: 0; font-size: 0.9rem; font-weight: 600; color: var(--text-2); border-bottom: 1px solid var(--border); background: var(--surface-2); position: sticky; top: 0; z-index: 2; }
568
- .session-list { flex: 1 1 auto; list-style: none; padding: 0; margin: 0; overflow-y: auto; }
912
+ .toolbar-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
913
+ .toggle-pill { display: inline-flex; align-items: center; gap: 0.4rem; background: var(--surface); border: 1px solid var(--border); border-radius: 999px; padding: 0.5rem 1rem; font-size: 0.85rem; font-weight: 600; color: var(--text-2); box-shadow: var(--shadow-sm); }
914
+ .toggle-pill:hover { background: var(--surface-3); color: var(--text); }
915
+ .toggle-pill[aria-expanded="true"] { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); background: color-mix(in srgb, var(--accent) 12%, var(--surface)); }
916
+ .toggle-chevron { transition: transform 0.18s ease; flex: 0 0 auto; }
917
+ .toggle-pill[aria-expanded="true"] .toggle-chevron { transform: rotate(90deg); }
918
+ .toggle-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 1.15rem; height: 1.15rem; padding: 0 0.3rem; border-radius: 999px; background: var(--accent); color: var(--bg); font-size: 0.68rem; font-weight: 700; }
919
+ .view-tabs { display: flex; align-items: center; gap: 0.15rem; background: var(--surface); border: 1px solid var(--border); border-radius: 999px; padding: 0.25rem; box-shadow: var(--shadow-sm); }
920
+ .view-tab { border: none; background: transparent; color: var(--text-2); border-radius: 999px; padding: 0.42rem 1rem; font-size: 0.85rem; font-weight: 600; display: inline-flex; align-items: center; gap: 0.4rem; }
921
+ .view-tab:hover { color: var(--text); background: var(--surface-3); }
922
+ .view-tab[aria-selected="true"] { background: var(--accent); color: var(--bg); }
923
+ .view-tab[aria-selected="true"]:hover { background: var(--accent); }
924
+ .view-tab .toggle-badge { background: color-mix(in srgb, var(--text) 15%, transparent); color: var(--text-2); }
925
+ .view-tab[aria-selected="true"] .toggle-badge { background: rgba(0,0,0,0.22); color: var(--bg); }
926
+ .filter-panel { display: none; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); gap: 1.1rem 1.25rem; align-items: start; padding: 1.15rem 1.25rem; background: var(--surface); border: 1px solid var(--border); border-radius: 0.85rem; box-shadow: var(--shadow-sm); animation: filterPanelIn 0.15s ease; }
927
+ .filter-panel.open { display: grid; }
928
+ .filter-panel .field { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; }
929
+ .filter-panel .field-wide { grid-column: span 2; min-width: 15rem; }
930
+ .filter-panel .field label { display: block; font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-3); }
931
+ .filter-panel .field select, .filter-panel .field input { width: 100%; }
932
+ @keyframes filterPanelIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
933
+ .ms { position: relative; display: flex; flex-direction: column; gap: 0.4rem; }
934
+ .ms select.ms-native { display: block; }
935
+ .ms.enhanced select.ms-native { display: none; }
936
+ .ms-pills { display: flex; flex-wrap: wrap; gap: 0.35rem; min-height: 2.1rem; padding: 0.3rem; background: var(--surface-2); border: 1px solid var(--border); border-radius: 0.5rem; align-items: center; }
937
+ .ms-pill { display: inline-flex; align-items: center; gap: 0.3rem; max-width: 100%; background: color-mix(in srgb, var(--accent) 16%, var(--surface-4)); border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border)); color: var(--text); border-radius: 999px; padding: 0.2rem 0.35rem 0.2rem 0.65rem; font-size: 0.75rem; font-weight: 600; line-height: 1.4; }
938
+ .ms-pill span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 10rem; }
939
+ .ms-pill button { all: unset; display: inline-flex; align-items: center; justify-content: center; width: 1.1rem; height: 1.1rem; border-radius: 50%; color: var(--text-2); font-size: 0.85rem; line-height: 1; cursor: pointer; }
940
+ .ms-pill button:hover { background: rgba(0,0,0,0.18); color: var(--text); }
941
+ .ms-pill--all { background: transparent; border-style: dashed; border-color: var(--border); color: var(--text-4); font-weight: 500; font-style: italic; }
942
+ .ms-search-wrap { position: relative; }
943
+ .ms-search { width: 100%; font-size: 0.78rem; padding: 0.4rem 0.6rem; }
944
+ .ms-dropdown { position: absolute; top: calc(100% + 0.3rem); left: 0; right: 0; z-index: 30; max-height: 12rem; overflow-y: auto; background: var(--surface-3); border: 1px solid var(--border); border-radius: 0.5rem; box-shadow: var(--shadow); padding: 0.3rem; }
945
+ .ms-option { padding: 0.4rem 0.6rem; border-radius: 0.3rem; font-size: 0.8rem; color: var(--text-2); cursor: pointer; }
946
+ .ms-option:hover { background: var(--surface-5); color: var(--text); }
947
+ .ms-empty { padding: 0.4rem 0.6rem; font-size: 0.75rem; color: var(--text-4); }
948
+ .dashboard-content-area { flex: 1 1 auto; }
949
+ .session-list-title { display: flex; align-items: center; gap: 0.5rem; font-size: 0.95rem; font-weight: 600; color: var(--text); margin: 0 0 0.5rem; }
950
+ .session-collapse-count { color: var(--text-4); font-weight: 500; }
951
+ .sessions-list-filter { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-2); background: var(--surface-2); border: 1px solid var(--border); border-radius: 0.4rem; padding: 0.5rem 0.75rem; margin-bottom: 0.75rem; }
952
+ .sessions-list-filter a { color: var(--accent); text-decoration: none; }
953
+ .sessions-list-filter a:hover { text-decoration: underline; }
954
+ .session-list-wrapper { display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--border); border-radius: 0.85rem; overflow: hidden; box-shadow: var(--shadow-sm); }
955
+ .session-list { list-style: none; padding: 0; margin: 0; }
569
956
  .status-badge { font-size: 0.65rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; padding: 0.15rem 0.45rem; border-radius: 0.25rem; }
570
957
  .status-active { background: rgba(34,197,94,0.15); color: var(--success); }
571
958
  .status-completed { background: rgba(96,165,250,0.15); color: var(--accent); }
@@ -599,13 +986,13 @@ button:hover { background: var(--surface-5); }
599
986
  .session-children .session-subagent .session-chevron { display: none; }
600
987
  .session-item.expanded > .session-children { display: block; }
601
988
  .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; }
602
- .main-content { display: grid; grid-template-rows: auto 1fr; gap: 1.25rem; min-height: 0; overflow: hidden; }
603
- .main-content:has(.session-detail-view) { grid-template-rows: 1fr; }
989
+ .main-content { display: flex; flex-direction: column; gap: 1.25rem; }
604
990
  .main-metrics .metric-grid { grid-template-columns: repeat(4, 1fr); gap: 1rem; }
605
991
  .main-metrics .metric-card { min-height: 5rem; }
606
992
  .main-metrics .metric-value { font-size: 1.65rem; }
607
- .top-charts { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; min-height: 0; }
608
- .chart-panel { background: var(--surface); border: 1px solid var(--border); border-radius: 0.5rem; padding: 1rem; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
993
+ .top-charts { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
994
+ .chart-panel { background: var(--surface); border: 1px solid var(--border); border-radius: 0.85rem; padding: 1.1rem; display: flex; flex-direction: column; min-height: 18rem; box-shadow: var(--shadow-sm); transition: box-shadow 0.15s, transform 0.15s; }
995
+ .chart-panel:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
609
996
  .chart-title { font-size: 0.9rem; font-weight: 600; color: var(--text-2); margin: 0 0 0.75rem; flex: 0 0 auto; }
610
997
  .chart-empty { color: var(--text-4); text-align: center; padding: 4rem 0; }
611
998
  .chart-max { text-align: right; color: var(--text-4); font-size: 0.7rem; margin-top: 0.25rem; flex: 0 0 auto; }
@@ -616,15 +1003,19 @@ button:hover { background: var(--surface-5); }
616
1003
  .event-chart text { fill: var(--text-3); font-size: 10px; }
617
1004
  .event-chart .y-label { font-size: 9px; fill: var(--text-4); }
618
1005
  .event-chart .grid { stroke: var(--surface-4); stroke-width: 1; }
619
- .tool-chart { display: flex; flex-direction: column; gap: 0.65rem; flex: 1 1 auto; min-height: 0; overflow-y: auto; }
1006
+ .tool-chart { display: flex; flex-direction: column; gap: 0.65rem; }
620
1007
  .tool-row { display: grid; grid-template-columns: 8rem 1fr 2.5rem; align-items: center; gap: 0.75rem; font-size: 0.8rem; }
1008
+ .chart-panel--wide-label .tool-row { grid-template-columns: 60% 1fr 2.5rem; }
1009
+ a.tool-row { text-decoration: none; color: inherit; border-radius: 0.3rem; padding: 0.15rem 0.3rem; margin: -0.15rem -0.3rem; transition: background 0.12s; }
1010
+ a.tool-row:hover { background: var(--surface-3); }
1011
+ a.tool-row:hover .tool-name { color: var(--accent); }
621
1012
  .tool-name { color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
622
1013
  .tool-bar-bg { background: var(--surface-4); border-radius: 0.4rem; height: 0.85rem; overflow: hidden; }
623
1014
  .tool-bar { background: linear-gradient(90deg, var(--accent), var(--accent-2)); height: 100%; border-radius: 0.4rem; }
624
1015
  .tool-count { color: var(--text-3); text-align: right; font-weight: 600; }
625
- .detail-area { background: var(--surface); border: 1px solid var(--border); border-radius: 0.5rem; padding: 1rem; }
1016
+ .detail-area { background: var(--surface); border: 1px solid var(--border); border-radius: 0.85rem; padding: 1rem; box-shadow: var(--shadow-sm); }
626
1017
  .detail-area > .empty { color: var(--text-4); text-align: center; padding: 4rem 0; }
627
- .session-detail-view { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: 0.75rem; background: var(--surface); border: 1px solid var(--border); border-radius: 0.5rem; padding: 1rem; overflow: hidden; }
1018
+ .session-detail-view { display: flex; flex-direction: column; gap: 0.75rem; background: var(--surface); border: 1px solid var(--border); border-radius: 0.85rem; padding: 1.1rem; box-shadow: var(--shadow-sm); }
628
1019
  .detail-header { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; flex: 0 0 auto; }
629
1020
  .detail-back { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.8rem; padding: 0.35rem 0.6rem; flex: 0 0 auto; }
630
1021
  .detail-title { font-size: 1rem; font-weight: 600; color: var(--text); word-break: break-all; flex: 1 1 auto; min-width: 0; margin: 0; }
@@ -665,6 +1056,11 @@ button:hover { background: var(--surface-5); }
665
1056
  .detail-attrs > div { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
666
1057
  .detail-attrs > div span:first-child { color: var(--text-4); font-size: 0.7rem; text-transform: capitalize; }
667
1058
  .detail-attrs > div span:last-child { color: var(--text); font-weight: 500; overflow: hidden; text-overflow: ellipsis; word-break: break-all; }
1059
+ .detail-summary { display: flex; flex-direction: column; gap: 0.5rem; padding: 0.6rem; background: var(--surface-2); border-radius: 0.4rem; }
1060
+ .summary-group { display: flex; flex-direction: column; gap: 0.3rem; }
1061
+ .summary-label { color: var(--text-4); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.02em; }
1062
+ .summary-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; }
1063
+ .chip { font-size: 0.75rem; color: var(--text-2); background: var(--surface-4); border: 1px solid var(--border); padding: 0.1rem 0.45rem; border-radius: 0.25rem; max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
668
1064
  .session-json { display: none; }
669
1065
  .pager { display: flex; justify-content: center; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; border-top: 1px solid var(--border); background: var(--surface-2); flex: 0 0 auto; }
670
1066
  .pager a, .pager button { background: var(--surface-4); color: var(--text); border: 1px solid var(--border); border-radius: 0.35rem; padding: 0.4rem 0.75rem; font-size: 0.85rem; text-decoration: none; }
@@ -672,7 +1068,8 @@ button:hover { background: var(--surface-5); }
672
1068
  .pager button:disabled { opacity: 0.45; cursor: not-allowed; }
673
1069
  .pager-info { color: var(--text-3); font-size: 0.85rem; }
674
1070
  .metric-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; }
675
- .metric-card { position: relative; overflow: hidden; background: var(--surface-2); border: 1px solid var(--border); border-radius: 0.5rem; padding: 0.85rem 1rem; display: flex; flex-direction: column; justify-content: center; }
1071
+ .metric-card { position: relative; overflow: hidden; background: var(--surface-2); border: 1px solid var(--border); border-radius: 0.75rem; padding: 0.9rem 1.1rem; display: flex; flex-direction: column; justify-content: center; box-shadow: var(--shadow-sm); transition: box-shadow 0.15s, transform 0.15s; }
1072
+ .metric-card:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
676
1073
  .metric-sessions .metric-icon { color: var(--accent); }
677
1074
  .metric-events .metric-icon { color: var(--accent-2); }
678
1075
  .metric-duration .metric-icon { color: var(--warning); }
@@ -680,9 +1077,20 @@ button:hover { background: var(--surface-5); }
680
1077
  .metric-icon { position: absolute; top: 0.75rem; right: 0.75rem; opacity: 0.8; }
681
1078
  .metric-label { font-size: 0.72rem; color: var(--text-2); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; }
682
1079
  .metric-value { font-size: 1.75rem; font-weight: 800; color: var(--text); margin-top: 0.25rem; letter-spacing: -0.02em; }
1080
+ .context-breakdown { min-height: 0; }
1081
+ .context-bars { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
1082
+ .context-bar-block { display: flex; flex-direction: column; gap: 0.5rem; }
1083
+ .context-bar-title { font-size: 0.78rem; font-weight: 600; color: var(--text-3); margin: 0; }
1084
+ .context-bar { display: flex; height: 0.85rem; border-radius: 0.4rem; overflow: hidden; background: var(--surface-4); }
1085
+ .context-segment { height: 100%; }
1086
+ .context-legend { display: flex; flex-direction: column; gap: 0.3rem; }
1087
+ .context-legend-row { display: flex; align-items: center; gap: 0.4rem; font-size: 0.78rem; color: var(--text-2); }
1088
+ .context-legend-dot { width: 0.6rem; height: 0.6rem; border-radius: 50%; flex: 0 0 auto; }
1089
+ .context-legend-label { flex: 1 1 auto; }
1090
+ .context-legend-value { color: var(--text-3); font-weight: 600; }
683
1091
  .empty { color: var(--text-4); text-align: center; padding: 2rem; }
684
- @media (max-width: 960px) { .dashboard-layout { grid-template-columns: 1fr; grid-template-rows: auto 1fr; } .session-sidebar { max-height: 24rem; } .session-list { max-height: 16rem; } .main-content { overflow-y: auto; grid-template-rows: auto auto; } .top-charts { grid-template-columns: 1fr; } .chart-panel { min-height: 16rem; } .metric-grid { grid-template-columns: repeat(2, 1fr); } .main-metrics .metric-grid { grid-template-columns: repeat(2, 1fr); } .detail-attrs { grid-template-columns: repeat(2, 1fr); } }
685
- @media (max-width: 640px) { .app { padding: 0.75rem; } .app-header { flex-direction: column; align-items: flex-start; } .header-controls, .filter-bar { width: 100%; } input.search { min-width: 0; width: 100%; } .search-wrap { width: 100%; } .metric-grid { grid-template-columns: 1fr; } .main-metrics .metric-grid { grid-template-columns: 1fr; } .detail-attrs { grid-template-columns: 1fr; } .detail-toolbar input[type="search"] { min-width: 0; width: 100%; } }
1092
+ @media (max-width: 960px) { .top-charts { grid-template-columns: 1fr; } .metric-grid { grid-template-columns: repeat(2, 1fr); } .main-metrics .metric-grid { grid-template-columns: repeat(2, 1fr); } .detail-attrs { grid-template-columns: repeat(2, 1fr); } .context-bars { grid-template-columns: 1fr; } }
1093
+ @media (max-width: 640px) { .app { padding: 0.75rem; } .app-header { flex-direction: column; align-items: flex-start; } .header-controls, .filter-panel, .toolbar-row { width: 100%; } .filter-panel { grid-template-columns: 1fr; } .filter-panel .field-wide { grid-column: span 1; } .view-tabs { flex: 1 1 auto; justify-content: center; } input.search { min-width: 0; width: 100%; } .search-wrap { width: 100%; } .metric-grid { grid-template-columns: 1fr; } .main-metrics .metric-grid { grid-template-columns: 1fr; } .detail-attrs { grid-template-columns: 1fr; } .detail-toolbar input[type="search"] { min-width: 0; width: 100%; } }
686
1094
  </style>
687
1095
  <script src="https://unpkg.com/htmx.org@2.0.10/dist/htmx.min.js" integrity="sha256-cepnGFv6jJjDnTFxfG/OXYUjcPzf0SnbRUN3TTFFwN4=" crossorigin="anonymous"><\/script>
688
1096
  <script src="https://unpkg.com/htmx-ext-sse@2.2.4/dist/sse.js" integrity="sha256-O1mSpUFhm6vvxMFpUFr0dN9cMDnaUeWblsz5JB7NYdI=" crossorigin="anonymous"><\/script>
@@ -691,20 +1099,21 @@ button:hover { background: var(--surface-5); }
691
1099
  <div class="app">
692
1100
  <div class="sticky-bar">
693
1101
  <form data-filter-form hx-get="/fragments/sessions" hx-target="#dashboard-content" hx-swap="innerHTML" hx-trigger="change, input changed delay:300ms from:.search">
694
- ${Tt(t)}${Dt(n,t)}
1102
+ <input type="hidden" name="view" value="${t.view===`list`?`list`:``}">
1103
+ ${i}${a}
695
1104
  </form>
696
1105
  </div>
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">
1106
+ <div id="dashboard-content" class="dashboard-content-area" sse-connect="/events/stream" hx-get="/fragments/sessions" hx-target="#dashboard-content" hx-swap="innerHTML" hx-trigger="sse:message">
698
1107
  ${r}
699
1108
  </div>
700
1109
  </div>
701
1110
  </body>
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
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
703
1112
 
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
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
705
1114
  data: ping
706
1115
 
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]
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]
708
1117
 
709
1118
  Options:
710
1119
  --source <source> filter by source (cursor, claude, ...)
@@ -719,7 +1128,7 @@ Options:
719
1128
  --format <format> output format: json (default), jsonl, summary
720
1129
  --db <path> database path (default: HAPPENIN_DB or ~/.happenin/happenin.db)
721
1130
  -h, --help show this help
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]
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]
723
1132
 
724
1133
  Options:
725
1134
  --source <source> filter by source (cursor, claude, ...)
@@ -737,8 +1146,8 @@ Options:
737
1146
  --format <format> output format: json (default), jsonl, summary
738
1147
  --db <path> database path (default: HAPPENIN_DB or ~/.happenin/happenin.db)
739
1148
  -h, --help show this help
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
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
742
1151
 
743
1152
  Run \`happenin <command>\`.
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{};
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{};