oh-my-customcode 0.36.1 → 0.36.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-customcode",
3
- "version": "0.36.1",
3
+ "version": "0.36.2",
4
4
  "description": "Batteries-included agent harness for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -72,6 +72,16 @@
72
72
  ],
73
73
  "description": "Validate file content hash before Edit — advisory staleness warning"
74
74
  },
75
+ {
76
+ "matcher": "tool == \"Write\" || tool == \"Edit\" || tool == \"Bash\"",
77
+ "hooks": [
78
+ {
79
+ "type": "command",
80
+ "command": "bash .claude/hooks/scripts/schema-validator.sh"
81
+ }
82
+ ],
83
+ "description": "Schema-based tool input validation — Phase 1 advisory only"
84
+ },
75
85
  {
76
86
  "matcher": "tool == \"Task\" || tool == \"Agent\"",
77
87
  "hooks": [
@@ -222,6 +232,26 @@
222
232
  ],
223
233
  "description": "Context budget advisor — track tool usage patterns and advise ecomode activation"
224
234
  },
235
+ {
236
+ "matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Task\" || tool == \"Agent\"",
237
+ "hooks": [
238
+ {
239
+ "type": "command",
240
+ "command": "bash .claude/hooks/scripts/stuck-detector.sh"
241
+ }
242
+ ],
243
+ "description": "Detect repetitive failure loops and advise recovery strategies"
244
+ },
245
+ {
246
+ "matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Task\" || tool == \"Agent\"",
247
+ "hooks": [
248
+ {
249
+ "type": "command",
250
+ "command": "bash .claude/hooks/scripts/cost-cap-advisor.sh"
251
+ }
252
+ ],
253
+ "description": "Advisory cost cap monitoring — warn when session cost approaches configurable limit"
254
+ },
225
255
  {
226
256
  "matcher": "tool == \"Read\"",
227
257
  "hooks": [
@@ -233,24 +263,24 @@
233
263
  "description": "Store content hashes for Read operations — enables Edit staleness detection"
234
264
  },
235
265
  {
236
- "matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Task\" || tool == \"Agent\"",
266
+ "matcher": "tool == \"Bash\" || tool == \"Read\"",
237
267
  "hooks": [
238
268
  {
239
269
  "type": "command",
240
- "command": "bash .claude/hooks/scripts/stuck-detector.sh"
270
+ "command": "bash .claude/hooks/scripts/secret-filter.sh"
241
271
  }
242
272
  ],
243
- "description": "Detect repetitive failure loops and advise recovery strategies"
273
+ "description": "Detect potential secrets in Bash/Read output advisory warning only"
244
274
  },
245
275
  {
246
- "matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Task\" || tool == \"Agent\"",
276
+ "matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Agent\"",
247
277
  "hooks": [
248
278
  {
249
279
  "type": "command",
250
- "command": "bash .claude/hooks/scripts/cost-cap-advisor.sh"
280
+ "command": "bash .claude/hooks/scripts/audit-log.sh"
251
281
  }
252
282
  ],
253
- "description": "Advisory cost cap monitoring warn when session cost approaches configurable limit"
283
+ "description": "Append-only audit log for state-changing tool operations"
254
284
  }
255
285
  ],
256
286
  "Stop": [
@@ -269,7 +299,7 @@
269
299
  "hooks": [
270
300
  {
271
301
  "type": "prompt",
272
- "prompt": "Session-end memory checkpoint (R011 enforcement). Check conversation history for these 3 steps: 1) sys-memory-keeper was delegated to update MEMORY.md 2) claude-mem save was attempted via ToolSearch + mcp__plugin_claude-mem_mcp-search__save_memory 3) episodic-memory verification was attempted via ToolSearch + mcp__plugin_episodic-memory_episodic-memory__search. Decision rules: If ALL 3 were attempted (success or failure both count): approve. If MCP tools are unavailable after ToolSearch attempt: approve with note. If session had no explicit session-end signal from user (quick question, no memory work): approve. If any step was NOT attempted despite user signaling session end: block with systemMessage listing the missing steps."
302
+ "prompt": "Session-end memory checkpoint (R011 enforcement). Check conversation history for these 2 steps: 1) sys-memory-keeper was delegated to update MEMORY.md 2) claude-mem save was attempted via ToolSearch + mcp__plugin_claude-mem_mcp-search__save_memory. Note: episodic-memory auto-indexes after session no manual verification needed. Decision rules: If BOTH were attempted (success or failure both count): approve. If MCP tools are unavailable after ToolSearch attempt: approve with note. If session had no explicit session-end signal from user (quick question, no memory work): approve. If any step was NOT attempted despite user signaling session end: block with systemMessage listing the missing steps."
273
303
  }
274
304
  ],
275
305
  "description": "Enforce R011 session-end memory saves — block stop if claude-mem or episodic-memory saves were skipped"
@@ -0,0 +1,55 @@
1
+ #!/bin/bash
2
+ # Audit Log Hook — Append-only JSONL persistence
3
+ # Trigger: PostToolUse on Edit, Write, Bash, Agent
4
+ # Purpose: Persistent audit trail for security and compliance
5
+ # Protocol: stdin JSON -> log entry -> stdout pass-through
6
+ # Always exits 0 (advisory only)
7
+
8
+ set -euo pipefail
9
+
10
+ input=$(cat)
11
+
12
+ # Extract fields from hook input
13
+ tool_name=$(echo "$input" | jq -r '.tool_name // "unknown"')
14
+ file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.command // ""' | head -c 200)
15
+ agent_type=$(echo "$input" | jq -r '.agent_type // "unknown"')
16
+ model=$(echo "$input" | jq -r '.model // "unknown"')
17
+ is_error=$(echo "$input" | jq -r '.tool_output.is_error // false')
18
+ timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
19
+
20
+ # Determine outcome
21
+ if [ "$is_error" = "true" ]; then
22
+ outcome="error"
23
+ else
24
+ outcome="success"
25
+ fi
26
+
27
+ # Audit log location
28
+ AUDIT_LOG="${HOME}/.claude/audit.jsonl"
29
+
30
+ # Ensure directory exists
31
+ mkdir -p "$(dirname "$AUDIT_LOG")"
32
+
33
+ # Write audit entry (append-only JSONL)
34
+ jq -cn \
35
+ --arg ts "$timestamp" \
36
+ --arg tool "$tool_name" \
37
+ --arg path "$file_path" \
38
+ --arg agent "$agent_type" \
39
+ --arg model "$model" \
40
+ --arg outcome "$outcome" \
41
+ --arg ppid "${PPID}" \
42
+ '{timestamp: $ts, tool: $tool, path: $path, agent_type: $agent, model: $model, outcome: $outcome, session_ppid: $ppid}' \
43
+ >> "$AUDIT_LOG" 2>/dev/null || true
44
+
45
+ # Daily rotation check (rotate if > 10MB)
46
+ if [ -f "$AUDIT_LOG" ]; then
47
+ file_size=$(stat -f%z "$AUDIT_LOG" 2>/dev/null || stat -c%s "$AUDIT_LOG" 2>/dev/null || echo "0")
48
+ if [ "$file_size" -gt 10485760 ]; then
49
+ mv "$AUDIT_LOG" "${AUDIT_LOG}.$(date -u +%Y%m%d%H%M%S)" 2>/dev/null || true
50
+ fi
51
+ fi
52
+
53
+ # Pass through
54
+ echo "$input"
55
+ exit 0
@@ -0,0 +1,88 @@
1
+ #!/bin/bash
2
+ # Schema Validator Hook — PreToolUse input validation
3
+ # Trigger: PreToolUse on Write, Edit, Bash
4
+ # Purpose: Validate tool inputs against JSON Schema definitions
5
+ # Phase 1: Advisory only (exit 0 with stderr warning)
6
+ # Protocol: stdin JSON -> validate -> stdout pass-through
7
+
8
+ set -euo pipefail
9
+
10
+ input=$(cat)
11
+
12
+ # Extract tool info
13
+ tool_name=$(echo "$input" | jq -r '.tool_name // "unknown"')
14
+ tool_input=$(echo "$input" | jq -r '.tool_input // {}')
15
+
16
+ SCHEMA_FILE=".claude/schemas/tool-inputs.json"
17
+
18
+ # Skip if schema file doesn't exist
19
+ if [ ! -f "$SCHEMA_FILE" ]; then
20
+ echo "$input"
21
+ exit 0
22
+ fi
23
+
24
+ warnings=()
25
+
26
+ case "$tool_name" in
27
+ "Write")
28
+ file_path=$(echo "$tool_input" | jq -r '.file_path // ""')
29
+ content=$(echo "$tool_input" | jq -r '.content // ""')
30
+
31
+ if [ -z "$file_path" ]; then
32
+ warnings+=("[Schema] Write: file_path is empty or missing")
33
+ fi
34
+ if [ -z "$content" ]; then
35
+ warnings+=("[Schema] Write: content is empty — creating empty file?")
36
+ fi
37
+ ;;
38
+
39
+ "Edit")
40
+ file_path=$(echo "$tool_input" | jq -r '.file_path // ""')
41
+ old_string=$(echo "$tool_input" | jq -r '.old_string // ""')
42
+ new_string=$(echo "$tool_input" | jq -r '.new_string // ""')
43
+
44
+ if [ -z "$file_path" ]; then
45
+ warnings+=("[Schema] Edit: file_path is empty or missing")
46
+ fi
47
+ if [ -z "$old_string" ]; then
48
+ warnings+=("[Schema] Edit: old_string is empty")
49
+ fi
50
+ if [ "$old_string" = "$new_string" ]; then
51
+ warnings+=("[Schema] Edit: old_string equals new_string — no-op edit")
52
+ fi
53
+ ;;
54
+
55
+ "Bash")
56
+ command=$(echo "$tool_input" | jq -r '.command // ""')
57
+
58
+ if [ -z "$command" ]; then
59
+ warnings+=("[Schema] Bash: command is empty")
60
+ fi
61
+
62
+ # Check dangerous patterns
63
+ if echo "$command" | grep -qE 'rm\s+-rf\s+/[^.]'; then
64
+ warnings+=("[Schema] Bash: DANGER — recursive delete from root detected")
65
+ fi
66
+ if echo "$command" | grep -qE '^\s*sudo\s+'; then
67
+ warnings+=("[Schema] Bash: elevated privilege command detected")
68
+ fi
69
+ if echo "$command" | grep -qE '> /dev/sd'; then
70
+ warnings+=("[Schema] Bash: direct disk write detected")
71
+ fi
72
+ if echo "$command" | grep -qE 'mkfs\.'; then
73
+ warnings+=("[Schema] Bash: filesystem format command detected")
74
+ fi
75
+ ;;
76
+ esac
77
+
78
+ # Output warnings (advisory only)
79
+ if [ ${#warnings[@]} -gt 0 ]; then
80
+ for w in "${warnings[@]}"; do
81
+ echo "$w" >&2
82
+ done
83
+ echo "[Schema] Phase 1: advisory only — not blocking" >&2
84
+ fi
85
+
86
+ # Always pass through (Phase 1)
87
+ echo "$input"
88
+ exit 0
@@ -0,0 +1,67 @@
1
+ #!/bin/bash
2
+ # Secret Output Filter Hook — Detect potential secrets in tool output
3
+ # Trigger: PostToolUse on Bash, Read
4
+ # Purpose: Advisory warning when potential secrets detected in output
5
+ # Protocol: stdin JSON -> scan -> stdout pass-through
6
+ # Always exits 0 (advisory only, never blocks)
7
+
8
+ set -euo pipefail
9
+
10
+ input=$(cat)
11
+
12
+ # Extract output to scan
13
+ tool_name=$(echo "$input" | jq -r '.tool_name // "unknown"')
14
+ output=$(echo "$input" | jq -r '.tool_output.output // ""')
15
+
16
+ # Skip if no output
17
+ if [ -z "$output" ] || [ "$output" = "null" ]; then
18
+ echo "$input"
19
+ exit 0
20
+ fi
21
+
22
+ # Secret patterns to detect
23
+ detected=false
24
+
25
+ # AWS Access Key ID
26
+ if echo "$output" | grep -qE 'AKIA[0-9A-Z]{16}'; then
27
+ echo "[Security] Potential AWS Access Key detected in ${tool_name} output" >&2
28
+ detected=true
29
+ fi
30
+
31
+ # OpenAI/Anthropic API Key
32
+ if echo "$output" | grep -qE 'sk-[a-zA-Z0-9]{32,}'; then
33
+ echo "[Security] Potential API key (sk-*) detected in ${tool_name} output" >&2
34
+ detected=true
35
+ fi
36
+
37
+ # GitHub Personal Access Token
38
+ if echo "$output" | grep -qE 'ghp_[a-zA-Z0-9]{36}'; then
39
+ echo "[Security] Potential GitHub PAT detected in ${tool_name} output" >&2
40
+ detected=true
41
+ fi
42
+
43
+ # Private Key
44
+ if echo "$output" | grep -qE '-----BEGIN.*PRIVATE KEY-----'; then
45
+ echo "[Security] Potential private key detected in ${tool_name} output" >&2
46
+ detected=true
47
+ fi
48
+
49
+ # Bearer Token (long)
50
+ if echo "$output" | grep -qE 'Bearer [a-zA-Z0-9._-]{20,}'; then
51
+ echo "[Security] Potential Bearer token detected in ${tool_name} output" >&2
52
+ detected=true
53
+ fi
54
+
55
+ # GitHub OAuth Token
56
+ if echo "$output" | grep -qE 'gho_[a-zA-Z0-9]{36}'; then
57
+ echo "[Security] Potential GitHub OAuth token detected in ${tool_name} output" >&2
58
+ detected=true
59
+ fi
60
+
61
+ if [ "$detected" = true ]; then
62
+ echo "[Security] Review output carefully — do NOT commit or expose secrets" >&2
63
+ fi
64
+
65
+ # Pass through (always)
66
+ echo "$input"
67
+ exit 0
@@ -0,0 +1,65 @@
1
+ #!/bin/bash
2
+ # Stop hook: Session compliance report (R265 Phase 1)
3
+ # Reads violation logs collected by PreToolUse hooks during the session
4
+ # Advisory only — never blocks session termination
5
+ # Ref: https://github.com/baekenough/oh-my-customcode/issues/265
6
+
7
+ set -euo pipefail
8
+
9
+ input=$(cat)
10
+
11
+ VIOLATIONS_FILE="/tmp/.claude-violations-${PPID}"
12
+ TASK_COUNT_FILE="/tmp/.claude-task-count-${PPID}"
13
+
14
+ echo "" >&2
15
+ echo "╔══════════════════════════════════════════════╗" >&2
16
+ echo "║ Session Compliance Report ║" >&2
17
+ echo "╚══════════════════════════════════════════════╝" >&2
18
+
19
+ # Count total Agent/Task calls
20
+ if [ -f "$TASK_COUNT_FILE" ]; then
21
+ TOTAL_TASKS=$(cat "$TASK_COUNT_FILE")
22
+ echo "[Compliance] Agent/Task calls this session: ${TOTAL_TASKS}" >&2
23
+ else
24
+ TOTAL_TASKS=0
25
+ echo "[Compliance] Agent/Task calls this session: 0" >&2
26
+ fi
27
+
28
+ # Check violations
29
+ if [ -f "$VIOLATIONS_FILE" ] && [ -s "$VIOLATIONS_FILE" ]; then
30
+ VIOLATION_COUNT=$(wc -l < "$VIOLATIONS_FILE" | tr -d ' ')
31
+ echo "[Compliance] Violations detected: ${VIOLATION_COUNT}" >&2
32
+ echo "" >&2
33
+
34
+ # Group by rule
35
+ R010_COUNT=$(grep -c '"rule":"R010"' "$VIOLATIONS_FILE" 2>/dev/null || echo "0")
36
+ R018_COUNT=$(grep -c '"rule":"R018"' "$VIOLATIONS_FILE" 2>/dev/null || echo "0")
37
+
38
+ if [ "$R010_COUNT" -gt 0 ]; then
39
+ echo " R010 (Git Delegation): ${R010_COUNT} violation(s)" >&2
40
+ grep '"rule":"R010"' "$VIOLATIONS_FILE" | jq -r '.detail' 2>/dev/null | while read -r detail; do
41
+ echo " - ${detail}" >&2
42
+ done
43
+ fi
44
+
45
+ if [ "$R018_COUNT" -gt 0 ]; then
46
+ echo " R018 (Agent Teams): ${R018_COUNT} violation(s)" >&2
47
+ grep '"rule":"R018"' "$VIOLATIONS_FILE" | jq -r '.detail' 2>/dev/null | while read -r detail; do
48
+ echo " - ${detail}" >&2
49
+ done
50
+ fi
51
+
52
+ echo "" >&2
53
+ echo "[Compliance] Review violations above and consider rule updates per R016." >&2
54
+ else
55
+ echo "[Compliance] No violations detected. All clear!" >&2
56
+ fi
57
+
58
+ echo "────────────────────────────────────────────────" >&2
59
+
60
+ # Cleanup temp files (best effort)
61
+ rm -f "$VIOLATIONS_FILE" 2>/dev/null || true
62
+
63
+ # CRITICAL: Always pass through input and exit 0
64
+ echo "$input"
65
+ exit 0
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.36.1",
2
+ "version": "0.36.2",
3
3
  "lastUpdated": "2026-03-14T00:00:00.000Z",
4
4
  "components": [
5
5
  {