sinapse-ai 7.0.5 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.sinapse-ai/core-config.yaml +2 -26
  2. package/.sinapse-ai/data/entity-registry.yaml +742 -917
  3. package/.sinapse-ai/data/registry-update-log.jsonl +22 -0
  4. package/.sinapse-ai/infrastructure/scripts/ide-sync/index.js +1 -49
  5. package/.sinapse-ai/infrastructure/scripts/validate-parity.js +0 -7
  6. package/.sinapse-ai/install-manifest.yaml +11 -43
  7. package/README.en.md +6 -11
  8. package/README.md +6 -11
  9. package/bin/cli.js +116 -75
  10. package/bin/modules/env-config.js +1 -2
  11. package/bin/sinapse-init.js +23 -188
  12. package/docs/ide-integration.md +22 -263
  13. package/docs/installation/README.md +4 -6
  14. package/docs/installation/faq.md +10 -33
  15. package/docs/installation/linux.md +0 -23
  16. package/docs/installation/macos.md +0 -10
  17. package/docs/installation/troubleshooting.md +5 -9
  18. package/docs/installation/v4-quick-start.md +1 -1
  19. package/docs/installation/windows.md +0 -18
  20. package/package.json +2 -9
  21. package/packages/installer/src/config/ide-configs.js +3 -49
  22. package/squads/claude-code-mastery/CHANGELOG.md +22 -0
  23. package/squads/claude-code-mastery/README.md +146 -0
  24. package/squads/claude-code-mastery/agents/claude-mastery-chief.md +554 -0
  25. package/squads/claude-code-mastery/agents/config-engineer.md +865 -0
  26. package/squads/claude-code-mastery/agents/hooks-architect.md +1013 -0
  27. package/squads/claude-code-mastery/agents/mcp-integrator.md +791 -0
  28. package/squads/claude-code-mastery/agents/project-integrator.md +1196 -0
  29. package/squads/claude-code-mastery/agents/roadmap-sentinel.md +931 -0
  30. package/squads/claude-code-mastery/agents/skill-craftsman.md +1250 -0
  31. package/squads/claude-code-mastery/agents/swarm-orqx.md +1008 -0
  32. package/squads/claude-code-mastery/checklists/agent-team-readiness-checklist.md +88 -0
  33. package/squads/claude-code-mastery/checklists/brownfield-readiness-checklist.md +91 -0
  34. package/squads/claude-code-mastery/checklists/change-checklist.md +75 -0
  35. package/squads/claude-code-mastery/checklists/context-rot-checklist.md +114 -0
  36. package/squads/claude-code-mastery/checklists/integration-audit-checklist.md +104 -0
  37. package/squads/claude-code-mastery/checklists/multi-agent-review-checklist.md +77 -0
  38. package/squads/claude-code-mastery/checklists/pre-push-checklist.md +79 -0
  39. package/squads/claude-code-mastery/data/ci-cd-patterns.yaml +412 -0
  40. package/squads/claude-code-mastery/data/claude-code-quick-ref.yaml +314 -0
  41. package/squads/claude-code-mastery/data/hook-patterns.yaml +512 -0
  42. package/squads/claude-code-mastery/data/mcp-integration-catalog.yaml +323 -0
  43. package/squads/claude-code-mastery/data/project-type-signatures.yaml +335 -0
  44. package/squads/claude-code-mastery/scripts/validate-setup.js +163 -0
  45. package/squads/claude-code-mastery/squad.yaml +205 -0
  46. package/squads/claude-code-mastery/tasks/audit-integration.md +219 -0
  47. package/squads/claude-code-mastery/tasks/audit-settings.md +206 -0
  48. package/squads/claude-code-mastery/tasks/audit-setup.md +225 -0
  49. package/squads/claude-code-mastery/tasks/brownfield-setup.md +322 -0
  50. package/squads/claude-code-mastery/tasks/ci-cd-setup.md +335 -0
  51. package/squads/claude-code-mastery/tasks/claude-md-engineer.md +334 -0
  52. package/squads/claude-code-mastery/tasks/configure-claude-code.md +215 -0
  53. package/squads/claude-code-mastery/tasks/context-rot-audit.md +329 -0
  54. package/squads/claude-code-mastery/tasks/create-agent-definition.md +278 -0
  55. package/squads/claude-code-mastery/tasks/create-rules.md +206 -0
  56. package/squads/claude-code-mastery/tasks/create-team-topology.md +258 -0
  57. package/squads/claude-code-mastery/tasks/diagnose.md +166 -0
  58. package/squads/claude-code-mastery/tasks/enterprise-config.md +346 -0
  59. package/squads/claude-code-mastery/tasks/hook-designer.md +272 -0
  60. package/squads/claude-code-mastery/tasks/integrate-project.md +304 -0
  61. package/squads/claude-code-mastery/tasks/mcp-integration-plan.md +229 -0
  62. package/squads/claude-code-mastery/tasks/mcp-workflow.md +285 -0
  63. package/squads/claude-code-mastery/tasks/multi-project-setup.md +228 -0
  64. package/squads/claude-code-mastery/tasks/optimize-context.md +217 -0
  65. package/squads/claude-code-mastery/tasks/optimize-workflow.md +226 -0
  66. package/squads/claude-code-mastery/tasks/parallel-decomposition.md +293 -0
  67. package/squads/claude-code-mastery/tasks/permission-strategy.md +266 -0
  68. package/squads/claude-code-mastery/tasks/sandbox-setup.md +279 -0
  69. package/squads/claude-code-mastery/tasks/setup-repository.md +230 -0
  70. package/squads/claude-code-mastery/tasks/setup-wizard.md +236 -0
  71. package/squads/claude-code-mastery/tasks/worktree-strategy.md +320 -0
  72. package/squads/claude-code-mastery/templates/claude-md-fullstack.md +147 -0
  73. package/squads/claude-code-mastery/templates/claude-md-library.md +175 -0
  74. package/squads/claude-code-mastery/templates/claude-md-microservices.md +186 -0
  75. package/squads/claude-code-mastery/templates/claude-md-mobile.md +198 -0
  76. package/squads/claude-code-mastery/templates/claude-md-monorepo.md +139 -0
  77. package/squads/claude-code-mastery/templates/github-actions-claude-ci.yml +348 -0
  78. package/squads/claude-code-mastery/templates/github-actions-claude-review.yml +179 -0
  79. package/squads/claude-code-mastery/workflows/wf-audit-complete.yaml +140 -0
  80. package/squads/claude-code-mastery/workflows/wf-knowledge-update.yaml +165 -0
  81. package/squads/claude-code-mastery/workflows/wf-project-setup.yaml +192 -0
  82. package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/antigravity.js +0 -105
  83. package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/cursor.js +0 -94
  84. package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/github-copilot.js +0 -184
  85. package/.sinapse-ai/infrastructure/scripts/validate-gemini-integration.js +0 -151
  86. package/.sinapse-ai/product/templates/ide-rules/antigravity-rules.md +0 -115
  87. package/.sinapse-ai/product/templates/ide-rules/copilot-rules.md +0 -92
  88. package/.sinapse-ai/product/templates/ide-rules/cursor-rules.md +0 -115
  89. package/.sinapse-ai/product/templates/ide-rules/gemini-rules.md +0 -87
  90. package/docs/pt/platforms/antigravity.md +0 -508
  91. package/docs/pt/platforms/cursor.md +0 -633
  92. package/docs/pt/platforms/gemini-cli.md +0 -481
  93. package/docs/pt/platforms/github-copilot.md +0 -478
@@ -0,0 +1,512 @@
1
+ # Hook Patterns — Common Automation Recipes for Claude Code
2
+ # Squad: claude-code-mastery
3
+ # Last updated: 2026-03-02
4
+
5
+ version: "1.0.0"
6
+
7
+ # Each pattern provides a complete, copy-paste-ready hook configuration
8
+ # with explanation and settings.json integration.
9
+
10
+ patterns:
11
+
12
+ # ---------------------------------------------------------------------------
13
+ # 1. DAMAGE CONTROL — Block destructive commands
14
+ # ---------------------------------------------------------------------------
15
+ - name: damage-control
16
+ event: PreToolUse
17
+ description: |
18
+ Intercept Bash tool calls and block dangerous commands before execution.
19
+ Prevents accidental rm -rf, git push --force, DROP TABLE, etc.
20
+ matcher: "Bash"
21
+ severity: critical
22
+ settings_json_snippet: |
23
+ {
24
+ "hooks": {
25
+ "PreToolUse": [
26
+ {
27
+ "matcher": "Bash",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "python3 ~/.claude/hooks/damage-control.py"
32
+ }
33
+ ]
34
+ }
35
+ ]
36
+ }
37
+ }
38
+ code_example:
39
+ language: python
40
+ filename: "~/.claude/hooks/damage-control.py"
41
+ code: |
42
+ #!/usr/bin/env python3
43
+ """Damage control hook — block destructive Bash commands."""
44
+ import json
45
+ import sys
46
+ import re
47
+
48
+ BLOCKED_PATTERNS = [
49
+ r'\brm\s+(-[rRf]+\s+|--recursive)', # rm -rf
50
+ r'\bgit\s+push\s+--force', # git push --force
51
+ r'\bgit\s+reset\s+--hard', # git reset --hard
52
+ r'\bgit\s+clean\s+-[fd]', # git clean -f/-d
53
+ r'\bDROP\s+(TABLE|DATABASE|SCHEMA)', # SQL destructive
54
+ r'\bTRUNCATE\s+TABLE', # SQL truncate
55
+ r'\bchmod\s+777', # Insecure perms
56
+ r'\bcurl\b.*\|\s*(sudo\s+)?bash', # Pipe curl to bash
57
+ r'\bsudo\s+rm\b', # sudo rm
58
+ ]
59
+
60
+ def main():
61
+ input_data = json.loads(sys.stdin.read())
62
+ tool_input = input_data.get("tool_input", {})
63
+ command = tool_input.get("command", "")
64
+
65
+ for pattern in BLOCKED_PATTERNS:
66
+ if re.search(pattern, command, re.IGNORECASE):
67
+ result = {
68
+ "decision": "block",
69
+ "reason": f"Blocked by damage-control: matches '{pattern}'"
70
+ }
71
+ print(json.dumps(result))
72
+ return
73
+
74
+ print(json.dumps({"decision": "approve"}))
75
+
76
+ if __name__ == "__main__":
77
+ main()
78
+
79
+ # ---------------------------------------------------------------------------
80
+ # 2. AUTO-LINT — Run linter after file modifications
81
+ # ---------------------------------------------------------------------------
82
+ - name: auto-lint
83
+ event: PostToolUse
84
+ description: |
85
+ Automatically run linter/formatter after Write or Edit tool calls.
86
+ Catches style issues immediately, maintaining code quality.
87
+ matcher: "Write|Edit"
88
+ severity: low
89
+ settings_json_snippet: |
90
+ {
91
+ "hooks": {
92
+ "PostToolUse": [
93
+ {
94
+ "matcher": "Write|Edit",
95
+ "hooks": [
96
+ {
97
+ "type": "command",
98
+ "command": "python3 ~/.claude/hooks/auto-lint.py"
99
+ }
100
+ ]
101
+ }
102
+ ]
103
+ }
104
+ }
105
+ code_example:
106
+ language: python
107
+ filename: "~/.claude/hooks/auto-lint.py"
108
+ code: |
109
+ #!/usr/bin/env python3
110
+ """Auto-lint hook — run appropriate linter after file changes."""
111
+ import json
112
+ import subprocess
113
+ import sys
114
+ import os
115
+
116
+ LINTERS = {
117
+ ".ts": "npx eslint --fix {file}",
118
+ ".tsx": "npx eslint --fix {file}",
119
+ ".js": "npx eslint --fix {file}",
120
+ ".jsx": "npx eslint --fix {file}",
121
+ ".py": "ruff check --fix {file}",
122
+ ".rs": "rustfmt {file}",
123
+ ".go": "gofmt -w {file}",
124
+ ".css": "npx prettier --write {file}",
125
+ ".json": "npx prettier --write {file}",
126
+ }
127
+
128
+ def main():
129
+ input_data = json.loads(sys.stdin.read())
130
+ tool_input = input_data.get("tool_input", {})
131
+ file_path = tool_input.get("file_path", "")
132
+
133
+ if not file_path or not os.path.exists(file_path):
134
+ return
135
+
136
+ _, ext = os.path.splitext(file_path)
137
+ lint_cmd = LINTERS.get(ext)
138
+
139
+ if lint_cmd:
140
+ cmd = lint_cmd.format(file=file_path)
141
+ try:
142
+ subprocess.run(
143
+ cmd, shell=True, capture_output=True,
144
+ timeout=30, cwd=os.getcwd()
145
+ )
146
+ except (subprocess.TimeoutExpired, Exception):
147
+ pass # Non-blocking — lint failure should not stop work
148
+
149
+ if __name__ == "__main__":
150
+ main()
151
+
152
+ # ---------------------------------------------------------------------------
153
+ # 3. NOTIFICATION — Send alerts to Slack/Discord
154
+ # ---------------------------------------------------------------------------
155
+ - name: notification
156
+ event: Notification
157
+ description: |
158
+ Forward Claude Code notifications to external channels.
159
+ Useful for team awareness of agent activity.
160
+ matcher: ""
161
+ severity: low
162
+ settings_json_snippet: |
163
+ {
164
+ "hooks": {
165
+ "Notification": [
166
+ {
167
+ "matcher": "",
168
+ "hooks": [
169
+ {
170
+ "type": "command",
171
+ "command": "python3 ~/.claude/hooks/notify.py"
172
+ }
173
+ ]
174
+ }
175
+ ]
176
+ }
177
+ }
178
+ code_example:
179
+ language: python
180
+ filename: "~/.claude/hooks/notify.py"
181
+ code: |
182
+ #!/usr/bin/env python3
183
+ """Notification hook — forward to Slack webhook."""
184
+ import json
185
+ import sys
186
+ import urllib.request
187
+
188
+ SLACK_WEBHOOK = os.environ.get("SLACK_WEBHOOK_URL", "")
189
+
190
+ def main():
191
+ if not SLACK_WEBHOOK:
192
+ return
193
+
194
+ input_data = json.loads(sys.stdin.read())
195
+ message = input_data.get("message", "Claude Code notification")
196
+ title = input_data.get("title", "Notification")
197
+
198
+ payload = {
199
+ "text": f"*{title}*\n{message}",
200
+ "username": "Claude Code",
201
+ "icon_emoji": ":robot_face:"
202
+ }
203
+
204
+ req = urllib.request.Request(
205
+ SLACK_WEBHOOK,
206
+ data=json.dumps(payload).encode(),
207
+ headers={"Content-Type": "application/json"}
208
+ )
209
+
210
+ try:
211
+ urllib.request.urlopen(req, timeout=5)
212
+ except Exception:
213
+ pass # Non-blocking
214
+
215
+ if __name__ == "__main__":
216
+ import os
217
+ main()
218
+
219
+ # ---------------------------------------------------------------------------
220
+ # 4. CONTEXT PRESERVATION — Save state before compaction
221
+ # ---------------------------------------------------------------------------
222
+ - name: context-preservation
223
+ event: PreCompact
224
+ description: |
225
+ Before context window compaction, save critical state to disk.
226
+ Preserves important decisions, file lists, and progress that
227
+ would otherwise be lost during compaction.
228
+ matcher: ""
229
+ severity: medium
230
+ settings_json_snippet: |
231
+ {
232
+ "hooks": {
233
+ "PreCompact": [
234
+ {
235
+ "matcher": "",
236
+ "hooks": [
237
+ {
238
+ "type": "command",
239
+ "command": "python3 ~/.claude/hooks/preserve-context.py"
240
+ }
241
+ ]
242
+ }
243
+ ]
244
+ }
245
+ }
246
+ code_example:
247
+ language: python
248
+ filename: "~/.claude/hooks/preserve-context.py"
249
+ code: |
250
+ #!/usr/bin/env python3
251
+ """PreCompact hook — preserve critical context before compaction."""
252
+ import json
253
+ import sys
254
+ import os
255
+ from datetime import datetime
256
+
257
+ PRESERVE_DIR = os.path.join(os.getcwd(), ".claude", "preserved-context")
258
+
259
+ def main():
260
+ input_data = json.loads(sys.stdin.read())
261
+ session_id = input_data.get("session_id", "unknown")
262
+ conversation = input_data.get("conversation_summary", "")
263
+
264
+ os.makedirs(PRESERVE_DIR, exist_ok=True)
265
+
266
+ timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
267
+ filename = f"context-{session_id[:8]}-{timestamp}.json"
268
+ filepath = os.path.join(PRESERVE_DIR, filename)
269
+
270
+ preserved = {
271
+ "timestamp": timestamp,
272
+ "session_id": session_id,
273
+ "summary": conversation,
274
+ "cwd": os.getcwd(),
275
+ }
276
+
277
+ with open(filepath, "w") as f:
278
+ json.dump(preserved, f, indent=2)
279
+
280
+ if __name__ == "__main__":
281
+ main()
282
+
283
+ # ---------------------------------------------------------------------------
284
+ # 5. COST TRACKING — Log token usage on session end
285
+ # ---------------------------------------------------------------------------
286
+ - name: cost-tracking
287
+ event: Stop
288
+ description: |
289
+ Track token usage and estimated cost at the end of each agent turn.
290
+ Writes to a JSONL log for analysis and budgeting.
291
+ matcher: ""
292
+ severity: low
293
+ settings_json_snippet: |
294
+ {
295
+ "hooks": {
296
+ "Stop": [
297
+ {
298
+ "matcher": "",
299
+ "hooks": [
300
+ {
301
+ "type": "command",
302
+ "command": "python3 ~/.claude/hooks/cost-tracker.py"
303
+ }
304
+ ]
305
+ }
306
+ ]
307
+ }
308
+ }
309
+ code_example:
310
+ language: python
311
+ filename: "~/.claude/hooks/cost-tracker.py"
312
+ code: |
313
+ #!/usr/bin/env python3
314
+ """Stop hook — log token usage and estimated cost."""
315
+ import json
316
+ import sys
317
+ import os
318
+ from datetime import datetime
319
+
320
+ LOG_FILE = os.path.expanduser("~/.claude/logs/cost-tracking.jsonl")
321
+
322
+ # Approximate pricing per 1M tokens (Claude Sonnet 4)
323
+ INPUT_COST_PER_M = 3.0
324
+ OUTPUT_COST_PER_M = 15.0
325
+
326
+ def main():
327
+ input_data = json.loads(sys.stdin.read())
328
+
329
+ input_tokens = input_data.get("input_tokens", 0)
330
+ output_tokens = input_data.get("output_tokens", 0)
331
+ session_id = input_data.get("session_id", "unknown")
332
+
333
+ cost_input = (input_tokens / 1_000_000) * INPUT_COST_PER_M
334
+ cost_output = (output_tokens / 1_000_000) * OUTPUT_COST_PER_M
335
+ total_cost = cost_input + cost_output
336
+
337
+ entry = {
338
+ "timestamp": datetime.now().isoformat(),
339
+ "session_id": session_id,
340
+ "input_tokens": input_tokens,
341
+ "output_tokens": output_tokens,
342
+ "estimated_cost_usd": round(total_cost, 4),
343
+ "project": os.path.basename(os.getcwd()),
344
+ }
345
+
346
+ os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True)
347
+ with open(LOG_FILE, "a") as f:
348
+ f.write(json.dumps(entry) + "\n")
349
+
350
+ if __name__ == "__main__":
351
+ main()
352
+
353
+ # ---------------------------------------------------------------------------
354
+ # 6. SECURITY GATE — Validate no secrets exposure
355
+ # ---------------------------------------------------------------------------
356
+ - name: security-gate
357
+ event: PreToolUse
358
+ description: |
359
+ Scan tool inputs for secrets, API keys, and credentials before execution.
360
+ Blocks Write/Edit calls that would commit sensitive data.
361
+ matcher: "Write|Edit|Bash"
362
+ severity: critical
363
+ settings_json_snippet: |
364
+ {
365
+ "hooks": {
366
+ "PreToolUse": [
367
+ {
368
+ "matcher": "Write|Edit|Bash",
369
+ "hooks": [
370
+ {
371
+ "type": "command",
372
+ "command": "python3 ~/.claude/hooks/security-gate.py"
373
+ }
374
+ ]
375
+ }
376
+ ]
377
+ }
378
+ }
379
+ code_example:
380
+ language: python
381
+ filename: "~/.claude/hooks/security-gate.py"
382
+ code: |
383
+ #!/usr/bin/env python3
384
+ """Security gate — detect secrets in tool inputs."""
385
+ import json
386
+ import sys
387
+ import re
388
+
389
+ SECRET_PATTERNS = [
390
+ (r'(?:api[_-]?key|apikey)\s*[:=]\s*["\']?[a-zA-Z0-9_\-]{20,}', "API Key"),
391
+ (r'(?:secret|password|passwd|pwd)\s*[:=]\s*["\']?[^\s"\']{8,}', "Password/Secret"),
392
+ (r'(?:token)\s*[:=]\s*["\']?[a-zA-Z0-9_\-\.]{20,}', "Token"),
393
+ (r'sk-[a-zA-Z0-9]{32,}', "OpenAI API Key"),
394
+ (r'ghp_[a-zA-Z0-9]{36}', "GitHub Personal Access Token"),
395
+ (r'-----BEGIN (?:RSA |EC )?PRIVATE KEY-----', "Private Key"),
396
+ (r'AKIA[0-9A-Z]{16}', "AWS Access Key"),
397
+ (r'mongodb\+srv://[^\s]+', "MongoDB Connection String"),
398
+ (r'postgres(?:ql)?://[^\s]+@[^\s]+', "PostgreSQL Connection String"),
399
+ ]
400
+
401
+ SAFE_FILES = [".env.example", ".env.template", ".env.sample"]
402
+
403
+ def main():
404
+ input_data = json.loads(sys.stdin.read())
405
+ tool_name = input_data.get("tool_name", "")
406
+ tool_input = input_data.get("tool_input", {})
407
+
408
+ content = ""
409
+ file_path = tool_input.get("file_path", "")
410
+
411
+ if tool_name in ("Write", "Edit"):
412
+ content = tool_input.get("content", "") + tool_input.get("new_string", "")
413
+ elif tool_name == "Bash":
414
+ content = tool_input.get("command", "")
415
+
416
+ # Skip safe files
417
+ if any(file_path.endswith(sf) for sf in SAFE_FILES):
418
+ print(json.dumps({"decision": "approve"}))
419
+ return
420
+
421
+ for pattern, label in SECRET_PATTERNS:
422
+ if re.search(pattern, content, re.IGNORECASE):
423
+ result = {
424
+ "decision": "block",
425
+ "reason": f"Security gate: potential {label} detected in {tool_name} input"
426
+ }
427
+ print(json.dumps(result))
428
+ return
429
+
430
+ print(json.dumps({"decision": "approve"}))
431
+
432
+ if __name__ == "__main__":
433
+ main()
434
+
435
+ # ---------------------------------------------------------------------------
436
+ # 7. TIMING LOGGER — Performance instrumentation
437
+ # ---------------------------------------------------------------------------
438
+ - name: timing-logger
439
+ event: PreToolUse
440
+ paired_event: PostToolUse
441
+ description: |
442
+ Log timestamps for every tool call to enable performance analysis.
443
+ Paired PreToolUse/PostToolUse hooks create a complete timeline.
444
+ matcher: ""
445
+ severity: low
446
+ settings_json_snippet: |
447
+ {
448
+ "hooks": {
449
+ "PreToolUse": [
450
+ {
451
+ "matcher": "",
452
+ "hooks": [
453
+ {
454
+ "type": "command",
455
+ "command": "node ~/.claude/hooks/timing-logger.js pre"
456
+ }
457
+ ]
458
+ }
459
+ ],
460
+ "PostToolUse": [
461
+ {
462
+ "matcher": "",
463
+ "hooks": [
464
+ {
465
+ "type": "command",
466
+ "command": "node ~/.claude/hooks/timing-logger.js post"
467
+ }
468
+ ]
469
+ }
470
+ ]
471
+ }
472
+ }
473
+ code_example:
474
+ language: javascript
475
+ filename: "~/.claude/hooks/timing-logger.js"
476
+ code: |
477
+ #!/usr/bin/env node
478
+ /**
479
+ * Timing logger hook - records PreToolUse/PostToolUse timestamps.
480
+ * Usage: node timing-logger.js pre|post
481
+ * Reads tool data from stdin, writes JSONL to ~/.claude/logs/timing-YYYY-MM-DD.jsonl
482
+ */
483
+ const fs = require('fs');
484
+ const path = require('path');
485
+ const os = require('os');
486
+
487
+ const phase = process.argv[2]; // 'pre' or 'post'
488
+ const LOG_DIR = path.join(os.homedir(), '.claude', 'logs');
489
+ const today = new Date().toISOString().slice(0, 10);
490
+ const logFile = path.join(LOG_DIR, `timing-${today}.jsonl`);
491
+
492
+ let input = '';
493
+ process.stdin.on('data', chunk => { input += chunk; });
494
+ process.stdin.on('end', () => {
495
+ try {
496
+ const data = JSON.parse(input);
497
+ const entry = {
498
+ timestamp: new Date().toISOString(),
499
+ epochMs: Date.now(),
500
+ event: phase === 'pre' ? 'PreToolUse' : 'PostToolUse',
501
+ tool: data.tool_name || 'unknown',
502
+ session: data.session_id || 'unknown',
503
+ };
504
+ if (phase === 'post' && data.duration_ms) {
505
+ entry.durationMs = data.duration_ms;
506
+ }
507
+ fs.mkdirSync(LOG_DIR, { recursive: true });
508
+ fs.appendFileSync(logFile, JSON.stringify(entry) + '\n');
509
+ } catch (e) {
510
+ // Non-blocking — do not fail the tool call
511
+ }
512
+ });