roadmap-skill 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,7 +30,7 @@
30
30
  animation: slideUp 0.3s ease-out;
31
31
  }
32
32
  </style>
33
- <script type="module" crossorigin src="./assets/main-BJPGJG4y.js"></script>
33
+ <script type="module" crossorigin src="./assets/main-DUDWZy_5.js"></script>
34
34
  </head>
35
35
  <body class="roadmap-grid">
36
36
  <div id="root"></div>
@@ -403,9 +403,18 @@ var TagService = class {
403
403
  code: "NOT_FOUND"
404
404
  };
405
405
  }
406
+ const taskTagCounts = /* @__PURE__ */ new Map();
407
+ for (const task of projectData.tasks) {
408
+ for (const tagId of task.tags) {
409
+ taskTagCounts.set(tagId, (taskTagCounts.get(tagId) ?? 0) + 1);
410
+ }
411
+ }
406
412
  return {
407
413
  success: true,
408
- data: projectData.tags
414
+ data: projectData.tags.map((tag) => ({
415
+ ...tag,
416
+ taskCount: taskTagCounts.get(tag.id) ?? 0
417
+ }))
409
418
  };
410
419
  } catch (error) {
411
420
  return {
@@ -934,13 +943,14 @@ function resolveAppPath() {
934
943
  candidates.push({ path: path4.join(pkgRoot, "dist/web/app"), source: "package.json resolve" });
935
944
  } catch {
936
945
  }
946
+ candidates.push({ path: path4.join(__dirname2, "web/app"), source: "__dirname/web/app" });
937
947
  candidates.push({ path: path4.join(__dirname2, "app"), source: "__dirname relative" });
938
948
  candidates.push({ path: path4.join(process.cwd(), "dist/web/app"), source: "process.cwd()" });
939
949
  candidates.push({ path: path4.join(__dirname2, "../web/app"), source: "__dirname/../web/app" });
940
950
  for (const { path: candidatePath, source } of candidates) {
941
951
  const indexPath = path4.join(candidatePath, "index.html");
942
952
  if (existsSync(indexPath)) {
943
- console.log(`[roadmap-skill] Static files found at: ${candidatePath} (via ${source})`);
953
+ console.error(`[roadmap-skill] Static files found at: ${candidatePath} (via ${source})`);
944
954
  return candidatePath;
945
955
  }
946
956
  }
@@ -1148,7 +1158,7 @@ function createServer(port = 7860) {
1148
1158
  });
1149
1159
  const server = app.listen(port, "127.0.0.1");
1150
1160
  server.once("listening", () => {
1151
- console.log(`Web interface server running at http://localhost:${port}`);
1161
+ console.error(`Web interface server running at http://localhost:${port}`);
1152
1162
  resolve(server);
1153
1163
  });
1154
1164
  server.once("error", (error) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../node_modules/tsup/assets/esm_shims.js","../../src/utils/file-helpers.ts","../../src/web/server.ts","../../src/storage/index.ts","../../src/utils/path-helpers.ts","../../src/services/index.ts","../../src/services/tag-service.ts","../../src/services/task-service.ts","../../src/services/types.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import * as fs from 'fs/promises';\n\n/**\n * Read and parse a JSON file\n * @param filePath - Path to the JSON file\n * @returns Parsed JSON data\n * @throws Error if file cannot be read or parsed\n */\nexport async function readJsonFile<T>(filePath: string): Promise<T> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to read JSON file ${filePath}: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Write data to a JSON file\n * @param filePath - Path to the JSON file\n * @param data - Data to serialize and write\n * @throws Error if file cannot be written\n */\nexport async function writeJsonFile<T>(filePath: string, data: T): Promise<void> {\n try {\n const content = JSON.stringify(data, null, 2);\n await fs.writeFile(filePath, content, 'utf-8');\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to write JSON file ${filePath}: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Ensure a directory exists, creating it recursively if needed\n * @param dirPath - Path to the directory\n * @throws Error if directory cannot be created\n */\nexport async function ensureDir(dirPath: string): Promise<void> {\n try {\n await fs.mkdir(dirPath, { recursive: true });\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);\n }\n throw error;\n }\n}\n","import express from 'express';\nimport * as path from 'path';\nimport { fileURLToPath } from 'url';\nimport { createRequire } from 'node:module';\nimport { existsSync } from 'node:fs';\nimport type { Server } from 'http';\nimport { storage } from '../storage/index.js';\nimport { TaskService, TagService } from '../services/index.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/**\n * Resolve the path to the web app static files using multiple fallback strategies.\n * Handles npx execution, global installation, local development, and bundled execution.\n */\nfunction resolveAppPath(): string {\n const candidates: Array<{ path: string; source: string }> = [];\n\n try {\n const require = createRequire(import.meta.url);\n const pkgRoot = path.dirname(require.resolve('../../package.json'));\n candidates.push({ path: path.join(pkgRoot, 'dist/web/app'), source: 'package.json resolve' });\n } catch {\n // Package.json not resolvable\n }\n\n candidates.push({ path: path.join(__dirname, 'app'), source: '__dirname relative' });\n candidates.push({ path: path.join(process.cwd(), 'dist/web/app'), source: 'process.cwd()' });\n candidates.push({ path: path.join(__dirname, '../web/app'), source: '__dirname/../web/app' });\n\n for (const { path: candidatePath, source } of candidates) {\n const indexPath = path.join(candidatePath, 'index.html');\n if (existsSync(indexPath)) {\n console.log(`[roadmap-skill] Static files found at: ${candidatePath} (via ${source})`);\n return candidatePath;\n }\n }\n\n const triedPaths = candidates.map(c => ` - ${c.path} (${c.source})`).join('\\n');\n throw new Error(\n `Cannot find web app static files.\\n\\n` +\n `Tried:\\n${triedPaths}\\n\\n` +\n `Ensure the package is installed correctly and web assets exist.`\n );\n}\n\nconst tagService = new TagService(storage);\n\nexport function createServer(port: number = 7860): Promise<Server> {\n return new Promise((resolve, reject) => {\n const app = express();\n\n app.use(express.json());\n\n app.get('/api/projects', async (_req, res) => {\n try {\n const projects = await storage.listProjects();\n res.json(projects);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/projects/:id', async (req, res) => {\n try {\n const project = await storage.readProject(req.params.id);\n if (!project) {\n res.status(404).json({ error: 'Project not found' });\n return;\n }\n res.json(project);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/tasks', async (req, res) => {\n try {\n const filters: Record<string, unknown> = { ...req.query };\n\n // Convert includeCompleted from string to boolean\n if (filters.includeCompleted !== undefined) {\n filters.includeCompleted = filters.includeCompleted === 'true';\n }\n\n const tasks = await storage.searchTasks(filters as any);\n res.json(tasks);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/projects', async (req, res) => {\n try {\n const project = await storage.createProject(req.body);\n res.json({ success: true, data: project });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.put('/api/projects', async (req, res) => {\n try {\n const { projectId, ...updateData } = req.body;\n const project = await storage.updateProject(projectId, updateData);\n res.json({ success: true, data: project });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.delete('/api/projects', async (req, res) => {\n try {\n const { projectId } = req.query;\n await storage.deleteProject(projectId as string);\n res.json({ success: true });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/tasks', async (req, res) => {\n try {\n const { projectId, ...taskData } = req.body;\n const result = await TaskService.create(projectId, taskData);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.put('/api/tasks', async (req, res) => {\n try {\n const { projectId, taskId, ...updateData } = req.body;\n const result = await TaskService.update(projectId, taskId, updateData);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.delete('/api/tasks', async (req, res) => {\n try {\n const { projectId, taskId } = req.query;\n const result = await TaskService.delete(projectId as string, taskId as string);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/projects/:projectId/tags', async (req, res) => {\n try {\n const { projectId } = req.params;\n const result = await tagService.create(projectId, req.body);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/projects/:projectId/tags', async (req, res) => {\n try {\n const { projectId } = req.params;\n const result = await tagService.list(projectId);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.put('/api/projects/:projectId/tags/:tagId', async (req, res) => {\n try {\n const { projectId, tagId } = req.params;\n const result = await tagService.update(projectId, tagId, req.body);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.delete('/api/projects/:projectId/tags/:tagId', async (req, res) => {\n try {\n const { projectId, tagId } = req.params;\n const result = await tagService.delete(projectId, tagId);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/backup', async (_req, res) => {\n try {\n const backup = await storage.exportAllData();\n const filename = `roadmap-skill-backup-${new Date().toISOString().split('T')[0]}.json`;\n res.setHeader('Content-Type', 'application/json');\n res.setHeader('Content-Disposition', `attachment; filename=\"${filename}\"`);\n res.json(backup);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/backup', async (req, res) => {\n try {\n const result = await storage.importAllData(req.body);\n res.json(result);\n } catch (error) {\n res.status(400).json({\n success: false,\n error: (error as Error).message,\n });\n }\n });\n\n const distPath = resolveAppPath();\n app.use(express.static(distPath));\n\n app.get('*', (req, res) => {\n if (req.path.startsWith('/api')) {\n res.status(404).json({ error: 'API not found' });\n return;\n }\n res.sendFile(path.join(distPath, 'index.html'));\n });\n\n const server = app.listen(port, '127.0.0.1');\n\n server.once('listening', () => {\n console.log(`Web interface server running at http://localhost:${port}`);\n resolve(server);\n });\n\n server.once('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EADDRINUSE') {\n reject(new Error(`Port ${port} is already in use`));\n return;\n }\n\n reject(error);\n });\n });\n}\n\n// Start server if run directly\nif (process.argv[1]?.endsWith('server.js')) {\n const port = parseInt(process.argv[2] || '7860', 10);\n createServer(port).catch(err => {\n console.error('Failed to start server:', err);\n process.exit(1);\n });\n}\n","import * as path from 'path';\nimport type {\n Project,\n ProjectData,\n Task,\n TaskSearchFilters,\n CreateProjectInput,\n UpdateProjectInput,\n} from '../models/index.js';\nimport { getStorageDir } from '../utils/path-helpers.js';\nimport { readJsonFile, writeJsonFile, ensureDir } from '../utils/file-helpers.js';\n\n/**\n * Storage class for managing roadmap-skill projects\n * Projects are stored as individual JSON files in ~/.roadmap-skill/projects/\n */\nexport class ProjectStorage {\n private storageDir: string;\n\n constructor() {\n this.storageDir = getStorageDir();\n }\n\n /**\n * Ensure the storage directory exists\n */\n async ensureDirectory(): Promise<void> {\n await ensureDir(this.storageDir);\n }\n\n /**\n * Get the file path for a project\n * @param projectId - The project ID\n * @returns Full path to the project JSON file\n */\n getFilePath(projectId: string): string {\n return path.join(this.storageDir, `${projectId}.json`);\n }\n\n /**\n * Create a new project\n * @param input - Project creation data\n * @returns The created project data\n */\n async createProject(input: CreateProjectInput): Promise<ProjectData> {\n await this.ensureDirectory();\n\n const now = new Date().toISOString();\n const projectId = `proj_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n const project: Project = {\n id: projectId,\n name: input.name,\n description: input.description,\n projectType: input.projectType,\n status: 'active',\n startDate: input.startDate,\n targetDate: input.targetDate,\n createdAt: now,\n updatedAt: now,\n };\n\n const projectData: ProjectData = {\n version: 1,\n project,\n milestones: [],\n tasks: [],\n tags: [],\n };\n\n const filePath = this.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return projectData;\n }\n\n /**\n * Read a project by ID\n * @param projectId - The project ID\n * @returns The project data or null if not found\n */\n async readProject(projectId: string): Promise<ProjectData | null> {\n try {\n const filePath = this.getFilePath(projectId);\n return await readJsonFile<ProjectData>(filePath);\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Update an existing project\n * @param projectId - The project ID\n * @param input - Project update data\n * @returns The updated project data or null if not found\n */\n async updateProject(\n projectId: string,\n input: UpdateProjectInput\n ): Promise<ProjectData | null> {\n const projectData = await this.readProject(projectId);\n if (!projectData) {\n return null;\n }\n\n const now = new Date().toISOString();\n\n projectData.project = {\n ...projectData.project,\n ...input,\n updatedAt: now,\n };\n\n const filePath = this.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return projectData;\n }\n\n /**\n * Delete a project by ID\n * @param projectId - The project ID\n * @returns True if deleted, false if not found\n */\n async deleteProject(projectId: string): Promise<boolean> {\n try {\n const filePath = this.getFilePath(projectId);\n const fs = await import('fs/promises');\n await fs.unlink(filePath);\n return true;\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * List all projects sorted by updatedAt (descending)\n * @returns Array of project summaries (project + metadata)\n */\n async listProjects(): Promise<Array<{ project: Project; taskCount: number; milestoneCount: number }>> {\n await this.ensureDirectory();\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.storageDir);\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const projects: Array<{ project: Project; taskCount: number; milestoneCount: number }> = [];\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.storageDir, file);\n const data = await readJsonFile<ProjectData>(filePath);\n projects.push({\n project: data.project,\n taskCount: data.tasks.length,\n milestoneCount: data.milestones.length,\n });\n } catch {\n // Skip invalid files\n continue;\n }\n }\n\n // Sort by updatedAt descending\n return projects.sort(\n (a, b) => new Date(b.project.updatedAt).getTime() - new Date(a.project.updatedAt).getTime()\n );\n }\n\n /**\n * Search tasks across all projects with filters\n * @param filters - Search filters\n * @returns Array of matching tasks with project context\n */\n async searchTasks(\n filters: TaskSearchFilters\n ): Promise<Array<{ task: Task; project: Project }>> {\n await this.ensureDirectory();\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.storageDir);\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const results: Array<{ task: Task; project: Project }> = [];\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.storageDir, file);\n const data = await readJsonFile<ProjectData>(filePath);\n\n // Skip if project filter doesn't match\n if (filters.projectId && data.project.id !== filters.projectId) {\n continue;\n }\n\n for (const task of data.tasks) {\n // Apply filters\n if (filters.status && task.status !== filters.status) {\n continue;\n }\n if (filters.priority && task.priority !== filters.priority) {\n continue;\n }\n if (filters.assignee && task.assignee !== filters.assignee) {\n continue;\n }\n if (filters.dueBefore && task.dueDate && task.dueDate > filters.dueBefore) {\n continue;\n }\n if (filters.dueAfter && task.dueDate && task.dueDate < filters.dueAfter) {\n continue;\n }\n if (\n filters.tags &&\n filters.tags.length > 0 &&\n !filters.tags.some((tag) => task.tags.includes(tag))\n ) {\n continue;\n }\n if (\n filters.searchText &&\n !task.title.toLowerCase().includes(filters.searchText.toLowerCase()) &&\n !task.description.toLowerCase().includes(filters.searchText.toLowerCase())\n ) {\n continue;\n }\n\n // Filter completed tasks (done status) when includeCompleted is false\n if (filters.includeCompleted === false && task.status === 'done') {\n continue;\n }\n\n results.push({ task, project: data.project });\n }\n } catch {\n // Skip invalid files\n continue;\n }\n }\n\n return results;\n }\n\n async exportAllData(): Promise<{\n version: number;\n exportedAt: string;\n projects: ProjectData[];\n }> {\n await this.ensureDirectory();\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.storageDir);\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const projects: ProjectData[] = [];\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.storageDir, file);\n const data = await readJsonFile<ProjectData>(filePath);\n projects.push(data);\n } catch {\n continue;\n }\n }\n\n return {\n version: 1,\n exportedAt: new Date().toISOString(),\n projects,\n };\n }\n\n async importAllData(data: {\n version: number;\n exportedAt: string;\n projects: ProjectData[];\n }): Promise<{\n success: boolean;\n imported: number;\n errors: number;\n errorDetails: string[];\n }> {\n await this.ensureDirectory();\n\n let imported = 0;\n let errors = 0;\n const errorDetails: string[] = [];\n\n if (!data.projects || !Array.isArray(data.projects)) {\n throw new Error('Invalid backup data: projects array is required');\n }\n\n for (const projectData of data.projects) {\n try {\n if (!projectData.project || !projectData.project.id) {\n errors++;\n errorDetails.push('Skipping invalid project: missing project or id');\n continue;\n }\n\n const filePath = this.getFilePath(projectData.project.id);\n await writeJsonFile(filePath, projectData);\n imported++;\n } catch (error) {\n errors++;\n const errorMessage = error instanceof Error ? error.message : String(error);\n errorDetails.push(`Failed to import project ${projectData.project?.id || 'unknown'}: ${errorMessage}`);\n }\n }\n\n return {\n success: errors === 0,\n imported,\n errors,\n errorDetails,\n };\n }\n}\n\nexport const storage = new ProjectStorage();\n","import * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Get the storage directory for roadmap-skill projects\n * Returns: ~/.roadmap-skill/projects\n */\nexport function getStorageDir(): string {\n const homeDir = os.homedir();\n return path.join(homeDir, '.roadmap-skill', 'projects');\n}\n","export { TagService } from './tag-service.js';\nexport { TaskService } from './task-service.js';\nexport * from './types.js';\n","/**\n * Tag Service - Unified business logic for tag management\n * Extracted from tag-tools.ts for better separation of concerns\n */\n\nimport type { ProjectStorage } from '../storage/index.js';\nimport type { Tag, ProjectData } from '../models/index.js';\nimport type {\n ServiceResult,\n CreateTagData,\n UpdateTagData,\n DeleteTagResult,\n TasksByTagResult,\n} from './types.js';\n\n/**\n * Generate a unique tag ID\n * @returns Tag ID in format tag_${timestamp}_${random}\n */\nfunction generateTagId(): string {\n return `tag_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * TagService - Handles all tag-related business logic\n */\nexport class TagService {\n private storage: ProjectStorage;\n\n constructor(storage: ProjectStorage) {\n this.storage = storage;\n }\n\n /**\n * Create a new tag in a project\n * @param projectId - The project ID\n * @param data - Tag creation data\n * @returns The created tag or error\n */\n async create(projectId: string, data: CreateTagData): Promise<ServiceResult<Tag>> {\n try {\n const projectData = await this.storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n // Check for duplicate tag name (case-insensitive)\n const existingTag = projectData.tags.find(\n (t) => t.name.toLowerCase() === data.name.toLowerCase()\n );\n if (existingTag) {\n return {\n success: false,\n error: `Tag with name '${data.name}' already exists in this project`,\n code: 'DUPLICATE_ERROR',\n };\n }\n\n const now = new Date().toISOString();\n const tag: Tag = {\n id: generateTagId(),\n name: data.name,\n color: data.color,\n description: data.description || '',\n createdAt: now,\n };\n\n projectData.tags.push(tag);\n projectData.project.updatedAt = now;\n\n await this.saveProjectData(projectId, projectData);\n\n return {\n success: true,\n data: tag,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to create tag',\n code: 'INTERNAL_ERROR',\n };\n }\n }\n\n /**\n * List all tags in a project\n * @param projectId - The project ID\n * @returns Array of tags or error\n */\n async list(projectId: string): Promise<ServiceResult<Tag[]>> {\n try {\n const projectData = await this.storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n return {\n success: true,\n data: projectData.tags,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to list tags',\n code: 'INTERNAL_ERROR',\n };\n }\n }\n\n /**\n * Update an existing tag\n * @param projectId - The project ID\n * @param tagId - The tag ID\n * @param data - Tag update data\n * @returns The updated tag or error\n */\n async update(\n projectId: string,\n tagId: string,\n data: UpdateTagData\n ): Promise<ServiceResult<Tag>> {\n try {\n const projectData = await this.storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const tagIndex = projectData.tags.findIndex((t) => t.id === tagId);\n if (tagIndex === -1) {\n return {\n success: false,\n error: `Tag with ID '${tagId}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n // Check if there is any field to update\n if (Object.keys(data).length === 0) {\n return {\n success: false,\n error: 'At least one field to update is required',\n code: 'VALIDATION_ERROR',\n };\n }\n\n // Check for duplicate name if updating name (case-insensitive)\n if (data.name) {\n const existingTag = projectData.tags.find(\n (t) =>\n t.name.toLowerCase() === data.name!.toLowerCase() && t.id !== tagId\n );\n if (existingTag) {\n return {\n success: false,\n error: `Tag with name '${data.name}' already exists in this project`,\n code: 'DUPLICATE_ERROR',\n };\n }\n }\n\n const now = new Date().toISOString();\n const existingTag = projectData.tags[tagIndex];\n\n const updatedTag: Tag = {\n ...existingTag,\n ...data,\n id: existingTag.id,\n createdAt: existingTag.createdAt,\n };\n\n projectData.tags[tagIndex] = updatedTag;\n projectData.project.updatedAt = now;\n\n await this.saveProjectData(projectId, projectData);\n\n return {\n success: true,\n data: updatedTag,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to update tag',\n code: 'INTERNAL_ERROR',\n };\n }\n }\n\n /**\n * Delete a tag by ID\n * Also removes the tag from all tasks that use it\n * @param projectId - The project ID\n * @param tagId - The tag ID\n * @returns Delete result with tag info and count of updated tasks\n */\n async delete(projectId: string, tagId: string): Promise<ServiceResult<DeleteTagResult>> {\n try {\n const projectData = await this.storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const tagIndex = projectData.tags.findIndex((t) => t.id === tagId);\n if (tagIndex === -1) {\n return {\n success: false,\n error: `Tag with ID '${tagId}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n const tag = projectData.tags[tagIndex];\n\n // Remove tag from all tasks that use it\n const now = new Date().toISOString();\n let tasksUpdated = 0;\n for (const task of projectData.tasks) {\n const tagIndexInTask = task.tags.indexOf(tagId);\n if (tagIndexInTask !== -1) {\n task.tags.splice(tagIndexInTask, 1);\n task.updatedAt = now;\n tasksUpdated++;\n }\n }\n\n projectData.tags.splice(tagIndex, 1);\n projectData.project.updatedAt = now;\n\n await this.saveProjectData(projectId, projectData);\n\n return {\n success: true,\n data: {\n deleted: true,\n tag,\n tasksUpdated,\n },\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to delete tag',\n code: 'INTERNAL_ERROR',\n };\n }\n }\n\n /**\n * Get all tasks that have a specific tag by tag name\n * @param projectId - The project ID\n * @param tagName - The tag name\n * @returns Tag info and matching tasks\n */\n async getTasksByTag(\n projectId: string,\n tagName: string\n ): Promise<ServiceResult<TasksByTagResult>> {\n try {\n const projectData = await this.storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n // Find tag by name (case-insensitive)\n const tag = projectData.tags.find(\n (t) => t.name.toLowerCase() === tagName.toLowerCase()\n );\n\n if (!tag) {\n return {\n success: false,\n error: `Tag with name '${tagName}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n // Find all tasks with this tag\n const tasks = projectData.tasks.filter((t) => t.tags.includes(tag.id));\n\n return {\n success: true,\n data: {\n tag,\n tasks,\n count: tasks.length,\n },\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to get tasks by tag',\n code: 'INTERNAL_ERROR',\n };\n }\n }\n\n /**\n * Helper method to save project data\n * @param projectId - The project ID\n * @param projectData - The project data to save\n */\n private async saveProjectData(projectId: string, projectData: ProjectData): Promise<void> {\n const filePath = this.storage.getFilePath(projectId);\n const { writeJsonFile } = await import('../utils/file-helpers.js');\n await writeJsonFile(filePath, projectData);\n }\n}\n","/**\n * Task Service - Unified business logic for task operations\n * Extracted from task-tools.ts to enable reuse across different interfaces\n */\n\nimport type { Task, TaskStatus } from '../models/index.js';\nimport { storage } from '../storage/index.js';\nimport { writeJsonFile } from '../utils/file-helpers.js';\nimport type {\n ServiceResult,\n CreateTaskData,\n UpdateTaskData,\n BatchUpdateTaskData,\n BatchUpdateResult,\n} from './types.js';\n\n/**\n * Generate a unique task ID\n * Format: task_${timestamp}_${random}\n */\nfunction generateTaskId(): string {\n return `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * Calculate completedAt based on status change\n * - When status changes to 'done', set completedAt to current time\n * - When status changes from 'done' to other, clear completedAt\n * - Otherwise, preserve existing completedAt\n */\nfunction calculateCompletedAt(\n currentStatus: TaskStatus,\n newStatus: TaskStatus | undefined,\n existingCompletedAt: string | null,\n now: string\n): string | null {\n // Status is not being updated\n if (!newStatus) {\n return existingCompletedAt;\n }\n\n // Status changing to 'done'\n if (newStatus === 'done' && currentStatus !== 'done') {\n return now;\n }\n\n // Status changing from 'done' to something else\n if (currentStatus === 'done' && newStatus !== 'done') {\n return null;\n }\n\n // Status changing but not involving 'done' transition\n return existingCompletedAt;\n}\n\n/**\n * TaskService - Business logic for task operations\n */\nexport const TaskService = {\n /**\n * Create a new task in a project\n * @param projectId - The project ID\n * @param data - Task creation data\n * @returns The created task or error\n */\n async create(projectId: string, data: CreateTaskData): Promise<ServiceResult<Task>> {\n try {\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const now = new Date().toISOString();\n const task: Task = {\n id: generateTaskId(),\n projectId,\n title: data.title,\n description: data.description,\n status: 'todo',\n priority: data.priority ?? 'medium',\n tags: data.tags ?? [],\n dueDate: data.dueDate ?? null,\n assignee: data.assignee ?? null,\n createdAt: now,\n updatedAt: now,\n completedAt: null,\n };\n\n projectData.tasks.push(task);\n projectData.project.updatedAt = now;\n\n const filePath = storage.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return {\n success: true,\n data: task,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to create task',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n\n /**\n * Get a specific task by project ID and task ID\n * @param projectId - The project ID\n * @param taskId - The task ID\n * @returns The task or error\n */\n async get(projectId: string, taskId: string): Promise<ServiceResult<Task>> {\n try {\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const task = projectData.tasks.find((t) => t.id === taskId);\n if (!task) {\n return {\n success: false,\n error: `Task with ID '${taskId}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n return {\n success: true,\n data: task,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to get task',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n\n /**\n * Update an existing task\n * Handles completedAt automatically based on status changes\n * @param projectId - The project ID\n * @param taskId - The task ID\n * @param data - Task update data\n * @returns The updated task or error\n */\n async update(\n projectId: string,\n taskId: string,\n data: UpdateTaskData\n ): Promise<ServiceResult<Task>> {\n try {\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);\n if (taskIndex === -1) {\n return {\n success: false,\n error: `Task with ID '${taskId}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n // Check if there's anything to update\n const updateKeys = Object.keys(data);\n if (updateKeys.length === 0) {\n return {\n success: false,\n error: 'At least one field to update is required',\n code: 'VALIDATION_ERROR',\n };\n }\n\n const now = new Date().toISOString();\n const existingTask = projectData.tasks[taskIndex];\n\n // Calculate completedAt based on status change\n const completedAt = calculateCompletedAt(\n existingTask.status,\n data.status,\n existingTask.completedAt,\n now\n );\n\n const updatedTask: Task = {\n ...existingTask,\n ...data,\n id: existingTask.id,\n projectId: existingTask.projectId,\n createdAt: existingTask.createdAt,\n updatedAt: now,\n completedAt,\n };\n\n projectData.tasks[taskIndex] = updatedTask;\n projectData.project.updatedAt = now;\n\n const filePath = storage.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return {\n success: true,\n data: updatedTask,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to update task',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n\n /**\n * Delete a task by project ID and task ID\n * @param projectId - The project ID\n * @param taskId - The task ID\n * @returns Void or error\n */\n async delete(projectId: string, taskId: string): Promise<ServiceResult<void>> {\n try {\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);\n if (taskIndex === -1) {\n return {\n success: false,\n error: `Task with ID '${taskId}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n const now = new Date().toISOString();\n projectData.tasks.splice(taskIndex, 1);\n projectData.project.updatedAt = now;\n\n const filePath = storage.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return {\n success: true,\n data: undefined,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to delete task',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n\n /**\n * List tasks with optional filters\n * @param filters - Optional filters for the search\n * @returns Array of tasks or error\n */\n async list(filters?: {\n projectId?: string;\n status?: TaskStatus;\n priority?: 'low' | 'medium' | 'high' | 'critical';\n tags?: string[];\n assignee?: string;\n dueBefore?: string;\n dueAfter?: string;\n includeCompleted?: boolean;\n }): Promise<ServiceResult<Task[]>> {\n try {\n const results = await storage.searchTasks({\n projectId: filters?.projectId,\n status: filters?.status,\n priority: filters?.priority,\n tags: filters?.tags,\n assignee: filters?.assignee,\n dueBefore: filters?.dueBefore,\n dueAfter: filters?.dueAfter,\n includeCompleted: filters?.includeCompleted,\n });\n\n // Extract just the tasks from the results\n const tasks = results.map((r) => r.task);\n\n return {\n success: true,\n data: tasks,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to list tasks',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n\n /**\n * Update multiple tasks at once\n * @param projectId - The project ID\n * @param taskIds - Array of task IDs to update\n * @param data - Batch update data\n * @returns Batch update result or error\n */\n async batchUpdate(\n projectId: string,\n taskIds: string[],\n data: BatchUpdateTaskData\n ): Promise<ServiceResult<BatchUpdateResult>> {\n try {\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const now = new Date().toISOString();\n const updatedTasks: Task[] = [];\n const notFoundIds: string[] = [];\n\n for (const taskId of taskIds) {\n const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);\n if (taskIndex === -1) {\n notFoundIds.push(taskId);\n continue;\n }\n\n const existingTask = projectData.tasks[taskIndex];\n let updatedTags = existingTask.tags;\n\n // Handle tags based on tagOperation\n if (data.tags && data.tags.length > 0) {\n const existingTags = existingTask.tags || [];\n switch (data.tagOperation) {\n case 'add':\n updatedTags = [...new Set([...existingTags, ...data.tags])];\n break;\n case 'remove':\n updatedTags = existingTags.filter((tag) => !data.tags!.includes(tag));\n break;\n case 'replace':\n default:\n updatedTags = data.tags;\n break;\n }\n }\n\n // Calculate completedAt based on status change\n const completedAt = calculateCompletedAt(\n existingTask.status,\n data.status,\n existingTask.completedAt,\n now\n );\n\n const updatedTask: Task = {\n ...existingTask,\n ...(data.status && { status: data.status }),\n ...(data.priority && { priority: data.priority }),\n tags: updatedTags,\n updatedAt: now,\n completedAt,\n };\n\n projectData.tasks[taskIndex] = updatedTask;\n updatedTasks.push(updatedTask);\n }\n\n if (updatedTasks.length === 0) {\n return {\n success: false,\n error: 'No tasks were found to update',\n code: 'NOT_FOUND',\n };\n }\n\n projectData.project.updatedAt = now;\n\n const filePath = storage.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return {\n success: true,\n data: {\n updatedTasks,\n updatedCount: updatedTasks.length,\n notFoundIds: notFoundIds.length > 0 ? notFoundIds : undefined,\n },\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to batch update tasks',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n};\n","/**\n * Service layer type definitions\n * Provides unified result types and service interfaces\n */\n\nimport type { ProjectStorage } from '../storage/index.js';\nimport type { Task, Tag, TaskStatus, TaskPriority } from '../models/index.js';\n\n// ============================================================================\n// Result Types\n// ============================================================================\n\n/**\n * Error codes for service operations\n */\nexport type ErrorCode =\n | 'NOT_FOUND'\n | 'VALIDATION_ERROR'\n | 'DUPLICATE_ERROR'\n | 'UNAUTHORIZED'\n | 'INTERNAL_ERROR';\n\n/**\n * Service result type - discriminated union for success/failure\n */\nexport type ServiceResult<T> =\n | { success: true; data: T }\n | { success: false; error: string; code: ErrorCode };\n\n// ============================================================================\n// Task Service Types\n// ============================================================================\n\nexport interface CreateTaskData {\n title: string;\n description: string;\n priority?: TaskPriority;\n tags?: string[];\n dueDate?: string | null;\n assignee?: string | null;\n}\n\nexport interface UpdateTaskData {\n title?: string;\n description?: string;\n status?: TaskStatus;\n priority?: TaskPriority;\n tags?: string[];\n dueDate?: string | null;\n assignee?: string | null;\n}\n\nexport interface BatchUpdateTaskData {\n status?: TaskStatus;\n priority?: TaskPriority;\n tags?: string[];\n tagOperation?: 'add' | 'remove' | 'replace';\n}\n\nexport interface BatchUpdateResult {\n updatedTasks: Task[];\n updatedCount: number;\n notFoundIds?: string[];\n}\n\n// ============================================================================\n// Tag Service Types\n// ============================================================================\n\nexport interface CreateTagData {\n name: string;\n color: string;\n description?: string;\n}\n\nexport interface UpdateTagData {\n name?: string;\n color?: string;\n description?: string;\n}\n\nexport interface DeleteTagResult {\n deleted: true;\n tag: Tag;\n tasksUpdated: number;\n}\n\nexport interface TasksByTagResult {\n tag: Tag;\n tasks: Task[];\n count: number;\n}\n\n// ============================================================================\n// Service Context\n// ============================================================================\n\n/**\n * Service context for dependency injection\n */\nexport interface ServiceContext {\n storage: ProjectStorage;\n}\n\n// ============================================================================\n// Service Interfaces\n// ============================================================================\n\n/**\n * Task Service interface\n */\nexport interface ITaskService {\n create(projectId: string, data: CreateTaskData): Promise<ServiceResult<Task>>;\n get(projectId: string, taskId: string): Promise<ServiceResult<Task>>;\n update(projectId: string, taskId: string, data: UpdateTaskData): Promise<ServiceResult<Task>>;\n delete(projectId: string, taskId: string): Promise<ServiceResult<void>>;\n list(filters?: {\n projectId?: string;\n status?: TaskStatus;\n priority?: TaskPriority;\n tags?: string[];\n assignee?: string;\n dueBefore?: string;\n dueAfter?: string;\n includeCompleted?: boolean;\n }): Promise<ServiceResult<Task[]>>;\n batchUpdate(\n projectId: string,\n taskIds: string[],\n data: BatchUpdateTaskData\n ): Promise<ServiceResult<BatchUpdateResult>>;\n}\n\n/**\n * Tag Service interface\n */\nexport interface ITagService {\n create(projectId: string, data: CreateTagData): Promise<ServiceResult<Tag>>;\n list(projectId: string): Promise<ServiceResult<Tag[]>>;\n update(projectId: string, tagId: string, data: UpdateTagData): Promise<ServiceResult<Tag>>;\n delete(projectId: string, tagId: string): Promise<ServiceResult<DeleteTagResult>>;\n getTasksByTag(projectId: string, tagName: string): Promise<ServiceResult<TasksByTagResult>>;\n}\n"],"mappings":"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","names":["path","fileURLToPath","path","path","fs","storage","existingTag","writeJsonFile","__filename","fileURLToPath","__dirname","require"]}
1
+ {"version":3,"sources":["../../node_modules/tsup/assets/esm_shims.js","../../src/utils/file-helpers.ts","../../src/web/server.ts","../../src/storage/index.ts","../../src/utils/path-helpers.ts","../../src/services/index.ts","../../src/services/tag-service.ts","../../src/services/task-service.ts","../../src/services/types.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import * as fs from 'fs/promises';\n\n/**\n * Read and parse a JSON file\n * @param filePath - Path to the JSON file\n * @returns Parsed JSON data\n * @throws Error if file cannot be read or parsed\n */\nexport async function readJsonFile<T>(filePath: string): Promise<T> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to read JSON file ${filePath}: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Write data to a JSON file\n * @param filePath - Path to the JSON file\n * @param data - Data to serialize and write\n * @throws Error if file cannot be written\n */\nexport async function writeJsonFile<T>(filePath: string, data: T): Promise<void> {\n try {\n const content = JSON.stringify(data, null, 2);\n await fs.writeFile(filePath, content, 'utf-8');\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to write JSON file ${filePath}: ${error.message}`);\n }\n throw error;\n }\n}\n\n/**\n * Ensure a directory exists, creating it recursively if needed\n * @param dirPath - Path to the directory\n * @throws Error if directory cannot be created\n */\nexport async function ensureDir(dirPath: string): Promise<void> {\n try {\n await fs.mkdir(dirPath, { recursive: true });\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);\n }\n throw error;\n }\n}\n","import express from 'express';\nimport * as path from 'path';\nimport { fileURLToPath } from 'url';\nimport { createRequire } from 'node:module';\nimport { existsSync } from 'node:fs';\nimport type { Server } from 'http';\nimport { storage } from '../storage/index.js';\nimport { TaskService, TagService } from '../services/index.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/**\n * Resolve the path to the web app static files using multiple fallback strategies.\n * Handles npx execution, global installation, local development, and bundled execution.\n */\nfunction resolveAppPath(): string {\n const candidates: Array<{ path: string; source: string }> = [];\n\n try {\n const require = createRequire(import.meta.url);\n const pkgRoot = path.dirname(require.resolve('../../package.json'));\n candidates.push({ path: path.join(pkgRoot, 'dist/web/app'), source: 'package.json resolve' });\n } catch {\n // Package.json not resolvable\n }\n\n // Primary path when bundled: __dirname = dist/, so dist/web/app is correct\n candidates.push({ path: path.join(__dirname, 'web/app'), source: '__dirname/web/app' });\n candidates.push({ path: path.join(__dirname, 'app'), source: '__dirname relative' });\n candidates.push({ path: path.join(process.cwd(), 'dist/web/app'), source: 'process.cwd()' });\n candidates.push({ path: path.join(__dirname, '../web/app'), source: '__dirname/../web/app' });\n\n for (const { path: candidatePath, source } of candidates) {\n const indexPath = path.join(candidatePath, 'index.html');\n if (existsSync(indexPath)) {\n console.error(`[roadmap-skill] Static files found at: ${candidatePath} (via ${source})`);\n return candidatePath;\n }\n }\n\n const triedPaths = candidates.map(c => ` - ${c.path} (${c.source})`).join('\\n');\n throw new Error(\n `Cannot find web app static files.\\n\\n` +\n `Tried:\\n${triedPaths}\\n\\n` +\n `Ensure the package is installed correctly and web assets exist.`\n );\n}\n\nconst tagService = new TagService(storage);\n\nexport function createServer(port: number = 7860): Promise<Server> {\n return new Promise((resolve, reject) => {\n const app = express();\n\n app.use(express.json());\n\n app.get('/api/projects', async (_req, res) => {\n try {\n const projects = await storage.listProjects();\n res.json(projects);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/projects/:id', async (req, res) => {\n try {\n const project = await storage.readProject(req.params.id);\n if (!project) {\n res.status(404).json({ error: 'Project not found' });\n return;\n }\n res.json(project);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/tasks', async (req, res) => {\n try {\n const filters: Record<string, unknown> = { ...req.query };\n\n // Convert includeCompleted from string to boolean\n if (filters.includeCompleted !== undefined) {\n filters.includeCompleted = filters.includeCompleted === 'true';\n }\n\n const tasks = await storage.searchTasks(filters as any);\n res.json(tasks);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/projects', async (req, res) => {\n try {\n const project = await storage.createProject(req.body);\n res.json({ success: true, data: project });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.put('/api/projects', async (req, res) => {\n try {\n const { projectId, ...updateData } = req.body;\n const project = await storage.updateProject(projectId, updateData);\n res.json({ success: true, data: project });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.delete('/api/projects', async (req, res) => {\n try {\n const { projectId } = req.query;\n await storage.deleteProject(projectId as string);\n res.json({ success: true });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/tasks', async (req, res) => {\n try {\n const { projectId, ...taskData } = req.body;\n const result = await TaskService.create(projectId, taskData);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.put('/api/tasks', async (req, res) => {\n try {\n const { projectId, taskId, ...updateData } = req.body;\n const result = await TaskService.update(projectId, taskId, updateData);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.delete('/api/tasks', async (req, res) => {\n try {\n const { projectId, taskId } = req.query;\n const result = await TaskService.delete(projectId as string, taskId as string);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/projects/:projectId/tags', async (req, res) => {\n try {\n const { projectId } = req.params;\n const result = await tagService.create(projectId, req.body);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/projects/:projectId/tags', async (req, res) => {\n try {\n const { projectId } = req.params;\n const result = await tagService.list(projectId);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.put('/api/projects/:projectId/tags/:tagId', async (req, res) => {\n try {\n const { projectId, tagId } = req.params;\n const result = await tagService.update(projectId, tagId, req.body);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.delete('/api/projects/:projectId/tags/:tagId', async (req, res) => {\n try {\n const { projectId, tagId } = req.params;\n const result = await tagService.delete(projectId, tagId);\n if (!result.success) {\n const statusCode = result.code === 'NOT_FOUND' ? 404 : 400;\n res.status(statusCode).json({ error: result.error });\n return;\n }\n res.json({ success: true, data: result.data });\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.get('/api/backup', async (_req, res) => {\n try {\n const backup = await storage.exportAllData();\n const filename = `roadmap-skill-backup-${new Date().toISOString().split('T')[0]}.json`;\n res.setHeader('Content-Type', 'application/json');\n res.setHeader('Content-Disposition', `attachment; filename=\"${filename}\"`);\n res.json(backup);\n } catch (error) {\n res.status(500).json({ error: (error as Error).message });\n }\n });\n\n app.post('/api/backup', async (req, res) => {\n try {\n const result = await storage.importAllData(req.body);\n res.json(result);\n } catch (error) {\n res.status(400).json({\n success: false,\n error: (error as Error).message,\n });\n }\n });\n\n const distPath = resolveAppPath();\n app.use(express.static(distPath));\n\n app.get('*', (req, res) => {\n if (req.path.startsWith('/api')) {\n res.status(404).json({ error: 'API not found' });\n return;\n }\n res.sendFile(path.join(distPath, 'index.html'));\n });\n\n const server = app.listen(port, '127.0.0.1');\n\n server.once('listening', () => {\n console.error(`Web interface server running at http://localhost:${port}`);\n resolve(server);\n });\n\n server.once('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EADDRINUSE') {\n reject(new Error(`Port ${port} is already in use`));\n return;\n }\n\n reject(error);\n });\n });\n}\n\n// Start server if run directly\nif (process.argv[1]?.endsWith('server.js')) {\n const port = parseInt(process.argv[2] || '7860', 10);\n createServer(port).catch(err => {\n console.error('Failed to start server:', err);\n process.exit(1);\n });\n}\n","import * as path from 'path';\nimport type {\n Project,\n ProjectData,\n Task,\n TaskSearchFilters,\n CreateProjectInput,\n UpdateProjectInput,\n} from '../models/index.js';\nimport { getStorageDir } from '../utils/path-helpers.js';\nimport { readJsonFile, writeJsonFile, ensureDir } from '../utils/file-helpers.js';\n\n/**\n * Storage class for managing roadmap-skill projects\n * Projects are stored as individual JSON files in ~/.roadmap-skill/projects/\n */\nexport class ProjectStorage {\n private storageDir: string;\n\n constructor() {\n this.storageDir = getStorageDir();\n }\n\n /**\n * Ensure the storage directory exists\n */\n async ensureDirectory(): Promise<void> {\n await ensureDir(this.storageDir);\n }\n\n /**\n * Get the file path for a project\n * @param projectId - The project ID\n * @returns Full path to the project JSON file\n */\n getFilePath(projectId: string): string {\n return path.join(this.storageDir, `${projectId}.json`);\n }\n\n /**\n * Create a new project\n * @param input - Project creation data\n * @returns The created project data\n */\n async createProject(input: CreateProjectInput): Promise<ProjectData> {\n await this.ensureDirectory();\n\n const now = new Date().toISOString();\n const projectId = `proj_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n const project: Project = {\n id: projectId,\n name: input.name,\n description: input.description,\n projectType: input.projectType,\n status: 'active',\n startDate: input.startDate,\n targetDate: input.targetDate,\n createdAt: now,\n updatedAt: now,\n };\n\n const projectData: ProjectData = {\n version: 1,\n project,\n milestones: [],\n tasks: [],\n tags: [],\n };\n\n const filePath = this.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return projectData;\n }\n\n /**\n * Read a project by ID\n * @param projectId - The project ID\n * @returns The project data or null if not found\n */\n async readProject(projectId: string): Promise<ProjectData | null> {\n try {\n const filePath = this.getFilePath(projectId);\n return await readJsonFile<ProjectData>(filePath);\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Update an existing project\n * @param projectId - The project ID\n * @param input - Project update data\n * @returns The updated project data or null if not found\n */\n async updateProject(\n projectId: string,\n input: UpdateProjectInput\n ): Promise<ProjectData | null> {\n const projectData = await this.readProject(projectId);\n if (!projectData) {\n return null;\n }\n\n const now = new Date().toISOString();\n\n projectData.project = {\n ...projectData.project,\n ...input,\n updatedAt: now,\n };\n\n const filePath = this.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return projectData;\n }\n\n /**\n * Delete a project by ID\n * @param projectId - The project ID\n * @returns True if deleted, false if not found\n */\n async deleteProject(projectId: string): Promise<boolean> {\n try {\n const filePath = this.getFilePath(projectId);\n const fs = await import('fs/promises');\n await fs.unlink(filePath);\n return true;\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * List all projects sorted by updatedAt (descending)\n * @returns Array of project summaries (project + metadata)\n */\n async listProjects(): Promise<Array<{ project: Project; taskCount: number; milestoneCount: number }>> {\n await this.ensureDirectory();\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.storageDir);\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const projects: Array<{ project: Project; taskCount: number; milestoneCount: number }> = [];\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.storageDir, file);\n const data = await readJsonFile<ProjectData>(filePath);\n projects.push({\n project: data.project,\n taskCount: data.tasks.length,\n milestoneCount: data.milestones.length,\n });\n } catch {\n // Skip invalid files\n continue;\n }\n }\n\n // Sort by updatedAt descending\n return projects.sort(\n (a, b) => new Date(b.project.updatedAt).getTime() - new Date(a.project.updatedAt).getTime()\n );\n }\n\n /**\n * Search tasks across all projects with filters\n * @param filters - Search filters\n * @returns Array of matching tasks with project context\n */\n async searchTasks(\n filters: TaskSearchFilters\n ): Promise<Array<{ task: Task; project: Project }>> {\n await this.ensureDirectory();\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.storageDir);\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const results: Array<{ task: Task; project: Project }> = [];\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.storageDir, file);\n const data = await readJsonFile<ProjectData>(filePath);\n\n // Skip if project filter doesn't match\n if (filters.projectId && data.project.id !== filters.projectId) {\n continue;\n }\n\n for (const task of data.tasks) {\n // Apply filters\n if (filters.status && task.status !== filters.status) {\n continue;\n }\n if (filters.priority && task.priority !== filters.priority) {\n continue;\n }\n if (filters.assignee && task.assignee !== filters.assignee) {\n continue;\n }\n if (filters.dueBefore && task.dueDate && task.dueDate > filters.dueBefore) {\n continue;\n }\n if (filters.dueAfter && task.dueDate && task.dueDate < filters.dueAfter) {\n continue;\n }\n if (\n filters.tags &&\n filters.tags.length > 0 &&\n !filters.tags.some((tag) => task.tags.includes(tag))\n ) {\n continue;\n }\n if (\n filters.searchText &&\n !task.title.toLowerCase().includes(filters.searchText.toLowerCase()) &&\n !task.description.toLowerCase().includes(filters.searchText.toLowerCase())\n ) {\n continue;\n }\n\n // Filter completed tasks (done status) when includeCompleted is false\n if (filters.includeCompleted === false && task.status === 'done') {\n continue;\n }\n\n results.push({ task, project: data.project });\n }\n } catch {\n // Skip invalid files\n continue;\n }\n }\n\n return results;\n }\n\n async exportAllData(): Promise<{\n version: number;\n exportedAt: string;\n projects: ProjectData[];\n }> {\n await this.ensureDirectory();\n\n const fs = await import('fs/promises');\n const files = await fs.readdir(this.storageDir);\n const jsonFiles = files.filter((f) => f.endsWith('.json'));\n\n const projects: ProjectData[] = [];\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.storageDir, file);\n const data = await readJsonFile<ProjectData>(filePath);\n projects.push(data);\n } catch {\n continue;\n }\n }\n\n return {\n version: 1,\n exportedAt: new Date().toISOString(),\n projects,\n };\n }\n\n async importAllData(data: {\n version: number;\n exportedAt: string;\n projects: ProjectData[];\n }): Promise<{\n success: boolean;\n imported: number;\n errors: number;\n errorDetails: string[];\n }> {\n await this.ensureDirectory();\n\n let imported = 0;\n let errors = 0;\n const errorDetails: string[] = [];\n\n if (!data.projects || !Array.isArray(data.projects)) {\n throw new Error('Invalid backup data: projects array is required');\n }\n\n for (const projectData of data.projects) {\n try {\n if (!projectData.project || !projectData.project.id) {\n errors++;\n errorDetails.push('Skipping invalid project: missing project or id');\n continue;\n }\n\n const filePath = this.getFilePath(projectData.project.id);\n await writeJsonFile(filePath, projectData);\n imported++;\n } catch (error) {\n errors++;\n const errorMessage = error instanceof Error ? error.message : String(error);\n errorDetails.push(`Failed to import project ${projectData.project?.id || 'unknown'}: ${errorMessage}`);\n }\n }\n\n return {\n success: errors === 0,\n imported,\n errors,\n errorDetails,\n };\n }\n}\n\nexport const storage = new ProjectStorage();\n","import * as os from 'os';\nimport * as path from 'path';\n\n/**\n * Get the storage directory for roadmap-skill projects\n * Returns: ~/.roadmap-skill/projects\n */\nexport function getStorageDir(): string {\n const homeDir = os.homedir();\n return path.join(homeDir, '.roadmap-skill', 'projects');\n}\n","export { TagService } from './tag-service.js';\nexport { TaskService } from './task-service.js';\nexport * from './types.js';\n","/**\n * Tag Service - Unified business logic for tag management\n * Extracted from tag-tools.ts for better separation of concerns\n */\n\nimport type { ProjectStorage } from '../storage/index.js';\nimport type { Tag, ProjectData } from '../models/index.js';\nimport type {\n ServiceResult,\n CreateTagData,\n UpdateTagData,\n DeleteTagResult,\n TasksByTagResult,\n} from './types.js';\n\n/**\n * Generate a unique tag ID\n * @returns Tag ID in format tag_${timestamp}_${random}\n */\nfunction generateTagId(): string {\n return `tag_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * TagService - Handles all tag-related business logic\n */\nexport class TagService {\n private storage: ProjectStorage;\n\n constructor(storage: ProjectStorage) {\n this.storage = storage;\n }\n\n /**\n * Create a new tag in a project\n * @param projectId - The project ID\n * @param data - Tag creation data\n * @returns The created tag or error\n */\n async create(projectId: string, data: CreateTagData): Promise<ServiceResult<Tag>> {\n try {\n const projectData = await this.storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n // Check for duplicate tag name (case-insensitive)\n const existingTag = projectData.tags.find(\n (t) => t.name.toLowerCase() === data.name.toLowerCase()\n );\n if (existingTag) {\n return {\n success: false,\n error: `Tag with name '${data.name}' already exists in this project`,\n code: 'DUPLICATE_ERROR',\n };\n }\n\n const now = new Date().toISOString();\n const tag: Tag = {\n id: generateTagId(),\n name: data.name,\n color: data.color,\n description: data.description || '',\n createdAt: now,\n };\n\n projectData.tags.push(tag);\n projectData.project.updatedAt = now;\n\n await this.saveProjectData(projectId, projectData);\n\n return {\n success: true,\n data: tag,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to create tag',\n code: 'INTERNAL_ERROR',\n };\n }\n }\n\n /**\n * List all tags in a project\n * @param projectId - The project ID\n * @returns Array of tags or error\n */\n async list(projectId: string): Promise<ServiceResult<Tag[]>> {\n try {\n const projectData = await this.storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const taskTagCounts = new Map<string, number>();\n for (const task of projectData.tasks) {\n for (const tagId of task.tags) {\n taskTagCounts.set(tagId, (taskTagCounts.get(tagId) ?? 0) + 1);\n }\n }\n\n return {\n success: true,\n data: projectData.tags.map((tag) => ({\n ...tag,\n taskCount: taskTagCounts.get(tag.id) ?? 0,\n })),\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to list tags',\n code: 'INTERNAL_ERROR',\n };\n }\n }\n\n /**\n * Update an existing tag\n * @param projectId - The project ID\n * @param tagId - The tag ID\n * @param data - Tag update data\n * @returns The updated tag or error\n */\n async update(\n projectId: string,\n tagId: string,\n data: UpdateTagData\n ): Promise<ServiceResult<Tag>> {\n try {\n const projectData = await this.storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const tagIndex = projectData.tags.findIndex((t) => t.id === tagId);\n if (tagIndex === -1) {\n return {\n success: false,\n error: `Tag with ID '${tagId}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n // Check if there is any field to update\n if (Object.keys(data).length === 0) {\n return {\n success: false,\n error: 'At least one field to update is required',\n code: 'VALIDATION_ERROR',\n };\n }\n\n // Check for duplicate name if updating name (case-insensitive)\n if (data.name) {\n const existingTag = projectData.tags.find(\n (t) =>\n t.name.toLowerCase() === data.name!.toLowerCase() && t.id !== tagId\n );\n if (existingTag) {\n return {\n success: false,\n error: `Tag with name '${data.name}' already exists in this project`,\n code: 'DUPLICATE_ERROR',\n };\n }\n }\n\n const now = new Date().toISOString();\n const existingTag = projectData.tags[tagIndex];\n\n const updatedTag: Tag = {\n ...existingTag,\n ...data,\n id: existingTag.id,\n createdAt: existingTag.createdAt,\n };\n\n projectData.tags[tagIndex] = updatedTag;\n projectData.project.updatedAt = now;\n\n await this.saveProjectData(projectId, projectData);\n\n return {\n success: true,\n data: updatedTag,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to update tag',\n code: 'INTERNAL_ERROR',\n };\n }\n }\n\n /**\n * Delete a tag by ID\n * Also removes the tag from all tasks that use it\n * @param projectId - The project ID\n * @param tagId - The tag ID\n * @returns Delete result with tag info and count of updated tasks\n */\n async delete(projectId: string, tagId: string): Promise<ServiceResult<DeleteTagResult>> {\n try {\n const projectData = await this.storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const tagIndex = projectData.tags.findIndex((t) => t.id === tagId);\n if (tagIndex === -1) {\n return {\n success: false,\n error: `Tag with ID '${tagId}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n const tag = projectData.tags[tagIndex];\n\n // Remove tag from all tasks that use it\n const now = new Date().toISOString();\n let tasksUpdated = 0;\n for (const task of projectData.tasks) {\n const tagIndexInTask = task.tags.indexOf(tagId);\n if (tagIndexInTask !== -1) {\n task.tags.splice(tagIndexInTask, 1);\n task.updatedAt = now;\n tasksUpdated++;\n }\n }\n\n projectData.tags.splice(tagIndex, 1);\n projectData.project.updatedAt = now;\n\n await this.saveProjectData(projectId, projectData);\n\n return {\n success: true,\n data: {\n deleted: true,\n tag,\n tasksUpdated,\n },\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to delete tag',\n code: 'INTERNAL_ERROR',\n };\n }\n }\n\n /**\n * Get all tasks that have a specific tag by tag name\n * @param projectId - The project ID\n * @param tagName - The tag name\n * @returns Tag info and matching tasks\n */\n async getTasksByTag(\n projectId: string,\n tagName: string\n ): Promise<ServiceResult<TasksByTagResult>> {\n try {\n const projectData = await this.storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n // Find tag by name (case-insensitive)\n const tag = projectData.tags.find(\n (t) => t.name.toLowerCase() === tagName.toLowerCase()\n );\n\n if (!tag) {\n return {\n success: false,\n error: `Tag with name '${tagName}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n // Find all tasks with this tag\n const tasks = projectData.tasks.filter((t) => t.tags.includes(tag.id));\n\n return {\n success: true,\n data: {\n tag,\n tasks,\n count: tasks.length,\n },\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to get tasks by tag',\n code: 'INTERNAL_ERROR',\n };\n }\n }\n\n /**\n * Helper method to save project data\n * @param projectId - The project ID\n * @param projectData - The project data to save\n */\n private async saveProjectData(projectId: string, projectData: ProjectData): Promise<void> {\n const filePath = this.storage.getFilePath(projectId);\n const { writeJsonFile } = await import('../utils/file-helpers.js');\n await writeJsonFile(filePath, projectData);\n }\n}\n","/**\n * Task Service - Unified business logic for task operations\n * Extracted from task-tools.ts to enable reuse across different interfaces\n */\n\nimport type { Task, TaskStatus } from '../models/index.js';\nimport { storage } from '../storage/index.js';\nimport { writeJsonFile } from '../utils/file-helpers.js';\nimport type {\n ServiceResult,\n CreateTaskData,\n UpdateTaskData,\n BatchUpdateTaskData,\n BatchUpdateResult,\n} from './types.js';\n\n/**\n * Generate a unique task ID\n * Format: task_${timestamp}_${random}\n */\nfunction generateTaskId(): string {\n return `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * Calculate completedAt based on status change\n * - When status changes to 'done', set completedAt to current time\n * - When status changes from 'done' to other, clear completedAt\n * - Otherwise, preserve existing completedAt\n */\nfunction calculateCompletedAt(\n currentStatus: TaskStatus,\n newStatus: TaskStatus | undefined,\n existingCompletedAt: string | null,\n now: string\n): string | null {\n // Status is not being updated\n if (!newStatus) {\n return existingCompletedAt;\n }\n\n // Status changing to 'done'\n if (newStatus === 'done' && currentStatus !== 'done') {\n return now;\n }\n\n // Status changing from 'done' to something else\n if (currentStatus === 'done' && newStatus !== 'done') {\n return null;\n }\n\n // Status changing but not involving 'done' transition\n return existingCompletedAt;\n}\n\n/**\n * TaskService - Business logic for task operations\n */\nexport const TaskService = {\n /**\n * Create a new task in a project\n * @param projectId - The project ID\n * @param data - Task creation data\n * @returns The created task or error\n */\n async create(projectId: string, data: CreateTaskData): Promise<ServiceResult<Task>> {\n try {\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const now = new Date().toISOString();\n const task: Task = {\n id: generateTaskId(),\n projectId,\n title: data.title,\n description: data.description,\n status: 'todo',\n priority: data.priority ?? 'medium',\n tags: data.tags ?? [],\n dueDate: data.dueDate ?? null,\n assignee: data.assignee ?? null,\n createdAt: now,\n updatedAt: now,\n completedAt: null,\n };\n\n projectData.tasks.push(task);\n projectData.project.updatedAt = now;\n\n const filePath = storage.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return {\n success: true,\n data: task,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to create task',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n\n /**\n * Get a specific task by project ID and task ID\n * @param projectId - The project ID\n * @param taskId - The task ID\n * @returns The task or error\n */\n async get(projectId: string, taskId: string): Promise<ServiceResult<Task>> {\n try {\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const task = projectData.tasks.find((t) => t.id === taskId);\n if (!task) {\n return {\n success: false,\n error: `Task with ID '${taskId}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n return {\n success: true,\n data: task,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to get task',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n\n /**\n * Update an existing task\n * Handles completedAt automatically based on status changes\n * @param projectId - The project ID\n * @param taskId - The task ID\n * @param data - Task update data\n * @returns The updated task or error\n */\n async update(\n projectId: string,\n taskId: string,\n data: UpdateTaskData\n ): Promise<ServiceResult<Task>> {\n try {\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);\n if (taskIndex === -1) {\n return {\n success: false,\n error: `Task with ID '${taskId}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n // Check if there's anything to update\n const updateKeys = Object.keys(data);\n if (updateKeys.length === 0) {\n return {\n success: false,\n error: 'At least one field to update is required',\n code: 'VALIDATION_ERROR',\n };\n }\n\n const now = new Date().toISOString();\n const existingTask = projectData.tasks[taskIndex];\n\n // Calculate completedAt based on status change\n const completedAt = calculateCompletedAt(\n existingTask.status,\n data.status,\n existingTask.completedAt,\n now\n );\n\n const updatedTask: Task = {\n ...existingTask,\n ...data,\n id: existingTask.id,\n projectId: existingTask.projectId,\n createdAt: existingTask.createdAt,\n updatedAt: now,\n completedAt,\n };\n\n projectData.tasks[taskIndex] = updatedTask;\n projectData.project.updatedAt = now;\n\n const filePath = storage.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return {\n success: true,\n data: updatedTask,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to update task',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n\n /**\n * Delete a task by project ID and task ID\n * @param projectId - The project ID\n * @param taskId - The task ID\n * @returns Void or error\n */\n async delete(projectId: string, taskId: string): Promise<ServiceResult<void>> {\n try {\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);\n if (taskIndex === -1) {\n return {\n success: false,\n error: `Task with ID '${taskId}' not found in project '${projectId}'`,\n code: 'NOT_FOUND',\n };\n }\n\n const now = new Date().toISOString();\n projectData.tasks.splice(taskIndex, 1);\n projectData.project.updatedAt = now;\n\n const filePath = storage.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return {\n success: true,\n data: undefined,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to delete task',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n\n /**\n * List tasks with optional filters\n * @param filters - Optional filters for the search\n * @returns Array of tasks or error\n */\n async list(filters?: {\n projectId?: string;\n status?: TaskStatus;\n priority?: 'low' | 'medium' | 'high' | 'critical';\n tags?: string[];\n assignee?: string;\n dueBefore?: string;\n dueAfter?: string;\n includeCompleted?: boolean;\n }): Promise<ServiceResult<Task[]>> {\n try {\n const results = await storage.searchTasks({\n projectId: filters?.projectId,\n status: filters?.status,\n priority: filters?.priority,\n tags: filters?.tags,\n assignee: filters?.assignee,\n dueBefore: filters?.dueBefore,\n dueAfter: filters?.dueAfter,\n includeCompleted: filters?.includeCompleted,\n });\n\n // Extract just the tasks from the results\n const tasks = results.map((r) => r.task);\n\n return {\n success: true,\n data: tasks,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to list tasks',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n\n /**\n * Update multiple tasks at once\n * @param projectId - The project ID\n * @param taskIds - Array of task IDs to update\n * @param data - Batch update data\n * @returns Batch update result or error\n */\n async batchUpdate(\n projectId: string,\n taskIds: string[],\n data: BatchUpdateTaskData\n ): Promise<ServiceResult<BatchUpdateResult>> {\n try {\n const projectData = await storage.readProject(projectId);\n if (!projectData) {\n return {\n success: false,\n error: `Project with ID '${projectId}' not found`,\n code: 'NOT_FOUND',\n };\n }\n\n const now = new Date().toISOString();\n const updatedTasks: Task[] = [];\n const notFoundIds: string[] = [];\n\n for (const taskId of taskIds) {\n const taskIndex = projectData.tasks.findIndex((t) => t.id === taskId);\n if (taskIndex === -1) {\n notFoundIds.push(taskId);\n continue;\n }\n\n const existingTask = projectData.tasks[taskIndex];\n let updatedTags = existingTask.tags;\n\n // Handle tags based on tagOperation\n if (data.tags && data.tags.length > 0) {\n const existingTags = existingTask.tags || [];\n switch (data.tagOperation) {\n case 'add':\n updatedTags = [...new Set([...existingTags, ...data.tags])];\n break;\n case 'remove':\n updatedTags = existingTags.filter((tag) => !data.tags!.includes(tag));\n break;\n case 'replace':\n default:\n updatedTags = data.tags;\n break;\n }\n }\n\n // Calculate completedAt based on status change\n const completedAt = calculateCompletedAt(\n existingTask.status,\n data.status,\n existingTask.completedAt,\n now\n );\n\n const updatedTask: Task = {\n ...existingTask,\n ...(data.status && { status: data.status }),\n ...(data.priority && { priority: data.priority }),\n tags: updatedTags,\n updatedAt: now,\n completedAt,\n };\n\n projectData.tasks[taskIndex] = updatedTask;\n updatedTasks.push(updatedTask);\n }\n\n if (updatedTasks.length === 0) {\n return {\n success: false,\n error: 'No tasks were found to update',\n code: 'NOT_FOUND',\n };\n }\n\n projectData.project.updatedAt = now;\n\n const filePath = storage.getFilePath(projectId);\n await writeJsonFile(filePath, projectData);\n\n return {\n success: true,\n data: {\n updatedTasks,\n updatedCount: updatedTasks.length,\n notFoundIds: notFoundIds.length > 0 ? notFoundIds : undefined,\n },\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Failed to batch update tasks',\n code: 'INTERNAL_ERROR',\n };\n }\n },\n};\n","/**\n * Service layer type definitions\n * Provides unified result types and service interfaces\n */\n\nimport type { ProjectStorage } from '../storage/index.js';\nimport type { Task, Tag, TaskStatus, TaskPriority } from '../models/index.js';\n\n// ============================================================================\n// Result Types\n// ============================================================================\n\n/**\n * Error codes for service operations\n */\nexport type ErrorCode =\n | 'NOT_FOUND'\n | 'VALIDATION_ERROR'\n | 'DUPLICATE_ERROR'\n | 'UNAUTHORIZED'\n | 'INTERNAL_ERROR';\n\n/**\n * Service result type - discriminated union for success/failure\n */\nexport type ServiceResult<T> =\n | { success: true; data: T }\n | { success: false; error: string; code: ErrorCode };\n\n// ============================================================================\n// Task Service Types\n// ============================================================================\n\nexport interface CreateTaskData {\n title: string;\n description: string;\n priority?: TaskPriority;\n tags?: string[];\n dueDate?: string | null;\n assignee?: string | null;\n}\n\nexport interface UpdateTaskData {\n title?: string;\n description?: string;\n status?: TaskStatus;\n priority?: TaskPriority;\n tags?: string[];\n dueDate?: string | null;\n assignee?: string | null;\n}\n\nexport interface BatchUpdateTaskData {\n status?: TaskStatus;\n priority?: TaskPriority;\n tags?: string[];\n tagOperation?: 'add' | 'remove' | 'replace';\n}\n\nexport interface BatchUpdateResult {\n updatedTasks: Task[];\n updatedCount: number;\n notFoundIds?: string[];\n}\n\n// ============================================================================\n// Tag Service Types\n// ============================================================================\n\nexport interface CreateTagData {\n name: string;\n color: string;\n description?: string;\n}\n\nexport interface UpdateTagData {\n name?: string;\n color?: string;\n description?: string;\n}\n\nexport interface DeleteTagResult {\n deleted: true;\n tag: Tag;\n tasksUpdated: number;\n}\n\nexport interface TasksByTagResult {\n tag: Tag;\n tasks: Task[];\n count: number;\n}\n\n// ============================================================================\n// Service Context\n// ============================================================================\n\n/**\n * Service context for dependency injection\n */\nexport interface ServiceContext {\n storage: ProjectStorage;\n}\n\n// ============================================================================\n// Service Interfaces\n// ============================================================================\n\n/**\n * Task Service interface\n */\nexport interface ITaskService {\n create(projectId: string, data: CreateTaskData): Promise<ServiceResult<Task>>;\n get(projectId: string, taskId: string): Promise<ServiceResult<Task>>;\n update(projectId: string, taskId: string, data: UpdateTaskData): Promise<ServiceResult<Task>>;\n delete(projectId: string, taskId: string): Promise<ServiceResult<void>>;\n list(filters?: {\n projectId?: string;\n status?: TaskStatus;\n priority?: TaskPriority;\n tags?: string[];\n assignee?: string;\n dueBefore?: string;\n dueAfter?: string;\n includeCompleted?: boolean;\n }): Promise<ServiceResult<Task[]>>;\n batchUpdate(\n projectId: string,\n taskIds: string[],\n data: BatchUpdateTaskData\n ): Promise<ServiceResult<BatchUpdateResult>>;\n}\n\n/**\n * Tag Service interface\n */\nexport interface ITagService {\n create(projectId: string, data: CreateTagData): Promise<ServiceResult<Tag>>;\n list(projectId: string): Promise<ServiceResult<Tag[]>>;\n update(projectId: string, tagId: string, data: UpdateTagData): Promise<ServiceResult<Tag>>;\n delete(projectId: string, tagId: string): Promise<ServiceResult<DeleteTagResult>>;\n getTasksByTag(projectId: string, tagName: string): Promise<ServiceResult<TasksByTagResult>>;\n}\n"],"mappings":"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","names":["path","fileURLToPath","path","path","fs","storage","existingTag","writeJsonFile","__filename","fileURLToPath","__dirname","require"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roadmap-skill",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "A MCP server for project roadmap management with task tracking, tagging, and web visualization",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",