@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,160 @@
1
+ // Type validation and coercion utilities
2
+
3
+ import type { VibeType, VibeTypeRequired } from '../ast';
4
+ import { RuntimeError, type SourceLocation } from '../errors';
5
+ import { isVibeValue, resolveValue } from './types';
6
+
7
+ // Map array types to their element types (type-safe alternative to string slicing)
8
+ const ARRAY_ELEMENT_TYPES: Record<string, VibeTypeRequired> = {
9
+ 'text[]': 'text',
10
+ 'json[]': 'json',
11
+ 'boolean[]': 'boolean',
12
+ 'number[]': 'number',
13
+ 'prompt[]': 'prompt',
14
+ };
15
+
16
+ /**
17
+ * Validates a value against a type annotation and coerces if necessary.
18
+ * Returns { value, inferredType } where inferredType is set when no explicit type was given.
19
+ */
20
+ export function validateAndCoerce(
21
+ value: unknown,
22
+ type: VibeType,
23
+ varName: string,
24
+ location?: SourceLocation,
25
+ source?: 'ai' | 'user'
26
+ ): { value: unknown; inferredType: VibeType } {
27
+ // Resolve VibeValue unless this is a direct AI result to an UNTYPED variable
28
+ // (source === 'ai' means the value came directly from an AI call)
29
+ // For typed variables, always resolve so type validation can work on the primitive value
30
+ const keepVibeValueWrapper = source === 'ai' && type === null;
31
+ if (!keepVibeValueWrapper) {
32
+ value = resolveValue(value);
33
+ }
34
+
35
+ // null is valid for any typed variable
36
+ if (value === null) {
37
+ return { value: null, inferredType: type };
38
+ }
39
+
40
+ // If no type annotation, infer from JavaScript type
41
+ if (!type) {
42
+ // For VibeValue, infer type from the underlying value, not the wrapper
43
+ const valueToInfer = resolveValue(value);
44
+
45
+ if (typeof valueToInfer === 'string') {
46
+ return { value, inferredType: 'text' };
47
+ }
48
+ if (typeof valueToInfer === 'boolean') {
49
+ return { value, inferredType: 'boolean' };
50
+ }
51
+ if (typeof valueToInfer === 'number') {
52
+ return { value, inferredType: 'number' };
53
+ }
54
+ if (typeof valueToInfer === 'object' && valueToInfer !== null) {
55
+ return { value, inferredType: 'json' };
56
+ }
57
+ // For other types (null, undefined), no type inference
58
+ return { value, inferredType: null };
59
+ }
60
+
61
+ // Validate array types (text[], json[], boolean[], number[], prompt[])
62
+ const elementType = ARRAY_ELEMENT_TYPES[type];
63
+ if (elementType) {
64
+ let arrayValue = value;
65
+
66
+ // If string, try to parse as JSON array
67
+ if (typeof value === 'string') {
68
+ try {
69
+ arrayValue = JSON.parse(value);
70
+ } catch {
71
+ throw new RuntimeError(`Variable '${varName}': invalid JSON array string`, location);
72
+ }
73
+ }
74
+
75
+ if (!Array.isArray(arrayValue)) {
76
+ throw new RuntimeError(`Variable '${varName}': expected ${type} (array), got ${typeof value}`, location);
77
+ }
78
+
79
+ // Validate each element recursively
80
+ const validatedElements = arrayValue.map((elem, i) => {
81
+ const { value: validated } = validateAndCoerce(elem, elementType, `${varName}[${i}]`, location);
82
+ return validated;
83
+ });
84
+
85
+ return { value: validatedElements, inferredType: type };
86
+ }
87
+
88
+ // Validate text type - must be a string
89
+ if (type === 'text') {
90
+ if (typeof value !== 'string') {
91
+ throw new RuntimeError(`Variable '${varName}': expected text (string), got ${typeof value}`, location);
92
+ }
93
+ return { value, inferredType: 'text' };
94
+ }
95
+
96
+ // Validate json type - must be object (not array)
97
+ if (type === 'json') {
98
+ let result = value;
99
+
100
+ // If string, try to parse as JSON
101
+ if (typeof value === 'string') {
102
+ try {
103
+ result = JSON.parse(value);
104
+ } catch {
105
+ throw new RuntimeError(`Variable '${varName}': invalid JSON string`, location);
106
+ }
107
+ }
108
+
109
+ // Validate the result is an object (not array, not primitive)
110
+ if (typeof result !== 'object' || result === null) {
111
+ throw new RuntimeError(`Variable '${varName}': expected json (object), got ${typeof value}`, location);
112
+ }
113
+ if (Array.isArray(result)) {
114
+ throw new RuntimeError(`Variable '${varName}': json type expects an object, not an array. Use json[] for arrays.`, location);
115
+ }
116
+ return { value: result, inferredType: 'json' };
117
+ }
118
+
119
+ // Validate boolean type - must be a boolean
120
+ if (type === 'boolean') {
121
+ if (typeof value !== 'boolean') {
122
+ throw new RuntimeError(`Variable '${varName}': expected boolean, got ${typeof value}`, location);
123
+ }
124
+ return { value, inferredType: 'boolean' };
125
+ }
126
+
127
+ // Validate number type - must be a finite number
128
+ if (type === 'number') {
129
+ if (typeof value !== 'number') {
130
+ throw new RuntimeError(`Variable '${varName}': expected number, got ${typeof value}`, location);
131
+ }
132
+ if (!Number.isFinite(value)) {
133
+ throw new RuntimeError(`Variable '${varName}': number must be finite, got ${value}`, location);
134
+ }
135
+ return { value, inferredType: 'number' };
136
+ }
137
+
138
+ // For prompt type, accept string values as-is
139
+ return { value, inferredType: type };
140
+ }
141
+
142
+ /**
143
+ * Strict boolean check - no truthy coercion allowed.
144
+ * Throws if value is not a boolean.
145
+ */
146
+ export function requireBoolean(value: unknown, context: string): boolean {
147
+ // Handle VibeValue with error - throw the error
148
+ if (isVibeValue(value) && value.err && value.errDetails) {
149
+ throw new Error(`${value.errDetails.type}: ${value.errDetails.message}`);
150
+ }
151
+
152
+ // Auto-unwrap VibeValue
153
+ const unwrapped = resolveValue(value);
154
+
155
+ if (typeof unwrapped !== 'boolean') {
156
+ const valueType = unwrapped === null ? 'null' : typeof unwrapped;
157
+ throw new Error(`TypeError: ${context} must be a boolean, got ${valueType}`);
158
+ }
159
+ return unwrapped;
160
+ }
@@ -0,0 +1,459 @@
1
+ /**
2
+ * Verbose Logger - JSONL logging for AI interactions, TS executions, and tool calls
3
+ *
4
+ * Outputs:
5
+ * - Main log: JSONL events to console and .vibe-logs/run-{timestamp}.jsonl
6
+ * - Context files: .vibe-logs/run-{timestamp}/do-000001.txt, etc.
7
+ */
8
+
9
+ import { mkdirSync, writeFileSync, appendFileSync, existsSync } from 'fs';
10
+ import { join, dirname } from 'path';
11
+ import type {
12
+ LogEvent,
13
+ RunStartEvent,
14
+ RunCompleteEvent,
15
+ AIStartEvent,
16
+ AICompleteEvent,
17
+ ToolStartEvent,
18
+ ToolCompleteEvent,
19
+ TSStartEvent,
20
+ TSCompleteEvent,
21
+ AILogMessage,
22
+ TokenUsage,
23
+ } from './types';
24
+
25
+ export interface VerboseLoggerOptions {
26
+ logDir?: string; // Base directory for logs (default: .vibe-logs)
27
+ printToConsole?: boolean; // Print events to console (default: true)
28
+ writeToFile?: boolean; // Write events to file (default: true)
29
+ }
30
+
31
+ interface AICallContext {
32
+ model: string;
33
+ modelDetails?: { name: string; provider: string; url?: string };
34
+ type: 'do' | 'vibe';
35
+ targetType: string | null;
36
+ messages: AILogMessage[];
37
+ }
38
+
39
+ /**
40
+ * VerboseLogger - Manages structured logging for Vibe runtime
41
+ */
42
+ export class VerboseLogger {
43
+ private logDir: string;
44
+ private runTimestamp: string;
45
+ private mainLogPath: string;
46
+ private contextDir: string;
47
+ private printToConsole: boolean;
48
+ private writeToFile: boolean;
49
+
50
+ private seq = 0;
51
+ private counters = { do: 0, vibe: 0, ts: 0, tsf: 0 };
52
+ private events: LogEvent[] = [];
53
+ private startTime: number = 0;
54
+
55
+ constructor(options: VerboseLoggerOptions = {}) {
56
+ this.logDir = options.logDir ?? '.vibe-logs';
57
+ this.printToConsole = options.printToConsole ?? true;
58
+ this.writeToFile = options.writeToFile ?? true;
59
+
60
+ // Generate timestamp for this run
61
+ this.runTimestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
62
+ this.mainLogPath = join(this.logDir, `run-${this.runTimestamp}.jsonl`);
63
+ this.contextDir = join(this.logDir, `run-${this.runTimestamp}`);
64
+ }
65
+
66
+ /**
67
+ * Initialize the logger (create directories)
68
+ */
69
+ private ensureDirectories(): void {
70
+ if (this.writeToFile) {
71
+ if (!existsSync(this.logDir)) {
72
+ mkdirSync(this.logDir, { recursive: true });
73
+ }
74
+ if (!existsSync(this.contextDir)) {
75
+ mkdirSync(this.contextDir, { recursive: true });
76
+ }
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Generate next ID for a given type
82
+ */
83
+ private nextId(type: 'do' | 'vibe' | 'ts' | 'tsf'): string {
84
+ this.counters[type]++;
85
+ return `${type}-${String(this.counters[type]).padStart(6, '0')}`;
86
+ }
87
+
88
+ /**
89
+ * Log an event (console + file + in-memory)
90
+ */
91
+ private logEvent(event: LogEvent): void {
92
+ this.events.push(event);
93
+
94
+ const jsonLine = JSON.stringify(event);
95
+
96
+ if (this.printToConsole) {
97
+ console.log(jsonLine);
98
+ }
99
+
100
+ if (this.writeToFile) {
101
+ this.ensureDirectories();
102
+ appendFileSync(this.mainLogPath, jsonLine + '\n');
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Write context file for an AI call
108
+ */
109
+ private writeContextFile(id: string, context: AICallContext): void {
110
+ if (!this.writeToFile) return;
111
+
112
+ this.ensureDirectories();
113
+
114
+ const lines: string[] = [
115
+ `=== AI Call: ${id} ===`,
116
+ `Model: ${context.modelDetails?.name ?? context.model} (${context.modelDetails?.provider ?? 'unknown'})`,
117
+ `Type: ${context.type}`,
118
+ `Target: ${context.targetType ?? 'text'}`,
119
+ `Timestamp: ${new Date().toISOString()}`,
120
+ '',
121
+ '=== MESSAGES ===',
122
+ '',
123
+ ];
124
+
125
+ for (const msg of context.messages) {
126
+ lines.push(`[${msg.role}]`);
127
+ lines.push(msg.content);
128
+
129
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
130
+ lines.push('');
131
+ lines.push('Tool calls:');
132
+ for (const tc of msg.toolCalls) {
133
+ lines.push(` - ${tc.toolName}(${JSON.stringify(tc.args)})`);
134
+ }
135
+ }
136
+
137
+ if (msg.toolResults && msg.toolResults.length > 0) {
138
+ lines.push('');
139
+ lines.push('Tool results:');
140
+ for (const tr of msg.toolResults) {
141
+ if (tr.error) {
142
+ lines.push(` - Error: ${tr.error}`);
143
+ } else {
144
+ lines.push(` - Result: ${JSON.stringify(tr.result)}`);
145
+ }
146
+ }
147
+ }
148
+
149
+ lines.push('');
150
+ }
151
+
152
+ lines.push('=== END MESSAGES ===');
153
+
154
+ const filePath = join(this.contextDir, `${id}.txt`);
155
+ writeFileSync(filePath, lines.join('\n'));
156
+ }
157
+
158
+ /**
159
+ * Write context file for a TS block
160
+ */
161
+ private writeTSContextFile(id: string, params: string[], paramValues: unknown[], body: string, location: { file: string; line: number }): void {
162
+ if (!this.writeToFile) return;
163
+
164
+ this.ensureDirectories();
165
+
166
+ const lines: string[] = [
167
+ `// TS Block: ${id}`,
168
+ `// Location: ${location.file}:${location.line}`,
169
+ ];
170
+
171
+ if (params.length > 0) {
172
+ const paramStr = params.map((p, i) => `${p} = ${JSON.stringify(paramValues[i])}`).join(', ');
173
+ lines.push(`// Params: ${paramStr}`);
174
+ }
175
+
176
+ lines.push('');
177
+ lines.push(body);
178
+
179
+ const filePath = join(this.contextDir, `${id}.ts`);
180
+ writeFileSync(filePath, lines.join('\n'));
181
+ }
182
+
183
+ /**
184
+ * Write context file for an imported TS function call
185
+ */
186
+ private writeTSFunctionContextFile(id: string, funcName: string, args: unknown[], location: { file: string; line: number }): void {
187
+ if (!this.writeToFile) return;
188
+
189
+ this.ensureDirectories();
190
+
191
+ const lines: string[] = [
192
+ `// TS Function Call: ${id}`,
193
+ `// Function: ${funcName}`,
194
+ `// Location: ${location.file}:${location.line}`,
195
+ '',
196
+ `${funcName}(`,
197
+ ];
198
+
199
+ for (let i = 0; i < args.length; i++) {
200
+ const comma = i < args.length - 1 ? ',' : '';
201
+ lines.push(` ${JSON.stringify(args[i], null, 2).split('\n').join('\n ')}${comma}`);
202
+ }
203
+
204
+ lines.push(')');
205
+
206
+ const filePath = join(this.contextDir, `${id}.ts`);
207
+ writeFileSync(filePath, lines.join('\n'));
208
+ }
209
+
210
+ // ============================================================================
211
+ // Public API
212
+ // ============================================================================
213
+
214
+ /**
215
+ * Log run start
216
+ */
217
+ start(file: string): void {
218
+ this.startTime = Date.now();
219
+
220
+ const event: RunStartEvent = {
221
+ seq: ++this.seq,
222
+ ts: new Date().toISOString(),
223
+ event: 'run_start',
224
+ file,
225
+ };
226
+
227
+ this.logEvent(event);
228
+ }
229
+
230
+ /**
231
+ * Log run completion
232
+ */
233
+ complete(status: 'completed' | 'error', error?: string): void {
234
+ const event: RunCompleteEvent = {
235
+ seq: ++this.seq,
236
+ ts: new Date().toISOString(),
237
+ event: 'run_complete',
238
+ durationMs: Date.now() - this.startTime,
239
+ status,
240
+ ...(error && { error }),
241
+ };
242
+
243
+ this.logEvent(event);
244
+ }
245
+
246
+ /**
247
+ * Log AI call start - returns the ID for this call
248
+ */
249
+ aiStart(
250
+ type: 'do' | 'vibe',
251
+ model: string,
252
+ prompt: string,
253
+ context: AICallContext
254
+ ): string {
255
+ const id = this.nextId(type);
256
+
257
+ const event: AIStartEvent = {
258
+ seq: ++this.seq,
259
+ ts: new Date().toISOString(),
260
+ event: 'ai_start',
261
+ id,
262
+ type,
263
+ model,
264
+ prompt: prompt.length > 100 ? prompt.slice(0, 100) + '...' : prompt,
265
+ };
266
+
267
+ this.logEvent(event);
268
+
269
+ // Write full context to file
270
+ this.writeContextFile(id, context);
271
+
272
+ return id;
273
+ }
274
+
275
+ /**
276
+ * Log AI call completion
277
+ */
278
+ aiComplete(
279
+ id: string,
280
+ durationMs: number,
281
+ usage?: TokenUsage,
282
+ toolCallCount = 0,
283
+ error?: string
284
+ ): void {
285
+ const event: AICompleteEvent = {
286
+ seq: ++this.seq,
287
+ ts: new Date().toISOString(),
288
+ event: 'ai_complete',
289
+ id,
290
+ durationMs,
291
+ ...(usage && { tokens: { in: usage.inputTokens, out: usage.outputTokens } }),
292
+ toolCalls: toolCallCount,
293
+ ...(error && { error }),
294
+ };
295
+
296
+ this.logEvent(event);
297
+ }
298
+
299
+ /**
300
+ * Log tool call start
301
+ */
302
+ toolStart(parentId: string, tool: string, args: Record<string, unknown>): void {
303
+ const event: ToolStartEvent = {
304
+ seq: ++this.seq,
305
+ ts: new Date().toISOString(),
306
+ event: 'tool_start',
307
+ parentId,
308
+ tool,
309
+ args,
310
+ };
311
+
312
+ this.logEvent(event);
313
+ }
314
+
315
+ /**
316
+ * Log tool call completion
317
+ */
318
+ toolComplete(
319
+ parentId: string,
320
+ tool: string,
321
+ durationMs: number,
322
+ success: boolean,
323
+ error?: string
324
+ ): void {
325
+ const event: ToolCompleteEvent = {
326
+ seq: ++this.seq,
327
+ ts: new Date().toISOString(),
328
+ event: 'tool_complete',
329
+ parentId,
330
+ tool,
331
+ durationMs,
332
+ success,
333
+ ...(error && { error }),
334
+ };
335
+
336
+ this.logEvent(event);
337
+ }
338
+
339
+ /**
340
+ * Log TS block start - returns the ID
341
+ */
342
+ tsBlockStart(
343
+ params: string[],
344
+ paramValues: unknown[],
345
+ body: string,
346
+ location: { file: string; line: number }
347
+ ): string {
348
+ const id = this.nextId('ts');
349
+
350
+ const event: TSStartEvent = {
351
+ seq: ++this.seq,
352
+ ts: new Date().toISOString(),
353
+ event: 'ts_start',
354
+ id,
355
+ tsType: 'block',
356
+ params,
357
+ location,
358
+ };
359
+
360
+ this.logEvent(event);
361
+
362
+ // Write TS code to context file
363
+ this.writeTSContextFile(id, params, paramValues, body, location);
364
+
365
+ return id;
366
+ }
367
+
368
+ /**
369
+ * Log imported TS function start - returns the ID
370
+ */
371
+ tsFunctionStart(
372
+ funcName: string,
373
+ args: unknown[],
374
+ location: { file: string; line: number }
375
+ ): string {
376
+ const id = this.nextId('tsf');
377
+
378
+ const event: TSStartEvent = {
379
+ seq: ++this.seq,
380
+ ts: new Date().toISOString(),
381
+ event: 'ts_start',
382
+ id,
383
+ tsType: 'function',
384
+ name: funcName,
385
+ params: [], // We don't have param names for imported functions
386
+ location,
387
+ };
388
+
389
+ this.logEvent(event);
390
+
391
+ // Write function call to context file
392
+ this.writeTSFunctionContextFile(id, funcName, args, location);
393
+
394
+ return id;
395
+ }
396
+
397
+ /**
398
+ * Log TS block completion
399
+ */
400
+ tsBlockComplete(id: string, durationMs: number, error?: string): void {
401
+ const event: TSCompleteEvent = {
402
+ seq: ++this.seq,
403
+ ts: new Date().toISOString(),
404
+ event: 'ts_complete',
405
+ id,
406
+ tsType: 'block',
407
+ durationMs,
408
+ ...(error && { error }),
409
+ };
410
+
411
+ this.logEvent(event);
412
+ }
413
+
414
+ /**
415
+ * Log imported TS function completion
416
+ */
417
+ tsFunctionComplete(id: string, durationMs: number, error?: string): void {
418
+ const event: TSCompleteEvent = {
419
+ seq: ++this.seq,
420
+ ts: new Date().toISOString(),
421
+ event: 'ts_complete',
422
+ id,
423
+ tsType: 'function',
424
+ durationMs,
425
+ ...(error && { error }),
426
+ };
427
+
428
+ this.logEvent(event);
429
+ }
430
+
431
+ /**
432
+ * Get all logged events (for testing/inspection)
433
+ */
434
+ getEvents(): LogEvent[] {
435
+ return [...this.events];
436
+ }
437
+
438
+ /**
439
+ * Get the main log file path
440
+ */
441
+ getMainLogPath(): string {
442
+ return this.mainLogPath;
443
+ }
444
+
445
+ /**
446
+ * Get the context directory path
447
+ */
448
+ getContextDir(): string {
449
+ return this.contextDir;
450
+ }
451
+ }
452
+
453
+ /**
454
+ * Create a no-op logger (when verbose is disabled)
455
+ * Returns null - callers should check for null before logging
456
+ */
457
+ export function createNoOpLogger(): null {
458
+ return null;
459
+ }
package/src/runtime.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Re-export from new location for backwards compatibility
2
+ export * from './runtime/index';
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Analyzer Context
3
+ *
4
+ * Shared interface and types for semantic analyzer modules.
5
+ * Provides a clean way to pass analyzer state to helper functions.
6
+ */
7
+ import type { SourceLocation } from '../errors';
8
+ import type { SymbolTable, SymbolKind } from './symbol-table';
9
+ import type { TsFunctionSignature } from './ts-signatures';
10
+
11
+ /**
12
+ * Context interface passed to analyzer helper functions.
13
+ * Contains the shared state needed for semantic analysis.
14
+ */
15
+ export interface AnalyzerContext {
16
+ /** Symbol table for scope management */
17
+ symbols: SymbolTable;
18
+
19
+ /** Map of imported TS function names to their signatures */
20
+ tsImportSignatures: Map<string, TsFunctionSignature>;
21
+
22
+ /** Base path for resolving imports */
23
+ basePath?: string;
24
+
25
+ /** Original source code (for error messages) */
26
+ source?: string;
27
+
28
+ /** Whether currently inside a function body */
29
+ inFunction: boolean;
30
+
31
+ /** Whether at top level (not in a block) */
32
+ atTopLevel: boolean;
33
+
34
+ /** Current loop nesting depth (0 = not in a loop) */
35
+ loopDepth: number;
36
+
37
+ /** Report an error at the given location */
38
+ error(message: string, location: SourceLocation): void;
39
+
40
+ /** Declare a symbol in the current scope */
41
+ declare(
42
+ name: string,
43
+ kind: SymbolKind,
44
+ location: SourceLocation,
45
+ options?: {
46
+ paramCount?: number;
47
+ typeAnnotation?: string | null;
48
+ paramTypes?: string[];
49
+ returnType?: string | null;
50
+ }
51
+ ): void;
52
+ }
53
+
54
+ /**
55
+ * Mutable context state that can be modified during analysis.
56
+ * Separate from AnalyzerContext to make mutation explicit.
57
+ */
58
+ export interface AnalyzerState {
59
+ inFunction: boolean;
60
+ atTopLevel: boolean;
61
+ loopDepth: number;
62
+ }