UNPKG

paqad-ai

Version:

Spec-driven development framework — AI agents that think before they type

118 lines (115 loc) • 3.42 kB
import { createRequire as __paqadCreateRequire } from 'module'; const require = __paqadCreateRequire(import.meta.url); import { DEFAULT_TRACKER_PATTERNS } from "./chunk-YA6SCYMC.js"; import { init_esm_shims } from "./chunk-IDFRWBMN.js"; // src/planning/ticket-ref-detect.ts init_esm_shims(); var JIRA_REF = /\b[A-Z][A-Z0-9]+-\d+\b/g; var GITHUB_REF = /(?<![\w/])#(\d+)\b/g; function patternsFor(kind) { switch (kind) { case "github-issues": return [GITHUB_REF]; case "jira": case "linear": return [JIRA_REF]; default: return [JIRA_REF, GITHUB_REF]; } } function detectTicketRefs(prompt, kind) { const seen = /* @__PURE__ */ new Set(); const out = []; for (const pattern of patternsFor(kind)) { pattern.lastIndex = 0; let match; while ((match = pattern.exec(prompt)) !== null) { const ref = match[0]; if (!seen.has(ref)) { seen.add(ref); out.push(ref); } } } return out; } var JIRA_KEY = "[A-Z][A-Z0-9]+-\\d+"; var SERVICENOW_KEY = new RegExp(`\\b${DEFAULT_TRACKER_PATTERNS.servicenow}\\b`); var SERVICENOW_KEYS = new RegExp(SERVICENOW_KEY.source, "g"); var URL = /https?:\/\/[^\s<>()"'`]+/g; var LINK_RULES = [ { tracker: "github-issues", pattern: /^https?:\/\/(github\.com)\/([\w.-]+)\/([\w.-]+)\/issues\/(\d+)/, ref: (match) => `${match[2]}/${match[3]}#${match[4]}` }, { tracker: "linear", pattern: new RegExp(`^https?:\\/\\/(linear\\.app)\\/[\\w.-]+\\/issue\\/(${JIRA_KEY})`, "i"), ref: (match) => match[2].toUpperCase() }, { tracker: "jira", pattern: new RegExp(`^https?:\\/\\/([\\w.-]+)\\/browse\\/(${JIRA_KEY})\\b`), ref: (match) => match[2] }, { tracker: "jira", pattern: new RegExp(`^https?:\\/\\/([\\w-]+\\.atlassian\\.net)\\/\\S*?\\b(${JIRA_KEY})\\b`), ref: (match) => match[2] }, { tracker: "servicenow", pattern: new RegExp( `^https?:\\/\\/([\\w-]+\\.service-now\\.com)\\/\\S*?(${DEFAULT_TRACKER_PATTERNS.servicenow})` ), ref: (match) => match[2] } ]; function detectTicketLinks(text) { const out = []; for (const [url] of text.matchAll(URL)) { for (const rule of LINK_RULES) { const match = rule.pattern.exec(url); if (match) { out.push({ tracker: rule.tracker, server: match[1].toLowerCase(), ref: rule.ref(match) }); break; } } } if (out.length > 0) return out; for (const [ref] of text.matchAll(SERVICENOW_KEYS)) { out.push({ tracker: "servicenow", server: "", ref }); } return out; } var BRANCH_NUMBERS = /^(\d+(?:-\d+)*)(?=-|$)/; function parseBranchTickets(branch) { if (!branch) return []; const leaf = branch.split("/").filter(Boolean).at(-1) ?? ""; const out = detectTicketRefs(leaf, "jira"); const numbers = BRANCH_NUMBERS.exec(leaf); if (numbers) { for (const n of numbers[1].split("-")) { const ref = `#${Number(n)}`; if (!out.includes(ref)) out.push(ref); } } return out; } function armIntakeNarration(refs) { if (refs.length === 0) return ""; const first = refs[0]; const list = refs.join(", "); return `**\u25B8 paqad** \xB7 detected a ticket reference (${list}) > Ground the work in the real ticket: run \`paqad-ai intake fetch ${first}\` so I build what it actually says, not a guess from the id.`; } export { detectTicketRefs, detectTicketLinks, parseBranchTickets, armIntakeNarration };