refactor(wt): extract cmd.list module

Amolith created

Assisted-by: Claude Opus 4.5 via Amp

Change summary

dist/wt             | 162 +++++++++++++++++++++++++---------------------
src/main.lua        |  78 ---------------------
src/wt/cmd/list.lua |  82 +++++++++++++++++++++++
3 files changed, 172 insertions(+), 150 deletions(-)

Detailed changes

dist/wt 🔗

@@ -861,6 +861,90 @@ end
 return M
 ]]
 
+_EMBEDDED_MODULES["wt.cmd.list"] = [[-- SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+--
+-- SPDX-License-Identifier: GPL-3.0-or-later
+
+local exit = require("wt.exit")
+local shell = require("wt.shell")
+local git = require("wt.git")
+local path_mod = require("wt.path")
+
+---@class wt.cmd.list
+local M = {}
+
+---List all worktrees with status
+function M.cmd_list()
+	local root, err = git.find_project_root()
+	if not root then
+		shell.die(err --[[@as string]])
+		return
+	end
+
+	local git_dir = root .. "/.bare"
+	local output, code = shell.run_cmd("GIT_DIR=" .. git_dir .. " git worktree list --porcelain")
+	if code ~= 0 then
+		shell.die("failed to list worktrees: " .. output, exit.EXIT_SYSTEM_ERROR)
+	end
+
+	---@type {path: string, head: string, branch: string}[]
+	local worktrees = {}
+	local current = {}
+
+	for line in output:gmatch("[^\n]+") do
+		local key, value = line:match("^(%S+)%s*(.*)$")
+		if key == "worktree" and value then
+			if current.path then
+				table.insert(worktrees, current)
+			end
+			if value:match("/%.bare$") then
+				current = {}
+			else
+				current = { path = value, head = "", branch = "(detached)" }
+			end
+		elseif key == "HEAD" and value then
+			current.head = value:sub(1, 7)
+		elseif key == "branch" and value then
+			current.branch = value:gsub("^refs/heads/", "")
+		elseif key == "bare" then
+			current = {}
+		end
+	end
+	if current.path then
+		table.insert(worktrees, current)
+	end
+
+	if #worktrees == 0 then
+		print("No worktrees found")
+		return
+	end
+
+	local cwd = shell.get_cwd() or ""
+
+	local rows = {}
+	for _, wt in ipairs(worktrees) do
+		local rel_path = path_mod.relative_path(cwd, wt.path)
+
+		local status_out = shell.run_cmd("git -C " .. wt.path .. " status --porcelain")
+		local status = status_out == "" and "clean" or "dirty"
+
+		table.insert(rows, rel_path .. "," .. wt.branch .. "," .. wt.head .. "," .. status)
+	end
+
+	local table_input = "Path,Branch,HEAD,Status\n" .. table.concat(rows, "\n")
+	table_input = table_input:gsub("EOF", "eof")
+	local table_cmd = "gum table --print <<'EOF'\n" .. table_input .. "\nEOF"
+	local table_handle = io.popen(table_cmd, "r")
+	if not table_handle then
+		return
+	end
+	io.write(table_handle:read("*a") or "")
+	table_handle:close()
+end
+
+return M
+]]
+
 _EMBEDDED_MODULES["wt.cmd.fetch"] = [[-- SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
 --
 -- SPDX-License-Identifier: GPL-3.0-or-later
@@ -945,6 +1029,9 @@ local show_command_help = help_mod.show_command_help
 local fetch_mod = require("wt.cmd.fetch")
 local cmd_fetch = fetch_mod.cmd_fetch
 
+local list_mod = require("wt.cmd.list")
+local cmd_list = list_mod.cmd_list
+
 ---@param args string[]
 local function cmd_clone(args)
 	-- Parse arguments: <url> [--remote name]... [--own]
@@ -1598,81 +1685,6 @@ local function cmd_remove(args)
 	end
 end
 
-local function cmd_list()
-	local root, err = find_project_root()
-	if not root then
-		die(err --[[@as string]])
-		return
-	end
-
-	local git_dir = root .. "/.bare"
-	local output, code = run_cmd("GIT_DIR=" .. git_dir .. " git worktree list --porcelain")
-	if code ~= 0 then
-		die("failed to list worktrees: " .. output, EXIT_SYSTEM_ERROR)
-	end
-
-	-- Parse porcelain output into worktree entries
-	---@type {path: string, head: string, branch: string}[]
-	local worktrees = {}
-	local current = {}
-
-	for line in output:gmatch("[^\n]+") do
-		local key, value = line:match("^(%S+)%s*(.*)$")
-		if key == "worktree" and value then
-			if current.path then
-				table.insert(worktrees, current)
-			end
-			-- Skip .bare directory
-			if value:match("/%.bare$") then
-				current = {}
-			else
-				current = { path = value, head = "", branch = "(detached)" }
-			end
-		elseif key == "HEAD" and value then
-			current.head = value:sub(1, 7)
-		elseif key == "branch" and value then
-			current.branch = value:gsub("^refs/heads/", "")
-		elseif key == "bare" then
-			-- Skip bare repo entry
-			current = {}
-		end
-	end
-	if current.path then
-		table.insert(worktrees, current)
-	end
-
-	if #worktrees == 0 then
-		print("No worktrees found")
-		return
-	end
-
-	-- Get current working directory
-	local cwd = get_cwd() or ""
-
-	-- Build table rows with status
-	local rows = {}
-	for _, wt in ipairs(worktrees) do
-		local rel_path = relative_path(cwd, wt.path)
-
-		-- Check dirty status
-		local status_out = run_cmd("git -C " .. wt.path .. " status --porcelain")
-		local status = status_out == "" and "clean" or "dirty"
-
-		table.insert(rows, rel_path .. "," .. wt.branch .. "," .. wt.head .. "," .. status)
-	end
-
-	-- Output via gum table
-	local table_input = "Path,Branch,HEAD,Status\n" .. table.concat(rows, "\n")
-	table_input = table_input:gsub("EOF", "eof")
-	local table_cmd = "gum table --print <<'EOF'\n" .. table_input .. "\nEOF"
-	local table_handle = io.popen(table_cmd, "r")
-	if not table_handle then
-		return
-	end
-	io.write(table_handle:read("*a") or "")
-	table_handle:close()
-end
-
 ---List directory entries (excluding . and ..)
 ---@param path string
 ---@return string[]

src/main.lua 🔗

@@ -57,6 +57,9 @@ local show_command_help = help_mod.show_command_help
 local fetch_mod = require("wt.cmd.fetch")
 local cmd_fetch = fetch_mod.cmd_fetch
 
+local list_mod = require("wt.cmd.list")
+local cmd_list = list_mod.cmd_list
+
 ---@param args string[]
 local function cmd_clone(args)
 	-- Parse arguments: <url> [--remote name]... [--own]
@@ -710,81 +713,6 @@ local function cmd_remove(args)
 	end
 end
 
-local function cmd_list()
-	local root, err = find_project_root()
-	if not root then
-		die(err --[[@as string]])
-		return
-	end
-
-	local git_dir = root .. "/.bare"
-	local output, code = run_cmd("GIT_DIR=" .. git_dir .. " git worktree list --porcelain")
-	if code ~= 0 then
-		die("failed to list worktrees: " .. output, EXIT_SYSTEM_ERROR)
-	end
-
-	-- Parse porcelain output into worktree entries
-	---@type {path: string, head: string, branch: string}[]
-	local worktrees = {}
-	local current = {}
-
-	for line in output:gmatch("[^\n]+") do
-		local key, value = line:match("^(%S+)%s*(.*)$")
-		if key == "worktree" and value then
-			if current.path then
-				table.insert(worktrees, current)
-			end
-			-- Skip .bare directory
-			if value:match("/%.bare$") then
-				current = {}
-			else
-				current = { path = value, head = "", branch = "(detached)" }
-			end
-		elseif key == "HEAD" and value then
-			current.head = value:sub(1, 7)
-		elseif key == "branch" and value then
-			current.branch = value:gsub("^refs/heads/", "")
-		elseif key == "bare" then
-			-- Skip bare repo entry
-			current = {}
-		end
-	end
-	if current.path then
-		table.insert(worktrees, current)
-	end
-
-	if #worktrees == 0 then
-		print("No worktrees found")
-		return
-	end
-
-	-- Get current working directory
-	local cwd = get_cwd() or ""
-
-	-- Build table rows with status
-	local rows = {}
-	for _, wt in ipairs(worktrees) do
-		local rel_path = relative_path(cwd, wt.path)
-
-		-- Check dirty status
-		local status_out = run_cmd("git -C " .. wt.path .. " status --porcelain")
-		local status = status_out == "" and "clean" or "dirty"
-
-		table.insert(rows, rel_path .. "," .. wt.branch .. "," .. wt.head .. "," .. status)
-	end
-
-	-- Output via gum table
-	local table_input = "Path,Branch,HEAD,Status\n" .. table.concat(rows, "\n")
-	table_input = table_input:gsub("EOF", "eof")
-	local table_cmd = "gum table --print <<'EOF'\n" .. table_input .. "\nEOF"
-	local table_handle = io.popen(table_cmd, "r")
-	if not table_handle then
-		return
-	end
-	io.write(table_handle:read("*a") or "")
-	table_handle:close()
-end
-
 ---List directory entries (excluding . and ..)
 ---@param path string
 ---@return string[]

src/wt/cmd/list.lua 🔗

@@ -0,0 +1,82 @@
+-- SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+--
+-- SPDX-License-Identifier: GPL-3.0-or-later
+
+local exit = require("wt.exit")
+local shell = require("wt.shell")
+local git = require("wt.git")
+local path_mod = require("wt.path")
+
+---@class wt.cmd.list
+local M = {}
+
+---List all worktrees with status
+function M.cmd_list()
+	local root, err = git.find_project_root()
+	if not root then
+		shell.die(err --[[@as string]])
+		return
+	end
+
+	local git_dir = root .. "/.bare"
+	local output, code = shell.run_cmd("GIT_DIR=" .. git_dir .. " git worktree list --porcelain")
+	if code ~= 0 then
+		shell.die("failed to list worktrees: " .. output, exit.EXIT_SYSTEM_ERROR)
+	end
+
+	---@type {path: string, head: string, branch: string}[]
+	local worktrees = {}
+	local current = {}
+
+	for line in output:gmatch("[^\n]+") do
+		local key, value = line:match("^(%S+)%s*(.*)$")
+		if key == "worktree" and value then
+			if current.path then
+				table.insert(worktrees, current)
+			end
+			if value:match("/%.bare$") then
+				current = {}
+			else
+				current = { path = value, head = "", branch = "(detached)" }
+			end
+		elseif key == "HEAD" and value then
+			current.head = value:sub(1, 7)
+		elseif key == "branch" and value then
+			current.branch = value:gsub("^refs/heads/", "")
+		elseif key == "bare" then
+			current = {}
+		end
+	end
+	if current.path then
+		table.insert(worktrees, current)
+	end
+
+	if #worktrees == 0 then
+		print("No worktrees found")
+		return
+	end
+
+	local cwd = shell.get_cwd() or ""
+
+	local rows = {}
+	for _, wt in ipairs(worktrees) do
+		local rel_path = path_mod.relative_path(cwd, wt.path)
+
+		local status_out = shell.run_cmd("git -C " .. wt.path .. " status --porcelain")
+		local status = status_out == "" and "clean" or "dirty"
+
+		table.insert(rows, rel_path .. "," .. wt.branch .. "," .. wt.head .. "," .. status)
+	end
+
+	local table_input = "Path,Branch,HEAD,Status\n" .. table.concat(rows, "\n")
+	table_input = table_input:gsub("EOF", "eof")
+	local table_cmd = "gum table --print <<'EOF'\n" .. table_input .. "\nEOF"
+	local table_handle = io.popen(table_cmd, "r")
+	if not table_handle then
+		return
+	end
+	io.write(table_handle:read("*a") or "")
+	table_handle:close()
+end
+
+return M