Validate args parameter in getExecCommand() against SAFE_ARGS_REGEX to
prevent command injection when returned string is passed to a shell.
Escape regex metacharacters in getCommandPattern() generic action branch
to prevent malformed patterns and unintended matching. Clean up stdin
listeners in readStdinJson() timeout path to prevent process hanging.
Fix grepFile() silently skipping matches when called with /g flag regex.
The global flag makes .test() stateful, causing alternating match/miss
on consecutive matching lines. Strip g flag since per-line testing
doesn't need global state.
Add first-ever tests for evaluate-session.js (5 tests: short session,
long session, missing transcript, malformed stdin, env var fallback)
and suggest-compact.js (5 tests: counter increment, threshold trigger,
periodic suggestions, below-threshold silence, invalid threshold).
Bug fixes:
- utils.js: prevent duplicate 'g' flag in countInFile regex construction
- validate-agents.js: handle CRLF line endings in frontmatter parsing
- validate-hooks.js: handle \t and \\ escape sequences in inline JS validation
- session-aliases.js: prevent NaN in date sort when timestamps are missing
- session-aliases.js: persist rollback on rename failure instead of silent loss
- session-manager.js: require absolute paths in getSessionStats to prevent
content strings ending with .tmp from being treated as file paths
New tests (164 total, up from 97):
- session-manager.test.js: 27 tests covering parseSessionFilename,
parseSessionMetadata, getSessionStats, CRUD operations, getSessionSize,
getSessionTitle, edge cases (null input, non-existent files, directories)
- session-aliases.test.js: 40 tests covering loadAliases (corrupted JSON,
invalid structure), setAlias (validation, reserved names), resolveAlias,
listAliases (sort, search, limit), deleteAlias, renameAlias, updateAliasTitle,
resolveSessionAlias, getAliasesForSession, cleanupAliases, atomic write
Also includes hook-generated improvements:
- utils.d.ts: document that readStdinJson never rejects
- session-aliases.d.ts: fix updateAliasTitle type to accept null
- package-manager.js: add try-catch to setProjectPackageManager writeFile
- Replace raw fs.readFileSync with readFile() from utils in
check-console-log.js and post-edit-console-warn.js to eliminate
TOCTOU race conditions (file deleted between existsSync and read)
- Remove redundant existsSync in post-edit-format.js (exec already
handles missing files via its catch block)
- Resolve path upfront in post-edit-typecheck.js before tsconfig walk
- Add type guard in getGitModifiedFiles() to skip non-string and
empty patterns before regex compilation
- Guard findFiles() against null/undefined dir and pattern parameters
(previously crashed with TypeError on .replace() or fs.existsSync())
- Wrap countInFile() and grepFile() regex construction in try-catch to
handle invalid regex strings like '(unclosed' (previously crashed with
SyntaxError: Invalid regular expression)
- Add try-catch to replaceInFile() with descriptive error logging
- Add 1MB size limit to readStdinJson() matching the PostToolUse hooks
(previously had unbounded stdin accumulation)
- Improve ensureDir() error message to include the directory path
- Add 128-char length limit to setAlias() to prevent oversized alias
names from inflating the JSON store
- Update utils.d.ts with new maxSize option on ReadStdinJsonOptions
- Remove duplicate getAliasesPath() from utils.js (only used in
session-aliases.js which has its own copy)
- session-aliases.js: validate cleanupAliases param is a function,
check saveAliases return value, guard resolveAlias against empty input
- Sync .cursor/skills/strategic-compact/suggest-compact.sh with the
fixed main version (CLAUDE_SESSION_ID instead of $$)
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
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)
Add a new /sessions command to manage Claude Code session history with
alias support for quick access to previous sessions.
Features:
- List sessions with pagination and filtering (by date, ID)
- Load and view session content and metadata
- Create memorable aliases for sessions
- Remove aliases
- Display session statistics (lines, items, size)
- List all aliases
New libraries:
- scripts/lib/session-manager.js - Core session CRUD operations
- scripts/lib/session-aliases.js - Alias management with atomic saves
New command:
- commands/sessions.md - Complete command with embedded scripts
Modified:
- scripts/lib/utils.js - Add getAliasesPath() export
- scripts/hooks/session-start.js - Show available aliases on session start
Session format support:
- Old: YYYY-MM-DD-session.tmp
- New: YYYY-MM-DD-<short-id>-session.tmp
Aliases are stored in ~/.claude/session-aliases.json with Windows-
compatible atomic writes and backup support.
Co-authored-by: 王志坚 <wangzhijian10@bgyfw.com>
Co-authored-by: Claude <noreply@anthropic.com>
- 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.
- fix(utils.js): prevent command injection in commandExists() by using
spawnSync instead of execSync with string interpolation, and validate
input to only allow alphanumeric chars, dash, underscore, dot (#42)
- fix(utils.js): add security documentation to runCommand() warning
against passing user-controlled input
- fix(setup-package-manager.js): replace <script> and <binary> with
[script-name] and [binary-name] to avoid XSS scanner false positives (#43)
- fix(doc-updater.md): replace invalid 'npx ts-morph' with correct
'npx tsx scripts/codemaps/generate.ts' since ts-morph is a library,
not a CLI tool (#51)
Fixes#42, #43, #51