Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386const std = @import("std");const Io = std.Io;const net = std.Io.net;const fmt = @import("format.zig");
const mode_cmd = @import("mode.zig");const schedule_cmd = @import("schedule.zig");const status_cmd = @import("status.zig");const config_cmd = @import("config.zig");const pomodoro_cmd = @import("pomodoro.zig");const media_cmd = @import("media.zig");const routine_cmd = @import("routine.zig");const sync_cmd = @import("sync.zig");const task_cmd = @import("tasks.zig");const plan_cmd = @import("plan.zig");const habit_cmd = @import("habits.zig");const playlist_cmd = @import("playlist.zig");const daemon = @import("../daemon/daemon.zig");const Config = @import("../config/loader.zig").Config;
pub fn run(io: Io, allocator: std.mem.Allocator, args: []const []const u8, cfg: Config) !void { if (args.len < 2) { try printUsage(io); return; }
const command = args[1];
if (std.mem.eql(u8, command, "mode")) { try mode_cmd.run(io, allocator, args[2..], cfg); } else if (std.mem.eql(u8, command, "schedule")) { try schedule_cmd.run(io, allocator, args[2..], cfg); } else if (std.mem.eql(u8, command, "status")) { try status_cmd.run(io, allocator, cfg); } else if (std.mem.eql(u8, command, "config")) { try config_cmd.run(io, allocator, args[2..], cfg); } else if (std.mem.eql(u8, command, "pomodoro")) { try pomodoro_cmd.run(io, allocator, args[2..], cfg); } else if (std.mem.eql(u8, command, "media")) { try media_cmd.run(io, allocator, args[2..], cfg); } else if (std.mem.eql(u8, command, "routine")) { try routine_cmd.run(io, allocator, args[2..], cfg); } else if (std.mem.eql(u8, command, "sync")) { try sync_cmd.run(io, allocator, args[2..], cfg); } else if (std.mem.eql(u8, command, "task")) { try task_cmd.run(io, allocator, args[2..]); } else if (std.mem.eql(u8, command, "plan")) { try plan_cmd.run(io, allocator, args[2..], cfg); } else if (std.mem.eql(u8, command, "habit")) { try habit_cmd.run(allocator, io, args[2..]); } else if (std.mem.eql(u8, command, "playlist")) { try playlist_cmd.run(allocator, io, args[2..]); } else if (std.mem.eql(u8, command, "process")) { try processCmd(io, allocator, args[2..]); } else if (std.mem.eql(u8, command, "iptables")) { try iptablesCmd(io, allocator, args[2..]); } else if (std.mem.eql(u8, command, "daemon")) { if (args.len > 2 and std.mem.eql(u8, args[2], "stop")) { const pid_content = std.Io.Dir.cwd().readFileAlloc(io, "/tmp/focusd.pid", allocator, .limited(32)) catch { try fmt.err(io, "no daemon running (no PID file)"); return; }; defer allocator.free(pid_content); const pid_str = std.mem.trim(u8, pid_content, "\n\r "); const pid = std.fmt.parseInt(i32, pid_str, 10) catch { try fmt.err(io, "invalid PID in file"); return; }; std.posix.kill(pid, std.posix.SIG.TERM) catch { try fmt.err(io, "failed to stop daemon"); return; }; try fmt.success(io, "daemon stopped"); } else { try daemon.runDaemon(allocator, io); } } else if (std.mem.eql(u8, command, "help") or std.mem.eql(u8, command, "--help")) { try printUsage(io); } else if (std.mem.eql(u8, command, "version") or std.mem.eql(u8, command, "--version")) { try Io.File.stdout().writeStreamingAll(io, "focusd 0.1.0\n"); } else { // Show condensed command list (like zig) const stdout = Io.File.stdout(); try stdout.writeStreamingAll(io, "\n"); try fmt.boldText(stdout, io, "Commands:\n"); try stdout.writeStreamingAll(io, "\n"); try fmt.dimText(stdout, io, " mode Manage focus modes\n"); try fmt.dimText(stdout, io, " schedule Manage time-based schedule rules\n"); try fmt.dimText(stdout, io, " task Manage task inventory\n"); try fmt.dimText(stdout, io, " habit Track daily habits with streaks\n"); try fmt.dimText(stdout, io, " playlist Focus music playback\n"); try fmt.dimText(stdout, io, " pomodoro Pomodoro timer sessions\n"); try fmt.dimText(stdout, io, " media Weekly media curation\n"); try fmt.dimText(stdout, io, " routine Cal Newport daily routines\n"); try fmt.dimText(stdout, io, " plan Weekly planning & reports\n"); try fmt.dimText(stdout, io, " sync Sync with external services\n"); try fmt.dimText(stdout, io, " status Daemon status overview\n"); try fmt.dimText(stdout, io, " config View/set configuration\n"); try fmt.dimText(stdout, io, " process Process monitor control\n"); try fmt.dimText(stdout, io, " iptables Iptables blocking control\n"); try fmt.dimText(stdout, io, " daemon Run the background daemon\n"); try fmt.dimText(stdout, io, " help Print this help and exit\n"); try fmt.dimText(stdout, io, " version Print version number and exit\n"); try stdout.writeStreamingAll(io, "\n");
// Suggest closest match (like git) const closest = findClosestCommand(command); if (closest) |suggestion| { var hint_buf: [128]u8 = undefined; const hint = std.fmt.bufPrint(&hint_buf, "'{s}' is similar to '{s}'", .{ suggestion, command }) catch ""; try fmt.dimText(stdout, io, hint); try stdout.writeStreamingAll(io, "\n\n"); }
// Error at the bottom (like zig) try fmt.err(io, "unknown command: "); try fmt.boldText(stdout, io, command); try stdout.writeStreamingAll(io, "\n"); }}
fn findClosestCommand(input: []const u8) ?[]const u8 { const commands = [_][]const u8{ "mode", "schedule", "task", "habit", "playlist", "pomodoro", "media", "routine", "plan", "sync", "status", "config", "process", "iptables", "daemon", "help", "version", };
var best_match: ?[]const u8 = null; var best_distance: usize = 3; // Max distance to consider
for (commands) |cmd| { const distance = levenshteinDistance(input, cmd); if (distance < best_distance) { best_distance = distance; best_match = cmd; } }
return best_match;}
fn levenshteinDistance(a: []const u8, b: []const u8) usize { const a_len = a.len; const b_len = b.len;
// Quick checks if (a_len == 0) return b_len; if (b_len == 0) return a_len; if (a_len == b_len and std.mem.eql(u8, a, b)) return 0;
// Simple distance for short strings var distance: usize = 0; var i: usize = 0; var j: usize = 0;
while (i < a_len and j < b_len) { if (a[i] == b[j]) { i += 1; j += 1; } else { distance += 1; if (i + 1 < a_len and j + 1 < b_len) { // Check if substitution or insertion/deletion is better if (a[i + 1] == b[j]) { i += 1; } else if (a[i] == b[j + 1]) { j += 1; } else { i += 1; j += 1; } } else if (i + 1 < a_len) { i += 1; } else { j += 1; } } }
distance += (a_len - i) + (b_len - j); return distance;}
fn processCmd(io: Io, allocator: std.mem.Allocator, args: []const []const u8) !void { const client = @import("../ipc/client.zig"); const stdout = Io.File.stdout();
if (args.len == 0) { try fmt.boldText(stdout, io, "focusd process"); try stdout.writeStreamingAll(io, " — process monitor control\n\n"); try fmt.boldText(stdout, io, "COMMANDS\n"); try stdout.writeStreamingAll(io, " status Show process monitor status\n"); try stdout.writeStreamingAll(io, " scan Scan and kill blocked processes\n"); return; }
const subcommand = args[0]; const c = client.Client.init(allocator, io);
if (std.mem.eql(u8, subcommand, "status")) { const raw = c.sendRaw("process status") catch { try fmt.err(io, "could not connect to daemon"); try fmt.warn(io, "start with: sudo focusd daemon"); return; }; defer allocator.free(raw); try stdout.writeStreamingAll(io, raw); try stdout.writeStreamingAll(io, "\n"); } else if (std.mem.eql(u8, subcommand, "scan")) { const raw = c.sendRaw("process scan") catch { try fmt.err(io, "could not connect to daemon"); return; }; defer allocator.free(raw); try stdout.writeStreamingAll(io, raw); try stdout.writeStreamingAll(io, "\n"); } else { try fmt.err(io, "unknown process command"); }}
fn iptablesCmd(io: Io, allocator: std.mem.Allocator, args: []const []const u8) !void { const client = @import("../ipc/client.zig"); const stdout = Io.File.stdout();
if (args.len == 0) { try fmt.boldText(stdout, io, "focusd iptables"); try stdout.writeStreamingAll(io, " — iptables blocking control\n\n"); try fmt.boldText(stdout, io, "COMMANDS\n"); try stdout.writeStreamingAll(io, " status Show iptables blocking status\n"); try stdout.writeStreamingAll(io, " cleanup Remove blocking chain\n"); return; }
const subcommand = args[0]; const c = client.Client.init(allocator, io);
if (std.mem.eql(u8, subcommand, "status")) { const raw = c.sendRaw("iptables status") catch { try fmt.err(io, "could not connect to daemon"); return; }; defer allocator.free(raw); try stdout.writeStreamingAll(io, raw); try stdout.writeStreamingAll(io, "\n"); } else if (std.mem.eql(u8, subcommand, "cleanup")) { const response = c.sendCommand("iptables cleanup") catch { try fmt.err(io, "could not connect to daemon"); return; }; switch (response) { .ok => try fmt.success(io, "iptables chain cleaned up"), else => try fmt.err(io, "could not cleanup iptables"), } } else { try fmt.err(io, "unknown iptables command"); }}
fn printUsage(io: Io) !void { const w = Io.File.stdout();
// Title try fmt.boldText(w, io, "focusd"); try fmt.dimText(w, io, " — Deep Focus Media Control System\n\n");
// Commands section try fmt.boldText(w, io, "COMMANDS\n"); try fmt.coloredText(w, io, " mode", fmt.Color.bold); try fmt.dimText(w, io, " Manage focus modes\n"); try fmt.coloredText(w, io, " schedule", fmt.Color.bold); try fmt.dimText(w, io, " Manage time-based schedule rules\n"); try fmt.coloredText(w, io, " task", fmt.Color.bold); try fmt.dimText(w, io, " Manage task inventory\n"); try fmt.coloredText(w, io, " pomodoro", fmt.Color.bold); try fmt.dimText(w, io, " Pomodoro timer sessions\n"); try fmt.coloredText(w, io, " media", fmt.Color.bold); try fmt.dimText(w, io, " Weekly media curation\n"); try fmt.coloredText(w, io, " routine", fmt.Color.bold); try fmt.dimText(w, io, " Cal Newport daily routines\n"); try fmt.coloredText(w, io, " plan", fmt.Color.bold); try fmt.dimText(w, io, " Weekly planning & reports\n"); try fmt.coloredText(w, io, " sync", fmt.Color.bold); try fmt.dimText(w, io, " Sync with external services\n"); try fmt.coloredText(w, io, " status", fmt.Color.bold); try fmt.dimText(w, io, " Daemon status overview\n"); try fmt.coloredText(w, io, " config", fmt.Color.bold); try fmt.dimText(w, io, " View/set configuration\n"); try fmt.coloredText(w, io, " process", fmt.Color.bold); try fmt.dimText(w, io, " Process monitor control\n"); try fmt.coloredText(w, io, " iptables", fmt.Color.bold); try fmt.dimText(w, io, " Iptables blocking control\n"); try fmt.coloredText(w, io, " daemon", fmt.Color.bold); try fmt.dimText(w, io, " Run the background daemon\n"); try fmt.coloredText(w, io, " help", fmt.Color.bold); try fmt.dimText(w, io, " Show this help message\n"); try fmt.coloredText(w, io, " version", fmt.Color.bold); try fmt.dimText(w, io, " Show version\n");
// Subcommands try fmt.writeSection(w, io, "MODE SUBCOMMANDS"); try fmt.writeStatusLine(w, io, " mode <name>", "Enter mode (work, reading, gaming, learning, writing, offline)", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " mode exit", "Exit current mode", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " mode list", "List available modes", fmt.Color.cyan);
try fmt.writeSection(w, io, "SCHEDULE SUBCOMMANDS"); try fmt.writeStatusLine(w, io, " schedule list", "Show schedule rules", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " schedule add", "Add schedule entry", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " schedule remove", "Remove schedule entry", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " schedule clear", "Clear all rules", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " schedule export", "Export to Waybar YAML", fmt.Color.cyan);
try fmt.writeSection(w, io, "TASK SUBCOMMANDS"); try fmt.writeStatusLine(w, io, " task add", "Add new task", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " task list", "Show all tasks", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " task done <id>", "Mark task completed", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " task status <id> <s>", "Update task status", fmt.Color.cyan);
try fmt.writeSection(w, io, "MEDIA SUBCOMMANDS"); try fmt.writeStatusLine(w, io, " media list", "Show weekly media plan", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " media add", "Add media to plan", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " media done <index>", "Mark media consumed", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " media plan", "Friday review guide", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " media newsletters", "Fetch from Gmail", fmt.Color.cyan);
try fmt.writeSection(w, io, "ROUTINE SUBCOMMANDS"); try fmt.writeStatusLine(w, io, " routine morning", "Morning routine (05:00)", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " routine shutdown", "Shutdown routine (17:00)", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " routine friday", "Friday weekly review", fmt.Color.cyan);
try fmt.writeSection(w, io, "POMODORO SUBCOMMANDS"); try fmt.writeStatusLine(w, io, " pomodoro start", "Start work session (25 min)", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " pomodoro stop", "Stop current session", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " pomodoro pause", "Pause/resume session", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " pomodoro break", "Start break manually", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " pomodoro config", "Configure durations", fmt.Color.cyan);
try fmt.writeSection(w, io, "PLAN SUBCOMMANDS"); try fmt.writeStatusLine(w, io, " plan slots", "Available time slots", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " plan week", "Weekly planning view", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " plan review", "Friday review checklist", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " plan book", "Current book from plan", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " plan report", "Generate weekly report", fmt.Color.cyan);
try fmt.writeSection(w, io, "SYNC SUBCOMMANDS"); try fmt.writeStatusLine(w, io, " sync push", "Push to blocks.adjarnor.dev", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " sync pull", "Pull from blocks.adjarnor.dev", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " sync calendar", "Show Google Calendar events", fmt.Color.cyan); try fmt.writeStatusLine(w, io, " sync calendar-update", "Write schedule to Calendar", fmt.Color.cyan);
// Modes try fmt.writeSection(w, io, "MODES"); try fmt.writeStatusLine(w, io, " work", "Focused professional work", fmt.Color.green); try fmt.writeStatusLine(w, io, " reading", "Deep reading and learning", fmt.Color.green); try fmt.writeStatusLine(w, io, " gaming", "Gaming time", fmt.Color.green); try fmt.writeStatusLine(w, io, " learning", "Active learning sessions", fmt.Color.green); try fmt.writeStatusLine(w, io, " writing", "Creative writing", fmt.Color.green); try fmt.writeStatusLine(w, io, " routine", "Follow daily routine", fmt.Color.green); try fmt.writeStatusLine(w, io, " offline", "Complete disconnection", fmt.Color.green);
// Examples try fmt.writeSection(w, io, "EXAMPLES"); try w.writeStreamingAll(io, " "); try fmt.dimText(w, io, "# Enter work mode"); try w.writeStreamingAll(io, "\n"); try w.writeStreamingAll(io, " focusd mode work --deep\n\n"); try w.writeStreamingAll(io, " "); try fmt.dimText(w, io, "# Add schedule rule"); try w.writeStreamingAll(io, "\n"); try w.writeStreamingAll(io, " focusd schedule add 09:00 17:00 work --deep\n\n"); try w.writeStreamingAll(io, " "); try fmt.dimText(w, io, "# Start pomodoro"); try w.writeStreamingAll(io, "\n"); try w.writeStreamingAll(io, " focusd pomodoro start\n\n"); try w.writeStreamingAll(io, " "); try fmt.dimText(w, io, "# Add task"); try w.writeStreamingAll(io, "\n"); try w.writeStreamingAll(io, " focusd task add \"Write thesis chapter\" high\n\n"); try w.writeStreamingAll(io, " "); try fmt.dimText(w, io, "# Export to Waybar"); try w.writeStreamingAll(io, "\n"); try w.writeStreamingAll(io, " focusd schedule export\n\n");}