xtrm-tools 2.0.3 → 2.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.
- package/config/hooks.json +33 -0
- package/hooks/README.md +3 -3
- package/hooks/main-guard.mjs +18 -8
- package/package.json +1 -1
package/config/hooks.json
CHANGED
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
14
|
"PreToolUse": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
17
|
+
"script": "main-guard.mjs",
|
|
18
|
+
"timeout": 5000
|
|
19
|
+
},
|
|
15
20
|
{
|
|
16
21
|
"matcher": "Read|Edit",
|
|
17
22
|
"script": "serena-workflow-reminder.py"
|
|
@@ -24,6 +29,34 @@
|
|
|
24
29
|
{
|
|
25
30
|
"matcher": "Edit|Write",
|
|
26
31
|
"script": "type-safety-enforcement.py"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"matcher": "Grep|Glob|Bash",
|
|
35
|
+
"script": "gitnexus/gitnexus-hook.cjs",
|
|
36
|
+
"timeout": 8000
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"matcher": "Edit|Write|MultiEdit|NotebookEdit|mcp__serena__*",
|
|
40
|
+
"script": "beads-edit-gate.mjs",
|
|
41
|
+
"timeout": 5000
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"matcher": "Bash",
|
|
45
|
+
"script": "beads-commit-gate.mjs",
|
|
46
|
+
"timeout": 5000
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"PostToolUse": [
|
|
50
|
+
{
|
|
51
|
+
"matcher": "Bash",
|
|
52
|
+
"script": "beads-close-memory-prompt.mjs",
|
|
53
|
+
"timeout": 5000
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"Stop": [
|
|
57
|
+
{
|
|
58
|
+
"script": "beads-stop-gate.mjs",
|
|
59
|
+
"timeout": 5000
|
|
27
60
|
}
|
|
28
61
|
]
|
|
29
62
|
},
|
package/hooks/README.md
CHANGED
|
@@ -154,13 +154,13 @@ Installed globally to `~/.claude/hooks/` by `xtrm install`. Require Node.js.
|
|
|
154
154
|
- `git commit` directly on protected branches
|
|
155
155
|
- `git push` to protected branches
|
|
156
156
|
|
|
157
|
-
**Configuration** (
|
|
157
|
+
**Configuration** (global Claude config):
|
|
158
158
|
```json
|
|
159
159
|
{
|
|
160
160
|
"hooks": {
|
|
161
161
|
"PreToolUse": [{
|
|
162
162
|
"matcher": "Edit|Write|MultiEdit|NotebookEdit|Bash",
|
|
163
|
-
"hooks": [{ "type": "command", "command": "~/.claude/hooks/main-guard.mjs", "timeout": 5000 }]
|
|
163
|
+
"hooks": [{ "type": "command", "command": "node \"~/.claude/hooks/main-guard.mjs\"", "timeout": 5000 }]
|
|
164
164
|
}]
|
|
165
165
|
}
|
|
166
166
|
}
|
|
@@ -256,4 +256,4 @@ Use `xtrm install` to deploy all hooks automatically. For manual setup:
|
|
|
256
256
|
|
|
257
257
|
3. Merge hook entries into `~/.claude/settings.json`.
|
|
258
258
|
|
|
259
|
-
4. Restart Claude Code.
|
|
259
|
+
4. Restart Claude Code.
|
package/hooks/main-guard.mjs
CHANGED
|
@@ -33,11 +33,25 @@ try {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const tool = input.tool_name ?? '';
|
|
36
|
+
const hookEventName = input.hook_event_name ?? 'PreToolUse';
|
|
37
|
+
|
|
38
|
+
function deny(reason) {
|
|
39
|
+
process.stdout.write(JSON.stringify({
|
|
40
|
+
systemMessage: reason,
|
|
41
|
+
hookSpecificOutput: {
|
|
42
|
+
hookEventName,
|
|
43
|
+
permissionDecision: 'deny',
|
|
44
|
+
permissionDecisionReason: reason,
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
process.stdout.write('\n');
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
36
50
|
|
|
37
51
|
const WRITE_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit']);
|
|
38
52
|
|
|
39
53
|
if (WRITE_TOOLS.has(tool)) {
|
|
40
|
-
|
|
54
|
+
deny(
|
|
41
55
|
`⛔ You are on '${branch}' — never edit files directly on master.\n\n` +
|
|
42
56
|
'Full workflow:\n' +
|
|
43
57
|
' 1. git checkout -b feature/<name> ← start here\n' +
|
|
@@ -48,7 +62,6 @@ if (WRITE_TOOLS.has(tool)) {
|
|
|
48
62
|
' 6. gh pr create --fill && gh pr merge --squash\n' +
|
|
49
63
|
' 7. git checkout master && git reset --hard origin/master\n'
|
|
50
64
|
);
|
|
51
|
-
process.exit(2);
|
|
52
65
|
}
|
|
53
66
|
|
|
54
67
|
const WORKFLOW =
|
|
@@ -89,11 +102,10 @@ if (tool === 'Bash') {
|
|
|
89
102
|
|
|
90
103
|
// Specific messages for common blocked operations
|
|
91
104
|
if (/^git\s+commit\b/.test(cmd)) {
|
|
92
|
-
|
|
105
|
+
deny(
|
|
93
106
|
`\u26D4 Don't commit directly to '${branch}' \u2014 use a feature branch.\n\n` +
|
|
94
107
|
WORKFLOW
|
|
95
108
|
);
|
|
96
|
-
process.exit(2);
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
if (/^git\s+push\b/.test(cmd)) {
|
|
@@ -102,7 +114,7 @@ if (tool === 'Bash') {
|
|
|
102
114
|
const explicitProtected = protectedBranches.some(b => lastToken === b || lastToken.endsWith(`:${b}`));
|
|
103
115
|
const impliedProtected = tokens.length <= 3 && protectedBranches.includes(branch);
|
|
104
116
|
if (explicitProtected || impliedProtected) {
|
|
105
|
-
|
|
117
|
+
deny(
|
|
106
118
|
`\u26D4 Don't push directly to '${branch}' \u2014 use the PR workflow.\n\n` +
|
|
107
119
|
'Next steps:\n' +
|
|
108
120
|
' 5. git push -u origin <feature-branch> \u2190 push your branch\n' +
|
|
@@ -113,14 +125,13 @@ if (tool === 'Bash') {
|
|
|
113
125
|
"If you're not on a feature branch yet:\n" +
|
|
114
126
|
' git checkout -b feature/<name> (then re-commit and push)\n'
|
|
115
127
|
);
|
|
116
|
-
process.exit(2);
|
|
117
128
|
}
|
|
118
129
|
// Pushing to a feature branch — allow
|
|
119
130
|
process.exit(0);
|
|
120
131
|
}
|
|
121
132
|
|
|
122
133
|
// Default deny — block everything else on protected branches
|
|
123
|
-
|
|
134
|
+
deny(
|
|
124
135
|
`\u26D4 Bash is restricted on '${branch}' \u2014 use a feature branch for file writes and script execution.\n\n` +
|
|
125
136
|
'Allowed on protected branches:\n' +
|
|
126
137
|
' git status / log / diff / branch / fetch / pull / stash\n' +
|
|
@@ -133,7 +144,6 @@ if (tool === 'Bash') {
|
|
|
133
144
|
' 1. git checkout -b feature/<name> \u2190 move to a feature branch, or\n' +
|
|
134
145
|
' 2. MAIN_GUARD_ALLOW_BASH=1 <command> (escape hatch, use sparingly)\n'
|
|
135
146
|
);
|
|
136
|
-
process.exit(2);
|
|
137
147
|
}
|
|
138
148
|
|
|
139
149
|
process.exit(0);
|