@vybestack/llxprt-code-core 0.1.15 → 0.1.16
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 +37 -2
- package/dist/src/code_assist/converter.d.ts +2 -1
- package/dist/src/code_assist/converter.js +2 -1
- package/dist/src/code_assist/converter.js.map +1 -1
- package/dist/src/code_assist/converter.test.js +13 -10
- package/dist/src/code_assist/converter.test.js.map +1 -1
- package/dist/src/code_assist/server.d.ts +2 -2
- package/dist/src/code_assist/server.js +4 -4
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +9 -9
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/code_assist/setup.js +1 -1
- package/dist/src/code_assist/setup.js.map +1 -1
- package/dist/src/code_assist/setup.test.js +2 -2
- package/dist/src/code_assist/setup.test.js.map +1 -1
- package/dist/src/config/config.d.ts +22 -4
- package/dist/src/config/config.js +58 -10
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +28 -0
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/flashFallback.test.js +11 -1
- package/dist/src/config/flashFallback.test.js.map +1 -1
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/config/profileManager.d.ts +42 -0
- package/dist/src/config/profileManager.js +114 -0
- package/dist/src/config/profileManager.js.map +1 -0
- package/dist/src/core/client.d.ts +11 -1
- package/dist/src/core/client.js +86 -30
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.d.ts +1 -1
- package/dist/src/core/client.test.js +95 -28
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.d.ts +2 -2
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/coreToolScheduler.js +39 -15
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +14 -17
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.js +34 -2
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +2 -2
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.js +11 -3
- package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +5 -2
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/prompts-async.test.d.ts +6 -0
- package/dist/src/core/prompts-async.test.js +115 -0
- package/dist/src/core/prompts-async.test.js.map +1 -0
- package/dist/src/core/prompts.d.ts +8 -1
- package/dist/src/core/prompts.js +97 -377
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/prompts.test.js +46 -192
- package/dist/src/core/prompts.test.js.map +1 -1
- package/dist/src/core/tokenLimits.d.ts +1 -1
- package/dist/src/core/tokenLimits.js +5 -1
- package/dist/src/core/tokenLimits.js.map +1 -1
- package/dist/src/core/turn.d.ts +3 -0
- package/dist/src/core/turn.js +3 -0
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/ide/detect-ide.d.ts +10 -0
- package/dist/src/ide/detect-ide.js +24 -0
- package/dist/src/ide/detect-ide.js.map +1 -0
- package/dist/src/ide/ide-client.d.ts +11 -1
- package/dist/src/ide/ide-client.js +37 -1
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ide-installer.d.ts +15 -0
- package/dist/src/ide/ide-installer.js +111 -0
- package/dist/src/ide/ide-installer.js.map +1 -0
- package/dist/src/ide/ide-installer.test.d.ts +6 -0
- package/dist/src/ide/ide-installer.test.js +78 -0
- package/dist/src/ide/ide-installer.test.js.map +1 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +11 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/oauth-provider.js +3 -3
- package/dist/src/mcp/oauth-provider.js.map +1 -1
- package/dist/src/mcp/oauth-provider.test.js +9 -8
- package/dist/src/mcp/oauth-provider.test.js.map +1 -1
- package/dist/src/parsers/TextToolCallParser.d.ts +1 -0
- package/dist/src/parsers/TextToolCallParser.js +45 -3
- package/dist/src/parsers/TextToolCallParser.js.map +1 -1
- package/dist/src/parsers/TextToolCallParser.multibyte.test.d.ts +1 -0
- package/dist/src/parsers/TextToolCallParser.multibyte.test.js +42 -0
- package/dist/src/parsers/TextToolCallParser.multibyte.test.js.map +1 -0
- package/dist/src/prompt-config/TemplateEngine.d.ts +35 -0
- package/dist/src/prompt-config/TemplateEngine.js +149 -0
- package/dist/src/prompt-config/TemplateEngine.js.map +1 -0
- package/dist/src/prompt-config/TemplateEngine.test.d.ts +1 -0
- package/dist/src/prompt-config/TemplateEngine.test.js +494 -0
- package/dist/src/prompt-config/TemplateEngine.test.js.map +1 -0
- package/dist/src/prompt-config/defaults/compression.md +58 -0
- package/dist/src/prompt-config/defaults/core-defaults.d.ts +5 -0
- package/dist/src/prompt-config/defaults/core-defaults.js +332 -0
- package/dist/src/prompt-config/defaults/core-defaults.js.map +1 -0
- package/dist/src/prompt-config/defaults/core.md +267 -0
- package/dist/src/prompt-config/defaults/env/git-repository.md +15 -0
- package/dist/src/prompt-config/defaults/env/ide-mode.md +3 -0
- package/dist/src/prompt-config/defaults/env/macos-seatbelt.md +3 -0
- package/dist/src/prompt-config/defaults/env/outside-of-sandbox.md +3 -0
- package/dist/src/prompt-config/defaults/env/sandbox.md +3 -0
- package/dist/src/prompt-config/defaults/index.d.ts +14 -0
- package/dist/src/prompt-config/defaults/index.js +21 -0
- package/dist/src/prompt-config/defaults/index.js.map +1 -0
- package/dist/src/prompt-config/defaults/provider-defaults.d.ts +5 -0
- package/dist/src/prompt-config/defaults/provider-defaults.js +26 -0
- package/dist/src/prompt-config/defaults/provider-defaults.js.map +1 -0
- package/dist/src/prompt-config/defaults/providers/gemini/models/gemini-2.5-flash/core.md +10 -0
- package/dist/src/prompt-config/defaults/service-defaults.d.ts +5 -0
- package/dist/src/prompt-config/defaults/service-defaults.js +52 -0
- package/dist/src/prompt-config/defaults/service-defaults.js.map +1 -0
- package/dist/src/prompt-config/defaults/tool-defaults.d.ts +5 -0
- package/dist/src/prompt-config/defaults/tool-defaults.js +81 -0
- package/dist/src/prompt-config/defaults/tool-defaults.js.map +1 -0
- package/dist/src/prompt-config/defaults/tools/edit.md +2 -0
- package/dist/src/prompt-config/defaults/tools/glob.md +2 -0
- package/dist/src/prompt-config/defaults/tools/grep.md +2 -0
- package/dist/src/prompt-config/defaults/tools/ls.md +1 -0
- package/dist/src/prompt-config/defaults/tools/memory.md +1 -0
- package/dist/src/prompt-config/defaults/tools/read-file.md +4 -0
- package/dist/src/prompt-config/defaults/tools/read-many-files.md +2 -0
- package/dist/src/prompt-config/defaults/tools/shell.md +5 -0
- package/dist/src/prompt-config/defaults/tools/todo-read.md +3 -0
- package/dist/src/prompt-config/defaults/tools/todo-write.md +50 -0
- package/dist/src/prompt-config/defaults/tools/web-fetch.md +3 -0
- package/dist/src/prompt-config/defaults/tools/web-search.md +3 -0
- package/dist/src/prompt-config/defaults/tools/write-file.md +4 -0
- package/dist/src/prompt-config/index.d.ts +16 -0
- package/dist/src/prompt-config/index.js +11 -0
- package/dist/src/prompt-config/index.js.map +1 -0
- package/dist/src/prompt-config/prompt-cache.d.ts +72 -0
- package/dist/src/prompt-config/prompt-cache.js +271 -0
- package/dist/src/prompt-config/prompt-cache.js.map +1 -0
- package/dist/src/prompt-config/prompt-cache.test.d.ts +6 -0
- package/dist/src/prompt-config/prompt-cache.test.js +437 -0
- package/dist/src/prompt-config/prompt-cache.test.js.map +1 -0
- package/dist/src/prompt-config/prompt-installer.d.ts +118 -0
- package/dist/src/prompt-config/prompt-installer.js +723 -0
- package/dist/src/prompt-config/prompt-installer.js.map +1 -0
- package/dist/src/prompt-config/prompt-installer.test.d.ts +7 -0
- package/dist/src/prompt-config/prompt-installer.test.js +503 -0
- package/dist/src/prompt-config/prompt-installer.test.js.map +1 -0
- package/dist/src/prompt-config/prompt-loader.d.ts +49 -0
- package/dist/src/prompt-config/prompt-loader.js +331 -0
- package/dist/src/prompt-config/prompt-loader.js.map +1 -0
- package/dist/src/prompt-config/prompt-loader.test.d.ts +5 -0
- package/dist/src/prompt-config/prompt-loader.test.js +413 -0
- package/dist/src/prompt-config/prompt-loader.test.js.map +1 -0
- package/dist/src/prompt-config/prompt-resolver.d.ts +74 -0
- package/dist/src/prompt-config/prompt-resolver.js +600 -0
- package/dist/src/prompt-config/prompt-resolver.js.map +1 -0
- package/dist/src/prompt-config/prompt-resolver.test.d.ts +1 -0
- package/dist/src/prompt-config/prompt-resolver.test.js +529 -0
- package/dist/src/prompt-config/prompt-resolver.test.js.map +1 -0
- package/dist/src/prompt-config/prompt-service.d.ts +108 -0
- package/dist/src/prompt-config/prompt-service.js +435 -0
- package/dist/src/prompt-config/prompt-service.js.map +1 -0
- package/dist/src/prompt-config/prompt-service.test.d.ts +1 -0
- package/dist/src/prompt-config/prompt-service.test.js +811 -0
- package/dist/src/prompt-config/prompt-service.test.js.map +1 -0
- package/dist/src/prompt-config/types.d.ts +30 -0
- package/dist/src/prompt-config/types.js +10 -0
- package/dist/src/prompt-config/types.js.map +1 -0
- package/dist/src/providers/IProvider.d.ts +10 -0
- package/dist/src/providers/anthropic/AnthropicProvider.d.ts +14 -1
- package/dist/src/providers/anthropic/AnthropicProvider.js +28 -2
- package/dist/src/providers/anthropic/AnthropicProvider.js.map +1 -1
- package/dist/src/providers/anthropic/AnthropicProvider.modelParams.test.d.ts +1 -0
- package/dist/src/providers/anthropic/AnthropicProvider.modelParams.test.js +48 -0
- package/dist/src/providers/anthropic/AnthropicProvider.modelParams.test.js.map +1 -0
- package/dist/src/providers/anthropic/AnthropicProvider.test.js +2 -1
- package/dist/src/providers/anthropic/AnthropicProvider.test.js.map +1 -1
- package/dist/src/providers/gemini/GeminiProvider.d.ts +11 -2
- package/dist/src/providers/gemini/GeminiProvider.integration.test.js +1 -1
- package/dist/src/providers/gemini/GeminiProvider.js +34 -9
- package/dist/src/providers/gemini/GeminiProvider.js.map +1 -1
- package/dist/src/providers/openai/OpenAIProvider.d.ts +11 -0
- package/dist/src/providers/openai/OpenAIProvider.js +100 -31
- package/dist/src/providers/openai/OpenAIProvider.js.map +1 -1
- package/dist/src/providers/openai/OpenAIProvider.responses.test.js +27 -3
- package/dist/src/providers/openai/OpenAIProvider.responses.test.js.map +1 -1
- package/dist/src/providers/openai/OpenAIProvider.shouldUseResponses.test.js +2 -1
- package/dist/src/providers/openai/OpenAIProvider.shouldUseResponses.test.js.map +1 -1
- package/dist/src/providers/openai/OpenAIProvider.test.js +307 -7
- package/dist/src/providers/openai/OpenAIProvider.test.js.map +1 -1
- package/dist/src/providers/openai/buildResponsesRequest.js +19 -2
- package/dist/src/providers/openai/buildResponsesRequest.js.map +1 -1
- package/dist/src/providers/test-utils/providerTestConfig.d.ts +21 -0
- package/dist/src/providers/test-utils/providerTestConfig.js +23 -0
- package/dist/src/providers/test-utils/providerTestConfig.js.map +1 -0
- package/dist/src/providers/types/IProviderConfig.d.ts +6 -0
- package/dist/src/services/loopDetectionService.d.ts +9 -0
- package/dist/src/services/loopDetectionService.js +44 -12
- package/dist/src/services/loopDetectionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.d.ts +1 -37
- package/dist/src/services/shellExecutionService.js +24 -55
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/{providers/IProviderConfig.js → services/shellExecutionService.multibyte.test.d.ts} +0 -1
- package/dist/src/services/shellExecutionService.multibyte.test.js +72 -0
- package/dist/src/services/shellExecutionService.multibyte.test.js.map +1 -0
- package/dist/src/services/shellExecutionService.test.js +11 -5
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/services/shellExecutionService.windows.multibyte.test.d.ts +6 -0
- package/dist/src/services/shellExecutionService.windows.multibyte.test.js +98 -0
- package/dist/src/services/shellExecutionService.windows.multibyte.test.js.map +1 -0
- package/dist/src/services/shellExecutionService.windows.test.d.ts +6 -0
- package/dist/src/services/shellExecutionService.windows.test.js +79 -0
- package/dist/src/services/shellExecutionService.windows.test.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +3 -2
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +22 -3
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +4 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +12 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/constants.d.ts +1 -1
- package/dist/src/telemetry/constants.js +1 -1
- package/dist/src/telemetry/constants.js.map +1 -1
- package/dist/src/telemetry/file-exporters.d.ts +2 -2
- package/dist/src/telemetry/file-exporters.js +47 -20
- package/dist/src/telemetry/file-exporters.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +2 -2
- package/dist/src/telemetry/loggers.js +13 -6
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.circular.js +2 -0
- package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +8 -3
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/sdk.d.ts +1 -1
- package/dist/src/telemetry/sdk.js +71 -73
- package/dist/src/telemetry/sdk.js.map +1 -1
- package/dist/src/telemetry/telemetry.test.js +7 -5
- package/dist/src/telemetry/telemetry.test.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +12 -4
- package/dist/src/telemetry/types.js +18 -4
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +3 -0
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
- package/dist/src/test-utils/mockWorkspaceContext.d.ts +13 -0
- package/dist/src/test-utils/mockWorkspaceContext.js +24 -0
- package/dist/src/test-utils/mockWorkspaceContext.js.map +1 -0
- package/dist/src/tools/edit.js +27 -3
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +104 -1
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/glob.js +53 -17
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/glob.test.js +25 -2
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/grep.d.ts +1 -1
- package/dist/src/tools/grep.js +81 -29
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/grep.test.js +72 -5
- package/dist/src/tools/grep.test.js.map +1 -1
- package/dist/src/tools/ls.js +4 -3
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/ls.test.d.ts +6 -0
- package/dist/src/tools/ls.test.js +356 -0
- package/dist/src/tools/ls.test.js.map +1 -0
- package/dist/src/tools/mcp-client.js +8 -3
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +34 -0
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/memoryTool.d.ts +17 -2
- package/dist/src/tools/memoryTool.js +130 -13
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/memoryTool.test.js +88 -3
- package/dist/src/tools/memoryTool.test.js.map +1 -1
- package/dist/src/tools/read-file.js +8 -6
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-file.test.js +30 -2
- package/dist/src/tools/read-file.test.js.map +1 -1
- package/dist/src/tools/read-many-files.js +22 -12
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/read-many-files.test.js +35 -2
- package/dist/src/tools/read-many-files.test.js.map +1 -1
- package/dist/src/tools/shell.js +16 -0
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.multibyte.test.d.ts +6 -0
- package/dist/src/tools/shell.multibyte.test.js +74 -0
- package/dist/src/tools/shell.multibyte.test.js.map +1 -0
- package/dist/src/tools/shell.test.js +33 -1
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/tool-error.d.ts +22 -0
- package/dist/src/tools/tool-error.js +27 -0
- package/dist/src/tools/tool-error.js.map +1 -0
- package/dist/src/tools/tool-registry.test.js +8 -0
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +8 -0
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/write-file.js +5 -3
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js +36 -2
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/types/modelParams.d.ts +62 -0
- package/dist/src/types/modelParams.js +7 -0
- package/dist/src/types/modelParams.js.map +1 -0
- package/dist/src/utils/bfsFileSearch.test.js +4 -2
- package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
- package/dist/src/utils/editCorrector.js +2 -2
- package/dist/src/utils/editCorrector.js.map +1 -1
- package/dist/src/utils/editor.d.ts +1 -1
- package/dist/src/utils/editor.js +9 -0
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/editor.test.js +21 -1
- package/dist/src/utils/editor.test.js.map +1 -1
- package/dist/src/utils/fileUtils.js +12 -1
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +29 -1
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/flashFallback.integration.test.js +8 -0
- package/dist/src/utils/flashFallback.integration.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.d.ts +1 -1
- package/dist/src/utils/memoryDiscovery.js +61 -73
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +4 -3
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.d.ts +19 -12
- package/dist/src/utils/memoryImportProcessor.js +241 -82
- package/dist/src/utils/memoryImportProcessor.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.test.js +595 -50
- package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.js +10 -25
- package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.test.js +6 -2
- package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
- package/dist/src/utils/sanitization.d.ts +20 -0
- package/dist/src/utils/sanitization.js +66 -0
- package/dist/src/utils/sanitization.js.map +1 -0
- package/dist/src/utils/sanitization.test.d.ts +6 -0
- package/dist/src/utils/sanitization.test.js +81 -0
- package/dist/src/utils/sanitization.test.js.map +1 -0
- package/dist/src/utils/secure-browser-launcher.d.ts +23 -0
- package/dist/src/utils/secure-browser-launcher.js +164 -0
- package/dist/src/utils/secure-browser-launcher.js.map +1 -0
- package/dist/src/utils/secure-browser-launcher.test.d.ts +6 -0
- package/dist/src/utils/secure-browser-launcher.test.js +149 -0
- package/dist/src/utils/secure-browser-launcher.test.js.map +1 -0
- package/dist/src/utils/summarizer.js +2 -2
- package/dist/src/utils/summarizer.js.map +1 -1
- package/dist/src/utils/unicodeUtils.d.ts +44 -0
- package/dist/src/utils/unicodeUtils.js +93 -0
- package/dist/src/utils/unicodeUtils.js.map +1 -0
- package/dist/src/utils/unicodeUtils.test.d.ts +6 -0
- package/dist/src/utils/unicodeUtils.test.js +120 -0
- package/dist/src/utils/unicodeUtils.test.js.map +1 -0
- package/dist/src/utils/workspaceContext.d.ts +47 -0
- package/dist/src/utils/workspaceContext.js +106 -0
- package/dist/src/utils/workspaceContext.js.map +1 -0
- package/dist/src/utils/workspaceContext.test.d.ts +6 -0
- package/dist/src/utils/workspaceContext.test.js +209 -0
- package/dist/src/utils/workspaceContext.test.js.map +1 -0
- package/package.json +2 -1
- package/dist/src/providers/IProviderConfig.d.ts +0 -31
- package/dist/src/providers/IProviderConfig.js.map +0 -1
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Installer - Creates directory structure and installs default prompt files
|
|
3
|
+
* while preserving user customizations.
|
|
4
|
+
*
|
|
5
|
+
* This is a TDD stub implementation. All methods throw "Not implemented" errors.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import * as os from 'os';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
// Constants
|
|
13
|
+
export const DEFAULT_BASE_DIR = '~/.llxprt/prompts';
|
|
14
|
+
export const REQUIRED_DIRECTORIES = [
|
|
15
|
+
'', // Base directory
|
|
16
|
+
'env', // Environment-specific prompts
|
|
17
|
+
'tools', // Tool-specific prompts
|
|
18
|
+
'providers', // Provider overrides
|
|
19
|
+
];
|
|
20
|
+
// Schema for defaults map
|
|
21
|
+
export const DefaultsMapSchema = z.record(z.string(), z.string());
|
|
22
|
+
/**
|
|
23
|
+
* PromptInstaller handles installation, validation, and maintenance of prompt files
|
|
24
|
+
*/
|
|
25
|
+
export class PromptInstaller {
|
|
26
|
+
/**
|
|
27
|
+
* Install default prompt files
|
|
28
|
+
* @param baseDir - Base directory for prompts (defaults to DEFAULT_BASE_DIR)
|
|
29
|
+
* @param defaults - Map of relative path to file content
|
|
30
|
+
* @param options - Installation options
|
|
31
|
+
* @returns Installation result with success status and details
|
|
32
|
+
*/
|
|
33
|
+
async install(baseDir, defaults, options) {
|
|
34
|
+
const installed = [];
|
|
35
|
+
const skipped = [];
|
|
36
|
+
const errors = [];
|
|
37
|
+
// Prepare installation
|
|
38
|
+
let expandedBaseDir = baseDir;
|
|
39
|
+
if (!expandedBaseDir) {
|
|
40
|
+
expandedBaseDir = this.expandPath(DEFAULT_BASE_DIR);
|
|
41
|
+
}
|
|
42
|
+
// Validate baseDir
|
|
43
|
+
if (expandedBaseDir.includes('..') || !path.isAbsolute(expandedBaseDir)) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
installed: [],
|
|
47
|
+
skipped: [],
|
|
48
|
+
errors: ['Invalid base directory'],
|
|
49
|
+
baseDir: expandedBaseDir,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Create directory structure
|
|
53
|
+
for (const dir of REQUIRED_DIRECTORIES) {
|
|
54
|
+
const fullPath = path.join(expandedBaseDir, dir);
|
|
55
|
+
if (options?.dryRun) {
|
|
56
|
+
if (options?.verbose) {
|
|
57
|
+
console.log('Would create:', fullPath);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
try {
|
|
62
|
+
await fs.mkdir(fullPath, { recursive: true, mode: 0o755 });
|
|
63
|
+
if (options?.verbose) {
|
|
64
|
+
console.log('Created directory:', fullPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
69
|
+
if (errorMsg.includes('EACCES') ||
|
|
70
|
+
errorMsg.includes('permission denied')) {
|
|
71
|
+
errors.push(`Permission denied: ${fullPath}`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
errors.push(`Failed to create directory ${fullPath}: ${errorMsg}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Install default files
|
|
80
|
+
for (const [relativePath, content] of Object.entries(defaults)) {
|
|
81
|
+
const fullPath = path.join(expandedBaseDir, relativePath);
|
|
82
|
+
const fileDir = path.dirname(fullPath);
|
|
83
|
+
// Create parent directory if needed
|
|
84
|
+
if (!existsSync(fileDir) && !options?.dryRun) {
|
|
85
|
+
try {
|
|
86
|
+
await fs.mkdir(fileDir, { recursive: true, mode: 0o755 });
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
errors.push(`Failed to create directory ${fileDir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Check existing file
|
|
94
|
+
if (!options?.dryRun && existsSync(fullPath) && !options?.force) {
|
|
95
|
+
skipped.push(relativePath);
|
|
96
|
+
if (options?.verbose) {
|
|
97
|
+
console.log('Preserving existing:', relativePath);
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
// Write file
|
|
102
|
+
if (options?.dryRun) {
|
|
103
|
+
if (options?.verbose) {
|
|
104
|
+
console.log('Would write:', fullPath);
|
|
105
|
+
}
|
|
106
|
+
installed.push(relativePath);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const tempPath = `${fullPath}.tmp.${Date.now()}.${Math.random().toString(36).substring(7)}`;
|
|
110
|
+
try {
|
|
111
|
+
// Write to temp file first (atomic write)
|
|
112
|
+
await fs.writeFile(tempPath, content, { mode: 0o644 });
|
|
113
|
+
// Rename temp to final - this is atomic and will fail if file exists
|
|
114
|
+
try {
|
|
115
|
+
await fs.rename(tempPath, fullPath);
|
|
116
|
+
installed.push(relativePath);
|
|
117
|
+
if (options?.verbose) {
|
|
118
|
+
console.log('Installed:', relativePath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (renameError) {
|
|
122
|
+
// If rename failed because file already exists, it's OK (race condition)
|
|
123
|
+
const renameMsg = renameError instanceof Error
|
|
124
|
+
? renameError.message
|
|
125
|
+
: String(renameError);
|
|
126
|
+
if (renameMsg.includes('EEXIST') || existsSync(fullPath)) {
|
|
127
|
+
skipped.push(relativePath);
|
|
128
|
+
// Clean up temp file
|
|
129
|
+
await fs.unlink(tempPath);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
throw renameError;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
138
|
+
if (errorMsg.includes('EACCES') ||
|
|
139
|
+
errorMsg.includes('Permission denied')) {
|
|
140
|
+
errors.push(`Permission denied: ${fullPath}. Try running with elevated permissions or changing the directory ownership.`);
|
|
141
|
+
}
|
|
142
|
+
else if (errorMsg.includes('ENOSPC')) {
|
|
143
|
+
errors.push(`Disk full: Cannot write ${fullPath}. Free up some disk space and try again.`);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
errors.push(`Failed to write ${fullPath}: ${errorMsg}`);
|
|
147
|
+
}
|
|
148
|
+
// Clean up temp file if it exists
|
|
149
|
+
try {
|
|
150
|
+
await fs.unlink(tempPath);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Ignore cleanup errors
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Set permissions on all files (if not dry run)
|
|
159
|
+
if (!options?.dryRun && errors.length === 0) {
|
|
160
|
+
try {
|
|
161
|
+
// Set base directory permissions
|
|
162
|
+
await fs.chmod(expandedBaseDir, 0o755);
|
|
163
|
+
// Set permissions on all subdirectories
|
|
164
|
+
for (const dir of REQUIRED_DIRECTORIES) {
|
|
165
|
+
if (dir !== '') {
|
|
166
|
+
const dirPath = path.join(expandedBaseDir, dir);
|
|
167
|
+
if (existsSync(dirPath)) {
|
|
168
|
+
await fs.chmod(dirPath, 0o755);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Set permissions on all installed files
|
|
173
|
+
for (const file of installed) {
|
|
174
|
+
const filePath = path.join(expandedBaseDir, file);
|
|
175
|
+
if (existsSync(filePath)) {
|
|
176
|
+
await fs.chmod(filePath, 0o644);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
// Non-critical error, don't fail the installation
|
|
182
|
+
if (options?.verbose) {
|
|
183
|
+
console.log('Could not set permissions:', error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
success: errors.length === 0,
|
|
189
|
+
installed,
|
|
190
|
+
skipped,
|
|
191
|
+
errors,
|
|
192
|
+
baseDir: expandedBaseDir,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Uninstall prompt files
|
|
197
|
+
* @param baseDir - Base directory for prompts
|
|
198
|
+
* @param options - Uninstallation options
|
|
199
|
+
* @returns Uninstallation result with removed files
|
|
200
|
+
*/
|
|
201
|
+
async uninstall(baseDir, options) {
|
|
202
|
+
const removed = [];
|
|
203
|
+
const errors = [];
|
|
204
|
+
// Validate inputs
|
|
205
|
+
let expandedBaseDir = baseDir;
|
|
206
|
+
if (!expandedBaseDir) {
|
|
207
|
+
expandedBaseDir = this.expandPath(DEFAULT_BASE_DIR);
|
|
208
|
+
}
|
|
209
|
+
// If base directory doesn't exist, return success with empty arrays
|
|
210
|
+
if (!existsSync(expandedBaseDir)) {
|
|
211
|
+
return {
|
|
212
|
+
success: true,
|
|
213
|
+
removed: [],
|
|
214
|
+
errors: [],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
// Build removal list
|
|
218
|
+
const toRemove = [];
|
|
219
|
+
if (options?.removeUserFiles) {
|
|
220
|
+
// Remove all files
|
|
221
|
+
try {
|
|
222
|
+
await this.collectAllFiles(expandedBaseDir, expandedBaseDir, toRemove);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
errors.push(`Failed to list files: ${error instanceof Error ? error.message : String(error)}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
// Remove only default files (core.md and files in standard directories)
|
|
230
|
+
const defaultPaths = [
|
|
231
|
+
'core.md',
|
|
232
|
+
'env/development.md',
|
|
233
|
+
'env/dev.md',
|
|
234
|
+
'tools/git.md',
|
|
235
|
+
'providers/openai.md',
|
|
236
|
+
];
|
|
237
|
+
for (const filePath of defaultPaths) {
|
|
238
|
+
const fullPath = path.join(expandedBaseDir, filePath);
|
|
239
|
+
if (existsSync(fullPath)) {
|
|
240
|
+
toRemove.push(filePath);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// Remove files
|
|
245
|
+
for (const file of toRemove) {
|
|
246
|
+
const fullPath = path.join(expandedBaseDir, file);
|
|
247
|
+
if (options?.dryRun) {
|
|
248
|
+
console.log('Would remove:', fullPath);
|
|
249
|
+
removed.push(file);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
try {
|
|
253
|
+
await fs.unlink(fullPath);
|
|
254
|
+
removed.push(file);
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
258
|
+
if (errorMsg.includes('EBUSY')) {
|
|
259
|
+
errors.push(`File in use: ${file}. Close any programs using this file and try again.`);
|
|
260
|
+
}
|
|
261
|
+
else if (errorMsg.includes('EACCES') ||
|
|
262
|
+
errorMsg.includes('Permission denied')) {
|
|
263
|
+
errors.push(`Permission denied: ${file}`);
|
|
264
|
+
}
|
|
265
|
+
else if (!errorMsg.includes('ENOENT')) {
|
|
266
|
+
// Ignore "file not found" errors
|
|
267
|
+
errors.push(`Failed to remove ${file}: ${errorMsg}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Remove empty directories (in reverse order to remove children first)
|
|
273
|
+
if (!options?.dryRun) {
|
|
274
|
+
const dirsToCheck = [...REQUIRED_DIRECTORIES].reverse();
|
|
275
|
+
for (const dir of dirsToCheck) {
|
|
276
|
+
const fullPath = dir === '' ? expandedBaseDir : path.join(expandedBaseDir, dir);
|
|
277
|
+
try {
|
|
278
|
+
const contents = await fs.readdir(fullPath);
|
|
279
|
+
if (contents.length === 0) {
|
|
280
|
+
await fs.rmdir(fullPath);
|
|
281
|
+
removed.push(dir === '' ? 'base directory' : dir);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
// Ignore errors when removing directories
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
success: errors.length === 0,
|
|
291
|
+
removed,
|
|
292
|
+
errors,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Helper method to recursively collect all files in a directory
|
|
297
|
+
*/
|
|
298
|
+
async collectAllFiles(baseDir, currentDir, files) {
|
|
299
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
300
|
+
for (const entry of entries) {
|
|
301
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
302
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
303
|
+
if (entry.isDirectory()) {
|
|
304
|
+
await this.collectAllFiles(baseDir, fullPath, files);
|
|
305
|
+
}
|
|
306
|
+
else if (entry.isFile()) {
|
|
307
|
+
files.push(relativePath);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Validate prompt installation
|
|
313
|
+
* @param baseDir - Base directory to validate
|
|
314
|
+
* @returns Validation result with issues found
|
|
315
|
+
*/
|
|
316
|
+
async validate(baseDir) {
|
|
317
|
+
// Setup validation
|
|
318
|
+
let expandedBaseDir = baseDir;
|
|
319
|
+
if (!expandedBaseDir) {
|
|
320
|
+
expandedBaseDir = this.expandPath(DEFAULT_BASE_DIR);
|
|
321
|
+
}
|
|
322
|
+
let isValid = true;
|
|
323
|
+
const errors = [];
|
|
324
|
+
const warnings = [];
|
|
325
|
+
const missing = [];
|
|
326
|
+
// Check base directory
|
|
327
|
+
if (!existsSync(expandedBaseDir)) {
|
|
328
|
+
errors.push('Base directory does not exist');
|
|
329
|
+
isValid = false;
|
|
330
|
+
return {
|
|
331
|
+
isValid,
|
|
332
|
+
errors,
|
|
333
|
+
warnings,
|
|
334
|
+
missing,
|
|
335
|
+
baseDir: expandedBaseDir,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
// Check directory structure
|
|
339
|
+
for (const dir of REQUIRED_DIRECTORIES) {
|
|
340
|
+
if (dir === '')
|
|
341
|
+
continue; // Skip base directory itself
|
|
342
|
+
const fullPath = path.join(expandedBaseDir, dir);
|
|
343
|
+
if (!existsSync(fullPath)) {
|
|
344
|
+
missing.push(dir);
|
|
345
|
+
warnings.push(`Missing directory: ${dir}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Check required files
|
|
349
|
+
const corePath = path.join(expandedBaseDir, 'core.md');
|
|
350
|
+
if (!existsSync(corePath)) {
|
|
351
|
+
missing.push('core.md');
|
|
352
|
+
errors.push('Missing required core.md');
|
|
353
|
+
isValid = false;
|
|
354
|
+
}
|
|
355
|
+
// Check permissions
|
|
356
|
+
try {
|
|
357
|
+
// Check read permission
|
|
358
|
+
await fs.access(expandedBaseDir, fs.constants.R_OK);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
errors.push('Cannot read from directory');
|
|
362
|
+
isValid = false;
|
|
363
|
+
}
|
|
364
|
+
try {
|
|
365
|
+
// Check write permission
|
|
366
|
+
await fs.access(expandedBaseDir, fs.constants.W_OK);
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
warnings.push('Cannot write to directory');
|
|
370
|
+
}
|
|
371
|
+
// Check file integrity
|
|
372
|
+
if (existsSync(corePath)) {
|
|
373
|
+
try {
|
|
374
|
+
const stats = await fs.stat(corePath);
|
|
375
|
+
if (stats.size === 0) {
|
|
376
|
+
warnings.push('Empty file: core.md');
|
|
377
|
+
}
|
|
378
|
+
// Check if file is readable
|
|
379
|
+
try {
|
|
380
|
+
await fs.access(corePath, fs.constants.R_OK);
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
errors.push('Cannot read: core.md');
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
errors.push(`Error checking core.md: ${error instanceof Error ? error.message : String(error)}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
// Check other default files if they exist
|
|
391
|
+
const defaultFiles = [
|
|
392
|
+
'env/development.md',
|
|
393
|
+
'tools/git.md',
|
|
394
|
+
'providers/openai.md',
|
|
395
|
+
];
|
|
396
|
+
for (const file of defaultFiles) {
|
|
397
|
+
const filePath = path.join(expandedBaseDir, file);
|
|
398
|
+
if (existsSync(filePath)) {
|
|
399
|
+
try {
|
|
400
|
+
const stats = await fs.stat(filePath);
|
|
401
|
+
if (stats.size === 0) {
|
|
402
|
+
warnings.push(`Empty file: ${file}`);
|
|
403
|
+
}
|
|
404
|
+
// Check if file is readable
|
|
405
|
+
try {
|
|
406
|
+
await fs.access(filePath, fs.constants.R_OK);
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
errors.push(`Cannot read: ${file}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
// Ignore errors for optional files
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
isValid,
|
|
419
|
+
errors,
|
|
420
|
+
warnings,
|
|
421
|
+
missing,
|
|
422
|
+
baseDir: expandedBaseDir,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Repair prompt installation
|
|
427
|
+
* @param baseDir - Base directory to repair
|
|
428
|
+
* @param defaults - Map of default files to restore
|
|
429
|
+
* @param options - Repair options
|
|
430
|
+
* @returns Repair result with fixed issues
|
|
431
|
+
*/
|
|
432
|
+
async repair(baseDir, defaults, options) {
|
|
433
|
+
// Run validation first
|
|
434
|
+
const validation = await this.validate(baseDir);
|
|
435
|
+
const expandedBaseDir = validation.baseDir;
|
|
436
|
+
const repaired = [];
|
|
437
|
+
const errors = [];
|
|
438
|
+
// Create base directory if it doesn't exist
|
|
439
|
+
if (!existsSync(expandedBaseDir)) {
|
|
440
|
+
try {
|
|
441
|
+
await fs.mkdir(expandedBaseDir, { recursive: true, mode: 0o755 });
|
|
442
|
+
repaired.push('base directory');
|
|
443
|
+
if (options?.verbose) {
|
|
444
|
+
console.log('Created base directory:', expandedBaseDir);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
errors.push(`Failed to create base directory: ${error instanceof Error ? error.message : String(error)}`);
|
|
449
|
+
return {
|
|
450
|
+
success: false,
|
|
451
|
+
repaired,
|
|
452
|
+
errors,
|
|
453
|
+
stillInvalid: validation.errors,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
// Fix missing directories
|
|
458
|
+
for (const missingItem of validation.missing) {
|
|
459
|
+
// Check if it's a directory (from REQUIRED_DIRECTORIES)
|
|
460
|
+
if (REQUIRED_DIRECTORIES.includes(missingItem)) {
|
|
461
|
+
const dirPath = path.join(expandedBaseDir, missingItem);
|
|
462
|
+
try {
|
|
463
|
+
await fs.mkdir(dirPath, { recursive: true, mode: 0o755 });
|
|
464
|
+
repaired.push(missingItem);
|
|
465
|
+
if (options?.verbose) {
|
|
466
|
+
console.log('Created directory:', dirPath);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
errors.push(`Failed to create directory ${missingItem}: ${error instanceof Error ? error.message : String(error)}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
// Fix missing default files
|
|
475
|
+
for (const missingItem of validation.missing) {
|
|
476
|
+
// Check if it's a file (has content in defaults)
|
|
477
|
+
if (defaults[missingItem]) {
|
|
478
|
+
const filePath = path.join(expandedBaseDir, missingItem);
|
|
479
|
+
const fileDir = path.dirname(filePath);
|
|
480
|
+
// Ensure parent directory exists
|
|
481
|
+
try {
|
|
482
|
+
await fs.mkdir(fileDir, { recursive: true, mode: 0o755 });
|
|
483
|
+
}
|
|
484
|
+
catch (error) {
|
|
485
|
+
errors.push(`Failed to create directory for ${missingItem}: ${error instanceof Error ? error.message : String(error)}`);
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
// Write the file
|
|
489
|
+
const tempPath = `${filePath}.tmp.${Date.now()}`;
|
|
490
|
+
try {
|
|
491
|
+
await fs.writeFile(tempPath, defaults[missingItem], { mode: 0o644 });
|
|
492
|
+
await fs.rename(tempPath, filePath);
|
|
493
|
+
repaired.push(missingItem);
|
|
494
|
+
if (options?.verbose) {
|
|
495
|
+
console.log('Restored file:', filePath);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
errors.push(`Failed to restore ${missingItem}: ${error instanceof Error ? error.message : String(error)}`);
|
|
500
|
+
try {
|
|
501
|
+
await fs.unlink(tempPath);
|
|
502
|
+
}
|
|
503
|
+
catch {
|
|
504
|
+
// Ignore cleanup errors
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// Always fix permissions (even if validation passed)
|
|
510
|
+
let permissionsFixed = false;
|
|
511
|
+
try {
|
|
512
|
+
// Fix directory permissions
|
|
513
|
+
await fs.chmod(expandedBaseDir, 0o755);
|
|
514
|
+
// Fix all file and directory permissions recursively
|
|
515
|
+
await this.fixFilePermissions(expandedBaseDir);
|
|
516
|
+
permissionsFixed = true;
|
|
517
|
+
if (options?.verbose) {
|
|
518
|
+
console.log('Fixed file permissions');
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
catch (error) {
|
|
522
|
+
errors.push(`Failed to fix permissions: ${error instanceof Error ? error.message : String(error)}`);
|
|
523
|
+
}
|
|
524
|
+
// If we started with a valid installation, don't report permissions as repaired
|
|
525
|
+
// Only report actual repairs that fixed validation issues
|
|
526
|
+
if (!validation.isValid && permissionsFixed && errors.length === 0) {
|
|
527
|
+
repaired.push('file permissions');
|
|
528
|
+
}
|
|
529
|
+
// Run validation again
|
|
530
|
+
const finalValidation = await this.validate(baseDir);
|
|
531
|
+
return {
|
|
532
|
+
success: finalValidation.isValid && errors.length === 0,
|
|
533
|
+
repaired,
|
|
534
|
+
errors,
|
|
535
|
+
stillInvalid: finalValidation.errors,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Create backup of current prompts
|
|
540
|
+
* @param baseDir - Base directory to backup
|
|
541
|
+
* @param backupPath - Where to save backup
|
|
542
|
+
* @returns Backup result with location and stats
|
|
543
|
+
*/
|
|
544
|
+
async backup(baseDir, backupPath) {
|
|
545
|
+
// Validate inputs
|
|
546
|
+
let expandedBaseDir = baseDir;
|
|
547
|
+
if (!expandedBaseDir) {
|
|
548
|
+
expandedBaseDir = this.expandPath(DEFAULT_BASE_DIR);
|
|
549
|
+
}
|
|
550
|
+
if (!existsSync(expandedBaseDir)) {
|
|
551
|
+
return {
|
|
552
|
+
success: false,
|
|
553
|
+
error: 'Nothing to backup',
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
if (!backupPath || backupPath.trim() === '') {
|
|
557
|
+
return {
|
|
558
|
+
success: false,
|
|
559
|
+
error: 'Invalid backup path',
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
// Create backup
|
|
563
|
+
const now = new Date();
|
|
564
|
+
const year = now.getFullYear();
|
|
565
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
566
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
567
|
+
const hours = String(now.getHours()).padStart(2, '0');
|
|
568
|
+
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
569
|
+
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
570
|
+
const timestamp = `${year}${month}${day}_${hours}${minutes}${seconds}`;
|
|
571
|
+
const backupDir = path.join(backupPath, `prompt-backup-${timestamp}`);
|
|
572
|
+
try {
|
|
573
|
+
// Create backup directory
|
|
574
|
+
await fs.mkdir(backupDir, { recursive: true });
|
|
575
|
+
// Copy files
|
|
576
|
+
let fileCount = 0;
|
|
577
|
+
let totalSize = 0;
|
|
578
|
+
await this.copyDirectory(expandedBaseDir, backupDir, async (filePath) => {
|
|
579
|
+
fileCount++;
|
|
580
|
+
const stats = await fs.stat(filePath);
|
|
581
|
+
totalSize += stats.size;
|
|
582
|
+
});
|
|
583
|
+
// Create manifest
|
|
584
|
+
const manifest = {
|
|
585
|
+
backupDate: new Date().toISOString(),
|
|
586
|
+
sourcePath: expandedBaseDir,
|
|
587
|
+
fileCount,
|
|
588
|
+
totalSize,
|
|
589
|
+
};
|
|
590
|
+
await fs.writeFile(path.join(backupDir, 'backup-manifest.json'), JSON.stringify(manifest, null, 2));
|
|
591
|
+
// Verify backup
|
|
592
|
+
const verifyCount = await this.countFiles(backupDir);
|
|
593
|
+
if (verifyCount !== fileCount + 1) {
|
|
594
|
+
// +1 for manifest
|
|
595
|
+
console.warn(`Backup verification warning: expected ${fileCount + 1} files, found ${verifyCount}`);
|
|
596
|
+
}
|
|
597
|
+
return {
|
|
598
|
+
success: true,
|
|
599
|
+
backupPath: backupDir,
|
|
600
|
+
fileCount,
|
|
601
|
+
totalSize,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
catch (error) {
|
|
605
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
606
|
+
// Clean up partial backup on error
|
|
607
|
+
try {
|
|
608
|
+
await fs.rm(backupDir, { recursive: true, force: true });
|
|
609
|
+
}
|
|
610
|
+
catch {
|
|
611
|
+
// Ignore cleanup errors
|
|
612
|
+
}
|
|
613
|
+
if (errorMsg.includes('ENOSPC')) {
|
|
614
|
+
return {
|
|
615
|
+
success: false,
|
|
616
|
+
error: 'Insufficient space: Not enough disk space for backup. Try a different location.',
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
else if (errorMsg.includes('EACCES') ||
|
|
620
|
+
errorMsg.includes('Permission denied')) {
|
|
621
|
+
return {
|
|
622
|
+
success: false,
|
|
623
|
+
error: `Permission denied: Cannot write to backup location. Try a different location or check permissions.`,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
return {
|
|
627
|
+
success: false,
|
|
628
|
+
error: `Backup failed: ${errorMsg}`,
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Helper method to copy a directory recursively
|
|
634
|
+
*/
|
|
635
|
+
async copyDirectory(source, dest, onFile) {
|
|
636
|
+
await fs.mkdir(dest, { recursive: true });
|
|
637
|
+
const entries = await fs.readdir(source, { withFileTypes: true });
|
|
638
|
+
for (const entry of entries) {
|
|
639
|
+
const sourcePath = path.join(source, entry.name);
|
|
640
|
+
const destPath = path.join(dest, entry.name);
|
|
641
|
+
if (entry.isDirectory()) {
|
|
642
|
+
await this.copyDirectory(sourcePath, destPath, onFile);
|
|
643
|
+
}
|
|
644
|
+
else if (entry.isFile()) {
|
|
645
|
+
await fs.copyFile(sourcePath, destPath);
|
|
646
|
+
await fs.chmod(destPath, 0o644);
|
|
647
|
+
if (onFile) {
|
|
648
|
+
await onFile(sourcePath);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Helper method to count files in a directory
|
|
655
|
+
*/
|
|
656
|
+
async countFiles(dir) {
|
|
657
|
+
let count = 0;
|
|
658
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
659
|
+
for (const entry of entries) {
|
|
660
|
+
if (entry.isDirectory()) {
|
|
661
|
+
count += await this.countFiles(path.join(dir, entry.name));
|
|
662
|
+
}
|
|
663
|
+
else if (entry.isFile()) {
|
|
664
|
+
count++;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return count;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Helper method to fix file permissions recursively
|
|
671
|
+
*/
|
|
672
|
+
async fixFilePermissions(dir) {
|
|
673
|
+
try {
|
|
674
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
675
|
+
for (const entry of entries) {
|
|
676
|
+
const fullPath = path.join(dir, entry.name);
|
|
677
|
+
try {
|
|
678
|
+
if (entry.isDirectory()) {
|
|
679
|
+
await fs.chmod(fullPath, 0o755);
|
|
680
|
+
await this.fixFilePermissions(fullPath);
|
|
681
|
+
}
|
|
682
|
+
else if (entry.isFile()) {
|
|
683
|
+
await fs.chmod(fullPath, 0o644);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
catch {
|
|
687
|
+
// Silently continue with other files - some filesystems don't support chmod
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
catch {
|
|
692
|
+
// Silently continue - permissions might not be changeable in some environments
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Expand path with home directory and environment variables
|
|
697
|
+
* @param path - Path to expand
|
|
698
|
+
* @returns Expanded absolute path
|
|
699
|
+
*/
|
|
700
|
+
expandPath(inputPath) {
|
|
701
|
+
// Handle null or empty input
|
|
702
|
+
if (!inputPath) {
|
|
703
|
+
return '';
|
|
704
|
+
}
|
|
705
|
+
let expandedPath = inputPath;
|
|
706
|
+
// Expand home directory
|
|
707
|
+
if (expandedPath.startsWith('~')) {
|
|
708
|
+
const homeDir = os.homedir();
|
|
709
|
+
expandedPath = expandedPath.replace(/^~/, homeDir);
|
|
710
|
+
}
|
|
711
|
+
// Expand environment variables with curly braces ${VAR}
|
|
712
|
+
expandedPath = expandedPath.replace(/\$\{([^}]+)\}/g, (match, varName) => process.env[varName] || match);
|
|
713
|
+
// Expand environment variables without curly braces $VAR
|
|
714
|
+
expandedPath = expandedPath.replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (match, varName) => process.env[varName] || match);
|
|
715
|
+
// Resolve to absolute path
|
|
716
|
+
if (!path.isAbsolute(expandedPath)) {
|
|
717
|
+
expandedPath = path.resolve(expandedPath);
|
|
718
|
+
}
|
|
719
|
+
// Normalize path (remove redundant separators, resolve . and ..)
|
|
720
|
+
return path.normalize(expandedPath);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
//# sourceMappingURL=prompt-installer.js.map
|