refactor(suggestion): drive suggestion decisions from one core step and split SuggestionService collaborators
This commit is contained in:
parent
9aa7302944
commit
190b6db284
13 changed files with 1994 additions and 496 deletions
49
packages/core/src/terminal-prompt.ts
Normal file
49
packages/core/src/terminal-prompt.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// packages/core — 터미널 화면에서 "지금 치고 있는 입력 줄" 을 뽑는다.
|
||||
//
|
||||
// 터미널은 화면 버퍼 전체가 입력창으로 읽힌다(상태줄·명령·출력 포함). 제안은 입력 줄만
|
||||
// 접두로 써야 하므로 그 파싱을 한곳에 둔다. 순수 함수만 둔다 — 다른 core 모듈을
|
||||
// import 하지 않는다 (input-intelligence 가 이 모듈을 다시 export 한다).
|
||||
|
||||
/** 상자·블록·도형·기타 기호·딩뱃 — 터미널 UI/상태줄의 지문이다. */
|
||||
export const NON_PROSE_GLYPH_PATTERN = /[←-⇿─-➿⬀-⯿]/u
|
||||
|
||||
/**
|
||||
* 입력 줄의 시작 표시: Claude Code/Codex `>`·`›`, starship `❯`, PowerShell `PS …>`, cmd `C:\…>`,
|
||||
* POSIX `$ # %`(단독이거나 앞에 경로·호스트 표지가 있을 때만 — 출력의 `100% 완료` 를 입력으로 보지 않는다).
|
||||
*/
|
||||
const TERMINAL_PROMPT_PATTERN =
|
||||
/^(?:PS [^>\n]{1,260}>|[A-Za-z]:\\[^>\n]{0,260}>|(?:[^\s]*[@:~/\\][^\s]*)?[$#%]|>|›|❯|»)[ \u00A0]?(.*)$/u
|
||||
/** 테두리·상자 줄(─━═╭╮╰╯ 등)이나 비어 있는 줄에서 입력 영역이 끝난다. */
|
||||
const TERMINAL_BORDER_PATTERN = /^[\s─━═┄┈╌╍╭╮╰╯┌┐└┘├┤┬┴┼│┃║▔▁▏▕-]*$/u
|
||||
const TERMINAL_EDGE_PATTERN = /^[\s│┃║]+|[\s│┃║]+$/gu
|
||||
const TERMINAL_SCAN_LINES = 40
|
||||
|
||||
/**
|
||||
* 터미널 화면 텍스트에서 "지금 치고 있는 입력" 을 뽑는다.
|
||||
*
|
||||
* 문서형 터미널은 캐럿 위치를 주지 않고 화면 전체를 돌려준다 — 마지막 줄은 대개
|
||||
* 상태줄이다. 아래에서 위로 가장 가까운 프롬프트 줄을 찾고, 그 아래로 테두리/빈 줄이
|
||||
* 나올 때까지의 이어진 줄(줄바꿈된 긴 입력)을 붙인다. 프롬프트를 못 찾으면 null —
|
||||
* 제안하지 않는다(출력·상태줄로 제안을 만들지 않는다).
|
||||
*/
|
||||
export function extractTerminalPromptPrefix(screen: string | null | undefined): string | null {
|
||||
if (!screen) return null
|
||||
const lines = screen.replace(/\r/g, '').split('\n')
|
||||
const start = Math.max(0, lines.length - TERMINAL_SCAN_LINES)
|
||||
for (let i = lines.length - 1; i >= start; i--) {
|
||||
const line = lines[i].replace(TERMINAL_EDGE_PATTERN, '')
|
||||
const match = TERMINAL_PROMPT_PATTERN.exec(line)
|
||||
if (!match) continue
|
||||
const parts = [match[1].trimEnd()]
|
||||
for (let j = i + 1; j < lines.length; j++) {
|
||||
const raw = lines[j]
|
||||
if (TERMINAL_BORDER_PATTERN.test(raw)) break
|
||||
const continuation = raw.replace(TERMINAL_EDGE_PATTERN, '')
|
||||
// 상태줄 기호(◑ ▕░ 등)가 섞인 줄은 입력이 아니다.
|
||||
if (!continuation || NON_PROSE_GLYPH_PATTERN.test(continuation)) break
|
||||
parts.push(continuation)
|
||||
}
|
||||
return parts.join(' ').replace(/\s+/gu, ' ').trimStart()
|
||||
}
|
||||
return null
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue