session-intelligence-cli
Advanced tools
| export declare function readStdin(): Promise<Record<string, unknown>>; |
| export function readStdin() { | ||
| return new Promise((resolve) => { | ||
| const chunks = []; | ||
| process.stdin.on("data", (chunk) => chunks.push(chunk)); | ||
| process.stdin.on("end", () => { | ||
| try { | ||
| const raw = Buffer.concat(chunks).toString("utf-8").trim(); | ||
| resolve(raw ? JSON.parse(raw) : {}); | ||
| } | ||
| catch { | ||
| resolve({}); | ||
| } | ||
| }); | ||
| process.stdin.on("error", () => resolve({})); | ||
| }); | ||
| } |
+13
-4
@@ -5,3 +5,3 @@ #!/usr/bin/env node | ||
| import { sendEvent } from "./api.js"; | ||
| // Called as: node report-bg.js <json-payload> | ||
| // Called as: node report-bg.js <json-payload> [source] | ||
| // Runs detached from the parent process to avoid being killed on session teardown. | ||
@@ -11,5 +11,10 @@ const raw = process.argv[2]; | ||
| process.exit(0); | ||
| const source = (process.argv[3] ?? "claude-code"); | ||
| const stdinData = JSON.parse(raw); | ||
| const sessionId = stdinData.session_id ?? ""; | ||
| const cwd = stdinData.cwd ?? process.cwd(); | ||
| const sessionId = source === "cursor" | ||
| ? stdinData.conversation_id ?? "" | ||
| : stdinData.session_id ?? ""; | ||
| const cwd = source === "cursor" | ||
| ? (stdinData.workspace_roots?.[0] ?? process.cwd()) | ||
| : (stdinData.cwd ?? process.cwd()); | ||
| const payload = { | ||
@@ -19,5 +24,9 @@ session_id: sessionId, | ||
| hook_event_name: "SessionEnd", | ||
| source: "claude-code", | ||
| source, | ||
| model: stdinData.model, | ||
| }; | ||
| if (source === "cursor") { | ||
| payload.duration_ms = stdinData.duration_ms; | ||
| payload.reason = stdinData.reason; | ||
| } | ||
| const transcriptPath = stdinData.transcript_path; | ||
@@ -24,0 +33,0 @@ if (transcriptPath) { |
+23
-19
@@ -0,3 +1,7 @@ | ||
| import { spawn } from "node:child_process"; | ||
| import { fileURLToPath } from "node:url"; | ||
| import { dirname, join } from "node:path"; | ||
| import { readGitState, readGitDiffStats } from "./git.js"; | ||
| import { sendEvent } from "./api.js"; | ||
| import { readStdin } from "./stdin.js"; | ||
| export const CURSOR_EVENT_MAP = { | ||
@@ -13,18 +17,2 @@ "session-start": "SessionStart", | ||
| }; | ||
| function readStdin() { | ||
| return new Promise((resolve) => { | ||
| const chunks = []; | ||
| process.stdin.on("data", (chunk) => chunks.push(chunk)); | ||
| process.stdin.on("end", () => { | ||
| try { | ||
| const raw = Buffer.concat(chunks).toString("utf-8").trim(); | ||
| resolve(raw ? JSON.parse(raw) : {}); | ||
| } | ||
| catch { | ||
| resolve({}); | ||
| } | ||
| }); | ||
| process.stdin.on("error", () => resolve({})); | ||
| }); | ||
| } | ||
| export function buildCursorPayload(eventType, stdinData, cwd) { | ||
@@ -37,5 +25,6 @@ const sessionId = stdinData.conversation_id ?? ""; | ||
| source: "cursor", | ||
| model: stdinData.model, | ||
| }; | ||
| if (eventType === "prompt") { | ||
| payload.prompt = stdinData.input ?? stdinData.prompt; | ||
| payload.prompt = stdinData.prompt ?? stdinData.input; | ||
| } | ||
@@ -61,4 +50,8 @@ else if (eventType === "tool-use") { | ||
| else if (eventType === "subagent-stop") { | ||
| payload.agent_type = stdinData.agent_type; | ||
| payload.subagent_type = stdinData.subagent_type; | ||
| } | ||
| else if (eventType === "session-end") { | ||
| payload.duration_ms = stdinData.duration_ms; | ||
| payload.reason = stdinData.reason; | ||
| } | ||
| return payload; | ||
@@ -68,6 +61,17 @@ } | ||
| const stdinData = await readStdin(); | ||
| // For session-end, spawn a detached background process so the hook | ||
| // can exit immediately and avoid being killed during session teardown. | ||
| if (eventType === "session-end") { | ||
| const scriptDir = dirname(fileURLToPath(import.meta.url)); | ||
| const bgScript = join(scriptDir, "report-bg.js"); | ||
| const child = spawn(process.execPath, [bgScript, JSON.stringify(stdinData), "cursor"], { | ||
| detached: true, | ||
| stdio: "ignore", | ||
| }); | ||
| child.unref(); | ||
| return; | ||
| } | ||
| const workspaceRoots = stdinData.workspace_roots; | ||
| const cwd = workspaceRoots?.[0] ?? process.cwd(); | ||
| const payload = buildCursorPayload(eventType, stdinData, cwd); | ||
| // Cursor hooks don't include git data — collect it ourselves | ||
| const git = readGitState(cwd); | ||
@@ -74,0 +78,0 @@ if (git) { |
+1
-16
@@ -6,2 +6,3 @@ import { spawn } from "node:child_process"; | ||
| import { sendEvent } from "./api.js"; | ||
| import { readStdin } from "./stdin.js"; | ||
| export const CLAUDE_EVENT_MAP = { | ||
@@ -15,18 +16,2 @@ "session-start": "SessionStart", | ||
| }; | ||
| function readStdin() { | ||
| return new Promise((resolve) => { | ||
| const chunks = []; | ||
| process.stdin.on("data", (chunk) => chunks.push(chunk)); | ||
| process.stdin.on("end", () => { | ||
| try { | ||
| const raw = Buffer.concat(chunks).toString("utf-8").trim(); | ||
| resolve(raw ? JSON.parse(raw) : {}); | ||
| } | ||
| catch { | ||
| resolve({}); | ||
| } | ||
| }); | ||
| process.stdin.on("error", () => resolve({})); | ||
| }); | ||
| } | ||
| export function buildClaudePayload(eventType, stdinData, cwd) { | ||
@@ -33,0 +18,0 @@ return { |
+8
-8
@@ -35,10 +35,10 @@ import { createInterface } from "node:readline"; | ||
| const CURSOR_HOOKS = { | ||
| sessionStart: [{ command: "session-intelligence report-cursor session-start", timeout: 5 }], | ||
| sessionEnd: [{ command: "session-intelligence report-cursor session-end", timeout: 5 }], | ||
| beforeSubmitPrompt: [{ command: "session-intelligence report-cursor prompt", timeout: 5 }], | ||
| postToolUse: [{ command: "session-intelligence report-cursor tool-use", timeout: 3 }], | ||
| afterFileEdit: [{ command: "session-intelligence report-cursor file-edit", timeout: 3 }], | ||
| afterShellExecution: [{ command: "session-intelligence report-cursor shell", timeout: 3 }], | ||
| stop: [{ command: "session-intelligence report-cursor stop", timeout: 5 }], | ||
| subagentStop: [{ command: "session-intelligence report-cursor subagent-stop", timeout: 3 }], | ||
| sessionStart: [{ command: "session-intelligence report-cursor session-start", timeout: 10 }], | ||
| sessionEnd: [{ command: "session-intelligence report-cursor session-end", timeout: 10 }], | ||
| beforeSubmitPrompt: [{ command: "session-intelligence report-cursor prompt", timeout: 10 }], | ||
| postToolUse: [{ command: "session-intelligence report-cursor tool-use", timeout: 10 }], | ||
| afterFileEdit: [{ command: "session-intelligence report-cursor file-edit", timeout: 10 }], | ||
| afterShellExecution: [{ command: "session-intelligence report-cursor shell", timeout: 10 }], | ||
| stop: [{ command: "session-intelligence report-cursor stop", timeout: 10 }], | ||
| subagentStop: [{ command: "session-intelligence report-cursor subagent-stop", timeout: 10 }], | ||
| }; | ||
@@ -45,0 +45,0 @@ export async function setup(args) { |
+1
-1
| { | ||
| "name": "session-intelligence-cli", | ||
| "version": "0.2.2", | ||
| "version": "0.3.0", | ||
| "description": "Real-time dashboard for AI coding sessions — track activity, git changes, token usage, and session history across Claude Code and Cursor", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+45
-16
| # Session Intelligence CLI | ||
| A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) plugin that gives you a real-time dashboard for all your coding sessions. | ||
| A plugin for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and [Cursor](https://cursor.com) that gives you a real-time dashboard for all your AI coding sessions. | ||
@@ -18,4 +18,6 @@ See what every session is doing, track token usage and git changes, and get a full activity history across all your projects. | ||
| ### Claude Code | ||
| ```bash | ||
| npx session-intelligence-cli setup | ||
| npx session-intelligence-cli setup --tool claude-code | ||
| ``` | ||
@@ -31,17 +33,42 @@ | ||
| ### Cursor | ||
| ```bash | ||
| npx session-intelligence-cli setup --tool cursor | ||
| ``` | ||
| This will: | ||
| 1. Ask for your **server URL** and **API key** | ||
| 2. Save config to `~/.session-intelligence/config.json` | ||
| 3. Register hooks in `~/.cursor/hooks.json` | ||
| Your next Cursor session will start reporting automatically. | ||
| ## How it works | ||
| The CLI registers lightweight [Claude Code hooks](https://docs.anthropic.com/en/docs/claude-code/hooks) that fire on session lifecycle events: | ||
| The CLI registers lightweight hooks that fire on session lifecycle events: | ||
| | Event | What it reports | | ||
| |---|---| | ||
| | `SessionStart` | New session created | | ||
| | `PostToolUse` | Tool activity (Read, Edit, Bash, etc.) | | ||
| | `Notification` | Permission requests / input needed | | ||
| | `SubagentStop` | Subagent task completion | | ||
| | `Stop` | Task turn completed | | ||
| | `SessionEnd` | Session archived with token usage summary | | ||
| | Event | Claude Code | Cursor | What it reports | | ||
| |---|---|---|---| | ||
| | `SessionStart` | ✓ | ✓ | New session created | | ||
| | `PostToolUse` | ✓ | ✓ | Tool activity (Read, Edit, Bash, etc.) | | ||
| | `UserPromptSubmit` | — | ✓ | User prompt submitted | | ||
| | `Notification` | ✓ | — | Permission requests / input needed | | ||
| | `SubagentStop` | ✓ | ✓ | Subagent task completion | | ||
| | `Stop` | ✓ | ✓ | Task turn completed | | ||
| | `SessionEnd` | ✓ | ✓ | Session archived with token usage summary | | ||
| Each hook reads the current git state (branch, last commit, uncommitted changes) and sends a small payload to your dashboard. All calls are fire-and-forget with no impact on Claude Code performance. | ||
| Each hook reads the current git state (branch, last commit, uncommitted changes) and sends a small payload to your dashboard. All calls are fire-and-forget with no impact on editor performance. | ||
| ## Importing existing sessions | ||
| Backfill your dashboard with past Claude Code sessions: | ||
| ```bash | ||
| npx session-intelligence-cli import | ||
| ``` | ||
| Options: `--project <name>`, `--since <date>`, `--dry-run`, `--dir <path>`. | ||
| ## Updating | ||
@@ -52,3 +79,4 @@ | ||
| ```bash | ||
| npx session-intelligence-cli@latest setup | ||
| npx session-intelligence-cli@latest setup --tool claude-code | ||
| npx session-intelligence-cli@latest setup --tool cursor | ||
| ``` | ||
@@ -59,4 +87,5 @@ | ||
| ```bash | ||
| session-intelligence setup # configure and install hooks | ||
| session-intelligence status # check connection and config | ||
| session-intelligence setup --tool <claude-code|cursor> # configure and install hooks | ||
| session-intelligence status # check connection and active sessions | ||
| session-intelligence import # backfill from Claude Code transcripts | ||
| ``` | ||
@@ -67,3 +96,3 @@ | ||
| - Node.js >= 18 | ||
| - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed | ||
| - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) or [Cursor](https://cursor.com) | ||
@@ -70,0 +99,0 @@ ## License |
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
36605
4.83%26
8.33%896
1.7%97
42.65%7
16.67%