@only1btayy/g2w 1.0.29 → 1.0.30
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/hooks/g2w-commit-guard.js +43 -0
- package/lib/install.js +67 -31
- package/package.json +1 -1
- package/skills/ready2save.md +10 -5
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// G2W Commit Guard — PreToolUse hook
|
|
3
|
+
// Fires before Bash commands. Hard blocks git commit and git push
|
|
4
|
+
// unless the user has explicitly approved. Exit code 1 = blocked.
|
|
5
|
+
|
|
6
|
+
let input = '';
|
|
7
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 3000);
|
|
8
|
+
process.stdin.setEncoding('utf8');
|
|
9
|
+
process.stdin.on('data', chunk => (input += chunk));
|
|
10
|
+
process.stdin.on('end', () => {
|
|
11
|
+
clearTimeout(stdinTimeout);
|
|
12
|
+
try {
|
|
13
|
+
const data = JSON.parse(input);
|
|
14
|
+
if (data.tool_name !== 'Bash') {
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const cmd = data.tool_input?.command || '';
|
|
19
|
+
|
|
20
|
+
// Detect git commit or git push (with optional flags/args)
|
|
21
|
+
const isCommit = /\bgit\s+commit\b/.test(cmd);
|
|
22
|
+
const isPush = /\bgit\s+push\b/.test(cmd);
|
|
23
|
+
|
|
24
|
+
if (isCommit || isPush) {
|
|
25
|
+
const action = isCommit ? 'commit' : 'push';
|
|
26
|
+
const output = {
|
|
27
|
+
hookSpecificOutput: {
|
|
28
|
+
hookEventName: 'PreToolUse',
|
|
29
|
+
additionalContext:
|
|
30
|
+
`🚫 G2W COMMIT GUARD: You are about to run git ${action}. ` +
|
|
31
|
+
'This requires explicit user approval. ' +
|
|
32
|
+
'Stop and ask the user before proceeding. Do not bypass this check.',
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
process.stdout.write(JSON.stringify(output));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
process.exit(0);
|
|
40
|
+
} catch {
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
});
|
package/lib/install.js
CHANGED
|
@@ -42,22 +42,12 @@ const G2W_HOOKS = {
|
|
|
42
42
|
]
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
|
-
matcher: '
|
|
45
|
+
matcher: 'Bash',
|
|
46
46
|
hooks: [
|
|
47
47
|
{
|
|
48
|
-
type: '
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
]
|
|
52
|
-
}
|
|
53
|
-
],
|
|
54
|
-
PostMessage: [
|
|
55
|
-
{
|
|
56
|
-
matcher: '.*',
|
|
57
|
-
hooks: [
|
|
58
|
-
{
|
|
59
|
-
type: 'prompt',
|
|
60
|
-
prompt: 'If the context window is at or above 80%, tell the user: "Hey — we\'re getting close to the context limit. Want me to run /g2w:ready2save to save progress and clear context?" If below 80%, do nothing.'
|
|
48
|
+
type: 'command',
|
|
49
|
+
command: 'node "{{HOOKS_DIR}}/g2w-commit-guard.js"',
|
|
50
|
+
timeout: 5
|
|
61
51
|
}
|
|
62
52
|
]
|
|
63
53
|
}
|
|
@@ -123,6 +113,13 @@ function mergeMcpServers(targetBase) {
|
|
|
123
113
|
return added;
|
|
124
114
|
}
|
|
125
115
|
|
|
116
|
+
// Unique identifiers for each G2W hook — used for merge and removal
|
|
117
|
+
const HOOK_SIGNATURES = {
|
|
118
|
+
scopeGuard: h => h.hooks?.some(hh => hh.command?.includes('g2w-scope-guard')),
|
|
119
|
+
aGame: h => h.hooks?.some(hh => hh.command?.includes('g2w-agame')),
|
|
120
|
+
commitGuard: h => h.hooks?.some(hh => hh.command?.includes('g2w-commit-guard')),
|
|
121
|
+
};
|
|
122
|
+
|
|
126
123
|
function mergeHooks(targetBase) {
|
|
127
124
|
const settingsPath = path.join(targetBase, '.claude', 'settings.json');
|
|
128
125
|
const hooksDir = path.join(targetBase, '.claude', 'hooks');
|
|
@@ -140,22 +137,48 @@ function mergeHooks(targetBase) {
|
|
|
140
137
|
JSON.stringify(G2W_HOOKS).replace(/\{\{HOOKS_DIR\}\}/g, hooksDir.replace(/\\/g, '/'))
|
|
141
138
|
);
|
|
142
139
|
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
140
|
+
// --- Migration: clean up old hook formats from previous G2W versions ---
|
|
141
|
+
|
|
142
|
+
// Remove old PostMessage hooks (no longer used)
|
|
143
|
+
if (existing.hooks.PostMessage) {
|
|
144
|
+
existing.hooks.PostMessage = existing.hooks.PostMessage.filter(h =>
|
|
145
|
+
!(h.hooks && h.hooks.some(hh => hh.prompt && hh.prompt.includes('ready2save')))
|
|
146
|
+
);
|
|
147
|
+
if (existing.hooks.PostMessage.length === 0) delete existing.hooks.PostMessage;
|
|
150
148
|
}
|
|
151
149
|
|
|
152
|
-
//
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
150
|
+
// Remove old UserPromptSubmit hooks (broke chat — Trust Layer + context warning)
|
|
151
|
+
if (existing.hooks.UserPromptSubmit) {
|
|
152
|
+
existing.hooks.UserPromptSubmit = existing.hooks.UserPromptSubmit.filter(h =>
|
|
153
|
+
!(h.hooks && h.hooks.some(hh =>
|
|
154
|
+
hh.prompt && (hh.prompt.includes('G2W Trust Layer') || hh.prompt.includes('Context check'))
|
|
155
|
+
))
|
|
156
|
+
);
|
|
157
|
+
if (existing.hooks.UserPromptSubmit.length === 0) delete existing.hooks.UserPromptSubmit;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Remove old Trust Layer prompt from PreToolUse (was there in early versions)
|
|
161
|
+
if (existing.hooks.PreToolUse) {
|
|
162
|
+
existing.hooks.PreToolUse = existing.hooks.PreToolUse.filter(h =>
|
|
163
|
+
!(h.hooks && h.hooks.some(hh => hh.prompt && hh.prompt.includes('Trust Layer')))
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// --- Merge current hooks: scope-guard, a-game, commit-guard on PreToolUse ---
|
|
168
|
+
|
|
169
|
+
if (!existing.hooks.PreToolUse) existing.hooks.PreToolUse = [];
|
|
170
|
+
for (const hook of resolvedHooks.PreToolUse) {
|
|
171
|
+
const isScopeGuard = HOOK_SIGNATURES.scopeGuard(hook);
|
|
172
|
+
const isAGame = HOOK_SIGNATURES.aGame(hook);
|
|
173
|
+
const isCommitGuard = HOOK_SIGNATURES.commitGuard(hook);
|
|
174
|
+
const alreadyExists = existing.hooks.PreToolUse.some(h =>
|
|
175
|
+
(isScopeGuard && HOOK_SIGNATURES.scopeGuard(h)) ||
|
|
176
|
+
(isAGame && HOOK_SIGNATURES.aGame(h)) ||
|
|
177
|
+
(isCommitGuard && HOOK_SIGNATURES.commitGuard(h))
|
|
178
|
+
);
|
|
179
|
+
if (!alreadyExists) {
|
|
180
|
+
existing.hooks.PreToolUse.push(hook);
|
|
181
|
+
}
|
|
159
182
|
}
|
|
160
183
|
|
|
161
184
|
fs.writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
|
|
@@ -189,7 +212,7 @@ function removeHooks(targetBase) {
|
|
|
189
212
|
const hooksDir = path.join(targetBase, '.claude', 'hooks');
|
|
190
213
|
|
|
191
214
|
// Remove hook scripts
|
|
192
|
-
['g2w-scope-guard.js', 'g2w-agame.js'].forEach(file => {
|
|
215
|
+
['g2w-scope-guard.js', 'g2w-agame.js', 'g2w-commit-guard.js'].forEach(file => {
|
|
193
216
|
const p = path.join(hooksDir, file);
|
|
194
217
|
if (fs.existsSync(p)) fs.rmSync(p);
|
|
195
218
|
});
|
|
@@ -199,18 +222,31 @@ function removeHooks(targetBase) {
|
|
|
199
222
|
try { existing = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch { return; }
|
|
200
223
|
|
|
201
224
|
if (existing.hooks) {
|
|
225
|
+
// Remove all G2W PreToolUse hooks (scope-guard, a-game, commit-guard, old Trust Layer prompt)
|
|
202
226
|
if (existing.hooks.PreToolUse) {
|
|
203
227
|
existing.hooks.PreToolUse = existing.hooks.PreToolUse.filter(h =>
|
|
228
|
+
!HOOK_SIGNATURES.scopeGuard(h) &&
|
|
229
|
+
!HOOK_SIGNATURES.aGame(h) &&
|
|
230
|
+
!HOOK_SIGNATURES.commitGuard(h) &&
|
|
231
|
+
!(h.hooks && h.hooks.some(hh => hh.prompt && hh.prompt.includes('Trust Layer')))
|
|
232
|
+
);
|
|
233
|
+
if (existing.hooks.PreToolUse.length === 0) delete existing.hooks.PreToolUse;
|
|
234
|
+
}
|
|
235
|
+
// Clean up old UserPromptSubmit hooks (broke chat — removed in v1.0.29+)
|
|
236
|
+
if (existing.hooks.UserPromptSubmit) {
|
|
237
|
+
existing.hooks.UserPromptSubmit = existing.hooks.UserPromptSubmit.filter(h =>
|
|
204
238
|
!(h.hooks && h.hooks.some(hh =>
|
|
205
|
-
|
|
206
|
-
(hh.prompt && hh.prompt.includes('Trust Layer'))
|
|
239
|
+
hh.prompt && (hh.prompt.includes('G2W Trust Layer') || hh.prompt.includes('Context check'))
|
|
207
240
|
))
|
|
208
241
|
);
|
|
242
|
+
if (existing.hooks.UserPromptSubmit.length === 0) delete existing.hooks.UserPromptSubmit;
|
|
209
243
|
}
|
|
244
|
+
// Clean up old PostMessage hooks from early versions
|
|
210
245
|
if (existing.hooks.PostMessage) {
|
|
211
246
|
existing.hooks.PostMessage = existing.hooks.PostMessage.filter(h =>
|
|
212
247
|
!(h.hooks && h.hooks.some(hh => hh.prompt && hh.prompt.includes('ready2save')))
|
|
213
248
|
);
|
|
249
|
+
if (existing.hooks.PostMessage.length === 0) delete existing.hooks.PostMessage;
|
|
214
250
|
}
|
|
215
251
|
}
|
|
216
252
|
|
package/package.json
CHANGED
package/skills/ready2save.md
CHANGED
|
@@ -16,10 +16,8 @@ You are closing out this session. Leave the project in a state where any future
|
|
|
16
16
|
|
|
17
17
|
Do NOT ask the user what was done, what to save, or what decisions were made. You witnessed the entire session — extract it yourself and present it. The user confirms or corrects your summary. Never ask them to recall.
|
|
18
18
|
|
|
19
|
-
2. **
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
Wait for the user to confirm. Do not assume based on what was discussed — the user decides.
|
|
19
|
+
2. **Identify the active project** — Read `~/.g2w/CURRENT.md` (Windows: `C:/Users/[username]/.g2w/CURRENT.md`). If it has an `active:` line, that's the project. Use it. Do NOT ask.
|
|
20
|
+
- Only if the file doesn't exist or has no `active:` line: list folders in `~/.g2w/projects/` and ask which one. This is the ONLY scenario where you ask.
|
|
23
21
|
|
|
24
22
|
3. **Update `.g2w/CURRENT.md`** in the confirmed project folder with exactly three sections:
|
|
25
23
|
```
|
|
@@ -56,7 +54,14 @@ You are closing out this session. Leave the project in a state where any future
|
|
|
56
54
|
- Verified: [how it was verified]
|
|
57
55
|
```
|
|
58
56
|
|
|
59
|
-
Keep it lean: do NOT run git status, git diff, or git log — you already know what changed.
|
|
57
|
+
Keep it lean: do NOT run git status, git diff, or git log — you already know what changed. Present the commit message to the user. Wait for approval. Then run it as **one single Bash call**:
|
|
58
|
+
```
|
|
59
|
+
git add [files] && git commit -m "type: summary
|
|
60
|
+
|
|
61
|
+
- What changed
|
|
62
|
+
- Verified: [how]"
|
|
63
|
+
```
|
|
64
|
+
Never split git add and git commit into separate Bash calls.
|
|
60
65
|
|
|
61
66
|
6. **Announce with personality:**
|
|
62
67
|
End the session with a closing quote. Pick ONE at random from this list:
|