@phuetz/code-buddy 0.1.0 → 0.1.2

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 (305) hide show
  1. package/.codebuddy/skills/bundled/brave-search/SKILL.md +490 -0
  2. package/.codebuddy/skills/bundled/exa-search/SKILL.md +1122 -0
  3. package/.codebuddy/skills/bundled/perplexity/SKILL.md +748 -0
  4. package/.codebuddy/skills/bundled/playwright/SKILL.md +520 -0
  5. package/.codebuddy/skills/bundled/puppeteer/SKILL.md +708 -0
  6. package/.codebuddy/skills/bundled/web-fetch/SKILL.md +1003 -0
  7. package/README.md +56 -0
  8. package/dist/agent/agent-state.d.ts +3 -3
  9. package/dist/agent/agent-state.js +6 -6
  10. package/dist/agent/agent-state.js.map +1 -1
  11. package/dist/agent/base-agent.d.ts +4 -4
  12. package/dist/agent/base-agent.js +22 -9
  13. package/dist/agent/base-agent.js.map +1 -1
  14. package/dist/agent/cache-trace.d.ts +56 -0
  15. package/dist/agent/cache-trace.js +98 -0
  16. package/dist/agent/cache-trace.js.map +1 -0
  17. package/dist/agent/codebuddy-agent.js +4 -2
  18. package/dist/agent/codebuddy-agent.js.map +1 -1
  19. package/dist/agent/execution/agent-executor.d.ts +4 -4
  20. package/dist/agent/execution/agent-executor.js +46 -14
  21. package/dist/agent/execution/agent-executor.js.map +1 -1
  22. package/dist/agent/facades/agent-context-facade.js +1 -3
  23. package/dist/agent/facades/agent-context-facade.js.map +1 -1
  24. package/dist/agent/facades/message-history-manager.js +14 -12
  25. package/dist/agent/facades/message-history-manager.js.map +1 -1
  26. package/dist/agent/facades/session-facade.d.ts +3 -3
  27. package/dist/agent/facades/session-facade.js +6 -6
  28. package/dist/agent/facades/session-facade.js.map +1 -1
  29. package/dist/agent/history-repair.d.ts +37 -0
  30. package/dist/agent/history-repair.js +124 -0
  31. package/dist/agent/history-repair.js.map +1 -0
  32. package/dist/agent/index.d.ts +3 -3
  33. package/dist/agent/index.js +3 -3
  34. package/dist/agent/index.js.map +1 -1
  35. package/dist/agent/isolation/agent-workspace.d.ts +1 -0
  36. package/dist/agent/isolation/agent-workspace.js +10 -0
  37. package/dist/agent/isolation/agent-workspace.js.map +1 -1
  38. package/dist/agent/specialized/archive-agent.d.ts +3 -0
  39. package/dist/agent/specialized/archive-agent.js +71 -31
  40. package/dist/agent/specialized/archive-agent.js.map +1 -1
  41. package/dist/agent/specialized/index.d.ts +9 -8
  42. package/dist/agent/specialized/index.js +16 -8
  43. package/dist/agent/specialized/index.js.map +1 -1
  44. package/dist/agent/specialized/security-review/agent.js +19 -8
  45. package/dist/agent/specialized/security-review/agent.js.map +1 -1
  46. package/dist/agent/tool-executor.js +5 -0
  47. package/dist/agent/tool-executor.js.map +1 -1
  48. package/dist/agent/turn-diff-tracker.d.ts +79 -0
  49. package/dist/agent/turn-diff-tracker.js +195 -0
  50. package/dist/agent/turn-diff-tracker.js.map +1 -0
  51. package/dist/browser/controller.js +8 -4
  52. package/dist/browser/controller.js.map +1 -1
  53. package/dist/browser-automation/browser-manager.js +8 -1
  54. package/dist/browser-automation/browser-manager.js.map +1 -1
  55. package/dist/checkpoints/checkpoint-versioning.js +78 -20
  56. package/dist/checkpoints/checkpoint-versioning.js.map +1 -1
  57. package/dist/cli/config-loader.js +2 -4
  58. package/dist/cli/config-loader.js.map +1 -1
  59. package/dist/codebuddy/client.js +70 -11
  60. package/dist/codebuddy/client.js.map +1 -1
  61. package/dist/codebuddy/tools.d.ts +1 -7
  62. package/dist/codebuddy/tools.js +2 -30
  63. package/dist/codebuddy/tools.js.map +1 -1
  64. package/dist/commands/cli/daemon-commands.d.ts +14 -0
  65. package/dist/commands/cli/daemon-commands.js +166 -0
  66. package/dist/commands/cli/daemon-commands.js.map +1 -0
  67. package/dist/commands/cli/speak-command.d.ts +10 -0
  68. package/dist/commands/cli/speak-command.js +97 -0
  69. package/dist/commands/cli/speak-command.js.map +1 -0
  70. package/dist/commands/cli/utility-commands.d.ts +10 -0
  71. package/dist/commands/cli/utility-commands.js +88 -0
  72. package/dist/commands/cli/utility-commands.js.map +1 -0
  73. package/dist/commands/handlers/fcs-handlers.js +1 -1
  74. package/dist/commands/handlers/fcs-handlers.js.map +1 -1
  75. package/dist/commands/handlers/memory-handlers.js +2 -1
  76. package/dist/commands/handlers/memory-handlers.js.map +1 -1
  77. package/dist/commands/handlers/vibe-handlers.js +0 -1
  78. package/dist/commands/handlers/vibe-handlers.js.map +1 -1
  79. package/dist/commands/handlers/worktree-handlers.js +11 -0
  80. package/dist/commands/handlers/worktree-handlers.js.map +1 -1
  81. package/dist/commands/index.d.ts +8 -7
  82. package/dist/commands/index.js +10 -8
  83. package/dist/commands/index.js.map +1 -1
  84. package/dist/commands/mcp.d.ts +1 -0
  85. package/dist/commands/mcp.js +66 -7
  86. package/dist/commands/mcp.js.map +1 -1
  87. package/dist/commands/pipeline.js +25 -13
  88. package/dist/commands/pipeline.js.map +1 -1
  89. package/dist/config/hot-reload/watcher.js +4 -4
  90. package/dist/config/hot-reload/watcher.js.map +1 -1
  91. package/dist/config/model-tools.d.ts +41 -0
  92. package/dist/config/model-tools.js +194 -0
  93. package/dist/config/model-tools.js.map +1 -0
  94. package/dist/context/context-manager-v2.d.ts +2 -1
  95. package/dist/context/context-manager-v2.js +34 -5
  96. package/dist/context/context-manager-v2.js.map +1 -1
  97. package/dist/context/index.d.ts +12 -12
  98. package/dist/context/index.js +25 -12
  99. package/dist/context/index.js.map +1 -1
  100. package/dist/daemon/daemon-manager.js +23 -19
  101. package/dist/daemon/daemon-manager.js.map +1 -1
  102. package/dist/database/database-manager.d.ts +4 -0
  103. package/dist/database/database-manager.js +16 -7
  104. package/dist/database/database-manager.js.map +1 -1
  105. package/dist/desktop-automation/nutjs-provider.js +89 -0
  106. package/dist/desktop-automation/nutjs-provider.js.map +1 -1
  107. package/dist/errors/index.d.ts +4 -4
  108. package/dist/errors/index.js +8 -4
  109. package/dist/errors/index.js.map +1 -1
  110. package/dist/fcs/builtins.d.ts +2 -6
  111. package/dist/fcs/builtins.js +2 -568
  112. package/dist/fcs/builtins.js.map +1 -1
  113. package/dist/fcs/codebuddy-bindings.d.ts +3 -43
  114. package/dist/fcs/codebuddy-bindings.js +2 -606
  115. package/dist/fcs/codebuddy-bindings.js.map +1 -1
  116. package/dist/fcs/index.d.ts +2 -27
  117. package/dist/fcs/index.js +2 -53
  118. package/dist/fcs/index.js.map +1 -1
  119. package/dist/fcs/lexer.d.ts +2 -37
  120. package/dist/fcs/lexer.js +2 -459
  121. package/dist/fcs/lexer.js.map +1 -1
  122. package/dist/fcs/parser.d.ts +2 -68
  123. package/dist/fcs/parser.js +2 -893
  124. package/dist/fcs/parser.js.map +1 -1
  125. package/dist/fcs/runtime.d.ts +2 -59
  126. package/dist/fcs/runtime.js +2 -623
  127. package/dist/fcs/runtime.js.map +1 -1
  128. package/dist/fcs/script-registry.d.ts +3 -69
  129. package/dist/fcs/script-registry.js +2 -219
  130. package/dist/fcs/script-registry.js.map +1 -1
  131. package/dist/fcs/sync-bindings.d.ts +3 -101
  132. package/dist/fcs/sync-bindings.js +2 -410
  133. package/dist/fcs/sync-bindings.js.map +1 -1
  134. package/dist/fcs/types.d.ts +2 -285
  135. package/dist/fcs/types.js +2 -103
  136. package/dist/fcs/types.js.map +1 -1
  137. package/dist/hooks/index.d.ts +4 -4
  138. package/dist/hooks/index.js +4 -4
  139. package/dist/hooks/index.js.map +1 -1
  140. package/dist/hooks/use-input-handler.d.ts +1 -1
  141. package/dist/index.js +20 -330
  142. package/dist/index.js.map +1 -1
  143. package/dist/input/voice-control.js +11 -5
  144. package/dist/input/voice-control.js.map +1 -1
  145. package/dist/integrations/json-rpc/server.d.ts +9 -0
  146. package/dist/integrations/json-rpc/server.js +43 -13
  147. package/dist/integrations/json-rpc/server.js.map +1 -1
  148. package/dist/integrations/mcp/mcp-server.js +1 -1
  149. package/dist/integrations/mcp/mcp-server.js.map +1 -1
  150. package/dist/integrations/notification-integrations.d.ts +1 -0
  151. package/dist/integrations/notification-integrations.js +6 -1
  152. package/dist/integrations/notification-integrations.js.map +1 -1
  153. package/dist/mcp/client.js +2 -1
  154. package/dist/mcp/client.js.map +1 -1
  155. package/dist/mcp/config.js +89 -5
  156. package/dist/mcp/config.js.map +1 -1
  157. package/dist/mcp/mcp-client.js +65 -14
  158. package/dist/mcp/mcp-client.js.map +1 -1
  159. package/dist/mcp/transports.d.ts +0 -1
  160. package/dist/mcp/transports.js +1 -5
  161. package/dist/mcp/transports.js.map +1 -1
  162. package/dist/mcp/types.d.ts +2 -0
  163. package/dist/memory/index.d.ts +2 -2
  164. package/dist/memory/index.js +2 -2
  165. package/dist/memory/index.js.map +1 -1
  166. package/dist/persistence/session-lock.d.ts +42 -0
  167. package/dist/persistence/session-lock.js +165 -0
  168. package/dist/persistence/session-lock.js.map +1 -0
  169. package/dist/persistence/session-store.d.ts +18 -3
  170. package/dist/persistence/session-store.js +90 -21
  171. package/dist/persistence/session-store.js.map +1 -1
  172. package/dist/plugins/conflict-detection.js +2 -1
  173. package/dist/plugins/conflict-detection.js.map +1 -1
  174. package/dist/plugins/index.d.ts +3 -3
  175. package/dist/plugins/index.js +3 -3
  176. package/dist/plugins/index.js.map +1 -1
  177. package/dist/plugins/isolated-plugin-runner.d.ts +6 -0
  178. package/dist/plugins/isolated-plugin-runner.js +19 -1
  179. package/dist/plugins/isolated-plugin-runner.js.map +1 -1
  180. package/dist/providers/local-llm-provider.js +28 -8
  181. package/dist/providers/local-llm-provider.js.map +1 -1
  182. package/dist/sandbox/docker-sandbox.js +7 -4
  183. package/dist/sandbox/docker-sandbox.js.map +1 -1
  184. package/dist/scripting/builtins.d.ts +8 -3
  185. package/dist/scripting/builtins.js +506 -355
  186. package/dist/scripting/builtins.js.map +1 -1
  187. package/dist/scripting/codebuddy-bindings.d.ts +47 -0
  188. package/dist/scripting/codebuddy-bindings.js +488 -0
  189. package/dist/scripting/codebuddy-bindings.js.map +1 -0
  190. package/dist/scripting/index.d.ts +33 -30
  191. package/dist/scripting/index.js +41 -36
  192. package/dist/scripting/index.js.map +1 -1
  193. package/dist/scripting/lexer.d.ts +31 -13
  194. package/dist/scripting/lexer.js +379 -292
  195. package/dist/scripting/lexer.js.map +1 -1
  196. package/dist/scripting/parser.d.ts +63 -44
  197. package/dist/scripting/parser.js +700 -473
  198. package/dist/scripting/parser.js.map +1 -1
  199. package/dist/scripting/runtime.d.ts +55 -24
  200. package/dist/scripting/runtime.js +600 -288
  201. package/dist/scripting/runtime.js.map +1 -1
  202. package/dist/scripting/script-registry.d.ts +54 -0
  203. package/dist/scripting/script-registry.js +202 -0
  204. package/dist/scripting/script-registry.js.map +1 -0
  205. package/dist/scripting/sync-bindings.d.ts +105 -0
  206. package/dist/scripting/sync-bindings.js +353 -0
  207. package/dist/scripting/sync-bindings.js.map +1 -0
  208. package/dist/scripting/types.d.ts +297 -199
  209. package/dist/scripting/types.js +86 -60
  210. package/dist/scripting/types.js.map +1 -1
  211. package/dist/search/usearch-index.js +42 -7
  212. package/dist/search/usearch-index.js.map +1 -1
  213. package/dist/security/bash-parser.d.ts +51 -0
  214. package/dist/security/bash-parser.js +327 -0
  215. package/dist/security/bash-parser.js.map +1 -0
  216. package/dist/security/index.d.ts +7 -5
  217. package/dist/security/index.js +8 -7
  218. package/dist/security/index.js.map +1 -1
  219. package/dist/security/skill-scanner.d.ts +36 -0
  220. package/dist/security/skill-scanner.js +149 -0
  221. package/dist/security/skill-scanner.js.map +1 -0
  222. package/dist/security/trust-folders.d.ts +1 -0
  223. package/dist/security/trust-folders.js +19 -1
  224. package/dist/security/trust-folders.js.map +1 -1
  225. package/dist/server/auth/index.d.ts +2 -2
  226. package/dist/server/auth/index.js +2 -2
  227. package/dist/server/auth/index.js.map +1 -1
  228. package/dist/server/middleware/index.d.ts +5 -5
  229. package/dist/server/middleware/index.js +5 -5
  230. package/dist/server/middleware/index.js.map +1 -1
  231. package/dist/server/middleware/rate-limit.js +15 -3
  232. package/dist/server/middleware/rate-limit.js.map +1 -1
  233. package/dist/server/websocket/handler.js +54 -6
  234. package/dist/server/websocket/handler.js.map +1 -1
  235. package/dist/skills/eligibility.js +26 -4
  236. package/dist/skills/eligibility.js.map +1 -1
  237. package/dist/tasks/background-tasks.js +5 -1
  238. package/dist/tasks/background-tasks.js.map +1 -1
  239. package/dist/tools/apply-patch.d.ts +55 -0
  240. package/dist/tools/apply-patch.js +273 -0
  241. package/dist/tools/apply-patch.js.map +1 -0
  242. package/dist/tools/hooks/default-hooks.d.ts +1 -1
  243. package/dist/tools/hooks/default-hooks.js +2 -1
  244. package/dist/tools/hooks/default-hooks.js.map +1 -1
  245. package/dist/tools/index.d.ts +10 -10
  246. package/dist/tools/index.js +11 -11
  247. package/dist/tools/index.js.map +1 -1
  248. package/dist/tools/registry/bash-tools.js +6 -3
  249. package/dist/tools/registry/bash-tools.js.map +1 -1
  250. package/dist/tools/registry/misc-tools.js +1 -2
  251. package/dist/tools/registry/misc-tools.js.map +1 -1
  252. package/dist/tools/registry/search-tools.js +1 -1
  253. package/dist/tools/registry/search-tools.js.map +1 -1
  254. package/dist/tools/registry/text-editor-tools.js +1 -1
  255. package/dist/tools/registry/text-editor-tools.js.map +1 -1
  256. package/dist/tools/registry/todo-tools.js +37 -5
  257. package/dist/tools/registry/todo-tools.js.map +1 -1
  258. package/dist/tools/registry/tool-registry.js +5 -4
  259. package/dist/tools/registry/tool-registry.js.map +1 -1
  260. package/dist/tools/registry/web-tools.d.ts +1 -1
  261. package/dist/tools/registry/web-tools.js +28 -8
  262. package/dist/tools/registry/web-tools.js.map +1 -1
  263. package/dist/tools/text-editor.d.ts +1 -1
  264. package/dist/tools/text-editor.js +23 -5
  265. package/dist/tools/text-editor.js.map +1 -1
  266. package/dist/tools/web-search.d.ts +52 -37
  267. package/dist/tools/web-search.js +368 -163
  268. package/dist/tools/web-search.js.map +1 -1
  269. package/dist/types/errors.d.ts +1 -1
  270. package/dist/types/errors.js +2 -8
  271. package/dist/types/errors.js.map +1 -1
  272. package/dist/types/index.d.ts +2 -1
  273. package/dist/types/index.js +1 -2
  274. package/dist/types/index.js.map +1 -1
  275. package/dist/ui/components/ChatInterface.d.ts +1 -1
  276. package/dist/ui/index.d.ts +17 -21
  277. package/dist/ui/index.js +25 -22
  278. package/dist/ui/index.js.map +1 -1
  279. package/dist/utils/config-validation/schema.d.ts +15 -15
  280. package/dist/utils/head-tail-truncation.d.ts +34 -0
  281. package/dist/utils/head-tail-truncation.js +98 -0
  282. package/dist/utils/head-tail-truncation.js.map +1 -0
  283. package/dist/utils/logger.js +3 -9
  284. package/dist/utils/logger.js.map +1 -1
  285. package/dist/utils/sanitize.d.ts +5 -0
  286. package/dist/utils/sanitize.js +19 -0
  287. package/dist/utils/sanitize.js.map +1 -1
  288. package/dist/utils/settings-manager.js +4 -4
  289. package/dist/utils/settings-manager.js.map +1 -1
  290. package/dist/workflows/index.d.ts +4 -279
  291. package/dist/workflows/index.js +8 -822
  292. package/dist/workflows/index.js.map +1 -1
  293. package/dist/workflows/state-manager.d.ts +77 -0
  294. package/dist/workflows/state-manager.js +198 -0
  295. package/dist/workflows/state-manager.js.map +1 -0
  296. package/dist/workflows/step-manager.d.ts +39 -0
  297. package/dist/workflows/step-manager.js +196 -0
  298. package/dist/workflows/step-manager.js.map +1 -0
  299. package/dist/workflows/types.d.ts +87 -0
  300. package/dist/workflows/types.js +5 -0
  301. package/dist/workflows/types.js.map +1 -0
  302. package/dist/workflows/workflow-engine.d.ts +34 -0
  303. package/dist/workflows/workflow-engine.js +354 -0
  304. package/dist/workflows/workflow-engine.js.map +1 -0
  305. package/package.json +5 -1
@@ -1,248 +1,223 @@
1
1
  /**
2
- * Buddy Script Lexer
2
+ * Unified Script Lexer
3
3
  *
4
- * Tokenizes Buddy Script source code into tokens
4
+ * Based on FCS lexer (superset) with Buddy Script additions:
5
+ * - Question token type for ternary (separate from Colon)
6
+ * - Pipeline |> operator
7
+ * - Decorators @
8
+ * - Indent/Dedent tracking
9
+ * - Shebang handling
10
+ * - Template strings, hex/binary/scientific numbers
11
+ *
12
+ * Extensions: .bs (primary), .fcs (backward-compatible alias)
5
13
  */
6
- import { TokenType } from './types.js';
7
- const KEYWORDS = {
8
- 'let': TokenType.LET,
9
- 'const': TokenType.CONST,
10
- 'function': TokenType.FUNCTION,
11
- 'return': TokenType.RETURN,
12
- 'if': TokenType.IF,
13
- 'else': TokenType.ELSE,
14
- 'while': TokenType.WHILE,
15
- 'for': TokenType.FOR,
16
- 'in': TokenType.IN,
17
- 'break': TokenType.BREAK,
18
- 'continue': TokenType.CONTINUE,
19
- 'try': TokenType.TRY,
20
- 'catch': TokenType.CATCH,
21
- 'throw': TokenType.THROW,
22
- 'import': TokenType.IMPORT,
23
- 'export': TokenType.EXPORT,
24
- 'async': TokenType.ASYNC,
25
- 'await': TokenType.AWAIT,
26
- 'true': TokenType.BOOLEAN,
27
- 'false': TokenType.BOOLEAN,
28
- 'null': TokenType.NULL,
29
- };
30
- export class Lexer {
14
+ import { TokenType, FCS_KEYWORDS } from './types.js';
15
+ export class FCSLexer {
31
16
  source;
32
- tokens = [];
33
- current = 0;
17
+ position = 0;
34
18
  line = 1;
35
19
  column = 1;
20
+ tokens = [];
21
+ indentStack = [0];
36
22
  constructor(source) {
37
23
  this.source = source;
38
24
  }
39
25
  tokenize() {
26
+ this.tokens = [];
27
+ this.position = 0;
28
+ this.line = 1;
29
+ this.column = 1;
30
+ // Handle shebang
31
+ if (this.source.startsWith('#!')) {
32
+ while (!this.isAtEnd() && this.peek() !== '\n') {
33
+ this.advance();
34
+ }
35
+ if (!this.isAtEnd())
36
+ this.advance(); // Skip newline
37
+ }
38
+ // Handle initial indentation
39
+ this.handleIndentation();
40
40
  while (!this.isAtEnd()) {
41
- this.scanToken();
41
+ this.skipWhitespaceNotNewline();
42
+ if (this.isAtEnd())
43
+ break;
44
+ const token = this.nextToken();
45
+ if (token && token.type !== TokenType.Comment) {
46
+ this.tokens.push(token);
47
+ }
42
48
  }
43
- this.tokens.push({
44
- type: TokenType.EOF,
45
- value: null,
46
- line: this.line,
47
- column: this.column,
48
- });
49
+ // Add remaining dedents
50
+ while (this.indentStack.length > 1) {
51
+ this.indentStack.pop();
52
+ this.addToken(TokenType.Dedent, '');
53
+ }
54
+ this.addToken(TokenType.EOF, '');
49
55
  return this.tokens;
50
56
  }
51
- scanToken() {
52
- const char = this.advance();
53
- switch (char) {
54
- // Single character tokens
55
- case '(':
56
- this.addToken(TokenType.LPAREN, '(');
57
- break;
58
- case ')':
59
- this.addToken(TokenType.RPAREN, ')');
60
- break;
61
- case '{':
62
- this.addToken(TokenType.LBRACE, '{');
63
- break;
64
- case '}':
65
- this.addToken(TokenType.RBRACE, '}');
66
- break;
67
- case '[':
68
- this.addToken(TokenType.LBRACKET, '[');
69
- break;
70
- case ']':
71
- this.addToken(TokenType.RBRACKET, ']');
72
- break;
73
- case ',':
74
- this.addToken(TokenType.COMMA, ',');
75
- break;
76
- case '.':
77
- this.addToken(TokenType.DOT, '.');
78
- break;
79
- case ':':
80
- this.addToken(TokenType.COLON, ':');
81
- break;
82
- case ';':
83
- this.addToken(TokenType.SEMICOLON, ';');
84
- break;
85
- case '?':
86
- this.addToken(TokenType.QUESTION, '?');
87
- break;
88
- // Operators
57
+ nextToken() {
58
+ const startPos = this.position;
59
+ const startLine = this.line;
60
+ const startColumn = this.column;
61
+ const ch = this.advance();
62
+ // Comments
63
+ if (ch === '/' && this.peek() === '/') {
64
+ return this.scanLineComment();
65
+ }
66
+ if (ch === '/' && this.peek() === '*') {
67
+ return this.scanBlockComment();
68
+ }
69
+ if (ch === '#' && this.column > 1) {
70
+ return this.scanLineComment();
71
+ }
72
+ // Strings
73
+ if (ch === '"')
74
+ return this.scanString('"', startPos, startLine, startColumn);
75
+ if (ch === "'")
76
+ return this.scanString("'", startPos, startLine, startColumn);
77
+ if (ch === '`')
78
+ return this.scanTemplateString(startPos, startLine, startColumn);
79
+ // Numbers
80
+ if (this.isDigit(ch)) {
81
+ return this.scanNumber(startPos, startLine, startColumn);
82
+ }
83
+ // Identifiers and keywords
84
+ if (this.isAlpha(ch)) {
85
+ return this.scanIdentifier(startPos, startLine, startColumn);
86
+ }
87
+ // Decorators
88
+ if (ch === '@')
89
+ return this.scanDecorator(startPos, startLine, startColumn);
90
+ // Operators and punctuation
91
+ switch (ch) {
89
92
  case '+':
90
- if (this.match('=')) {
91
- this.addToken(TokenType.PLUS_ASSIGN, '+=');
92
- }
93
- else {
94
- this.addToken(TokenType.PLUS, '+');
95
- }
96
- break;
93
+ if (this.match('='))
94
+ return this.createToken(TokenType.PlusAssign, '+=', startPos, startLine, startColumn);
95
+ if (this.match('+'))
96
+ return this.createToken(TokenType.Plus, '++', startPos, startLine, startColumn);
97
+ return this.createToken(TokenType.Plus, '+', startPos, startLine, startColumn);
97
98
  case '-':
98
- if (this.match('=')) {
99
- this.addToken(TokenType.MINUS_ASSIGN, '-=');
100
- }
101
- else {
102
- this.addToken(TokenType.MINUS, '-');
103
- }
104
- break;
99
+ if (this.match('='))
100
+ return this.createToken(TokenType.MinusAssign, '-=', startPos, startLine, startColumn);
101
+ if (this.match('-'))
102
+ return this.createToken(TokenType.Minus, '--', startPos, startLine, startColumn);
103
+ if (this.match('>'))
104
+ return this.createToken(TokenType.Arrow, '->', startPos, startLine, startColumn);
105
+ return this.createToken(TokenType.Minus, '-', startPos, startLine, startColumn);
105
106
  case '*':
106
- if (this.match('*')) {
107
- this.addToken(TokenType.POWER, '**');
108
- }
109
- else {
110
- this.addToken(TokenType.MULTIPLY, '*');
111
- }
112
- break;
107
+ if (this.match('*'))
108
+ return this.createToken(TokenType.Power, '**', startPos, startLine, startColumn);
109
+ if (this.match('='))
110
+ return this.createToken(TokenType.MultiplyAssign, '*=', startPos, startLine, startColumn);
111
+ return this.createToken(TokenType.Multiply, '*', startPos, startLine, startColumn);
113
112
  case '/':
114
- if (this.match('/')) {
115
- // Single-line comment
116
- while (this.peek() !== '\n' && !this.isAtEnd()) {
117
- this.advance();
118
- }
119
- }
120
- else if (this.match('*')) {
121
- // Multi-line comment
122
- this.multiLineComment();
123
- }
124
- else {
125
- this.addToken(TokenType.DIVIDE, '/');
126
- }
127
- break;
113
+ if (this.match('='))
114
+ return this.createToken(TokenType.DivideAssign, '/=', startPos, startLine, startColumn);
115
+ return this.createToken(TokenType.Divide, '/', startPos, startLine, startColumn);
128
116
  case '%':
129
- this.addToken(TokenType.MODULO, '%');
130
- break;
117
+ return this.createToken(TokenType.Modulo, '%', startPos, startLine, startColumn);
131
118
  case '=':
132
- if (this.match('=')) {
133
- if (this.match('=')) {
134
- this.addToken(TokenType.EQUALS, '===');
135
- }
136
- else {
137
- this.addToken(TokenType.EQUALS, '==');
138
- }
139
- }
140
- else if (this.match('>')) {
141
- this.addToken(TokenType.ARROW, '=>');
142
- }
143
- else {
144
- this.addToken(TokenType.ASSIGN, '=');
145
- }
146
- break;
119
+ if (this.match('='))
120
+ return this.createToken(TokenType.Equal, '==', startPos, startLine, startColumn);
121
+ if (this.match('>'))
122
+ return this.createToken(TokenType.Arrow, '=>', startPos, startLine, startColumn);
123
+ return this.createToken(TokenType.Assign, '=', startPos, startLine, startColumn);
147
124
  case '!':
148
- if (this.match('=')) {
149
- if (this.match('=')) {
150
- this.addToken(TokenType.NOT_EQUALS, '!==');
151
- }
152
- else {
153
- this.addToken(TokenType.NOT_EQUALS, '!=');
154
- }
155
- }
156
- else {
157
- this.addToken(TokenType.NOT, '!');
158
- }
159
- break;
125
+ if (this.match('='))
126
+ return this.createToken(TokenType.NotEqual, '!=', startPos, startLine, startColumn);
127
+ return this.createToken(TokenType.Not, '!', startPos, startLine, startColumn);
160
128
  case '<':
161
- if (this.match('=')) {
162
- this.addToken(TokenType.LESS_EQUAL, '<=');
163
- }
164
- else {
165
- this.addToken(TokenType.LESS_THAN, '<');
166
- }
167
- break;
129
+ if (this.match('='))
130
+ return this.createToken(TokenType.LessEqual, '<=', startPos, startLine, startColumn);
131
+ return this.createToken(TokenType.Less, '<', startPos, startLine, startColumn);
168
132
  case '>':
169
- if (this.match('=')) {
170
- this.addToken(TokenType.GREATER_EQUAL, '>=');
171
- }
172
- else {
173
- this.addToken(TokenType.GREATER_THAN, '>');
174
- }
175
- break;
133
+ if (this.match('='))
134
+ return this.createToken(TokenType.GreaterEqual, '>=', startPos, startLine, startColumn);
135
+ return this.createToken(TokenType.Greater, '>', startPos, startLine, startColumn);
176
136
  case '&':
177
- if (this.match('&')) {
178
- this.addToken(TokenType.AND, '&&');
179
- }
180
- break;
137
+ if (this.match('&'))
138
+ return this.createToken(TokenType.And, '&&', startPos, startLine, startColumn);
139
+ return null;
181
140
  case '|':
182
- if (this.match('|')) {
183
- this.addToken(TokenType.OR, '||');
184
- }
185
- break;
186
- // Whitespace
187
- case ' ':
188
- case '\r':
189
- case '\t':
190
- // Ignore whitespace
191
- break;
141
+ if (this.match('|'))
142
+ return this.createToken(TokenType.Or, '||', startPos, startLine, startColumn);
143
+ if (this.match('>'))
144
+ return this.createToken(TokenType.Pipeline, '|>', startPos, startLine, startColumn);
145
+ return null;
146
+ // Delimiters
147
+ case '(':
148
+ return this.createToken(TokenType.LeftParen, '(', startPos, startLine, startColumn);
149
+ case ')':
150
+ return this.createToken(TokenType.RightParen, ')', startPos, startLine, startColumn);
151
+ case '{':
152
+ return this.createToken(TokenType.LeftBrace, '{', startPos, startLine, startColumn);
153
+ case '}':
154
+ return this.createToken(TokenType.RightBrace, '}', startPos, startLine, startColumn);
155
+ case '[':
156
+ return this.createToken(TokenType.LeftBracket, '[', startPos, startLine, startColumn);
157
+ case ']':
158
+ return this.createToken(TokenType.RightBracket, ']', startPos, startLine, startColumn);
159
+ // Punctuation
160
+ case ';':
161
+ return this.createToken(TokenType.Semicolon, ';', startPos, startLine, startColumn);
162
+ case ',':
163
+ return this.createToken(TokenType.Comma, ',', startPos, startLine, startColumn);
164
+ case '.':
165
+ return this.createToken(TokenType.Dot, '.', startPos, startLine, startColumn);
166
+ case ':':
167
+ return this.createToken(TokenType.Colon, ':', startPos, startLine, startColumn);
168
+ case '?':
169
+ // Use dedicated Question token type for ternary
170
+ return this.createToken(TokenType.Question, '?', startPos, startLine, startColumn);
171
+ // Newline
192
172
  case '\n':
193
- this.line++;
194
- this.column = 1;
195
- break;
196
- // Strings
197
- case '"':
198
- case "'":
199
- this.string(char);
200
- break;
201
- case '`':
202
- this.templateLiteral();
203
- break;
173
+ return this.handleNewline(startPos, startLine, startColumn);
204
174
  default:
205
- if (this.isDigit(char)) {
206
- this.number(char);
207
- }
208
- else if (this.isAlpha(char)) {
209
- this.identifier(char);
210
- }
211
- else {
212
- throw new Error(`Unexpected character '${char}' at line ${this.line}, column ${this.column}`);
213
- }
175
+ throw new Error(`Unexpected character '${ch}' at ${startLine}:${startColumn}`);
214
176
  }
215
177
  }
216
- string(quote) {
178
+ scanString(quote, startPos, startLine, startColumn) {
217
179
  let value = '';
218
- while (this.peek() !== quote && !this.isAtEnd()) {
219
- if (this.peek() === '\n') {
220
- this.line++;
221
- this.column = 1;
222
- }
180
+ while (!this.isAtEnd() && this.peek() !== quote) {
223
181
  if (this.peek() === '\\') {
224
- this.advance();
225
- const escaped = this.advance();
226
- switch (escaped) {
227
- case 'n':
228
- value += '\n';
229
- break;
230
- case 't':
231
- value += '\t';
232
- break;
233
- case 'r':
234
- value += '\r';
235
- break;
236
- case '\\':
237
- value += '\\';
238
- break;
239
- case '"':
240
- value += '"';
241
- break;
242
- case "'":
243
- value += "'";
244
- break;
245
- default: value += escaped;
182
+ this.advance(); // Skip backslash
183
+ if (!this.isAtEnd()) {
184
+ const escaped = this.advance();
185
+ switch (escaped) {
186
+ case 'n':
187
+ value += '\n';
188
+ break;
189
+ case 't':
190
+ value += '\t';
191
+ break;
192
+ case 'r':
193
+ value += '\r';
194
+ break;
195
+ case '\\':
196
+ value += '\\';
197
+ break;
198
+ case '"':
199
+ value += '"';
200
+ break;
201
+ case "'":
202
+ value += "'";
203
+ break;
204
+ default: value += escaped;
205
+ }
206
+ }
207
+ }
208
+ else if (this.peek() === '$' && this.peekNext() === '{') {
209
+ // String interpolation - keep as-is for parser
210
+ value += '${';
211
+ this.advance(); // $
212
+ this.advance(); // {
213
+ let braceCount = 1;
214
+ while (!this.isAtEnd() && braceCount > 0) {
215
+ const ch = this.advance();
216
+ value += ch;
217
+ if (ch === '{')
218
+ braceCount++;
219
+ else if (ch === '}')
220
+ braceCount--;
246
221
  }
247
222
  }
248
223
  else {
@@ -250,136 +225,248 @@ export class Lexer {
250
225
  }
251
226
  }
252
227
  if (this.isAtEnd()) {
253
- throw new Error(`Unterminated string at line ${this.line}`);
228
+ throw new Error(`Unterminated string at ${startLine}:${startColumn}`);
254
229
  }
255
230
  this.advance(); // Closing quote
256
- this.addToken(TokenType.STRING, value);
231
+ return this.createToken(TokenType.String, value, startPos, startLine, startColumn);
257
232
  }
258
- templateLiteral() {
259
- let value = '`';
260
- while (this.peek() !== '`' && !this.isAtEnd()) {
261
- if (this.peek() === '\n') {
262
- this.line++;
263
- this.column = 1;
233
+ scanTemplateString(startPos, startLine, startColumn) {
234
+ let value = '';
235
+ while (!this.isAtEnd() && this.peek() !== '`') {
236
+ if (this.peek() === '$' && this.peekNext() === '{') {
237
+ value += '${';
238
+ this.advance(); // $
239
+ this.advance(); // {
240
+ let braceCount = 1;
241
+ while (!this.isAtEnd() && braceCount > 0) {
242
+ const ch = this.advance();
243
+ value += ch;
244
+ if (ch === '{')
245
+ braceCount++;
246
+ else if (ch === '}')
247
+ braceCount--;
248
+ }
249
+ }
250
+ else {
251
+ const ch = this.advance();
252
+ value += ch;
253
+ if (ch === '\n') {
254
+ this.line++;
255
+ this.column = 1;
256
+ }
264
257
  }
265
- value += this.advance();
266
- }
267
- if (this.isAtEnd()) {
268
- throw new Error(`Unterminated template literal at line ${this.line}`);
269
258
  }
270
- value += this.advance(); // Closing backtick
271
- this.addToken(TokenType.STRING, value);
259
+ if (!this.isAtEnd())
260
+ this.advance(); // Closing backtick
261
+ return this.createToken(TokenType.String, value, startPos, startLine, startColumn);
272
262
  }
273
- number(first) {
274
- let value = first;
275
- while (this.isDigit(this.peek())) {
276
- value += this.advance();
277
- }
278
- // Decimal
279
- if (this.peek() === '.' && this.isDigit(this.peekNext())) {
280
- value += this.advance(); // .
281
- while (this.isDigit(this.peek())) {
263
+ scanNumber(startPos, startLine, startColumn) {
264
+ let value = this.source[startPos];
265
+ // Hex number
266
+ if (value === '0' && (this.peek() === 'x' || this.peek() === 'X')) {
267
+ value += this.advance(); // x
268
+ while (this.isHexDigit(this.peek())) {
282
269
  value += this.advance();
283
270
  }
284
271
  }
285
- // Scientific notation
286
- if (this.peek() === 'e' || this.peek() === 'E') {
287
- value += this.advance();
288
- if (this.peek() === '+' || this.peek() === '-') {
272
+ // Binary number
273
+ else if (value === '0' && (this.peek() === 'b' || this.peek() === 'B')) {
274
+ value += this.advance(); // b
275
+ while (this.peek() === '0' || this.peek() === '1') {
289
276
  value += this.advance();
290
277
  }
278
+ }
279
+ // Decimal number
280
+ else {
291
281
  while (this.isDigit(this.peek())) {
292
282
  value += this.advance();
293
283
  }
284
+ // Decimal part
285
+ if (this.peek() === '.' && this.isDigit(this.peekNext())) {
286
+ value += this.advance(); // .
287
+ while (this.isDigit(this.peek())) {
288
+ value += this.advance();
289
+ }
290
+ }
291
+ // Exponent
292
+ if (this.peek() === 'e' || this.peek() === 'E') {
293
+ value += this.advance(); // e
294
+ if (this.peek() === '+' || this.peek() === '-') {
295
+ value += this.advance();
296
+ }
297
+ while (this.isDigit(this.peek())) {
298
+ value += this.advance();
299
+ }
300
+ }
294
301
  }
295
- this.addToken(TokenType.NUMBER, parseFloat(value));
302
+ return this.createToken(TokenType.Number, value, startPos, startLine, startColumn);
296
303
  }
297
- identifier(first) {
298
- let value = first;
304
+ scanIdentifier(startPos, startLine, startColumn) {
305
+ let value = this.source[startPos];
299
306
  while (this.isAlphaNumeric(this.peek())) {
300
307
  value += this.advance();
301
308
  }
302
- const keyword = KEYWORDS[value];
303
- if (keyword) {
304
- if (keyword === TokenType.BOOLEAN) {
305
- this.addToken(TokenType.BOOLEAN, value === 'true');
309
+ // Check for keywords
310
+ if (FCS_KEYWORDS.has(value)) {
311
+ if (value === 'true' || value === 'false') {
312
+ return this.createToken(TokenType.Boolean, value, startPos, startLine, startColumn);
306
313
  }
307
- else if (keyword === TokenType.NULL) {
308
- this.addToken(TokenType.NULL, null);
314
+ if (value === 'null') {
315
+ return this.createToken(TokenType.Null, value, startPos, startLine, startColumn);
316
+ }
317
+ return this.createToken(TokenType.Keyword, value, startPos, startLine, startColumn);
318
+ }
319
+ return this.createToken(TokenType.Identifier, value, startPos, startLine, startColumn);
320
+ }
321
+ scanDecorator(startPos, startLine, startColumn) {
322
+ let value = '@';
323
+ while (this.isAlphaNumeric(this.peek())) {
324
+ value += this.advance();
325
+ }
326
+ return this.createToken(TokenType.Decorator, value, startPos, startLine, startColumn);
327
+ }
328
+ scanLineComment() {
329
+ while (!this.isAtEnd() && this.peek() !== '\n') {
330
+ this.advance();
331
+ }
332
+ return null; // Comments are skipped
333
+ }
334
+ scanBlockComment() {
335
+ this.advance(); // Skip *
336
+ while (!this.isAtEnd() && !(this.peek() === '*' && this.peekNext() === '/')) {
337
+ const ch = this.advance();
338
+ if (ch === '\n') {
339
+ this.line++;
340
+ this.column = 1;
341
+ }
342
+ }
343
+ if (!this.isAtEnd()) {
344
+ this.advance(); // *
345
+ this.advance(); // /
346
+ }
347
+ return null; // Comments are skipped
348
+ }
349
+ handleNewline(startPos, startLine, startColumn) {
350
+ // Count indentation on next line
351
+ let indentLevel = 0;
352
+ while (this.peek() === ' ' || this.peek() === '\t') {
353
+ if (this.advance() === '\t') {
354
+ indentLevel += 4;
309
355
  }
310
356
  else {
311
- this.addToken(keyword, value);
357
+ indentLevel++;
312
358
  }
313
359
  }
314
- else {
315
- this.addToken(TokenType.IDENTIFIER, value);
360
+ // Skip blank lines
361
+ if (this.peek() === '\n' || this.peek() === '#' || (this.peek() === '/' && this.peekNext() === '/')) {
362
+ return this.createToken(TokenType.Newline, '\n', startPos, startLine, startColumn);
363
+ }
364
+ const currentIndent = this.indentStack[this.indentStack.length - 1];
365
+ if (indentLevel > currentIndent) {
366
+ this.indentStack.push(indentLevel);
367
+ this.addToken(TokenType.Indent, '');
316
368
  }
369
+ else if (indentLevel < currentIndent) {
370
+ while (this.indentStack.length > 1 && this.indentStack[this.indentStack.length - 1] > indentLevel) {
371
+ this.indentStack.pop();
372
+ this.addToken(TokenType.Dedent, '');
373
+ }
374
+ }
375
+ return this.createToken(TokenType.Newline, '\n', startPos, startLine, startColumn);
317
376
  }
318
- multiLineComment() {
319
- while (!this.isAtEnd()) {
320
- if (this.peek() === '*' && this.peekNext() === '/') {
321
- this.advance(); // *
322
- this.advance(); // /
323
- return;
377
+ handleIndentation() {
378
+ let indentLevel = 0;
379
+ while (this.position < this.source.length && (this.source[this.position] === ' ' || this.source[this.position] === '\t')) {
380
+ if (this.source[this.position] === '\t') {
381
+ indentLevel += 4;
324
382
  }
325
- if (this.peek() === '\n') {
326
- this.line++;
327
- this.column = 1;
383
+ else {
384
+ indentLevel++;
328
385
  }
329
- this.advance();
386
+ this.position++;
387
+ this.column++;
388
+ }
389
+ if (indentLevel > 0) {
390
+ this.indentStack.push(indentLevel);
391
+ this.addToken(TokenType.Indent, '');
330
392
  }
331
- throw new Error(`Unterminated multi-line comment at line ${this.line}`);
393
+ }
394
+ // Helper methods
395
+ isAtEnd() {
396
+ return this.position >= this.source.length;
332
397
  }
333
398
  advance() {
334
- const char = this.source[this.current];
335
- this.current++;
399
+ if (this.isAtEnd())
400
+ return '\0';
401
+ const ch = this.source[this.position++];
336
402
  this.column++;
337
- return char;
403
+ if (ch === '\n') {
404
+ this.line++;
405
+ this.column = 1;
406
+ }
407
+ return ch;
408
+ }
409
+ peek() {
410
+ return this.isAtEnd() ? '\0' : this.source[this.position];
411
+ }
412
+ peekNext() {
413
+ return this.position + 1 >= this.source.length ? '\0' : this.source[this.position + 1];
338
414
  }
339
415
  match(expected) {
340
- if (this.isAtEnd())
341
- return false;
342
- if (this.source[this.current] !== expected)
416
+ if (this.isAtEnd() || this.source[this.position] !== expected) {
343
417
  return false;
344
- this.current++;
418
+ }
419
+ this.position++;
345
420
  this.column++;
346
421
  return true;
347
422
  }
348
- peek() {
349
- if (this.isAtEnd())
350
- return '\0';
351
- return this.source[this.current];
423
+ skipWhitespaceNotNewline() {
424
+ while (!this.isAtEnd()) {
425
+ const ch = this.peek();
426
+ if (ch === ' ' || ch === '\r' || ch === '\t') {
427
+ this.advance();
428
+ }
429
+ else {
430
+ break;
431
+ }
432
+ }
352
433
  }
353
- peekNext() {
354
- if (this.current + 1 >= this.source.length)
355
- return '\0';
356
- return this.source[this.current + 1];
434
+ isDigit(ch) {
435
+ return ch >= '0' && ch <= '9';
357
436
  }
358
- isAtEnd() {
359
- return this.current >= this.source.length;
437
+ isAlpha(ch) {
438
+ return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '_';
360
439
  }
361
- isDigit(char) {
362
- return char >= '0' && char <= '9';
440
+ isAlphaNumeric(ch) {
441
+ return this.isDigit(ch) || this.isAlpha(ch);
363
442
  }
364
- isAlpha(char) {
365
- return (char >= 'a' && char <= 'z') ||
366
- (char >= 'A' && char <= 'Z') ||
367
- char === '_' ||
368
- char === '$';
443
+ isHexDigit(ch) {
444
+ return this.isDigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
369
445
  }
370
- isAlphaNumeric(char) {
371
- return this.isAlpha(char) || this.isDigit(char);
446
+ createToken(type, value, pos, line, col) {
447
+ return {
448
+ type,
449
+ value,
450
+ position: pos,
451
+ line,
452
+ column: col,
453
+ length: this.position - pos,
454
+ };
372
455
  }
373
456
  addToken(type, value) {
374
457
  this.tokens.push({
375
458
  type,
376
459
  value,
460
+ position: this.position,
377
461
  line: this.line,
378
- column: this.column - String(value).length,
462
+ column: this.column,
463
+ length: 0,
379
464
  });
380
465
  }
381
466
  }
467
+ /** @deprecated Use FCSLexer instead */
468
+ export const Lexer = FCSLexer;
382
469
  export function tokenize(source) {
383
- return new Lexer(source).tokenize();
470
+ return new FCSLexer(source).tokenize();
384
471
  }
385
472
  //# sourceMappingURL=lexer.js.map