@ebowwa/coder 0.2.1 → 0.7.64
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.
- package/README.md +31 -32
- package/dist/core/__tests__/permissions.test.d.ts +12 -0
- package/dist/core/__tests__/permissions.test.d.ts.map +1 -0
- package/dist/core/__tests__/permissions.test.js +851 -0
- package/dist/core/agent-loop/__tests__/compaction.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/compaction.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/compaction.test.js +209 -0
- package/dist/core/agent-loop/__tests__/formatters.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/formatters.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/formatters.test.js +195 -0
- package/dist/core/agent-loop/__tests__/index.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/index.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/index.test.js +121 -0
- package/dist/core/agent-loop/__tests__/loop-state.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/loop-state.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/loop-state.test.js +340 -0
- package/dist/core/agent-loop/__tests__/message-builder.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/message-builder.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/message-builder.test.js +178 -0
- package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts +5 -0
- package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts.map +1 -0
- package/dist/core/agent-loop/__tests__/tool-executor.test.js +331 -0
- package/dist/core/agent-loop/compaction.d.ts +39 -0
- package/dist/core/agent-loop/compaction.d.ts.map +1 -0
- package/dist/core/agent-loop/compaction.js +51 -0
- package/dist/core/agent-loop/formatters.d.ts +21 -0
- package/dist/core/agent-loop/formatters.d.ts.map +1 -0
- package/dist/core/agent-loop/formatters.js +42 -0
- package/dist/core/agent-loop/index.d.ts +25 -0
- package/dist/core/agent-loop/index.d.ts.map +1 -0
- package/dist/core/agent-loop/index.js +83 -0
- package/dist/core/agent-loop/loop-state.d.ts +74 -0
- package/dist/core/agent-loop/loop-state.d.ts.map +1 -0
- package/dist/core/agent-loop/loop-state.js +147 -0
- package/dist/core/agent-loop/message-builder.d.ts +13 -0
- package/dist/core/agent-loop/message-builder.d.ts.map +1 -0
- package/dist/core/agent-loop/message-builder.js +49 -0
- package/dist/core/agent-loop/tool-executor.d.ts +23 -0
- package/dist/core/agent-loop/tool-executor.d.ts.map +1 -0
- package/dist/core/agent-loop/tool-executor.js +152 -0
- package/dist/core/agent-loop/turn-executor.d.ts +57 -0
- package/dist/core/agent-loop/turn-executor.d.ts.map +1 -0
- package/dist/core/agent-loop/turn-executor.js +124 -0
- package/dist/core/agent-loop/types.d.ts +141 -0
- package/dist/core/agent-loop/types.d.ts.map +1 -0
- package/dist/core/agent-loop/types.js +4 -0
- package/dist/core/agent-loop.d.ts +17 -0
- package/dist/core/agent-loop.d.ts.map +1 -0
- package/dist/core/agent-loop.js +16 -0
- package/dist/core/api-client-impl.d.ts +62 -0
- package/dist/core/api-client-impl.d.ts.map +1 -0
- package/dist/core/api-client-impl.js +479 -0
- package/dist/core/api-client.d.ts +6 -0
- package/dist/core/api-client.d.ts.map +1 -0
- package/dist/core/api-client.js +5 -0
- package/dist/core/checkpoints.d.ts +128 -0
- package/dist/core/checkpoints.d.ts.map +1 -0
- package/dist/core/checkpoints.js +438 -0
- package/dist/core/claude-md.d.ts +71 -0
- package/dist/core/claude-md.d.ts.map +1 -0
- package/dist/core/claude-md.js +198 -0
- package/dist/core/cognitive-security/hooks.d.ts +138 -0
- package/dist/core/cognitive-security/hooks.d.ts.map +1 -0
- package/dist/core/cognitive-security/hooks.js +389 -0
- package/dist/core/cognitive-security/index.d.ts +751 -0
- package/dist/core/cognitive-security/index.d.ts.map +1 -0
- package/dist/core/cognitive-security/index.js +1123 -0
- package/dist/core/cognitive-security/middleware.d.ts +136 -0
- package/dist/core/cognitive-security/middleware.d.ts.map +1 -0
- package/dist/core/cognitive-security/middleware.js +376 -0
- package/dist/core/config-loader.d.ts +127 -0
- package/dist/core/config-loader.d.ts.map +1 -0
- package/dist/core/config-loader.js +219 -0
- package/dist/core/context-compaction.d.ts +87 -0
- package/dist/core/context-compaction.d.ts.map +1 -0
- package/dist/core/context-compaction.js +428 -0
- package/dist/core/git-status.d.ts +25 -0
- package/dist/core/git-status.d.ts.map +1 -0
- package/dist/core/git-status.js +204 -0
- package/dist/core/image.d.ts +69 -0
- package/dist/core/image.d.ts.map +1 -0
- package/dist/core/image.js +290 -0
- package/dist/core/image.test.d.ts +2 -0
- package/dist/core/image.test.d.ts.map +1 -0
- package/dist/core/image.test.js +149 -0
- package/dist/core/models.d.ts +123 -0
- package/dist/core/models.d.ts.map +1 -0
- package/dist/core/models.js +325 -0
- package/dist/core/permissions.d.ts +81 -0
- package/dist/core/permissions.d.ts.map +1 -0
- package/dist/core/permissions.js +327 -0
- package/dist/core/retry.d.ts +25 -0
- package/dist/core/retry.d.ts.map +1 -0
- package/dist/core/retry.js +121 -0
- package/dist/core/session-store.d.ts +9 -0
- package/dist/core/session-store.d.ts.map +1 -0
- package/dist/core/session-store.js +10 -0
- package/dist/core/sessions/export.d.ts +47 -0
- package/dist/core/sessions/export.d.ts.map +1 -0
- package/dist/core/sessions/export.js +256 -0
- package/dist/core/sessions/index.d.ts +132 -0
- package/dist/core/sessions/index.d.ts.map +1 -0
- package/dist/core/sessions/index.js +442 -0
- package/dist/core/sessions/metadata.d.ts +77 -0
- package/dist/core/sessions/metadata.d.ts.map +1 -0
- package/dist/core/sessions/metadata.js +233 -0
- package/dist/core/sessions/persistence.d.ts +72 -0
- package/dist/core/sessions/persistence.d.ts.map +1 -0
- package/dist/core/sessions/persistence.js +201 -0
- package/dist/core/sessions/types.d.ts +110 -0
- package/dist/core/sessions/types.d.ts.map +1 -0
- package/dist/core/sessions/types.js +4 -0
- package/dist/core/stream-highlighter.d.ts +18 -0
- package/dist/core/stream-highlighter.d.ts.map +1 -0
- package/dist/core/stream-highlighter.js +916 -0
- package/dist/core/system-reminders.d.ts +89 -0
- package/dist/core/system-reminders.d.ts.map +1 -0
- package/dist/core/system-reminders.js +285 -0
- package/dist/ecosystem/hooks/__tests__/index.test.d.ts +5 -0
- package/dist/ecosystem/hooks/__tests__/index.test.d.ts.map +1 -0
- package/dist/ecosystem/hooks/__tests__/index.test.js +458 -0
- package/dist/ecosystem/hooks/index.d.ts +59 -0
- package/dist/ecosystem/hooks/index.d.ts.map +1 -0
- package/dist/ecosystem/hooks/index.js +294 -0
- package/dist/ecosystem/hooks/prompt-evaluator.d.ts +32 -0
- package/dist/ecosystem/hooks/prompt-evaluator.d.ts.map +1 -0
- package/dist/ecosystem/hooks/prompt-evaluator.js +229 -0
- package/dist/ecosystem/skills/index.d.ts +55 -0
- package/dist/ecosystem/skills/index.d.ts.map +1 -0
- package/dist/ecosystem/skills/index.js +258 -0
- package/dist/ecosystem/tools/__tests__/index.test.d.ts +7 -0
- package/dist/ecosystem/tools/__tests__/index.test.d.ts.map +1 -0
- package/dist/ecosystem/tools/__tests__/index.test.js +856 -0
- package/dist/ecosystem/tools/index.d.ts +24 -0
- package/dist/ecosystem/tools/index.d.ts.map +1 -0
- package/dist/ecosystem/tools/index.js +1709 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -2
- package/dist/interfaces/mcp/client.d.ts +40 -0
- package/dist/interfaces/mcp/client.d.ts.map +1 -0
- package/dist/interfaces/mcp/client.js +309 -0
- package/dist/interfaces/ui/index.d.ts +36 -0
- package/dist/interfaces/ui/index.d.ts.map +1 -0
- package/dist/interfaces/ui/index.js +61 -0
- package/dist/interfaces/ui/spinner.d.ts +140 -0
- package/dist/interfaces/ui/spinner.d.ts.map +1 -0
- package/dist/interfaces/ui/spinner.js +342 -0
- package/dist/interfaces/ui/terminal/cli/index.d.ts +12 -0
- package/dist/interfaces/ui/terminal/cli/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/cli/index.js +167 -0
- package/dist/interfaces/ui/terminal/shared/args.d.ts +39 -0
- package/dist/interfaces/ui/terminal/shared/args.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/args.js +176 -0
- package/dist/interfaces/ui/terminal/shared/index.d.ts +11 -0
- package/dist/interfaces/ui/terminal/shared/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/index.js +16 -0
- package/dist/interfaces/ui/terminal/shared/loading-state.d.ts +124 -0
- package/dist/interfaces/ui/terminal/shared/loading-state.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/loading-state.js +246 -0
- package/dist/interfaces/ui/terminal/shared/query.d.ts +22 -0
- package/dist/interfaces/ui/terminal/shared/query.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/query.js +100 -0
- package/dist/interfaces/ui/terminal/shared/setup.d.ts +33 -0
- package/dist/interfaces/ui/terminal/shared/setup.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/setup.js +226 -0
- package/dist/interfaces/ui/terminal/shared/status-line.d.ts +117 -0
- package/dist/interfaces/ui/terminal/shared/status-line.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/status-line.js +267 -0
- package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts +38 -0
- package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/shared/system-prompt.js +102 -0
- package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts +39 -0
- package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/HelpPanel.js +215 -0
- package/dist/interfaces/ui/terminal/tui/InputContext.d.ts +91 -0
- package/dist/interfaces/ui/terminal/tui/InputContext.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/InputContext.js +154 -0
- package/dist/interfaces/ui/terminal/tui/InputField.d.ts +18 -0
- package/dist/interfaces/ui/terminal/tui/InputField.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/InputField.js +41 -0
- package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts +16 -0
- package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/InteractiveTUI.js +451 -0
- package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts +10 -0
- package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/MessageArea.js +91 -0
- package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts +48 -0
- package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/MessageStore.js +151 -0
- package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts +9 -0
- package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/StatusBar.js +36 -0
- package/dist/interfaces/ui/terminal/tui/commands.d.ts +21 -0
- package/dist/interfaces/ui/terminal/tui/commands.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/commands.js +359 -0
- package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts +115 -0
- package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.js +306 -0
- package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts +92 -0
- package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/MultilineInput.js +399 -0
- package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts +59 -0
- package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/PaneManager.js +139 -0
- package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts +68 -0
- package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/Sidebar.js +340 -0
- package/dist/interfaces/ui/terminal/tui/components/index.d.ts +23 -0
- package/dist/interfaces/ui/terminal/tui/components/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/components/index.js +51 -0
- package/dist/interfaces/ui/terminal/tui/console.d.ts +20 -0
- package/dist/interfaces/ui/terminal/tui/console.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/console.js +46 -0
- package/dist/interfaces/ui/terminal/tui/index.d.ts +20 -0
- package/dist/interfaces/ui/terminal/tui/index.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/index.js +28 -0
- package/dist/interfaces/ui/terminal/tui/run.d.ts +13 -0
- package/dist/interfaces/ui/terminal/tui/run.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/run.js +31 -0
- package/dist/interfaces/ui/terminal/tui/spinner.d.ts +44 -0
- package/dist/interfaces/ui/terminal/tui/spinner.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/spinner.js +59 -0
- package/dist/interfaces/ui/terminal/tui/tui-app.d.ts +39 -0
- package/dist/interfaces/ui/terminal/tui/tui-app.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/tui-app.js +198 -0
- package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts +167 -0
- package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/tui-footer.js +330 -0
- package/dist/interfaces/ui/terminal/tui/types.d.ts +165 -0
- package/dist/interfaces/ui/terminal/tui/types.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/types.js +5 -0
- package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts +23 -0
- package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/useInputHandler.js +72 -0
- package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts +90 -0
- package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts.map +1 -0
- package/dist/interfaces/ui/terminal/tui/useNativeInput.js +188 -0
- package/dist/native/index.d.ts +480 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +1625 -0
- package/dist/teammates/index.d.ts +161 -0
- package/dist/teammates/index.d.ts.map +1 -0
- package/dist/teammates/index.js +827 -0
- package/dist/types/index.d.ts +482 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +52 -0
- package/native/README.md +5 -5
- package/native/index.darwin-arm64.node +0 -0
- package/native/index.node +0 -0
- package/native/package.json +4 -4
- package/package.json +33 -16
- package/packages/src/core/__tests__/permissions.test.ts +1091 -0
- package/packages/src/core/agent-loop/__tests__/compaction.test.ts +280 -0
- package/packages/src/core/agent-loop/__tests__/formatters.test.ts +234 -0
- package/packages/src/core/agent-loop/__tests__/index.test.ts +162 -0
- package/packages/src/core/agent-loop/__tests__/loop-state.test.ts +413 -0
- package/packages/src/core/agent-loop/__tests__/message-builder.test.ts +229 -0
- package/packages/src/core/agent-loop/__tests__/tool-executor.test.ts +457 -0
- package/packages/src/core/agent-loop/compaction.ts +88 -0
- package/packages/src/core/agent-loop/formatters.ts +50 -0
- package/packages/src/core/agent-loop/index.ts +135 -0
- package/packages/src/core/agent-loop/loop-state.ts +187 -0
- package/packages/src/core/agent-loop/message-builder.ts +62 -0
- package/packages/src/core/agent-loop/tool-executor.ts +211 -0
- package/packages/src/core/agent-loop/turn-executor.ts +222 -0
- package/packages/src/core/agent-loop/types.ts +148 -0
- package/packages/src/core/agent-loop.ts +18 -0
- package/packages/src/core/api-client-impl.ts +619 -0
- package/packages/src/core/api-client.ts +6 -0
- package/packages/src/core/checkpoints.ts +606 -0
- package/packages/src/core/claude-md.ts +272 -0
- package/packages/src/core/cognitive-security/hooks.ts +590 -0
- package/packages/src/core/cognitive-security/index.ts +2041 -0
- package/packages/src/core/cognitive-security/middleware.ts +536 -0
- package/packages/src/core/config-loader.ts +324 -0
- package/packages/src/core/context-compaction.ts +578 -0
- package/packages/src/core/git-status.ts +262 -0
- package/packages/src/core/image.test.ts +180 -0
- package/packages/src/core/image.ts +350 -0
- package/packages/src/core/lmdb.db +0 -0
- package/packages/src/core/lmdb.db-lock +0 -0
- package/packages/src/core/models.ts +430 -0
- package/packages/src/core/normalizers/todo +4 -0
- package/packages/src/core/permissions.ts +431 -0
- package/packages/src/core/retry.ts +170 -0
- package/packages/src/core/session-store.ts +36 -0
- package/packages/src/core/sessions/export.ts +329 -0
- package/packages/src/core/sessions/index.ts +587 -0
- package/packages/src/core/sessions/metadata.ts +309 -0
- package/packages/src/core/sessions/persistence.ts +244 -0
- package/packages/src/core/sessions/types.ts +169 -0
- package/packages/src/core/stream-highlighter.ts +1123 -0
- package/packages/src/core/system-reminders.ts +402 -0
- package/packages/src/core/todo +8 -0
- package/packages/src/ecosystem/hooks/__tests__/index.test.ts +561 -0
- package/packages/src/ecosystem/hooks/index.ts +341 -0
- package/packages/src/ecosystem/hooks/prompt-evaluator.ts +300 -0
- package/packages/src/ecosystem/skills/index.ts +295 -0
- package/packages/src/ecosystem/tools/__tests__/index.test.ts +1335 -0
- package/packages/src/ecosystem/tools/index.ts +1877 -0
- package/packages/src/index.ts +120 -0
- package/packages/src/interfaces/mcp/client.ts +389 -0
- package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
- package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
- package/packages/src/interfaces/ui/index.ts +161 -0
- package/packages/src/interfaces/ui/lmdb.db +0 -0
- package/packages/src/interfaces/ui/lmdb.db-lock +0 -0
- package/packages/src/interfaces/ui/spinner.ts +451 -0
- package/packages/src/interfaces/ui/terminal/cli/index.ts +228 -0
- package/packages/src/interfaces/ui/terminal/lmdb.db +0 -0
- package/packages/src/interfaces/ui/terminal/lmdb.db-lock +0 -0
- package/packages/src/interfaces/ui/terminal/shared/args.ts +222 -0
- package/packages/src/interfaces/ui/terminal/shared/index.ts +71 -0
- package/packages/src/interfaces/ui/terminal/shared/loading-state.ts +322 -0
- package/packages/src/interfaces/ui/terminal/shared/query.ts +146 -0
- package/packages/src/interfaces/ui/terminal/shared/setup.ts +295 -0
- package/packages/src/interfaces/ui/terminal/shared/status-line.ts +358 -0
- package/packages/src/interfaces/ui/terminal/shared/system-prompt.ts +146 -0
- package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +262 -0
- package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +232 -0
- package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +62 -0
- package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +537 -0
- package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +107 -0
- package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +240 -0
- package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +54 -0
- package/packages/src/interfaces/ui/terminal/tui/commands.ts +438 -0
- package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +584 -0
- package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +614 -0
- package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +333 -0
- package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +604 -0
- package/packages/src/interfaces/ui/terminal/tui/components/index.ts +118 -0
- package/packages/src/interfaces/ui/terminal/tui/console.ts +49 -0
- package/packages/src/interfaces/ui/terminal/tui/index.ts +90 -0
- package/packages/src/interfaces/ui/terminal/tui/run.tsx +42 -0
- package/packages/src/interfaces/ui/terminal/tui/spinner.ts +69 -0
- package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +390 -0
- package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +422 -0
- package/packages/src/interfaces/ui/terminal/tui/types.ts +186 -0
- package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +104 -0
- package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +239 -0
- package/packages/src/lmdb.db +0 -0
- package/packages/src/lmdb.db-lock +0 -0
- package/packages/src/native/index.ts +2345 -0
- package/packages/src/teammates/index.ts +982 -0
- package/packages/src/types/index.ts +722 -0
- package/dist/cli.js +0 -148
- package/dist/index-0pkak453.js +0 -136
- package/dist/index-0qd0x8b4.js +0 -110
- package/dist/index-0x3kprq6.js +0 -240
- package/dist/index-1eawy937.js +0 -308
- package/dist/index-24m2aygy.js +0 -240
- package/dist/index-29xcjnne.js +0 -280
- package/dist/index-2avyytn5.js +0 -349
- package/dist/index-4ms367ey.js +0 -136
- package/dist/index-4w2t3b0m.js +0 -240
- package/dist/index-4xfgd8nz.js +0 -261
- package/dist/index-5acjp9gc.js +0 -157
- package/dist/index-5s15hr56.js +0 -136
- package/dist/index-6e4wf341.js +0 -349
- package/dist/index-6fvnkedw.js +0 -240
- package/dist/index-6rqpmd4g.js +0 -128
- package/dist/index-77ckwnbm.js +0 -280
- package/dist/index-9knxy49k.js +0 -128
- package/dist/index-9zrnw4zx.js +0 -128
- package/dist/index-bk21w99v.js +0 -280
- package/dist/index-c41n76fv.js +0 -240
- package/dist/index-cb4ppjdt.js +0 -255
- package/dist/index-cfb2edt6.js +0 -240
- package/dist/index-cmfa38hh.js +0 -308
- package/dist/index-datjz8q1.js +0 -257
- package/dist/index-eadf4wvn.js +0 -240
- package/dist/index-em5k0m3z.js +0 -345
- package/dist/index-gh8r333a.js +0 -110
- package/dist/index-gkx6k2tr.js +0 -261
- package/dist/index-h5cabfks.js +0 -155
- package/dist/index-hcrpwyy3.js +0 -261
- package/dist/index-hk7fwwa8.js +0 -257
- package/dist/index-jb8cw7f8.js +0 -136
- package/dist/index-kbyw4th1.js +0 -347
- package/dist/index-kgj5gqnm.js +0 -345
- package/dist/index-mdf6xp1z.js +0 -255
- package/dist/index-mrhv8kvc.js +0 -280
- package/dist/index-mt4743dd.js +0 -161
- package/dist/index-qnwsg97q.js +0 -240
- package/dist/index-qwdy6x44.js +0 -261
- package/dist/index-rmj77261.js +0 -157
- package/dist/index-sbbw1a61.js +0 -349
- package/dist/index-svy5bcpn.js +0 -345
- package/dist/index-tvmy7tm9.js +0 -261
- package/dist/index-tzz4vzkj.js +0 -312
- package/dist/index-vz80zmhe.js +0 -110
- package/dist/index-wed2fk67.js +0 -240
- package/dist/index-wksgzz8e.js +0 -280
- package/dist/index-wn2m4wma.js +0 -240
- package/dist/index-xha05vjc.js +0 -257
- package/dist/index-yc6eh8p8.js +0 -136
- package/dist/index-ycjxx9ft.js +0 -240
- package/dist/index-z0gzd0fc.js +0 -110
- package/dist/index-z8cwtf8j.js +0 -240
- package/dist/index-zy5mtt00.js +0 -128
|
@@ -0,0 +1,1123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming text highlighter for code blocks and diffs
|
|
3
|
+
* Buffers text and highlights as content completes during streaming output
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { highlight_code, highlight_diff } from "../native/index.js";
|
|
7
|
+
|
|
8
|
+
// ANSI color codes - Base16 Ocean color scheme
|
|
9
|
+
const COLORS = {
|
|
10
|
+
reset: "\x1b[0m",
|
|
11
|
+
bold: "\x1b[1m",
|
|
12
|
+
dim: "\x1b[2m",
|
|
13
|
+
italic: "\x1b[3m",
|
|
14
|
+
underline: "\x1b[4m",
|
|
15
|
+
strikethrough: "\x1b[9m",
|
|
16
|
+
// Syntax colors
|
|
17
|
+
purple: "\x1b[38;2;180;142;173m", // keywords
|
|
18
|
+
blue: "\x1b[38;2;143;161;179m", // functions
|
|
19
|
+
green: "\x1b[38;2;163;190;140m", // strings, additions
|
|
20
|
+
orange: "\x1b[38;2;208;135;112m", // numbers
|
|
21
|
+
red: "\x1b[38;2;191;97;106m", // types, deletions
|
|
22
|
+
cyan: "\x1b[38;2;150;181;180m", // builtins
|
|
23
|
+
gray: "\x1b[38;2;192;197;206m", // default
|
|
24
|
+
yellow: "\x1b[38;2;235;203;139m", // warnings, highlights
|
|
25
|
+
comment: "\x1b[38;2;101;115;126m", // comments
|
|
26
|
+
// Markdown specific
|
|
27
|
+
header: "\x1b[38;2;143;161;179m\x1b[1m", // bold blue
|
|
28
|
+
link: "\x1b[38;2;150;181;180m\x1b[4m", // cyan underline
|
|
29
|
+
list: "\x1b[38;2;180;142;173m", // purple bullet
|
|
30
|
+
blockquote: "\x1b[38;2;101;115;126m\x1b[3m", // dim italic gray
|
|
31
|
+
hr: "\x1b[38;2;101;115;126m", // dim gray
|
|
32
|
+
tableBorder: "\x1b[38;2;143;161;179m", // blue for table borders
|
|
33
|
+
tableHeader: "\x1b[38;2;180;142;173m\x1b[1m", // bold purple for table headers
|
|
34
|
+
// Financial/Trading colors
|
|
35
|
+
money: "\x1b[38;2;163;190;140m", // green for money amounts
|
|
36
|
+
moneyLoss: "\x1b[38;2;191;97;106m", // red for losses
|
|
37
|
+
moneyGain: "\x1b[38;2;140;190;140m\x1b[1m", // bold green for gains
|
|
38
|
+
percentUp: "\x1b[38;2;163;190;140m", // green for positive %
|
|
39
|
+
percentDown: "\x1b[38;2;191;97;106m", // red for negative %
|
|
40
|
+
ticker: "\x1b[38;2;235;203;139m\x1b[1m", // bold yellow for stock tickers
|
|
41
|
+
crypto: "\x1b[38;2;247;140;108m", // orange for crypto
|
|
42
|
+
timestamp: "\x1b[38;2;101;115;126m", // dim gray for timestamps
|
|
43
|
+
math: "\x1b[38;2;180;142;173m", // purple for math operators
|
|
44
|
+
bullish: "\x1b[38;2;163;190;140m\x1b[1m", // bold green for bullish terms
|
|
45
|
+
bearish: "\x1b[38;2;191;97;106m\x1b[1m", // bold red for bearish terms
|
|
46
|
+
neutral: "\x1b[38;2;235;203;139m", // yellow for neutral terms
|
|
47
|
+
priceTarget: "\x1b[38;2;143;161;179m\x1b[4m", // blue underline for price targets
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Currency symbols and their names
|
|
51
|
+
const CURRENCY_SYMBOLS: Record<string, string> = {
|
|
52
|
+
'$': 'USD', '€': 'EUR', '£': 'GBP', '¥': 'JPY', '₹': 'INR',
|
|
53
|
+
'₽': 'RUB', '₩': 'KRW', '₿': 'BTC', 'Ξ': 'ETH', '₮': 'USDT',
|
|
54
|
+
'₳': 'ADA', '₴': 'UAH', '₸': 'KZT', '₺': 'TRY', '₼': 'AZN',
|
|
55
|
+
'₪': 'ILS', '₫': 'VND', '₭': 'LAK', '₱': 'PHP', '₲': 'PYG',
|
|
56
|
+
'₵': 'GHS', '₡': 'CRC', '₦': 'NGN', '₣': 'CHF', '₤': 'TRY',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Crypto symbols
|
|
60
|
+
const CRYPTO_SYMBOLS = new Set([
|
|
61
|
+
'BTC', 'ETH', 'USDT', 'BNB', 'XRP', 'SOL', 'USDC', 'ADA', 'DOGE', 'DOT',
|
|
62
|
+
'MATIC', 'LTC', 'SHIB', 'TRX', 'AVAX', 'LINK', 'ATOM', 'UNI', 'XMR', 'ETC',
|
|
63
|
+
'XLM', 'BCH', 'APT', 'NEAR', 'FIL', 'LDO', 'ARB', 'OP', 'HBAR', 'VET',
|
|
64
|
+
'ICP', 'QNT', 'AAVE', 'GRT', 'ALGO', 'SAND', 'MANA', 'AXS', 'FTM', 'EGLD',
|
|
65
|
+
'SUSHI', 'THETA', 'XTZ', 'EOS', 'FLOW', 'CHZ', 'SNX', 'LUNC', 'RUNE', 'KAVA',
|
|
66
|
+
'CRV', 'COMP', 'YFI', 'MKR', 'ZEC', 'DASH', 'NEO', 'WAVES', 'ENJ', 'BAT',
|
|
67
|
+
'1INCH', 'ANKR', 'CELO', 'CVC', 'DIA', 'GNO', 'KNC', 'OCEAN', 'OMG', 'REN',
|
|
68
|
+
'RLC', 'STORJ', 'SYN', 'UMA', 'ZRX', 'BLUR', 'DYDX', 'GMX', 'PEPE', 'BONK',
|
|
69
|
+
'WIF', 'FLOKI', 'INJ', 'SUI', 'SEI', 'TIA', 'IMX', 'ORDI', 'JUP', 'W',
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
// Stock ticker pattern (1-5 uppercase letters)
|
|
73
|
+
const TICKER_PATTERN = /\$?([A-Z]{1,5})(?=\s|\.|,|:|;|!|\?|$|\(|\)|\[|\]|-)/g;
|
|
74
|
+
|
|
75
|
+
// Trading terms
|
|
76
|
+
const BULLISH_TERMS = new Set([
|
|
77
|
+
'bull', 'bullish', 'long', 'buy', 'call', 'pump', 'moon', 'rocket', 'lambo',
|
|
78
|
+
'hold', 'hodl', 'diamond', 'hands', 'gains', 'profit', 'rally', 'surge',
|
|
79
|
+
'breakout', 'uptrend', 'accumulation', 'support', 'bounce', 'recovery',
|
|
80
|
+
'all-time high', 'ath', 'bagholder', 'whale buy', 'accumulation', 'undervalued',
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
const BEARISH_TERMS = new Set([
|
|
84
|
+
'bear', 'bearish', 'short', 'sell', 'put', 'dump', 'crash', 'dump', 'rekt',
|
|
85
|
+
'liquidated', 'loss', 'bleed', 'downtrend', 'breakdown', 'resistance',
|
|
86
|
+
'correction', 'dip', ' capitulation', 'panic sell', 'overvalued', 'bubble',
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
const NEUTRAL_TERMS = new Set([
|
|
90
|
+
'position', 'entry', 'exit', 'stop', 'limit', 'market', 'order', 'trade',
|
|
91
|
+
'volume', 'liquidity', 'spread', 'slippage', 'leverage', 'margin', 'futures',
|
|
92
|
+
'options', 'perpetual', 'funding', 'basis', 'arbitrage', 'hedge', 'delta',
|
|
93
|
+
'gamma', 'theta', 'vega', 'iv', 'hv', 'volatility', 'oi', 'open interest',
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
// Math operators and symbols
|
|
97
|
+
const MATH_PATTERNS = [
|
|
98
|
+
{ pattern: /[+\-×÷*\/%^=<>≤≥≠≈±√∫∑∏∂∆∇]/g, color: 'math' },
|
|
99
|
+
{ pattern: /\b(pi|π|e|phi|inf|infinity|NaN|null)\b/gi, color: 'orange' },
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Detects if content looks like a diff (unified diff format)
|
|
104
|
+
*/
|
|
105
|
+
function isDiffContent(content: string): boolean {
|
|
106
|
+
const lines = content.split('\n').filter(l => l.length > 0);
|
|
107
|
+
if (lines.length < 3) return false;
|
|
108
|
+
|
|
109
|
+
// Check for diff markers
|
|
110
|
+
let diffMarkers = 0;
|
|
111
|
+
for (const line of lines.slice(0, 10)) {
|
|
112
|
+
if (line.startsWith('+++') || line.startsWith('---') ||
|
|
113
|
+
line.startsWith('@@') || line.startsWith('diff ') ||
|
|
114
|
+
line.startsWith('index ')) {
|
|
115
|
+
diffMarkers++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Also check for +/- lines
|
|
120
|
+
let plusLines = 0;
|
|
121
|
+
let minusLines = 0;
|
|
122
|
+
for (const line of lines) {
|
|
123
|
+
if (line.startsWith('+') && !line.startsWith('+++')) plusLines++;
|
|
124
|
+
if (line.startsWith('-') && !line.startsWith('---')) minusLines++;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return diffMarkers >= 2 || (plusLines > 0 && minusLines > 0);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Highlight diff content with ANSI colors
|
|
132
|
+
* Supports unified diff, git diff, and context diff formats
|
|
133
|
+
*/
|
|
134
|
+
function highlightDiffContent(content: string): string {
|
|
135
|
+
const lines = content.split('\n');
|
|
136
|
+
const result: string[] = [];
|
|
137
|
+
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
if (line.startsWith('@@')) {
|
|
140
|
+
// Hunk header - cyan with function context
|
|
141
|
+
// Format: @@ -start,count +start,count @@ optional_function_name
|
|
142
|
+
const match = line.match(/^(@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@)(.*)$/);
|
|
143
|
+
if (match) {
|
|
144
|
+
result.push(`${COLORS.cyan}${match[1]}${COLORS.reset}${COLORS.dim}${match[2]}${COLORS.reset}`);
|
|
145
|
+
} else {
|
|
146
|
+
result.push(`${COLORS.cyan}${line}${COLORS.reset}`);
|
|
147
|
+
}
|
|
148
|
+
} else if (line.startsWith('+++') || line.startsWith('---')) {
|
|
149
|
+
// File headers - bold
|
|
150
|
+
const prefix = line.startsWith('+++') ? '+' : '-';
|
|
151
|
+
const rest = line.slice(3);
|
|
152
|
+
result.push(`${COLORS.bold}${prefix}${prefix}${prefix}${COLORS.reset}${COLORS.cyan}${rest}${COLORS.reset}`);
|
|
153
|
+
} else if (line.startsWith('diff --git')) {
|
|
154
|
+
// Git diff header
|
|
155
|
+
result.push(`${COLORS.dim}${line}${COLORS.reset}`);
|
|
156
|
+
} else if (line.startsWith('index ')) {
|
|
157
|
+
// Index line
|
|
158
|
+
result.push(`${COLORS.dim}${line}${COLORS.reset}`);
|
|
159
|
+
} else if (line.startsWith('new file') || line.startsWith('deleted file')) {
|
|
160
|
+
// New/deleted file markers
|
|
161
|
+
result.push(`${COLORS.yellow}${line}${COLORS.reset}`);
|
|
162
|
+
} else if (line.startsWith('Binary files')) {
|
|
163
|
+
// Binary file markers
|
|
164
|
+
result.push(`${COLORS.yellow}${line}${COLORS.reset}`);
|
|
165
|
+
} else if (line.startsWith('rename from') || line.startsWith('rename to')) {
|
|
166
|
+
// Rename markers
|
|
167
|
+
result.push(`${COLORS.purple}${line}${COLORS.reset}`);
|
|
168
|
+
} else if (line.startsWith('similarity index')) {
|
|
169
|
+
// Similarity index
|
|
170
|
+
result.push(`${COLORS.dim}${line}${COLORS.reset}`);
|
|
171
|
+
} else if (line.startsWith('+')) {
|
|
172
|
+
// Additions - green with brighter first char
|
|
173
|
+
if (line === '+') {
|
|
174
|
+
result.push(`${COLORS.green}+${COLORS.reset}`);
|
|
175
|
+
} else {
|
|
176
|
+
result.push(`${COLORS.green}+${line.slice(1)}${COLORS.reset}`);
|
|
177
|
+
}
|
|
178
|
+
} else if (line.startsWith('-')) {
|
|
179
|
+
// Deletions - red
|
|
180
|
+
if (line === '-') {
|
|
181
|
+
result.push(`${COLORS.red}-${COLORS.reset}`);
|
|
182
|
+
} else {
|
|
183
|
+
result.push(`${COLORS.red}-${line.slice(1)}${COLORS.reset}`);
|
|
184
|
+
}
|
|
185
|
+
} else if (line.startsWith(' ') || line === '') {
|
|
186
|
+
// Context lines - dim
|
|
187
|
+
result.push(`${COLORS.dim}${line}${COLORS.reset}`);
|
|
188
|
+
} else if (line.match(/^(={67}|>{67}|<{67})/)) {
|
|
189
|
+
// Context diff separators
|
|
190
|
+
result.push(`${COLORS.dim}${line}${COLORS.reset}`);
|
|
191
|
+
} else if (line.startsWith('***************')) {
|
|
192
|
+
// Context diff hunk marker
|
|
193
|
+
result.push(`${COLORS.cyan}${line}${COLORS.reset}`);
|
|
194
|
+
} else if (line.startsWith('*** ') || line.startsWith('--- ')) {
|
|
195
|
+
// Context diff file markers (at line start, not diff markers)
|
|
196
|
+
result.push(`${COLORS.cyan}${line}${COLORS.reset}`);
|
|
197
|
+
} else {
|
|
198
|
+
result.push(line);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return result.join('\n');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Highlight inline code with a subtle color
|
|
207
|
+
*/
|
|
208
|
+
function highlightInlineCode(text: string): string {
|
|
209
|
+
// Skip if we're inside a code block
|
|
210
|
+
if (text.includes('```')) {
|
|
211
|
+
return text;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Match inline code with single backticks
|
|
215
|
+
// Also handle escaped backticks and nested code
|
|
216
|
+
return text
|
|
217
|
+
// Single backtick inline code
|
|
218
|
+
.replace(/`([^`\n]+)`/g, (_, code) => {
|
|
219
|
+
return `${COLORS.dim}\`${COLORS.orange}${code}${COLORS.dim}\`${COLORS.reset}`;
|
|
220
|
+
})
|
|
221
|
+
// Double backtick inline code (allows single backticks inside)
|
|
222
|
+
.replace(/``([^`\n]+)``/g, (_, code) => {
|
|
223
|
+
return `${COLORS.dim}\`\`${COLORS.orange}${code}${COLORS.dim}\`\`${COLORS.reset}`;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Highlight URLs that aren't already part of markdown links
|
|
229
|
+
*/
|
|
230
|
+
function highlightUrls(text: string): string {
|
|
231
|
+
// Skip if already in a markdown link
|
|
232
|
+
if (/\[[^\]]+\]\([^)]+\)/.test(text)) {
|
|
233
|
+
return text;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Match bare URLs (http:// or https://)
|
|
237
|
+
return text.replace(
|
|
238
|
+
/(https?:\/\/[^\s<>\[\]()]+)/g,
|
|
239
|
+
`${COLORS.link}$1${COLORS.reset}`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Highlight emoji shortcodes :emoji:
|
|
245
|
+
*/
|
|
246
|
+
function highlightEmoji(text: string): string {
|
|
247
|
+
return text.replace(/:([a-z0-9_+-]+):/g, (_, name) => {
|
|
248
|
+
return `${COLORS.yellow}:${name}:${COLORS.reset}`;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ===== Financial/Trading Highlighting =====
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Format large numbers with K, M, B, T suffixes
|
|
256
|
+
*/
|
|
257
|
+
function formatLargeNumber(num: number): string {
|
|
258
|
+
if (num >= 1e12) return (num / 1e12).toFixed(1) + 'T';
|
|
259
|
+
if (num >= 1e9) return (num / 1e9).toFixed(1) + 'B';
|
|
260
|
+
if (num >= 1e6) return (num / 1e6).toFixed(1) + 'M';
|
|
261
|
+
if (num >= 1e3) return (num / 1e3).toFixed(1) + 'K';
|
|
262
|
+
return num.toString();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Highlight currency amounts ($100, €50, ¥1000, etc.)
|
|
267
|
+
*/
|
|
268
|
+
function highlightCurrency(text: string): string {
|
|
269
|
+
// Skip if inside code block or has ANSI codes
|
|
270
|
+
if (text.includes('```') || text.includes('\x1b[')) return text;
|
|
271
|
+
|
|
272
|
+
// Currency symbols pattern - match currency symbol followed by number
|
|
273
|
+
const currencyPattern = new RegExp(
|
|
274
|
+
`([${Object.keys(CURRENCY_SYMBOLS).join('')}])\\s?([\\d,]+(?:\\.\\d{1,8})?)`,
|
|
275
|
+
'g'
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
let result = text.replace(currencyPattern, (_, symbol, amount) => {
|
|
279
|
+
const currency = CURRENCY_SYMBOLS[symbol] || '';
|
|
280
|
+
return `${COLORS.money}${symbol}${amount}${COLORS.reset}`;
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Named currency formats (USD 100, EUR 50)
|
|
284
|
+
result = result.replace(
|
|
285
|
+
/\b(USD|EUR|GBP|JPY|CAD|AUD|CHF|CNY|INR|MXN|BRL|KRW|SGD|HKD|NOK|SEK|DKK|NZD|ZAR|RUB|TRY|PLN|THB|IDR|MYR|PHP|VND)\s+([\d,]+(?:\.\d{1,8})?)/gi,
|
|
286
|
+
(_, currency, amount) => `${COLORS.money}${currency} ${amount}${COLORS.reset}`
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Highlight cryptocurrency amounts and symbols
|
|
294
|
+
*/
|
|
295
|
+
function highlightCrypto(text: string): string {
|
|
296
|
+
// Skip if inside code block
|
|
297
|
+
if (text.includes('```')) return text;
|
|
298
|
+
|
|
299
|
+
let result = text;
|
|
300
|
+
|
|
301
|
+
// Crypto amounts with symbol (0.5 BTC, 10 ETH, etc.)
|
|
302
|
+
const cryptoPattern = new RegExp(
|
|
303
|
+
`([\\d,]+(?:\\.\\d{1,8})?)\\s*(${Array.from(CRYPTO_SYMBOLS).join('|')})(?=\\s|\\.|\,|:|;|!|\\?|$)`,
|
|
304
|
+
'gi'
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
result = result.replace(cryptoPattern, (_, amount, symbol) => {
|
|
308
|
+
const upperSymbol = symbol.toUpperCase();
|
|
309
|
+
return `${COLORS.crypto}${amount} ${upperSymbol}${COLORS.reset}`;
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Satoshi amounts
|
|
313
|
+
result = result.replace(
|
|
314
|
+
/([\d,]+(?:\.\d{1,8})?)\s*(sats?|satoshis?)(?=\s|\.|,|:|;|!|\?|$)/gi,
|
|
315
|
+
(_, amount, unit) => {
|
|
316
|
+
return `${COLORS.crypto}${amount} ${unit.toLowerCase()}${COLORS.reset}`;
|
|
317
|
+
}
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
// Gwei amounts (gas prices)
|
|
321
|
+
result = result.replace(
|
|
322
|
+
/([\d,]+(?:\.\d{1,2})?)\s*gwei(?=\s|\.|,|:|;|!|\?|$)/gi,
|
|
323
|
+
(_, amount) => {
|
|
324
|
+
return `${COLORS.crypto}${amount} gwei${COLORS.reset}`;
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
// Wei amounts
|
|
329
|
+
result = result.replace(
|
|
330
|
+
/([\d,]+)\s*wei(?=\s|\.|,|:|;|!|\?|$)/gi,
|
|
331
|
+
(_, amount) => {
|
|
332
|
+
return `${COLORS.dim}${amount} wei${COLORS.reset}`;
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Highlight percentages with color based on positive/negative
|
|
341
|
+
*/
|
|
342
|
+
function highlightPercentages(text: string): string {
|
|
343
|
+
// Skip if inside code block or already has ANSI codes
|
|
344
|
+
if (text.includes('```') || text.includes('\x1b[')) return text;
|
|
345
|
+
|
|
346
|
+
let result = text;
|
|
347
|
+
|
|
348
|
+
// Positive percentages (+5.2%, +10%) - must have space or start before
|
|
349
|
+
result = result.replace(
|
|
350
|
+
/(^|\s)\+([\d,]+(?:\.\d{1,4})?)\s*%/gm,
|
|
351
|
+
(_, prefix, num) => `${prefix}${COLORS.percentUp}+${num}%${COLORS.reset}`
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
// Negative percentages (-5.2%, -10%) - must have space or start before
|
|
355
|
+
result = result.replace(
|
|
356
|
+
/(^|\s)-([\d,]+(?:\.\d{1,4})?)\s*%/gm,
|
|
357
|
+
(_, prefix, num) => `${prefix}${COLORS.percentDown}-${num}%${COLORS.reset}`
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// Neutral percentages (5.2%, 10%) - must not be part of +/-
|
|
361
|
+
result = result.replace(
|
|
362
|
+
/(^|\s)([\d,]+(?:\.\d{1,4})?)\s*%/gm,
|
|
363
|
+
(_, prefix, num) => `${prefix}${COLORS.orange}${num}%${COLORS.reset}`
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
return result;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Highlight stock tickers ($AAPL, $TSLA, etc.)
|
|
371
|
+
*/
|
|
372
|
+
function highlightTickers(text: string): string {
|
|
373
|
+
// Skip if inside code block or already has ANSI codes
|
|
374
|
+
if (text.includes('```') || text.includes('\x1b[')) return text;
|
|
375
|
+
|
|
376
|
+
// Match $TICKER with word boundaries - must have $ prefix and be 1-5 uppercase letters
|
|
377
|
+
return text.replace(
|
|
378
|
+
/(?<![a-zA-Z0-9])\$([A-Z]{1,5})(?![a-zA-Z0-9])/g,
|
|
379
|
+
(_, ticker) => `${COLORS.ticker}$${ticker}${COLORS.reset}`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Highlight trading terms (bullish/bearish/neutral)
|
|
385
|
+
*/
|
|
386
|
+
function highlightTradingTerms(text: string): string {
|
|
387
|
+
// Skip if inside code block or already has ANSI codes
|
|
388
|
+
if (text.includes('```') || text.includes('\x1b[')) return text;
|
|
389
|
+
|
|
390
|
+
let result = text;
|
|
391
|
+
|
|
392
|
+
// Bullish terms - whole word matches only
|
|
393
|
+
const bullishRegex = new RegExp(
|
|
394
|
+
`\\b(${Array.from(BULLISH_TERMS).join('|')})\\b`,
|
|
395
|
+
'gi'
|
|
396
|
+
);
|
|
397
|
+
result = result.replace(bullishRegex, (match) => {
|
|
398
|
+
return `${COLORS.bullish}${match}${COLORS.reset}`;
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Bearish terms - whole word matches only
|
|
402
|
+
const bearishRegex = new RegExp(
|
|
403
|
+
`\\b(${Array.from(BEARISH_TERMS).join('|')})\\b`,
|
|
404
|
+
'gi'
|
|
405
|
+
);
|
|
406
|
+
result = result.replace(bearishRegex, (match) => {
|
|
407
|
+
return `${COLORS.bearish}${match}${COLORS.reset}`;
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
return result;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Highlight price targets and ranges
|
|
415
|
+
*/
|
|
416
|
+
function highlightPriceTargets(text: string): string {
|
|
417
|
+
// Skip if inside code block
|
|
418
|
+
if (text.includes('```')) return text;
|
|
419
|
+
|
|
420
|
+
let result = text;
|
|
421
|
+
|
|
422
|
+
// Price targets ($100 target, PT: $150, etc.)
|
|
423
|
+
result = result.replace(
|
|
424
|
+
/\b(?:PT|price target|target|TP|take profit)\s*:?\s*([\$€£¥]?\s*[\d,]+(?:\.\d{1,4})?)/gi,
|
|
425
|
+
(_, price) => `${COLORS.priceTarget}PT: ${price}${COLORS.reset}`
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
// Stop loss levels
|
|
429
|
+
result = result.replace(
|
|
430
|
+
/\b(?:SL|stop loss|stop)\s*:?\s*([\$€£¥]?\s*[\d,]+(?:\.\d{1,4})?)/gi,
|
|
431
|
+
(_, price) => `${COLORS.red}SL: ${price}${COLORS.reset}`
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
// Entry levels
|
|
435
|
+
result = result.replace(
|
|
436
|
+
/\b(?:entry|enter|buy in)\s*:?\s*([\$€£¥]?\s*[\d,]+(?:\.\d{1,4})?)/gi,
|
|
437
|
+
(_, price) => `${COLORS.green}Entry: ${price}${COLORS.reset}`
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
// Price ranges ($100-$150, 50-75)
|
|
441
|
+
result = result.replace(
|
|
442
|
+
/([\$€£¥]?\s*[\d,]+(?:\.\d{1,4})?)\s*[-–]\s*([\$€£¥]?\s*[\d,]+(?:\.\d{1,4})?)/g,
|
|
443
|
+
(_, low, high) => `${COLORS.money}${low}-${high}${COLORS.reset}`
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Highlight timestamps and dates
|
|
451
|
+
*/
|
|
452
|
+
function highlightTimestamps(text: string): string {
|
|
453
|
+
// Skip if inside code block or already has ANSI codes
|
|
454
|
+
if (text.includes('```') || text.includes('\x1b[')) return text;
|
|
455
|
+
|
|
456
|
+
let result = text;
|
|
457
|
+
|
|
458
|
+
// ISO dates (2024-01-15) - specific pattern
|
|
459
|
+
result = result.replace(
|
|
460
|
+
/\b(\d{4}-\d{2}-\d{2})\b/g,
|
|
461
|
+
(_, date) => `${COLORS.timestamp}${date}${COLORS.reset}`
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
// ISO datetime with T (2024-01-15T14:30:00Z)
|
|
465
|
+
result = result.replace(
|
|
466
|
+
/\b(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(Z)?\b/g,
|
|
467
|
+
(_, datetime, z) => `${COLORS.timestamp}${datetime}${z || ''}${COLORS.reset}`
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
// Time only (14:30:00) - must have word boundary
|
|
471
|
+
result = result.replace(
|
|
472
|
+
/\b(\d{2}:\d{2}:\d{2})\b/g,
|
|
473
|
+
(_, time) => `${COLORS.timestamp}${time}${COLORS.reset}`
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
// Relative times (1h ago, 2d ago, etc.)
|
|
477
|
+
result = result.replace(
|
|
478
|
+
/\b(\d+)\s*(second|minute|hour|day|week|month|year)s?\s+ago\b/gi,
|
|
479
|
+
(_, num, unit) => `${COLORS.timestamp}${num} ${unit}s ago${COLORS.reset}`
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
return result;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Highlight mathematical expressions
|
|
487
|
+
*/
|
|
488
|
+
function highlightMath(text: string): string {
|
|
489
|
+
// Skip if inside code block or has ANSI codes
|
|
490
|
+
if (text.includes('```') || text.includes('\x1b[')) return text;
|
|
491
|
+
|
|
492
|
+
let result = text;
|
|
493
|
+
|
|
494
|
+
// Mathematical constants - be specific with word boundaries
|
|
495
|
+
result = result.replace(
|
|
496
|
+
/\b(pi|π|euler|infinity|inf)\b/gi,
|
|
497
|
+
(_, constant) => `${COLORS.orange}${constant}${COLORS.reset}`
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
// Exponents (x^2, 10^6) - only in math contexts
|
|
501
|
+
result = result.replace(
|
|
502
|
+
/(\d)\^(\d+)/g,
|
|
503
|
+
(_, base, exp) => `${COLORS.math}${base}^${exp}${COLORS.reset}`
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
// Scientific notation (1.5e10, 2E-5)
|
|
507
|
+
result = result.replace(
|
|
508
|
+
/(\d+\.\d+)[eE]([+-]?\d+)/g,
|
|
509
|
+
(_, mantissa, exp) => `${COLORS.orange}${mantissa}e${exp}${COLORS.reset}`
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
return result;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Highlight P/L (profit/loss) statements
|
|
517
|
+
*/
|
|
518
|
+
function highlightPL(text: string): string {
|
|
519
|
+
// Skip if inside code block
|
|
520
|
+
if (text.includes('```')) return text;
|
|
521
|
+
|
|
522
|
+
let result = text;
|
|
523
|
+
|
|
524
|
+
// P/L with amounts
|
|
525
|
+
result = result.replace(
|
|
526
|
+
/\b(?:P\/L|PnL|profit\/loss|PL)\s*:?\s*([+\-]?[\$€£¥]?\s*[\d,]+(?:\.\d{1,4})?)/gi,
|
|
527
|
+
(_, amount) => {
|
|
528
|
+
const isPositive = !amount.startsWith('-');
|
|
529
|
+
const color = isPositive ? COLORS.moneyGain : COLORS.moneyLoss;
|
|
530
|
+
return `${color}P/L: ${amount}${COLORS.reset}`;
|
|
531
|
+
}
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
// ROI percentages
|
|
535
|
+
result = result.replace(
|
|
536
|
+
/\b(?:ROI|return)\s*:?\s*([+\-]?[\d,]+(?:\.\d{1,2})?\s*%)/gi,
|
|
537
|
+
(_, percent) => {
|
|
538
|
+
const isPositive = !percent.startsWith('-');
|
|
539
|
+
const color = isPositive ? COLORS.percentUp : COLORS.percentDown;
|
|
540
|
+
return `${color}ROI: ${percent}${COLORS.reset}`;
|
|
541
|
+
}
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
// Unrealized/Realized P/L
|
|
545
|
+
result = result.replace(
|
|
546
|
+
/\b(unrealized|realized)\s+(p\/l|pnl|profit|loss)\s*:?\s*([+\-]?[\$€£¥]?\s*[\d,]+(?:\.\d{1,4})?)/gi,
|
|
547
|
+
(_, status, _type, amount) => {
|
|
548
|
+
const isPositive = !amount.startsWith('-');
|
|
549
|
+
const color = isPositive ? COLORS.moneyGain : COLORS.moneyLoss;
|
|
550
|
+
return `${color}${status} P/L: ${amount}${COLORS.reset}`;
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
return result;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Highlight OHLC (Open, High, Low, Close) values
|
|
559
|
+
*/
|
|
560
|
+
function highlightOHLC(text: string): string {
|
|
561
|
+
// Skip if inside code block
|
|
562
|
+
if (text.includes('```')) return text;
|
|
563
|
+
|
|
564
|
+
let result = text;
|
|
565
|
+
|
|
566
|
+
// OHLC pattern
|
|
567
|
+
result = result.replace(
|
|
568
|
+
/\b(?:O|Open)\s*:?\s*([\d,]+(?:\.\d{1,8})?)/gi,
|
|
569
|
+
(_, val) => `${COLORS.neutral}O: ${val}${COLORS.reset}`
|
|
570
|
+
);
|
|
571
|
+
result = result.replace(
|
|
572
|
+
/\b(?:H|High)\s*:?\s*([\d,]+(?:\.\d{1,8})?)/gi,
|
|
573
|
+
(_, val) => `${COLORS.moneyGain}H: ${val}${COLORS.reset}`
|
|
574
|
+
);
|
|
575
|
+
result = result.replace(
|
|
576
|
+
/\b(?:L|Low)\s*:?\s*([\d,]+(?:\.\d{1,8})?)/gi,
|
|
577
|
+
(_, val) => `${COLORS.moneyLoss}L: ${val}${COLORS.reset}`
|
|
578
|
+
);
|
|
579
|
+
result = result.replace(
|
|
580
|
+
/\b(?:C|Close)\s*:?\s*([\d,]+(?:\.\d{1,8})?)/gi,
|
|
581
|
+
(_, val) => `${COLORS.neutral}C: ${val}${COLORS.reset}`
|
|
582
|
+
);
|
|
583
|
+
|
|
584
|
+
// Volume
|
|
585
|
+
result = result.replace(
|
|
586
|
+
/\b(?:V|Vol|Volume)\s*:?\s*([\d,]+(?:\.\d{1,2})?[KkMmBb]?)/gi,
|
|
587
|
+
(_, val) => `${COLORS.cyan}Vol: ${val}${COLORS.reset}`
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
// Market Cap
|
|
591
|
+
result = result.replace(
|
|
592
|
+
/\b(?:MCap|Market Cap|Market Capitalization)\s*:?\s*([\$€£¥]?\s*[\d,]+(?:\.\d{1,2})?[KkMmBbTt]?)/gi,
|
|
593
|
+
(_, val) => `${COLORS.money}MCap: ${val}${COLORS.reset}`
|
|
594
|
+
);
|
|
595
|
+
|
|
596
|
+
return result;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Apply all financial highlighting - runs FIRST before any other highlighting
|
|
601
|
+
*/
|
|
602
|
+
function highlightFinancial(text: string): string {
|
|
603
|
+
// Skip if already has ANSI codes or inside code block
|
|
604
|
+
if (text.includes('\x1b[') || text.includes('```')) return text;
|
|
605
|
+
|
|
606
|
+
let result = text;
|
|
607
|
+
|
|
608
|
+
// Order matters - most specific patterns first
|
|
609
|
+
// 1. Crypto (specific symbols)
|
|
610
|
+
result = highlightCrypto(result);
|
|
611
|
+
// 2. Currency (specific symbols)
|
|
612
|
+
result = highlightCurrency(result);
|
|
613
|
+
// 3. Percentages (with +/-)
|
|
614
|
+
result = highlightPercentages(result);
|
|
615
|
+
// 4. P/L statements
|
|
616
|
+
result = highlightPL(result);
|
|
617
|
+
// 5. Price targets (PT:, SL:)
|
|
618
|
+
result = highlightPriceTargets(result);
|
|
619
|
+
// 6. OHLC values
|
|
620
|
+
result = highlightOHLC(result);
|
|
621
|
+
// 7. Stock tickers ($AAPL)
|
|
622
|
+
result = highlightTickers(result);
|
|
623
|
+
// 8. Trading terms (bullish/bearish)
|
|
624
|
+
result = highlightTradingTerms(result);
|
|
625
|
+
// 9. Timestamps
|
|
626
|
+
result = highlightTimestamps(result);
|
|
627
|
+
// 10. Math expressions
|
|
628
|
+
result = highlightMath(result);
|
|
629
|
+
|
|
630
|
+
return result;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Highlight markdown formatting in plain text
|
|
635
|
+
*/
|
|
636
|
+
function highlightMarkdown(text: string): string {
|
|
637
|
+
let result = text;
|
|
638
|
+
|
|
639
|
+
// Skip processing if we're inside a code block (basic check)
|
|
640
|
+
if (text.includes('```')) {
|
|
641
|
+
return result;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Horizontal rules (---, ***, ___) - must be on own line
|
|
645
|
+
result = result.replace(/^( {0,3})([-*_])( {0,2}\2){2,}$/gm,
|
|
646
|
+
`${COLORS.hr}$1$2$2$2${COLORS.reset}`);
|
|
647
|
+
|
|
648
|
+
// Headers (# ## ### etc) - with setext style support
|
|
649
|
+
result = result.replace(/^(#{1,6})\s+(.+)$/gm, (_, hashes, content) => {
|
|
650
|
+
// Color the header level differently based on depth
|
|
651
|
+
const level = hashes.length;
|
|
652
|
+
if (level === 1) {
|
|
653
|
+
return `${COLORS.bold}${COLORS.blue}${hashes} ${content}${COLORS.reset}`;
|
|
654
|
+
} else if (level === 2) {
|
|
655
|
+
return `${COLORS.bold}${COLORS.cyan}${hashes} ${content}${COLORS.reset}`;
|
|
656
|
+
}
|
|
657
|
+
return `${COLORS.header}${hashes} ${content}${COLORS.reset}`;
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
// Setext-style headers (underlined with === or ---)
|
|
661
|
+
result = result.replace(/^(.+)\n([=]+)$/gm, (_, content, line) => {
|
|
662
|
+
return `${COLORS.bold}${COLORS.blue}${content}${COLORS.reset}\n${COLORS.hr}${line}${COLORS.reset}`;
|
|
663
|
+
});
|
|
664
|
+
result = result.replace(/^(.+)\n([-]+)$/gm, (_, content, line) => {
|
|
665
|
+
return `${COLORS.bold}${COLORS.cyan}${content}${COLORS.reset}\n${COLORS.hr}${line}${COLORS.reset}`;
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
// Blockquotes (> at start of line)
|
|
669
|
+
result = result.replace(/^(>{1,})\s?(.*)$/gm, (_, markers, content) => {
|
|
670
|
+
const depth = markers.length;
|
|
671
|
+
// Different color for nested quotes
|
|
672
|
+
const color = depth > 1 ? COLORS.dim : COLORS.blockquote;
|
|
673
|
+
return `${COLORS.green}${markers}${COLORS.reset} ${color}${content}${COLORS.reset}`;
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// Tables - simple detection and formatting
|
|
677
|
+
result = result.replace(/^\|(.+)\|\s*$/gm, (match, content) => {
|
|
678
|
+
// Table row
|
|
679
|
+
const cells = content.split('|');
|
|
680
|
+
const formattedCells = cells.map((cell: string) => {
|
|
681
|
+
const trimmed = cell.trim();
|
|
682
|
+
if (trimmed) {
|
|
683
|
+
return ` ${COLORS.gray}${trimmed}${COLORS.reset} `;
|
|
684
|
+
}
|
|
685
|
+
return cell;
|
|
686
|
+
}).join(`${COLORS.tableBorder}|${COLORS.reset}`);
|
|
687
|
+
return `${COLORS.tableBorder}|${COLORS.reset}${formattedCells}${COLORS.tableBorder}|${COLORS.reset}`;
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
// Table separator line (|---|---|)
|
|
691
|
+
result = result.replace(/^\|([-:\s|]+)\|\s*$/gm, (_, content) => {
|
|
692
|
+
return `${COLORS.tableBorder}|${content}|${COLORS.reset}`;
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
// Bold (**text** or __text__)
|
|
696
|
+
result = result.replace(/\*\*([^*]+)\*\*/g, `${COLORS.bold}$1${COLORS.reset}`);
|
|
697
|
+
result = result.replace(/__([^_]+)__/g, `${COLORS.bold}$1${COLORS.reset}`);
|
|
698
|
+
|
|
699
|
+
// Strikethrough (~~text~~)
|
|
700
|
+
result = result.replace(/~~([^~]+)~~/g, `${COLORS.strikethrough}${COLORS.dim}$1${COLORS.reset}`);
|
|
701
|
+
|
|
702
|
+
// Italic (*text* or _text_) - be careful not to match inside words
|
|
703
|
+
result = result.replace(/(?<![a-zA-Z\*])\*([^*]+)\*(?![a-zA-Z\*])/g, `${COLORS.italic}$1${COLORS.reset}`);
|
|
704
|
+
result = result.replace(/(?<![a-zA-Z_])_([^_]+)_(?![a-zA-Z_])/g, `${COLORS.italic}$1${COLORS.reset}`);
|
|
705
|
+
|
|
706
|
+
// Bold+Italic (***text*** or ___text___)
|
|
707
|
+
result = result.replace(/\*\*\*([^*]+)\*\*\*/g, `${COLORS.bold}${COLORS.italic}$1${COLORS.reset}`);
|
|
708
|
+
result = result.replace(/___([^_]+)___/g, `${COLORS.bold}${COLORS.italic}$1${COLORS.reset}`);
|
|
709
|
+
|
|
710
|
+
// Task lists (- [ ] or - [x])
|
|
711
|
+
result = result.replace(/^(\s*)[-*]\s\[([ xX])\]/gm, (_, indent, checked) => {
|
|
712
|
+
const mark = checked.toLowerCase() === 'x' ? '✓' : ' ';
|
|
713
|
+
const color = checked.toLowerCase() === 'x' ? COLORS.green : COLORS.dim;
|
|
714
|
+
return `${indent}${COLORS.list}-${COLORS.reset} ${color}[${mark}]${COLORS.reset}`;
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
// Regular list items (- or * at start of line)
|
|
718
|
+
result = result.replace(/^(\s*)([-*])\s(?!\[)/gm, `$1${COLORS.list}$2${COLORS.reset} `);
|
|
719
|
+
|
|
720
|
+
// Numbered lists
|
|
721
|
+
result = result.replace(/^(\s*)(\d+\.)(?=\s)/gm, `$1${COLORS.list}$2${COLORS.reset}`);
|
|
722
|
+
|
|
723
|
+
// Definition lists (term : definition)
|
|
724
|
+
result = result.replace(/^([^:\n]+):\s{2,}(.+)$/gm,
|
|
725
|
+
`${COLORS.bold}$1${COLORS.reset}:${COLORS.dim} $2${COLORS.reset}`);
|
|
726
|
+
|
|
727
|
+
// Links [text](url)
|
|
728
|
+
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
729
|
+
`${COLORS.link}[$1]${COLORS.reset}${COLORS.dim}($2)${COLORS.reset}`);
|
|
730
|
+
|
|
731
|
+
// Reference-style links [text][ref]
|
|
732
|
+
result = result.replace(/\[([^\]]+)\]\[([^\]]*)\]/g,
|
|
733
|
+
`${COLORS.link}[$1]${COLORS.reset}${COLORS.dim}[$2]${COLORS.reset}`);
|
|
734
|
+
|
|
735
|
+
// Images 
|
|
736
|
+
result = result.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,
|
|
737
|
+
`${COLORS.yellow}![${COLORS.reset}${COLORS.dim}$1${COLORS.reset}${COLORS.yellow}]${COLORS.reset}${COLORS.dim}($2)${COLORS.reset}`);
|
|
738
|
+
|
|
739
|
+
// Footnotes [^1] and [^1]: definition
|
|
740
|
+
result = result.replace(/\[\^([^\]]+)\]/g, `${COLORS.yellow}[^$1]${COLORS.reset}`);
|
|
741
|
+
result = result.replace(/^\[\^([^\]]+)\]:\s*(.+)$/gm,
|
|
742
|
+
`${COLORS.yellow}[^$1]${COLORS.reset}:${COLORS.dim} $2${COLORS.reset}`);
|
|
743
|
+
|
|
744
|
+
// HTML tags (basic detection)
|
|
745
|
+
result = result.replace(/<\/?([a-zA-Z][a-zA-Z0-9]*)(\s[^>]*)?>/g,
|
|
746
|
+
`${COLORS.dim}<${COLORS.purple}$1${COLORS.reset}$2${COLORS.dim}>${COLORS.reset}`);
|
|
747
|
+
|
|
748
|
+
// Highlight/mark (==text==)
|
|
749
|
+
result = result.replace(/==([^=]+)==/g, `${COLORS.yellow}$1${COLORS.reset}`);
|
|
750
|
+
|
|
751
|
+
// Keyboard shortcuts (<kbd>text</kbd> - if not already handled by HTML)
|
|
752
|
+
result = result.replace(/<kbd>([^<]+)<\/kbd>/gi,
|
|
753
|
+
`${COLORS.dim}[${COLORS.orange}$1${COLORS.dim}]${COLORS.reset}`);
|
|
754
|
+
|
|
755
|
+
return result;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
type BlockState = 'text' | 'code' | 'diff';
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Extract language and optional file path from fence info
|
|
762
|
+
* Examples: "typescript", "typescript path/to/file.ts", "ts {linenos=true}"
|
|
763
|
+
*/
|
|
764
|
+
function parseFenceInfo(fenceInfo: string): { language: string; filePath?: string; attrs: string } {
|
|
765
|
+
const parts = fenceInfo.split(/\s+/);
|
|
766
|
+
const language = parts[0] || "";
|
|
767
|
+
let filePath: string | undefined;
|
|
768
|
+
let attrs = "";
|
|
769
|
+
|
|
770
|
+
for (let i = 1; i < parts.length; i++) {
|
|
771
|
+
const part = parts[i];
|
|
772
|
+
if (!part) continue;
|
|
773
|
+
if (part.includes('=')) {
|
|
774
|
+
attrs += (attrs ? ' ' : '') + part;
|
|
775
|
+
} else if (part.includes('.') || part.includes('/')) {
|
|
776
|
+
// Looks like a file path
|
|
777
|
+
filePath = part;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return { language, filePath, attrs };
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Map file extensions to languages
|
|
786
|
+
*/
|
|
787
|
+
const EXT_TO_LANG: Record<string, string> = {
|
|
788
|
+
'.ts': 'typescript',
|
|
789
|
+
'.tsx': 'typescript',
|
|
790
|
+
'.js': 'javascript',
|
|
791
|
+
'.jsx': 'javascript',
|
|
792
|
+
'.mjs': 'javascript',
|
|
793
|
+
'.cjs': 'javascript',
|
|
794
|
+
'.py': 'python',
|
|
795
|
+
'.rb': 'ruby',
|
|
796
|
+
'.rs': 'rust',
|
|
797
|
+
'.go': 'go',
|
|
798
|
+
'.java': 'java',
|
|
799
|
+
'.kt': 'kotlin',
|
|
800
|
+
'.swift': 'swift',
|
|
801
|
+
'.c': 'c',
|
|
802
|
+
'.cpp': 'cpp',
|
|
803
|
+
'.cc': 'cpp',
|
|
804
|
+
'.cxx': 'cpp',
|
|
805
|
+
'.h': 'c',
|
|
806
|
+
'.hpp': 'cpp',
|
|
807
|
+
'.cs': 'csharp',
|
|
808
|
+
'.php': 'php',
|
|
809
|
+
'.sh': 'bash',
|
|
810
|
+
'.bash': 'bash',
|
|
811
|
+
'.zsh': 'bash',
|
|
812
|
+
'.ps1': 'powershell',
|
|
813
|
+
'.json': 'json',
|
|
814
|
+
'.yaml': 'yaml',
|
|
815
|
+
'.yml': 'yaml',
|
|
816
|
+
'.toml': 'toml',
|
|
817
|
+
'.md': 'markdown',
|
|
818
|
+
'.html': 'html',
|
|
819
|
+
'.htm': 'html',
|
|
820
|
+
'.css': 'css',
|
|
821
|
+
'.scss': 'css',
|
|
822
|
+
'.less': 'css',
|
|
823
|
+
'.sql': 'sql',
|
|
824
|
+
'.xml': 'xml',
|
|
825
|
+
'.svg': 'xml',
|
|
826
|
+
'.vue': 'vue',
|
|
827
|
+
'.svelte': 'svelte',
|
|
828
|
+
'.dockerfile': 'dockerfile',
|
|
829
|
+
'.makefile': 'makefile',
|
|
830
|
+
'.cmake': 'cmake',
|
|
831
|
+
'.lua': 'lua',
|
|
832
|
+
'.r': 'r',
|
|
833
|
+
'.ex': 'elixir',
|
|
834
|
+
'.exs': 'elixir',
|
|
835
|
+
'.erl': 'erlang',
|
|
836
|
+
'.hs': 'haskell',
|
|
837
|
+
'.ml': 'ocaml',
|
|
838
|
+
'.clj': 'clojure',
|
|
839
|
+
'.scala': 'scala',
|
|
840
|
+
'.groovy': 'groovy',
|
|
841
|
+
'.pl': 'perl',
|
|
842
|
+
'.pm': 'perl',
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Detect language from file path
|
|
847
|
+
*/
|
|
848
|
+
function detectLanguageFromPath(filePath: string): string | undefined {
|
|
849
|
+
const ext = filePath.toLowerCase().slice(filePath.lastIndexOf('.'));
|
|
850
|
+
return EXT_TO_LANG[ext];
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Apply all text highlighting (markdown, inline code, urls, emoji)
|
|
855
|
+
*/
|
|
856
|
+
function highlightAllText(text: string): string {
|
|
857
|
+
// Skip if text already has ANSI codes (already processed)
|
|
858
|
+
if (text.includes('\x1b[')) {
|
|
859
|
+
return text;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
let result = text;
|
|
863
|
+
|
|
864
|
+
// Apply in order - more specific patterns should come first
|
|
865
|
+
// Financial highlighting first since it has specific patterns
|
|
866
|
+
result = highlightFinancial(result);
|
|
867
|
+
// Then inline code (but skip if in code blocks)
|
|
868
|
+
result = highlightInlineCode(result);
|
|
869
|
+
// Markdown formatting
|
|
870
|
+
result = highlightMarkdown(result);
|
|
871
|
+
// URLs and emoji last
|
|
872
|
+
result = highlightUrls(result);
|
|
873
|
+
result = highlightEmoji(result);
|
|
874
|
+
|
|
875
|
+
return result;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Creates a streaming highlighter that detects and highlights code blocks
|
|
880
|
+
* and diffs as they complete during streaming output.
|
|
881
|
+
*/
|
|
882
|
+
export function createStreamHighlighter(): {
|
|
883
|
+
process: (text: string) => string;
|
|
884
|
+
flush: () => string;
|
|
885
|
+
} {
|
|
886
|
+
let buffer = "";
|
|
887
|
+
let state: BlockState = 'text';
|
|
888
|
+
let codeBlockLang = "";
|
|
889
|
+
let blockContent = "";
|
|
890
|
+
let blockFilePath = "";
|
|
891
|
+
|
|
892
|
+
function process(text: string): string {
|
|
893
|
+
buffer += text;
|
|
894
|
+
let output = "";
|
|
895
|
+
|
|
896
|
+
while (true) {
|
|
897
|
+
if (state === 'code' || state === 'diff') {
|
|
898
|
+
// Look for closing fence (``` at start of line)
|
|
899
|
+
const closeMatch = buffer.match(/\n```/);
|
|
900
|
+
if (!closeMatch) {
|
|
901
|
+
// No closing fence yet, keep buffering
|
|
902
|
+
// But output most of the buffer to avoid memory issues
|
|
903
|
+
if (buffer.length > 2000) {
|
|
904
|
+
const safeChunk = buffer.slice(0, -200); // Keep last 200 chars in case fence is partial
|
|
905
|
+
blockContent += safeChunk;
|
|
906
|
+
// For diffs, we can output as we go with line-by-line highlighting
|
|
907
|
+
if (state === 'diff') {
|
|
908
|
+
output += highlightDiffContent(safeChunk);
|
|
909
|
+
} else {
|
|
910
|
+
output += safeChunk; // Code gets highlighted at end for proper syntax
|
|
911
|
+
}
|
|
912
|
+
buffer = buffer.slice(-200);
|
|
913
|
+
}
|
|
914
|
+
break;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const closeIndex = closeMatch.index!;
|
|
918
|
+
|
|
919
|
+
// Found closing fence - extract remaining content
|
|
920
|
+
blockContent += buffer.slice(0, closeIndex);
|
|
921
|
+
|
|
922
|
+
// Highlight based on block type
|
|
923
|
+
let highlighted: string;
|
|
924
|
+
const effectiveLang = codeBlockLang || (blockFilePath ? detectLanguageFromPath(blockFilePath) : '');
|
|
925
|
+
|
|
926
|
+
if (state === 'diff' || (codeBlockLang === 'diff' || isDiffContent(blockContent))) {
|
|
927
|
+
highlighted = highlightDiffContent(blockContent);
|
|
928
|
+
} else if (effectiveLang) {
|
|
929
|
+
try {
|
|
930
|
+
highlighted = highlight_code(blockContent, effectiveLang).html;
|
|
931
|
+
} catch {
|
|
932
|
+
highlighted = blockContent;
|
|
933
|
+
}
|
|
934
|
+
} else {
|
|
935
|
+
highlighted = blockContent;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// Remove trailing reset and whitespace from highlighted code
|
|
939
|
+
const trimmed = highlighted
|
|
940
|
+
.replace(/\x1b\[0m$/, '')
|
|
941
|
+
.trimEnd();
|
|
942
|
+
|
|
943
|
+
output += trimmed;
|
|
944
|
+
output += `\n${COLORS.reset}\`\`\``; // Newline, reset, and closing fence
|
|
945
|
+
|
|
946
|
+
// Skip past the closing fence in buffer
|
|
947
|
+
buffer = buffer.slice(closeIndex + 4); // +4 for "\n```"
|
|
948
|
+
|
|
949
|
+
state = 'text';
|
|
950
|
+
codeBlockLang = "";
|
|
951
|
+
blockContent = "";
|
|
952
|
+
blockFilePath = "";
|
|
953
|
+
} else {
|
|
954
|
+
// Look for opening fence: ``` followed by optional lang and newline
|
|
955
|
+
// Support: ```typescript, ```typescript file.ts, ```{ .typescript }
|
|
956
|
+
const openMatch = buffer.match(/```([^\n]*)\n/);
|
|
957
|
+
if (!openMatch) {
|
|
958
|
+
// No complete opening fence yet
|
|
959
|
+
// Check if buffer might contain partial fence at end
|
|
960
|
+
const partialFence = buffer.match(/```([^\n]*)$/);
|
|
961
|
+
if (partialFence) {
|
|
962
|
+
// Partial fence - output everything before it and keep fence in buffer
|
|
963
|
+
const beforeFence = buffer.slice(0, buffer.length - partialFence[0].length);
|
|
964
|
+
// Apply all text highlighting
|
|
965
|
+
output += highlightAllText(beforeFence);
|
|
966
|
+
buffer = partialFence[0];
|
|
967
|
+
break;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// No fence at all - output everything with markdown highlighting
|
|
971
|
+
// Keep last 4 chars in case of partial ``` (but try to keep complete lines)
|
|
972
|
+
if (buffer.length > 4) {
|
|
973
|
+
// Find the last newline to avoid splitting mid-line
|
|
974
|
+
const lastNewline = buffer.lastIndexOf('\n', buffer.length - 4);
|
|
975
|
+
let toOutput: string;
|
|
976
|
+
if (lastNewline > 0 && buffer.length - lastNewline <= 10) {
|
|
977
|
+
// If there's a recent newline, output up to it
|
|
978
|
+
toOutput = buffer.slice(0, lastNewline + 1);
|
|
979
|
+
buffer = buffer.slice(lastNewline + 1);
|
|
980
|
+
} else if (buffer.length > 100) {
|
|
981
|
+
// Long buffer with no newline - output all but last 4 chars
|
|
982
|
+
toOutput = buffer.slice(0, -4);
|
|
983
|
+
buffer = buffer.slice(-4);
|
|
984
|
+
} else {
|
|
985
|
+
// Short buffer - keep it all for now
|
|
986
|
+
break;
|
|
987
|
+
}
|
|
988
|
+
output += highlightAllText(toOutput);
|
|
989
|
+
}
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// Found complete opening fence
|
|
994
|
+
const fenceStart = openMatch.index!;
|
|
995
|
+
const fenceInfo = openMatch[1] || "";
|
|
996
|
+
const { language, filePath } = parseFenceInfo(fenceInfo);
|
|
997
|
+
|
|
998
|
+
// Output text before the fence with markdown highlighting
|
|
999
|
+
const beforeFence = buffer.slice(0, fenceStart);
|
|
1000
|
+
output += highlightAllText(beforeFence);
|
|
1001
|
+
|
|
1002
|
+
// Output the opening fence line
|
|
1003
|
+
// Show with file path if available
|
|
1004
|
+
const detectedLang = language || (filePath ? detectLanguageFromPath(filePath) : '');
|
|
1005
|
+
if (filePath) {
|
|
1006
|
+
const langDisplay = language || detectedLang || '';
|
|
1007
|
+
output += `${COLORS.dim}\`\`\`${COLORS.cyan}${langDisplay}${COLORS.reset} ${COLORS.comment}${filePath}${COLORS.reset}\n`;
|
|
1008
|
+
} else if (language) {
|
|
1009
|
+
output += `${COLORS.dim}\`\`\`${COLORS.cyan}${language}${COLORS.reset}\n`;
|
|
1010
|
+
} else {
|
|
1011
|
+
output += `${COLORS.dim}\`\`\`${COLORS.reset}\n`;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// Skip past the opening fence in buffer
|
|
1015
|
+
buffer = buffer.slice(fenceStart + openMatch[0].length);
|
|
1016
|
+
|
|
1017
|
+
// Determine block type
|
|
1018
|
+
if (language === 'diff' || language === 'patch') {
|
|
1019
|
+
state = 'diff';
|
|
1020
|
+
} else {
|
|
1021
|
+
state = 'code';
|
|
1022
|
+
}
|
|
1023
|
+
codeBlockLang = language;
|
|
1024
|
+
blockContent = "";
|
|
1025
|
+
blockFilePath = filePath || "";
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
return output;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
function flush(): string {
|
|
1033
|
+
let output = "";
|
|
1034
|
+
|
|
1035
|
+
if (state === 'code' || state === 'diff') {
|
|
1036
|
+
// Unclosed code block - highlight what we have
|
|
1037
|
+
const effectiveLang = codeBlockLang || (blockFilePath ? detectLanguageFromPath(blockFilePath) : '');
|
|
1038
|
+
|
|
1039
|
+
if (state === 'diff' || isDiffContent(blockContent)) {
|
|
1040
|
+
output += highlightDiffContent(blockContent);
|
|
1041
|
+
} else if (effectiveLang) {
|
|
1042
|
+
try {
|
|
1043
|
+
output += highlight_code(blockContent, effectiveLang).html;
|
|
1044
|
+
} catch {
|
|
1045
|
+
output += blockContent;
|
|
1046
|
+
}
|
|
1047
|
+
} else {
|
|
1048
|
+
output += blockContent;
|
|
1049
|
+
}
|
|
1050
|
+
output += COLORS.reset;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
if (buffer) {
|
|
1054
|
+
output += highlightAllText(buffer);
|
|
1055
|
+
buffer = "";
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
return output;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
return { process, flush };
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Highlight complete text with code blocks
|
|
1066
|
+
* Used for non-streaming contexts or final output
|
|
1067
|
+
*/
|
|
1068
|
+
export function highlightTextWithCodeBlocks(text: string): string {
|
|
1069
|
+
// Split by code blocks and highlight each
|
|
1070
|
+
// Support language + optional file path: ```typescript path/to/file.ts
|
|
1071
|
+
const codeBlockRegex = /```([^\n]*)\n([\s\S]*?)```/g;
|
|
1072
|
+
let result = "";
|
|
1073
|
+
let lastIndex = 0;
|
|
1074
|
+
|
|
1075
|
+
let match;
|
|
1076
|
+
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
1077
|
+
// Add text before code block with all highlighting
|
|
1078
|
+
const beforeText = text.slice(lastIndex, match.index);
|
|
1079
|
+
result += highlightAllText(beforeText);
|
|
1080
|
+
|
|
1081
|
+
const fenceInfo = match[1] || "";
|
|
1082
|
+
const { language, filePath } = parseFenceInfo(fenceInfo);
|
|
1083
|
+
const code = match[2] || "";
|
|
1084
|
+
|
|
1085
|
+
// Determine effective language
|
|
1086
|
+
const effectiveLang = language || (filePath ? detectLanguageFromPath(filePath) : '');
|
|
1087
|
+
|
|
1088
|
+
// Show fence with file path if available
|
|
1089
|
+
if (filePath) {
|
|
1090
|
+
const langDisplay = language || effectiveLang || '';
|
|
1091
|
+
result += `${COLORS.dim}\`\`\`${COLORS.cyan}${langDisplay}${COLORS.reset} ${COLORS.comment}${filePath}${COLORS.reset}\n`;
|
|
1092
|
+
} else if (language) {
|
|
1093
|
+
result += `${COLORS.dim}\`\`\`${COLORS.cyan}${language}${COLORS.reset}\n`;
|
|
1094
|
+
} else {
|
|
1095
|
+
result += `${COLORS.dim}\`\`\`${COLORS.reset}\n`;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
if (language === 'diff' || language === 'patch' || isDiffContent(code)) {
|
|
1099
|
+
// Highlight as diff
|
|
1100
|
+
result += highlightDiffContent(code);
|
|
1101
|
+
result += `\n${COLORS.reset}\`\`\``;
|
|
1102
|
+
} else if (effectiveLang) {
|
|
1103
|
+
// Highlight with language
|
|
1104
|
+
try {
|
|
1105
|
+
result += highlight_code(code, effectiveLang).html;
|
|
1106
|
+
} catch {
|
|
1107
|
+
result += code;
|
|
1108
|
+
}
|
|
1109
|
+
result += `\n${COLORS.reset}\`\`\``;
|
|
1110
|
+
} else {
|
|
1111
|
+
// No language, output as-is
|
|
1112
|
+
result += code;
|
|
1113
|
+
result += `\n${COLORS.reset}\`\`\``;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
lastIndex = match.index + match[0].length;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// Add remaining text with all highlighting
|
|
1120
|
+
result += highlightAllText(text.slice(lastIndex));
|
|
1121
|
+
|
|
1122
|
+
return result;
|
|
1123
|
+
}
|