surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
| @@ -1,0 +1,5 @@ | ||
| 1 | +/** | |
| 2 | + * The line an ask ends with when its diff was cut to fit the prompt. | |
| 3 | + */ | |
| 4 | +export const ASK_CUT_NOTE = | |
| 5 | + '(The rest of this diff was cut: it did not fit in the prompt.)' |
| @@ -1,0 +1,33 @@ | ||
| 1 | +import { ASK_CUT_NOTE } from './ask-cut-note.js' | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * An armed ask's text as it fits in the room a prompt's context has left: | |
| 5 | + * whole when it fits, else its first lines and ASK_CUT_NOTE. | |
| 6 | + * | |
| 7 | + * @param text the armed ask's text | |
| 8 | + * @param room the characters the context has left | |
| 9 | + * @returns the text to attach, or undefined when not even its first line fits | |
| 10 | + */ | |
| 11 | +export function fittedAskTextOf(text: string, room: number): string | undefined { | |
| 12 | + if (text.length <= room) { | |
| 13 | + return text | |
| 14 | + } | |
| 15 | + | |
| 16 | + const kept: string[] = [] | |
| 17 | + let used = ASK_CUT_NOTE.length | |
| 18 | + | |
| 19 | + for (const line of text.split('\n')) { | |
| 20 | + const cost = line.length + 1 | |
| 21 | + | |
| 22 | + if (used + cost > room) { | |
| 23 | + break | |
| 24 | + } | |
| 25 | + | |
| 26 | + kept.push(line) | |
| 27 | + used += cost | |
| 28 | + } | |
| 29 | + | |
| 30 | + const hasBody = kept.length > 1 | |
| 31 | + | |
| 32 | + return hasBody ? `${kept.join('\n')}\n${ASK_CUT_NOTE}` : undefined | |
| 33 | +} |
| @@ -1,4 +1,6 @@ | ||
| 1 | 1 | export * from './armed-ask' |
| 2 | 2 | export * from './armed-ask-of.js' |
| 3 | +export * from './ask-cut-note.js' | |
| 4 | +export * from './fitted-ask-text-of.js' | |
| 3 | 5 | |
| 4 | 6 | export * as default from '.' |
| @@ -6,5 +6,6 @@ | ||
| 6 | 6 | export * from './max-listed-directories.js' |
| 7 | 7 | export * from './max-untracked-probes.js' |
| 8 | 8 | export * from './pane-top-pad-rows.js' |
| 9 | +export * from './prompt-context-max-chars.js' | |
| 9 | 10 | |
| 10 | 11 | export * as default from '.' |
| @@ -1,0 +1,5 @@ | ||
| 1 | +/** | |
| 2 | + * The most characters a prompt's context carries, its entries together; the | |
| 3 | + * engine refuses a context past it, so an ask is fitted to what is left. | |
| 4 | + */ | |
| 5 | +export const PROMPT_CONTEXT_MAX_CHARS = 32_000 |
| @@ -40,6 +40,7 @@ | ||
| 40 | 40 | let hasShownOnce = false |
| 41 | 41 | let wasDrawnSinceProbe = false |
| 42 | 42 | let armed: Ask.ArmedAsk | null = null |
| 43 | + let carrying: Ask.ArmedAsk | null = null | |
| 43 | 44 | let isRefreshing = false |
| 44 | 45 | let isRefreshQueued = false |
| 45 | 46 | let generation = 0 |
| @@ -592,21 +593,45 @@ | ||
| 592 | 593 | on('prompt.submit', async ($, e, next) => { |
| 593 | 594 | const asked = armed |
| 594 | 595 | |
| 595 | - if (!host || !asked) { | |
| 596 | + if (!host || !asked || carrying === asked) { | |
| 596 | 597 | return next(e) |
| 597 | 598 | } |
| 598 | 599 | |
| 599 | - const result = await next({ | |
| 600 | - ...e, | |
| 601 | - context: [...(e.context ?? []), asked.text], | |
| 602 | - }) | |
| 600 | + const context = e.context ?? [] | |
| 601 | + const used = context.reduce((sum, entry) => sum + entry.length, 0) | |
| 602 | + const text = Ask.fittedAskTextOf( | |
| 603 | + asked.text, | |
| 604 | + Limits.PROMPT_CONTEXT_MAX_CHARS - used, | |
| 605 | + ) | |
| 603 | 606 | |
| 604 | - if (result.drop === undefined && armed === asked) { | |
| 607 | + if (text === undefined) { | |
| 605 | 608 | disarm(host) |
| 609 | + host.status( | |
| 610 | + `${Views.sanitizeName(asked.path)}'s diff did not fit in the prompt ` + | |
| 611 | + `and was dropped`, | |
| 612 | + ) | |
| 606 | 613 | redraw(host) |
| 607 | - Record.recorderOf(host).asked(asked.lines) | |
| 614 | + | |
| 615 | + return next(e) | |
| 608 | 616 | } |
| 609 | 617 | |
| 610 | - return result | |
| 618 | + carrying = asked | |
| 619 | + | |
| 620 | + try { | |
| 621 | + const result = await next({ ...e, context: [...context, text] }) | |
| 622 | + | |
| 623 | + if (result.drop === undefined) { | |
| 624 | + Record.recorderOf(host).asked(asked.lines) | |
| 625 | + | |
| 626 | + if (armed === asked) { | |
| 627 | + disarm(host) | |
| 628 | + redraw(host) | |
| 629 | + } | |
| 630 | + } | |
| 631 | + | |
| 632 | + return result | |
| 633 | + } finally { | |
| 634 | + carrying = null | |
| 635 | + } | |
| 611 | 636 | }) |
| 612 | 637 | } |