fix: 6 bugs fixed, 67 tests added for session-manager and session-aliases

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
This commit is contained in:
Affaan Mustafa
2026-02-12 15:50:04 -08:00
parent ff9a91319f
commit 76b271ab6b
11 changed files with 790 additions and 17 deletions

View File

@@ -206,7 +206,7 @@ function findFiles(dir, pattern, options = {}) {
async function readStdinJson(options = {}) {
const { timeoutMs = 5000, maxSize = 1024 * 1024 } = options;
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
let data = '';
let settled = false;
@@ -235,16 +235,19 @@ async function readStdinJson(options = {}) {
clearTimeout(timer);
try {
resolve(data.trim() ? JSON.parse(data) : {});
} catch (err) {
reject(err);
} catch {
// Consistent with timeout path: resolve with empty object
// so hooks don't crash on malformed input
resolve({});
}
});
process.stdin.on('error', err => {
process.stdin.on('error', () => {
if (settled) return;
settled = true;
clearTimeout(timer);
reject(err);
// Resolve with empty object so hooks don't crash on stdin errors
resolve({});
});
});
}
@@ -414,7 +417,7 @@ function countInFile(filePath, pattern) {
try {
if (pattern instanceof RegExp) {
// Ensure global flag is set for correct counting
regex = pattern.global ? pattern : new RegExp(pattern.source, pattern.flags + 'g');
regex = pattern.global ? pattern : new RegExp(pattern.source, pattern.flags.includes('g') ? pattern.flags : pattern.flags + 'g');
} else if (typeof pattern === 'string') {
regex = new RegExp(pattern, 'g');
} else {