claude-code-wakatime 2.1.3 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -50,6 +50,73 @@ function getLastHeartbeat() {
50
50
  return 0;
51
51
  }
52
52
  }
53
+ function getModifiedFile(transcriptPath) {
54
+ try {
55
+ if (!transcriptPath || !fs_1.default.existsSync(transcriptPath)) {
56
+ return undefined;
57
+ }
58
+ const content = fs_1.default.readFileSync(transcriptPath, 'utf-8');
59
+ const lines = content.split('\n');
60
+ const fileLineChanges = new Map();
61
+ const lastHeartbeatAt = getLastHeartbeat();
62
+ for (const line of lines) {
63
+ if (line.trim()) {
64
+ try {
65
+ const logEntry = JSON.parse(line);
66
+ if (!logEntry.timestamp)
67
+ continue;
68
+ const entryTimestamp = new Date(logEntry.timestamp).getTime() / 1000;
69
+ if (entryTimestamp >= lastHeartbeatAt) {
70
+ let filePath;
71
+ // Check for file paths in tool use results
72
+ if (logEntry.toolUse?.parameters?.file_path) {
73
+ filePath = logEntry.toolUse.parameters.file_path;
74
+ }
75
+ // Check for file paths in tool use results for multi-edit
76
+ if (logEntry.toolUse?.parameters?.edits) {
77
+ filePath = logEntry.toolUse.parameters.file_path;
78
+ }
79
+ // Check for file paths and line changes in structured patch
80
+ if (logEntry.toolUseResult?.structuredPatch) {
81
+ const patches = logEntry.toolUseResult.structuredPatch;
82
+ for (const patch of patches) {
83
+ if (patch.file) {
84
+ filePath = patch.file;
85
+ if (patch.newLines !== undefined && patch.oldLines !== undefined) {
86
+ const lineChanges = Math.abs(patch.newLines - patch.oldLines);
87
+ fileLineChanges.set(filePath, (fileLineChanges.get(filePath) || 0) + lineChanges);
88
+ }
89
+ }
90
+ }
91
+ }
92
+ if (filePath && !fileLineChanges.has(filePath)) {
93
+ fileLineChanges.set(filePath, 0);
94
+ }
95
+ }
96
+ }
97
+ catch {
98
+ // ignore
99
+ }
100
+ }
101
+ }
102
+ if (fileLineChanges.size === 0) {
103
+ return undefined;
104
+ }
105
+ // Find file with most line changes
106
+ let maxChanges = 0;
107
+ let mostChangedFile;
108
+ for (const [file, changes] of fileLineChanges.entries()) {
109
+ if (changes > maxChanges) {
110
+ maxChanges = changes;
111
+ mostChangedFile = file;
112
+ }
113
+ }
114
+ return mostChangedFile;
115
+ }
116
+ catch {
117
+ return undefined;
118
+ }
119
+ }
53
120
  function calculateLineChanges(transcriptPath) {
54
121
  try {
55
122
  if (!transcriptPath || !fs_1.default.existsSync(transcriptPath)) {
@@ -94,11 +161,18 @@ function updateState() {
94
161
  function sendHeartbeat(inp) {
95
162
  const projectFolder = inp?.cwd;
96
163
  try {
164
+ let entity = 'claude code';
165
+ if (inp?.transcript_path) {
166
+ const modifiedFile = getModifiedFile(inp.transcript_path);
167
+ if (modifiedFile) {
168
+ entity = modifiedFile;
169
+ }
170
+ }
97
171
  const args = [
98
172
  '--entity',
99
- 'claude code',
173
+ entity,
100
174
  '--entity-type',
101
- 'app',
175
+ entity === 'claude code' ? 'app' : 'file',
102
176
  '--category',
103
177
  'ai coding',
104
178
  '--plugin',
@@ -144,7 +218,7 @@ function main() {
144
218
  // ignore
145
219
  }
146
220
  }
147
- if (inp?.hook_event_name === 'SessionStart demo') {
221
+ if (inp?.hook_event_name === 'SessionStart') {
148
222
  deps.checkAndInstallCli();
149
223
  }
150
224
  if (shouldSendHeartbeat(inp)) {
package/dist/version.js CHANGED
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  // This file is auto-generated during build. Do not edit manually.
5
- exports.VERSION = '2.1.0';
5
+ exports.VERSION = '2.1.3';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-wakatime",
3
- "version": "2.1.3",
3
+ "version": "2.2.0",
4
4
  "description": "WakaTime plugin for Claude Code",
5
5
  "bin": {
6
6
  "claude-code-wakatime": "dist/index.js"
package/src/index.ts CHANGED
@@ -60,6 +60,81 @@ function getLastHeartbeat() {
60
60
  }
61
61
  }
62
62
 
63
+ function getModifiedFile(transcriptPath: string): string | undefined {
64
+ try {
65
+ if (!transcriptPath || !fs.existsSync(transcriptPath)) {
66
+ return undefined;
67
+ }
68
+
69
+ const content = fs.readFileSync(transcriptPath, 'utf-8');
70
+ const lines = content.split('\n');
71
+ const fileLineChanges = new Map<string, number>();
72
+
73
+ const lastHeartbeatAt = getLastHeartbeat();
74
+ for (const line of lines) {
75
+ if (line.trim()) {
76
+ try {
77
+ const logEntry = JSON.parse(line);
78
+ if (!logEntry.timestamp) continue;
79
+
80
+ const entryTimestamp = new Date(logEntry.timestamp).getTime() / 1000;
81
+ if (entryTimestamp >= lastHeartbeatAt) {
82
+ let filePath: string | undefined;
83
+
84
+ // Check for file paths in tool use results
85
+ if (logEntry.toolUse?.parameters?.file_path) {
86
+ filePath = logEntry.toolUse.parameters.file_path;
87
+ }
88
+
89
+ // Check for file paths in tool use results for multi-edit
90
+ if (logEntry.toolUse?.parameters?.edits) {
91
+ filePath = logEntry.toolUse.parameters.file_path;
92
+ }
93
+
94
+ // Check for file paths and line changes in structured patch
95
+ if (logEntry.toolUseResult?.structuredPatch) {
96
+ const patches = logEntry.toolUseResult.structuredPatch;
97
+ for (const patch of patches) {
98
+ if (patch.file) {
99
+ filePath = patch.file as string;
100
+ if (patch.newLines !== undefined && patch.oldLines !== undefined) {
101
+ const lineChanges = Math.abs(patch.newLines - patch.oldLines);
102
+ fileLineChanges.set(filePath, (fileLineChanges.get(filePath) || 0) + lineChanges);
103
+ }
104
+ }
105
+ }
106
+ }
107
+
108
+ if (filePath && !fileLineChanges.has(filePath)) {
109
+ fileLineChanges.set(filePath, 0);
110
+ }
111
+ }
112
+ } catch {
113
+ // ignore
114
+ }
115
+ }
116
+ }
117
+
118
+ if (fileLineChanges.size === 0) {
119
+ return undefined;
120
+ }
121
+
122
+ // Find file with most line changes
123
+ let maxChanges = 0;
124
+ let mostChangedFile: string | undefined;
125
+ for (const [file, changes] of fileLineChanges.entries()) {
126
+ if (changes > maxChanges) {
127
+ maxChanges = changes;
128
+ mostChangedFile = file;
129
+ }
130
+ }
131
+
132
+ return mostChangedFile;
133
+ } catch {
134
+ return undefined;
135
+ }
136
+ }
137
+
63
138
  function calculateLineChanges(transcriptPath: string): number {
64
139
  try {
65
140
  if (!transcriptPath || !fs.existsSync(transcriptPath)) {
@@ -108,11 +183,19 @@ function updateState() {
108
183
  function sendHeartbeat(inp: Input | undefined) {
109
184
  const projectFolder = inp?.cwd;
110
185
  try {
186
+ let entity = 'claude code';
187
+ if (inp?.transcript_path) {
188
+ const modifiedFile = getModifiedFile(inp.transcript_path);
189
+ if (modifiedFile) {
190
+ entity = modifiedFile;
191
+ }
192
+ }
193
+
111
194
  const args: string[] = [
112
195
  '--entity',
113
- 'claude code',
196
+ entity,
114
197
  '--entity-type',
115
- 'app',
198
+ entity === 'claude code' ? 'app' : 'file',
116
199
  '--category',
117
200
  'ai coding',
118
201
  '--plugin',