jott-cli 0.5.6__tar.gz → 0.7.0__tar.gz
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.
- {jott_cli-0.5.6/jott_cli.egg-info → jott_cli-0.7.0}/PKG-INFO +21 -20
- {jott_cli-0.5.6 → jott_cli-0.7.0}/README.md +20 -19
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/__init__.py +1 -1
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/_app_navigation_mixin.py +0 -36
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/_dispatch_mixin.py +122 -27
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/app.py +8 -12
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_ai_analysis_mixin.py +2 -2
- jott_cli-0.7.0/jot/commands/_claude_mixin.py +390 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_core_mixin.py +44 -4
- jott_cli-0.7.0/jot/commands/_github_mixin.py +296 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_metadata_mixin.py +7 -3
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_notes_mixin.py +18 -1
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/handler.py +4 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/_crud_mixin.py +40 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/constants.py +6 -0
- jott_cli-0.7.0/jot/integrations/github/issues.py +105 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/display_help.py +55 -56
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/display_projects.py +1 -1
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/display_tasks.py +18 -0
- jott_cli-0.7.0/jot/utils/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0/jott_cli.egg-info}/PKG-INFO +21 -20
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jott_cli.egg-info/SOURCES.txt +5 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/pyproject.toml +1 -1
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_command_handler.py +64 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_dispatch.py +2 -8
- jott_cli-0.7.0/tests/test_github.py +234 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/LICENSE +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/categories/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/categories/config.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/categories/manager.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/categories/templates.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/cli/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/cli/archive.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/cli/config.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/cli/views.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_ai_suggest_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_audio_timer_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_bulk_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_context_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_gcal_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_transfer_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/commands/_web_clipboard_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/_age_backlog_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/_compress_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/_delete_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/_export_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/_id_migration_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/_metadata_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/_navigation_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/_persistence_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/_subtask_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/archive_manager.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/id_manager.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/core/task_manager.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/integrations/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/integrations/gcal/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/integrations/gcal/account_manager.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/integrations/gcal/auth.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/integrations/gcal/events.py +0 -0
- {jott_cli-0.5.6/jot/integrations/keywords → jott_cli-0.7.0/jot/integrations/github}/__init__.py +0 -0
- {jott_cli-0.5.6/jot/projects → jott_cli-0.7.0/jot/integrations/keywords}/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/integrations/keywords/_config_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/integrations/keywords/_handlers_mixin.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/integrations/keywords/handler.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/mcp/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/mcp/handlers.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/mcp/schemas.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/mcp/server.py +0 -0
- {jott_cli-0.5.6/jot/utils → jott_cli-0.7.0/jot/projects}/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/projects/backup.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/projects/registry.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/__init__.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/display.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/display_archive.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/display_footer.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/formatting.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/input.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/picker.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/rendering.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/ui/styles.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/utils/date_utils.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/utils/text_utils.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jot/utils/validation.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jott_cli.egg-info/dependency_links.txt +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jott_cli.egg-info/entry_points.txt +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jott_cli.egg-info/requires.txt +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/jott_cli.egg-info/top_level.txt +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/setup.cfg +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/setup.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_edit_edge_cases.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_fuzzy_search.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_gcal_notes.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_highlight.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_input.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_jot.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_picker.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_styles.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_subtask_notes.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_terminal_wrap.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_today_filter.py +0 -0
- {jott_cli-0.5.6 → jott_cli-0.7.0}/tests/test_transfer_subtasks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jott-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Feature-rich interactive CLI task manager with AI integration, calendar sync, and keyword automation
|
|
5
5
|
Author-email: Scott Anderson <sonander@gmail.com>
|
|
6
6
|
Maintainer-email: Scott Anderson <sonander@gmail.com>
|
|
@@ -62,12 +62,12 @@ Dynamic: license-file
|
|
|
62
62
|
- **Dual-mode interface**: Quick-add mode ↔ Command mode (ESC to switch)
|
|
63
63
|
- **Cross-project task routing**: `jott myproject "task"` from anywhere
|
|
64
64
|
- **Keyword automation**: Tasks starting with `bullet:`, `gcal:`, `ai:`, `analyze:` trigger actions
|
|
65
|
-
- **AI integration**: Claude Code integration
|
|
65
|
+
- **AI integration**: Claude Code integration via the `analyze:` keyword for task analysis
|
|
66
66
|
- **Google Calendar sync**: Bidirectional sync (export tasks, import events)
|
|
67
67
|
- **Text-to-speech**: Read all tasks aloud with Shift+2
|
|
68
68
|
- **Fuzzy search**: Filter tasks quickly with `Ctrl+/`
|
|
69
69
|
- **Fuzzy project picker**: Interactive search when moving tasks between projects
|
|
70
|
-
- **
|
|
70
|
+
- **Leader chord system**: `.` prefix for 20+ shortcuts (e.g., `.m` move, `.n` notes)
|
|
71
71
|
- **Category system**: Up to 12 categories per project
|
|
72
72
|
- **Task metadata**: Priority, status, day-of-week assignment, notes
|
|
73
73
|
- **Backup system**: Organized monthly backups
|
|
@@ -121,34 +121,35 @@ Just type and press Enter:
|
|
|
121
121
|
### Keyboard Shortcuts
|
|
122
122
|
|
|
123
123
|
**Task Management:**
|
|
124
|
-
- `
|
|
125
|
-
- `
|
|
126
|
-
- `
|
|
127
|
-
- `
|
|
124
|
+
- `.n` - Edit task notes
|
|
125
|
+
- `Ctrl+D` - Delete current task
|
|
126
|
+
- `.a` - Toggle archived tasks
|
|
127
|
+
- `.f` - Toggle inline notes display
|
|
128
|
+
- `.u` - Toggle ALL CAPS
|
|
128
129
|
- `Shift+4` - AI-powered task suggestion
|
|
129
130
|
|
|
130
131
|
**Scheduling:**
|
|
131
|
-
- `
|
|
132
|
-
- `
|
|
133
|
-
- `
|
|
132
|
+
- `.w` - Assign day of week
|
|
133
|
+
- `.p` - Set priority
|
|
134
|
+
- `.x` - Set status
|
|
134
135
|
|
|
135
136
|
**AI Integration:**
|
|
136
|
-
- `
|
|
137
|
-
- `
|
|
138
|
-
- `Shift+J` - Mark as agent task
|
|
137
|
+
- `analyze:` keyword - Analyze task with Claude Code (plan mode)
|
|
138
|
+
- `.j` - Mark as agent task
|
|
139
139
|
|
|
140
140
|
**Integrations:**
|
|
141
141
|
- `Shift+2` - Read tasks aloud (TTS)
|
|
142
142
|
- `Shift+3` - Re-authenticate Google Calendar
|
|
143
|
-
-
|
|
144
|
-
- `
|
|
145
|
-
- `
|
|
143
|
+
- `.G` - Export to Google Calendar (single or bulk with time tags)
|
|
144
|
+
- `.i` - Import from Google Calendar
|
|
145
|
+
- `.b` - Start priority timer (12-minute focus session)
|
|
146
146
|
|
|
147
147
|
**Navigation & System:**
|
|
148
148
|
- `↑/↓` or `Ctrl+N/P` - Navigate tasks
|
|
149
149
|
- `Shift+↑/↓` - Reorder tasks
|
|
150
|
-
- `Ctrl
|
|
151
|
-
- `
|
|
150
|
+
- `Ctrl+F` - Fuzzy search mode
|
|
151
|
+
- `.z` - Switch project
|
|
152
|
+
- `.l` - Toggle all categories view
|
|
152
153
|
- `Tab` - Cycle through categories
|
|
153
154
|
- `Ctrl+R` - Register current project
|
|
154
155
|
- `Ctrl+U` - Open URLs from task text
|
|
@@ -170,7 +171,7 @@ Organize tasks with categories (up to 12 per project):
|
|
|
170
171
|
|
|
171
172
|
```bash
|
|
172
173
|
# Switch to category
|
|
173
|
-
|
|
174
|
+
Ctrl+C
|
|
174
175
|
|
|
175
176
|
# Create category-specific tasks
|
|
176
177
|
jott --category=bugs "Fix login redirect"
|
|
@@ -202,7 +203,7 @@ jott myproject "task text"
|
|
|
202
203
|
1. Enable Google Calendar API
|
|
203
204
|
2. Download credentials
|
|
204
205
|
3. Save as `~/.jot/gcal-accounts/default/credentials.json`
|
|
205
|
-
4. Use
|
|
206
|
+
4. Use `.G` to export tasks
|
|
206
207
|
|
|
207
208
|
**Re-authentication:**
|
|
208
209
|
- Press `Shift+3` to force re-authentication (if token expires or switching accounts)
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
- **Dual-mode interface**: Quick-add mode ↔ Command mode (ESC to switch)
|
|
13
13
|
- **Cross-project task routing**: `jott myproject "task"` from anywhere
|
|
14
14
|
- **Keyword automation**: Tasks starting with `bullet:`, `gcal:`, `ai:`, `analyze:` trigger actions
|
|
15
|
-
- **AI integration**: Claude Code integration
|
|
15
|
+
- **AI integration**: Claude Code integration via the `analyze:` keyword for task analysis
|
|
16
16
|
- **Google Calendar sync**: Bidirectional sync (export tasks, import events)
|
|
17
17
|
- **Text-to-speech**: Read all tasks aloud with Shift+2
|
|
18
18
|
- **Fuzzy search**: Filter tasks quickly with `Ctrl+/`
|
|
19
19
|
- **Fuzzy project picker**: Interactive search when moving tasks between projects
|
|
20
|
-
- **
|
|
20
|
+
- **Leader chord system**: `.` prefix for 20+ shortcuts (e.g., `.m` move, `.n` notes)
|
|
21
21
|
- **Category system**: Up to 12 categories per project
|
|
22
22
|
- **Task metadata**: Priority, status, day-of-week assignment, notes
|
|
23
23
|
- **Backup system**: Organized monthly backups
|
|
@@ -71,34 +71,35 @@ Just type and press Enter:
|
|
|
71
71
|
### Keyboard Shortcuts
|
|
72
72
|
|
|
73
73
|
**Task Management:**
|
|
74
|
-
- `
|
|
75
|
-
- `
|
|
76
|
-
- `
|
|
77
|
-
- `
|
|
74
|
+
- `.n` - Edit task notes
|
|
75
|
+
- `Ctrl+D` - Delete current task
|
|
76
|
+
- `.a` - Toggle archived tasks
|
|
77
|
+
- `.f` - Toggle inline notes display
|
|
78
|
+
- `.u` - Toggle ALL CAPS
|
|
78
79
|
- `Shift+4` - AI-powered task suggestion
|
|
79
80
|
|
|
80
81
|
**Scheduling:**
|
|
81
|
-
- `
|
|
82
|
-
- `
|
|
83
|
-
- `
|
|
82
|
+
- `.w` - Assign day of week
|
|
83
|
+
- `.p` - Set priority
|
|
84
|
+
- `.x` - Set status
|
|
84
85
|
|
|
85
86
|
**AI Integration:**
|
|
86
|
-
- `
|
|
87
|
-
- `
|
|
88
|
-
- `Shift+J` - Mark as agent task
|
|
87
|
+
- `analyze:` keyword - Analyze task with Claude Code (plan mode)
|
|
88
|
+
- `.j` - Mark as agent task
|
|
89
89
|
|
|
90
90
|
**Integrations:**
|
|
91
91
|
- `Shift+2` - Read tasks aloud (TTS)
|
|
92
92
|
- `Shift+3` - Re-authenticate Google Calendar
|
|
93
|
-
-
|
|
94
|
-
- `
|
|
95
|
-
- `
|
|
93
|
+
- `.G` - Export to Google Calendar (single or bulk with time tags)
|
|
94
|
+
- `.i` - Import from Google Calendar
|
|
95
|
+
- `.b` - Start priority timer (12-minute focus session)
|
|
96
96
|
|
|
97
97
|
**Navigation & System:**
|
|
98
98
|
- `↑/↓` or `Ctrl+N/P` - Navigate tasks
|
|
99
99
|
- `Shift+↑/↓` - Reorder tasks
|
|
100
|
-
- `Ctrl
|
|
101
|
-
- `
|
|
100
|
+
- `Ctrl+F` - Fuzzy search mode
|
|
101
|
+
- `.z` - Switch project
|
|
102
|
+
- `.l` - Toggle all categories view
|
|
102
103
|
- `Tab` - Cycle through categories
|
|
103
104
|
- `Ctrl+R` - Register current project
|
|
104
105
|
- `Ctrl+U` - Open URLs from task text
|
|
@@ -120,7 +121,7 @@ Organize tasks with categories (up to 12 per project):
|
|
|
120
121
|
|
|
121
122
|
```bash
|
|
122
123
|
# Switch to category
|
|
123
|
-
|
|
124
|
+
Ctrl+C
|
|
124
125
|
|
|
125
126
|
# Create category-specific tasks
|
|
126
127
|
jott --category=bugs "Fix login redirect"
|
|
@@ -152,7 +153,7 @@ jott myproject "task text"
|
|
|
152
153
|
1. Enable Google Calendar API
|
|
153
154
|
2. Download credentials
|
|
154
155
|
3. Save as `~/.jot/gcal-accounts/default/credentials.json`
|
|
155
|
-
4. Use
|
|
156
|
+
4. Use `.G` to export tasks
|
|
156
157
|
|
|
157
158
|
**Re-authentication:**
|
|
158
159
|
- Press `Shift+3` to force re-authentication (if token expires or switching accounts)
|
|
@@ -7,7 +7,7 @@ import importlib.util
|
|
|
7
7
|
import os
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
-
__version__ = "0.
|
|
10
|
+
__version__ = "0.7.0"
|
|
11
11
|
|
|
12
12
|
# When building Sphinx docs, skip the dynamic import bridge and heavy deps.
|
|
13
13
|
# Set JOT_SPHINX_BUILD=1 in docs/sphinx/conf.py before importing jot.
|
|
@@ -8,7 +8,6 @@ from jot.commands import CommandHandler
|
|
|
8
8
|
from jot.core.constants import MODE_QUICK_ADD
|
|
9
9
|
from jot.core.task_manager import TaskManager
|
|
10
10
|
from jot.ui.styles import RESET, CYAN
|
|
11
|
-
from jot.utils.date_utils import get_today_day_name
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class AppNavigationMixin:
|
|
@@ -37,41 +36,6 @@ class AppNavigationMixin:
|
|
|
37
36
|
return True
|
|
38
37
|
return False
|
|
39
38
|
|
|
40
|
-
def _handle_toggle(self, key):
|
|
41
|
-
"""Process shared toggle keys O/Y/F. Returns True if
|
|
42
|
-
the key was a toggle key."""
|
|
43
|
-
st = self.state
|
|
44
|
-
|
|
45
|
-
toggle_map = {
|
|
46
|
-
'O': ('sort_by_day', None),
|
|
47
|
-
'Y': ('show_today_only', None),
|
|
48
|
-
'F': ('show_notes_inline', 'inline notes'),
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if key not in toggle_map:
|
|
52
|
-
return False
|
|
53
|
-
|
|
54
|
-
attr, label = toggle_map[key]
|
|
55
|
-
|
|
56
|
-
if key == 'O':
|
|
57
|
-
st.sort_by_day = not st.sort_by_day
|
|
58
|
-
msg = "Sorting by day" if st.sort_by_day else "Normal order"
|
|
59
|
-
elif key == 'Y':
|
|
60
|
-
st.show_today_only = not st.show_today_only
|
|
61
|
-
today = get_today_day_name()
|
|
62
|
-
msg = (f"Showing only {today} tasks"
|
|
63
|
-
if st.show_today_only else "Showing all tasks")
|
|
64
|
-
else:
|
|
65
|
-
current = getattr(st, attr)
|
|
66
|
-
setattr(st, attr, not current)
|
|
67
|
-
new_val = not current
|
|
68
|
-
status = "Showing" if new_val else "Hiding"
|
|
69
|
-
msg = f"{status} {label}"
|
|
70
|
-
|
|
71
|
-
print(f"\n{CYAN}\u2713 {msg}{RESET}")
|
|
72
|
-
time.sleep(0.3)
|
|
73
|
-
st.input_buffer = ""
|
|
74
|
-
return True
|
|
75
39
|
|
|
76
40
|
def _reload_task_manager(self, task_manager):
|
|
77
41
|
"""Replace the active TaskManager and CommandHandler."""
|
|
@@ -8,6 +8,7 @@ from jot.core.constants import (
|
|
|
8
8
|
)
|
|
9
9
|
from jot.ui.input import get_key
|
|
10
10
|
from jot.ui.styles import RESET, CYAN
|
|
11
|
+
from jot.utils.date_utils import get_today_day_name
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class DispatchMixin:
|
|
@@ -34,17 +35,7 @@ class DispatchMixin:
|
|
|
34
35
|
self._handle_switch_category()
|
|
35
36
|
return
|
|
36
37
|
|
|
37
|
-
if key == 'Z':
|
|
38
|
-
self._handle_switch_project()
|
|
39
|
-
return
|
|
40
38
|
|
|
41
|
-
if key == 'L':
|
|
42
|
-
should_toggle, new_mode = (
|
|
43
|
-
self.command_handler.toggle_all_categories_view(st.mode))
|
|
44
|
-
if should_toggle:
|
|
45
|
-
st.mode = new_mode
|
|
46
|
-
st.input_buffer = ""
|
|
47
|
-
return
|
|
48
39
|
|
|
49
40
|
if key == '=':
|
|
50
41
|
self.task_manager.sort_by_priority()
|
|
@@ -57,15 +48,6 @@ class DispatchMixin:
|
|
|
57
48
|
st.input_buffer = ""
|
|
58
49
|
return
|
|
59
50
|
|
|
60
|
-
if self._handle_toggle(key):
|
|
61
|
-
return
|
|
62
|
-
|
|
63
|
-
if key == 'V':
|
|
64
|
-
st.mode = MODE_MULTISELECT
|
|
65
|
-
st.selected_tasks.clear()
|
|
66
|
-
st.input_buffer = ""
|
|
67
|
-
return
|
|
68
|
-
|
|
69
51
|
if key == '\x06': # Ctrl+F
|
|
70
52
|
st.mode = MODE_FUZZY_SEARCH
|
|
71
53
|
st.search_buffer = ""
|
|
@@ -153,10 +135,6 @@ class DispatchMixin:
|
|
|
153
135
|
time.sleep(0.3)
|
|
154
136
|
return
|
|
155
137
|
|
|
156
|
-
if key in ('O', 'Y'):
|
|
157
|
-
self._handle_toggle(key)
|
|
158
|
-
return
|
|
159
|
-
|
|
160
138
|
if key:
|
|
161
139
|
st.running = self.command_handler.handle(key)
|
|
162
140
|
|
|
@@ -213,9 +191,6 @@ class DispatchMixin:
|
|
|
213
191
|
st.selected_tasks.add(current_task['id'])
|
|
214
192
|
return
|
|
215
193
|
|
|
216
|
-
if key == 'B':
|
|
217
|
-
self._multiselect_bulk_action()
|
|
218
|
-
return
|
|
219
194
|
|
|
220
195
|
def _multiselect_bulk_action(self):
|
|
221
196
|
"""Process bulk action menu in multiselect mode."""
|
|
@@ -301,6 +276,10 @@ class DispatchMixin:
|
|
|
301
276
|
self._toggle_collapse()
|
|
302
277
|
st.input_buffer = ""
|
|
303
278
|
return
|
|
279
|
+
if second == 'd':
|
|
280
|
+
self.command_handler.duplicate_task()
|
|
281
|
+
st.input_buffer = ""
|
|
282
|
+
return
|
|
304
283
|
if second == 'c':
|
|
305
284
|
self.command_handler.copy_to_clipboard()
|
|
306
285
|
st.input_buffer = ""
|
|
@@ -328,6 +307,116 @@ class DispatchMixin:
|
|
|
328
307
|
self.command_handler.export_to_gcal()
|
|
329
308
|
st.input_buffer = ""
|
|
330
309
|
return
|
|
310
|
+
if second == 'f':
|
|
311
|
+
st.show_notes_inline = not st.show_notes_inline
|
|
312
|
+
status = "Showing" if st.show_notes_inline else "Hiding"
|
|
313
|
+
print(f"\n{CYAN}\u2713 {status} inline notes{RESET}")
|
|
314
|
+
time.sleep(0.3)
|
|
315
|
+
st.input_buffer = ""
|
|
316
|
+
return
|
|
317
|
+
if second == 'k':
|
|
318
|
+
self.command_handler.copy_task_to_project()
|
|
319
|
+
st.input_buffer = ""
|
|
320
|
+
return
|
|
321
|
+
if second == 'h':
|
|
322
|
+
self.command_handler.set_priority_high()
|
|
323
|
+
st.input_buffer = ""
|
|
324
|
+
return
|
|
325
|
+
if second == 'p':
|
|
326
|
+
self.command_handler.set_priority()
|
|
327
|
+
st.input_buffer = ""
|
|
328
|
+
return
|
|
329
|
+
if second == 'm':
|
|
330
|
+
self.command_handler.move_task_to_project()
|
|
331
|
+
st.input_buffer = ""
|
|
332
|
+
return
|
|
333
|
+
if second == 'u':
|
|
334
|
+
self.command_handler.toggle_caps()
|
|
335
|
+
st.input_buffer = ""
|
|
336
|
+
return
|
|
337
|
+
if second == 'e':
|
|
338
|
+
self.command_handler.trigger_keyword_action()
|
|
339
|
+
st.input_buffer = ""
|
|
340
|
+
return
|
|
341
|
+
if second == 'b':
|
|
342
|
+
self.command_handler.start_priority_timer()
|
|
343
|
+
st.input_buffer = ""
|
|
344
|
+
return
|
|
345
|
+
if second == 'j':
|
|
346
|
+
self.command_handler.set_agent_task()
|
|
347
|
+
st.input_buffer = ""
|
|
348
|
+
return
|
|
349
|
+
if second == 'l':
|
|
350
|
+
should_toggle, new_mode = (
|
|
351
|
+
self.command_handler.toggle_all_categories_view(st.mode))
|
|
352
|
+
if should_toggle:
|
|
353
|
+
st.mode = new_mode
|
|
354
|
+
st.input_buffer = ""
|
|
355
|
+
return
|
|
356
|
+
if second == 'n':
|
|
357
|
+
self.command_handler.edit_task_notes()
|
|
358
|
+
st.input_buffer = ""
|
|
359
|
+
return
|
|
360
|
+
if second == 'o':
|
|
361
|
+
st.sort_by_day = not st.sort_by_day
|
|
362
|
+
msg = "Sorting by day" if st.sort_by_day else "Normal order"
|
|
363
|
+
print(f"\n{CYAN}\u2713 {msg}{RESET}")
|
|
364
|
+
time.sleep(0.3)
|
|
365
|
+
st.input_buffer = ""
|
|
366
|
+
return
|
|
367
|
+
if second == 'w':
|
|
368
|
+
self.command_handler.assign_day()
|
|
369
|
+
st.input_buffer = ""
|
|
370
|
+
return
|
|
371
|
+
if second == 'x':
|
|
372
|
+
self.command_handler.set_status()
|
|
373
|
+
st.input_buffer = ""
|
|
374
|
+
return
|
|
375
|
+
if second == 'y':
|
|
376
|
+
st.show_today_only = not st.show_today_only
|
|
377
|
+
today = get_today_day_name()
|
|
378
|
+
msg = (f"Showing only {today} tasks"
|
|
379
|
+
if st.show_today_only else "Showing all tasks")
|
|
380
|
+
print(f"\n{CYAN}\u2713 {msg}{RESET}")
|
|
381
|
+
time.sleep(0.3)
|
|
382
|
+
st.input_buffer = ""
|
|
383
|
+
return
|
|
384
|
+
if second == 'z':
|
|
385
|
+
self._handle_switch_project()
|
|
386
|
+
st.input_buffer = ""
|
|
387
|
+
return
|
|
388
|
+
if second == 'A':
|
|
389
|
+
self.command_handler.ask_claude()
|
|
390
|
+
st.input_buffer = ""
|
|
391
|
+
return
|
|
392
|
+
if second == 'C':
|
|
393
|
+
self.command_handler.launch_claude()
|
|
394
|
+
st.input_buffer = ""
|
|
395
|
+
return
|
|
396
|
+
if second == 'v':
|
|
397
|
+
self.command_handler.send_to_local_claude()
|
|
398
|
+
st.input_buffer = ""
|
|
399
|
+
return
|
|
400
|
+
if second == 'V':
|
|
401
|
+
self.command_handler.send_to_claude()
|
|
402
|
+
st.input_buffer = ""
|
|
403
|
+
return
|
|
404
|
+
if second == 'X':
|
|
405
|
+
self.command_handler.execute_claude()
|
|
406
|
+
st.input_buffer = ""
|
|
407
|
+
return
|
|
408
|
+
if second == 'I':
|
|
409
|
+
self.command_handler.import_from_github()
|
|
410
|
+
st.input_buffer = ""
|
|
411
|
+
return
|
|
412
|
+
if second == 'H':
|
|
413
|
+
self.command_handler.export_to_github()
|
|
414
|
+
st.input_buffer = ""
|
|
415
|
+
return
|
|
416
|
+
if second == 'D':
|
|
417
|
+
self.command_handler.close_github_issue()
|
|
418
|
+
st.input_buffer = ""
|
|
419
|
+
return
|
|
331
420
|
# Not a chord — treat '.' as normal input
|
|
332
421
|
st.input_buffer += '.'
|
|
333
422
|
if second and second != '.':
|
|
@@ -402,10 +491,16 @@ class DispatchMixin:
|
|
|
402
491
|
"""Handle a keypress in ALL_CATEGORIES mode."""
|
|
403
492
|
st = self.state
|
|
404
493
|
|
|
405
|
-
if key == '\x1b'
|
|
494
|
+
if key == '\x1b':
|
|
406
495
|
st.mode = MODE_QUICK_ADD
|
|
407
496
|
return
|
|
408
497
|
|
|
498
|
+
if key == '.':
|
|
499
|
+
second = get_key(timeout=0.5)
|
|
500
|
+
if second == 'l':
|
|
501
|
+
st.mode = MODE_QUICK_ADD
|
|
502
|
+
return
|
|
503
|
+
|
|
409
504
|
filtered = (
|
|
410
505
|
tasks_to_display
|
|
411
506
|
if st.show_today_only or st.collapsed_parents
|
|
@@ -56,23 +56,11 @@ class App(DispatchMixin, AppNavigationMixin):
|
|
|
56
56
|
"""Interactive event loop for the jot TUI."""
|
|
57
57
|
|
|
58
58
|
_QUICK_ADD_SIMPLE = {
|
|
59
|
-
'M': 'move_task_to_project',
|
|
60
|
-
'\x0b': 'copy_task_to_project', # Ctrl+K
|
|
61
59
|
'\x14': 'transfer_task_to_category', # Ctrl+T
|
|
62
60
|
'\x13': 'sync_subtasks', # Ctrl+S
|
|
63
61
|
'\x04': 'delete_current', # Ctrl+D
|
|
64
|
-
'W': 'assign_day',
|
|
65
|
-
'P': 'set_priority',
|
|
66
|
-
'H': 'set_priority_high',
|
|
67
|
-
'X': 'set_status',
|
|
68
|
-
'E': 'trigger_keyword_action',
|
|
69
|
-
'N': 'edit_task_notes',
|
|
70
62
|
'\x15': 'open_url', # Ctrl+U
|
|
71
|
-
'(': 'ultrathink_task',
|
|
72
|
-
')': 'execute_analysis_task',
|
|
73
63
|
'$': 'suggest_task',
|
|
74
|
-
'J': 'set_agent_task',
|
|
75
|
-
'B': 'start_priority_timer',
|
|
76
64
|
'!': 'fix_duplicate_ids_interactive',
|
|
77
65
|
'@': 'read_tasks_aloud',
|
|
78
66
|
'#': 'reauthenticate_google_calendar',
|
|
@@ -116,6 +104,7 @@ class App(DispatchMixin, AppNavigationMixin):
|
|
|
116
104
|
tasks_to_display = self._prepare_tasks()
|
|
117
105
|
self._render(tasks_to_display)
|
|
118
106
|
self._needs_render = False
|
|
107
|
+
self._clear_terminal_claude_status()
|
|
119
108
|
else:
|
|
120
109
|
tasks_to_display = self._prepare_tasks()
|
|
121
110
|
|
|
@@ -279,6 +268,7 @@ class App(DispatchMixin, AppNavigationMixin):
|
|
|
279
268
|
match_positions=st.match_positions,
|
|
280
269
|
collapsed_parents=st.collapsed_parents,
|
|
281
270
|
all_tasks=all_tasks,
|
|
271
|
+
claude_status=self.command_handler._claude_status,
|
|
282
272
|
)
|
|
283
273
|
|
|
284
274
|
sys.stdout.write('\033[?25h')
|
|
@@ -298,6 +288,12 @@ class App(DispatchMixin, AppNavigationMixin):
|
|
|
298
288
|
# Auto-refresh & paste
|
|
299
289
|
# ------------------------------------------------------------------
|
|
300
290
|
|
|
291
|
+
def _clear_terminal_claude_status(self):
|
|
292
|
+
"""Clear 'done'/'error' statuses after one render cycle."""
|
|
293
|
+
status = self.command_handler._claude_status
|
|
294
|
+
if status and status not in ("thinking...", "editing..."):
|
|
295
|
+
self.command_handler._claude_status = ""
|
|
296
|
+
|
|
301
297
|
def _handle_auto_refresh(self, key):
|
|
302
298
|
"""Detect external file changes on timeout."""
|
|
303
299
|
if key is not None:
|
|
@@ -177,7 +177,7 @@ class AiAnalysisMixin:
|
|
|
177
177
|
return True
|
|
178
178
|
|
|
179
179
|
def execute_analysis_task(self):
|
|
180
|
-
"""Execute analysis plan with Claude Code
|
|
180
|
+
"""Execute analysis plan with Claude Code."""
|
|
181
181
|
current_task = self.task_manager.get_current_task()
|
|
182
182
|
if not current_task:
|
|
183
183
|
print("\n✗ No current task selected")
|
|
@@ -191,7 +191,7 @@ class AiAnalysisMixin:
|
|
|
191
191
|
analysis_file = Path.cwd() / f".jot.analysis.{task_id}.org"
|
|
192
192
|
if not analysis_file.exists():
|
|
193
193
|
print(f"\n{YELLOW}⚠️ No analysis found for this task{RESET}")
|
|
194
|
-
print(f"\n{DIM}Tip:
|
|
194
|
+
print(f"\n{DIM}Tip: Use the ~analyze:~ keyword to create an analysis plan{RESET}")
|
|
195
195
|
print("\nPress Enter to continue...", end='', flush=True)
|
|
196
196
|
input()
|
|
197
197
|
return True
|