opencode-config/.opencode/tools/create-issue.ts
Sergey 15fc7d014d
refactor(tools): add repo parameter + shared module + tunnel tests (#65)
* refactor(tools): extract shared module for gh spawnSync logic

* feat(tools): add repo parameter to 5 GitHub tools

* test(tools): add tunnel tool tests

* test(tools): add repo parameter test cases for 5 tools

* docs(handoff): scaffold handoff and ADR for PR

* docs(handoff): set PR number

* docs(project-map): update after PR#65 structural changes

---------

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

60 lines
No EOL
2.6 KiB
TypeScript

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: type(scope): description (<=80 chars description)
- Types: feat, fix, chore, docs, refactor, test, style, perf
- Scope is mandatory
- Title in English only
- Body must contain '## Контекст' heading
- Body must contain '## Задача' heading
- Body must contain '## Критерии приемки' heading
- Body in Russian (must contain Cyrillic)`
export default tool({
description: "Create a GitHub issue with title/body validation. Validates: title format type(scope): description (<=80), English title, body headings (## Контекст, ## Задача, ## Критерии приемки), 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 ## Контекст, ## Задача, ## Критерии приемки 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 (!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()}`
},
})