@sriinnu/runic 1.2.0 → 2.11.1
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/CHANGELOG.md +267 -0
- package/LICENSE +373 -21
- package/README.md +98 -84
- package/THIRD-PARTY-NOTICES.md +651 -0
- package/package.json +14 -4
- package/.swiftformat +0 -50
- package/.swiftlint.yml +0 -160
- package/CONTRIBUTING.md +0 -456
- package/Icon.icns +0 -0
- package/Icon.icon/Assets/runic.png +0 -0
- package/Icon.icon/icon.json +0 -36
- package/Package.resolved +0 -51
- package/Package.swift +0 -136
- package/Packages/Helix/LICENSE +0 -21
- package/Packages/Helix/Package.swift +0 -29
- package/Packages/Helix/README.md +0 -976
- package/Packages/Helix/Sources/Helix/Binding.swift +0 -111
- package/Packages/Helix/Sources/Helix/CommandParser.swift +0 -160
- package/Packages/Helix/Sources/Helix/CommandSignature.swift +0 -117
- package/Packages/Helix/Sources/Helix/ExpressibleFromArgument.swift +0 -72
- package/Packages/Helix/Sources/Helix/HelpGenerator.swift +0 -175
- package/Packages/Helix/Sources/Helix/NameSpecification.swift +0 -98
- package/Packages/Helix/Sources/Helix/ParsableCommand.swift +0 -204
- package/Packages/Helix/Sources/Helix/Platform/DarwinPlatformContext.swift +0 -44
- package/Packages/Helix/Sources/Helix/Platform/LinuxPlatformContext.swift +0 -34
- package/Packages/Helix/Sources/Helix/Platform/PlatformContext+Default.swift +0 -91
- package/Packages/Helix/Sources/Helix/Platform/PlatformContext.swift +0 -94
- package/Packages/Helix/Sources/Helix/Platform/PlatformPath.swift +0 -167
- package/Packages/Helix/Sources/Helix/Platform/Stdio.swift +0 -99
- package/Packages/Helix/Sources/Helix/Platform/WebPlatformContext.swift +0 -146
- package/Packages/Helix/Sources/Helix/Platform/WindowsPlatformContext.swift +0 -97
- package/Packages/Helix/Sources/Helix/Program.swift +0 -204
- package/Packages/Helix/Sources/Helix/PropertyWrappers.swift +0 -367
- package/Packages/Silo/LICENSE +0 -21
- package/Packages/Silo/Package.swift +0 -34
- package/Packages/Silo/README.md +0 -466
- package/Packages/Silo/Sources/Silo/BinaryCookiesReader.swift +0 -169
- package/Packages/Silo/Sources/Silo/BrowserCookieAnalytics.swift +0 -365
- package/Packages/Silo/Sources/Silo/BrowserCookieBackends.swift +0 -594
- package/Packages/Silo/Sources/Silo/BrowserCookieClient.swift +0 -326
- package/Packages/Silo/Sources/Silo/BrowserCookieExport.swift +0 -154
- package/Packages/Silo/Sources/Silo/BrowserCookieImport.swift +0 -119
- package/Packages/Silo/Sources/Silo/BrowserCookieMockStore.swift +0 -121
- package/Packages/Silo/Sources/Silo/BrowserCookieModels.swift +0 -391
- package/Packages/Silo/Sources/Silo/ChromiumCookieReader.swift +0 -240
- package/Packages/Silo/Sources/Silo/CryptoSupport.swift +0 -86
- package/Packages/Silo/Sources/Silo/FirefoxCookieReader.swift +0 -172
- package/Packages/Silo/Sources/Silo/LinuxChromiumCookieReader.swift +0 -172
- package/Packages/Silo/Sources/Silo/LinuxSecretService.swift +0 -105
- package/Packages/Silo/Sources/Silo/MacOSChromiumCookieReader.swift +0 -191
- package/Packages/Silo/Sources/Silo/SQLiteSnapshot.swift +0 -41
- package/Packages/Silo/Sources/Silo/WindowsChromiumCookieReader.swift +0 -110
- package/RuniciOS/App/RuniciOSApp.swift +0 -105
- package/RuniciOS/Core/iOSUsageStore.swift +0 -192
- package/RuniciOS/Views/ProviderDetailView.swift +0 -312
- package/RuniciOS/Views/ProviderListView.swift +0 -228
- package/RuniciOS/Widgets/LargeWidget.swift +0 -367
- package/RuniciOS/Widgets/LiveActivity.swift +0 -342
- package/RuniciOS/Widgets/LockScreenWidget.swift +0 -273
- package/RuniciOS/Widgets/MediumWidget.swift +0 -370
- package/RuniciOS/Widgets/RunicWidget.swift +0 -274
- package/RuniciOS/Widgets/SmallWidget.swift +0 -281
- package/RuniciOS/Widgets/WidgetModels.swift +0 -285
- package/RuniciOS/Widgets/WidgetTimeline.swift +0 -270
- package/Scripts/CREATION-SUMMARY.md +0 -367
- package/Scripts/DIRECTORY-STRUCTURE.md +0 -358
- package/Scripts/GETTING-STARTED-CHECKLIST.md +0 -364
- package/Scripts/INDEX.md +0 -365
- package/Scripts/QUICK-REFERENCE.md +0 -164
- package/Scripts/README-SCRIPTS.md +0 -926
- package/Scripts/build-android.sh +0 -526
- package/Scripts/build-ios.sh +0 -529
- package/Scripts/build-macos.sh +0 -603
- package/Scripts/build-windows.sh +0 -504
- package/Scripts/build_icon.sh +0 -49
- package/Scripts/changelog-to-html.sh +0 -89
- package/Scripts/check-release-assets.sh +0 -10
- package/Scripts/ci-config.yml +0 -474
- package/Scripts/compile_and_run.sh +0 -219
- package/Scripts/compile_and_run_adhoc.sh +0 -4
- package/Scripts/docs-list.mjs +0 -119
- package/Scripts/make_appcast.sh +0 -58
- package/Scripts/package_app.sh +0 -409
- package/Scripts/release-lib.sh +0 -214
- package/Scripts/release.sh +0 -76
- package/Scripts/setup-all.sh +0 -387
- package/Scripts/setup-ios.sh +0 -404
- package/Scripts/setup-react-native.sh +0 -523
- package/Scripts/sign-and-notarize.sh +0 -122
- package/Scripts/test_live_update.sh +0 -38
- package/Scripts/validate_changelog.sh +0 -23
- package/Scripts/verify_appcast.sh +0 -92
- package/Sources/Runic/About.swift +0 -93
- package/Sources/Runic/AppNotifications.swift +0 -115
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +0 -666
- package/Sources/Runic/Controllers/StatusItemController+Animation.swift +0 -518
- package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +0 -810
- package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +0 -87
- package/Sources/Runic/Controllers/StatusItemController+Menu.swift +0 -1407
- package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +0 -337
- package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +0 -395
- package/Sources/Runic/Controllers/StatusItemController.swift +0 -360
- package/Sources/Runic/Core/BudgetNotificationManager.swift +0 -80
- package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +0 -103
- package/Sources/Runic/Core/Login/CodexLoginRunner.swift +0 -154
- package/Sources/Runic/Core/Login/CursorLoginRunner.swift +0 -206
- package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +0 -92
- package/Sources/Runic/Core/Rendering/IconRenderer.swift +0 -1216
- package/Sources/Runic/Core/Rendering/IconView.swift +0 -120
- package/Sources/Runic/Core/Rendering/ProviderBrandIcon.swift +0 -71
- package/Sources/Runic/Core/RunicAnimation.swift +0 -53
- package/Sources/Runic/Core/RunicColors.swift +0 -73
- package/Sources/Runic/Core/RunicCornerRadius.swift +0 -15
- package/Sources/Runic/Core/RunicSpacing.swift +0 -68
- package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +0 -158
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +0 -119
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +0 -119
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +0 -30
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/SettingsStore.swift +0 -1711
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +0 -20
- package/Sources/Runic/Core/Stores/UsageStore+Status.swift +0 -189
- package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +0 -33
- package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +0 -67
- package/Sources/Runic/Core/Stores/UsageStore.swift +0 -3605
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +0 -118
- package/Sources/Runic/Core/UsageExporter.swift +0 -193
- package/Sources/Runic/Date+RelativeDescription.swift +0 -21
- package/Sources/Runic/HiddenWindowView.swift +0 -32
- package/Sources/Runic/InstallOrigin.swift +0 -9
- package/Sources/Runic/KeyboardShortcuts+Names.swift +0 -6
- package/Sources/Runic/MouseLocationReader.swift +0 -65
- package/Sources/Runic/Notifications+Runic.swift +0 -6
- package/Sources/Runic/OpenAICreditsPurchaseWindowController.swift +0 -503
- package/Sources/Runic/ProviderRegistry.swift +0 -100
- package/Sources/Runic/ProviderSwitcherButtons.swift +0 -267
- package/Sources/Runic/Providers/Antigravity/AntigravityLoginFlow.swift +0 -11
- package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +0 -14
- package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +0 -52
- package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +0 -42
- package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Claude/ClaudeLoginFlow.swift +0 -26
- package/Sources/Runic/Providers/Claude/ClaudeProviderImplementation.swift +0 -14
- package/Sources/Runic/Providers/Codex/CodexLoginFlow.swift +0 -18
- package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Copilot/CopilotLoginFlow.swift +0 -193
- package/Sources/Runic/Providers/Copilot/CopilotProviderImplementation.swift +0 -110
- package/Sources/Runic/Providers/Cursor/CursorLoginFlow.swift +0 -28
- package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Factory/FactoryLoginFlow.swift +0 -12
- package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Gemini/GeminiLoginFlow.swift +0 -20
- package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +0 -32
- package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Shared/ProviderCatalog.swift +0 -15
- package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +0 -48
- package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +0 -66
- package/Sources/Runic/Providers/Shared/ProviderLoginFlow.swift +0 -12
- package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +0 -99
- package/Sources/Runic/Providers/Shared/SystemSettingsLinks.swift +0 -18
- package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +0 -24
- package/Sources/Runic/Resources/ProviderIcon-antigravity.svg +0 -2
- package/Sources/Runic/Resources/ProviderIcon-auggie.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-azure.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-bedrock.svg +0 -7
- package/Sources/Runic/Resources/ProviderIcon-cerebras.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-claude.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-codex.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-cohere.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-copilot.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-cursor.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-deepseek.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-factory.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-fireworks.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-gemini.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-groq.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-kimi.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-minimax.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-mistral.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-openrouter.svg +0 -7
- package/Sources/Runic/Resources/ProviderIcon-perplexity.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-qwen.svg +0 -9
- package/Sources/Runic/Resources/ProviderIcon-sambanova.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-together.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-vertexai.svg +0 -17
- package/Sources/Runic/Resources/ProviderIcon-xai.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-zai.svg +0 -5
- package/Sources/Runic/Resources/RunicMenubarIcon.svg +0 -7
- package/Sources/Runic/Resources/provider-context-windows.json +0 -20
- package/Sources/Runic/RunicApp.swift +0 -357
- package/Sources/Runic/SessionQuotaNotifications.swift +0 -58
- package/Sources/Runic/UsagePaceText.swift +0 -52
- package/Sources/Runic/UsageProgressBar.swift +0 -224
- package/Sources/Runic/Utilities/DisplayLink.swift +0 -96
- package/Sources/Runic/Utilities/LoadingPattern.swift +0 -60
- package/Sources/Runic/Utilities/PerformanceConstants.swift +0 -85
- package/Sources/Runic/Views/Menu/AlertsMenuView.swift +0 -256
- package/Sources/Runic/Views/Menu/ChartPanelStyle.swift +0 -22
- package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +0 -275
- package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +0 -377
- package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +0 -312
- package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +0 -312
- package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +0 -228
- package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +0 -228
- package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +0 -155
- package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +0 -64
- package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +0 -268
- package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +0 -104
- package/Sources/Runic/Views/Menu/MenuCardView.swift +0 -1900
- package/Sources/Runic/Views/Menu/MenuContent.swift +0 -150
- package/Sources/Runic/Views/Menu/MenuDescriptor.swift +0 -369
- package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +0 -42
- package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +0 -235
- package/Sources/Runic/Views/Menu/OverviewMenuView.swift +0 -278
- package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +0 -209
- package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +0 -220
- package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +0 -88
- package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +0 -201
- package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +0 -398
- package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +0 -232
- package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +0 -364
- package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +0 -190
- package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +0 -132
- package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +0 -618
- package/Sources/Runic/Views/Preferences/LiquidComponents.swift +0 -305
- package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +0 -271
- package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +0 -268
- package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +0 -422
- package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +0 -641
- package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +0 -344
- package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +0 -230
- package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +0 -322
- package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +0 -469
- package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +0 -231
- package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +0 -50
- package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +0 -567
- package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +0 -288
- package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +0 -3435
- package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +0 -25
- package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +0 -59
- package/Sources/Runic/Views/Preferences/PreferencesView.swift +0 -206
- package/Sources/Runic/Views/Rating/QualityRatingView.swift +0 -238
- package/Sources/Runic/Views/Team/TeamDashboardView.swift +0 -371
- package/Sources/Runic/Views/Team/TeamInviteSheet.swift +0 -175
- package/Sources/Runic/Views/Team/TeamManagementView.swift +0 -761
- package/Sources/Runic/Views/Team/TeamMemberRow.swift +0 -181
- package/Sources/RunicCLI/CLIEntry.swift +0 -942
- package/Sources/RunicCLI/CLIRenderer.swift +0 -87
- package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +0 -588
- package/Sources/RunicCLI/Commands/ModelsCommand.swift +0 -362
- package/Sources/RunicCLI/Commands/ProjectsCommand.swift +0 -439
- package/Sources/RunicCLI/Commands/ResetCommand.swift +0 -429
- package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +0 -168
- package/Sources/RunicClaudeWatchdog/main.swift +0 -122
- package/Sources/RunicClaudeWebProbe/main.swift +0 -62
- package/Sources/RunicCore/AlertRuleStore.swift +0 -187
- package/Sources/RunicCore/BrowserCookieImportOrder.swift +0 -25
- package/Sources/RunicCore/CopilotUsageModels.swift +0 -84
- package/Sources/RunicCore/CostUsageFetcher.swift +0 -119
- package/Sources/RunicCore/CostUsageModels.swift +0 -479
- package/Sources/RunicCore/CreditsModels.swift +0 -50
- package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +0 -269
- package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +0 -633
- package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +0 -500
- package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +0 -679
- package/Sources/RunicCore/Host/Process/SubprocessRunner.swift +0 -135
- package/Sources/RunicCore/Integrations/GitHubIntegration.swift +0 -173
- package/Sources/RunicCore/Logging/JSONStderrLogHandler.swift +0 -79
- package/Sources/RunicCore/Logging/OSLogLogHandler.swift +0 -86
- package/Sources/RunicCore/Logging/RunicLog.swift +0 -123
- package/Sources/RunicCore/Models/EnhancedUsageModels.swift +0 -371
- package/Sources/RunicCore/OpenAIDashboardModels.swift +0 -210
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +0 -676
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +0 -492
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardNavigationDelegate.swift +0 -51
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardParser.swift +0 -483
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardScrapeScript.swift +0 -558
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebViewCache.swift +0 -204
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebsiteDataStore.swift +0 -78
- package/Sources/RunicCore/PathEnvironment.swift +0 -482
- package/Sources/RunicCore/Performance/PerformanceModels.swift +0 -371
- package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +0 -409
- package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +0 -178
- package/Sources/RunicCore/ProjectBudgetStore.swift +0 -99
- package/Sources/RunicCore/ProviderCostSnapshot.swift +0 -29
- package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +0 -66
- package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +0 -685
- package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +0 -142
- package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +0 -141
- package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +0 -188
- package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +0 -126
- package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +0 -168
- package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Claude/ClaudeCLISession.swift +0 -332
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift +0 -180
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +0 -137
- package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +0 -207
- package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +0 -654
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +0 -28
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageFetcher.swift +0 -480
- package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +0 -780
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +0 -147
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +0 -280
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexTokenRefresher.swift +0 -100
- package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +0 -243
- package/Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift +0 -125
- package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +0 -24
- package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +0 -181
- package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +0 -85
- package/Sources/RunicCore/Providers/Copilot/CopilotDeviceFlow.swift +0 -123
- package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +0 -114
- package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +0 -100
- package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +0 -291
- package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +0 -386
- package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +0 -65
- package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +0 -595
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +0 -85
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +0 -108
- package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +0 -398
- package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +0 -65
- package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +0 -1276
- package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +0 -65
- package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +0 -836
- package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +0 -71
- package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +0 -77
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +0 -92
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxSettingsReader.swift +0 -54
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +0 -168
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +0 -911
- package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +0 -87
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +0 -203
- package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/ProviderBranding.swift +0 -25
- package/Sources/RunicCore/Providers/ProviderCLIConfig.swift +0 -17
- package/Sources/RunicCore/Providers/ProviderDescriptor.swift +0 -129
- package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +0 -263
- package/Sources/RunicCore/Providers/ProviderSettingsSnapshot.swift +0 -98
- package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +0 -501
- package/Sources/RunicCore/Providers/ProviderVersionDetector.swift +0 -68
- package/Sources/RunicCore/Providers/Providers.swift +0 -184
- package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Qwen/QwenSettingsReader.swift +0 -30
- package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +0 -315
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +0 -123
- package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +0 -48
- package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +0 -144
- package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +0 -72
- package/Sources/RunicCore/Providers/Zai/ZaiSettingsReader.swift +0 -41
- package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +0 -718
- package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +0 -385
- package/Sources/RunicCore/Sync/SyncConflictResolver.swift +0 -310
- package/Sources/RunicCore/Sync/SyncProtocol.swift +0 -288
- package/Sources/RunicCore/Sync/SyncRecord.swift +0 -405
- package/Sources/RunicCore/Sync/SyncUsageExample.swift +0 -294
- package/Sources/RunicCore/Sync/TeamSyncRecords.swift +0 -775
- package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +0 -333
- package/Sources/RunicCore/Team/TeamModels.swift +0 -583
- package/Sources/RunicCore/Team/TeamShowbackExporter.swift +0 -495
- package/Sources/RunicCore/Team/TeamStore.swift +0 -792
- package/Sources/RunicCore/TextParsing.swift +0 -55
- package/Sources/RunicCore/UsageFetcher.swift +0 -694
- package/Sources/RunicCore/UsageFormatter.swift +0 -410
- package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +0 -427
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +0 -494
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +0 -158
- package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +0 -550
- package/Sources/RunicCore/UsageLedger/UsageLedger.swift +0 -29
- package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +0 -660
- package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +0 -209
- package/Sources/RunicCore/UsageLedger/UsageLedgerModels.swift +0 -417
- package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +0 -245
- package/Sources/RunicCore/UsagePace.swift +0 -81
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +0 -73
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +0 -74
- package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +0 -208
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Claude.swift +0 -455
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Timestamp.swift +0 -132
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +0 -589
- package/Sources/RunicCore/WidgetSnapshot.swift +0 -183
- package/Sources/RunicMacroSupport/ProviderRegistrationMacros.swift +0 -14
- package/Sources/RunicMacros/ProviderRegistrationMacros.swift +0 -216
- package/Sources/RunicWidget/RunicWidgetBundle.swift +0 -79
- package/Sources/RunicWidget/RunicWidgetProvider.swift +0 -283
- package/Sources/RunicWidget/RunicWidgetViews.swift +0 -680
- package/Tests/RunicTests/AntigravityStatusProbeTests.swift +0 -50
- package/Tests/RunicTests/AppDelegateTests.swift +0 -43
- package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +0 -22
- package/Tests/RunicTests/CLIArgumentParsingTests.swift +0 -70
- package/Tests/RunicTests/CLICostTests.swift +0 -65
- package/Tests/RunicTests/CLIProviderSelectionTests.swift +0 -22
- package/Tests/RunicTests/CLISnapshotTests.swift +0 -72
- package/Tests/RunicTests/CLIWebFallbackTests.swift +0 -58
- package/Tests/RunicTests/ClaudeOAuthTests.swift +0 -164
- package/Tests/RunicTests/ClaudeResilienceTests.swift +0 -30
- package/Tests/RunicTests/ClaudeUsageTests.swift +0 -370
- package/Tests/RunicTests/CodexOAuthTests.swift +0 -122
- package/Tests/RunicTests/CodexUsageLogSourceTests.swift +0 -82
- package/Tests/RunicTests/CopilotTokenResolverTests.swift +0 -75
- package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +0 -75
- package/Tests/RunicTests/CostUsageDecodingTests.swift +0 -394
- package/Tests/RunicTests/CostUsagePricingTests.swift +0 -50
- package/Tests/RunicTests/CostUsageScannerTests.swift +0 -700
- package/Tests/RunicTests/CursorStatusProbeTests.swift +0 -323
- package/Tests/RunicTests/FactoryStatusProbeTests.swift +0 -69
- package/Tests/RunicTests/GeminiAPITestHelpers.swift +0 -94
- package/Tests/RunicTests/GeminiLoginAlertTests.swift +0 -28
- package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +0 -283
- package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +0 -279
- package/Tests/RunicTests/GeminiStatusProbeTests.swift +0 -233
- package/Tests/RunicTests/GeminiTestEnvironment.swift +0 -87
- package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +0 -73
- package/Tests/RunicTests/InstallOriginTests.swift +0 -18
- package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +0 -9
- package/Tests/RunicTests/LiveAccountTests.swift +0 -24
- package/Tests/RunicTests/LoadingPatternTests.swift +0 -35
- package/Tests/RunicTests/MenuCardModelTests.swift +0 -545
- package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +0 -8
- package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +0 -8
- package/Tests/RunicTests/MiniMaxParsingTests.swift +0 -68
- package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +0 -8
- package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +0 -112
- package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +0 -17
- package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +0 -75
- package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +0 -27
- package/Tests/RunicTests/OpenAIDashboardParserTests.swift +0 -134
- package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +0 -53
- package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +0 -92
- package/Tests/RunicTests/PathBuilderTests.swift +0 -237
- package/Tests/RunicTests/ProviderFetchPipelineTests.swift +0 -160
- package/Tests/RunicTests/ProviderIconResourcesTests.swift +0 -38
- package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +0 -16
- package/Tests/RunicTests/ProviderRegistryTests.swift +0 -29
- package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +0 -202
- package/Tests/RunicTests/ProviderToggleStoreTests.swift +0 -51
- package/Tests/RunicTests/RunicTests.swift +0 -120
- package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +0 -36
- package/Tests/RunicTests/SettingsStoreTests.swift +0 -161
- package/Tests/RunicTests/StatusItemAnimationTests.swift +0 -49
- package/Tests/RunicTests/StatusMenuTests.swift +0 -434
- package/Tests/RunicTests/StatusProbeTests.swift +0 -377
- package/Tests/RunicTests/SubprocessRunnerTests.swift +0 -20
- package/Tests/RunicTests/SubscriptionDetectionTests.swift +0 -65
- package/Tests/RunicTests/TTYCommandRunnerTests.swift +0 -148
- package/Tests/RunicTests/TTYIntegrationTests.swift +0 -41
- package/Tests/RunicTests/TeamShowbackExporterTests.swift +0 -159
- package/Tests/RunicTests/TextParsingTests.swift +0 -12
- package/Tests/RunicTests/UsageFormatterTests.swift +0 -112
- package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +0 -172
- package/Tests/RunicTests/UsageLedgerForecastTests.swift +0 -213
- package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +0 -83
- package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +0 -170
- package/Tests/RunicTests/UsagePaceTests.swift +0 -77
- package/Tests/RunicTests/UsagePaceTextTests.swift +0 -109
- package/Tests/RunicTests/UsagePolicyEngineTests.swift +0 -91
- package/Tests/RunicTests/WidgetSnapshotTests.swift +0 -45
- package/Tests/RunicTests/ZaiProviderTests.swift +0 -61
- package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +0 -6
- package/TestsLinux/PlatformGatingTests.swift +0 -46
- package/appcast.xml +0 -144
- package/assets/logo-concept-stack.svg +0 -24
- package/assets/logo-minimal-bars.svg +0 -15
- package/assets/logo.svg +0 -20
- package/assets/logo2.svg +0 -43
- package/bin/docs-list +0 -5
- package/build-and-run.sh +0 -369
- package/docs/.nojekyll +0 -1
- package/docs/CNAME +0 -1
- package/docs/antigravity.md +0 -75
- package/docs/architecture.md +0 -32
- package/docs/claude.md +0 -93
- package/docs/cli-commands/integration_guide.md +0 -371
- package/docs/cli-commands/quick_reference.md +0 -362
- package/docs/cli-commands/readme.md +0 -361
- package/docs/cli.md +0 -156
- package/docs/cloudkit_sync_guide.md +0 -495
- package/docs/codebase_review.md +0 -288
- package/docs/codex-oauth.md +0 -635
- package/docs/codex.md +0 -104
- package/docs/codexbar.png +0 -0
- package/docs/copilot.md +0 -44
- package/docs/cursor.md +0 -46
- package/docs/design/designcomparisonchart.md +0 -510
- package/docs/design/implementationguide.md +0 -792
- package/docs/design/menubaricondesignconcepts.md +0 -796
- package/docs/design/mockups/hybridiconconcept.svg +0 -62
- package/docs/design/mockups/infinityiconconcept.svg +0 -41
- package/docs/design/mockups/infinityiconstates.svg +0 -129
- package/docs/design/mockups/wavelogorefined.svg +0 -48
- package/docs/design/quick_summary.md +0 -426
- package/docs/design/readme.md +0 -478
- package/docs/design/sizespecifications.md +0 -575
- package/docs/design-system.md +0 -836
- package/docs/factory.md +0 -103
- package/docs/gemini.md +0 -73
- package/docs/icon.md +0 -37
- package/docs/icon.png +0 -0
- package/docs/index.html +0 -175
- package/docs/keychain_fix.md +0 -274
- package/docs/minimax.md +0 -71
- package/docs/packaging.md +0 -25
- package/docs/progress-bar-improvements.md +0 -240
- package/docs/progress-bar-quick-reference.md +0 -283
- package/docs/progress-bar-technical-details.md +0 -432
- package/docs/progress-bar-visual-test.swift +0 -375
- package/docs/provider.md +0 -202
- package/docs/providers.md +0 -86
- package/docs/refactor/macros.md +0 -35
- package/docs/refresh-loop.md +0 -22
- package/docs/releasing-homebrew.md +0 -35
- package/docs/releasing.md +0 -121
- package/docs/research/concrete_todo_2026-02-22.md +0 -47
- package/docs/research/core-feature-research.md +0 -248
- package/docs/research/feature_uiux_roadmap_2026-02-22.md +0 -202
- package/docs/research/parity_matrix.md +0 -49
- package/docs/research/provider_expansion_candidates_2026-02-22.md +0 -80
- package/docs/research/provider_model_token_project_matrix_2026-02-23.md +0 -46
- package/docs/research/release_gate_checklist.md +0 -57
- package/docs/site.css +0 -505
- package/docs/sparkle.md +0 -28
- package/docs/status.md +0 -28
- package/docs/sync/implementation.md +0 -393
- package/docs/sync/readme.md +0 -317
- package/docs/sync_files_summary.md +0 -391
- package/docs/sync_quick_reference.md +0 -225
- package/docs/todo.md +0 -11
- package/docs/ui.md +0 -29
- package/docs/web-integration.md +0 -9
- package/docs/widgets.md +0 -85
- package/docs/workflows/error-handling-improvements.md +0 -434
- package/docs/workflows/folder_structure.md +0 -299
- package/docs/workflows/loading_states.md +0 -446
- package/docs/workflows/loading_states_examples.md +0 -565
- package/docs/workflows/loading_states_flow.md +0 -430
- package/docs/workflows/performance_optimizations.md +0 -457
- package/docs/workflows/phase1-cli-testing-report.md +0 -313
- package/docs/workflows/quick_reference.md +0 -228
- package/docs/workflows/readme.md +0 -59
- package/docs/workflows/recent_improvements.md +0 -275
- package/docs/workflows/sync_enablement_summary.md +0 -400
- package/docs/zai.md +0 -43
- package/runic.png +0 -0
- package/version.env +0 -2
package/Packages/Helix/README.md
DELETED
|
@@ -1,976 +0,0 @@
|
|
|
1
|
-
# Helix
|
|
2
|
-
|
|
3
|
-
<p align="center">
|
|
4
|
-
<picture>
|
|
5
|
-
<source media="(prefers-color-scheme: dark)" srcset="logo-dark.svg" />
|
|
6
|
-
<img src="logo.svg" alt="Helix Logo" width="200" />
|
|
7
|
-
</picture>
|
|
8
|
-
</p>
|
|
9
|
-
|
|
10
|
-
<p align="center">
|
|
11
|
-
<img src="https://img.shields.io/badge/Swift-6.0+-orange.svg" />
|
|
12
|
-
<img src="https://img.shields.io/badge/Platforms-Apple%20%7C%20Linux%20%7C%20Windows%20%7C%20WASI-lightgrey.svg" />
|
|
13
|
-
<img src="https://img.shields.io/badge/License-MIT-blue.svg" />
|
|
14
|
-
<img src="https://github.com/sriinnu/Helix/actions/workflows/ci.yml/badge.svg" />
|
|
15
|
-
</p>
|
|
16
|
-
|
|
17
|
-
A modern, declarative command-line parsing framework for Swift that uses property wrappers to create elegant CLI interfaces. Inspired by Swift ArgumentParser but with a lighter footprint and simplified API.
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Table of Contents
|
|
22
|
-
|
|
23
|
-
1. [Quick Start](#quick-start)
|
|
24
|
-
2. [Architecture](#architecture)
|
|
25
|
-
3. [Property Wrappers](#property-wrappers)
|
|
26
|
-
4. [Custom Types](#custom-types)
|
|
27
|
-
5. [Validation](#validation)
|
|
28
|
-
6. [Subcommands](#subcommands)
|
|
29
|
-
7. [Cross-Platform Support](#cross-platform-support)
|
|
30
|
-
8. [Testing](#testing)
|
|
31
|
-
9. [API Reference](#api-reference)
|
|
32
|
-
10. [Migration Guide](#migration-guide)
|
|
33
|
-
11. [Best Practices](#best-practices)
|
|
34
|
-
12. [Performance](#performance)
|
|
35
|
-
13. [FAQ](#faq)
|
|
36
|
-
14. [Contributing](#contributing)
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## Quick Start
|
|
41
|
-
|
|
42
|
-
### Installation
|
|
43
|
-
|
|
44
|
-
Add Helix to your `Package.swift`:
|
|
45
|
-
|
|
46
|
-
```swift
|
|
47
|
-
dependencies: [
|
|
48
|
-
.package(url: "https://github.com/sriinnu/Helix", from: "1.0.0")
|
|
49
|
-
]
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Then add it to your target:
|
|
53
|
-
|
|
54
|
-
```swift
|
|
55
|
-
.target(
|
|
56
|
-
name: "YourTool",
|
|
57
|
-
dependencies: ["Helix"]
|
|
58
|
-
)
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Your First Command
|
|
62
|
-
|
|
63
|
-
```swift
|
|
64
|
-
import Helix
|
|
65
|
-
|
|
66
|
-
struct GreetCommand: ParsableCommand {
|
|
67
|
-
static var commandDescription: CommandDescription {
|
|
68
|
-
CommandDescription(commandName: "greet", abstract: "Print a greeting")
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
@Option(name: .shortAndLong, help: "Name to greet")
|
|
72
|
-
var name: String = "World"
|
|
73
|
-
|
|
74
|
-
@Flag(name: .shortAndLong, help: "Use formal greeting")
|
|
75
|
-
var formal: Bool = false
|
|
76
|
-
|
|
77
|
-
mutating func run() async throws {
|
|
78
|
-
let greeting = formal ? "Good day, \(name)." : "Hello, \(name)!"
|
|
79
|
-
print(greeting)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
try await GreetCommand.main()
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**Usage:**
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
$ greet --name Alice
|
|
90
|
-
Hello, Alice!
|
|
91
|
-
|
|
92
|
-
$ greet --name Bob --formal
|
|
93
|
-
Good day, Bob.
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Note: `ParsedValues` uses property labels (e.g. `logLevel`, `failFast`) as keys, not the kebab-case CLI names.
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## Architecture
|
|
101
|
-
|
|
102
|
-
### Execution Flow
|
|
103
|
-
|
|
104
|
-
```
|
|
105
|
-
User Input (argv)
|
|
106
|
-
↓
|
|
107
|
-
Program.resolve() - Command/subcommand resolution
|
|
108
|
-
↓
|
|
109
|
-
CommandParser.parse() - Tokenization and parsing
|
|
110
|
-
↓
|
|
111
|
-
Binding - automatic property wrapper binding
|
|
112
|
-
↓
|
|
113
|
-
validate() - Optional validation
|
|
114
|
-
↓
|
|
115
|
-
run() - Execute command logic
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Component Overview
|
|
119
|
-
|
|
120
|
-
| Component | Purpose |
|
|
121
|
-
|-----------|---------|
|
|
122
|
-
| `ParsableCommand` | Main protocol all commands conform to |
|
|
123
|
-
| `@Option` | Named parameters (`--output file.txt`) |
|
|
124
|
-
| `@Argument` | Positional parameters (`file.txt`) |
|
|
125
|
-
| `@Flag` | Boolean switches (`--verbose`) |
|
|
126
|
-
| `@OptionGroup` | Reusable option sets |
|
|
127
|
-
| `CommandSignature` | Metadata about command parameters |
|
|
128
|
-
| `CommandParser` | Tokenizes and parses arguments |
|
|
129
|
-
| `Program` | Routes argv to commands |
|
|
130
|
-
| `PlatformContext` | Platform abstraction layer |
|
|
131
|
-
| `PlatformPath` | Cross-platform file paths |
|
|
132
|
-
|
|
133
|
-
### How It Works
|
|
134
|
-
|
|
135
|
-
1. **Reflection-Based Discovery**: Helix uses Swift's `Mirror` API to automatically discover `@Option`, `@Argument`, and `@Flag` properties on your command types.
|
|
136
|
-
|
|
137
|
-
2. **Two-Phase Parsing**:
|
|
138
|
-
- **Resolution**: `Program.resolve()` identifies which command/subcommand to run
|
|
139
|
-
- **Parsing**: `CommandParser.parse()` extracts values from argv
|
|
140
|
-
|
|
141
|
-
3. **Property Binding**: Parsed values are automatically bound to your command properties (including `@OptionGroup`).
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## Property Wrappers
|
|
146
|
-
|
|
147
|
-
### `@Option` - Named Parameters
|
|
148
|
-
|
|
149
|
-
Options are named parameters that accept values:
|
|
150
|
-
|
|
151
|
-
```swift
|
|
152
|
-
// Required option (no default)
|
|
153
|
-
@Option(help: "Output file path")
|
|
154
|
-
var output: String
|
|
155
|
-
|
|
156
|
-
// Optional option (with default)
|
|
157
|
-
@Option(help: "Verbosity level (0-5)")
|
|
158
|
-
var verbose: Int = 0
|
|
159
|
-
|
|
160
|
-
// Short and long names
|
|
161
|
-
@Option(name: .shortAndLong, help: "Configuration file")
|
|
162
|
-
var config: String = "config.json"
|
|
163
|
-
|
|
164
|
-
// Custom names
|
|
165
|
-
@Option(name: .short('o'), help: "Output file")
|
|
166
|
-
var outputFile: String
|
|
167
|
-
|
|
168
|
-
@Option(name: .longName("output-dir"), help: "Directory for output")
|
|
169
|
-
var outputDirectory: String
|
|
170
|
-
|
|
171
|
-
// Multiple names
|
|
172
|
-
@Option(names: [.short('v'), .longName("verbose")], help: "Verbose output")
|
|
173
|
-
var verboseMode: Bool = false
|
|
174
|
-
|
|
175
|
-
// Environment variable fallback
|
|
176
|
-
@Option(name: .shortAndLong, envVar: "API_KEY", help: "API key")
|
|
177
|
-
var apiKey: String?
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
`envVar` provides environment variable fallback when the option is not provided on the command line.
|
|
181
|
-
|
|
182
|
-
### `@Argument` - Positional Parameters
|
|
183
|
-
|
|
184
|
-
Arguments are parsed by position, not by name:
|
|
185
|
-
|
|
186
|
-
```swift
|
|
187
|
-
// Required argument
|
|
188
|
-
@Argument(help: "Input file to process")
|
|
189
|
-
var input: String
|
|
190
|
-
|
|
191
|
-
// Optional argument
|
|
192
|
-
@Argument(help: "Output file (defaults to stdout)")
|
|
193
|
-
var output: String?
|
|
194
|
-
|
|
195
|
-
// Multiple arguments (parsed in order)
|
|
196
|
-
@Argument(help: "Source files")
|
|
197
|
-
var sources: [String]
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### `@Flag` - Boolean Switches
|
|
201
|
-
|
|
202
|
-
Flags are boolean options that don't take values:
|
|
203
|
-
|
|
204
|
-
```swift
|
|
205
|
-
// Simple flag
|
|
206
|
-
@Flag(help: "Enable debug mode")
|
|
207
|
-
var debug: Bool = false
|
|
208
|
-
|
|
209
|
-
// Short form
|
|
210
|
-
@Flag(name: .short('v'), help: "Verbose output")
|
|
211
|
-
var verbose: Bool = false
|
|
212
|
-
|
|
213
|
-
// Long form
|
|
214
|
-
@Flag(name: .longName("verbose"), help: "Verbose output")
|
|
215
|
-
var verbose: Bool = false
|
|
216
|
-
|
|
217
|
-
// Combined short
|
|
218
|
-
@Flag(name: .shortAndLong, help: "Force overwrite")
|
|
219
|
-
var force: Bool = false
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### `@OptionGroup` - Reusable Option Sets
|
|
223
|
-
|
|
224
|
-
Group related options together:
|
|
225
|
-
|
|
226
|
-
```swift
|
|
227
|
-
struct LoggingOptions: HelixParsable {
|
|
228
|
-
@Option(help: "Log level (debug|info|warn|error)")
|
|
229
|
-
var level: String = "info"
|
|
230
|
-
|
|
231
|
-
@Option(help: "Log file path")
|
|
232
|
-
var file: String?
|
|
233
|
-
|
|
234
|
-
@Flag(help: "Include timestamps")
|
|
235
|
-
var timestamps: Bool = true
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
struct MyCommand: ParsableCommand {
|
|
239
|
-
@OptionGroup
|
|
240
|
-
var logging: LoggingOptions
|
|
241
|
-
|
|
242
|
-
mutating func run() async throws {
|
|
243
|
-
print("Logging at level: \(logging.level)")
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
When parsing manually, pass `Self.descriptor.signature.flattened()` to `CommandParser` to include options from option groups.
|
|
249
|
-
`ParsableCommand.main()` already uses flattened signatures; the note above only applies if you instantiate `CommandParser` manually.
|
|
250
|
-
|
|
251
|
-
### Name Specification Options
|
|
252
|
-
|
|
253
|
-
| Specification | Generated Names | Example |
|
|
254
|
-
|--------------|-----------------|---------|
|
|
255
|
-
| `.automatic` | `--property-name` | `--output-file` |
|
|
256
|
-
| `.short('o')` | `-o` | `-o file.txt` |
|
|
257
|
-
| `.longName("out")` | `--out` | `--out file.txt` |
|
|
258
|
-
| `.shortAndLong` | `-o`, `--output` | `-o file.txt` / `--output file.txt` |
|
|
259
|
-
| `.customShort('x')` | `-x` | `-x file.txt` |
|
|
260
|
-
| `.customLong("output")` | `--output` | `--output file.txt` |
|
|
261
|
-
|
|
262
|
-
Note: `--name=value` and `-o=value` are supported. Joined short option values (e.g. `-ovalue`) are not supported.
|
|
263
|
-
|
|
264
|
-
---
|
|
265
|
-
|
|
266
|
-
## Custom Types
|
|
267
|
-
|
|
268
|
-
### Conforming to `ExpressibleFromArgument`
|
|
269
|
-
|
|
270
|
-
Add support for custom types by conforming to `ExpressibleFromArgument`:
|
|
271
|
-
|
|
272
|
-
```swift
|
|
273
|
-
enum LogLevel: String, ExpressibleFromArgument {
|
|
274
|
-
case debug, info, warning, error
|
|
275
|
-
|
|
276
|
-
init?(argument: String) {
|
|
277
|
-
self.init(rawValue: argument.lowercased())
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Usage
|
|
282
|
-
@Option(help: "Log level")
|
|
283
|
-
var logLevel: LogLevel = .info
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### Complex Types
|
|
287
|
-
|
|
288
|
-
```swift
|
|
289
|
-
struct ServerAddress: ExpressibleFromArgument {
|
|
290
|
-
let host: String
|
|
291
|
-
let port: Int
|
|
292
|
-
|
|
293
|
-
init?(argument: String) {
|
|
294
|
-
let parts = argument.split(separator: ":", maxSplits: 1)
|
|
295
|
-
guard parts.count == 2,
|
|
296
|
-
let port = Int(parts[1]) else {
|
|
297
|
-
return nil
|
|
298
|
-
}
|
|
299
|
-
self.host = String(parts[0])
|
|
300
|
-
self.port = port
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Usage
|
|
305
|
-
@Option(help: "Server address (host:port)")
|
|
306
|
-
var server: ServerAddress
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
### Built-in Type Support
|
|
310
|
-
|
|
311
|
-
Helix supports these types out of the box:
|
|
312
|
-
|
|
313
|
-
| Type | Notes |
|
|
314
|
-
|------|-------|
|
|
315
|
-
| `String` | Direct assignment |
|
|
316
|
-
| `Substring` | Converted from String |
|
|
317
|
-
| `Int`, `Int32`, `Int64` | Base-10 parsing |
|
|
318
|
-
| `Double`, `Float` | Decimal parsing |
|
|
319
|
-
| `Bool` | `true/false/t/1/yes/y` / `false/f/0/no/n` |
|
|
320
|
-
| `Optional<T>` | Wraps any supported type |
|
|
321
|
-
| `Array<T>` | Options support comma-separated values and repeated occurrences; positional arrays consume remaining positionals |
|
|
322
|
-
|
|
323
|
-
---
|
|
324
|
-
|
|
325
|
-
## Validation
|
|
326
|
-
|
|
327
|
-
Add custom validation logic with the `validate()` method:
|
|
328
|
-
|
|
329
|
-
```swift
|
|
330
|
-
struct DeployCommand: ParsableCommand {
|
|
331
|
-
@Option(help: "Environment (dev|staging|prod)")
|
|
332
|
-
var environment: String
|
|
333
|
-
|
|
334
|
-
@Option(help: "Timeout in seconds (1-300)")
|
|
335
|
-
var timeout: Int = 60
|
|
336
|
-
|
|
337
|
-
@Argument(help: "Service name")
|
|
338
|
-
var service: String
|
|
339
|
-
|
|
340
|
-
mutating func validate() throws {
|
|
341
|
-
// Validate environment
|
|
342
|
-
let validEnvironments = ["dev", "staging", "prod"]
|
|
343
|
-
guard validEnvironments.contains(environment) else {
|
|
344
|
-
throw ValidationError("Environment must be one of: \(validEnvironments.joined(separator: ", "))")
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Validate timeout range
|
|
348
|
-
guard (1...300).contains(timeout) else {
|
|
349
|
-
throw ValidationError("Timeout must be between 1 and 300 seconds")
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Cross-field validation
|
|
353
|
-
if environment == "prod" && timeout < 30 {
|
|
354
|
-
throw ValidationError("Production deployments require at least 30 second timeout")
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
mutating func run() async throws {
|
|
359
|
-
print("Deploying \(service) to \(environment) with \(timeout)s timeout")
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
### ValidationError
|
|
365
|
-
|
|
366
|
-
```swift
|
|
367
|
-
throw ValidationError("Custom error message")
|
|
368
|
-
|
|
369
|
-
// Or use CustomStringConvertible types
|
|
370
|
-
struct AppError: Error, CustomStringConvertible {
|
|
371
|
-
var description: String
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
throw AppError(description: "Something went wrong")
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
---
|
|
378
|
-
|
|
379
|
-
## Subcommands
|
|
380
|
-
|
|
381
|
-
Create hierarchical CLI tools with nested commands:
|
|
382
|
-
|
|
383
|
-
```swift
|
|
384
|
-
struct GitCommand: ParsableCommand {
|
|
385
|
-
static var commandDescription: CommandDescription {
|
|
386
|
-
CommandDescription(
|
|
387
|
-
commandName: "git",
|
|
388
|
-
abstract: "Distributed version control",
|
|
389
|
-
discussion: "Git is a free and open source distributed version control system.",
|
|
390
|
-
subcommands: [
|
|
391
|
-
CloneCommand.self,
|
|
392
|
-
CommitCommand.self,
|
|
393
|
-
PushCommand.self,
|
|
394
|
-
PullCommand.self
|
|
395
|
-
],
|
|
396
|
-
defaultSubcommand: nil // No default - user must specify
|
|
397
|
-
)
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
struct CloneCommand: ParsableCommand {
|
|
402
|
-
@Argument(help: "Repository URL")
|
|
403
|
-
var repository: String
|
|
404
|
-
|
|
405
|
-
@Option(name: .shortAndLong, help: "Directory to clone into")
|
|
406
|
-
var directory: String?
|
|
407
|
-
|
|
408
|
-
static var commandDescription: CommandDescription {
|
|
409
|
-
CommandDescription(
|
|
410
|
-
commandName: "clone",
|
|
411
|
-
abstract: "Clone a repository"
|
|
412
|
-
)
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
mutating func run() async throws {
|
|
416
|
-
print("Cloning \(repository)...")
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
**Usage:**
|
|
422
|
-
|
|
423
|
-
```bash
|
|
424
|
-
$ git clone https://github.com/user/repo --directory myproject
|
|
425
|
-
$ git commit -m "Fix bug"
|
|
426
|
-
$ git push origin main
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
`ParsableCommand.main()` dispatches to subcommands automatically. When you run the executable, you typically do not need to include the root command name as the first argument.
|
|
430
|
-
|
|
431
|
-
### Default Subcommand
|
|
432
|
-
|
|
433
|
-
Automatically route to a subcommand when none specified:
|
|
434
|
-
|
|
435
|
-
```swift
|
|
436
|
-
struct ToolCommand: ParsableCommand {
|
|
437
|
-
static var commandDescription: CommandDescription {
|
|
438
|
-
CommandDescription(
|
|
439
|
-
commandName: "tool",
|
|
440
|
-
subcommands: [BuildCommand.self, TestCommand.self],
|
|
441
|
-
defaultSubcommand: BuildCommand.self // Auto-routes to 'build'
|
|
442
|
-
)
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
---
|
|
448
|
-
|
|
449
|
-
## Cross-Platform Support
|
|
450
|
-
|
|
451
|
-
Helix runs on multiple platforms with a unified API:
|
|
452
|
-
|
|
453
|
-
### Supported Platforms
|
|
454
|
-
|
|
455
|
-
| Platform | Minimum Version | Status |
|
|
456
|
-
|----------|-----------------|--------|
|
|
457
|
-
| macOS | 14.0+ | ✅ Full Support |
|
|
458
|
-
| iOS | 17.0+ | ✅ Full Support |
|
|
459
|
-
| tvOS | 17.0+ | ✅ Full Support |
|
|
460
|
-
| watchOS | 10.0+ | ✅ Full Support |
|
|
461
|
-
| visionOS | 1.0+ | ✅ Full Support |
|
|
462
|
-
| Linux | - | ✅ Supported (CI) |
|
|
463
|
-
| Windows | - | ✅ Supported (CI) |
|
|
464
|
-
| WASI | - | ⚠️ Supported (not covered by CI here) |
|
|
465
|
-
|
|
466
|
-
Platform contexts live in `Sources/Helix/Platform` and are selected via compile-time checks.
|
|
467
|
-
|
|
468
|
-
### Platform Context
|
|
469
|
-
|
|
470
|
-
Access platform-specific functionality through `PlatformContext`:
|
|
471
|
-
|
|
472
|
-
```swift
|
|
473
|
-
import Helix
|
|
474
|
-
|
|
475
|
-
struct PlatformCommand: ParsableCommand {
|
|
476
|
-
mutating func run() async throws {
|
|
477
|
-
// Get arguments
|
|
478
|
-
let args = PlatformContext.current.arguments
|
|
479
|
-
print("Arguments: \(args)")
|
|
480
|
-
|
|
481
|
-
// Get environment variable
|
|
482
|
-
if let path = PlatformContext.current.environmentVariable("PATH") {
|
|
483
|
-
print("PATH: \(path)")
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Current working directory
|
|
487
|
-
let cwd = PlatformContext.current.currentWorkingDirectory
|
|
488
|
-
print("Working directory: \(cwd)")
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
### PlatformPath
|
|
494
|
-
|
|
495
|
-
Cross-platform file path handling:
|
|
496
|
-
|
|
497
|
-
```swift
|
|
498
|
-
let path = PlatformPath("/home/user/documents/file.txt")
|
|
499
|
-
|
|
500
|
-
// Platform-aware operations
|
|
501
|
-
#if os(Windows)
|
|
502
|
-
print(path.string) // Uses backslashes
|
|
503
|
-
#else
|
|
504
|
-
print(path.string) // Uses forward slashes
|
|
505
|
-
#endif
|
|
506
|
-
|
|
507
|
-
// Path operations
|
|
508
|
-
let parent = path.deletingExtension // /home/user/documents/file
|
|
509
|
-
let ext = path.extension // "txt"
|
|
510
|
-
let joined = path.appending("data") // /home/user/documents/file.txt/data
|
|
511
|
-
|
|
512
|
-
// Environment variable expansion
|
|
513
|
-
let expanded = path.expandingEnvironmentVariables(["HOME": "/home/user"])
|
|
514
|
-
|
|
515
|
-
// Home directory
|
|
516
|
-
let home = PlatformPath.homeDirectory
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
### Custom Platform Context
|
|
520
|
-
|
|
521
|
-
Create mock contexts for testing:
|
|
522
|
-
|
|
523
|
-
```swift
|
|
524
|
-
let mockContext = MockPlatformContext(
|
|
525
|
-
arguments: ["test", "--verbose"],
|
|
526
|
-
environment: ["TEST_MODE": "1"],
|
|
527
|
-
currentDirectory: "/test/workspace"
|
|
528
|
-
)
|
|
529
|
-
|
|
530
|
-
// Use in tests
|
|
531
|
-
let args = mockContext.arguments // ["test", "--verbose"]
|
|
532
|
-
let env = mockContext.environment // ["TEST_MODE": "1"]
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
---
|
|
536
|
-
|
|
537
|
-
## Testing
|
|
538
|
-
|
|
539
|
-
### Running Tests
|
|
540
|
-
|
|
541
|
-
```bash
|
|
542
|
-
swift test
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
### Test Coverage
|
|
546
|
-
|
|
547
|
-
| Test File | Coverage |
|
|
548
|
-
|-----------|----------|
|
|
549
|
-
| `CommandParserTests.swift` | Tokenization, parsing, options, flags |
|
|
550
|
-
| `HelpGeneratorTests.swift` | Usage/help formatting |
|
|
551
|
-
| `HelpVersionBehaviorTests.swift` | `--help`, `--version`, empty-invocation help |
|
|
552
|
-
| `PropertyWrappersTests.swift` | Wrapper initialization and registration |
|
|
553
|
-
| `ParsableCommandBindingTests.swift` | End-to-end binding for options/flags/arguments/option groups |
|
|
554
|
-
| `PlatformContextTests.swift` | Platform abstraction |
|
|
555
|
-
| `PlatformPathTests.swift` | Path operations |
|
|
556
|
-
| `StdioTests.swift` | I/O stream handling |
|
|
557
|
-
|
|
558
|
-
### Mock Platform Context
|
|
559
|
-
|
|
560
|
-
Use `MockPlatformContext` for deterministic testing:
|
|
561
|
-
|
|
562
|
-
```swift
|
|
563
|
-
let ctx = MockPlatformContext(
|
|
564
|
-
arguments: ["cmd", "arg1", "arg2"],
|
|
565
|
-
environment: ["KEY": "value"],
|
|
566
|
-
currentDirectory: "/test"
|
|
567
|
-
)
|
|
568
|
-
|
|
569
|
-
// Verify environment
|
|
570
|
-
XCTAssertEqual(ctx.environmentVariable("KEY"), "value")
|
|
571
|
-
|
|
572
|
-
// Verify arguments
|
|
573
|
-
XCTAssertEqual(ctx.arguments, ["arg1", "arg2"])
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
### Unit Testing Commands
|
|
577
|
-
|
|
578
|
-
```swift
|
|
579
|
-
import XCTest
|
|
580
|
-
@testable import Helix
|
|
581
|
-
|
|
582
|
-
final class MyCommandTests: XCTestCase {
|
|
583
|
-
func testParsing() throws {
|
|
584
|
-
var command = MyCommand()
|
|
585
|
-
|
|
586
|
-
// Manually set properties for testing
|
|
587
|
-
command.name = "test"
|
|
588
|
-
command.count = 5
|
|
589
|
-
|
|
590
|
-
XCTAssertEqual(command.name, "test")
|
|
591
|
-
XCTAssertEqual(command.count, 5)
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
func testValidation() throws {
|
|
595
|
-
var command = MyCommand()
|
|
596
|
-
command.value = -1 // Invalid
|
|
597
|
-
|
|
598
|
-
XCTAssertThrowsError(try command.validate())
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
---
|
|
604
|
-
|
|
605
|
-
## API Reference
|
|
606
|
-
|
|
607
|
-
### ParsableCommand Protocol
|
|
608
|
-
|
|
609
|
-
```swift
|
|
610
|
-
@MainActor
|
|
611
|
-
public protocol ParsableCommand: Sendable {
|
|
612
|
-
/// Required initializer
|
|
613
|
-
init()
|
|
614
|
-
|
|
615
|
-
/// Command metadata (optional, defaults to auto-generated)
|
|
616
|
-
static var commandDescription: CommandDescription { get }
|
|
617
|
-
|
|
618
|
-
/// Main command logic
|
|
619
|
-
mutating func run() async throws
|
|
620
|
-
|
|
621
|
-
/// Validation (optional)
|
|
622
|
-
mutating func validate() throws
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
extension ParsableCommand {
|
|
626
|
-
/// Run with arguments from current platform
|
|
627
|
-
static func main() async throws
|
|
628
|
-
|
|
629
|
-
/// Run with custom arguments
|
|
630
|
-
static func main(arguments: [String]) async throws
|
|
631
|
-
|
|
632
|
-
/// Create a descriptor for this command
|
|
633
|
-
static var descriptor: CommandDescriptor { get }
|
|
634
|
-
}
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
Note: `Program.resolve` accepts `argv` with or without the root command name when you initialize `Program` with a single descriptor. If you provide multiple root descriptors, the first argument must select the command. `DefaultPlatformContext.shared.arguments` strips the executable name.
|
|
638
|
-
|
|
639
|
-
### CommandDescription
|
|
640
|
-
|
|
641
|
-
```swift
|
|
642
|
-
public struct CommandDescription: Sendable {
|
|
643
|
-
public var commandName: String?
|
|
644
|
-
public var abstract: String
|
|
645
|
-
public var discussion: String?
|
|
646
|
-
public var version: String?
|
|
647
|
-
public var subcommands: [any ParsableCommand.Type]
|
|
648
|
-
public var defaultSubcommand: (any ParsableCommand.Type)?
|
|
649
|
-
public var showHelpOnEmptyInvocation: Bool
|
|
650
|
-
}
|
|
651
|
-
```
|
|
652
|
-
|
|
653
|
-
### HelixError
|
|
654
|
-
|
|
655
|
-
```swift
|
|
656
|
-
public enum HelixError: Error, Sendable {
|
|
657
|
-
case missingCommand
|
|
658
|
-
case unknownCommand(String)
|
|
659
|
-
case missingSubcommand(command: String)
|
|
660
|
-
case unknownSubcommand(command: String, name: String)
|
|
661
|
-
case parsingError(String)
|
|
662
|
-
case missingEnvironmentVariable(String)
|
|
663
|
-
case webAssemblyExit(Int32)
|
|
664
|
-
case optionNotBound(String)
|
|
665
|
-
case argumentNotBound(String)
|
|
666
|
-
case validationError(String)
|
|
667
|
-
case helpRequested
|
|
668
|
-
case versionRequested
|
|
669
|
-
}
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
### ExitCode
|
|
673
|
-
|
|
674
|
-
```swift
|
|
675
|
-
public struct ExitCode: Error, Equatable, Sendable {
|
|
676
|
-
public let rawValue: Int32
|
|
677
|
-
public static let success = ExitCode(0)
|
|
678
|
-
public static let failure = ExitCode(1)
|
|
679
|
-
}
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
### ValidationError
|
|
683
|
-
|
|
684
|
-
```swift
|
|
685
|
-
public struct ValidationError: Error, LocalizedError, Sendable {
|
|
686
|
-
public init(_ message: String)
|
|
687
|
-
public var errorDescription: String? { message }
|
|
688
|
-
}
|
|
689
|
-
```
|
|
690
|
-
|
|
691
|
-
---
|
|
692
|
-
|
|
693
|
-
## Migration Guide
|
|
694
|
-
|
|
695
|
-
### From Swift ArgumentParser
|
|
696
|
-
|
|
697
|
-
| ArgumentParser | Helix |
|
|
698
|
-
|----------------|-------|
|
|
699
|
-
| `ArgumentParser` | `Program` |
|
|
700
|
-
| `ParsableCommand` | `ParsableCommand` |
|
|
701
|
-
| `Option` | `@Option` |
|
|
702
|
-
| `Argument` | `@Argument` |
|
|
703
|
-
| `Flag` | `@Flag` |
|
|
704
|
-
| `OptionGroup` | `@OptionGroup` |
|
|
705
|
-
| `validate()` | `validate()` |
|
|
706
|
-
|
|
707
|
-
### Key Differences
|
|
708
|
-
|
|
709
|
-
1. **Command Name**: Helix defaults to the type name (with a trailing `Command` removed). Use `commandName` in `CommandDescription` when you need lowercase CLI names or when relying on `defaultSubcommand`.
|
|
710
|
-
|
|
711
|
-
2. **Subcommand Registration**: ArgumentParser uses nested types; Helix uses `subcommands` array in `CommandDescription`.
|
|
712
|
-
|
|
713
|
-
3. **Main Entry**: ArgumentParser uses `@main` attribute; Helix uses `try await MyCommand.main(arguments:)`. If you initialize `Program` with multiple root descriptors, the first argument must select the command. If you initialize `Program` with a single descriptor, `Program.resolve` can also accept `argv` without the root command name.
|
|
714
|
-
|
|
715
|
-
### Example Migration
|
|
716
|
-
|
|
717
|
-
**Before (ArgumentParser):**
|
|
718
|
-
|
|
719
|
-
```swift
|
|
720
|
-
import ArgumentParser
|
|
721
|
-
|
|
722
|
-
struct Math: ParsableCommand {
|
|
723
|
-
@Option(name: .short)
|
|
724
|
-
var operation: String
|
|
725
|
-
|
|
726
|
-
@Argument
|
|
727
|
-
var numbers: [Int]
|
|
728
|
-
|
|
729
|
-
func run() throws {
|
|
730
|
-
// ...
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
**After (Helix):**
|
|
736
|
-
|
|
737
|
-
```swift
|
|
738
|
-
import Helix
|
|
739
|
-
|
|
740
|
-
struct MathCommand: ParsableCommand {
|
|
741
|
-
@Option(name: .short('o'), help: "Operation")
|
|
742
|
-
var operation: String
|
|
743
|
-
|
|
744
|
-
@Argument(help: "Numbers to process")
|
|
745
|
-
var numbers: [Int]
|
|
746
|
-
|
|
747
|
-
static var commandDescription: CommandDescription {
|
|
748
|
-
CommandDescription(
|
|
749
|
-
commandName: "math",
|
|
750
|
-
abstract: "Perform mathematical operations"
|
|
751
|
-
)
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
mutating func run() async throws {
|
|
755
|
-
// ...
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
```
|
|
759
|
-
|
|
760
|
-
---
|
|
761
|
-
|
|
762
|
-
## Best Practices
|
|
763
|
-
|
|
764
|
-
### 1. Command Organization
|
|
765
|
-
|
|
766
|
-
```swift
|
|
767
|
-
// Group related commands
|
|
768
|
-
enum Database {
|
|
769
|
-
struct MigrateCommand: ParsableCommand { /* ... */ }
|
|
770
|
-
struct SeedCommand: ParsableCommand { /* ... */ }
|
|
771
|
-
struct BackupCommand: ParsableCommand { /* ... */ }
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// Or use namespaces
|
|
775
|
-
struct CLI {
|
|
776
|
-
struct Init: ParsableCommand { /* ... */ }
|
|
777
|
-
struct Build: ParsableCommand { /* ... */ }
|
|
778
|
-
struct Test: ParsableCommand { /* ... */ }
|
|
779
|
-
}
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
### 2. Error Handling
|
|
783
|
-
|
|
784
|
-
```swift
|
|
785
|
-
enum CLIError: Error, CustomStringConvertible {
|
|
786
|
-
case fileNotFound(String)
|
|
787
|
-
case invalidConfig(String)
|
|
788
|
-
case networkError(Error)
|
|
789
|
-
|
|
790
|
-
var description: String {
|
|
791
|
-
switch self {
|
|
792
|
-
case .fileNotFound(let path):
|
|
793
|
-
return "File not found: \(path)"
|
|
794
|
-
case .invalidConfig(let message):
|
|
795
|
-
return "Invalid configuration: \(message)"
|
|
796
|
-
case .networkError(let error):
|
|
797
|
-
return "Network error: \(error.localizedDescription)"
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
struct MyCommand: ParsableCommand {
|
|
803
|
-
mutating func run() async throws {
|
|
804
|
-
do {
|
|
805
|
-
try await performOperation()
|
|
806
|
-
} catch let error as CLIError {
|
|
807
|
-
print("Error: \(error)")
|
|
808
|
-
throw ExitCode.failure
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
```
|
|
813
|
-
|
|
814
|
-
### 3. Reusable Option Groups
|
|
815
|
-
|
|
816
|
-
```swift
|
|
817
|
-
struct CommonOptions: HelixParsable {
|
|
818
|
-
@Flag(name: .shortAndLong, help: "Verbose output")
|
|
819
|
-
var verbose: Bool = false
|
|
820
|
-
|
|
821
|
-
@Option(name: .short('o'), help: "Output file")
|
|
822
|
-
var output: String?
|
|
823
|
-
|
|
824
|
-
@Flag(name: .short('f'), help: "Force overwrite")
|
|
825
|
-
var force: Bool = false
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
struct BuildCommand: ParsableCommand {
|
|
829
|
-
@OptionGroup
|
|
830
|
-
var common: CommonOptions
|
|
831
|
-
|
|
832
|
-
@Option(help: "Configuration")
|
|
833
|
-
var config: String = "release"
|
|
834
|
-
|
|
835
|
-
mutating func run() async throws {
|
|
836
|
-
if common.verbose {
|
|
837
|
-
print("Configuration: \(config)")
|
|
838
|
-
}
|
|
839
|
-
// ...
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
```
|
|
843
|
-
|
|
844
|
-
---
|
|
845
|
-
|
|
846
|
-
## Performance
|
|
847
|
-
|
|
848
|
-
### Parsing Overhead
|
|
849
|
-
|
|
850
|
-
Helix is designed for minimal overhead:
|
|
851
|
-
|
|
852
|
-
- **Reflection**: Only used once during descriptor generation
|
|
853
|
-
- **Parsing**: Single-pass tokenizer with O(n) complexity
|
|
854
|
-
- **Memory**: No intermediate allocations beyond argument storage
|
|
855
|
-
|
|
856
|
-
### Large Argument Counts
|
|
857
|
-
|
|
858
|
-
Helix handles large argument counts efficiently:
|
|
859
|
-
|
|
860
|
-
```bash
|
|
861
|
-
# Process thousands of files
|
|
862
|
-
$ process *.txt --output results.json
|
|
863
|
-
```
|
|
864
|
-
|
|
865
|
-
### Concurrency
|
|
866
|
-
|
|
867
|
-
All commands run on the main actor (`@MainActor`) for thread safety:
|
|
868
|
-
|
|
869
|
-
```swift
|
|
870
|
-
@MainActor
|
|
871
|
-
public protocol ParsableCommand: Sendable {
|
|
872
|
-
mutating func run() async throws
|
|
873
|
-
}
|
|
874
|
-
```
|
|
875
|
-
|
|
876
|
-
---
|
|
877
|
-
|
|
878
|
-
## FAQ
|
|
879
|
-
|
|
880
|
-
### Q: How do I handle secret values?
|
|
881
|
-
|
|
882
|
-
```swift
|
|
883
|
-
struct SecureCommand: ParsableCommand {
|
|
884
|
-
@Option(help: "API key (prefer using env var)")
|
|
885
|
-
var apiKey: String?
|
|
886
|
-
|
|
887
|
-
mutating func run() async throws {
|
|
888
|
-
let key = apiKey ?? PlatformContext.current.environmentVariable("API_KEY") ?? ""
|
|
889
|
-
// Use key...
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
```
|
|
893
|
-
|
|
894
|
-
### Q: Can I use Helix in a synchronous context?
|
|
895
|
-
|
|
896
|
-
`run()` must be `async throws` to conform to `ParsableCommand`, but you can keep the body synchronous:
|
|
897
|
-
|
|
898
|
-
```swift
|
|
899
|
-
struct SyncCommand: ParsableCommand {
|
|
900
|
-
@Option(help: "Name")
|
|
901
|
-
var name: String
|
|
902
|
-
|
|
903
|
-
mutating func run() async throws {
|
|
904
|
-
print("Hello, \(name)!")
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
```
|
|
908
|
-
|
|
909
|
-
### Q: How do I handle `--help` and `--version`?
|
|
910
|
-
|
|
911
|
-
Helix supports built-in help and version handling:
|
|
912
|
-
|
|
913
|
-
- `-h` / `--help` prints help and exits before running command logic.
|
|
914
|
-
- `-V` / `--version` prints `CommandDescription.version` (if set) and exits before running command logic.
|
|
915
|
-
- `CommandDescription.showHelpOnEmptyInvocation = true` prints help when invoked with no arguments.
|
|
916
|
-
|
|
917
|
-
```swift
|
|
918
|
-
struct MyCommand: ParsableCommand {
|
|
919
|
-
static var commandDescription: CommandDescription {
|
|
920
|
-
CommandDescription(version: "1.0.0", showHelpOnEmptyInvocation: true)
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
mutating func run() async throws { /* ... */ }
|
|
924
|
-
}
|
|
925
|
-
```
|
|
926
|
-
|
|
927
|
-
### Q: Can I use Helix with Vapor or other server frameworks?
|
|
928
|
-
|
|
929
|
-
Yes! Helix can be used alongside any Swift framework:
|
|
930
|
-
|
|
931
|
-
```swift
|
|
932
|
-
// In a Vapor route
|
|
933
|
-
app.post("run") { req async in
|
|
934
|
-
try await MyCommand.main(arguments: req.body.string.split(separator: " ").map(String.init))
|
|
935
|
-
return "Done"
|
|
936
|
-
}
|
|
937
|
-
```
|
|
938
|
-
|
|
939
|
-
---
|
|
940
|
-
|
|
941
|
-
## Contributing
|
|
942
|
-
|
|
943
|
-
Contributions are welcome. See `CONTRIBUTING.md`.
|
|
944
|
-
|
|
945
|
-
### Areas for Contribution
|
|
946
|
-
|
|
947
|
-
1. Shell completion script generation
|
|
948
|
-
2. Help text with ANSI colors and richer formatting
|
|
949
|
-
3. Additional type conformances (URL, UUID, Decimal)
|
|
950
|
-
4. Cross-platform test coverage (Windows, WASI)
|
|
951
|
-
5. More example commands and cookbook patterns
|
|
952
|
-
|
|
953
|
-
## Security
|
|
954
|
-
|
|
955
|
-
See `SECURITY.md`.
|
|
956
|
-
|
|
957
|
-
## Changelog
|
|
958
|
-
|
|
959
|
-
See `CHANGELOG.md`.
|
|
960
|
-
|
|
961
|
-
---
|
|
962
|
-
|
|
963
|
-
## License
|
|
964
|
-
|
|
965
|
-
Helix is released under the MIT License. See [LICENSE](LICENSE) for details.
|
|
966
|
-
|
|
967
|
-
---
|
|
968
|
-
|
|
969
|
-
## Acknowledgments
|
|
970
|
-
|
|
971
|
-
Inspired by:
|
|
972
|
-
- [Swift ArgumentParser](https://github.com/apple/swift-argument-parser)
|
|
973
|
-
|
|
974
|
-
---
|
|
975
|
-
|
|
976
|
-
<p align="center">Made with ❤️ for the Swift community</p>
|