@jagit/hook-claude-code 0.0.0 → 0.0.2
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.d.ts +6 -1
- package/dist/index.js +106 -5
- package/package.json +8 -7
package/dist/index.d.ts
CHANGED
|
@@ -20,5 +20,10 @@ interface TranscriptEntry {
|
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
export declare function
|
|
23
|
+
export declare function calculateBaseTokens(costUsd: number | null): number | null;
|
|
24
|
+
export declare function parseGitDiff(output: string): {
|
|
25
|
+
linesAdded: number;
|
|
26
|
+
linesRemoved: number;
|
|
27
|
+
};
|
|
28
|
+
export declare function buildPayload(stdin: StopStdin, read?: (path: string) => TranscriptEntry[], checkExists?: (path: string) => boolean, readFile?: (path: string, enc: BufferEncoding) => string): AgentSessionPayload;
|
|
24
29
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { readFileSync, existsSync, rmSync } from "node:fs";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
3
4
|
import { realpathSync } from "node:fs";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { resolveGitUsername, reportSession } from "@jagit/agent-reporter";
|
|
7
|
+
import { createJiraWorklog } from "@jagit/shared";
|
|
6
8
|
function readTranscript(path) {
|
|
7
9
|
return readFileSync(path, "utf-8")
|
|
8
10
|
.split("\n")
|
|
@@ -17,10 +19,58 @@ function readTranscript(path) {
|
|
|
17
19
|
function hasToolUse(content) {
|
|
18
20
|
return Array.isArray(content) && content.some((b) => b?.type === "tool_use");
|
|
19
21
|
}
|
|
20
|
-
export function
|
|
22
|
+
export function calculateBaseTokens(costUsd) {
|
|
23
|
+
if (costUsd === null)
|
|
24
|
+
return null;
|
|
25
|
+
// Fixed rate: 1 USD = 4,000,000 BT
|
|
26
|
+
return costUsd * 4000000;
|
|
27
|
+
}
|
|
28
|
+
export function parseGitDiff(output) {
|
|
29
|
+
let linesAdded = 0;
|
|
30
|
+
let linesRemoved = 0;
|
|
31
|
+
const lines = output.split("\n").filter((l) => l.trim().length > 0);
|
|
32
|
+
for (const line of lines) {
|
|
33
|
+
const parts = line.split("\t");
|
|
34
|
+
if (parts.length >= 2) {
|
|
35
|
+
if (parts[0] !== "-")
|
|
36
|
+
linesAdded += parseInt(parts[0], 10);
|
|
37
|
+
if (parts[1] !== "-")
|
|
38
|
+
linesRemoved += parseInt(parts[1], 10);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { linesAdded, linesRemoved };
|
|
42
|
+
}
|
|
43
|
+
function getLocFromCommit(cwd, initialCommitSha) {
|
|
44
|
+
try {
|
|
45
|
+
const output = execSync(`git diff --numstat ${initialCommitSha} HEAD`, {
|
|
46
|
+
cwd,
|
|
47
|
+
encoding: "utf-8",
|
|
48
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
49
|
+
});
|
|
50
|
+
return parseGitDiff(output);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function buildPayload(stdin, read = readTranscript, checkExists = existsSync, readFile = readFileSync) {
|
|
21
57
|
const entries = read(stdin.transcript_path);
|
|
22
|
-
let inputTokens = 0, cachedInputTokens = 0, outputTokens = 0, toolCallCount = 0;
|
|
58
|
+
let inputTokens = 0, cachedInputTokens = 0, cacheCreationInputTokens = 0, outputTokens = 0, toolCallCount = 0;
|
|
23
59
|
let model = "unknown";
|
|
60
|
+
let initialCommitSha = null;
|
|
61
|
+
let durationMs = undefined;
|
|
62
|
+
const statePath = stdin.cwd ? `${stdin.cwd}/.jigit-session-${stdin.session_id}.json` : "";
|
|
63
|
+
if (statePath && checkExists(statePath)) {
|
|
64
|
+
try {
|
|
65
|
+
const stateRaw = readFile(statePath, "utf-8");
|
|
66
|
+
const state = JSON.parse(stateRaw);
|
|
67
|
+
if (state.initialCommitSha)
|
|
68
|
+
initialCommitSha = state.initialCommitSha;
|
|
69
|
+
if (state.totalDurationMs !== undefined)
|
|
70
|
+
durationMs = state.totalDurationMs;
|
|
71
|
+
}
|
|
72
|
+
catch { }
|
|
73
|
+
}
|
|
24
74
|
for (const e of entries) {
|
|
25
75
|
if (e.message?.role !== "assistant")
|
|
26
76
|
continue;
|
|
@@ -31,11 +81,21 @@ export function buildPayload(stdin, read = readTranscript) {
|
|
|
31
81
|
const u = e.message.usage;
|
|
32
82
|
if (u) {
|
|
33
83
|
inputTokens += u.input_tokens ?? 0;
|
|
34
|
-
cachedInputTokens +=
|
|
84
|
+
cachedInputTokens += u.cache_read_input_tokens ?? 0;
|
|
85
|
+
cacheCreationInputTokens += u.cache_creation_input_tokens ?? 0;
|
|
35
86
|
outputTokens += u.output_tokens ?? 0;
|
|
36
87
|
}
|
|
37
88
|
}
|
|
38
89
|
const startedAt = entries.find((e) => e.timestamp)?.timestamp ?? new Date().toISOString();
|
|
90
|
+
let linesAdded = undefined;
|
|
91
|
+
let linesRemoved = undefined;
|
|
92
|
+
if (stdin.cwd && initialCommitSha) {
|
|
93
|
+
const loc = getLocFromCommit(stdin.cwd, initialCommitSha);
|
|
94
|
+
if (loc) {
|
|
95
|
+
linesAdded = loc.linesAdded;
|
|
96
|
+
linesRemoved = loc.linesRemoved;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
39
99
|
return {
|
|
40
100
|
tool: "claude-code",
|
|
41
101
|
sessionId: stdin.session_id,
|
|
@@ -43,17 +103,58 @@ export function buildPayload(stdin, read = readTranscript) {
|
|
|
43
103
|
model,
|
|
44
104
|
inputTokens,
|
|
45
105
|
cachedInputTokens,
|
|
106
|
+
cacheCreationInputTokens,
|
|
46
107
|
outputTokens,
|
|
47
108
|
costUsd: null,
|
|
48
109
|
toolCallCount,
|
|
49
110
|
startedAt,
|
|
111
|
+
initialCommitSha: initialCommitSha ?? undefined,
|
|
112
|
+
durationMs,
|
|
113
|
+
linesAdded,
|
|
114
|
+
linesRemoved,
|
|
50
115
|
};
|
|
51
116
|
}
|
|
52
117
|
async function main() {
|
|
53
118
|
try {
|
|
54
119
|
const raw = readFileSync(0, "utf-8");
|
|
55
120
|
const stdin = JSON.parse(raw);
|
|
56
|
-
|
|
121
|
+
const payload = buildPayload(stdin);
|
|
122
|
+
await reportSession(payload);
|
|
123
|
+
if (payload.durationMs && stdin.cwd) {
|
|
124
|
+
const statePath = `${stdin.cwd}/.jigit-session-${stdin.session_id}.json`;
|
|
125
|
+
if (existsSync(statePath)) {
|
|
126
|
+
const baseUrl = process.env.JAGIT_BASE_URL?.trim();
|
|
127
|
+
const apiKey = process.env.JAGIT_API_KEY?.trim();
|
|
128
|
+
if (baseUrl && apiKey) {
|
|
129
|
+
try {
|
|
130
|
+
const res = await fetch(`${baseUrl}/api/agent-sessions?sessionId=${stdin.session_id}`, {
|
|
131
|
+
headers: { "x-api-key": apiKey },
|
|
132
|
+
});
|
|
133
|
+
if (res.ok) {
|
|
134
|
+
const data = await res.json();
|
|
135
|
+
const session = data.rows?.[0];
|
|
136
|
+
if (session?.jiraTicketId && session.costUsd) {
|
|
137
|
+
const baseTokens = calculateBaseTokens(session.costUsd);
|
|
138
|
+
if (baseTokens) {
|
|
139
|
+
await createJiraWorklog({
|
|
140
|
+
ticketId: session.jiraTicketId,
|
|
141
|
+
durationMs: payload.durationMs,
|
|
142
|
+
baseTokens,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
console.error("[hook-claude-code] Failed to create worklog:", err);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
rmSync(statePath, { force: true });
|
|
154
|
+
}
|
|
155
|
+
catch { }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
57
158
|
}
|
|
58
159
|
catch (err) {
|
|
59
160
|
console.error("[hook-claude-code]", err instanceof Error ? err.message : err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jagit/hook-claude-code",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"jagit-hook-claude-code": "dist/index.js"
|
|
@@ -8,16 +8,17 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"dist"
|
|
10
10
|
],
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "tsc -p tsconfig.json",
|
|
13
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
14
|
-
"test": "vitest run"
|
|
15
|
-
},
|
|
16
11
|
"dependencies": {
|
|
17
|
-
"@jagit/agent-reporter": "
|
|
12
|
+
"@jagit/agent-reporter": "0.0.2",
|
|
13
|
+
"@jagit/shared": "^0.0.2"
|
|
18
14
|
},
|
|
19
15
|
"devDependencies": {
|
|
20
16
|
"@types/node": "^25.9.3",
|
|
21
17
|
"vitest": "^2.1.9"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.json",
|
|
21
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
22
|
+
"test": "vitest run"
|
|
22
23
|
}
|
|
23
24
|
}
|