claude-attribution 1.0.0 → 1.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/package.json +1 -1
- package/src/cli.ts +31 -9
- package/src/setup/install.ts +10 -21
- package/src/setup/templates/hooks.json +5 -5
- package/src/setup/templates/metrics-command.md +2 -2
- package/src/setup/templates/post-commit.sh +1 -1
- package/src/setup/templates/pr-command.md +4 -4
- package/src/setup/templates/pre-push.sh +1 -1
- package/src/setup/templates/start-command.md +1 -1
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -50,18 +50,40 @@ switch (cmd) {
|
|
|
50
50
|
case "post-commit":
|
|
51
51
|
await import("./attribution/commit.ts");
|
|
52
52
|
break;
|
|
53
|
-
case "pre-push":
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
case "pre-push": {
|
|
54
|
+
// Git passes pushed refs on stdin: "<local-ref> <local-sha> <remote-ref> <remote-sha>\n..."
|
|
55
|
+
// Skip notes push for tag-only and notes pushes to avoid SSH connection conflicts.
|
|
56
|
+
const stdin = await new Promise<string>((resolve) => {
|
|
57
|
+
let data = "";
|
|
58
|
+
process.stdin.setEncoding("utf8");
|
|
59
|
+
process.stdin.on("data", (chunk: string) => (data += chunk));
|
|
60
|
+
process.stdin.on("end", () => resolve(data));
|
|
61
|
+
});
|
|
62
|
+
const isBranchPush = stdin
|
|
63
|
+
.trim()
|
|
64
|
+
.split("\n")
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.some((line) => {
|
|
67
|
+
const remoteRef = line.split(" ")[2] ?? "";
|
|
68
|
+
return (
|
|
69
|
+
!remoteRef.startsWith("refs/tags/") &&
|
|
70
|
+
!remoteRef.startsWith("refs/notes/")
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
if (isBranchPush) {
|
|
74
|
+
try {
|
|
75
|
+
await execFileAsync("git", [
|
|
76
|
+
"push",
|
|
77
|
+
"origin",
|
|
78
|
+
"refs/notes/claude-attribution",
|
|
79
|
+
]);
|
|
80
|
+
} catch {
|
|
81
|
+
// Ignore — notes push failure must not block git push
|
|
82
|
+
}
|
|
62
83
|
}
|
|
63
84
|
process.exit(0);
|
|
64
85
|
break;
|
|
86
|
+
}
|
|
65
87
|
default:
|
|
66
88
|
console.error(`Unknown hook: ${rest[0]}`);
|
|
67
89
|
process.exit(1);
|
package/src/setup/install.ts
CHANGED
|
@@ -86,7 +86,7 @@ function detectHookManager(repoRoot: string): "husky" | "lefthook" | "none" {
|
|
|
86
86
|
|
|
87
87
|
async function installPrePushHook(repoRoot: string): Promise<void> {
|
|
88
88
|
const manager = detectHookManager(repoRoot);
|
|
89
|
-
const runLine = `
|
|
89
|
+
const runLine = `claude-attribution hook pre-push || true`;
|
|
90
90
|
|
|
91
91
|
if (manager === "husky") {
|
|
92
92
|
const huskyHook = join(repoRoot, ".husky", "pre-push");
|
|
@@ -126,14 +126,12 @@ async function installPrePushHook(repoRoot: string): Promise<void> {
|
|
|
126
126
|
join(ATTRIBUTION_ROOT, "src", "setup", "templates", "pre-push.sh"),
|
|
127
127
|
"utf8",
|
|
128
128
|
);
|
|
129
|
-
const template = rawTemplate.replace(/\{\{CLI_BIN\}\}/g, () => CLI_BIN);
|
|
130
|
-
|
|
131
129
|
if (existsSync(hookDest)) {
|
|
132
130
|
const existing = await readFile(hookDest, "utf8");
|
|
133
131
|
if (!existing.includes("claude-attribution")) {
|
|
134
132
|
await writeFile(
|
|
135
133
|
hookDest,
|
|
136
|
-
existing.trimEnd() + "\n\n# claude-attribution\n" +
|
|
134
|
+
existing.trimEnd() + "\n\n# claude-attribution\n" + rawTemplate,
|
|
137
135
|
);
|
|
138
136
|
await chmod(hookDest, 0o755);
|
|
139
137
|
return;
|
|
@@ -141,13 +139,13 @@ async function installPrePushHook(repoRoot: string): Promise<void> {
|
|
|
141
139
|
// Already ours — replace
|
|
142
140
|
}
|
|
143
141
|
|
|
144
|
-
await writeFile(hookDest,
|
|
142
|
+
await writeFile(hookDest, rawTemplate);
|
|
145
143
|
await chmod(hookDest, 0o755);
|
|
146
144
|
}
|
|
147
145
|
|
|
148
146
|
async function installGitHook(repoRoot: string): Promise<void> {
|
|
149
147
|
const manager = detectHookManager(repoRoot);
|
|
150
|
-
const runLine = `
|
|
148
|
+
const runLine = `claude-attribution hook post-commit || true`;
|
|
151
149
|
|
|
152
150
|
if (manager === "husky") {
|
|
153
151
|
// Husky: add post-commit file to .husky/ directory
|
|
@@ -189,14 +187,13 @@ async function installGitHook(repoRoot: string): Promise<void> {
|
|
|
189
187
|
join(ATTRIBUTION_ROOT, "src", "setup", "templates", "post-commit.sh"),
|
|
190
188
|
"utf8",
|
|
191
189
|
);
|
|
192
|
-
const newContent = template.replace(/\{\{CLI_BIN\}\}/g, () => CLI_BIN);
|
|
193
190
|
|
|
194
191
|
if (existsSync(hookDest)) {
|
|
195
192
|
const existing = await readFile(hookDest, "utf8");
|
|
196
193
|
if (!existing.includes("claude-attribution")) {
|
|
197
194
|
await writeFile(
|
|
198
195
|
hookDest,
|
|
199
|
-
existing.trimEnd() + "\n\n# claude-attribution\n" +
|
|
196
|
+
existing.trimEnd() + "\n\n# claude-attribution\n" + template,
|
|
200
197
|
);
|
|
201
198
|
await chmod(hookDest, 0o755);
|
|
202
199
|
return;
|
|
@@ -204,7 +201,7 @@ async function installGitHook(repoRoot: string): Promise<void> {
|
|
|
204
201
|
// Already ours — replace
|
|
205
202
|
}
|
|
206
203
|
|
|
207
|
-
await writeFile(hookDest,
|
|
204
|
+
await writeFile(hookDest, template);
|
|
208
205
|
await chmod(hookDest, 0o755);
|
|
209
206
|
}
|
|
210
207
|
|
|
@@ -245,9 +242,7 @@ async function main() {
|
|
|
245
242
|
join(ATTRIBUTION_ROOT, "src", "setup", "templates", "hooks.json"),
|
|
246
243
|
"utf8",
|
|
247
244
|
);
|
|
248
|
-
const hooksConfig = JSON.parse(
|
|
249
|
-
hooksTemplate.replace(/\{\{CLI_BIN\}\}/g, () => CLI_BIN),
|
|
250
|
-
) as HooksConfig;
|
|
245
|
+
const hooksConfig = JSON.parse(hooksTemplate) as HooksConfig;
|
|
251
246
|
|
|
252
247
|
await mergeHooks(settingsPath, hooksConfig);
|
|
253
248
|
console.log("✓ Merged hooks into .claude/settings.json");
|
|
@@ -288,27 +283,21 @@ async function main() {
|
|
|
288
283
|
join(ATTRIBUTION_ROOT, "src", "setup", "templates", "metrics-command.md"),
|
|
289
284
|
"utf8",
|
|
290
285
|
);
|
|
291
|
-
|
|
292
|
-
/\{\{CLI_BIN\}\}/g,
|
|
293
|
-
() => CLI_BIN,
|
|
294
|
-
);
|
|
295
|
-
await writeFile(join(commandsDir, "metrics.md"), metricsContent);
|
|
286
|
+
await writeFile(join(commandsDir, "metrics.md"), metricsTemplate);
|
|
296
287
|
console.log("✓ Installed .claude/commands/metrics.md (/metrics command)");
|
|
297
288
|
|
|
298
289
|
const startTemplate = await readFile(
|
|
299
290
|
join(ATTRIBUTION_ROOT, "src", "setup", "templates", "start-command.md"),
|
|
300
291
|
"utf8",
|
|
301
292
|
);
|
|
302
|
-
|
|
303
|
-
await writeFile(join(commandsDir, "start.md"), startContent);
|
|
293
|
+
await writeFile(join(commandsDir, "start.md"), startTemplate);
|
|
304
294
|
console.log("✓ Installed .claude/commands/start.md (/start command)");
|
|
305
295
|
|
|
306
296
|
const prTemplate = await readFile(
|
|
307
297
|
join(ATTRIBUTION_ROOT, "src", "setup", "templates", "pr-command.md"),
|
|
308
298
|
"utf8",
|
|
309
299
|
);
|
|
310
|
-
|
|
311
|
-
await writeFile(join(commandsDir, "pr.md"), prContent);
|
|
300
|
+
await writeFile(join(commandsDir, "pr.md"), prTemplate);
|
|
312
301
|
console.log("✓ Installed .claude/commands/pr.md (/pr command)");
|
|
313
302
|
|
|
314
303
|
console.log("\nDone! claude-attribution is active for this repo.");
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"hooks": [
|
|
6
6
|
{
|
|
7
7
|
"type": "command",
|
|
8
|
-
"command": "
|
|
8
|
+
"command": "claude-attribution hook pre-tool-use"
|
|
9
9
|
}
|
|
10
10
|
]
|
|
11
11
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"hooks": [
|
|
17
17
|
{
|
|
18
18
|
"type": "command",
|
|
19
|
-
"command": "
|
|
19
|
+
"command": "claude-attribution hook post-tool-use"
|
|
20
20
|
}
|
|
21
21
|
]
|
|
22
22
|
}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"hooks": [
|
|
28
28
|
{
|
|
29
29
|
"type": "command",
|
|
30
|
-
"command": "
|
|
30
|
+
"command": "claude-attribution hook subagent"
|
|
31
31
|
}
|
|
32
32
|
]
|
|
33
33
|
}
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"hooks": [
|
|
39
39
|
{
|
|
40
40
|
"type": "command",
|
|
41
|
-
"command": "
|
|
41
|
+
"command": "claude-attribution hook subagent"
|
|
42
42
|
}
|
|
43
43
|
]
|
|
44
44
|
}
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"hooks": [
|
|
50
50
|
{
|
|
51
51
|
"type": "command",
|
|
52
|
-
"command": "
|
|
52
|
+
"command": "claude-attribution hook stop"
|
|
53
53
|
}
|
|
54
54
|
]
|
|
55
55
|
}
|
|
@@ -7,7 +7,7 @@ Generate session metrics for PR documentation.
|
|
|
7
7
|
Run the metrics calculator:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
10
|
+
claude-attribution metrics
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
This outputs PR-ready markdown covering:
|
|
@@ -21,7 +21,7 @@ This outputs PR-ready markdown covering:
|
|
|
21
21
|
To run with a specific session ID:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
|
|
24
|
+
claude-attribution metrics <session-id>
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Session IDs are in `.claude/logs/tool-usage.jsonl`.
|
|
@@ -5,7 +5,7 @@ Run this when you're ready to open a pull request. Collects AI metrics and creat
|
|
|
5
5
|
## Instructions
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
claude-attribution pr "feat: your PR title"
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
This will:
|
|
@@ -17,9 +17,9 @@ This will:
|
|
|
17
17
|
## Options
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
claude-attribution pr "feat: title" --draft # Open as draft PR
|
|
21
|
+
claude-attribution pr "feat: title" --base develop # Target a different base branch
|
|
22
|
+
claude-attribution pr # Title derived from branch name
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Requirements
|