braintracker 1.1.0 → 1.3.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/README.md CHANGED
@@ -31,23 +31,18 @@ Removes the hook files and cleans up `~/.claude/settings.json`.
31
31
 
32
32
  ## Verify
33
33
 
34
- Invoke any skill inside Claude Code (e.g. `/review`), then:
34
+ After a conversation, two files are written per session under `~/.claude/plugin/cache/braintracker/<project>/` (`<project>` is the working directory name):
35
+
36
+ **Full conversation transcript** — every message, all text and tool content:
35
37
 
36
38
  ```bash
37
- cat ~/.claude/plugin/cache/braintracker/<project>/<session_id>.jsonl
39
+ cat ~/.claude/plugin/cache/braintracker/<project>/<session_id>.transcript.json
38
40
  ```
39
41
 
40
- Each line is a JSON object:
41
-
42
- ```json
43
- {
44
- "type": "skill_invocation",
45
- "session_id": "abc123",
46
- "skill": "review",
47
- "triggered_by": "review this PR",
48
- "started_at": "2026-05-25T10:00:00Z",
49
- "duration_seconds": 4.2
50
- }
42
+ **Event log** one JSON line per turn / skill invocation:
43
+
44
+ ```bash
45
+ cat ~/.claude/plugin/cache/braintracker/<project>/<session_id>.jsonl
51
46
  ```
52
47
 
53
48
  ## How It Works
@@ -10,6 +10,8 @@ const PRE_CMD = `node ${path.join(DEST_HOOKS, 'pre_skill.js')}`;
10
10
  const POST_CMD = `node ${path.join(DEST_HOOKS, 'post_skill.js')}`;
11
11
  const PRE_PROMPT_CMD = `node ${path.join(DEST_HOOKS, 'pre_prompt.js')}`;
12
12
  const POST_PROMPT_CMD = `node ${path.join(DEST_HOOKS, 'post_prompt.js')}`;
13
+ const PRE_TURN_CMD = `node ${path.join(DEST_HOOKS, 'pre_turn.js')}`;
14
+ const POST_TURN_CMD = `node ${path.join(DEST_HOOKS, 'post_turn.js')}`;
13
15
 
14
16
  function readSettings() {
15
17
  try { return JSON.parse(fs.readFileSync(SETTINGS, 'utf8')); } catch { return {}; }
@@ -44,7 +46,7 @@ function removeHook(arr, command) {
44
46
  function install() {
45
47
  const SRC_HOOKS = path.join(__dirname, '..', 'braintracker', 'hooks');
46
48
  fs.mkdirSync(DEST_HOOKS, { recursive: true });
47
- for (const file of ['pre_skill.js', 'post_skill.js', 'pre_prompt.js', 'post_prompt.js']) {
49
+ for (const file of ['pre_skill.js', 'post_skill.js', 'pre_prompt.js', 'post_prompt.js', 'pre_turn.js', 'post_turn.js']) {
48
50
  fs.copyFileSync(path.join(SRC_HOOKS, file), path.join(DEST_HOOKS, file));
49
51
  }
50
52
 
@@ -57,7 +59,9 @@ function install() {
57
59
  addHook(settings.hooks.PreToolUse, 'Skill', PRE_CMD);
58
60
  addHook(settings.hooks.PostToolUse, 'Skill', POST_CMD);
59
61
  addHook(settings.hooks.UserPromptSubmit, null, PRE_PROMPT_CMD);
62
+ addHook(settings.hooks.UserPromptSubmit, null, PRE_TURN_CMD);
60
63
  addHook(settings.hooks.Stop, null, POST_PROMPT_CMD);
64
+ addHook(settings.hooks.Stop, null, POST_TURN_CMD);
61
65
  writeSettings(settings);
62
66
 
63
67
  console.log('braintracker installed.');
@@ -72,7 +76,9 @@ function uninstall() {
72
76
  settings.hooks.PreToolUse = removeHook(settings.hooks.PreToolUse, PRE_CMD);
73
77
  settings.hooks.PostToolUse = removeHook(settings.hooks.PostToolUse, POST_CMD);
74
78
  settings.hooks.UserPromptSubmit = removeHook(settings.hooks.UserPromptSubmit, PRE_PROMPT_CMD);
79
+ settings.hooks.UserPromptSubmit = removeHook(settings.hooks.UserPromptSubmit, PRE_TURN_CMD);
75
80
  settings.hooks.Stop = removeHook(settings.hooks.Stop, POST_PROMPT_CMD);
81
+ settings.hooks.Stop = removeHook(settings.hooks.Stop, POST_TURN_CMD);
76
82
  }
77
83
  writeSettings(settings);
78
84
 
@@ -85,7 +91,9 @@ function disable() {
85
91
  settings.hooks.PreToolUse = removeHook(settings.hooks.PreToolUse, PRE_CMD);
86
92
  settings.hooks.PostToolUse = removeHook(settings.hooks.PostToolUse, POST_CMD);
87
93
  settings.hooks.UserPromptSubmit = removeHook(settings.hooks.UserPromptSubmit, PRE_PROMPT_CMD);
94
+ settings.hooks.UserPromptSubmit = removeHook(settings.hooks.UserPromptSubmit, PRE_TURN_CMD);
88
95
  settings.hooks.Stop = removeHook(settings.hooks.Stop, POST_PROMPT_CMD);
96
+ settings.hooks.Stop = removeHook(settings.hooks.Stop, POST_TURN_CMD);
89
97
  }
90
98
  writeSettings(settings);
91
99
  console.log('braintracker disabled. Hook files kept. Run "npx braintracker enable" to re-enable.');
@@ -106,7 +114,9 @@ function enable() {
106
114
  addHook(settings.hooks.PreToolUse, 'Skill', PRE_CMD);
107
115
  addHook(settings.hooks.PostToolUse, 'Skill', POST_CMD);
108
116
  addHook(settings.hooks.UserPromptSubmit, null, PRE_PROMPT_CMD);
117
+ addHook(settings.hooks.UserPromptSubmit, null, PRE_TURN_CMD);
109
118
  addHook(settings.hooks.Stop, null, POST_PROMPT_CMD);
119
+ addHook(settings.hooks.Stop, null, POST_TURN_CMD);
110
120
  writeSettings(settings);
111
121
 
112
122
  console.log('braintracker enabled.');
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+
6
+ let raw = '';
7
+ process.stdin.on('data', chunk => (raw += chunk));
8
+ process.stdin.on('end', () => {
9
+ let payload;
10
+ try { payload = JSON.parse(raw); } catch { process.exit(0); }
11
+
12
+ const sessionId = payload.session_id;
13
+ const prj = path.basename(process.cwd());
14
+ const cacheDir = path.join(os.homedir(), '.claude', 'plugin', 'cache', 'braintracker', prj);
15
+ const preFile = path.join(cacheDir, `${sessionId}.turn.pre.json`);
16
+
17
+ let preData;
18
+ try { preData = JSON.parse(fs.readFileSync(preFile, 'utf8')); } catch { process.exit(0); }
19
+
20
+ const transcript = payload.transcript || [];
21
+
22
+ // overwrite transcript file with full conversation on every turn
23
+ const transcriptFile = path.join(cacheDir, `${sessionId}.transcript.json`);
24
+ fs.writeFileSync(transcriptFile, JSON.stringify(transcript, null, 2));
25
+
26
+ const entry = {
27
+ type: 'conversation_turn',
28
+ session_id: sessionId,
29
+ user_prompt: preData.prompt,
30
+ started_at: new Date(preData.start_time).toISOString(),
31
+ duration_seconds: (Date.now() - preData.start_time) / 1000,
32
+ };
33
+
34
+ const logFile = path.join(cacheDir, `${sessionId}.jsonl`);
35
+ fs.appendFileSync(logFile, JSON.stringify(entry) + '\n');
36
+ fs.unlinkSync(preFile);
37
+
38
+ process.exit(0);
39
+ });
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+
6
+ let raw = '';
7
+ process.stdin.on('data', chunk => (raw += chunk));
8
+ process.stdin.on('end', () => {
9
+ let payload;
10
+ try { payload = JSON.parse(raw); } catch { process.exit(0); }
11
+
12
+ const sessionId = payload.session_id;
13
+ const prompt = (payload.prompt || '').trim();
14
+ const prj = path.basename(process.cwd());
15
+ const cacheDir = path.join(os.homedir(), '.claude', 'plugin', 'cache', 'braintracker', prj);
16
+
17
+ fs.mkdirSync(cacheDir, { recursive: true });
18
+
19
+ const preFile = path.join(cacheDir, `${sessionId}.turn.pre.json`);
20
+ fs.writeFileSync(preFile, JSON.stringify({
21
+ session_id: sessionId,
22
+ prompt,
23
+ start_time: Date.now(),
24
+ }));
25
+
26
+ process.exit(0);
27
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braintracker",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Claude Code plugin that logs every skill invocation with duration and triggering prompt",
5
5
  "bin": {
6
6
  "braintracker": "bin/braintracker.js"