@hasna/hooks 0.0.1 → 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.js +366 -0
- package/hooks/hook-agentmessages/bin/cli.ts +125 -0
- package/package.json +2 -2
- package/hooks/hook-agentmessages/src/check-messages.ts +0 -151
- package/hooks/hook-agentmessages/src/install.ts +0 -126
- package/hooks/hook-agentmessages/src/session-start.ts +0 -255
- package/hooks/hook-agentmessages/src/uninstall.ts +0 -89
- package/hooks/hook-branchprotect/src/cli.ts +0 -126
- package/hooks/hook-branchprotect/src/hook.ts +0 -88
- package/hooks/hook-branchprotect/tsconfig.json +0 -25
- package/hooks/hook-checkbugs/src/cli.ts +0 -628
- package/hooks/hook-checkbugs/src/hook.ts +0 -335
- package/hooks/hook-checkbugs/tsconfig.json +0 -15
- package/hooks/hook-checkdocs/src/cli.ts +0 -628
- package/hooks/hook-checkdocs/src/hook.ts +0 -310
- package/hooks/hook-checkdocs/tsconfig.json +0 -15
- package/hooks/hook-checkfiles/src/cli.ts +0 -545
- package/hooks/hook-checkfiles/src/hook.ts +0 -321
- package/hooks/hook-checkfiles/tsconfig.json +0 -15
- package/hooks/hook-checklint/src/cli-patch.ts +0 -32
- package/hooks/hook-checklint/src/cli.ts +0 -667
- package/hooks/hook-checklint/src/hook.ts +0 -473
- package/hooks/hook-checklint/tsconfig.json +0 -15
- package/hooks/hook-checkpoint/src/cli.ts +0 -191
- package/hooks/hook-checkpoint/src/hook.ts +0 -207
- package/hooks/hook-checkpoint/tsconfig.json +0 -25
- package/hooks/hook-checksecurity/src/cli.ts +0 -601
- package/hooks/hook-checksecurity/src/hook.ts +0 -334
- package/hooks/hook-checksecurity/tsconfig.json +0 -15
- package/hooks/hook-checktasks/src/cli.ts +0 -578
- package/hooks/hook-checktasks/src/hook.ts +0 -308
- package/hooks/hook-checktasks/tsconfig.json +0 -20
- package/hooks/hook-checktests/src/cli.ts +0 -627
- package/hooks/hook-checktests/src/hook.ts +0 -334
- package/hooks/hook-checktests/tsconfig.json +0 -15
- package/hooks/hook-contextrefresh/src/cli.ts +0 -152
- package/hooks/hook-contextrefresh/src/hook.ts +0 -148
- package/hooks/hook-contextrefresh/tsconfig.json +0 -25
- package/hooks/hook-gitguard/src/cli.ts +0 -159
- package/hooks/hook-gitguard/src/hook.ts +0 -129
- package/hooks/hook-gitguard/tsconfig.json +0 -25
- package/hooks/hook-packageage/src/cli.ts +0 -165
- package/hooks/hook-packageage/src/hook.ts +0 -177
- package/hooks/hook-packageage/tsconfig.json +0 -25
- package/hooks/hook-phonenotify/src/cli.ts +0 -196
- package/hooks/hook-phonenotify/src/hook.ts +0 -139
- package/hooks/hook-phonenotify/tsconfig.json +0 -25
- package/hooks/hook-precompact/src/cli.ts +0 -168
- package/hooks/hook-precompact/src/hook.ts +0 -122
- package/hooks/hook-precompact/tsconfig.json +0 -25
- package/src/cli/components/App.tsx +0 -191
- package/src/cli/components/CategorySelect.tsx +0 -37
- package/src/cli/components/DataTable.tsx +0 -133
- package/src/cli/components/Header.tsx +0 -18
- package/src/cli/components/HookSelect.tsx +0 -29
- package/src/cli/components/InstallProgress.tsx +0 -105
- package/src/cli/components/SearchView.tsx +0 -86
- package/src/cli/index.tsx +0 -218
- package/src/index.ts +0 -31
- package/src/lib/installer.ts +0 -288
- package/src/lib/registry.ts +0 -205
- package/tsconfig.json +0 -17
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Claude Code Hook: checkpoint
|
|
5
|
-
*
|
|
6
|
-
* PreToolUse hook that creates shadow git snapshots before any file
|
|
7
|
-
* Write/Edit operations. This provides a safety net with easy rollback
|
|
8
|
-
* capability without cluttering the main project's git history.
|
|
9
|
-
*
|
|
10
|
-
* Shadow repo is stored in .claude-checkpoints/ (gitignored).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { readFileSync, existsSync, mkdirSync, writeFileSync, appendFileSync } from "fs";
|
|
14
|
-
import { execSync } from "child_process";
|
|
15
|
-
import { join, resolve } from "path";
|
|
16
|
-
|
|
17
|
-
interface HookInput {
|
|
18
|
-
session_id: string;
|
|
19
|
-
cwd: string;
|
|
20
|
-
tool_name: string;
|
|
21
|
-
tool_input: Record<string, unknown>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface HookOutput {
|
|
25
|
-
decision?: "approve" | "block";
|
|
26
|
-
reason?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const CHECKPOINT_DIR = ".claude-checkpoints";
|
|
30
|
-
const TOOLS_TO_CHECKPOINT = ["Write", "Edit", "NotebookEdit"];
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Read JSON from stdin
|
|
34
|
-
*/
|
|
35
|
-
function readStdinJson(): HookInput | null {
|
|
36
|
-
try {
|
|
37
|
-
const input = readFileSync(0, "utf-8").trim();
|
|
38
|
-
if (!input) return null;
|
|
39
|
-
return JSON.parse(input);
|
|
40
|
-
} catch {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Initialize shadow git repo for checkpoints
|
|
47
|
-
*/
|
|
48
|
-
function initShadowRepo(cwd: string): string {
|
|
49
|
-
const shadowPath = join(cwd, CHECKPOINT_DIR);
|
|
50
|
-
|
|
51
|
-
if (!existsSync(shadowPath)) {
|
|
52
|
-
mkdirSync(shadowPath, { recursive: true });
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const gitDir = join(shadowPath, ".git");
|
|
56
|
-
if (!existsSync(gitDir)) {
|
|
57
|
-
execSync("git init", { cwd: shadowPath, stdio: "pipe" });
|
|
58
|
-
execSync('git config user.email "hook-checkpoint@claude.local"', {
|
|
59
|
-
cwd: shadowPath,
|
|
60
|
-
stdio: "pipe",
|
|
61
|
-
});
|
|
62
|
-
execSync('git config user.name "hook-checkpoint"', {
|
|
63
|
-
cwd: shadowPath,
|
|
64
|
-
stdio: "pipe",
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Create initial commit
|
|
68
|
-
writeFileSync(join(shadowPath, ".checkpoint-init"), new Date().toISOString());
|
|
69
|
-
execSync("git add -A && git commit -m 'init: checkpoint repo'", {
|
|
70
|
-
cwd: shadowPath,
|
|
71
|
-
stdio: "pipe",
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Ensure .claude-checkpoints is gitignored in main project
|
|
76
|
-
const mainGitignore = join(cwd, ".gitignore");
|
|
77
|
-
if (existsSync(mainGitignore)) {
|
|
78
|
-
const content = readFileSync(mainGitignore, "utf-8");
|
|
79
|
-
if (!content.includes(CHECKPOINT_DIR)) {
|
|
80
|
-
appendFileSync(mainGitignore, `\n${CHECKPOINT_DIR}/\n`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return shadowPath;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Copy the target file into the shadow repo and commit
|
|
89
|
-
*/
|
|
90
|
-
function createCheckpoint(
|
|
91
|
-
cwd: string,
|
|
92
|
-
shadowPath: string,
|
|
93
|
-
toolName: string,
|
|
94
|
-
filePath: string,
|
|
95
|
-
sessionId: string
|
|
96
|
-
): void {
|
|
97
|
-
const absFilePath = resolve(cwd, filePath);
|
|
98
|
-
|
|
99
|
-
if (!existsSync(absFilePath)) {
|
|
100
|
-
// File doesn't exist yet (new file being created) — log but no snapshot needed
|
|
101
|
-
const logEntry = `[${new Date().toISOString()}] ${toolName} creating new file: ${filePath}\n`;
|
|
102
|
-
appendFileSync(join(shadowPath, "checkpoint.log"), logEntry);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Copy file to shadow repo preserving relative path
|
|
107
|
-
const relativePath = filePath.startsWith("/")
|
|
108
|
-
? filePath.replace(cwd, "").replace(/^\//, "")
|
|
109
|
-
: filePath;
|
|
110
|
-
const shadowFilePath = join(shadowPath, "files", relativePath);
|
|
111
|
-
const shadowFileDir = join(shadowFilePath, "..");
|
|
112
|
-
|
|
113
|
-
mkdirSync(shadowFileDir, { recursive: true });
|
|
114
|
-
|
|
115
|
-
const content = readFileSync(absFilePath);
|
|
116
|
-
writeFileSync(shadowFilePath, content);
|
|
117
|
-
|
|
118
|
-
// Create metadata
|
|
119
|
-
const metadata = {
|
|
120
|
-
tool: toolName,
|
|
121
|
-
file: filePath,
|
|
122
|
-
session: sessionId,
|
|
123
|
-
timestamp: new Date().toISOString(),
|
|
124
|
-
};
|
|
125
|
-
writeFileSync(
|
|
126
|
-
join(shadowPath, "last-checkpoint.json"),
|
|
127
|
-
JSON.stringify(metadata, null, 2)
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
// Commit to shadow repo
|
|
131
|
-
try {
|
|
132
|
-
execSync("git add -A", { cwd: shadowPath, stdio: "pipe" });
|
|
133
|
-
const msg = `checkpoint: ${toolName} ${relativePath}`;
|
|
134
|
-
execSync(`git commit -m "${msg}" --allow-empty`, {
|
|
135
|
-
cwd: shadowPath,
|
|
136
|
-
stdio: "pipe",
|
|
137
|
-
});
|
|
138
|
-
} catch {
|
|
139
|
-
// Commit may fail if nothing changed — that's fine
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Log
|
|
143
|
-
const logEntry = `[${new Date().toISOString()}] ${toolName} → ${relativePath} (session: ${sessionId})\n`;
|
|
144
|
-
appendFileSync(join(shadowPath, "checkpoint.log"), logEntry);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Extract file path from tool input
|
|
149
|
-
*/
|
|
150
|
-
function getFilePath(toolName: string, toolInput: Record<string, unknown>): string | null {
|
|
151
|
-
switch (toolName) {
|
|
152
|
-
case "Write":
|
|
153
|
-
case "Edit":
|
|
154
|
-
return (toolInput.file_path as string) || null;
|
|
155
|
-
case "NotebookEdit":
|
|
156
|
-
return (toolInput.notebook_path as string) || null;
|
|
157
|
-
default:
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Output hook response
|
|
164
|
-
*/
|
|
165
|
-
function respond(output: HookOutput): void {
|
|
166
|
-
console.log(JSON.stringify(output));
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Main hook execution
|
|
171
|
-
*/
|
|
172
|
-
export function run(): void {
|
|
173
|
-
const input = readStdinJson();
|
|
174
|
-
|
|
175
|
-
if (!input) {
|
|
176
|
-
respond({ decision: "approve" });
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Only checkpoint file-modifying tools
|
|
181
|
-
if (!TOOLS_TO_CHECKPOINT.includes(input.tool_name)) {
|
|
182
|
-
respond({ decision: "approve" });
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const filePath = getFilePath(input.tool_name, input.tool_input);
|
|
187
|
-
if (!filePath) {
|
|
188
|
-
respond({ decision: "approve" });
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
try {
|
|
193
|
-
const shadowPath = initShadowRepo(input.cwd);
|
|
194
|
-
createCheckpoint(input.cwd, shadowPath, input.tool_name, filePath, input.session_id);
|
|
195
|
-
} catch (error) {
|
|
196
|
-
// Never block operations due to checkpoint failures
|
|
197
|
-
const errMsg = error instanceof Error ? error.message : String(error);
|
|
198
|
-
console.error(`[hook-checkpoint] Warning: checkpoint failed: ${errMsg}`);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Always approve — checkpointing is non-blocking
|
|
202
|
-
respond({ decision: "approve" });
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (import.meta.main) {
|
|
206
|
-
run();
|
|
207
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ESNext",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"lib": ["ESNext"],
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"allowImportingTsExtensions": true,
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"forceConsistentCasingInFileNames": true,
|
|
12
|
-
"resolveJsonModule": true,
|
|
13
|
-
"isolatedModules": true,
|
|
14
|
-
"noEmit": true,
|
|
15
|
-
"noUnusedLocals": true,
|
|
16
|
-
"noUnusedParameters": true,
|
|
17
|
-
"declaration": true,
|
|
18
|
-
"declarationMap": true,
|
|
19
|
-
"outDir": "./dist",
|
|
20
|
-
"rootDir": "./src",
|
|
21
|
-
"types": ["bun-types"]
|
|
22
|
-
},
|
|
23
|
-
"include": ["src/**/*"],
|
|
24
|
-
"exclude": ["node_modules", "dist"]
|
|
25
|
-
}
|