Commit 359cc58
fix: support Windows path encoding for projects, sessions, and subagents (#50)
* fix: support Windows path encoding for projects, sessions, and subagents
Claude Code on Windows encodes project paths differently from Unix:
Unix: /Users/me/repo -> -Users-me-repo (leading / becomes -)
Windows: C:\Code\Tools -> C--Code-Tools (colon+backslash become dashes)
Without this fix, the API failed to discover any projects on Windows because
all encoded dir names like "C--Code-Tools" were filtered out by startswith("-")
checks, and encode_path/decode_path produced incorrect paths.
Changes:
- models/project.py: fix encode_path for Windows absolute paths (C:\ -> C--);
fix decode_path to recover C:/... paths from C-- encoded names
- utils.py: add is_encoded_project_dir() helper (handles both - and X-- prefixes);
replace all startswith("-") dir-scan guards with the new helper
- db/indexer.py: use is_encoded_project_dir() for project dir filtering
- routers/{projects,sessions,agent_analytics,history}.py: same guard fix
- services/{desktop_sessions,session_lookup}.py: same guard fix
- services/subagent_types.py: open JSONL with encoding="utf-8", errors="replace"
to prevent UnicodeDecodeError (cp1252 default on Windows) for subagent pages
- models/{history,plugin}.py: same UTF-8 encoding fix for file reads
- config.py: add localhost:5199 to CORS allowed origins
* Add comprehensive UI testing scripts and automation for critical screens
- Introduced `quick_browser_test.py` for quick visual checks of critical pages.
- Created `test_browser_ui.py` for detailed UI testing with content verification.
- Added `test_subagent_ui.py` for testing subagent session viewing.
- Implemented `test_ui_comprehensive.py` for a final comprehensive UI test with actual content verification.
- Developed `test_ui_detailed.py` for thorough testing of all screens, including navigation, filters, and accessibility.
- Added batch scripts (`start.bat` and `start.sh`) for easy backend and frontend startup.
- Created initial `ui_test_results.json` for storing test results.
- Enhanced error handling and logging throughout the test scripts.
* fix: resolve Svelte 5 canvas binding issue in analytics page
- Changed chartCanvas from derived.by to direct let binding
- Added svelte-ignore directive for non-reactive update
- Fixes Svelte compiler warnings about bind:this with derived
* fix: backport UX, timezone, and plugin skill detection fixes (#48)
* feat: extract and display image attachments from user messages
Parses base64 image blocks from UserMessage content, surfaces them
via image_attachments field, and renders them in the frontend timeline,
conversation overview, and expandable prompt components.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(api): use local timezone for dashboard stats and daily trend grouping
The dashboard "today"/"yesterday"/"this week" stats and all 28 SQL daily
trend queries were using UTC date boundaries instead of the machine's
local timezone. On a UTC-8 machine, this caused the homepage to show
1 session instead of 24 for "today".
- Add shared `local_timezone()` and `utc_to_local_date()` helpers in
utils.py using `datetime.astimezone()` (DST-safe, no stale offsets)
- Fix dashboard endpoint to use local calendar date boundaries
- Replace all DATE(s.start_time) with timezone-adjusted expressions
in db/queries.py (28 occurrences)
- Fix Python-side date grouping in plugins router (5 occurrences)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(plugins): detect skills from manifest custom paths
Plugins like impeccable store skills in non-default directories
(e.g., .claude/skills/ instead of skills/) and declare these paths
in their .claude-plugin/plugin.json manifest. Our capability scanner
only checked hardcoded default directories, returning 0 skills.
- Add read_plugin_manifest() and _resolve_manifest_dirs() helpers
to scan both default and manifest-declared custom paths
- Update scan_plugin_capabilities(), read_command_contents(),
list_plugin_skills(), get_plugin_skill_content() to use them
- Update _resolve_skill_info() to check manifest paths when
resolving individual skill files
- Fix route ordering: move /{plugin_name:path} catch-all to end
so /skills and /skills/content sub-routes are reachable
- Add "Browse all N skill files" link on plugin detail page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: replace ambiguous for/break with next() for first user message lookup
The for/break pattern had the break at the same indent as the if,
making it always fire on first iteration regardless of the condition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address code review findings across 3 cherry-picked commits
- media_type allowlist for image attachments (prevents non-image data URIs)
- rename shadowed `date` param to `day` in _local_day_boundaries
- guard naive datetimes in utc_to_local_date (assume UTC)
- remove unused plugins_base variable in _resolve_manifest_dirs
- promote _find_skill_in_version_dir to module level (was needlessly nested)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: sort imports in plugins and skills routers to pass ruff I001
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: apply ruff formatter to message, plugins, and sessions modules
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add zero-use skills display
* feat: persist accordion state and scroll position on skills/commands pages
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Ayush Jhunjhunwala <48875674+the-non-expert@users.noreply.github.com>
* fix: address code review findings for PR #50 Windows path encoding
Review fixes for the Windows path encoding PR:
CRITICAL:
- Revert API_BASE default port from 9005 back to 8000 (contributor's
local config leaked into config.ts)
HIGH:
- Remove 11 root-level test scripts, JSON results, and start scripts
that were committed as debugging artifacts
- Update test_projects.py and test_sessions.py to use
is_encoded_project_dir() instead of startswith("-")
- Add 21 unit tests: TestEncodePathWindows (5), TestDecodePathWindows (5),
TestIsEncodedProjectDir (11), fix wrong assertion in existing test
MEDIUM:
- Move `import re` from function scope to module level in project.py
- Rename _resolve_manifest_dirs → resolve_manifest_dirs (public API,
used across plugin.py, plugins.py, skills.py)
- Add docstring note about false-positive edge case in
is_encoded_project_dir()
All 1359 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: consolidate path encoding and add full Windows/Linux support
Closes 6 gaps in cross-platform path handling:
API — eliminate code duplication:
- history.py: replace standalone encode_path() with delegation to
canonical Project.encode_path() (was Unix-only, now handles Windows)
- indexer.py: replace inline decode/encode in _detect_project_path()
and _resolve_project_path() with Project.decode_path()/encode_path()
API — cross-platform path recognition:
- project.py: add _is_absolute_path() helper that recognizes Windows
paths (C:\, D:/) and UNC paths (\\server\share) on any host OS
- project.py: normalize backslashes in cwd values from JSONL sessions
so Windows paths are stored consistently with forward slashes
Frontend — Windows path support:
- utils.ts: add Windows C-- pattern to decodeProjectPath() for proper
drive letter reconstruction (C--Code-Tools → C:/Code/Tools)
- utils.ts: add Windows prefix stripping to getProjectNameFromEncoded()
- grouped-projects.ts: normalize backslashes in getDisplayName() and
getRelativePath() for cross-platform path comparison
Tests: 19 new tests covering Windows paths across all changed functions
(API: 127 pass, full suite: 1375 pass; frontend: 186 pass)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: fix ruff lint errors — duplicate functions, import sorting, unused imports
- utils.py: remove duplicate local_timezone() and utc_to_local_date() definitions
- routers/history.py: remove unused is_encoded_project_dir import
- routers/agent_analytics.py, plugins.py, skills.py: fix import sorting (I001)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add cross-platform path test suite and Windows mock fixtures
56 new tests (52 run on Mac/Linux, 4 Windows-only):
- TestEncodingRoundtrip: parametrized encode/decode for all platforms
- TestDirectoryFilterCrossPlatform: is_encoded_project_dir coverage
- TestIsAbsolutePathCrossPlatform: _is_absolute_path on all formats
- TestWindowsSessionCwdRecovery: mock JSONL with Windows backslash cwd
- TestMixedProjectListing: Unix + Windows dirs coexist correctly
- TestWindowsNativePathlib: real pathlib validation (Windows CI only)
- TestNonWindowsCrossPlatform: verifies why our helpers are needed
Fixtures: add temp_windows_project_dir and sample_windows_session_jsonl
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: fix import sorting in cross-platform path tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: normalize backslashes before splitting so C:\Users\me\repo produces "me/repo" instead of the raw path string. Paths from history.jsonl on Windows are not pre-normalized.
* fix: make test suite fully cross-platform on Windows Python 3.12+
Replace hardcoded Unix paths with tmp_path, compare git_root_path as Path objects, mock Path.home() directly, use stat().st_size for file sizes, add skipif guard for symlink test, fix get_project_name() backslash handling.
* style: fix ruff formatting in test_desktop_sessions.py
* fix: normalize backslashes to forward slashes in projects router and grouped-projects util
Ensure Windows paths from history.jsonl are normalized before use in
project_path comparisons and git_root_path grouping logic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address code review findings for Windows path encoding PR
- Remove duplicate local_timezone/utc_to_local_date definitions (merge artifact)
- Replace bare raise Exception with _FallbackToFilesystem sentinel for control flow
- Reject bare dash "-" in is_encoded_project_dir (filesystem root, never created by Claude Code)
- Remove unused CORS port 5199
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: fix ruff formatting in projects router
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Cuong Ng <nhcuongit@gmail.com>
Co-authored-by: Jayant Devkar <55962509+JayantDevkar@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Ayush Jhunjhunwala <48875674+the-non-expert@users.noreply.github.com>
Co-authored-by: ShivumBhagat <bhagatshivum@gmail.com>1 parent bee0e1f commit 359cc58
28 files changed
Lines changed: 974 additions & 130 deletions
File tree
- api
- db
- models
- routers
- services
- tests
- api
- frontend/src
- lib
- utils
- routes
- analytics
- projects/[project_slug]
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
| 25 | + | |
24 | 26 | | |
25 | 27 | | |
26 | 28 | | |
| |||
91 | 93 | | |
92 | 94 | | |
93 | 95 | | |
94 | | - | |
| 96 | + | |
95 | 97 | | |
96 | 98 | | |
97 | 99 | | |
| |||
576 | 578 | | |
577 | 579 | | |
578 | 580 | | |
579 | | - | |
580 | | - | |
581 | | - | |
582 | | - | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
583 | 586 | | |
584 | 587 | | |
585 | 588 | | |
| |||
687 | 690 | | |
688 | 691 | | |
689 | 692 | | |
| 693 | + | |
| 694 | + | |
690 | 695 | | |
691 | 696 | | |
692 | | - | |
693 | | - | |
694 | | - | |
| 697 | + | |
695 | 698 | | |
696 | 699 | | |
697 | 700 | | |
698 | 701 | | |
699 | 702 | | |
700 | 703 | | |
701 | 704 | | |
702 | | - | |
703 | | - | |
704 | | - | |
705 | | - | |
706 | | - | |
| 705 | + | |
707 | 706 | | |
708 | 707 | | |
709 | 708 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
104 | 111 | | |
105 | 112 | | |
106 | 113 | | |
107 | 114 | | |
108 | | - | |
109 | | - | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
110 | 119 | | |
111 | 120 | | |
112 | 121 | | |
| |||
139 | 148 | | |
140 | 149 | | |
141 | 150 | | |
142 | | - | |
| 151 | + | |
143 | 152 | | |
144 | 153 | | |
145 | 154 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
229 | 229 | | |
230 | 230 | | |
231 | 231 | | |
232 | | - | |
| 232 | + | |
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
| |||
356 | 356 | | |
357 | 357 | | |
358 | 358 | | |
359 | | - | |
| 359 | + | |
360 | 360 | | |
361 | 361 | | |
362 | 362 | | |
| |||
449 | 449 | | |
450 | 450 | | |
451 | 451 | | |
452 | | - | |
| 452 | + | |
453 | 453 | | |
454 | 454 | | |
455 | 455 | | |
456 | 456 | | |
457 | 457 | | |
458 | 458 | | |
459 | | - | |
| 459 | + | |
460 | 460 | | |
461 | 461 | | |
462 | 462 | | |
463 | 463 | | |
464 | 464 | | |
465 | 465 | | |
466 | 466 | | |
467 | | - | |
| 467 | + | |
468 | 468 | | |
469 | 469 | | |
470 | 470 | | |
| |||
486 | 486 | | |
487 | 487 | | |
488 | 488 | | |
489 | | - | |
| 489 | + | |
490 | 490 | | |
491 | 491 | | |
492 | 492 | | |
| |||
547 | 547 | | |
548 | 548 | | |
549 | 549 | | |
550 | | - | |
| 550 | + | |
551 | 551 | | |
552 | 552 | | |
553 | 553 | | |
| |||
560 | 560 | | |
561 | 561 | | |
562 | 562 | | |
563 | | - | |
| 563 | + | |
564 | 564 | | |
565 | 565 | | |
566 | 566 | | |
| |||
591 | 591 | | |
592 | 592 | | |
593 | 593 | | |
594 | | - | |
| 594 | + | |
595 | 595 | | |
596 | 596 | | |
597 | 597 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
62 | 63 | | |
63 | 64 | | |
64 | 65 | | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
65 | 85 | | |
66 | 86 | | |
67 | 87 | | |
| |||
89 | 109 | | |
90 | 110 | | |
91 | 111 | | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
92 | 115 | | |
93 | 116 | | |
94 | 117 | | |
95 | 118 | | |
96 | | - | |
| 119 | + | |
97 | 120 | | |
98 | 121 | | |
99 | | - | |
| 122 | + | |
100 | 123 | | |
101 | 124 | | |
| 125 | + | |
102 | 126 | | |
103 | | - | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
104 | 132 | | |
105 | 133 | | |
106 | 134 | | |
107 | 135 | | |
108 | 136 | | |
109 | 137 | | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
110 | 141 | | |
111 | 142 | | |
112 | 143 | | |
| |||
116 | 147 | | |
117 | 148 | | |
118 | 149 | | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
123 | 164 | | |
124 | 165 | | |
125 | 166 | | |
| |||
157 | 198 | | |
158 | 199 | | |
159 | 200 | | |
160 | | - | |
161 | | - | |
162 | | - | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
163 | 204 | | |
164 | 205 | | |
165 | 206 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
| |||
83 | 84 | | |
84 | 85 | | |
85 | 86 | | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
| 87 | + | |
93 | 88 | | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
94 | 93 | | |
95 | 94 | | |
96 | 95 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
244 | 244 | | |
245 | 245 | | |
246 | 246 | | |
247 | | - | |
248 | | - | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
253 | 251 | | |
254 | 252 | | |
255 | 253 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
30 | 29 | | |
31 | 30 | | |
32 | 31 | | |
| 32 | + | |
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| |||
1232 | 1232 | | |
1233 | 1233 | | |
1234 | 1234 | | |
1235 | | - | |
| 1235 | + | |
1236 | 1236 | | |
1237 | 1237 | | |
1238 | 1238 | | |
| |||
1258 | 1258 | | |
1259 | 1259 | | |
1260 | 1260 | | |
1261 | | - | |
| 1261 | + | |
1262 | 1262 | | |
1263 | 1263 | | |
1264 | 1264 | | |
| |||
1350 | 1350 | | |
1351 | 1351 | | |
1352 | 1352 | | |
1353 | | - | |
| 1353 | + | |
1354 | 1354 | | |
1355 | 1355 | | |
1356 | 1356 | | |
| |||
1362 | 1362 | | |
1363 | 1363 | | |
1364 | 1364 | | |
1365 | | - | |
1366 | | - | |
1367 | | - | |
| 1365 | + | |
1368 | 1366 | | |
1369 | 1367 | | |
1370 | 1368 | | |
| |||
0 commit comments