@vibe-lang/runtime 0.2.5

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 (250) hide show
  1. package/package.json +46 -0
  2. package/src/ast/index.ts +375 -0
  3. package/src/ast.ts +2 -0
  4. package/src/debug/advanced-features.ts +482 -0
  5. package/src/debug/bun-inspector.ts +424 -0
  6. package/src/debug/handoff-manager.ts +283 -0
  7. package/src/debug/index.ts +150 -0
  8. package/src/debug/runner.ts +365 -0
  9. package/src/debug/server.ts +565 -0
  10. package/src/debug/stack-merger.ts +267 -0
  11. package/src/debug/state.ts +581 -0
  12. package/src/debug/test/advanced-features.test.ts +300 -0
  13. package/src/debug/test/e2e.test.ts +218 -0
  14. package/src/debug/test/handoff-manager.test.ts +256 -0
  15. package/src/debug/test/runner.test.ts +256 -0
  16. package/src/debug/test/stack-merger.test.ts +163 -0
  17. package/src/debug/test/state.test.ts +400 -0
  18. package/src/debug/test/ts-debug-integration.test.ts +374 -0
  19. package/src/debug/test/ts-import-tracker.test.ts +125 -0
  20. package/src/debug/test/ts-source-map.test.ts +169 -0
  21. package/src/debug/ts-import-tracker.ts +151 -0
  22. package/src/debug/ts-source-map.ts +171 -0
  23. package/src/errors/index.ts +124 -0
  24. package/src/index.ts +358 -0
  25. package/src/lexer/index.ts +348 -0
  26. package/src/lexer.ts +2 -0
  27. package/src/parser/index.ts +792 -0
  28. package/src/parser/parse.ts +45 -0
  29. package/src/parser/test/async.test.ts +248 -0
  30. package/src/parser/test/destructuring.test.ts +167 -0
  31. package/src/parser/test/do-expression.test.ts +486 -0
  32. package/src/parser/test/errors/do-expression.test.ts +95 -0
  33. package/src/parser/test/errors/error-locations.test.ts +230 -0
  34. package/src/parser/test/errors/invalid-expressions.test.ts +144 -0
  35. package/src/parser/test/errors/missing-tokens.test.ts +126 -0
  36. package/src/parser/test/errors/model-declaration.test.ts +185 -0
  37. package/src/parser/test/errors/nested-blocks.test.ts +226 -0
  38. package/src/parser/test/errors/unclosed-delimiters.test.ts +122 -0
  39. package/src/parser/test/errors/unexpected-tokens.test.ts +120 -0
  40. package/src/parser/test/import-export.test.ts +143 -0
  41. package/src/parser/test/literals.test.ts +404 -0
  42. package/src/parser/test/model-declaration.test.ts +161 -0
  43. package/src/parser/test/nested-blocks.test.ts +402 -0
  44. package/src/parser/test/parser.test.ts +743 -0
  45. package/src/parser/test/private.test.ts +136 -0
  46. package/src/parser/test/template-literal.test.ts +127 -0
  47. package/src/parser/test/tool-declaration.test.ts +302 -0
  48. package/src/parser/test/ts-block.test.ts +252 -0
  49. package/src/parser/test/type-annotations.test.ts +254 -0
  50. package/src/parser/visitor/helpers.ts +330 -0
  51. package/src/parser/visitor.ts +794 -0
  52. package/src/parser.ts +2 -0
  53. package/src/runtime/ai/cache-chunking.test.ts +69 -0
  54. package/src/runtime/ai/cache-chunking.ts +73 -0
  55. package/src/runtime/ai/client.ts +109 -0
  56. package/src/runtime/ai/context.ts +168 -0
  57. package/src/runtime/ai/formatters.ts +316 -0
  58. package/src/runtime/ai/index.ts +38 -0
  59. package/src/runtime/ai/language-ref.ts +38 -0
  60. package/src/runtime/ai/providers/anthropic.ts +253 -0
  61. package/src/runtime/ai/providers/google.ts +201 -0
  62. package/src/runtime/ai/providers/openai.ts +156 -0
  63. package/src/runtime/ai/retry.ts +100 -0
  64. package/src/runtime/ai/return-tools.ts +301 -0
  65. package/src/runtime/ai/test/client.test.ts +83 -0
  66. package/src/runtime/ai/test/formatters.test.ts +485 -0
  67. package/src/runtime/ai/test/retry.test.ts +137 -0
  68. package/src/runtime/ai/test/return-tools.test.ts +450 -0
  69. package/src/runtime/ai/test/tool-loop.test.ts +319 -0
  70. package/src/runtime/ai/test/tool-schema.test.ts +241 -0
  71. package/src/runtime/ai/tool-loop.ts +203 -0
  72. package/src/runtime/ai/tool-schema.ts +151 -0
  73. package/src/runtime/ai/types.ts +113 -0
  74. package/src/runtime/ai-logger.ts +255 -0
  75. package/src/runtime/ai-provider.ts +347 -0
  76. package/src/runtime/async/dependencies.ts +276 -0
  77. package/src/runtime/async/executor.ts +293 -0
  78. package/src/runtime/async/index.ts +43 -0
  79. package/src/runtime/async/scheduling.ts +163 -0
  80. package/src/runtime/async/test/dependencies.test.ts +284 -0
  81. package/src/runtime/async/test/executor.test.ts +388 -0
  82. package/src/runtime/context.ts +357 -0
  83. package/src/runtime/exec/ai.ts +139 -0
  84. package/src/runtime/exec/expressions.ts +475 -0
  85. package/src/runtime/exec/frames.ts +26 -0
  86. package/src/runtime/exec/functions.ts +305 -0
  87. package/src/runtime/exec/interpolation.ts +312 -0
  88. package/src/runtime/exec/statements.ts +604 -0
  89. package/src/runtime/exec/tools.ts +129 -0
  90. package/src/runtime/exec/typescript.ts +215 -0
  91. package/src/runtime/exec/variables.ts +279 -0
  92. package/src/runtime/index.ts +975 -0
  93. package/src/runtime/modules.ts +452 -0
  94. package/src/runtime/serialize.ts +103 -0
  95. package/src/runtime/state.ts +489 -0
  96. package/src/runtime/stdlib/core.ts +45 -0
  97. package/src/runtime/stdlib/directory.test.ts +156 -0
  98. package/src/runtime/stdlib/edit.test.ts +154 -0
  99. package/src/runtime/stdlib/fastEdit.test.ts +201 -0
  100. package/src/runtime/stdlib/glob.test.ts +106 -0
  101. package/src/runtime/stdlib/grep.test.ts +144 -0
  102. package/src/runtime/stdlib/index.ts +16 -0
  103. package/src/runtime/stdlib/readFile.test.ts +123 -0
  104. package/src/runtime/stdlib/tools/index.ts +707 -0
  105. package/src/runtime/stdlib/writeFile.test.ts +157 -0
  106. package/src/runtime/step.ts +969 -0
  107. package/src/runtime/test/ai-context.test.ts +1086 -0
  108. package/src/runtime/test/ai-result-object.test.ts +419 -0
  109. package/src/runtime/test/ai-tool-flow.test.ts +859 -0
  110. package/src/runtime/test/async-execution-order.test.ts +618 -0
  111. package/src/runtime/test/async-execution.test.ts +344 -0
  112. package/src/runtime/test/async-nested.test.ts +660 -0
  113. package/src/runtime/test/async-parallel-timing.test.ts +546 -0
  114. package/src/runtime/test/basic1.test.ts +154 -0
  115. package/src/runtime/test/binary-operators.test.ts +431 -0
  116. package/src/runtime/test/break-statement.test.ts +257 -0
  117. package/src/runtime/test/context-modes.test.ts +650 -0
  118. package/src/runtime/test/context.test.ts +466 -0
  119. package/src/runtime/test/core-functions.test.ts +228 -0
  120. package/src/runtime/test/e2e.test.ts +88 -0
  121. package/src/runtime/test/error-locations/error-locations.test.ts +80 -0
  122. package/src/runtime/test/error-locations/main-error.vibe +4 -0
  123. package/src/runtime/test/error-locations/main-import-error.vibe +3 -0
  124. package/src/runtime/test/error-locations/utils/helper.vibe +5 -0
  125. package/src/runtime/test/for-in.test.ts +312 -0
  126. package/src/runtime/test/helpers.ts +69 -0
  127. package/src/runtime/test/imports.test.ts +334 -0
  128. package/src/runtime/test/json-expressions.test.ts +232 -0
  129. package/src/runtime/test/literals.test.ts +372 -0
  130. package/src/runtime/test/logical-indexing.test.ts +478 -0
  131. package/src/runtime/test/member-methods.test.ts +324 -0
  132. package/src/runtime/test/model-config.test.ts +338 -0
  133. package/src/runtime/test/null-handling.test.ts +342 -0
  134. package/src/runtime/test/private-visibility.test.ts +332 -0
  135. package/src/runtime/test/runtime-state.test.ts +514 -0
  136. package/src/runtime/test/scoping.test.ts +370 -0
  137. package/src/runtime/test/string-interpolation.test.ts +354 -0
  138. package/src/runtime/test/template-literal.test.ts +181 -0
  139. package/src/runtime/test/tool-execution.test.ts +467 -0
  140. package/src/runtime/test/tool-schema-generation.test.ts +477 -0
  141. package/src/runtime/test/tostring.test.ts +210 -0
  142. package/src/runtime/test/ts-block.test.ts +594 -0
  143. package/src/runtime/test/ts-error-location.test.ts +231 -0
  144. package/src/runtime/test/types.test.ts +732 -0
  145. package/src/runtime/test/verbose-logger.test.ts +710 -0
  146. package/src/runtime/test/vibe-expression.test.ts +54 -0
  147. package/src/runtime/test/vibe-value-errors.test.ts +541 -0
  148. package/src/runtime/test/while.test.ts +232 -0
  149. package/src/runtime/tools/builtin.ts +30 -0
  150. package/src/runtime/tools/directory-tools.ts +70 -0
  151. package/src/runtime/tools/file-tools.ts +228 -0
  152. package/src/runtime/tools/index.ts +5 -0
  153. package/src/runtime/tools/registry.ts +48 -0
  154. package/src/runtime/tools/search-tools.ts +134 -0
  155. package/src/runtime/tools/security.ts +36 -0
  156. package/src/runtime/tools/system-tools.ts +312 -0
  157. package/src/runtime/tools/test/fixtures/base-types.ts +40 -0
  158. package/src/runtime/tools/test/fixtures/test-types.ts +132 -0
  159. package/src/runtime/tools/test/registry.test.ts +713 -0
  160. package/src/runtime/tools/test/security.test.ts +86 -0
  161. package/src/runtime/tools/test/system-tools.test.ts +679 -0
  162. package/src/runtime/tools/test/ts-schema.test.ts +357 -0
  163. package/src/runtime/tools/ts-schema.ts +341 -0
  164. package/src/runtime/tools/types.ts +89 -0
  165. package/src/runtime/tools/utility-tools.ts +198 -0
  166. package/src/runtime/ts-eval.ts +126 -0
  167. package/src/runtime/types.ts +797 -0
  168. package/src/runtime/validation.ts +160 -0
  169. package/src/runtime/verbose-logger.ts +459 -0
  170. package/src/runtime.ts +2 -0
  171. package/src/semantic/analyzer-context.ts +62 -0
  172. package/src/semantic/analyzer-validators.ts +575 -0
  173. package/src/semantic/analyzer-visitors.ts +534 -0
  174. package/src/semantic/analyzer.ts +83 -0
  175. package/src/semantic/index.ts +11 -0
  176. package/src/semantic/symbol-table.ts +58 -0
  177. package/src/semantic/test/async-validation.test.ts +301 -0
  178. package/src/semantic/test/compress-validation.test.ts +179 -0
  179. package/src/semantic/test/const-reassignment.test.ts +111 -0
  180. package/src/semantic/test/control-flow.test.ts +346 -0
  181. package/src/semantic/test/destructuring.test.ts +185 -0
  182. package/src/semantic/test/duplicate-declarations.test.ts +168 -0
  183. package/src/semantic/test/export-validation.test.ts +111 -0
  184. package/src/semantic/test/fixtures/math.ts +31 -0
  185. package/src/semantic/test/imports.test.ts +148 -0
  186. package/src/semantic/test/json-type.test.ts +68 -0
  187. package/src/semantic/test/literals.test.ts +127 -0
  188. package/src/semantic/test/model-validation.test.ts +179 -0
  189. package/src/semantic/test/prompt-validation.test.ts +343 -0
  190. package/src/semantic/test/scoping.test.ts +312 -0
  191. package/src/semantic/test/tool-validation.test.ts +306 -0
  192. package/src/semantic/test/ts-type-checking.test.ts +563 -0
  193. package/src/semantic/test/type-constraints.test.ts +111 -0
  194. package/src/semantic/test/type-inference.test.ts +87 -0
  195. package/src/semantic/test/type-validation.test.ts +552 -0
  196. package/src/semantic/test/undefined-variables.test.ts +163 -0
  197. package/src/semantic/ts-block-checker.ts +204 -0
  198. package/src/semantic/ts-signatures.ts +194 -0
  199. package/src/semantic/ts-types.ts +170 -0
  200. package/src/semantic/types.ts +58 -0
  201. package/tests/fixtures/conditional-logic.vibe +14 -0
  202. package/tests/fixtures/function-call.vibe +16 -0
  203. package/tests/fixtures/imports/cycle-detection/a.vibe +6 -0
  204. package/tests/fixtures/imports/cycle-detection/b.vibe +5 -0
  205. package/tests/fixtures/imports/cycle-detection/main.vibe +3 -0
  206. package/tests/fixtures/imports/module-isolation/main-b.vibe +8 -0
  207. package/tests/fixtures/imports/module-isolation/main.vibe +9 -0
  208. package/tests/fixtures/imports/module-isolation/moduleA.vibe +6 -0
  209. package/tests/fixtures/imports/module-isolation/moduleB.vibe +6 -0
  210. package/tests/fixtures/imports/nested-import/helper.vibe +6 -0
  211. package/tests/fixtures/imports/nested-import/main.vibe +3 -0
  212. package/tests/fixtures/imports/nested-import/utils.ts +3 -0
  213. package/tests/fixtures/imports/nested-isolation/file2.vibe +15 -0
  214. package/tests/fixtures/imports/nested-isolation/file3.vibe +10 -0
  215. package/tests/fixtures/imports/nested-isolation/main.vibe +21 -0
  216. package/tests/fixtures/imports/pure-cycle/a.vibe +5 -0
  217. package/tests/fixtures/imports/pure-cycle/b.vibe +5 -0
  218. package/tests/fixtures/imports/pure-cycle/main.vibe +3 -0
  219. package/tests/fixtures/imports/ts-boolean/checks.ts +14 -0
  220. package/tests/fixtures/imports/ts-boolean/main.vibe +10 -0
  221. package/tests/fixtures/imports/ts-boolean/type-mismatch.vibe +5 -0
  222. package/tests/fixtures/imports/ts-boolean/use-constant.vibe +18 -0
  223. package/tests/fixtures/imports/ts-error-handling/helpers.ts +42 -0
  224. package/tests/fixtures/imports/ts-error-handling/main.vibe +5 -0
  225. package/tests/fixtures/imports/ts-import/main.vibe +4 -0
  226. package/tests/fixtures/imports/ts-import/math.ts +9 -0
  227. package/tests/fixtures/imports/ts-variables/call-non-function.vibe +5 -0
  228. package/tests/fixtures/imports/ts-variables/data.ts +10 -0
  229. package/tests/fixtures/imports/ts-variables/import-json.vibe +5 -0
  230. package/tests/fixtures/imports/ts-variables/import-type-mismatch.vibe +5 -0
  231. package/tests/fixtures/imports/ts-variables/import-variable.vibe +5 -0
  232. package/tests/fixtures/imports/vibe-import/greet.vibe +5 -0
  233. package/tests/fixtures/imports/vibe-import/main.vibe +3 -0
  234. package/tests/fixtures/multiple-ai-calls.vibe +10 -0
  235. package/tests/fixtures/simple-greeting.vibe +6 -0
  236. package/tests/fixtures/template-literals.vibe +11 -0
  237. package/tests/integration/basic-ai/basic-ai.integration.test.ts +166 -0
  238. package/tests/integration/basic-ai/basic-ai.vibe +12 -0
  239. package/tests/integration/bug-fix/bug-fix.integration.test.ts +201 -0
  240. package/tests/integration/bug-fix/buggy-code.ts +22 -0
  241. package/tests/integration/bug-fix/fix-bug.vibe +21 -0
  242. package/tests/integration/compress/compress.integration.test.ts +206 -0
  243. package/tests/integration/destructuring/destructuring.integration.test.ts +92 -0
  244. package/tests/integration/hello-world-translator/hello-world-translator.integration.test.ts +61 -0
  245. package/tests/integration/line-annotator/context-modes.integration.test.ts +261 -0
  246. package/tests/integration/line-annotator/line-annotator.integration.test.ts +148 -0
  247. package/tests/integration/multi-feature/cumulative-sum.integration.test.ts +75 -0
  248. package/tests/integration/multi-feature/number-analyzer.integration.test.ts +191 -0
  249. package/tests/integration/multi-feature/number-analyzer.vibe +59 -0
  250. package/tests/integration/tool-calls/tool-calls.integration.test.ts +93 -0
@@ -0,0 +1,232 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { parse } from '../../parser/parse';
3
+ import { createInitialState, runUntilPause } from '../index';
4
+
5
+ describe('Runtime While Loop', () => {
6
+ // ============================================================================
7
+ // Basic while loop
8
+ // ============================================================================
9
+
10
+ test('while loop executes while condition is true', () => {
11
+ const ast = parse(`
12
+ let keepGoing = true
13
+ let executed = false
14
+ while keepGoing {
15
+ executed = true
16
+ keepGoing = false
17
+ }
18
+ `);
19
+ let state = createInitialState(ast);
20
+ state = runUntilPause(state);
21
+
22
+ expect(state.status).toBe('completed');
23
+ const frame = state.callStack[0];
24
+ expect(frame.locals['executed'].value).toBe(true);
25
+ expect(frame.locals['keepGoing'].value).toBe(false);
26
+ });
27
+
28
+ test('while loop with false condition never executes', () => {
29
+ const ast = parse(`
30
+ let executed = false
31
+ while false {
32
+ executed = true
33
+ }
34
+ `);
35
+ let state = createInitialState(ast);
36
+ state = runUntilPause(state);
37
+
38
+ expect(state.status).toBe('completed');
39
+ const frame = state.callStack[0];
40
+ expect(frame.locals['executed'].value).toBe(false);
41
+ });
42
+
43
+ test('while loop with true initial condition executes once then exits', () => {
44
+ const ast = parse(`
45
+ let counter = true
46
+ let loopRan = false
47
+ while counter {
48
+ loopRan = true
49
+ counter = false
50
+ }
51
+ `);
52
+ let state = createInitialState(ast);
53
+ state = runUntilPause(state);
54
+
55
+ expect(state.status).toBe('completed');
56
+ const frame = state.callStack[0];
57
+ expect(frame.locals['loopRan'].value).toBe(true);
58
+ expect(frame.locals['counter'].value).toBe(false);
59
+ });
60
+
61
+ // ============================================================================
62
+ // Scoping
63
+ // ============================================================================
64
+
65
+ test('variables declared in while body are cleaned up each iteration', () => {
66
+ // This tests that body-scoped variables don't persist
67
+ const ast = parse(`
68
+ let flag = true
69
+ let outsideVar = false
70
+ while flag {
71
+ let insideVar = true
72
+ outsideVar = insideVar
73
+ flag = false
74
+ }
75
+ `);
76
+ let state = createInitialState(ast);
77
+ state = runUntilPause(state);
78
+
79
+ expect(state.status).toBe('completed');
80
+ const frame = state.callStack[0];
81
+ expect(frame.locals['outsideVar'].value).toBe(true);
82
+ // insideVar should not exist in the frame after loop completes
83
+ expect(frame.locals['insideVar']).toBeUndefined();
84
+ });
85
+
86
+ test('while loop does not leak scope to outer context', () => {
87
+ const ast = parse(`
88
+ let preLoop = true
89
+ while false {
90
+ let loopVar = true
91
+ }
92
+ `);
93
+ let state = createInitialState(ast);
94
+ state = runUntilPause(state);
95
+
96
+ expect(state.status).toBe('completed');
97
+ const frame = state.callStack[0];
98
+ expect(frame.locals['preLoop'].value).toBe(true);
99
+ expect(frame.locals['loopVar']).toBeUndefined();
100
+ });
101
+
102
+ // ============================================================================
103
+ // Nested while loops
104
+ // ============================================================================
105
+
106
+ test('nested while loops work correctly', () => {
107
+ const ast = parse(`
108
+ let outer = true
109
+ let inner = true
110
+ let outerRan = false
111
+ let innerRan = false
112
+ while outer {
113
+ outerRan = true
114
+ while inner {
115
+ innerRan = true
116
+ inner = false
117
+ }
118
+ outer = false
119
+ }
120
+ `);
121
+ let state = createInitialState(ast);
122
+ state = runUntilPause(state);
123
+
124
+ expect(state.status).toBe('completed');
125
+ const frame = state.callStack[0];
126
+ expect(frame.locals['outerRan'].value).toBe(true);
127
+ expect(frame.locals['innerRan'].value).toBe(true);
128
+ expect(frame.locals['outer'].value).toBe(false);
129
+ expect(frame.locals['inner'].value).toBe(false);
130
+ });
131
+
132
+ // ============================================================================
133
+ // Error cases
134
+ // ============================================================================
135
+
136
+ test('while with non-boolean condition throws error', () => {
137
+ const ast = parse(`
138
+ while "not a boolean" {
139
+ let x = 1
140
+ }
141
+ `);
142
+ let state = createInitialState(ast);
143
+ state = runUntilPause(state);
144
+
145
+ expect(state.status).toBe('error');
146
+ expect(state.error).toContain('boolean');
147
+ });
148
+
149
+ test('while with number condition throws error', () => {
150
+ const ast = parse(`
151
+ while 1 {
152
+ let x = 1
153
+ }
154
+ `);
155
+ let state = createInitialState(ast);
156
+ state = runUntilPause(state);
157
+
158
+ expect(state.status).toBe('error');
159
+ expect(state.error).toContain('boolean');
160
+ });
161
+
162
+ test('while with string variable condition throws error', () => {
163
+ const ast = parse(`
164
+ const x = "test"
165
+ while x {
166
+ let y = 1
167
+ }
168
+ `);
169
+ let state = createInitialState(ast);
170
+ state = runUntilPause(state);
171
+
172
+ expect(state.status).toBe('error');
173
+ expect(state.error).toContain('boolean');
174
+ });
175
+
176
+ test('while with number variable condition throws error', () => {
177
+ const ast = parse(`
178
+ let count = 5
179
+ while count {
180
+ count = 0
181
+ }
182
+ `);
183
+ let state = createInitialState(ast);
184
+ state = runUntilPause(state);
185
+
186
+ expect(state.status).toBe('error');
187
+ expect(state.error).toContain('boolean');
188
+ });
189
+
190
+ // ============================================================================
191
+ // Integration with other constructs
192
+ // ============================================================================
193
+
194
+ test('while loop with if statement inside', () => {
195
+ const ast = parse(`
196
+ let flag = true
197
+ let tookBranch = false
198
+ while flag {
199
+ if true {
200
+ tookBranch = true
201
+ }
202
+ flag = false
203
+ }
204
+ `);
205
+ let state = createInitialState(ast);
206
+ state = runUntilPause(state);
207
+
208
+ expect(state.status).toBe('completed');
209
+ const frame = state.callStack[0];
210
+ expect(frame.locals['tookBranch'].value).toBe(true);
211
+ });
212
+
213
+ test('while loop inside function', () => {
214
+ const ast = parse(`
215
+ function loopOnce(): boolean {
216
+ let done = false
217
+ while true {
218
+ done = true
219
+ return done
220
+ }
221
+ return false
222
+ }
223
+ let result = loopOnce()
224
+ `);
225
+ let state = createInitialState(ast);
226
+ state = runUntilPause(state);
227
+
228
+ expect(state.status).toBe('completed');
229
+ const frame = state.callStack[0];
230
+ expect(frame.locals['result'].value).toBe(true);
231
+ });
232
+ });
@@ -0,0 +1,30 @@
1
+ import type { RegisteredTool } from './types';
2
+ import { fileTools } from './file-tools';
3
+ import { searchTools } from './search-tools';
4
+ import { directoryTools } from './directory-tools';
5
+ import { utilityTools } from './utility-tools';
6
+ import { systemTools } from './system-tools';
7
+
8
+ /**
9
+ * Standard tools available in the Vibe runtime.
10
+ * Combines all tool categories into a single array.
11
+ *
12
+ * Categories:
13
+ * - File operations: readFile, writeFile, appendFile, fileExists, listDir, edit
14
+ * - File search: glob, grep
15
+ * - Directory operations: mkdir, dirExists
16
+ * - Utilities: env, sleep, now, jsonParse, jsonStringify, print, random, uuid
17
+ * - System: bash, runCode
18
+ *
19
+ * Total: 20 tools
20
+ */
21
+ export const allTools: RegisteredTool[] = [
22
+ ...fileTools,
23
+ ...searchTools,
24
+ ...directoryTools,
25
+ ...utilityTools,
26
+ ...systemTools,
27
+ ];
28
+
29
+ /** @deprecated Use allTools instead */
30
+ export const builtinTools = allTools;
@@ -0,0 +1,70 @@
1
+ import type { RegisteredTool, ToolContext } from './types';
2
+ import { validatePathInSandbox } from './security';
3
+
4
+ /**
5
+ * Directory operation tools: mkdir, dirExists
6
+ */
7
+ export const directoryTools = [
8
+ {
9
+ name: 'mkdir',
10
+ kind: 'builtin',
11
+ schema: {
12
+ name: 'mkdir',
13
+ description: 'Create a directory.',
14
+ parameters: [
15
+ {
16
+ name: 'path',
17
+ type: { type: 'string' },
18
+ description: 'The directory path to create',
19
+ required: true,
20
+ },
21
+ {
22
+ name: 'recursive',
23
+ type: { type: 'boolean' },
24
+ description: 'Create parent directories as needed (default: false)',
25
+ required: false,
26
+ },
27
+ ],
28
+ returns: { type: 'boolean' },
29
+ },
30
+ executor: async (args: Record<string, unknown>, context?: ToolContext) => {
31
+ const inputPath = args.path as string;
32
+ const safePath = context ? validatePathInSandbox(inputPath, context.rootDir) : inputPath;
33
+ const recursive = args.recursive as boolean | undefined;
34
+
35
+ const fs = await import('fs/promises');
36
+ await fs.mkdir(safePath, { recursive: recursive ?? false });
37
+ return true;
38
+ },
39
+ },
40
+
41
+ {
42
+ name: 'dirExists',
43
+ kind: 'builtin',
44
+ schema: {
45
+ name: 'dirExists',
46
+ description: 'Check if a directory exists.',
47
+ parameters: [
48
+ {
49
+ name: 'path',
50
+ type: { type: 'string' },
51
+ description: 'The directory path to check',
52
+ required: true,
53
+ },
54
+ ],
55
+ returns: { type: 'boolean' },
56
+ },
57
+ executor: async (args: Record<string, unknown>, context?: ToolContext) => {
58
+ const inputPath = args.path as string;
59
+ const safePath = context ? validatePathInSandbox(inputPath, context.rootDir) : inputPath;
60
+
61
+ const fs = await import('fs/promises');
62
+ try {
63
+ const stats = await fs.stat(safePath);
64
+ return stats.isDirectory();
65
+ } catch {
66
+ return false;
67
+ }
68
+ },
69
+ },
70
+ ] satisfies RegisteredTool[];
@@ -0,0 +1,228 @@
1
+ import type { RegisteredTool, ToolContext } from './types';
2
+ import { validatePathInSandbox } from './security';
3
+
4
+ /**
5
+ * File operation tools: read, write, append, exists, list, edit
6
+ */
7
+ export const fileTools = [
8
+ {
9
+ name: 'readFile',
10
+ kind: 'builtin',
11
+ schema: {
12
+ name: 'readFile',
13
+ description: 'Read the contents of a file as text. Optionally read a range of lines.',
14
+ parameters: [
15
+ {
16
+ name: 'path',
17
+ type: { type: 'string' },
18
+ description: 'The file path to read',
19
+ required: true,
20
+ },
21
+ {
22
+ name: 'startLine',
23
+ type: { type: 'number' },
24
+ description: 'First line to read (1-based, inclusive). If omitted, starts from beginning.',
25
+ required: false,
26
+ },
27
+ {
28
+ name: 'endLine',
29
+ type: { type: 'number' },
30
+ description: 'Last line to read (1-based, inclusive). If omitted, reads to end of file.',
31
+ required: false,
32
+ },
33
+ ],
34
+ returns: { type: 'string' },
35
+ },
36
+ executor: async (args: Record<string, unknown>, context?: ToolContext) => {
37
+ const inputPath = args.path as string;
38
+ const safePath = context ? validatePathInSandbox(inputPath, context.rootDir) : inputPath;
39
+ const startLine = args.startLine as number | undefined;
40
+ const endLine = args.endLine as number | undefined;
41
+
42
+ const file = Bun.file(safePath);
43
+ const content = await file.text();
44
+
45
+ // If no line range specified, return entire file
46
+ if (startLine === undefined && endLine === undefined) {
47
+ return content;
48
+ }
49
+
50
+ // Split into lines and extract the requested range
51
+ const lines = content.split('\n');
52
+ const start = startLine !== undefined ? Math.max(1, startLine) - 1 : 0; // Convert to 0-based
53
+ const end = endLine !== undefined ? Math.min(lines.length, endLine) : lines.length;
54
+
55
+ return lines.slice(start, end).join('\n');
56
+ },
57
+ },
58
+
59
+ {
60
+ name: 'writeFile',
61
+ kind: 'builtin',
62
+ schema: {
63
+ name: 'writeFile',
64
+ description: 'Write content to a file.',
65
+ parameters: [
66
+ {
67
+ name: 'path',
68
+ type: { type: 'string' },
69
+ description: 'The file path to write to',
70
+ required: true,
71
+ },
72
+ {
73
+ name: 'content',
74
+ type: { type: 'string' },
75
+ description: 'The content to write',
76
+ required: true,
77
+ },
78
+ ],
79
+ returns: { type: 'boolean' },
80
+ },
81
+ executor: async (args: Record<string, unknown>, context?: ToolContext) => {
82
+ const inputPath = args.path as string;
83
+ const safePath = context ? validatePathInSandbox(inputPath, context.rootDir) : inputPath;
84
+ const content = args.content as string;
85
+ await Bun.write(safePath, content);
86
+ return true;
87
+ },
88
+ },
89
+
90
+ {
91
+ name: 'appendFile',
92
+ kind: 'builtin',
93
+ schema: {
94
+ name: 'appendFile',
95
+ description: 'Append content to a file.',
96
+ parameters: [
97
+ {
98
+ name: 'path',
99
+ type: { type: 'string' },
100
+ description: 'The file path to append to',
101
+ required: true,
102
+ },
103
+ {
104
+ name: 'content',
105
+ type: { type: 'string' },
106
+ description: 'The content to append',
107
+ required: true,
108
+ },
109
+ ],
110
+ returns: { type: 'boolean' },
111
+ },
112
+ executor: async (args: Record<string, unknown>, context?: ToolContext) => {
113
+ const inputPath = args.path as string;
114
+ const safePath = context ? validatePathInSandbox(inputPath, context.rootDir) : inputPath;
115
+ const content = args.content as string;
116
+ const file = Bun.file(safePath);
117
+ const existing = (await file.exists()) ? await file.text() : '';
118
+ await Bun.write(safePath, existing + content);
119
+ return true;
120
+ },
121
+ },
122
+
123
+ {
124
+ name: 'fileExists',
125
+ kind: 'builtin',
126
+ schema: {
127
+ name: 'fileExists',
128
+ description: 'Check if a file exists.',
129
+ parameters: [
130
+ {
131
+ name: 'path',
132
+ type: { type: 'string' },
133
+ description: 'The file path to check',
134
+ required: true,
135
+ },
136
+ ],
137
+ returns: { type: 'boolean' },
138
+ },
139
+ executor: async (args: Record<string, unknown>, context?: ToolContext) => {
140
+ const inputPath = args.path as string;
141
+ const safePath = context ? validatePathInSandbox(inputPath, context.rootDir) : inputPath;
142
+ const file = Bun.file(safePath);
143
+ return await file.exists();
144
+ },
145
+ },
146
+
147
+ {
148
+ name: 'listDir',
149
+ kind: 'builtin',
150
+ schema: {
151
+ name: 'listDir',
152
+ description: 'List files in a directory.',
153
+ parameters: [
154
+ {
155
+ name: 'path',
156
+ type: { type: 'string' },
157
+ description: 'The directory path to list',
158
+ required: true,
159
+ },
160
+ ],
161
+ returns: { type: 'array', items: { type: 'string' } },
162
+ },
163
+ executor: async (args: Record<string, unknown>, context?: ToolContext) => {
164
+ const inputPath = args.path as string;
165
+ const safePath = context ? validatePathInSandbox(inputPath, context.rootDir) : inputPath;
166
+ const fs = await import('fs/promises');
167
+ return await fs.readdir(safePath);
168
+ },
169
+ },
170
+
171
+ {
172
+ name: 'edit',
173
+ kind: 'builtin',
174
+ schema: {
175
+ name: 'edit',
176
+ description:
177
+ 'Find and replace text in a file. The oldText must match exactly once in the file.',
178
+ parameters: [
179
+ {
180
+ name: 'path',
181
+ type: { type: 'string' },
182
+ description: 'The file path to edit',
183
+ required: true,
184
+ },
185
+ {
186
+ name: 'oldText',
187
+ type: { type: 'string' },
188
+ description: 'The text to find (must match exactly once)',
189
+ required: true,
190
+ },
191
+ {
192
+ name: 'newText',
193
+ type: { type: 'string' },
194
+ description: 'The text to replace with',
195
+ required: true,
196
+ },
197
+ ],
198
+ returns: { type: 'boolean' },
199
+ },
200
+ executor: async (args: Record<string, unknown>, context?: ToolContext) => {
201
+ const inputPath = args.path as string;
202
+ const safePath = context ? validatePathInSandbox(inputPath, context.rootDir) : inputPath;
203
+ const oldText = args.oldText as string;
204
+ const newText = args.newText as string;
205
+
206
+ const file = Bun.file(safePath);
207
+ const content = await file.text();
208
+
209
+ // Count occurrences of oldText
210
+ const matches = content.split(oldText).length - 1;
211
+
212
+ if (matches === 0) {
213
+ throw new Error(`edit failed: oldText not found in file`);
214
+ }
215
+
216
+ if (matches > 1) {
217
+ throw new Error(
218
+ `edit failed: oldText matches ${matches} times, must match exactly once. Provide more context to make the match unique.`
219
+ );
220
+ }
221
+
222
+ // Exactly one match - safe to replace
223
+ const newContent = content.replace(oldText, newText);
224
+ await Bun.write(safePath, newContent);
225
+ return true;
226
+ },
227
+ },
228
+ ] satisfies RegisteredTool[];
@@ -0,0 +1,5 @@
1
+ // Tool system types and utilities
2
+ export * from './types';
3
+ export { extractTypeSchema, vibeTypeToJsonSchema, clearSchemaCache, createTypeExtractor } from './ts-schema';
4
+ export type { TypeExtractor } from './ts-schema';
5
+ export { validatePathInSandbox } from './security';
@@ -0,0 +1,48 @@
1
+ import type { RegisteredTool, ToolRegistry, ToolSchema } from './types';
2
+ import { allTools } from './builtin';
3
+
4
+ /**
5
+ * Create a new tool registry.
6
+ * Tools are stored in a Map for O(1) lookup.
7
+ */
8
+ export function createToolRegistry(): ToolRegistry {
9
+ const tools = new Map<string, RegisteredTool>();
10
+
11
+ return {
12
+ register(tool: RegisteredTool): void {
13
+ // Allow overwriting existing tools (user tools can shadow builtins)
14
+ tools.set(tool.name, tool);
15
+ },
16
+
17
+ registerAll(toolsToAdd: RegisteredTool[]): void {
18
+ for (const tool of toolsToAdd) {
19
+ tools.set(tool.name, tool);
20
+ }
21
+ },
22
+
23
+ get(name: string): RegisteredTool | undefined {
24
+ return tools.get(name);
25
+ },
26
+
27
+ has(name: string): boolean {
28
+ return tools.has(name);
29
+ },
30
+
31
+ list(): RegisteredTool[] {
32
+ return Array.from(tools.values());
33
+ },
34
+
35
+ getSchemas(): ToolSchema[] {
36
+ return Array.from(tools.values()).map((t) => t.schema);
37
+ },
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Create a tool registry pre-populated with standard tools.
43
+ */
44
+ export function createToolRegistryWithBuiltins(): ToolRegistry {
45
+ const registry = createToolRegistry();
46
+ registry.registerAll(allTools);
47
+ return registry;
48
+ }