taskplane 0.24.3 → 0.24.4
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":
|
package/dashboard/server.cjs
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const http = require("http");
|
|
13
13
|
const fs = require("fs");
|
|
14
14
|
const path = require("path");
|
|
15
|
-
const {
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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(
|
|
1210
|
-
|
|
1211
|
-
|
|
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() {
|