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
14 changes: 10 additions & 4 deletions .github/workflows/plugin-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ name: Plugin Build
on:
pull_request:
paths:
- 'wt-intellij-plugin/**'
- 'wt-jetbrains-plugin/**'
- '.github/workflows/plugin-build.yml'
push:
branches: [main]
paths:
- 'wt-intellij-plugin/**'
- 'wt-jetbrains-plugin/**'
- '.github/workflows/plugin-build.yml'

jobs:
build:
Expand All @@ -20,6 +22,10 @@ jobs:
distribution: temurin
java-version: 17

- name: Build and test
working-directory: wt-intellij-plugin
- name: Build
working-directory: wt-jetbrains-plugin
run: ./gradlew build --no-build-cache

- name: Test
working-directory: wt-jetbrains-plugin
run: ./gradlew test --no-build-cache
1 change: 0 additions & 1 deletion wt-jetbrains-plugin/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
build/
.gradle/
.intellijPlatform/
gradle/wrapper/gradle-wrapper.jar
326 changes: 326 additions & 0 deletions wt-jetbrains-plugin/META-INF/plugin.xml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion wt-jetbrains-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ kotlin {
}

repositories {
maven(url = "https://maven.global.square/artifactory/square-public")
mavenCentral()
intellijPlatform {
defaultRepositories()
}
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,20 @@ package com.block.wt.actions.metadata
import com.block.wt.actions.WtConfigAction
import com.block.wt.services.MetadataService
import com.block.wt.ui.Notifications
import com.block.wt.ui.WorktreePanel
import com.block.wt.util.PathHelper
import com.intellij.openapi.actionSystem.AnActionEvent

class ExportMetadataAction : WtConfigAction() {

override fun isAvailable(e: AnActionEvent): Boolean {
if (!super.isAvailable(e)) return false
// Export only from the main worktree — symlinks from feature worktrees
// become dead when the feature worktree is deleted.
val wt = e.getData(WorktreePanel.DATA_KEY)?.getSelectedWorktree()
return wt == null || wt.isMain
}

override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
val config = requireConfig(e) ?: return
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.block.wt.actions.util

import com.block.wt.actions.WtTableAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.ide.CopyPasteManager
import java.awt.datatransfer.StringSelection

class CopyBranchNameAction : WtTableAction() {

override fun update(e: AnActionEvent) {
val wt = getSelectedPanel(e)?.getSelectedWorktree()
e.presentation.isEnabledAndVisible = wt?.branch != null
}

override fun actionPerformed(e: AnActionEvent) {
val wt = getSelectedPanel(e)?.getSelectedWorktree() ?: return
val branch = wt.branch ?: return
CopyPasteManager.getInstance().setContents(StringSelection(branch))
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.block.wt.agent

import com.block.wt.util.PlatformUtil
import com.intellij.openapi.diagnostic.Logger
import java.nio.file.Files
import java.nio.file.Path
Expand Down Expand Up @@ -51,7 +52,7 @@ object AgentDetector : AgentDetection {
* Returns empty set on failure (Windows, permission denied, lsof not found, etc.)
*/
private fun detectViaLsof(): Set<Path> {
if (isWindows()) return emptySet()
if (PlatformUtil.isWindows()) return emptySet()

return try {
val lsofPath = if (Files.exists(Path.of("/usr/sbin/lsof"))) "/usr/sbin/lsof"
Expand Down Expand Up @@ -132,7 +133,7 @@ object AgentDetector : AgentDetection {

/** Best-effort reverse of encodePath. Lossy, so we validate via round-trip. */
private fun decodeDirToPath(encoded: String): Path? {
if (isWindows()) return null
if (PlatformUtil.isWindows()) return null
val candidate = "/" + encoded.replace("-", "/")
return try {
val path = Path.of(candidate).normalize()
Expand All @@ -142,5 +143,4 @@ object AgentDetector : AgentDetection {
}
}

private fun isWindows(): Boolean = System.getProperty("os.name").lowercase().contains("win")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.block.wt.agent

import java.nio.file.Files
import java.nio.file.Path

/** Shared resolution of Claude Code session directories and files. */
object ClaudeSessionPaths {

private const val CLAUDE_PROJECTS_DIR = ".claude/projects"
private val NON_ALNUM = Regex("[^a-zA-Z0-9]")

/** Returns the Claude projects dir for [worktreePath], or null if it doesn't exist. */
fun resolveProjectDir(worktreePath: Path): Path? {
val projectsDir = Path.of(System.getProperty("user.home")).resolve(CLAUDE_PROJECTS_DIR)
if (!Files.isDirectory(projectsDir)) return null
val encoded = NON_ALNUM.replace(worktreePath.toString(), "-")
val dir = projectsDir.resolve(encoded)
return if (Files.isDirectory(dir)) dir else null
}

/** Returns the `.jsonl` session file for [sessionId], or null if not found. */
fun resolveSessionFile(worktreePath: Path, sessionId: String): Path? {
val dir = resolveProjectDir(worktreePath) ?: return null
val file = dir.resolve("$sessionId.jsonl")
return if (Files.isRegularFile(file)) file else null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.block.wt.experiment.sessiondetection

enum class AgentSessionState {
RUNNING, // PID alive AND .jsonl written to within 60s
IDLE, // PID alive but .jsonl unchanged for 60s-10m
}

enum class AgentTerminalKind {
INTELLIJ, ITERM2, TERMINAL_APP, UNKNOWN;

val displayName: String get() = when (this) {
INTELLIJ -> "IntelliJ"
ITERM2 -> "iTerm"
TERMINAL_APP -> "Terminal"
UNKNOWN -> ""
}
}

data class AgentSessionInfo(
val sessionId: String,
val state: AgentSessionState,
val pid: Long?,
val tty: String?,
val terminalKind: AgentTerminalKind = AgentTerminalKind.UNKNOWN,
val lastActivityMs: Long,
val sessionStartMs: Long,
)
Loading