Profpatsch/.claude/ghci-capture.zig
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
const std = @import("std");
const print = std.debug.print;

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    // Parse command line arguments
    const args = try std.process.argsAlloc(allocator);
    const session_name = if (args.len > 1) args[1] else "whatcd-repl";

    // Run tmux capture command
    const tmux_cmd = try std.fmt.allocPrint(allocator, "tmux capture-pane -t {s} -S -500 -p", .{session_name});

    // Execute tmux capture
    var child = std.process.Child.init(&[_][]const u8{ "sh", "-c", tmux_cmd }, allocator);
    child.stdout_behavior = .Pipe;
    child.stderr_behavior = .Pipe;

    try child.spawn();
    
    // Read the output first
    const stdout = child.stdout.?.reader();
    const content = try stdout.readAllAlloc(allocator, 1024 * 1024); // 1MB max
    
    const result = try child.wait();

    if (result != .Exited or result.Exited != 0) {
        std.debug.print("Error: Could not capture from tmux session '{s}'\n", .{session_name});
        
        // Show available sessions
        var list_child = std.process.Child.init(&[_][]const u8{ "tmux", "list-sessions" }, allocator);
        list_child.stdout_behavior = .Inherit;
        list_child.stderr_behavior = .Inherit;
        _ = list_child.spawn() catch {
            std.debug.print("No tmux sessions found\n", .{});
            return;
        };
        _ = try list_child.wait();
        return;
    }

    if (content.len == 0) {
        std.debug.print("Error: No output captured from session '{s}'\n", .{session_name});
        return;
    }

    // Find last occurrence of '\nghci>'
    const needle = "\nghci>";
    const last_prompt_pos = std.mem.lastIndexOf(u8, content, needle);

    if (last_prompt_pos == null) {
        std.debug.print("Warning: No 'ghci>' prompt found. Showing last 1000 chars:\n", .{});
        const start = if (content.len > 1000) content.len - 1000 else 0;
        print("{s}\n", .{content[start..]});
        return;
    }

    // Check if the content after the last prompt is essentially empty (just whitespace)
    const from_last_prompt = content[last_prompt_pos.?..];
    var has_meaningful_content = false;
    var i: usize = needle.len; // Skip past "ghci>"
    while (i < from_last_prompt.len) : (i += 1) {
        const char = from_last_prompt[i];
        if (char != ' ' and char != '\t' and char != '\n' and char != '\r') {
            has_meaningful_content = true;
            break;
        }
    }

    // If last prompt has no meaningful content, show more context
    if (!has_meaningful_content) {
        // Find the second-to-last prompt and show from there
        const before_last = content[0..last_prompt_pos.?];
        const second_last_prompt_pos = std.mem.lastIndexOf(u8, before_last, needle);
        
        if (second_last_prompt_pos != null) {
            print("{s}", .{content[second_last_prompt_pos.?..]});
        } else {
            // No second prompt found, show last 1000 chars
            const start = if (content.len > 1000) content.len - 1000 else 0;
            print("{s}", .{content[start..]});
        }
    } else {
        // Print everything from the last prompt onwards
        print("{s}", .{from_last_prompt});
    }
}