mirror of
https://github.com/aljazceru/claude-code-viewer.git
synced 2026-01-04 14:14:22 +01:00
refactor: add platform effects
This commit is contained in:
@@ -1,24 +1,10 @@
|
||||
import path from "node:path";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("computeClaudeProjectFilePath", () => {
|
||||
const TEST_GLOBAL_CLAUDE_DIR = "/test/mock/claude";
|
||||
const TEST_PROJECTS_DIR = path.join(TEST_GLOBAL_CLAUDE_DIR, "projects");
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.doMock("../../../lib/env", () => ({
|
||||
env: {
|
||||
get: (key: string) => {
|
||||
if (key === "GLOBAL_CLAUDE_DIR") {
|
||||
return TEST_GLOBAL_CLAUDE_DIR;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
it("プロジェクトパスからClaudeの設定ディレクトリパスを計算する", async () => {
|
||||
const { computeClaudeProjectFilePath } = await import(
|
||||
"./computeClaudeProjectFilePath"
|
||||
@@ -27,7 +13,10 @@ describe("computeClaudeProjectFilePath", () => {
|
||||
const projectPath = "/home/me/dev/example";
|
||||
const expected = `${TEST_PROJECTS_DIR}/-home-me-dev-example`;
|
||||
|
||||
const result = computeClaudeProjectFilePath(projectPath);
|
||||
const result = computeClaudeProjectFilePath({
|
||||
projectPath,
|
||||
claudeProjectsDirPath: TEST_PROJECTS_DIR,
|
||||
});
|
||||
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
@@ -40,7 +29,10 @@ describe("computeClaudeProjectFilePath", () => {
|
||||
const projectPath = "/home/me/dev/example/";
|
||||
const expected = `${TEST_PROJECTS_DIR}/-home-me-dev-example`;
|
||||
|
||||
const result = computeClaudeProjectFilePath(projectPath);
|
||||
const result = computeClaudeProjectFilePath({
|
||||
projectPath,
|
||||
claudeProjectsDirPath: TEST_PROJECTS_DIR,
|
||||
});
|
||||
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Path } from "@effect/platform";
|
||||
import { Effect } from "effect";
|
||||
import { claudeProjectsDirPath } from "../../../lib/config/paths";
|
||||
|
||||
export const computeClaudeProjectFilePath = (projectPath: string) =>
|
||||
export const computeClaudeProjectFilePath = (options: {
|
||||
projectPath: string;
|
||||
claudeProjectsDirPath: string;
|
||||
}) =>
|
||||
Effect.gen(function* () {
|
||||
const path = yield* Path.Path;
|
||||
const { projectPath, claudeProjectsDirPath } = options;
|
||||
|
||||
return path.join(
|
||||
claudeProjectsDirPath,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CommandExecutor, Path } from "@effect/platform";
|
||||
import { NodeContext } from "@effect/platform-node";
|
||||
import { Effect, Layer } from "effect";
|
||||
import { EnvService } from "../../platform/services/EnvService";
|
||||
import * as ClaudeCode from "./ClaudeCode";
|
||||
|
||||
describe("ClaudeCode.Config", () => {
|
||||
@@ -19,6 +20,7 @@ describe("ClaudeCode.Config", () => {
|
||||
|
||||
const config = await Effect.runPromise(
|
||||
ClaudeCode.Config.pipe(
|
||||
Effect.provide(EnvService.Live),
|
||||
Effect.provide(Path.layer),
|
||||
Effect.provide(CommandExecutorTest),
|
||||
),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { query as originalQuery } from "@anthropic-ai/claude-code";
|
||||
import { Command, Path } from "@effect/platform";
|
||||
import { Effect } from "effect";
|
||||
import { env } from "../../../lib/env";
|
||||
import { EnvService } from "../../platform/services/EnvService";
|
||||
import * as ClaudeCodeVersion from "./ClaudeCodeVersion";
|
||||
|
||||
type CCQuery = typeof originalQuery;
|
||||
@@ -10,8 +10,9 @@ type CCQueryOptions = NonNullable<Parameters<CCQuery>[0]["options"]>;
|
||||
|
||||
export const Config = Effect.gen(function* () {
|
||||
const path = yield* Path.Path;
|
||||
const envService = yield* EnvService;
|
||||
|
||||
const specifiedExecutablePath = env.get(
|
||||
const specifiedExecutablePath = yield* envService.getEnv(
|
||||
"CLAUDE_CODE_VIEWER_CC_EXECUTABLE_PATH",
|
||||
);
|
||||
|
||||
@@ -21,7 +22,7 @@ export const Config = Effect.gen(function* () {
|
||||
: (yield* Command.string(
|
||||
Command.make("which", "claude").pipe(
|
||||
Command.env({
|
||||
PATH: env.get("PATH"),
|
||||
PATH: yield* envService.getEnv("PATH"),
|
||||
}),
|
||||
Command.runInShell(true),
|
||||
),
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { FileSystem, Path } from "@effect/platform";
|
||||
import { Context, Effect, Layer } from "effect";
|
||||
import { claudeCommandsDirPath } from "../../../lib/config/paths";
|
||||
import type { ControllerResponse } from "../../../lib/effect/toEffectResponse";
|
||||
import type { InferEffect } from "../../../lib/effect/types";
|
||||
import { ApplicationContext } from "../../platform/services/ApplicationContext";
|
||||
import { ProjectRepository } from "../../project/infrastructure/ProjectRepository";
|
||||
import { ClaudeCodeService } from "../services/ClaudeCodeService";
|
||||
|
||||
const LayerImpl = Effect.gen(function* () {
|
||||
const projectRepository = yield* ProjectRepository;
|
||||
const claudeCodeService = yield* ClaudeCodeService;
|
||||
const context = yield* ApplicationContext;
|
||||
const fs = yield* FileSystem.FileSystem;
|
||||
const path = yield* Path.Path;
|
||||
|
||||
@@ -19,7 +20,7 @@ const LayerImpl = Effect.gen(function* () {
|
||||
const { project } = yield* projectRepository.getProject(projectId);
|
||||
|
||||
const globalCommands: string[] = yield* fs
|
||||
.readDirectory(claudeCommandsDirPath)
|
||||
.readDirectory(context.claudeCodePaths.claudeCommandsDirPath)
|
||||
.pipe(
|
||||
Effect.map((items) =>
|
||||
items
|
||||
|
||||
@@ -2,14 +2,14 @@ import { Context, Effect, Layer } from "effect";
|
||||
import type { PublicSessionProcess } from "../../../../types/session-process";
|
||||
import type { ControllerResponse } from "../../../lib/effect/toEffectResponse";
|
||||
import type { InferEffect } from "../../../lib/effect/types";
|
||||
import { HonoConfigService } from "../../hono/services/HonoConfigService";
|
||||
import { UserConfigService } from "../../platform/services/UserConfigService";
|
||||
import { ProjectRepository } from "../../project/infrastructure/ProjectRepository";
|
||||
import { ClaudeCodeLifeCycleService } from "../services/ClaudeCodeLifeCycleService";
|
||||
|
||||
const LayerImpl = Effect.gen(function* () {
|
||||
const projectRepository = yield* ProjectRepository;
|
||||
const claudeCodeLifeCycleService = yield* ClaudeCodeLifeCycleService;
|
||||
const honoConfigService = yield* HonoConfigService;
|
||||
const userConfigService = yield* UserConfigService;
|
||||
|
||||
const getSessionProcesses = () =>
|
||||
Effect.gen(function* () {
|
||||
@@ -40,7 +40,7 @@ const LayerImpl = Effect.gen(function* () {
|
||||
const { projectId, message, baseSessionId } = options;
|
||||
|
||||
const { project } = yield* projectRepository.getProject(projectId);
|
||||
const config = yield* honoConfigService.getConfig();
|
||||
const userConfig = yield* userConfigService.getUserConfig();
|
||||
|
||||
if (project.meta.projectPath === null) {
|
||||
return {
|
||||
@@ -55,7 +55,7 @@ const LayerImpl = Effect.gen(function* () {
|
||||
projectId,
|
||||
sessionId: baseSessionId,
|
||||
},
|
||||
config: config,
|
||||
userConfig,
|
||||
message,
|
||||
});
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ import type { CommandExecutor } from "@effect/platform/CommandExecutor";
|
||||
import { Context, Effect, Layer, Runtime } from "effect";
|
||||
import { ulid } from "ulid";
|
||||
import { controllablePromise } from "../../../../lib/controllablePromise";
|
||||
import type { Config } from "../../../lib/config/config";
|
||||
import type { UserConfig } from "../../../lib/config/config";
|
||||
import type { InferEffect } from "../../../lib/effect/types";
|
||||
import { EventBus } from "../../events/services/EventBus";
|
||||
import type { EnvService } from "../../platform/services/EnvService";
|
||||
import { SessionRepository } from "../../session/infrastructure/SessionRepository";
|
||||
import { VirtualConversationDatabase } from "../../session/infrastructure/VirtualConversationDatabase";
|
||||
import type { SessionMetaService } from "../../session/services/SessionMetaService";
|
||||
@@ -36,6 +37,7 @@ const LayerImpl = Effect.gen(function* () {
|
||||
| VirtualConversationDatabase
|
||||
| SessionMetaService
|
||||
| ClaudeCodePermissionService
|
||||
| EnvService
|
||||
>();
|
||||
|
||||
const continueTask = (options: {
|
||||
@@ -78,7 +80,7 @@ const LayerImpl = Effect.gen(function* () {
|
||||
};
|
||||
|
||||
const startTask = (options: {
|
||||
config: Config;
|
||||
userConfig: UserConfig;
|
||||
baseSession: {
|
||||
cwd: string;
|
||||
projectId: string;
|
||||
@@ -86,7 +88,7 @@ const LayerImpl = Effect.gen(function* () {
|
||||
};
|
||||
message: string;
|
||||
}) => {
|
||||
const { baseSession, message, config } = options;
|
||||
const { baseSession, message, userConfig } = options;
|
||||
|
||||
return Effect.gen(function* () {
|
||||
const {
|
||||
@@ -258,7 +260,7 @@ const LayerImpl = Effect.gen(function* () {
|
||||
const permissionOptions =
|
||||
yield* permissionService.createCanUseToolRelatedOptions({
|
||||
taskId: task.def.taskId,
|
||||
config,
|
||||
userConfig,
|
||||
sessionId: task.def.baseSessionId,
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
PermissionRequest,
|
||||
PermissionResponse,
|
||||
} from "../../../../types/permissions";
|
||||
import type { Config } from "../../../lib/config/config";
|
||||
import type { UserConfig } from "../../../lib/config/config";
|
||||
import type { InferEffect } from "../../../lib/effect/types";
|
||||
import { EventBus } from "../../events/services/EventBus";
|
||||
import * as ClaudeCode from "../models/ClaudeCode";
|
||||
@@ -51,10 +51,10 @@ const LayerImpl = Effect.gen(function* () {
|
||||
|
||||
const createCanUseToolRelatedOptions = (options: {
|
||||
taskId: string;
|
||||
config: Config;
|
||||
userConfig: UserConfig;
|
||||
sessionId?: string;
|
||||
}) => {
|
||||
const { taskId, config, sessionId } = options;
|
||||
const { taskId, userConfig, sessionId } = options;
|
||||
|
||||
return Effect.gen(function* () {
|
||||
const claudeCodeConfig = yield* ClaudeCode.Config;
|
||||
@@ -69,11 +69,11 @@ const LayerImpl = Effect.gen(function* () {
|
||||
}
|
||||
|
||||
const canUseTool: CanUseTool = async (toolName, toolInput, _options) => {
|
||||
if (config.permissionMode !== "default") {
|
||||
if (userConfig.permissionMode !== "default") {
|
||||
// Convert Claude Code permission modes to canUseTool behaviors
|
||||
if (
|
||||
config.permissionMode === "bypassPermissions" ||
|
||||
config.permissionMode === "acceptEdits"
|
||||
userConfig.permissionMode === "bypassPermissions" ||
|
||||
userConfig.permissionMode === "acceptEdits"
|
||||
) {
|
||||
return {
|
||||
behavior: "allow" as const,
|
||||
@@ -123,7 +123,7 @@ const LayerImpl = Effect.gen(function* () {
|
||||
|
||||
return {
|
||||
canUseTool,
|
||||
permissionMode: config.permissionMode,
|
||||
permissionMode: userConfig.permissionMode,
|
||||
} as const;
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user