pumuki-ast-hooks 5.5.12 → 5.5.15

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 (22) hide show
  1. package/bin/install.js +0 -0
  2. package/package.json +2 -3
  3. package/scripts/hooks-system/.audit-reports/auto-recovery.log +1 -0
  4. package/scripts/hooks-system/.audit-reports/install-wizard.log +4 -0
  5. package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +24 -0
  6. package/scripts/hooks-system/application/services/PlatformDetectionService.js +5 -1
  7. package/scripts/hooks-system/application/services/RealtimeGuardService.js +1 -0
  8. package/scripts/hooks-system/application/services/guard/EvidenceManager.js +33 -8
  9. package/scripts/hooks-system/application/services/installation/ConfigurationGeneratorService.js +5 -0
  10. package/scripts/hooks-system/application/services/installation/InstallService.js +42 -0
  11. package/scripts/hooks-system/application/services/monitoring/EvidenceMonitorService.js +1 -3
  12. package/scripts/hooks-system/bin/cli.js +19 -1
  13. package/scripts/hooks-system/bin/evidence-guard +110 -0
  14. package/scripts/hooks-system/bin/update-evidence.sh +2 -2
  15. package/scripts/hooks-system/infrastructure/ast/ast-core.js +1 -1
  16. package/scripts/hooks-system/infrastructure/ast/ast-intelligence.js +20 -6
  17. package/scripts/hooks-system/infrastructure/daemons/evidence-guard.js +155 -0
  18. package/scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js +1 -3
  19. package/scripts/hooks-system/infrastructure/orchestration/intelligent-audit.js +21 -5
  20. package/scripts/hooks-system/infrastructure/shell/orchestrators/audit-orchestrator.sh +0 -25
  21. package/scripts/hooks-system/infrastructure/utils/timestamp-helper.sh +12 -4
  22. package/scripts/hooks-system/infrastructure/watchdog/__tests__/.audit-reports/token-monitor.log +3 -0
package/bin/install.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki-ast-hooks",
3
- "version": "5.5.12",
3
+ "version": "5.5.15",
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,7 +116,6 @@
116
116
  },
117
117
  "exports": {
118
118
  ".": "./index.js",
119
- "./package.json": "./package.json",
120
119
  "./ast": "./scripts/hooks-system/infrastructure/ast/ast-intelligence.js",
121
120
  "./domain": "./scripts/hooks-system/domain/index.js",
122
121
  "./application": "./scripts/hooks-system/application/index.js",
@@ -124,4 +123,4 @@
124
123
  "./skills": "./skills/skill-rules.json",
125
124
  "./hooks": "./hooks/index.js"
126
125
  }
127
- }
126
+ }
@@ -6,3 +6,4 @@
6
6
  {"timestamp":"2026-01-03T13:47:40.470Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
7
7
  {"timestamp":"2026-01-03T13:53:08.976Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
8
8
  {"timestamp":"2026-01-03T14:05:35.588Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
9
+ {"timestamp":"2026-01-04T07:12:12.600Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
@@ -30,3 +30,7 @@
30
30
  {"timestamp":"2026-01-03T14:05:35.955Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
31
31
  {"timestamp":"2026-01-03T14:05:35.955Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
32
32
  {"timestamp":"2026-01-03T14:05:35.955Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
33
+ {"timestamp":"2026-01-04T07:12:12.823Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_START","data":{"repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"},"context":{}}
34
+ {"timestamp":"2026-01-04T07:12:12.834Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
35
+ {"timestamp":"2026-01-04T07:12:12.835Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
36
+ {"timestamp":"2026-01-04T07:12:12.835Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
@@ -94,3 +94,27 @@
94
94
  {"timestamp":1767449135588,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
95
95
  {"timestamp":1767449135588,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
96
96
  {"timestamp":1767449135588,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
97
+ {"timestamp":1767510732597,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
98
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
99
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
100
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
101
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
102
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
103
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
104
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
105
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
106
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
107
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
108
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
109
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
110
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
111
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
112
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
113
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
114
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
115
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
116
+ {"timestamp":1767510732599,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
117
+ {"timestamp":1767510732600,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
118
+ {"timestamp":1767510732600,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
119
+ {"timestamp":1767510732600,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
120
+ {"timestamp":1767510732600,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
@@ -116,7 +116,11 @@ class PlatformDetectionService {
116
116
  _detectPlatformUncached(filePath) {
117
117
  const lowerPath = filePath.toLowerCase();
118
118
 
119
- if (lowerPath.includes('apps/backend/') || lowerPath.includes('scripts/hooks-system/') || lowerPath.includes('/services/') || lowerPath.includes('services/') || lowerPath.includes('/functions/') || lowerPath.includes('functions/')) {
119
+ if (lowerPath.includes('scripts/hooks-system/')) {
120
+ return 'other';
121
+ }
122
+
123
+ if (lowerPath.includes('apps/backend/') || lowerPath.includes('/services/') || lowerPath.includes('services/') || lowerPath.includes('/functions/') || lowerPath.includes('functions/')) {
120
124
  return 'backend';
121
125
  }
122
126
  if (lowerPath.includes('apps/web-app/') || lowerPath.includes('apps/admin')) {
@@ -281,6 +281,7 @@ class RealtimeGuardService {
281
281
  }
282
282
 
283
283
  _startGitFlowSync() {
284
+ if (!this.monitors || !this.monitors.gitFlow) return;
284
285
  if (!this.monitors.gitFlow.autoSyncEnabled) return;
285
286
 
286
287
  this.auditLogger.record({
@@ -2,6 +2,10 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { recordMetric } = require('../../../infrastructure/telemetry/metrics-logger');
4
4
  const env = require('../../../config/env.js');
5
+ const { execFile } = require('child_process');
6
+ const { promisify } = require('util');
7
+
8
+ const execFileAsync = promisify(execFile);
5
9
 
6
10
  class EvidenceManager {
7
11
  constructor(evidencePath, notifier, auditLogger, delegate = null) {
@@ -9,7 +13,7 @@ class EvidenceManager {
9
13
  this.notifier = notifier;
10
14
  this.auditLogger = auditLogger;
11
15
  this.delegate = delegate;
12
- this.staleThresholdMs = env.getNumber('HOOK_GUARD_EVIDENCE_STALE_THRESHOLD', 60000);
16
+ this.staleThresholdMs = env.getNumber('HOOK_GUARD_EVIDENCE_STALE_THRESHOLD', 180000);
13
17
  this.reminderIntervalMs = env.getNumber('HOOK_GUARD_EVIDENCE_REMINDER_INTERVAL', 60000);
14
18
  this.inactivityGraceMs = env.getNumber('HOOK_GUARD_INACTIVITY_GRACE_MS', 120000);
15
19
  this.pollIntervalMs = env.getNumber('HOOK_GUARD_EVIDENCE_POLL_INTERVAL', 30000);
@@ -54,10 +58,10 @@ class EvidenceManager {
54
58
  }
55
59
  const raw = fs.readFileSync(this.evidencePath, 'utf8');
56
60
  const json = JSON.parse(raw);
57
- const rootMs = new Date(json?.timestamp).getTime();
58
- const severityMs = new Date(json?.severity_metrics?.last_updated).getTime();
59
- const ms = [rootMs, severityMs].filter(Number.isFinite).reduce((max, v) => Math.max(max, v), NaN);
60
- if (!Number.isFinite(ms)) return null;
61
+ const ts = json?.timestamp;
62
+ if (!ts) return null;
63
+ const ms = new Date(ts).getTime();
64
+ if (Number.isNaN(ms)) return null;
61
65
  return ms;
62
66
  } catch (error) {
63
67
  const msg = error && error.message ? error.message : String(error);
@@ -127,7 +131,7 @@ class EvidenceManager {
127
131
  }
128
132
 
129
133
  async attemptAutoRefresh(reason = 'manual') {
130
- if (!env.getBool('HOOK_GUARD_AUTO_REFRESH', false)) return;
134
+ if (!env.getBool('HOOK_GUARD_AUTO_REFRESH', true)) return;
131
135
 
132
136
  const updateScriptCandidates = [
133
137
  path.join(process.cwd(), 'scripts/hooks-system/bin/update-evidence.sh'),
@@ -165,8 +169,29 @@ class EvidenceManager {
165
169
  }
166
170
 
167
171
  async runDirectEvidenceRefresh(_reason) {
168
- // Specific implementation if needed
169
- return;
172
+ const updateScriptCandidates = [
173
+ path.join(process.cwd(), 'scripts/hooks-system/bin/update-evidence.sh'),
174
+ path.join(process.cwd(), 'node_modules/@pumuki/ast-intelligence-hooks/scripts/hooks-system/bin/update-evidence.sh'),
175
+ path.join(process.cwd(), 'node_modules/pumuki-ast-hooks/scripts/hooks-system/bin/update-evidence.sh')
176
+ ];
177
+
178
+ const updateScript = updateScriptCandidates.find(p => fs.existsSync(p));
179
+ if (!updateScript) return;
180
+
181
+ try {
182
+ await execFileAsync('bash', [updateScript, '--auto'], {
183
+ cwd: process.cwd(),
184
+ env: {
185
+ ...process.env,
186
+ AUTO_EVIDENCE_TRIGGER: 'auto',
187
+ AUTO_EVIDENCE_REASON: 'guard.auto_refresh',
188
+ AUTO_EVIDENCE_SUMMARY: 'auto-refresh'
189
+ }
190
+ });
191
+ } catch (error) {
192
+ const msg = error && error.message ? error.message : String(error);
193
+ this.notifier.appendDebugLog(`EVIDENCE_AUTO_REFRESH_ERROR|${msg}`);
194
+ }
170
195
  }
171
196
 
172
197
  updateUserActivity() {
@@ -110,6 +110,11 @@ class ConfigurationGeneratorService {
110
110
  // Add helper scripts
111
111
  packageJson.scripts['ast:refresh'] = 'node scripts/hooks-system/bin/update-evidence.sh';
112
112
  packageJson.scripts['ast:audit'] = 'node scripts/hooks-system/infrastructure/ast/ast-intelligence.js';
113
+ packageJson.scripts['ast:guard:start'] = 'bash scripts/hooks-system/bin/evidence-guard start';
114
+ packageJson.scripts['ast:guard:stop'] = 'bash scripts/hooks-system/bin/evidence-guard stop';
115
+ packageJson.scripts['ast:guard:restart'] = 'bash scripts/hooks-system/bin/evidence-guard restart';
116
+ packageJson.scripts['ast:guard:status'] = 'bash scripts/hooks-system/bin/evidence-guard status';
117
+ packageJson.scripts['ast:guard:logs'] = 'bash scripts/hooks-system/bin/evidence-guard logs';
113
118
 
114
119
  fs.writeFileSync(projectPackageJsonPath, JSON.stringify(packageJson, null, 2));
115
120
  this.logSuccess('npm scripts added');
@@ -123,10 +123,48 @@ class InstallService {
123
123
  this.logStep('8/8', 'Adding npm scripts to package.json...');
124
124
  this.configGenerator.addNpmScripts();
125
125
 
126
+ this.logStep('8.5/8', 'Starting evidence guard daemon...');
127
+ this.startEvidenceGuard();
128
+
126
129
  this.logger.info('INSTALLATION_COMPLETED_SUCCESSFULLY');
127
130
  this.printFooter();
128
131
  }
129
132
 
133
+ startEvidenceGuard() {
134
+ const { spawn } = require('child_process');
135
+ const guardScript = path.join(this.targetRoot, 'scripts/hooks-system/bin/evidence-guard');
136
+
137
+ if (!fs.existsSync(guardScript)) {
138
+ this.logWarning('Evidence guard script not found, skipping daemon start');
139
+ return;
140
+ }
141
+
142
+ try {
143
+ const child = spawn('bash', [guardScript, 'start'], {
144
+ cwd: this.targetRoot,
145
+ stdio: 'pipe',
146
+ detached: false
147
+ });
148
+
149
+ let output = '';
150
+ child.stdout.on('data', (data) => { output += data.toString(); });
151
+ child.stderr.on('data', (data) => { output += data.toString(); });
152
+
153
+ child.on('close', (code) => {
154
+ if (code === 0) {
155
+ this.logSuccess('Evidence guard daemon started');
156
+ } else {
157
+ this.logWarning('Failed to start evidence guard daemon');
158
+ if (output) {
159
+ console.log(output);
160
+ }
161
+ }
162
+ });
163
+ } catch (error) {
164
+ this.logWarning(`Failed to start evidence guard: ${error.message}`);
165
+ }
166
+ }
167
+
130
168
  printHeader() {
131
169
  const versionPadded = `v${this.version}`.padStart(24).padEnd(48);
132
170
  process.stdout.write(`${COLORS.blue}
@@ -141,6 +179,10 @@ ${COLORS.reset}\n`);
141
179
  process.stdout.write(`
142
180
  ${COLORS.green}✨ Installation Complete! ✨${COLORS.reset}
143
181
 
182
+ ${COLORS.cyan}Evidence Guard Daemon:${COLORS.reset}
183
+ - Auto-refresh is now running in background (every 180 seconds)
184
+ - Manage with: ${COLORS.yellow}npm run ast:guard:{start|stop|status|logs}${COLORS.reset}
185
+
144
186
  ${COLORS.cyan}Next Steps:${COLORS.reset}
145
187
  1. Review generated configuration in ${COLORS.yellow}scripts/hooks-system/config/project.config.json${COLORS.reset}
146
188
  2. Run ${COLORS.yellow}./manage-library.sh verify${COLORS.reset} to check installation
@@ -89,9 +89,7 @@ class EvidenceMonitorService {
89
89
  try {
90
90
  const raw = this.fs.readFileSync(this.evidencePath, 'utf8');
91
91
  const data = JSON.parse(raw);
92
- const rootTimestamp = new Date(data.timestamp).getTime();
93
- const severityTimestamp = new Date(data?.severity_metrics?.last_updated).getTime();
94
- const timestamp = [rootTimestamp, severityTimestamp].filter(Number.isFinite).reduce((max, v) => Math.max(max, v), NaN);
92
+ const timestamp = new Date(data.timestamp).getTime();
95
93
  if (!Number.isFinite(timestamp)) {
96
94
  this.notify({
97
95
  message: 'Evidence timestamp is invalid.',
@@ -16,6 +16,24 @@ const args = process.argv.slice(3);
16
16
 
17
17
  const HOOKS_ROOT = path.join(__dirname, '..');
18
18
 
19
+ function formatLocalTimestamp(date = new Date()) {
20
+ const year = date.getFullYear();
21
+ const month = String(date.getMonth() + 1).padStart(2, '0');
22
+ const day = String(date.getDate()).padStart(2, '0');
23
+ const hours = String(date.getHours()).padStart(2, '0');
24
+ const minutes = String(date.getMinutes()).padStart(2, '0');
25
+ const seconds = String(date.getSeconds()).padStart(2, '0');
26
+ const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
27
+
28
+ const offsetMinutes = date.getTimezoneOffset();
29
+ const sign = offsetMinutes <= 0 ? '+' : '-';
30
+ const absolute = Math.abs(offsetMinutes);
31
+ const offsetHours = String(Math.floor(absolute / 60)).padStart(2, '0');
32
+ const offsetMins = String(absolute % 60).padStart(2, '0');
33
+
34
+ return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}${sign}${offsetHours}:${offsetMins}`;
35
+ }
36
+
19
37
  function resolveRepoRoot() {
20
38
  try {
21
39
  const output = execSync('git rev-parse --show-toplevel', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
@@ -140,7 +158,7 @@ const commands = {
140
158
 
141
159
  const next = {
142
160
  ...existing,
143
- timestamp: new Date().toISOString(),
161
+ timestamp: formatLocalTimestamp(),
144
162
  trigger: process.env.AUTO_EVIDENCE_TRIGGER ?? existing.trigger,
145
163
  reason: process.env.AUTO_EVIDENCE_REASON ?? existing.reason,
146
164
  summary: process.env.AUTO_EVIDENCE_SUMMARY ?? existing.summary,
@@ -0,0 +1,110 @@
1
+ #!/bin/bash
2
+
3
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
5
+ GUARD_SCRIPT="$SCRIPT_DIR/../infrastructure/daemons/evidence-guard.js"
6
+ PID_FILE="$PROJECT_ROOT/.evidence-guard.pid"
7
+ LOG_FILE="$PROJECT_ROOT/.evidence-guard.log"
8
+
9
+ command="${1:-status}"
10
+
11
+ case "$command" in
12
+ start)
13
+ if [ -f "$PID_FILE" ]; then
14
+ PID=$(cat "$PID_FILE")
15
+ if kill -0 "$PID" 2>/dev/null; then
16
+ echo "Evidence guard is already running (PID: $PID)"
17
+ exit 0
18
+ else
19
+ rm -f "$PID_FILE"
20
+ fi
21
+ fi
22
+
23
+ echo "Starting evidence guard..."
24
+ nohup node "$GUARD_SCRIPT" > "$LOG_FILE" 2>&1 &
25
+ sleep 1
26
+
27
+ if [ -f "$PID_FILE" ]; then
28
+ PID=$(cat "$PID_FILE")
29
+ echo "Evidence guard started (PID: $PID)"
30
+ echo "Log file: $LOG_FILE"
31
+ else
32
+ echo "Failed to start evidence guard"
33
+ exit 1
34
+ fi
35
+ ;;
36
+
37
+ stop)
38
+ if [ ! -f "$PID_FILE" ]; then
39
+ echo "Evidence guard is not running"
40
+ exit 0
41
+ fi
42
+
43
+ PID=$(cat "$PID_FILE")
44
+ if kill -0 "$PID" 2>/dev/null; then
45
+ echo "Stopping evidence guard (PID: $PID)..."
46
+ kill "$PID"
47
+ sleep 1
48
+
49
+ if kill -0 "$PID" 2>/dev/null; then
50
+ echo "Force killing evidence guard..."
51
+ kill -9 "$PID"
52
+ fi
53
+
54
+ rm -f "$PID_FILE"
55
+ echo "Evidence guard stopped"
56
+ else
57
+ echo "Evidence guard is not running (stale PID file)"
58
+ rm -f "$PID_FILE"
59
+ fi
60
+ ;;
61
+
62
+ restart)
63
+ "$0" stop
64
+ sleep 1
65
+ "$0" start
66
+ ;;
67
+
68
+ status)
69
+ if [ ! -f "$PID_FILE" ]; then
70
+ echo "Evidence guard is not running"
71
+ exit 1
72
+ fi
73
+
74
+ PID=$(cat "$PID_FILE")
75
+ if kill -0 "$PID" 2>/dev/null; then
76
+ echo "Evidence guard is running (PID: $PID)"
77
+ if [ -f "$LOG_FILE" ]; then
78
+ echo ""
79
+ echo "Recent activity:"
80
+ tail -n 5 "$LOG_FILE"
81
+ fi
82
+ exit 0
83
+ else
84
+ echo "Evidence guard is not running (stale PID file)"
85
+ rm -f "$PID_FILE"
86
+ exit 1
87
+ fi
88
+ ;;
89
+
90
+ logs)
91
+ if [ -f "$LOG_FILE" ]; then
92
+ tail -f "$LOG_FILE"
93
+ else
94
+ echo "No log file found"
95
+ exit 1
96
+ fi
97
+ ;;
98
+
99
+ *)
100
+ echo "Usage: $0 {start|stop|restart|status|logs}"
101
+ echo ""
102
+ echo "Commands:"
103
+ echo " start - Start the evidence guard daemon"
104
+ echo " stop - Stop the evidence guard daemon"
105
+ echo " restart - Restart the evidence guard daemon"
106
+ echo " status - Check if the daemon is running"
107
+ echo " logs - Tail the daemon logs"
108
+ exit 1
109
+ ;;
110
+ esac
@@ -14,9 +14,9 @@ for arg in "$@"; do
14
14
  fi
15
15
  done
16
16
 
17
- CLI="$REPO_ROOT/node_modules/pumuki-ast-hooks/scripts/hooks-system/bin/cli.js"
17
+ CLI="$REPO_ROOT/scripts/hooks-system/bin/cli.js"
18
18
  if [[ ! -f "$CLI" ]]; then
19
- CLI="$REPO_ROOT/scripts/hooks-system/bin/cli.js"
19
+ CLI="$REPO_ROOT/node_modules/pumuki-ast-hooks/scripts/hooks-system/bin/cli.js"
20
20
  fi
21
21
 
22
22
  if [[ ! -f "$CLI" ]]; then
@@ -339,7 +339,7 @@ function platformOf(filePath) {
339
339
  if (p.includes("/apps/mobile-android/") || p.includes("/apps/android/")) return "android";
340
340
 
341
341
  if (p.includes("/landing-page/") || p.includes("landing-page/")) return "frontend";
342
- if (p.includes("/scripts/hooks-system/") || p.includes("scripts/hooks-system/")) return "backend";
342
+ if (p.includes("/scripts/hooks-system/") || p.includes("scripts/hooks-system/")) return null;
343
343
  if (p.includes("/packages/ast-hooks/") || p.includes("packages/ast-hooks/")) return "backend";
344
344
 
345
345
  if (p.endsWith(".swift")) return "ios";
@@ -3,6 +3,24 @@ const path = require("path");
3
3
  const fs = require("fs");
4
4
  const env = require("../../config/env.js");
5
5
 
6
+ function formatLocalTimestamp(date = new Date()) {
7
+ const year = date.getFullYear();
8
+ const month = String(date.getMonth() + 1).padStart(2, '0');
9
+ const day = String(date.getDate()).padStart(2, '0');
10
+ const hours = String(date.getHours()).padStart(2, '0');
11
+ const minutes = String(date.getMinutes()).padStart(2, '0');
12
+ const seconds = String(date.getSeconds()).padStart(2, '0');
13
+ const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
14
+
15
+ const offsetMinutes = date.getTimezoneOffset();
16
+ const sign = offsetMinutes <= 0 ? '+' : '-';
17
+ const absolute = Math.abs(offsetMinutes);
18
+ const offsetHours = String(Math.floor(absolute / 60)).padStart(2, '0');
19
+ const offsetMins = String(absolute % 60).padStart(2, '0');
20
+
21
+ return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}${sign}${offsetHours}:${offsetMins}`;
22
+ }
23
+
6
24
  const astModulesPath = __dirname;
7
25
  const { createProject, platformOf, mapToLevel } = require(path.join(astModulesPath, "ast-core"));
8
26
  const { MacOSNotificationAdapter } = require(path.join(__dirname, '../adapters/MacOSNotificationAdapter'));
@@ -474,7 +492,7 @@ function saveDetailedReport(findings, levelTotals, platformTotals, project, root
474
492
  findings,
475
493
  metadata: {
476
494
  totalFiles: project.getSourceFiles().length,
477
- timestamp: new Date().toISOString(),
495
+ timestamp: formatLocalTimestamp(),
478
496
  root,
479
497
  stagingOnlyMode: !!(context && context.stagingOnlyMode),
480
498
  stagedFiles: Array.isArray(context && context.stagedFiles) ? context.stagedFiles : [],
@@ -503,12 +521,8 @@ function updateAIEvidenceMetrics(findings, levelTotals, root) {
503
521
  try {
504
522
  const evidence = JSON.parse(fs.readFileSync(evidencePath, 'utf8'));
505
523
 
506
- const now = new Date().toISOString();
507
-
508
- evidence.timestamp = now;
509
-
510
524
  evidence.severity_metrics = {
511
- last_updated: now,
525
+ last_updated: formatLocalTimestamp(),
512
526
  total_violations: findings.length,
513
527
  by_severity: {
514
528
  CRITICAL: levelTotals.CRITICAL || 0,
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require('child_process');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ const REFRESH_INTERVAL_MS = 180000;
8
+ const EVIDENCE_FILE = '.AI_EVIDENCE.json';
9
+ const PID_FILE = '.evidence-guard.pid';
10
+
11
+ class EvidenceGuard {
12
+ constructor() {
13
+ this.projectRoot = this.findProjectRoot();
14
+ this.pidFile = path.join(this.projectRoot, PID_FILE);
15
+ this.evidenceFile = path.join(this.projectRoot, EVIDENCE_FILE);
16
+ this.updateScript = this.findUpdateScript();
17
+ this.isRunning = false;
18
+ this.intervalId = null;
19
+ }
20
+
21
+ findProjectRoot() {
22
+ let currentDir = process.cwd();
23
+ while (currentDir !== '/') {
24
+ if (fs.existsSync(path.join(currentDir, 'package.json'))) {
25
+ return currentDir;
26
+ }
27
+ currentDir = path.dirname(currentDir);
28
+ }
29
+ return process.cwd();
30
+ }
31
+
32
+ findUpdateScript() {
33
+ const possiblePaths = [
34
+ path.join(this.projectRoot, 'scripts/hooks-system/bin/update-evidence.sh'),
35
+ path.join(this.projectRoot, 'node_modules/pumuki-ast-hooks/scripts/hooks-system/bin/update-evidence.sh'),
36
+ path.join(__dirname, '../../bin/update-evidence.sh')
37
+ ];
38
+
39
+ for (const scriptPath of possiblePaths) {
40
+ if (fs.existsSync(scriptPath)) {
41
+ return scriptPath;
42
+ }
43
+ }
44
+
45
+ throw new Error('update-evidence.sh not found');
46
+ }
47
+
48
+ writePidFile() {
49
+ try {
50
+ fs.writeFileSync(this.pidFile, process.pid.toString(), 'utf8');
51
+ } catch (error) {
52
+ console.error('[EvidenceGuard] Failed to write PID file:', error.message);
53
+ }
54
+ }
55
+
56
+ removePidFile() {
57
+ try {
58
+ if (fs.existsSync(this.pidFile)) {
59
+ fs.unlinkSync(this.pidFile);
60
+ }
61
+ } catch (error) {
62
+ console.error('[EvidenceGuard] Failed to remove PID file:', error.message);
63
+ }
64
+ }
65
+
66
+ isAlreadyRunning() {
67
+ if (!fs.existsSync(this.pidFile)) {
68
+ return false;
69
+ }
70
+
71
+ try {
72
+ const pid = parseInt(fs.readFileSync(this.pidFile, 'utf8').trim(), 10);
73
+ process.kill(pid, 0);
74
+ return true;
75
+ } catch (error) {
76
+ this.removePidFile();
77
+ return false;
78
+ }
79
+ }
80
+
81
+ async refreshEvidence() {
82
+ return new Promise((resolve) => {
83
+ const child = spawn('bash', [this.updateScript, '--auto'], {
84
+ cwd: this.projectRoot,
85
+ stdio: 'ignore',
86
+ detached: false
87
+ });
88
+
89
+ child.on('close', (code) => {
90
+ if (code === 0) {
91
+ console.log(`[EvidenceGuard] Evidence refreshed at ${new Date().toISOString()}`);
92
+ } else {
93
+ console.error(`[EvidenceGuard] Refresh failed with code ${code}`);
94
+ }
95
+ resolve();
96
+ });
97
+
98
+ child.on('error', (error) => {
99
+ console.error('[EvidenceGuard] Refresh error:', error.message);
100
+ resolve();
101
+ });
102
+ });
103
+ }
104
+
105
+ async start() {
106
+ if (this.isAlreadyRunning()) {
107
+ console.log('[EvidenceGuard] Already running');
108
+ process.exit(0);
109
+ }
110
+
111
+ this.writePidFile();
112
+ this.isRunning = true;
113
+
114
+ console.log('[EvidenceGuard] Started');
115
+ console.log(`[EvidenceGuard] Project root: ${this.projectRoot}`);
116
+ console.log(`[EvidenceGuard] Refresh interval: ${REFRESH_INTERVAL_MS / 1000}s`);
117
+
118
+ await this.refreshEvidence();
119
+
120
+ this.intervalId = setInterval(async () => {
121
+ if (this.isRunning) {
122
+ await this.refreshEvidence();
123
+ }
124
+ }, REFRESH_INTERVAL_MS);
125
+
126
+ process.on('SIGTERM', () => this.stop());
127
+ process.on('SIGINT', () => this.stop());
128
+ process.on('exit', () => this.cleanup());
129
+ }
130
+
131
+ stop() {
132
+ console.log('[EvidenceGuard] Stopping...');
133
+ this.isRunning = false;
134
+ if (this.intervalId) {
135
+ clearInterval(this.intervalId);
136
+ this.intervalId = null;
137
+ }
138
+ this.cleanup();
139
+ process.exit(0);
140
+ }
141
+
142
+ cleanup() {
143
+ this.removePidFile();
144
+ }
145
+ }
146
+
147
+ if (require.main === module) {
148
+ const guard = new EvidenceGuard();
149
+ guard.start().catch((error) => {
150
+ console.error('[EvidenceGuard] Fatal error:', error);
151
+ process.exit(1);
152
+ });
153
+ }
154
+
155
+ module.exports = EvidenceGuard;
@@ -761,8 +761,6 @@ async function validateAndFix(params) {
761
761
  const McpProtocolHandler = require('./services/McpProtocolHandler');
762
762
  const protocolHandler = new McpProtocolHandler(process.stdin, process.stdout);
763
763
 
764
- const SERVER_NAME = (process.env.MCP_SERVER_NAME || '').trim() || 'ast-intelligence-automation';
765
-
766
764
  async function handleMcpMessage(message) {
767
765
  try {
768
766
  const request = JSON.parse(message);
@@ -790,7 +788,7 @@ async function handleMcpMessage(message) {
790
788
  }
791
789
  },
792
790
  serverInfo: {
793
- name: SERVER_NAME,
791
+ name: 'ast-intelligence-automation',
794
792
  version: '3.0.0',
795
793
  description: 'Autonomous AST Intelligence + Git Flow Automation'
796
794
  }
@@ -10,6 +10,24 @@ const { toErrorMessage } = require('../utils/error-utils');
10
10
  const fs = require('fs');
11
11
  const path = require('path');
12
12
 
13
+ function formatLocalTimestamp(date = new Date()) {
14
+ const year = date.getFullYear();
15
+ const month = String(date.getMonth() + 1).padStart(2, '0');
16
+ const day = String(date.getDate()).padStart(2, '0');
17
+ const hours = String(date.getHours()).padStart(2, '0');
18
+ const minutes = String(date.getMinutes()).padStart(2, '0');
19
+ const seconds = String(date.getSeconds()).padStart(2, '0');
20
+ const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
21
+
22
+ const offsetMinutes = date.getTimezoneOffset();
23
+ const sign = offsetMinutes <= 0 ? '+' : '-';
24
+ const absolute = Math.abs(offsetMinutes);
25
+ const offsetHours = String(Math.floor(absolute / 60)).padStart(2, '0');
26
+ const offsetMins = String(absolute % 60).padStart(2, '0');
27
+
28
+ return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}${sign}${offsetHours}:${offsetMins}`;
29
+ }
30
+
13
31
  function resolveAuditTmpDir() {
14
32
  const configured = (env.get('AUDIT_TMP', '') || '').trim();
15
33
  if (configured.length > 0) {
@@ -147,7 +165,7 @@ function saveEnhancedViolations(violations) {
147
165
  const outputPath = path.join(resolveAuditTmpDir(), 'ast-summary-enhanced.json');
148
166
 
149
167
  const enhanced = {
150
- timestamp: new Date().toISOString(),
168
+ timestamp: formatLocalTimestamp(),
151
169
  generator: 'AST Intelligence v2.0 with Severity Evaluation',
152
170
  intelligentEvaluation: true,
153
171
  totalViolations: violations.length,
@@ -180,10 +198,8 @@ function updateAIEvidence(violations, gateResult, tokenUsage) {
180
198
  try {
181
199
  const evidence = JSON.parse(fs.readFileSync(evidencePath, 'utf8'));
182
200
 
183
- evidence.timestamp = new Date().toISOString();
184
-
185
201
  evidence.severity_metrics = {
186
- last_updated: new Date().toISOString(),
202
+ last_updated: formatLocalTimestamp(),
187
203
  total_violations: violations.length,
188
204
  by_severity: {
189
205
  CRITICAL: violations.filter(v => v.severity === 'CRITICAL').length,
@@ -255,7 +271,7 @@ function updateAIEvidence(violations, gateResult, tokenUsage) {
255
271
  const nextGate = {
256
272
  status: gateResult.passed ? 'ALLOWED' : 'BLOCKED',
257
273
  scope: gateScope === 'repo' || gateScope === 'repository' ? 'repo' : 'staging',
258
- last_check: new Date().toISOString(),
274
+ last_check: formatLocalTimestamp(),
259
275
  violations: blockingViolations.map(v => ({
260
276
  file: v.filePath || v.file || 'unknown',
261
277
  line: v.line || null,
@@ -771,13 +771,6 @@ summarize_all() {
771
771
  print_final_signature
772
772
  exit 0
773
773
  fi
774
-
775
- local is_revert_in_progress=0
776
- if [[ "${AST_ALLOW_REVERT:-1}" == "1" ]] && command -v git >/dev/null 2>&1; then
777
- if git rev-parse -q --verify REVERT_HEAD >/dev/null 2>&1; then
778
- is_revert_in_progress=1
779
- fi
780
- fi
781
774
  local gate_crit gate_high gate_med gate_low gate_es
782
775
 
783
776
  # Decide gate values based on mode
@@ -802,15 +795,6 @@ summarize_all() {
802
795
  # Block on ANY violation (CRITICAL + HIGH + MEDIUM + LOW)
803
796
  if (( gate_crit > 0 || gate_high > 0 || gate_med > 0 || gate_low > 0 || gate_es > 0 )); then
804
797
  printf "\n"
805
- if (( is_revert_in_progress == 1 )); then
806
- printf "%b[REVERT MODE - COMMIT ALLOWED]%b\n" "$YELLOW" "$NC"
807
- printf " Detected git revert in progress (REVERT_HEAD).\n"
808
- printf " Skipping quality gate blocking for pre-existing violations.\n"
809
- printf " Tip: set AST_ALLOW_REVERT=0 to enforce gates during revert.\n"
810
- printf "\n"
811
- print_final_signature
812
- exit 0
813
- fi
814
798
  if [[ "${BLOCK_ON_REPO_VIOLATIONS:-0}" == "1" ]]; then
815
799
  printf "%b[COMMIT BLOCKED - STRICT REPO+STAGING]%b\n" "$RED" "$NC"
816
800
  printf " CRITICAL violations (repository): %s\n" "$gate_crit"
@@ -835,15 +819,6 @@ summarize_all() {
835
819
  # Standard mode: Block only on CRITICAL/HIGH IN STAGING
836
820
  if (( gate_crit > 0 || gate_high > 0 )); then
837
821
  printf "\n"
838
- if (( is_revert_in_progress == 1 )); then
839
- printf "%b[REVERT MODE - COMMIT ALLOWED]%b\n" "$YELLOW" "$NC"
840
- printf " Detected git revert in progress (REVERT_HEAD).\n"
841
- printf " Skipping quality gate blocking for pre-existing violations.\n"
842
- printf " Tip: set AST_ALLOW_REVERT=0 to enforce gates during revert.\n"
843
- printf "\n"
844
- print_final_signature
845
- exit 0
846
- fi
847
822
  printf "%b[COMMIT BLOCKED - CRITICAL/HIGH]%b\n" "$RED" "$NC"
848
823
  printf " CRITICAL violations in staging: %s\n" "$gate_crit"
849
824
  printf " HIGH violations in staging: %s\n" "$gate_high"
@@ -11,7 +11,9 @@
11
11
  # Get current timestamp in ISO 8601 with milliseconds
12
12
  # Returns: 2025-11-06T10:45:23.123Z
13
13
  get_current_timestamp() {
14
- date -u +"%Y-%m-%dT%H:%M:%S.000Z"
14
+ local raw
15
+ raw=$(date +"%Y-%m-%dT%H:%M:%S.000%z")
16
+ echo "$raw" | sed -E 's/([+-][0-9]{2})([0-9]{2})$/\1:\2/'
15
17
  }
16
18
 
17
19
  # Get current timestamp in epoch seconds
@@ -26,10 +28,16 @@ iso_to_epoch() {
26
28
  local timestamp="$1"
27
29
 
28
30
  # Strip milliseconds if present (2025-11-06T10:45:23.123Z → 2025-11-06T10:45:23Z)
29
- local clean_ts=$(echo "$timestamp" | sed 's/\.[0-9]*Z$/Z/')
31
+ local clean_ts
32
+ clean_ts=$(echo "$timestamp" | sed -E 's/\.[0-9]+Z$/Z/')
30
33
 
31
- # Convert to epoch (macOS date command)
32
- TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%SZ" "$clean_ts" +%s 2>/dev/null || echo "0"
34
+ if echo "$clean_ts" | grep -qE 'Z$'; then
35
+ TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%SZ" "$clean_ts" +%s 2>/dev/null || echo "0"
36
+ return 0
37
+ fi
38
+
39
+ clean_ts=$(echo "$timestamp" | sed -E 's/\.[0-9]+([+-][0-9]{2}):([0-9]{2})$/\1\2/' | sed -E 's/([+-][0-9]{2}):([0-9]{2})$/\1\2/')
40
+ date -j -f "%Y-%m-%dT%H:%M:%S%z" "$clean_ts" +%s 2>/dev/null || echo "0"
33
41
  }
34
42
 
35
43
  # Get age of timestamp in seconds
@@ -37,3 +37,6 @@
37
37
  {"timestamp":"2026-01-03T14:05:38.098Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
38
38
  {"timestamp":"2026-01-03T14:05:38.102Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
39
39
  {"timestamp":"2026-01-03T14:05:38.103Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
40
+ {"timestamp":"2026-01-04T07:12:14.852Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
41
+ {"timestamp":"2026-01-04T07:12:14.855Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
42
+ {"timestamp":"2026-01-04T07:12:14.856Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}