@paths.design/caws-cli 8.0.1 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/commands/archive.d.ts +2 -1
  2. package/dist/commands/archive.d.ts.map +1 -1
  3. package/dist/commands/archive.js +114 -6
  4. package/dist/commands/burnup.d.ts.map +1 -1
  5. package/dist/commands/burnup.js +109 -10
  6. package/dist/commands/diagnose.js +1 -1
  7. package/dist/commands/mode.js +24 -14
  8. package/dist/commands/provenance.js +216 -93
  9. package/dist/commands/quality-gates.d.ts.map +1 -1
  10. package/dist/commands/quality-gates.js +3 -1
  11. package/dist/commands/specs.js +184 -6
  12. package/dist/commands/status.d.ts.map +1 -1
  13. package/dist/commands/status.js +134 -10
  14. package/dist/commands/templates.js +2 -2
  15. package/dist/error-handler.js +6 -98
  16. package/dist/generators/jest-config-generator.js +242 -0
  17. package/dist/index.js +4 -7
  18. package/dist/minimal-cli.js +3 -1
  19. package/dist/scaffold/claude-hooks.js +316 -0
  20. package/dist/scaffold/index.js +18 -0
  21. package/dist/templates/.claude/README.md +190 -0
  22. package/dist/templates/.claude/hooks/audit.sh +96 -0
  23. package/dist/templates/.claude/hooks/block-dangerous.sh +90 -0
  24. package/dist/templates/.claude/hooks/naming-check.sh +97 -0
  25. package/dist/templates/.claude/hooks/quality-check.sh +68 -0
  26. package/dist/templates/.claude/hooks/scan-secrets.sh +85 -0
  27. package/dist/templates/.claude/hooks/scope-guard.sh +105 -0
  28. package/dist/templates/.claude/hooks/validate-spec.sh +76 -0
  29. package/dist/templates/.claude/settings.json +95 -0
  30. package/dist/test-analysis.js +203 -10
  31. package/dist/utils/error-categories.js +210 -0
  32. package/dist/utils/quality-gates-utils.js +402 -0
  33. package/dist/utils/typescript-detector.js +36 -90
  34. package/dist/validation/spec-validation.js +59 -6
  35. package/package.json +5 -3
  36. package/templates/.claude/README.md +190 -0
  37. package/templates/.claude/hooks/audit.sh +96 -0
  38. package/templates/.claude/hooks/block-dangerous.sh +90 -0
  39. package/templates/.claude/hooks/naming-check.sh +97 -0
  40. package/templates/.claude/hooks/quality-check.sh +68 -0
  41. package/templates/.claude/hooks/scan-secrets.sh +85 -0
  42. package/templates/.claude/hooks/scope-guard.sh +105 -0
  43. package/templates/.claude/hooks/validate-spec.sh +76 -0
  44. package/templates/.claude/settings.json +95 -0
@@ -0,0 +1,316 @@
1
+ /**
2
+ * @fileoverview Claude Code Hooks Scaffolding
3
+ * Functions for setting up Claude Code hooks for CAWS projects
4
+ * @author @darianrosebrook
5
+ */
6
+
7
+ const fs = require('fs-extra');
8
+ const path = require('path');
9
+ const chalk = require('chalk');
10
+
11
+ // Import detection utilities
12
+ const { detectCAWSSetup, findPackageRoot } = require('../utils/detection');
13
+
14
+ /**
15
+ * Scaffold Claude Code hooks for a CAWS project
16
+ * Creates .claude/settings.json with hooks and .claude/hooks/ directory with scripts
17
+ *
18
+ * @param {string} projectDir - Project directory path
19
+ * @param {string[]} levels - Hook levels to enable: 'safety', 'quality', 'scope', 'audit'
20
+ */
21
+ async function scaffoldClaudeHooks(projectDir, levels = ['safety', 'quality', 'scope', 'audit']) {
22
+ try {
23
+ const claudeDir = path.join(projectDir, '.claude');
24
+ const claudeHooksDir = path.join(claudeDir, 'hooks');
25
+
26
+ // Create .claude directory structure
27
+ await fs.ensureDir(claudeDir);
28
+ await fs.ensureDir(claudeHooksDir);
29
+
30
+ // Determine template directory - prefer bundled templates
31
+ const setup = detectCAWSSetup(projectDir);
32
+
33
+ // Find package root using shared utility
34
+ const packageRoot = findPackageRoot(__dirname);
35
+
36
+ // Try templates relative to package root first (works in both dev and global install)
37
+ const bundledTemplateDir = path.join(packageRoot, 'templates');
38
+ const fallbackTemplateDir = path.join(__dirname, '../../templates');
39
+ const templateDir = fs.existsSync(bundledTemplateDir)
40
+ ? bundledTemplateDir
41
+ : fs.existsSync(fallbackTemplateDir)
42
+ ? fallbackTemplateDir
43
+ : setup.templateDir || path.resolve(__dirname, '../templates');
44
+
45
+ const claudeTemplateDir = path.join(templateDir, '.claude');
46
+ const claudeHooksTemplateDir = path.join(claudeTemplateDir, 'hooks');
47
+
48
+ if (!fs.existsSync(claudeTemplateDir)) {
49
+ console.warn(chalk.yellow('⚠️ Claude Code hooks templates not found'));
50
+ console.warn(chalk.blue('💡 Skipping Claude Code hooks setup'));
51
+ return;
52
+ }
53
+
54
+ // Map levels to hook scripts
55
+ const hookMapping = {
56
+ safety: ['block-dangerous.sh', 'scan-secrets.sh'],
57
+ quality: ['quality-check.sh', 'validate-spec.sh'],
58
+ scope: ['scope-guard.sh', 'naming-check.sh'],
59
+ audit: ['audit.sh'],
60
+ };
61
+
62
+ // Determine which hooks to enable
63
+ const enabledHooks = new Set();
64
+ levels.forEach((level) => {
65
+ const hooks = hookMapping[level] || [];
66
+ hooks.forEach((hook) => enabledHooks.add(hook));
67
+ });
68
+
69
+ // Always enable audit.sh if any hooks are enabled
70
+ if (enabledHooks.size > 0) {
71
+ enabledHooks.add('audit.sh');
72
+ }
73
+
74
+ // Copy enabled hook scripts
75
+ const allHookScripts = [
76
+ 'audit.sh',
77
+ 'validate-spec.sh',
78
+ 'quality-check.sh',
79
+ 'scan-secrets.sh',
80
+ 'block-dangerous.sh',
81
+ 'scope-guard.sh',
82
+ 'naming-check.sh',
83
+ ];
84
+
85
+ for (const script of allHookScripts) {
86
+ if (enabledHooks.has(script)) {
87
+ const sourcePath = path.join(claudeHooksTemplateDir, script);
88
+ const destPath = path.join(claudeHooksDir, script);
89
+
90
+ if (fs.existsSync(sourcePath)) {
91
+ await fs.copy(sourcePath, destPath);
92
+ // Make executable
93
+ await fs.chmod(destPath, 0o755);
94
+ }
95
+ }
96
+ }
97
+
98
+ // Generate settings.json with hooks configuration
99
+ const settings = generateClaudeSettings(levels, enabledHooks);
100
+
101
+ // Check for existing settings and merge
102
+ const settingsPath = path.join(claudeDir, 'settings.json');
103
+ if (fs.existsSync(settingsPath)) {
104
+ try {
105
+ const existingSettings = await fs.readJSON(settingsPath);
106
+ // Merge hooks, preserving existing non-hook settings
107
+ settings.hooks = {
108
+ ...existingSettings.hooks,
109
+ ...settings.hooks,
110
+ };
111
+ // Preserve other settings
112
+ Object.keys(existingSettings).forEach((key) => {
113
+ if (key !== 'hooks' && !(key in settings)) {
114
+ settings[key] = existingSettings[key];
115
+ }
116
+ });
117
+ } catch (error) {
118
+ console.warn(chalk.yellow('⚠️ Could not merge existing settings:'), error.message);
119
+ }
120
+ }
121
+
122
+ // Write settings.json
123
+ await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
124
+
125
+ // Copy README if it exists
126
+ const readmePath = path.join(claudeTemplateDir, 'README.md');
127
+ if (fs.existsSync(readmePath)) {
128
+ await fs.copy(readmePath, path.join(claudeDir, 'README.md'));
129
+ }
130
+
131
+ console.log(chalk.green('✅ Claude Code hooks configured'));
132
+ console.log(chalk.gray(` Enabled: ${levels.join(', ')}`));
133
+ console.log(
134
+ chalk.gray(` Scripts: ${Array.from(enabledHooks).length} hook scripts installed`)
135
+ );
136
+ console.log(chalk.blue('💡 Hooks will activate on next Claude Code session'));
137
+ } catch (error) {
138
+ console.error(chalk.yellow('⚠️ Failed to setup Claude Code hooks:'), error.message);
139
+ console.log(chalk.blue('💡 You can manually copy .claude/ directory later'));
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Generate Claude Code settings with hooks configuration
145
+ * @param {string[]} levels - Enabled hook levels
146
+ * @param {Set<string>} enabledHooks - Set of enabled hook script names
147
+ * @returns {Object} Settings object for settings.json
148
+ */
149
+ function generateClaudeSettings(levels, _enabledHooks) {
150
+ const settings = {
151
+ hooks: {},
152
+ };
153
+
154
+ // Build hooks configuration based on enabled levels
155
+ // Claude Code uses different event names and matcher patterns
156
+
157
+ if (levels.includes('safety')) {
158
+ // Block dangerous bash commands
159
+ settings.hooks.PreToolUse = settings.hooks.PreToolUse || [];
160
+ settings.hooks.PreToolUse.push({
161
+ matcher: 'Bash',
162
+ hooks: [
163
+ {
164
+ type: 'command',
165
+ command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/block-dangerous.sh',
166
+ timeout: 10,
167
+ },
168
+ ],
169
+ });
170
+
171
+ // Scan for secrets on file read
172
+ settings.hooks.PreToolUse.push({
173
+ matcher: 'Read',
174
+ hooks: [
175
+ {
176
+ type: 'command',
177
+ command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/scan-secrets.sh',
178
+ timeout: 10,
179
+ },
180
+ ],
181
+ });
182
+ }
183
+
184
+ if (levels.includes('quality')) {
185
+ // Run quality checks after file edits
186
+ settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
187
+ settings.hooks.PostToolUse.push({
188
+ matcher: 'Write|Edit',
189
+ hooks: [
190
+ {
191
+ type: 'command',
192
+ command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/quality-check.sh',
193
+ timeout: 30,
194
+ },
195
+ {
196
+ type: 'command',
197
+ command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/validate-spec.sh',
198
+ timeout: 15,
199
+ },
200
+ ],
201
+ });
202
+ }
203
+
204
+ if (levels.includes('scope')) {
205
+ // Scope guard before file writes
206
+ settings.hooks.PreToolUse = settings.hooks.PreToolUse || [];
207
+ settings.hooks.PreToolUse.push({
208
+ matcher: 'Write|Edit',
209
+ hooks: [
210
+ {
211
+ type: 'command',
212
+ command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/scope-guard.sh',
213
+ timeout: 10,
214
+ },
215
+ ],
216
+ });
217
+
218
+ // Naming check after edits
219
+ settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
220
+ settings.hooks.PostToolUse.push({
221
+ matcher: 'Write|Edit',
222
+ hooks: [
223
+ {
224
+ type: 'command',
225
+ command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/naming-check.sh',
226
+ timeout: 10,
227
+ },
228
+ ],
229
+ });
230
+ }
231
+
232
+ if (levels.includes('audit')) {
233
+ // Session audit logging
234
+ settings.hooks.SessionStart = settings.hooks.SessionStart || [];
235
+ settings.hooks.SessionStart.push({
236
+ hooks: [
237
+ {
238
+ type: 'command',
239
+ command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/audit.sh session-start',
240
+ timeout: 5,
241
+ },
242
+ ],
243
+ });
244
+
245
+ settings.hooks.Stop = settings.hooks.Stop || [];
246
+ settings.hooks.Stop.push({
247
+ hooks: [
248
+ {
249
+ type: 'command',
250
+ command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/audit.sh stop',
251
+ timeout: 5,
252
+ },
253
+ ],
254
+ });
255
+
256
+ // Audit tool usage
257
+ settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
258
+ settings.hooks.PostToolUse.push({
259
+ matcher: 'Write|Edit|Bash',
260
+ hooks: [
261
+ {
262
+ type: 'command',
263
+ command: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/audit.sh tool-use',
264
+ timeout: 5,
265
+ },
266
+ ],
267
+ });
268
+ }
269
+
270
+ return settings;
271
+ }
272
+
273
+ /**
274
+ * Check if Claude Code hooks are already configured
275
+ * @param {string} projectDir - Project directory
276
+ * @returns {boolean} True if hooks are configured
277
+ */
278
+ function hasClaudeHooks(projectDir) {
279
+ const settingsPath = path.join(projectDir, '.claude', 'settings.json');
280
+ if (!fs.existsSync(settingsPath)) {
281
+ return false;
282
+ }
283
+
284
+ try {
285
+ const settings = fs.readJSONSync(settingsPath);
286
+ return settings.hooks && Object.keys(settings.hooks).length > 0;
287
+ } catch {
288
+ return false;
289
+ }
290
+ }
291
+
292
+ /**
293
+ * List configured Claude Code hooks
294
+ * @param {string} projectDir - Project directory
295
+ * @returns {Object} Hook configuration or null
296
+ */
297
+ function getClaudeHooksConfig(projectDir) {
298
+ const settingsPath = path.join(projectDir, '.claude', 'settings.json');
299
+ if (!fs.existsSync(settingsPath)) {
300
+ return null;
301
+ }
302
+
303
+ try {
304
+ const settings = fs.readJSONSync(settingsPath);
305
+ return settings.hooks || null;
306
+ } catch {
307
+ return null;
308
+ }
309
+ }
310
+
311
+ module.exports = {
312
+ scaffoldClaudeHooks,
313
+ generateClaudeSettings,
314
+ hasClaudeHooks,
315
+ getClaudeHooksConfig,
316
+ };
@@ -16,6 +16,9 @@ const { detectsPublishing } = require('../utils/project-analysis');
16
16
  const { scaffoldGitHooks } = require('./git-hooks');
17
17
  const { updateGitignore } = require('../utils/gitignore-updater');
18
18
 
19
+ // Import Claude Code hooks scaffolding
20
+ const { scaffoldClaudeHooks } = require('./claude-hooks');
21
+
19
22
  // CLI version from package.json
20
23
  const CLI_VERSION = require('../../package.json').version;
21
24
 
@@ -120,8 +123,22 @@ async function scaffoldIDEIntegrations(targetDir, options) {
120
123
  dest: '.cursor/README.md',
121
124
  desc: 'Cursor integration documentation',
122
125
  },
126
+
127
+ // Claude Code hooks
128
+ {
129
+ src: '.claude/README.md',
130
+ dest: '.claude/README.md',
131
+ desc: 'Claude Code integration documentation',
132
+ },
123
133
  ];
124
134
 
135
+ // Setup Claude Code hooks
136
+ try {
137
+ await scaffoldClaudeHooks(targetDir, ['safety', 'quality', 'scope', 'audit']);
138
+ } catch (error) {
139
+ console.log(chalk.yellow(`⚠️ Claude Code hooks setup failed: ${error.message}`));
140
+ }
141
+
125
142
  for (const template of ideTemplates) {
126
143
  const srcPath = path.join(templateDir, template.src);
127
144
  const destPath = path.join(targetDir, template.dest);
@@ -769,5 +786,6 @@ async function scaffoldProject(options) {
769
786
  module.exports = {
770
787
  scaffoldProject,
771
788
  scaffoldIDEIntegrations,
789
+ scaffoldClaudeHooks,
772
790
  setScaffoldDependencies,
773
791
  };
@@ -0,0 +1,190 @@
1
+ # Claude Code Integration for CAWS
2
+
3
+ This directory contains Claude Code hooks and configuration for CAWS (Coding Agent Working Standard) integration.
4
+
5
+ ## Overview
6
+
7
+ CAWS hooks for Claude Code provide:
8
+
9
+ - **Safety Gates**: Block dangerous commands and scan for secrets
10
+ - **Quality Gates**: Run CAWS quality checks after file edits
11
+ - **Scope Guards**: Validate edits against the working spec's scope
12
+ - **Audit Logging**: Track agent actions for compliance
13
+
14
+ ## Directory Structure
15
+
16
+ ```
17
+ .claude/
18
+ ├── settings.json # Claude Code settings with hooks configuration
19
+ ├── hooks/ # Hook scripts
20
+ │ ├── audit.sh # Session and action logging
21
+ │ ├── block-dangerous.sh # Block destructive commands
22
+ │ ├── scan-secrets.sh # Warn when reading sensitive files
23
+ │ ├── quality-check.sh # Run CAWS quality gates
24
+ │ ├── validate-spec.sh # Validate spec files
25
+ │ ├── scope-guard.sh # Check scope boundaries
26
+ │ └── naming-check.sh # Validate file naming conventions
27
+ ├── logs/ # Audit logs (gitignored)
28
+ └── README.md # This file
29
+ ```
30
+
31
+ ## Hook Events
32
+
33
+ ### PreToolUse Hooks
34
+
35
+ Run before Claude executes a tool:
36
+
37
+ | Hook | Matcher | Purpose |
38
+ |------|---------|---------|
39
+ | `block-dangerous.sh` | `Bash` | Block destructive shell commands |
40
+ | `scan-secrets.sh` | `Read` | Warn when reading sensitive files |
41
+ | `scope-guard.sh` | `Write\|Edit` | Check scope boundaries before edits |
42
+
43
+ ### PostToolUse Hooks
44
+
45
+ Run after Claude executes a tool:
46
+
47
+ | Hook | Matcher | Purpose |
48
+ |------|---------|---------|
49
+ | `quality-check.sh` | `Write\|Edit` | Run CAWS quality gates |
50
+ | `validate-spec.sh` | `Write\|Edit` | Validate spec file changes |
51
+ | `naming-check.sh` | `Write` | Check file naming conventions |
52
+ | `audit.sh` | `Write\|Edit\|Bash` | Log tool usage |
53
+
54
+ ### Session Hooks
55
+
56
+ | Hook | Event | Purpose |
57
+ |------|-------|---------|
58
+ | `audit.sh session-start` | `SessionStart` | Log session start |
59
+ | `audit.sh stop` | `Stop` | Log session end |
60
+
61
+ ## Configuration
62
+
63
+ ### Enable/Disable Hooks
64
+
65
+ Edit `settings.json` to enable or disable specific hooks. Remove entries from the `hooks` object to disable them.
66
+
67
+ ### Hook Levels
68
+
69
+ The scaffold supports four hook levels:
70
+
71
+ - **safety**: Block dangerous commands, scan for secrets
72
+ - **quality**: Run quality gates on file edits
73
+ - **scope**: Validate edits against spec scope
74
+ - **audit**: Log all agent actions
75
+
76
+ Run `caws init --hooks=safety,quality` to enable specific levels.
77
+
78
+ ## Audit Logs
79
+
80
+ Audit logs are written to `.claude/logs/`:
81
+
82
+ - `audit.log` - All-time log (appended)
83
+ - `audit-YYYY-MM-DD.log` - Daily logs
84
+
85
+ Logs are JSON-formatted for easy parsing:
86
+
87
+ ```json
88
+ {
89
+ "timestamp": "2024-01-15T10:30:00Z",
90
+ "session_id": "abc123",
91
+ "event": "tool_use",
92
+ "tool": "Write",
93
+ "file": "src/index.ts",
94
+ "cwd": "/project"
95
+ }
96
+ ```
97
+
98
+ ## Customization
99
+
100
+ ### Adding Custom Hooks
101
+
102
+ 1. Create a new script in `.claude/hooks/`
103
+ 2. Make it executable: `chmod +x .claude/hooks/my-hook.sh`
104
+ 3. Add it to `settings.json`:
105
+
106
+ ```json
107
+ {
108
+ "hooks": {
109
+ "PostToolUse": [
110
+ {
111
+ "matcher": "Write|Edit",
112
+ "hooks": [
113
+ {
114
+ "type": "command",
115
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/my-hook.sh",
116
+ "timeout": 10
117
+ }
118
+ ]
119
+ }
120
+ ]
121
+ }
122
+ }
123
+ ```
124
+
125
+ ### Hook Input/Output
126
+
127
+ Hooks receive JSON input via stdin:
128
+
129
+ ```json
130
+ {
131
+ "session_id": "abc123",
132
+ "hook_event_name": "PostToolUse",
133
+ "tool_name": "Write",
134
+ "tool_input": {
135
+ "file_path": "/path/to/file.ts",
136
+ "content": "..."
137
+ },
138
+ "tool_response": { "success": true }
139
+ }
140
+ ```
141
+
142
+ Hooks can output JSON to control Claude's behavior:
143
+
144
+ ```json
145
+ {
146
+ "decision": "block",
147
+ "reason": "Quality gate failed: ..."
148
+ }
149
+ ```
150
+
151
+ Or add context:
152
+
153
+ ```json
154
+ {
155
+ "hookSpecificOutput": {
156
+ "hookEventName": "PostToolUse",
157
+ "additionalContext": "Remember to update the tests."
158
+ }
159
+ }
160
+ ```
161
+
162
+ ## Troubleshooting
163
+
164
+ ### Hooks Not Running
165
+
166
+ 1. Check `settings.json` syntax: `cat .claude/settings.json | jq .`
167
+ 2. Verify scripts are executable: `ls -la .claude/hooks/`
168
+ 3. Test hooks manually: `echo '{}' | .claude/hooks/audit.sh`
169
+
170
+ ### Permission Errors
171
+
172
+ Make all hook scripts executable:
173
+
174
+ ```bash
175
+ chmod +x .claude/hooks/*.sh
176
+ ```
177
+
178
+ ### Debug Hooks
179
+
180
+ Run Claude Code with `--debug` to see hook execution details:
181
+
182
+ ```bash
183
+ claude --debug
184
+ ```
185
+
186
+ ## Further Reading
187
+
188
+ - [Claude Code Hooks Documentation](https://code.claude.com/docs/en/hooks)
189
+ - [CAWS Quality Gates](../../docs/quality-gates.md)
190
+ - [CAWS Scope Management](../../docs/scope-management.md)
@@ -0,0 +1,96 @@
1
+ #!/bin/bash
2
+ # CAWS Audit Hook for Claude Code
3
+ # Logs agent actions for compliance and debugging
4
+ # @author @darianrosebrook
5
+
6
+ set -euo pipefail
7
+
8
+ # Get event type from argument or input
9
+ EVENT_TYPE="${1:-tool-use}"
10
+
11
+ # Read JSON input from stdin
12
+ INPUT=$(cat)
13
+
14
+ # Parse common fields from Claude Code hook input
15
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
16
+ CWD=$(echo "$INPUT" | jq -r '.cwd // "."')
17
+ HOOK_EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // "unknown"')
18
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
19
+ PERMISSION_MODE=$(echo "$INPUT" | jq -r '.permission_mode // "default"')
20
+
21
+ # Ensure log directory exists
22
+ LOG_DIR="${CLAUDE_PROJECT_DIR:-.}/.claude/logs"
23
+ mkdir -p "$LOG_DIR"
24
+
25
+ # Log file path
26
+ LOG_FILE="$LOG_DIR/audit.log"
27
+ DATE_LOG_FILE="$LOG_DIR/audit-$(date +%Y-%m-%d).log"
28
+
29
+ # Timestamp
30
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
31
+
32
+ # Build log entry based on event type
33
+ case "$EVENT_TYPE" in
34
+ session-start)
35
+ SOURCE=$(echo "$INPUT" | jq -r '.source // "unknown"')
36
+ MODEL=$(echo "$INPUT" | jq -r '.model // "unknown"')
37
+ LOG_ENTRY=$(jq -n \
38
+ --arg ts "$TIMESTAMP" \
39
+ --arg sid "$SESSION_ID" \
40
+ --arg event "session_start" \
41
+ --arg source "$SOURCE" \
42
+ --arg model "$MODEL" \
43
+ --arg cwd "$CWD" \
44
+ '{timestamp: $ts, session_id: $sid, event: $event, source: $source, model: $model, cwd: $cwd}')
45
+ ;;
46
+
47
+ stop)
48
+ STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
49
+ LOG_ENTRY=$(jq -n \
50
+ --arg ts "$TIMESTAMP" \
51
+ --arg sid "$SESSION_ID" \
52
+ --arg event "session_stop" \
53
+ --arg cwd "$CWD" \
54
+ --argjson hook_active "$STOP_HOOK_ACTIVE" \
55
+ '{timestamp: $ts, session_id: $sid, event: $event, cwd: $cwd, stop_hook_active: $hook_active}')
56
+ ;;
57
+
58
+ tool-use)
59
+ # Extract tool-specific info
60
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')
61
+ TOOL_RESPONSE=$(echo "$INPUT" | jq -c '.tool_response // {}')
62
+ TOOL_USE_ID=$(echo "$INPUT" | jq -r '.tool_use_id // ""')
63
+
64
+ # For file operations, extract the path
65
+ FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // ""')
66
+ COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // ""')
67
+
68
+ LOG_ENTRY=$(jq -n \
69
+ --arg ts "$TIMESTAMP" \
70
+ --arg sid "$SESSION_ID" \
71
+ --arg event "tool_use" \
72
+ --arg tool "$TOOL_NAME" \
73
+ --arg file "$FILE_PATH" \
74
+ --arg cmd "$COMMAND" \
75
+ --arg cwd "$CWD" \
76
+ --arg mode "$PERMISSION_MODE" \
77
+ '{timestamp: $ts, session_id: $sid, event: $event, tool: $tool, file: $file, command: $cmd, cwd: $cwd, permission_mode: $mode}')
78
+ ;;
79
+
80
+ *)
81
+ LOG_ENTRY=$(jq -n \
82
+ --arg ts "$TIMESTAMP" \
83
+ --arg sid "$SESSION_ID" \
84
+ --arg event "$EVENT_TYPE" \
85
+ --arg hook "$HOOK_EVENT" \
86
+ --arg cwd "$CWD" \
87
+ '{timestamp: $ts, session_id: $sid, event: $event, hook_event: $hook, cwd: $cwd}')
88
+ ;;
89
+ esac
90
+
91
+ # Append to log files
92
+ echo "$LOG_ENTRY" >> "$LOG_FILE"
93
+ echo "$LOG_ENTRY" >> "$DATE_LOG_FILE"
94
+
95
+ # Success - allow operation to continue
96
+ exit 0