Skip to content

refactor(tui): prompt deep-module PR 3 — canonical completion + async workspace index#220

Merged
elkaix merged 5 commits into
mainfrom
refactor/tui-prompt-deep-module-pr3
Jul 21, 2026
Merged

refactor(tui): prompt deep-module PR 3 — canonical completion + async workspace index#220
elkaix merged 5 commits into
mainfrom
refactor/tui-prompt-deep-module-pr3

Conversation

@elkaix

@elkaix elkaix commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Part 3 of the prompt deep-module refactor series (docs/superpowers/plans/2026-07-19-tui-prompt-deep-module-refactor.md, Tasks 6–7). Stacked on #219.

Task 6 — One canonical completion context

  • New prompting/completion/context.py: CompletionKind + frozen CompletionContext with parse_completion_context() as the single slash/file token parser; root-dropdown vs mid-line ghost activation preserved as facets of one parse.
  • New prompting/completion/slash.py: SlashCommandCompleter, SlashCommandAutoSuggest, and lexer slash decisions moved behind the context; no caller re-parses slash text.
  • Quoted file mentions: @"path with spaces" now parse and complete, retaining quotes and escaping embedded quotes/backslashes; unquoted mentions keep whitespace termination.
  • prompt.py stays the compatibility façade: all previously public/tested names re-exported (pinned by test_prompt_public_contract.py).

Task 7 — Async, cwd-aware workspace index

  • New prompting/completion/workspace.py: generation-owned WorkspaceIndex (set_root/snapshot/request_refresh/aclose) over the pythinker_host.Host interface. snapshot() and get_completions() perform no I/O (pinned by test); stale-generation/stale-root scan results can never publish; Git failure and bounded traversal publish explicit degraded snapshots; results truncate at 1,000 entries.
  • HostFileMentionCompleter reads snapshots only; LocalFileMentionCompleter kept as a façade alias with should_complete unchanged.
  • Index root re-syncs to HostPath.cwd() before each prompt read; all discovery tasks are owned by PromptLifecycle with cancellation and awaited shutdown.
  • on_publish callback restarts the open file-mention completion menu when a fresh snapshot lands, so async discovery results appear without retyping (caught by tests/e2e/test_file_mention_e2e.py, which passes unmodified).

Verification

  • make check-pythinker-code: All checks passed (ruff + format + pyright + ty).
  • make test-pythinker-code: full unit suite + tests_e2e green (final phase 65 passed, 4 skipped).
  • Focused: tests/ui_and_conv 3062 passed; tests/e2e/test_file_mention_e2e.py 4 passed.

Summary by CodeRabbit

  • New Features

    • Unified completion parsing/context for slash commands and @ file mentions, including quoted paths (e.g., paths with spaces) and inline highlighting.
    • Added asynchronous, workspace-aware @ file mention completion backed by refreshed snapshots.
  • Bug Fixes

    • Improved prompt completion hardening (escape/dismiss rules, punctuation/escaping, better ranking for @dir/name).
    • Hardened workspace indexing during Git scans and mid-session CWD changes; avoids crashes and stale results by safely refreshing/cancelling obsolete work.
  • Documentation

    • Updated spellcheck allowlist for caf.
  • Tests

    • Expanded async completion, parsing/highlighting boundary, workspace indexing, and limit enforcement coverage.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 49d75f77-4dc4-4456-98e1-b69eb778b67a

📥 Commits

Reviewing files that changed from the base of the PR and between 42179a6 and 7627f66.

📒 Files selected for processing (2)
  • src/pythinker_code/ui/shell/prompting/completion/workspace.py
  • tests/ui_and_conv/test_workspace_index.py

📝 Walkthrough

Walkthrough

This PR unifies slash-command and file-mention completion through shared context parsing, adds slash and workspace completion modules, introduces asynchronous Git-aware workspace indexing, integrates the index into the prompt session, and expands related tests and release metadata.

Changes

Unified completion context and async workspace indexing

Layer / File(s) Summary
Canonical completion context parser
src/pythinker_code/ui/shell/prompting/completion/context.py
Adds shared parsing for slash commands, slash arguments, quoted file mentions, token boundaries, and inline contexts.
Slash command completion primitives
src/pythinker_code/ui/shell/prompting/completion/slash.py
Adds slash matching, cancellation, highlighting, autosuggestions, ranked completions, and task-state metadata.
Async workspace indexing and file mentions
src/pythinker_code/ui/shell/prompting/completion/workspace.py
Adds generation-scoped snapshots, bounded filesystem/Git discovery, asynchronous refreshes, and ranked quoted file completions.
Completion package and prompt integration
src/pythinker_code/ui/shell/prompting/completion/__init__.py, src/pythinker_code/ui/shell/prompt.py
Exports completion primitives and wires workspace indexing, file mentions, publish refreshes, slash parsing, and cwd synchronization into the prompt session.
Context and highlighting validation
tests/ui_and_conv/test_slash_completer.py, tests/ui_and_conv/test_slash_highlight.py
Tests slash context parsing, argument boundaries, mid-token rejection, cancellation, and quoted or punctuation-adjacent mentions.
File completion behavior validation
tests/ui_and_conv/test_file_completer.py, tests/ui_and_conv/_prompt_lifecycle.py
Converts tests to asynchronous index-backed helpers and covers limits, guards, quoting, Unicode paths, traversal, ranking, filtering, and renames.
Workspace index lifecycle validation
tests/ui_and_conv/test_workspace_index.py
Adds fake-host tests for generation ordering, cancellation, fallback, bounds, filtering, snapshot reads, ranking, and publish timing.
Release notes and typo configuration
CHANGELOG.md, pyproject.toml
Documents the completion changes and allows a test fixture word in typo checks.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Prompt as prompt.py
  participant Completer as HostFileMentionCompleter
  participant Index as WorkspaceIndex
  participant Host as Host/Git
  Prompt->>Completer: request file completions
  Completer->>Index: request_refresh(fragment)
  Index->>Host: discover workspace paths
  Host-->>Index: return indexed entries
  Index-->>Completer: publish WorkspaceSnapshot
  Completer-->>Prompt: return ranked candidates
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description has useful detail, but it does not follow the required template and is missing the linked issue and checklist sections. Add the required Related Issue and Checklist sections, including the issue link and all checklist items, and format the description to match the template.
Docstring Coverage ⚠️ Warning Docstring coverage is 18.95% which is insufficient. The required threshold is 70.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title follows the conventional commits format and accurately summarizes the refactor.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/tui-prompt-deep-module-pr3

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

@elkaix
elkaix force-pushed the refactor/tui-prompt-deep-module-pr2 branch from 6d842fb to b8d8ec1 Compare July 20, 2026 17:50
@elkaix
elkaix force-pushed the refactor/tui-prompt-deep-module-pr3 branch from 3baca3f to 14d58a5 Compare July 20, 2026 18:50
@elkaix
elkaix changed the base branch from refactor/tui-prompt-deep-module-pr2 to main July 20, 2026 18:50
@elkaix elkaix closed this Jul 20, 2026
@elkaix elkaix reopened this Jul 20, 2026
@elkaix

elkaix commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.


You're currently rate limited under our Fair Usage Limits Policy. Your recent PR review activity is in the 95th percentile or higher among CodeRabbit users, so adaptive limits apply. Your next review will be available in 17 minutes.

@elkaix

elkaix commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@elkaix

elkaix commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@elkaix

elkaix commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pythinker_code/ui/shell/prompt.py`:
- Around line 3687-3694: Guard the HostPath.cwd() call in _prompt_once with the
same OSError handling used by the constructor, preserving the existing
workspace-root comparison and workspace_index.set_root behavior when the lookup
succeeds.
- Around line 1784-1796: Guard the HostPath.cwd() call in the shell
initialization flow around _workspace_root and handle OSError using the file’s
established fallback behavior from _render_bottom_toolbar or
_build_statusline_context. Apply the same required protection to the related
_prompt_once occurrence, preserving workspace setup when the current directory
is available.

In `@src/pythinker_code/ui/shell/prompting/completion/context.py`:
- Around line 205-212: Collapse the redundant conditional in the surrounding
completion logic to a single `if slash.kind is CompletionKind.SLASH_ARGUMENT`
branch that returns `slash`. Remove the unused `argument_commands` and
`argument_index` checks from this branch, leaving any actual argument-completion
gating to `SlashCommandCompleter.get_completions`.

In `@src/pythinker_code/ui/shell/prompting/completion/workspace.py`:
- Around line 102-136: Reorder the logic in request_refresh so the current
snapshot freshness check for the new key runs before cancelling any in-flight
_refresh_task. Return without cancellation when the requested key is already
fresh; only cancel an existing task after determining that new refresh work must
be scheduled, while preserving the same-key deduplication behavior.

In `@tests/ui_and_conv/test_workspace_index.py`:
- Around line 137-139: Update _InspectableWorkspaceIndex.degraded and
WorkspaceIndex to expose degraded status through a public method or property, or
through snapshot()’s returned value, instead of reading the private _snapshots
dictionary. Ensure the test asserts the observable degraded behavior without
assuming a single populated snapshot key.
- Around line 24-38: Move the superset _Lifecycle test double, including its
created-task tracking and drain() method, from
tests/ui_and_conv/test_workspace_index.py lines 24-38 into shared test support
such as tests/ui_and_conv/conftest.py; update test_workspace_index.py to use
that shared definition. Remove the duplicate _Lifecycle from
tests/ui_and_conv/test_file_completer.py lines 27-36 and import the shared
_Lifecycle there.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 92d8b6a8-54cb-4985-8da4-684f826d44b5

📥 Commits

Reviewing files that changed from the base of the PR and between 30f7b17 and 14d58a5.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • pyproject.toml
  • src/pythinker_code/ui/shell/prompt.py
  • src/pythinker_code/ui/shell/prompting/completion/__init__.py
  • src/pythinker_code/ui/shell/prompting/completion/context.py
  • src/pythinker_code/ui/shell/prompting/completion/slash.py
  • src/pythinker_code/ui/shell/prompting/completion/workspace.py
  • tests/ui_and_conv/test_file_completer.py
  • tests/ui_and_conv/test_slash_completer.py
  • tests/ui_and_conv/test_slash_highlight.py
  • tests/ui_and_conv/test_workspace_index.py

Comment thread src/pythinker_code/ui/shell/prompt.py
Comment thread src/pythinker_code/ui/shell/prompt.py
Comment thread src/pythinker_code/ui/shell/prompting/completion/context.py Outdated
Comment thread src/pythinker_code/ui/shell/prompting/completion/workspace.py
Comment thread tests/ui_and_conv/test_workspace_index.py Outdated
Comment thread tests/ui_and_conv/test_workspace_index.py Outdated
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.


You're currently rate limited under our Fair Usage Limits Policy. Your recent PR review activity is in the 95th percentile or higher among CodeRabbit users, so adaptive limits apply. Your next review will be available in 17 minutes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pythinker_code/ui/shell/prompting/completion/slash.py`:
- Around line 55-73: Update discard_slash_command to also handle SLASH_ARGUMENT
when argument_index is 0, preserving the existing slash-command buffer removal
behavior for both completion kinds. Continue returning False for other
completion contexts so the fallback behavior remains available where
appropriate.

In `@src/pythinker_code/ui/shell/prompting/completion/workspace.py`:
- Around line 486-522: Update the ranking logic in _rank to compare each
candidate basename with the basename portion of the typed fragment, not the full
fragment including directory prefixes. Preserve the existing startswith,
contains, and fallback category ordering and test_penalty behavior for both
scoped and unscoped completions.
- Around line 409-441: Update _run_git so process.stdout and process.stderr are
drained concurrently rather than sequentially, ensuring verbose Git output
cannot deadlock on a full pipe. Preserve the existing bounded-read behavior,
timeout and process-stop handling, return-code check, and stdout decoding.

In `@tests/ui_and_conv/test_slash_completer.py`:
- Around line 226-247: Correct
test_completion_context_reports_first_and_later_arguments so escaped_whitespace
validates the intended escape behavior instead of merely asserting a second
argument: either assert that the backslash-escaped space remains one token with
the appropriate argument index, or use ordinary whitespace for later-argument
coverage and add a separate explicit malformed/unsupported escape case. Keep the
existing first-argument and later-argument assertions aligned with the contract.

In `@tests/ui_and_conv/test_slash_highlight.py`:
- Around line 132-134: Update
test_mention_after_allowed_punctuation_is_highlighted and the shared mention
parser so trailing sentence punctuation is excluded from file-mention spans.
Ensure _mentions(_lex_line("read (`@src/main.py`), now")) returns only
"`@src/main.py`", with completion matching operating on the punctuation-free path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 056fa868-42a9-436f-8e9f-776fb959b26b

📥 Commits

Reviewing files that changed from the base of the PR and between 30f7b17 and 14d58a5.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • pyproject.toml
  • src/pythinker_code/ui/shell/prompt.py
  • src/pythinker_code/ui/shell/prompting/completion/__init__.py
  • src/pythinker_code/ui/shell/prompting/completion/context.py
  • src/pythinker_code/ui/shell/prompting/completion/slash.py
  • src/pythinker_code/ui/shell/prompting/completion/workspace.py
  • tests/ui_and_conv/test_file_completer.py
  • tests/ui_and_conv/test_slash_completer.py
  • tests/ui_and_conv/test_slash_highlight.py
  • tests/ui_and_conv/test_workspace_index.py

Comment thread src/pythinker_code/ui/shell/prompting/completion/slash.py
Comment thread src/pythinker_code/ui/shell/prompting/completion/workspace.py
Comment thread src/pythinker_code/ui/shell/prompting/completion/workspace.py
Comment thread tests/ui_and_conv/test_slash_completer.py
Comment thread tests/ui_and_conv/test_slash_highlight.py
- Escape now dismisses the slash-argument menu (cancel_completion fallback)
  instead of doing nothing when there is no draft command to strip.
- Strip trailing prose punctuation from unquoted @-mention highlight spans.
- Rank scoped @dir/name completions by the fragment's basename so prefix
  priority applies to path-scoped fragments.
- Drain git stdout/stderr concurrently to avoid a pipe deadlock on verbose
  output.
- Guard the mid-session HostPath.cwd() lookup so a removed working directory
  no longer crashes the prompt turn.
- Collapse a dead SLASH_ARGUMENT branch in the completion parser.
- De-duplicate the PromptLifecycle test double, make the degraded-state test
  helper multi-key-safe, and re-frame the backslash-whitespace parser test to
  document the real (non-escape-aware) contract.
Comment thread tests/ui_and_conv/test_workspace_index.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
src/pythinker_code/ui/shell/prompt.py (2)

3683-3692: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Only refresh an already-open file-completion menu.

This callback checks whether the buffer resembles an @ mention, but not whether completion is currently active. After Escape dismisses @... completion, a later snapshot publish calls start_completion() and reopens the menu unexpectedly. Require an existing complete_state before restarting completion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pythinker_code/ui/shell/prompt.py` around lines 3683 - 3692, Update
_on_workspace_snapshot_published to return unless the buffer already has an
active complete_state, in addition to the existing app-running and
HostFileMentionCompleter checks. Only call start_completion and app.invalidate
when an existing file-completion menu is still open.

1785-1791: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Close the workspace index on synchronous context exit.

The closer is registered with PromptLifecycle, but __exit__ at Line 3394 only cancels the status task and statusline runner; it does not invoke lifecycle shutdown, unlike __aexit__. A with CustomPromptSession() caller can therefore leave workspace discovery tasks or processes running. Add a synchronous cancellation/close path for this resource.

As per coding guidelines, background workers, tasks, and processes must define lifecycle, cancellation, and shutdown.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pythinker_code/ui/shell/prompt.py` around lines 1785 - 1791, Update the
synchronous __exit__ path in CustomPromptSession to invoke the PromptLifecycle
shutdown/close operation, ensuring the registered workspace index closer
(self._workspace_index.aclose) runs during with-block exit. Preserve the
existing status-task and statusline cleanup, and align the synchronous behavior
with __aexit__ without changing asynchronous lifecycle handling.

Source: Coding guidelines

tests/ui_and_conv/test_file_completer.py (1)

24-42: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Close the lifecycle in a finally block.

If refresh or completion collection raises, await lifecycle.aclose() is skipped, leaving lifecycle-owned tasks alive and potentially contaminating later tests. Wrap the helper body in try/finally and close the lifecycle unconditionally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ui_and_conv/test_file_completer.py` around lines 24 - 42, Update the
_completion_results helper to wrap workspace setup, refresh, and completion
collection in a try/finally block, moving lifecycle.aclose() into the finally
section so it runs even when an earlier operation raises. Preserve the existing
completion return behavior on success.
src/pythinker_code/ui/shell/prompting/completion/workspace.py (3)

183-187: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not log absolute workspace paths or raw exception details.

These logs interpolate root and exc; other messages in this module also log paths, command arguments, and user-derived scopes. Workspace paths commonly contain usernames or project-sensitive names. Redact them and log stable identifiers plus categorized exception types instead.

As per coding guidelines, logs must never leak PII, tokens, secrets, or unsafe diagnostic details.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pythinker_code/ui/shell/prompting/completion/workspace.py` around lines
183 - 187, Update the warning log in the workspace refresh failure path to avoid
interpolating the absolute root or raw exception. Replace them with a stable,
redacted workspace identifier and a safe categorized exception type, and audit
nearby logging in the same module for paths, command arguments, or user-derived
scopes that require the same redaction.

Source: Coding guidelines


95-100: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Expose degraded and unavailable snapshot states.

snapshot() collapses “not indexed,” degraded/partial, and empty workspaces into the same empty tuple; task-scheduling failure also returns without publishing a status. HostFileMentionCompleter therefore presents incomplete results as ordinary “no matches.” Return or expose a status-bearing snapshot so callers can distinguish pending, failed, degraded, and successful results.

As per coding guidelines, observable results must distinguish success, failure, degraded output, and no data.

Also applies to: 127-132

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pythinker_code/ui/shell/prompting/completion/workspace.py` around lines
95 - 100, Update Workspace.snapshot to return a status-bearing result that
distinguishes pending, unavailable/failed, degraded, successful, and genuinely
empty snapshots instead of collapsing them to an empty tuple. Preserve immutable
entries for successful results, and publish an explicit failure/unavailable
status when snapshot task scheduling fails. Update HostFileMentionCompleter and
other snapshot consumers to handle these states without presenting incomplete or
unavailable results as ordinary “no matches.”

Source: Coding guidelines


158-166: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Reject Windows path escapes in workspace discovery.
_snapshot_key() only checks /-delimited .., so Windows fragments like a\\..\\..\\secret/x or C:\\secret/x can still reach root.joinpath(...) in _discover_bounded() and turn into an escaping or absolute path. Normalize both separators, reject drive-qualified/absolute scopes, and enforce containment before bounded traversal and git ls-files. Add a Windows regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pythinker_code/ui/shell/prompting/completion/workspace.py` around lines
158 - 166, Update _snapshot_key() to normalize backslashes as path separators,
reject Windows drive-qualified and absolute scopes, and reject any normalized
scope containing parent traversal. In _discover_bounded(), enforce that the
resolved traversal path remains contained within the workspace root before
performing bounded traversal or git ls-files. Add a regression test covering
Windows-style traversal and drive-qualified fragments.
♻️ Duplicate comments (1)
src/pythinker_code/ui/shell/prompt.py (1)

1784-1791: 🩺 Stability & Availability | 🟠 Major

Guard all constructor HostPath.cwd() calls.

HostPath.cwd() can still raise here, and the constructor already calls it unguarded at Line 1706 for history hashing. If the working directory disappears, construction fails before _prompt_once() can apply its new guard. Resolve the root once with the established CwdLostError/OSError handling and reuse it for both history and workspace setup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pythinker_code/ui/shell/prompt.py` around lines 1784 - 1791, Update the
constructor’s existing HostPath.cwd() usage, including the history-hashing setup
and the workspace initialization around _workspace_root, to resolve the current
directory once inside the established CwdLostError/OSError handling. Reuse that
resolved root for both history and WorkspaceIndex construction so no constructor
path calls HostPath.cwd() unguarded.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pythinker_code/ui/shell/prompting/completion/workspace.py`:
- Around line 417-420: Update the process stream-reading flow around
_read_bounded and asyncio.gather to create explicit stdout and stderr tasks, and
on either reader’s failure cancel and await both tasks before completing
cleanup, while preserving process termination. Add a regression test covering an
exception from one reader and verifying the sibling reader is cancelled and
awaited.

---

Outside diff comments:
In `@src/pythinker_code/ui/shell/prompt.py`:
- Around line 3683-3692: Update _on_workspace_snapshot_published to return
unless the buffer already has an active complete_state, in addition to the
existing app-running and HostFileMentionCompleter checks. Only call
start_completion and app.invalidate when an existing file-completion menu is
still open.
- Around line 1785-1791: Update the synchronous __exit__ path in
CustomPromptSession to invoke the PromptLifecycle shutdown/close operation,
ensuring the registered workspace index closer (self._workspace_index.aclose)
runs during with-block exit. Preserve the existing status-task and statusline
cleanup, and align the synchronous behavior with __aexit__ without changing
asynchronous lifecycle handling.

In `@src/pythinker_code/ui/shell/prompting/completion/workspace.py`:
- Around line 183-187: Update the warning log in the workspace refresh failure
path to avoid interpolating the absolute root or raw exception. Replace them
with a stable, redacted workspace identifier and a safe categorized exception
type, and audit nearby logging in the same module for paths, command arguments,
or user-derived scopes that require the same redaction.
- Around line 95-100: Update Workspace.snapshot to return a status-bearing
result that distinguishes pending, unavailable/failed, degraded, successful, and
genuinely empty snapshots instead of collapsing them to an empty tuple. Preserve
immutable entries for successful results, and publish an explicit
failure/unavailable status when snapshot task scheduling fails. Update
HostFileMentionCompleter and other snapshot consumers to handle these states
without presenting incomplete or unavailable results as ordinary “no matches.”
- Around line 158-166: Update _snapshot_key() to normalize backslashes as path
separators, reject Windows drive-qualified and absolute scopes, and reject any
normalized scope containing parent traversal. In _discover_bounded(), enforce
that the resolved traversal path remains contained within the workspace root
before performing bounded traversal or git ls-files. Add a regression test
covering Windows-style traversal and drive-qualified fragments.

In `@tests/ui_and_conv/test_file_completer.py`:
- Around line 24-42: Update the _completion_results helper to wrap workspace
setup, refresh, and completion collection in a try/finally block, moving
lifecycle.aclose() into the finally section so it runs even when an earlier
operation raises. Preserve the existing completion return behavior on success.

---

Duplicate comments:
In `@src/pythinker_code/ui/shell/prompt.py`:
- Around line 1784-1791: Update the constructor’s existing HostPath.cwd() usage,
including the history-hashing setup and the workspace initialization around
_workspace_root, to resolve the current directory once inside the established
CwdLostError/OSError handling. Reuse that resolved root for both history and
WorkspaceIndex construction so no constructor path calls HostPath.cwd()
unguarded.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7e603fa5-06ac-4a78-ae46-55d23960a736

📥 Commits

Reviewing files that changed from the base of the PR and between 14d58a5 and 42179a6.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • src/pythinker_code/ui/shell/prompt.py
  • src/pythinker_code/ui/shell/prompting/completion/context.py
  • src/pythinker_code/ui/shell/prompting/completion/workspace.py
  • tests/ui_and_conv/_prompt_lifecycle.py
  • tests/ui_and_conv/test_file_completer.py
  • tests/ui_and_conv/test_slash_completer.py
  • tests/ui_and_conv/test_slash_highlight.py
  • tests/ui_and_conv/test_workspace_index.py

Comment thread src/pythinker_code/ui/shell/prompting/completion/workspace.py Outdated
The concurrent stdout/stderr drain in _run_git used asyncio.gather, which
leaves the sibling _read_bounded coroutine running after the first reader
raises — only the process was killed, so the other reader could keep draining
a closing pipe. Move the concurrent read into _read_streams, which uses
explicit tasks and cancels+awaits both readers on any failure (or cancellation)
before propagating. Adds a regression test for a reader-exception path.
@elkaix
elkaix merged commit c0b7aee into main Jul 21, 2026
60 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant