MCPcopy
hub / github.com/larksuite/cli / collectDomainsFromText

Function collectDomainsFromText

scripts/issue-labels/index.js:143–211  ·  view source on GitHub ↗

* Infer candidate domain services from issue title and body text. * * @param {string} title * @param {string} body * @returns {string[]}

(title, body)

Source from the content-addressed store, hash-verified

141 * @returns {string[]}
142 */
143function collectDomainsFromText(title, body) {
144 const normalizedBody = String(body || "")
145 .replace(/(["'])(?:(?=(\\?))\2.)*?\1/gs, (segment) => {
146 return /lark-cli\s+/i.test(segment) && segment.length > 80 ? '""' : segment;
147 });
148 const text = normalizeText(title, normalizedBody);
149 const titleText = String(title || "").toLowerCase();
150
151 const hits = new Set();
152
153 function normalizeService(svc) {
154 const s = String(svc || "").toLowerCase();
155 if (s === "docs") return "doc";
156 return s;
157 }
158
159 // 1) Explicit domain labels in text: domain/<service>
160 const explicit = new RegExp(`\\bdomain\\/(${DOMAIN_REGEX_ALTERNATION})\\b`, "gi");
161 for (const match of text.matchAll(explicit)) {
162 const svc = match && match[1] ? normalizeService(match[1]) : "";
163 if (DOMAIN_SERVICES.includes(svc)) hits.add(svc);
164 }
165
166 // 2) Command mention: lark-cli <service> / lark cli <service>
167 const cmd = new RegExp(`\\blark[-\\s]?cli\\s+(${DOMAIN_REGEX_ALTERNATION})\\b`, "gi");
168 for (const match of text.matchAll(cmd)) {
169 const svc = match && match[1] ? normalizeService(match[1]) : "";
170 if (DOMAIN_SERVICES.includes(svc)) hits.add(svc);
171 }
172
173 // 3) Loose title match: if title contains a standalone service word.
174 // This is intentionally limited to TITLE to reduce false positives.
175 // NOTE: exclude `im` here because it's too common in English text (e.g. "im stuck").
176 const looseServices = DOMAIN_SERVICES.filter((s) => s !== "im");
177 for (const svc of looseServices) {
178 const pattern = svc === "doc" ? "\\bdocs?\\b" : `\\b${svc}\\b`;
179 const re = new RegExp(pattern, "i");
180 if (re.test(titleText)) hits.add(svc);
181 }
182
183 // 4) Keyword heuristics (for users who don't paste the exact command)
184 // Keep this conservative; add keywords only when they are strongly tied to a domain.
185 const keywordMap = {
186 base: [/\bbase\s*\+/i, /\bbase-token\b/i, /open-apis\/bitable\//i, /\brecords?\/(search|list)\b/i, /多维表格/],
187 doc: [/\bdocx\b/i, /\bfeishu document\b/i, /\blark document\b/i, /\bdocument comments?\b/i, /飞书文档|云文档|文档/],
188 drive: [/\bdrive\b/i, /\bfolder token\b/i, /create_folder/i, /drive\/v1\/files/i, /\bdrive\s*\+/i],
189 sheets: [/电子表格/, /\bsheets\s*\+/i],
190 calendar: [/日历/, /\bcalendar\s*\+/i],
191 mail: [/邮件/, /\bmail\s*\+/i],
192 task: [/任务清单/, /飞书任务/, /\btask\s*\+/i],
193 wiki: [/知识库/, /\bwiki\s*\+/i],
194 minutes: [/妙记/, /\bminutes\s*\+/i],
195 vc: [/\bvc\s*\+/i, /飞书会议|视频会议|创建会议/],
196 im: [/消息|群聊|私聊/, /\bim\s*\+/i, /im\/v1/i],
197 auth: [/\bauth\s+(login|status|check|logout)\b/i, /\bkeychain\b/i, /\buser_access_token\b/i, /\buser token\b/i, /\bconsent\b/i, /授权|登录|scope authorization/],
198 core: [/\bpostinstall\b/i, /\bconfig(\.json)?\b/i, /\bconfig\s+(init|show|remove)\b/i, /\bpackage\.json\b/i, /\bscripts\/install\.js\b/i, /\bbun\b/i, /\bskills?\b/i, /\btrae\b/i, /\bprofile\b/i, /\bmulti-account\b/i, /\bprivate deployment\b/i, /\bbinary release\b/i, /\bbinary fails?\b/i, /\bunsupported platform\b/i, /\bebadplatform\b/i, /\bwindows\b.*\bbinary\b|\bbinary\b.*\bwindows\b/i, /\briscv64\b.*\bsupport/i, /私有化|安装脚本|配置文件|多账号|多个应用|多用户|持久化连接|服务器端/],
199 };
200 for (const [svc, patterns] of Object.entries(keywordMap)) {

Callers 1

classifyIssueTextFunction · 0.85

Calls 3

normalizeTextFunction · 0.85
normalizeServiceFunction · 0.85
addMethod · 0.80

Tested by

no test coverage detected