repoburg 1.3.159 → 1.3.161

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.
Files changed (58) hide show
  1. package/backend/.env +2 -2
  2. package/backend/dist/src/interactive-chat/chat.service.d.ts +2 -2
  3. package/backend/dist/src/interactive-chat/chat.service.js.map +1 -1
  4. package/backend/dist/src/llm-orchestration/action-handlers/session-status.handler.js +5 -111
  5. package/backend/dist/src/llm-orchestration/action-handlers/session-status.handler.js.map +1 -1
  6. package/backend/dist/src/llm-orchestration/llm-turn-processor.service.d.ts +7 -1
  7. package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js +73 -10
  8. package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js.map +1 -1
  9. package/backend/dist/src/llm-orchestration/session-status.util.d.ts +5 -0
  10. package/backend/dist/src/llm-orchestration/session-status.util.js +115 -0
  11. package/backend/dist/src/llm-orchestration/session-status.util.js.map +1 -0
  12. package/backend/dist/src/llm-provider/ollama-llm.provider.js +1 -0
  13. package/backend/dist/src/llm-provider/ollama-llm.provider.js.map +1 -1
  14. package/backend/dist/src/seeding/data/agents/control-repoburg.d.ts +1 -1
  15. package/backend/dist/src/seeding/data/agents/control-repoburg.js +1 -1
  16. package/backend/dist/src/seeding/data/agents/control-repoburg.js.map +1 -1
  17. package/backend/dist/src/seeding/data/system-prompts/control-repoburg-agent.d.ts +1 -1
  18. package/backend/dist/src/seeding/data/system-prompts/control-repoburg-agent.js +5 -2
  19. package/backend/dist/src/seeding/data/system-prompts/control-repoburg-agent.js.map +1 -1
  20. package/backend/dist/src/seeding/data/tool-hooks/eslint-hook.d.ts +1 -1
  21. package/backend/dist/src/seeding/data/tool-hooks/eslint-hook.js +47 -8
  22. package/backend/dist/src/seeding/data/tool-hooks/eslint-hook.js.map +1 -1
  23. package/backend/dist/src/seeding/data/tool-hooks/example-hook.d.ts +1 -1
  24. package/backend/dist/src/seeding/data/tool-hooks/example-hook.js +69 -7
  25. package/backend/dist/src/seeding/data/tool-hooks/example-hook.js.map +1 -1
  26. package/backend/dist/src/seeding/data/tool-hooks/readme.d.ts +1 -1
  27. package/backend/dist/src/seeding/data/tool-hooks/readme.js +96 -2
  28. package/backend/dist/src/seeding/data/tool-hooks/readme.js.map +1 -1
  29. package/backend/dist/src/tool-hooks/hook-context.interface.d.ts +76 -0
  30. package/backend/dist/src/tool-hooks/hook-context.interface.js +3 -0
  31. package/backend/dist/src/tool-hooks/hook-context.interface.js.map +1 -0
  32. package/backend/dist/src/tool-hooks/hook-runtime.d.ts +26 -0
  33. package/backend/dist/src/tool-hooks/hook-runtime.js +105 -0
  34. package/backend/dist/src/tool-hooks/hook-runtime.js.map +1 -0
  35. package/backend/dist/src/tool-hooks/tool-hooks.module.js +3 -2
  36. package/backend/dist/src/tool-hooks/tool-hooks.module.js.map +1 -1
  37. package/backend/dist/src/tool-hooks/tool-hooks.service.d.ts +9 -2
  38. package/backend/dist/src/tool-hooks/tool-hooks.service.js +125 -33
  39. package/backend/dist/src/tool-hooks/tool-hooks.service.js.map +1 -1
  40. package/backend/dist/test-hooks/combo-hook.d.ts +1 -0
  41. package/backend/dist/test-hooks/combo-hook.js +18 -0
  42. package/backend/dist/test-hooks/combo-hook.js.map +1 -0
  43. package/backend/dist/test-hooks/context-size-hook.d.ts +1 -0
  44. package/backend/dist/test-hooks/context-size-hook.js +17 -0
  45. package/backend/dist/test-hooks/context-size-hook.js.map +1 -0
  46. package/backend/dist/test-hooks/error-hook.d.ts +1 -0
  47. package/backend/dist/test-hooks/error-hook.js +19 -0
  48. package/backend/dist/test-hooks/error-hook.js.map +1 -0
  49. package/backend/dist/test-hooks/sub-agent-hook.d.ts +1 -0
  50. package/backend/dist/test-hooks/sub-agent-hook.js +11 -0
  51. package/backend/dist/test-hooks/sub-agent-hook.js.map +1 -0
  52. package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
  53. package/backend/test-hooks/combo-hook.ts +21 -0
  54. package/backend/test-hooks/context-size-hook.ts +19 -0
  55. package/backend/test-hooks/error-hook.ts +19 -0
  56. package/backend/test-hooks/sub-agent-hook.ts +12 -0
  57. package/package.json +1 -1
  58. package/backend/dist/tsconfig.tsbuildinfo +0 -1
@@ -13,6 +13,23 @@ You can configure these hooks in the "Tool Hooks" section of the Repoburg UI.
13
13
  2. **Go to UI**: Open Repoburg -> Tool Hooks -> Add Hook.
14
14
  3. **Configure**: Select the tool (e.g., \`patch\`), timing (\`before\` or \`after\`), and your script.
15
15
 
16
+ ## Wildcard Hooks
17
+
18
+ You can use \`*\` as the tool name to create a hook that runs for **every** tool.
19
+ Wildcard hooks always execute **before** tool-specific hooks.
20
+
21
+ **Example:** A \`*\` before-hook runs before every tool action:
22
+
23
+ | Hook Config | Execution Order |
24
+ |-------------|----------------|
25
+ | \`*\` before → \`audit.ts\` | Runs 1st |
26
+ | \`create_file\` before → \`validate.ts\` | Runs 2nd |
27
+
28
+ This is useful for:
29
+ - **Logging/auditing** all tool actions
30
+ - **Context size checks** before any action
31
+ - **Global policies** that apply regardless of tool type
32
+
16
33
  ## Script Interface
17
34
 
18
35
  Your script must **export a default function** that receives the context.
@@ -24,7 +41,7 @@ interface HookContext {
24
41
  hook_type: 'before' | 'after';
25
42
 
26
43
  action: {
27
- tool_name: string; // e.g. "patch"
44
+ tool_name: string; // e.g. "patch", or any tool name for wildcard hooks
28
45
  args: any; // e.g. { file_path: "src/app.ts", patch_code: "..." }
29
46
 
30
47
  // Only present for 'after' hooks
@@ -35,7 +52,54 @@ interface HookContext {
35
52
  };
36
53
  };
37
54
 
38
- plan_context: any; // Shared state of the current AI plan
55
+ plan_context: {
56
+ originalParsedActions: Array<{ tool_name: string; arguments: any }>;
57
+ feedback: {
58
+ validationErrors: Array<{ tool_name: string; error: string }>;
59
+ invalidToolErrors: Array<{ tool_name: string; arguments: any }>;
60
+ };
61
+ flags: {
62
+ should_halt: boolean;
63
+ halt_reason?: string;
64
+ is_final?: boolean;
65
+ follow_up_initiated?: boolean;
66
+ };
67
+ };
68
+
69
+ session: {
70
+ id: string; // Current session ID
71
+ };
72
+
73
+ api: {
74
+ // Get the token count of the current session's conversation history
75
+ getContextSize(): Promise<number>;
76
+
77
+ // Invoke a sub-agent synchronously (blocks until the sub-agent completes)
78
+ invokeSubAgent(agentName: string, prompt: string, options?: { sessionId?: string }): Promise<{
79
+ content: string;
80
+ success: boolean;
81
+ childSessionId: string;
82
+ iterations: number;
83
+ }>;
84
+
85
+ // Get session status: message count, token estimates, grouped message summaries
86
+ getSessionStatus(): Promise<{
87
+ message_count: number;
88
+ total_est_tokens: number;
89
+ groups: Array<{
90
+ label: string;
91
+ label_suffix: string;
92
+ message_count: number;
93
+ est_tokens: number;
94
+ messages: Array<{
95
+ role: string;
96
+ preview: string;
97
+ thoughts?: string;
98
+ est_tokens: number;
99
+ }>;
100
+ }>;
101
+ }>;
102
+ };
39
103
  }
40
104
  \`\`\`
41
105
 
@@ -55,5 +119,35 @@ interface HookOutput {
55
119
  update_context?: Record<string, any>;
56
120
  }
57
121
  \`\`\`
122
+
123
+ ### Examples
124
+
125
+ **Block edits to locked files (wildcard hook):**
126
+
127
+ \`\`\`typescript
128
+ export default async function(context: HookContext): Promise<HookOutput> {
129
+ const filePath = context.action?.args?.file_path || '';
130
+ if (filePath.includes('lock')) {
131
+ return {
132
+ should_halt_plan: true,
133
+ message: 'BLOCKED: Editing locked files is forbidden.'
134
+ };
135
+ }
136
+ return { should_halt_plan: false };
137
+ }
138
+ \`\`\`
139
+
140
+ **Auto-compress context when it grows too large (wildcard hook):**
141
+
142
+ \`\`\`typescript
143
+ export default async function(context: HookContext): Promise<HookOutput> {
144
+ const status = await context.api.getSessionStatus();
145
+ if (status.total_est_tokens > 70000) {
146
+ const result = await context.api.invokeSubAgent('compress-agent', 'Compress conversation history');
147
+ return { message: 'Context was ' + status.total_est_tokens + ' tokens. Compressed.' };
148
+ }
149
+ return { should_halt_plan: false };
150
+ }
151
+ \`\`\`
58
152
  `;
59
153
  //# sourceMappingURL=readme.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"readme.js","sourceRoot":"","sources":["../../../../../src/seeding/data/tool-hooks/readme.ts"],"names":[],"mappings":";;;AAAa,QAAA,QAAQ,GAAG,WAAW,CAAC;AACvB,QAAA,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDtB,CAAC"}
1
+ {"version":3,"file":"readme.js","sourceRoot":"","sources":["../../../../../src/seeding/data/tool-hooks/readme.ts"],"names":[],"mappings":";;;AAAa,QAAA,QAAQ,GAAG,WAAW,CAAC;AACvB,QAAA,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmJtB,CAAC"}
@@ -0,0 +1,76 @@
1
+ export interface HookAction {
2
+ tool_name: string;
3
+ args: Record<string, any>;
4
+ result?: {
5
+ status: 'SUCCESS' | 'FAILURE';
6
+ output?: string;
7
+ error?: string;
8
+ };
9
+ }
10
+ export interface HookPlanContext {
11
+ originalParsedActions: Array<{
12
+ tool_name: string;
13
+ arguments: any;
14
+ }>;
15
+ feedback: {
16
+ validationErrors: Array<{
17
+ tool_name: string;
18
+ error: string;
19
+ }>;
20
+ invalidToolErrors: Array<{
21
+ tool_name: string;
22
+ arguments: any;
23
+ }>;
24
+ };
25
+ flags: {
26
+ should_halt: boolean;
27
+ halt_reason?: string;
28
+ is_final?: boolean;
29
+ follow_up_initiated?: boolean;
30
+ };
31
+ }
32
+ export interface HookSession {
33
+ id: string;
34
+ }
35
+ export interface HookSubAgentResult {
36
+ content: string;
37
+ success: boolean;
38
+ childSessionId: string;
39
+ iterations: number;
40
+ }
41
+ export interface HookApi {
42
+ getContextSize(): Promise<number>;
43
+ invokeSubAgent(agentName: string, prompt: string, options?: {
44
+ sessionId?: string;
45
+ }): Promise<HookSubAgentResult>;
46
+ getSessionStatus(): Promise<HookSessionStatus>;
47
+ }
48
+ export interface HookSessionStatusGroup {
49
+ label: string;
50
+ label_suffix: string;
51
+ message_count: number;
52
+ est_tokens: number;
53
+ messages: Array<{
54
+ role: string;
55
+ preview: string;
56
+ thoughts?: string;
57
+ est_tokens: number;
58
+ }>;
59
+ }
60
+ export interface HookSessionStatus {
61
+ message_count: number;
62
+ total_est_tokens: number;
63
+ groups: HookSessionStatusGroup[];
64
+ }
65
+ export interface HookContext {
66
+ hook_type: 'before' | 'after';
67
+ action: HookAction;
68
+ plan_context: HookPlanContext;
69
+ session: HookSession;
70
+ api: HookApi;
71
+ }
72
+ export interface HookOutput {
73
+ should_halt_plan?: boolean;
74
+ message?: string;
75
+ update_context?: Record<string, any>;
76
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=hook-context.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-context.interface.js","sourceRoot":"","sources":["../../../src/tool-hooks/hook-context.interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ import { HookApi, HookSessionStatus, HookSubAgentResult } from './hook-context.interface';
2
+ export type HistoryProvider = (sessionId: string) => Promise<Array<{
3
+ parts: Array<{
4
+ text: string;
5
+ }>;
6
+ role: string;
7
+ }>>;
8
+ export type SubAgentInvoker = (agentName: string, prompt: string, parentSessionId: string, options?: {
9
+ sessionId?: string;
10
+ }) => Promise<HookSubAgentResult>;
11
+ export type SessionStatusProvider = (sessionId: string) => Promise<HookSessionStatus>;
12
+ export declare class HookRuntime {
13
+ private readonly logger;
14
+ private sessionId;
15
+ private historyProvider;
16
+ private subAgentInvoker;
17
+ private sessionStatusProvider;
18
+ setSessionId(sessionId: string): void;
19
+ setHistoryProvider(provider: HistoryProvider): void;
20
+ setSubAgentInvoker(invoker: SubAgentInvoker): void;
21
+ setSessionStatusProvider(provider: SessionStatusProvider): void;
22
+ createApi(): HookApi;
23
+ private getContextSize;
24
+ private invokeSubAgent;
25
+ private getSessionStatus;
26
+ }
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var HookRuntime_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.HookRuntime = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const gpt_tokenizer_1 = require("gpt-tokenizer");
13
+ let HookRuntime = HookRuntime_1 = class HookRuntime {
14
+ constructor() {
15
+ this.logger = new common_1.Logger(HookRuntime_1.name);
16
+ this.historyProvider = null;
17
+ this.subAgentInvoker = null;
18
+ this.sessionStatusProvider = null;
19
+ }
20
+ setSessionId(sessionId) {
21
+ this.sessionId = sessionId;
22
+ }
23
+ setHistoryProvider(provider) {
24
+ this.historyProvider = provider;
25
+ }
26
+ setSubAgentInvoker(invoker) {
27
+ this.subAgentInvoker = invoker;
28
+ }
29
+ setSessionStatusProvider(provider) {
30
+ this.sessionStatusProvider = provider;
31
+ }
32
+ createApi() {
33
+ return {
34
+ getContextSize: async () => {
35
+ return this.getContextSize();
36
+ },
37
+ invokeSubAgent: async (agentName, prompt, options) => {
38
+ return this.invokeSubAgent(agentName, prompt, options);
39
+ },
40
+ getSessionStatus: async () => {
41
+ return this.getSessionStatus();
42
+ },
43
+ };
44
+ }
45
+ async getContextSize() {
46
+ if (!this.sessionId) {
47
+ throw new Error('HookRuntime: sessionId not set. Cannot compute context size.');
48
+ }
49
+ if (!this.historyProvider) {
50
+ throw new Error('HookRuntime: historyProvider not set. Cannot compute context size.');
51
+ }
52
+ try {
53
+ const history = await this.historyProvider(this.sessionId);
54
+ const text = history
55
+ .map((h) => h.parts.map((p) => p.text).join(''))
56
+ .join(' ');
57
+ return (0, gpt_tokenizer_1.countTokens)(text);
58
+ }
59
+ catch (error) {
60
+ this.logger.error(`HookRuntime: Failed to compute context size for session ${this.sessionId}: ${error.message}`);
61
+ throw error;
62
+ }
63
+ }
64
+ async invokeSubAgent(agentName, prompt, options) {
65
+ if (!this.sessionId) {
66
+ throw new Error('HookRuntime: sessionId not set. Cannot invoke sub-agent.');
67
+ }
68
+ if (!this.subAgentInvoker) {
69
+ throw new Error('HookRuntime: subAgentInvoker not set. Cannot invoke sub-agent.');
70
+ }
71
+ try {
72
+ const result = await this.subAgentInvoker(agentName, prompt, this.sessionId, options?.sessionId ? { sessionId: options.sessionId } : undefined);
73
+ return result;
74
+ }
75
+ catch (error) {
76
+ this.logger.error(`HookRuntime: invokeSubAgent failed for agent "${agentName}": ${error.message}`);
77
+ return {
78
+ content: `Sub-agent invocation failed: ${error.message}`,
79
+ success: false,
80
+ childSessionId: '',
81
+ iterations: 0,
82
+ };
83
+ }
84
+ }
85
+ async getSessionStatus() {
86
+ if (!this.sessionId) {
87
+ throw new Error('HookRuntime: sessionId not set. Cannot get session status.');
88
+ }
89
+ if (!this.sessionStatusProvider) {
90
+ throw new Error('HookRuntime: sessionStatusProvider not set. Cannot get session status.');
91
+ }
92
+ try {
93
+ return await this.sessionStatusProvider(this.sessionId);
94
+ }
95
+ catch (error) {
96
+ this.logger.error(`HookRuntime: getSessionStatus failed for session ${this.sessionId}: ${error.message}`);
97
+ throw error;
98
+ }
99
+ }
100
+ };
101
+ exports.HookRuntime = HookRuntime;
102
+ exports.HookRuntime = HookRuntime = HookRuntime_1 = __decorate([
103
+ (0, common_1.Injectable)()
104
+ ], HookRuntime);
105
+ //# sourceMappingURL=hook-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-runtime.js","sourceRoot":"","sources":["../../../src/tool-hooks/hook-runtime.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAoD;AAMpD,iDAA4C;AAgBrC,IAAM,WAAW,mBAAjB,MAAM,WAAW;IAAjB;QACY,WAAM,GAAG,IAAI,eAAM,CAAC,aAAW,CAAC,IAAI,CAAC,CAAC;QAE/C,oBAAe,GAA2B,IAAI,CAAC;QAC/C,oBAAe,GAA2B,IAAI,CAAC;QAC/C,0BAAqB,GAAiC,IAAI,CAAC;IAqJrE,CAAC;IA/IC,YAAY,CAAC,SAAiB;QAC5B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAMD,kBAAkB,CAAC,QAAyB;QAC1C,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;IAClC,CAAC;IAMD,kBAAkB,CAAC,OAAwB;QACzC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;IACjC,CAAC;IAMD,wBAAwB,CAAC,QAA+B;QACtD,IAAI,CAAC,qBAAqB,GAAG,QAAQ,CAAC;IACxC,CAAC;IAMD,SAAS;QACP,OAAO;YACL,cAAc,EAAE,KAAK,IAAqB,EAAE;gBAC1C,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;YAC/B,CAAC;YACD,cAAc,EAAE,KAAK,EACnB,SAAiB,EACjB,MAAc,EACd,OAAgC,EACH,EAAE;gBAC/B,OAAO,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACzD,CAAC;YACD,gBAAgB,EAAE,KAAK,IAAgC,EAAE;gBACvD,OAAO,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACjC,CAAC;SACF,CAAC;IACJ,CAAC;IAKO,KAAK,CAAC,cAAc;QAC1B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,8DAA8D,CAC/D,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC3D,MAAM,IAAI,GAAG,OAAO;iBACjB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;iBAC/C,IAAI,CAAC,GAAG,CAAC,CAAC;YACb,OAAO,IAAA,2BAAW,EAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,2DAA2D,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,OAAO,EAAE,CAC9F,CAAC;YACF,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAKO,KAAK,CAAC,cAAc,CAC1B,SAAiB,EACjB,MAAc,EACd,OAAgC;QAEhC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,0DAA0D,CAC3D,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,gEAAgE,CACjE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CACvC,SAAS,EACT,MAAM,EACN,IAAI,CAAC,SAAS,EACd,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAClE,CAAC;YACF,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,iDAAiD,SAAS,MAAM,KAAK,CAAC,OAAO,EAAE,CAChF,CAAC;YACF,OAAO;gBACL,OAAO,EAAE,gCAAgC,KAAK,CAAC,OAAO,EAAE;gBACxD,OAAO,EAAE,KAAK;gBACd,cAAc,EAAE,EAAE;gBAClB,UAAU,EAAE,CAAC;aACd,CAAC;QACJ,CAAC;IACH,CAAC;IAKO,KAAK,CAAC,gBAAgB;QAC5B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,wEAAwE,CACzE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,oDAAoD,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,OAAO,EAAE,CACvF,CAAC;YACF,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF,CAAA;AA1JY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;GACA,WAAW,CA0JvB"}
@@ -12,6 +12,7 @@ const typeorm_1 = require("@nestjs/typeorm");
12
12
  const core_entities_1 = require("../core-entities");
13
13
  const tool_hooks_controller_1 = require("./tool-hooks.controller");
14
14
  const tool_hooks_service_1 = require("./tool-hooks.service");
15
+ const hook_runtime_1 = require("./hook-runtime");
15
16
  const execution_logs_module_1 = require("../execution-logs/execution-logs.module");
16
17
  let ToolHooksModule = class ToolHooksModule {
17
18
  };
@@ -20,8 +21,8 @@ exports.ToolHooksModule = ToolHooksModule = __decorate([
20
21
  (0, common_1.Module)({
21
22
  imports: [typeorm_1.TypeOrmModule.forFeature([core_entities_1.ToolHook]), execution_logs_module_1.ExecutionLogsModule],
22
23
  controllers: [tool_hooks_controller_1.ToolHooksController],
23
- providers: [tool_hooks_service_1.ToolHooksService],
24
- exports: [tool_hooks_service_1.ToolHooksService],
24
+ providers: [tool_hooks_service_1.ToolHooksService, hook_runtime_1.HookRuntime],
25
+ exports: [tool_hooks_service_1.ToolHooksService, hook_runtime_1.HookRuntime],
25
26
  })
26
27
  ], ToolHooksModule);
27
28
  //# sourceMappingURL=tool-hooks.module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-hooks.module.js","sourceRoot":"","sources":["../../../src/tool-hooks/tool-hooks.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,oDAA4C;AAC5C,mEAA8D;AAC9D,6DAAwD;AACxD,mFAA8E;AAQvE,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAN3B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,uBAAa,CAAC,UAAU,CAAC,CAAC,wBAAQ,CAAC,CAAC,EAAE,2CAAmB,CAAC;QACpE,WAAW,EAAE,CAAC,2CAAmB,CAAC;QAClC,SAAS,EAAE,CAAC,qCAAgB,CAAC;QAC7B,OAAO,EAAE,CAAC,qCAAgB,CAAC;KAC5B,CAAC;GACW,eAAe,CAAG"}
1
+ {"version":3,"file":"tool-hooks.module.js","sourceRoot":"","sources":["../../../src/tool-hooks/tool-hooks.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,oDAA4C;AAC5C,mEAA8D;AAC9D,6DAAwD;AACxD,iDAA6C;AAC7C,mFAA8E;AAQvE,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAN3B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,uBAAa,CAAC,UAAU,CAAC,CAAC,wBAAQ,CAAC,CAAC,EAAE,2CAAmB,CAAC;QACpE,WAAW,EAAE,CAAC,2CAAmB,CAAC;QAClC,SAAS,EAAE,CAAC,qCAAgB,EAAE,0BAAW,CAAC;QAC1C,OAAO,EAAE,CAAC,qCAAgB,EAAE,0BAAW,CAAC;KACzC,CAAC;GACW,eAAe,CAAG"}
@@ -2,12 +2,15 @@ import { Repository } from 'typeorm';
2
2
  import { ToolHook } from '../core-entities';
3
3
  import { CreateToolHookDto } from './dto/create-tool-hook.dto';
4
4
  import { UpdateToolHookDto } from './dto/update-tool-hook.dto';
5
+ import { HookRuntime } from './hook-runtime';
6
+ import { HookContext, HookOutput } from './hook-context.interface';
5
7
  export declare class ToolHooksService {
6
8
  private readonly toolHooksRepository;
9
+ private readonly hookRuntime;
7
10
  private readonly logger;
8
11
  private readonly projectRoot;
9
12
  private readonly hooksDir;
10
- constructor(toolHooksRepository: Repository<ToolHook>);
13
+ constructor(toolHooksRepository: Repository<ToolHook>, hookRuntime: HookRuntime);
11
14
  private ensureHooksDir;
12
15
  private createExampleFiles;
13
16
  findAll(): Promise<ToolHook[]>;
@@ -22,7 +25,11 @@ export declare class ToolHooksService {
22
25
  private getArgsInterface;
23
26
  generateSamplePayload(toolName: string, timing: 'before' | 'after'): any;
24
27
  private getSampleArgs;
25
- executeHook(scriptFilename: string, payload: any): Promise<any>;
28
+ executeHook(scriptFilename: string, payload: HookContext): Promise<HookOutput>;
29
+ executeHookWithLogs(scriptFilename: string, payload: HookContext): Promise<{
30
+ result: HookOutput;
31
+ logs: string[];
32
+ }>;
26
33
  testHook(scriptFilename: string, payload: any): Promise<{
27
34
  result: any;
28
35
  logs: string[];
@@ -18,15 +18,16 @@ const common_1 = require("@nestjs/common");
18
18
  const typeorm_1 = require("@nestjs/typeorm");
19
19
  const typeorm_2 = require("typeorm");
20
20
  const core_entities_1 = require("../core-entities");
21
+ const hook_runtime_1 = require("./hook-runtime");
21
22
  const fs = require("fs/promises");
22
23
  const path = require("path");
23
- const vm = require("vm");
24
24
  const readmeData = require("../seeding/data/tool-hooks/readme");
25
25
  const exampleHookData = require("../seeding/data/tool-hooks/example-hook");
26
26
  const eslintHookData = require("../seeding/data/tool-hooks/eslint-hook");
27
27
  let ToolHooksService = ToolHooksService_1 = class ToolHooksService {
28
- constructor(toolHooksRepository) {
28
+ constructor(toolHooksRepository, hookRuntime) {
29
29
  this.toolHooksRepository = toolHooksRepository;
30
+ this.hookRuntime = hookRuntime;
30
31
  this.logger = new common_1.Logger(ToolHooksService_1.name);
31
32
  this.projectRoot = process.env.REPOBURG_PROJECT_PATH || process.cwd();
32
33
  this.hooksDir = path.join(this.projectRoot, '.repoburg', 'hooks');
@@ -73,8 +74,18 @@ let ToolHooksService = ToolHooksService_1 = class ToolHooksService {
73
74
  await this.toolHooksRepository.delete(id);
74
75
  }
75
76
  async findByTrigger(toolName, timing) {
76
- return this.toolHooksRepository.find({
77
- where: { tool_name: toolName, timing, is_active: true },
77
+ const hooks = await this.toolHooksRepository.find({
78
+ where: [
79
+ { tool_name: toolName, timing, is_active: true },
80
+ { tool_name: '*', timing, is_active: true },
81
+ ],
82
+ });
83
+ return hooks.sort((a, b) => {
84
+ if (a.tool_name === '*' && b.tool_name !== '*')
85
+ return -1;
86
+ if (a.tool_name !== '*' && b.tool_name === '*')
87
+ return 1;
88
+ return 0;
78
89
  });
79
90
  }
80
91
  async listScripts() {
@@ -94,18 +105,30 @@ let ToolHooksService = ToolHooksService_1 = class ToolHooksService {
94
105
  await fs.writeFile(scriptPath, content, 'utf-8');
95
106
  }
96
107
  generateTemplate(toolName) {
108
+ const isWildcard = toolName === '*';
97
109
  const argsType = this.getArgsInterface(toolName);
110
+ const pascalName = isWildcard ? 'AnyTool' : this.pascalCase(toolName);
98
111
  return `/**
99
- * Hook for tool: ${toolName}
112
+ * Hook for tool: ${isWildcard ? 'any tool (wildcard)' : toolName}
100
113
  */
101
114
 
102
115
  // --- Type Definitions ---
103
116
 
104
- export interface ${this.pascalCase(toolName)}Args {
117
+ export interface ${pascalName}Args {
105
118
  ${argsType}
106
119
  }
107
120
 
108
- export interface PlanContext {
121
+ export interface HookAction {
122
+ tool_name: ${isWildcard ? 'string' : `'${toolName}'`};
123
+ args: ${pascalName}Args;
124
+ result?: {
125
+ status: 'SUCCESS' | 'FAILURE';
126
+ output?: string;
127
+ error?: string;
128
+ };
129
+ }
130
+
131
+ export interface HookPlanContext {
109
132
  originalParsedActions: Array<{ tool_name: string; arguments: any }>;
110
133
  feedback: {
111
134
  validationErrors: Array<{ tool_name: string; error: string }>;
@@ -114,26 +137,56 @@ export interface PlanContext {
114
137
  flags: {
115
138
  should_halt: boolean;
116
139
  halt_reason?: string;
117
- is_final: boolean;
118
- follow_up_initiated: boolean;
140
+ is_final?: boolean;
141
+ follow_up_initiated?: boolean;
119
142
  };
120
143
  }
121
144
 
122
- export interface HookAction {
123
- tool_name: '${toolName}';
124
- args: ${this.pascalCase(toolName)}Args;
125
- // Only present for 'after' hooks
126
- result?: {
127
- status: 'SUCCESS' | 'FAILURE';
128
- output?: string;
129
- error?: string;
130
- };
145
+ export interface HookSession {
146
+ id: string;
147
+ }
148
+
149
+ export interface HookSubAgentResult {
150
+ content: string;
151
+ success: boolean;
152
+ childSessionId: string;
153
+ iterations: number;
154
+ }
155
+
156
+ export interface HookSessionStatusGroup {
157
+ label: string;
158
+ label_suffix: string;
159
+ message_count: number;
160
+ est_tokens: number;
161
+ messages: Array<{
162
+ role: string;
163
+ preview: string;
164
+ thoughts?: string;
165
+ est_tokens: number;
166
+ }>;
167
+ }
168
+
169
+ export interface HookSessionStatus {
170
+ message_count: number;
171
+ total_est_tokens: number;
172
+ groups: HookSessionStatusGroup[];
173
+ }
174
+
175
+ export interface HookApi {
176
+ /** Get the current context size in tokens for the session */
177
+ getContextSize(): Promise<number>;
178
+ /** Invoke a sub-agent synchronously (blocks until complete) */
179
+ invokeSubAgent(agentName: string, prompt: string, options?: { sessionId?: string }): Promise<HookSubAgentResult>;
180
+ /** Get session status: message count, token estimates, grouped message summaries */
181
+ getSessionStatus(): Promise<HookSessionStatus>;
131
182
  }
132
183
 
133
184
  export interface HookContext {
134
185
  hook_type: 'before' | 'after';
135
186
  action: HookAction;
136
- plan_context: PlanContext;
187
+ plan_context: HookPlanContext;
188
+ session: HookSession;
189
+ api: HookApi;
137
190
  }
138
191
 
139
192
  export interface HookOutput {
@@ -146,8 +199,13 @@ export interface HookOutput {
146
199
 
147
200
  export default async function(context: HookContext): Promise<HookOutput | void> {
148
201
  const { args } = context.action;
149
-
150
- // Add your hook logic here
202
+
203
+ // Example: Check session status and compress if too large
204
+ // const status = await context.api.getSessionStatus();
205
+ // if (status.total_est_tokens > 70000) {
206
+ // const result = await context.api.invokeSubAgent('compress-agent', 'Compress conversation history');
207
+ // return { message: \`Compressed from \${status.total_est_tokens} tokens\` };
208
+ // }
151
209
 
152
210
  return {
153
211
  should_halt_plan: false
@@ -163,6 +221,9 @@ export default async function(context: HookContext): Promise<HookOutput | void>
163
221
  }
164
222
  getArgsInterface(toolName) {
165
223
  switch (toolName) {
224
+ case '*':
225
+ return ` // Wildcard hooks receive args for any tool
226
+ [key: string]: any;`;
166
227
  case 'patch':
167
228
  return ` file_path: string;
168
229
  patch_code: string;`;
@@ -208,13 +269,22 @@ export default async function(context: HookContext): Promise<HookOutput | void>
208
269
  : {}),
209
270
  },
210
271
  plan_context: {
272
+ originalParsedActions: [{ tool_name: toolName, arguments: sampleArgs }],
211
273
  flags: { should_halt: false },
212
- feedback: { validationErrors: [] },
274
+ feedback: { validationErrors: [], invalidToolErrors: [] },
275
+ },
276
+ session: { id: 'sample-session-id' },
277
+ api: {
278
+ getContextSize: '[async function] Returns token count of session history.',
279
+ invokeSubAgent: '[async function] Invokes a sub-agent and returns result.',
280
+ getSessionStatus: '[async function] Returns session status with message count, token estimates, and grouped summaries.',
213
281
  },
214
282
  };
215
283
  }
216
284
  getSampleArgs(toolName) {
217
285
  switch (toolName) {
286
+ case '*':
287
+ return { file_path: 'src/example.ts', content: '// example' };
218
288
  case 'patch':
219
289
  return { file_path: 'src/main.ts', patch_code: '// ... code ...' };
220
290
  case 'create_file':
@@ -240,6 +310,34 @@ export default async function(context: HookContext): Promise<HookOutput | void>
240
310
  async executeHook(scriptFilename, payload) {
241
311
  return this.runScript(scriptFilename, payload, console);
242
312
  }
313
+ async executeHookWithLogs(scriptFilename, payload) {
314
+ const logs = [];
315
+ const mockConsole = {
316
+ log: (...args) => logs.push(args
317
+ .map((a) => (typeof a === 'object' ? JSON.stringify(a) : String(a)))
318
+ .join(' ')),
319
+ error: (...args) => logs.push('[ERROR] ' +
320
+ args
321
+ .map((a) => typeof a === 'object' ? JSON.stringify(a) : String(a))
322
+ .join(' ')),
323
+ warn: (...args) => logs.push('[WARN] ' +
324
+ args
325
+ .map((a) => typeof a === 'object' ? JSON.stringify(a) : String(a))
326
+ .join(' ')),
327
+ info: (...args) => logs.push('[INFO] ' +
328
+ args
329
+ .map((a) => typeof a === 'object' ? JSON.stringify(a) : String(a))
330
+ .join(' ')),
331
+ };
332
+ try {
333
+ const result = await this.runScript(scriptFilename, payload, mockConsole);
334
+ return { result, logs };
335
+ }
336
+ catch (e) {
337
+ logs.push(`[SYSTEM ERROR] ${e.message}`);
338
+ throw e;
339
+ }
340
+ }
243
341
  async testHook(scriptFilename, payload) {
244
342
  const logs = [];
245
343
  const mockConsole = {
@@ -293,16 +391,8 @@ export default async function(context: HookContext): Promise<HookOutput | void>
293
391
  },
294
392
  });
295
393
  const moduleRef = { exports: {} };
296
- const sandbox = {
297
- module: moduleRef,
298
- exports: moduleRef.exports,
299
- require: require,
300
- console: consoleEnv,
301
- process: process,
302
- };
303
- const context = vm.createContext(sandbox);
304
- const script = new vm.Script(outputText);
305
- script.runInContext(context);
394
+ const moduleFunc = new Function('module', 'exports', 'require', 'console', 'process', '__filename', '__dirname', outputText);
395
+ moduleFunc(moduleRef, moduleRef.exports, require, consoleEnv, process, scriptPath, this.hooksDir);
306
396
  const exported = moduleRef.exports;
307
397
  const defaultExport = exported.default || exported;
308
398
  if (typeof defaultExport !== 'function') {
@@ -321,6 +411,8 @@ exports.ToolHooksService = ToolHooksService;
321
411
  exports.ToolHooksService = ToolHooksService = ToolHooksService_1 = __decorate([
322
412
  (0, common_1.Injectable)(),
323
413
  __param(0, (0, typeorm_1.InjectRepository)(core_entities_1.ToolHook)),
324
- __metadata("design:paramtypes", [typeorm_2.Repository])
414
+ __param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => hook_runtime_1.HookRuntime))),
415
+ __metadata("design:paramtypes", [typeorm_2.Repository,
416
+ hook_runtime_1.HookRuntime])
325
417
  ], ToolHooksService);
326
418
  //# sourceMappingURL=tool-hooks.service.js.map