Claude Code sends tool output as `tool_response` in PostToolUse hook
payloads, but observe.sh only checked for `tool_output` and `output`.
This caused all observations to have empty output fields, making the
observer pipeline blind to tool results.
Adds `tool_response` as the primary field to check, with backward-
compatible fallback to the existing `tool_output` and `output` fields.
* fix(hooks): scrub secrets and harden hook security
- Scrub common secret patterns (api_key, token, password, etc.) from
observation logs before persisting to JSONL (observe.sh)
- Auto-purge observation files older than 30 days (observe.sh)
- Strip embedded credentials from git remote URLs before saving to
projects.json (detect-project.sh)
- Add command prefix allowlist to runCommand — only git, node, npx,
which, where are permitted (utils.js)
- Sanitize CLAUDE_SESSION_ID in temp file paths to prevent path
traversal (suggest-compact.js)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(hooks): address review feedback from CodeRabbit and Cubic
- Reject shell command-chaining operators (;|&`) in runCommand, strip
quoted sections before checking to avoid false positives (utils.js)
- Remove command string from blocked error message to avoid leaking
secrets (utils.js)
- Fix Python regex quoting: switch outer shell string from double to
single quotes so regex compiles correctly (observe.sh)
- Add optional auth scheme match (Bearer, Basic) to secret scrubber
regex (observe.sh)
- Scope auto-purge to current project dir and match only archived
files (observations-*.jsonl), not live queue (observe.sh)
- Add second fallback after session ID sanitization to prevent empty
string (suggest-compact.js)
- Preserve backward compatibility when credential stripping changes
project hash — detect and migrate legacy directories
(detect-project.sh)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(hooks): block $() substitution, fix Bearer redaction, add security tests
- Add $ and \n to blocked shell metacharacters in runCommand to prevent
command substitution via $(cmd) and newline injection (utils.js)
- Make auth scheme group capturing so Bearer/Basic is preserved in
redacted output instead of being silently dropped (observe.sh)
- Add 10 unit tests covering runCommand allowlist blocking (rm, curl,
bash prefixes) and metacharacter rejection (;|&`$ chaining), plus
error message leak prevention (utils.test.js)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(hooks): scrub parse-error fallback, strengthen security tests
Address remaining reviewer feedback from CodeRabbit and Cubic:
- Scrub secrets in observe.sh parse-error fallback path (was writing
raw unsanitized input to observations file)
- Remove redundant re.IGNORECASE flag ((?i) inline flag already set)
- Add inline comment documenting quote-stripping limitation trade-off
- Fix misleading test name for error-output test
- Add 5 new security tests: single-quote passthrough, mixed
quoted+unquoted metacharacters, prefix boundary (no trailing space),
npx acceptance, and newline injection
- Improve existing quoted-metacharacter test to actually exercise
quote-stripping logic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(security): block $() and backtick inside quotes in runCommand
Shell evaluates $() and backticks inside double quotes, so checking
only the unquoted portion was insufficient. Now $ and ` are rejected
anywhere in the command string, while ; | & remain quote-aware.
Addresses CodeRabbit and Cubic review feedback on PR #348.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(continuous-learning-v2): observer background process crashes immediately
Three bugs prevent the observer from running:
1. Nested session detection: When launched from a Claude Code session,
the child process inherits CLAUDECODE env var, causing `claude` CLI
to refuse with "cannot be launched inside another session". Fix: unset
CLAUDECODE in the background process.
2. set -e kills the loop: The parent script's `set -e` is inherited by
the subshell. When `claude` exits non-zero (e.g. max turns reached),
the entire observer loop dies. Fix: `set +e` in the background process.
3. Subshell dies when parent exits: `( ... ) & disown` loses IO handles
when the parent shell exits, killing the background process. Fix: use
`nohup /bin/bash -c '...'` for full detachment, and `sleep & wait`
to allow SIGUSR1 to interrupt sleep without killing the process.
Additionally, the prompt for Haiku now includes the exact instinct file
format inline (YAML frontmatter with id/trigger/confidence/domain/source
fields), since the previous prompt referenced "the observer agent spec"
which Haiku could not actually read, resulting in instinct files that
the CLI parser could not parse.
* fix: address review feedback on observer process management
- Use `env` to pass variables to child process instead of quote-splicing,
avoiding shell injection risk from special chars in paths
- Add USR1_FIRED flag to prevent double analysis when SIGUSR1 interrupts
the sleep/wait cycle
- Track SLEEP_PID and kill it in both TERM trap and USR1 handler to
prevent orphaned sleep processes from accumulating
- Consolidate cleanup logic into a dedicated cleanup() function
* fix: guard PID file cleanup against race condition on restart
Only remove PID file in cleanup trap if it still belongs to the
current process, preventing a restarted observer from losing its
PID file when the old process exits.
- Fix MD012 trailing blank lines in commands/projects.md and commands/promote.md
- Fix MD050 strong-style in continuous-learning-v2 (escape __tests__ as inline code)
- Extract doc-file-warning hook to standalone script to fix hooks validator regex parsing
- Update session-end test to match #317 behavior (always update summary content)
- Allow shell script hooks in integration test format validation
All 992 tests passing.
New articles:
- the-security-guide.md: "The Shorthand Guide to Securing Your Agent" (595 lines)
Attack vectors, sandboxing, sanitization, OWASP Top 10, observability
- the-openclaw-guide.md: "The Hidden Danger of OpenClaw" (470 lines)
Security analysis of OpenClaw, MiniClaw thesis, industry evidence
External link sanitization (22 files across EN, zh-CN, zh-TW, ja-JP, .cursor):
- Removed third-party GitHub links from skills and guides
- Replaced with inline descriptions to prevent transitive prompt injection
- Kept official org links (Anthropic, Google, Supabase, Mixedbread)
- Add do-catch error handling to SwiftUI streaming example in
foundation-models-on-device
- Add Auto Layout constraints to UIKit glass effect example in
liquid-glass-design
- Promote build settings prerequisite from code comment to visible
blockquote warning in swift-concurrency-6-2
- Add required name/description properties and @Generable to RecipeSearchTool
- Fix missing argument label in session.respond(to:) call
- Remove non-existent .scrollExtensionMode API, replace with correct guidance
- Change PhotoProcessor from struct to class for cache mutation support
- Fix method name mismatch in @concurrent example caller
Add skill covering Swift 6.2 Approachable Concurrency model including
single-threaded defaults, @concurrent for explicit background offloading,
isolated conformances, and MainActor default inference mode.
Add skill covering Apple's FoundationModels framework for on-device
language model integration, including @Generable guided generation,
tool calling, and snapshot streaming patterns.
Add origin field to all skill files to track their source repository.
This enables users to identify where distributed skills originated from.
Fixesaffaan-m/everything-claude-code#246
Changes based on CodeRabbit review feedback:
1. Remove entire .cursor/ directory — it was an identical copy of the
main skills/commands/agents/rules, causing maintenance drift.
Users of Cursor can reference the canonical files directly.
2. Use explicit `is not None` checks instead of truthiness for
parsed['input'] and parsed['output']. Empty strings or empty
dicts are valid values that should be preserved.
The observe.sh script receives "pre" or "post" as $1 from the hook
config, but the Python code was looking for a "hook_type" field in
the stdin JSON. Claude Code does NOT include "hook_type" in the
JSON payload passed to hooks, so it always defaulted to "unknown",
causing all observations to be recorded as "tool_complete" —
PreToolUse events were never distinguished from PostToolUse.
Fix: capture $1 as HOOK_PHASE and pass it to Python via env var.
This also fixes TIMESTAMP export in the .cursor copy where inline
`VAR=val cmd` syntax didn't propagate to the python subprocess.
Add automated visa document translation skill that:
- Supports multiple OCR methods (macOS Vision, EasyOCR, Tesseract)
- Translates documents to professional English
- Generates bilingual PDFs (original + translation)
- Handles various document types (bank statements, employment certificates, etc.)
- Perfect for visa applications to Australia, USA, Canada, UK, EU
Tested with retirement certificates and other official documents.
Fix ItemListView to accept viewModel via init with default parameter
so previews can inject mocks. Fix ExpensiveChartView Equatable to
compare full array instead of only count.
The inline environment variable syntax `TIMESTAMP="$timestamp" echo ...`
does not work correctly because:
1. The pipe creates a subshell that doesn't inherit the variable
2. The environment variable is set for echo, not for the piped python
Fixed by using `export` and separating the commands:
- export TIMESTAMP="$timestamp"
- echo "$INPUT_JSON" | python3 -c "..."
This ensures the TIMESTAMP variable is available to the python subprocess.
Fixes#227
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>