pumuki-ast-hooks 5.3.18 → 5.3.19

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 (36) hide show
  1. package/docs/VIOLATIONS_RESOLUTION_PLAN.md +23 -25
  2. package/package.json +2 -2
  3. package/scripts/hooks-system/application/CompositionRoot.js +24 -73
  4. package/scripts/hooks-system/application/services/DynamicRulesLoader.js +1 -2
  5. package/scripts/hooks-system/application/services/GitTreeState.js +139 -13
  6. package/scripts/hooks-system/application/services/HookSystemScheduler.js +43 -0
  7. package/scripts/hooks-system/application/services/PlaybookRunner.js +1 -1
  8. package/scripts/hooks-system/application/services/RealtimeGuardService.js +15 -85
  9. package/scripts/hooks-system/application/services/guard/GuardAutoManagerService.js +2 -31
  10. package/scripts/hooks-system/application/services/guard/GuardConfig.js +9 -17
  11. package/scripts/hooks-system/application/services/guard/GuardHeartbeatMonitor.js +9 -6
  12. package/scripts/hooks-system/application/services/guard/GuardProcessManager.js +0 -29
  13. package/scripts/hooks-system/application/services/installation/GitEnvironmentService.js +1 -4
  14. package/scripts/hooks-system/application/services/installation/McpConfigurator.js +1 -2
  15. package/scripts/hooks-system/application/services/logging/AuditLogger.js +86 -1
  16. package/scripts/hooks-system/application/services/logging/UnifiedLogger.js +4 -13
  17. package/scripts/hooks-system/application/services/monitoring/EvidenceMonitor.js +1 -0
  18. package/scripts/hooks-system/application/services/monitoring/EvidenceMonitorService.js +3 -7
  19. package/scripts/hooks-system/application/services/token/TokenMetricsService.js +1 -14
  20. package/scripts/hooks-system/bin/__tests__/evidence-update.spec.js +49 -0
  21. package/scripts/hooks-system/bin/cli.js +1 -15
  22. package/scripts/hooks-system/domain/events/index.js +6 -32
  23. package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidAnalysisOrchestrator.js +2 -3
  24. package/scripts/hooks-system/infrastructure/ast/ast-core.js +20 -12
  25. package/scripts/hooks-system/infrastructure/ast/ast-intelligence.js +18 -8
  26. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/BackendPatternDetector.js +1 -2
  27. package/scripts/hooks-system/infrastructure/ast/backend/ast-backend.js +8 -10
  28. package/scripts/hooks-system/infrastructure/ast/frontend/ast-frontend.js +196 -196
  29. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSASTIntelligentAnalyzer.spec.js +66 -0
  30. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSASTIntelligentAnalyzer.js +2 -3
  31. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSArchitectureRules.js +24 -86
  32. package/scripts/hooks-system/infrastructure/hooks/skill-activation-prompt.js +2 -3
  33. package/scripts/hooks-system/infrastructure/logging/UnifiedLoggerFactory.js +5 -35
  34. package/scripts/hooks-system/infrastructure/orchestration/intelligent-audit.js +16 -86
  35. package/scripts/hooks-system/infrastructure/telemetry/metrics-server.js +2 -51
  36. package/scripts/hooks-system/infrastructure/validators/enforce-english-literals.js +8 -6
@@ -10,7 +10,7 @@
10
10
  ---
11
11
 
12
12
  ## 📊 Resumen Ejecutivo
13
- - **Estado actual:** ⚠️ Acción requerida (217 críticas, 1 alta, 117 medias, 211 bajas)
13
+ - **Estado actual:** ⚠️ Acción requerida (91 críticas, 0 altas, 30 medias, 14 bajas)
14
14
  - **Branch:** `fix/audit-staged-severity-case-insensitive`
15
15
  - **Fecha de inicio:** 30/12/2025 — **ETA general:** 15/01/2026
16
16
  - **Objetivo:** Reducir a 0 las CRÍTICAS/HIGH y bajar el total < 20 antes de permitir commits sin bypass.
@@ -35,7 +35,7 @@ gantt
35
35
  Auditoría de seguridad : crit3, after crit2, 3d
36
36
  Métricas Prometheus : crit4, after crit3, 2d
37
37
  Patrones de confiabilidad : crit5, after crit4, 2d
38
-
38
+
39
39
  section Fase 2: HIGH + MEDIUM
40
40
  Corrección HIGH : high1, after crit5, 1d
41
41
  Refactorización MEDIUM : med1, after high1, 5d
@@ -46,50 +46,48 @@ gantt
46
46
 
47
47
  ---
48
48
 
49
- ## 🔴 Fase 1: Violaciones CRÍTICAS (217)
49
+ ## 🔴 Fase 1: Violaciones CRÍTICAS (91)
50
50
  | Estado | Violación | Cant. | Responsable | DOD (Definition of Done) | Doc |
51
51
  |--------|-----------|-------|-------------|--------------------------|-----|
52
- | | backend.error.custom_exceptions | 105 | BE | CustomError base + reemplazo de `Error` genérico en BE; tests pasando | [Guía de excepciones](../docs/error-handling.md) |
53
- | | backend.config.missing_env_separation | 80 | BE | Config por entorno (dev/stg/prod), sin secretos hardcode | [Config entornos](../docs/env-configuration.md) |
54
- | | backend.security.missing_audit_logging | 69 | BE | Audit trail en operaciones sensibles + logs estructurados | [Audit logging](../docs/security-auditing.md) |
55
- | | backend.metrics.missing_prometheus | 64 | BE | Endpoints /metrics, instrumentación clave y dashboard base | [Prometheus](../docs/metrics-monitoring.md) |
56
- | | backend.reliability.missing_bulkhead | 40 | BE | Limitadores/aislamiento en puntos críticos + pruebas de carga | [Reliability](../docs/reliability-patterns.md) |
52
+ | 🚧 | backend.observability.missing_prometheus | 45 | BE | Añadir métricas Prometheus a todos los servicios críticos; endpoints /metrics funcionando | [Prometheus](../docs/metrics-monitoring.md) |
53
+ | | backend.config.missing_env_separation | 27 | BE | Config por entorno (dev/stg/prod), sin secretos hardcode | [Config entornos](../docs/env-configuration.md) |
54
+ | | backend.security.missing_audit_logging | 23 | BE | Audit trail en operaciones sensibles + logs estructurados | [Audit logging](../docs/security-auditing.md) |
55
+ | | backend.error.custom_exceptions | 16 | BE | CustomError base + reemplazo de `Error` genérico en BE; tests pasando | [Guía de excepciones](../docs/error-handling.md) |
56
+ | | backend.reliability.missing_bulkhead | 10 | BE | Limitadores/aislamiento en puntos críticos + pruebas de carga | [Reliability](../docs/reliability-patterns.md) |
57
+ | ⏳ | backend.event.emitter | 4 | BE | try/catch en manejadores de eventos; manejo de errores robusto | [Eventos](../docs/event-handling.md) |
58
+ | ⏳ | backend.observability.missing_alerting | 3 | SRE | Alertas en métricas críticas; umbrales definidos | [Alerting](../docs/alerting-system.md) |
59
+ | ⏳ | backend.config.missing_validation | 1 | BE | Validación de configuración al inicio | [Config validation](../docs/config-validation.md) |
60
+ | ⏳ | backend.config.missing_env_validation | 1 | BE | Validación de variables de entorno requeridas | [Env validation](../docs/env-validation.md) |
61
+ | ⏳ | backend.database.raw_sql | 1 | BE | Uso de ORM en lugar de raw SQL | [Database](../docs/database-layer.md) |
57
62
 
58
63
  ---
59
64
 
60
- ## 🟠 Fase 2: Violaciones HIGH + MEDIUM (118)
65
+ ## 🟠 Fase 2: Violaciones HIGH + MEDIUM (30)
61
66
  | Estado | Violación | Cant. | Responsable | DOD | Doc |
62
67
  |--------|-----------|-------|-------------|-----|-----|
63
- | ✅ | HIGH (1) | 1 | BE | No aplica (repo JS puro sin TS); se documenta enfoque de seguridad de tipos | [Type safety](../docs/type-safety.md) |
64
- | | backend.testing.mocks | 40 | QA/BE | Mocks revisados; cobertura > 80% en módulos afectados | [Testing](../docs/testing-strategies.md) |
65
- | ✅ | backend.event.handler | 26 | BE | Handlers idempotentes + tests de eventos | [Eventos](../docs/event-handling.md) |
66
- | ⏳ | backend.observability.missing_prometheus | 24 | BE | Métricas por handler; dashboards mínimos | [Observabilidad](../docs/observability.md) |
67
- | ✅ | backend.auth.missing_cors | 17 | BE | No aplica al hook-system (no expone servidor HTTP); la detección queda en proyectos auditados | [CORS](../docs/cors-configuration.md) |
68
- | ✅ | backend.observability.missing_alerting | 10 | SRE | Alertas en métricas críticas; umbrales definidos | [Alerting](../docs/alerting-system.md) |
68
+ | ✅ | HIGH | 0 | BE | No aplica (repo JS puro sin TS); se documenta enfoque de seguridad de tipos | [Type safety](../docs/type-safety.md) |
69
+ | | MEDIUM | 30 | BE | Resolver violaciones de complejidad media restantes | [Medium violations](../docs/medium-violations.md) |
69
70
 
70
71
  ---
71
72
 
72
- ## 🔵 Fase 3: Violaciones LOW (211)
73
+ ## 🔵 Fase 3: Violaciones LOW (14)
73
74
  | Estado | Violación | Cant. | Responsable | DOD | Doc |
74
75
  |--------|-----------|-------|-------------|-----|-----|
75
- | ⏳ | frontend.code_quality.comment | 4 | FE | Comentarios depurados; lint OK | [Code standards](../docs/code-standards.md) |
76
- | ⏳ | frontend.code_quality.magic_number | 4 | FE | Constantes declaradas; tests ajustados | [Constantes](../docs/constants-vs-magic-numbers.md) |
77
- | ⏳ | frontend.devops.hardcoded_feature_flag | 2 | FE/DevOps | Flags externalizados por entorno | [Feature flags](../docs/feature-flags.md) |
78
- | ⏳ | frontend.performance.code_splitting | 2 | FE | Split aplicado en rutas pesadas; bundle size reducido | [Perf FE](../docs/frontend-performance.md) |
79
- | ⏳ | frontend.performance.missing_code_splitting | 2 | FE | Lazy loading habilitado en vistas grandes | [Code splitting](../docs/code-splitting.md) |
76
+ | ⏳ | LOW | 14 | BE/FE | Resolver violaciones de baja prioridad restantes | [Low violations](../docs/low-violations.md) |
80
77
 
81
78
  ---
82
79
 
83
80
  ## 📈 Métricas de Progreso
84
81
  | Fase | Total | Completado | % |
85
82
  |------|-------|------------|---|
86
- | CRÍTICAS | 217 | 0 | 0% |
87
- | HIGH + MEDIUM | 118 | 0 | 0% |
88
- | LOW | 211 | 0 | 0% |
89
- | **TOTAL** | **546** | **0** | **0%** |
83
+ | CRÍTICAS | 91 | 0 | 0% |
84
+ | HIGH + MEDIUM | 30 | 0 | 0% |
85
+ | LOW | 14 | 0 | 0% |
86
+ | **TOTAL** | **135** | **0** | **0%** |
90
87
 
91
88
  **Riesgos actualizados:**
92
89
  1) Implementación de Prometheus podría requerir cambios de infra; 2) Revisión de seguridad depende de disponibilidad de equipo; 3) Refactorizaciones pueden impactar tiempos.
93
90
 
94
91
  **Comentarios/Notas colaborativas:**
95
92
  - Añade comentarios bajo cada tabla al cerrar tareas (usa la leyenda para actualizar estados).
93
+ - Progreso en métricas Prometheus: añadidas a 27 servicios, pero 45 violaciones restantes indican necesidad de más instrumentación.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki-ast-hooks",
3
- "version": "5.3.18",
3
+ "version": "5.3.19",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -116,4 +116,4 @@
116
116
  "./skills": "./skills/skill-rules.json",
117
117
  "./hooks": "./hooks/index.js"
118
118
  }
119
- }
119
+ }
@@ -21,54 +21,19 @@ const TokenMonitor = require('./services/monitoring/TokenMonitor');
21
21
  const ActivityMonitor = require('./services/monitoring/ActivityMonitor');
22
22
  const DevDocsMonitor = require('./services/monitoring/DevDocsMonitor');
23
23
  const AstMonitor = require('./services/monitoring/AstMonitor');
24
- const AuditLogger = require('./services/logging/AuditLogger');
25
24
 
26
25
  const path = require('path');
27
26
  const fs = require('fs');
28
- const env = require('../config/env');
29
-
30
- // Import recordMetric for prometheus metrics
31
- const { recordMetric } = require('../infrastructure/telemetry/metrics-logger');
32
27
 
33
28
  class CompositionRoot {
34
29
  constructor(repoRoot) {
35
30
  this.repoRoot = repoRoot;
36
31
  this.instances = new Map();
37
32
 
38
- this._initializeInfrastructure();
39
- this._initializeServices();
40
- this._initializeMonitors();
41
- }
42
-
43
- _initializeInfrastructure() {
44
33
  // Ensure audit directories exist
45
- this.auditDir = path.join(this.repoRoot, '.audit-reports');
46
- this.tempDir = path.join(this.repoRoot, '.audit_tmp');
34
+ this.auditDir = path.join(repoRoot, '.audit-reports');
35
+ this.tempDir = path.join(repoRoot, '.audit_tmp');
47
36
  this._ensureDirectories([this.auditDir, this.tempDir]);
48
-
49
- // Audit log composition root creation
50
- const auditLogger = this.getAuditLogger();
51
- auditLogger.log({
52
- action: 'composition_root_created',
53
- category: 'system',
54
- severity: 'info',
55
- message: 'CompositionRoot initialized for dependency injection',
56
- metadata: {
57
- repoRoot: this.repoRoot,
58
- auditDir: this.auditDir,
59
- tempDir: this.tempDir
60
- }
61
- });
62
- }
63
-
64
- _initializeServices() {
65
- // Services are created lazily when requested
66
- // This reduces the initialization load
67
- }
68
-
69
- _initializeMonitors() {
70
- // Monitors are created lazily when requested
71
- // This reduces the initialization load
72
37
  }
73
38
 
74
39
  _ensureDirectories(dirs) {
@@ -86,7 +51,7 @@ class CompositionRoot {
86
51
  file: {
87
52
  enabled: true,
88
53
  path: path.join(this.auditDir, 'guard-audit.jsonl'),
89
- level: env.get('HOOK_LOG_LEVEL', env.isProd ? 'warn' : 'info')
54
+ level: process.env.HOOK_LOG_LEVEL || 'info'
90
55
  },
91
56
  console: {
92
57
  enabled: false,
@@ -108,18 +73,6 @@ class CompositionRoot {
108
73
  return this.instances.get('notificationService');
109
74
  }
110
75
 
111
- getAuditLogger() {
112
- if (!this.instances.has('auditLogger')) {
113
- const logger = this.getLogger();
114
- this.instances.set('auditLogger', new AuditLogger({
115
- repoRoot: this.repoRoot,
116
- filename: path.join('.audit_tmp', 'audit.log'),
117
- logger
118
- }));
119
- }
120
- return this.instances.get('auditLogger');
121
- }
122
-
123
76
  // --- Infrastructure Adapters ---
124
77
 
125
78
  getNotificationAdapter() {
@@ -230,9 +183,9 @@ class CompositionRoot {
230
183
  getEvidenceMonitor() {
231
184
  if (!this.instances.has('evidenceMonitor')) {
232
185
  this.instances.set('evidenceMonitor', new EvidenceMonitor(this.repoRoot, {
233
- staleThresholdMs: env.getNumber('HOOK_GUARD_EVIDENCE_STALE_THRESHOLD', 180000),
234
- pollIntervalMs: env.getNumber('HOOK_GUARD_EVIDENCE_POLL_INTERVAL', 30000),
235
- reminderIntervalMs: env.getNumber('HOOK_GUARD_EVIDENCE_REMINDER_INTERVAL', 60000)
186
+ staleThresholdMs: Number(process.env.HOOK_GUARD_EVIDENCE_STALE_THRESHOLD || 180000),
187
+ pollIntervalMs: Number(process.env.HOOK_GUARD_EVIDENCE_POLL_INTERVAL || 30000),
188
+ reminderIntervalMs: Number(process.env.HOOK_GUARD_EVIDENCE_REMINDER_INTERVAL || 60000)
236
189
  }));
237
190
  }
238
191
  return this.instances.get('evidenceMonitor');
@@ -241,11 +194,11 @@ class CompositionRoot {
241
194
  getGitTreeMonitor() {
242
195
  if (!this.instances.has('gitTreeMonitor')) {
243
196
  this.instances.set('gitTreeMonitor', new GitTreeMonitor(this.repoRoot, {
244
- stagedThreshold: env.getNumber('HOOK_GUARD_DIRTY_TREE_STAGED_LIMIT', 10),
245
- unstagedThreshold: env.getNumber('HOOK_GUARD_DIRTY_TREE_UNSTAGED_LIMIT', 15),
246
- totalThreshold: env.getNumber('HOOK_GUARD_DIRTY_TREE_TOTAL_LIMIT', 20),
247
- checkIntervalMs: env.getNumber('HOOK_GUARD_DIRTY_TREE_INTERVAL', 60000),
248
- reminderMs: env.getNumber('HOOK_GUARD_DIRTY_TREE_REMINDER', 300000)
197
+ stagedThreshold: Number(process.env.HOOK_GUARD_DIRTY_TREE_STAGED_LIMIT || 10),
198
+ unstagedThreshold: Number(process.env.HOOK_GUARD_DIRTY_TREE_UNSTAGED_LIMIT || 15),
199
+ totalThreshold: Number(process.env.HOOK_GUARD_DIRTY_TREE_TOTAL_LIMIT || 20),
200
+ checkIntervalMs: Number(process.env.HOOK_GUARD_DIRTY_TREE_INTERVAL || 60000),
201
+ reminderMs: Number(process.env.HOOK_GUARD_DIRTY_TREE_REMINDER || 300000)
249
202
  }));
250
203
  }
251
204
  return this.instances.get('gitTreeMonitor');
@@ -266,11 +219,11 @@ class CompositionRoot {
266
219
  const github = this.getGitHubAdapter();
267
220
 
268
221
  this.instances.set('gitFlowService', new GitFlowService(this.repoRoot, {
269
- developBranch: env.get('HOOK_GUARD_GITFLOW_DEVELOP_BRANCH', 'develop'),
270
- mainBranch: env.get('HOOK_GUARD_GITFLOW_MAIN_BRANCH', 'main'),
271
- autoSyncEnabled: env.getBool('HOOK_GUARD_GITFLOW_AUTOSYNC', true),
272
- autoCleanEnabled: env.getBool('HOOK_GUARD_GITFLOW_AUTOCLEAN', true),
273
- requireClean: env.getBool('HOOK_GUARD_GITFLOW_REQUIRE_CLEAN', true)
222
+ developBranch: process.env.HOOK_GUARD_GITFLOW_DEVELOP_BRANCH || 'develop',
223
+ mainBranch: process.env.HOOK_GUARD_GITFLOW_MAIN_BRANCH || 'main',
224
+ autoSyncEnabled: process.env.HOOK_GUARD_GITFLOW_AUTOSYNC !== 'false',
225
+ autoCleanEnabled: process.env.HOOK_GUARD_GITFLOW_AUTOCLEAN !== 'false',
226
+ requireClean: process.env.HOOK_GUARD_GITFLOW_REQUIRE_CLEAN !== 'false'
274
227
  }, logger, gitQuery, gitCommand, github));
275
228
  }
276
229
  return this.instances.get('gitFlowService');
@@ -281,7 +234,7 @@ class CompositionRoot {
281
234
  const logger = this.getLogger();
282
235
  this.instances.set('activityMonitor', new ActivityMonitor({
283
236
  repoRoot: this.repoRoot,
284
- inactivityGraceMs: env.getNumber('HOOK_GUARD_INACTIVITY_GRACE_MS', 420000),
237
+ inactivityGraceMs: Number(process.env.HOOK_GUARD_INACTIVITY_GRACE_MS || 420000),
285
238
  logger
286
239
  }));
287
240
  }
@@ -294,9 +247,9 @@ class CompositionRoot {
294
247
  const notificationService = this.getNotificationService();
295
248
  this.instances.set('devDocsMonitor', new DevDocsMonitor({
296
249
  repoRoot: this.repoRoot,
297
- checkIntervalMs: env.getNumber('HOOK_GUARD_DEV_DOCS_CHECK_INTERVAL', 300000),
298
- staleThresholdMs: env.getNumber('HOOK_GUARD_DEV_DOCS_STALE_THRESHOLD', 86400000),
299
- autoRefreshEnabled: env.getBool('HOOK_GUARD_DEV_DOCS_AUTO_REFRESH', true),
250
+ checkIntervalMs: Number(process.env.HOOK_GUARD_DEV_DOCS_CHECK_INTERVAL || 300000),
251
+ staleThresholdMs: Number(process.env.HOOK_GUARD_DEV_DOCS_STALE_THRESHOLD || 86400000),
252
+ autoRefreshEnabled: process.env.HOOK_GUARD_DEV_DOCS_AUTO_REFRESH !== 'false',
300
253
  logger,
301
254
  notificationService
302
255
  }));
@@ -310,9 +263,9 @@ class CompositionRoot {
310
263
  const notificationService = this.getNotificationService();
311
264
  this.instances.set('astMonitor', new AstMonitor({
312
265
  repoRoot: this.repoRoot,
313
- debounceMs: env.getNumber('HOOK_AST_WATCH_DEBOUNCE', 8000),
314
- cooldownMs: env.getNumber('HOOK_AST_WATCH_COOLDOWN', 30000),
315
- enabled: env.getBool('HOOK_AST_WATCH', true),
266
+ debounceMs: Number(process.env.HOOK_AST_WATCH_DEBOUNCE || 8000),
267
+ cooldownMs: Number(process.env.HOOK_AST_WATCH_COOLDOWN || 30000),
268
+ enabled: process.env.HOOK_AST_WATCH !== 'false',
316
269
  logger,
317
270
  notificationService
318
271
  }));
@@ -338,7 +291,6 @@ class CompositionRoot {
338
291
  const notificationService = this.getNotificationService();
339
292
  const monitors = this.getMonitors();
340
293
  const orchestrator = this.getOrchestrator();
341
- const auditLogger = this.getAuditLogger();
342
294
  const config = {
343
295
  debugLogPath: path.join(this.auditDir, 'guard-debug.log'),
344
296
  repoRoot: this.repoRoot
@@ -349,8 +301,7 @@ class CompositionRoot {
349
301
  notificationService,
350
302
  monitors,
351
303
  orchestration: orchestrator,
352
- config,
353
- auditLogger
304
+ config
354
305
  }));
355
306
  }
356
307
  return this.instances.get('guardService');
@@ -1,6 +1,5 @@
1
1
  const fs = require('fs').promises;
2
2
  const path = require('path');
3
- const env = require('../config/env');
4
3
 
5
4
  class DynamicRulesLoader {
6
5
  constructor(rulesDirectory, logger = console) {
@@ -28,7 +27,7 @@ class DynamicRulesLoader {
28
27
  return [this.rulesDirectory];
29
28
  }
30
29
 
31
- const envRaw = env.get('AST_RULES_DIRECTORIES');
30
+ const envRaw = process.env.AST_RULES_DIRECTORIES;
32
31
  if (envRaw && typeof envRaw === 'string' && envRaw.trim().length > 0) {
33
32
  return envRaw
34
33
  .split(',')
@@ -1,16 +1,53 @@
1
1
  const { execSync } = require('child_process');
2
2
 
3
+ // Import recordMetric for prometheus metrics
4
+ const { recordMetric } = require('../../../infrastructure/telemetry/metrics-logger');
5
+
3
6
  const extractFilePath = line => {
7
+ recordMetric({
8
+ hook: 'git_tree_state',
9
+ operation: 'extract_file_path',
10
+ status: 'started',
11
+ lineLength: line ? line.length : 0
12
+ });
13
+
4
14
  if (!line) {
15
+ recordMetric({
16
+ hook: 'git_tree_state',
17
+ operation: 'extract_file_path',
18
+ status: 'success',
19
+ result: ''
20
+ });
5
21
  return '';
6
22
  }
7
23
  if (line.startsWith('??')) {
8
- return line.slice(3).trim();
24
+ const result = line.slice(3).trim();
25
+ recordMetric({
26
+ hook: 'git_tree_state',
27
+ operation: 'extract_file_path',
28
+ status: 'success',
29
+ resultLength: result.length
30
+ });
31
+ return result;
9
32
  }
10
- return line.length > 3 ? line.slice(3).trim() : line.trim();
33
+ const result = line.length > 3 ? line.slice(3).trim() : line.trim();
34
+ recordMetric({
35
+ hook: 'git_tree_state',
36
+ operation: 'extract_file_path',
37
+ status: 'success',
38
+ resultLength: result.length
39
+ });
40
+ return result;
11
41
  };
12
42
 
13
43
  const buildStateFromStatus = (statusOutput = '') => {
44
+ recordMetric({
45
+ hook: 'git_tree_state',
46
+ operation: 'build_state_from_status',
47
+ status: 'started',
48
+ statusOutputLength: statusOutput ? statusOutput.length : 0
49
+ });
50
+
14
51
  const lines = statusOutput
15
52
  .split('\n')
16
53
  .map(entry => entry.replace(/\r$/, ''))
@@ -44,24 +81,51 @@ const buildStateFromStatus = (statusOutput = '') => {
44
81
 
45
82
  const uniqueSet = new Set([...stagedSet, ...workingSet]);
46
83
 
47
- return {
84
+ const result = {
48
85
  stagedFiles: Array.from(stagedSet),
49
86
  workingFiles: Array.from(workingSet),
50
87
  stagedCount: stagedSet.size,
51
88
  workingCount: workingSet.size,
52
89
  uniqueCount: uniqueSet.size
53
90
  };
91
+
92
+ recordMetric({
93
+ hook: 'git_tree_state',
94
+ operation: 'build_state_from_status',
95
+ status: 'success',
96
+ stagedCount: stagedSet.size,
97
+ workingCount: workingSet.size,
98
+ uniqueCount: uniqueSet.size
99
+ });
100
+
101
+ return result;
54
102
  };
55
103
 
56
104
  const getGitTreeState = ({ repoRoot = process.cwd() } = {}) => {
105
+ recordMetric({
106
+ hook: 'git_tree_state',
107
+ operation: 'get_git_tree_state',
108
+ status: 'started',
109
+ repoRoot: repoRoot.substring(0, 100)
110
+ });
111
+
57
112
  try {
58
113
  const statusOutput = execSync('git status --porcelain', {
59
114
  cwd: repoRoot,
60
115
  encoding: 'utf8'
61
116
  });
62
- return buildStateFromStatus(statusOutput);
117
+ const result = buildStateFromStatus(statusOutput);
118
+ recordMetric({
119
+ hook: 'git_tree_state',
120
+ operation: 'get_git_tree_state',
121
+ status: 'success',
122
+ stagedCount: result.stagedCount,
123
+ workingCount: result.workingCount,
124
+ uniqueCount: result.uniqueCount
125
+ });
126
+ return result;
63
127
  } catch (error) {
64
- return {
128
+ const result = {
65
129
  stagedFiles: [],
66
130
  workingFiles: [],
67
131
  stagedCount: 0,
@@ -69,37 +133,92 @@ const getGitTreeState = ({ repoRoot = process.cwd() } = {}) => {
69
133
  uniqueCount: 0,
70
134
  error: error.message
71
135
  };
136
+ recordMetric({
137
+ hook: 'git_tree_state',
138
+ operation: 'get_git_tree_state',
139
+ status: 'failed',
140
+ error: error.message
141
+ });
142
+ return result;
72
143
  }
73
144
  };
74
145
 
75
146
  const isTreeBeyondLimit = (state, limits) => {
147
+ recordMetric({
148
+ hook: 'git_tree_state',
149
+ operation: 'is_tree_beyond_limit',
150
+ status: 'started',
151
+ hasState: !!state,
152
+ limitsType: typeof limits
153
+ });
154
+
76
155
  if (!state) {
156
+ recordMetric({
157
+ hook: 'git_tree_state',
158
+ operation: 'is_tree_beyond_limit',
159
+ status: 'success',
160
+ result: false
161
+ });
77
162
  return false;
78
163
  }
79
-
164
+
80
165
  if (typeof limits === 'number') {
81
166
  const limit = limits;
82
167
  if (!Number.isFinite(limit) || limit <= 0) {
168
+ recordMetric({
169
+ hook: 'git_tree_state',
170
+ operation: 'is_tree_beyond_limit',
171
+ status: 'success',
172
+ result: false
173
+ });
83
174
  return false;
84
175
  }
85
- return (
176
+ const result = (
86
177
  state.stagedCount > limit ||
87
178
  state.workingCount > limit ||
88
179
  state.uniqueCount > limit
89
180
  );
181
+ recordMetric({
182
+ hook: 'git_tree_state',
183
+ operation: 'is_tree_beyond_limit',
184
+ status: 'success',
185
+ result: result
186
+ });
187
+ return result;
90
188
  }
91
-
189
+
92
190
  const { stagedLimit = 10, unstagedLimit = 15, totalLimit = 20 } = limits || {};
93
-
191
+
94
192
  const stagedExceeded = Number.isFinite(stagedLimit) && stagedLimit > 0 && state.stagedCount > stagedLimit;
95
193
  const unstagedExceeded = Number.isFinite(unstagedLimit) && unstagedLimit > 0 && state.workingCount > unstagedLimit;
96
194
  const totalExceeded = Number.isFinite(totalLimit) && totalLimit > 0 && state.uniqueCount > totalLimit;
97
-
98
- return stagedExceeded || unstagedExceeded || totalExceeded;
195
+
196
+ const result = stagedExceeded || unstagedExceeded || totalExceeded;
197
+ recordMetric({
198
+ hook: 'git_tree_state',
199
+ operation: 'is_tree_beyond_limit',
200
+ status: 'success',
201
+ result: result
202
+ });
203
+ return result;
99
204
  };
100
205
 
101
206
  const summarizeTreeState = (state, limits) => {
207
+ recordMetric({
208
+ hook: 'git_tree_state',
209
+ operation: 'summarize_tree_state',
210
+ status: 'started',
211
+ hasState: !!state,
212
+ limitsType: typeof limits
213
+ });
214
+
102
215
  if (!state) {
216
+ recordMetric({
217
+ hook: 'git_tree_state',
218
+ operation: 'summarize_tree_state',
219
+ status: 'success',
220
+ result: 'no data'
221
+ });
103
222
  return 'no data';
104
223
  }
105
224
  const parts = [
@@ -107,7 +226,7 @@ const summarizeTreeState = (state, limits) => {
107
226
  `working ${state.workingCount}`,
108
227
  `unique ${state.uniqueCount}`
109
228
  ];
110
-
229
+
111
230
  if (typeof limits === 'number') {
112
231
  const limit = limits;
113
232
  if (Number.isFinite(limit) && limit > 0) {
@@ -129,7 +248,14 @@ const summarizeTreeState = (state, limits) => {
129
248
  parts.push(`(${limitParts.join(', ')})`);
130
249
  }
131
250
  }
132
- return parts.join(', ');
251
+ const result = parts.join(', ');
252
+ recordMetric({
253
+ hook: 'git_tree_state',
254
+ operation: 'summarize_tree_state',
255
+ status: 'success',
256
+ resultLength: result.length
257
+ });
258
+ return result;
133
259
  };
134
260
 
135
261
  module.exports = {
@@ -3,23 +3,66 @@ const HookSystemStateMachine = require('../state/HookSystemStateMachine');
3
3
 
4
4
  class HookSystemScheduler {
5
5
  constructor({ orchestrator, contextEngine, intervalMs = 30000 }) {
6
+ recordMetric({
7
+ hook: 'hook_system_scheduler',
8
+ operation: 'constructor',
9
+ status: 'started',
10
+ intervalMs
11
+ });
12
+
6
13
  this.orchestrator = orchestrator;
7
14
  this.contextEngine = contextEngine;
8
15
  this.intervalMs = intervalMs;
9
16
  this.stateMachine = new HookSystemStateMachine();
10
17
  this.timer = null;
18
+
19
+ recordMetric({
20
+ hook: 'hook_system_scheduler',
21
+ operation: 'constructor',
22
+ status: 'success',
23
+ intervalMs
24
+ });
11
25
  }
12
26
 
13
27
  start() {
28
+ recordMetric({
29
+ hook: 'hook_system_scheduler',
30
+ operation: 'start',
31
+ status: 'started',
32
+ intervalMs: this.intervalMs
33
+ });
34
+
14
35
  if (this.timer) return;
36
+
15
37
  this.timer = setInterval(() => this.tick(), this.intervalMs);
38
+
39
+ recordMetric({
40
+ hook: 'hook_system_scheduler',
41
+ operation: 'start',
42
+ status: 'success',
43
+ intervalMs: this.intervalMs
44
+ });
16
45
  }
17
46
 
18
47
  stop() {
48
+ recordMetric({
49
+ hook: 'hook_system_scheduler',
50
+ operation: 'stop',
51
+ status: 'started',
52
+ hadTimer: !!this.timer
53
+ });
54
+
19
55
  if (this.timer) {
20
56
  clearInterval(this.timer);
21
57
  this.timer = null;
22
58
  }
59
+
60
+ recordMetric({
61
+ hook: 'hook_system_scheduler',
62
+ operation: 'stop',
63
+ status: 'success',
64
+ hadTimer: !!this.timer
65
+ });
23
66
  }
24
67
 
25
68
  async tick() {
@@ -18,7 +18,7 @@ class PlaybookRunner {
18
18
  run(id) {
19
19
  const playbook = this.playbooks[id];
20
20
  if (!playbook) {
21
- throw new NotFoundError(`Playbook '${id}'`);
21
+ throw new Error(`Playbook '${id}' not found`);
22
22
  }
23
23
 
24
24
  for (const step of playbook.steps) {