* Introduces /aside — a mid-task side conversation command inspired by
Claude Code's native /btw feature. Allows users to ask a question while
Claude is actively working without losing task context or touching any files.
Key behaviors:
- Freezes current task state before answering (read-only during aside)
- Delivers answers in a consistent ASIDE / Back to task format
- Auto-resumes the active task after answering
- Handles edge cases: no question given, answer reveals a blocker,
question implies a task redirect, chained asides, ambiguous questions,
and answers that suggest code changes without making them
* Two documentation inconsistencies fixed:
* Fixed 4 pre-existing lint errors in skills/videodb/ that were causing CI to fail across all PR checks: - api-reference.md: add blockquote continuation line to fix MD028 - capture-reference.md: wrap bare URL to fix MD034 - SKILL.md: wrap bare URL to fix MD034
* feat: add project cooldown log to prevent rapid observer re-spawn
Adds session-guardian.sh, called by observer-loop.sh before each Haiku
spawn. It reads ~/.claude/observer-last-run.log and blocks the cycle if
the same project was observed within OBSERVER_INTERVAL_SECONDS (default
300s).
Prevents self-referential loops where a spawned session triggers
observe.sh, which signals the observer before the cooldown has elapsed.
Uses a mkdir-based lock for safe concurrent access across multiple
simultaneously-observed projects. Log entries use tab-delimited format
to handle paths containing spaces. Fails open on lock contention.
Config:
OBSERVER_INTERVAL_SECONDS default: 300
OBSERVER_LAST_RUN_LOG default: ~/.claude/observer-last-run.log
No external dependencies. Works on macOS, Linux, Windows (Git Bash/MSYS2).
* feat: extend session-guardian with time window and idle detection gates
Adds Gate 1 (active hours check) and Gate 3 (system idle detection) to
session-guardian.sh, building on the per-project cooldown log from PR 1.
Gate 1 — Time Window:
- OBSERVER_ACTIVE_HOURS_START/END (default 800–2300 local time)
- Uses date +%k%M with 10# prefix to avoid octal crash at midnight
- Toolless on all platforms; set both vars to 0 to disable
Gate 3 — Idle Detection:
- macOS: ioreg + awk (built-in, no deps)
- Linux: xprintidle if available, else fail open
- Windows (Git Bash/MSYS2): PowerShell GetLastInputInfo via Add-Type
- Unknown/headless: always returns 0 (fail open)
- OBSERVER_MAX_IDLE_SECONDS=0 disables gate
Fixes in this commit:
- 10# base-10 prefix prevents octal arithmetic crash on midnight minutes
containing digits 8 or 9 (e.g. 00:08 = "008" is invalid octal)
- PowerShell output piped through tr -d '\r' to strip Windows CRLF;
also uses [long] cast to avoid TickCount 32-bit overflow after 24 days
- mktemp now uses log file directory instead of TMPDIR to ensure
same-filesystem mv on Linux (atomic rename instead of copy+unlink)
- mkdir -p failure exits 0 (fail open) rather than crashing under set -e
- Numeric validation on last_spawn prevents arithmetic error on corrupt log
Gate execution order: 1 (time, ~0ms) → 2 (cooldown, ~1ms) → 3 (idle, ~50ms)
* fix: harden session guardian gates
---------
Co-authored-by: Affaan Mustafa <affaan@dcube.ai>
* fix(observe): add 5-layer automated session guard to prevent self-loop observations
observe.sh currently fires for ALL hook events including automated/programmatic
sessions: the ECC observer's own Haiku analysis runs, claude-mem observer
sessions, CI pipelines, and any other tool that spawns `claude --print`.
This causes an infinite feedback loop where automated sessions generate
observations that trigger more automated analysis, burning Haiku tokens with
no human activity.
Add a 5-layer guard block after the `disabled` check:
Layer 1: agent_id payload field — only present in subagent hooks; skip any
subagent-scoped session (always automated by definition).
Layer 2: CLAUDE_CODE_ENTRYPOINT env var — Claude Code sets this to sdk-ts,
sdk-py, sdk-cli, mcp, or remote for programmatic/SDK invocations.
Skip if any non-cli entrypoint is detected. This is universal: catches
any tool using the Anthropic SDK without requiring tool cooperation.
Layer 3: ECC_HOOK_PROFILE=minimal — existing ECC mechanism; respect it here
to suppress non-essential hooks in observer contexts.
Layer 4: ECC_SKIP_OBSERVE=1 — cooperative env var any external tool can set
before spawning automated sessions (explicit opt-out contract).
Layer 5: CWD path exclusions — skip sessions whose working directory matches
known observer-session path patterns. Configurable via
ECC_OBSERVE_SKIP_PATHS (comma-separated substrings, default:
"observer-sessions,.claude-mem").
Also fix observer-loop.sh to set ECC_SKIP_OBSERVE=1 and ECC_HOOK_PROFILE=minimal
before spawning the Haiku analysis subprocess, making the observer loop
self-aware and closing the ECC→ECC self-observation loop without needing
external coordination.
Fixes: observe.sh fires unconditionally on automated sessions (#398)
* fix(observe): address review feedback — reorder guards cheapest-first, fix empty pattern bug
Two issues flagged by Copilot and CodeRabbit in PR #399:
1. Layer ordering: the agent_id check spawns a Python subprocess but ran
before the cheap env-var checks (CLAUDE_CODE_ENTRYPOINT, ECC_HOOK_PROFILE,
ECC_SKIP_OBSERVE). Reorder to put all env-var checks first (Layers 1-3),
then the subprocess-requiring agent_id check (Layer 4). Automated sessions
that set env vars — the common case — now exit without spawning Python.
2. Empty pattern bug in Layer 5: if ECC_OBSERVE_SKIP_PATHS contains a trailing
comma or spaces after commas (e.g. "path1, path2" or "path1,"), _pattern
becomes empty or whitespace-only, and the glob *""* matches every CWD,
silently disabling all observations. Fix: trim leading/trailing whitespace
from each pattern and skip empty patterns with `continue`.
* fix: fail closed for non-cli entrypoints
---------
Co-authored-by: Affaan Mustafa <affaan@dcube.ai>
* feat(skills): add prompt-optimizer skill and /prompt-optimize command
Adds a prompt-optimizer skill that analyzes draft prompts, matches them
to ECC components (skills/commands/agents), and outputs a ready-to-paste
optimized prompt. Advisory role only — never executes the task.
Features:
- 6-phase analysis pipeline (project detection, intent, scope, component
matching, missing context, workflow + model recommendation)
- Auto-detects project tech stack from package.json, go.mod, etc.
- Maps intents to ECC commands, skills, and agents by type and tech stack
- Recommends correct model tier (Sonnet vs Opus) based on task complexity
- Outputs Full + Quick versions of the optimized prompt
- Hard gate: never executes the task, only produces advisory output
- AskUserQuestion trigger when 3+ critical context items are missing
- Multi-prompt splitting guidance for HIGH/EPIC scope tasks
- Feedback footer for iterative refinement
Also adds /prompt-optimize command as an explicit invocation entry point.
* fix: keep prompt optimizer advisory-only
* fix: refine prompt optimizer guidance
---------
Co-authored-by: Affaan Mustafa <affaan@dcube.ai>
* fix: restore ci compatibility on windows
* fix: normalize hook path assertions on windows
* fix: relax repo root assertion on windows
* fix: keep hook root assertion strict on windows
- Fix path traversal regex prefix confusion in perl-security skill
- Revert v1.4.0 changelog entry (Perl not part of that release)
- Rename $a/$b to $x/$y to avoid shadowing sort globals
- Replace return undef with bare return per perlcritic rules
The Explore agent is a "Fast agent" optimized for codebase exploration,
not deep reasoning. The skill-stocktake V4 design requires holistic AI
judgment (actionability, scope fit, uniqueness, currency) which needs
the full reasoning capability of the conversation's main model.
Additionally, the Agent tool has no `model` parameter — specifying
`model: opus` was silently ignored, causing the evaluation to run on
the lightweight Explore model. This resulted in all skills receiving
"Keep" verdicts without genuine critical analysis.
Changing to `general-purpose` agent ensures evaluation runs on the
conversation's main model (e.g., Opus 4.6), enabling the holistic
judgment that V4 was designed for.
- Pin installation to specific commit hash (full SHA) to mitigate
supply-chain risk (cubic-dev-ai feedback)
- Add "When to Use", "How It Works", "Examples" sections to match
repo skill format conventions (coderabbitai feedback)
- Add review-before-update instructions for safe version upgrades
- Emphasize zero-runtime-risk: pure Markdown, no executable code
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>