@ebowwa/glm-daemon 0.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.
Files changed (50) hide show
  1. package/README.md +264 -0
  2. package/dist/agent.d.ts +37 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +97 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/daemon.d.ts +91 -0
  7. package/dist/daemon.d.ts.map +1 -0
  8. package/dist/daemon.js +449 -0
  9. package/dist/daemon.js.map +1 -0
  10. package/dist/hooks.d.ts +34 -0
  11. package/dist/hooks.d.ts.map +1 -0
  12. package/dist/hooks.js +82 -0
  13. package/dist/hooks.js.map +1 -0
  14. package/dist/index.d.ts +21 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +31 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/state.d.ts +31 -0
  19. package/dist/state.d.ts.map +1 -0
  20. package/dist/state.js +67 -0
  21. package/dist/state.js.map +1 -0
  22. package/dist/tools.d.ts +42 -0
  23. package/dist/tools.d.ts.map +1 -0
  24. package/dist/tools.js +110 -0
  25. package/dist/tools.js.map +1 -0
  26. package/dist/types.d.ts +222 -0
  27. package/dist/types.d.ts.map +1 -0
  28. package/dist/types.js +19 -0
  29. package/dist/types.js.map +1 -0
  30. package/dist/worktree.d.ts +42 -0
  31. package/dist/worktree.d.ts.map +1 -0
  32. package/dist/worktree.js +86 -0
  33. package/dist/worktree.js.map +1 -0
  34. package/package.json +70 -0
  35. package/src/agent.js +166 -0
  36. package/src/agent.ts +110 -0
  37. package/src/daemon.js +591 -0
  38. package/src/daemon.ts +529 -0
  39. package/src/hooks.js +145 -0
  40. package/src/hooks.ts +94 -0
  41. package/src/index.js +105 -0
  42. package/src/index.ts +43 -0
  43. package/src/state.js +168 -0
  44. package/src/state.ts +77 -0
  45. package/src/tools.js +192 -0
  46. package/src/tools.ts +134 -0
  47. package/src/types.js +21 -0
  48. package/src/types.ts +249 -0
  49. package/src/worktree.js +195 -0
  50. package/src/worktree.ts +122 -0
package/src/daemon.ts ADDED
@@ -0,0 +1,529 @@
1
+ /**
2
+ * GLM Daemon - Core Daemon
3
+ *
4
+ * Main orchestrator for autonomous GLM 4.7 agents.
5
+ * Implements SLAM pattern: State → Loop → Action → Memory
6
+ */
7
+
8
+ import { promises as fsp } from "fs";
9
+ import { join } from "path";
10
+ import type {
11
+ GLMDaemonConfig,
12
+ GLMDaemonState,
13
+ GLMPhase,
14
+ GLMDaemonStatus,
15
+ } from "./types.js";
16
+ import { GLMAgent } from "./agent.js";
17
+ import { HookSystem } from "./hooks.js";
18
+ import { ToolBridge } from "./tools.js";
19
+ import { StateManager } from "./state.js";
20
+ import { WorktreeManager } from "./worktree.js";
21
+ import { Team } from "@ebowwa/teammates";
22
+ import { GLMClient } from "@ebowwa/ai/client";
23
+
24
+ const STATE_FILE = ".glm-daemon.json";
25
+ const SETTINGS_FILE = ".claude/settings.local.json";
26
+
27
+ export class GLMDaemon {
28
+ private config: GLMDaemonConfig;
29
+ private state: GLMDaemonState;
30
+ private statePath: string;
31
+ private agent: GLMAgent;
32
+ private hooks: HookSystem;
33
+ private tools: ToolBridge;
34
+ private stateManager: StateManager;
35
+ private worktreeManager: WorktreeManager;
36
+ private team: Team | null = null;
37
+ private subagents: Map<string, GLMAgent> = new Map();
38
+ private running = false;
39
+ private client: GLMClient;
40
+
41
+ constructor(config: GLMDaemonConfig) {
42
+ this.config = config;
43
+ this.statePath = join(config.cwd, STATE_FILE);
44
+ this.client = new GLMClient();
45
+
46
+ // Initialize state
47
+ this.state = this.initializeState();
48
+
49
+ // Initialize components
50
+ this.hooks = new HookSystem(config.hooks || {});
51
+ this.tools = new ToolBridge(config.tools || {});
52
+ this.stateManager = new StateManager(this.statePath);
53
+ this.worktreeManager = new WorktreeManager(config);
54
+
55
+ // Initialize main agent
56
+ this.agent = new GLMAgent({
57
+ agentId: "glm-daemon",
58
+ name: "GLM Daemon",
59
+ prompt: this.buildSystemPrompt(),
60
+ model: config.model,
61
+ tools: this.tools.getAvailableTools(),
62
+ });
63
+ }
64
+
65
+ /**
66
+ * Initialize daemon state
67
+ */
68
+ private initializeState(): GLMDaemonState {
69
+ const now = new Date().toISOString();
70
+ return {
71
+ prompt: "", // Will be set when starting
72
+ promise: this.config.completionPromise || "TASK_COMPLETE",
73
+ iteration: 0,
74
+ maxIterations: this.config.maxIterations || 0,
75
+ startTime: now,
76
+ lastUpdate: now,
77
+ tokens: {
78
+ totalInput: 0,
79
+ totalOutput: 0,
80
+ byIteration: [],
81
+ },
82
+ filesChanged: [],
83
+ workMemory: {
84
+ completedFiles: [],
85
+ fileChecksums: {},
86
+ },
87
+ slam: {
88
+ enabled: true,
89
+ phase: "planning",
90
+ state: {
91
+ currentTask: "",
92
+ beliefs: {},
93
+ goals: [this.config.completionPromise || "TASK_COMPLETE"],
94
+ },
95
+ subtasks: [],
96
+ currentSubtask: null,
97
+ completedSubtasks: [],
98
+ memory: {
99
+ actionsTaken: [],
100
+ outcomes: {},
101
+ patterns: {},
102
+ },
103
+ },
104
+ git: {
105
+ enabled: this.config.autoCommit || this.config.autoPR || false,
106
+ autoCommit: this.config.autoCommit || false,
107
+ autoPR: this.config.autoPR || false,
108
+ baseBranch: this.config.baseBranch || "main",
109
+ useWorktree: this.config.enableWorktree || false,
110
+ branchName: "",
111
+ branchCreated: false,
112
+ currentCommit: "",
113
+ },
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Build system prompt from config and state
119
+ */
120
+ private buildSystemPrompt(): string {
121
+ return `You are an autonomous AI agent powered by GLM 4.7.
122
+
123
+ Your task: ${this.state.prompt}
124
+
125
+ You work in SLAM phases:
126
+ - Planning: Break down the task into subtasks
127
+ - Executing: Work on the current subtask
128
+ - Paranoid: Review your work for bugs and issues
129
+ - Reviewing: Check quality of completed work
130
+ - Fixing: Address any issues found
131
+ - Committing: Commit your changes
132
+ - Complete: Task is finished
133
+
134
+ Use tools when available. Be thorough but efficient.
135
+
136
+ Current phase: ${this.state.slam.phase}
137
+ Iteration: ${this.state.iteration}
138
+ `;
139
+ }
140
+
141
+ /**
142
+ * Start the daemon with a prompt
143
+ */
144
+ async start(prompt: string): Promise<string> {
145
+ if (this.running) {
146
+ throw new Error("Daemon already running");
147
+ }
148
+
149
+ this.state.prompt = prompt;
150
+ this.state.slam.state.currentTask = prompt;
151
+ this.running = true;
152
+
153
+ // Setup worktree if enabled
154
+ if (this.config.enableWorktree) {
155
+ const worktreePath = await this.worktreeManager.createWorktree();
156
+ this.state.git.branchName = await this.worktreeManager.createBranch();
157
+ this.state.git.branchCreated = true;
158
+ }
159
+
160
+ // Initialize team for multi-agent coordination
161
+ this.team = await Team.create({
162
+ name: this.config.teamName,
163
+ description: `GLM daemon for: ${prompt.substring(0, 50)}...`,
164
+ });
165
+
166
+ // Run session start hook
167
+ await this.hooks.execute("onSessionStart", this.state);
168
+
169
+ // Save initial state
170
+ await this.stateManager.save(this.state);
171
+
172
+ // Start main loop
173
+ this.runLoop().catch((error) => {
174
+ console.error("[GLMDaemon] Loop error:", error);
175
+ this.running = false;
176
+ });
177
+
178
+ return this.config.teamName;
179
+ }
180
+
181
+ /**
182
+ * Main daemon loop (SLAM pattern)
183
+ */
184
+ private async runLoop(): Promise<void> {
185
+ while (this.running) {
186
+ // Check completion condition
187
+ if (this.checkCompletion()) {
188
+ await this.complete();
189
+ break;
190
+ }
191
+
192
+ // Check iteration limit
193
+ if (
194
+ this.state.maxIterations > 0 &&
195
+ this.state.iteration >= this.state.maxIterations
196
+ ) {
197
+ console.log(`[GLMDaemon] Max iterations reached: ${this.state.maxIterations}`);
198
+ await this.stop();
199
+ break;
200
+ }
201
+
202
+ // Run iteration start hook
203
+ await this.hooks.execute("onIterationStart", this.state.iteration);
204
+
205
+ // Execute current SLAM phase
206
+ await this.executePhase();
207
+
208
+ // Update state
209
+ this.state.iteration++;
210
+ this.state.lastUpdate = new Date().toISOString();
211
+
212
+ // Save state
213
+ await this.stateManager.save(this.state);
214
+
215
+ // Run iteration end hook
216
+ await this.hooks.execute("onIterationEnd", this.state.iteration, null);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Execute current SLAM phase
222
+ */
223
+ private async executePhase(): Promise<void> {
224
+ const phase = this.state.slam.phase;
225
+
226
+ switch (phase) {
227
+ case "planning":
228
+ await this.phasePlanning();
229
+ break;
230
+ case "executing":
231
+ await this.phaseExecuting();
232
+ break;
233
+ case "paranoid":
234
+ await this.phaseParanoid();
235
+ break;
236
+ case "reviewing":
237
+ await this.phaseReviewing();
238
+ break;
239
+ case "fixing":
240
+ await this.phaseFixing();
241
+ break;
242
+ case "committing":
243
+ await this.phaseCommitting();
244
+ break;
245
+ case "complete":
246
+ await this.complete();
247
+ break;
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Planning phase - break down task into subtasks
253
+ */
254
+ private async phasePlanning(): Promise<void> {
255
+ console.log("[GLMDaemon] Phase: Planning");
256
+
257
+ const prompt = `${this.state.prompt}
258
+
259
+ Break this task down into 3-7 concrete subtasks.
260
+ For each subtask, specify:
261
+ - Title (short description)
262
+ - Description (what needs to be done)
263
+ - Dependencies (which subtasks must come first)
264
+
265
+ Respond with a JSON array of subtasks.
266
+ `;
267
+
268
+ const response = await this.agent.execute(prompt);
269
+ const subtasks = this.parseSubtasks(response);
270
+
271
+ this.state.slam.subtasks = subtasks;
272
+ this.state.slam.phase = "executing";
273
+ this.state.slam.currentSubtask = subtasks[0]?.id || null;
274
+ }
275
+
276
+ /**
277
+ * Executing phase - work on current subtask
278
+ */
279
+ private async phaseExecuting(): Promise<void> {
280
+ const currentSubtaskId = this.state.slam.currentSubtask;
281
+ if (!currentSubtaskId) {
282
+ this.state.slam.phase = "reviewing";
283
+ return;
284
+ }
285
+
286
+ const subtask = this.state.slam.subtasks.find((st) => st.id === currentSubtaskId);
287
+ if (!subtask) {
288
+ this.state.slam.phase = "reviewing";
289
+ return;
290
+ }
291
+
292
+ console.log(`[GLMDaemon] Executing subtask: ${subtask.title}`);
293
+
294
+ const prompt = `Work on this subtask: ${subtask.title}
295
+
296
+ ${subtask.description}
297
+
298
+ Use available tools to complete the work.
299
+ Focus on code changes, testing, and verification.
300
+ `;
301
+
302
+ const response = await this.agent.execute(prompt);
303
+
304
+ // Update subtask status
305
+ subtask.status = "completed";
306
+ subtask.result = response;
307
+ this.state.slam.completedSubtasks.push(currentSubtaskId);
308
+
309
+ // Move to next subtask or paranoid phase
310
+ const nextSubtask = this.state.slam.subtasks.find(
311
+ (st) => !this.state.slam.completedSubtasks.includes(st.id)
312
+ );
313
+
314
+ if (nextSubtask) {
315
+ this.state.slam.currentSubtask = nextSubtask.id;
316
+ } else {
317
+ this.state.slam.phase = "paranoid";
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Paranoid phase - quality check after every edit
323
+ */
324
+ private async phaseParanoid(): Promise<void> {
325
+ console.log("[GLMDaemon] Phase: Paranoid (quality check)");
326
+
327
+ const prompt = `Review the changes made in this session for:
328
+ - Bugs or errors
329
+ - Edge cases not handled
330
+ - Security vulnerabilities
331
+ - Performance issues
332
+ - Breaking changes
333
+
334
+ Task: ${this.state.prompt}
335
+
336
+ Changes made:
337
+ ${this.state.filesChanged.map((f) => `- ${f}`).join("\n")}
338
+
339
+ Be thorough. If issues found, we'll fix them. If clean, we proceed to review.
340
+ `;
341
+
342
+ const response = await this.agent.execute(prompt);
343
+ const hasIssues = this.detectIssues(response);
344
+
345
+ if (hasIssues) {
346
+ this.state.slam.phase = "fixing";
347
+ } else {
348
+ this.state.slam.phase = "reviewing";
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Reviewing phase - final quality review
354
+ */
355
+ private async phaseReviewing(): Promise<void> {
356
+ console.log("[GLMDaemon] Phase: Reviewing");
357
+
358
+ const prompt = `Final review of the completed work.
359
+
360
+ Task: ${this.state.prompt}
361
+ Completion promise: ${this.state.promise}
362
+
363
+ Does the implementation fully satisfy the completion promise?
364
+ Are there any remaining issues or TODOs?
365
+
366
+ If complete and clean, respond with "APPROVED".
367
+ Otherwise, list remaining issues.
368
+ `;
369
+
370
+ const response = await this.agent.execute(prompt);
371
+
372
+ if (response.includes("APPROVED")) {
373
+ this.state.slam.phase = "committing";
374
+ } else {
375
+ this.state.slam.phase = "fixing";
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Fixing phase - fix issues found
381
+ */
382
+ private async phaseFixing(): Promise<void> {
383
+ console.log("[GLMDaemon] Phase: Fixing");
384
+
385
+ const prompt = `Fix the issues identified in the review.
386
+
387
+ Use tools to make necessary changes.
388
+ Test your fixes thoroughly.
389
+ `;
390
+
391
+ await this.agent.execute(prompt);
392
+ this.state.slam.phase = "paranoid"; // Re-check after fixes
393
+ }
394
+
395
+ /**
396
+ * Committing phase - commit changes
397
+ */
398
+ private async phaseCommitting(): Promise<void> {
399
+ console.log("[GLMDaemon] Phase: Committing");
400
+
401
+ if (this.state.git.autoCommit) {
402
+ // Commit changes via worktree manager
403
+ await this.worktreeManager.commitChanges({
404
+ message: `feat: ${this.state.prompt.substring(0, 50)}`,
405
+ files: this.state.filesChanged,
406
+ });
407
+ }
408
+
409
+ this.state.slam.phase = "complete";
410
+ }
411
+
412
+ /**
413
+ * Complete the daemon run
414
+ */
415
+ private async complete(): Promise<void> {
416
+ console.log("[GLMDaemon] Task complete!");
417
+ this.state.slam.phase = "complete";
418
+ this.running = false;
419
+
420
+ // Run session end hook
421
+ await this.hooks.execute("onSessionEnd", this.state);
422
+
423
+ // Save final state
424
+ await this.stateManager.save(this.state);
425
+
426
+ // Create PR if enabled
427
+ if (this.state.git.autoPR) {
428
+ await this.worktreeManager.createPullRequest({
429
+ title: this.state.prompt.substring(0, 50),
430
+ body: `Completes: ${this.state.promise}`,
431
+ });
432
+ }
433
+ }
434
+
435
+ /**
436
+ * Stop the daemon
437
+ */
438
+ async stop(): Promise<void> {
439
+ console.log("[GLMDaemon] Stopping...");
440
+ this.running = false;
441
+
442
+ // Cleanup
443
+ if (this.team) {
444
+ await this.team.broadcast("team-lead", "Daemon stopping");
445
+ }
446
+
447
+ // Save final state
448
+ await this.stateManager.save(this.state);
449
+ }
450
+
451
+ /**
452
+ * Get daemon status
453
+ */
454
+ getStatus(): GLMDaemonStatus {
455
+ return {
456
+ id: this.config.teamName,
457
+ status: this.running ? "running" : "stopped",
458
+ phase: this.state.slam.phase,
459
+ iteration: this.state.iteration,
460
+ prompt: this.state.prompt,
461
+ currentTask: this.state.slam.state.currentTask,
462
+ totalSubtasks: this.state.slam.subtasks.length,
463
+ completedSubtasks: this.state.slam.completedSubtasks.length,
464
+ startedAt: this.state.startTime,
465
+ lastUpdate: this.state.lastUpdate,
466
+ };
467
+ }
468
+
469
+ /**
470
+ * Parse subtasks from agent response
471
+ */
472
+ private parseSubtasks(response: string): Array<{
473
+ id: string;
474
+ title: string;
475
+ description: string;
476
+ status: "pending" | "in_progress" | "completed" | "blocked" | "failed";
477
+ dependencies?: string[];
478
+ }> {
479
+ try {
480
+ const jsonMatch = response.match(/\[[\s\S]*\]/);
481
+ if (jsonMatch) {
482
+ const parsed = JSON.parse(jsonMatch[0]);
483
+ return parsed.map((st: any, idx: number) => ({
484
+ id: st.id || `subtask-${idx}`,
485
+ title: st.title || st.task || `Subtask ${idx + 1}`,
486
+ description: st.description || "",
487
+ status: "pending" as const,
488
+ dependencies: st.dependencies || [],
489
+ }));
490
+ }
491
+ } catch {
492
+ // Fallback: create single subtask from prompt
493
+ return [{
494
+ id: "subtask-0",
495
+ title: this.state.prompt.substring(0, 50),
496
+ description: this.state.prompt,
497
+ status: "pending",
498
+ }];
499
+ }
500
+
501
+ return [];
502
+ }
503
+
504
+ /**
505
+ * Detect issues in paranoid check response
506
+ */
507
+ private detectIssues(response: string): boolean {
508
+ const issueKeywords = [
509
+ "bug", "error", "issue", "problem", "vulnerability",
510
+ "fix", "improve", "missing", "incomplete", "broken"
511
+ ];
512
+
513
+ const lower = response.toLowerCase();
514
+ return issueKeywords.some((kw) => lower.includes(kw));
515
+ }
516
+
517
+ /**
518
+ * Check if completion promise is met
519
+ */
520
+ private checkCompletion(): boolean {
521
+ // Simple check: all subtasks complete
522
+ const allComplete = this.state.slam.subtasks.length > 0 &&
523
+ this.state.slam.subtasks.every((st) =>
524
+ this.state.slam.completedSubtasks.includes(st.id)
525
+ );
526
+
527
+ return allComplete || this.state.slam.phase === "complete";
528
+ }
529
+ }
package/src/hooks.js ADDED
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ /**
3
+ * GLM Daemon - Hook System
4
+ *
5
+ * Implements hook lifecycle for daemon events.
6
+ * Based on Claude Code's hook system.
7
+ */
8
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
9
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
10
+ return new (P || (P = Promise))(function (resolve, reject) {
11
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
12
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
13
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
14
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
15
+ });
16
+ };
17
+ var __generator = (this && this.__generator) || function (thisArg, body) {
18
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
19
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
20
+ function verb(n) { return function (v) { return step([n, v]); }; }
21
+ function step(op) {
22
+ if (f) throw new TypeError("Generator is already executing.");
23
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
24
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
25
+ if (y = 0, t) op = [op[0] & 2, t.value];
26
+ switch (op[0]) {
27
+ case 0: case 1: t = op; break;
28
+ case 4: _.label++; return { value: op[1], done: false };
29
+ case 5: _.label++; y = op[1]; op = [0]; continue;
30
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
31
+ default:
32
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
33
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
34
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
35
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
36
+ if (t[2]) _.ops.pop();
37
+ _.trys.pop(); continue;
38
+ }
39
+ op = body.call(thisArg, _);
40
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
41
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
42
+ }
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.HookSystem = void 0;
46
+ var HookSystem = /** @class */ (function () {
47
+ function HookSystem(config) {
48
+ this.hooks = new Map();
49
+ // Register hooks from config
50
+ if (config.onSessionStart) {
51
+ this.register("onSessionStart", config.onSessionStart);
52
+ }
53
+ if (config.onSessionEnd) {
54
+ this.register("onSessionEnd", config.onSessionEnd);
55
+ }
56
+ if (config.onPreToolUse) {
57
+ this.register("onPreToolUse", config.onPreToolUse);
58
+ }
59
+ if (config.onPostToolUse) {
60
+ this.register("onPostToolUse", config.onPostToolUse);
61
+ }
62
+ if (config.onSubagentStart) {
63
+ this.register("onSubagentStart", config.onSubagentStart);
64
+ }
65
+ if (config.onSubagentStop) {
66
+ this.register("onSubagentStop", config.onSubagentStop);
67
+ }
68
+ if (config.onIterationStart) {
69
+ this.register("onIterationStart", config.onIterationStart);
70
+ }
71
+ if (config.onIterationEnd) {
72
+ this.register("onIterationEnd", config.onIterationEnd);
73
+ }
74
+ }
75
+ /**
76
+ * Register a hook handler
77
+ */
78
+ HookSystem.prototype.register = function (event, handler) {
79
+ if (!this.hooks.has(event)) {
80
+ this.hooks.set(event, []);
81
+ }
82
+ this.hooks.get(event).push(handler);
83
+ };
84
+ /**
85
+ * Execute all handlers for an event
86
+ */
87
+ HookSystem.prototype.execute = function (event) {
88
+ var args = [];
89
+ for (var _i = 1; _i < arguments.length; _i++) {
90
+ args[_i - 1] = arguments[_i];
91
+ }
92
+ return __awaiter(this, void 0, void 0, function () {
93
+ var handlers, _a, handlers_1, handler, error_1;
94
+ return __generator(this, function (_b) {
95
+ switch (_b.label) {
96
+ case 0:
97
+ handlers = this.hooks.get(event);
98
+ if (!handlers) {
99
+ return [2 /*return*/];
100
+ }
101
+ _a = 0, handlers_1 = handlers;
102
+ _b.label = 1;
103
+ case 1:
104
+ if (!(_a < handlers_1.length)) return [3 /*break*/, 6];
105
+ handler = handlers_1[_a];
106
+ _b.label = 2;
107
+ case 2:
108
+ _b.trys.push([2, 4, , 5]);
109
+ return [4 /*yield*/, handler.apply(void 0, args)];
110
+ case 3:
111
+ _b.sent();
112
+ return [3 /*break*/, 5];
113
+ case 4:
114
+ error_1 = _b.sent();
115
+ console.error("[HookSystem] Error in ".concat(event, ":"), error_1);
116
+ return [3 /*break*/, 5];
117
+ case 5:
118
+ _a++;
119
+ return [3 /*break*/, 1];
120
+ case 6: return [2 /*return*/];
121
+ }
122
+ });
123
+ });
124
+ };
125
+ /**
126
+ * Remove all handlers for an event
127
+ */
128
+ HookSystem.prototype.clear = function (event) {
129
+ this.hooks.delete(event);
130
+ };
131
+ /**
132
+ * Remove all hooks
133
+ */
134
+ HookSystem.prototype.clearAll = function () {
135
+ this.hooks.clear();
136
+ };
137
+ /**
138
+ * Get registered events
139
+ */
140
+ HookSystem.prototype.getEvents = function () {
141
+ return Array.from(this.hooks.keys());
142
+ };
143
+ return HookSystem;
144
+ }());
145
+ exports.HookSystem = HookSystem;