24 Commits

  • review: broaden CLAUDE_TRANSCRIPT_PATH fallback to cover missing/empty JSON fields
    Previously the env fallback ran only when JSON.parse threw. If stdin was valid
    JSON but omitted transcript_path or provided a non-string/empty value, the
    script dropped to the getSessionIdShort() fallback path, re-introducing the
    collision this PR targets.
    
    Validate the parsed transcript_path and apply the env-var fallback for any
    unusable value, not just malformed JSON. Matches coderabbit's outside-diff
    suggestion and keeps both input-source paths equivalent.
    
    Refs #1494
  • review: apply sanitizeSessionId to UUID shortId, fix test comment
    - Route the transcript-derived shortId through sanitizeSessionId so the
      fallback and transcript branches remain byte-for-byte equivalent for any
      non-UUID session IDs that still land in CLAUDE_SESSION_ID (greptile P1).
    - Clarify the inline comment in the first regression test: clearing
      CLAUDE_SESSION_ID exercises the transcript_path branch, not the
      getSessionIdShort() fallback (coderabbit P2).
    
    Refs #1494
  • review: address P1/P2 bot feedback on shortId derivation
    - Use last-8 chars of transcript UUID instead of first-8, matching
      getSessionIdShort()'s .slice(-8) convention. Same session now produces the
      same filename whether shortId comes from CLAUDE_SESSION_ID or transcript_path,
      so existing .tmp files are not orphaned on upgrade.
    - Normalize extracted hex prefix to lowercase to avoid case-driven filename
      divergence from sanitizeSessionId()'s lowercase output.
    - Explicitly clear CLAUDE_SESSION_ID in the first regression test so the env
      leak from parent test runs cannot hide the fallback path.
    - Add regression tests for the lowercase-normalization path and for the case
      where CLAUDE_SESSION_ID and transcript_path refer to the same UUID (backward
      compat guarantee).
    
    Refs #1494
  • fix(hooks): isolate session-end.js filename using transcript_path UUID
    When session-end.js runs and CLAUDE_SESSION_ID is unset, getSessionIdShort()
    falls back to the project/worktree name. If any other Stop-hook in the chain
    spawns a claude subprocess (e.g. an AI-summary generator using 'claude -p'),
    the subprocess also fires the full Stop chain and writes to the same project-
    name-based filename, clobbering the parent's valid session summary with a
    summary of the summarization prompt itself.
    
    Fix: when stdin JSON (or CLAUDE_TRANSCRIPT_PATH) provides a transcript_path,
    extract the first 8 hex chars of the session UUID from the filename and use
    that as shortId. Falls back to the original getSessionIdShort() when no
    transcript_path is available, so existing behavior is preserved for all
    callers that do not set it.
    
    Adds a regression test in tests/hooks/hooks.test.js.
    
    Refs #1494
  • fix: strip ANSI escape codes from session persistence hooks (#642) (#684)
    Windows terminals emit control sequences (cursor movement, screen
    clearing) that leaked into session.tmp files and were injected
    verbatim into Claude's context on the next session start.
    
    Add a comprehensive stripAnsi() to utils.js that handles CSI, OSC,
    charset selection, and bare ESC sequences. Apply it in session-end.js
    (when extracting user messages from the transcript) and in
    session-start.js (safety net before injecting session content).
  • fix(session-end): always update session summary content (#317)
    * fix(session-end): always update session summary content
    
    Previously, session-end.js would only write content to session files
    on first creation. Subsequent sessions would only update the timestamp,
    causing stale content (e.g., old tasks, resolved issues) to persist
    indefinitely.
    
    This fix ensures that every session end updates the summary section
    with fresh content from the current transcript, keeping cross-session
    context accurate and relevant.
    
    Fixes: #187 (partially - addresses stale content issue)
    
    Changes:
    - Remove the blank-template-only check
    - Replace entire Session Summary section on every session end
    - Keep timestamp update separate from content update
    
    * fix(session-end): match both summary headers and prevent duplicate stats
    
    Fixes two issues identified in PR #317 code review:
    
    1. CodeRabbit: Updated regex to match both `## Session Summary` and
       `## Current State` headers, ensuring files created from blank template
       can be updated with fresh summaries.
    
    2. Cubic: Changed regex lookahead `(?=### Stats|$)` to end-of-string `$`
       to prevent duplicate `### Stats` sections. The old pattern stopped before
       `### Stats` without consuming it, but buildSummarySection() also emits
       a `### Stats` block, causing duplication on each session update.
    
    Changes:
    - Regex now: `/## (?:Session Summary|Current State)[\s\S]*?$/`
    - Matches both header variants used in blank template and populated sessions
    - Matches to end-of-string to cleanly replace entire summary section
    
    ---------
    
    Co-authored-by: will <will@192.168.5.31>
  • fix: collapse newlines in user messages to prevent markdown list breaks in session-end
    User messages containing newline characters were being added as-is to
    markdown list items in buildSummarySection(), breaking the list format.
    Now newlines are replaced with spaces before backtick escaping.
  • fix: Windows compatibility for hook scripts (execFileSync + tmux) (#215)
    * fix: Windows compatibility for hook scripts
    
    - post-edit-format.js: add `shell: process.platform === 'win32'` to
      execFileSync options so npx.cmd is resolved via cmd.exe on Windows
    - post-edit-typecheck.js: same fix for tsc invocation via npx
    - hooks.json: skip tmux-dependent hooks on Windows where tmux is
      unavailable (dev-server blocker and long-running command reminder)
    
    On Windows, execFileSync('npx', ...) without shell:true fails with
    ENOENT because Node.js cannot directly execute .cmd files. These
    hooks silently fail on all Windows installations.
    
    The tmux hooks unconditionally block dev server commands (exit 2) or
    warn about tmux on Windows where tmux is not available.
    
    Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
    
    * fix: parse Claude Code JSONL transcript format correctly
    
    The session-end hook expected user messages at entry.content, but
    Claude Code's actual JSONL format nests them at entry.message.content.
    This caused all session files to be blank templates (0 user messages
    despite 136+ actual entries).
    
    - Check entry.message?.content in addition to entry.content
    - Extract tool_use blocks from assistant message.content arrays
    
    Verified with Claude Code v2.1.41 JSONL transcripts.
    
    Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: ddungan <sckim@mococo.co.kr>
    Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
  • fix: broken cross-references, version sync, and enhanced command validator
    - Fix /build-and-fix → /build-fix in tdd.md, plan.md (+ cursor, zh-CN)
    - Fix non-existent explorer agent → planner in orchestrate.md (+ cursor, zh-CN, zh-TW)
    - Fix /python-test → /tdd in python-review.md (+ cursor, zh-CN)
    - Sync package.json version from 1.0.0 to 1.4.1 to match plugin.json
    - Enhance validate-commands.js with cross-reference checking:
      command refs, agent path refs, skill dir refs, workflow diagrams
    - Strip fenced code blocks before scanning to avoid false positives
    - Skip hypothetical "Creates:" lines in evolve.md examples
    - Add 46 new tests (suggest-compact, session-manager, utils, hooks)
  • fix: 3 bugs fixed, stdin encoding hardened, 37 CI validator tests added
    Bug fixes:
    - utils.js: glob-to-regex conversion now escapes all regex special chars
      (+, ^, $, |, (), {}, [], \) before converting * and ? wildcards
    - validate-hooks.js: escape sequence processing order corrected —
      \\\\ now processed before \\n and \\t to prevent double-processing
    - 6 hooks: added process.stdin.setEncoding('utf8') to prevent
      multi-byte UTF-8 character corruption at chunk boundaries
      (check-console-log, post-edit-format, post-edit-typecheck,
      post-edit-console-warn, session-end, evaluate-session)
    
    New tests (37):
    - CI validator test suite (tests/ci/validators.test.js):
      - validate-agents: 9 tests (real project, frontmatter parsing,
        BOM/CRLF, colons in values, missing fields, non-md skip)
      - validate-hooks: 13 tests (real project, invalid JSON, invalid
        event types, missing fields, async/timeout validation, inline JS
        syntax, array commands, legacy format)
      - validate-skills: 6 tests (real project, missing SKILL.md, empty
        files, non-directory entries)
      - validate-commands: 5 tests (real project, empty files, non-md skip)
      - validate-rules: 4 tests (real project, empty files)
    
    Total test count: 228 (up from 191)
  • fix: 2 bugs fixed, 17 tests added for hook scripts
    Bug fixes:
    - evaluate-session.js: whitespace-tolerant regex for counting user
      messages in JSONL transcripts (/"type":"user"/ → /"type"\s*:\s*"user"/)
    - session-end.js: guard against null elements in content arrays
      (c.text → (c && c.text) to prevent TypeError)
    
    New tests (17):
    - evaluate-session: whitespace JSON regression test
    - session-end: null content array elements regression test
    - post-edit-console-warn: 5 tests (warn, skip non-JS, clean files,
      missing file, stdout passthrough)
    - post-edit-format: 3 tests (empty stdin, non-JS skip, invalid JSON)
    - post-edit-typecheck: 4 tests (empty stdin, non-TS skip, missing file,
      no tsconfig)
    
    Total test count: 191 (up from 164)
  • fix: migrate hooks to stdin JSON input, fix duplicate main() calls, add threshold validation
    - Migrate session-end.js and evaluate-session.js from CLAUDE_TRANSCRIPT_PATH
      env var to stdin JSON transcript_path (correct hook input mechanism)
    - Remove duplicate main() calls that ran before stdin was read, causing
      session files to be created with empty data
    - Add range validation (1-10000) on COMPACT_THRESHOLD in suggest-compact.js
      to prevent negative or absurdly large thresholds
    - Add integration/hooks.test.js to tests/run-all.js so CI runs all 97 tests
    - Update evaluate-session.sh to parse transcript_path from stdin JSON
    - Update hooks.test.js to pass transcript_path via stdin instead of env var
    - Sync .cursor/ copies
  • fix: path traversal in install.sh, error logging in hooks
    - Validate language names in install.sh to prevent path traversal via
      malicious args like ../../etc (only allow [a-zA-Z0-9_-])
    - Replace silent catch in check-console-log.js with stderr logging so
      hook failures are visible to the user for debugging
    - Escape backticks in session-end.js user messages to prevent markdown
      structure corruption in session files
  • fix: harden error handling, fix TOCTOU races, and improve test accuracy
    Core library fixes:
    - session-manager.js: wrap all statSync calls in try-catch to prevent
      TOCTOU crashes when files are deleted between readdir and stat
    - session-manager.js: use birthtime||ctime fallback for Linux compat
    - session-manager.js: remove redundant existsSync before readFile
    - utils.js: fix findFiles TOCTOU race on statSync inside readdir loop
    
    Hook improvements:
    - Add 1MB stdin buffer limits to all PostToolUse hooks to prevent
      unbounded memory growth from large payloads
    - suggest-compact.js: use fd-based atomic read+write for counter file
      to reduce race window between concurrent invocations
    - session-end.js: log when transcript file is missing, check
      replaceInFile return value for failed timestamp updates
    - start-observer.sh: log claude CLI failures instead of silently
      swallowing them, check observations file exists before analysis
    
    Test fixes:
    - Fix blocking hook tests to send matching input (dev server command)
      and expect correct exit code 2 instead of 1
  • fix: improve error handling, fix bugs, and optimize core libraries
    utils.js:
    - Fix countInFile: enforce global flag on regex to prevent silent
      under-counting (match() without /g returns only first match)
    - Add 5s timeout to readStdinJson to prevent hooks hanging forever
    - Handle EEXIST race condition in ensureDir
    - Pre-compile regex patterns in getGitModifiedFiles to avoid N*M
      compilations and catch invalid patterns before filtering
    - Add JSDoc documentation to all improved functions
    
    session-manager.js:
    - Fix getSessionById triple file read: pass pre-read content to
      getSessionStats instead of re-reading from disk
    - Allow getSessionStats to accept content string directly
    
    session-aliases.js:
    - Wrap temp file cleanup in try/catch to prevent cascading errors
    
    check-console-log.js:
    - Refactor to use shared utils (isGitRepo, getGitModifiedFiles, log)
      instead of raw execSync calls
    - Add exclusion patterns for test files, config files, and scripts/
      where console.log is intentional
    
    session-end.js:
    - Log count of skipped unparseable transcript lines for diagnostics
    
    suggest-compact.js:
    - Guard against NaN from corrupted counter files
    
    package-manager.js:
    - Remove dead fallbackOrder parameter (unused after #162 fix)
  • fix(sessions): make session hooks actually persist and load context (#187)
    session-end.js: Extract meaningful summaries from CLAUDE_TRANSCRIPT_PATH
    instead of writing blank template files. Pulls user messages, tools used,
    and files modified from the session transcript JSONL.
    
    session-start.js: Output the latest session summary to stdout (via the
    output() helper) so it gets injected into Claude's conversation context,
    instead of only logging to stderr which just shows briefly in the terminal.
  • fix: resolve ESLint errors and update tests for project-name fallback
    - Fix 16 ESLint no-unused-vars errors across hook scripts and tests
    - Add eslint-disable comment for intentional control-regex in ANSI stripper
    - Update session file test to use getSessionIdShort() instead of hardcoded 'default'
      (reflects PR #110's project-name fallback behavior)
    - Add marketing/ to .gitignore (local drafts)
    - Add skill-create-output.js (terminal output formatter)
    
    All 69 tests now pass. CI should be green.
  • feat: v1.1.0 release - session ID tracking, async hooks, new skills
    - Add session ID to session filenames (Issue #62)
    - Add getSessionIdShort() helper for unique per-session tracking
    - Add async hooks documentation with example
    - Create iterative-retrieval skill for progressive context refinement
    - Add continuous-learning-v2 skill with instinct-based learning
    - Add ecc.tools ecosystem section to README
    - Update skills list in README
    
    All 67 tests passing.
  • feat: cross-platform support with Node.js scripts
    - Rewrite all bash hooks to Node.js for Windows/macOS/Linux compatibility
    - Add package manager auto-detection (npm, pnpm, yarn, bun)
    - Add scripts/lib/ with cross-platform utilities
    - Add /setup-pm command for package manager configuration
    - Add comprehensive test suite (62 tests)
    
    Co-authored-by: zerx-lab