@jacques-ai/cli 0.0.7-alpha.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/dist/assets/mascot-ansi.d.ts +8 -0
- package/dist/assets/mascot-ansi.d.ts.map +1 -0
- package/dist/assets/mascot-ansi.js +15 -0
- package/dist/assets/mascot-ansi.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +53 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/client-helper.d.ts +14 -0
- package/dist/commands/client-helper.d.ts.map +1 -0
- package/dist/commands/client-helper.js +38 -0
- package/dist/commands/client-helper.js.map +1 -0
- package/dist/commands/dashboard.d.ts +8 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +159 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/list.d.ts +5 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +13 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/search.d.ts +13 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +79 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/setup.d.ts +8 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +32 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +27 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/components/ActiveSessionsView.d.ts +18 -0
- package/dist/components/ActiveSessionsView.d.ts.map +1 -0
- package/dist/components/ActiveSessionsView.js +68 -0
- package/dist/components/ActiveSessionsView.js.map +1 -0
- package/dist/components/AddContextConfirmView.d.ts +21 -0
- package/dist/components/AddContextConfirmView.d.ts.map +1 -0
- package/dist/components/AddContextConfirmView.js +44 -0
- package/dist/components/AddContextConfirmView.js.map +1 -0
- package/dist/components/App.d.ts +11 -0
- package/dist/components/App.d.ts.map +1 -0
- package/dist/components/App.js +292 -0
- package/dist/components/App.js.map +1 -0
- package/dist/components/ArchiveBrowserView.d.ts +40 -0
- package/dist/components/ArchiveBrowserView.d.ts.map +1 -0
- package/dist/components/ArchiveBrowserView.js +161 -0
- package/dist/components/ArchiveBrowserView.js.map +1 -0
- package/dist/components/ArchiveInitProgressView.d.ts +16 -0
- package/dist/components/ArchiveInitProgressView.d.ts.map +1 -0
- package/dist/components/ArchiveInitProgressView.js +72 -0
- package/dist/components/ArchiveInitProgressView.js.map +1 -0
- package/dist/components/AutoCompactToggle.d.ts +16 -0
- package/dist/components/AutoCompactToggle.d.ts.map +1 -0
- package/dist/components/AutoCompactToggle.js +12 -0
- package/dist/components/AutoCompactToggle.js.map +1 -0
- package/dist/components/BottomControls.d.ts +12 -0
- package/dist/components/BottomControls.d.ts.map +1 -0
- package/dist/components/BottomControls.js +11 -0
- package/dist/components/BottomControls.js.map +1 -0
- package/dist/components/CompactHeader.d.ts +21 -0
- package/dist/components/CompactHeader.d.ts.map +1 -0
- package/dist/components/CompactHeader.js +63 -0
- package/dist/components/CompactHeader.js.map +1 -0
- package/dist/components/CompactPanel.d.ts +22 -0
- package/dist/components/CompactPanel.d.ts.map +1 -0
- package/dist/components/CompactPanel.js +31 -0
- package/dist/components/CompactPanel.js.map +1 -0
- package/dist/components/ContentBox.d.ts +14 -0
- package/dist/components/ContentBox.d.ts.map +1 -0
- package/dist/components/ContentBox.js +24 -0
- package/dist/components/ContentBox.js.map +1 -0
- package/dist/components/ContextProgress.d.ts +14 -0
- package/dist/components/ContextProgress.d.ts.map +1 -0
- package/dist/components/ContextProgress.js +36 -0
- package/dist/components/ContextProgress.js.map +1 -0
- package/dist/components/Dashboard.d.ts +46 -0
- package/dist/components/Dashboard.d.ts.map +1 -0
- package/dist/components/Dashboard.js +67 -0
- package/dist/components/Dashboard.js.map +1 -0
- package/dist/components/FilterSelectionView.d.ts +15 -0
- package/dist/components/FilterSelectionView.d.ts.map +1 -0
- package/dist/components/FilterSelectionView.js +27 -0
- package/dist/components/FilterSelectionView.js.map +1 -0
- package/dist/components/GoogleDocsBrowserView.d.ts +20 -0
- package/dist/components/GoogleDocsBrowserView.d.ts.map +1 -0
- package/dist/components/GoogleDocsBrowserView.js +72 -0
- package/dist/components/GoogleDocsBrowserView.js.map +1 -0
- package/dist/components/HandoffBrowserView.d.ts +20 -0
- package/dist/components/HandoffBrowserView.d.ts.map +1 -0
- package/dist/components/HandoffBrowserView.js +66 -0
- package/dist/components/HandoffBrowserView.js.map +1 -0
- package/dist/components/Header.d.ts +14 -0
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.js +16 -0
- package/dist/components/Header.js.map +1 -0
- package/dist/components/HorizontalMenu.d.ts +24 -0
- package/dist/components/HorizontalMenu.d.ts.map +1 -0
- package/dist/components/HorizontalMenu.js +20 -0
- package/dist/components/HorizontalMenu.js.map +1 -0
- package/dist/components/ImageMascot.d.ts +8 -0
- package/dist/components/ImageMascot.d.ts.map +1 -0
- package/dist/components/ImageMascot.js +7 -0
- package/dist/components/ImageMascot.js.map +1 -0
- package/dist/components/LLMWorkingView.d.ts +19 -0
- package/dist/components/LLMWorkingView.d.ts.map +1 -0
- package/dist/components/LLMWorkingView.js +72 -0
- package/dist/components/LLMWorkingView.js.map +1 -0
- package/dist/components/LoadContextView.d.ts +22 -0
- package/dist/components/LoadContextView.d.ts.map +1 -0
- package/dist/components/LoadContextView.js +83 -0
- package/dist/components/LoadContextView.js.map +1 -0
- package/dist/components/MainMenuView.d.ts +20 -0
- package/dist/components/MainMenuView.d.ts.map +1 -0
- package/dist/components/MainMenuView.js +49 -0
- package/dist/components/MainMenuView.js.map +1 -0
- package/dist/components/Mascot.d.ts +24 -0
- package/dist/components/Mascot.d.ts.map +1 -0
- package/dist/components/Mascot.js +32 -0
- package/dist/components/Mascot.js.map +1 -0
- package/dist/components/Menu.d.ts +22 -0
- package/dist/components/Menu.d.ts.map +1 -0
- package/dist/components/Menu.js +28 -0
- package/dist/components/Menu.js.map +1 -0
- package/dist/components/NotionBrowserView.d.ts +21 -0
- package/dist/components/NotionBrowserView.d.ts.map +1 -0
- package/dist/components/NotionBrowserView.js +75 -0
- package/dist/components/NotionBrowserView.js.map +1 -0
- package/dist/components/ObsidianBrowserView.d.ts +21 -0
- package/dist/components/ObsidianBrowserView.d.ts.map +1 -0
- package/dist/components/ObsidianBrowserView.js +74 -0
- package/dist/components/ObsidianBrowserView.js.map +1 -0
- package/dist/components/ObsidianConfigView.d.ts +19 -0
- package/dist/components/ObsidianConfigView.d.ts.map +1 -0
- package/dist/components/ObsidianConfigView.js +45 -0
- package/dist/components/ObsidianConfigView.js.map +1 -0
- package/dist/components/PlaceholderView.d.ts +15 -0
- package/dist/components/PlaceholderView.d.ts.map +1 -0
- package/dist/components/PlaceholderView.js +20 -0
- package/dist/components/PlaceholderView.js.map +1 -0
- package/dist/components/PlanViewerView.d.ts +26 -0
- package/dist/components/PlanViewerView.d.ts.map +1 -0
- package/dist/components/PlanViewerView.js +170 -0
- package/dist/components/PlanViewerView.js.map +1 -0
- package/dist/components/ProgressBar.d.ts +24 -0
- package/dist/components/ProgressBar.d.ts.map +1 -0
- package/dist/components/ProgressBar.js +23 -0
- package/dist/components/ProgressBar.js.map +1 -0
- package/dist/components/ProjectDashboardView.d.ts +38 -0
- package/dist/components/ProjectDashboardView.d.ts.map +1 -0
- package/dist/components/ProjectDashboardView.js +129 -0
- package/dist/components/ProjectDashboardView.js.map +1 -0
- package/dist/components/SaveContextView.d.ts +32 -0
- package/dist/components/SaveContextView.d.ts.map +1 -0
- package/dist/components/SaveContextView.js +41 -0
- package/dist/components/SaveContextView.js.map +1 -0
- package/dist/components/SessionDetails.d.ts +14 -0
- package/dist/components/SessionDetails.d.ts.map +1 -0
- package/dist/components/SessionDetails.js +87 -0
- package/dist/components/SessionDetails.js.map +1 -0
- package/dist/components/SessionsExperimentView.d.ts +35 -0
- package/dist/components/SessionsExperimentView.d.ts.map +1 -0
- package/dist/components/SessionsExperimentView.js +347 -0
- package/dist/components/SessionsExperimentView.js.map +1 -0
- package/dist/components/SessionsList.d.ts +20 -0
- package/dist/components/SessionsList.d.ts.map +1 -0
- package/dist/components/SessionsList.js +75 -0
- package/dist/components/SessionsList.js.map +1 -0
- package/dist/components/SettingsView.d.ts +48 -0
- package/dist/components/SettingsView.d.ts.map +1 -0
- package/dist/components/SettingsView.js +156 -0
- package/dist/components/SettingsView.js.map +1 -0
- package/dist/components/SourceSelectionView.d.ts +26 -0
- package/dist/components/SourceSelectionView.d.ts.map +1 -0
- package/dist/components/SourceSelectionView.js +109 -0
- package/dist/components/SourceSelectionView.js.map +1 -0
- package/dist/components/VerticalMenu.d.ts +18 -0
- package/dist/components/VerticalMenu.d.ts.map +1 -0
- package/dist/components/VerticalMenu.js +15 -0
- package/dist/components/VerticalMenu.js.map +1 -0
- package/dist/components/ascii-art/index.d.ts +6 -0
- package/dist/components/ascii-art/index.d.ts.map +1 -0
- package/dist/components/ascii-art/index.js +6 -0
- package/dist/components/ascii-art/index.js.map +1 -0
- package/dist/components/ascii-art/progress.d.ts +53 -0
- package/dist/components/ascii-art/progress.d.ts.map +1 -0
- package/dist/components/ascii-art/progress.js +102 -0
- package/dist/components/ascii-art/progress.js.map +1 -0
- package/dist/components/ascii-art/scene.d.ts +34 -0
- package/dist/components/ascii-art/scene.d.ts.map +1 -0
- package/dist/components/ascii-art/scene.js +72 -0
- package/dist/components/ascii-art/scene.js.map +1 -0
- package/dist/components/index.d.ts +27 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +25 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/layout/HorizontalLayout.d.ts +19 -0
- package/dist/components/layout/HorizontalLayout.d.ts.map +1 -0
- package/dist/components/layout/HorizontalLayout.js +62 -0
- package/dist/components/layout/HorizontalLayout.js.map +1 -0
- package/dist/components/layout/VerticalLayout.d.ts +16 -0
- package/dist/components/layout/VerticalLayout.d.ts.map +1 -0
- package/dist/components/layout/VerticalLayout.js +32 -0
- package/dist/components/layout/VerticalLayout.js.map +1 -0
- package/dist/components/layout/index.d.ts +6 -0
- package/dist/components/layout/index.d.ts.map +1 -0
- package/dist/components/layout/index.js +4 -0
- package/dist/components/layout/index.js.map +1 -0
- package/dist/components/layout/theme.d.ts +20 -0
- package/dist/components/layout/theme.d.ts.map +1 -0
- package/dist/components/layout/theme.js +23 -0
- package/dist/components/layout/theme.js.map +1 -0
- package/dist/components/setup/DoneStep.d.ts +7 -0
- package/dist/components/setup/DoneStep.d.ts.map +1 -0
- package/dist/components/setup/DoneStep.js +31 -0
- package/dist/components/setup/DoneStep.js.map +1 -0
- package/dist/components/setup/InstallingStep.d.ts +11 -0
- package/dist/components/setup/InstallingStep.d.ts.map +1 -0
- package/dist/components/setup/InstallingStep.js +27 -0
- package/dist/components/setup/InstallingStep.js.map +1 -0
- package/dist/components/setup/OptionsStep.d.ts +9 -0
- package/dist/components/setup/OptionsStep.d.ts.map +1 -0
- package/dist/components/setup/OptionsStep.js +39 -0
- package/dist/components/setup/OptionsStep.js.map +1 -0
- package/dist/components/setup/PrerequisitesStep.d.ts +7 -0
- package/dist/components/setup/PrerequisitesStep.d.ts.map +1 -0
- package/dist/components/setup/PrerequisitesStep.js +31 -0
- package/dist/components/setup/PrerequisitesStep.js.map +1 -0
- package/dist/components/setup/SetupFrame.d.ts +17 -0
- package/dist/components/setup/SetupFrame.d.ts.map +1 -0
- package/dist/components/setup/SetupFrame.js +33 -0
- package/dist/components/setup/SetupFrame.js.map +1 -0
- package/dist/components/setup/SetupWizard.d.ts +9 -0
- package/dist/components/setup/SetupWizard.d.ts.map +1 -0
- package/dist/components/setup/SetupWizard.js +149 -0
- package/dist/components/setup/SetupWizard.js.map +1 -0
- package/dist/components/setup/Spinner.d.ts +10 -0
- package/dist/components/setup/Spinner.d.ts.map +1 -0
- package/dist/components/setup/Spinner.js +20 -0
- package/dist/components/setup/Spinner.js.map +1 -0
- package/dist/components/setup/SyncStep.d.ts +11 -0
- package/dist/components/setup/SyncStep.d.ts.map +1 -0
- package/dist/components/setup/SyncStep.js +47 -0
- package/dist/components/setup/SyncStep.js.map +1 -0
- package/dist/components/setup/VerificationStep.d.ts +7 -0
- package/dist/components/setup/VerificationStep.d.ts.map +1 -0
- package/dist/components/setup/VerificationStep.js +27 -0
- package/dist/components/setup/VerificationStep.js.map +1 -0
- package/dist/components/setup/WelcomeStep.d.ts +6 -0
- package/dist/components/setup/WelcomeStep.d.ts.map +1 -0
- package/dist/components/setup/WelcomeStep.js +17 -0
- package/dist/components/setup/WelcomeStep.js.map +1 -0
- package/dist/components/shared/ProgressLine.d.ts +10 -0
- package/dist/components/shared/ProgressLine.d.ts.map +1 -0
- package/dist/components/shared/ProgressLine.js +18 -0
- package/dist/components/shared/ProgressLine.js.map +1 -0
- package/dist/components/shared/ProjectLine.d.ts +10 -0
- package/dist/components/shared/ProjectLine.d.ts.map +1 -0
- package/dist/components/shared/ProjectLine.js +19 -0
- package/dist/components/shared/ProjectLine.js.map +1 -0
- package/dist/components/shared/ScanningIndicator.d.ts +9 -0
- package/dist/components/shared/ScanningIndicator.d.ts.map +1 -0
- package/dist/components/shared/ScanningIndicator.js +22 -0
- package/dist/components/shared/ScanningIndicator.js.map +1 -0
- package/dist/components/shared/StatusLine.d.ts +13 -0
- package/dist/components/shared/StatusLine.d.ts.map +1 -0
- package/dist/components/shared/StatusLine.js +24 -0
- package/dist/components/shared/StatusLine.js.map +1 -0
- package/dist/components/shared/StatusLine.test.d.ts +8 -0
- package/dist/components/shared/StatusLine.test.d.ts.map +1 -0
- package/dist/components/shared/StatusLine.test.js +50 -0
- package/dist/components/shared/StatusLine.test.js.map +1 -0
- package/dist/components/shared/index.d.ts +4 -0
- package/dist/components/shared/index.d.ts.map +1 -0
- package/dist/components/shared/index.js +4 -0
- package/dist/components/shared/index.js.map +1 -0
- package/dist/handoff/catalog.test.d.ts +5 -0
- package/dist/handoff/catalog.test.d.ts.map +1 -0
- package/dist/handoff/catalog.test.js +172 -0
- package/dist/handoff/catalog.test.js.map +1 -0
- package/dist/handoff/subagents.test.d.ts +8 -0
- package/dist/handoff/subagents.test.d.ts.map +1 -0
- package/dist/handoff/subagents.test.js +139 -0
- package/dist/handoff/subagents.test.js.map +1 -0
- package/dist/hooks/useAddContextFlow.d.ts +29 -0
- package/dist/hooks/useAddContextFlow.d.ts.map +1 -0
- package/dist/hooks/useAddContextFlow.js +83 -0
- package/dist/hooks/useAddContextFlow.js.map +1 -0
- package/dist/hooks/useArchiveBrowser.d.ts +37 -0
- package/dist/hooks/useArchiveBrowser.d.ts.map +1 -0
- package/dist/hooks/useArchiveBrowser.js +175 -0
- package/dist/hooks/useArchiveBrowser.js.map +1 -0
- package/dist/hooks/useClaudeToken.d.ts +29 -0
- package/dist/hooks/useClaudeToken.d.ts.map +1 -0
- package/dist/hooks/useClaudeToken.js +166 -0
- package/dist/hooks/useClaudeToken.js.map +1 -0
- package/dist/hooks/useGoogleDocsBrowser.d.ts +29 -0
- package/dist/hooks/useGoogleDocsBrowser.d.ts.map +1 -0
- package/dist/hooks/useGoogleDocsBrowser.js +146 -0
- package/dist/hooks/useGoogleDocsBrowser.js.map +1 -0
- package/dist/hooks/useHandoffBrowser.d.ts +27 -0
- package/dist/hooks/useHandoffBrowser.d.ts.map +1 -0
- package/dist/hooks/useHandoffBrowser.js +92 -0
- package/dist/hooks/useHandoffBrowser.js.map +1 -0
- package/dist/hooks/useJacquesClient.d.ts +69 -0
- package/dist/hooks/useJacquesClient.d.ts.map +1 -0
- package/dist/hooks/useJacquesClient.js +248 -0
- package/dist/hooks/useJacquesClient.js.map +1 -0
- package/dist/hooks/useLlmWorking.d.ts +30 -0
- package/dist/hooks/useLlmWorking.d.ts.map +1 -0
- package/dist/hooks/useLlmWorking.js +127 -0
- package/dist/hooks/useLlmWorking.js.map +1 -0
- package/dist/hooks/useLoadContext.d.ts +27 -0
- package/dist/hooks/useLoadContext.d.ts.map +1 -0
- package/dist/hooks/useLoadContext.js +102 -0
- package/dist/hooks/useLoadContext.js.map +1 -0
- package/dist/hooks/useNotification.d.ts +36 -0
- package/dist/hooks/useNotification.d.ts.map +1 -0
- package/dist/hooks/useNotification.js +89 -0
- package/dist/hooks/useNotification.js.map +1 -0
- package/dist/hooks/useNotification.test.d.ts +9 -0
- package/dist/hooks/useNotification.test.d.ts.map +1 -0
- package/dist/hooks/useNotification.test.js +144 -0
- package/dist/hooks/useNotification.test.js.map +1 -0
- package/dist/hooks/useNotionBrowser.d.ts +30 -0
- package/dist/hooks/useNotionBrowser.d.ts.map +1 -0
- package/dist/hooks/useNotionBrowser.js +154 -0
- package/dist/hooks/useNotionBrowser.js.map +1 -0
- package/dist/hooks/useObsidianBrowser.d.ts +42 -0
- package/dist/hooks/useObsidianBrowser.d.ts.map +1 -0
- package/dist/hooks/useObsidianBrowser.js +63 -0
- package/dist/hooks/useObsidianBrowser.js.map +1 -0
- package/dist/hooks/useObsidianConfig.d.ts +28 -0
- package/dist/hooks/useObsidianConfig.d.ts.map +1 -0
- package/dist/hooks/useObsidianConfig.js +119 -0
- package/dist/hooks/useObsidianConfig.js.map +1 -0
- package/dist/hooks/useObsidianFileBrowser.d.ts +29 -0
- package/dist/hooks/useObsidianFileBrowser.d.ts.map +1 -0
- package/dist/hooks/useObsidianFileBrowser.js +114 -0
- package/dist/hooks/useObsidianFileBrowser.js.map +1 -0
- package/dist/hooks/useProjectDashboard.d.ts +38 -0
- package/dist/hooks/useProjectDashboard.d.ts.map +1 -0
- package/dist/hooks/useProjectDashboard.js +224 -0
- package/dist/hooks/useProjectDashboard.js.map +1 -0
- package/dist/hooks/useProjectSelector.d.ts +17 -0
- package/dist/hooks/useProjectSelector.d.ts.map +1 -0
- package/dist/hooks/useProjectSelector.js +43 -0
- package/dist/hooks/useProjectSelector.js.map +1 -0
- package/dist/hooks/useProjectSelector.test.d.ts +7 -0
- package/dist/hooks/useProjectSelector.test.d.ts.map +1 -0
- package/dist/hooks/useProjectSelector.test.js +117 -0
- package/dist/hooks/useProjectSelector.test.js.map +1 -0
- package/dist/hooks/useSaveFlow.d.ts +30 -0
- package/dist/hooks/useSaveFlow.d.ts.map +1 -0
- package/dist/hooks/useSaveFlow.js +195 -0
- package/dist/hooks/useSaveFlow.js.map +1 -0
- package/dist/hooks/useSessions.test.d.ts +7 -0
- package/dist/hooks/useSessions.test.d.ts.map +1 -0
- package/dist/hooks/useSessions.test.js +153 -0
- package/dist/hooks/useSessions.test.js.map +1 -0
- package/dist/hooks/useSessionsExperiment.d.ts +97 -0
- package/dist/hooks/useSessionsExperiment.d.ts.map +1 -0
- package/dist/hooks/useSessionsExperiment.js +321 -0
- package/dist/hooks/useSessionsExperiment.js.map +1 -0
- package/dist/hooks/useSettings.d.ts +47 -0
- package/dist/hooks/useSettings.d.ts.map +1 -0
- package/dist/hooks/useSettings.js +245 -0
- package/dist/hooks/useSettings.js.map +1 -0
- package/dist/hooks/useSetupWizard.d.ts +35 -0
- package/dist/hooks/useSetupWizard.d.ts.map +1 -0
- package/dist/hooks/useSetupWizard.js +438 -0
- package/dist/hooks/useSetupWizard.js.map +1 -0
- package/dist/hooks/useUsageLimits.d.ts +24 -0
- package/dist/hooks/useUsageLimits.d.ts.map +1 -0
- package/dist/hooks/useUsageLimits.js +58 -0
- package/dist/hooks/useUsageLimits.js.map +1 -0
- package/dist/hooks/useUsageLimits.test.d.ts +7 -0
- package/dist/hooks/useUsageLimits.test.d.ts.map +1 -0
- package/dist/hooks/useUsageLimits.test.js +136 -0
- package/dist/hooks/useUsageLimits.test.js.map +1 -0
- package/dist/hooks/useWorktrees.d.ts +28 -0
- package/dist/hooks/useWorktrees.d.ts.map +1 -0
- package/dist/hooks/useWorktrees.js +57 -0
- package/dist/hooks/useWorktrees.js.map +1 -0
- package/dist/hooks/useWorktrees.test.d.ts +7 -0
- package/dist/hooks/useWorktrees.test.d.ts.map +1 -0
- package/dist/hooks/useWorktrees.test.js +119 -0
- package/dist/hooks/useWorktrees.test.js.map +1 -0
- package/dist/templates/compact-prompt.d.ts +16 -0
- package/dist/templates/compact-prompt.d.ts.map +1 -0
- package/dist/templates/compact-prompt.js +47 -0
- package/dist/templates/compact-prompt.js.map +1 -0
- package/dist/templates/context-skill.d.ts +8 -0
- package/dist/templates/context-skill.d.ts.map +1 -0
- package/dist/templates/context-skill.js +42 -0
- package/dist/templates/context-skill.js.map +1 -0
- package/dist/types.d.ts +127 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/activity.d.ts +17 -0
- package/dist/utils/activity.d.ts.map +1 -0
- package/dist/utils/activity.js +78 -0
- package/dist/utils/activity.js.map +1 -0
- package/dist/utils/bottom-controls.d.ts +24 -0
- package/dist/utils/bottom-controls.d.ts.map +1 -0
- package/dist/utils/bottom-controls.js +29 -0
- package/dist/utils/bottom-controls.js.map +1 -0
- package/dist/utils/clipboard.d.ts +2 -0
- package/dist/utils/clipboard.d.ts.map +1 -0
- package/dist/utils/clipboard.js +20 -0
- package/dist/utils/clipboard.js.map +1 -0
- package/dist/utils/constants.d.ts +6 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +6 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/format.d.ts +17 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +31 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/session-mode.d.ts +14 -0
- package/dist/utils/session-mode.d.ts.map +1 -0
- package/dist/utils/session-mode.js +29 -0
- package/dist/utils/session-mode.js.map +1 -0
- package/dist/utils/session-mode.test.d.ts +7 -0
- package/dist/utils/session-mode.test.d.ts.map +1 -0
- package/dist/utils/session-mode.test.js +102 -0
- package/dist/utils/session-mode.test.js.map +1 -0
- package/dist/utils/sessions-items-builder.d.ts +29 -0
- package/dist/utils/sessions-items-builder.d.ts.map +1 -0
- package/dist/utils/sessions-items-builder.js +394 -0
- package/dist/utils/sessions-items-builder.js.map +1 -0
- package/dist/utils/settings.d.ts +100 -0
- package/dist/utils/settings.d.ts.map +1 -0
- package/dist/utils/settings.js +206 -0
- package/dist/utils/settings.js.map +1 -0
- package/dist/websocket-client.d.ts +70 -0
- package/dist/websocket-client.d.ts.map +1 -0
- package/dist/websocket-client.js +188 -0
- package/dist/websocket-client.js.map +1 -0
- package/hooks/adapters/__init__.py +17 -0
- package/hooks/adapters/__pycache__/__init__.cpython-311.pyc +0 -0
- package/hooks/adapters/__pycache__/__init__.cpython-313.pyc +0 -0
- package/hooks/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
- package/hooks/adapters/__pycache__/base.cpython-311.pyc +0 -0
- package/hooks/adapters/__pycache__/base.cpython-313.pyc +0 -0
- package/hooks/adapters/__pycache__/base.cpython-314.pyc +0 -0
- package/hooks/adapters/__pycache__/calibration.cpython-311.pyc +0 -0
- package/hooks/adapters/__pycache__/calibration.cpython-313.pyc +0 -0
- package/hooks/adapters/__pycache__/calibration.cpython-314.pyc +0 -0
- package/hooks/adapters/__pycache__/claude_code.cpython-311.pyc +0 -0
- package/hooks/adapters/__pycache__/claude_code.cpython-313.pyc +0 -0
- package/hooks/adapters/__pycache__/claude_code.cpython-314.pyc +0 -0
- package/hooks/adapters/__pycache__/cursor.cpython-311.pyc +0 -0
- package/hooks/adapters/__pycache__/cursor.cpython-313.pyc +0 -0
- package/hooks/adapters/__pycache__/cursor.cpython-314.pyc +0 -0
- package/hooks/adapters/__pycache__/skills.cpython-311.pyc +0 -0
- package/hooks/adapters/__pycache__/skills.cpython-313.pyc +0 -0
- package/hooks/adapters/__pycache__/skills.cpython-314.pyc +0 -0
- package/hooks/adapters/__pycache__/test_adapters.cpython-311-pytest-9.0.2.pyc +0 -0
- package/hooks/adapters/__pycache__/test_adapters.cpython-313.pyc +0 -0
- package/hooks/adapters/__pycache__/test_calibration.cpython-311-pytest-9.0.2.pyc +0 -0
- package/hooks/adapters/__pycache__/test_skills.cpython-311-pytest-9.0.2.pyc +0 -0
- package/hooks/adapters/__pycache__/test_tokenizer.cpython-311-pytest-9.0.2.pyc +0 -0
- package/hooks/adapters/__pycache__/tokenizer.cpython-311.pyc +0 -0
- package/hooks/adapters/__pycache__/tokenizer.cpython-313.pyc +0 -0
- package/hooks/adapters/__pycache__/tokenizer.cpython-314.pyc +0 -0
- package/hooks/adapters/base.py +459 -0
- package/hooks/adapters/calibration.py +236 -0
- package/hooks/adapters/claude_code.py +316 -0
- package/hooks/adapters/context_parser.py +288 -0
- package/hooks/adapters/cursor.py +258 -0
- package/hooks/adapters/skills.py +160 -0
- package/hooks/adapters/template.py +281 -0
- package/hooks/adapters/test_adapters.py +516 -0
- package/hooks/adapters/test_calibration.py +235 -0
- package/hooks/adapters/test_skills.py +162 -0
- package/hooks/adapters/test_tokenizer.py +158 -0
- package/hooks/adapters/tokenizer.py +192 -0
- package/hooks/claude-code/pre-tool-use.py +36 -0
- package/hooks/claude-code/register-session.py +35 -0
- package/hooks/claude-code/report-activity.py +32 -0
- package/hooks/claude-code/session-idle.py +32 -0
- package/hooks/claude-code/statusline.sh +91 -0
- package/hooks/claude-code/unregister-session.py +32 -0
- package/hooks/cursor/README.md +61 -0
- package/hooks/cursor/after-agent-response.py +97 -0
- package/hooks/cursor/hooks.json.template +30 -0
- package/hooks/cursor/post-tool-use.py +32 -0
- package/hooks/cursor/pre-compact.py +61 -0
- package/hooks/cursor/session-end.py +32 -0
- package/hooks/cursor/session-start.py +66 -0
- package/hooks/git-detect.sh +47 -0
- package/hooks/install.py +441 -0
- package/hooks/jacques-register-session.py +286 -0
- package/hooks/jacques-report-activity.py +145 -0
- package/hooks/jacques-session-idle.py +57 -0
- package/hooks/jacques-unregister-session.py +57 -0
- package/hooks/requirements.txt +9 -0
- package/hooks/statusline.py +457 -0
- package/hooks/statusline.sh +212 -0
- package/package.json +63 -0
- package/skills/jacques-continue/SKILL.md +87 -0
- package/skills/jacques-handoff/SKILL.md +147 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
base.py - Base Adapter for Jacques Source Integration
|
|
4
|
+
|
|
5
|
+
Provides shared functionality for all source adapters:
|
|
6
|
+
- Unix socket communication with Jacques server
|
|
7
|
+
- JSON input parsing with error handling
|
|
8
|
+
- Common payload building
|
|
9
|
+
- Session title extraction from transcripts
|
|
10
|
+
- Fallback file writing when server unavailable
|
|
11
|
+
|
|
12
|
+
All source-specific adapters (ClaudeCodeAdapter, CursorAdapter, etc.)
|
|
13
|
+
should extend this base class.
|
|
14
|
+
"""
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
import os
|
|
18
|
+
import socket
|
|
19
|
+
import time
|
|
20
|
+
import platform
|
|
21
|
+
import tempfile
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Optional, Any
|
|
24
|
+
from abc import ABC, abstractmethod
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BaseAdapter(ABC):
|
|
28
|
+
"""
|
|
29
|
+
Abstract base class for Jacques source adapters.
|
|
30
|
+
|
|
31
|
+
Provides common functionality for:
|
|
32
|
+
- Sending events to the Jacques server via Unix socket
|
|
33
|
+
- Parsing JSON input from stdin
|
|
34
|
+
- Building normalized event payloads
|
|
35
|
+
- Extracting session titles from transcripts
|
|
36
|
+
|
|
37
|
+
Subclasses must implement:
|
|
38
|
+
- source: str property identifying the source (e.g., 'claude_code', 'cursor')
|
|
39
|
+
- get_session_id(input_data): Extract session ID from tool-specific input
|
|
40
|
+
- get_project_path(input_data): Extract project path from tool-specific input
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
DEFAULT_SOCKET_PATH = (
|
|
44
|
+
r'\\.\pipe\jacques' if platform.system() == 'Windows'
|
|
45
|
+
else os.environ.get('JACQUES_SOCKET_PATH', '/tmp/jacques.sock')
|
|
46
|
+
)
|
|
47
|
+
DEFAULT_TIMEOUT = 1.0
|
|
48
|
+
FALLBACK_PATH = Path.home() / '.jacques' / 'pending-events.jsonl'
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def source(self) -> str:
|
|
53
|
+
"""Unique identifier for this source (e.g., 'claude_code', 'cursor')."""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def get_session_id(self, input_data: dict) -> Optional[str]:
|
|
58
|
+
"""Extract session ID from tool-specific input format."""
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def get_project_path(self, input_data: dict) -> Optional[str]:
|
|
63
|
+
"""Extract project path from tool-specific input format."""
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
# =========================================================================
|
|
67
|
+
# Input Parsing
|
|
68
|
+
# =========================================================================
|
|
69
|
+
|
|
70
|
+
def parse_input(self) -> Optional[dict]:
|
|
71
|
+
"""
|
|
72
|
+
Parse JSON input from stdin with error handling.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Parsed dict if successful, None if parsing fails.
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
return json.load(sys.stdin)
|
|
79
|
+
except json.JSONDecodeError as e:
|
|
80
|
+
self._log_error(f"Invalid JSON input: {e}")
|
|
81
|
+
return None
|
|
82
|
+
except Exception as e:
|
|
83
|
+
self._log_error(f"Error reading input: {e}")
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
def validate_session_id(self, input_data: dict) -> Optional[str]:
|
|
87
|
+
"""
|
|
88
|
+
Extract and validate session ID from input data.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Session ID string if valid, None otherwise.
|
|
92
|
+
"""
|
|
93
|
+
session_id = self.get_session_id(input_data)
|
|
94
|
+
if not session_id:
|
|
95
|
+
self._log_error("No session_id in input")
|
|
96
|
+
return None
|
|
97
|
+
return session_id
|
|
98
|
+
|
|
99
|
+
# =========================================================================
|
|
100
|
+
# Server Communication
|
|
101
|
+
# =========================================================================
|
|
102
|
+
|
|
103
|
+
def send_to_server(
|
|
104
|
+
self,
|
|
105
|
+
payload: dict,
|
|
106
|
+
socket_path: str = None,
|
|
107
|
+
timeout: float = None
|
|
108
|
+
) -> bool:
|
|
109
|
+
"""
|
|
110
|
+
Send payload to Jacques server via IPC.
|
|
111
|
+
|
|
112
|
+
Uses Unix socket on macOS/Linux, Named Pipe on Windows.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
payload: Dict to send as JSON
|
|
116
|
+
socket_path: Path to IPC socket (default: platform-specific)
|
|
117
|
+
timeout: Socket timeout in seconds (default: 1.0)
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
True if sent successfully, False otherwise.
|
|
121
|
+
"""
|
|
122
|
+
socket_path = socket_path or self.DEFAULT_SOCKET_PATH
|
|
123
|
+
timeout = timeout or self.DEFAULT_TIMEOUT
|
|
124
|
+
data = json.dumps(payload).encode() + b'\n'
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
if platform.system() == 'Windows':
|
|
128
|
+
# Windows: write directly to Named Pipe as a file
|
|
129
|
+
with open(socket_path, 'wb') as pipe:
|
|
130
|
+
pipe.write(data)
|
|
131
|
+
else:
|
|
132
|
+
# Unix: use AF_UNIX socket
|
|
133
|
+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
134
|
+
sock.settimeout(timeout)
|
|
135
|
+
sock.connect(socket_path)
|
|
136
|
+
sock.sendall(data)
|
|
137
|
+
sock.close()
|
|
138
|
+
return True
|
|
139
|
+
except Exception as e:
|
|
140
|
+
self._log_error(f"Failed to send to server: {e}")
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
def write_fallback(self, payload: dict) -> bool:
|
|
144
|
+
"""
|
|
145
|
+
Write payload to fallback file when server is unavailable.
|
|
146
|
+
|
|
147
|
+
The fallback file stores events as newline-delimited JSON (JSONL).
|
|
148
|
+
These can be replayed when the server comes back online.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
payload: Dict to write as JSON
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
True if written successfully, False otherwise.
|
|
155
|
+
"""
|
|
156
|
+
try:
|
|
157
|
+
self.FALLBACK_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
158
|
+
with open(self.FALLBACK_PATH, 'a') as f:
|
|
159
|
+
f.write(json.dumps(payload) + '\n')
|
|
160
|
+
return True
|
|
161
|
+
except Exception as e:
|
|
162
|
+
self._log_error(f"Failed to write fallback: {e}")
|
|
163
|
+
return False
|
|
164
|
+
|
|
165
|
+
def send_event(self, payload: dict, use_fallback: bool = True) -> bool:
|
|
166
|
+
"""
|
|
167
|
+
Send event to server with optional fallback.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
payload: Event payload to send
|
|
171
|
+
use_fallback: If True, write to fallback file on failure
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
True if sent or written to fallback successfully.
|
|
175
|
+
"""
|
|
176
|
+
if self.send_to_server(payload):
|
|
177
|
+
return True
|
|
178
|
+
if use_fallback:
|
|
179
|
+
return self.write_fallback(payload)
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
# =========================================================================
|
|
183
|
+
# Payload Building
|
|
184
|
+
# =========================================================================
|
|
185
|
+
|
|
186
|
+
def build_base_payload(
|
|
187
|
+
self,
|
|
188
|
+
event: str,
|
|
189
|
+
session_id: str,
|
|
190
|
+
**extra_fields
|
|
191
|
+
) -> dict:
|
|
192
|
+
"""
|
|
193
|
+
Build base payload structure for any event type.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
event: Event type (session_start, activity, idle, session_end, context_update)
|
|
197
|
+
session_id: Session identifier
|
|
198
|
+
**extra_fields: Additional fields to include
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Dict with base event structure plus extra fields.
|
|
202
|
+
"""
|
|
203
|
+
payload = {
|
|
204
|
+
"event": event,
|
|
205
|
+
"timestamp": int(time.time() * 1000), # Convert to milliseconds
|
|
206
|
+
"session_id": session_id,
|
|
207
|
+
"source": self.source,
|
|
208
|
+
}
|
|
209
|
+
payload.update(extra_fields)
|
|
210
|
+
return payload
|
|
211
|
+
|
|
212
|
+
# =========================================================================
|
|
213
|
+
# Project Info Extraction
|
|
214
|
+
# =========================================================================
|
|
215
|
+
|
|
216
|
+
def extract_project_info(self, input_data: dict) -> dict:
|
|
217
|
+
"""
|
|
218
|
+
Extract project name and path from input data.
|
|
219
|
+
|
|
220
|
+
Returns dict with:
|
|
221
|
+
- project: Project name (basename of path)
|
|
222
|
+
- project_path: Full path to project
|
|
223
|
+
- cwd: Current working directory
|
|
224
|
+
"""
|
|
225
|
+
project_path = self.get_project_path(input_data) or ''
|
|
226
|
+
cwd = input_data.get('cwd', os.getcwd())
|
|
227
|
+
|
|
228
|
+
if project_path:
|
|
229
|
+
project_name = os.path.basename(project_path)
|
|
230
|
+
elif cwd:
|
|
231
|
+
project_name = os.path.basename(cwd)
|
|
232
|
+
else:
|
|
233
|
+
project_name = 'Unknown'
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
'project': project_name,
|
|
237
|
+
'project_path': project_path,
|
|
238
|
+
'cwd': cwd,
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# =========================================================================
|
|
242
|
+
# Session Title Extraction
|
|
243
|
+
# =========================================================================
|
|
244
|
+
|
|
245
|
+
def extract_session_title(self, transcript_path: Optional[str]) -> Optional[str]:
|
|
246
|
+
"""
|
|
247
|
+
Extract session title from Claude Code transcript file.
|
|
248
|
+
|
|
249
|
+
Checks for (in priority order):
|
|
250
|
+
1. Explicit 'title' field
|
|
251
|
+
2. 'summary' type entries
|
|
252
|
+
3. First user message (truncated to 80 chars)
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
transcript_path: Path to the transcript JSONL file
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Extracted title string, or None if not found.
|
|
259
|
+
"""
|
|
260
|
+
if not transcript_path:
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
path = Path(transcript_path)
|
|
264
|
+
if not path.exists():
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
title = None
|
|
268
|
+
first_user_message = None
|
|
269
|
+
summary_text = None
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
with open(path, 'r') as f:
|
|
273
|
+
lines = f.readlines()
|
|
274
|
+
|
|
275
|
+
# Check recent lines first for updated title/summary
|
|
276
|
+
recent_lines = lines[-100:] if len(lines) > 100 else lines
|
|
277
|
+
|
|
278
|
+
for line in recent_lines:
|
|
279
|
+
try:
|
|
280
|
+
entry = json.loads(line.strip())
|
|
281
|
+
|
|
282
|
+
# Check for explicit title
|
|
283
|
+
if 'title' in entry:
|
|
284
|
+
title = entry['title']
|
|
285
|
+
|
|
286
|
+
# Check for summary type
|
|
287
|
+
if entry.get('type') == 'summary':
|
|
288
|
+
summary_content = entry.get('summary', '')
|
|
289
|
+
if summary_content:
|
|
290
|
+
summary_text = summary_content.split('.')[0][:200]
|
|
291
|
+
|
|
292
|
+
except json.JSONDecodeError:
|
|
293
|
+
continue
|
|
294
|
+
|
|
295
|
+
# Check first user message if no title found
|
|
296
|
+
if not title and not summary_text:
|
|
297
|
+
for line in lines[:20]:
|
|
298
|
+
try:
|
|
299
|
+
entry = json.loads(line.strip())
|
|
300
|
+
if entry.get('type') == 'human':
|
|
301
|
+
msg = entry.get('message', {})
|
|
302
|
+
content = msg.get('content', '') if isinstance(msg, dict) else ''
|
|
303
|
+
if isinstance(content, str) and content:
|
|
304
|
+
first_user_message = content.strip()[:200]
|
|
305
|
+
if len(content) > 200:
|
|
306
|
+
first_user_message += '...'
|
|
307
|
+
break
|
|
308
|
+
except:
|
|
309
|
+
continue
|
|
310
|
+
|
|
311
|
+
except Exception as e:
|
|
312
|
+
self._log_error(f"Error reading transcript: {e}")
|
|
313
|
+
|
|
314
|
+
return title or summary_text or first_user_message
|
|
315
|
+
|
|
316
|
+
def generate_fallback_title(self, project_name: str) -> str:
|
|
317
|
+
"""Generate a fallback title when transcript is empty/unavailable."""
|
|
318
|
+
return f"Session in {project_name}"
|
|
319
|
+
|
|
320
|
+
# =========================================================================
|
|
321
|
+
# Terminal Identity
|
|
322
|
+
# =========================================================================
|
|
323
|
+
|
|
324
|
+
def get_terminal_identity(self) -> dict:
|
|
325
|
+
"""
|
|
326
|
+
Get terminal-specific identifiers from environment and system.
|
|
327
|
+
|
|
328
|
+
Returns dict with terminal identification info for session tracking.
|
|
329
|
+
Cross-platform: TTY detection skipped on Windows (no TTY concept).
|
|
330
|
+
"""
|
|
331
|
+
tty = None
|
|
332
|
+
|
|
333
|
+
# Try to get TTY (Unix only — Windows has no TTY concept)
|
|
334
|
+
if platform.system() != 'Windows':
|
|
335
|
+
try:
|
|
336
|
+
if sys.stdin.isatty():
|
|
337
|
+
tty = os.ttyname(sys.stdin.fileno())
|
|
338
|
+
except:
|
|
339
|
+
pass
|
|
340
|
+
|
|
341
|
+
if not tty:
|
|
342
|
+
try:
|
|
343
|
+
result = os.popen("tty 2>/dev/null").read().strip()
|
|
344
|
+
if result and result != "not a tty":
|
|
345
|
+
tty = result
|
|
346
|
+
except:
|
|
347
|
+
pass
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
"tty": tty,
|
|
351
|
+
"terminal_pid": os.getppid(),
|
|
352
|
+
"term_program": os.environ.get("TERM_PROGRAM"),
|
|
353
|
+
"iterm_session_id": os.environ.get("ITERM_SESSION_ID"),
|
|
354
|
+
"term_session_id": os.environ.get("TERM_SESSION_ID"),
|
|
355
|
+
"kitty_window_id": os.environ.get("KITTY_WINDOW_ID"),
|
|
356
|
+
"wezterm_pane": os.environ.get("WEZTERM_PANE"),
|
|
357
|
+
"wt_session": os.environ.get("WT_SESSION"),
|
|
358
|
+
"vscode_injection": os.environ.get("VSCODE_INJECTION"),
|
|
359
|
+
"windowid": os.environ.get("WINDOWID"),
|
|
360
|
+
"term": os.environ.get("TERM"),
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
def build_terminal_key(self, terminal: dict) -> str:
|
|
364
|
+
"""
|
|
365
|
+
Build a unique key for this terminal instance.
|
|
366
|
+
|
|
367
|
+
Priority: iTerm > Kitty > WezTerm > Windows Terminal > TTY > PID
|
|
368
|
+
"""
|
|
369
|
+
if terminal.get("iterm_session_id"):
|
|
370
|
+
return f"ITERM:{terminal['iterm_session_id']}"
|
|
371
|
+
if terminal.get("kitty_window_id"):
|
|
372
|
+
return f"KITTY:{terminal['kitty_window_id']}"
|
|
373
|
+
if terminal.get("wezterm_pane"):
|
|
374
|
+
return f"WEZTERM:{terminal['wezterm_pane']}"
|
|
375
|
+
if terminal.get("wt_session"):
|
|
376
|
+
return f"WT:{terminal['wt_session']}"
|
|
377
|
+
if terminal.get("tty"):
|
|
378
|
+
return f"TTY:{terminal['tty']}"
|
|
379
|
+
if terminal.get("terminal_pid"):
|
|
380
|
+
return f"PID:{terminal['terminal_pid']}"
|
|
381
|
+
return f"UNKNOWN:{time.time()}"
|
|
382
|
+
|
|
383
|
+
# =========================================================================
|
|
384
|
+
# Git Detection
|
|
385
|
+
# =========================================================================
|
|
386
|
+
|
|
387
|
+
def detect_git_info(self, project_path: str) -> dict:
|
|
388
|
+
"""
|
|
389
|
+
Detect git branch, worktree, and repo root from project directory.
|
|
390
|
+
|
|
391
|
+
Uses hooks/git-detect.sh as single source of truth.
|
|
392
|
+
Falls back to inline detection if script not found.
|
|
393
|
+
|
|
394
|
+
Returns dict with:
|
|
395
|
+
- git_branch: Current branch name (empty string if not a git repo)
|
|
396
|
+
- git_worktree: Worktree name if in a worktree (empty string otherwise)
|
|
397
|
+
- git_repo_root: Main worktree root path (empty string if not a git repo)
|
|
398
|
+
"""
|
|
399
|
+
result = {'git_branch': '', 'git_worktree': '', 'git_repo_root': ''}
|
|
400
|
+
if not project_path or not os.path.isdir(project_path):
|
|
401
|
+
return result
|
|
402
|
+
try:
|
|
403
|
+
import subprocess
|
|
404
|
+
# Try git-detect.sh first (single source of truth)
|
|
405
|
+
script_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
406
|
+
script_path = os.path.join(script_dir, 'git-detect.sh')
|
|
407
|
+
if os.path.isfile(script_path) and os.access(script_path, os.X_OK):
|
|
408
|
+
proc = subprocess.run(
|
|
409
|
+
[script_path, project_path],
|
|
410
|
+
capture_output=True, text=True, timeout=5
|
|
411
|
+
)
|
|
412
|
+
if proc.returncode == 0:
|
|
413
|
+
lines = proc.stdout.split('\n')
|
|
414
|
+
result['git_branch'] = lines[0] if len(lines) > 0 else ''
|
|
415
|
+
result['git_worktree'] = lines[1] if len(lines) > 1 else ''
|
|
416
|
+
result['git_repo_root'] = lines[2] if len(lines) > 2 else ''
|
|
417
|
+
return result
|
|
418
|
+
|
|
419
|
+
# Fallback: inline detection (same algorithm as git-detect.sh)
|
|
420
|
+
proc = subprocess.run(
|
|
421
|
+
['git', '-C', project_path, 'rev-parse', '--abbrev-ref', 'HEAD', '--git-common-dir'],
|
|
422
|
+
capture_output=True, text=True, timeout=5
|
|
423
|
+
)
|
|
424
|
+
if proc.returncode == 0:
|
|
425
|
+
lines = proc.stdout.strip().split('\n')
|
|
426
|
+
if len(lines) >= 2:
|
|
427
|
+
result['git_branch'] = lines[0]
|
|
428
|
+
common = lines[1]
|
|
429
|
+
if common == '.git':
|
|
430
|
+
result['git_repo_root'] = os.path.realpath(project_path)
|
|
431
|
+
elif common:
|
|
432
|
+
result['git_repo_root'] = os.path.dirname(common)
|
|
433
|
+
result['git_worktree'] = os.path.basename(project_path)
|
|
434
|
+
except Exception:
|
|
435
|
+
pass
|
|
436
|
+
return result
|
|
437
|
+
|
|
438
|
+
# =========================================================================
|
|
439
|
+
# Debug Logging
|
|
440
|
+
# =========================================================================
|
|
441
|
+
|
|
442
|
+
def log_debug(self, input_data: dict, hook_name: str) -> None:
|
|
443
|
+
"""
|
|
444
|
+
Log input data for debugging purposes.
|
|
445
|
+
|
|
446
|
+
Writes to {tempdir}/jacques-hook-debug.log
|
|
447
|
+
"""
|
|
448
|
+
try:
|
|
449
|
+
log_path = os.path.join(tempfile.gettempdir(), 'jacques-hook-debug.log')
|
|
450
|
+
with open(log_path, 'a') as f:
|
|
451
|
+
f.write(f"\n=== {hook_name} [{self.source}] {time.strftime('%Y-%m-%d %H:%M:%S')} ===\n")
|
|
452
|
+
f.write(json.dumps(input_data, indent=2))
|
|
453
|
+
f.write("\n")
|
|
454
|
+
except:
|
|
455
|
+
pass
|
|
456
|
+
|
|
457
|
+
def _log_error(self, message: str) -> None:
|
|
458
|
+
"""Log error message to stderr."""
|
|
459
|
+
print(f"[jacques:{self.source}] {message}", file=sys.stderr)
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
calibration.py - Token Estimation Calibration for Jacques
|
|
4
|
+
|
|
5
|
+
Stores and retrieves calibration factors to improve token estimation accuracy.
|
|
6
|
+
When preCompact provides actual token counts, we calculate a correction factor
|
|
7
|
+
that can be applied to future estimates for that session.
|
|
8
|
+
|
|
9
|
+
Calibration data is stored in ~/.jacques/calibration.json
|
|
10
|
+
|
|
11
|
+
Example calibration flow:
|
|
12
|
+
1. afterAgentResponse estimates 50,000 tokens
|
|
13
|
+
2. User runs /summarize, preCompact reports actual 55,000 tokens
|
|
14
|
+
3. Correction factor = 55000 / 50000 = 1.1
|
|
15
|
+
4. Future estimates are multiplied by 1.1 for better accuracy
|
|
16
|
+
"""
|
|
17
|
+
import json
|
|
18
|
+
import sys
|
|
19
|
+
import time
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
# Calibration data file path
|
|
24
|
+
CALIBRATION_PATH = Path.home() / '.jacques' / 'calibration.json'
|
|
25
|
+
|
|
26
|
+
# In-memory cache for current session
|
|
27
|
+
_calibration_cache: dict = None
|
|
28
|
+
_cache_loaded = False
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _load_calibration() -> dict:
|
|
32
|
+
"""Load calibration data from file."""
|
|
33
|
+
global _calibration_cache, _cache_loaded
|
|
34
|
+
|
|
35
|
+
if _cache_loaded and _calibration_cache is not None:
|
|
36
|
+
return _calibration_cache
|
|
37
|
+
|
|
38
|
+
_calibration_cache = {
|
|
39
|
+
"sessions": {}, # session_id -> {factor, last_estimate, last_actual, updated_at}
|
|
40
|
+
"global_factor": 1.0, # Weighted average across all sessions
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
if CALIBRATION_PATH.exists():
|
|
45
|
+
with open(CALIBRATION_PATH, 'r') as f:
|
|
46
|
+
data = json.load(f)
|
|
47
|
+
_calibration_cache.update(data)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
print(f"[jacques:calibration] Error loading calibration: {e}", file=sys.stderr)
|
|
50
|
+
|
|
51
|
+
_cache_loaded = True
|
|
52
|
+
return _calibration_cache
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _save_calibration() -> bool:
|
|
56
|
+
"""Save calibration data to file."""
|
|
57
|
+
try:
|
|
58
|
+
CALIBRATION_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
59
|
+
with open(CALIBRATION_PATH, 'w') as f:
|
|
60
|
+
json.dump(_calibration_cache, f, indent=2)
|
|
61
|
+
return True
|
|
62
|
+
except Exception as e:
|
|
63
|
+
print(f"[jacques:calibration] Error saving calibration: {e}", file=sys.stderr)
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_factor(session_id: str) -> float:
|
|
68
|
+
"""
|
|
69
|
+
Get correction factor for a session.
|
|
70
|
+
|
|
71
|
+
Only returns session-specific factors, NOT global factors.
|
|
72
|
+
Global factors from old sessions with different estimation logic
|
|
73
|
+
can cause wildly inaccurate results.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
session_id: Session identifier
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Correction factor (default 1.0 if no calibration for this session).
|
|
80
|
+
"""
|
|
81
|
+
data = _load_calibration()
|
|
82
|
+
|
|
83
|
+
# Only use session-specific factor
|
|
84
|
+
# Don't use global_factor - it may be from old sessions with wrong estimates
|
|
85
|
+
session_data = data.get("sessions", {}).get(session_id)
|
|
86
|
+
if session_data and "factor" in session_data:
|
|
87
|
+
return session_data["factor"]
|
|
88
|
+
|
|
89
|
+
# Default to 1.0 for new sessions - no adjustment until calibrated
|
|
90
|
+
return 1.0
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def set_factor(session_id: str, factor: float) -> None:
|
|
94
|
+
"""
|
|
95
|
+
Set correction factor for a session.
|
|
96
|
+
|
|
97
|
+
Also updates the global factor as a weighted average.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
session_id: Session identifier
|
|
101
|
+
factor: Correction factor (actual / estimated)
|
|
102
|
+
"""
|
|
103
|
+
data = _load_calibration()
|
|
104
|
+
|
|
105
|
+
if "sessions" not in data:
|
|
106
|
+
data["sessions"] = {}
|
|
107
|
+
|
|
108
|
+
# Clamp factor to reasonable range (0.5 to 2.0)
|
|
109
|
+
factor = max(0.5, min(2.0, factor))
|
|
110
|
+
|
|
111
|
+
data["sessions"][session_id] = {
|
|
112
|
+
"factor": factor,
|
|
113
|
+
"updated_at": time.time(),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# Update global factor as average of recent sessions
|
|
117
|
+
recent_factors = [
|
|
118
|
+
s.get("factor", 1.0)
|
|
119
|
+
for s in data["sessions"].values()
|
|
120
|
+
if s.get("updated_at", 0) > time.time() - 86400 # Last 24 hours
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
if recent_factors:
|
|
124
|
+
data["global_factor"] = sum(recent_factors) / len(recent_factors)
|
|
125
|
+
|
|
126
|
+
_save_calibration()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_last_estimate(session_id: str) -> Optional[int]:
|
|
130
|
+
"""
|
|
131
|
+
Get the last token estimate for a session.
|
|
132
|
+
|
|
133
|
+
Used when preCompact fires to calculate correction factor.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
session_id: Session identifier
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Last estimated token count, or None if not available.
|
|
140
|
+
"""
|
|
141
|
+
data = _load_calibration()
|
|
142
|
+
session_data = data.get("sessions", {}).get(session_id)
|
|
143
|
+
|
|
144
|
+
if session_data:
|
|
145
|
+
return session_data.get("last_estimate")
|
|
146
|
+
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def set_last_estimate(session_id: str, tokens: int) -> None:
|
|
151
|
+
"""
|
|
152
|
+
Store the latest token estimate for a session.
|
|
153
|
+
|
|
154
|
+
Called after each afterAgentResponse to track estimates.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
session_id: Session identifier
|
|
158
|
+
tokens: Estimated token count
|
|
159
|
+
"""
|
|
160
|
+
data = _load_calibration()
|
|
161
|
+
|
|
162
|
+
if "sessions" not in data:
|
|
163
|
+
data["sessions"] = {}
|
|
164
|
+
|
|
165
|
+
if session_id not in data["sessions"]:
|
|
166
|
+
data["sessions"][session_id] = {}
|
|
167
|
+
|
|
168
|
+
data["sessions"][session_id]["last_estimate"] = tokens
|
|
169
|
+
data["sessions"][session_id]["estimate_time"] = time.time()
|
|
170
|
+
|
|
171
|
+
_save_calibration()
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def calibrate_from_actual(session_id: str, actual_tokens: int) -> Optional[float]:
|
|
175
|
+
"""
|
|
176
|
+
Calculate and store correction factor from actual token count.
|
|
177
|
+
|
|
178
|
+
Called when preCompact provides actual metrics.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
session_id: Session identifier
|
|
182
|
+
actual_tokens: Actual token count from preCompact
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Calculated correction factor, or None if no estimate available.
|
|
186
|
+
"""
|
|
187
|
+
estimated = get_last_estimate(session_id)
|
|
188
|
+
|
|
189
|
+
if not estimated or estimated <= 0:
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
factor = actual_tokens / estimated
|
|
193
|
+
set_factor(session_id, factor)
|
|
194
|
+
|
|
195
|
+
# Store actual for reference
|
|
196
|
+
data = _load_calibration()
|
|
197
|
+
if session_id in data.get("sessions", {}):
|
|
198
|
+
data["sessions"][session_id]["last_actual"] = actual_tokens
|
|
199
|
+
data["sessions"][session_id]["calibrated_at"] = time.time()
|
|
200
|
+
_save_calibration()
|
|
201
|
+
|
|
202
|
+
return factor
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def clear_session(session_id: str) -> None:
|
|
206
|
+
"""
|
|
207
|
+
Clear calibration data for a session.
|
|
208
|
+
|
|
209
|
+
Called when session ends.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
session_id: Session identifier
|
|
213
|
+
"""
|
|
214
|
+
data = _load_calibration()
|
|
215
|
+
|
|
216
|
+
if session_id in data.get("sessions", {}):
|
|
217
|
+
del data["sessions"][session_id]
|
|
218
|
+
_save_calibration()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def get_calibration_stats() -> dict:
|
|
222
|
+
"""
|
|
223
|
+
Get calibration statistics for debugging.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Dict with session count, global factor, etc.
|
|
227
|
+
"""
|
|
228
|
+
data = _load_calibration()
|
|
229
|
+
|
|
230
|
+
sessions = data.get("sessions", {})
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
"session_count": len(sessions),
|
|
234
|
+
"global_factor": data.get("global_factor", 1.0),
|
|
235
|
+
"calibrated_sessions": sum(1 for s in sessions.values() if "factor" in s),
|
|
236
|
+
}
|