@ponythewhite/base-context-tui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +23 -0
  2. package/NOTICE +22 -0
  3. package/README.md +784 -0
  4. package/dist/LICENSE +23 -0
  5. package/dist/NOTICE +22 -0
  6. package/dist/autocomplete.d.ts +61 -0
  7. package/dist/autocomplete.d.ts.map +1 -0
  8. package/dist/autocomplete.js +601 -0
  9. package/dist/autocomplete.js.map +1 -0
  10. package/dist/build-info.json +14 -0
  11. package/dist/components/box.d.ts +24 -0
  12. package/dist/components/box.d.ts.map +1 -0
  13. package/dist/components/box.js +114 -0
  14. package/dist/components/box.js.map +1 -0
  15. package/dist/components/cancellable-loader.d.ts +22 -0
  16. package/dist/components/cancellable-loader.d.ts.map +1 -0
  17. package/dist/components/cancellable-loader.js +35 -0
  18. package/dist/components/cancellable-loader.js.map +1 -0
  19. package/dist/components/editor.d.ts +275 -0
  20. package/dist/components/editor.d.ts.map +1 -0
  21. package/dist/components/editor.js +1968 -0
  22. package/dist/components/editor.js.map +1 -0
  23. package/dist/components/image.d.ts +34 -0
  24. package/dist/components/image.d.ts.map +1 -0
  25. package/dist/components/image.js +96 -0
  26. package/dist/components/image.js.map +1 -0
  27. package/dist/components/input.d.ts +37 -0
  28. package/dist/components/input.d.ts.map +1 -0
  29. package/dist/components/input.js +385 -0
  30. package/dist/components/input.js.map +1 -0
  31. package/dist/components/loader.d.ts +29 -0
  32. package/dist/components/loader.d.ts.map +1 -0
  33. package/dist/components/loader.js +69 -0
  34. package/dist/components/loader.js.map +1 -0
  35. package/dist/components/markdown.d.ts +105 -0
  36. package/dist/components/markdown.d.ts.map +1 -0
  37. package/dist/components/markdown.js +776 -0
  38. package/dist/components/markdown.js.map +1 -0
  39. package/dist/components/select-list.d.ts +61 -0
  40. package/dist/components/select-list.d.ts.map +1 -0
  41. package/dist/components/select-list.js +206 -0
  42. package/dist/components/select-list.js.map +1 -0
  43. package/dist/components/settings-list.d.ts +49 -0
  44. package/dist/components/settings-list.d.ts.map +1 -0
  45. package/dist/components/settings-list.js +170 -0
  46. package/dist/components/settings-list.js.map +1 -0
  47. package/dist/components/spacer.d.ts +12 -0
  48. package/dist/components/spacer.d.ts.map +1 -0
  49. package/dist/components/spacer.js +21 -0
  50. package/dist/components/spacer.js.map +1 -0
  51. package/dist/components/text.d.ts +19 -0
  52. package/dist/components/text.d.ts.map +1 -0
  53. package/dist/components/text.js +77 -0
  54. package/dist/components/text.js.map +1 -0
  55. package/dist/components/truncated-text.d.ts +14 -0
  56. package/dist/components/truncated-text.d.ts.map +1 -0
  57. package/dist/components/truncated-text.js +44 -0
  58. package/dist/components/truncated-text.js.map +1 -0
  59. package/dist/editor-component.d.ts +51 -0
  60. package/dist/editor-component.d.ts.map +1 -0
  61. package/dist/editor-component.js +2 -0
  62. package/dist/editor-component.js.map +1 -0
  63. package/dist/fullscreen.d.ts +105 -0
  64. package/dist/fullscreen.d.ts.map +1 -0
  65. package/dist/fullscreen.js +597 -0
  66. package/dist/fullscreen.js.map +1 -0
  67. package/dist/fuzzy.d.ts +26 -0
  68. package/dist/fuzzy.d.ts.map +1 -0
  69. package/dist/fuzzy.js +114 -0
  70. package/dist/fuzzy.js.map +1 -0
  71. package/dist/index.d.ts +29 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +27 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/keybindings.d.ts +239 -0
  76. package/dist/keybindings.d.ts.map +1 -0
  77. package/dist/keybindings.js +222 -0
  78. package/dist/keybindings.js.map +1 -0
  79. package/dist/keys.d.ts +184 -0
  80. package/dist/keys.d.ts.map +1 -0
  81. package/dist/keys.js +1163 -0
  82. package/dist/keys.js.map +1 -0
  83. package/dist/kill-ring.d.ts +26 -0
  84. package/dist/kill-ring.d.ts.map +1 -0
  85. package/dist/kill-ring.js +42 -0
  86. package/dist/kill-ring.js.map +1 -0
  87. package/dist/latex.d.ts +21 -0
  88. package/dist/latex.d.ts.map +1 -0
  89. package/dist/latex.js +797 -0
  90. package/dist/latex.js.map +1 -0
  91. package/dist/mouse.d.ts +24 -0
  92. package/dist/mouse.d.ts.map +1 -0
  93. package/dist/mouse.js +34 -0
  94. package/dist/mouse.js.map +1 -0
  95. package/dist/render-cache.d.ts +9 -0
  96. package/dist/render-cache.d.ts.map +1 -0
  97. package/dist/render-cache.js +23 -0
  98. package/dist/render-cache.js.map +1 -0
  99. package/dist/selection-metadata.d.ts +22 -0
  100. package/dist/selection-metadata.d.ts.map +1 -0
  101. package/dist/selection-metadata.js +141 -0
  102. package/dist/selection-metadata.js.map +1 -0
  103. package/dist/slash-command-context.d.ts +12 -0
  104. package/dist/slash-command-context.d.ts.map +1 -0
  105. package/dist/slash-command-context.js +32 -0
  106. package/dist/slash-command-context.js.map +1 -0
  107. package/dist/stdin-buffer.d.ts +50 -0
  108. package/dist/stdin-buffer.d.ts.map +1 -0
  109. package/dist/stdin-buffer.js +313 -0
  110. package/dist/stdin-buffer.js.map +1 -0
  111. package/dist/terminal-colors.d.ts +31 -0
  112. package/dist/terminal-colors.d.ts.map +1 -0
  113. package/dist/terminal-colors.js +161 -0
  114. package/dist/terminal-colors.js.map +1 -0
  115. package/dist/terminal-image.d.ts +85 -0
  116. package/dist/terminal-image.d.ts.map +1 -0
  117. package/dist/terminal-image.js +324 -0
  118. package/dist/terminal-image.js.map +1 -0
  119. package/dist/terminal.d.ts +113 -0
  120. package/dist/terminal.d.ts.map +1 -0
  121. package/dist/terminal.js +486 -0
  122. package/dist/terminal.js.map +1 -0
  123. package/dist/tui.d.ts +313 -0
  124. package/dist/tui.d.ts.map +1 -0
  125. package/dist/tui.js +1683 -0
  126. package/dist/tui.js.map +1 -0
  127. package/dist/undo-stack.d.ts +16 -0
  128. package/dist/undo-stack.d.ts.map +1 -0
  129. package/dist/undo-stack.js +26 -0
  130. package/dist/undo-stack.js.map +1 -0
  131. package/dist/utils.d.ts +98 -0
  132. package/dist/utils.d.ts.map +1 -0
  133. package/dist/utils.js +1233 -0
  134. package/dist/utils.js.map +1 -0
  135. package/package.json +63 -0
package/dist/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mario Zechner
4
+ Copyright (c) 2026 Prime Intellect
5
+ Copyright (c) 2026 BaseModelAI and Jacek Dąbrowski
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/dist/NOTICE ADDED
@@ -0,0 +1,22 @@
1
+ Synerise base-context is a source-owned fork of Prime Agent v0.9.3.
2
+ Repository: https://github.com/BaseModelAI/base-context
3
+ Fork ancestor: 915c78f42c248b08238dd27fcd4bcab32c60beab
4
+
5
+ Upstream: https://github.com/PrimeIntellect-ai/prime-agent
6
+ Copyright (c) 2025 Mario Zechner
7
+ Copyright (c) 2026 Prime Intellect
8
+ Fork modifications: Copyright (c) 2026 BaseModelAI and Jacek Dąbrowski
9
+
10
+ Distributed under the MIT License. See LICENSE.
11
+ Provider names and model identifiers identify their respective providers;
12
+ this fork is not an official Prime Intellect release.
13
+
14
+ Prime Agent builds on Mario Zechner's Pi project:
15
+ https://github.com/badlogic/pi-mono
16
+
17
+ We also credit Prime Intellect's PrimeRL authors for their open-source
18
+ reinforcement-learning work: https://github.com/PrimeIntellect-ai/prime-rl
19
+ This acknowledgement does not imply that PrimeRL is a runtime dependency
20
+ or that upstream authors endorse this fork.
21
+
22
+ Third-party dependencies retain their own licenses and notices.
@@ -0,0 +1,61 @@
1
+ export interface AutocompleteItem {
2
+ value: string;
3
+ label: string;
4
+ description?: string;
5
+ argumentHint?: string;
6
+ sourceTag?: string;
7
+ takesArgument?: boolean;
8
+ }
9
+ type Awaitable<T> = T | Promise<T>;
10
+ export interface SlashCommand {
11
+ name: string;
12
+ aliases?: readonly string[];
13
+ description?: string;
14
+ argumentHint?: string;
15
+ sourceTag?: string;
16
+ takesArgument?: boolean;
17
+ getArgumentCompletions?(argumentPrefix: string): Awaitable<AutocompleteItem[] | null>;
18
+ }
19
+ export interface AutocompleteSuggestions {
20
+ items: AutocompleteItem[];
21
+ prefix: string;
22
+ kind?: "slash-command" | "file";
23
+ }
24
+ export interface AutocompleteProvider {
25
+ getSuggestions(lines: string[], cursorLine: number, cursorCol: number, options: {
26
+ signal: AbortSignal;
27
+ force?: boolean;
28
+ }): Promise<AutocompleteSuggestions | null>;
29
+ applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
30
+ lines: string[];
31
+ cursorLine: number;
32
+ cursorCol: number;
33
+ };
34
+ shouldTriggerFileCompletion?(lines: string[], cursorLine: number, cursorCol: number): boolean;
35
+ }
36
+ export declare class CombinedAutocompleteProvider implements AutocompleteProvider {
37
+ private commands;
38
+ private basePath;
39
+ private fdPath;
40
+ constructor(commands: (AutocompleteItem | SlashCommand)[] | undefined, basePath: string, fdPath?: string | null);
41
+ getSuggestions(lines: string[], cursorLine: number, cursorCol: number, options: {
42
+ signal: AbortSignal;
43
+ force?: boolean;
44
+ }): Promise<AutocompleteSuggestions | null>;
45
+ applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
46
+ lines: string[];
47
+ cursorLine: number;
48
+ cursorCol: number;
49
+ };
50
+ private extractAtPrefix;
51
+ private extractPathPrefix;
52
+ private expandHomePath;
53
+ private resolveScopedFuzzyQuery;
54
+ private scopedPathForDisplay;
55
+ private getFileSuggestions;
56
+ private scoreEntry;
57
+ private getFuzzyFileSuggestions;
58
+ shouldTriggerFileCompletion(lines: string[], cursorLine: number, cursorCol: number): boolean;
59
+ }
60
+ export {};
61
+ //# sourceMappingURL=autocomplete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../src/autocomplete.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"child_process\";\nimport { readdirSync, statSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { basename, dirname, join } from \"path\";\nimport { fuzzyFilter } from \"./fuzzy.js\";\nimport { getSlashCommandContext } from \"./slash-command-context.js\";\n\nconst PATH_DELIMITERS = new Set([\" \", \"\\t\", '\"', \"'\", \"=\"]);\n\nfunction toDisplayPath(value: string): string {\n\treturn value.replace(/\\\\/g, \"/\");\n}\n\nfunction escapeRegex(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction buildFdPathQuery(query: string): string {\n\tconst normalized = toDisplayPath(query);\n\tif (!normalized.includes(\"/\")) {\n\t\treturn normalized;\n\t}\n\n\tconst hasTrailingSeparator = normalized.endsWith(\"/\");\n\tconst trimmed = normalized.replace(/^\\/+|\\/+$/g, \"\");\n\tif (!trimmed) {\n\t\treturn normalized;\n\t}\n\n\tconst separatorPattern = \"[\\\\\\\\/]\";\n\tconst segments = trimmed\n\t\t.split(\"/\")\n\t\t.filter(Boolean)\n\t\t.map((segment) => escapeRegex(segment));\n\tif (segments.length === 0) {\n\t\treturn normalized;\n\t}\n\n\tlet pattern = segments.join(separatorPattern);\n\tif (hasTrailingSeparator) {\n\t\tpattern += separatorPattern;\n\t}\n\treturn pattern;\n}\n\nfunction findLastDelimiter(text: string): number {\n\tfor (let i = text.length - 1; i >= 0; i -= 1) {\n\t\tif (PATH_DELIMITERS.has(text[i] ?? \"\")) {\n\t\t\treturn i;\n\t\t}\n\t}\n\treturn -1;\n}\n\nfunction findUnclosedQuoteStart(text: string): number | null {\n\tlet inQuotes = false;\n\tlet quoteStart = -1;\n\n\tfor (let i = 0; i < text.length; i += 1) {\n\t\tif (text[i] === '\"') {\n\t\t\tinQuotes = !inQuotes;\n\t\t\tif (inQuotes) {\n\t\t\t\tquoteStart = i;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn inQuotes ? quoteStart : null;\n}\n\nfunction isTokenStart(text: string, index: number): boolean {\n\treturn index === 0 || PATH_DELIMITERS.has(text[index - 1] ?? \"\");\n}\n\nfunction extractQuotedPrefix(text: string): string | null {\n\tconst quoteStart = findUnclosedQuoteStart(text);\n\tif (quoteStart === null) {\n\t\treturn null;\n\t}\n\n\tif (quoteStart > 0 && text[quoteStart - 1] === \"@\") {\n\t\tif (!isTokenStart(text, quoteStart - 1)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn text.slice(quoteStart - 1);\n\t}\n\n\tif (!isTokenStart(text, quoteStart)) {\n\t\treturn null;\n\t}\n\n\treturn text.slice(quoteStart);\n}\n\nfunction parsePathPrefix(prefix: string): { rawPrefix: string; isAtPrefix: boolean; isQuotedPrefix: boolean } {\n\tif (prefix.startsWith('@\"')) {\n\t\treturn { rawPrefix: prefix.slice(2), isAtPrefix: true, isQuotedPrefix: true };\n\t}\n\tif (prefix.startsWith('\"')) {\n\t\treturn { rawPrefix: prefix.slice(1), isAtPrefix: false, isQuotedPrefix: true };\n\t}\n\tif (prefix.startsWith(\"@\")) {\n\t\treturn { rawPrefix: prefix.slice(1), isAtPrefix: true, isQuotedPrefix: false };\n\t}\n\treturn { rawPrefix: prefix, isAtPrefix: false, isQuotedPrefix: false };\n}\n\nfunction buildCompletionValue(\n\tpath: string,\n\toptions: { isDirectory: boolean; isAtPrefix: boolean; isQuotedPrefix: boolean },\n): string {\n\tconst needsQuotes = options.isQuotedPrefix || path.includes(\" \");\n\tconst prefix = options.isAtPrefix ? \"@\" : \"\";\n\n\tif (!needsQuotes) {\n\t\treturn `${prefix}${path}`;\n\t}\n\n\tconst openQuote = `${prefix}\"`;\n\tconst closeQuote = '\"';\n\treturn `${openQuote}${path}${closeQuote}`;\n}\n\nasync function walkDirectoryWithFd(\n\tbaseDir: string,\n\tfdPath: string,\n\tquery: string,\n\tmaxResults: number,\n\tsignal: AbortSignal,\n): Promise<Array<{ path: string; isDirectory: boolean }>> {\n\tconst args = [\n\t\t\"--base-directory\",\n\t\tbaseDir,\n\t\t\"--max-results\",\n\t\tString(maxResults),\n\t\t\"--type\",\n\t\t\"f\",\n\t\t\"--type\",\n\t\t\"d\",\n\t\t\"--follow\",\n\t\t\"--hidden\",\n\t\t\"--exclude\",\n\t\t\".git\",\n\t\t\"--exclude\",\n\t\t\".git/*\",\n\t\t\"--exclude\",\n\t\t\".git/**\",\n\t];\n\n\tif (toDisplayPath(query).includes(\"/\")) {\n\t\targs.push(\"--full-path\");\n\t}\n\n\tif (query) {\n\t\targs.push(buildFdPathQuery(query));\n\t}\n\n\treturn await new Promise((resolve) => {\n\t\tif (signal.aborted) {\n\t\t\tresolve([]);\n\t\t\treturn;\n\t\t}\n\n\t\tconst child = spawn(fdPath, args, {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\t\tlet stdout = \"\";\n\t\tlet resolved = false;\n\n\t\tconst finish = (results: Array<{ path: string; isDirectory: boolean }>) => {\n\t\t\tif (resolved) return;\n\t\t\tresolved = true;\n\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\tresolve(results);\n\t\t};\n\n\t\tconst onAbort = () => {\n\t\t\tif (child.exitCode === null) {\n\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t}\n\t\t};\n\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tchild.stdout.setEncoding(\"utf-8\");\n\t\tchild.stdout.on(\"data\", (chunk: string) => {\n\t\t\tstdout += chunk;\n\t\t});\n\t\tchild.on(\"error\", () => {\n\t\t\tfinish([]);\n\t\t});\n\t\tchild.on(\"close\", (code) => {\n\t\t\tif (signal.aborted || code !== 0 || !stdout) {\n\t\t\t\tfinish([]);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst lines = stdout.trim().split(\"\\n\").filter(Boolean);\n\t\t\tconst results: Array<{ path: string; isDirectory: boolean }> = [];\n\n\t\t\tfor (const line of lines) {\n\t\t\t\tconst displayLine = toDisplayPath(line);\n\t\t\t\tconst hasTrailingSeparator = displayLine.endsWith(\"/\");\n\t\t\t\tconst normalizedPath = hasTrailingSeparator ? displayLine.slice(0, -1) : displayLine;\n\t\t\t\tif (normalizedPath === \".git\" || normalizedPath.startsWith(\".git/\") || normalizedPath.includes(\"/.git/\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: displayLine,\n\t\t\t\t\tisDirectory: hasTrailingSeparator,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tfinish(results);\n\t\t});\n\t});\n}\n\nexport interface AutocompleteItem {\n\tvalue: string;\n\tlabel: string;\n\tdescription?: string;\n\targumentHint?: string;\n\tsourceTag?: string;\n\ttakesArgument?: boolean;\n}\n\ntype Awaitable<T> = T | Promise<T>;\n\nexport interface SlashCommand {\n\tname: string;\n\taliases?: readonly string[];\n\tdescription?: string;\n\targumentHint?: string;\n\tsourceTag?: string;\n\ttakesArgument?: boolean;\n\tgetArgumentCompletions?(argumentPrefix: string): Awaitable<AutocompleteItem[] | null>;\n}\n\nexport interface AutocompleteSuggestions {\n\titems: AutocompleteItem[];\n\tprefix: string; // What we're matching against (e.g., \"/\" or \"src/\")\n\tkind?: \"slash-command\" | \"file\";\n}\n\nexport interface AutocompleteProvider {\n\tgetSuggestions(\n\t\tlines: string[],\n\t\tcursorLine: number,\n\t\tcursorCol: number,\n\t\toptions: { signal: AbortSignal; force?: boolean },\n\t): Promise<AutocompleteSuggestions | null>;\n\n\tapplyCompletion(\n\t\tlines: string[],\n\t\tcursorLine: number,\n\t\tcursorCol: number,\n\t\titem: AutocompleteItem,\n\t\tprefix: string,\n\t): {\n\t\tlines: string[];\n\t\tcursorLine: number;\n\t\tcursorCol: number;\n\t};\n\n\tshouldTriggerFileCompletion?(lines: string[], cursorLine: number, cursorCol: number): boolean;\n}\n\nexport class CombinedAutocompleteProvider implements AutocompleteProvider {\n\tprivate commands: (SlashCommand | AutocompleteItem)[];\n\tprivate basePath: string;\n\tprivate fdPath: string | null;\n\n\tconstructor(commands: (SlashCommand | AutocompleteItem)[] = [], basePath: string, fdPath: string | null = null) {\n\t\tthis.commands = commands;\n\t\tthis.basePath = basePath;\n\t\tthis.fdPath = fdPath;\n\t}\n\n\tasync getSuggestions(\n\t\tlines: string[],\n\t\tcursorLine: number,\n\t\tcursorCol: number,\n\t\toptions: { signal: AbortSignal; force?: boolean },\n\t): Promise<AutocompleteSuggestions | null> {\n\t\tconst currentLine = lines[cursorLine] || \"\";\n\t\tconst textBeforeCursor = currentLine.slice(0, cursorCol);\n\n\t\tconst atPrefix = this.extractAtPrefix(textBeforeCursor);\n\t\tif (atPrefix) {\n\t\t\tconst { rawPrefix, isQuotedPrefix } = parsePathPrefix(atPrefix);\n\t\t\tconst suggestions = await this.getFuzzyFileSuggestions(rawPrefix, {\n\t\t\t\tisQuotedPrefix,\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\t\t\tif (suggestions.length === 0) return null;\n\n\t\t\treturn {\n\t\t\t\titems: suggestions,\n\t\t\t\tprefix: atPrefix,\n\t\t\t\tkind: \"file\",\n\t\t\t};\n\t\t}\n\n\t\tconst slashContext = options.force ? null : getSlashCommandContext(lines, cursorLine, cursorCol);\n\t\tif (slashContext?.kind === \"name\") {\n\t\t\tconst prefix = slashContext.prefix.slice(1);\n\t\t\tconst commandItems = this.commands.map((cmd) => {\n\t\t\t\tconst name = \"name\" in cmd ? cmd.name : cmd.value;\n\t\t\t\tconst aliases = \"aliases\" in cmd && cmd.aliases ? cmd.aliases : [];\n\t\t\t\tconst argumentHint = \"argumentHint\" in cmd && cmd.argumentHint ? cmd.argumentHint : undefined;\n\t\t\t\tconst sourceTag = \"sourceTag\" in cmd && cmd.sourceTag ? cmd.sourceTag : undefined;\n\t\t\t\tconst takesArgument = \"takesArgument\" in cmd ? cmd.takesArgument === true : false;\n\t\t\t\treturn {\n\t\t\t\t\tname,\n\t\t\t\t\tsearchText: [name, ...aliases].join(\" \"),\n\t\t\t\t\tlabel: name,\n\t\t\t\t\tdescription: cmd.description,\n\t\t\t\t\targumentHint,\n\t\t\t\t\tsourceTag,\n\t\t\t\t\ttakesArgument,\n\t\t\t\t};\n\t\t\t});\n\n\t\t\tconst filtered = fuzzyFilter(commandItems, prefix, (item) => item.searchText).map((item) => ({\n\t\t\t\tvalue: item.name,\n\t\t\t\tlabel: item.label,\n\t\t\t\t...(item.description && { description: item.description }),\n\t\t\t\t...(item.argumentHint && { argumentHint: item.argumentHint }),\n\t\t\t\t...(item.sourceTag && { sourceTag: item.sourceTag }),\n\t\t\t\t...(item.takesArgument && { takesArgument: true }),\n\t\t\t}));\n\n\t\t\tif (filtered.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\titems: filtered,\n\t\t\t\t\tprefix: slashContext.prefix,\n\t\t\t\t\tkind: \"slash-command\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} else if (slashContext?.kind === \"argument\") {\n\t\t\tconst command = this.commands.find((cmd) => {\n\t\t\t\tconst name = \"name\" in cmd ? cmd.name : cmd.value;\n\t\t\t\treturn name === slashContext.commandName;\n\t\t\t});\n\t\t\tif (!command || !(\"getArgumentCompletions\" in command) || !command.getArgumentCompletions) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst argumentSuggestions = await command.getArgumentCompletions(slashContext.prefix);\n\t\t\tif (!Array.isArray(argumentSuggestions) || argumentSuggestions.length === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\titems: argumentSuggestions,\n\t\t\t\tprefix: slashContext.prefix,\n\t\t\t};\n\t\t}\n\n\t\tconst pathMatch = this.extractPathPrefix(textBeforeCursor, options.force ?? false);\n\t\tif (pathMatch === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst suggestions = this.getFileSuggestions(pathMatch);\n\t\tif (suggestions.length === 0) return null;\n\n\t\treturn {\n\t\t\titems: suggestions,\n\t\t\tprefix: pathMatch,\n\t\t\tkind: \"file\",\n\t\t};\n\t}\n\n\tapplyCompletion(\n\t\tlines: string[],\n\t\tcursorLine: number,\n\t\tcursorCol: number,\n\t\titem: AutocompleteItem,\n\t\tprefix: string,\n\t): { lines: string[]; cursorLine: number; cursorCol: number } {\n\t\tconst currentLine = lines[cursorLine] || \"\";\n\t\tconst beforePrefix = currentLine.slice(0, cursorCol - prefix.length);\n\t\tconst afterCursor = currentLine.slice(cursorCol);\n\t\tconst isQuotedPrefix = prefix.startsWith('\"') || prefix.startsWith('@\"');\n\t\tconst hasLeadingQuoteAfterCursor = afterCursor.startsWith('\"');\n\t\tconst hasTrailingQuoteInItem = item.value.endsWith('\"');\n\t\tconst adjustedAfterCursor =\n\t\t\tisQuotedPrefix && hasTrailingQuoteInItem && hasLeadingQuoteAfterCursor ? afterCursor.slice(1) : afterCursor;\n\t\tconst slashContext = getSlashCommandContext(lines, cursorLine, cursorCol);\n\n\t\tconst isSlashCommand =\n\t\t\tslashContext?.kind === \"name\" &&\n\t\t\tslashContext.prefix === prefix &&\n\t\t\tthis.commands.some((command) => (\"name\" in command ? command.name : command.value) === item.value);\n\t\tif (isSlashCommand) {\n\t\t\tconst hasSeparatorAfterCursor = /^[ \\t]/.test(adjustedAfterCursor);\n\t\t\tconst separator = hasSeparatorAfterCursor ? \"\" : \" \";\n\t\t\tconst newLine = `${beforePrefix}/${item.value}${separator}${adjustedAfterCursor}`;\n\t\t\tconst newLines = [...lines];\n\t\t\tnewLines[cursorLine] = newLine;\n\n\t\t\treturn {\n\t\t\t\tlines: newLines,\n\t\t\t\tcursorLine,\n\t\t\t\tcursorCol: beforePrefix.length + item.value.length + 2,\n\t\t\t};\n\t\t}\n\n\t\tif (prefix.startsWith(\"@\")) {\n\t\t\t// Don't add space after directories so user can continue autocompleting\n\t\t\tconst isDirectory = item.label.endsWith(\"/\");\n\t\t\tconst suffix = isDirectory ? \"\" : \" \";\n\t\t\tconst newLine = `${beforePrefix + item.value}${suffix}${adjustedAfterCursor}`;\n\t\t\tconst newLines = [...lines];\n\t\t\tnewLines[cursorLine] = newLine;\n\n\t\t\tconst hasTrailingQuote = item.value.endsWith('\"');\n\t\t\tconst cursorOffset = isDirectory && hasTrailingQuote ? item.value.length - 1 : item.value.length;\n\n\t\t\treturn {\n\t\t\t\tlines: newLines,\n\t\t\t\tcursorLine,\n\t\t\t\tcursorCol: beforePrefix.length + cursorOffset + suffix.length,\n\t\t\t};\n\t\t}\n\n\t\tif (slashContext?.kind === \"argument\" && slashContext.prefix === prefix) {\n\t\t\tconst newLine = beforePrefix + item.value + adjustedAfterCursor;\n\t\t\tconst newLines = [...lines];\n\t\t\tnewLines[cursorLine] = newLine;\n\n\t\t\tconst isDirectory = item.label.endsWith(\"/\");\n\t\t\tconst hasTrailingQuote = item.value.endsWith('\"');\n\t\t\tconst cursorOffset = isDirectory && hasTrailingQuote ? item.value.length - 1 : item.value.length;\n\n\t\t\treturn {\n\t\t\t\tlines: newLines,\n\t\t\t\tcursorLine,\n\t\t\t\tcursorCol: beforePrefix.length + cursorOffset,\n\t\t\t};\n\t\t}\n\n\t\tconst newLine = beforePrefix + item.value + adjustedAfterCursor;\n\t\tconst newLines = [...lines];\n\t\tnewLines[cursorLine] = newLine;\n\n\t\tconst isDirectory = item.label.endsWith(\"/\");\n\t\tconst hasTrailingQuote = item.value.endsWith('\"');\n\t\tconst cursorOffset = isDirectory && hasTrailingQuote ? item.value.length - 1 : item.value.length;\n\n\t\treturn {\n\t\t\tlines: newLines,\n\t\t\tcursorLine,\n\t\t\tcursorCol: beforePrefix.length + cursorOffset,\n\t\t};\n\t}\n\n\tprivate extractAtPrefix(text: string): string | null {\n\t\tconst quotedPrefix = extractQuotedPrefix(text);\n\t\tif (quotedPrefix?.startsWith('@\"')) {\n\t\t\treturn quotedPrefix;\n\t\t}\n\n\t\tconst lastDelimiterIndex = findLastDelimiter(text);\n\t\tconst tokenStart = lastDelimiterIndex === -1 ? 0 : lastDelimiterIndex + 1;\n\n\t\tif (text[tokenStart] === \"@\") {\n\t\t\treturn text.slice(tokenStart);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate extractPathPrefix(text: string, forceExtract: boolean = false): string | null {\n\t\tconst quotedPrefix = extractQuotedPrefix(text);\n\t\tif (quotedPrefix) {\n\t\t\treturn quotedPrefix;\n\t\t}\n\n\t\tconst lastDelimiterIndex = findLastDelimiter(text);\n\t\tconst pathPrefix = lastDelimiterIndex === -1 ? text : text.slice(lastDelimiterIndex + 1);\n\n\t\tif (forceExtract) {\n\t\t\treturn pathPrefix;\n\t\t}\n\n\t\t// For natural triggers, return if it looks like a path, ends with /, starts with ~/, .\n\t\tif (pathPrefix.includes(\"/\") || pathPrefix.startsWith(\".\") || pathPrefix.startsWith(\"~/\")) {\n\t\t\treturn pathPrefix;\n\t\t}\n\n\t\tif (pathPrefix === \"\" && text.endsWith(\" \")) {\n\t\t\treturn pathPrefix;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate expandHomePath(path: string): string {\n\t\tif (path.startsWith(\"~/\")) {\n\t\t\tconst expandedPath = join(homedir(), path.slice(2));\n\t\t\treturn path.endsWith(\"/\") && !expandedPath.endsWith(\"/\") ? `${expandedPath}/` : expandedPath;\n\t\t} else if (path === \"~\") {\n\t\t\treturn homedir();\n\t\t}\n\t\treturn path;\n\t}\n\n\tprivate resolveScopedFuzzyQuery(rawQuery: string): { baseDir: string; query: string; displayBase: string } | null {\n\t\tconst normalizedQuery = toDisplayPath(rawQuery);\n\t\tconst slashIndex = normalizedQuery.lastIndexOf(\"/\");\n\t\tif (slashIndex === -1) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst displayBase = normalizedQuery.slice(0, slashIndex + 1);\n\t\tconst query = normalizedQuery.slice(slashIndex + 1);\n\n\t\tlet baseDir: string;\n\t\tif (displayBase.startsWith(\"~/\")) {\n\t\t\tbaseDir = this.expandHomePath(displayBase);\n\t\t} else if (displayBase.startsWith(\"/\")) {\n\t\t\tbaseDir = displayBase;\n\t\t} else {\n\t\t\tbaseDir = join(this.basePath, displayBase);\n\t\t}\n\n\t\ttry {\n\t\t\tif (!statSync(baseDir).isDirectory()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn { baseDir, query, displayBase };\n\t}\n\n\tprivate scopedPathForDisplay(displayBase: string, relativePath: string): string {\n\t\tconst normalizedRelativePath = toDisplayPath(relativePath);\n\t\tif (displayBase === \"/\") {\n\t\t\treturn `/${normalizedRelativePath}`;\n\t\t}\n\t\treturn `${toDisplayPath(displayBase)}${normalizedRelativePath}`;\n\t}\n\n\tprivate getFileSuggestions(prefix: string): AutocompleteItem[] {\n\t\ttry {\n\t\t\tlet searchDir: string;\n\t\t\tlet searchPrefix: string;\n\t\t\tconst { rawPrefix, isAtPrefix, isQuotedPrefix } = parsePathPrefix(prefix);\n\t\t\tlet expandedPrefix = rawPrefix;\n\n\t\t\tif (expandedPrefix.startsWith(\"~\")) {\n\t\t\t\texpandedPrefix = this.expandHomePath(expandedPrefix);\n\t\t\t}\n\n\t\t\tconst isRootPrefix =\n\t\t\t\trawPrefix === \"\" ||\n\t\t\t\trawPrefix === \"./\" ||\n\t\t\t\trawPrefix === \"../\" ||\n\t\t\t\trawPrefix === \"~\" ||\n\t\t\t\trawPrefix === \"~/\" ||\n\t\t\t\trawPrefix === \"/\" ||\n\t\t\t\t(isAtPrefix && rawPrefix === \"\");\n\n\t\t\tif (isRootPrefix) {\n\t\t\t\tif (rawPrefix.startsWith(\"~\") || expandedPrefix.startsWith(\"/\")) {\n\t\t\t\t\tsearchDir = expandedPrefix;\n\t\t\t\t} else {\n\t\t\t\t\tsearchDir = join(this.basePath, expandedPrefix);\n\t\t\t\t}\n\t\t\t\tsearchPrefix = \"\";\n\t\t\t} else if (rawPrefix.endsWith(\"/\")) {\n\t\t\t\tif (rawPrefix.startsWith(\"~\") || expandedPrefix.startsWith(\"/\")) {\n\t\t\t\t\tsearchDir = expandedPrefix;\n\t\t\t\t} else {\n\t\t\t\t\tsearchDir = join(this.basePath, expandedPrefix);\n\t\t\t\t}\n\t\t\t\tsearchPrefix = \"\";\n\t\t\t} else {\n\t\t\t\tconst dir = dirname(expandedPrefix);\n\t\t\t\tconst file = basename(expandedPrefix);\n\t\t\t\tif (rawPrefix.startsWith(\"~\") || expandedPrefix.startsWith(\"/\")) {\n\t\t\t\t\tsearchDir = dir;\n\t\t\t\t} else {\n\t\t\t\t\tsearchDir = join(this.basePath, dir);\n\t\t\t\t}\n\t\t\t\tsearchPrefix = file;\n\t\t\t}\n\n\t\t\tconst entries = readdirSync(searchDir, { withFileTypes: true });\n\t\t\tconst suggestions: AutocompleteItem[] = [];\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (!entry.name.toLowerCase().startsWith(searchPrefix.toLowerCase())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tlet isDirectory = entry.isDirectory();\n\t\t\t\tif (!isDirectory && entry.isSymbolicLink()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fullPath = join(searchDir, entry.name);\n\t\t\t\t\t\tisDirectory = statSync(fullPath).isDirectory();\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Ignore broken or inaccessible symbolic links.\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet relativePath: string;\n\t\t\t\tconst name = entry.name;\n\t\t\t\tconst displayPrefix = rawPrefix;\n\n\t\t\t\tif (displayPrefix.endsWith(\"/\")) {\n\t\t\t\t\trelativePath = displayPrefix + name;\n\t\t\t\t} else if (displayPrefix.includes(\"/\") || displayPrefix.includes(\"\\\\\")) {\n\t\t\t\t\tif (displayPrefix.startsWith(\"~/\")) {\n\t\t\t\t\t\tconst homeRelativeDir = displayPrefix.slice(2); // Remove ~/\n\t\t\t\t\t\tconst dir = dirname(homeRelativeDir);\n\t\t\t\t\t\trelativePath = `~/${dir === \".\" ? name : join(dir, name)}`;\n\t\t\t\t\t} else if (displayPrefix.startsWith(\"/\")) {\n\t\t\t\t\t\tconst dir = dirname(displayPrefix);\n\t\t\t\t\t\tif (dir === \"/\") {\n\t\t\t\t\t\t\trelativePath = `/${name}`;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\trelativePath = `${dir}/${name}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\trelativePath = join(dirname(displayPrefix), name);\n\t\t\t\t\t\tif (displayPrefix.startsWith(\"./\") && !relativePath.startsWith(\"./\")) {\n\t\t\t\t\t\t\trelativePath = `./${relativePath}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (displayPrefix.startsWith(\"~\")) {\n\t\t\t\t\t\trelativePath = `~/${name}`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\trelativePath = name;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trelativePath = toDisplayPath(relativePath);\n\t\t\t\tconst pathValue = isDirectory ? `${relativePath}/` : relativePath;\n\t\t\t\tconst value = buildCompletionValue(pathValue, {\n\t\t\t\t\tisDirectory,\n\t\t\t\t\tisAtPrefix,\n\t\t\t\t\tisQuotedPrefix,\n\t\t\t\t});\n\n\t\t\t\tsuggestions.push({\n\t\t\t\t\tvalue,\n\t\t\t\t\tlabel: name + (isDirectory ? \"/\" : \"\"),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tsuggestions.sort((a, b) => {\n\t\t\t\tconst aIsDir = a.value.endsWith(\"/\");\n\t\t\t\tconst bIsDir = b.value.endsWith(\"/\");\n\t\t\t\tif (aIsDir && !bIsDir) return -1;\n\t\t\t\tif (!aIsDir && bIsDir) return 1;\n\t\t\t\treturn a.label.localeCompare(b.label);\n\t\t\t});\n\n\t\t\treturn suggestions;\n\t\t} catch (_e) {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tprivate scoreEntry(filePath: string, query: string, isDirectory: boolean): number {\n\t\tconst fileName = basename(filePath);\n\t\tconst lowerFileName = fileName.toLowerCase();\n\t\tconst lowerQuery = query.toLowerCase();\n\n\t\tlet score = 0;\n\n\t\tif (lowerFileName === lowerQuery) score = 100;\n\t\telse if (lowerFileName.startsWith(lowerQuery)) score = 80;\n\t\telse if (lowerFileName.includes(lowerQuery)) score = 50;\n\t\telse if (filePath.toLowerCase().includes(lowerQuery)) score = 30;\n\n\t\tif (isDirectory && score > 0) score += 10;\n\n\t\treturn score;\n\t}\n\n\tprivate async getFuzzyFileSuggestions(\n\t\tquery: string,\n\t\toptions: { isQuotedPrefix: boolean; signal: AbortSignal },\n\t): Promise<AutocompleteItem[]> {\n\t\tif (!this.fdPath || options.signal.aborted) {\n\t\t\treturn [];\n\t\t}\n\n\t\ttry {\n\t\t\tconst scopedQuery = this.resolveScopedFuzzyQuery(query);\n\t\t\tconst fdBaseDir = scopedQuery?.baseDir ?? this.basePath;\n\t\t\tconst fdQuery = scopedQuery?.query ?? query;\n\t\t\tconst entries = await walkDirectoryWithFd(fdBaseDir, this.fdPath, fdQuery, 100, options.signal);\n\t\t\tif (options.signal.aborted) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst scoredEntries = entries\n\t\t\t\t.map((entry) => ({\n\t\t\t\t\t...entry,\n\t\t\t\t\tscore: fdQuery ? this.scoreEntry(entry.path, fdQuery, entry.isDirectory) : 1,\n\t\t\t\t}))\n\t\t\t\t.filter((entry) => entry.score > 0);\n\n\t\t\tscoredEntries.sort((a, b) => b.score - a.score);\n\t\t\tconst topEntries = scoredEntries.slice(0, 20);\n\n\t\t\tconst suggestions: AutocompleteItem[] = [];\n\t\t\tfor (const { path: entryPath, isDirectory } of topEntries) {\n\t\t\t\tconst pathWithoutSlash = isDirectory ? entryPath.slice(0, -1) : entryPath;\n\t\t\t\tconst displayPath = scopedQuery\n\t\t\t\t\t? this.scopedPathForDisplay(scopedQuery.displayBase, pathWithoutSlash)\n\t\t\t\t\t: pathWithoutSlash;\n\t\t\t\tconst entryName = basename(pathWithoutSlash);\n\t\t\t\tconst completionPath = isDirectory ? `${displayPath}/` : displayPath;\n\t\t\t\tconst value = buildCompletionValue(completionPath, {\n\t\t\t\t\tisDirectory,\n\t\t\t\t\tisAtPrefix: true,\n\t\t\t\t\tisQuotedPrefix: options.isQuotedPrefix,\n\t\t\t\t});\n\n\t\t\t\tsuggestions.push({\n\t\t\t\t\tvalue,\n\t\t\t\t\tlabel: entryName + (isDirectory ? \"/\" : \"\"),\n\t\t\t\t\tdescription: displayPath,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn suggestions;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tshouldTriggerFileCompletion(lines: string[], cursorLine: number, cursorCol: number): boolean {\n\t\treturn getSlashCommandContext(lines, cursorLine, cursorCol)?.kind !== \"name\";\n\t}\n}\n"]}