Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

### Fixes
- Decouple the hook handler from `lib/utils.ts` so `--hook <Event>` no longer crashes with `Cannot find package 'clsx'` when launched from a clone that hasn't run `bun install`. The handler's static graph (`handler.ts` → `resolve-permission-mode.ts` → `lib/codex-sessions.ts` → `lib/log-entries.ts`) was reaching `lib/utils.ts` just to use `formatDate`, which dragged in the dashboard's `clsx` + `tailwind-merge` deps. Move `formatDate` into its own `lib/format-date.ts` (no third-party deps) and update server-side callers (`lib/log-entries.ts`, `lib/projects.ts`) plus dashboard callers (`app/components/{project-list,sessions-list}.tsx`, `app/project/[name]/page.tsx`) and the `__tests__/lib/utils.test.ts` import. `lib/utils.ts` now only exports `cn`.
- Fix `mintlify validate` failing on `docs/de/dashboard.mdx` after #228 added German „..." quotation marks inside two `<Tab title="…">` attributes — the inner straight `"` ended the JSX attribute value, then the leftover `"` produced an `Unexpected character "` parse error. Drop the inner quotes (matching how every other locale renders the tab labels) so the docs CI job passes again.
- Fix `block-read-outside-cwd` falsely denying Bash commands that contain unquoted glob patterns or compound argv tokens. The unquoted-path regex in `extractAbsolutePaths` (`src/hooks/builtin-policies.ts`) only excluded `[a-zA-Z0-9_.\-~\\]` from its negative lookbehind, so a `/`-prefixed glob suffix or volume-mount target was misread as a standalone absolute path: `grep ... docs/*/dashboard.mdx | head` extracted `/dashboard.mdx`, and `docker run -v /host:/docs ...` extracted `/docs`. Both paths then resolved outside cwd and the read-like-command branch denied the call. Add `*?:=` to the lookbehind exclusion class so a `/` immediately following a glob meta or a separator no longer starts a match. Existing whitelisted/quoted/space-separated path detection is unchanged.
Comment thread
NiveditJain marked this conversation as resolved.
Outdated

Expand Down
3 changes: 2 additions & 1 deletion __tests__/lib/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, it, expect } from "vitest";
import { cn, formatDate } from "@/lib/utils";
import { cn } from "@/lib/utils";
import { formatDate } from "@/lib/format-date";

describe("cn", () => {
it("merges multiple class names", () => {
Expand Down
2 changes: 1 addition & 1 deletion app/components/project-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { useState, useMemo, useEffect, useRef } from "react";
import { ProjectFolder } from "@/lib/projects";
import { decodeFolderName } from "@/lib/paths";
import { formatDate } from "@/lib/utils";
import { formatDate } from "@/lib/format-date";
import {
FILTER_PRESETS,
ITEMS_PER_PAGE,
Expand Down
2 changes: 1 addition & 1 deletion app/components/sessions-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { useState, useMemo, useEffect, useRef } from "react";
import { SessionFile } from "@/lib/projects";
import { formatDate } from "@/lib/utils";
import { formatDate } from "@/lib/format-date";
import {
FILTER_PRESETS,
ITEMS_PER_PAGE,
Expand Down
2 changes: 1 addition & 1 deletion app/project/[name]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { existsSync } from "fs";
import { stat } from "fs/promises";
import Link from "next/link";
import { ArrowLeft } from "lucide-react";
import { formatDate } from "@/lib/utils";
import { formatDate } from "@/lib/format-date";
import SessionsList from "@/app/components/sessions-list";

export const dynamic = "force-dynamic";
Expand Down
21 changes: 21 additions & 0 deletions lib/format-date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Formats a date to a readable string format (e.g., "Jan 15, 2024, 3:45 PM").
*
* Creates a new Intl.DateTimeFormat on each call intentionally — this runs
* server-side where there's no shared state concern. The client-side hot-path
* formatter in lib/log-format.ts caches its instance at module scope instead.
*
* Lives in its own module (rather than lib/utils.ts) so server-side callers
* — including the hook handler's transitive imports — don't need to pull in
* clsx/tailwind-merge just to format a date.
*/
export function formatDate(date: Date): string {
return new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "2-digit",
hour12: true,
}).format(date);
}
2 changes: 1 addition & 1 deletion lib/log-entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { resolveProjectPath } from "./projects";
import { resolveSubagentPath } from "./resolve-subagent-path";
import { runtimeCache } from "./runtime-cache";
import { batchAll } from "./concurrency";
import { formatDate } from "./utils";
import { formatDate } from "./format-date";
import { formatDuration } from "./format-duration";

// ── Source Tagging ──
Expand Down
2 changes: 1 addition & 1 deletion lib/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { getClaudeProjectsPath } from "./paths";
import { runtimeCache } from "./runtime-cache";
import { batchAll } from "./concurrency";
import { logWarn, logError } from "./logger";
import { formatDate } from "./utils";
import { formatDate } from "./format-date";

export const UUID_RE = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
export const PATH_TRAVERSAL_RE = /(^|[\\/])\.\.($|[\\/])/;
Expand Down
28 changes: 6 additions & 22 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,15 @@
/**
* General-purpose utility helpers.
* Tailwind class-name merger.
*
* - `cn()` — merges Tailwind CSS class names with conflict resolution.
* - `formatDate()` — human-readable absolute date/time string (no ms).
* `cn()` combines `clsx` (conditional class composition) with `tailwind-merge`
* (last-wins conflict resolution for Tailwind utility classes).
*
* Date formatting lives in lib/format-date.ts — kept separate so server-side
* callers don't need to load clsx/tailwind-merge just to print a timestamp.
*/
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

/**
* Formats a date to a readable string format (e.g., "Jan 15, 2024, 3:45 PM").
*
* Creates a new Intl.DateTimeFormat on each call intentionally — this runs
* server-side where there's no shared state concern. The client-side hot-path
* formatter in lib/log-format.ts caches its instance at module scope instead.
*/
export function formatDate(date: Date): string {
return new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "2-digit",
hour12: true,
}).format(date);
}

Loading