backlog-mcp 0.27.0 → 0.27.1
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.
|
@@ -26,7 +26,7 @@ function registerViewerRoutes(app) {
|
|
|
26
26
|
const filterConfig = statusMap[filter || "active"] || statusMap.active;
|
|
27
27
|
return storage.list({
|
|
28
28
|
...filterConfig,
|
|
29
|
-
limit: limit ? parseInt(limit) :
|
|
29
|
+
limit: limit ? parseInt(limit) : 1e4
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
32
|
app.get("/tasks/:id", async (request, reply) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewer-routes.mjs","names":[],"sources":["../../src/server/viewer-routes.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport fastifyStatic from '@fastify/static';\nimport { exec } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport matter from 'gray-matter';\nimport { storage } from '../storage/backlog.js';\nimport { resourceManager } from '../resources/manager.js';\nimport { paths } from '../utils/paths.js';\n\nexport function registerViewerRoutes(app: FastifyInstance) {\n // Static files - serve from dist/viewer (built assets)\n app.register(fastifyStatic, {\n root: paths.viewerDist,\n prefix: '/',\n });\n\n // List tasks\n app.get('/tasks', async (request) => {\n const { filter, limit } = request.query as { filter?: string; limit?: string };\n \n const statusMap: Record<string, any> = {\n active: { status: ['open', 'in_progress', 'blocked'] },\n completed: { status: ['done', 'cancelled'] },\n all: {},\n };\n \n const filterConfig = statusMap[filter || 'active'] || statusMap.active;\n const tasks = storage.list({ ...filterConfig, limit: limit ? parseInt(limit) :
|
|
1
|
+
{"version":3,"file":"viewer-routes.mjs","names":[],"sources":["../../src/server/viewer-routes.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport fastifyStatic from '@fastify/static';\nimport { exec } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport matter from 'gray-matter';\nimport { storage } from '../storage/backlog.js';\nimport { resourceManager } from '../resources/manager.js';\nimport { paths } from '../utils/paths.js';\n\nexport function registerViewerRoutes(app: FastifyInstance) {\n // Static files - serve from dist/viewer (built assets)\n app.register(fastifyStatic, {\n root: paths.viewerDist,\n prefix: '/',\n });\n\n // List tasks\n app.get('/tasks', async (request) => {\n const { filter, limit } = request.query as { filter?: string; limit?: string };\n \n const statusMap: Record<string, any> = {\n active: { status: ['open', 'in_progress', 'blocked'] },\n completed: { status: ['done', 'cancelled'] },\n all: {},\n };\n \n const filterConfig = statusMap[filter || 'active'] || statusMap.active;\n const tasks = storage.list({ ...filterConfig, limit: limit ? parseInt(limit) : 10000 });\n \n return tasks;\n });\n\n // Get single task\n app.get('/tasks/:id', async (request, reply) => {\n const { id } = request.params as { id: string };\n const task = storage.get(id);\n \n if (!task) {\n return reply.code(404).send({ error: 'Task not found' });\n }\n \n // Include raw markdown for copy button\n const raw = storage.getMarkdown(id);\n \n return { ...task, raw };\n });\n\n // System status\n app.get('/api/status', async () => {\n const tasks = storage.list({ limit: 10000 });\n const address = app.server.address();\n const port = typeof address === 'object' && address ? address.port : 3030;\n \n return {\n version: paths.getVersion(),\n port,\n dataDir: paths.backlogDataDir,\n taskCount: tasks.length,\n uptime: Math.floor(process.uptime())\n };\n });\n\n // Open task in editor\n app.get('/open/:id', async (request, reply) => {\n const { id } = request.params as { id: string };\n const filePath = storage.getFilePath(id);\n \n if (!filePath) {\n return reply.code(404).send({ error: 'Task not found' });\n }\n \n exec(`open \"${filePath}\"`);\n return { status: 'Opening...' };\n });\n\n // Resource proxy\n app.get('/resource', async (request, reply) => {\n const { path: filePath } = request.query as { path?: string };\n \n if (!filePath) {\n return reply.code(400).send({ error: 'Missing path parameter' });\n }\n \n if (!existsSync(filePath)) {\n return reply.code(404).send({ error: 'File not found', path: filePath });\n }\n \n try {\n const content = readFileSync(filePath, 'utf-8');\n const ext = filePath.split('.').pop()?.toLowerCase() || 'txt';\n const mimeMap: Record<string, string> = {\n md: 'text/markdown',\n ts: 'text/typescript',\n js: 'text/javascript',\n json: 'application/json',\n txt: 'text/plain',\n };\n \n let frontmatter = {};\n let bodyContent = content;\n \n // Parse frontmatter for markdown files\n if (ext === 'md') {\n const parsed = matter(content);\n frontmatter = parsed.data;\n bodyContent = parsed.content;\n }\n \n return {\n content: bodyContent,\n frontmatter,\n type: mimeMap[ext] || 'text/plain',\n path: filePath,\n fileUri: `file://${filePath}`,\n mcpUri: resourceManager.toUri(filePath),\n ext\n };\n } catch (error: any) {\n return reply.code(500).send({ error: 'Failed to read file', message: error.message });\n }\n });\n\n // MCP resource proxy\n app.get('/mcp/resource', async (request, reply) => {\n const { uri } = request.query as { uri?: string };\n \n if (!uri || !uri.startsWith('mcp://backlog/')) {\n return reply.code(400).send({ error: 'Invalid MCP URI' });\n }\n \n try {\n const resource = resourceManager.read(uri);\n const filePath = resourceManager.resolve(uri);\n const ext = filePath.split('.').pop()?.toLowerCase() || 'txt';\n \n return {\n content: resource.content,\n frontmatter: resource.frontmatter || {},\n type: resource.mimeType,\n path: filePath,\n fileUri: `file://${filePath}`,\n mcpUri: uri,\n ext\n };\n } catch (error: any) {\n return reply.code(404).send({ error: 'Resource not found', uri, message: error.message });\n }\n });\n\n // Open resource in viewer\n app.get('/open', async (request, reply) => {\n const { uri } = request.query as { uri?: string };\n \n if (!uri) {\n return reply.code(400).send({ error: 'Missing uri parameter' });\n }\n \n return reply.redirect(`/?resource=${encodeURIComponent(uri)}`);\n });\n}\n"],"mappings":"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"}
|
package/dist/storage/backlog.mjs
CHANGED
|
@@ -39,7 +39,9 @@ var BacklogStorage = class BacklogStorage {
|
|
|
39
39
|
if (existsSync(this.tasksPath)) for (const file of readdirSync(this.tasksPath).filter((f) => f.endsWith(".md"))) {
|
|
40
40
|
const filePath = join(this.tasksPath, file);
|
|
41
41
|
try {
|
|
42
|
-
|
|
42
|
+
const task = this.markdownToTask(readFileSync(filePath, "utf-8"));
|
|
43
|
+
if (!task.id) continue;
|
|
44
|
+
yield task;
|
|
43
45
|
} catch (error) {
|
|
44
46
|
if (error.code !== "ENOENT") throw error;
|
|
45
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backlog.mjs","names":[],"sources":["../../src/storage/backlog.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, unlinkSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport matter from 'gray-matter';\nimport type { Task, Status, TaskType } from './schema.js';\nimport { paths } from '@/utils/paths.js';\n\nconst TASKS_DIR = 'tasks';\n\nclass BacklogStorage {\n private static instance: BacklogStorage;\n\n static getInstance(): BacklogStorage {\n if (!BacklogStorage.instance) {\n BacklogStorage.instance = new BacklogStorage();\n }\n return BacklogStorage.instance;\n }\n\n private get tasksPath(): string {\n return join(paths.backlogDataDir, TASKS_DIR);\n }\n\n private ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n private taskFilePath(id: string): string {\n return join(this.tasksPath, `${id}.md`);\n }\n\n private taskToMarkdown(task: Task): string {\n const { description, ...frontmatter } = task;\n return matter.stringify(description || '', frontmatter);\n }\n\n private markdownToTask(content: string): Task {\n const { data, content: description } = matter(content);\n return { ...data, description: description.trim() } as Task;\n }\n\n getFilePath(id: string): string | null {\n const path = this.taskFilePath(id);\n return existsSync(path) ? path : null;\n }\n\n private *iterateTasks(): Generator<Task> {\n if (existsSync(this.tasksPath)) {\n for (const file of readdirSync(this.tasksPath).filter(f => f.endsWith('.md'))) {\n const filePath = join(this.tasksPath, file);\n try {\n
|
|
1
|
+
{"version":3,"file":"backlog.mjs","names":[],"sources":["../../src/storage/backlog.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, unlinkSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport matter from 'gray-matter';\nimport type { Task, Status, TaskType } from './schema.js';\nimport { paths } from '@/utils/paths.js';\n\nconst TASKS_DIR = 'tasks';\n\nclass BacklogStorage {\n private static instance: BacklogStorage;\n\n static getInstance(): BacklogStorage {\n if (!BacklogStorage.instance) {\n BacklogStorage.instance = new BacklogStorage();\n }\n return BacklogStorage.instance;\n }\n\n private get tasksPath(): string {\n return join(paths.backlogDataDir, TASKS_DIR);\n }\n\n private ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n }\n\n private taskFilePath(id: string): string {\n return join(this.tasksPath, `${id}.md`);\n }\n\n private taskToMarkdown(task: Task): string {\n const { description, ...frontmatter } = task;\n return matter.stringify(description || '', frontmatter);\n }\n\n private markdownToTask(content: string): Task {\n const { data, content: description } = matter(content);\n return { ...data, description: description.trim() } as Task;\n }\n\n getFilePath(id: string): string | null {\n const path = this.taskFilePath(id);\n return existsSync(path) ? path : null;\n }\n\n private *iterateTasks(): Generator<Task> {\n if (existsSync(this.tasksPath)) {\n for (const file of readdirSync(this.tasksPath).filter(f => f.endsWith('.md'))) {\n const filePath = join(this.tasksPath, file);\n try {\n const task = this.markdownToTask(readFileSync(filePath, 'utf-8'));\n // Skip malformed tasks without valid ID\n if (!task.id) continue;\n yield task;\n } catch (error) {\n // Skip files that were deleted between listing and reading\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n }\n }\n\n get(id: string): Task | undefined {\n const path = this.taskFilePath(id);\n if (existsSync(path)) {\n return this.markdownToTask(readFileSync(path, 'utf-8'));\n }\n return undefined;\n }\n\n getMarkdown(id: string): string | null {\n const path = this.taskFilePath(id);\n if (existsSync(path)) {\n return readFileSync(path, 'utf-8');\n }\n return null;\n }\n\n list(filter?: { status?: Status[]; type?: TaskType; epic_id?: string; limit?: number }): Task[] {\n const { status, type, epic_id, limit = 20 } = filter ?? {};\n\n let tasks = Array.from(this.iterateTasks());\n \n if (status) tasks = tasks.filter(t => status.includes(t.status));\n if (type) tasks = tasks.filter(t => (t.type ?? 'task') === type);\n if (epic_id) tasks = tasks.filter(t => t.epic_id === epic_id);\n\n return tasks\n .sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())\n .slice(0, limit);\n }\n\n add(task: Task): void {\n this.ensureDir(this.tasksPath);\n const filePath = this.taskFilePath(task.id);\n writeFileSync(filePath, this.taskToMarkdown(task));\n }\n\n save(task: Task): void {\n this.ensureDir(this.tasksPath);\n const filePath = this.taskFilePath(task.id);\n writeFileSync(filePath, this.taskToMarkdown(task));\n }\n\n delete(id: string): boolean {\n const path = this.taskFilePath(id);\n if (existsSync(path)) {\n unlinkSync(path);\n \n // Delete associated resources if they exist\n const resourcesPath = join(paths.backlogDataDir, 'resources', id);\n if (existsSync(resourcesPath)) {\n rmSync(resourcesPath, { recursive: true, force: true });\n }\n \n return true;\n }\n return false;\n }\n\n counts(): { total_tasks: number; total_epics: number; by_status: Record<Status, number> } {\n const by_status: Record<Status, number> = {\n open: 0,\n in_progress: 0,\n blocked: 0,\n done: 0,\n cancelled: 0,\n };\n\n let total_tasks = 0;\n let total_epics = 0;\n\n for (const task of this.iterateTasks()) {\n by_status[task.status]++;\n if ((task.type ?? 'task') === 'epic') {\n total_epics++;\n } else {\n total_tasks++;\n }\n }\n\n return { total_tasks, total_epics, by_status };\n }\n\n getMaxId(type?: 'task' | 'epic'): number {\n const pattern = type === 'epic' ? /^EPIC-(\\d{4,})\\.md$/ : /^TASK-(\\d{4,})\\.md$/;\n let maxNum = 0;\n\n if (existsSync(this.tasksPath)) {\n for (const file of readdirSync(this.tasksPath)) {\n const match = pattern.exec(file);\n if (match?.[1]) {\n const num = parseInt(match[1], 10);\n if (num > maxNum) maxNum = num;\n }\n }\n }\n\n return maxNum;\n }\n}\n\nexport const storage = BacklogStorage.getInstance();\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backlog-mcp",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.1",
|
|
4
4
|
"description": "Minimal task backlog MCP server for Claude and AI agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"@types/node": "^22.0.0",
|
|
57
57
|
"concurrently": "^9.2.1",
|
|
58
58
|
"esbuild": "^0.27.2",
|
|
59
|
+
"memfs": "^4.56.10",
|
|
59
60
|
"tsdown": "^0.20.1",
|
|
60
61
|
"tsx": "^4.19.0",
|
|
61
62
|
"typescript": "^5.7.0",
|