Compare commits
1 commit
main
...
feat/sprin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
870d1d356a |
5 changed files with 21 additions and 30 deletions
10
src/lib/code.ts
Normal file
10
src/lib/code.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// Sync met Scrum4Me/lib/code.ts — bewust duplicate (geen gedeeld package)
|
||||
// om de MCP-server eigenstandig te houden.
|
||||
//
|
||||
// Extraheert het achterste getal uit een code-string (bijv. "ST-001" → 1,
|
||||
// "T-42" → 42). Gebruikt als sort_order bij create_story / create_task.
|
||||
export function parseCodeNumber(code: string | null | undefined): number {
|
||||
if (!code) return 0
|
||||
const m = code.match(/(\d+)$/)
|
||||
return m ? Number.parseInt(m[1], 10) : 0
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import { prisma } from '../prisma.js'
|
|||
import { requireWriteAccess } from '../auth.js'
|
||||
import { userCanAccessProduct } from '../access.js'
|
||||
import { toolError, toolJson, withToolErrors } from '../errors.js'
|
||||
import { parseCodeNumber } from '../lib/code.js'
|
||||
|
||||
const STORY_AUTO_RE = /^ST-(\d+)$/
|
||||
const MAX_CODE_ATTEMPTS = 3
|
||||
|
|
@ -46,7 +47,6 @@ const inputSchema = z.object({
|
|||
description: z.string().max(4000).optional(),
|
||||
acceptance_criteria: z.string().max(4000).optional(),
|
||||
priority: z.number().int().min(1).max(4),
|
||||
sort_order: z.number().optional(),
|
||||
// Optionele sprint-koppeling: bij creatie de story direct aan een sprint
|
||||
// hangen (status=IN_SPRINT). De sprint moet bij hetzelfde product horen.
|
||||
sprint_id: z.string().min(1).optional(),
|
||||
|
|
@ -59,7 +59,6 @@ export async function handleCreateStory(
|
|||
description,
|
||||
acceptance_criteria,
|
||||
priority,
|
||||
sort_order,
|
||||
sprint_id,
|
||||
}: z.infer<typeof inputSchema>,
|
||||
) {
|
||||
|
|
@ -90,19 +89,10 @@ export async function handleCreateStory(
|
|||
}
|
||||
}
|
||||
|
||||
let resolvedSortOrder = sort_order
|
||||
if (resolvedSortOrder === undefined) {
|
||||
const last = await prisma.story.findFirst({
|
||||
where: { pbi_id, priority },
|
||||
orderBy: { sort_order: 'desc' },
|
||||
select: { sort_order: true },
|
||||
})
|
||||
resolvedSortOrder = (last?.sort_order ?? 0) + 1.0
|
||||
}
|
||||
|
||||
let lastError: unknown
|
||||
for (let attempt = 0; attempt < MAX_CODE_ATTEMPTS; attempt++) {
|
||||
const code = await generateNextStoryCode(pbi.product_id)
|
||||
const resolvedSortOrder = parseCodeNumber(code)
|
||||
try {
|
||||
const story = await prisma.story.create({
|
||||
data: {
|
||||
|
|
@ -146,7 +136,7 @@ export function registerCreateStoryTool(server: McpServer) {
|
|||
{
|
||||
title: 'Create story',
|
||||
description:
|
||||
'Add a story under an existing PBI. Optionally link it to a sprint via sprint_id — when given, the story is created with status=IN_SPRINT and the sprint must belong to the same product as the PBI; otherwise status=OPEN and the story lands in the product backlog. Sort_order auto-set to last+1 within the PBI/priority group if not provided. Forbidden for demo accounts.',
|
||||
'Add a story under an existing PBI. Optionally link it to a sprint via sprint_id — when given, the story is created with status=IN_SPRINT and the sprint must belong to the same product as the PBI; otherwise status=OPEN and the story lands in the product backlog. Sort_order is derived from the auto-generated code number. Forbidden for demo accounts.',
|
||||
inputSchema,
|
||||
},
|
||||
handleCreateStory,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { prisma } from '../prisma.js'
|
|||
import { requireWriteAccess } from '../auth.js'
|
||||
import { userCanAccessProduct } from '../access.js'
|
||||
import { toolError, toolJson, withToolErrors } from '../errors.js'
|
||||
import { parseCodeNumber } from '../lib/code.js'
|
||||
|
||||
const TASK_AUTO_RE = /^T-(\d+)$/
|
||||
const MAX_CODE_ATTEMPTS = 3
|
||||
|
|
@ -44,7 +45,6 @@ const inputSchema = z.object({
|
|||
description: z.string().max(4000).optional(),
|
||||
implementation_plan: z.string().max(8000).optional(),
|
||||
priority: z.number().int().min(1).max(4),
|
||||
sort_order: z.number().optional(),
|
||||
// Cross-repo override: zet expliciet de repo waarop de worker deze task
|
||||
// moet uitvoeren (overrides product.repo_url). Gebruik dit voor PBI's die
|
||||
// werk in meerdere repos coördineren — bv. PBI op Scrum4Me-product met
|
||||
|
|
@ -60,10 +60,10 @@ export function registerCreateTaskTool(server: McpServer) {
|
|||
{
|
||||
title: 'Create task',
|
||||
description:
|
||||
'Add a task under an existing story. Inherits sprint_id from the story (denormalized). Status defaults to TO_DO. Sort_order auto-set to last+1 within the story/priority group if not provided. Optional repo_url overrides the product.repo_url for cross-repo work (e.g. tasks targeting scrum4me-mcp under a Scrum4Me PBI). Forbidden for demo accounts.',
|
||||
'Add a task under an existing story. Inherits sprint_id from the story (denormalized). Status defaults to TO_DO. Sort_order is derived from the auto-generated code number. Optional repo_url overrides the product.repo_url for cross-repo work (e.g. tasks targeting scrum4me-mcp under a Scrum4Me PBI). Forbidden for demo accounts.',
|
||||
inputSchema,
|
||||
},
|
||||
async ({ story_id, title, description, implementation_plan, priority, sort_order, repo_url }) =>
|
||||
async ({ story_id, title, description, implementation_plan, priority, repo_url }) =>
|
||||
withToolErrors(async () => {
|
||||
const auth = await requireWriteAccess()
|
||||
|
||||
|
|
@ -76,19 +76,10 @@ export function registerCreateTaskTool(server: McpServer) {
|
|||
return toolError(`Story ${story_id} not accessible`)
|
||||
}
|
||||
|
||||
let resolvedSortOrder = sort_order
|
||||
if (resolvedSortOrder === undefined) {
|
||||
const last = await prisma.task.findFirst({
|
||||
where: { story_id, priority },
|
||||
orderBy: { sort_order: 'desc' },
|
||||
select: { sort_order: true },
|
||||
})
|
||||
resolvedSortOrder = (last?.sort_order ?? 0) + 1.0
|
||||
}
|
||||
|
||||
let lastError: unknown
|
||||
for (let attempt = 0; attempt < MAX_CODE_ATTEMPTS; attempt++) {
|
||||
const code = await generateNextTaskCode(story.product_id)
|
||||
const resolvedSortOrder = parseCodeNumber(code)
|
||||
try {
|
||||
const task = await prisma.task.create({
|
||||
data: {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export function registerGetClaudeContextTool(server: McpServer) {
|
|||
{ tasks: { some: { status: { not: 'DONE' } } } },
|
||||
],
|
||||
},
|
||||
orderBy: [{ priority: 'asc' }, { sort_order: 'asc' }],
|
||||
orderBy: [{ sort_order: 'asc' }],
|
||||
select: {
|
||||
id: true,
|
||||
code: true,
|
||||
|
|
@ -73,7 +73,7 @@ export function registerGetClaudeContextTool(server: McpServer) {
|
|||
priority: true,
|
||||
status: true,
|
||||
tasks: {
|
||||
orderBy: [{ priority: 'asc' }, { sort_order: 'asc' }],
|
||||
orderBy: [{ sort_order: 'asc' }],
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
|
|
|
|||
|
|
@ -606,10 +606,10 @@ export async function getFullJobContext(jobId: string) {
|
|||
},
|
||||
tasks: {
|
||||
where: { status: 'TO_DO' },
|
||||
orderBy: [{ priority: 'asc' }, { sort_order: 'asc' }],
|
||||
orderBy: [{ sort_order: 'asc' }],
|
||||
},
|
||||
},
|
||||
orderBy: [{ priority: 'asc' }, { sort_order: 'asc' }],
|
||||
orderBy: [{ sort_order: 'asc' }],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue