@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.
@@ -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
+ }