(prompt: string)
| 477 | * is excluded from the member-access form since it's a file reference, not a symbol. |
| 478 | */ |
| 479 | export function extractCodeTokens(prompt: string): string[] { |
| 480 | if (!prompt) return []; |
| 481 | const out = new Set<string>(); |
| 482 | // camelCase / PascalCase-with-inner-cap (getUserId, parseToken, UserService) or |
| 483 | // snake_case (article_publish, get_user) — a whole identifier run that has an |
| 484 | // inner lower→upper transition or an underscore flanked by alphanumerics. |
| 485 | for (const m of prompt.matchAll(/[A-Za-z_$][\w$]*/g)) { |
| 486 | const w = m[0]; |
| 487 | if (/[a-z][A-Z]/.test(w) || /[A-Za-z0-9]_[A-Za-z0-9]/.test(w)) out.add(w); |
| 488 | } |
| 489 | // call form: an identifier directly before '(' — parseToken(, render(). No |
| 490 | // whitespace before '(' so prose like "the function (entry point)" doesn't trip it. |
| 491 | for (const m of prompt.matchAll(/([A-Za-z_$][\w$]*)\(/g)) out.add(m[1]!); |
| 492 | // member access on identifiers (user.login) — but not a doc/data filename. |
| 493 | for (const m of prompt.matchAll(/([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)/g)) { |
| 494 | if (!DOC_DATA_EXT.test(m[0])) { out.add(m[1]!); out.add(m[2]!); } |
| 495 | } |
| 496 | return [...out]; |
| 497 | } |
| 498 | |
| 499 | /** |
| 500 | * Cheap, graph-free candidate gate for the front-load hook: could `prompt` be a |
no outgoing calls
no test coverage detected