pumuki-ast-hooks 5.5.12 → 5.5.14

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.
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.14",
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() {
@@ -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,
@@ -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,
@@ -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)"}}