Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions wt-jetbrains-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,18 @@ To update, rebuild and reinstall. The old version is replaced automatically.
| wt CLI command | Plugin equivalent |
|---|---|
| `wt list [-v]` | **Worktrees** tool window with async status indicators |
| `wt add [-b] <branch>` | **Create Worktree** dialog (stash/pull/create/restore) |
| `wt switch <worktree>` | **Switch Worktree** with atomic symlink swap |
| `wt remove [--merged]` | **Remove Worktree** / **Remove Merged** with safety checks |
| `wt context [name]` | **Context selector** in status bar + popup |
| `wt add [-b] <branch>` | **Create Worktree** dialog |
| `wt switch <worktree>` | **Switch Worktree** (double-click or action) |
| `wt remove [--merged]` | **Remove Worktree** / **Remove Merged** |
| `wt adopt` | **Adopt Worktree** (same `wt/adopted` marker) |
| `wt context add` | **Add Context** dialog |
| `wt context` | Auto-detected from project path |
| `wt metadata-export` | **Export Metadata to Vault** |
| `wt metadata-import` | **Import Metadata from Vault** |
| `wt-metadata-refresh` | **Refresh Bazel Targets** |
| `wt cd` | **Open in Terminal** |

The plugin reads the same `~/.wt/` config files as the CLI. Both work side by side. A file watcher auto-refreshes the tool window on external changes.
The plugin reads the same `~/.wt/` config files as the CLI. Both work side by side. Worktrees adopted via the CLI are recognized by the plugin and vice versa. A file watcher auto-refreshes the tool window on external changes.

---

Expand All @@ -54,7 +56,7 @@ The plugin reads the same `~/.wt/` config files as the CLI. Both work side by si
| Branch | Checked-out branch |
| Status | `⚠`conflicts `●`staged `✱`modified `…`untracked `↑`ahead `↓`behind |
| Agent | Active Claude Code session IDs (truncated; hover for full) |
| Provisioned | `✓` current context, `~` other context |
| Adopted | `✓` adopted by current context, `~` adopted by another context |

Double-click a row to switch. Hover Status or Agent cells for details.

Expand All @@ -69,7 +71,7 @@ Also available under **VCS > Worktrees**.

### Status Bar

Shows current context (e.g. `wt: java`). Click to switch.
Shows current worktree (e.g. `wt: java`). Click to switch worktrees.

### Settings

Expand All @@ -79,7 +81,7 @@ Shows current context (e.g. `wt: java`). Click to switch.
- Status indicator loading
- Auto-export metadata on shutdown (default: off)
- Switch/remove confirmation dialogs
- Provision prompt on switch
- Adopt prompt on switch

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.block.wt.actions.WtConfigAction
import com.block.wt.git.GitConfigHelper
import com.block.wt.services.ContextService
import com.block.wt.ui.Notifications
import com.block.wt.util.ConfigFileHelper
import com.block.wt.util.PathHelper
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.ui.Messages
Expand Down Expand Up @@ -42,6 +43,17 @@ class DeleteContextAction : WtConfigAction() {
}

ContextService.getInstance(project).reload()

val currentName = ConfigFileHelper.readCurrentContext()
if (currentName == config.name) {
val remaining = ContextService.getInstance(project).contexts.value
if (remaining.isNotEmpty()) {
ConfigFileHelper.writeCurrentContext(remaining.first().name)
} else {
Files.deleteIfExists(PathHelper.currentFile)
}
}

Notifications.info(project, "Context Deleted", "Context '${config.name}' deleted")
} catch (ex: Exception) {
Notifications.error(project, "Delete Failed", ex.message ?: "Unknown error")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ class ReprovisionContextAction : WtConfigAction() {
"This will remove the current wt configuration for '${config.name}' and let you re-create it.\n" +
"Existing worktree directories will be kept.\n\n" +
"Continue?",
"Re-provision Context",
"Re-adopt Context",
Messages.getQuestionIcon(),
)
if (answer != Messages.YES) return

runInBackground(project, "Re-provisioning Context", cancellable = false) { indicator ->
runInBackground(project, "Re-adopting Context", cancellable = false) { indicator ->
try {
indicator.text = "Removing git config..."
GitConfigHelper.removeAllConfig(config.mainRepoRoot)
Expand All @@ -44,7 +44,7 @@ class ReprovisionContextAction : WtConfigAction() {

ContextService.getInstance(project).reload()
} catch (ex: Exception) {
Notifications.error(project, "Re-provision Failed", ex.message ?: "Unknown error")
Notifications.error(project, "Re-adopt Failed", ex.message ?: "Unknown error")
return@runInBackground
}

Expand Down Expand Up @@ -112,7 +112,7 @@ class ReprovisionContextAction : WtConfigAction() {
}

WorktreeService.getInstance(project).refreshWorktreeList()
Notifications.info(project, "Context Re-provisioned", "Context '$contextName' re-provisioned")
Notifications.info(project, "Context Re-adopted", "Context '$contextName' re-adopted")
} catch (ex: Exception) {
Notifications.error(project, "Context Creation Failed", ex.message ?: "Unknown error")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,19 @@ class ProvisionWorktreeAction : WtTableAction() {
}

if (wt.isProvisionedByCurrentContext) {
Notifications.info(project, "Already Provisioned", "This worktree is already provisioned by '$currentContextName'")
Notifications.info(project, "Already Adopted", "This worktree is already adopted by '$currentContextName'")
return
}

ProgressManager.getInstance().run(object : Task.Backgroundable(
project, "Provisioning Worktree", true
project, "Adopting Worktree", true
) {
override fun run(indicator: com.intellij.openapi.progress.ProgressIndicator) {
indicator.isIndeterminate = false
runBlockingCancellable {
ProvisionHelper.provisionWorktree(project, wt.path, config, scope = indicator.asScope())
WorktreeService.getInstance(project).refreshWorktreeList()
Notifications.info(project, "Worktree Provisioned", "Provisioned ${wt.displayName} for context '$currentContextName'")
Notifications.info(project, "Worktree Adopted", "Adopted ${wt.displayName} for context '$currentContextName'")
}
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ object ProvisionHelper {
if (errors.isNotEmpty()) {
Notifications.warning(
project,
"Provisioning Warnings",
"Provisioned with issues:\n${errors.joinToString("\n• ", prefix = "• ")}",
"Adoption Warnings",
"Adopted with issues:\n${errors.joinToString("\n• ", prefix = "• ")}",
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ object ProvisionSwitchHelper {
if (answer != Messages.YES) return
}

// Provision prompt: only when the worktree is not provisioned by the current context
// Adopt prompt: only when the worktree is not adopted by the current context
val contextService = ContextService.getInstance(project)
val config = contextService.getCurrentConfig()
val currentContextName = config?.name
Expand All @@ -57,8 +57,8 @@ object ProvisionSwitchHelper {
"Worktree '${wt.displayName}' has existing files that conflict with " +
"context '$currentContextName':\n\n$conflictSummary\n\n" +
"Overwrite: replace with this context's vault.\n" +
"Keep: mark as provisioned without changing files.",
"Provision Worktree?",
"Keep: mark as adopted without changing files.",
"Adopt Worktree?",
options,
0, // default: overwrite
Messages.getQuestionIcon(),
Expand All @@ -83,13 +83,13 @@ object ProvisionSwitchHelper {
else -> return // Cancel or closed
}
} else {
// No conflicts — simpler provision prompt
// No conflicts — simpler adopt prompt
val answer = Messages.showYesNoCancelDialog(
project,
"Worktree '${wt.displayName}' is not provisioned by context '$currentContextName'.\n\n" +
"Provisioning will import IDE metadata and install Bazel symlinks.",
"Provision Worktree?",
"Provision && Switch",
"Worktree '${wt.displayName}' is not adopted by context '$currentContextName'.\n\n" +
"Adopting will import IDE metadata and install Bazel symlinks.",
"Adopt Worktree?",
"Adopt && Switch",
"Switch Only",
"Cancel",
Messages.getQuestionIcon(),
Expand All @@ -115,7 +115,7 @@ object ProvisionSwitchHelper {
val config = ContextService.getInstance(project).getCurrentConfig() ?: return

ProgressManager.getInstance().run(object : Task.Backgroundable(
project, "Provisioning & Switching Worktree", true
project, "Adopting & Switching Worktree", true
) {
override fun run(indicator: ProgressIndicator) {
indicator.isIndeterminate = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ class WtSettingsComponent {
.comment("Disable to speed up worktree list loading for repos with many worktrees")
}
row {
checkBox("Prompt to provision when switching to non-provisioned worktrees")
checkBox("Prompt to adopt when switching to non-adopted worktrees")
.bindSelected(settings.state::promptProvisionOnSwitch)
.comment("Shows Provision & Switch / Switch Only / Cancel dialog")
.comment("Shows Adopt & Switch / Switch Only / Cancel dialog")
}
row("Auto-refresh interval (seconds, 0 to disable):") {
spinner(0..600, 5)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ import javax.swing.JPanel

/**
* One-time setup dialog shown when a context is first used.
* Lists all non-provisioned worktrees and lets the user pick which to provision.
* Lists all non-adopted worktrees and lets the user pick which to adopt.
*
* Results:
* - OK ("Provision Selected") → provisions checked worktrees, marks context as set up
* - CANCEL with "Skip" → marks context as set up without provisioning
* - OK ("Adopt Selected") → adopts checked worktrees, marks context as set up
* - CANCEL with "Skip" → marks context as set up without adopting
* - CANCEL with close button → does nothing, will ask again next time
*/
class ContextSetupDialog(
Expand All @@ -55,7 +55,7 @@ class ContextSetupDialog(

init {
title = "Set Up Context: ${config.name}"
setOKButtonText("Provision Selected")
setOKButtonText("Adopt Selected")
setCancelButtonText("Remind Me Later")
init()
}
Expand All @@ -68,8 +68,8 @@ class ContextSetupDialog(
layout = BoxLayout(this, BoxLayout.Y_AXIS)
border = JBUI.Borders.emptyBottom(12)

add(JBLabel("The following worktrees haven't been provisioned by context '${config.name}'."))
add(JBLabel("Select which ones to provision:").apply {
add(JBLabel("The following worktrees haven't been adopted by context '${config.name}'."))
add(JBLabel("Select which ones to adopt:").apply {
border = JBUI.Borders.emptyTop(4)
})
}
Expand All @@ -84,7 +84,7 @@ class ContextSetupDialog(
}
panel.add(JBScrollPane(checkBoxList), BorderLayout.CENTER)

val footer = JBLabel("Worktrees with existing project files will be marked as provisioned without changes. " +
val footer = JBLabel("Worktrees with existing project files will be marked as adopted without changes. " +
"Others will have metadata imported from the vault.").apply {
foreground = JBUI.CurrentTheme.Label.disabledForeground()
border = JBUI.Borders.emptyTop(8)
Expand Down Expand Up @@ -132,24 +132,24 @@ class ContextSetupDialog(
val settings = WtPluginSettings.getInstance()
if (config.name in settings.state.setupCompletedContexts) return

// Only show if there are non-provisioned worktrees
val hasUnprovisioned = worktrees.any { !it.isProvisionedByCurrentContext }
if (!hasUnprovisioned) {
// All worktrees are already provisioned — mark as done silently
// Only show if there are non-adopted worktrees
val hasUnadopted = worktrees.any { !it.isProvisionedByCurrentContext }
if (!hasUnadopted) {
// All worktrees are already adopted — mark as done silently
markSetupComplete(config.name)
return
}

val dialog = ContextSetupDialog(project, config, worktrees)
if (dialog.showAndGet()) {
// OK — provision selected worktrees
// OK — adopt selected worktrees
val selected = dialog.getSelectedEntries()
if (selected.isNotEmpty()) {
runProvisioning(project, config, selected)
runAdoption(project, config, selected)
}
markSetupComplete(config.name)
} else if (dialog.wasSkipped()) {
// Skip — mark as done without provisioning
// Skip — mark as done without adopting
markSetupComplete(config.name)
}
// else: Remind Me Later / closed — do nothing, will ask again
Expand All @@ -165,13 +165,13 @@ class ContextSetupDialog(
}
}

private fun runProvisioning(
private fun runAdoption(
project: Project,
config: ContextConfig,
entries: List<WorktreeEntry>,
) {
ProgressManager.getInstance().run(object : Task.Backgroundable(
project, "Provisioning Worktrees", true
project, "Adopting Worktrees", true
) {
override fun run(indicator: ProgressIndicator) {
indicator.isIndeterminate = false
Expand All @@ -182,7 +182,7 @@ class ContextSetupDialog(
indicator.checkCanceled()
val wtStart = i.toDouble() / entries.size
val wtSize = 1.0 / entries.size
scope.text("Provisioning ${entry.wt.displayName}...")
scope.text("Adopting ${entry.wt.displayName}...")

ProvisionHelper.provisionWorktree(
project,
Expand All @@ -199,7 +199,7 @@ class ContextSetupDialog(
Notifications.info(
project,
"Context Setup Complete",
"Provisioned ${entries.size} worktree(s) for context '${config.name}'",
"Adopted ${entries.size} worktree(s) for context '${config.name}'",
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ class WorktreePanel(private val project: Project) : JPanel(BorderLayout()), Data

private fun buildProvisionTooltip(wt: WorktreeInfo): String {
if (!wt.isProvisioned) {
return "Not provisioned \u2014 right-click to provision"
return "Not adopted \u2014 right-click to adopt"
}

val adoptedContext = ProvisionMarkerService.readAdoptedContext(wt.path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class WorktreeTableModel : AbstractTableModel() {
const val COL_PR = 4
const val COL_PROVISIONED = 5

val COLUMN_NAMES = arrayOf("Path", "Branch", "Status", "Agent", "PR", "Provisioned")
val COLUMN_NAMES = arrayOf("Path", "Branch", "Status", "Agent", "PR", "Adopted")
}

fun setWorktrees(newWorktrees: List<WorktreeInfo>) {
Expand Down
8 changes: 4 additions & 4 deletions wt-jetbrains-plugin/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@

<action id="Wt.ProvisionWorktree"
class="com.block.wt.actions.worktree.ProvisionWorktreeAction"
text="Provision Worktree..."
description="Provision an existing worktree for the current wt context"/>
text="Adopt Worktree..."
description="Adopt an existing worktree for the current wt context"/>

<separator/>

Expand All @@ -113,7 +113,7 @@

<action id="Wt.ReprovisionContext"
class="com.block.wt.actions.context.ReprovisionContextAction"
text="Re-provision Context..."
text="Re-adopt Context..."
description="Remove and re-create the wt context configuration"/>

<action id="Wt.DeleteContext"
Expand Down Expand Up @@ -198,7 +198,7 @@
<separator/>
<action id="Wt.Context.ProvisionWorktree"
class="com.block.wt.actions.worktree.ProvisionWorktreeAction"
text="Provision Worktree..."
text="Adopt Worktree..."
icon="AllIcons.Actions.Download"/>
<reference ref="Wt.Toolbar.Remove"/>
<action id="Wt.Context.RemoveMerged"
Expand Down
Loading
Loading