byterover-cli 0.4.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -9
- package/dist/commands/curate.d.ts +1 -3
- package/dist/commands/curate.js +14 -51
- package/dist/commands/main.d.ts +8 -0
- package/dist/commands/main.js +29 -8
- package/dist/commands/query.d.ts +1 -3
- package/dist/commands/query.js +8 -35
- package/dist/config/context-tree-domains.d.ts +5 -0
- package/dist/config/context-tree-domains.js +6 -1
- package/dist/config/environment.js +9 -9
- package/dist/constants.d.ts +14 -0
- package/dist/constants.js +18 -0
- package/dist/core/domain/cipher/agent/agent-info.d.ts +199 -0
- package/dist/core/domain/cipher/agent/agent-info.js +143 -0
- package/dist/core/domain/cipher/agent/agent-registry.d.ts +96 -0
- package/dist/core/domain/cipher/agent/agent-registry.js +254 -0
- package/dist/core/domain/cipher/agent/index.d.ts +4 -1
- package/dist/core/domain/cipher/agent/index.js +7 -1
- package/dist/core/domain/cipher/agent-events/types.d.ts +355 -2
- package/dist/core/domain/cipher/agent-events/types.js +11 -0
- package/dist/core/domain/cipher/errors/error-normalizer.d.ts +156 -0
- package/dist/core/domain/cipher/errors/error-normalizer.js +379 -0
- package/dist/core/domain/cipher/errors/file-system-error.d.ts +2 -1
- package/dist/core/domain/cipher/errors/file-system-error.js +3 -2
- package/dist/core/domain/cipher/errors/system-prompt-error-codes.d.ts +79 -0
- package/dist/core/domain/cipher/errors/system-prompt-error-codes.js +80 -0
- package/dist/core/domain/cipher/errors/system-prompt-error.d.ts +114 -0
- package/dist/core/domain/cipher/errors/system-prompt-error.js +144 -0
- package/dist/core/domain/cipher/file-system/types.d.ts +57 -0
- package/dist/core/domain/cipher/llm/error-codes.d.ts +51 -0
- package/dist/core/domain/cipher/llm/error-codes.js +51 -0
- package/dist/core/domain/cipher/llm/index.d.ts +9 -0
- package/dist/core/domain/cipher/llm/index.js +13 -0
- package/dist/core/domain/cipher/llm/registry.d.ts +113 -0
- package/dist/core/domain/cipher/llm/registry.js +244 -0
- package/dist/core/domain/cipher/llm/schemas.d.ts +155 -0
- package/dist/core/domain/cipher/llm/schemas.js +151 -0
- package/dist/core/domain/cipher/llm/types.d.ts +121 -0
- package/dist/core/domain/cipher/llm/types.js +60 -0
- package/dist/core/domain/cipher/storage/message-storage-types.d.ts +114 -5
- package/dist/core/domain/cipher/streaming/types.d.ts +119 -0
- package/dist/core/domain/cipher/streaming/types.js +16 -0
- package/dist/core/domain/cipher/system-prompt/types.d.ts +44 -0
- package/dist/core/domain/cipher/todos/types.d.ts +34 -0
- package/dist/core/domain/cipher/tools/constants.d.ts +5 -2
- package/dist/core/domain/cipher/tools/constants.js +5 -2
- package/dist/core/domain/cipher/tools/types.d.ts +31 -0
- package/dist/core/domain/entities/event.d.ts +1 -1
- package/dist/core/domain/entities/event.js +3 -1
- package/dist/core/domain/errors/connection-error.d.ts +33 -0
- package/dist/core/domain/errors/connection-error.js +54 -0
- package/dist/core/domain/errors/core-process-error.d.ts +27 -0
- package/dist/core/domain/errors/core-process-error.js +43 -0
- package/dist/core/domain/errors/task-error.d.ts +64 -0
- package/dist/core/domain/errors/task-error.js +116 -0
- package/dist/core/domain/errors/transport-error.d.ts +72 -0
- package/dist/core/domain/errors/transport-error.js +114 -0
- package/dist/core/domain/instance/index.d.ts +1 -0
- package/dist/core/domain/instance/index.js +1 -0
- package/dist/core/domain/instance/types.d.ts +57 -0
- package/dist/core/domain/instance/types.js +72 -0
- package/dist/core/domain/knowledge/directory-manager.d.ts +16 -0
- package/dist/core/domain/knowledge/directory-manager.js +31 -0
- package/dist/core/domain/transport/index.d.ts +2 -0
- package/dist/core/domain/transport/index.js +2 -0
- package/dist/core/domain/transport/schemas.d.ts +1149 -0
- package/dist/core/domain/transport/schemas.js +554 -0
- package/dist/core/domain/transport/types.d.ts +67 -0
- package/dist/core/domain/transport/types.js +7 -0
- package/dist/core/interfaces/cipher/cipher-services.d.ts +15 -3
- package/dist/core/interfaces/cipher/i-chat-session.d.ts +47 -5
- package/dist/core/interfaces/cipher/i-cipher-agent.d.ts +39 -4
- package/dist/core/interfaces/cipher/i-content-generator.d.ts +3 -5
- package/dist/core/interfaces/cipher/i-file-system.d.ts +12 -1
- package/dist/core/interfaces/cipher/i-llm-service.d.ts +4 -5
- package/dist/core/interfaces/cipher/i-todo-storage.d.ts +24 -0
- package/dist/core/interfaces/cipher/i-todo-storage.js +1 -0
- package/dist/core/interfaces/cipher/i-tool-plugin.d.ts +90 -0
- package/dist/core/interfaces/cipher/i-tool-plugin.js +1 -0
- package/dist/core/interfaces/cipher/i-tool-provider.d.ts +3 -2
- package/dist/core/interfaces/cipher/i-tool-scheduler.d.ts +4 -0
- package/dist/core/interfaces/cipher/index.d.ts +35 -0
- package/dist/core/interfaces/cipher/index.js +11 -0
- package/dist/core/interfaces/cipher/message-factory.d.ts +155 -0
- package/dist/core/interfaces/cipher/message-factory.js +252 -0
- package/dist/core/interfaces/cipher/message-type-guards.d.ts +139 -0
- package/dist/core/interfaces/cipher/message-type-guards.js +173 -0
- package/dist/core/interfaces/cipher/message-types.d.ts +279 -5
- package/dist/core/interfaces/cipher/message-types.js +6 -0
- package/dist/core/interfaces/cipher/sanitization-types.d.ts +147 -0
- package/dist/core/interfaces/cipher/sanitization-types.js +46 -0
- package/dist/core/interfaces/executor/i-curate-executor.d.ts +34 -0
- package/dist/core/interfaces/executor/i-curate-executor.js +1 -0
- package/dist/core/interfaces/executor/i-query-executor.d.ts +32 -0
- package/dist/core/interfaces/executor/i-query-executor.js +1 -0
- package/dist/core/interfaces/executor/index.d.ts +2 -0
- package/dist/core/interfaces/executor/index.js +2 -0
- package/dist/core/interfaces/instance/i-instance-discovery.d.ts +45 -0
- package/dist/core/interfaces/instance/i-instance-discovery.js +1 -0
- package/dist/core/interfaces/instance/i-instance-manager.d.ts +58 -0
- package/dist/core/interfaces/instance/i-instance-manager.js +1 -0
- package/dist/core/interfaces/instance/index.d.ts +2 -0
- package/dist/core/interfaces/instance/index.js +2 -0
- package/dist/core/interfaces/noop-implementations.d.ts +53 -0
- package/dist/core/interfaces/noop-implementations.js +62 -0
- package/dist/core/interfaces/transport/i-transport-client.d.ts +97 -0
- package/dist/core/interfaces/transport/i-transport-client.js +1 -0
- package/dist/core/interfaces/transport/i-transport-server.d.ts +93 -0
- package/dist/core/interfaces/transport/i-transport-server.js +1 -0
- package/dist/core/interfaces/transport/index.d.ts +2 -0
- package/dist/core/interfaces/transport/index.js +2 -0
- package/dist/hooks/init/welcome.js +9 -24
- package/dist/infra/cipher/agent/agent-error-codes.d.ts +16 -0
- package/dist/infra/cipher/agent/agent-error-codes.js +17 -0
- package/dist/infra/cipher/agent/agent-error.d.ts +54 -0
- package/dist/infra/cipher/agent/agent-error.js +79 -0
- package/dist/infra/cipher/agent/agent-schemas.d.ts +264 -0
- package/dist/infra/cipher/agent/agent-schemas.js +97 -0
- package/dist/infra/cipher/agent/agent-state-manager.d.ts +140 -0
- package/dist/infra/cipher/agent/agent-state-manager.js +275 -0
- package/dist/infra/cipher/agent/base-agent.d.ts +118 -0
- package/dist/infra/cipher/agent/base-agent.js +240 -0
- package/dist/infra/cipher/agent/cipher-agent.d.ts +165 -0
- package/dist/infra/cipher/agent/cipher-agent.js +546 -0
- package/dist/infra/cipher/agent/index.d.ts +22 -0
- package/dist/infra/cipher/agent/index.js +24 -0
- package/dist/infra/cipher/agent/service-initializer.d.ts +79 -0
- package/dist/infra/cipher/{agent-service-factory.js → agent/service-initializer.js} +117 -68
- package/dist/infra/cipher/agent/types.d.ts +35 -0
- package/dist/infra/cipher/agent/types.js +1 -0
- package/dist/infra/cipher/blob/blob-reference-resolver.d.ts +107 -0
- package/dist/infra/cipher/blob/blob-reference-resolver.js +228 -0
- package/dist/infra/cipher/blob/blob-reference-utils.d.ts +117 -0
- package/dist/infra/cipher/blob/blob-reference-utils.js +230 -0
- package/dist/infra/cipher/consumer/consumer-lock.js +1 -0
- package/dist/infra/cipher/consumer/consumer-service.js +1 -0
- package/dist/infra/cipher/consumer/execution-consumer.d.ts +6 -1
- package/dist/infra/cipher/consumer/execution-consumer.js +54 -16
- package/dist/infra/cipher/consumer/index.d.ts +1 -1
- package/dist/infra/cipher/consumer/index.js +2 -1
- package/dist/infra/cipher/consumer/queue-polling-service.js +1 -0
- package/dist/infra/cipher/file-system/binary-utils.d.ts +43 -0
- package/dist/infra/cipher/file-system/binary-utils.js +164 -0
- package/dist/infra/cipher/file-system/context-tree-file-system-factory.d.ts +9 -0
- package/dist/infra/cipher/file-system/context-tree-file-system-factory.js +24 -0
- package/dist/infra/cipher/file-system/file-system-service.d.ts +17 -1
- package/dist/infra/cipher/file-system/file-system-service.js +327 -36
- package/dist/infra/cipher/file-system/path-validator.d.ts +32 -0
- package/dist/infra/cipher/file-system/path-validator.js +111 -6
- package/dist/infra/cipher/interactive-loop.js +41 -33
- package/dist/infra/cipher/llm/capability-cache.d.ts +87 -0
- package/dist/infra/cipher/llm/capability-cache.js +125 -0
- package/dist/infra/cipher/llm/context/compaction/compaction-service.d.ts +32 -0
- package/dist/infra/cipher/llm/context/compaction/compaction-service.js +44 -3
- package/dist/infra/cipher/llm/context/compression/enhanced-compaction.d.ts +112 -0
- package/dist/infra/cipher/llm/context/compression/enhanced-compaction.js +175 -0
- package/dist/infra/cipher/llm/context/compression/filter-compacted.d.ts +83 -0
- package/dist/infra/cipher/llm/context/compression/filter-compacted.js +150 -0
- package/dist/infra/cipher/llm/context/compression/index.d.ts +5 -0
- package/dist/infra/cipher/llm/context/compression/index.js +6 -0
- package/dist/infra/cipher/llm/context/compression/reactive-overflow.d.ts +107 -0
- package/dist/infra/cipher/llm/context/compression/reactive-overflow.js +272 -0
- package/dist/infra/cipher/llm/context/context-manager.d.ts +47 -1
- package/dist/infra/cipher/llm/context/context-manager.js +129 -0
- package/dist/infra/cipher/llm/context/utils.js +17 -4
- package/dist/infra/cipher/llm/generators/byterover-content-generator.js +4 -2
- package/dist/infra/cipher/llm/internal-llm-service.d.ts +50 -17
- package/dist/infra/cipher/llm/internal-llm-service.js +273 -50
- package/dist/infra/cipher/llm/openrouter-llm-service.d.ts +6 -8
- package/dist/infra/cipher/llm/openrouter-llm-service.js +14 -16
- package/dist/infra/cipher/llm/retry/retry-policy.d.ts +1 -0
- package/dist/infra/cipher/llm/retry/retry-policy.js +11 -0
- package/dist/infra/cipher/llm/retry/retry-with-backoff.js +3 -2
- package/dist/infra/cipher/llm/sanitization/base64-utils.d.ts +102 -0
- package/dist/infra/cipher/llm/sanitization/base64-utils.js +182 -0
- package/dist/infra/cipher/llm/sanitization/index.d.ts +12 -0
- package/dist/infra/cipher/llm/sanitization/index.js +13 -0
- package/dist/infra/cipher/llm/sanitization/tool-sanitizer.d.ts +74 -0
- package/dist/infra/cipher/llm/sanitization/tool-sanitizer.js +398 -0
- package/dist/infra/cipher/llm/stream-processor.d.ts +158 -0
- package/dist/infra/cipher/llm/stream-processor.js +276 -0
- package/dist/infra/cipher/llm/tokenizers/claude-tokenizer.d.ts +13 -20
- package/dist/infra/cipher/llm/tokenizers/claude-tokenizer.js +17 -24
- package/dist/infra/cipher/llm/tokenizers/gemini-tokenizer.d.ts +12 -11
- package/dist/infra/cipher/llm/tokenizers/gemini-tokenizer.js +16 -15
- package/dist/infra/cipher/llm/tokenizers/openrouter-tokenizer.d.ts +15 -7
- package/dist/infra/cipher/llm/tokenizers/openrouter-tokenizer.js +22 -10
- package/dist/infra/cipher/llm/tool-output-processor.d.ts +51 -0
- package/dist/infra/cipher/llm/tool-output-processor.js +139 -0
- package/dist/infra/cipher/process/command-validator.d.ts +23 -0
- package/dist/infra/cipher/process/command-validator.js +75 -0
- package/dist/infra/cipher/process/path-utils.d.ts +66 -0
- package/dist/infra/cipher/process/path-utils.js +94 -0
- package/dist/infra/cipher/process/process-service.d.ts +32 -0
- package/dist/infra/cipher/process/process-service.js +98 -17
- package/dist/infra/cipher/session/chat-session.d.ts +56 -7
- package/dist/infra/cipher/session/chat-session.js +163 -13
- package/dist/infra/cipher/session/index.d.ts +1 -0
- package/dist/infra/cipher/session/index.js +2 -0
- package/dist/infra/cipher/session/message-queue.d.ts +65 -0
- package/dist/infra/cipher/session/message-queue.js +90 -0
- package/dist/infra/cipher/session/session-manager.d.ts +106 -5
- package/dist/infra/cipher/session/session-manager.js +254 -7
- package/dist/infra/cipher/session/session-status.d.ts +137 -0
- package/dist/infra/cipher/session/session-status.js +184 -0
- package/dist/infra/cipher/session/title-generator.d.ts +8 -0
- package/dist/infra/cipher/session/title-generator.js +31 -0
- package/dist/infra/cipher/storage/message-storage-service.d.ts +65 -2
- package/dist/infra/cipher/storage/message-storage-service.js +300 -54
- package/dist/infra/cipher/storage/tool-part-factory.d.ts +116 -0
- package/dist/infra/cipher/storage/tool-part-factory.js +197 -0
- package/dist/infra/cipher/system-prompt/contributor-schemas.d.ts +516 -0
- package/dist/infra/cipher/system-prompt/contributor-schemas.js +85 -0
- package/dist/infra/cipher/system-prompt/contributors/agent-prompt-contributor.d.ts +59 -0
- package/dist/infra/cipher/system-prompt/contributors/agent-prompt-contributor.js +131 -0
- package/dist/infra/cipher/system-prompt/contributors/companion-contributor.d.ts +54 -0
- package/dist/infra/cipher/system-prompt/contributors/companion-contributor.js +107 -0
- package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.d.ts +68 -0
- package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.js +179 -0
- package/dist/infra/cipher/system-prompt/contributors/datetime-contributor.d.ts +25 -0
- package/dist/infra/cipher/system-prompt/contributors/datetime-contributor.js +29 -0
- package/dist/infra/cipher/system-prompt/contributors/environment-contributor.d.ts +25 -0
- package/dist/infra/cipher/system-prompt/contributors/environment-contributor.js +54 -0
- package/dist/infra/cipher/system-prompt/contributors/file-contributor.d.ts +60 -0
- package/dist/infra/cipher/system-prompt/contributors/file-contributor.js +128 -0
- package/dist/infra/cipher/system-prompt/contributors/index.d.ts +13 -0
- package/dist/infra/cipher/system-prompt/contributors/index.js +8 -0
- package/dist/infra/cipher/system-prompt/contributors/memory-contributor.d.ts +40 -0
- package/dist/infra/cipher/system-prompt/contributors/memory-contributor.js +56 -0
- package/dist/infra/cipher/system-prompt/contributors/static-contributor.d.ts +26 -0
- package/dist/infra/cipher/system-prompt/contributors/static-contributor.js +31 -0
- package/dist/infra/cipher/system-prompt/environment-context-builder.d.ts +112 -0
- package/dist/infra/cipher/system-prompt/environment-context-builder.js +256 -0
- package/dist/infra/cipher/system-prompt/prompt-cache.d.ts +102 -0
- package/dist/infra/cipher/system-prompt/prompt-cache.js +156 -0
- package/dist/infra/cipher/system-prompt/schemas.d.ts +151 -0
- package/dist/infra/cipher/system-prompt/schemas.js +94 -0
- package/dist/infra/cipher/system-prompt/system-prompt-manager.d.ts +136 -0
- package/dist/infra/cipher/system-prompt/system-prompt-manager.js +307 -0
- package/dist/infra/cipher/todos/todo-storage-service.d.ts +26 -0
- package/dist/infra/cipher/todos/todo-storage-service.js +28 -0
- package/dist/infra/cipher/tools/core-tool-scheduler.js +5 -1
- package/dist/infra/cipher/tools/default-policy-rules.js +1 -1
- package/dist/infra/cipher/tools/implementations/bash-exec-tool.d.ts +1 -0
- package/dist/infra/cipher/tools/implementations/bash-exec-tool.js +27 -10
- package/dist/infra/cipher/tools/implementations/bash-output-tool.js +1 -5
- package/dist/infra/cipher/tools/implementations/batch-tool.d.ts +12 -0
- package/dist/infra/cipher/tools/implementations/batch-tool.js +142 -0
- package/dist/infra/cipher/tools/implementations/curate-tool.js +195 -68
- package/dist/infra/cipher/tools/implementations/list-directory-tool.d.ts +12 -0
- package/dist/infra/cipher/tools/implementations/list-directory-tool.js +52 -0
- package/dist/infra/cipher/tools/implementations/read-file-tool.d.ts +8 -1
- package/dist/infra/cipher/tools/implementations/read-file-tool.js +17 -7
- package/dist/infra/cipher/tools/implementations/read-todos-tool.d.ts +11 -0
- package/dist/infra/cipher/tools/implementations/read-todos-tool.js +39 -0
- package/dist/infra/cipher/tools/implementations/{detect-domains-tool.d.ts → spec-analyze-tool.d.ts} +1 -1
- package/dist/infra/cipher/tools/implementations/{detect-domains-tool.js → spec-analyze-tool.js} +9 -7
- package/dist/infra/cipher/tools/implementations/task-tool.d.ts +34 -0
- package/dist/infra/cipher/tools/implementations/task-tool.js +207 -0
- package/dist/infra/cipher/tools/implementations/write-todos-tool.d.ts +4 -1
- package/dist/infra/cipher/tools/implementations/write-todos-tool.js +19 -63
- package/dist/infra/cipher/tools/index.d.ts +1 -1
- package/dist/infra/cipher/tools/index.js +1 -1
- package/dist/infra/cipher/tools/plugins/index.d.ts +3 -0
- package/dist/infra/cipher/tools/plugins/index.js +2 -0
- package/dist/infra/cipher/tools/plugins/logging-plugin.d.ts +28 -0
- package/dist/infra/cipher/tools/plugins/logging-plugin.js +66 -0
- package/dist/infra/cipher/tools/plugins/plugin-manager.d.ts +81 -0
- package/dist/infra/cipher/tools/plugins/plugin-manager.js +122 -0
- package/dist/infra/cipher/tools/streaming/index.d.ts +1 -0
- package/dist/infra/cipher/tools/streaming/index.js +1 -0
- package/dist/infra/cipher/tools/streaming/metadata-handler.d.ts +31 -0
- package/dist/infra/cipher/tools/streaming/metadata-handler.js +39 -0
- package/dist/infra/cipher/tools/tool-description-loader.d.ts +57 -0
- package/dist/infra/cipher/tools/tool-description-loader.js +108 -0
- package/dist/infra/cipher/tools/tool-manager.d.ts +38 -4
- package/dist/infra/cipher/tools/tool-manager.js +107 -11
- package/dist/infra/cipher/tools/tool-provider-getter.d.ts +6 -0
- package/dist/infra/cipher/tools/tool-provider-getter.js +1 -0
- package/dist/infra/cipher/tools/tool-provider.d.ts +32 -7
- package/dist/infra/cipher/tools/tool-provider.js +81 -25
- package/dist/infra/cipher/tools/tool-registry.d.ts +23 -0
- package/dist/infra/cipher/tools/tool-registry.js +58 -16
- package/dist/infra/context-tree/file-context-tree-snapshot-service.js +10 -4
- package/dist/infra/context-tree/file-context-tree-writer-service.d.ts +4 -3
- package/dist/infra/context-tree/file-context-tree-writer-service.js +6 -4
- package/dist/infra/context-tree/path-utils.d.ts +7 -0
- package/dist/infra/context-tree/path-utils.js +7 -0
- package/dist/infra/core/executors/curate-executor.d.ts +35 -0
- package/dist/infra/core/executors/curate-executor.js +123 -0
- package/dist/infra/core/executors/index.d.ts +2 -0
- package/dist/infra/core/executors/index.js +2 -0
- package/dist/infra/core/executors/query-executor.d.ts +23 -0
- package/dist/infra/core/executors/query-executor.js +51 -0
- package/dist/infra/core/task-processor.d.ts +81 -0
- package/dist/infra/core/task-processor.js +115 -0
- package/dist/infra/instance/file-instance-discovery.d.ts +31 -0
- package/dist/infra/instance/file-instance-discovery.js +84 -0
- package/dist/infra/instance/file-instance-manager.d.ts +46 -0
- package/dist/infra/instance/file-instance-manager.js +123 -0
- package/dist/infra/instance/index.d.ts +3 -0
- package/dist/infra/instance/index.js +3 -0
- package/dist/infra/instance/process-utils.d.ts +14 -0
- package/dist/infra/instance/process-utils.js +39 -0
- package/dist/infra/process/agent-worker.d.ts +20 -0
- package/dist/infra/process/agent-worker.js +602 -0
- package/dist/infra/process/index.d.ts +12 -0
- package/dist/infra/process/index.js +11 -0
- package/dist/infra/process/ipc-types.d.ts +55 -0
- package/dist/infra/process/ipc-types.js +12 -0
- package/dist/infra/process/process-manager.d.ts +154 -0
- package/dist/infra/process/process-manager.js +471 -0
- package/dist/infra/process/task-queue-manager.d.ts +123 -0
- package/dist/infra/process/task-queue-manager.js +226 -0
- package/dist/infra/process/transport-handlers.d.ts +124 -0
- package/dist/infra/process/transport-handlers.js +348 -0
- package/dist/infra/process/transport-worker.d.ts +20 -0
- package/dist/infra/process/transport-worker.js +168 -0
- package/dist/infra/repl/commands/curate-command.js +0 -5
- package/dist/infra/repl/commands/query-command.js +0 -3
- package/dist/infra/repl/repl-startup.d.ts +4 -0
- package/dist/infra/repl/repl-startup.js +10 -1
- package/dist/infra/repl/transport-client-helper.d.ts +9 -0
- package/dist/infra/repl/transport-client-helper.js +96 -0
- package/dist/infra/transport/index.d.ts +4 -0
- package/dist/infra/transport/index.js +4 -0
- package/dist/infra/transport/port-utils.d.ts +42 -0
- package/dist/infra/transport/port-utils.js +84 -0
- package/dist/infra/transport/socket-io-transport-client.d.ts +45 -0
- package/dist/infra/transport/socket-io-transport-client.js +270 -0
- package/dist/infra/transport/socket-io-transport-server.d.ts +35 -0
- package/dist/infra/transport/socket-io-transport-server.js +207 -0
- package/dist/infra/transport/transport-client-factory.d.ts +76 -0
- package/dist/infra/transport/transport-client-factory.js +168 -0
- package/dist/infra/transport/transport-factory.d.ts +33 -0
- package/dist/infra/transport/transport-factory.js +59 -0
- package/dist/infra/usecase/curate-use-case.d.ts +8 -55
- package/dist/infra/usecase/curate-use-case.js +73 -259
- package/dist/infra/usecase/init-use-case.js +19 -8
- package/dist/infra/usecase/login-use-case.js +9 -3
- package/dist/infra/usecase/query-use-case.d.ts +18 -45
- package/dist/infra/usecase/query-use-case.js +251 -326
- package/dist/infra/usecase/status-use-case.js +1 -1
- package/dist/resources/prompts/{curate-context-tree-curation.yml → curate.yml} +25 -22
- package/dist/resources/prompts/explore.yml +78 -0
- package/dist/resources/prompts/plan.yml +114 -0
- package/dist/resources/prompts/reflection.yml +1 -1
- package/dist/resources/prompts/system-prompt.yml +15 -8
- package/dist/resources/prompts/tool-outputs.yml +0 -5
- package/dist/resources/tools/bash_exec.txt +98 -0
- package/dist/resources/tools/bash_output.txt +40 -0
- package/dist/resources/tools/batch.txt +28 -0
- package/dist/resources/tools/create_knowledge_topic.txt +23 -0
- package/dist/resources/tools/curate.txt +22 -0
- package/dist/resources/tools/delete_memory.txt +1 -0
- package/dist/resources/tools/detect_domains.txt +11 -0
- package/dist/resources/tools/edit_file.txt +1 -0
- package/dist/resources/tools/edit_memory.txt +1 -0
- package/dist/resources/tools/glob_files.txt +20 -0
- package/dist/resources/tools/grep_content.txt +18 -0
- package/dist/resources/tools/kill_process.txt +16 -0
- package/dist/resources/tools/list_directory.txt +16 -0
- package/dist/resources/tools/list_memories.txt +1 -0
- package/dist/resources/tools/read_file.txt +31 -0
- package/dist/resources/tools/read_memory.txt +1 -0
- package/dist/resources/tools/read_todos.txt +17 -0
- package/dist/resources/tools/search_history.txt +1 -0
- package/dist/resources/tools/task.txt +23 -0
- package/dist/resources/tools/write_file.txt +1 -0
- package/dist/resources/tools/write_memory.txt +1 -0
- package/dist/resources/tools/write_todos.txt +29 -0
- package/dist/tui/app.js +9 -13
- package/dist/tui/components/command-details.d.ts +14 -0
- package/dist/tui/components/command-details.js +35 -0
- package/dist/tui/components/execution/execution-changes.d.ts +5 -0
- package/dist/tui/components/execution/execution-changes.js +19 -4
- package/dist/tui/components/execution/execution-content.d.ts +4 -2
- package/dist/tui/components/execution/execution-content.js +26 -13
- package/dist/tui/components/execution/execution-input.js +3 -3
- package/dist/tui/components/execution/execution-progress.d.ts +2 -2
- package/dist/tui/components/execution/execution-progress.js +8 -6
- package/dist/tui/components/execution/log-item.d.ts +3 -4
- package/dist/tui/components/execution/log-item.js +2 -5
- package/dist/tui/components/footer.js +9 -4
- package/dist/tui/components/header.d.ts +3 -3
- package/dist/tui/components/header.js +5 -3
- package/dist/tui/components/index.d.ts +1 -0
- package/dist/tui/components/index.js +1 -0
- package/dist/tui/components/onboarding/copyable-prompt.d.ts +5 -3
- package/dist/tui/components/onboarding/copyable-prompt.js +7 -8
- package/dist/tui/components/onboarding/onboarding-flow.js +35 -25
- package/dist/tui/components/scrollable-list.js +12 -10
- package/dist/tui/components/suggestions.js +39 -41
- package/dist/tui/components/tab-bar.d.ts +2 -1
- package/dist/tui/components/tab-bar.js +3 -4
- package/dist/tui/constants.d.ts +0 -5
- package/dist/tui/constants.js +0 -5
- package/dist/tui/contexts/auth-context.js +9 -2
- package/dist/tui/contexts/{use-commands.js → commands-context.js} +6 -5
- package/dist/tui/contexts/index.d.ts +6 -1
- package/dist/tui/contexts/index.js +6 -1
- package/dist/tui/contexts/onboarding-context.d.ts +1 -1
- package/dist/tui/contexts/onboarding-context.js +9 -9
- package/dist/tui/contexts/tasks-context.d.ts +84 -0
- package/dist/tui/contexts/tasks-context.js +218 -0
- package/dist/tui/contexts/transport-context.d.ts +29 -0
- package/dist/tui/contexts/transport-context.js +82 -0
- package/dist/tui/hooks/index.d.ts +10 -6
- package/dist/tui/hooks/index.js +7 -6
- package/dist/tui/hooks/use-activity-logs.d.ts +3 -11
- package/dist/tui/hooks/use-activity-logs.js +87 -34
- package/dist/tui/hooks/use-auth-polling.d.ts +24 -0
- package/dist/tui/hooks/use-auth-polling.js +104 -0
- package/dist/tui/hooks/use-slash-command-processor.js +0 -1
- package/dist/tui/hooks/use-slash-completion.js +1 -1
- package/dist/tui/hooks/use-tab-navigation.d.ts +2 -1
- package/dist/tui/hooks/use-tab-navigation.js +16 -7
- package/dist/tui/hooks/use-terminal-breakpoint.d.ts +21 -0
- package/dist/tui/hooks/use-terminal-breakpoint.js +38 -0
- package/dist/tui/hooks/use-ui-heights.d.ts +120 -0
- package/dist/tui/hooks/use-ui-heights.js +88 -0
- package/dist/tui/providers/app-providers.js +2 -6
- package/dist/tui/types/commands.d.ts +0 -26
- package/dist/tui/types/index.d.ts +1 -1
- package/dist/tui/types/ui.d.ts +9 -4
- package/dist/tui/utils/line.d.ts +11 -0
- package/dist/tui/utils/line.js +16 -0
- package/dist/tui/utils/log.d.ts +27 -0
- package/dist/tui/utils/log.js +114 -0
- package/dist/tui/views/command-view.d.ts +7 -0
- package/dist/tui/views/command-view.js +103 -80
- package/dist/tui/views/login-view.js +7 -4
- package/dist/tui/views/logs-view.d.ts +13 -0
- package/dist/tui/views/logs-view.js +27 -52
- package/dist/utils/connection-error-handler.d.ts +16 -0
- package/dist/utils/connection-error-handler.js +49 -0
- package/dist/utils/crash-log.d.ts +14 -0
- package/dist/utils/crash-log.js +19 -0
- package/dist/utils/file-helpers.d.ts +14 -0
- package/dist/utils/file-helpers.js +21 -0
- package/dist/utils/global-logs-path.d.ts +11 -0
- package/dist/utils/global-logs-path.js +37 -0
- package/dist/utils/process-logger.d.ts +53 -0
- package/dist/utils/process-logger.js +253 -0
- package/dist/utils/sandbox-detector.d.ts +31 -0
- package/dist/utils/sandbox-detector.js +122 -0
- package/oclif.manifest.json +10 -198
- package/package.json +5 -1
- package/dist/commands/cipher-agent/run.d.ts +0 -142
- package/dist/commands/cipher-agent/run.js +0 -555
- package/dist/commands/cipher-agent/set-prompt.d.ts +0 -16
- package/dist/commands/cipher-agent/set-prompt.js +0 -58
- package/dist/commands/cipher-agent/show-prompt.d.ts +0 -13
- package/dist/commands/cipher-agent/show-prompt.js +0 -53
- package/dist/commands/foo.d.ts +0 -14
- package/dist/commands/foo.js +0 -66
- package/dist/infra/cipher/agent-service-factory.d.ts +0 -93
- package/dist/infra/cipher/cipher-agent-state-manager.d.ts +0 -63
- package/dist/infra/cipher/cipher-agent-state-manager.js +0 -108
- package/dist/infra/cipher/cipher-agent.d.ts +0 -182
- package/dist/infra/cipher/cipher-agent.js +0 -317
- package/dist/infra/cipher/system-prompt/simple-prompt-factory.d.ts +0 -106
- package/dist/infra/cipher/system-prompt/simple-prompt-factory.js +0 -297
- package/dist/infra/cipher/tools/implementations/find-knowledge-topics-tool.d.ts +0 -7
- package/dist/infra/cipher/tools/implementations/find-knowledge-topics-tool.js +0 -424
- package/dist/resources/prompts/modes/autonomous.yml +0 -9
- package/dist/resources/prompts/query-context-tree-retrieval.yml +0 -48
- package/dist/tui/contexts/consumer.d.ts +0 -31
- package/dist/tui/contexts/consumer.js +0 -56
- package/dist/tui/hooks/use-consumer.d.ts +0 -12
- package/dist/tui/hooks/use-consumer.js +0 -50
- package/dist/tui/hooks/use-queue-polling.d.ts +0 -31
- package/dist/tui/hooks/use-queue-polling.js +0 -90
- /package/dist/tui/contexts/{use-commands.d.ts → commands-context.d.ts} +0 -0
- /package/dist/tui/contexts/{use-mode.d.ts → mode-context.d.ts} +0 -0
- /package/dist/tui/contexts/{use-mode.js → mode-context.js} +0 -0
- /package/dist/tui/contexts/{use-theme.d.ts → theme-context.d.ts} +0 -0
- /package/dist/tui/contexts/{use-theme.js → theme-context.js} +0 -0
|
@@ -5,9 +5,113 @@ import { EOL } from 'node:os';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { DirectoryNotFoundError, EditOperationError, FileNotFoundError, FileTooLargeError, GlobOperationError, InvalidExtensionError, InvalidPathError, InvalidPatternError, PathBlockedError, PathNotAllowedError, PathTraversalError, ReadOperationError, SearchOperationError, ServiceNotInitializedError, StringNotFoundError, StringNotUniqueError, WriteOperationError, } from '../../../core/domain/cipher/errors/file-system-error.js';
|
|
7
7
|
import { getErrorMessage } from '../../../utils/error-helpers.js';
|
|
8
|
+
import { getMimeType, isBinaryFile, isMediaFile, isPdfFile } from './binary-utils.js';
|
|
8
9
|
import { createGitignoreFilter } from './gitignore-filter.js';
|
|
9
10
|
import { collectFileMetadata, escapeIfExactMatch, extractPaths, sortFilesByRecency } from './glob-utils.js';
|
|
10
11
|
import { PathValidator } from './path-validator.js';
|
|
12
|
+
/**
|
|
13
|
+
* Maximum line length for search results.
|
|
14
|
+
* Prevents context overflow from minified files or long lines.
|
|
15
|
+
*/
|
|
16
|
+
const MAX_LINE_LENGTH = 2000;
|
|
17
|
+
/**
|
|
18
|
+
* Default number of lines to read when no limit specified.
|
|
19
|
+
* Prevents context overflow while providing enough content.
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_READ_LIMIT = 2000;
|
|
22
|
+
/**
|
|
23
|
+
* Number of lines to include in the preview.
|
|
24
|
+
*/
|
|
25
|
+
const PREVIEW_LINES = 20;
|
|
26
|
+
/**
|
|
27
|
+
* Buffer size for binary file detection (4KB sample).
|
|
28
|
+
*/
|
|
29
|
+
const BINARY_DETECTION_BUFFER_SIZE = 4096;
|
|
30
|
+
/**
|
|
31
|
+
* Whitelist of .env file patterns that are safe to read.
|
|
32
|
+
* These are typically example/template files without real secrets.
|
|
33
|
+
*/
|
|
34
|
+
const ENV_WHITELIST = ['.env.sample', '.env.example', '.env.template', '.env.defaults'];
|
|
35
|
+
/**
|
|
36
|
+
* Truncates a line to MAX_LINE_LENGTH, adding ellipsis if truncated.
|
|
37
|
+
*/
|
|
38
|
+
function truncateLine(line) {
|
|
39
|
+
if (line.length <= MAX_LINE_LENGTH) {
|
|
40
|
+
return line;
|
|
41
|
+
}
|
|
42
|
+
return line.slice(0, MAX_LINE_LENGTH) + '...';
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Formats content with line numbers in 00001| format.
|
|
46
|
+
* @param lines - Array of lines to format
|
|
47
|
+
* @param startLine - Starting line number (1-based)
|
|
48
|
+
* @returns Formatted string with line numbers
|
|
49
|
+
*/
|
|
50
|
+
function formatWithLineNumbers(lines, startLine) {
|
|
51
|
+
return lines
|
|
52
|
+
.map((line, index) => {
|
|
53
|
+
const lineNum = (startLine + index).toString().padStart(5, '0');
|
|
54
|
+
return `${lineNum}| ${line}`;
|
|
55
|
+
})
|
|
56
|
+
.join('\n');
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Finds similar files in a directory for "Did you mean?" suggestions.
|
|
60
|
+
* @param dirPath - Directory to search in
|
|
61
|
+
* @param fileName - File name to find similar matches for
|
|
62
|
+
* @returns Array of suggested file paths (max 3)
|
|
63
|
+
*/
|
|
64
|
+
async function findSimilarFiles(dirPath, fileName) {
|
|
65
|
+
try {
|
|
66
|
+
const entries = await fs.readdir(dirPath);
|
|
67
|
+
const lowerFileName = fileName.toLowerCase();
|
|
68
|
+
const suggestions = entries
|
|
69
|
+
.filter((entry) => {
|
|
70
|
+
const lowerEntry = entry.toLowerCase();
|
|
71
|
+
return lowerEntry.includes(lowerFileName) || lowerFileName.includes(lowerEntry);
|
|
72
|
+
})
|
|
73
|
+
.slice(0, 3)
|
|
74
|
+
.map((entry) => path.join(dirPath, entry));
|
|
75
|
+
return suggestions;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Default patterns to ignore when listing directories.
|
|
83
|
+
* Common build artifacts, caches, and IDE directories.
|
|
84
|
+
*/
|
|
85
|
+
const DEFAULT_IGNORE_PATTERNS = [
|
|
86
|
+
'node_modules/',
|
|
87
|
+
'__pycache__/',
|
|
88
|
+
'.git/',
|
|
89
|
+
'dist/',
|
|
90
|
+
'build/',
|
|
91
|
+
'target/',
|
|
92
|
+
'vendor/',
|
|
93
|
+
'bin/',
|
|
94
|
+
'obj/',
|
|
95
|
+
'.idea/',
|
|
96
|
+
'.vscode/',
|
|
97
|
+
'.zig-cache/',
|
|
98
|
+
'zig-out/',
|
|
99
|
+
'.coverage/',
|
|
100
|
+
'coverage/',
|
|
101
|
+
'tmp/',
|
|
102
|
+
'temp/',
|
|
103
|
+
'.cache/',
|
|
104
|
+
'cache/',
|
|
105
|
+
'logs/',
|
|
106
|
+
'.venv/',
|
|
107
|
+
'venv/',
|
|
108
|
+
'env/',
|
|
109
|
+
'.byterover/',
|
|
110
|
+
];
|
|
111
|
+
/**
|
|
112
|
+
* Maximum number of files for directory listing.
|
|
113
|
+
*/
|
|
114
|
+
const LIST_DIRECTORY_LIMIT = 100;
|
|
11
115
|
/**
|
|
12
116
|
* File system service implementation.
|
|
13
117
|
* Provides secure, validated file system operations with comprehensive
|
|
@@ -200,17 +304,108 @@ export class FileSystemService {
|
|
|
200
304
|
}
|
|
201
305
|
this.initialized = true;
|
|
202
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* List files and directories in a path.
|
|
309
|
+
*/
|
|
310
|
+
async listDirectory(dirPath, options = {}) {
|
|
311
|
+
this.ensureInitialized();
|
|
312
|
+
// Resolve path
|
|
313
|
+
const resolvedPath = path.resolve(this.config.workingDirectory, dirPath || '.');
|
|
314
|
+
// Validate path
|
|
315
|
+
const validation = this.pathValidator.validate(resolvedPath, 'read');
|
|
316
|
+
if (!validation.valid) {
|
|
317
|
+
this.throwValidationError(resolvedPath, validation.error);
|
|
318
|
+
}
|
|
319
|
+
const { normalizedPath } = validation;
|
|
320
|
+
// Verify directory exists
|
|
321
|
+
try {
|
|
322
|
+
const stats = await fs.stat(normalizedPath);
|
|
323
|
+
if (!stats.isDirectory()) {
|
|
324
|
+
throw new DirectoryNotFoundError(normalizedPath);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
if (error.code === 'ENOENT') {
|
|
329
|
+
throw new DirectoryNotFoundError(normalizedPath);
|
|
330
|
+
}
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
const maxResults = options.maxResults ?? LIST_DIRECTORY_LIMIT;
|
|
334
|
+
const ignorePatterns = [...DEFAULT_IGNORE_PATTERNS, ...(options.ignore ?? [])];
|
|
335
|
+
// Use glob with ** pattern to get all files
|
|
336
|
+
const files = await glob('**/*', {
|
|
337
|
+
absolute: false,
|
|
338
|
+
cwd: normalizedPath,
|
|
339
|
+
follow: false,
|
|
340
|
+
ignore: ignorePatterns.map((p) => `**/${p}**`),
|
|
341
|
+
nodir: true,
|
|
342
|
+
});
|
|
343
|
+
// Limit results
|
|
344
|
+
const truncated = files.length > maxResults;
|
|
345
|
+
const limitedFiles = files.slice(0, maxResults);
|
|
346
|
+
// Build directory structure
|
|
347
|
+
const dirs = new Set();
|
|
348
|
+
const filesByDir = new Map();
|
|
349
|
+
for (const file of limitedFiles) {
|
|
350
|
+
const dir = path.dirname(file);
|
|
351
|
+
const parts = dir === '.' ? [] : dir.split('/');
|
|
352
|
+
// Add all parent directories
|
|
353
|
+
for (let i = 0; i <= parts.length; i++) {
|
|
354
|
+
const currentDirPath = i === 0 ? '.' : parts.slice(0, i).join('/');
|
|
355
|
+
dirs.add(currentDirPath);
|
|
356
|
+
}
|
|
357
|
+
// Add file to its directory
|
|
358
|
+
if (!filesByDir.has(dir)) {
|
|
359
|
+
filesByDir.set(dir, []);
|
|
360
|
+
}
|
|
361
|
+
filesByDir.get(dir).push(path.basename(file));
|
|
362
|
+
}
|
|
363
|
+
// Build entries array
|
|
364
|
+
const entries = limitedFiles.map((file) => ({
|
|
365
|
+
isDirectory: false,
|
|
366
|
+
name: path.basename(file),
|
|
367
|
+
path: file,
|
|
368
|
+
}));
|
|
369
|
+
// Render tree
|
|
370
|
+
const tree = this.renderDirectoryTree(normalizedPath, dirs, filesByDir);
|
|
371
|
+
return {
|
|
372
|
+
count: limitedFiles.length,
|
|
373
|
+
entries,
|
|
374
|
+
tree,
|
|
375
|
+
truncated,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
203
378
|
/**
|
|
204
379
|
* Read the contents of a file.
|
|
380
|
+
*
|
|
381
|
+
* Features:
|
|
382
|
+
* - Relative path support (resolved against working directory)
|
|
383
|
+
* - Image/PDF files returned as base64 attachments
|
|
384
|
+
* - Binary file detection and rejection
|
|
385
|
+
* - .env file blocking with whitelist for example files
|
|
386
|
+
* - XML-wrapped output for clearer LLM parsing
|
|
387
|
+
* - Preview metadata (first 20 lines)
|
|
205
388
|
*/
|
|
206
389
|
async readFile(filePath, options = {}) {
|
|
207
390
|
this.ensureInitialized();
|
|
391
|
+
// Resolve relative paths against working directory
|
|
392
|
+
let resolvedPath = filePath;
|
|
393
|
+
if (!path.isAbsolute(filePath)) {
|
|
394
|
+
resolvedPath = path.resolve(this.config.workingDirectory, filePath);
|
|
395
|
+
}
|
|
208
396
|
// Validate path
|
|
209
|
-
const validation = this.pathValidator.validate(
|
|
397
|
+
const validation = this.pathValidator.validate(resolvedPath, 'read');
|
|
210
398
|
if (!validation.valid) {
|
|
211
|
-
this.throwValidationError(
|
|
399
|
+
this.throwValidationError(resolvedPath, validation.error);
|
|
212
400
|
}
|
|
213
401
|
const { normalizedPath } = validation;
|
|
402
|
+
// Check .env file whitelist (allow .env.sample, .env.example, etc.)
|
|
403
|
+
const fileName = path.basename(normalizedPath).toLowerCase();
|
|
404
|
+
const isEnvFile = fileName.includes('.env');
|
|
405
|
+
const isWhitelisted = ENV_WHITELIST.some((w) => fileName.endsWith(w));
|
|
406
|
+
if (isEnvFile && !isWhitelisted) {
|
|
407
|
+
throw new PathBlockedError(normalizedPath, 'Environment files are blocked for security. Only example files (.env.sample, .env.example) are allowed.');
|
|
408
|
+
}
|
|
214
409
|
try {
|
|
215
410
|
// Check if file exists
|
|
216
411
|
let stats;
|
|
@@ -219,6 +414,13 @@ export class FileSystemService {
|
|
|
219
414
|
}
|
|
220
415
|
catch (error) {
|
|
221
416
|
if (error.code === 'ENOENT') {
|
|
417
|
+
// Try to find similar files for suggestions
|
|
418
|
+
const dirPath = path.dirname(normalizedPath);
|
|
419
|
+
const baseName = path.basename(normalizedPath);
|
|
420
|
+
const suggestions = await findSimilarFiles(dirPath, baseName);
|
|
421
|
+
if (suggestions.length > 0) {
|
|
422
|
+
throw new FileNotFoundError(normalizedPath, `File not found: ${normalizedPath}\n\nDid you mean one of these?\n${suggestions.join('\n')}`);
|
|
423
|
+
}
|
|
222
424
|
throw new FileNotFoundError(normalizedPath);
|
|
223
425
|
}
|
|
224
426
|
throw error;
|
|
@@ -227,30 +429,81 @@ export class FileSystemService {
|
|
|
227
429
|
if (stats.size > this.config.maxFileSize) {
|
|
228
430
|
throw new FileTooLargeError(normalizedPath, stats.size, this.config.maxFileSize);
|
|
229
431
|
}
|
|
230
|
-
//
|
|
432
|
+
// Handle image/PDF files - return as base64 attachment
|
|
433
|
+
if (isMediaFile(normalizedPath)) {
|
|
434
|
+
const buffer = await fs.readFile(normalizedPath);
|
|
435
|
+
const mimeType = getMimeType(normalizedPath) ?? 'application/octet-stream';
|
|
436
|
+
const fileType = isPdfFile(normalizedPath) ? 'PDF' : 'Image';
|
|
437
|
+
const baseName = path.basename(normalizedPath);
|
|
438
|
+
return {
|
|
439
|
+
attachment: {
|
|
440
|
+
base64: buffer.toString('base64'),
|
|
441
|
+
fileName: baseName,
|
|
442
|
+
mimeType,
|
|
443
|
+
},
|
|
444
|
+
content: `[${fileType} file: ${baseName}]`,
|
|
445
|
+
encoding: 'base64',
|
|
446
|
+
formattedContent: `<file>\n[${fileType} file: ${baseName} (${stats.size} bytes)]\n</file>`,
|
|
447
|
+
lines: 1,
|
|
448
|
+
message: `${fileType} file read successfully. Content available as base64 attachment.`,
|
|
449
|
+
size: stats.size,
|
|
450
|
+
totalLines: 1,
|
|
451
|
+
truncated: false,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
// Check for binary files (read first 4KB for detection)
|
|
455
|
+
const handle = await fs.open(normalizedPath, 'r');
|
|
456
|
+
const sampleBuffer = Buffer.alloc(BINARY_DETECTION_BUFFER_SIZE);
|
|
457
|
+
const { bytesRead } = await handle.read(sampleBuffer, 0, BINARY_DETECTION_BUFFER_SIZE, 0);
|
|
458
|
+
await handle.close();
|
|
459
|
+
if (isBinaryFile(normalizedPath, sampleBuffer.subarray(0, bytesRead))) {
|
|
460
|
+
throw new ReadOperationError(normalizedPath, 'Cannot read binary file. Use a hex editor or appropriate tool for binary files.');
|
|
461
|
+
}
|
|
462
|
+
// Read text file
|
|
231
463
|
const encoding = (options.encoding ?? 'utf8');
|
|
232
|
-
const
|
|
233
|
-
//
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
464
|
+
const rawContent = await fs.readFile(normalizedPath, encoding);
|
|
465
|
+
// Split into lines
|
|
466
|
+
const allLines = rawContent.split('\n');
|
|
467
|
+
const totalLines = allLines.length;
|
|
468
|
+
// Apply offset and limit (with default limit of 2000)
|
|
469
|
+
const offset = options.offset !== undefined && options.offset > 0 ? options.offset - 1 : 0;
|
|
470
|
+
const limit = options.limit ?? DEFAULT_READ_LIMIT;
|
|
471
|
+
const endLine = Math.min(offset + limit, totalLines);
|
|
472
|
+
const selectedLines = allLines.slice(offset, endLine);
|
|
473
|
+
const truncated = endLine < totalLines;
|
|
474
|
+
// Truncate long lines
|
|
475
|
+
const truncatedLines = selectedLines.map((line) => truncateLine(line));
|
|
476
|
+
// Format with line numbers
|
|
477
|
+
const numberedContent = formatWithLineNumbers(truncatedLines, offset + 1);
|
|
478
|
+
// Build informative message
|
|
479
|
+
const lastReadLine = offset + selectedLines.length;
|
|
480
|
+
let message;
|
|
481
|
+
if (truncated) {
|
|
482
|
+
const remainingLines = totalLines - lastReadLine;
|
|
483
|
+
message =
|
|
484
|
+
`Read lines ${offset + 1}-${lastReadLine} of ${totalLines} total lines. ` +
|
|
485
|
+
`${remainingLines} more lines available. Use offset=${lastReadLine + 1} to continue reading.`;
|
|
244
486
|
}
|
|
245
487
|
else {
|
|
246
|
-
|
|
488
|
+
message = `End of file - read ${selectedLines.length} lines (${totalLines} total).`;
|
|
247
489
|
}
|
|
248
|
-
|
|
490
|
+
// Generate preview (first N lines of selected content)
|
|
491
|
+
const previewLines = truncatedLines.slice(0, PREVIEW_LINES);
|
|
492
|
+
const preview = previewLines.join('\n');
|
|
493
|
+
// Wrap in XML tags for clearer LLM parsing
|
|
494
|
+
const truncationNote = truncated
|
|
495
|
+
? `\n\n(File has more lines. Use offset=${lastReadLine + 1} to continue)`
|
|
496
|
+
: `\n\n(End of file - ${totalLines} lines)`;
|
|
497
|
+
const formattedContent = `<file>\n${numberedContent}${truncationNote}\n</file>`;
|
|
249
498
|
return {
|
|
250
|
-
content:
|
|
499
|
+
content: truncatedLines.join('\n'),
|
|
251
500
|
encoding,
|
|
501
|
+
formattedContent,
|
|
252
502
|
lines: selectedLines.length,
|
|
503
|
+
message,
|
|
504
|
+
preview,
|
|
253
505
|
size: stats.size,
|
|
506
|
+
totalLines,
|
|
254
507
|
truncated,
|
|
255
508
|
};
|
|
256
509
|
}
|
|
@@ -260,7 +513,8 @@ export class FileSystemService {
|
|
|
260
513
|
error instanceof FileTooLargeError ||
|
|
261
514
|
error instanceof PathNotAllowedError ||
|
|
262
515
|
error instanceof PathTraversalError ||
|
|
263
|
-
error instanceof PathBlockedError
|
|
516
|
+
error instanceof PathBlockedError ||
|
|
517
|
+
error instanceof ReadOperationError) {
|
|
264
518
|
throw error;
|
|
265
519
|
}
|
|
266
520
|
// Wrap other errors
|
|
@@ -291,12 +545,24 @@ export class FileSystemService {
|
|
|
291
545
|
if (matches === null) {
|
|
292
546
|
return this.searchContentJS(pattern, options);
|
|
293
547
|
}
|
|
548
|
+
// Collect file modification times for sorting (recent files first)
|
|
549
|
+
const matchesWithMtime = await Promise.all(matches.map(async (match) => {
|
|
550
|
+
try {
|
|
551
|
+
const stats = await fs.stat(match.file);
|
|
552
|
+
return { ...match, mtime: stats.mtime.getTime() };
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
return { ...match, mtime: 0 };
|
|
556
|
+
}
|
|
557
|
+
}));
|
|
558
|
+
// Sort by modification time (newest first)
|
|
559
|
+
matchesWithMtime.sort((a, b) => (b.mtime ?? 0) - (a.mtime ?? 0));
|
|
294
560
|
// Apply maxResults limit
|
|
295
|
-
const truncated =
|
|
561
|
+
const truncated = matchesWithMtime.length > maxResults;
|
|
296
562
|
return {
|
|
297
|
-
filesSearched: new Set(
|
|
298
|
-
matches:
|
|
299
|
-
totalMatches:
|
|
563
|
+
filesSearched: new Set(matchesWithMtime.map((m) => m.file)).size,
|
|
564
|
+
matches: matchesWithMtime.slice(0, maxResults),
|
|
565
|
+
totalMatches: matchesWithMtime.length,
|
|
300
566
|
truncated,
|
|
301
567
|
};
|
|
302
568
|
}
|
|
@@ -373,11 +639,11 @@ export class FileSystemService {
|
|
|
373
639
|
if (contextLines > 0) {
|
|
374
640
|
// Lines before
|
|
375
641
|
for (let j = Math.max(0, lineIndex - contextLines); j < lineIndex; j++) {
|
|
376
|
-
before.push(lines[j]);
|
|
642
|
+
before.push(truncateLine(lines[j]));
|
|
377
643
|
}
|
|
378
644
|
// Lines after
|
|
379
645
|
for (let j = lineIndex + 1; j < Math.min(lines.length, lineIndex + 1 + contextLines); j++) {
|
|
380
|
-
after.push(lines[j]);
|
|
646
|
+
after.push(truncateLine(lines[j]));
|
|
381
647
|
}
|
|
382
648
|
}
|
|
383
649
|
return { after, before };
|
|
@@ -493,13 +759,38 @@ export class FileSystemService {
|
|
|
493
759
|
if (!Number.isNaN(lineNumber)) {
|
|
494
760
|
results.push({
|
|
495
761
|
file: path.resolve(basePath, filePath),
|
|
496
|
-
line: content,
|
|
762
|
+
line: truncateLine(content),
|
|
497
763
|
lineNumber,
|
|
498
764
|
});
|
|
499
765
|
}
|
|
500
766
|
}
|
|
501
767
|
return results;
|
|
502
768
|
}
|
|
769
|
+
/**
|
|
770
|
+
* Renders a directory tree as a string.
|
|
771
|
+
*/
|
|
772
|
+
renderDirectoryTree(basePath, dirs, filesByDir) {
|
|
773
|
+
const renderDir = (dirPath, depth) => {
|
|
774
|
+
const indent = ' '.repeat(depth);
|
|
775
|
+
let output = '';
|
|
776
|
+
if (depth > 0) {
|
|
777
|
+
output += `${indent}${path.basename(dirPath)}/\n`;
|
|
778
|
+
}
|
|
779
|
+
const childIndent = ' '.repeat(depth + 1);
|
|
780
|
+
const children = [...dirs].filter((d) => path.dirname(d) === dirPath && d !== dirPath).sort();
|
|
781
|
+
// Render subdirectories first
|
|
782
|
+
for (const child of children) {
|
|
783
|
+
output += renderDir(child, depth + 1);
|
|
784
|
+
}
|
|
785
|
+
// Render files
|
|
786
|
+
const files = filesByDir.get(dirPath) ?? [];
|
|
787
|
+
for (const file of files.sort()) {
|
|
788
|
+
output += `${childIndent}${file}\n`;
|
|
789
|
+
}
|
|
790
|
+
return output;
|
|
791
|
+
};
|
|
792
|
+
return `${basePath}/\n${renderDir('.', 0)}`;
|
|
793
|
+
}
|
|
503
794
|
/**
|
|
504
795
|
* JavaScript-based content search implementation.
|
|
505
796
|
* Used as fallback when native grep commands are unavailable or when context lines are needed.
|
|
@@ -548,7 +839,7 @@ export class FileSystemService {
|
|
|
548
839
|
matches.push({
|
|
549
840
|
context: contextLines > 0 ? context : undefined,
|
|
550
841
|
file: filePath,
|
|
551
|
-
line,
|
|
842
|
+
line: truncateLine(line),
|
|
552
843
|
lineNumber: i + 1, // 1-based line numbers
|
|
553
844
|
});
|
|
554
845
|
}
|
|
@@ -600,7 +891,7 @@ export class FileSystemService {
|
|
|
600
891
|
const child = spawn(cmd, args, { cwd, windowsHide: true });
|
|
601
892
|
const chunks = [];
|
|
602
893
|
if (signal) {
|
|
603
|
-
signal.addEventListener('abort', () => child.kill());
|
|
894
|
+
signal.addEventListener('abort', () => child.kill(), { once: true });
|
|
604
895
|
}
|
|
605
896
|
child.stdout.on('data', (chunk) => chunks.push(chunk));
|
|
606
897
|
child.on('error', reject);
|
|
@@ -618,24 +909,24 @@ export class FileSystemService {
|
|
|
618
909
|
/**
|
|
619
910
|
* Throws the appropriate error based on validation error message.
|
|
620
911
|
*/
|
|
621
|
-
throwValidationError(
|
|
912
|
+
throwValidationError(filePath, error) {
|
|
622
913
|
if (error.includes('empty')) {
|
|
623
|
-
throw new InvalidPathError(
|
|
914
|
+
throw new InvalidPathError(filePath, error);
|
|
624
915
|
}
|
|
625
916
|
if (error.includes('traversal')) {
|
|
626
|
-
throw new PathTraversalError(
|
|
917
|
+
throw new PathTraversalError(filePath);
|
|
627
918
|
}
|
|
628
919
|
if (error.includes('not in allowed paths')) {
|
|
629
|
-
throw new PathNotAllowedError(
|
|
920
|
+
throw new PathNotAllowedError(filePath, this.config.allowedPaths);
|
|
630
921
|
}
|
|
631
922
|
if (error.includes('blocked')) {
|
|
632
|
-
throw new PathBlockedError(
|
|
923
|
+
throw new PathBlockedError(filePath, error);
|
|
633
924
|
}
|
|
634
925
|
if (error.includes('extension')) {
|
|
635
|
-
const ext =
|
|
636
|
-
throw new InvalidExtensionError(
|
|
926
|
+
const ext = filePath.split('.').pop() ?? '';
|
|
927
|
+
throw new InvalidExtensionError(filePath, ext);
|
|
637
928
|
}
|
|
638
929
|
// Fallback
|
|
639
|
-
throw new InvalidPathError(
|
|
930
|
+
throw new InvalidPathError(filePath, error);
|
|
640
931
|
}
|
|
641
932
|
}
|
|
@@ -62,8 +62,40 @@ export declare class PathValidator {
|
|
|
62
62
|
* Normalizes and resolves a file path to an absolute path.
|
|
63
63
|
* Uses realpath to resolve symlinks if the path exists.
|
|
64
64
|
*
|
|
65
|
+
* Handles path duplication prevention:
|
|
66
|
+
* - If the working directory ends with a path segment that matches the start
|
|
67
|
+
* of the relative file path, the resolution is adjusted to prevent duplication.
|
|
68
|
+
* - Example: workingDir = "/project/.brv/context-tree", filePath = ".brv/context-tree/domain/file.md"
|
|
69
|
+
* Without fix: "/project/.brv/context-tree/.brv/context-tree/domain/file.md" (WRONG)
|
|
70
|
+
* With fix: "/project/.brv/context-tree/domain/file.md" (CORRECT)
|
|
71
|
+
*
|
|
65
72
|
* @param filePath - Path to normalize
|
|
66
73
|
* @returns Normalized absolute path
|
|
67
74
|
*/
|
|
68
75
|
private normalizeAndResolve;
|
|
76
|
+
/**
|
|
77
|
+
* For non-existent files, try to resolve the parent directory's real path
|
|
78
|
+
* and append the filename. This ensures consistency with existing paths
|
|
79
|
+
* when symlinks are involved (e.g., /var -> /private/var on macOS).
|
|
80
|
+
*
|
|
81
|
+
* @param filePath - Absolute path to a potentially non-existent file
|
|
82
|
+
* @returns Path with parent directory symlinks resolved
|
|
83
|
+
*/
|
|
84
|
+
private resolveParentRealPath;
|
|
85
|
+
/**
|
|
86
|
+
* Resolves a relative path against the working directory while preventing
|
|
87
|
+
* path segment duplication.
|
|
88
|
+
*
|
|
89
|
+
* @param filePath - Relative path to resolve
|
|
90
|
+
* @returns Resolved absolute path
|
|
91
|
+
*/
|
|
92
|
+
private resolveWithoutDuplication;
|
|
93
|
+
/**
|
|
94
|
+
* Checks if two arrays of path segments match.
|
|
95
|
+
*
|
|
96
|
+
* @param segments1 - First array of path segments
|
|
97
|
+
* @param segments2 - Second array of path segments
|
|
98
|
+
* @returns True if all segments match
|
|
99
|
+
*/
|
|
100
|
+
private segmentsMatch;
|
|
69
101
|
}
|
|
@@ -21,10 +21,28 @@ export class PathValidator {
|
|
|
21
21
|
* @param config - File system configuration
|
|
22
22
|
*/
|
|
23
23
|
constructor(config) {
|
|
24
|
-
|
|
24
|
+
// Resolve working directory, trying to resolve symlinks for consistency
|
|
25
|
+
const resolvedWorkingDir = path.resolve(config.workingDirectory);
|
|
26
|
+
try {
|
|
27
|
+
this.workingDirectory = realpathSync.native(resolvedWorkingDir);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
this.workingDirectory = resolvedWorkingDir;
|
|
31
|
+
}
|
|
25
32
|
this.blockedExtensions = new Set(config.blockedExtensions.map((ext) => ext.toLowerCase()));
|
|
26
33
|
// Normalize and resolve all allowed paths to absolute paths
|
|
27
|
-
|
|
34
|
+
// Also try to resolve symlinks for consistent comparison with realpathSync results
|
|
35
|
+
this.normalizedAllowedPaths = config.allowedPaths.map((allowedPath) => {
|
|
36
|
+
const resolved = path.resolve(this.workingDirectory, allowedPath);
|
|
37
|
+
try {
|
|
38
|
+
// Try to get the real path (resolving symlinks like /var -> /private/var on macOS)
|
|
39
|
+
return realpathSync.native(resolved);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Path might not exist yet, use resolved path as-is
|
|
43
|
+
return resolved;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
28
46
|
// Normalize blocked paths
|
|
29
47
|
this.normalizedBlockedPaths = config.blockedPaths.map((blockedPath) => path.isAbsolute(blockedPath) ? path.normalize(blockedPath) : blockedPath);
|
|
30
48
|
}
|
|
@@ -164,21 +182,108 @@ export class PathValidator {
|
|
|
164
182
|
* Normalizes and resolves a file path to an absolute path.
|
|
165
183
|
* Uses realpath to resolve symlinks if the path exists.
|
|
166
184
|
*
|
|
185
|
+
* Handles path duplication prevention:
|
|
186
|
+
* - If the working directory ends with a path segment that matches the start
|
|
187
|
+
* of the relative file path, the resolution is adjusted to prevent duplication.
|
|
188
|
+
* - Example: workingDir = "/project/.brv/context-tree", filePath = ".brv/context-tree/domain/file.md"
|
|
189
|
+
* Without fix: "/project/.brv/context-tree/.brv/context-tree/domain/file.md" (WRONG)
|
|
190
|
+
* With fix: "/project/.brv/context-tree/domain/file.md" (CORRECT)
|
|
191
|
+
*
|
|
167
192
|
* @param filePath - Path to normalize
|
|
168
193
|
* @returns Normalized absolute path
|
|
169
194
|
*/
|
|
170
195
|
normalizeAndResolve(filePath) {
|
|
171
|
-
//
|
|
172
|
-
|
|
196
|
+
// If the path is already absolute, just normalize it.
|
|
197
|
+
// For relative paths, check for potential path duplication.
|
|
198
|
+
// This can happen when:
|
|
199
|
+
// 1. workingDirectory ends with a subdirectory like ".brv/context-tree"
|
|
200
|
+
// 2. filePath starts with the same subdirectory ".brv/context-tree/..."
|
|
201
|
+
// In this case, we should resolve from the parent to avoid duplication.
|
|
202
|
+
let normalizedPath = path.isAbsolute(filePath)
|
|
203
|
+
? path.normalize(filePath)
|
|
204
|
+
: this.resolveWithoutDuplication(filePath);
|
|
173
205
|
// Try to resolve symlinks if path exists
|
|
174
206
|
try {
|
|
175
207
|
// Use native variant to preserve casing on Windows
|
|
176
208
|
normalizedPath = realpathSync.native(normalizedPath);
|
|
177
209
|
}
|
|
178
210
|
catch {
|
|
179
|
-
// Path doesn't exist yet (e.g., for writes)
|
|
180
|
-
|
|
211
|
+
// Path doesn't exist yet (e.g., for writes)
|
|
212
|
+
// Try to resolve the parent directory's real path for consistency
|
|
213
|
+
normalizedPath = this.resolveParentRealPath(normalizedPath);
|
|
181
214
|
}
|
|
182
215
|
return normalizedPath;
|
|
183
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* For non-existent files, try to resolve the parent directory's real path
|
|
219
|
+
* and append the filename. This ensures consistency with existing paths
|
|
220
|
+
* when symlinks are involved (e.g., /var -> /private/var on macOS).
|
|
221
|
+
*
|
|
222
|
+
* @param filePath - Absolute path to a potentially non-existent file
|
|
223
|
+
* @returns Path with parent directory symlinks resolved
|
|
224
|
+
*/
|
|
225
|
+
resolveParentRealPath(filePath) {
|
|
226
|
+
const dir = path.dirname(filePath);
|
|
227
|
+
const base = path.basename(filePath);
|
|
228
|
+
try {
|
|
229
|
+
// Try to resolve the parent directory
|
|
230
|
+
const realDir = realpathSync.native(dir);
|
|
231
|
+
return path.join(realDir, base);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// Parent doesn't exist either, try grandparent
|
|
235
|
+
const grandparentDir = path.dirname(dir);
|
|
236
|
+
const parentBase = path.basename(dir);
|
|
237
|
+
try {
|
|
238
|
+
const realGrandparent = realpathSync.native(grandparentDir);
|
|
239
|
+
return path.join(realGrandparent, parentBase, base);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// Give up and return normalized path
|
|
243
|
+
return path.normalize(filePath);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Resolves a relative path against the working directory while preventing
|
|
249
|
+
* path segment duplication.
|
|
250
|
+
*
|
|
251
|
+
* @param filePath - Relative path to resolve
|
|
252
|
+
* @returns Resolved absolute path
|
|
253
|
+
*/
|
|
254
|
+
resolveWithoutDuplication(filePath) {
|
|
255
|
+
// Normalize the file path to handle different separators
|
|
256
|
+
const normalizedFilePath = path.normalize(filePath);
|
|
257
|
+
// Split both paths into segments for comparison
|
|
258
|
+
const workingDirSegments = this.workingDirectory.split(path.sep).filter(Boolean);
|
|
259
|
+
const filePathSegments = normalizedFilePath.split(path.sep).filter(Boolean);
|
|
260
|
+
// Try to find a matching suffix of workingDirectory that matches
|
|
261
|
+
// the prefix of filePath to detect potential duplication
|
|
262
|
+
for (let suffixLen = Math.min(workingDirSegments.length, filePathSegments.length); suffixLen > 0; suffixLen--) {
|
|
263
|
+
const workingDirSuffix = workingDirSegments.slice(-suffixLen);
|
|
264
|
+
const filePathPrefix = filePathSegments.slice(0, suffixLen);
|
|
265
|
+
// Check if the suffix of working dir matches the prefix of file path
|
|
266
|
+
if (this.segmentsMatch(workingDirSuffix, filePathPrefix)) {
|
|
267
|
+
// Found a match! Remove the duplicate prefix from file path
|
|
268
|
+
// and resolve from working directory
|
|
269
|
+
const remainingSegments = filePathSegments.slice(suffixLen);
|
|
270
|
+
return path.join(this.workingDirectory, ...remainingSegments);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// No duplication detected, resolve normally
|
|
274
|
+
return path.resolve(this.workingDirectory, filePath);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Checks if two arrays of path segments match.
|
|
278
|
+
*
|
|
279
|
+
* @param segments1 - First array of path segments
|
|
280
|
+
* @param segments2 - Second array of path segments
|
|
281
|
+
* @returns True if all segments match
|
|
282
|
+
*/
|
|
283
|
+
segmentsMatch(segments1, segments2) {
|
|
284
|
+
if (segments1.length !== segments2.length) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
return segments1.every((seg, i) => seg === segments2[i]);
|
|
288
|
+
}
|
|
184
289
|
}
|