UNPKG

mcp-casual-interview

Version:

MCP server for casual interview preparation workflow management

162 lines 5.64 kB
import { ok, err } from 'neverthrow'; import path from 'path'; import { z } from 'zod'; import { WorkflowPolicies } from '../domain/term/interview-workflow/index.js'; import { getDataDirectory, saveJsonFile, readJsonFile, fileExists } from './filesystem.js'; const WorkflowStateSchema = z.enum([ 'not_started', 'information_initially_registered', 'career_summary_created', 'match_position_determined', 'match_degree_prediagnosed', 'attract_plan_determined', 'completed' ]); const WorkflowStateDataSchema = z.object({ state: WorkflowStateSchema, notionPageUrl: z.string().nullable(), lastUpdated: z.string() }); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Storage Implementation // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ const getFilePath = () => { const dataDir = getDataDirectory(); return path.join(dataDir, 'workflow-state.json'); }; const getDefaultState = () => ({ state: 'not_started', notionPageUrl: null, lastUpdated: new Date().toISOString() }); const mapFileSystemError = (error) => ({ type: 'FileSystemError', message: error.message, cause: error.cause }); /** * Get current workflow state */ export const getCurrentState = async () => { const filePath = getFilePath(); const existsResult = await fileExists(filePath); if (existsResult.isErr()) { return err(mapFileSystemError(existsResult.error)); } if (!existsResult.value) { // File doesn't exist, create with default state const defaultState = getDefaultState(); const saveResult = await saveJsonFile(filePath, defaultState); if (saveResult.isErr()) { return err(mapFileSystemError(saveResult.error)); } return ok(defaultState); } const readResult = await readJsonFile(filePath); if (readResult.isErr()) { return err(mapFileSystemError(readResult.error)); } // Validate data structure const parseResult = WorkflowStateDataSchema.safeParse(readResult.value); if (!parseResult.success) { return err({ type: 'ValidationError', message: `ワークフロー状態データの検証に失敗しました: ${parseResult.error.message}`, cause: parseResult.error }); } return ok(parseResult.data); }; /** * Update workflow state with transition validation */ export const updateState = async (newState) => { const currentResult = await getCurrentState(); if (currentResult.isErr()) { return err(currentResult.error); } const currentData = currentResult.value; // Validate state transition if (!WorkflowPolicies.StateTransitions.canTransition(currentData.state, newState)) { return err({ type: 'InvalidStateTransition', message: `無効な状態遷移です: ${currentData.state} から ${newState} への遷移は許可されていません` }); } const updatedData = { ...currentData, state: newState, lastUpdated: new Date().toISOString() }; const filePath = getFilePath(); const saveResult = await saveJsonFile(filePath, updatedData); if (saveResult.isErr()) { return err(mapFileSystemError(saveResult.error)); } return ok(updatedData); }; /** * Force set workflow state (bypasses transition validation) */ export const forceSetState = async (newState) => { const currentResult = await getCurrentState(); if (currentResult.isErr()) { return err(currentResult.error); } const currentData = currentResult.value; const updatedData = { ...currentData, state: newState, lastUpdated: new Date().toISOString() }; const filePath = getFilePath(); const saveResult = await saveJsonFile(filePath, updatedData); if (saveResult.isErr()) { return err(mapFileSystemError(saveResult.error)); } return ok(updatedData); }; /** * Update notion page URL */ export const updateNotionPageUrl = async (url) => { const currentResult = await getCurrentState(); if (currentResult.isErr()) { return err(currentResult.error); } const currentData = currentResult.value; const updatedData = { ...currentData, notionPageUrl: url, lastUpdated: new Date().toISOString() }; const filePath = getFilePath(); const saveResult = await saveJsonFile(filePath, updatedData); if (saveResult.isErr()) { return err(mapFileSystemError(saveResult.error)); } return ok(updatedData); }; /** * Get notion page URL */ export const getNotionPageUrl = async () => { const currentResult = await getCurrentState(); if (currentResult.isErr()) { return err(currentResult.error); } return ok(currentResult.value.notionPageUrl ?? "未登録です"); }; /** * Clear workflow state (reset to default) */ export const clearState = async () => { const defaultState = getDefaultState(); const filePath = getFilePath(); const saveResult = await saveJsonFile(filePath, defaultState); if (saveResult.isErr()) { return err(mapFileSystemError(saveResult.error)); } return ok(defaultState); }; //# sourceMappingURL=workflow-state-storage.js.map