@frumu/tandem 0.4.44 → 0.5.1

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.
Files changed (2) hide show
  1. package/bin/tandem.js +77 -1
  2. package/package.json +1 -1
package/bin/tandem.js CHANGED
@@ -525,6 +525,72 @@ async function buildDiagnostics(env = process.env) {
525
525
  };
526
526
  }
527
527
 
528
+ function buildWorktreeCleanupPayload(cli) {
529
+ const repoRoot = String(cli.value("repo-root") || "").trim();
530
+ return {
531
+ repo_root: repoRoot || undefined,
532
+ dry_run: !cli.has("apply"),
533
+ remove_orphan_dirs: !cli.has("keep-orphan-dirs"),
534
+ };
535
+ }
536
+
537
+ async function requestWorktreeCleanup(cli, env = process.env) {
538
+ const paths = resolveTandemPaths(env);
539
+ const engineUrl = `http://${paths.engineHost}:${paths.enginePort}`;
540
+ const response = await fetch(`${engineUrl}/worktree/cleanup`, {
541
+ method: "POST",
542
+ headers: { "content-type": "application/json" },
543
+ body: JSON.stringify(buildWorktreeCleanupPayload(cli)),
544
+ signal: AbortSignal.timeout(15_000),
545
+ });
546
+ if (!response.ok) {
547
+ const text = await response.text().catch(() => "");
548
+ throw new Error(`Worktree cleanup failed (${response.status})${text ? `: ${text}` : ""}`);
549
+ }
550
+ return await response.json();
551
+ }
552
+
553
+ function printWorktreeCleanupReport(report, json = false) {
554
+ if (json) {
555
+ console.log(JSON.stringify(report, null, 2));
556
+ return;
557
+ }
558
+ const staleCount = Array.isArray(report.stale_paths) ? report.stale_paths.length : 0;
559
+ const activeCount = Array.isArray(report.active_paths) ? report.active_paths.length : 0;
560
+ const removedCount =
561
+ (Array.isArray(report.cleaned_worktrees) ? report.cleaned_worktrees.length : 0) +
562
+ (Array.isArray(report.orphan_dirs_removed) ? report.orphan_dirs_removed.length : 0);
563
+ const failureCount = Array.isArray(report.failures) ? report.failures.length : 0;
564
+ printLines([
565
+ `[Tandem] worktree cleanup: ${report.dry_run ? "preview" : "applied"}`,
566
+ `[Tandem] repo root: ${report.repo_root || "unknown"}`,
567
+ `[Tandem] managed root: ${report.managed_root || "unknown"}`,
568
+ `[Tandem] active tracked: ${activeCount}`,
569
+ `[Tandem] stale candidates: ${staleCount}`,
570
+ `[Tandem] removed: ${removedCount}`,
571
+ `[Tandem] failures: ${failureCount}`,
572
+ ]);
573
+ const logRows = [];
574
+ for (const row of Array.isArray(report.cleaned_worktrees) ? report.cleaned_worktrees : []) {
575
+ logRows.push(` removed worktree: ${row.path}${row.branch ? ` (${row.branch})` : ""}`);
576
+ }
577
+ for (const row of Array.isArray(report.orphan_dirs_removed) ? report.orphan_dirs_removed : []) {
578
+ logRows.push(` removed orphan dir: ${row.path}`);
579
+ }
580
+ if (report.dry_run) {
581
+ for (const row of Array.isArray(report.stale_paths) ? report.stale_paths : []) {
582
+ logRows.push(` stale candidate: ${row.path}${row.branch ? ` (${row.branch})` : ""}`);
583
+ }
584
+ for (const path of Array.isArray(report.orphan_dirs) ? report.orphan_dirs : []) {
585
+ logRows.push(` orphan dir: ${path}`);
586
+ }
587
+ }
588
+ for (const row of Array.isArray(report.failures) ? report.failures : []) {
589
+ logRows.push(` failure: ${row.path || row.code || "unknown"}${row.error ? ` -> ${row.error}` : row.stderr ? ` -> ${row.stderr}` : ""}`);
590
+ }
591
+ if (logRows.length) printLines(logRows);
592
+ }
593
+
528
594
  async function printDiagnostics(report, json = false) {
529
595
  if (json) {
530
596
  console.log(JSON.stringify(report, null, 2));
@@ -744,7 +810,7 @@ async function main(argv = process.argv.slice(2), env = process.env) {
744
810
 
745
811
  if (!command) {
746
812
  console.log(`[Tandem] ${packageInfo.name} ${packageInfo.version}`);
747
- console.log("[Tandem] Use: tandem doctor | tandem status | tandem service install | tandem install panel");
813
+ console.log("[Tandem] Use: tandem doctor | tandem doctor worktrees | tandem status | tandem service install | tandem install panel");
748
814
  return 0;
749
815
  }
750
816
 
@@ -754,6 +820,7 @@ async function main(argv = process.argv.slice(2), env = process.env) {
754
820
  "",
755
821
  "Commands:",
756
822
  " tandem doctor",
823
+ " tandem doctor worktrees [--repo-root /abs/path] [--apply] [--json]",
757
824
  " tandem status",
758
825
  " tandem service install|start|stop|restart|status|logs",
759
826
  " tandem install panel",
@@ -767,6 +834,12 @@ async function main(argv = process.argv.slice(2), env = process.env) {
767
834
  }
768
835
 
769
836
  if (command === "doctor") {
837
+ const subcommand = String(argv[1] || "").trim().toLowerCase();
838
+ if (subcommand === "worktrees" || subcommand === "worktree") {
839
+ const report = await requestWorktreeCleanup(cli, env);
840
+ printWorktreeCleanupReport(report, cli.has("json"));
841
+ return Array.isArray(report.failures) && report.failures.length ? 1 : 0;
842
+ }
770
843
  const report = await buildDiagnostics(env);
771
844
  await printDiagnostics(report, cli.has("json"));
772
845
  return report.engine.reachable ? 0 : 1;
@@ -853,10 +926,13 @@ module.exports = {
853
926
  parseArgs,
854
927
  printDiagnostics,
855
928
  printStatus,
929
+ printWorktreeCleanupReport,
856
930
  queryEngineServiceState,
931
+ requestWorktreeCleanup,
857
932
  resolveTandemHomeDir,
858
933
  resolveTandemPaths,
859
934
  runAddonCli,
860
935
  runCommand,
936
+ buildWorktreeCleanupPayload,
861
937
  updatePackage,
862
938
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frumu/tandem",
3
- "version": "0.4.44",
3
+ "version": "0.5.1",
4
4
  "description": "Tandem master CLI and engine binary distribution",
5
5
  "homepage": "https://tandem.ac",
6
6
  "bin": {