Skip to content

Allow creating kindra temp worktrees#5

Merged
Pajn merged 2 commits into
mainfrom
kindra-integration
Jul 2, 2026
Merged

Allow creating kindra temp worktrees#5
Pajn merged 2 commits into
mainfrom
kindra-integration

Conversation

@Pajn

@Pajn Pajn commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added Kindra-powered “create temporary worktree” support in the worktree picker for configured repositories, including branch slug validation/normalization and a new “create” option shown with a + marker.
    • Extended picker behavior to detect Kindra temp worktrees and require confirmation before safe removal.
  • Bug Fixes
    • Improved “trunk” branch resolution using the remote default when available, with sensible main/master fallbacks.
  • Documentation
    • Updated documentation to cover the Kindra temp-worktree picker UX and configuration.
  • Tests
    • Added integration and picker-flow coverage for Kindra temp-worktree detection, creation, and invalid branch handling.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new wisp-kindra crate, wires it into workspace and picker flows, adds temporary worktree creation and removal support, updates the UI and README, and adjusts scripts/release.sh for the excluded wisp-embers manifest.

Changes

Kindra Temp Worktree Feature

Layer / File(s) Summary
Workspace and crate wiring
Cargo.toml, crates/wisp-bin/Cargo.toml, crates/wisp-kindra/Cargo.toml
Registers wisp-kindra in the workspace and adds it as a dependency of wisp-bin, with a new crate manifest.
Kindra provider and config parsing
crates/wisp-kindra/src/lib.rs, crates/wisp-kindra/tests/integration.rs
Defines KindraProvider, CommandKindraProvider, KindraError, config and metadata parsing, stdout path parsing, and unit and integration tests.
Trunk branch resolution
crates/wisp-bin/src/git.rs
Adds helpers to resolve the repository trunk branch from origin/HEAD or local main/master.
CreateTempWorktree row kind and marker
crates/wisp-core/src/view.rs, crates/wisp-ui/src/lib.rs
Adds SessionListItemKind::CreateTempWorktree and renders it with a + marker.
Picker flow and temp worktree creation
crates/wisp-bin/src/main.rs
Adds Kindra context and branch normalization, wires selection activation to create temp worktrees and sessions, appends the new row in worktree mode, adds delete confirmation flow, updates preview rendering, and tests the Kindra path.
README documentation
README.md
Documents the Kindra integration, workspace entry, requirements note, and worktree-mode behavior.

Release Script wisp-embers Version Bump

Layer / File(s) Summary
Embers manifest bump and lockfile refresh
scripts/release.sh
Adds the wisp-embers manifest path, rewrites version updates, bumps the standalone package version, and refreshes its lockfile metadata.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant run_surface
  participant KindraProvider
  participant Session

  User->>run_surface: types in worktree mode
  run_surface->>KindraProvider: temp_worktrees_configured(repo_root)
  KindraProvider-->>run_surface: configured
  run_surface->>run_surface: append CreateTempWorktree row
  User->>run_surface: selects CreateTempWorktree
  run_surface->>KindraProvider: create_temp_worktree(repo_root, branch, trunk)
  KindraProvider-->>run_surface: worktree path
  run_surface->>Session: create/switch session

  User->>run_surface: closes session-less temp worktree
  run_surface->>KindraProvider: remove_temp_worktree(repo_root, branch)
  KindraProvider-->>run_surface: removed
Loading

Possibly related PRs

  • Pajn/wisp#2: This PR extends the picker/session-list flow with Kindra-backed CreateTempWorktree items and activation handling.
  • Pajn/wisp#3: Both PRs add new SessionListItemKind handling and matching UI rendering in the picker stack.

Poem

I hop through branches, bright and keen,
A tiny + where sprouts are seen.
Kindra hums, the trunk is found,
New worktrees bloom from picker ground.
Thump! The session hops along,
While release scripts keep versions strong. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding support for creating Kindra temporary worktrees.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Pajn

Pajn commented Jul 1, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
✅ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
scripts/release.sh (1)

82-131: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate version-bump logic between workspace and embers heredocs.

The new embers python block (Lines 110-131) mirrors the existing workspace-bump block (Lines 82-108) almost line-for-line — same regex-and-write pattern, differing only in section header, expected match count, and error message. Consider extracting a shared helper (bash function or a single parameterized python script) that takes (file_path, section_header, exact_match_required) to avoid future drift between the two blocks.

♻️ Example consolidation approach
-  python3 - "${CARGO_TOML}" "${version}" <<'PY'
-import pathlib
-import re
-import sys
-
-path = pathlib.Path(sys.argv[1])
-version = sys.argv[2]
-text = path.read_text()
-updated, count = re.subn(
-    r'(?ms)^(\[workspace\.package\]\n.*?^version = ")([^"]+)(")$',
-    rf'\g<1>{version}\3',
-    text,
-    count=1,
-)
-if count != 1:
-    raise SystemExit("failed to update workspace.package.version")
-
-updated, count = re.subn(
-    r'(?m)^((?:wisp(?:-[a-z]+)?) = \{ version = ")([^"]+)(", path = "crates/[^"]+" \})$',
-    rf'\g<1>{version}\3',
-    updated,
-)
-if count == 0:
-    raise SystemExit("failed to update internal workspace dependency versions")
-
-path.write_text(updated)
-PY
-
-  python3 - "${EMBERS_CARGO_TOML}" "${version}" <<'PY'
-import pathlib
-import re
-import sys
-
-path = pathlib.Path(sys.argv[1])
-version = sys.argv[2]
-text = path.read_text()
-updated, count = re.subn(
-    r'(?ms)^(\[package\]\n.*?^version = ")([^"]+)(")$',
-    rf'\g<1>{version}\3',
-    text,
-    count=1,
-)
-if count != 1:
-    raise SystemExit("failed to update wisp-embers package version")
-
-path.write_text(updated)
-PY
+  python3 - "${CARGO_TOML}" "${EMBERS_CARGO_TOML}" "${version}" <<'PY'
+import pathlib
+import re
+import sys
+
+def bump_section(path, header, version, expected):
+    text = path.read_text()
+    updated, count = re.subn(
+        rf'(?ms)^(\{header}\n.*?^version = ")([^"]+)(")$',
+        rf'\g<1>{version}\3',
+        text,
+        count=1,
+    )
+    if count != expected:
+        raise SystemExit(f"failed to update {header} version in {path}")
+    path.write_text(updated)
+
+cargo_toml, embers_toml, version = pathlib.Path(sys.argv[1]), pathlib.Path(sys.argv[2]), sys.argv[3]
+bump_section(cargo_toml, r"\[workspace\.package\]", version, 1)
+
+text = cargo_toml.read_text()
+updated, count = re.subn(
+    r'(?m)^((?:wisp(?:-[a-z]+)?) = \{ version = ")([^"]+)(", path = "crates/[^"]+" \})$',
+    rf'\g<1>{version}\3',
+    text,
+)
+if count == 0:
+    raise SystemExit("failed to update internal workspace dependency versions")
+cargo_toml.write_text(updated)
+
+bump_section(embers_toml, r"\[package\]", version, 1)
+PY
🤖 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 `@scripts/release.sh` around lines 82 - 131, The release script has duplicated
version-update logic in the workspace and embers heredoc blocks, so consolidate
the repeated regex/read/write flow into a single shared helper or parameterized
Python script. Refactor the logic around the existing version bump steps in the
release script so one reusable path handles both the workspace.package update
and the wisp-embers package update, with parameters for the target file, section
header, and required match count to keep the behavior aligned.
🤖 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 `@crates/wisp-bin/src/main.rs`:
- Around line 1079-1086: The temporary branch name helper only normalizes
whitespace, so invalid git branch names can still reach the temp-worktree flow
and fail later. Update kindra_temp_branch_name, or add a guard before
create_temp_worktree, to validate the generated branch name against git branch
rules and return None/reject the action for names like fix: crash, foo..bar, or
foo.lock before constructing the CreateTempWorktree row.

In `@crates/wisp-kindra/src/lib.rs`:
- Around line 122-138: The `Command::output()` error handling in `Kindra::run`
is misclassifying non-NotFound spawn failures as `KindraError::CommandFailed`,
even though no process stderr exists. Update the `map_err` branch in
`crates/wisp-kindra/src/lib.rs` to map IO errors other than
`ErrorKind::NotFound` to `KindraError::SpawnFailed` instead of `CommandFailed`,
and keep `CommandFailed` reserved for actual process execution failures with
real stderr from the command.

In `@README.md`:
- Line 90: Documentation for temporary worktree creation is out of sync with the
actual worktree mode behavior. Update the README section describing the Kindra
temp worktree flow so it states that the typed filter text is normalized to a
branch slug (whitespace becomes dashes) and that the trunk is resolved from the
remote default branch before falling back to main/master. Keep the wording
aligned with the worktree mode flow that appends the + row and runs kin wt temp,
but remove the claim that the raw filter text is used directly as the branch
name.

---

Outside diff comments:
In `@scripts/release.sh`:
- Around line 82-131: The release script has duplicated version-update logic in
the workspace and embers heredoc blocks, so consolidate the repeated
regex/read/write flow into a single shared helper or parameterized Python
script. Refactor the logic around the existing version bump steps in the release
script so one reusable path handles both the workspace.package update and the
wisp-embers package update, with parameters for the target file, section header,
and required match count to keep the behavior aligned.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 79f47d6d-5de5-4b01-a8bc-dbcac4d12b7b

📥 Commits

Reviewing files that changed from the base of the PR and between 2ff9557 and 79ec543.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • Cargo.toml
  • README.md
  • crates/wisp-bin/Cargo.toml
  • crates/wisp-bin/src/git.rs
  • crates/wisp-bin/src/main.rs
  • crates/wisp-core/src/view.rs
  • crates/wisp-kindra/Cargo.toml
  • crates/wisp-kindra/src/lib.rs
  • crates/wisp-kindra/tests/integration.rs
  • crates/wisp-ui/src/lib.rs
  • scripts/release.sh

Comment thread crates/wisp-bin/src/main.rs
Comment thread crates/wisp-kindra/src/lib.rs
Comment thread README.md Outdated
@Pajn Pajn force-pushed the kindra-integration branch from 79ec543 to 0165733 Compare July 1, 2026 21:56
@Pajn

Pajn commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
✅ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@crates/wisp-bin/src/main.rs`:
- Around line 1157-1172: The CreateTempWorktree activation path in
activate_filter_selection is recomputing the trunk instead of reusing the
already-resolved value from KindraTempContext.trunk built in run_surface. Thread
the cached trunk through the SessionListItem/command_hint (or add a dedicated
field) and use that value when calling kindra.create_temp_worktree, so the
preview and activation share the same trunk, avoid duplicate git::trunk_branch
calls, and make activate_filter_selection_creates_kindra_temp_worktree test the
real resolved branch instead of the fallback.

In `@crates/wisp-kindra/src/lib.rs`:
- Around line 66-91: Add a timeout to the Kindra subprocess calls so a stalled
git/kin process cannot block the flow forever. Update the process launch in
git_common_dir and create_temp_worktree to use a bounded wait or cancellable
worker pattern instead of unbounded Command::output(), and make sure the timeout
path returns a clean None/error through the same callers that currently handle
subprocess failure.
- Around line 101-120: The create_temp_worktree boundary currently only trims
whitespace, so dash-prefixed branch names like -foo can still be treated as
flags by the git/clap command path. Update create_temp_worktree in wisp-kindra
to reject branch names that start with -, alongside the existing empty check,
and return KindraError::InvalidBranch from that path so direct callers cannot
pass a misparsed branch name.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 26e573f9-91e7-4d82-8876-de71f39b80d5

📥 Commits

Reviewing files that changed from the base of the PR and between 79ec543 and 0165733.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • Cargo.toml
  • README.md
  • crates/wisp-bin/Cargo.toml
  • crates/wisp-bin/src/git.rs
  • crates/wisp-bin/src/main.rs
  • crates/wisp-core/src/view.rs
  • crates/wisp-kindra/Cargo.toml
  • crates/wisp-kindra/src/lib.rs
  • crates/wisp-kindra/tests/integration.rs
  • crates/wisp-ui/src/lib.rs
  • scripts/release.sh

Comment thread crates/wisp-bin/src/main.rs
Comment on lines +66 to +91
/// Resolves the absolute git common directory for `repo_root`, where shared
/// repository state (including `kindra.toml`) lives for linked worktrees.
fn git_common_dir(&self, repo_root: &Path) -> Option<PathBuf> {
let output = Command::new(&self.git_binary)
.current_dir(repo_root)
.args(["rev-parse", "--git-common-dir"])
.output()
.ok()?;
if !output.status.success() {
return None;
}

let raw = String::from_utf8_lossy(&output.stdout);
let trimmed = raw.trim();
if trimmed.is_empty() {
return None;
}

let common_dir = PathBuf::from(trimmed);
Some(if common_dir.is_absolute() {
common_dir
} else {
repo_root.join(common_dir)
})
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how KindraProvider methods are invoked in the picker/session flow,
# to confirm whether they run on a thread that can block the UI.
rg -n 'temp_worktrees_configured|create_temp_worktree' crates/wisp-bin/src/main.rs -B3 -A3

Repository: Pajn/wisp

Length of output: 1332


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant files, then inspect the call sites and implementations.
ast-grep outline crates/wisp-bin/src/main.rs --view expanded | sed -n '1,220p'
printf '\n==== lib.rs outline ====\n'
ast-grep outline crates/wisp-kindra/src/lib.rs --view expanded | sed -n '1,220p'

printf '\n==== main.rs around temp_worktrees_configured ====\n'
sed -n '1620,1685p' crates/wisp-bin/src/main.rs

printf '\n==== main.rs around create_temp_worktree ====\n'
sed -n '1140,1185p' crates/wisp-bin/src/main.rs

printf '\n==== wisp-kindra lib.rs around subprocess calls ====\n'
sed -n '1,180p' crates/wisp-kindra/src/lib.rs

Repository: Pajn/wisp

Length of output: 21998


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for existing timeout/cancellation patterns and other synchronous subprocess usage.
rg -n '\btimeout\b|poll\(|try_wait\(|wait_timeout|Command::output\(|Command::spawn\(' crates -g '!**/target/**'

printf '\n==== wisp-kindra tests and helpers ====\n'
sed -n '230,320p' crates/wisp-kindra/src/lib.rs

Repository: Pajn/wisp

Length of output: 2516


Add a timeout around Kindra subprocesses git_common_dir and create_temp_worktree call Command::output() with no deadline, so a stalled git/kin process can block the picker/session-activation flow indefinitely. Consider a bounded wait or moving the call to a cancellable worker thread.

🤖 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 `@crates/wisp-kindra/src/lib.rs` around lines 66 - 91, Add a timeout to the
Kindra subprocess calls so a stalled git/kin process cannot block the flow
forever. Update the process launch in git_common_dir and create_temp_worktree to
use a bounded wait or cancellable worker pattern instead of unbounded
Command::output(), and make sure the timeout path returns a clean None/error
through the same callers that currently handle subprocess failure.

Comment thread crates/wisp-kindra/src/lib.rs
@Pajn Pajn force-pushed the kindra-integration branch from 0165733 to eff1b16 Compare July 2, 2026 13:17
@Pajn

Pajn commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
✅ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@crates/wisp-bin/src/git.rs`:
- Around line 220-232: The local_branch_exists helper is using
Command::status(), which inherits git stdout/stderr and can corrupt the picker
UI in raw mode. Update local_branch_exists in git.rs to capture child output the
same way remote_default_branch does, so any git warnings or hints are swallowed
instead of written directly to the terminal, while keeping the success/failure
check based on the exit status.

In `@crates/wisp-kindra/tests/integration.rs`:
- Around line 9-20: The integration tests still use a manual unique temp
directory helper, which can leave artifacts behind on panic. Replace the
`unique_root`/`UNIQUE_ROOT_COUNTER` pattern in `integration.rs` with
`tempfile::TempDir` so cleanup happens automatically via `Drop`, and update the
tests that currently take `&root` to use `root.path()` instead. Also remove any
explicit `remove_dir_all` cleanup at the end of the affected tests since
`TempDir` should own the lifecycle.

In `@scripts/release.sh`:
- Around line 124-137: The internal workspace dependency bump in the release
script only checks for a non-zero regex match, so it can partially update
Cargo.toml without failing. Update the logic around the Python re.subn block in
scripts/release.sh to assert the full expected number of wisp path-dependency
matches, similar to bump_section_version, so any layout mismatch or missed
dependency causes the release to fail instead of silently leaving an old version
behind.
- Around line 151-154: The release script is hardcoding the wisp-embers manifest
path instead of reusing the existing EMBERS_CARGO_TOML variable. Update the
cargo metadata step in scripts/release.sh to reference EMBERS_CARGO_TOML
consistently so the path stays centralized and does not drift if the crate
location changes.
- Around line 79-115: The section-scoped matcher in bump_section_version can run
past the intended TOML section and update a later version field instead of
stopping at the next header. Tighten the regex in bump_section_version so it
only searches within the target section body and explicitly stops before the
next [section] line. Use the existing bump_section_version helper and its call
sites in bump_workspace_versions as the place to apply the fix, and keep the
expected match count check so layout drift still fails the release.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e72d258d-f14d-45da-a969-1727eeea2c83

📥 Commits

Reviewing files that changed from the base of the PR and between 0165733 and eff1b16.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • Cargo.toml
  • README.md
  • crates/wisp-bin/Cargo.toml
  • crates/wisp-bin/src/git.rs
  • crates/wisp-bin/src/main.rs
  • crates/wisp-core/src/view.rs
  • crates/wisp-kindra/Cargo.toml
  • crates/wisp-kindra/src/lib.rs
  • crates/wisp-kindra/tests/integration.rs
  • crates/wisp-ui/src/lib.rs
  • scripts/release.sh

Comment thread crates/wisp-bin/src/git.rs
Comment on lines +9 to +20
static UNIQUE_ROOT_COUNTER: AtomicU64 = AtomicU64::new(0);

fn unique_root() -> PathBuf {
let dir = std::env::temp_dir().join(format!(
"wisp-kindra-it-{}-{}",
std::process::id(),
UNIQUE_ROOT_COUNTER.fetch_add(1, Ordering::SeqCst)
));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).expect("create temp root");
dir
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider tempfile for automatic cleanup on test failure.

The manual unique_root/remove_dir_all pattern leaves directories behind if an assertion panics mid-test. The tempfile crate's TempDir cleans up via Drop even on panic, removing the need for manual counters and explicit removal calls.

♻️ Suggested refactor
-static UNIQUE_ROOT_COUNTER: AtomicU64 = AtomicU64::new(0);
-
-fn unique_root() -> PathBuf {
-    let dir = std::env::temp_dir().join(format!(
-        "wisp-kindra-it-{}-{}",
-        std::process::id(),
-        UNIQUE_ROOT_COUNTER.fetch_add(1, Ordering::SeqCst)
-    ));
-    let _ = std::fs::remove_dir_all(&dir);
-    std::fs::create_dir_all(&dir).expect("create temp root");
-    dir
-}
+fn unique_root() -> tempfile::TempDir {
+    tempfile::tempdir().expect("create temp root")
+}

Callers would use root.path() instead of &root, and drop the manual remove_dir_all(&root) cleanup calls at the end of each 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 `@crates/wisp-kindra/tests/integration.rs` around lines 9 - 20, The integration
tests still use a manual unique temp directory helper, which can leave artifacts
behind on panic. Replace the `unique_root`/`UNIQUE_ROOT_COUNTER` pattern in
`integration.rs` with `tempfile::TempDir` so cleanup happens automatically via
`Drop`, and update the tests that currently take `&root` to use `root.path()`
instead. Also remove any explicit `remove_dir_all` cleanup at the end of the
affected tests since `TempDir` should own the lifecycle.

Comment thread scripts/release.sh
Comment thread scripts/release.sh
Comment thread scripts/release.sh Outdated
@Pajn Pajn force-pushed the kindra-integration branch from eff1b16 to e300261 Compare July 2, 2026 18:37
@Pajn

Pajn commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
✅ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
crates/wisp-kindra/src/lib.rs (1)

76-98: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Still no timeout on kin/git subprocess calls.

git_common_dir and run_kin both call Command::output() with no deadline. A stalled git/kin process (e.g. hung network prompt, lock contention) will block the picker/session-activation flow indefinitely. This was already flagged on a previous commit and remains unaddressed.

Also applies to: 172-211

🤖 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 `@crates/wisp-kindra/src/lib.rs` around lines 76 - 98, `git_common_dir` still
shells out with an unbounded `Command::output()`, so stalled `git`/`kin`
subprocesses can block the flow indefinitely. Update both `git_common_dir` and
`run_kin` to use a timeout-aware process execution path (for example via a
helper that kills the child after a deadline) and handle timeout errors cleanly
by returning `None`/propagating an error. Use the existing `git_common_dir` and
`run_kin` symbols to locate the two subprocess call sites and apply the same
deadline behavior consistently.
🤖 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 `@crates/wisp-bin/src/git.rs`:
- Around line 199-234: The git subprocesses used by remote_default_branch and
local_branch_exists do not have any timeout, so a stalled git command can block
trunk_branch and the picker UI indefinitely. Update these helpers to run git
through the project’s existing timeout-aware subprocess pattern (the same
approach used elsewhere for wisp-kindra), and have trunk_branch propagate or
handle timeout failures cleanly when calling remote_default_branch and
local_branch_exists.

In `@crates/wisp-bin/src/main.rs`:
- Around line 2102-2134: The ConfirmDeleteWorktree branch in run_surface
currently uses `kindra.remove_temp_worktree(&repo_root, &branch)?`, which exits
the whole picker on an expected deletion failure. Change this arm to handle the
error inline: call `remove_temp_worktree` without propagating, surface the
failure through the existing in-app error reporting path used by the
picker/sidebar, and then return the user to the filter view while preserving the
current query/selection state. Use the `InputMode::ConfirmDeleteWorktree` match
arm, `run_surface`, and `kindra.remove_temp_worktree` to locate the change.

---

Duplicate comments:
In `@crates/wisp-kindra/src/lib.rs`:
- Around line 76-98: `git_common_dir` still shells out with an unbounded
`Command::output()`, so stalled `git`/`kin` subprocesses can block the flow
indefinitely. Update both `git_common_dir` and `run_kin` to use a timeout-aware
process execution path (for example via a helper that kills the child after a
deadline) and handle timeout errors cleanly by returning `None`/propagating an
error. Use the existing `git_common_dir` and `run_kin` symbols to locate the two
subprocess call sites and apply the same deadline behavior consistently.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: bb75f74e-dd5f-46bb-9590-ff66b62867c9

📥 Commits

Reviewing files that changed from the base of the PR and between eff1b16 and e300261.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • Cargo.toml
  • README.md
  • crates/wisp-bin/Cargo.toml
  • crates/wisp-bin/src/git.rs
  • crates/wisp-bin/src/main.rs
  • crates/wisp-core/src/view.rs
  • crates/wisp-kindra/Cargo.toml
  • crates/wisp-kindra/src/lib.rs
  • crates/wisp-kindra/tests/integration.rs
  • crates/wisp-ui/src/lib.rs
  • scripts/release.sh

Comment on lines +199 to +234
fn remote_default_branch(repo_root: &Path) -> Option<String> {
let output = Command::new("git")
.current_dir(repo_root)
.args(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"])
.output()
.ok()?;
if !output.status.success() {
return None;
}

let raw = String::from_utf8_lossy(&output.stdout);
let trimmed = raw.trim();
// `origin/main` -> `main`
let branch = trimmed.strip_prefix("origin/").unwrap_or(trimmed);
if branch.is_empty() {
None
} else {
Some(branch.to_string())
}
}

fn local_branch_exists(repo_root: &Path, branch: &str) -> bool {
// Capture output rather than inheriting the terminal: `status()` would let any
// git warning/hint leak onto the raw-mode picker and corrupt the display.
Command::new("git")
.current_dir(repo_root)
.args([
"show-ref",
"--verify",
"--quiet",
&format!("refs/heads/{branch}"),
])
.output()
.map(|output| output.status.success())
.unwrap_or(false)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

No timeout on git subprocess calls used to resolve the trunk branch.

remote_default_branch and local_branch_exists (called up to 3x from trunk_branch) shell out via Command::output() with no deadline. Per the call sites in main.rs (git::trunk_branch(repo_root) during temp-worktree activation and when building KindraTempContext), a stalled git process here would block the picker UI indefinitely — the same risk already flagged for wisp-kindra's subprocess calls, but at a separate call site.

🤖 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 `@crates/wisp-bin/src/git.rs` around lines 199 - 234, The git subprocesses used
by remote_default_branch and local_branch_exists do not have any timeout, so a
stalled git command can block trunk_branch and the picker UI indefinitely.
Update these helpers to run git through the project’s existing timeout-aware
subprocess pattern (the same approach used elsewhere for wisp-kindra), and have
trunk_branch propagate or handle timeout failures cleanly when calling
remote_default_branch and local_branch_exists.

Comment on lines +2102 to +2134
InputMode::ConfirmDeleteWorktree {
branch,
repo_root,
filter_query,
} => {
let branch = branch.clone();
let repo_root = repo_root.clone();
let filter_query = filter_query.clone();

// Never forces: a dirty worktree makes kin fail and the
// error propagates rather than discarding uncommitted work.
kindra.remove_temp_worktree(&repo_root, &branch)?;

let reloaded_state = load_domain_state(backend)?;
apply_reloaded_state(
reloaded_state,
picker_mode,
session_sort,
&mut active_client_id,
&mut session_items,
&mut pending_branch_names,
&mut branch_status_updates,
&mut details_preview_provider,
&mut deferred_branch_status,
);
query = filter_query;
input_mode = InputMode::Filter;
preview_session_id = None;
preview_refreshed_at = None;
if preview_enabled {
preview = Some(Vec::new());
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Deleting a dirty temp worktree exits the whole picker instead of showing an error.

kindra.remove_temp_worktree(&repo_root, &branch)? propagates via ?, and this arm is inside run_surface's main loop, whose Result is returned from the function on error. Since deletion deliberately never forces (per the adjacent comment, a dirty worktree makes kin fail on purpose), this is an expected, likely-common failure path — yet it terminates the entire interactive picker/sidebar session rather than surfacing the error and returning to the filter view. The user loses their query/selection state for something as routine as trying to delete a worktree with uncommitted changes. This also doesn't match the README's description ("Kindra reports the error"), which implies in-app feedback rather than an app exit.

💡 Illustrative fix: handle the error inline instead of propagating
                     InputMode::ConfirmDeleteWorktree {
                         branch,
                         repo_root,
                         filter_query,
                     } => {
                         let branch = branch.clone();
                         let repo_root = repo_root.clone();
                         let filter_query = filter_query.clone();

                         // Never forces: a dirty worktree makes kin fail and the
-                        // error propagates rather than discarding uncommitted work.
-                        kindra.remove_temp_worktree(&repo_root, &branch)?;
-
-                        let reloaded_state = load_domain_state(backend)?;
-                        apply_reloaded_state(
-                            reloaded_state,
-                            picker_mode,
-                            session_sort,
-                            &mut active_client_id,
-                            &mut session_items,
-                            &mut pending_branch_names,
-                            &mut branch_status_updates,
-                            &mut details_preview_provider,
-                            &mut deferred_branch_status,
-                        );
-                        query = filter_query;
-                        input_mode = InputMode::Filter;
-                        preview_session_id = None;
-                        preview_refreshed_at = None;
-                        if preview_enabled {
-                            preview = Some(Vec::new());
-                        }
+                        // rather than discarding uncommitted work — surface the
+                        // failure instead of exiting the picker.
+                        match kindra.remove_temp_worktree(&repo_root, &branch) {
+                            Ok(()) => {
+                                let reloaded_state = load_domain_state(backend)?;
+                                apply_reloaded_state(
+                                    reloaded_state,
+                                    picker_mode,
+                                    session_sort,
+                                    &mut active_client_id,
+                                    &mut session_items,
+                                    &mut pending_branch_names,
+                                    &mut branch_status_updates,
+                                    &mut details_preview_provider,
+                                    &mut deferred_branch_status,
+                                );
+                            }
+                            Err(_error) => {
+                                // TODO: surface `_error` to the user (status line/preview banner).
+                            }
+                        }
+                        query = filter_query;
+                        input_mode = InputMode::Filter;
+                        preview_session_id = None;
+                        preview_refreshed_at = None;
+                        if preview_enabled {
+                            preview = Some(Vec::new());
+                        }
                     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
InputMode::ConfirmDeleteWorktree {
branch,
repo_root,
filter_query,
} => {
let branch = branch.clone();
let repo_root = repo_root.clone();
let filter_query = filter_query.clone();
// Never forces: a dirty worktree makes kin fail and the
// error propagates rather than discarding uncommitted work.
kindra.remove_temp_worktree(&repo_root, &branch)?;
let reloaded_state = load_domain_state(backend)?;
apply_reloaded_state(
reloaded_state,
picker_mode,
session_sort,
&mut active_client_id,
&mut session_items,
&mut pending_branch_names,
&mut branch_status_updates,
&mut details_preview_provider,
&mut deferred_branch_status,
);
query = filter_query;
input_mode = InputMode::Filter;
preview_session_id = None;
preview_refreshed_at = None;
if preview_enabled {
preview = Some(Vec::new());
}
}
InputMode::ConfirmDeleteWorktree {
branch,
repo_root,
filter_query,
} => {
let branch = branch.clone();
let repo_root = repo_root.clone();
let filter_query = filter_query.clone();
// Never forces: a dirty worktree makes kin fail and the
// rather than discarding uncommitted work — surface the
// failure instead of exiting the picker.
match kindra.remove_temp_worktree(&repo_root, &branch) {
Ok(()) => {
let reloaded_state = load_domain_state(backend)?;
apply_reloaded_state(
reloaded_state,
picker_mode,
session_sort,
&mut active_client_id,
&mut session_items,
&mut pending_branch_names,
&mut branch_status_updates,
&mut details_preview_provider,
&mut deferred_branch_status,
);
}
Err(_error) => {
// TODO: surface `_error` to the user (status line/preview banner).
}
}
query = filter_query;
input_mode = InputMode::Filter;
preview_session_id = None;
preview_refreshed_at = None;
if preview_enabled {
preview = Some(Vec::new());
}
}
🤖 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 `@crates/wisp-bin/src/main.rs` around lines 2102 - 2134, The
ConfirmDeleteWorktree branch in run_surface currently uses
`kindra.remove_temp_worktree(&repo_root, &branch)?`, which exits the whole
picker on an expected deletion failure. Change this arm to handle the error
inline: call `remove_temp_worktree` without propagating, surface the failure
through the existing in-app error reporting path used by the picker/sidebar, and
then return the user to the filter view while preserving the current
query/selection state. Use the `InputMode::ConfirmDeleteWorktree` match arm,
`run_surface`, and `kindra.remove_temp_worktree` to locate the change.

@Pajn Pajn merged commit 6089ecb into main Jul 2, 2026
3 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