@vibetasks/cli 0.2.0 → 0.3.0

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.
@@ -1,7 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ checkClipboardForError,
4
+ createClipboardMonitor,
5
+ detectError,
6
+ formatErrorForNotes,
7
+ getErrorSummary
8
+ } from "../chunk-PZF4VRDG.js";
9
+ import {
10
+ daemonConfigManager
11
+ } from "../chunk-2KRLRG4G.js";
2
12
 
3
13
  // bin/vibetasks.ts
4
- import { Command as Command13 } from "commander";
14
+ import { Command as Command16 } from "commander";
5
15
 
6
16
  // src/commands/login.ts
7
17
  import { Command } from "commander";
@@ -1055,7 +1065,13 @@ import chalk11 from "chalk";
1055
1065
  import ora8 from "ora";
1056
1066
  import fs from "fs/promises";
1057
1067
  import path from "path";
1058
- var hooksCommand = new Command10("hooks").description("Manage git hooks integration").command("install").description("Install git hooks in current repository").action(async () => {
1068
+ function getVibetasksHooksPath() {
1069
+ const cliRoot = path.dirname(path.dirname(path.dirname(new URL(import.meta.url).pathname)));
1070
+ const normalizedPath = process.platform === "win32" && cliRoot.startsWith("/") ? cliRoot.slice(1) : cliRoot;
1071
+ return path.join(normalizedPath, "hooks");
1072
+ }
1073
+ var hooksCommand = new Command10("hooks").description("Manage hooks integration (git and Claude Code)");
1074
+ hooksCommand.command("install").description("Install git hooks in current repository").action(async () => {
1059
1075
  const spinner = ora8("Installing git hooks...").start();
1060
1076
  try {
1061
1077
  const gitDir = path.join(process.cwd(), ".git");
@@ -1070,7 +1086,7 @@ var hooksCommand = new Command10("hooks").description("Manage git hooks integrat
1070
1086
  await fs.mkdir(hooksDir, { recursive: true });
1071
1087
  const postCommitPath = path.join(hooksDir, "post-commit");
1072
1088
  const hookScript = `#!/bin/sh
1073
- # TaskFlow post-commit hook
1089
+ # VibeTasks post-commit hook
1074
1090
  # Automatically updates tasks based on commit messages
1075
1091
 
1076
1092
  # Get the commit message
@@ -1085,14 +1101,14 @@ if [ -n "$TASK_ID" ]; then
1085
1101
 
1086
1102
  if [ -n "$IS_COMPLETE" ]; then
1087
1103
  # Mark task as complete
1088
- taskflow done "$TASK_ID" 2>/dev/null || true
1104
+ vibetasks done "$TASK_ID" 2>/dev/null || true
1089
1105
  else
1090
1106
  # Add commit info to task notes
1091
1107
  COMMIT_HASH=$(git log -1 --pretty=%h)
1092
1108
  COMMIT_TYPE=$(echo "$COMMIT_MSG" | grep -oP '^\\w+(?=:)' || echo "update")
1093
1109
 
1094
1110
  # Update task with commit reference (silently, don't block commit)
1095
- taskflow update "$TASK_ID" --notes "Commit $COMMIT_HASH: $COMMIT_TYPE" 2>/dev/null || true
1111
+ vibetasks update "$TASK_ID" --notes "Commit $COMMIT_HASH: $COMMIT_TYPE" 2>/dev/null || true
1096
1112
  fi
1097
1113
  fi
1098
1114
 
@@ -1100,13 +1116,13 @@ exit 0
1100
1116
  `;
1101
1117
  await fs.writeFile(postCommitPath, hookScript, { mode: 493 });
1102
1118
  spinner.succeed(chalk11.green("Git hooks installed!"));
1103
- console.log(chalk11.gray("\n\u{1F4CC} Post-commit hook created"));
1104
- console.log(chalk11.gray("Location:"), postCommitPath);
1105
- console.log(chalk11.blue.bold("\n\u2728 How to use:\n"));
1106
- console.log(chalk11.white("1. Link commits to tasks:"));
1107
- console.log(chalk11.gray(' git commit -m "feat: Add login [TASK-abc123]"'));
1108
- console.log(chalk11.white("\n2. Auto-complete tasks:"));
1109
- console.log(chalk11.gray(' git commit -m "feat: Complete feature [TASK-abc123] [COMPLETE]"'));
1119
+ console.log(chalk11.gray("\n Post-commit hook created"));
1120
+ console.log(chalk11.gray(" Location:"), postCommitPath);
1121
+ console.log(chalk11.blue.bold("\n How to use:\n"));
1122
+ console.log(chalk11.white(" 1. Link commits to tasks:"));
1123
+ console.log(chalk11.gray(' git commit -m "feat: Add login [TASK-abc123]"'));
1124
+ console.log(chalk11.white("\n 2. Auto-complete tasks:"));
1125
+ console.log(chalk11.gray(' git commit -m "feat: Complete feature [TASK-abc123] [COMPLETE]"'));
1110
1126
  console.log();
1111
1127
  process.exit(0);
1112
1128
  } catch (error) {
@@ -1117,6 +1133,106 @@ Error: ${error.message}
1117
1133
  process.exit(1);
1118
1134
  }
1119
1135
  });
1136
+ hooksCommand.command("claude").description("Generate Claude Code hook configuration for TodoWrite sync").option("--project <path>", "Project path (defaults to current directory)").option("--output", "Output config to stdout instead of installing").action(async (options) => {
1137
+ const spinner = ora8("Generating Claude Code hook configuration...").start();
1138
+ try {
1139
+ const projectPath = options.project || process.cwd();
1140
+ const hooksPath = getVibetasksHooksPath();
1141
+ const syncScriptPath = path.join(hooksPath, "sync-todos.js");
1142
+ const hookConfig = {
1143
+ hooks: {
1144
+ SubagentStop: [
1145
+ {
1146
+ command: `node "${syncScriptPath.replace(/\\/g, "/")}"`,
1147
+ timeout: 1e4,
1148
+ input: {
1149
+ todoState: true
1150
+ }
1151
+ }
1152
+ ]
1153
+ }
1154
+ };
1155
+ if (options.output) {
1156
+ spinner.stop();
1157
+ console.log(JSON.stringify(hookConfig, null, 2));
1158
+ process.exit(0);
1159
+ }
1160
+ const claudeDir = path.join(projectPath, ".claude");
1161
+ await fs.mkdir(claudeDir, { recursive: true });
1162
+ const settingsPath = path.join(claudeDir, "settings.json");
1163
+ let existingSettings = {};
1164
+ try {
1165
+ const data = await fs.readFile(settingsPath, "utf-8");
1166
+ existingSettings = JSON.parse(data);
1167
+ } catch {
1168
+ }
1169
+ existingSettings.hooks = existingSettings.hooks || {};
1170
+ existingSettings.hooks.SubagentStop = existingSettings.hooks.SubagentStop || [];
1171
+ const existingHookIndex = existingSettings.hooks.SubagentStop.findIndex(
1172
+ (h) => h.command?.includes("sync-todos.js")
1173
+ );
1174
+ if (existingHookIndex >= 0) {
1175
+ existingSettings.hooks.SubagentStop[existingHookIndex] = hookConfig.hooks.SubagentStop[0];
1176
+ spinner.text = "Updating existing Claude Code hook...";
1177
+ } else {
1178
+ existingSettings.hooks.SubagentStop.push(hookConfig.hooks.SubagentStop[0]);
1179
+ }
1180
+ await fs.writeFile(settingsPath, JSON.stringify(existingSettings, null, 2), "utf-8");
1181
+ spinner.succeed(chalk11.green("Claude Code hooks configured!"));
1182
+ console.log(chalk11.gray("\n Configuration written to:"));
1183
+ console.log(chalk11.white(` ${settingsPath}`));
1184
+ console.log(chalk11.gray("\n Hook script location:"));
1185
+ console.log(chalk11.white(` ${syncScriptPath}`));
1186
+ console.log(chalk11.blue.bold("\n How it works:\n"));
1187
+ console.log(chalk11.white(" 1. When Claude marks a TodoWrite item as completed"));
1188
+ console.log(chalk11.white(" 2. The SubagentStop hook triggers"));
1189
+ console.log(chalk11.white(" 3. sync-todos.js syncs status to vibetasks"));
1190
+ console.log(chalk11.gray("\n Status mapping:"));
1191
+ console.log(chalk11.gray(" pending -> todo"));
1192
+ console.log(chalk11.gray(" in_progress -> vibing"));
1193
+ console.log(chalk11.gray(" completed -> done"));
1194
+ console.log(chalk11.yellow.bold("\n Note: Ensure vibetasks MCP is configured in Claude Code."));
1195
+ console.log(chalk11.gray(" Run: vibetasks setup claude\n"));
1196
+ process.exit(0);
1197
+ } catch (error) {
1198
+ spinner.fail(chalk11.red("Failed to configure Claude Code hooks"));
1199
+ console.error(chalk11.red(`
1200
+ Error: ${error.message}
1201
+ `));
1202
+ process.exit(1);
1203
+ }
1204
+ });
1205
+ hooksCommand.command("status").description("Show status of installed hooks").action(async () => {
1206
+ console.log(chalk11.blue.bold("\n VibeTasks Hooks Status\n"));
1207
+ const gitHooksDir = path.join(process.cwd(), ".git", "hooks");
1208
+ const postCommitPath = path.join(gitHooksDir, "post-commit");
1209
+ try {
1210
+ const content = await fs.readFile(postCommitPath, "utf-8");
1211
+ if (content.includes("vibetasks") || content.includes("VibeTasks")) {
1212
+ console.log(chalk11.green(" [x] Git post-commit hook installed"));
1213
+ } else {
1214
+ console.log(chalk11.yellow(" [ ] Git post-commit hook (exists but not vibetasks)"));
1215
+ }
1216
+ } catch {
1217
+ console.log(chalk11.gray(" [ ] Git post-commit hook not installed"));
1218
+ }
1219
+ const claudeSettingsPath = path.join(process.cwd(), ".claude", "settings.json");
1220
+ try {
1221
+ const content = await fs.readFile(claudeSettingsPath, "utf-8");
1222
+ const settings = JSON.parse(content);
1223
+ if (settings.hooks?.SubagentStop?.some((h) => h.command?.includes("sync-todos"))) {
1224
+ console.log(chalk11.green(" [x] Claude Code SubagentStop hook configured"));
1225
+ } else {
1226
+ console.log(chalk11.gray(" [ ] Claude Code SubagentStop hook not configured"));
1227
+ }
1228
+ } catch {
1229
+ console.log(chalk11.gray(" [ ] Claude Code hooks not configured"));
1230
+ }
1231
+ console.log(chalk11.gray("\n To install hooks:"));
1232
+ console.log(chalk11.gray(" Git: vibetasks hooks install"));
1233
+ console.log(chalk11.gray(" Claude: vibetasks hooks claude\n"));
1234
+ process.exit(0);
1235
+ });
1120
1236
 
1121
1237
  // src/commands/init.ts
1122
1238
  import { Command as Command11 } from "commander";
@@ -1723,8 +1839,801 @@ var setupCommand = new Command12("setup").description("Interactive setup wizard
1723
1839
  process.exit(0);
1724
1840
  });
1725
1841
 
1842
+ // src/commands/watch.ts
1843
+ import { Command as Command13 } from "commander";
1844
+ import chalk14 from "chalk";
1845
+ import ora11 from "ora";
1846
+ import inquirer6 from "inquirer";
1847
+ import { AuthManager as AuthManager13, TaskOperations as TaskOperations9 } from "@vibetasks/core";
1848
+ import { detectProject as detectProject4 } from "@vibetasks/shared/utils/project-detector";
1849
+ var watchCommand = new Command13("watch").description("Monitor clipboard for errors and offer to capture them as tasks").option("-i, --interval <ms>", "Polling interval in milliseconds", "1000").option("-a, --auto", "Auto-create tasks without prompting").option("-q, --quiet", "Minimal output (only show errors)").option("--project <name>", "Override auto-detected project").action(async (options) => {
1850
+ const interval = parseInt(options.interval || "1000", 10);
1851
+ if (!options.quiet) {
1852
+ console.log();
1853
+ console.log(chalk14.cyan.bold("VibeTasks Clipboard Monitor"));
1854
+ console.log(chalk14.gray("Watching for errors in your clipboard..."));
1855
+ console.log();
1856
+ }
1857
+ let authManager;
1858
+ let taskOps;
1859
+ try {
1860
+ authManager = new AuthManager13();
1861
+ taskOps = await TaskOperations9.fromAuthManager(authManager);
1862
+ } catch (error) {
1863
+ if (error.message.includes("Not authenticated")) {
1864
+ console.error(chalk14.yellow("\nYou need to login first"));
1865
+ console.error(chalk14.gray("Run: vibetasks login\n"));
1866
+ } else {
1867
+ console.error(chalk14.red(`
1868
+ Error: ${error.message}
1869
+ `));
1870
+ }
1871
+ process.exit(1);
1872
+ }
1873
+ let projectTag = options.project;
1874
+ if (!projectTag) {
1875
+ try {
1876
+ const detected = await detectProject4(process.cwd());
1877
+ projectTag = detected.name;
1878
+ if (!options.quiet) {
1879
+ console.log(chalk14.gray(`Project: ${chalk14.white(projectTag)}`));
1880
+ }
1881
+ } catch {
1882
+ }
1883
+ }
1884
+ const recentErrors = /* @__PURE__ */ new Set();
1885
+ const handleError = async (error) => {
1886
+ const errorHash = `${error.type}:${error.message.substring(0, 50)}`;
1887
+ if (recentErrors.has(errorHash)) {
1888
+ return;
1889
+ }
1890
+ console.log();
1891
+ console.log(chalk14.red.bold("Error detected in clipboard!"));
1892
+ console.log(chalk14.gray("\u2500".repeat(50)));
1893
+ console.log();
1894
+ console.log(chalk14.yellow(getErrorSummary(error)));
1895
+ console.log();
1896
+ if (error.suggestion) {
1897
+ console.log(chalk14.cyan("Suggestion: ") + error.suggestion);
1898
+ console.log();
1899
+ }
1900
+ if (options.auto) {
1901
+ await createTaskFromError(error, taskOps, projectTag);
1902
+ recentErrors.add(errorHash);
1903
+ return;
1904
+ }
1905
+ const { action } = await inquirer6.prompt([
1906
+ {
1907
+ type: "list",
1908
+ name: "action",
1909
+ message: "What would you like to do?",
1910
+ choices: [
1911
+ { name: "Create task from this error", value: "create" },
1912
+ { name: "Show full error", value: "show" },
1913
+ { name: "Ignore", value: "ignore" },
1914
+ { name: "Stop watching", value: "stop" }
1915
+ ]
1916
+ }
1917
+ ]);
1918
+ switch (action) {
1919
+ case "create":
1920
+ await createTaskFromError(error, taskOps, projectTag);
1921
+ recentErrors.add(errorHash);
1922
+ break;
1923
+ case "show":
1924
+ console.log();
1925
+ console.log(chalk14.gray("\u2500".repeat(50)));
1926
+ console.log(error.rawText);
1927
+ console.log(chalk14.gray("\u2500".repeat(50)));
1928
+ console.log();
1929
+ const { afterShow } = await inquirer6.prompt([
1930
+ {
1931
+ type: "list",
1932
+ name: "afterShow",
1933
+ message: "Now what?",
1934
+ choices: [
1935
+ { name: "Create task from this error", value: "create" },
1936
+ { name: "Ignore", value: "ignore" }
1937
+ ]
1938
+ }
1939
+ ]);
1940
+ if (afterShow === "create") {
1941
+ await createTaskFromError(error, taskOps, projectTag);
1942
+ recentErrors.add(errorHash);
1943
+ }
1944
+ break;
1945
+ case "stop":
1946
+ monitor.stop();
1947
+ process.exit(0);
1948
+ break;
1949
+ case "ignore":
1950
+ default:
1951
+ if (!options.quiet) {
1952
+ console.log(chalk14.gray("Error ignored, continuing to watch..."));
1953
+ }
1954
+ break;
1955
+ }
1956
+ console.log();
1957
+ };
1958
+ const monitor = createClipboardMonitor({
1959
+ interval,
1960
+ onError: handleError,
1961
+ onStart: () => {
1962
+ if (!options.quiet) {
1963
+ console.log(chalk14.green("Started watching clipboard"));
1964
+ console.log(chalk14.gray(`Interval: ${interval}ms`));
1965
+ if (options.auto) {
1966
+ console.log(chalk14.yellow("Auto-create mode: ON"));
1967
+ }
1968
+ console.log();
1969
+ console.log(chalk14.gray("Press Ctrl+C to stop"));
1970
+ console.log();
1971
+ }
1972
+ },
1973
+ onStop: () => {
1974
+ if (!options.quiet) {
1975
+ console.log();
1976
+ console.log(chalk14.yellow("Stopped watching clipboard"));
1977
+ }
1978
+ }
1979
+ });
1980
+ process.on("SIGINT", () => {
1981
+ monitor.stop();
1982
+ process.exit(0);
1983
+ });
1984
+ process.on("SIGTERM", () => {
1985
+ monitor.stop();
1986
+ process.exit(0);
1987
+ });
1988
+ await monitor.start();
1989
+ await new Promise(() => {
1990
+ });
1991
+ });
1992
+ async function createTaskFromError(error, taskOps, projectTag) {
1993
+ const spinner = ora11("Creating task...").start();
1994
+ try {
1995
+ const title = generateTaskTitle(error);
1996
+ const task = await taskOps.createTask({
1997
+ title,
1998
+ notes: formatErrorForNotes(error),
1999
+ notes_format: "markdown",
2000
+ priority: error.category === "typescript" ? "medium" : "high",
2001
+ project_tag: projectTag,
2002
+ created_by: "human",
2003
+ // User triggered this
2004
+ status: "todo"
2005
+ });
2006
+ const tagName = `error-${error.category}`;
2007
+ try {
2008
+ const tag = await taskOps.findOrCreateTag(tagName, getTagColor(error.category));
2009
+ await taskOps.linkTaskTags(task.id, [tag.id]);
2010
+ } catch {
2011
+ }
2012
+ spinner.succeed(chalk14.green("Task created!"));
2013
+ console.log(chalk14.gray(`ID: ${task.id.substring(0, 8)}...`));
2014
+ console.log(chalk14.white.bold(title));
2015
+ } catch (err) {
2016
+ spinner.fail(chalk14.red("Failed to create task"));
2017
+ console.error(chalk14.red(`Error: ${err.message}`));
2018
+ }
2019
+ }
2020
+ function generateTaskTitle(error) {
2021
+ const prefix = getCategoryPrefix(error.category);
2022
+ let title = `${prefix} ${error.type}`;
2023
+ if (error.message && error.message.length < 60) {
2024
+ title += `: ${error.message}`;
2025
+ } else if (error.message) {
2026
+ const shortMessage = error.message.split(/[.!?\n]/)[0].trim();
2027
+ if (shortMessage.length < 60) {
2028
+ title += `: ${shortMessage}`;
2029
+ }
2030
+ }
2031
+ if (error.file) {
2032
+ const fileName = error.file.split(/[/\\]/).pop();
2033
+ if (fileName && title.length + fileName.length < 100) {
2034
+ title += ` in ${fileName}`;
2035
+ }
2036
+ }
2037
+ if (title.length > 120) {
2038
+ title = title.substring(0, 117) + "...";
2039
+ }
2040
+ return title;
2041
+ }
2042
+ function getCategoryPrefix(category) {
2043
+ const prefixes = {
2044
+ nodejs: "Fix",
2045
+ npm: "Resolve npm",
2046
+ expo: "Fix Expo",
2047
+ "react-native": "Fix RN",
2048
+ webpack: "Fix build",
2049
+ typescript: "Fix TS",
2050
+ python: "Fix Python",
2051
+ generic: "Fix"
2052
+ };
2053
+ return prefixes[category] || "Fix";
2054
+ }
2055
+ function getTagColor(category) {
2056
+ const colors = {
2057
+ nodejs: "#68A063",
2058
+ // Node green
2059
+ npm: "#CB3837",
2060
+ // npm red
2061
+ expo: "#000020",
2062
+ // Expo dark
2063
+ "react-native": "#61DAFB",
2064
+ // React blue
2065
+ webpack: "#8DD6F9",
2066
+ // Webpack blue
2067
+ typescript: "#3178C6",
2068
+ // TS blue
2069
+ python: "#3776AB",
2070
+ // Python blue
2071
+ generic: "#FF6B6B"
2072
+ // Generic red
2073
+ };
2074
+ return colors[category] || "#FF6B6B";
2075
+ }
2076
+ var checkCommand = new Command13("check-clipboard").description("Check clipboard once for errors").action(async () => {
2077
+ console.log(chalk14.gray("Checking clipboard..."));
2078
+ const error = await checkClipboardForError();
2079
+ if (!error) {
2080
+ console.log(chalk14.green("No errors detected in clipboard."));
2081
+ process.exit(0);
2082
+ }
2083
+ console.log();
2084
+ console.log(chalk14.red.bold("Error detected!"));
2085
+ console.log(chalk14.gray("\u2500".repeat(50)));
2086
+ console.log();
2087
+ console.log(chalk14.yellow(getErrorSummary(error)));
2088
+ console.log();
2089
+ if (error.suggestion) {
2090
+ console.log(chalk14.cyan("Suggestion: ") + error.suggestion);
2091
+ }
2092
+ console.log();
2093
+ console.log(chalk14.gray("Use `vibetasks watch` to monitor continuously."));
2094
+ process.exit(0);
2095
+ });
2096
+
2097
+ // src/commands/archive.ts
2098
+ import { Command as Command14 } from "commander";
2099
+ import chalk15 from "chalk";
2100
+ import ora12 from "ora";
2101
+ import inquirer7 from "inquirer";
2102
+ import { AuthManager as AuthManager14, TaskOperations as TaskOperations10 } from "@vibetasks/core";
2103
+ var archiveCommand = new Command14("archive").description("Archive a task or manage archived tasks").argument("[task-id]", "Task ID to archive (full or first 8 characters)").option("-l, --list", "List all archived tasks").option("-u, --unarchive <id>", "Unarchive a task by ID").option("--pick", "Pick from completed tasks to archive").action(async (taskId, options) => {
2104
+ const spinner = ora12();
2105
+ try {
2106
+ const authManager = new AuthManager14();
2107
+ const taskOps = await TaskOperations10.fromAuthManager(authManager);
2108
+ if (options.list) {
2109
+ spinner.start("Fetching archived tasks...");
2110
+ const archivedTasks = await taskOps.getArchivedTasks();
2111
+ spinner.stop();
2112
+ if (archivedTasks.length === 0) {
2113
+ console.log(chalk15.yellow("\nNo archived tasks found.\n"));
2114
+ process.exit(0);
2115
+ }
2116
+ console.log(chalk15.white("\nArchived Tasks:\n"));
2117
+ archivedTasks.forEach((task2, index) => {
2118
+ const archivedDate = task2.archived_at ? new Date(task2.archived_at).toLocaleDateString() : "Unknown";
2119
+ console.log(
2120
+ chalk15.gray(`${index + 1}. `) + chalk15.white(task2.title) + chalk15.gray(` [${task2.id.slice(0, 8)}]`) + chalk15.gray(` - Archived: ${archivedDate}`)
2121
+ );
2122
+ });
2123
+ console.log(chalk15.gray(`
2124
+ Total: ${archivedTasks.length} archived tasks
2125
+ `));
2126
+ console.log(chalk15.gray("To restore a task: vibetasks archive --unarchive <id>\n"));
2127
+ process.exit(0);
2128
+ }
2129
+ if (options.unarchive) {
2130
+ spinner.start("Restoring task...");
2131
+ let targetId2 = options.unarchive;
2132
+ if (targetId2.length < 32) {
2133
+ const archivedTasks = await taskOps.getArchivedTasks();
2134
+ const matchingTask = archivedTasks.find((t) => t.id.startsWith(targetId2));
2135
+ if (!matchingTask) {
2136
+ spinner.fail(chalk15.red("Archived task not found"));
2137
+ console.error(chalk15.gray(`
2138
+ No archived task found with ID starting with: ${targetId2}
2139
+ `));
2140
+ process.exit(1);
2141
+ }
2142
+ targetId2 = matchingTask.id;
2143
+ }
2144
+ const task2 = await taskOps.unarchiveTask(targetId2);
2145
+ spinner.succeed(chalk15.green("Task restored!"));
2146
+ console.log(chalk15.gray(`
2147
+ Restored: ${task2.title}`));
2148
+ console.log(chalk15.gray(`Status set to: done
2149
+ `));
2150
+ process.exit(0);
2151
+ }
2152
+ if (options.pick || !taskId) {
2153
+ const allTasks = await taskOps.getTasks("completed");
2154
+ if (allTasks.length === 0) {
2155
+ console.log(chalk15.yellow("\nNo completed tasks to archive. Complete some tasks first!\n"));
2156
+ process.exit(0);
2157
+ }
2158
+ const { selectedTask } = await inquirer7.prompt([{
2159
+ type: "list",
2160
+ name: "selectedTask",
2161
+ message: "Pick a completed task to archive:",
2162
+ choices: allTasks.map((t) => ({
2163
+ name: `${t.title}${t.completed_at ? chalk15.gray(` - Completed: ${new Date(t.completed_at).toLocaleDateString()}`) : ""}`,
2164
+ value: t.id
2165
+ }))
2166
+ }]);
2167
+ taskId = selectedTask;
2168
+ }
2169
+ if (!taskId) {
2170
+ console.log(chalk15.yellow("\nUsage: vibetasks archive <task-id>"));
2171
+ console.log(chalk15.gray(" or: vibetasks archive --pick"));
2172
+ console.log(chalk15.gray(" or: vibetasks archive --list"));
2173
+ console.log(chalk15.gray(" or: vibetasks archive --unarchive <id>\n"));
2174
+ process.exit(1);
2175
+ }
2176
+ spinner.start("Archiving task...");
2177
+ let targetId = taskId;
2178
+ if (taskId.length < 32) {
2179
+ const allTasks = await taskOps.getTasks("all", true);
2180
+ const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
2181
+ if (!matchingTask) {
2182
+ spinner.fail(chalk15.red("Task not found"));
2183
+ console.error(chalk15.gray(`
2184
+ No task found with ID starting with: ${taskId}
2185
+ `));
2186
+ process.exit(1);
2187
+ }
2188
+ targetId = matchingTask.id;
2189
+ }
2190
+ const task = await taskOps.archiveTask(targetId);
2191
+ spinner.succeed(chalk15.green("Task archived!"));
2192
+ console.log(chalk15.gray(`
2193
+ Archived: ${task.title}`));
2194
+ console.log(chalk15.gray(`Archived at: ${(/* @__PURE__ */ new Date()).toLocaleString()}`));
2195
+ console.log(chalk15.gray("\nTo view archived tasks: vibetasks archive --list"));
2196
+ console.log(chalk15.gray(`To restore: vibetasks archive --unarchive ${targetId.slice(0, 8)}
2197
+ `));
2198
+ process.exit(0);
2199
+ } catch (error) {
2200
+ spinner.fail(chalk15.red("Operation failed"));
2201
+ if (error.message.includes("Not authenticated")) {
2202
+ console.error(chalk15.yellow("\nYou need to login first"));
2203
+ console.error(chalk15.gray("Run: vibetasks login\n"));
2204
+ } else {
2205
+ console.error(chalk15.red(`
2206
+ Error: ${error.message}
2207
+ `));
2208
+ }
2209
+ process.exit(1);
2210
+ }
2211
+ });
2212
+
2213
+ // src/commands/daemon.ts
2214
+ import { Command as Command15 } from "commander";
2215
+ import chalk16 from "chalk";
2216
+ import ora13 from "ora";
2217
+ import { spawn } from "child_process";
2218
+ import path3 from "path";
2219
+ import { fileURLToPath } from "url";
2220
+ import { AuthManager as AuthManager15, TaskOperations as TaskOperations11 } from "@vibetasks/core";
2221
+ var __filename2 = fileURLToPath(import.meta.url);
2222
+ var __dirname2 = path3.dirname(__filename2);
2223
+ var daemonCommand = new Command15("daemon").description("Manage the VibeTasks error capture daemon").addCommand(createStartCommand()).addCommand(createStopCommand()).addCommand(createStatusCommand()).addCommand(createConfigureCommand()).addCommand(createCaptureCommand());
2224
+ function createStartCommand() {
2225
+ return new Command15("start").description("Start the error capture daemon").option("-f, --foreground", "Run in foreground (useful for debugging)").option("--no-notify", "Disable startup notification").action(async (options) => {
2226
+ const spinner = ora13("Starting VibeTasks daemon...").start();
2227
+ try {
2228
+ if (await daemonConfigManager.isDaemonRunning()) {
2229
+ spinner.warn(chalk16.yellow("Daemon is already running"));
2230
+ const pid = await daemonConfigManager.getPid();
2231
+ console.log(chalk16.gray(`PID: ${pid}`));
2232
+ process.exit(0);
2233
+ }
2234
+ const config = await daemonConfigManager.getConfig();
2235
+ if (!config.enabled) {
2236
+ spinner.fail(chalk16.red("Daemon is disabled in configuration"));
2237
+ console.log(chalk16.gray("Run: vibetasks daemon configure --enable"));
2238
+ process.exit(1);
2239
+ }
2240
+ if (options.foreground) {
2241
+ spinner.succeed(chalk16.green("Starting daemon in foreground mode"));
2242
+ console.log(chalk16.gray("Press Ctrl+C to stop\n"));
2243
+ await runDaemonForeground(config);
2244
+ } else {
2245
+ const daemonProcess = await startDaemonBackground(config);
2246
+ if (daemonProcess && daemonProcess.pid) {
2247
+ await daemonConfigManager.savePid(daemonProcess.pid);
2248
+ spinner.succeed(chalk16.green("VibeTasks daemon started"));
2249
+ console.log(chalk16.gray(`PID: ${daemonProcess.pid}`));
2250
+ console.log(chalk16.cyan(`Keyboard shortcut: ${config.keyboard_shortcut}`));
2251
+ console.log(chalk16.gray("\nThe daemon is now running in the background."));
2252
+ console.log(chalk16.gray('Use "vibetasks daemon stop" to stop it.'));
2253
+ } else {
2254
+ throw new Error("Failed to start daemon process");
2255
+ }
2256
+ }
2257
+ process.exit(0);
2258
+ } catch (error) {
2259
+ spinner.fail(chalk16.red("Failed to start daemon"));
2260
+ console.error(chalk16.red(`Error: ${error.message}`));
2261
+ process.exit(1);
2262
+ }
2263
+ });
2264
+ }
2265
+ function createStopCommand() {
2266
+ return new Command15("stop").description("Stop the error capture daemon").action(async () => {
2267
+ const spinner = ora13("Stopping VibeTasks daemon...").start();
2268
+ try {
2269
+ const pid = await daemonConfigManager.getPid();
2270
+ if (!pid) {
2271
+ spinner.warn(chalk16.yellow("Daemon is not running"));
2272
+ process.exit(0);
2273
+ }
2274
+ try {
2275
+ process.kill(pid, "SIGTERM");
2276
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
2277
+ try {
2278
+ process.kill(pid, 0);
2279
+ process.kill(pid, "SIGKILL");
2280
+ } catch {
2281
+ }
2282
+ await daemonConfigManager.removePid();
2283
+ spinner.succeed(chalk16.green("VibeTasks daemon stopped"));
2284
+ } catch (error) {
2285
+ if (error.code === "ESRCH") {
2286
+ await daemonConfigManager.removePid();
2287
+ spinner.warn(chalk16.yellow("Daemon was not running (stale PID file removed)"));
2288
+ } else {
2289
+ throw error;
2290
+ }
2291
+ }
2292
+ process.exit(0);
2293
+ } catch (error) {
2294
+ spinner.fail(chalk16.red("Failed to stop daemon"));
2295
+ console.error(chalk16.red(`Error: ${error.message}`));
2296
+ process.exit(1);
2297
+ }
2298
+ });
2299
+ }
2300
+ function createStatusCommand() {
2301
+ return new Command15("status").description("Check daemon status").action(async () => {
2302
+ try {
2303
+ const isRunning = await daemonConfigManager.isDaemonRunning();
2304
+ const config = await daemonConfigManager.getConfig();
2305
+ const pid = await daemonConfigManager.getPid();
2306
+ console.log(chalk16.blue.bold("\nVibeTasks Daemon Status\n"));
2307
+ if (isRunning) {
2308
+ console.log(chalk16.green(" Status: Running"));
2309
+ console.log(chalk16.gray(` PID: ${pid}`));
2310
+ } else {
2311
+ console.log(chalk16.yellow(" Status: Stopped"));
2312
+ }
2313
+ console.log(chalk16.gray(` Enabled: ${config.enabled ? "Yes" : "No"}`));
2314
+ console.log(chalk16.gray(` Keyboard Shortcut: ${config.keyboard_shortcut}`));
2315
+ console.log(chalk16.gray(` Auto-start: ${config.auto_start ? "Yes" : "No"}`));
2316
+ console.log(chalk16.gray(` Auto-create tasks: ${config.auto_create_task ? "Yes" : "No"}`));
2317
+ console.log(chalk16.gray(` Config path: ${daemonConfigManager.getConfigDir()}`));
2318
+ console.log();
2319
+ process.exit(0);
2320
+ } catch (error) {
2321
+ console.error(chalk16.red(`Error: ${error.message}`));
2322
+ process.exit(1);
2323
+ }
2324
+ });
2325
+ }
2326
+ function createConfigureCommand() {
2327
+ return new Command15("configure").description("Configure daemon settings").option("--enable", "Enable the daemon").option("--disable", "Disable the daemon").option("--shortcut <keys>", 'Set keyboard shortcut (e.g., "Ctrl+Alt+E")').option("--auto-start <bool>", "Enable/disable auto-start on login").option("--auto-task <bool>", "Automatically create tasks from errors").option("--notification-duration <ms>", "Notification display duration in ms").option("--reset", "Reset to default configuration").action(async (options) => {
2328
+ try {
2329
+ if (options.reset) {
2330
+ const { DEFAULT_DAEMON_CONFIG } = await import("../daemon-config-EUSBQA4E.js");
2331
+ await daemonConfigManager.saveConfig(DEFAULT_DAEMON_CONFIG);
2332
+ console.log(chalk16.green("\nConfiguration reset to defaults\n"));
2333
+ process.exit(0);
2334
+ }
2335
+ const updates = {};
2336
+ if (options.enable) updates.enabled = true;
2337
+ if (options.disable) updates.enabled = false;
2338
+ if (options.shortcut) updates.keyboard_shortcut = options.shortcut;
2339
+ if (options.autoStart !== void 0) updates.auto_start = options.autoStart === "true";
2340
+ if (options.autoTask !== void 0) updates.auto_create_task = options.autoTask === "true";
2341
+ if (options.notificationDuration) updates.notification_duration = parseInt(options.notificationDuration, 10);
2342
+ if (Object.keys(updates).length === 0) {
2343
+ const config = await daemonConfigManager.getConfig();
2344
+ console.log(chalk16.blue.bold("\nDaemon Configuration\n"));
2345
+ console.log(JSON.stringify(config, null, 2));
2346
+ console.log();
2347
+ } else {
2348
+ await daemonConfigManager.saveConfig(updates);
2349
+ console.log(chalk16.green("\nConfiguration updated\n"));
2350
+ if (await daemonConfigManager.isDaemonRunning()) {
2351
+ console.log(chalk16.yellow("Note: Restart the daemon for changes to take effect"));
2352
+ console.log(chalk16.gray("Run: vibetasks daemon stop && vibetasks daemon start\n"));
2353
+ }
2354
+ }
2355
+ process.exit(0);
2356
+ } catch (error) {
2357
+ console.error(chalk16.red(`Error: ${error.message}`));
2358
+ process.exit(1);
2359
+ }
2360
+ });
2361
+ }
2362
+ function createCaptureCommand() {
2363
+ return new Command15("capture").description("Manually capture clipboard and check for errors").option("--create-task", "Create a task from the captured error").option("--raw", "Show raw clipboard content").action(async (options) => {
2364
+ const spinner = ora13("Capturing clipboard...").start();
2365
+ try {
2366
+ const clipboard = await import("clipboardy");
2367
+ const content = await clipboard.default.read();
2368
+ spinner.stop();
2369
+ if (!content || content.trim().length === 0) {
2370
+ console.log(chalk16.yellow("\nClipboard is empty\n"));
2371
+ process.exit(0);
2372
+ }
2373
+ if (options.raw) {
2374
+ console.log(chalk16.blue.bold("\nClipboard Content:\n"));
2375
+ console.log(content);
2376
+ console.log();
2377
+ }
2378
+ const error = detectError(content);
2379
+ if (!error) {
2380
+ console.log(chalk16.yellow("\nNo error detected in clipboard content\n"));
2381
+ if (!options.raw) {
2382
+ console.log(chalk16.gray("First 200 characters:"));
2383
+ console.log(chalk16.gray(content.substring(0, 200) + (content.length > 200 ? "..." : "")));
2384
+ console.log();
2385
+ }
2386
+ process.exit(0);
2387
+ }
2388
+ console.log(chalk16.red.bold("\nError Detected!\n"));
2389
+ console.log(chalk16.cyan("Type: ") + chalk16.white(error.type));
2390
+ console.log(chalk16.cyan("Category: ") + chalk16.white(error.category));
2391
+ console.log(chalk16.cyan("Message: ") + chalk16.white(error.message));
2392
+ if (error.file) {
2393
+ console.log(chalk16.cyan("File: ") + chalk16.white(error.file));
2394
+ if (error.line) {
2395
+ console.log(chalk16.cyan("Line: ") + chalk16.white(error.line.toString()) + (error.column ? chalk16.gray(`:${error.column}`) : ""));
2396
+ }
2397
+ }
2398
+ if (error.suggestion) {
2399
+ console.log(chalk16.green("\nSuggestion: ") + chalk16.white(error.suggestion));
2400
+ }
2401
+ if (error.stack) {
2402
+ console.log(chalk16.cyan("\nStack Trace (first 5 lines):"));
2403
+ const stackLines = error.stack.split("\n").slice(0, 5);
2404
+ stackLines.forEach((line) => {
2405
+ console.log(chalk16.gray(" " + line));
2406
+ });
2407
+ }
2408
+ if (options.createTask) {
2409
+ console.log();
2410
+ await createTaskFromError2(error);
2411
+ }
2412
+ console.log();
2413
+ process.exit(0);
2414
+ } catch (error) {
2415
+ spinner.fail(chalk16.red("Failed to capture clipboard"));
2416
+ console.error(chalk16.red(`Error: ${error.message}`));
2417
+ process.exit(1);
2418
+ }
2419
+ });
2420
+ }
2421
+ async function startDaemonBackground(config) {
2422
+ const workerPath = path3.resolve(__dirname2, "..", "daemon-worker.js");
2423
+ const child = spawn(process.execPath, [workerPath], {
2424
+ detached: true,
2425
+ stdio: "ignore",
2426
+ env: {
2427
+ ...process.env,
2428
+ VIBETASKS_DAEMON: "true",
2429
+ VIBETASKS_SHORTCUT: config.keyboard_shortcut
2430
+ }
2431
+ });
2432
+ child.unref();
2433
+ return child;
2434
+ }
2435
+ async function runDaemonForeground(config) {
2436
+ console.log(chalk16.blue.bold("VibeTasks Error Capture Daemon"));
2437
+ console.log(chalk16.gray(`Keyboard shortcut: ${config.keyboard_shortcut}`));
2438
+ console.log(chalk16.gray("Listening for keyboard shortcut...\n"));
2439
+ let keyListener;
2440
+ try {
2441
+ const { GlobalKeyboardListener } = await import("node-global-key-listener");
2442
+ keyListener = new GlobalKeyboardListener();
2443
+ console.log(chalk16.green("Using node-global-key-listener"));
2444
+ const shortcut = daemonConfigManager.parseShortcut(config.keyboard_shortcut);
2445
+ let modifiersPressed = { ctrl: false, alt: false, shift: false };
2446
+ keyListener.addListener((event, down) => {
2447
+ const keyName = event.name?.toUpperCase() || "";
2448
+ if (event.name === "LEFT CTRL" || event.name === "RIGHT CTRL") {
2449
+ modifiersPressed.ctrl = event.state === "DOWN";
2450
+ }
2451
+ if (event.name === "LEFT ALT" || event.name === "RIGHT ALT") {
2452
+ modifiersPressed.alt = event.state === "DOWN";
2453
+ }
2454
+ if (event.name === "LEFT SHIFT" || event.name === "RIGHT SHIFT") {
2455
+ modifiersPressed.shift = event.state === "DOWN";
2456
+ }
2457
+ if (event.state === "DOWN" && keyName === shortcut.key.toUpperCase() && modifiersPressed.ctrl === shortcut.ctrl && modifiersPressed.alt === shortcut.alt && modifiersPressed.shift === shortcut.shift) {
2458
+ console.log(chalk16.cyan(`
2459
+ [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Shortcut triggered!`));
2460
+ handleShortcutTriggered(config).catch((err) => {
2461
+ console.error(chalk16.red(`Error handling shortcut: ${err.message}`));
2462
+ });
2463
+ }
2464
+ });
2465
+ } catch (error) {
2466
+ console.log(chalk16.yellow(`Note: node-global-key-listener not available: ${error.message}`));
2467
+ console.log(chalk16.gray("Install with: npm install node-global-key-listener"));
2468
+ console.log(chalk16.gray("\nFalling back to manual capture mode."));
2469
+ console.log(chalk16.gray('Use "vibetasks daemon capture" to manually capture clipboard.\n'));
2470
+ await new Promise(() => {
2471
+ });
2472
+ }
2473
+ process.on("SIGINT", () => {
2474
+ console.log(chalk16.yellow("\nShutting down daemon..."));
2475
+ if (keyListener && keyListener.kill) {
2476
+ keyListener.kill();
2477
+ }
2478
+ process.exit(0);
2479
+ });
2480
+ process.on("SIGTERM", () => {
2481
+ console.log(chalk16.yellow("\nReceived SIGTERM, shutting down..."));
2482
+ if (keyListener && keyListener.kill) {
2483
+ keyListener.kill();
2484
+ }
2485
+ process.exit(0);
2486
+ });
2487
+ await new Promise(() => {
2488
+ });
2489
+ }
2490
+ async function handleShortcutTriggered(config) {
2491
+ try {
2492
+ const clipboard = await import("clipboardy");
2493
+ const content = await clipboard.default.read();
2494
+ if (!content || content.trim().length === 0) {
2495
+ await showNotification("VibeTasks", "Clipboard is empty", "info");
2496
+ return;
2497
+ }
2498
+ const error = detectError(content);
2499
+ if (!error) {
2500
+ await showNotification("VibeTasks", "No error detected in clipboard", "info");
2501
+ console.log(chalk16.gray("Clipboard content did not match error patterns"));
2502
+ return;
2503
+ }
2504
+ const summary = getErrorSummary(error);
2505
+ console.log(chalk16.red("Error detected:"));
2506
+ console.log(chalk16.white(summary));
2507
+ await showNotification(
2508
+ "VibeTasks: Error Detected",
2509
+ summary,
2510
+ "error",
2511
+ config.notification_duration
2512
+ );
2513
+ if (config.auto_create_task) {
2514
+ console.log(chalk16.cyan("Creating task..."));
2515
+ await createTaskFromError2(error);
2516
+ }
2517
+ } catch (err) {
2518
+ console.error(chalk16.red(`Error: ${err.message}`));
2519
+ await showNotification("VibeTasks Error", err.message, "error");
2520
+ }
2521
+ }
2522
+ async function showNotification(title, message, type = "info", duration) {
2523
+ try {
2524
+ const notifier = await import("node-notifier");
2525
+ const options = {
2526
+ title,
2527
+ message: message.substring(0, 256),
2528
+ // Windows limits notification length
2529
+ sound: type === "error",
2530
+ wait: false
2531
+ };
2532
+ if (process.platform === "win32") {
2533
+ options.appID = "VibeTasks";
2534
+ }
2535
+ notifier.default.notify(options);
2536
+ } catch (error) {
2537
+ console.log(chalk16.blue(`[Notification] ${title}: ${message}`));
2538
+ }
2539
+ }
2540
+ function generateTaskTitle2(error) {
2541
+ const prefix = getCategoryPrefix2(error.category);
2542
+ let title = `${prefix} ${error.type}`;
2543
+ if (error.message && error.message.length < 60) {
2544
+ title += `: ${error.message}`;
2545
+ } else if (error.message) {
2546
+ const shortMessage = error.message.split(/[.!?\n]/)[0].trim();
2547
+ if (shortMessage.length < 60) {
2548
+ title += `: ${shortMessage}`;
2549
+ }
2550
+ }
2551
+ if (error.file) {
2552
+ const fileName = error.file.split(/[/\\]/).pop();
2553
+ if (fileName && title.length + fileName.length < 100) {
2554
+ title += ` in ${fileName}`;
2555
+ }
2556
+ }
2557
+ if (title.length > 120) {
2558
+ title = title.substring(0, 117) + "...";
2559
+ }
2560
+ return title;
2561
+ }
2562
+ function getCategoryPrefix2(category) {
2563
+ const prefixes = {
2564
+ nodejs: "Fix",
2565
+ npm: "Resolve npm",
2566
+ expo: "Fix Expo",
2567
+ "react-native": "Fix RN",
2568
+ webpack: "Fix build",
2569
+ typescript: "Fix TS",
2570
+ python: "Fix Python",
2571
+ generic: "Fix"
2572
+ };
2573
+ return prefixes[category] || "Fix";
2574
+ }
2575
+ async function createTaskFromError2(error) {
2576
+ const spinner = ora13("Creating task from error...").start();
2577
+ try {
2578
+ const authManager = new AuthManager15();
2579
+ const taskOps = await TaskOperations11.fromAuthManager(authManager);
2580
+ const title = generateTaskTitle2(error);
2581
+ const notes = formatErrorForNotes(error);
2582
+ const task = await taskOps.createTask({
2583
+ title,
2584
+ notes,
2585
+ notes_format: "markdown",
2586
+ priority: error.category === "typescript" ? "medium" : "high",
2587
+ created_by: "ai",
2588
+ status: "todo"
2589
+ });
2590
+ const tagName = `error-${error.category}`;
2591
+ try {
2592
+ const tag = await taskOps.findOrCreateTag(tagName, getTagColor2(error.category));
2593
+ await taskOps.linkTaskTags(task.id, [tag.id]);
2594
+ } catch {
2595
+ }
2596
+ spinner.succeed(chalk16.green("Task created!"));
2597
+ console.log(chalk16.gray(`ID: ${task.id.substring(0, 8)}...`));
2598
+ console.log(chalk16.white.bold(task.title));
2599
+ await showNotification(
2600
+ "VibeTasks: Task Created",
2601
+ `Created: ${title.substring(0, 100)}`,
2602
+ "success"
2603
+ );
2604
+ } catch (err) {
2605
+ spinner.fail(chalk16.red("Failed to create task"));
2606
+ if (err.message.includes("Not authenticated")) {
2607
+ console.log(chalk16.yellow("You need to login first: vibetasks login"));
2608
+ } else {
2609
+ console.log(chalk16.red(`Error: ${err.message}`));
2610
+ }
2611
+ }
2612
+ }
2613
+ function getTagColor2(category) {
2614
+ const colors = {
2615
+ nodejs: "#68A063",
2616
+ // Node green
2617
+ npm: "#CB3837",
2618
+ // npm red
2619
+ expo: "#000020",
2620
+ // Expo dark
2621
+ "react-native": "#61DAFB",
2622
+ // React blue
2623
+ webpack: "#8DD6F9",
2624
+ // Webpack blue
2625
+ typescript: "#3178C6",
2626
+ // TS blue
2627
+ python: "#3776AB",
2628
+ // Python blue
2629
+ generic: "#FF6B6B"
2630
+ // Generic red
2631
+ };
2632
+ return colors[category] || "#FF6B6B";
2633
+ }
2634
+
1726
2635
  // bin/vibetasks.ts
1727
- var program = new Command13();
2636
+ var program = new Command16();
1728
2637
  program.name("vibetasks").description("VibeTasks CLI - Fast task management for vibers").version("0.2.0");
1729
2638
  program.addCommand(setupCommand);
1730
2639
  program.addCommand(loginCommand);
@@ -1738,4 +2647,8 @@ program.addCommand(deleteCommand);
1738
2647
  program.addCommand(configCommand);
1739
2648
  program.addCommand(hooksCommand);
1740
2649
  program.addCommand(initCommand);
2650
+ program.addCommand(watchCommand);
2651
+ program.addCommand(checkCommand);
2652
+ program.addCommand(archiveCommand);
2653
+ program.addCommand(daemonCommand);
1741
2654
  program.parse();