@@ -8,7 +8,7 @@
|
||||
- This document is in English for context efficiency
|
||||
|
||||
**NEVER**:
|
||||
- Use `as` type casting (explain the problem to the user instead)
|
||||
- Use `as` type casting in ANY context including test code (explain the problem to the user instead)
|
||||
- Use raw `fetch` or bypass TanStack Query for API calls
|
||||
- Run `pnpm dev` or `pnpm start` (dev servers)
|
||||
- Use `node:fs`, `node:path`, etc. directly (use Effect-TS equivalents)
|
||||
|
||||
@@ -71,7 +71,7 @@ The application reads Claude Code conversation logs from:
|
||||
|
||||
### System Requirements
|
||||
|
||||
- **Node.js**: Version 20.18.1 or later
|
||||
- **Node.js**: Version 20.19.0 or later
|
||||
- **Operating Systems**: macOS and Linux (Windows is not supported)
|
||||
|
||||
### Claude Code Compatibility
|
||||
@@ -89,7 +89,7 @@ Claude Code Viewer reads several reserved environment variables. All values are
|
||||
|
||||
| Key | Description |
|
||||
| --- | --- |
|
||||
| CLAUDE_CODE_VIEWER_CC_EXECUTABLE_PATH | Path to Claude Code installation |
|
||||
| CLAUDE_CODE_VIEWER_CC_EXECUTABLE_PATH | Path to Claude Code installation. If not set, uses system PATH installation, or falls back to bundled version from dependencies |
|
||||
| PORT | Port number for Claude Code Viewer to run on |
|
||||
|
||||
### User Settings
|
||||
|
||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 283 KiB After Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 285 KiB After Width: | Height: | Size: 344 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 344 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 312 KiB After Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 312 KiB After Width: | Height: | Size: 344 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 284 KiB After Width: | Height: | Size: 344 KiB |
|
Before Width: | Height: | Size: 283 KiB After Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 284 KiB After Width: | Height: | Size: 344 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 85 KiB |
12
package.json
@@ -42,8 +42,8 @@
|
||||
"lingui:compile": "lingui compile --typescript"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.22",
|
||||
"@anthropic-ai/claude-code": "^2.0.22",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.23",
|
||||
"@anthropic-ai/claude-code": "^2.0.24",
|
||||
"@effect/platform": "^0.92.1",
|
||||
"@effect/platform-node": "^0.98.4",
|
||||
"@hono/zod-validator": "^0.7.4",
|
||||
@@ -70,7 +70,6 @@
|
||||
"next": "15.5.6",
|
||||
"next-themes": "^0.4.6",
|
||||
"parse-git-diff": "^0.0.19",
|
||||
"playwright": "^1.56.1",
|
||||
"prexit": "^2.3.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
@@ -89,16 +88,17 @@
|
||||
"@lingui/conf": "^5.5.1",
|
||||
"@lingui/format-json": "^5.5.1",
|
||||
"@lingui/loader": "^5.5.1",
|
||||
"@tailwindcss/postcss": "^4.1.14",
|
||||
"@tailwindcss/postcss": "^4.1.15",
|
||||
"@tsconfig/strictest": "^2.0.6",
|
||||
"@types/node": "^24.8.1",
|
||||
"@types/node": "^24.9.1",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"playwright": "^1.56.1",
|
||||
"release-it": "^19.0.5",
|
||||
"release-it-pnpm": "^4.6.6",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"tailwindcss": "^4.1.15",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^3.2.4"
|
||||
|
||||
592
pnpm-lock.yaml
generated
@@ -223,7 +223,6 @@ export const DiffModal: FC<DiffModalProps> = ({
|
||||
const initialSelection = new Map(
|
||||
diffData.data.files.map((file) => [file.filePath, true]),
|
||||
);
|
||||
console.log("[DiffModal] Initializing file selection:", initialSelection);
|
||||
setSelectedFiles(initialSelection);
|
||||
}
|
||||
}, [diffData]);
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type { SDKMessage, SDKUserMessage } from "@anthropic-ai/claude-code";
|
||||
import type {
|
||||
SDKMessage,
|
||||
SDKUserMessage,
|
||||
} from "@anthropic-ai/claude-agent-sdk";
|
||||
import { controllablePromise } from "../../../../lib/controllablePromise";
|
||||
|
||||
export type OnMessage = (message: SDKMessage) => void | Promise<void>;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { query as agentSdkQuery } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { query as claudeCodeQuery } from "@anthropic-ai/claude-code";
|
||||
import { Command, Path } from "@effect/platform";
|
||||
import { Effect } from "effect";
|
||||
import { Data, Effect } from "effect";
|
||||
import { EnvService } from "../../platform/services/EnvService";
|
||||
import * as ClaudeCodeVersion from "./ClaudeCodeVersion";
|
||||
|
||||
@@ -19,25 +19,74 @@ type SharedOptions = Pick<
|
||||
Extract<keyof AgentSdkQueryOptions, keyof CCQueryOptions>
|
||||
>;
|
||||
|
||||
export const Config = Effect.gen(function* () {
|
||||
class ClaudeCodePathNotFoundError extends Data.TaggedError(
|
||||
"ClaudeCodePathNotFoundError",
|
||||
)<{
|
||||
message: string;
|
||||
}> {}
|
||||
|
||||
const resolveClaudeCodePath = Effect.gen(function* () {
|
||||
const path = yield* Path.Path;
|
||||
const envService = yield* EnvService;
|
||||
|
||||
// 1. Environment variable (highest priority)
|
||||
const specifiedExecutablePath = yield* envService.getEnv(
|
||||
"CLAUDE_CODE_VIEWER_CC_EXECUTABLE_PATH",
|
||||
);
|
||||
if (specifiedExecutablePath !== undefined) {
|
||||
return path.resolve(specifiedExecutablePath);
|
||||
}
|
||||
|
||||
const claudeCodeExecutablePath =
|
||||
specifiedExecutablePath !== undefined
|
||||
? path.resolve(specifiedExecutablePath)
|
||||
: (yield* Command.string(
|
||||
Command.make("which", "claude").pipe(
|
||||
Command.env({
|
||||
PATH: yield* envService.getEnv("PATH"),
|
||||
}),
|
||||
Command.runInShell(true),
|
||||
),
|
||||
)).trim();
|
||||
// 2. System PATH lookup
|
||||
const pathEnv = yield* envService.getEnv("PATH");
|
||||
const whichClaude = yield* Command.string(
|
||||
Command.make("which", "claude").pipe(
|
||||
Command.env({
|
||||
PATH: pathEnv,
|
||||
}),
|
||||
Command.runInShell(true),
|
||||
),
|
||||
).pipe(
|
||||
Effect.map((output) => output.trim()),
|
||||
Effect.map((output) => (output === "" ? null : output)), // 存在しない時、空文字になる模様
|
||||
Effect.catchAll(() => Effect.succeed(null)),
|
||||
);
|
||||
|
||||
if (whichClaude !== null) {
|
||||
return whichClaude;
|
||||
}
|
||||
|
||||
// 3. Project dependency @anthropic-ai/claude-code/cli.js (fallback)
|
||||
const projectClaudeCode = yield* Effect.try(() => {
|
||||
// Next.js では import.meta.resolve が使えない
|
||||
// __dirname もバンドルされてしまうため、無理やりパスを組み立てる
|
||||
const parts = __dirname.split("/");
|
||||
const packagePath = parts
|
||||
.slice(0, parts.indexOf("claude-code-viewer") + 1)
|
||||
.join("/");
|
||||
|
||||
return path.join(
|
||||
packagePath,
|
||||
"node_modules",
|
||||
"@anthropic-ai",
|
||||
"claude-code",
|
||||
"cli.js",
|
||||
);
|
||||
}).pipe(
|
||||
Effect.catchAll(() => {
|
||||
return Effect.fail(
|
||||
new ClaudeCodePathNotFoundError({
|
||||
message: "Claude Code CLI not found in any location",
|
||||
}),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
return projectClaudeCode;
|
||||
});
|
||||
|
||||
export const Config = Effect.gen(function* () {
|
||||
const claudeCodeExecutablePath = yield* resolveClaudeCodePath;
|
||||
|
||||
const claudeCodeVersion = ClaudeCodeVersion.fromCLIString(
|
||||
yield* Command.string(Command.make(claudeCodeExecutablePath, "--version")),
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type { SDKMessage, SDKUserMessage } from "@anthropic-ai/claude-code";
|
||||
import type {
|
||||
SDKMessage,
|
||||
SDKUserMessage,
|
||||
} from "@anthropic-ai/claude-agent-sdk";
|
||||
import type { FileSystem, Path } from "@effect/platform";
|
||||
import type { CommandExecutor } from "@effect/platform/CommandExecutor";
|
||||
import { Context, Effect, Layer, Runtime } from "effect";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CanUseTool } from "@anthropic-ai/claude-code";
|
||||
import type { CanUseTool } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { Context, Effect, Layer, Ref } from "effect";
|
||||
import { ulid } from "ulid";
|
||||
import type {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SDKResultMessage } from "@anthropic-ai/claude-code";
|
||||
import type { SDKResultMessage } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { Context, Data, Effect, Layer, Ref } from "effect";
|
||||
import type { InferEffect } from "../../../lib/effect/types";
|
||||
import { EventBus } from "../../events/services/EventBus";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SDKSystemMessage } from "@anthropic-ai/claude-code";
|
||||
import type { SDKSystemMessage } from "@anthropic-ai/claude-agent-sdk";
|
||||
|
||||
export type InitMessageContext = {
|
||||
initMessage: SDKSystemMessage;
|
||||
|
||||
@@ -125,12 +125,6 @@ const LayerImpl = Effect.gen(function* () {
|
||||
Effect.gen(function* () {
|
||||
const { projectId, files, message } = options;
|
||||
|
||||
console.log("[GitController.commitFiles] Request:", {
|
||||
projectId,
|
||||
files,
|
||||
message,
|
||||
});
|
||||
|
||||
const { project } = yield* projectRepository.getProject(projectId);
|
||||
if (project.meta.projectPath === null) {
|
||||
console.log("[GitController.commitFiles] Project path is null");
|
||||
|
||||
@@ -120,9 +120,7 @@ const LayerImpl = Effect.gen(function* () {
|
||||
);
|
||||
}
|
||||
|
||||
console.log("[GitService.stageFiles] Staging files:", files, "in", cwd);
|
||||
const result = yield* execGitCommand(["add", ...files], cwd);
|
||||
console.log("[GitService.stageFiles] Stage result:", result);
|
||||
return result;
|
||||
});
|
||||
|
||||
|
||||