claude-spp 0.1.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.
Files changed (47) hide show
  1. package/LICENSE.txt +21 -0
  2. package/README.md +147 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +164 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config/loader.d.ts +29 -0
  8. package/dist/config/loader.d.ts.map +1 -0
  9. package/dist/config/loader.js +81 -0
  10. package/dist/config/loader.js.map +1 -0
  11. package/dist/config/schema.d.ts +84 -0
  12. package/dist/config/schema.d.ts.map +1 -0
  13. package/dist/config/schema.js +104 -0
  14. package/dist/config/schema.js.map +1 -0
  15. package/dist/git/history.d.ts +55 -0
  16. package/dist/git/history.d.ts.map +1 -0
  17. package/dist/git/history.js +376 -0
  18. package/dist/git/history.js.map +1 -0
  19. package/dist/hooks/file-matcher.d.ts +22 -0
  20. package/dist/hooks/file-matcher.d.ts.map +1 -0
  21. package/dist/hooks/file-matcher.js +86 -0
  22. package/dist/hooks/file-matcher.js.map +1 -0
  23. package/dist/hooks/index.d.ts +4 -0
  24. package/dist/hooks/index.d.ts.map +1 -0
  25. package/dist/hooks/index.js +4 -0
  26. package/dist/hooks/index.js.map +1 -0
  27. package/dist/hooks/pre-tool-use.d.ts +35 -0
  28. package/dist/hooks/pre-tool-use.d.ts.map +1 -0
  29. package/dist/hooks/pre-tool-use.js +132 -0
  30. package/dist/hooks/pre-tool-use.js.map +1 -0
  31. package/dist/hooks/system-prompt.d.ts +9 -0
  32. package/dist/hooks/system-prompt.d.ts.map +1 -0
  33. package/dist/hooks/system-prompt.js +88 -0
  34. package/dist/hooks/system-prompt.js.map +1 -0
  35. package/dist/index.d.ts +8 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +13 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/init.d.ts +25 -0
  40. package/dist/init.d.ts.map +1 -0
  41. package/dist/init.js +274 -0
  42. package/dist/init.js.map +1 -0
  43. package/dist/stats.d.ts +40 -0
  44. package/dist/stats.d.ts.map +1 -0
  45. package/dist/stats.js +146 -0
  46. package/dist/stats.js.map +1 -0
  47. package/package.json +41 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Line count result
3
+ */
4
+ export interface LineCounts {
5
+ humanLines: number;
6
+ claudeLines: number;
7
+ humanCommits: number;
8
+ claudeCommits: number;
9
+ fromCache: boolean;
10
+ commitsScanned: number;
11
+ }
12
+ /**
13
+ * Get the current HEAD commit hash (exported version)
14
+ */
15
+ export declare function getHeadCommitHash(projectPath: string): string | null;
16
+ /**
17
+ * Get total number of commits in the repo
18
+ */
19
+ export declare function getTotalCommitCount(projectPath: string): number;
20
+ /**
21
+ * Commit info for display
22
+ */
23
+ export interface CommitInfo {
24
+ shortHash: string;
25
+ title: string;
26
+ date: Date;
27
+ }
28
+ /**
29
+ * Get info about a specific commit
30
+ * Returns null if the commit doesn't exist
31
+ */
32
+ export declare function getCommitInfo(projectPath: string, commitHash: string): CommitInfo | null;
33
+ /**
34
+ * Get the hash of the Nth commit (1-indexed, chronological order)
35
+ * Returns null if there aren't enough commits
36
+ */
37
+ export declare function getNthCommitHash(projectPath: string, n: number): string | null;
38
+ /**
39
+ * Clear the cache
40
+ */
41
+ export declare function clearCache(projectPath: string): void;
42
+ /**
43
+ * Calculate line counts from git history with caching
44
+ */
45
+ export declare function getLineCounts(projectPath: string): LineCounts;
46
+ /**
47
+ * Get line counts with optional filters
48
+ * @param since If null, no time filter. If Date, filters commits after this date.
49
+ * @param afterCommit If provided, only includes commits after this commit hash (exclusive).
50
+ */
51
+ export declare function getLineCountsWithWindow(projectPath: string, options: {
52
+ since: Date | null;
53
+ afterCommit?: string | null;
54
+ }): LineCounts;
55
+ //# sourceMappingURL=history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/git/history.ts"],"names":[],"mappings":"AAqBA;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AA4BD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEpE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAU/D;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;CACZ;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAiBxF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB9E;AAyDD;;GAEG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAKpD;AAkHD;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CA6E7D;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE;IAAE,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC3D,UAAU,CA6CZ"}
@@ -0,0 +1,376 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { execSync } from "node:child_process";
4
+ import { z } from "zod";
5
+ const CACHE_FILE = ".claude-spp/.git_history_cache.json";
6
+ /**
7
+ * Cache schema for git history line counts
8
+ */
9
+ const GitHistoryCacheSchema = z.object({
10
+ userEmail: z.string(),
11
+ lastCommit: z.string(),
12
+ humanLines: z.number().int().min(0),
13
+ claudeLines: z.number().int().min(0),
14
+ humanCommits: z.number().int().min(0),
15
+ claudeCommits: z.number().int().min(0),
16
+ });
17
+ /**
18
+ * Check if we're in a git repository
19
+ */
20
+ function isGitRepo(projectPath) {
21
+ try {
22
+ execSync("git rev-parse --git-dir", { cwd: projectPath, stdio: "ignore" });
23
+ return true;
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ /**
30
+ * Get the current HEAD commit hash
31
+ */
32
+ function getHeadCommit(projectPath) {
33
+ try {
34
+ return execSync("git rev-parse HEAD", {
35
+ cwd: projectPath,
36
+ encoding: "utf-8",
37
+ }).trim();
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ }
43
+ /**
44
+ * Get the current HEAD commit hash (exported version)
45
+ */
46
+ export function getHeadCommitHash(projectPath) {
47
+ return getHeadCommit(projectPath);
48
+ }
49
+ /**
50
+ * Get total number of commits in the repo
51
+ */
52
+ export function getTotalCommitCount(projectPath) {
53
+ try {
54
+ const output = execSync("git rev-list --count HEAD", {
55
+ cwd: projectPath,
56
+ encoding: "utf-8",
57
+ });
58
+ return parseInt(output.trim(), 10) || 0;
59
+ }
60
+ catch {
61
+ return 0;
62
+ }
63
+ }
64
+ /**
65
+ * Get info about a specific commit
66
+ * Returns null if the commit doesn't exist
67
+ */
68
+ export function getCommitInfo(projectPath, commitHash) {
69
+ // Format: short hash, subject (title), ISO date
70
+ const output = execSync(`git log -1 --format="%h%x00%s%x00%cI" ${commitHash}`, {
71
+ cwd: projectPath,
72
+ encoding: "utf-8",
73
+ }).trim();
74
+ if (!output) {
75
+ return null;
76
+ }
77
+ const [shortHash, title, dateStr] = output.split("\0");
78
+ return {
79
+ shortHash,
80
+ title,
81
+ date: new Date(dateStr),
82
+ };
83
+ }
84
+ /**
85
+ * Get the hash of the Nth commit (1-indexed, chronological order)
86
+ * Returns null if there aren't enough commits
87
+ */
88
+ export function getNthCommitHash(projectPath, n) {
89
+ try {
90
+ // Get all commits in chronological order (oldest first)
91
+ const output = execSync(`git rev-list --reverse HEAD`, {
92
+ cwd: projectPath,
93
+ encoding: "utf-8",
94
+ });
95
+ const commits = output.trim().split("\n").filter(Boolean);
96
+ if (commits.length >= n) {
97
+ return commits[n - 1]; // n is 1-indexed
98
+ }
99
+ return null;
100
+ }
101
+ catch {
102
+ return null;
103
+ }
104
+ }
105
+ /**
106
+ * Check if a commit is an ancestor of HEAD
107
+ */
108
+ function isAncestor(projectPath, commit) {
109
+ try {
110
+ execSync(`git merge-base --is-ancestor ${commit} HEAD`, {
111
+ cwd: projectPath,
112
+ stdio: "ignore",
113
+ });
114
+ return true;
115
+ }
116
+ catch {
117
+ return false;
118
+ }
119
+ }
120
+ /**
121
+ * Get the cache file path
122
+ */
123
+ function getCachePath(projectPath) {
124
+ return path.join(projectPath, CACHE_FILE);
125
+ }
126
+ /**
127
+ * Load cache from file
128
+ */
129
+ function loadCache(projectPath) {
130
+ const cachePath = getCachePath(projectPath);
131
+ if (!fs.existsSync(cachePath)) {
132
+ return null;
133
+ }
134
+ try {
135
+ const raw = fs.readFileSync(cachePath, "utf-8");
136
+ const json = JSON.parse(raw);
137
+ return GitHistoryCacheSchema.parse(json);
138
+ }
139
+ catch {
140
+ return null;
141
+ }
142
+ }
143
+ /**
144
+ * Save cache to file
145
+ */
146
+ function saveCache(projectPath, cache) {
147
+ const cachePath = getCachePath(projectPath);
148
+ const sppDir = path.dirname(cachePath);
149
+ if (!fs.existsSync(sppDir)) {
150
+ fs.mkdirSync(sppDir, { recursive: true });
151
+ }
152
+ fs.writeFileSync(cachePath, JSON.stringify(cache, null, 2) + "\n", "utf-8");
153
+ }
154
+ /**
155
+ * Clear the cache
156
+ */
157
+ export function clearCache(projectPath) {
158
+ const cachePath = getCachePath(projectPath);
159
+ if (fs.existsSync(cachePath)) {
160
+ fs.unlinkSync(cachePath);
161
+ }
162
+ }
163
+ /**
164
+ * Parse a commit to get line counts and attribution
165
+ */
166
+ function parseCommit(projectPath, commitHash, parentHash) {
167
+ // Check if commit is co-authored by Claude
168
+ const message = execSync(`git log -1 --format="%B" ${commitHash}`, {
169
+ cwd: projectPath,
170
+ encoding: "utf-8",
171
+ });
172
+ const isClaude = /Co-Authored-By:.*Claude/i.test(message);
173
+ // Get lines added in this commit
174
+ let linesAdded = 0;
175
+ let linesDeleted = 0;
176
+ try {
177
+ let diffCmd;
178
+ if (parentHash) {
179
+ diffCmd = `git diff --numstat ${parentHash} ${commitHash}`;
180
+ }
181
+ else {
182
+ // First commit - diff against empty tree
183
+ diffCmd = `git diff --numstat 4b825dc642cb6eb9a060e54bf8d69288fbee4904 ${commitHash}`;
184
+ }
185
+ const numstat = execSync(diffCmd, {
186
+ cwd: projectPath,
187
+ encoding: "utf-8",
188
+ });
189
+ // Parse numstat output: "added\tdeleted\tfilename"
190
+ for (const line of numstat.split("\n")) {
191
+ if (!line.trim())
192
+ continue;
193
+ const parts = line.split("\t");
194
+ if (parts.length >= 2) {
195
+ const added = parseInt(parts[0], 10);
196
+ const deleted = parseInt(parts[1], 10);
197
+ if (!isNaN(added)) {
198
+ linesAdded += added;
199
+ }
200
+ if (!isNaN(deleted)) {
201
+ linesDeleted += deleted;
202
+ }
203
+ }
204
+ }
205
+ }
206
+ catch {
207
+ // Ignore errors (e.g., binary files)
208
+ }
209
+ return {
210
+ humanLines: isClaude ? 0 : linesAdded + linesDeleted,
211
+ claudeLines: isClaude ? linesAdded + linesDeleted : 0,
212
+ isClaudeCommit: isClaude,
213
+ };
214
+ }
215
+ function getCurrentGitUser(projectPath) {
216
+ const gitUser = execSync("git config user.email", {
217
+ cwd: projectPath,
218
+ encoding: "utf-8",
219
+ }).trim();
220
+ if (!gitUser) {
221
+ throw new Error("git user.email is not set.");
222
+ }
223
+ return gitUser;
224
+ }
225
+ /**
226
+ * Get commit hashes from startCommit (exclusive) to endCommit (inclusive)
227
+ * Returns array of { hash, parent } objects, oldest first
228
+ * @param since Optional date to filter commits (git --since flag)
229
+ */
230
+ function getCommitRange(projectPath, startCommit, endCommit, since) {
231
+ try {
232
+ const range = startCommit ? `${startCommit}..${endCommit}` : endCommit;
233
+ // Build git log command with optional --since flag
234
+ const sinceArg = since ? `--since="${since.toISOString()}"` : "";
235
+ const userEmail = getCurrentGitUser(projectPath);
236
+ const authorArg = userEmail ? `--author="${userEmail}"` : "";
237
+ const cmd = `git log --reverse --format="%H %P" ${authorArg} ${sinceArg} ${range}`.trim();
238
+ // Get commits with their parents
239
+ const output = execSync(cmd, {
240
+ cwd: projectPath,
241
+ encoding: "utf-8",
242
+ });
243
+ const commits = [];
244
+ for (const line of output.split("\n")) {
245
+ if (!line.trim())
246
+ continue;
247
+ const parts = line.trim().split(" ");
248
+ const hash = parts[0];
249
+ const parent = parts[1] || null;
250
+ commits.push({ hash, parent });
251
+ }
252
+ return commits;
253
+ }
254
+ catch {
255
+ return [];
256
+ }
257
+ }
258
+ /**
259
+ * Calculate line counts from git history with caching
260
+ */
261
+ export function getLineCounts(projectPath) {
262
+ // Default result for non-git repos
263
+ if (!isGitRepo(projectPath)) {
264
+ throw new Error("Must be a git repo");
265
+ }
266
+ const head = getHeadCommit(projectPath);
267
+ if (!head) {
268
+ throw new Error("Head commit not found");
269
+ }
270
+ const currentGitUser = getCurrentGitUser(projectPath);
271
+ // Try to load cache
272
+ const cache = loadCache(projectPath);
273
+ // Check if cache is valid and up to date
274
+ if (cache && cache.lastCommit === head && cache.userEmail === currentGitUser) {
275
+ return {
276
+ humanLines: cache.humanLines,
277
+ claudeLines: cache.claudeLines,
278
+ humanCommits: cache.humanCommits,
279
+ claudeCommits: cache.claudeCommits,
280
+ fromCache: true,
281
+ commitsScanned: 0,
282
+ };
283
+ }
284
+ // Check if cache is valid but needs incremental update
285
+ let startCommit = null;
286
+ let humanLines = 0;
287
+ let claudeLines = 0;
288
+ let humanCommits = 0;
289
+ let claudeCommits = 0;
290
+ if (cache && cache.userEmail === currentGitUser && isAncestor(projectPath, cache.lastCommit)) {
291
+ // Cache is valid, start from there
292
+ startCommit = cache.lastCommit;
293
+ humanLines = cache.humanLines;
294
+ claudeLines = cache.claudeLines;
295
+ humanCommits = cache.humanCommits;
296
+ claudeCommits = cache.claudeCommits;
297
+ }
298
+ // Get commits to process
299
+ const commits = getCommitRange(projectPath, startCommit, head);
300
+ // Process each commit
301
+ for (const { hash, parent } of commits) {
302
+ const result = parseCommit(projectPath, hash, parent);
303
+ humanLines += result.humanLines;
304
+ claudeLines += result.claudeLines;
305
+ if (result.isClaudeCommit) {
306
+ claudeCommits++;
307
+ }
308
+ else {
309
+ humanCommits++;
310
+ }
311
+ }
312
+ // Save updated cache
313
+ saveCache(projectPath, {
314
+ userEmail: currentGitUser,
315
+ lastCommit: head,
316
+ humanLines,
317
+ claudeLines,
318
+ humanCommits,
319
+ claudeCommits,
320
+ });
321
+ return {
322
+ humanLines,
323
+ claudeLines,
324
+ humanCommits,
325
+ claudeCommits,
326
+ fromCache: false,
327
+ commitsScanned: commits.length,
328
+ };
329
+ }
330
+ /**
331
+ * Get line counts with optional filters
332
+ * @param since If null, no time filter. If Date, filters commits after this date.
333
+ * @param afterCommit If provided, only includes commits after this commit hash (exclusive).
334
+ */
335
+ export function getLineCountsWithWindow(projectPath, options) {
336
+ // If no filters at all, use the cached version
337
+ if (options.since === null && !options.afterCommit) {
338
+ return getLineCounts(projectPath);
339
+ }
340
+ // Filtered query bypasses cache
341
+ if (!isGitRepo(projectPath)) {
342
+ throw new Error("Must be a git repo");
343
+ }
344
+ const head = getHeadCommit(projectPath);
345
+ if (!head) {
346
+ throw new Error("Head commit not found");
347
+ }
348
+ // Get commits with optional start commit filter
349
+ const startCommit = options.afterCommit ?? null;
350
+ const commits = getCommitRange(projectPath, startCommit, head, options.since ?? undefined);
351
+ let humanLines = 0;
352
+ let claudeLines = 0;
353
+ let humanCommits = 0;
354
+ let claudeCommits = 0;
355
+ // Process each commit
356
+ for (const { hash, parent } of commits) {
357
+ const result = parseCommit(projectPath, hash, parent);
358
+ humanLines += result.humanLines;
359
+ claudeLines += result.claudeLines;
360
+ if (result.isClaudeCommit) {
361
+ claudeCommits++;
362
+ }
363
+ else {
364
+ humanCommits++;
365
+ }
366
+ }
367
+ return {
368
+ humanLines,
369
+ claudeLines,
370
+ humanCommits,
371
+ claudeCommits,
372
+ fromCache: false,
373
+ commitsScanned: commits.length,
374
+ };
375
+ }
376
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/git/history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Normalize a file path to be relative to the project root
3
+ * Handles both absolute and relative paths
4
+ */
5
+ export declare function normalizeFilePath(filePath: string, projectPath: string): string;
6
+ /**
7
+ * Check if a file path matches a pattern
8
+ * Pattern can be:
9
+ * - Exact path: "src/test.ts"
10
+ * - Directory prefix: "src/components/" (matches files in that dir)
11
+ * - Glob pattern: "src/**\/*.ts", "src/*.ts"
12
+ */
13
+ export declare function fileMatchesPattern(filePath: string, pattern: string, projectPath: string): boolean;
14
+ /**
15
+ * Check if a file matches any of the given patterns
16
+ */
17
+ export declare function fileMatchesPatterns(filePath: string, patterns: string[], projectPath: string): boolean;
18
+ /**
19
+ * Check if a file path is within the .claude-spp directory
20
+ */
21
+ export declare function isSppInternalFile(filePath: string, projectPath: string): boolean;
22
+ //# sourceMappingURL=file-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-matcher.d.ts","sourceRoot":"","sources":["../../src/hooks/file-matcher.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAe/E;AA2BD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAClB,OAAO,CA6BT;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAAE,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAIT;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAGhF"}
@@ -0,0 +1,86 @@
1
+ import * as path from "node:path";
2
+ /**
3
+ * Normalize a file path to be relative to the project root
4
+ * Handles both absolute and relative paths
5
+ */
6
+ export function normalizeFilePath(filePath, projectPath) {
7
+ // If already relative, return as-is
8
+ if (!path.isAbsolute(filePath)) {
9
+ return filePath;
10
+ }
11
+ // Make absolute path relative to project
12
+ const relativePath = path.relative(projectPath, filePath);
13
+ // If the path goes outside the project (starts with ..), return original
14
+ if (relativePath.startsWith("..")) {
15
+ return filePath;
16
+ }
17
+ return relativePath;
18
+ }
19
+ /**
20
+ * Convert a simple glob pattern to a regex
21
+ * Supports: * (any chars except /), ** (any chars including /), ? (single char)
22
+ */
23
+ function globToRegex(pattern) {
24
+ let regex = pattern
25
+ // Escape special regex chars except * and ?
26
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
27
+ // **/ at the start or after / should match zero or more directories
28
+ .replace(/\*\*\//g, "<<<GLOBSTAR_SLASH>>>")
29
+ // Remaining ** matches anything
30
+ .replace(/\*\*/g, "<<<GLOBSTAR>>>")
31
+ // * matches anything except path separators
32
+ .replace(/\*/g, "[^/]*")
33
+ // ? matches single character
34
+ .replace(/\?/g, ".")
35
+ // **/ should match zero or more directories (including empty)
36
+ .replace(/<<<GLOBSTAR_SLASH>>>/g, "(?:.*\\/)?")
37
+ // ** matches anything
38
+ .replace(/<<<GLOBSTAR>>>/g, ".*");
39
+ // Anchor to start and end
40
+ return new RegExp(`^${regex}$`);
41
+ }
42
+ /**
43
+ * Check if a file path matches a pattern
44
+ * Pattern can be:
45
+ * - Exact path: "src/test.ts"
46
+ * - Directory prefix: "src/components/" (matches files in that dir)
47
+ * - Glob pattern: "src/**\/*.ts", "src/*.ts"
48
+ */
49
+ export function fileMatchesPattern(filePath, pattern, projectPath) {
50
+ // Normalize the file path
51
+ const normalizedFile = normalizeFilePath(filePath, projectPath);
52
+ // Normalize the pattern (remove leading ./)
53
+ let normalizedPattern = pattern.replace(/^\.\//, "");
54
+ // If pattern ends with /, it's a directory prefix - match anything inside
55
+ if (normalizedPattern.endsWith("/")) {
56
+ return normalizedFile.startsWith(normalizedPattern);
57
+ }
58
+ // If pattern contains glob characters, use glob matching
59
+ if (normalizedPattern.includes("*") || normalizedPattern.includes("?")) {
60
+ const regex = globToRegex(normalizedPattern);
61
+ return regex.test(normalizedFile);
62
+ }
63
+ // Otherwise, exact match or directory prefix match
64
+ if (normalizedFile === normalizedPattern) {
65
+ return true;
66
+ }
67
+ // Also match if pattern is a directory and file is inside it
68
+ if (normalizedFile.startsWith(normalizedPattern + "/")) {
69
+ return true;
70
+ }
71
+ return false;
72
+ }
73
+ /**
74
+ * Check if a file matches any of the given patterns
75
+ */
76
+ export function fileMatchesPatterns(filePath, patterns, projectPath) {
77
+ return patterns.some((pattern) => fileMatchesPattern(filePath, pattern, projectPath));
78
+ }
79
+ /**
80
+ * Check if a file path is within the .claude-spp directory
81
+ */
82
+ export function isSppInternalFile(filePath, projectPath) {
83
+ const normalizedFile = normalizeFilePath(filePath, projectPath);
84
+ return normalizedFile.startsWith(".claude-spp/") || normalizedFile === ".claude-spp";
85
+ }
86
+ //# sourceMappingURL=file-matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-matcher.js","sourceRoot":"","sources":["../../src/hooks/file-matcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { generateSystemPrompt, generateStatusLine } from "./system-prompt.js";
2
+ export { preToolUseHook, runPreToolUseHook, type PreToolUseHookInput, type PreToolUseHookOutput, } from "./pre-tool-use.js";
3
+ export { normalizeFilePath, fileMatchesPattern, fileMatchesPatterns, isSppInternalFile, } from "./file-matcher.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { generateSystemPrompt, generateStatusLine } from "./system-prompt.js";
2
+ export { preToolUseHook, runPreToolUseHook, } from "./pre-tool-use.js";
3
+ export { normalizeFilePath, fileMatchesPattern, fileMatchesPatterns, isSppInternalFile, } from "./file-matcher.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EACL,cAAc,EACd,iBAAiB,GAGlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Hook input from Claude Code (actual format)
3
+ */
4
+ export interface PreToolUseHookInput {
5
+ session_id?: string;
6
+ transcript_path?: string;
7
+ cwd: string;
8
+ permission_mode?: string;
9
+ hook_event_name?: string;
10
+ tool_name: string;
11
+ tool_input: Record<string, unknown>;
12
+ tool_use_id?: string;
13
+ }
14
+ /**
15
+ * Hook output to Claude Code (correct format)
16
+ */
17
+ export interface PreToolUseHookOutput {
18
+ hookSpecificOutput: {
19
+ hookEventName: "PreToolUse";
20
+ permissionDecision: "allow" | "deny" | "ask";
21
+ permissionDecisionReason?: string;
22
+ };
23
+ }
24
+ /**
25
+ * Pre-tool-use hook
26
+ * Called before Claude uses a tool
27
+ * Checks the work ratio before allowing writes
28
+ */
29
+ export declare function preToolUseHook(input: PreToolUseHookInput): PreToolUseHookOutput;
30
+ /**
31
+ * CLI entry point for pre-tool-use hook
32
+ * Reads input from stdin, writes output to stdout
33
+ */
34
+ export declare function runPreToolUseHook(): Promise<void>;
35
+ //# sourceMappingURL=pre-tool-use.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-tool-use.d.ts","sourceRoot":"","sources":["../../src/hooks/pre-tool-use.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,kBAAkB,EAAE;QAClB,aAAa,EAAE,YAAY,CAAC;QAC5B,kBAAkB,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;QAC7C,wBAAwB,CAAC,EAAE,MAAM,CAAC;KACnC,CAAC;CACH;AAiDD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,oBAAoB,CAkD/E;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAoCvD"}