@a16njs/glob-hook 0.0.1
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/README.md +128 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +115 -0
- package/dist/index.js.map +1 -0
- package/dist/io.d.ts +28 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +48 -0
- package/dist/io.js.map +1 -0
- package/dist/matcher.d.ts +9 -0
- package/dist/matcher.d.ts.map +1 -0
- package/dist/matcher.js +17 -0
- package/dist/matcher.js.map +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# @a16njs/glob-hook
|
|
2
|
+
|
|
3
|
+
CLI tool for glob-based file path matching in Claude Code hooks. Part of the [a16n](https://github.com/texarkanine/a16n) project.
|
|
4
|
+
|
|
5
|
+
## Why This Package?
|
|
6
|
+
|
|
7
|
+
Claude Code hooks use a `matcher` field that matches **tool names** (Read, Write), not file paths. To apply rules to specific file patterns (like `**/*.tsx` for React files), you need external tooling.
|
|
8
|
+
|
|
9
|
+
**glob-hook** bridges this gap:
|
|
10
|
+
- Reads hook input from stdin (JSON with `tool_input.file_path`)
|
|
11
|
+
- Matches the file path against glob patterns using [micromatch](https://github.com/micromatch/micromatch)
|
|
12
|
+
- Outputs `additionalContext` if matched, enabling pattern-based rule injection
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Via npx (recommended for hooks)
|
|
18
|
+
npx @a16njs/glob-hook --globs "**/*.tsx" --context-file "rules.txt"
|
|
19
|
+
|
|
20
|
+
# Or install globally
|
|
21
|
+
npm install -g @a16njs/glob-hook
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Basic Usage
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Pipe hook JSON to glob-hook
|
|
30
|
+
echo '{"tool_input":{"file_path":"src/Button.tsx"}}' | \
|
|
31
|
+
npx @a16njs/glob-hook \
|
|
32
|
+
--globs "**/*.tsx" \
|
|
33
|
+
--context-file ".a16n/rules/react.txt"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### In Claude Code Hooks
|
|
37
|
+
|
|
38
|
+
Configure in `.claude/settings.local.json`:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"hooks": {
|
|
43
|
+
"PreToolUse": [
|
|
44
|
+
{
|
|
45
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
46
|
+
"hooks": [
|
|
47
|
+
{
|
|
48
|
+
"type": "command",
|
|
49
|
+
"command": "npx @a16njs/glob-hook --globs '**/*.tsx,**/*.ts' --context-file '.a16n/rules/typescript.txt'"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Options
|
|
59
|
+
|
|
60
|
+
| Option | Required | Description |
|
|
61
|
+
|--------|----------|-------------|
|
|
62
|
+
| `--globs` | Yes | Comma-separated glob patterns (e.g., `"**/*.ts,**/*.tsx"`) |
|
|
63
|
+
| `--context-file` | Yes | Path to file containing context to inject when matched |
|
|
64
|
+
|
|
65
|
+
## Output Format
|
|
66
|
+
|
|
67
|
+
### When Pattern Matches
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"hookSpecificOutput": {
|
|
72
|
+
"additionalContext": "<contents of context file>"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### When Pattern Doesn't Match
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### On Error
|
|
84
|
+
|
|
85
|
+
Always outputs `{}` (empty object) and logs errors to stderr. This ensures hook failures don't break Claude Code.
|
|
86
|
+
|
|
87
|
+
## Glob Pattern Examples
|
|
88
|
+
|
|
89
|
+
| Pattern | Matches |
|
|
90
|
+
|---------|---------|
|
|
91
|
+
| `**/*.tsx` | All `.tsx` files in any directory |
|
|
92
|
+
| `**/*.ts,**/*.tsx` | All TypeScript files |
|
|
93
|
+
| `src/components/**` | All files under `src/components/` |
|
|
94
|
+
| `*.config.js` | Config files in root |
|
|
95
|
+
| `**/*.test.ts` | All test files |
|
|
96
|
+
|
|
97
|
+
Patterns use [micromatch](https://github.com/micromatch/micromatch) syntax with `dot: true` (matches dotfiles).
|
|
98
|
+
|
|
99
|
+
## Integration with a16n
|
|
100
|
+
|
|
101
|
+
This package is part of the a16n conversion pipeline. When converting Cursor FileRules to Claude:
|
|
102
|
+
|
|
103
|
+
1. a16n reads Cursor rules with glob patterns
|
|
104
|
+
2. For each rule, a16n generates:
|
|
105
|
+
- A context file (`.a16n/rules/<name>.txt`)
|
|
106
|
+
- A hook configuration using glob-hook
|
|
107
|
+
3. Claude hooks invoke glob-hook to match file patterns
|
|
108
|
+
|
|
109
|
+
## Requirements
|
|
110
|
+
|
|
111
|
+
- Node.js >= 18.0.0
|
|
112
|
+
|
|
113
|
+
## Development
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Install dependencies
|
|
117
|
+
pnpm install
|
|
118
|
+
|
|
119
|
+
# Run tests
|
|
120
|
+
pnpm --filter @a16njs/glob-hook test
|
|
121
|
+
|
|
122
|
+
# Build
|
|
123
|
+
pnpm --filter @a16njs/glob-hook build
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { matchesAny } from './matcher';
|
|
4
|
+
import { parseStdin, writeOutput, createEmptyOutput, createMatchOutput } from './io';
|
|
5
|
+
/**
|
|
6
|
+
* Parse command line arguments.
|
|
7
|
+
* Uses raw process.argv for fastest startup (no dependencies).
|
|
8
|
+
*/
|
|
9
|
+
function parseArgs() {
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
let globs = null;
|
|
12
|
+
let contextFile = null;
|
|
13
|
+
for (let i = 0; i < args.length; i++) {
|
|
14
|
+
const arg = args[i];
|
|
15
|
+
if (arg === '--globs' && i + 1 < args.length) {
|
|
16
|
+
globs = args[++i] ?? null;
|
|
17
|
+
}
|
|
18
|
+
else if (arg === '--context-file' && i + 1 < args.length) {
|
|
19
|
+
contextFile = args[++i] ?? null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return { globs, contextFile };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read entire stdin.
|
|
26
|
+
* Returns empty string if stdin is a TTY or no data.
|
|
27
|
+
*/
|
|
28
|
+
async function readAllStdin() {
|
|
29
|
+
// If stdin is a TTY (interactive), no piped data
|
|
30
|
+
if (process.stdin.isTTY) {
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
const chunks = [];
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
process.stdin.on('data', (chunk) => {
|
|
36
|
+
chunks.push(chunk);
|
|
37
|
+
});
|
|
38
|
+
process.stdin.on('end', () => {
|
|
39
|
+
resolve(Buffer.concat(chunks).toString('utf-8'));
|
|
40
|
+
});
|
|
41
|
+
process.stdin.on('error', () => {
|
|
42
|
+
resolve('');
|
|
43
|
+
});
|
|
44
|
+
// Resume stdin to start receiving data
|
|
45
|
+
process.stdin.resume();
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Read context file content.
|
|
50
|
+
* Returns null if file cannot be read.
|
|
51
|
+
*/
|
|
52
|
+
function readContextFile(filePath) {
|
|
53
|
+
try {
|
|
54
|
+
return readFileSync(filePath, 'utf-8');
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Main CLI entry point.
|
|
62
|
+
* Always exits 0 to prevent hook failures in Claude.
|
|
63
|
+
*/
|
|
64
|
+
async function main() {
|
|
65
|
+
try {
|
|
66
|
+
// Parse arguments
|
|
67
|
+
const { globs, contextFile } = parseArgs();
|
|
68
|
+
// Validate required arguments
|
|
69
|
+
if (!globs || !contextFile) {
|
|
70
|
+
console.error('glob-hook: Missing required arguments --globs and --context-file');
|
|
71
|
+
writeOutput(createEmptyOutput());
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Read stdin
|
|
75
|
+
const stdinData = await readAllStdin();
|
|
76
|
+
// Parse input JSON
|
|
77
|
+
const input = parseStdin(stdinData);
|
|
78
|
+
if (!input) {
|
|
79
|
+
console.error('glob-hook: Invalid or empty JSON input');
|
|
80
|
+
writeOutput(createEmptyOutput());
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Extract file path
|
|
84
|
+
const filePath = input.tool_input?.file_path;
|
|
85
|
+
if (!filePath) {
|
|
86
|
+
// No file_path in input - silently output empty (not an error, just not applicable)
|
|
87
|
+
writeOutput(createEmptyOutput());
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Parse glob patterns (comma-separated)
|
|
91
|
+
const patterns = globs.split(',').map((p) => p.trim()).filter(Boolean);
|
|
92
|
+
// Check if file matches any pattern
|
|
93
|
+
if (!matchesAny(filePath, patterns)) {
|
|
94
|
+
writeOutput(createEmptyOutput());
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Match found - read context file
|
|
98
|
+
const context = readContextFile(contextFile);
|
|
99
|
+
if (!context) {
|
|
100
|
+
console.error(`glob-hook: Cannot read context file: ${contextFile}`);
|
|
101
|
+
writeOutput(createEmptyOutput());
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Output context
|
|
105
|
+
writeOutput(createMatchOutput(context));
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
// Log error but don't fail the hook
|
|
109
|
+
console.error('glob-hook: Unexpected error:', err instanceof Error ? err.message : err);
|
|
110
|
+
writeOutput(createEmptyOutput());
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Run
|
|
114
|
+
main();
|
|
115
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/io.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { HookInput, HookOutput } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Parse JSON input from a string.
|
|
4
|
+
* Returns null if parsing fails or input is empty.
|
|
5
|
+
*
|
|
6
|
+
* @param input - Raw string input (typically from stdin)
|
|
7
|
+
* @returns Parsed HookInput or null if invalid
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseStdin(input: string): HookInput | null;
|
|
10
|
+
/**
|
|
11
|
+
* Write JSON output to stdout.
|
|
12
|
+
*
|
|
13
|
+
* @param output - HookOutput to serialize and write
|
|
14
|
+
*/
|
|
15
|
+
export declare function writeOutput(output: HookOutput): void;
|
|
16
|
+
/**
|
|
17
|
+
* Create an empty output (for no-match or error cases).
|
|
18
|
+
* Claude hooks interpret {} as "no special output".
|
|
19
|
+
*/
|
|
20
|
+
export declare function createEmptyOutput(): HookOutput;
|
|
21
|
+
/**
|
|
22
|
+
* Create a match output with additionalContext.
|
|
23
|
+
*
|
|
24
|
+
* @param context - The context content to inject
|
|
25
|
+
* @returns HookOutput with additionalContext set
|
|
26
|
+
*/
|
|
27
|
+
export declare function createMatchOutput(context: string): HookOutput;
|
|
28
|
+
//# sourceMappingURL=io.d.ts.map
|
package/dist/io.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAW1D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAEpD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,UAAU,CAE9C;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAM7D"}
|
package/dist/io.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse JSON input from a string.
|
|
3
|
+
* Returns null if parsing fails or input is empty.
|
|
4
|
+
*
|
|
5
|
+
* @param input - Raw string input (typically from stdin)
|
|
6
|
+
* @returns Parsed HookInput or null if invalid
|
|
7
|
+
*/
|
|
8
|
+
export function parseStdin(input) {
|
|
9
|
+
const trimmed = input.trim();
|
|
10
|
+
if (!trimmed) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(trimmed);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Write JSON output to stdout.
|
|
22
|
+
*
|
|
23
|
+
* @param output - HookOutput to serialize and write
|
|
24
|
+
*/
|
|
25
|
+
export function writeOutput(output) {
|
|
26
|
+
console.log(JSON.stringify(output));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Create an empty output (for no-match or error cases).
|
|
30
|
+
* Claude hooks interpret {} as "no special output".
|
|
31
|
+
*/
|
|
32
|
+
export function createEmptyOutput() {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create a match output with additionalContext.
|
|
37
|
+
*
|
|
38
|
+
* @param context - The context content to inject
|
|
39
|
+
* @returns HookOutput with additionalContext set
|
|
40
|
+
*/
|
|
41
|
+
export function createMatchOutput(context) {
|
|
42
|
+
return {
|
|
43
|
+
hookSpecificOutput: {
|
|
44
|
+
additionalContext: context,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=io.js.map
|
package/dist/io.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAc,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAAkB;IAC5C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,OAAO;QACL,kBAAkB,EAAE;YAClB,iBAAiB,EAAE,OAAO;SAC3B;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a file path matches any of the provided glob patterns.
|
|
3
|
+
*
|
|
4
|
+
* @param filePath - The file path to test
|
|
5
|
+
* @param patterns - Array of glob patterns to match against
|
|
6
|
+
* @returns true if the file path matches any pattern, false otherwise
|
|
7
|
+
*/
|
|
8
|
+
export declare function matchesAny(filePath: string, patterns: string[]): boolean;
|
|
9
|
+
//# sourceMappingURL=matcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../src/matcher.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAQxE"}
|
package/dist/matcher.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import micromatch from 'micromatch';
|
|
2
|
+
/**
|
|
3
|
+
* Check if a file path matches any of the provided glob patterns.
|
|
4
|
+
*
|
|
5
|
+
* @param filePath - The file path to test
|
|
6
|
+
* @param patterns - Array of glob patterns to match against
|
|
7
|
+
* @returns true if the file path matches any pattern, false otherwise
|
|
8
|
+
*/
|
|
9
|
+
export function matchesAny(filePath, patterns) {
|
|
10
|
+
if (patterns.length === 0) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return micromatch.isMatch(filePath, patterns, {
|
|
14
|
+
dot: true, // Match dotfiles like .eslintrc.js
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=matcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matcher.js","sourceRoot":"","sources":["../src/matcher.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,QAAkB;IAC7D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE;QAC5C,GAAG,EAAE,IAAI,EAAE,mCAAmC;KAC/C,CAAC,CAAC;AACL,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input received from Claude Code hook system via stdin.
|
|
3
|
+
*
|
|
4
|
+
* @see https://docs.anthropic.com/en/docs/claude-code/hooks
|
|
5
|
+
*/
|
|
6
|
+
export interface HookInput {
|
|
7
|
+
hook_event_name?: 'PreToolUse' | 'PostToolUse';
|
|
8
|
+
tool_name: string;
|
|
9
|
+
tool_input: {
|
|
10
|
+
file_path?: string;
|
|
11
|
+
content?: string;
|
|
12
|
+
command?: string;
|
|
13
|
+
};
|
|
14
|
+
tool_response?: {
|
|
15
|
+
content?: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Output to Claude Code hook system via stdout.
|
|
20
|
+
*
|
|
21
|
+
* @see https://docs.anthropic.com/en/docs/claude-code/hooks
|
|
22
|
+
*/
|
|
23
|
+
export interface HookOutput {
|
|
24
|
+
hookSpecificOutput?: {
|
|
25
|
+
hookEventName?: string;
|
|
26
|
+
additionalContext?: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parsed CLI options.
|
|
31
|
+
*/
|
|
32
|
+
export interface CliOptions {
|
|
33
|
+
/** Comma-separated glob patterns to match against file paths */
|
|
34
|
+
globs: string;
|
|
35
|
+
/** Path to file containing context to inject when matched */
|
|
36
|
+
contextFile: string;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,eAAe,CAAC,EAAE,YAAY,GAAG,aAAa,CAAC;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE;QACV,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,aAAa,CAAC,EAAE;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,kBAAkB,CAAC,EAAE;QACnB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;CACrB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@a16njs/glob-hook",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI tool for glob-based file path matching in Claude Code hooks",
|
|
5
|
+
"license": "AGPL-3.0",
|
|
6
|
+
"author": "Texarkanine",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Texarkanine/a16n.git",
|
|
10
|
+
"directory": "packages/glob-hook"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/Texarkanine/a16n#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/Texarkanine/a16n/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"a16n",
|
|
18
|
+
"glob",
|
|
19
|
+
"hook",
|
|
20
|
+
"claude",
|
|
21
|
+
"cli"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"bin": {
|
|
27
|
+
"glob-hook": "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": ["dist"],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"clean": "rimraf dist *.tsbuildinfo",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"micromatch": "^4.0.8"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/micromatch": "^4.0.9",
|
|
48
|
+
"@types/node": "^20.0.0",
|
|
49
|
+
"typescript": "^5.4.0",
|
|
50
|
+
"vitest": "^2.0.0"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18.0.0"
|
|
54
|
+
}
|
|
55
|
+
}
|