feat(features): add claude-code-agent-loader, mcp-loader, session-state
This commit is contained in:
91
notepad.md
91
notepad.md
@@ -225,3 +225,94 @@ All tasks execution STARTED: Thu Dec 4 16:52:57 KST 2025
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [2025-12-09 16:24] - Task 4: Add claude-code-agent-loader feature
|
||||||
|
|
||||||
|
### DISCOVERED ISSUES
|
||||||
|
- None - straightforward file copy task
|
||||||
|
|
||||||
|
### IMPLEMENTATION DECISIONS
|
||||||
|
- Copied 3 files from opencode-cc-plugin: `index.ts`, `loader.ts`, `types.ts`
|
||||||
|
- Import path `../../shared/frontmatter` unchanged - already compatible with oh-my-opencode structure
|
||||||
|
- No `log()` usage in source files - no logger integration needed
|
||||||
|
|
||||||
|
### PROBLEMS FOR NEXT TASKS
|
||||||
|
- None identified - agent-loader is self-contained
|
||||||
|
|
||||||
|
### VERIFICATION RESULTS
|
||||||
|
- Ran: `bun run typecheck` → exit 0, no errors
|
||||||
|
- Directory structure verified: `claude-code-agent-loader/` created with 3 files
|
||||||
|
- Functions exported: `loadUserAgents()`, `loadProjectAgents()`
|
||||||
|
|
||||||
|
### LEARNINGS
|
||||||
|
- Source location: `~/local-workspaces/opencode-cc-plugin/src/features/agent-loader/`
|
||||||
|
- Agent loader uses `parseFrontmatter` from shared module
|
||||||
|
- Agent configs loaded from `~/.claude/agents/` (user) and `.claude/agents/` (project)
|
||||||
|
- Scope is appended to description: `(user)` or `(project)`
|
||||||
|
|
||||||
|
소요 시간: ~1분
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [2025-12-09 16:25] - Task 5: Add claude-code-mcp-loader feature
|
||||||
|
|
||||||
|
### DISCOVERED ISSUES
|
||||||
|
- None - straightforward file copy task
|
||||||
|
|
||||||
|
### IMPLEMENTATION DECISIONS
|
||||||
|
- Copied 5 files from opencode-cc-plugin: `index.ts`, `loader.ts`, `transformer.ts`, `env-expander.ts`, `types.ts`
|
||||||
|
- Import path `../../shared/logger` unchanged - already compatible with oh-my-opencode structure
|
||||||
|
- Kept `Bun.file()` usage - oh-my-opencode targets Bun runtime
|
||||||
|
- Environment variable expansion supports `${VAR}` and `${VAR:-default}` syntax
|
||||||
|
|
||||||
|
### PROBLEMS FOR NEXT TASKS
|
||||||
|
- None identified - mcp-loader is self-contained
|
||||||
|
- Does NOT conflict with src/mcp/ (builtin MCPs are separate)
|
||||||
|
|
||||||
|
### VERIFICATION RESULTS
|
||||||
|
- Ran: `bun run typecheck` → exit 0, no errors
|
||||||
|
- Directory structure verified: `claude-code-mcp-loader/` created with 5 files
|
||||||
|
- Functions exported: `loadMcpConfigs()`, `formatLoadedServersForToast()`, `transformMcpServer()`, `expandEnvVars()`, `expandEnvVarsInObject()`
|
||||||
|
|
||||||
|
### LEARNINGS
|
||||||
|
- Source location: `~/local-workspaces/opencode-cc-plugin/src/features/mcp-loader/`
|
||||||
|
- MCP configs loaded from:
|
||||||
|
- `~/.claude/.mcp.json` (user scope)
|
||||||
|
- `.mcp.json` (project scope)
|
||||||
|
- `.claude/.mcp.json` (local scope)
|
||||||
|
- Later scope overrides earlier scope for same server name
|
||||||
|
- Supports stdio, http, and sse server types
|
||||||
|
|
||||||
|
소요 시간: ~1분
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [2025-12-09 16:24] - Task 6: Add claude-code-session-state feature
|
||||||
|
|
||||||
|
### DISCOVERED ISSUES
|
||||||
|
- None - straightforward file copy task
|
||||||
|
|
||||||
|
### IMPLEMENTATION DECISIONS
|
||||||
|
- Copied 4 files from opencode-cc-plugin: `types.ts`, `state.ts`, `detector.ts`, `index.ts`
|
||||||
|
- No import path changes needed - files are completely self-contained
|
||||||
|
- No external dependencies - types are defined locally
|
||||||
|
|
||||||
|
### PROBLEMS FOR NEXT TASKS
|
||||||
|
- Task 7 should import from `./features/claude-code-session-state` in src/index.ts
|
||||||
|
- Task 7 should remove local session variables and use the module's getter/setters
|
||||||
|
|
||||||
|
### VERIFICATION RESULTS
|
||||||
|
- Directory created: `src/features/claude-code-session-state/` (4 files confirmed)
|
||||||
|
- Exports available: sessionErrorState, sessionInterruptState, subagentSessions, sessionFirstMessageProcessed (Maps/Sets)
|
||||||
|
- Exports available: currentSessionID, currentSessionTitle, mainSessionID (state vars)
|
||||||
|
- Exports available: setCurrentSession(), setMainSession(), getCurrentSessionID(), getCurrentSessionTitle(), getMainSessionID() (getters/setters)
|
||||||
|
- Exports available: detectInterrupt() function
|
||||||
|
|
||||||
|
### LEARNINGS
|
||||||
|
- Session state module is completely self-contained - no external dependencies
|
||||||
|
- Uses barrel export pattern: index.ts re-exports everything from types, state, detector
|
||||||
|
- Source directory: `~/local-workspaces/opencode-cc-plugin/src/features/session-state/`
|
||||||
|
|
||||||
|
소요 시간: ~1분
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|||||||
2
src/features/claude-code-agent-loader/index.ts
Normal file
2
src/features/claude-code-agent-loader/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./types"
|
||||||
|
export * from "./loader"
|
||||||
93
src/features/claude-code-agent-loader/loader.ts
Normal file
93
src/features/claude-code-agent-loader/loader.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import { existsSync, readdirSync, readFileSync } from "fs"
|
||||||
|
import { homedir } from "os"
|
||||||
|
import { join, basename } from "path"
|
||||||
|
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||||
|
import { parseFrontmatter } from "../../shared/frontmatter"
|
||||||
|
import type { AgentScope, AgentFrontmatter, LoadedAgent } from "./types"
|
||||||
|
|
||||||
|
function parseToolsConfig(toolsStr?: string): Record<string, boolean> | undefined {
|
||||||
|
if (!toolsStr) return undefined
|
||||||
|
|
||||||
|
const tools = toolsStr.split(",").map((t) => t.trim()).filter(Boolean)
|
||||||
|
if (tools.length === 0) return undefined
|
||||||
|
|
||||||
|
const result: Record<string, boolean> = {}
|
||||||
|
for (const tool of tools) {
|
||||||
|
result[tool.toLowerCase()] = true
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMarkdownFile(entry: { name: string; isFile: () => boolean }): boolean {
|
||||||
|
return !entry.name.startsWith(".") && entry.name.endsWith(".md") && entry.isFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadAgentsFromDir(agentsDir: string, scope: AgentScope): LoadedAgent[] {
|
||||||
|
if (!existsSync(agentsDir)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = readdirSync(agentsDir, { withFileTypes: true })
|
||||||
|
const agents: LoadedAgent[] = []
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!isMarkdownFile(entry)) continue
|
||||||
|
|
||||||
|
const agentPath = join(agentsDir, entry.name)
|
||||||
|
const agentName = basename(entry.name, ".md")
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = readFileSync(agentPath, "utf-8")
|
||||||
|
const { data, body } = parseFrontmatter<AgentFrontmatter>(content)
|
||||||
|
|
||||||
|
const name = data.name || agentName
|
||||||
|
const originalDescription = data.description || ""
|
||||||
|
|
||||||
|
const formattedDescription = `(${scope}) ${originalDescription}`
|
||||||
|
|
||||||
|
const config: AgentConfig = {
|
||||||
|
description: formattedDescription,
|
||||||
|
mode: "subagent",
|
||||||
|
prompt: body.trim(),
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolsConfig = parseToolsConfig(data.tools)
|
||||||
|
if (toolsConfig) {
|
||||||
|
config.tools = toolsConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
agents.push({
|
||||||
|
name,
|
||||||
|
path: agentPath,
|
||||||
|
config,
|
||||||
|
scope,
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return agents
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadUserAgents(): Record<string, AgentConfig> {
|
||||||
|
const userAgentsDir = join(homedir(), ".claude", "agents")
|
||||||
|
const agents = loadAgentsFromDir(userAgentsDir, "user")
|
||||||
|
|
||||||
|
const result: Record<string, AgentConfig> = {}
|
||||||
|
for (const agent of agents) {
|
||||||
|
result[agent.name] = agent.config
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadProjectAgents(): Record<string, AgentConfig> {
|
||||||
|
const projectAgentsDir = join(process.cwd(), ".claude", "agents")
|
||||||
|
const agents = loadAgentsFromDir(projectAgentsDir, "project")
|
||||||
|
|
||||||
|
const result: Record<string, AgentConfig> = {}
|
||||||
|
for (const agent of agents) {
|
||||||
|
result[agent.name] = agent.config
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
17
src/features/claude-code-agent-loader/types.ts
Normal file
17
src/features/claude-code-agent-loader/types.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||||
|
|
||||||
|
export type AgentScope = "user" | "project"
|
||||||
|
|
||||||
|
export interface AgentFrontmatter {
|
||||||
|
name?: string
|
||||||
|
description?: string
|
||||||
|
model?: string
|
||||||
|
tools?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoadedAgent {
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
config: AgentConfig
|
||||||
|
scope: AgentScope
|
||||||
|
}
|
||||||
27
src/features/claude-code-mcp-loader/env-expander.ts
Normal file
27
src/features/claude-code-mcp-loader/env-expander.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
export function expandEnvVars(value: string): string {
|
||||||
|
return value.replace(
|
||||||
|
/\$\{([^}:]+)(?::-([^}]*))?\}/g,
|
||||||
|
(_, varName: string, defaultValue?: string) => {
|
||||||
|
const envValue = process.env[varName]
|
||||||
|
if (envValue !== undefined) return envValue
|
||||||
|
if (defaultValue !== undefined) return defaultValue
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function expandEnvVarsInObject<T>(obj: T): T {
|
||||||
|
if (obj === null || obj === undefined) return obj
|
||||||
|
if (typeof obj === "string") return expandEnvVars(obj) as T
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return obj.map((item) => expandEnvVarsInObject(item)) as T
|
||||||
|
}
|
||||||
|
if (typeof obj === "object") {
|
||||||
|
const result: Record<string, unknown> = {}
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
result[key] = expandEnvVarsInObject(value)
|
||||||
|
}
|
||||||
|
return result as T
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
11
src/features/claude-code-mcp-loader/index.ts
Normal file
11
src/features/claude-code-mcp-loader/index.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* MCP Configuration Loader
|
||||||
|
*
|
||||||
|
* Loads Claude Code .mcp.json format configurations from multiple scopes
|
||||||
|
* and transforms them to OpenCode SDK format
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from "./types"
|
||||||
|
export * from "./loader"
|
||||||
|
export * from "./transformer"
|
||||||
|
export * from "./env-expander"
|
||||||
89
src/features/claude-code-mcp-loader/loader.ts
Normal file
89
src/features/claude-code-mcp-loader/loader.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { existsSync } from "fs"
|
||||||
|
import { homedir } from "os"
|
||||||
|
import { join } from "path"
|
||||||
|
import type {
|
||||||
|
ClaudeCodeMcpConfig,
|
||||||
|
LoadedMcpServer,
|
||||||
|
McpLoadResult,
|
||||||
|
McpScope,
|
||||||
|
} from "./types"
|
||||||
|
import { transformMcpServer } from "./transformer"
|
||||||
|
import { log } from "../../shared/logger"
|
||||||
|
|
||||||
|
interface McpConfigPath {
|
||||||
|
path: string
|
||||||
|
scope: McpScope
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMcpConfigPaths(): McpConfigPath[] {
|
||||||
|
const home = homedir()
|
||||||
|
const cwd = process.cwd()
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ path: join(home, ".claude", ".mcp.json"), scope: "user" },
|
||||||
|
{ path: join(cwd, ".mcp.json"), scope: "project" },
|
||||||
|
{ path: join(cwd, ".claude", ".mcp.json"), scope: "local" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadMcpConfigFile(
|
||||||
|
filePath: string
|
||||||
|
): Promise<ClaudeCodeMcpConfig | null> {
|
||||||
|
if (!existsSync(filePath)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = await Bun.file(filePath).text()
|
||||||
|
return JSON.parse(content) as ClaudeCodeMcpConfig
|
||||||
|
} catch (error) {
|
||||||
|
log(`Failed to load MCP config from ${filePath}`, error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadMcpConfigs(): Promise<McpLoadResult> {
|
||||||
|
const servers: McpLoadResult["servers"] = {}
|
||||||
|
const loadedServers: LoadedMcpServer[] = []
|
||||||
|
const paths = getMcpConfigPaths()
|
||||||
|
|
||||||
|
for (const { path, scope } of paths) {
|
||||||
|
const config = await loadMcpConfigFile(path)
|
||||||
|
if (!config?.mcpServers) continue
|
||||||
|
|
||||||
|
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
||||||
|
if (serverConfig.disabled) {
|
||||||
|
log(`Skipping disabled MCP server "${name}"`, { path })
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const transformed = transformMcpServer(name, serverConfig)
|
||||||
|
servers[name] = transformed
|
||||||
|
|
||||||
|
const existingIndex = loadedServers.findIndex((s) => s.name === name)
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
loadedServers.splice(existingIndex, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedServers.push({ name, scope, config: transformed })
|
||||||
|
|
||||||
|
log(`Loaded MCP server "${name}" from ${scope}`, { path })
|
||||||
|
} catch (error) {
|
||||||
|
log(`Failed to transform MCP server "${name}"`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { servers, loadedServers }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatLoadedServersForToast(
|
||||||
|
loadedServers: LoadedMcpServer[]
|
||||||
|
): string {
|
||||||
|
if (loadedServers.length === 0) return ""
|
||||||
|
|
||||||
|
return loadedServers
|
||||||
|
.map((server) => `${server.name} (${server.scope})`)
|
||||||
|
.join(", ")
|
||||||
|
}
|
||||||
53
src/features/claude-code-mcp-loader/transformer.ts
Normal file
53
src/features/claude-code-mcp-loader/transformer.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import type {
|
||||||
|
ClaudeCodeMcpServer,
|
||||||
|
McpLocalConfig,
|
||||||
|
McpRemoteConfig,
|
||||||
|
McpServerConfig,
|
||||||
|
} from "./types"
|
||||||
|
import { expandEnvVarsInObject } from "./env-expander"
|
||||||
|
|
||||||
|
export function transformMcpServer(
|
||||||
|
name: string,
|
||||||
|
server: ClaudeCodeMcpServer
|
||||||
|
): McpServerConfig {
|
||||||
|
const expanded = expandEnvVarsInObject(server)
|
||||||
|
const serverType = expanded.type ?? "stdio"
|
||||||
|
|
||||||
|
if (serverType === "http" || serverType === "sse") {
|
||||||
|
if (!expanded.url) {
|
||||||
|
throw new Error(
|
||||||
|
`MCP server "${name}" requires url for type "${serverType}"`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const config: McpRemoteConfig = {
|
||||||
|
type: "remote",
|
||||||
|
url: expanded.url,
|
||||||
|
enabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expanded.headers && Object.keys(expanded.headers).length > 0) {
|
||||||
|
config.headers = expanded.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expanded.command) {
|
||||||
|
throw new Error(`MCP server "${name}" requires command for stdio type`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const commandArray = [expanded.command, ...(expanded.args ?? [])]
|
||||||
|
|
||||||
|
const config: McpLocalConfig = {
|
||||||
|
type: "local",
|
||||||
|
command: commandArray,
|
||||||
|
enabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expanded.env && Object.keys(expanded.env).length > 0) {
|
||||||
|
config.environment = expanded.env
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
42
src/features/claude-code-mcp-loader/types.ts
Normal file
42
src/features/claude-code-mcp-loader/types.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
export type McpScope = "user" | "project" | "local"
|
||||||
|
|
||||||
|
export interface ClaudeCodeMcpServer {
|
||||||
|
type?: "http" | "sse" | "stdio"
|
||||||
|
url?: string
|
||||||
|
command?: string
|
||||||
|
args?: string[]
|
||||||
|
env?: Record<string, string>
|
||||||
|
headers?: Record<string, string>
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ClaudeCodeMcpConfig {
|
||||||
|
mcpServers?: Record<string, ClaudeCodeMcpServer>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface McpLocalConfig {
|
||||||
|
type: "local"
|
||||||
|
command: string[]
|
||||||
|
environment?: Record<string, string>
|
||||||
|
enabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface McpRemoteConfig {
|
||||||
|
type: "remote"
|
||||||
|
url: string
|
||||||
|
headers?: Record<string, string>
|
||||||
|
enabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type McpServerConfig = McpLocalConfig | McpRemoteConfig
|
||||||
|
|
||||||
|
export interface LoadedMcpServer {
|
||||||
|
name: string
|
||||||
|
scope: McpScope
|
||||||
|
config: McpServerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface McpLoadResult {
|
||||||
|
servers: Record<string, McpServerConfig>
|
||||||
|
loadedServers: LoadedMcpServer[]
|
||||||
|
}
|
||||||
21
src/features/claude-code-session-state/detector.ts
Normal file
21
src/features/claude-code-session-state/detector.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
export function detectInterrupt(error: unknown): boolean {
|
||||||
|
if (!error) return false
|
||||||
|
|
||||||
|
if (typeof error === "object") {
|
||||||
|
const errObj = error as Record<string, unknown>
|
||||||
|
const name = errObj.name as string | undefined
|
||||||
|
const message = errObj.message as string | undefined
|
||||||
|
|
||||||
|
if (name === "MessageAbortedError" || name === "AbortError") return true
|
||||||
|
if (name === "DOMException" && message?.includes("abort")) return true
|
||||||
|
const msgLower = message?.toLowerCase()
|
||||||
|
if (msgLower?.includes("aborted") || msgLower?.includes("cancelled") || msgLower?.includes("interrupted")) return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof error === "string") {
|
||||||
|
const lower = error.toLowerCase()
|
||||||
|
return lower.includes("abort") || lower.includes("cancel") || lower.includes("interrupt")
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
3
src/features/claude-code-session-state/index.ts
Normal file
3
src/features/claude-code-session-state/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export * from "./types"
|
||||||
|
export * from "./state"
|
||||||
|
export * from "./detector"
|
||||||
31
src/features/claude-code-session-state/state.ts
Normal file
31
src/features/claude-code-session-state/state.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import type { SessionErrorState, SessionInterruptState } from "./types"
|
||||||
|
|
||||||
|
export const sessionErrorState = new Map<string, SessionErrorState>()
|
||||||
|
export const sessionInterruptState = new Map<string, SessionInterruptState>()
|
||||||
|
export const subagentSessions = new Set<string>()
|
||||||
|
export const sessionFirstMessageProcessed = new Set<string>()
|
||||||
|
|
||||||
|
export let currentSessionID: string | undefined
|
||||||
|
export let currentSessionTitle: string | undefined
|
||||||
|
export let mainSessionID: string | undefined
|
||||||
|
|
||||||
|
export function setCurrentSession(id: string | undefined, title: string | undefined) {
|
||||||
|
currentSessionID = id
|
||||||
|
currentSessionTitle = title
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setMainSession(id: string | undefined) {
|
||||||
|
mainSessionID = id
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentSessionID(): string | undefined {
|
||||||
|
return currentSessionID
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentSessionTitle(): string | undefined {
|
||||||
|
return currentSessionTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMainSessionID(): string | undefined {
|
||||||
|
return mainSessionID
|
||||||
|
}
|
||||||
8
src/features/claude-code-session-state/types.ts
Normal file
8
src/features/claude-code-session-state/types.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export interface SessionErrorState {
|
||||||
|
hasError: boolean
|
||||||
|
errorMessage?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SessionInterruptState {
|
||||||
|
interrupted: boolean
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user