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 +77 -3
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/index.ts +85 -2
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
|
-
|
|
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
|
|
221
|
+
if (inp?.hook_event_name === 'SessionStart') {
|
|
148
222
|
deps.checkAndInstallCli();
|
|
149
223
|
}
|
|
150
224
|
if (shouldSendHeartbeat(inp)) {
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
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
|
-
|
|
196
|
+
entity,
|
|
114
197
|
'--entity-type',
|
|
115
|
-
'app',
|
|
198
|
+
entity === 'claude code' ? 'app' : 'file',
|
|
116
199
|
'--category',
|
|
117
200
|
'ai coding',
|
|
118
201
|
'--plugin',
|