zdev 0.2.3 → 0.2.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/dist/index.js CHANGED
@@ -2764,6 +2764,20 @@ Run 'zdev list' to see active features`);
2764
2764
 
2765
2765
  // src/commands/list.ts
2766
2766
  import { existsSync as existsSync6 } from "fs";
2767
+ function getTmuxSessions(pattern) {
2768
+ const socketDir = process.env.CLAWDBOT_TMUX_SOCKET_DIR || "/tmp/clawdbot-tmux-sockets";
2769
+ const socket = `${socketDir}/clawdbot.sock`;
2770
+ let result = run("tmux", ["-S", socket, "list-sessions", "-F", "#{session_name}"]);
2771
+ if (!result.success) {
2772
+ result = run("tmux", ["list-sessions", "-F", "#{session_name}"]);
2773
+ }
2774
+ if (!result.success)
2775
+ return [];
2776
+ const sessions = result.stdout.split(`
2777
+ `).filter(Boolean);
2778
+ const patternWords = pattern.split("-").filter((w) => w.length > 2);
2779
+ return sessions.filter((name) => patternWords.some((word) => name.toLowerCase().includes(word.toLowerCase())));
2780
+ }
2767
2781
  async function list(options = {}) {
2768
2782
  const config = loadConfig();
2769
2783
  const allocations = Object.entries(config.allocations);
@@ -2798,7 +2812,20 @@ No active features.
2798
2812
  const worktreeExists = existsSync6(worktreePath);
2799
2813
  const frontendRunning = alloc.pids.frontend ? isProcessRunning(alloc.pids.frontend) : false;
2800
2814
  const convexRunning = alloc.pids.convex ? isProcessRunning(alloc.pids.convex) : false;
2801
- const statusEmoji = frontendRunning && convexRunning ? "\uD83D\uDFE2" : frontendRunning || convexRunning ? "\uD83D\uDFE1" : "\uD83D\uDD34";
2815
+ const featureSlug = name.toLowerCase().replace(/[^a-z0-9]/g, "-");
2816
+ const projectSlug = alloc.project.toLowerCase().replace(/[^a-z0-9]/g, "-");
2817
+ const featureOnly = alloc.branch.replace("feature/", "").toLowerCase().replace(/[^a-z0-9]/g, "-");
2818
+ let tmuxSessions = getTmuxSessions(featureSlug);
2819
+ if (tmuxSessions.length === 0) {
2820
+ tmuxSessions = getTmuxSessions(projectSlug);
2821
+ }
2822
+ if (tmuxSessions.length === 0 && featureOnly !== projectSlug) {
2823
+ tmuxSessions = getTmuxSessions(featureOnly);
2824
+ }
2825
+ const hasTmux = tmuxSessions.length > 0;
2826
+ const isRunning = frontendRunning || convexRunning || hasTmux;
2827
+ const isFullyRunning = frontendRunning && convexRunning || hasTmux && tmuxSessions.length >= 2;
2828
+ const statusEmoji = isFullyRunning ? "\uD83D\uDFE2" : isRunning ? "\uD83D\uDFE1" : "\uD83D\uDD34";
2802
2829
  console.log(`${statusEmoji} ${name}`);
2803
2830
  console.log(` Project: ${alloc.project}`);
2804
2831
  console.log(` Branch: ${alloc.branch}`);
@@ -2807,8 +2834,15 @@ No active features.
2807
2834
  if (alloc.funnelPath && traefikStatus.devDomain) {
2808
2835
  console.log(` Public: https://${alloc.funnelPath}.${traefikStatus.devDomain}`);
2809
2836
  }
2810
- console.log(` Frontend: ${frontendRunning ? `running (PID: ${alloc.pids.frontend})` : "stopped"}`);
2811
- console.log(` Convex: ${convexRunning ? `running (PID: ${alloc.pids.convex})` : "stopped"}`);
2837
+ if (alloc.pids.frontend || alloc.pids.convex) {
2838
+ console.log(` Frontend: ${frontendRunning ? `running (PID: ${alloc.pids.frontend})` : "stopped"}`);
2839
+ console.log(` Convex: ${convexRunning ? `running (PID: ${alloc.pids.convex})` : "stopped"}`);
2840
+ }
2841
+ if (hasTmux) {
2842
+ console.log(` Tmux: ${tmuxSessions.join(", ")}`);
2843
+ } else if (!frontendRunning && !convexRunning) {
2844
+ console.log(` Servers: stopped`);
2845
+ }
2812
2846
  console.log(` Started: ${new Date(alloc.started).toLocaleString()}`);
2813
2847
  console.log();
2814
2848
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zdev",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Multi-agent worktree development environment for cloud dev with preview URLs",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,7 +5,32 @@ import {
5
5
  ZEBU_HOME,
6
6
  WORKTREES_DIR,
7
7
  } from "../config.js";
8
- import { isProcessRunning, getTraefikStatus } from "../utils.js";
8
+ import { isProcessRunning, getTraefikStatus, run } from "../utils.js";
9
+
10
+ // Get tmux sessions matching a pattern
11
+ function getTmuxSessions(pattern: string): string[] {
12
+ const socketDir = process.env.CLAWDBOT_TMUX_SOCKET_DIR || "/tmp/clawdbot-tmux-sockets";
13
+ const socket = `${socketDir}/clawdbot.sock`;
14
+
15
+ // Try clawdbot socket first
16
+ let result = run("tmux", ["-S", socket, "list-sessions", "-F", "#{session_name}"]);
17
+
18
+ if (!result.success) {
19
+ // Fall back to default tmux socket
20
+ result = run("tmux", ["list-sessions", "-F", "#{session_name}"]);
21
+ }
22
+
23
+ if (!result.success) return [];
24
+
25
+ const sessions = result.stdout.split("\n").filter(Boolean);
26
+
27
+ // Match if session name contains any word from the pattern
28
+ const patternWords = pattern.split("-").filter(w => w.length > 2);
29
+
30
+ return sessions.filter(name =>
31
+ patternWords.some(word => name.toLowerCase().includes(word.toLowerCase()))
32
+ );
33
+ }
9
34
 
10
35
  export interface ListOptions {
11
36
  json?: boolean;
@@ -52,8 +77,25 @@ export async function list(options: ListOptions = {}): Promise<void> {
52
77
  ? isProcessRunning(alloc.pids.convex)
53
78
  : false;
54
79
 
55
- const statusEmoji = frontendRunning && convexRunning ? "🟢" :
56
- frontendRunning || convexRunning ? "🟡" : "🔴";
80
+ // Check for tmux sessions related to this feature
81
+ // Try multiple patterns: full name, project name, feature name
82
+ const featureSlug = name.toLowerCase().replace(/[^a-z0-9]/g, "-");
83
+ const projectSlug = alloc.project.toLowerCase().replace(/[^a-z0-9]/g, "-");
84
+ const featureOnly = alloc.branch.replace("feature/", "").toLowerCase().replace(/[^a-z0-9]/g, "-");
85
+
86
+ let tmuxSessions = getTmuxSessions(featureSlug);
87
+ if (tmuxSessions.length === 0) {
88
+ tmuxSessions = getTmuxSessions(projectSlug);
89
+ }
90
+ if (tmuxSessions.length === 0 && featureOnly !== projectSlug) {
91
+ tmuxSessions = getTmuxSessions(featureOnly);
92
+ }
93
+ const hasTmux = tmuxSessions.length > 0;
94
+
95
+ const isRunning = frontendRunning || convexRunning || hasTmux;
96
+ const isFullyRunning = (frontendRunning && convexRunning) || (hasTmux && tmuxSessions.length >= 2);
97
+
98
+ const statusEmoji = isFullyRunning ? "🟢" : isRunning ? "🟡" : "🔴";
57
99
 
58
100
  console.log(`${statusEmoji} ${name}`);
59
101
  console.log(` Project: ${alloc.project}`);
@@ -65,8 +107,19 @@ export async function list(options: ListOptions = {}): Promise<void> {
65
107
  console.log(` Public: https://${alloc.funnelPath}.${traefikStatus.devDomain}`);
66
108
  }
67
109
 
68
- console.log(` Frontend: ${frontendRunning ? `running (PID: ${alloc.pids.frontend})` : "stopped"}`);
69
- console.log(` Convex: ${convexRunning ? `running (PID: ${alloc.pids.convex})` : "stopped"}`);
110
+ // Show PID-based status
111
+ if (alloc.pids.frontend || alloc.pids.convex) {
112
+ console.log(` Frontend: ${frontendRunning ? `running (PID: ${alloc.pids.frontend})` : "stopped"}`);
113
+ console.log(` Convex: ${convexRunning ? `running (PID: ${alloc.pids.convex})` : "stopped"}`);
114
+ }
115
+
116
+ // Show tmux sessions if any
117
+ if (hasTmux) {
118
+ console.log(` Tmux: ${tmuxSessions.join(", ")}`);
119
+ } else if (!frontendRunning && !convexRunning) {
120
+ console.log(` Servers: stopped`);
121
+ }
122
+
70
123
  console.log(` Started: ${new Date(alloc.started).toLocaleString()}`);
71
124
  console.log();
72
125
  }