taskplane 0.24.3 → 0.24.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/taskplane.mjs CHANGED
@@ -1955,251 +1955,6 @@ function parseWorkspaceYaml(raw) {
1955
1955
  return result;
1956
1956
  }
1957
1957
 
1958
- // ─── install-tmux ───────────────────────────────────────────────────────────
1959
-
1960
- /**
1961
- * Install or upgrade tmux for Git Bash on Windows.
1962
- *
1963
- * Downloads tmux and libevent packages from the official MSYS2 package
1964
- * repository, extracts the required binaries, and places them in ~/bin/.
1965
- *
1966
- * Requirements:
1967
- * - Windows with Git Bash (provides tar and msys-2.0.dll runtime)
1968
- * - Node.js >= 21.7 (native zstd decompression)
1969
- *
1970
- * The install target is ~/bin/ because:
1971
- * - It's user-writable (no admin rights needed)
1972
- * - Git Bash includes it in PATH by default
1973
- * - It doesn't conflict with Git's own /usr/bin/
1974
- */
1975
-
1976
- const TMUX_PACKAGES = [
1977
- {
1978
- name: "tmux",
1979
- url: "https://mirror.msys2.org/msys/x86_64/tmux-3.6.a-1-x86_64.pkg.tar.zst",
1980
- version: "3.6a",
1981
- files: ["usr/bin/tmux.exe"],
1982
- },
1983
- {
1984
- name: "libevent",
1985
- url: "https://mirror.msys2.org/msys/x86_64/libevent-2.1.12-4-x86_64.pkg.tar.zst",
1986
- version: "2.1.12",
1987
- files: ["usr/bin/msys-event-2-1-7.dll", "usr/bin/msys-event_core-2-1-7.dll"],
1988
- },
1989
- ];
1990
-
1991
- const TMUX_INSTALL_DIR_NAME = "bin";
1992
-
1993
- function getTmuxInstallDir() {
1994
- return path.join(process.env.HOME || process.env.USERPROFILE || "", TMUX_INSTALL_DIR_NAME);
1995
- }
1996
-
1997
- function detectCurrentTmux() {
1998
- try {
1999
- const out = execSync("tmux -V", { encoding: "utf-8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"] }).trim();
2000
- const match = out.match(/tmux\s+([\d.]+\w*)/);
2001
- const version = match ? match[1] : "unknown";
2002
-
2003
- // Find where tmux lives
2004
- let location = "";
2005
- try {
2006
- location = execSync("which tmux", { encoding: "utf-8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"], shell: "C:/Program Files/Git/bin/bash.exe" }).trim();
2007
- } catch { /* ignore */ }
2008
-
2009
- return { installed: true, version, location };
2010
- } catch {
2011
- return { installed: false, version: null, location: null };
2012
- }
2013
- }
2014
-
2015
- async function httpFollowRedirects(url) {
2016
- const https = await import("node:https");
2017
- const http = await import("node:http");
2018
-
2019
- return new Promise((resolve, reject) => {
2020
- const mod = url.startsWith("https") ? https.default : http.default;
2021
- mod.get(url, { headers: { "User-Agent": "taskplane" } }, (res) => {
2022
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
2023
- return httpFollowRedirects(res.headers.location).then(resolve, reject);
2024
- }
2025
- if (res.statusCode !== 200) {
2026
- return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
2027
- }
2028
- const chunks = [];
2029
- res.on("data", (chunk) => chunks.push(chunk));
2030
- res.on("end", () => resolve(Buffer.concat(chunks)));
2031
- res.on("error", reject);
2032
- }).on("error", reject);
2033
- });
2034
- }
2035
-
2036
- function toPosixPath(p) {
2037
- return p.replace(/\\/g, "/").replace(/^([A-Z]):/i, (_m, d) => "/" + d.toLowerCase());
2038
- }
2039
-
2040
- async function cmdInstallTmux(args) {
2041
- const checkOnly = args.includes("--check");
2042
- const force = args.includes("--force");
2043
-
2044
- console.log(`\n${c.bold}Taskplane — tmux installer${c.reset}\n`);
2045
-
2046
- // ── Platform check ───────────────────────────────────────────
2047
- if (process.platform !== "win32") {
2048
- console.log(` ${INFO} This command is for Windows only.`);
2049
- console.log(` On macOS: ${c.cyan}brew install tmux${c.reset}`);
2050
- console.log(` On Linux: ${c.cyan}sudo apt install tmux${c.reset} (or your distro's package manager)\n`);
2051
- return;
2052
- }
2053
-
2054
- // ── Node.js version check (need >= 21.7 for zstd) ───────────
2055
- const [nodeMajor, nodeMinor] = process.versions.node.split(".").map(Number);
2056
- if (nodeMajor < 21 || (nodeMajor === 21 && nodeMinor < 7)) {
2057
- die(`Node.js >= 21.7 required for zstd decompression (found ${process.versions.node}).\n Upgrade Node.js: https://nodejs.org/`);
2058
- }
2059
-
2060
- // ── Git Bash check ───────────────────────────────────────────
2061
- const gitBashBin = "C:/Program Files/Git/bin/bash.exe";
2062
- if (!fs.existsSync(gitBashBin)) {
2063
- die(`Git Bash not found at ${gitBashBin}.\n Install Git for Windows: https://git-scm.com/downloads`);
2064
- }
2065
-
2066
- // ── MSYS2 runtime check ──────────────────────────────────────
2067
- const msysDll = "C:/Program Files/Git/usr/bin/msys-2.0.dll";
2068
- if (!fs.existsSync(msysDll)) {
2069
- die(`MSYS2 runtime (msys-2.0.dll) not found.\n This should ship with Git for Windows. Reinstall Git if missing.`);
2070
- }
2071
-
2072
- // ── Current tmux status ──────────────────────────────────────
2073
- const current = detectCurrentTmux();
2074
- if (current.installed) {
2075
- console.log(` ${OK} tmux ${c.bold}${current.version}${c.reset} found`);
2076
- if (current.location) {
2077
- console.log(` Location: ${c.dim}${current.location}${c.reset}`);
2078
- }
2079
- } else {
2080
- console.log(` ${FAIL} tmux not found`);
2081
- }
2082
-
2083
- const targetVersion = TMUX_PACKAGES[0].version;
2084
- console.log(` ${INFO} Available version: ${c.bold}${targetVersion}${c.reset} (MSYS2 package)\n`);
2085
-
2086
- if (checkOnly) return;
2087
-
2088
- // ── Skip if already up to date ───────────────────────────────
2089
- if (current.installed && current.version === targetVersion && !force) {
2090
- console.log(` ${OK} Already up to date. Use ${c.cyan}--force${c.reset} to reinstall.\n`);
2091
- return;
2092
- }
2093
-
2094
- // ── Download and extract ─────────────────────────────────────
2095
- const zlib = await import("node:zlib");
2096
- const os = await import("node:os");
2097
- const tmpDir = os.default.tmpdir();
2098
- const extractDir = path.join(tmpDir, "taskplane-tmux-install");
2099
-
2100
- // Clean previous extract
2101
- fs.rmSync(extractDir, { recursive: true, force: true });
2102
- fs.mkdirSync(extractDir, { recursive: true });
2103
-
2104
- for (const pkg of TMUX_PACKAGES) {
2105
- process.stdout.write(` ⏳ Downloading ${pkg.name} (v${pkg.version})...`);
2106
- let buf;
2107
- try {
2108
- buf = await httpFollowRedirects(pkg.url);
2109
- } catch (err) {
2110
- console.log(` ${c.red}failed${c.reset}`);
2111
- die(`Download failed: ${err.message}\n URL: ${pkg.url}`);
2112
- }
2113
- console.log(` ${c.green}${Math.round(buf.length / 1024)}KB${c.reset}`);
2114
-
2115
- // Decompress zstd → tar
2116
- let tar;
2117
- try {
2118
- tar = zlib.default.zstdDecompressSync(buf);
2119
- } catch (err) {
2120
- die(`zstd decompression failed for ${pkg.name}: ${err.message}`);
2121
- }
2122
- const tarPath = path.join(tmpDir, `${pkg.name}.pkg.tar`);
2123
- fs.writeFileSync(tarPath, tar);
2124
-
2125
- // Extract needed files
2126
- const posixTar = toPosixPath(tarPath);
2127
- const posixExtract = toPosixPath(extractDir);
2128
- for (const f of pkg.files) {
2129
- try {
2130
- execSync(`tar xf "${posixTar}" -C "${posixExtract}" ${f}`, {
2131
- shell: gitBashBin,
2132
- timeout: 10000,
2133
- stdio: ["pipe", "pipe", "pipe"],
2134
- });
2135
- } catch (err) {
2136
- die(`Failed to extract ${f} from ${pkg.name}: ${err.message}`);
2137
- }
2138
- }
2139
-
2140
- // Clean up tar
2141
- try { fs.unlinkSync(tarPath); } catch { /* best effort */ }
2142
- }
2143
-
2144
- // ── Install to ~/bin/ ────────────────────────────────────────
2145
- const installDir = getTmuxInstallDir();
2146
- fs.mkdirSync(installDir, { recursive: true });
2147
-
2148
- const extractBinDir = path.join(extractDir, "usr", "bin");
2149
- const installedFiles = [];
2150
- const lockedFiles = [];
2151
- for (const f of fs.readdirSync(extractBinDir)) {
2152
- const src = path.join(extractBinDir, f);
2153
- const dest = path.join(installDir, f);
2154
- try {
2155
- fs.copyFileSync(src, dest);
2156
- installedFiles.push(f);
2157
- } catch (err) {
2158
- if (err.code === "EBUSY" || err.code === "EPERM") {
2159
- // File is locked (e.g., DLL in use by running tmux session).
2160
- // Try rename-then-copy: rename old file, copy new, delete old.
2161
- const backup = dest + ".old";
2162
- try {
2163
- if (fs.existsSync(backup)) fs.unlinkSync(backup);
2164
- fs.renameSync(dest, backup);
2165
- fs.copyFileSync(src, dest);
2166
- try { fs.unlinkSync(backup); } catch { /* clean up later */ }
2167
- installedFiles.push(f);
2168
- } catch {
2169
- lockedFiles.push(f);
2170
- }
2171
- } else {
2172
- throw err;
2173
- }
2174
- }
2175
- }
2176
-
2177
- // Clean up extract dir
2178
- fs.rmSync(extractDir, { recursive: true, force: true });
2179
-
2180
- // ── Verify ───────────────────────────────────────────────────
2181
- console.log("");
2182
- if (lockedFiles.length > 0) {
2183
- console.log(` ${WARN} Some files are locked (tmux may be running): ${lockedFiles.join(", ")}`);
2184
- console.log(` Close all tmux sessions and re-run ${c.cyan}taskplane install-tmux --force${c.reset}`);
2185
- if (installedFiles.length > 0) {
2186
- console.log(` Updated: ${installedFiles.join(", ")}`);
2187
- }
2188
- } else {
2189
- const verify = detectCurrentTmux();
2190
- if (verify.installed) {
2191
- console.log(` ${OK} tmux ${c.bold}${verify.version}${c.reset} installed successfully`);
2192
- console.log(` Location: ${c.dim}${installDir}${c.reset}`);
2193
- console.log(` Files: ${installedFiles.join(", ")}`);
2194
- } else {
2195
- console.log(` ${WARN} Files installed to ${installDir} but tmux not found on PATH.`);
2196
- console.log(` Ensure ${c.cyan}~/bin${c.reset} is in your PATH. In Git Bash, add to ~/.bashrc:`);
2197
- console.log(` ${c.dim}export PATH="$HOME/bin:$PATH"${c.reset}`);
2198
- }
2199
- }
2200
- console.log("");
2201
- }
2202
-
2203
1958
  // ─── doctor ─────────────────────────────────────────────────────────────────
2204
1959
 
2205
1960
  function resolveDoctorConfigLocation(projectRoot, isWorkspaceMode) {
@@ -2272,46 +2027,6 @@ function cmdDoctor() {
2272
2027
  const isWorkspaceMode = wsResult.mode === "workspace";
2273
2028
  const configLocation = resolveDoctorConfigLocation(projectRoot, isWorkspaceMode);
2274
2029
 
2275
- // Check tmux and spawn_mode compatibility
2276
- const hasTmux = commandExists("tmux");
2277
- console.log(
2278
- ` ${hasTmux ? OK : `${WARN}`} tmux installed${hasTmux ? ` ${c.dim}(${getVersion("tmux", "-V")})${c.reset}` : ` ${c.dim}(optional — needed for spawn_mode: tmux)${c.reset}`}`
2279
- );
2280
- if (!hasTmux && process.platform === "win32") {
2281
- console.log(` ${c.dim}→ Run ${c.cyan}taskplane install-tmux${c.dim} to install${c.reset}`);
2282
- }
2283
-
2284
- // Check if project config requires tmux but it's not installed
2285
- const orchConfigPath = path.join(configLocation.root, configLocation.prefix, "task-orchestrator.yaml");
2286
- const orchJsonPath = path.join(configLocation.root, configLocation.prefix, "taskplane-config.json");
2287
- let projectSpawnMode = null;
2288
- try {
2289
- if (fs.existsSync(orchJsonPath)) {
2290
- const json = JSON.parse(fs.readFileSync(orchJsonPath, "utf-8"));
2291
- projectSpawnMode =
2292
- json?.orchestrator?.orchestrator?.spawnMode ||
2293
- json?.orchestrator?.spawnMode ||
2294
- json?.orchestrator?.spawn_mode ||
2295
- null;
2296
- } else if (fs.existsSync(orchConfigPath)) {
2297
- const raw = fs.readFileSync(orchConfigPath, "utf-8");
2298
- const match = raw.match(/spawn_mode:\s*["']?(\w+)["']?/);
2299
- if (match) projectSpawnMode = match[1];
2300
- }
2301
- } catch { /* best effort */ }
2302
-
2303
- if (projectSpawnMode === "tmux" && !hasTmux) {
2304
- issues++;
2305
- console.log(
2306
- ` ${FAIL} spawn_mode is ${c.bold}"tmux"${c.reset} but tmux is not installed`
2307
- );
2308
- if (process.platform === "win32") {
2309
- console.log(` ${c.dim}→ Run ${c.cyan}taskplane install-tmux${c.dim} to install tmux${c.reset}`);
2310
- } else {
2311
- console.log(` ${c.dim}→ Install tmux: ${c.cyan}brew install tmux${c.dim} (macOS) or ${c.cyan}sudo apt install tmux${c.dim} (Linux)${c.reset}`);
2312
- }
2313
- }
2314
-
2315
2030
  // Check package installation
2316
2031
  const pkgJson = path.join(PACKAGE_ROOT, "package.json");
2317
2032
  const pkgVersion = getPackageVersion();
@@ -2858,7 +2573,6 @@ ${c.bold}Usage:${c.reset}
2858
2573
  ${c.bold}Commands:${c.reset}
2859
2574
  ${c.cyan}init${c.reset} Scaffold Taskplane config in the current project
2860
2575
  ${c.cyan}doctor${c.reset} Validate installation and project configuration
2861
- ${c.cyan}install-tmux${c.reset} Install or upgrade tmux for Git Bash (Windows)
2862
2576
  ${c.cyan}version${c.reset} Show version information
2863
2577
  ${c.cyan}dashboard${c.reset} Launch the web-based orchestrator dashboard
2864
2578
  ${c.cyan}uninstall${c.reset} Remove Taskplane project files and/or package install
@@ -2886,13 +2600,7 @@ ${c.bold}Uninstall options:${c.reset}
2886
2600
  --remove-tasks Also remove task area directories from task-runner.yaml
2887
2601
  --all Equivalent to --package + --remove-tasks
2888
2602
 
2889
- ${c.bold}Install-tmux options:${c.reset}
2890
- --check Check tmux status without installing
2891
- --force Reinstall even if tmux is already present
2892
-
2893
2603
  ${c.bold}Examples:${c.reset}
2894
- taskplane install-tmux # Install or upgrade tmux
2895
- taskplane install-tmux --check # Check tmux status only
2896
2604
  taskplane init # Interactive project setup
2897
2605
  taskplane init --preset full # Quick setup with defaults
2898
2606
  taskplane init --preset full --tasks-root docs/task-management
@@ -2924,9 +2632,6 @@ switch (command) {
2924
2632
  case "doctor":
2925
2633
  cmdDoctor();
2926
2634
  break;
2927
- case "install-tmux":
2928
- await cmdInstallTmux(args);
2929
- break;
2930
2635
  case "version":
2931
2636
  case "--version":
2932
2637
  case "-v":
@@ -12,7 +12,7 @@
12
12
  const http = require("http");
13
13
  const fs = require("fs");
14
14
  const path = require("path");
15
- const { execFileSync, exec } = require("child_process");
15
+ const { exec } = require("child_process");
16
16
  // url module not needed — we parse with new URL() below
17
17
 
18
18
  // ─── Configuration ──────────────────────────────────────────────────────────
@@ -192,17 +192,9 @@ function parseStatusMd(taskFolder) {
192
192
  }
193
193
 
194
194
  function getTmuxSessions() {
195
- try {
196
- const output = execFileSync('tmux list-sessions -F "#{session_name}"', {
197
- encoding: "utf-8",
198
- timeout: 5000,
199
- shell: true,
200
- stdio: ["ignore", "pipe", "ignore"],
201
- }).trim();
202
- return output ? output.split("\n").map((s) => s.trim()).filter(Boolean) : [];
203
- } catch {
204
- return [];
205
- }
195
+ // Runtime V2 no longer relies on TMUX sessions.
196
+ // Keep field shape stable for dashboard clients that still read `tmuxSessions`.
197
+ return [];
206
198
  }
207
199
 
208
200
  function checkDoneFile(taskFolder) {
@@ -1206,17 +1198,9 @@ function handlePaneSSE(req, res, sessionName) {
1206
1198
  });
1207
1199
  }
1208
1200
 
1209
- function captureTmuxPane(sessionName) {
1210
- try {
1211
- // Capture with ANSI escape sequences (-e), full scrollback visible area (-p)
1212
- const output = execFileSync(
1213
- `tmux capture-pane -t "${sessionName}" -p -e`,
1214
- { encoding: "utf-8", timeout: 3000, shell: true, stdio: ["ignore", "pipe", "ignore"] }
1215
- );
1216
- return output;
1217
- } catch {
1218
- return null;
1219
- }
1201
+ function captureTmuxPane(_sessionName) {
1202
+ // Runtime V2 no longer captures TMUX panes.
1203
+ return null;
1220
1204
  }
1221
1205
 
1222
1206
  function broadcastPaneCaptures() {