opencode-config/.opencode/tools/create-issue.ts
Sergey c491b5fad1
fix(issue): align headings with create-issue validation and add SDD sections (#169)
* fix(issue): align headings with create-issue validation and add SDD sections

* docs(issue): add handoff, ADR, and fix CI for SDD validation

* docs(pr-169): fix handoff sections

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-31 19:42:14 +03:00

73 lines
No EOL
3.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { tool } from "@opencode-ai/plugin"
import { runGh, formatResult } from "./_shared"
const TITLE_REGEX = /^(feat|fix|chore|docs|refactor|test|style|perf)\([^)]+\): .{1,80}$/
const CYRILLIC = /[\u0400-\u04FF]/
const RULES = `Rules:
- Title: conventional format type(scope): description (≤80 chars, English only)
- Body must contain '## Контекст' heading (зачем + текущее состояние)
- Body must contain '## Задача' heading (что делаем)
- Body must contain '## Контракты' heading (ожидаемое поведение / API)
- Body must contain '## Инварианты' heading (правила без исключений)
- Body must contain '## Граничные случаи' heading (что при ошибках)
- Body must contain '## Вне scope' heading (что НЕ делаем)
- Body must contain '## Критерии приемки' heading (как проверяем)
- Body must contain Cyrillic (Russian language required)`
export default tool({
description: "Create a GitHub issue with title/body validation. Validates: title format type(scope): description (<=80), English title, body headings (## Контекст, ## Задача, ## Контракты, ## Инварианты, ## Граничные случаи, ## Вне scope, ## Критерии приемки), body in Russian. Optional labels. Returns issue URL on success.",
args: {
title: tool.schema.string().describe("Issue title (conventional format: type(scope): description, <=80 chars)"),
body: tool.schema.string().describe("Issue body in Russian with ## Контекст, ## Задача, ## Контракты, ## Инварианты, ## Граничные случаи, ## Вне scope, ## Критерии приемки headings"),
labels: tool.schema.array(tool.schema.string()).optional().describe("Labels to assign (e.g. ['bug', 'enhancement'])"),
repo: tool.schema.string().optional().describe("Optional repo (owner/name). If omitted, gh auto-detects from context.worktree."),
},
async execute(args, context) {
const title = args.title
const body = args.body
if (!TITLE_REGEX.test(title)) {
return `❌ Issue title must match format: type(scope): description\n\n${RULES}`
}
if (CYRILLIC.test(title)) {
return `❌ Issue title must be in English\n\n${RULES}`
}
if (!body.includes("## Контекст")) {
return `❌ Issue body must contain '## Контекст' heading\n\n${RULES}`
}
if (!body.includes("## Задача")) {
return `❌ Issue body must contain '## Задача' heading\n\n${RULES}`
}
if (!body.includes("## Критерии приемки")) {
return `❌ Issue body must contain '## Критерии приемки' heading\n\n${RULES}`
}
if (!body.includes("## Контракты")) {
return `❌ Issue body must contain '## Контракты' heading\n\n${RULES}`
}
if (!body.includes("## Инварианты")) {
return `❌ Issue body must contain '## Инварианты' heading\n\n${RULES}`
}
if (!body.includes("## Граничные случаи")) {
return `❌ Issue body must contain '## Граничные случаи' heading\n\n${RULES}`
}
if (!body.includes("## Вне scope")) {
return `❌ Issue body must contain '## Вне scope' heading\n\n${RULES}`
}
if (!CYRILLIC.test(body)) {
return `❌ Issue body must be in Russian\n\n${RULES}`
}
const ghArgs = ["issue", "create", "--title", title, "--body", body]
if (args.labels && args.labels.length > 0) {
ghArgs.push("--label", args.labels.join(","))
}
const r = runGh(ghArgs, args.repo, { cwd: context.worktree })
if (r.status !== 0) {
return formatResult(r, "gh issue create")
}
return `Issue created: ${r.stdout.trim()}`
},
})