@nanocollective/nanocoder 1.14.2 → 1.15.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 (185) hide show
  1. package/README.md +103 -5
  2. package/dist/ai-sdk-client.d.ts +22 -0
  3. package/dist/ai-sdk-client.d.ts.map +1 -0
  4. package/dist/ai-sdk-client.js +385 -0
  5. package/dist/ai-sdk-client.js.map +1 -0
  6. package/dist/app.d.ts.map +1 -1
  7. package/dist/app.js +1 -1
  8. package/dist/app.js.map +1 -1
  9. package/dist/client-factory.d.ts.map +1 -1
  10. package/dist/client-factory.js +5 -5
  11. package/dist/client-factory.js.map +1 -1
  12. package/dist/commands/index.d.ts +1 -0
  13. package/dist/commands/index.d.ts.map +1 -1
  14. package/dist/commands/index.js +1 -0
  15. package/dist/commands/index.js.map +1 -1
  16. package/dist/commands/recommendations.js +1 -1
  17. package/dist/commands/recommendations.js.map +1 -1
  18. package/dist/commands/streaming.d.ts +3 -0
  19. package/dist/commands/streaming.d.ts.map +1 -0
  20. package/dist/commands/streaming.js +23 -0
  21. package/dist/commands/streaming.js.map +1 -0
  22. package/dist/commands.d.ts.map +1 -1
  23. package/dist/commands.js +17 -3
  24. package/dist/commands.js.map +1 -1
  25. package/dist/components/assistant-message.d.ts +0 -1
  26. package/dist/components/assistant-message.d.ts.map +1 -1
  27. package/dist/components/assistant-message.js +70 -122
  28. package/dist/components/assistant-message.js.map +1 -1
  29. package/dist/components/thinking-indicator.js +1 -1
  30. package/dist/components/thinking-indicator.js.map +1 -1
  31. package/dist/components/user-input.d.ts.map +1 -1
  32. package/dist/components/user-input.js +135 -27
  33. package/dist/components/user-input.js.map +1 -1
  34. package/dist/components/user-message.d.ts.map +1 -1
  35. package/dist/components/user-message.js +34 -1
  36. package/dist/components/user-message.js.map +1 -1
  37. package/dist/config/preferences.d.ts +2 -0
  38. package/dist/config/preferences.d.ts.map +1 -1
  39. package/dist/config/preferences.js +10 -0
  40. package/dist/config/preferences.js.map +1 -1
  41. package/dist/hooks/useAppInitialization.d.ts.map +1 -1
  42. package/dist/hooks/useAppInitialization.js +2 -1
  43. package/dist/hooks/useAppInitialization.js.map +1 -1
  44. package/dist/hooks/useChatHandler.d.ts +2 -0
  45. package/dist/hooks/useChatHandler.d.ts.map +1 -1
  46. package/dist/hooks/useChatHandler.js +59 -9
  47. package/dist/hooks/useChatHandler.js.map +1 -1
  48. package/dist/mcp/mcp-client.d.ts +20 -1
  49. package/dist/mcp/mcp-client.d.ts.map +1 -1
  50. package/dist/mcp/mcp-client.js +57 -0
  51. package/dist/mcp/mcp-client.js.map +1 -1
  52. package/dist/message-handler.d.ts.map +1 -1
  53. package/dist/message-handler.js +7 -2
  54. package/dist/message-handler.js.map +1 -1
  55. package/dist/tool-calling/index.d.ts +1 -1
  56. package/dist/tool-calling/index.d.ts.map +1 -1
  57. package/dist/tool-calling/index.js +1 -1
  58. package/dist/tool-calling/index.js.map +1 -1
  59. package/dist/tool-calling/json-parser.d.ts +19 -2
  60. package/dist/tool-calling/json-parser.d.ts.map +1 -1
  61. package/dist/tool-calling/json-parser.js +65 -28
  62. package/dist/tool-calling/json-parser.js.map +1 -1
  63. package/dist/tool-calling/json-parser.spec.d.ts +2 -0
  64. package/dist/tool-calling/json-parser.spec.d.ts.map +1 -0
  65. package/dist/tool-calling/json-parser.spec.js +518 -0
  66. package/dist/tool-calling/json-parser.spec.js.map +1 -0
  67. package/dist/tool-calling/tool-parser.d.ts +20 -0
  68. package/dist/tool-calling/tool-parser.d.ts.map +1 -0
  69. package/dist/tool-calling/tool-parser.js +58 -0
  70. package/dist/tool-calling/tool-parser.js.map +1 -0
  71. package/dist/tool-calling/tool-parser.spec.d.ts +2 -0
  72. package/dist/tool-calling/tool-parser.spec.d.ts.map +1 -0
  73. package/dist/tool-calling/tool-parser.spec.js +250 -0
  74. package/dist/tool-calling/tool-parser.spec.js.map +1 -0
  75. package/dist/tool-calling/xml-parser.d.ts +17 -0
  76. package/dist/tool-calling/xml-parser.d.ts.map +1 -1
  77. package/dist/tool-calling/xml-parser.js +101 -13
  78. package/dist/tool-calling/xml-parser.js.map +1 -1
  79. package/dist/tool-calling/xml-parser.spec.d.ts +2 -0
  80. package/dist/tool-calling/xml-parser.spec.d.ts.map +1 -0
  81. package/dist/tool-calling/xml-parser.spec.js +437 -0
  82. package/dist/tool-calling/xml-parser.spec.js.map +1 -0
  83. package/dist/tools/create-file.d.ts.map +1 -1
  84. package/dist/tools/create-file.js +26 -23
  85. package/dist/tools/create-file.js.map +1 -1
  86. package/dist/tools/delete-lines.d.ts.map +1 -1
  87. package/dist/tools/delete-lines.js +29 -27
  88. package/dist/tools/delete-lines.js.map +1 -1
  89. package/dist/tools/execute-bash.d.ts.map +1 -1
  90. package/dist/tools/execute-bash.js +21 -19
  91. package/dist/tools/execute-bash.js.map +1 -1
  92. package/dist/tools/fetch-url.d.ts.map +1 -1
  93. package/dist/tools/fetch-url.js +21 -19
  94. package/dist/tools/fetch-url.js.map +1 -1
  95. package/dist/tools/index.d.ts +2 -2
  96. package/dist/tools/index.d.ts.map +1 -1
  97. package/dist/tools/index.js +8 -6
  98. package/dist/tools/index.js.map +1 -1
  99. package/dist/tools/insert-lines.d.ts.map +1 -1
  100. package/dist/tools/insert-lines.js +29 -27
  101. package/dist/tools/insert-lines.js.map +1 -1
  102. package/dist/tools/read-file.d.ts.map +1 -1
  103. package/dist/tools/read-file.js +23 -19
  104. package/dist/tools/read-file.js.map +1 -1
  105. package/dist/tools/read-many-files.d.ts.map +1 -1
  106. package/dist/tools/read-many-files.js +24 -20
  107. package/dist/tools/read-many-files.js.map +1 -1
  108. package/dist/tools/replace-lines.d.ts.map +1 -1
  109. package/dist/tools/replace-lines.js +33 -31
  110. package/dist/tools/replace-lines.js.map +1 -1
  111. package/dist/tools/search-files.d.ts.map +1 -1
  112. package/dist/tools/search-files.js +33 -31
  113. package/dist/tools/search-files.js.map +1 -1
  114. package/dist/tools/tool-manager.d.ts +35 -19
  115. package/dist/tools/tool-manager.d.ts.map +1 -1
  116. package/dist/tools/tool-manager.js +63 -33
  117. package/dist/tools/tool-manager.js.map +1 -1
  118. package/dist/tools/tool-registry.d.ts +121 -0
  119. package/dist/tools/tool-registry.d.ts.map +1 -0
  120. package/dist/tools/tool-registry.js +195 -0
  121. package/dist/tools/tool-registry.js.map +1 -0
  122. package/dist/tools/web-search.d.ts.map +1 -1
  123. package/dist/tools/web-search.js +25 -23
  124. package/dist/tools/web-search.js.map +1 -1
  125. package/dist/types/config.d.ts +2 -1
  126. package/dist/types/config.d.ts.map +1 -1
  127. package/dist/types/core.d.ts +58 -3
  128. package/dist/types/core.d.ts.map +1 -1
  129. package/dist/types/core.js +2 -0
  130. package/dist/types/core.js.map +1 -1
  131. package/dist/utils/file-autocomplete.d.ts +31 -0
  132. package/dist/utils/file-autocomplete.d.ts.map +1 -0
  133. package/dist/utils/file-autocomplete.js +156 -0
  134. package/dist/utils/file-autocomplete.js.map +1 -0
  135. package/dist/utils/file-autocomplete.spec.d.ts +2 -0
  136. package/dist/utils/file-autocomplete.spec.d.ts.map +1 -0
  137. package/dist/utils/file-autocomplete.spec.js +142 -0
  138. package/dist/utils/file-autocomplete.spec.js.map +1 -0
  139. package/dist/utils/file-content-loader.d.ts +31 -0
  140. package/dist/utils/file-content-loader.d.ts.map +1 -0
  141. package/dist/utils/file-content-loader.js +142 -0
  142. package/dist/utils/file-content-loader.js.map +1 -0
  143. package/dist/utils/file-content-loader.spec.d.ts +2 -0
  144. package/dist/utils/file-content-loader.spec.d.ts.map +1 -0
  145. package/dist/utils/file-content-loader.spec.js +140 -0
  146. package/dist/utils/file-content-loader.spec.js.map +1 -0
  147. package/dist/utils/file-mention-handler.d.ts +21 -0
  148. package/dist/utils/file-mention-handler.d.ts.map +1 -0
  149. package/dist/utils/file-mention-handler.js +59 -0
  150. package/dist/utils/file-mention-handler.js.map +1 -0
  151. package/dist/utils/file-mention-handler.spec.d.ts +2 -0
  152. package/dist/utils/file-mention-handler.spec.d.ts.map +1 -0
  153. package/dist/utils/file-mention-handler.spec.js +147 -0
  154. package/dist/utils/file-mention-handler.spec.js.map +1 -0
  155. package/dist/utils/file-mention-parser.d.ts +40 -0
  156. package/dist/utils/file-mention-parser.d.ts.map +1 -0
  157. package/dist/utils/file-mention-parser.js +122 -0
  158. package/dist/utils/file-mention-parser.js.map +1 -0
  159. package/dist/utils/file-mention-parser.spec.d.ts +2 -0
  160. package/dist/utils/file-mention-parser.spec.d.ts.map +1 -0
  161. package/dist/utils/file-mention-parser.spec.js +149 -0
  162. package/dist/utils/file-mention-parser.spec.js.map +1 -0
  163. package/dist/utils/fuzzy-matching.d.ts +13 -0
  164. package/dist/utils/fuzzy-matching.d.ts.map +1 -0
  165. package/dist/utils/fuzzy-matching.js +127 -0
  166. package/dist/utils/fuzzy-matching.js.map +1 -0
  167. package/dist/utils/fuzzy-matching.spec.d.ts +2 -0
  168. package/dist/utils/fuzzy-matching.spec.d.ts.map +1 -0
  169. package/dist/utils/fuzzy-matching.spec.js +123 -0
  170. package/dist/utils/fuzzy-matching.spec.js.map +1 -0
  171. package/dist/utils/prompt-assembly.spec.js +100 -2
  172. package/dist/utils/prompt-assembly.spec.js.map +1 -1
  173. package/dist/utils/prompt-processor.d.ts +2 -2
  174. package/dist/utils/prompt-processor.d.ts.map +1 -1
  175. package/dist/utils/prompt-processor.js +24 -41
  176. package/dist/utils/prompt-processor.js.map +1 -1
  177. package/package.json +8 -5
  178. package/dist/langgraph-client.d.ts +0 -19
  179. package/dist/langgraph-client.d.ts.map +0 -1
  180. package/dist/langgraph-client.js +0 -295
  181. package/dist/langgraph-client.js.map +0 -1
  182. package/dist/mcp/mcp-tool-adapter.d.ts +0 -22
  183. package/dist/mcp/mcp-tool-adapter.d.ts.map +0 -1
  184. package/dist/mcp/mcp-tool-adapter.js +0 -50
  185. package/dist/mcp/mcp-tool-adapter.js.map +0 -1
@@ -1,4 +1,7 @@
1
1
  import React from 'react';
2
+ import { tool, jsonSchema, type Tool as AISDKTool } from 'ai';
3
+ export { tool, jsonSchema };
4
+ export type AISDKCoreTool = AISDKTool<any, any>;
2
5
  export interface Message {
3
6
  role: 'user' | 'assistant' | 'system' | 'tool';
4
7
  content: string;
@@ -37,9 +40,55 @@ export interface Tool {
37
40
  };
38
41
  }
39
42
  export type ToolHandler = (input: any) => Promise<string>;
43
+ /**
44
+ * Tool formatter type for Ink UI
45
+ * Formats tool arguments and results for display in the CLI
46
+ */
47
+ export type ToolFormatter = (args: any, result?: string) => string | Promise<string> | React.ReactElement | Promise<React.ReactElement>;
48
+ /**
49
+ * Tool validator type for pre-execution validation
50
+ * Returns validation result with optional error message
51
+ */
52
+ export type ToolValidator = (args: any) => Promise<{
53
+ valid: true;
54
+ } | {
55
+ valid: false;
56
+ error: string;
57
+ }>;
58
+ /**
59
+ * Unified tool entry interface
60
+ *
61
+ * Provides a structured way to manage all tool metadata in one place:
62
+ * - name: Tool name for registry and lookup
63
+ * - tool: Native AI SDK CoreTool (without execute for human-in-the-loop)
64
+ * - handler: Manual execution handler called after user confirmation
65
+ * - formatter: Optional React component for rich CLI UI display
66
+ * - validator: Optional pre-execution validation function
67
+ */
68
+ export interface ToolEntry {
69
+ name: string;
70
+ tool: AISDKCoreTool;
71
+ handler: ToolHandler;
72
+ formatter?: ToolFormatter;
73
+ validator?: ToolValidator;
74
+ }
75
+ /**
76
+ * Nanocoder's extended tool definition
77
+ *
78
+ * Uses AI SDK's native CoreTool with Nanocoder-specific metadata:
79
+ * - name: Tool name (metadata for registry and lookup)
80
+ * - tool: Native AI SDK CoreTool (using tool() and jsonSchema()) WITHOUT execute function
81
+ * - handler: Manual execution function called after user confirmation (human-in-the-loop)
82
+ * - formatter: React component for rich UI display in terminal
83
+ * - validator: Optional pre-execution validation
84
+ * - requiresConfirmation: Whether to show confirmation UI (default: true)
85
+ *
86
+ * Note: We keep 'name' as metadata since AI SDK's Tool type doesn't expose it.
87
+ */
40
88
  export interface ToolDefinition {
89
+ name: string;
90
+ tool: AISDKCoreTool;
41
91
  handler: ToolHandler;
42
- config: Tool;
43
92
  formatter?: (args: any, result?: string) => string | Promise<string> | React.ReactElement | Promise<React.ReactElement>;
44
93
  requiresConfirmation?: boolean;
45
94
  validator?: (args: any) => Promise<{
@@ -48,6 +97,7 @@ export interface ToolDefinition {
48
97
  valid: false;
49
98
  error: string;
50
99
  }>;
100
+ config?: Tool;
51
101
  }
52
102
  interface LLMMessage {
53
103
  role: 'assistant';
@@ -59,15 +109,20 @@ export interface LLMChatResponse {
59
109
  message: LLMMessage;
60
110
  }>;
61
111
  }
112
+ export interface StreamCallbacks {
113
+ onToken?: (token: string) => void;
114
+ onToolCall?: (toolCall: ToolCall) => void;
115
+ onFinish?: () => void;
116
+ }
62
117
  export interface LLMClient {
63
118
  getCurrentModel(): string;
64
119
  setModel(model: string): void;
65
120
  getContextSize(): number;
66
121
  getAvailableModels(): Promise<string[]>;
67
- chat(messages: Message[], tools: Tool[], signal?: AbortSignal): Promise<LLMChatResponse>;
122
+ chat(messages: Message[], tools: Record<string, AISDKCoreTool>, signal?: AbortSignal): Promise<LLMChatResponse>;
123
+ chatStream?(messages: Message[], tools: Record<string, AISDKCoreTool>, callbacks: StreamCallbacks, signal?: AbortSignal): Promise<LLMChatResponse>;
68
124
  clearContext(): Promise<void>;
69
125
  }
70
126
  export type DevelopmentMode = 'normal' | 'auto-accept' | 'plan';
71
127
  export declare const DEVELOPMENT_MODE_LABELS: Record<DevelopmentMode, string>;
72
- export {};
73
128
  //# sourceMappingURL=core.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../source/types/core.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../source/types/core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAC,IAAI,EAAE,UAAU,EAAE,KAAK,IAAI,IAAI,SAAS,EAAC,MAAM,IAAI,CAAC;AAE5D,OAAO,EAAC,IAAI,EAAE,UAAU,EAAC,CAAC;AAM1B,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAIhD,MAAM,WAAW,OAAO;IACvB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,QAAQ;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC,CAAC;CACF;AAED,MAAM,WAAW,UAAU;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,IAAI;IACpB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE;YACX,IAAI,EAAE,QAAQ,CAAC;YACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;YAChD,QAAQ,EAAE,MAAM,EAAE,CAAC;SACnB,CAAC;KACF,CAAC;CACF;AAID,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAE3B,IAAI,EAAE,GAAG,EACT,MAAM,CAAC,EAAE,MAAM,KAEb,MAAM,GACN,OAAO,CAAC,MAAM,CAAC,GACf,KAAK,CAAC,YAAY,GAClB,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;AAE/B;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAE3B,IAAI,EAAE,GAAG,KACL,OAAO,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAC,GAAG;IAAC,KAAK,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,CAAC,CAAC;AAE5D;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,SAAS,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc;IAE9B,IAAI,EAAE,MAAM,CAAC;IAEb,IAAI,EAAE,aAAa,CAAC;IAEpB,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,CAEX,IAAI,EAAE,GAAG,EACT,MAAM,CAAC,EAAE,MAAM,KAEb,MAAM,GACN,OAAO,CAAC,MAAM,CAAC,GACf,KAAK,CAAC,YAAY,GAClB,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC/B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,SAAS,CAAC,EAAE,CAEX,IAAI,EAAE,GAAG,KACL,OAAO,CAAC;QAAC,KAAK,EAAE,IAAI,CAAA;KAAC,GAAG;QAAC,KAAK,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IAE5D,MAAM,CAAC,EAAE,IAAI,CAAC;CACd;AAED,UAAU,UAAU;IACnB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,KAAK,CAAC;QACd,OAAO,EAAE,UAAU,CAAC;KACpB,CAAC,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC1C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACzB,eAAe,IAAI,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,IAAI,MAAM,CAAC;IACzB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACxC,IAAI,CACH,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACpC,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5B,UAAU,CAAC,CACV,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACpC,SAAS,EAAE,eAAe,EAC1B,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5B,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,aAAa,GAAG,MAAM,CAAC;AAEhE,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAInE,CAAC"}
@@ -1,3 +1,5 @@
1
+ import { tool, jsonSchema } from 'ai';
2
+ export { tool, jsonSchema };
1
3
  export const DEVELOPMENT_MODE_LABELS = {
2
4
  normal: '▶ normal mode on',
3
5
  'auto-accept': '⏵⏵ auto-accept mode on',
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","sourceRoot":"","sources":["../../source/types/core.ts"],"names":[],"mappings":"AA8FA,MAAM,CAAC,MAAM,uBAAuB,GAAoC;IACvE,MAAM,EAAE,kBAAkB;IAC1B,aAAa,EAAE,wBAAwB;IACvC,IAAI,EAAE,gBAAgB;CACtB,CAAC"}
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../source/types/core.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,IAAI,EAAE,UAAU,EAAyB,MAAM,IAAI,CAAC;AAE5D,OAAO,EAAC,IAAI,EAAE,UAAU,EAAC,CAAC;AA8K1B,MAAM,CAAC,MAAM,uBAAuB,GAAoC;IACvE,MAAM,EAAE,kBAAkB;IAC1B,aAAa,EAAE,wBAAwB;IACvC,IAAI,EAAE,gBAAgB;CACtB,CAAC"}
@@ -0,0 +1,31 @@
1
+ interface FileCompletion {
2
+ path: string;
3
+ displayPath: string;
4
+ score: number;
5
+ isDirectory: boolean;
6
+ }
7
+ /**
8
+ * Fuzzy match scoring algorithm for file paths
9
+ * Returns a score from 0 to 1000 (higher = better match)
10
+ * @deprecated Use fuzzyScoreFilePath from fuzzy-matching.ts instead
11
+ */
12
+ export declare function fuzzyScore(filePath: string, query: string): number;
13
+ /**
14
+ * Extract the current @mention being typed at cursor position
15
+ * Returns the mention text and its position in the input
16
+ */
17
+ export declare function getCurrentFileMention(input: string, cursorPosition?: number): {
18
+ mention: string;
19
+ startIndex: number;
20
+ endIndex: number;
21
+ } | null;
22
+ /**
23
+ * Get file completions for a partial path
24
+ */
25
+ export declare function getFileCompletions(partialPath: string, cwd: string, maxResults?: number): Promise<FileCompletion[]>;
26
+ /**
27
+ * Clear the file list cache (useful for testing or when files change)
28
+ */
29
+ export declare function clearFileListCache(): void;
30
+ export {};
31
+ //# sourceMappingURL=file-autocomplete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-autocomplete.d.ts","sourceRoot":"","sources":["../../source/utils/file-autocomplete.ts"],"names":[],"mappings":"AASA,UAAU,cAAc;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;CACrB;AAqFD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,GACrB;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,GAAG,IAAI,CA8ChE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACvC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAW,GACrB,OAAO,CAAC,cAAc,EAAE,CAAC,CAwB3B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -0,0 +1,156 @@
1
+ import { exec } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { readFileSync, existsSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import ignore from 'ignore';
6
+ import { fuzzyScoreFilePath } from './fuzzy-matching.js';
7
+ const execAsync = promisify(exec);
8
+ /**
9
+ * Load and parse .gitignore file, returns an ignore instance
10
+ */
11
+ function loadGitignore(cwd) {
12
+ const ig = ignore();
13
+ const gitignorePath = join(cwd, '.gitignore');
14
+ // Always ignore common directories
15
+ ig.add([
16
+ 'node_modules',
17
+ '.git',
18
+ 'dist',
19
+ 'build',
20
+ 'coverage',
21
+ '.next',
22
+ '.nuxt',
23
+ 'out',
24
+ '.cache',
25
+ ]);
26
+ // Load .gitignore if it exists
27
+ if (existsSync(gitignorePath)) {
28
+ try {
29
+ const gitignoreContent = readFileSync(gitignorePath, 'utf-8');
30
+ ig.add(gitignoreContent);
31
+ }
32
+ catch {
33
+ // Silently fail if we can't read .gitignore
34
+ // The hardcoded ignores above will still apply
35
+ }
36
+ }
37
+ return ig;
38
+ }
39
+ let fileListCache = null;
40
+ const CACHE_TTL = 5000; // 5 seconds
41
+ /**
42
+ * Get list of all files in the project (respecting gitignore)
43
+ */
44
+ async function getAllFiles(cwd) {
45
+ // Check cache
46
+ const now = Date.now();
47
+ if (fileListCache && now - fileListCache.timestamp < CACHE_TTL) {
48
+ return fileListCache.files;
49
+ }
50
+ try {
51
+ const ig = loadGitignore(cwd);
52
+ // Use find to list all files, excluding common large directories
53
+ const { stdout } = await execAsync(`find . -type f -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/dist/*" -not -path "*/build/*" -not -path "*/coverage/*" -not -path "*/.next/*" -not -path "*/.nuxt/*" -not -path "*/out/*" -not -path "*/.cache/*"`, { cwd, maxBuffer: 1024 * 1024 * 10 });
54
+ const allFiles = stdout
55
+ .trim()
56
+ .split('\n')
57
+ .filter(Boolean)
58
+ .map(line => line.replace(/^\.\//, '')) // Remove leading "./"
59
+ .filter(file => !ig.ignores(file)); // Filter by gitignore
60
+ // Update cache
61
+ fileListCache = {
62
+ files: allFiles,
63
+ timestamp: now,
64
+ };
65
+ return allFiles;
66
+ }
67
+ catch (error) {
68
+ // If find fails, return empty array
69
+ console.error('Failed to list files:', error);
70
+ return [];
71
+ }
72
+ }
73
+ /**
74
+ * Fuzzy match scoring algorithm for file paths
75
+ * Returns a score from 0 to 1000 (higher = better match)
76
+ * @deprecated Use fuzzyScoreFilePath from fuzzy-matching.ts instead
77
+ */
78
+ export function fuzzyScore(filePath, query) {
79
+ return fuzzyScoreFilePath(filePath, query);
80
+ }
81
+ /**
82
+ * Extract the current @mention being typed at cursor position
83
+ * Returns the mention text and its position in the input
84
+ */
85
+ export function getCurrentFileMention(input, cursorPosition) {
86
+ const pos = cursorPosition ?? input.length;
87
+ // Find the last @ before cursor
88
+ let startIndex = -1;
89
+ for (let i = pos - 1; i >= 0; i--) {
90
+ if (input[i] === '@') {
91
+ startIndex = i;
92
+ break;
93
+ }
94
+ // Stop if we hit whitespace (except for path separators)
95
+ if (input[i] === ' ' || input[i] === '\t' || input[i] === '\n') {
96
+ break;
97
+ }
98
+ }
99
+ if (startIndex === -1) {
100
+ return null;
101
+ }
102
+ // Find the end of the mention (next whitespace or end of string)
103
+ let endIndex = pos;
104
+ for (let i = pos; i < input.length; i++) {
105
+ if (input[i] === ' ' ||
106
+ input[i] === '\t' ||
107
+ input[i] === '\n' ||
108
+ input[i] === '@') {
109
+ break;
110
+ }
111
+ endIndex = i + 1;
112
+ }
113
+ // Extract mention text (without the @)
114
+ const fullText = input.substring(startIndex, endIndex);
115
+ const mention = fullText.substring(1); // Remove @ prefix
116
+ // Remove line range suffix if present (e.g., ":10-20")
117
+ const mentionWithoutRange = mention.replace(/:\d+(-\d+)?$/, '');
118
+ return {
119
+ mention: mentionWithoutRange,
120
+ startIndex,
121
+ endIndex,
122
+ };
123
+ }
124
+ /**
125
+ * Get file completions for a partial path
126
+ */
127
+ export async function getFileCompletions(partialPath, cwd, maxResults = 20) {
128
+ // Get all files
129
+ const allFiles = await getAllFiles(cwd);
130
+ // Score each file
131
+ const scoredFiles = allFiles
132
+ .map(file => ({
133
+ path: file,
134
+ displayPath: file.length > 50 ? '...' + file.slice(-47) : file,
135
+ score: fuzzyScoreFilePath(file, partialPath),
136
+ isDirectory: false, // We're only listing files, not directories
137
+ }))
138
+ .filter(f => f.score > 0) // Only include matches
139
+ .sort((a, b) => {
140
+ // Sort by score (descending)
141
+ if (b.score !== a.score) {
142
+ return b.score - a.score;
143
+ }
144
+ // If scores are equal, sort alphabetically
145
+ return a.path.localeCompare(b.path);
146
+ })
147
+ .slice(0, maxResults); // Limit results
148
+ return scoredFiles;
149
+ }
150
+ /**
151
+ * Clear the file list cache (useful for testing or when files change)
152
+ */
153
+ export function clearFileListCache() {
154
+ fileListCache = null;
155
+ }
156
+ //# sourceMappingURL=file-autocomplete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-autocomplete.js","sourceRoot":"","sources":["../../source/utils/file-autocomplete.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=file-autocomplete.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-autocomplete.spec.d.ts","sourceRoot":"","sources":["../../source/utils/file-autocomplete.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,142 @@
1
+ import test from 'ava';
2
+ import { fuzzyScore, getCurrentFileMention, clearFileListCache, } from './file-autocomplete.js';
3
+ console.log(`\nfile-autocomplete.spec.ts`);
4
+ // Test fuzzyScore()
5
+ test('fuzzy score: exact match gets highest score', t => {
6
+ const score = fuzzyScore('app.tsx', 'app.tsx');
7
+ t.is(score, 1000);
8
+ });
9
+ test('fuzzy score: exact filename match', t => {
10
+ const score = fuzzyScore('src/components/app.tsx', 'app.tsx');
11
+ t.is(score, 900);
12
+ });
13
+ test('fuzzy score: path ends with query', t => {
14
+ const score = fuzzyScore('src/components/Button.tsx', 'Button.tsx');
15
+ // This matches exact filename, so it gets 900 not 850
16
+ t.is(score, 900);
17
+ });
18
+ test('fuzzy score: filename starts with query', t => {
19
+ const score = fuzzyScore('src/components/Button.tsx', 'butt');
20
+ t.is(score, 800);
21
+ });
22
+ test('fuzzy score: path starts with query', t => {
23
+ const score = fuzzyScore('src/components/Button.tsx', 'src/comp');
24
+ t.is(score, 750);
25
+ });
26
+ test('fuzzy score: filename contains query', t => {
27
+ const score = fuzzyScore('src/components/Button.tsx', 'ton');
28
+ t.is(score, 700);
29
+ });
30
+ test('fuzzy score: path contains query', t => {
31
+ const score = fuzzyScore('src/components/Button.tsx', 'compo');
32
+ // "compo" is a substring in path but doesn't start the path
33
+ t.is(score, 600);
34
+ });
35
+ test('fuzzy score: sequential character match', t => {
36
+ const score = fuzzyScore('src/components/Button.tsx', 'btn');
37
+ t.true(score > 0 && score < 600);
38
+ });
39
+ test('fuzzy score: no match returns 0', t => {
40
+ const score = fuzzyScore('app.tsx', 'xyz');
41
+ t.is(score, 0);
42
+ });
43
+ test('fuzzy score: empty query returns 0', t => {
44
+ const score = fuzzyScore('app.tsx', '');
45
+ t.is(score, 0);
46
+ });
47
+ test('fuzzy score: case insensitive', t => {
48
+ const score1 = fuzzyScore('App.tsx', 'app');
49
+ const score2 = fuzzyScore('app.tsx', 'APP');
50
+ t.true(score1 > 0);
51
+ t.true(score2 > 0);
52
+ });
53
+ test('fuzzy score: prefers shorter paths', t => {
54
+ const shortPath = fuzzyScore('app.tsx', 'app');
55
+ const longPath = fuzzyScore('src/components/nested/app.tsx', 'app');
56
+ // Both match filename "app.tsx" starting with "app", so they get same score
57
+ // The scoring doesn't currently prefer shorter paths
58
+ t.is(shortPath, longPath);
59
+ });
60
+ test('fuzzy score: consecutive matches get bonus', t => {
61
+ // "but" matches consecutively in "Button"
62
+ const consecutive = fuzzyScore('Button.tsx', 'but');
63
+ // "btn" requires skipping characters
64
+ const nonConsecutive = fuzzyScore('Button.tsx', 'btn');
65
+ t.true(consecutive > nonConsecutive);
66
+ });
67
+ // Test getCurrentFileMention()
68
+ test('extracts mention at end of string', t => {
69
+ const result = getCurrentFileMention('Fix @src/app');
70
+ t.truthy(result);
71
+ t.is(result.mention, 'src/app');
72
+ t.is(result.startIndex, 4);
73
+ t.is(result.endIndex, 12);
74
+ });
75
+ test('extracts mention in middle of string', t => {
76
+ // Cursor at position 15 is right after "x" in ".tsx"
77
+ const result = getCurrentFileMention('Check @app.tsx please', 14);
78
+ t.truthy(result);
79
+ t.is(result.mention, 'app.tsx');
80
+ t.is(result.startIndex, 6);
81
+ });
82
+ test('extracts mention with cursor in middle', t => {
83
+ const result = getCurrentFileMention('Fix @src/ap', 11);
84
+ t.truthy(result);
85
+ t.is(result.mention, 'src/ap');
86
+ });
87
+ test('returns null when no @ symbol', t => {
88
+ const result = getCurrentFileMention('Just regular text');
89
+ t.is(result, null);
90
+ });
91
+ test('returns null when @ is after cursor', t => {
92
+ const result = getCurrentFileMention('Text @file.txt', 3);
93
+ t.is(result, null);
94
+ });
95
+ test('returns null when @ is followed by space', t => {
96
+ const result = getCurrentFileMention('Fix @ file.txt');
97
+ t.is(result, null);
98
+ });
99
+ test('strips line range from mention', t => {
100
+ const result = getCurrentFileMention('Fix @app.tsx:10-20');
101
+ t.truthy(result);
102
+ t.is(result.mention, 'app.tsx');
103
+ });
104
+ test('strips single line number from mention', t => {
105
+ const result = getCurrentFileMention('Fix @app.tsx:10');
106
+ t.truthy(result);
107
+ t.is(result.mention, 'app.tsx');
108
+ });
109
+ test('handles multiple @ symbols', t => {
110
+ const result = getCurrentFileMention('Compare @a.ts and @b.ts', 24);
111
+ t.truthy(result);
112
+ t.is(result.mention, 'b.ts');
113
+ });
114
+ test('handles @ at start of string', t => {
115
+ const result = getCurrentFileMention('@app.tsx');
116
+ t.truthy(result);
117
+ t.is(result.mention, 'app.tsx');
118
+ t.is(result.startIndex, 0);
119
+ });
120
+ test('handles path with slashes', t => {
121
+ const result = getCurrentFileMention('@src/components/Button.tsx');
122
+ t.truthy(result);
123
+ t.is(result.mention, 'src/components/Button.tsx');
124
+ });
125
+ test('stops at whitespace', t => {
126
+ // When cursor is at end (after "here"), we look back and hit whitespace
127
+ // So we should use cursor position right after the file mention
128
+ const result = getCurrentFileMention('@app.tsx here', 8);
129
+ t.truthy(result);
130
+ t.is(result.mention, 'app.tsx');
131
+ t.is(result.endIndex, 8);
132
+ });
133
+ test('stops at next @ symbol', t => {
134
+ const result = getCurrentFileMention('@app.tsx@other', 8);
135
+ t.truthy(result);
136
+ t.is(result.mention, 'app.tsx');
137
+ });
138
+ // Clear cache between tests (if needed)
139
+ test.afterEach(() => {
140
+ clearFileListCache();
141
+ });
142
+ //# sourceMappingURL=file-autocomplete.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-autocomplete.spec.js","sourceRoot":"","sources":["../../source/utils/file-autocomplete.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ interface FileContentResult {
2
+ success: boolean;
3
+ content?: string;
4
+ error?: string;
5
+ metadata: {
6
+ path: string;
7
+ absolutePath: string;
8
+ size: number;
9
+ lineCount: number;
10
+ lineRange?: {
11
+ start: number;
12
+ end?: number;
13
+ };
14
+ tokens: number;
15
+ };
16
+ }
17
+ /**
18
+ * Load file content with optional line range
19
+ * Silently handles errors - returns success: false instead of throwing
20
+ */
21
+ export declare function loadFileContent(filePath: string, lineRange?: {
22
+ start: number;
23
+ end?: number;
24
+ }): Promise<FileContentResult>;
25
+ /**
26
+ * Format file content with header for LLM context
27
+ * Used when assembling the prompt
28
+ */
29
+ export declare function formatFileForContext(result: FileContentResult): string;
30
+ export {};
31
+ //# sourceMappingURL=file-content-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-content-loader.d.ts","sourceRoot":"","sources":["../../source/utils/file-content-loader.ts"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAA;SAAC,CAAC;QAC1C,MAAM,EAAE,MAAM,CAAC;KACf,CAAC;CACF;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAC,GACvC,OAAO,CAAC,iBAAiB,CAAC,CA2G5B;AAiBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CA0BtE"}
@@ -0,0 +1,142 @@
1
+ import { resolve } from 'node:path';
2
+ import { readFile, stat } from 'node:fs/promises';
3
+ /**
4
+ * Load file content with optional line range
5
+ * Silently handles errors - returns success: false instead of throwing
6
+ */
7
+ export async function loadFileContent(filePath, lineRange) {
8
+ try {
9
+ const absPath = resolve(filePath);
10
+ // Check if file exists and get stats
11
+ const fileStats = await stat(absPath);
12
+ // Check if it's a file (not directory)
13
+ if (!fileStats.isFile()) {
14
+ return {
15
+ success: false,
16
+ error: 'Path is not a file',
17
+ metadata: {
18
+ path: filePath,
19
+ absolutePath: absPath,
20
+ size: 0,
21
+ lineCount: 0,
22
+ lineRange,
23
+ tokens: 0,
24
+ },
25
+ };
26
+ }
27
+ // Read file content
28
+ let content;
29
+ try {
30
+ content = await readFile(absPath, 'utf-8');
31
+ }
32
+ catch {
33
+ // File might be binary or unreadable
34
+ return {
35
+ success: false,
36
+ error: 'Failed to read file (might be binary)',
37
+ metadata: {
38
+ path: filePath,
39
+ absolutePath: absPath,
40
+ size: fileStats.size,
41
+ lineCount: 0,
42
+ lineRange,
43
+ tokens: 0,
44
+ },
45
+ };
46
+ }
47
+ // Split into lines
48
+ const allLines = content.split('\n');
49
+ const totalLines = allLines.length;
50
+ // Extract line range if specified
51
+ let selectedLines;
52
+ let actualLineRange;
53
+ if (lineRange) {
54
+ const start = Math.max(1, lineRange.start);
55
+ const end = lineRange.end ? Math.min(totalLines, lineRange.end) : start;
56
+ // Validate range
57
+ if (start > totalLines) {
58
+ // Invalid range, return empty
59
+ selectedLines = [];
60
+ }
61
+ else {
62
+ // Arrays are 0-indexed, but line numbers are 1-indexed
63
+ selectedLines = allLines.slice(start - 1, end);
64
+ actualLineRange = { start, end: lineRange.end ? end : undefined };
65
+ }
66
+ }
67
+ else {
68
+ // No line range, use all lines
69
+ selectedLines = allLines;
70
+ }
71
+ // Format with line numbers
72
+ const formattedContent = formatFileContentWithLineNumbers(selectedLines, lineRange?.start || 1);
73
+ // Calculate metadata
74
+ const size = content.length;
75
+ const tokens = Math.ceil(formattedContent.length / 4); // Rough token estimate
76
+ return {
77
+ success: true,
78
+ content: formattedContent,
79
+ metadata: {
80
+ path: filePath,
81
+ absolutePath: absPath,
82
+ size,
83
+ lineCount: selectedLines.length,
84
+ lineRange: actualLineRange,
85
+ tokens,
86
+ },
87
+ };
88
+ }
89
+ catch (error) {
90
+ // File doesn't exist or other error
91
+ const absPath = resolve(filePath);
92
+ return {
93
+ success: false,
94
+ error: error instanceof Error ? error.message : 'Unknown error',
95
+ metadata: {
96
+ path: filePath,
97
+ absolutePath: absPath,
98
+ size: 0,
99
+ lineCount: 0,
100
+ lineRange,
101
+ tokens: 0,
102
+ },
103
+ };
104
+ }
105
+ }
106
+ /**
107
+ * Format file lines with line numbers
108
+ */
109
+ function formatFileContentWithLineNumbers(lines, startLineNumber) {
110
+ let result = '';
111
+ for (let i = 0; i < lines.length; i++) {
112
+ const lineNum = String(startLineNumber + i).padStart(4, ' ');
113
+ result += `${lineNum}: ${lines[i]}\n`;
114
+ }
115
+ return result.slice(0, -1); // Remove trailing newline
116
+ }
117
+ /**
118
+ * Format file content with header for LLM context
119
+ * Used when assembling the prompt
120
+ */
121
+ export function formatFileForContext(result) {
122
+ if (!result.success || !result.content) {
123
+ return `Error loading file: ${result.metadata.path}`;
124
+ }
125
+ const { path, lineCount, lineRange, tokens } = result.metadata;
126
+ let header;
127
+ if (lineRange) {
128
+ const rangeStr = lineRange.end
129
+ ? `${lineRange.start}-${lineRange.end}`
130
+ : `${lineRange.start}`;
131
+ header = `=== File: ${path} (Lines ${rangeStr}) ===`;
132
+ }
133
+ else {
134
+ header = `=== File: ${path} ===`;
135
+ }
136
+ const footer = '='.repeat(header.length);
137
+ const stats = lineRange
138
+ ? `Lines: ${lineRange.start}${lineRange.end ? `-${lineRange.end}` : ''} (${lineCount} lines, ~${tokens} tokens)`
139
+ : `Lines: ${lineCount}, ~${tokens} tokens`;
140
+ return `${header}\n${stats}\n\n${result.content}\n${footer}`;
141
+ }
142
+ //# sourceMappingURL=file-content-loader.js.map