@andrebuzeli/git-mcp 15.8.4 → 15.8.6
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 +39 -125
- package/package.json +28 -44
- package/src/index.js +146 -139
- package/src/providers/providerManager.js +203 -217
- package/src/tools/git-diff.js +137 -126
- package/src/tools/git-help.js +285 -285
- package/src/tools/git-remote.js +472 -472
- package/src/tools/git-workflow.js +403 -403
- package/src/utils/env.js +104 -104
- package/src/utils/errors.js +431 -431
- package/src/utils/gitAdapter.js +932 -951
- package/src/utils/hooks.js +255 -255
- package/src/utils/metrics.js +198 -198
- package/src/utils/providerExec.js +58 -58
- package/src/utils/repoHelpers.js +160 -160
- package/src/utils/retry.js +123 -123
- package/install.sh +0 -68
package/src/utils/hooks.js
CHANGED
|
@@ -1,255 +1,255 @@
|
|
|
1
|
-
// Sistema de Hooks para git-mcp
|
|
2
|
-
// Permite executar código customizado antes/depois de operações
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Tipos de hooks disponíveis
|
|
6
|
-
*/
|
|
7
|
-
export const HOOK_TYPES = {
|
|
8
|
-
// Workflow hooks
|
|
9
|
-
PRE_INIT: "pre:init",
|
|
10
|
-
POST_INIT: "post:init",
|
|
11
|
-
PRE_COMMIT: "pre:commit",
|
|
12
|
-
POST_COMMIT: "post:commit",
|
|
13
|
-
PRE_PUSH: "pre:push",
|
|
14
|
-
POST_PUSH: "post:push",
|
|
15
|
-
PRE_PULL: "pre:pull",
|
|
16
|
-
POST_PULL: "post:pull",
|
|
17
|
-
|
|
18
|
-
// Branch hooks
|
|
19
|
-
PRE_BRANCH_CREATE: "pre:branch:create",
|
|
20
|
-
POST_BRANCH_CREATE: "post:branch:create",
|
|
21
|
-
PRE_BRANCH_DELETE: "pre:branch:delete",
|
|
22
|
-
POST_BRANCH_DELETE: "post:branch:delete",
|
|
23
|
-
PRE_CHECKOUT: "pre:checkout",
|
|
24
|
-
POST_CHECKOUT: "post:checkout",
|
|
25
|
-
|
|
26
|
-
// Merge hooks
|
|
27
|
-
PRE_MERGE: "pre:merge",
|
|
28
|
-
POST_MERGE: "post:merge",
|
|
29
|
-
ON_CONFLICT: "on:conflict",
|
|
30
|
-
|
|
31
|
-
// Reset hooks
|
|
32
|
-
PRE_RESET: "pre:reset",
|
|
33
|
-
POST_RESET: "post:reset",
|
|
34
|
-
|
|
35
|
-
// Remote hooks
|
|
36
|
-
PRE_SYNC: "pre:sync",
|
|
37
|
-
POST_SYNC: "post:sync",
|
|
38
|
-
|
|
39
|
-
// Error handling
|
|
40
|
-
ON_ERROR: "on:error"
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// Armazena hooks registrados
|
|
44
|
-
const registeredHooks = new Map();
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Registra um hook
|
|
48
|
-
* @param {string} hookType - Tipo do hook (usar HOOK_TYPES)
|
|
49
|
-
* @param {Function} handler - Função async (context) => result
|
|
50
|
-
* @param {Object} options - { priority: number, name: string }
|
|
51
|
-
* @returns {string} - ID do hook para remover depois
|
|
52
|
-
*/
|
|
53
|
-
export function registerHook(hookType, handler, options = {}) {
|
|
54
|
-
const { priority = 0, name = "anonymous" } = options;
|
|
55
|
-
const hookId = `${hookType}_${name}_${Date.now()}`;
|
|
56
|
-
|
|
57
|
-
if (!registeredHooks.has(hookType)) {
|
|
58
|
-
registeredHooks.set(hookType, []);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
registeredHooks.get(hookType).push({
|
|
62
|
-
id: hookId,
|
|
63
|
-
handler,
|
|
64
|
-
priority,
|
|
65
|
-
name
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// Ordena por prioridade (maior primeiro)
|
|
69
|
-
registeredHooks.get(hookType).sort((a, b) => b.priority - a.priority);
|
|
70
|
-
|
|
71
|
-
return hookId;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Remove um hook específico
|
|
76
|
-
* @param {string} hookId - ID retornado por registerHook
|
|
77
|
-
*/
|
|
78
|
-
export function unregisterHook(hookId) {
|
|
79
|
-
for (const [type, hooks] of registeredHooks.entries()) {
|
|
80
|
-
const index = hooks.findIndex(h => h.id === hookId);
|
|
81
|
-
if (index !== -1) {
|
|
82
|
-
hooks.splice(index, 1);
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Remove todos os hooks de um tipo
|
|
91
|
-
* @param {string} hookType - Tipo do hook
|
|
92
|
-
*/
|
|
93
|
-
export function clearHooks(hookType) {
|
|
94
|
-
if (hookType) {
|
|
95
|
-
registeredHooks.delete(hookType);
|
|
96
|
-
} else {
|
|
97
|
-
registeredHooks.clear();
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Executa todos os hooks de um tipo
|
|
103
|
-
* @param {string} hookType - Tipo do hook
|
|
104
|
-
* @param {Object} context - Contexto passado para os handlers
|
|
105
|
-
* @returns {Object} - { success: boolean, results: [], errors: [] }
|
|
106
|
-
*/
|
|
107
|
-
export async function runHooks(hookType, context = {}) {
|
|
108
|
-
const hooks = registeredHooks.get(hookType) || [];
|
|
109
|
-
|
|
110
|
-
if (hooks.length === 0) {
|
|
111
|
-
return { success: true, results: [], errors: [], skipped: true };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const results = [];
|
|
115
|
-
const errors = [];
|
|
116
|
-
let shouldContinue = true;
|
|
117
|
-
|
|
118
|
-
for (const hook of hooks) {
|
|
119
|
-
if (!shouldContinue) break;
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
const result = await hook.handler({
|
|
123
|
-
...context,
|
|
124
|
-
hookType,
|
|
125
|
-
hookName: hook.name
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
results.push({
|
|
129
|
-
hookId: hook.id,
|
|
130
|
-
name: hook.name,
|
|
131
|
-
success: true,
|
|
132
|
-
result
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// Hook pode retornar { abort: true } para parar execução
|
|
136
|
-
if (result?.abort) {
|
|
137
|
-
shouldContinue = false;
|
|
138
|
-
}
|
|
139
|
-
} catch (error) {
|
|
140
|
-
errors.push({
|
|
141
|
-
hookId: hook.id,
|
|
142
|
-
name: hook.name,
|
|
143
|
-
error: error.message || String(error)
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// Por padrão, continua mesmo com erro
|
|
147
|
-
// Hook pode definir { stopOnError: true } nas options
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
success: errors.length === 0,
|
|
153
|
-
results,
|
|
154
|
-
errors,
|
|
155
|
-
aborted: !shouldContinue
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Wrapper para executar função com hooks pre/post
|
|
161
|
-
* @param {string} operationType - Tipo da operação (ex: "commit", "push")
|
|
162
|
-
* @param {Function} fn - Função principal async
|
|
163
|
-
* @param {Object} context - Contexto para os hooks
|
|
164
|
-
*/
|
|
165
|
-
export async function withHooks(operationType, fn, context = {}) {
|
|
166
|
-
const preHookType = `pre:${operationType}`;
|
|
167
|
-
const postHookType = `post:${operationType}`;
|
|
168
|
-
|
|
169
|
-
// Pre-hooks
|
|
170
|
-
const preResult = await runHooks(preHookType, context);
|
|
171
|
-
if (preResult.aborted) {
|
|
172
|
-
return {
|
|
173
|
-
success: false,
|
|
174
|
-
abortedByHook: true,
|
|
175
|
-
hookResults: preResult,
|
|
176
|
-
message: `Operação abortada por hook ${preHookType}`
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Operação principal
|
|
181
|
-
let mainResult;
|
|
182
|
-
let mainError = null;
|
|
183
|
-
|
|
184
|
-
try {
|
|
185
|
-
mainResult = await fn();
|
|
186
|
-
} catch (error) {
|
|
187
|
-
mainError = error;
|
|
188
|
-
|
|
189
|
-
// Executa hook de erro
|
|
190
|
-
await runHooks(HOOK_TYPES.ON_ERROR, {
|
|
191
|
-
...context,
|
|
192
|
-
error: {
|
|
193
|
-
message: error.message,
|
|
194
|
-
code: error.code,
|
|
195
|
-
stack: error.stack
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
throw error;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Post-hooks
|
|
203
|
-
const postResult = await runHooks(postHookType, {
|
|
204
|
-
...context,
|
|
205
|
-
result: mainResult
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
return mainResult;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Lista todos os hooks registrados
|
|
213
|
-
*/
|
|
214
|
-
export function listHooks() {
|
|
215
|
-
const list = {};
|
|
216
|
-
for (const [type, hooks] of registeredHooks.entries()) {
|
|
217
|
-
list[type] = hooks.map(h => ({
|
|
218
|
-
id: h.id,
|
|
219
|
-
name: h.name,
|
|
220
|
-
priority: h.priority
|
|
221
|
-
}));
|
|
222
|
-
}
|
|
223
|
-
return list;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Verifica se há hooks registrados para um tipo
|
|
228
|
-
*/
|
|
229
|
-
export function hasHooks(hookType) {
|
|
230
|
-
const hooks = registeredHooks.get(hookType);
|
|
231
|
-
return hooks && hooks.length > 0;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Exemplo de registro de hooks
|
|
236
|
-
*/
|
|
237
|
-
export function exampleHookSetup() {
|
|
238
|
-
// Hook para logging
|
|
239
|
-
registerHook(HOOK_TYPES.POST_COMMIT, async (ctx) => {
|
|
240
|
-
console.log(`[Hook] Commit criado: ${ctx.result?.sha}`);
|
|
241
|
-
}, { name: "commit-logger", priority: 10 });
|
|
242
|
-
|
|
243
|
-
// Hook para validação pré-push
|
|
244
|
-
registerHook(HOOK_TYPES.PRE_PUSH, async (ctx) => {
|
|
245
|
-
// Exemplo: verificar se branch é protegida
|
|
246
|
-
if (ctx.branch === "main" && !ctx.force) {
|
|
247
|
-
console.warn("[Hook] Push para main sem force - considere criar PR");
|
|
248
|
-
}
|
|
249
|
-
}, { name: "branch-protection", priority: 100 });
|
|
250
|
-
|
|
251
|
-
// Hook para notificação de erro
|
|
252
|
-
registerHook(HOOK_TYPES.ON_ERROR, async (ctx) => {
|
|
253
|
-
console.error(`[Hook] Erro: ${ctx.error?.message}`);
|
|
254
|
-
}, { name: "error-notifier", priority: 0 });
|
|
255
|
-
}
|
|
1
|
+
// Sistema de Hooks para git-mcp
|
|
2
|
+
// Permite executar código customizado antes/depois de operações
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tipos de hooks disponíveis
|
|
6
|
+
*/
|
|
7
|
+
export const HOOK_TYPES = {
|
|
8
|
+
// Workflow hooks
|
|
9
|
+
PRE_INIT: "pre:init",
|
|
10
|
+
POST_INIT: "post:init",
|
|
11
|
+
PRE_COMMIT: "pre:commit",
|
|
12
|
+
POST_COMMIT: "post:commit",
|
|
13
|
+
PRE_PUSH: "pre:push",
|
|
14
|
+
POST_PUSH: "post:push",
|
|
15
|
+
PRE_PULL: "pre:pull",
|
|
16
|
+
POST_PULL: "post:pull",
|
|
17
|
+
|
|
18
|
+
// Branch hooks
|
|
19
|
+
PRE_BRANCH_CREATE: "pre:branch:create",
|
|
20
|
+
POST_BRANCH_CREATE: "post:branch:create",
|
|
21
|
+
PRE_BRANCH_DELETE: "pre:branch:delete",
|
|
22
|
+
POST_BRANCH_DELETE: "post:branch:delete",
|
|
23
|
+
PRE_CHECKOUT: "pre:checkout",
|
|
24
|
+
POST_CHECKOUT: "post:checkout",
|
|
25
|
+
|
|
26
|
+
// Merge hooks
|
|
27
|
+
PRE_MERGE: "pre:merge",
|
|
28
|
+
POST_MERGE: "post:merge",
|
|
29
|
+
ON_CONFLICT: "on:conflict",
|
|
30
|
+
|
|
31
|
+
// Reset hooks
|
|
32
|
+
PRE_RESET: "pre:reset",
|
|
33
|
+
POST_RESET: "post:reset",
|
|
34
|
+
|
|
35
|
+
// Remote hooks
|
|
36
|
+
PRE_SYNC: "pre:sync",
|
|
37
|
+
POST_SYNC: "post:sync",
|
|
38
|
+
|
|
39
|
+
// Error handling
|
|
40
|
+
ON_ERROR: "on:error"
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Armazena hooks registrados
|
|
44
|
+
const registeredHooks = new Map();
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Registra um hook
|
|
48
|
+
* @param {string} hookType - Tipo do hook (usar HOOK_TYPES)
|
|
49
|
+
* @param {Function} handler - Função async (context) => result
|
|
50
|
+
* @param {Object} options - { priority: number, name: string }
|
|
51
|
+
* @returns {string} - ID do hook para remover depois
|
|
52
|
+
*/
|
|
53
|
+
export function registerHook(hookType, handler, options = {}) {
|
|
54
|
+
const { priority = 0, name = "anonymous" } = options;
|
|
55
|
+
const hookId = `${hookType}_${name}_${Date.now()}`;
|
|
56
|
+
|
|
57
|
+
if (!registeredHooks.has(hookType)) {
|
|
58
|
+
registeredHooks.set(hookType, []);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
registeredHooks.get(hookType).push({
|
|
62
|
+
id: hookId,
|
|
63
|
+
handler,
|
|
64
|
+
priority,
|
|
65
|
+
name
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Ordena por prioridade (maior primeiro)
|
|
69
|
+
registeredHooks.get(hookType).sort((a, b) => b.priority - a.priority);
|
|
70
|
+
|
|
71
|
+
return hookId;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Remove um hook específico
|
|
76
|
+
* @param {string} hookId - ID retornado por registerHook
|
|
77
|
+
*/
|
|
78
|
+
export function unregisterHook(hookId) {
|
|
79
|
+
for (const [type, hooks] of registeredHooks.entries()) {
|
|
80
|
+
const index = hooks.findIndex(h => h.id === hookId);
|
|
81
|
+
if (index !== -1) {
|
|
82
|
+
hooks.splice(index, 1);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Remove todos os hooks de um tipo
|
|
91
|
+
* @param {string} hookType - Tipo do hook
|
|
92
|
+
*/
|
|
93
|
+
export function clearHooks(hookType) {
|
|
94
|
+
if (hookType) {
|
|
95
|
+
registeredHooks.delete(hookType);
|
|
96
|
+
} else {
|
|
97
|
+
registeredHooks.clear();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Executa todos os hooks de um tipo
|
|
103
|
+
* @param {string} hookType - Tipo do hook
|
|
104
|
+
* @param {Object} context - Contexto passado para os handlers
|
|
105
|
+
* @returns {Object} - { success: boolean, results: [], errors: [] }
|
|
106
|
+
*/
|
|
107
|
+
export async function runHooks(hookType, context = {}) {
|
|
108
|
+
const hooks = registeredHooks.get(hookType) || [];
|
|
109
|
+
|
|
110
|
+
if (hooks.length === 0) {
|
|
111
|
+
return { success: true, results: [], errors: [], skipped: true };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const results = [];
|
|
115
|
+
const errors = [];
|
|
116
|
+
let shouldContinue = true;
|
|
117
|
+
|
|
118
|
+
for (const hook of hooks) {
|
|
119
|
+
if (!shouldContinue) break;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const result = await hook.handler({
|
|
123
|
+
...context,
|
|
124
|
+
hookType,
|
|
125
|
+
hookName: hook.name
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
results.push({
|
|
129
|
+
hookId: hook.id,
|
|
130
|
+
name: hook.name,
|
|
131
|
+
success: true,
|
|
132
|
+
result
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Hook pode retornar { abort: true } para parar execução
|
|
136
|
+
if (result?.abort) {
|
|
137
|
+
shouldContinue = false;
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
errors.push({
|
|
141
|
+
hookId: hook.id,
|
|
142
|
+
name: hook.name,
|
|
143
|
+
error: error.message || String(error)
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Por padrão, continua mesmo com erro
|
|
147
|
+
// Hook pode definir { stopOnError: true } nas options
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
success: errors.length === 0,
|
|
153
|
+
results,
|
|
154
|
+
errors,
|
|
155
|
+
aborted: !shouldContinue
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Wrapper para executar função com hooks pre/post
|
|
161
|
+
* @param {string} operationType - Tipo da operação (ex: "commit", "push")
|
|
162
|
+
* @param {Function} fn - Função principal async
|
|
163
|
+
* @param {Object} context - Contexto para os hooks
|
|
164
|
+
*/
|
|
165
|
+
export async function withHooks(operationType, fn, context = {}) {
|
|
166
|
+
const preHookType = `pre:${operationType}`;
|
|
167
|
+
const postHookType = `post:${operationType}`;
|
|
168
|
+
|
|
169
|
+
// Pre-hooks
|
|
170
|
+
const preResult = await runHooks(preHookType, context);
|
|
171
|
+
if (preResult.aborted) {
|
|
172
|
+
return {
|
|
173
|
+
success: false,
|
|
174
|
+
abortedByHook: true,
|
|
175
|
+
hookResults: preResult,
|
|
176
|
+
message: `Operação abortada por hook ${preHookType}`
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Operação principal
|
|
181
|
+
let mainResult;
|
|
182
|
+
let mainError = null;
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
mainResult = await fn();
|
|
186
|
+
} catch (error) {
|
|
187
|
+
mainError = error;
|
|
188
|
+
|
|
189
|
+
// Executa hook de erro
|
|
190
|
+
await runHooks(HOOK_TYPES.ON_ERROR, {
|
|
191
|
+
...context,
|
|
192
|
+
error: {
|
|
193
|
+
message: error.message,
|
|
194
|
+
code: error.code,
|
|
195
|
+
stack: error.stack
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Post-hooks
|
|
203
|
+
const postResult = await runHooks(postHookType, {
|
|
204
|
+
...context,
|
|
205
|
+
result: mainResult
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return mainResult;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Lista todos os hooks registrados
|
|
213
|
+
*/
|
|
214
|
+
export function listHooks() {
|
|
215
|
+
const list = {};
|
|
216
|
+
for (const [type, hooks] of registeredHooks.entries()) {
|
|
217
|
+
list[type] = hooks.map(h => ({
|
|
218
|
+
id: h.id,
|
|
219
|
+
name: h.name,
|
|
220
|
+
priority: h.priority
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
return list;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Verifica se há hooks registrados para um tipo
|
|
228
|
+
*/
|
|
229
|
+
export function hasHooks(hookType) {
|
|
230
|
+
const hooks = registeredHooks.get(hookType);
|
|
231
|
+
return hooks && hooks.length > 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Exemplo de registro de hooks
|
|
236
|
+
*/
|
|
237
|
+
export function exampleHookSetup() {
|
|
238
|
+
// Hook para logging
|
|
239
|
+
registerHook(HOOK_TYPES.POST_COMMIT, async (ctx) => {
|
|
240
|
+
console.log(`[Hook] Commit criado: ${ctx.result?.sha}`);
|
|
241
|
+
}, { name: "commit-logger", priority: 10 });
|
|
242
|
+
|
|
243
|
+
// Hook para validação pré-push
|
|
244
|
+
registerHook(HOOK_TYPES.PRE_PUSH, async (ctx) => {
|
|
245
|
+
// Exemplo: verificar se branch é protegida
|
|
246
|
+
if (ctx.branch === "main" && !ctx.force) {
|
|
247
|
+
console.warn("[Hook] Push para main sem force - considere criar PR");
|
|
248
|
+
}
|
|
249
|
+
}, { name: "branch-protection", priority: 100 });
|
|
250
|
+
|
|
251
|
+
// Hook para notificação de erro
|
|
252
|
+
registerHook(HOOK_TYPES.ON_ERROR, async (ctx) => {
|
|
253
|
+
console.error(`[Hook] Erro: ${ctx.error?.message}`);
|
|
254
|
+
}, { name: "error-notifier", priority: 0 });
|
|
255
|
+
}
|