Commit 6b321c9
feat(tui): clickable file paths in chat tool calls (OSC 8) (#141)
* feat(tui): clickable file paths in chat tool calls (OSC 8)
Adds _osc8.py with a cached capability probe (env-var driven, no tty
round-trip) and file_link() helper that emits ESC]8 hyperlinks in
capable terminals (iTerm2, WezTerm, kitty, ghostty, vscode terminal,
Alacritty). ToolCallView._header_line() wraps the path= argument of
file-operating tools so users can cmd-click to open the file directly
in their editor. Falls back to plain text in unsupported terminals.
Override with KAGAN_OSC8=1 / KAGAN_OSC8=0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: delete unimplemented unified-client proposal, document pi_rpc, sweep drift (R11)
- docs/internal/architecture/unified-client.md deleted: 1229-line document opening with "This document proposes a breaking change" with no inbound references; pure unimplemented proposal with no place in the authoritative architecture directory.
- docs/internal/architecture/core.md: added "Pi RPC adapter" section (~55 lines) covering wire framing, byte guards, lifecycle, event translation table, cancellation, and backend registry hook; updated backend registry table to include pi-coding-agent; added adapters/pi_rpc.py and adapters/pi_rpc_messages.py to module layout; updated "Two Streaming Paths" to "Three Streaming Paths" to reflect the Pi RPC path.
- Drift fixed (2 hits): _transitions.py filename corrected to transitions.py in module layout and section header (the module was renamed to a public module without underscore prefix).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(web): collapse duplicate KaganApiClient into shared base (R7)
- packages/web/src/lib/api/client.ts: replaced 711-LOC full reimplementation
with 134-LOC thin subclass extending shared KaganApiClient; web-specific
behaviour (configureBundledWeb, bundled-mode URL routing, legacy aliases)
is isolated in the subclass.
- packages/shared/api-client/src/client.ts: promoted 20 methods from the web
client that are generic (analytics, integrations, doctor, interruptChatTurn,
getChatMessages, searchMentions, resolveProjectFolder) and fixed getTasks to
accept repoId, getTurnStatus to return TurnStatusResponse.
- packages/shared/api-client/src/wire.ts + scripts/generate_wire_types.py:
added ChatStreamError, ChatStreamSessionUpdated, CHAT_STREAM_EVENT const to
cover the full /api/chat/{id}/stream wire surface.
- packages/web/src/lib/chat/use-chat-stream.ts: CHAT_STREAM_EVENT const and
ChatStreamEventType now imported from @kagan/shared-api-client; fixed bug
where SESSION_UPDATED handler read msg.label instead of msg.session.label.
- packages/web/src/components/board/edit-task-dialog.tsx: removed github_issue
from PATCH updateTask call (field not in TaskUpdateRequest on server).
LOC delta: web/client.ts −577, shared/client.ts +207, use-chat-stream −5 = −375 net.
Methods moved to shared: getTasks(repoId), getTurnStatus(TurnStatusResponse),
interruptChatTurn, getChatMessages, resolveProjectFolder, searchMentions,
getBackendStats, getSessionTimeline, getAnalyticsExport, getRecommendedBackend,
getAnalyticsByRole, getAnalyticsByTaskType, getAnalyticsByRoleAndTaskType,
recommendBackendForTask, getStatsByRole, getStatsByTaskType, getCombinedStats,
getIntegrations, getIntegrationPreflight, detectIntegrationRepo,
previewIntegrationIssues, runIntegrationSync, getDoctorReport.
Browser-only methods on subclass: configureBundledWeb, isBundledWeb, getFullUrl
override, getHealth override, getBaseUrl override.
Kept _doRequest override: web uses relative URLs (same-origin), shared uses
full protocol://host URLs — the subclass overrides getFullUrl to return bare
path in bundled mode.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(core): typed ChatSessionView/SessionSummary, kill legacy dicts (R6)
- Deleted chat_session_to_legacy_dict (dict[str, Any] with magic string keys) and active_session_summaries returning dict[str, dict[str, Any]]
- Replaced with ChatSessionView (Pydantic BaseModel, mutable for in-place label mutation by ensure_session_title) and SessionSummary in core/_sessions.py
- Updated 13 call sites: cli/chat controller + picker + title, server _chat_routes, tui orchestrator_sessions + kanban + session_resume_modal + chat widget, plus 4 test files; test helpers use .model_dump() to preserve backward-compatible return type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(tests): move fixtures to helpers, kill asyncio.sleep, narrow github mocks (R10)
- Fixtures: moved client/config/integration to tests/integrations/conftest.py, browse_endpoint to tests/server/conftest.py, git_board deduped into shared helpers; added bare_board for project-management tests (6 fixture definitions removed from test files)
- Sleeps: replaced asyncio.sleep(0.040/0.05) with asyncio.wait_for / asyncio.Event().wait() in test_chat_batched_approvals, test_acp_session, test_chat_sse, test_agent_kill (4 timing-based sleeps eliminated)
- GitHub: private _extract_label_names/_map_labels unit tests moved to tests/unit/test_github_helpers.py; integration tests kept in tests/integrations/ using conftest fixtures
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(web): delete dead ArtifactsPanel; dedupe cycleDockMode util (R12)
Deleted: ArtifactsPanel.tsx (160 LOC), ArtifactsPanel.test.tsx (140 LOC),
lib/atoms/artifacts.ts (32 LOC) — speculative subtree with zero live callers.
Extracted DockedChatRailMode type and cycleDockMode function from both
use-global-shortcuts.ts and app-layout.tsx into a single canonical util at
packages/web/src/lib/layout/dock-mode.ts (9 LOC). Both files now import from it.
Net: -335 LOC, tsc + vitest (367 tests) + build all green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(core): delete Fernet-at-rest from settings (no production callers) (R14)
- Deleted 107 LOC from src/kagan/core/_settings.py: _SENSITIVE_SUFFIXES, _FERNET_PREFIX, _is_sensitive_key, _secret_key_path, _load_or_create_fernet_key, _encrypt_value, _decrypt_value, SettingsDecryptError, _maybe_encrypt, _maybe_decrypt and all Fernet call sites in get_settings/set_settings
- cryptography dep NOT removed — still required by src/kagan/server/crypto/_tls.py (x509/TLS)
- Deleted 6 Fernet tests from tests/core/test_audit_cleanup.py; 5 F4 injection + 1 rate-limit tests retained
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(core/server): match event.kind for ChatEvent dispatch (R9)
- _chat_routes._chat_event_to_sse_frame: replaced 8-arm isinstance chain
with match event.kind (ChatEvent discriminator); removed 8 now-unused
variant imports from kagan.core.chat
- core/chat/acp.acp_update_to_chat_event: replaced 5-arm ACP isinstance
chain with match update.session_update (ACP schema discriminator);
removed deferred acp.schema imports no longer needed at call sites
- cli/chat/_renderer.on_event: replaced 6-arm ChatEvent isinstance chain
with match event.kind; fallthrough to case _: pass preserves prior
implicit no-op for unrecognised variants; pyrefly exhaustiveness clean
McCabe: all three functions drop by 1–2 (branch elision from match vs
elif); no semantic change — pure structural refactor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(tests): purge tautology tests + relocate misplaced units per testing.md (R21)
- Moved 4 files (server/test_{middleware,presence,sse_polling,web_ui}.py) to
tests/unit/server/; added pytestmark=[pytest.mark.unit] to test_web_ui.py
which was missing it (testing.md L48).
- Moved 3 files (core/test_pi_coding_agent_backend.py, mcp/contract/
test_doctor_endpoint.py, core/test_cli_update_bootstrap.py) to
tests/unit/{,mcp/,cli/}; all import private modules (_agent, _environment_checks,
_web_ui, _sse, _bootstrap) — unit/ is the correct home (testing.md L46-47).
- Deleted 4 files: core/test_analytics.py + core/test_analytics_smoke.py
duplicate tests/unit/core/test_task_classification.py verbatim;
server/test_chat_sse.py imports _chat_event_to_sse_frame + patches
SpawnPerTurnACPFactory directly on the production module (L332, L334);
integrations/test_mcp_github_tools.py calls tool functions bypassing the MCP
router tautology — shape asserted not behavior (L332).
- Deleted integrations/test_github_two_way_sync.py: imports 6 private github
helpers (_parse_criteria_lines, _render_criteria_comment, etc.) and patches
4 network functions; observable behavior covered by tests/integrations/test_github.py.
NOTE: parser-unit coverage for _parse_criteria_lines/_render_criteria_comment
is now dark — a follow-up R21b can add tests/unit/integrations/test_github_parsers.py.
- Rewrote test_install_rc_zero_promotes_settings_and_emits_telemetry in
tests/tui/test_doctor_modal.py: removed direct TelemetryEvent DB row query
(testing.md L333); replaced with observable-state assertions (settings written,
modal dismissed to setup-flow).
- Fixed tests/core/test_cli_surface.py: replaced kagan.cli._bootstrap.make_client
with kagan.core.KaganCore (public API) — private import in a behavioral suite
(testing.md L339). CLI-surface monkeypatching retained per L334-336 carve-out.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(server): type ServerContext through require_context (R13)
- Replaced 81 ctx: Any annotations with ctx: ServerContext across 9 server modules
- Approach A (direct annotation): no decorator changes, no call-site edits
- Added ServerContext to TYPE_CHECKING imports in each affected file; no public alias needed as ServerContext is already public in kagan.server.mcp.server
- Pyrefly: 0 errors before → 0 errors after (73 suppressed unchanged); _poll_db_changes retains ctx_or_tasks: Any intentionally (accepts ServerContext or bare Tasks in tests)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(web): simplify VisuallyHidden to span (R20)
* refactor(core): inline single integration protocol (R16)
* refactor(core): flatten ACP client base (R17)
* refactor(tui): dedupe task data protocol (R18)
* refactor(core): collapse hook framework to guard functions (R15)
* refactor(chat): collapse spawn-per-turn ACP factory (R19)
* docs: refresh post-guido architecture notes
* fix(tui): preserve full OSC 8 file link targets
* ci: unblock Snyk security scans
* fix(web): use same-origin health check before auth hydration
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>1 parent f228035 commit 6b321c9
107 files changed
Lines changed: 2872 additions & 6068 deletions
File tree
- .github/workflows
- docs
- concepts
- internal
- architecture
- refinements
- packages
- web/src
- components
- a11y
- board
- layout
- session
- lib
- api
- atoms
- chat
- hooks
- layout
- scripts
- src/kagan
- cli/chat
- core
- chat
- integrations
- server
- tui
- screens
- widgets
- tests
- core
- helpers
- integrations
- mcp/contract
- server
- tui
- unit
- cli
- core
- mcp
- server
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
71 | | - | |
| 71 | + | |
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
81 | 81 | | |
82 | 82 | | |
83 | 83 | | |
84 | | - | |
| 84 | + | |
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
149 | | - | |
| 149 | + | |
150 | 150 | | |
151 | 151 | | |
152 | 152 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
74 | | - | |
| 74 | + | |
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
83 | | - | |
| 83 | + | |
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
| 92 | + | |
| 93 | + | |
92 | 94 | | |
93 | 95 | | |
94 | 96 | | |
95 | 97 | | |
96 | 98 | | |
97 | 99 | | |
98 | 100 | | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
106 | 104 | | |
107 | 105 | | |
108 | 106 | | |
| |||
265 | 263 | | |
266 | 264 | | |
267 | 265 | | |
268 | | - | |
| 266 | + | |
269 | 267 | | |
270 | | - | |
271 | | - | |
272 | | - | |
273 | | - | |
274 | | - | |
275 | | - | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
276 | 274 | | |
277 | | - | |
| 275 | + | |
278 | 276 | | |
279 | 277 | | |
280 | 278 | | |
281 | | - | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
282 | 282 | | |
283 | 283 | | |
284 | 284 | | |
| |||
317 | 317 | | |
318 | 318 | | |
319 | 319 | | |
320 | | - | |
| 320 | + | |
321 | 321 | | |
322 | 322 | | |
323 | 323 | | |
| |||
357 | 357 | | |
358 | 358 | | |
359 | 359 | | |
| 360 | + | |
360 | 361 | | |
361 | | - | |
| 362 | + | |
362 | 363 | | |
363 | 364 | | |
364 | 365 | | |
| |||
386 | 387 | | |
387 | 388 | | |
388 | 389 | | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
389 | 448 | | |
390 | 449 | | |
391 | 450 | | |
| |||
0 commit comments