chore: format files

This commit is contained in:
d-kimsuon
2025-08-30 03:18:42 +09:00
parent 16ec283e50
commit bfa19a6e85
54 changed files with 539 additions and 541 deletions

View File

@@ -1,8 +1,8 @@
import type { HonoAppType } from "./app";
import { getProjects } from "../service/project/getProjects";
import { getProject } from "../service/project/getProject";
import { getSessions } from "../service/session/getSessions";
import { getProjects } from "../service/project/getProjects";
import { getSession } from "../service/session/getSession";
import { getSessions } from "../service/session/getSessions";
import type { HonoAppType } from "./app";
export const routes = (app: HonoAppType) => {
return app

View File

@@ -7,9 +7,7 @@ const matchSchema = z.object({
content: z.string(),
});
export const parseCommandXml = (
content: string
):
export type ParsedCommand =
| {
kind: "command";
commandName: string;
@@ -28,7 +26,9 @@ export const parseCommandXml = (
| {
kind: "text";
content: string;
} => {
};
export const parseCommandXml = (content: string): ParsedCommand => {
const matches = Array.from(content.matchAll(regExp))
.map((match) => matchSchema.safeParse(match.groups))
.filter((result) => result.success)
@@ -42,16 +42,16 @@ export const parseCommandXml = (
}
const commandName = matches.find(
(match) => match.tag === "command-name"
(match) => match.tag === "command-name",
)?.content;
const commandArgs = matches.find(
(match) => match.tag === "command-args"
(match) => match.tag === "command-args",
)?.content;
const commandMessage = matches.find(
(match) => match.tag === "command-message"
(match) => match.tag === "command-message",
)?.content;
const localCommandStdout = matches.find(
(match) => match.tag === "local-command-stdout"
(match) => match.tag === "local-command-stdout",
)?.content;
switch (true) {

View File

@@ -1,11 +1,11 @@
import { existsSync } from "node:fs";
import type { Project } from "../types";
import { decodeProjectId } from "./id";
import { getProjectMeta } from "./getProjectMeta";
import { decodeProjectId } from "./id";
export const getProject = async (
projectId: string
projectId: string,
): Promise<{ project: Project }> => {
const fullPath = decodeProjectId(projectId);
if (!existsSync(fullPath)) {

View File

@@ -1,13 +1,18 @@
import { statSync } from "node:fs";
import { readdir, readFile } from "node:fs/promises";
import { basename, dirname, resolve } from "node:path";
import { basename, resolve } from "node:path";
import { parseJsonl } from "../parseJsonl";
import type { ProjectMeta } from "../types";
const projectMetaCache = new Map<string, ProjectMeta>();
const projectPathCache = new Map<string, string | null>();
const extractProjectPathFromJsonl = async (filePath: string) => {
const cached = projectPathCache.get(filePath);
if (cached !== undefined) {
return cached;
}
const extractMetaFromJsonl = async (filePath: string) => {
const content = await readFile(filePath, "utf-8");
const lines = content.split("\n");
@@ -25,19 +30,16 @@ const extractMetaFromJsonl = async (filePath: string) => {
break;
}
return {
cwd,
} as const;
if (cwd !== null) {
projectPathCache.set(filePath, cwd);
}
return cwd;
};
export const getProjectMeta = async (
claudeProjectPath: string
claudeProjectPath: string,
): Promise<ProjectMeta> => {
const cached = projectMetaCache.get(claudeProjectPath);
if (cached !== undefined) {
return cached;
}
const dirents = await readdir(claudeProjectPath, { withFileTypes: true });
const files = dirents
.filter((d) => d.isFile() && d.name.endsWith(".jsonl"))
@@ -46,7 +48,7 @@ export const getProjectMeta = async (
({
fullPath: resolve(d.parentPath, d.name),
stats: statSync(resolve(d.parentPath, d.name)),
} as const)
}) as const,
)
.toSorted((a, b) => {
return a.stats.ctime.getTime() - b.stats.ctime.getTime();
@@ -54,30 +56,26 @@ export const getProjectMeta = async (
const lastModifiedUnixTime = files.at(-1)?.stats.ctime.getTime();
let cwd: string | null = null;
let projectPath: string | null = null;
for (const file of files) {
const result = await extractMetaFromJsonl(file.fullPath);
projectPath = await extractProjectPathFromJsonl(file.fullPath);
if (result.cwd === null) {
if (projectPath === null) {
continue;
}
cwd = result.cwd;
break;
}
const projectMeta: ProjectMeta = {
projectName: cwd ? basename(cwd) : null,
projectPath: cwd,
projectName: projectPath ? basename(projectPath) : null,
projectPath,
lastModifiedAt: lastModifiedUnixTime
? new Date(lastModifiedUnixTime)
: null,
sessionCount: files.length,
};
projectMetaCache.set(claudeProjectPath, projectMeta);
return projectMeta;
};

View File

@@ -3,8 +3,8 @@ import { resolve } from "node:path";
import { claudeProjectPath } from "../paths";
import type { Project } from "../types";
import { encodeProjectId } from "./id";
import { getProjectMeta } from "./getProjectMeta";
import { encodeProjectId } from "./id";
export const getProjects = async (): Promise<{ projects: Project[] }> => {
const dirents = await readdir(claudeProjectPath, { withFileTypes: true });
@@ -24,6 +24,11 @@ export const getProjects = async (): Promise<{ projects: Project[] }> => {
);
return {
projects,
projects: projects.sort((a, b) => {
return (
(b.meta.lastModifiedAt?.getTime() ?? 0) -
(a.meta.lastModifiedAt?.getTime() ?? 0)
);
}),
};
};

View File

@@ -1,13 +1,13 @@
import { readFile } from "node:fs/promises";
import { decodeProjectId } from "../project/id";
import { resolve } from "node:path";
import { parseJsonl } from "../parseJsonl";
import { decodeProjectId } from "../project/id";
import type { SessionDetail } from "../types";
import { getSessionMeta } from "./getSessionMeta";
export const getSession = async (
projectId: string,
sessionId: string
sessionId: string,
): Promise<{
session: SessionDetail;
}> => {

View File

@@ -1,26 +1,21 @@
import { statSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { parseJsonl } from "../parseJsonl";
import type { Conversation } from "../../../lib/conversation-schema";
import { type ParsedCommand, parseCommandXml } from "../parseCommandXml";
import { parseJsonl } from "../parseJsonl";
import type { SessionMeta } from "../types";
const sessionMetaCache = new Map<string, SessionMeta>();
const firstCommandCache = new Map<string, ParsedCommand | null>();
export const getSessionMeta = async (
jsonlFilePath: string
): Promise<SessionMeta> => {
const cached = sessionMetaCache.get(jsonlFilePath);
const getFirstCommand = (
jsonlFilePath: string,
lines: string[],
): ParsedCommand | null => {
const cached = firstCommandCache.get(jsonlFilePath);
if (cached !== undefined) {
return cached;
}
const stats = statSync(jsonlFilePath);
const lastModifiedUnixTime = stats.ctime.getTime();
const content = await readFile(jsonlFilePath, "utf-8");
const lines = content.split("\n");
let firstUserMessage: Conversation | null = null;
for (const line of lines) {
@@ -35,12 +30,10 @@ export const getSessionMeta = async (
break;
}
const sessionMeta: SessionMeta = {
messageCount: lines.length,
firstContent:
firstUserMessage === null
? null
: typeof firstUserMessage.message.content === "string"
const firstMessageText =
firstUserMessage === null
? null
: typeof firstUserMessage.message.content === "string"
? firstUserMessage.message.content
: (() => {
const firstContent = firstUserMessage.message.content.at(0);
@@ -48,13 +41,34 @@ export const getSessionMeta = async (
if (typeof firstContent === "string") return firstContent;
if (firstContent.type === "text") return firstContent.text;
return null;
})(),
})();
const firstCommand =
firstMessageText === null ? null : parseCommandXml(firstMessageText);
if (firstCommand !== null) {
firstCommandCache.set(jsonlFilePath, firstCommand);
}
return firstCommand;
};
export const getSessionMeta = async (
jsonlFilePath: string,
): Promise<SessionMeta> => {
const stats = statSync(jsonlFilePath);
const lastModifiedUnixTime = stats.ctime.getTime();
const content = await readFile(jsonlFilePath, "utf-8");
const lines = content.split("\n");
const sessionMeta: SessionMeta = {
messageCount: lines.length,
firstCommand: getFirstCommand(jsonlFilePath, lines),
lastModifiedAt: lastModifiedUnixTime
? new Date(lastModifiedUnixTime)
: null,
};
sessionMetaCache.set(jsonlFilePath, sessionMeta);
return sessionMeta;
};

View File

@@ -26,6 +26,11 @@ export const getSessions = async (
);
return {
sessions,
sessions: sessions.sort((a, b) => {
return (
(b.meta.lastModifiedAt?.getTime() ?? 0) -
(a.meta.lastModifiedAt?.getTime() ?? 0)
);
}),
};
};

View File

@@ -1,4 +1,5 @@
import type { Conversation } from "../../lib/conversation-schema";
import type { ParsedCommand } from "./parseCommandXml";
export type Project = {
id: string;
@@ -21,7 +22,7 @@ export type Session = {
export type SessionMeta = {
messageCount: number;
firstContent: string | null;
firstCommand: ParsedCommand | null;
lastModifiedAt: Date | null;
};