@vibetasks/cli 0.6.11 → 0.6.13

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/dist/bin/vibetasks.js +1738 -212
  2. package/package.json +3 -2
@@ -11,7 +11,7 @@ import {
11
11
  } from "../chunk-2KRLRG4G.js";
12
12
 
13
13
  // bin/vibetasks.ts
14
- import { Command as Command30 } from "commander";
14
+ import { Command as Command37 } from "commander";
15
15
  import { createRequire } from "module";
16
16
 
17
17
  // src/commands/login.ts
@@ -415,7 +415,7 @@ import { randomUUID as randomUUID2 } from "crypto";
415
415
  import { writeFile, readFile, mkdir } from "fs/promises";
416
416
  import { join } from "path";
417
417
  import { homedir } from "os";
418
- import { randomUUID } from "crypto";
418
+ import { randomUUID, randomInt } from "crypto";
419
419
  import { AuthManager as AuthManager2 } from "@vibetasks/core";
420
420
  var ADJECTIVES = [
421
421
  "swift",
@@ -555,11 +555,12 @@ var SessionManager = class {
555
555
  }
556
556
  /**
557
557
  * Generate a memorable session ID like "swift-fox-42"
558
+ * Uses cryptographically secure random generation
558
559
  */
559
560
  generateSessionId() {
560
- const adj = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
561
- const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
562
- const num = Math.floor(Math.random() * 100);
561
+ const adj = ADJECTIVES[randomInt(ADJECTIVES.length)];
562
+ const noun = NOUNS[randomInt(NOUNS.length)];
563
+ const num = randomInt(100);
563
564
  return `${adj}-${noun}-${num}`;
564
565
  }
565
566
  /**
@@ -725,7 +726,7 @@ var SessionManager = class {
725
726
  * Includes: vibing tasks with context, git status, files changed, next steps
726
727
  */
727
728
  async generateHandoff() {
728
- const { execSync: execSync2 } = await import("child_process");
729
+ const { execSync: execSync3 } = await import("child_process");
729
730
  const lines = [];
730
731
  lines.push("# AI Handoff");
731
732
  lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}`);
@@ -739,7 +740,7 @@ var SessionManager = class {
739
740
  lines.push("");
740
741
  }
741
742
  try {
742
- const gitStatus = execSync2("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
743
+ const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
743
744
  if (gitStatus) {
744
745
  const changedFiles = gitStatus.split("\n").slice(0, 20);
745
746
  lines.push("## Uncommitted Changes");
@@ -752,7 +753,7 @@ var SessionManager = class {
752
753
  } catch {
753
754
  }
754
755
  try {
755
- const recentCommits = execSync2("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
756
+ const recentCommits = execSync3("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
756
757
  if (recentCommits) {
757
758
  lines.push("## Recent Commits");
758
759
  recentCommits.split("\n").forEach((c) => lines.push(`- ${c}`));
@@ -787,7 +788,8 @@ import { join as join2 } from "path";
787
788
  import { homedir as homedir2 } from "os";
788
789
  var ShortIdManager = class {
789
790
  storePath;
790
- maxIds = 99;
791
+ maxIds = 9999;
792
+ // Support up to 9999 short IDs (database assigns them)
791
793
  constructor() {
792
794
  this.storePath = join2(homedir2(), ".vibetasks", "short-ids.json");
793
795
  }
@@ -837,25 +839,54 @@ var ShortIdManager = class {
837
839
  }
838
840
  }
839
841
  /**
840
- * Resolve an ID that could be either short (1-99) or full UUID
842
+ * Resolve a partial UUID (8 characters) to full UUID by searching database
841
843
  */
842
- async resolveId(idOrShortId) {
843
- const shortId = parseInt(idOrShortId, 10);
844
- if (!isNaN(shortId) && shortId >= 1 && shortId <= this.maxIds) {
845
- const fullId = await this.getFullId(shortId);
846
- if (fullId) {
847
- return fullId;
848
- }
844
+ async resolvePartialUuid(partialId, taskOps) {
845
+ if (!/^[0-9a-f]{8}$/i.test(partialId)) {
846
+ return partialId;
847
+ }
848
+ const allTasks = await taskOps.getTasks("all");
849
+ const matches = allTasks.filter((t) => t.id.toLowerCase().startsWith(partialId.toLowerCase()));
850
+ if (matches.length === 0) {
849
851
  throw new Error(
850
- `Short ID #${shortId} not found. Run 'vibetasks list --short-ids' first to generate mappings.`
852
+ `No task found starting with "${partialId}". Run 'vibetasks list' to see all tasks.`
851
853
  );
852
854
  }
855
+ if (matches.length > 1) {
856
+ const shortIds = matches.map((t) => t.short_id).filter(Boolean);
857
+ const suggestion = shortIds.length > 0 ? `Try using short ID: ${shortIds.join(" or ")}` : "Provide more characters to narrow down";
858
+ throw new Error(
859
+ `Multiple tasks match "${partialId}". ${suggestion}`
860
+ );
861
+ }
862
+ return matches[0].id;
863
+ }
864
+ /**
865
+ * Resolve an ID that could be either short (1-9999), partial UUID (8 chars), or full UUID
866
+ */
867
+ async resolveId(idOrShortId, taskOps) {
868
+ const isOnlyNumeric = /^\d+$/.test(idOrShortId);
869
+ if (isOnlyNumeric) {
870
+ const shortId = parseInt(idOrShortId, 10);
871
+ if (shortId >= 1 && shortId <= this.maxIds) {
872
+ const fullId = await this.getFullId(shortId);
873
+ if (fullId) {
874
+ return fullId;
875
+ }
876
+ throw new Error(
877
+ `Short ID #${shortId} not found. Run 'vibetasks list --short-ids' first to generate mappings.`
878
+ );
879
+ }
880
+ }
881
+ if (taskOps && /^[0-9a-f]{8}$/i.test(idOrShortId)) {
882
+ return await this.resolvePartialUuid(idOrShortId, taskOps);
883
+ }
853
884
  return idOrShortId;
854
885
  }
855
886
  };
856
887
 
857
888
  // src/commands/add.ts
858
- var addCommand = new Command2("add").description("Add a new task").argument("<title>", "Task title").option("-n, --notes <notes>", "Task notes (markdown supported)").option("-d, --due <date>", 'Due date (YYYY-MM-DD, "today", "tomorrow", "+3d")').option("-p, --priority <level>", "Priority: low, medium, high", "none").option("-t, --tags <tags...>", "Tags (space-separated)").option("-s, --subtasks <subtasks...>", "Subtasks (space-separated, use quotes for multi-word)").option("--project <name>", "Override auto-detected project").option("-e, --energy <level>", "Energy required: low, medium, high").option("-a, --acceptance <criteria...>", 'Acceptance criteria - "DONE WHEN" conditions (use quotes for multi-word)').option("-f, --files <files...>", "Relevant files for this task").option("--non-goals <goals...>", "Explicit non-goals / out of scope items").action(async (title, options) => {
889
+ var addCommand = new Command2("add").description("Add a new task").argument("<title>", "Task title").option("-n, --notes <notes>", "Task notes (markdown supported)").option("-d, --due <date>", 'Due date (YYYY-MM-DD, "today", "tomorrow", "+3d")').option("-p, --priority <level>", "Priority: low, medium, high", "none").option("-t, --tags <tags...>", "Tags (space-separated)").option("-s, --subtasks <subtasks...>", "Subtasks (space-separated, use quotes for multi-word)").option("--subtasks-json <json>", "Rich subtasks as JSON (with instructions, links, warnings, etc.)").option("--project <name>", "Override auto-detected project").option("-e, --energy <level>", "Energy required: low, medium, high").option("-a, --acceptance <criteria...>", 'Acceptance criteria - "DONE WHEN" conditions (use quotes for multi-word)').option("-f, --files <files...>", "Relevant files for this task").option("--non-goals <goals...>", "Explicit non-goals / out of scope items").option("--for <who>", "Who should complete this: human, ai, both (default: both)").action(async (title, options) => {
859
890
  const spinner = ora2("Creating task...").start();
860
891
  try {
861
892
  const authManager = new AuthManager3();
@@ -893,11 +924,34 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
893
924
  throw new Error(`Invalid energy level. Must be one of: ${validEnergy.join(", ")}`);
894
925
  }
895
926
  }
896
- const subtasksJson = options.subtasks?.map((subtaskTitle) => ({
897
- id: randomUUID2(),
898
- title: subtaskTitle,
899
- done: false
900
- })) || [];
927
+ let subtasksJson = [];
928
+ if (options.subtasksJson) {
929
+ try {
930
+ const parsed = JSON.parse(options.subtasksJson);
931
+ subtasksJson = Array.isArray(parsed) ? parsed : [parsed];
932
+ subtasksJson = subtasksJson.map((st) => ({
933
+ id: st.id || randomUUID2(),
934
+ title: st.title,
935
+ done: st.done || false,
936
+ notes: st.notes,
937
+ assigned_to: st.assigned_to,
938
+ link: st.link,
939
+ instructions: st.instructions,
940
+ aiPrompt: st.aiPrompt,
941
+ code: st.code,
942
+ warning: st.warning,
943
+ tip: st.tip
944
+ }));
945
+ } catch (error) {
946
+ throw new Error(`Invalid subtasks JSON: ${error.message}`);
947
+ }
948
+ } else if (options.subtasks) {
949
+ subtasksJson = options.subtasks.map((subtaskTitle) => ({
950
+ id: randomUUID2(),
951
+ title: subtaskTitle,
952
+ done: false
953
+ }));
954
+ }
901
955
  const acceptanceCriteria = options.acceptance?.map((text) => ({
902
956
  id: randomUUID2(),
903
957
  text,
@@ -917,6 +971,7 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
917
971
  at: (/* @__PURE__ */ new Date()).toISOString()
918
972
  }];
919
973
  }
974
+ const assignedTo = options.for === "human" ? "human" : options.for === "ai" ? "ai" : options.for === "both" ? "both" : "both";
920
975
  const task = await taskOps.createTask({
921
976
  title,
922
977
  notes: options.notes,
@@ -927,6 +982,8 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
927
982
  // AUTO-TAGGED!
928
983
  created_by: createdBy,
929
984
  // ai or human
985
+ assigned_to: assignedTo,
986
+ // who can complete this task
930
987
  status: createdBy === "ai" ? "vibing" : "todo",
931
988
  // AI tasks start vibing
932
989
  energy_required: options.energy,
@@ -971,6 +1028,11 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
971
1028
  console.log(chalk3.gray(`\u{1F916} Created by: ${chalk3.cyan("AI")}`));
972
1029
  console.log(chalk3.gray(`\u26A1 Status: ${chalk3.magenta("vibing")}`));
973
1030
  }
1031
+ if (assignedTo === "human") {
1032
+ console.log(chalk3.gray(`\u{1F464} For: ${chalk3.yellow("HUMAN TASK")} (AI cannot complete)`));
1033
+ } else if (assignedTo === "ai") {
1034
+ console.log(chalk3.gray(`\u{1F916} For: ${chalk3.cyan("AI TASK")} (human review optional)`));
1035
+ }
974
1036
  console.log(chalk3.gray(`ID: ${task.id.substring(0, 8)}...`));
975
1037
  console.log(chalk3.white.bold(task.title));
976
1038
  if (task.notes) {
@@ -998,6 +1060,21 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
998
1060
  console.log(chalk3.cyan(`Subtasks: ${subtasksJson.length}`));
999
1061
  subtasksJson.forEach((st, i) => {
1000
1062
  console.log(chalk3.gray(` ${i + 1}. ${st.title}`));
1063
+ if (st.instructions) {
1064
+ console.log(chalk3.gray(` \u{1F4DD} Has instructions`));
1065
+ }
1066
+ if (st.link) {
1067
+ console.log(chalk3.gray(` \u{1F517} Link: ${st.link.label || st.link.url}`));
1068
+ }
1069
+ if (st.code) {
1070
+ console.log(chalk3.gray(` \u{1F4BB} Has code snippet`));
1071
+ }
1072
+ if (st.warning) {
1073
+ console.log(chalk3.red(` \u26A0\uFE0F ${st.warning}`));
1074
+ }
1075
+ if (st.tip) {
1076
+ console.log(chalk3.blue(` \u{1F4A1} ${st.tip}`));
1077
+ }
1001
1078
  });
1002
1079
  }
1003
1080
  if (acceptanceCriteria.length > 0) {
@@ -1059,9 +1136,9 @@ var listCommand = new Command3("list").description("List tasks").argument("[filt
1059
1136
  tasks = tasks.filter((t) => t.created_by === options.createdBy);
1060
1137
  }
1061
1138
  if (options.status) {
1062
- const validStatuses = ["todo", "vibing", "done"];
1139
+ const validStatuses = ["todo", "vibing", "done", "paused", "archived"];
1063
1140
  if (!validStatuses.includes(options.status)) {
1064
- console.error(chalk4.red(`Invalid status filter. Must be: todo, vibing, or done`));
1141
+ console.error(chalk4.red(`Invalid status filter. Must be: todo, vibing, done, paused, or archived`));
1065
1142
  process.exit(1);
1066
1143
  }
1067
1144
  tasks = tasks.filter((t) => t.status === options.status);
@@ -1157,7 +1234,13 @@ ${filterMessages[filter] || "No tasks found"}.
1157
1234
  const id = task.id.substring(0, 8);
1158
1235
  const status = statusColor(`${statusEmojis[task.status || "todo"]} ${task.status || "todo"}`);
1159
1236
  const title = task.status === "done" ? chalk4.strikethrough(task.title) : task.title;
1160
- const titleWithAI = task.created_by === "ai" ? `\u{1F916} ${title}` : title;
1237
+ let titleWithIndicators = title;
1238
+ if (task.assigned_to === "human") {
1239
+ titleWithIndicators = `\u{1F464} ${title}`;
1240
+ } else if (task.created_by === "ai") {
1241
+ titleWithIndicators = `\u{1F916} ${title}`;
1242
+ }
1243
+ const titleWithAI = titleWithIndicators;
1161
1244
  const project = task.project_tag ? chalk4.blue(task.project_tag) : chalk4.gray("-");
1162
1245
  const priority = priorityColor(task.priority || "none");
1163
1246
  const dueDate = task.due_date ? task.due_date.split("T")[0] : chalk4.gray("-");
@@ -1214,10 +1297,10 @@ var doneCommand = new Command4("done").description("Mark a task as complete").ar
1214
1297
  const authManager = new AuthManager5();
1215
1298
  const taskOps = await TaskOperations3.fromAuthManager(authManager);
1216
1299
  const shortIdManager = new ShortIdManager();
1217
- let taskId = await shortIdManager.resolveId(id);
1218
- if (taskId.length < 32) {
1300
+ let taskId2 = await shortIdManager.resolveId(id, taskOps);
1301
+ if (taskId2.length < 32) {
1219
1302
  const allTasks = await taskOps.getTasks("all");
1220
- const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
1303
+ const matchingTask = allTasks.find((t) => t.id.startsWith(taskId2));
1221
1304
  if (!matchingTask) {
1222
1305
  spinner.fail(chalk5.red("Task not found"));
1223
1306
  console.error(chalk5.gray(`
@@ -1225,11 +1308,11 @@ No task found with ID starting with: ${id}
1225
1308
  `));
1226
1309
  process.exit(1);
1227
1310
  }
1228
- taskId = matchingTask.id;
1311
+ taskId2 = matchingTask.id;
1229
1312
  }
1230
1313
  const sessionManager2 = getSessionManager();
1231
1314
  const currentSession = await sessionManager2.getOrCreateSession();
1232
- const existingTask = await taskOps.getTask(taskId);
1315
+ const existingTask = await taskOps.getTask(taskId2);
1233
1316
  const existingHistory = existingTask?.session_history || [];
1234
1317
  const updatedHistory = [
1235
1318
  ...existingHistory,
@@ -1248,8 +1331,8 @@ No task found with ID starting with: ${id}
1248
1331
  if (contextNotes) {
1249
1332
  updatePayload.context_notes = contextNotes;
1250
1333
  }
1251
- await taskOps.updateTask(taskId, updatePayload);
1252
- const task = await taskOps.completeTask(taskId);
1334
+ await taskOps.updateTask(taskId2, updatePayload);
1335
+ const task = await taskOps.completeTask(taskId2);
1253
1336
  await sessionManager2.logAction({
1254
1337
  type: "task_completed",
1255
1338
  description: `Completed: "${task.title}"`,
@@ -1270,10 +1353,22 @@ No task found with ID starting with: ${id}
1270
1353
  if (error.message.includes("Not authenticated")) {
1271
1354
  console.error(chalk5.yellow("\n\u26A0\uFE0F You need to login first"));
1272
1355
  console.error(chalk5.gray("Run: taskflow login\n"));
1356
+ } else if (error.message.includes("not found")) {
1357
+ console.error(chalk5.red(`
1358
+ Task not found: ${id}`));
1359
+ console.error(chalk5.gray('Tip: Run "vibetasks list" to see all tasks\n'));
1360
+ } else if (error.message.includes("JSON")) {
1361
+ console.error(chalk5.red(`
1362
+ Database error: ${error.message}`));
1363
+ console.error(chalk5.gray("This might be a database schema issue. Check task status in the web dashboard.\n"));
1273
1364
  } else {
1274
1365
  console.error(chalk5.red(`
1275
- Error: ${error.message}
1276
- `));
1366
+ Error: ${error.message}`));
1367
+ console.error(chalk5.gray(`
1368
+ Task ID: ${taskId || id}`));
1369
+ console.error(chalk5.gray("Full error details:"));
1370
+ console.error(error);
1371
+ console.log();
1277
1372
  }
1278
1373
  process.exit(1);
1279
1374
  }
@@ -1284,16 +1379,20 @@ import { Command as Command5 } from "commander";
1284
1379
  import chalk6 from "chalk";
1285
1380
  import ora4 from "ora";
1286
1381
  import inquirer from "inquirer";
1287
- import { AuthManager as AuthManager6, TaskOperations as TaskOperations4 } from "@vibetasks/core";
1288
- var vibingCommand = new Command5("vibing").alias("start").alias("v").description("Start working on a task (move to vibing status)").argument("[task-id]", "Task ID or short # (1-99) to start vibing on").option("-p, --pick", "Pick from todo tasks interactively").action(async (taskIdInput, options) => {
1382
+ import { spawn, execSync } from "child_process";
1383
+ import path from "path";
1384
+ import { fileURLToPath } from "url";
1385
+ import { AuthManager as AuthManager6, TaskOperations as TaskOperations4, createBranchName } from "@vibetasks/core";
1386
+ var __dirname2 = path.dirname(fileURLToPath(import.meta.url));
1387
+ var vibingCommand = new Command5("vibing").alias("start").alias("v").description("Start working on a task (move to vibing status)").argument("[task-id]", "Task ID or short # (1-99) to start vibing on").option("-p, --pick", "Pick from todo tasks interactively").option("--no-branch", "Skip automatic Git branch creation").option("--branch-type <type>", "Branch type: feature, fix, bugfix, hotfix, chore", "feature").option("--autonomous", "Launch autonomous Ralph loop (external orchestration)").option("--max-iterations <n>", "Maximum iterations for autonomous loop", "10").option("--agent <type>", "Agent to use: vibebot|opencode|claude", "vibebot").option("--no-verify-build", "Skip build verification in autonomous mode").option("--no-verify-tests", "Skip test verification in autonomous mode").action(async (taskIdInput, options) => {
1289
1388
  const spinner = ora4();
1290
1389
  try {
1291
1390
  const authManager = new AuthManager6();
1292
1391
  const taskOps = await TaskOperations4.fromAuthManager(authManager);
1293
1392
  const shortIdManager = new ShortIdManager();
1294
- let taskId;
1393
+ let taskId2;
1295
1394
  if (taskIdInput) {
1296
- taskId = await shortIdManager.resolveId(taskIdInput);
1395
+ taskId2 = await shortIdManager.resolveId(taskIdInput, taskOps);
1297
1396
  }
1298
1397
  const allTasks = await taskOps.getTasks("all");
1299
1398
  const vibingTasks = allTasks.filter((t) => t.status === "vibing" && !t.completed);
@@ -1301,7 +1400,7 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
1301
1400
  console.log(chalk6.gray(`
1302
1401
  Currently vibing on ${vibingTasks.length} task(s)`));
1303
1402
  }
1304
- if (!taskId || options.pick) {
1403
+ if (!taskId2 || options.pick) {
1305
1404
  const todoTasks = allTasks.filter((t) => t.status === "todo" && !t.completed);
1306
1405
  if (todoTasks.length === 0) {
1307
1406
  console.log(chalk6.yellow('\nNo todo tasks found. Add one with `vibetasks add "task title"`\n'));
@@ -1316,10 +1415,10 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
1316
1415
  value: t.id
1317
1416
  }))
1318
1417
  }]);
1319
- taskId = selectedTask;
1418
+ taskId2 = selectedTask;
1320
1419
  }
1321
1420
  spinner.start("Starting task...");
1322
- const task = allTasks.find((t) => t.id === taskId);
1421
+ const task = allTasks.find((t) => t.id === taskId2);
1323
1422
  const sessionManager2 = getSessionManager();
1324
1423
  const currentSession = await sessionManager2.getOrCreateSession();
1325
1424
  const existingSessionHistory = task?.session_history || [];
@@ -1335,7 +1434,7 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
1335
1434
  }
1336
1435
  ];
1337
1436
  }
1338
- await taskOps.updateTask(taskId, {
1437
+ await taskOps.updateTask(taskId2, {
1339
1438
  status: "vibing",
1340
1439
  completed: false,
1341
1440
  session_id: currentSession.id,
@@ -1344,16 +1443,88 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
1344
1443
  });
1345
1444
  spinner.succeed(chalk6.green("Now vibing on:"));
1346
1445
  console.log(chalk6.white(`
1347
- ${task?.title || taskId}
1446
+ ${task?.title || taskId2}
1348
1447
  `));
1349
1448
  if (lastSession && lastSession.session_id !== currentSession.id) {
1350
1449
  console.log(chalk6.cyan(` \u{1F4CB} Continuing from session: ${lastSession.session_id}`));
1351
1450
  console.log(chalk6.gray(` This task has been worked on by ${existingSessionHistory.length} session(s)
1352
1451
  `));
1353
1452
  }
1453
+ if (options.branch !== false && task) {
1454
+ const branchType = options.branchType || "feature";
1455
+ const branchName = createBranchName(
1456
+ { short_id: task.short_id, title: task.title },
1457
+ branchType
1458
+ );
1459
+ console.log(chalk6.cyan(`
1460
+ \u{1F33F} Creating branch: ${branchName}`));
1461
+ try {
1462
+ execSync("git rev-parse --git-dir", { stdio: "pipe" });
1463
+ try {
1464
+ execSync(`git rev-parse --verify ${branchName}`, { stdio: "pipe" });
1465
+ console.log(chalk6.yellow(` Branch already exists. Checking out...`));
1466
+ execSync(`git checkout ${branchName}`, { stdio: "inherit" });
1467
+ } catch {
1468
+ execSync(`git checkout -b ${branchName}`, { stdio: "inherit" });
1469
+ }
1470
+ console.log(chalk6.green(` \u2713 On branch: ${branchName}`));
1471
+ console.log(chalk6.gray(` When you merge a PR from this branch, VT-${task.short_id} will auto-complete!
1472
+ `));
1473
+ await taskOps.updateTask(taskId2, {
1474
+ context_notes: `Branch: ${branchName}
1475
+ ${task.context_notes || ""}`
1476
+ });
1477
+ } catch (err) {
1478
+ console.log(chalk6.yellow(` \u26A0\uFE0F Not in a git repo or git error: ${err.message}`));
1479
+ }
1480
+ }
1481
+ if (options.autonomous) {
1482
+ console.log(chalk6.cyan("\n\u{1F3B5} Starting autonomous Ralph loop...\n"));
1483
+ console.log(chalk6.gray(` Agent: ${options.agent}`));
1484
+ console.log(chalk6.gray(` Max Iterations: ${options.maxIterations}`));
1485
+ console.log(chalk6.gray(` Build Verification: ${options.verifyBuild !== false ? "enabled" : "disabled"}`));
1486
+ console.log(chalk6.gray(` Test Verification: ${options.verifyTests !== false ? "enabled" : "disabled"}`));
1487
+ console.log(chalk6.gray(`
1488
+ Press Ctrl+C to stop the loop at any time.
1489
+ `));
1490
+ const scriptPath = path.join(__dirname2, "../../scripts/vibing-loop.sh");
1491
+ const env = {
1492
+ ...process.env,
1493
+ VERIFY_BUILD: options.verifyBuild !== false ? "true" : "false",
1494
+ VERIFY_TESTS: options.verifyTests !== false ? "true" : "false"
1495
+ };
1496
+ const loopProcess = spawn("bash", [
1497
+ scriptPath,
1498
+ taskId2,
1499
+ options.maxIterations || "10",
1500
+ options.agent || "vibebot"
1501
+ ], {
1502
+ stdio: "inherit",
1503
+ cwd: process.cwd(),
1504
+ env
1505
+ });
1506
+ loopProcess.on("exit", (code) => {
1507
+ if (code === 0) {
1508
+ console.log(chalk6.green("\n\u2705 Autonomous loop completed successfully\n"));
1509
+ } else if (code === null) {
1510
+ console.log(chalk6.yellow("\n\u26A0\uFE0F Loop interrupted by user\n"));
1511
+ } else {
1512
+ console.log(chalk6.red(`
1513
+ \u274C Loop exited with code ${code}
1514
+ `));
1515
+ }
1516
+ });
1517
+ loopProcess.on("error", (err) => {
1518
+ console.error(chalk6.red("\n\u274C Failed to start loop:"), err.message);
1519
+ process.exit(1);
1520
+ });
1521
+ return;
1522
+ }
1354
1523
  console.log(chalk6.gray(" Tips:"));
1355
1524
  console.log(chalk6.gray(" \u2022 Update context notes as you work"));
1356
- console.log(chalk6.gray(" \u2022 Run `vibetasks done " + taskId.slice(0, 8) + "` when finished"));
1525
+ console.log(chalk6.gray(" \u2022 Run `vibetasks done " + taskId2.slice(0, 8) + "` when finished"));
1526
+ console.log(chalk6.gray(" \u2022 Use --autonomous flag to start Ralph loop"));
1527
+ console.log(chalk6.gray(" \u2022 Use --no-branch to skip auto branch creation"));
1357
1528
  console.log(chalk6.gray(' \u2022 In Claude Code, say "show my vibing tasks"\n'));
1358
1529
  } catch (error) {
1359
1530
  spinner.fail(chalk6.red("Failed to start task"));
@@ -1437,19 +1608,8 @@ var updateCommand = new Command7("update").description("Update a task").argument
1437
1608
  try {
1438
1609
  const authManager = new AuthManager8();
1439
1610
  const taskOps = await TaskOperations6.fromAuthManager(authManager);
1440
- let taskId = id;
1441
- if (id.length < 32) {
1442
- const allTasks = await taskOps.getTasks("all");
1443
- const matchingTask = allTasks.find((t) => t.id.startsWith(id));
1444
- if (!matchingTask) {
1445
- spinner.fail(chalk8.red("Task not found"));
1446
- console.error(chalk8.gray(`
1447
- No task found with ID starting with: ${id}
1448
- `));
1449
- process.exit(1);
1450
- }
1451
- taskId = matchingTask.id;
1452
- }
1611
+ const shortIdManager = new ShortIdManager();
1612
+ let taskId2 = await shortIdManager.resolveId(id, taskOps);
1453
1613
  const updates = {};
1454
1614
  if (options.title) updates.title = options.title;
1455
1615
  if (options.notes) updates.notes = options.notes;
@@ -1477,7 +1637,7 @@ No task found with ID starting with: ${id}
1477
1637
  }
1478
1638
  updates.energy_required = options.energy;
1479
1639
  }
1480
- const task = await taskOps.updateTask(taskId, updates);
1640
+ const task = await taskOps.updateTask(taskId2, updates);
1481
1641
  spinner.succeed(chalk8.green("Task updated!"));
1482
1642
  console.log(chalk8.gray(`
1483
1643
  \u2713 ${task.title}
@@ -1507,23 +1667,10 @@ var deleteCommand = new Command8("delete").description("Delete a task").argument
1507
1667
  try {
1508
1668
  const authManager = new AuthManager9();
1509
1669
  const taskOps = await TaskOperations7.fromAuthManager(authManager);
1510
- let taskId = id;
1511
- let taskTitle = "";
1512
- if (id.length < 32) {
1513
- const allTasks = await taskOps.getTasks("all");
1514
- const matchingTask = allTasks.find((t) => t.id.startsWith(id));
1515
- if (!matchingTask) {
1516
- console.error(chalk9.red("\nTask not found"));
1517
- console.error(chalk9.gray(`No task found with ID starting with: ${id}
1518
- `));
1519
- process.exit(1);
1520
- }
1521
- taskId = matchingTask.id;
1522
- taskTitle = matchingTask.title;
1523
- } else {
1524
- const task = await taskOps.getTask(taskId);
1525
- taskTitle = task.title;
1526
- }
1670
+ const shortIdManager = new ShortIdManager();
1671
+ const taskId2 = await shortIdManager.resolveId(id, taskOps);
1672
+ const task = await taskOps.getTask(taskId2);
1673
+ const taskTitle = task.title;
1527
1674
  if (!options.yes) {
1528
1675
  const answers = await inquirer2.prompt([
1529
1676
  {
@@ -1539,7 +1686,7 @@ var deleteCommand = new Command8("delete").description("Delete a task").argument
1539
1686
  }
1540
1687
  }
1541
1688
  const spinner = ora6("Deleting task...").start();
1542
- await taskOps.deleteTask(taskId);
1689
+ await taskOps.deleteTask(taskId2);
1543
1690
  spinner.succeed(chalk9.green("Task deleted!"));
1544
1691
  console.log(chalk9.gray(`
1545
1692
  \u2717 ${taskTitle}
@@ -1623,17 +1770,17 @@ import { Command as Command10 } from "commander";
1623
1770
  import chalk11 from "chalk";
1624
1771
  import ora7 from "ora";
1625
1772
  import fs from "fs/promises";
1626
- import path from "path";
1773
+ import path2 from "path";
1627
1774
  function getVibetasksHooksPath() {
1628
- const cliRoot = path.dirname(path.dirname(path.dirname(new URL(import.meta.url).pathname)));
1775
+ const cliRoot = path2.dirname(path2.dirname(path2.dirname(new URL(import.meta.url).pathname)));
1629
1776
  const normalizedPath = process.platform === "win32" && cliRoot.startsWith("/") ? cliRoot.slice(1) : cliRoot;
1630
- return path.join(normalizedPath, "hooks");
1777
+ return path2.join(normalizedPath, "hooks");
1631
1778
  }
1632
1779
  var hooksCommand = new Command10("hooks").description("Manage hooks integration (git and Claude Code)");
1633
1780
  hooksCommand.command("install").description("Install git hooks in current repository").action(async () => {
1634
1781
  const spinner = ora7("Installing git hooks...").start();
1635
1782
  try {
1636
- const gitDir = path.join(process.cwd(), ".git");
1783
+ const gitDir = path2.join(process.cwd(), ".git");
1637
1784
  try {
1638
1785
  await fs.access(gitDir);
1639
1786
  } catch {
@@ -1641,9 +1788,9 @@ hooksCommand.command("install").description("Install git hooks in current reposi
1641
1788
  console.error(chalk11.gray("\nRun this command from the root of a git repository.\n"));
1642
1789
  process.exit(1);
1643
1790
  }
1644
- const hooksDir = path.join(gitDir, "hooks");
1791
+ const hooksDir = path2.join(gitDir, "hooks");
1645
1792
  await fs.mkdir(hooksDir, { recursive: true });
1646
- const postCommitPath = path.join(hooksDir, "post-commit");
1793
+ const postCommitPath = path2.join(hooksDir, "post-commit");
1647
1794
  const hookScript = `#!/bin/sh
1648
1795
  # VibeTasks post-commit hook
1649
1796
  # Automatically updates tasks based on commit messages
@@ -1697,7 +1844,7 @@ hooksCommand.command("claude").description("Generate Claude Code hook configurat
1697
1844
  try {
1698
1845
  const projectPath = options.project || process.cwd();
1699
1846
  const hooksPath = getVibetasksHooksPath();
1700
- const syncScriptPath = path.join(hooksPath, "sync-todos.js");
1847
+ const syncScriptPath = path2.join(hooksPath, "sync-todos.js");
1701
1848
  const hookConfig = {
1702
1849
  hooks: {
1703
1850
  SubagentStop: [
@@ -1716,9 +1863,9 @@ hooksCommand.command("claude").description("Generate Claude Code hook configurat
1716
1863
  console.log(JSON.stringify(hookConfig, null, 2));
1717
1864
  process.exit(0);
1718
1865
  }
1719
- const claudeDir = path.join(projectPath, ".claude");
1866
+ const claudeDir = path2.join(projectPath, ".claude");
1720
1867
  await fs.mkdir(claudeDir, { recursive: true });
1721
- const settingsPath = path.join(claudeDir, "settings.json");
1868
+ const settingsPath = path2.join(claudeDir, "settings.json");
1722
1869
  let existingSettings = {};
1723
1870
  try {
1724
1871
  const data = await fs.readFile(settingsPath, "utf-8");
@@ -1763,8 +1910,8 @@ Error: ${error.message}
1763
1910
  });
1764
1911
  hooksCommand.command("status").description("Show status of installed hooks").action(async () => {
1765
1912
  console.log(chalk11.blue.bold("\n VibeTasks Hooks Status\n"));
1766
- const gitHooksDir = path.join(process.cwd(), ".git", "hooks");
1767
- const postCommitPath = path.join(gitHooksDir, "post-commit");
1913
+ const gitHooksDir = path2.join(process.cwd(), ".git", "hooks");
1914
+ const postCommitPath = path2.join(gitHooksDir, "post-commit");
1768
1915
  try {
1769
1916
  const content = await fs.readFile(postCommitPath, "utf-8");
1770
1917
  if (content.includes("vibetasks") || content.includes("VibeTasks")) {
@@ -1775,7 +1922,7 @@ hooksCommand.command("status").description("Show status of installed hooks").act
1775
1922
  } catch {
1776
1923
  console.log(chalk11.gray(" [ ] Git post-commit hook not installed"));
1777
1924
  }
1778
- const claudeSettingsPath = path.join(process.cwd(), ".claude", "settings.json");
1925
+ const claudeSettingsPath = path2.join(process.cwd(), ".claude", "settings.json");
1779
1926
  try {
1780
1927
  const content = await fs.readFile(claudeSettingsPath, "utf-8");
1781
1928
  const settings = JSON.parse(content);
@@ -1863,7 +2010,7 @@ import inquirer4 from "inquirer";
1863
2010
  import ora9 from "ora";
1864
2011
  import chalk13 from "chalk";
1865
2012
  import fs2 from "fs/promises";
1866
- import path2 from "path";
2013
+ import path3 from "path";
1867
2014
  import os from "os";
1868
2015
  import { createServer as createServer2 } from "http";
1869
2016
  import { exec as exec2 } from "child_process";
@@ -1905,12 +2052,12 @@ async function getAvailablePort2(startPort = 3737) {
1905
2052
  });
1906
2053
  }
1907
2054
  function getClaudeConfigPath() {
1908
- const configHome = process.env.XDG_CONFIG_HOME || path2.join(os.homedir(), ".config");
1909
- return path2.join(configHome, "claude-code", "config.json");
2055
+ const configHome = process.env.XDG_CONFIG_HOME || path3.join(os.homedir(), ".config");
2056
+ return path3.join(configHome, "claude-code", "config.json");
1910
2057
  }
1911
2058
  async function claudeCodeConfigExists() {
1912
2059
  try {
1913
- const configDir = path2.dirname(getClaudeConfigPath());
2060
+ const configDir = path3.dirname(getClaudeConfigPath());
1914
2061
  await fs2.access(configDir);
1915
2062
  return true;
1916
2063
  } catch {
@@ -2125,7 +2272,7 @@ async function stepClaudeCodeConfig() {
2125
2272
  const spinner = ora9("Configuring Claude Code...").start();
2126
2273
  try {
2127
2274
  const configPath = getClaudeConfigPath();
2128
- const configDir = path2.dirname(configPath);
2275
+ const configDir = path3.dirname(configPath);
2129
2276
  await fs2.mkdir(configDir, { recursive: true });
2130
2277
  let config = {};
2131
2278
  try {
@@ -2374,7 +2521,7 @@ async function runNonInteractiveSetup() {
2374
2521
  }
2375
2522
  console.log(chalk13.gray("\nConfiguring Claude Code MCP server..."));
2376
2523
  const configPath = getClaudeConfigPath();
2377
- const configDir = path2.dirname(configPath);
2524
+ const configDir = path3.dirname(configPath);
2378
2525
  try {
2379
2526
  await fs2.mkdir(configDir, { recursive: true });
2380
2527
  let config = {};
@@ -2705,7 +2852,7 @@ import chalk15 from "chalk";
2705
2852
  import ora11 from "ora";
2706
2853
  import inquirer6 from "inquirer";
2707
2854
  import { AuthManager as AuthManager14, TaskOperations as TaskOperations10 } from "@vibetasks/core";
2708
- 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) => {
2855
+ 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 (taskId2, options) => {
2709
2856
  const spinner = ora11();
2710
2857
  try {
2711
2858
  const authManager = new AuthManager14();
@@ -2754,7 +2901,7 @@ Restored: ${task2.title}`));
2754
2901
  `));
2755
2902
  process.exit(0);
2756
2903
  }
2757
- if (options.pick || !taskId) {
2904
+ if (options.pick || !taskId2) {
2758
2905
  const allTasks = await taskOps.getTasks("completed");
2759
2906
  if (allTasks.length === 0) {
2760
2907
  console.log(chalk15.yellow("\nNo completed tasks to archive. Complete some tasks first!\n"));
@@ -2769,16 +2916,16 @@ Restored: ${task2.title}`));
2769
2916
  value: t.id
2770
2917
  }))
2771
2918
  }]);
2772
- taskId = selectedTask;
2919
+ taskId2 = selectedTask;
2773
2920
  }
2774
- if (!taskId) {
2921
+ if (!taskId2) {
2775
2922
  console.log(chalk15.yellow("\nUsage: vibetasks archive <task-id>"));
2776
2923
  console.log(chalk15.gray(" or: vibetasks archive --pick"));
2777
2924
  console.log(chalk15.gray(" or: vibetasks archive --list"));
2778
2925
  console.log(chalk15.gray(" or: vibetasks archive --unarchive <id>\n"));
2779
2926
  process.exit(1);
2780
2927
  }
2781
- const validTaskId = taskId;
2928
+ const validTaskId = taskId2;
2782
2929
  spinner.start("Archiving task...");
2783
2930
  let targetId = validTaskId;
2784
2931
  if (validTaskId.length < 32) {
@@ -2787,7 +2934,7 @@ Restored: ${task2.title}`));
2787
2934
  if (!matchingTask) {
2788
2935
  spinner.fail(chalk15.red("Task not found"));
2789
2936
  console.error(chalk15.gray(`
2790
- No task found with ID starting with: ${taskId}
2937
+ No task found with ID starting with: ${taskId2}
2791
2938
  `));
2792
2939
  process.exit(1);
2793
2940
  }
@@ -2820,13 +2967,13 @@ Error: ${error.message}
2820
2967
  import { Command as Command15 } from "commander";
2821
2968
  import chalk16 from "chalk";
2822
2969
  import ora12 from "ora";
2823
- import { spawn } from "child_process";
2824
- import path3 from "path";
2825
- import { fileURLToPath } from "url";
2970
+ import { spawn as spawn2 } from "child_process";
2971
+ import path4 from "path";
2972
+ import { fileURLToPath as fileURLToPath2 } from "url";
2826
2973
  import { AuthManager as AuthManager15, TaskOperations as TaskOperations11 } from "@vibetasks/core";
2827
2974
  import { generateErrorTaskTitle as generateErrorTaskTitle2 } from "@vibetasks/shared";
2828
- var __filename2 = fileURLToPath(import.meta.url);
2829
- var __dirname2 = path3.dirname(__filename2);
2975
+ var __filename2 = fileURLToPath2(import.meta.url);
2976
+ var __dirname3 = path4.dirname(__filename2);
2830
2977
  var daemonCommand = new Command15("daemon").description("Manage the VibeTasks error capture daemon").addCommand(createStartCommand()).addCommand(createStopCommand()).addCommand(createStatusCommand()).addCommand(createConfigureCommand()).addCommand(createCaptureCommand());
2831
2978
  function createStartCommand() {
2832
2979
  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) => {
@@ -3026,8 +3173,8 @@ function createCaptureCommand() {
3026
3173
  });
3027
3174
  }
3028
3175
  async function startDaemonBackground(config) {
3029
- const workerPath = path3.resolve(__dirname2, "..", "daemon-worker.js");
3030
- const child = spawn(process.execPath, [workerPath], {
3176
+ const workerPath = path4.resolve(__dirname3, "..", "daemon-worker.js");
3177
+ const child = spawn2(process.execPath, [workerPath], {
3031
3178
  detached: true,
3032
3179
  stdio: "ignore",
3033
3180
  env: {
@@ -3484,6 +3631,31 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
3484
3631
  console.log(chalk18.yellow("\u26A0 Sync failed (not logged in or server unavailable)"));
3485
3632
  }
3486
3633
  })
3634
+ ).addCommand(
3635
+ new Command17("compress").description("Compress session history to reduce context usage").option("--dry-run", "Show what would be compressed without making changes").action(async (options) => {
3636
+ const sessionManager2 = getSessionManager();
3637
+ const session = await sessionManager2.getCurrentSession();
3638
+ if (!session) {
3639
+ console.log(chalk18.yellow("No active session to compress"));
3640
+ return;
3641
+ }
3642
+ console.log(chalk18.cyan("Compressing session:"), chalk18.bold(session.id));
3643
+ console.log(chalk18.gray(`Current actions: ${session.actions.length}`));
3644
+ if (options.dryRun) {
3645
+ console.log(chalk18.yellow("DRY RUN - No changes will be made"));
3646
+ console.log();
3647
+ console.log(chalk18.cyan("Compression would:"));
3648
+ console.log(chalk18.gray(" \u2022 Summarize completed subtasks"));
3649
+ console.log(chalk18.gray(" \u2022 Remove redundant action logs"));
3650
+ console.log(chalk18.gray(" \u2022 Preserve critical decisions and blockers"));
3651
+ console.log(chalk18.gray(" \u2022 Maintain active task context"));
3652
+ console.log();
3653
+ console.log(chalk18.white("Estimated reduction: 60-80% of context size"));
3654
+ return;
3655
+ }
3656
+ console.log(chalk18.yellow("\u26A0 Compression not yet implemented"));
3657
+ console.log(chalk18.gray("This feature will be available in the next version"));
3658
+ })
3487
3659
  );
3488
3660
  function formatDuration(start, end) {
3489
3661
  const ms = new Date(end).getTime() - new Date(start).getTime();
@@ -3544,20 +3716,8 @@ subtaskCommand.command("list").description("List subtasks for a task").argument(
3544
3716
  const authManager = new AuthManager17();
3545
3717
  const taskOps = await TaskOperations13.fromAuthManager(authManager);
3546
3718
  const shortIdManager = new ShortIdManager();
3547
- let taskId = await shortIdManager.resolveId(taskIdArg);
3548
- if (taskId.length < 32) {
3549
- const allTasks = await taskOps.getTasks("all");
3550
- const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
3551
- if (!matchingTask) {
3552
- spinner.fail(chalk19.red("Task not found"));
3553
- console.error(chalk19.gray(`
3554
- No task found with ID: ${taskIdArg}
3555
- `));
3556
- process.exit(1);
3557
- }
3558
- taskId = matchingTask.id;
3559
- }
3560
- const task = await taskOps.getTask(taskId);
3719
+ let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
3720
+ const task = await taskOps.getTask(taskId2);
3561
3721
  const subtasks = task.subtasks_json || [];
3562
3722
  spinner.stop();
3563
3723
  console.log();
@@ -3592,20 +3752,8 @@ subtaskCommand.command("done").description("Mark subtasks as complete").argument
3592
3752
  const authManager = new AuthManager17();
3593
3753
  const taskOps = await TaskOperations13.fromAuthManager(authManager);
3594
3754
  const shortIdManager = new ShortIdManager();
3595
- let taskId = await shortIdManager.resolveId(taskIdArg);
3596
- if (taskId.length < 32) {
3597
- const allTasks = await taskOps.getTasks("all");
3598
- const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
3599
- if (!matchingTask) {
3600
- spinner.fail(chalk19.red("Task not found"));
3601
- console.error(chalk19.gray(`
3602
- No task found with ID: ${taskIdArg}
3603
- `));
3604
- process.exit(1);
3605
- }
3606
- taskId = matchingTask.id;
3607
- }
3608
- const task = await taskOps.getTask(taskId);
3755
+ let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
3756
+ const task = await taskOps.getTask(taskId2);
3609
3757
  const subtasks = [...task.subtasks_json || []];
3610
3758
  if (subtasks.length === 0) {
3611
3759
  spinner.fail(chalk19.yellow("No subtasks to update"));
@@ -3639,7 +3787,7 @@ Valid range: 1-${subtasks.length}
3639
3787
  spinner.succeed(chalk19.yellow("All specified subtasks were already done"));
3640
3788
  process.exit(0);
3641
3789
  }
3642
- await taskOps.updateTask(taskId, { subtasks_json: subtasks });
3790
+ await taskOps.updateTask(taskId2, { subtasks_json: subtasks });
3643
3791
  const doneCount = subtasks.filter((s) => s.done).length;
3644
3792
  spinner.succeed(chalk19.green(`Marked ${updated.length} subtask(s) as done`));
3645
3793
  console.log();
@@ -3650,7 +3798,7 @@ Valid range: 1-${subtasks.length}
3650
3798
  console.log(chalk19.gray(`Progress: ${doneCount}/${subtasks.length} completed`));
3651
3799
  if (doneCount === subtasks.length) {
3652
3800
  console.log();
3653
- console.log(chalk19.cyan(`All subtasks complete! Run: vibetasks done ${taskId.substring(0, 8)}`));
3801
+ console.log(chalk19.cyan(`All subtasks complete! Run: vibetasks done ${taskId2.substring(0, 8)}`));
3654
3802
  }
3655
3803
  console.log();
3656
3804
  process.exit(0);
@@ -3668,20 +3816,8 @@ subtaskCommand.command("undo").description("Mark subtasks as incomplete").argume
3668
3816
  const authManager = new AuthManager17();
3669
3817
  const taskOps = await TaskOperations13.fromAuthManager(authManager);
3670
3818
  const shortIdManager = new ShortIdManager();
3671
- let taskId = await shortIdManager.resolveId(taskIdArg);
3672
- if (taskId.length < 32) {
3673
- const allTasks = await taskOps.getTasks("all");
3674
- const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
3675
- if (!matchingTask) {
3676
- spinner.fail(chalk19.red("Task not found"));
3677
- console.error(chalk19.gray(`
3678
- No task found with ID: ${taskIdArg}
3679
- `));
3680
- process.exit(1);
3681
- }
3682
- taskId = matchingTask.id;
3683
- }
3684
- const task = await taskOps.getTask(taskId);
3819
+ let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
3820
+ const task = await taskOps.getTask(taskId2);
3685
3821
  const subtasks = [...task.subtasks_json || []];
3686
3822
  if (subtasks.length === 0) {
3687
3823
  spinner.fail(chalk19.yellow("No subtasks to update"));
@@ -3715,7 +3851,7 @@ Valid range: 1-${subtasks.length}
3715
3851
  spinner.succeed(chalk19.yellow("All specified subtasks were already incomplete"));
3716
3852
  process.exit(0);
3717
3853
  }
3718
- await taskOps.updateTask(taskId, { subtasks_json: subtasks });
3854
+ await taskOps.updateTask(taskId2, { subtasks_json: subtasks });
3719
3855
  const doneCount = subtasks.filter((s) => s.done).length;
3720
3856
  spinner.succeed(chalk19.yellow(`Marked ${updated.length} subtask(s) as incomplete`));
3721
3857
  console.log();
@@ -4072,14 +4208,288 @@ inboxCommand.addCommand(inboxStatsCommand);
4072
4208
  // src/commands/handoff.ts
4073
4209
  import { Command as Command21 } from "commander";
4074
4210
  import chalk22 from "chalk";
4211
+ import { randomUUID as randomUUID3 } from "crypto";
4075
4212
  import { AuthManager as AuthManager20, TaskOperations as TaskOperations16 } from "@vibetasks/core";
4076
- var handoffCommand = new Command21("handoff").description("Generate comprehensive handoff for the next AI (vibing tasks, context notes, git status)").argument("[session-id]", "Session ID (shows that session only)").option("--full", "Include full action history from session").action(async (sessionId, options) => {
4213
+ function extractDecisions(session) {
4214
+ if (!session?.actions) return [];
4215
+ const decisions = [];
4216
+ const decisionKeywords = ["decided", "choosing", "using", "approach", "instead of", "because", "opted for"];
4217
+ for (const action of session.actions) {
4218
+ const desc = action.description.toLowerCase();
4219
+ const hasDecisionKeyword = decisionKeywords.some((kw) => desc.includes(kw));
4220
+ if (hasDecisionKeyword || action.type === "note") {
4221
+ const sentences = action.description.split(/[.!?]\s+/);
4222
+ const decision = sentences[0];
4223
+ const rationale = sentences.slice(1).join(". ");
4224
+ decisions.push({
4225
+ decision,
4226
+ rationale: rationale || "See session history for context",
4227
+ timestamp: action.timestamp
4228
+ });
4229
+ }
4230
+ }
4231
+ return decisions.slice(-10);
4232
+ }
4233
+ function extractModifiedFiles(session) {
4234
+ if (!session?.actions) return [];
4235
+ const fileMap = /* @__PURE__ */ new Map();
4236
+ for (const action of session.actions) {
4237
+ if (action.type === "file_modified" && action.filePath) {
4238
+ fileMap.set(action.filePath, (fileMap.get(action.filePath) || 0) + 1);
4239
+ }
4240
+ }
4241
+ const files = Array.from(fileMap.entries()).sort((a, b) => b[1] - a[1]).map(([path6, count]) => ({
4242
+ path: path6,
4243
+ relevance: count >= 5 ? "critical" : count >= 2 ? "important" : "reference"
4244
+ }));
4245
+ return files.slice(0, 20);
4246
+ }
4247
+ async function generateStructuredHandoff(sessionManager2, includeFull) {
4248
+ const session = await sessionManager2.getCurrentSession();
4249
+ const authManager = new AuthManager20();
4250
+ const taskOps = await TaskOperations16.fromAuthManager(authManager);
4251
+ const allTasks = await taskOps.getTasks("all");
4252
+ const vibingTasks = allTasks.filter((t) => t.status === "vibing");
4253
+ const completed = [];
4254
+ const nextSteps = [];
4255
+ const blockers = [];
4256
+ const taskIds = [];
4257
+ let intent = "Continue working on vibing tasks";
4258
+ let inProgress = "";
4259
+ for (const task of vibingTasks) {
4260
+ taskIds.push(task.id);
4261
+ const contextNotes = task.context_notes || "";
4262
+ const intentMatch = contextNotes.match(/INTENT:\s*(.+?)(?:\n|$)/i);
4263
+ if (intentMatch) {
4264
+ intent = intentMatch[1].trim();
4265
+ } else {
4266
+ intent = task.title;
4267
+ }
4268
+ const doneMatch = contextNotes.match(/DONE:\s*(.+?)(?:\n(?:CURRENT|NEXT|BLOCKED)|$)/is);
4269
+ if (doneMatch) {
4270
+ completed.push(doneMatch[1].trim());
4271
+ }
4272
+ const currentMatch = contextNotes.match(/CURRENT:\s*(.+?)(?:\n(?:NEXT|BLOCKED)|$)/is);
4273
+ if (currentMatch) {
4274
+ inProgress = currentMatch[1].trim();
4275
+ }
4276
+ const nextMatch = contextNotes.match(/NEXT:\s*(.+?)(?:\n(?:BLOCKED)|$)/is);
4277
+ if (nextMatch) {
4278
+ nextSteps.push(nextMatch[1].trim());
4279
+ }
4280
+ const blockedMatch = contextNotes.match(/BLOCKED:\s*(.+?)$/is);
4281
+ if (blockedMatch && blockedMatch[1].trim() !== "None") {
4282
+ blockers.push({
4283
+ type: "external",
4284
+ description: blockedMatch[1].trim(),
4285
+ context: `Task: ${task.title}`
4286
+ });
4287
+ }
4288
+ const subtasks = task.subtasks_json || task.subtasks || [];
4289
+ subtasks.forEach((s) => {
4290
+ if (s.done) {
4291
+ completed.push(s.title);
4292
+ } else if (!inProgress) {
4293
+ inProgress = s.title;
4294
+ } else {
4295
+ nextSteps.push(s.title);
4296
+ }
4297
+ });
4298
+ }
4299
+ let gitBranch;
4300
+ let uncommittedFiles = [];
4301
+ try {
4302
+ const { execSync: execSync3 } = await import("child_process");
4303
+ gitBranch = execSync3("git branch --show-current", { encoding: "utf-8", timeout: 2e3 }).trim();
4304
+ const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 2e3 }).trim();
4305
+ uncommittedFiles = gitStatus.split("\n").filter(Boolean);
4306
+ } catch {
4307
+ }
4308
+ const handoff = {
4309
+ intent,
4310
+ progress: {
4311
+ completed: completed.slice(0, 10),
4312
+ // Limit to 10 for token efficiency
4313
+ in_progress: inProgress || "No current work in progress",
4314
+ next_steps: nextSteps.slice(0, 5)
4315
+ // Limit to 5 next steps
4316
+ },
4317
+ state: {
4318
+ git_branch: gitBranch,
4319
+ uncommitted_files: uncommittedFiles.slice(0, 20)
4320
+ // Limit to 20 files
4321
+ },
4322
+ decisions: extractDecisions(session),
4323
+ blockers,
4324
+ files: extractModifiedFiles(session),
4325
+ sessionId: session?.id || "unknown",
4326
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4327
+ taskIds
4328
+ };
4329
+ return handoff;
4330
+ }
4331
+ function estimateTokens(text) {
4332
+ return Math.ceil(text.length / 4);
4333
+ }
4334
+ async function generateA2AHandoff(sessionManager2, options = {}) {
4335
+ const maxTokens = options.maxTokens || 1e4;
4336
+ let estimatedTokens = 0;
4337
+ const authManager = new AuthManager20();
4338
+ const taskOps = await TaskOperations16.fromAuthManager(authManager);
4339
+ const allTasks = await taskOps.getTasks("all");
4340
+ const vibingTasks = allTasks.filter((t) => t.status === "vibing");
4341
+ const tier1Tasks = vibingTasks.slice(0, 3).map((task) => ({
4342
+ id: task.id,
4343
+ a2a_task_id: task.a2a_task_id,
4344
+ title: task.title,
4345
+ status: task.status || "vibing",
4346
+ semantic_vector_id: task.semantic_vector_id,
4347
+ last_update: task.updated_at || task.created_at || (/* @__PURE__ */ new Date()).toISOString(),
4348
+ priority: task.priority,
4349
+ subtasks: task.subtasks_json || task.subtasks || []
4350
+ }));
4351
+ const tier1Json = JSON.stringify(tier1Tasks);
4352
+ estimatedTokens += estimateTokens(tier1Json);
4353
+ let handoff_notes = "";
4354
+ const tools_used = [];
4355
+ const decision_tree = [];
4356
+ const blockers = [];
4357
+ const next_actions = [];
4358
+ for (const task of vibingTasks.slice(0, 3)) {
4359
+ const contextNotes = task.context_notes || "";
4360
+ const intentMatch = contextNotes.match(/INTENT:\s*(.+?)(?:\n|$)/i);
4361
+ if (intentMatch) {
4362
+ handoff_notes = intentMatch[1].trim();
4363
+ }
4364
+ const doneMatch = contextNotes.match(/DONE:\s*(.+?)(?:\n(?:CURRENT|NEXT|BLOCKED)|$)/is);
4365
+ if (doneMatch) {
4366
+ decision_tree.push({
4367
+ decision: "Completed",
4368
+ reasoning: doneMatch[1].trim()
4369
+ });
4370
+ }
4371
+ const nextMatch = contextNotes.match(/NEXT:\s*(.+?)(?:\n(?:BLOCKED)|$)/is);
4372
+ if (nextMatch) {
4373
+ next_actions.push(nextMatch[1].trim());
4374
+ }
4375
+ const blockedMatch = contextNotes.match(/BLOCKED:\s*(.+?)$/is);
4376
+ if (blockedMatch && blockedMatch[1].trim() !== "None") {
4377
+ blockers.push({
4378
+ type: "external",
4379
+ description: blockedMatch[1].trim(),
4380
+ context: `Task: ${task.title}`
4381
+ });
4382
+ }
4383
+ }
4384
+ handoff_notes = handoff_notes || "Continue working on vibing tasks";
4385
+ const context_summary = handoff_notes;
4386
+ estimatedTokens += estimateTokens(JSON.stringify({ handoff_notes, context_summary, decision_tree, blockers, next_actions }));
4387
+ let uncommittedFiles = [];
4388
+ let gitBranch;
4389
+ if (estimatedTokens < maxTokens * 0.7) {
4390
+ try {
4391
+ const { execSync: execSync3 } = await import("child_process");
4392
+ gitBranch = execSync3("git branch --show-current", { encoding: "utf-8", timeout: 2e3 }).trim();
4393
+ const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 2e3 }).trim();
4394
+ uncommittedFiles = gitStatus.split("\n").filter(Boolean).slice(0, 20);
4395
+ estimatedTokens += estimateTokens(JSON.stringify({ git_branch: gitBranch, uncommitted_files: uncommittedFiles }));
4396
+ } catch {
4397
+ }
4398
+ }
4399
+ const session = await sessionManager2.getCurrentSession();
4400
+ const sessionId = session?.id || "unknown";
4401
+ const executionContext = {
4402
+ tools_used,
4403
+ decision_tree,
4404
+ blockers,
4405
+ next_actions
4406
+ };
4407
+ const resourceRequirements = {
4408
+ capabilities: ["code_generation", "task_management"],
4409
+ estimated_time_minutes: vibingTasks.length * 15,
4410
+ // Rough estimate: 15 min per task
4411
+ priority: vibingTasks.some((t) => t.priority === "high") ? "high" : "medium"
4412
+ };
4413
+ const handoffId = randomUUID3();
4414
+ const a2aRequest = {
4415
+ jsonrpc: "2.0",
4416
+ method: "vibetasks.handoff",
4417
+ params: {
4418
+ handoff_type: "continue",
4419
+ tasks: tier1Tasks,
4420
+ handoff_notes,
4421
+ context_summary,
4422
+ execution_context: executionContext,
4423
+ resource_requirements: resourceRequirements,
4424
+ semantic_context_ids: tier1Tasks.map((t) => t.semantic_vector_id).filter(Boolean),
4425
+ workspace_id: options.workspaceId,
4426
+ source_session_id: sessionId
4427
+ },
4428
+ id: handoffId
4429
+ };
4430
+ estimatedTokens = estimateTokens(JSON.stringify(a2aRequest));
4431
+ return {
4432
+ request: a2aRequest,
4433
+ estimatedTokens
4434
+ };
4435
+ }
4436
+ var handoffCommand = new Command21("handoff").description("Generate comprehensive handoff for the next AI (vibing tasks, context notes, git status)").argument("[session-id]", "Session ID (shows that session only)").option("--full", "Include full action history from session").option("--json", "Output as structured JSON (HandoffContext format)").option("--a2a", "Output as A2A JSON-RPC 2.0 format (for agent-to-agent handoffs)").option("--max-tokens <number>", "Maximum tokens for A2A handoff (default: 10000)", parseInt).option("--workspace-id <id>", "Workspace ID for A2A task IDs").option("--save", "Save handoff to .vibebot/handoffs/").option("--auto-update-context", "Automatically update current vibing task context notes").action(async (sessionId, options) => {
4077
4437
  const sessionManager2 = getSessionManager();
4078
- if (sessionId) {
4438
+ if (sessionId && !options?.json) {
4079
4439
  const handoff = await sessionManager2.getSessionHandoff(sessionId);
4080
4440
  console.log(handoff);
4081
4441
  return;
4082
4442
  }
4443
+ if (options?.a2a) {
4444
+ try {
4445
+ const { request, estimatedTokens } = await generateA2AHandoff(sessionManager2, {
4446
+ maxTokens: options?.maxTokens,
4447
+ workspaceId: options?.workspaceId
4448
+ });
4449
+ if (options?.save) {
4450
+ const fs4 = await import("fs/promises");
4451
+ const path6 = await import("path");
4452
+ const handoffDir = path6.join(process.cwd(), ".vibebot", "handoffs");
4453
+ await fs4.mkdir(handoffDir, { recursive: true });
4454
+ const filename = `a2a_${request.id}.json`;
4455
+ const filepath = path6.join(handoffDir, filename);
4456
+ await fs4.writeFile(filepath, JSON.stringify(request, null, 2));
4457
+ console.error(chalk22.green("\u2713 A2A handoff saved:"), chalk22.gray(filepath));
4458
+ console.error(chalk22.gray(` Estimated tokens: ${estimatedTokens}`));
4459
+ }
4460
+ console.log(JSON.stringify(request, null, 2));
4461
+ if (!options?.save) {
4462
+ console.error(chalk22.gray(`
4463
+ # Estimated tokens: ${estimatedTokens}`));
4464
+ }
4465
+ return;
4466
+ } catch (error) {
4467
+ console.error(chalk22.red("Error generating A2A handoff:"), error);
4468
+ process.exit(1);
4469
+ }
4470
+ }
4471
+ if (options?.json || options?.save) {
4472
+ try {
4473
+ const handoffData = await generateStructuredHandoff(sessionManager2, options?.full);
4474
+ if (options?.save) {
4475
+ const fs4 = await import("fs/promises");
4476
+ const path6 = await import("path");
4477
+ const handoffDir = path6.join(process.cwd(), ".vibebot", "handoffs");
4478
+ await fs4.mkdir(handoffDir, { recursive: true });
4479
+ const filename = `${handoffData.sessionId}_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-")}.json`;
4480
+ const filepath = path6.join(handoffDir, filename);
4481
+ await fs4.writeFile(filepath, JSON.stringify(handoffData, null, 2));
4482
+ console.log(chalk22.green("\u2713 Handoff saved:"), chalk22.gray(filepath));
4483
+ }
4484
+ if (options?.json) {
4485
+ console.log(JSON.stringify(handoffData, null, 2));
4486
+ }
4487
+ return;
4488
+ } catch (error) {
4489
+ console.error(chalk22.red("Error generating handoff:"), error);
4490
+ process.exit(1);
4491
+ }
4492
+ }
4083
4493
  console.log(chalk22.cyan("# AI Handoff"));
4084
4494
  console.log(chalk22.gray(`Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}`));
4085
4495
  console.log();
@@ -4140,8 +4550,8 @@ var handoffCommand = new Command21("handoff").description("Generate comprehensiv
4140
4550
  console.log();
4141
4551
  }
4142
4552
  try {
4143
- const { execSync: execSync2 } = await import("child_process");
4144
- const gitStatus = execSync2("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
4553
+ const { execSync: execSync3 } = await import("child_process");
4554
+ const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
4145
4555
  if (gitStatus) {
4146
4556
  const files = gitStatus.split("\n");
4147
4557
  console.log(chalk22.yellow(`## Uncommitted Changes (${files.length} files)`));
@@ -4151,7 +4561,7 @@ var handoffCommand = new Command21("handoff").description("Generate comprehensiv
4151
4561
  }
4152
4562
  console.log();
4153
4563
  }
4154
- const commits = execSync2("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
4564
+ const commits = execSync3("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
4155
4565
  if (commits) {
4156
4566
  console.log(chalk22.yellow("## Recent Commits"));
4157
4567
  commits.split("\n").forEach((c) => console.log(chalk22.gray(` ${c}`)));
@@ -4180,7 +4590,7 @@ import { readFile as readFile3 } from "fs/promises";
4180
4590
  import { existsSync } from "fs";
4181
4591
  import { AuthManager as AuthManager21, TaskOperations as TaskOperations17 } from "@vibetasks/core";
4182
4592
  import { detectProject as detectProject6 } from "@vibetasks/shared/utils/project-detector";
4183
- import { randomUUID as randomUUID3 } from "crypto";
4593
+ import { randomUUID as randomUUID4 } from "crypto";
4184
4594
  var parsePrdCommand = new Command22("parse-prd").description("Parse a PRD/requirements document and create tasks").argument("<file>", "Path to PRD/requirements file (markdown)").option("-t, --title <title>", "Override task title (auto-generated if not provided)").option("-p, --priority <level>", "Priority: low, medium, high", "high").option("--project <name>", "Override auto-detected project").option("--dry-run", "Show what would be created without creating").option("-v, --verbose", "Show detailed parsing output").action(async (filePath, options) => {
4185
4595
  const spinner = ora15("Parsing PRD...").start();
4186
4596
  try {
@@ -4200,7 +4610,7 @@ var parsePrdCommand = new Command22("parse-prd").description("Parse a PRD/requir
4200
4610
  }
4201
4611
  const finalTitle = options.title || parsed.title || "Implement PRD";
4202
4612
  const subtasksJson = parsed.tasks.map((task2, i) => ({
4203
- id: randomUUID3(),
4613
+ id: randomUUID4(),
4204
4614
  title: task2,
4205
4615
  done: false
4206
4616
  }));
@@ -4453,21 +4863,21 @@ function parsePrdContent(content, verbose = false) {
4453
4863
  import { Command as Command23 } from "commander";
4454
4864
  import chalk24 from "chalk";
4455
4865
  import { AuthManager as AuthManager22, TaskOperations as TaskOperations18 } from "@vibetasks/core";
4456
- import { execSync } from "child_process";
4866
+ import { execSync as execSync2 } from "child_process";
4457
4867
  import * as readline from "readline";
4458
- var checkpointCommand = new Command23("checkpoint").alias("cp").description("Quick context save for current work (interactive)").argument("[task-id]", "Task ID (defaults to your vibing task)").option("-q, --quick", "Quick mode: just git diff + timestamp, no prompts").option("-m, --message <msg>", "Context message (skip prompt)").option("--no-git", "Skip git diff attachment").action(async (taskId, options) => {
4868
+ var checkpointCommand = new Command23("checkpoint").alias("cp").description("Quick context save for current work (interactive)").argument("[task-id]", "Task ID (defaults to your vibing task)").option("-q, --quick", "Quick mode: just git diff + timestamp, no prompts").option("-m, --message <msg>", "Context message (skip prompt)").option("--no-git", "Skip git diff attachment").action(async (taskId2, options) => {
4459
4869
  try {
4460
4870
  const authManager = new AuthManager22();
4461
4871
  const taskOps = await TaskOperations18.fromAuthManager(authManager);
4462
4872
  const sessionManager2 = getSessionManager();
4463
4873
  let task;
4464
- if (taskId) {
4874
+ if (taskId2) {
4465
4875
  const allTasks = await taskOps.getTasks("all");
4466
4876
  task = allTasks.find(
4467
- (t) => t.id.startsWith(taskId) || t.short_id?.toString() === taskId
4877
+ (t) => t.id.startsWith(taskId2) || t.short_id?.toString() === taskId2
4468
4878
  );
4469
4879
  if (!task) {
4470
- console.error(chalk24.red(`Task not found: ${taskId}`));
4880
+ console.error(chalk24.red(`Task not found: ${taskId2}`));
4471
4881
  process.exit(1);
4472
4882
  }
4473
4883
  } else {
@@ -4591,9 +5001,9 @@ Error: ${error.message}
4591
5001
  function getGitContext() {
4592
5002
  const parts = [];
4593
5003
  try {
4594
- const branch = execSync("git branch --show-current", { encoding: "utf-8" }).trim();
5004
+ const branch = execSync2("git branch --show-current", { encoding: "utf-8" }).trim();
4595
5005
  parts.push(`Branch: ${branch}`);
4596
- const status = execSync("git status --porcelain", { encoding: "utf-8" }).trim();
5006
+ const status = execSync2("git status --porcelain", { encoding: "utf-8" }).trim();
4597
5007
  if (status) {
4598
5008
  const lines = status.split("\n").slice(0, 20);
4599
5009
  parts.push(`
@@ -4603,7 +5013,7 @@ ${lines.join("\n")}`);
4603
5013
  parts.push(`... and ${status.split("\n").length - 20} more`);
4604
5014
  }
4605
5015
  }
4606
- const log = execSync("git log --oneline -3", { encoding: "utf-8" }).trim();
5016
+ const log = execSync2("git log --oneline -3", { encoding: "utf-8" }).trim();
4607
5017
  if (log) {
4608
5018
  parts.push(`
4609
5019
  Recent commits:
@@ -4619,7 +5029,7 @@ ${log}`);
4619
5029
  import { Command as Command24 } from "commander";
4620
5030
  import chalk25 from "chalk";
4621
5031
  import { AuthManager as AuthManager23, TaskOperations as TaskOperations19 } from "@vibetasks/core";
4622
- var logCommand = new Command24("log").description("Show change history for a task (like git log)").argument("[task]", "Task ID or short number").option("-n, --limit <number>", "Maximum number of changes to show", "20").option("--oneline", "Compact one-line format").option("--since <date>", "Show changes after date (YYYY-MM-DD)").option("--until <date>", "Show changes before date (YYYY-MM-DD)").option("--type <types>", "Filter by change type (comma-separated)").option("--grep <query>", "Search change messages (ignores task arg)").option("--session <id>", "Show changes from specific session").action(async (taskArg, options) => {
5032
+ var logCommand = new Command24("log").description("Show change history for a task (like git log)").argument("[task]", "Task ID or short number").option("-n, --limit <number>", "Maximum number of changes to show", "20").option("--oneline", "Compact one-line format").option("--since <date>", "Show changes after date (YYYY-MM-DD)").option("--until <date>", "Show changes before date (YYYY-MM-DD)").option("--type <types>", "Filter by change type (comma-separated)").option("--grep <query>", "Search change messages (ignores task arg)").option("--session <id>", "Show changes from specific session").option("--source <name>", "Filter by source: sentry, github, slack, email, ai, human").action(async (taskArg, options) => {
4623
5033
  try {
4624
5034
  const authManager = new AuthManager23();
4625
5035
  const taskOps = await TaskOperations19.fromAuthManager(authManager);
@@ -4644,19 +5054,19 @@ Found ${changes2.length} change(s) matching "${options.grep}":
4644
5054
  console.error(chalk25.gray('Usage: vibetasks log <task-id> or vibetasks log --grep "query"'));
4645
5055
  process.exit(1);
4646
5056
  }
4647
- let taskId = taskArg;
5057
+ let taskId2 = taskArg;
4648
5058
  if (/^\d+$/.test(taskArg)) {
4649
5059
  const shortIdManager = new ShortIdManager();
4650
5060
  const resolved = await shortIdManager.resolveId(taskArg);
4651
5061
  if (resolved) {
4652
- taskId = resolved;
5062
+ taskId2 = resolved;
4653
5063
  } else {
4654
5064
  console.error(chalk25.red(`Task #${taskArg} not found`));
4655
5065
  console.error(chalk25.gray("Run: vibetasks list --short-ids to see available tasks"));
4656
5066
  process.exit(1);
4657
5067
  }
4658
5068
  }
4659
- const task = await taskOps.getTask(taskId);
5069
+ const task = await taskOps.getTask(taskId2);
4660
5070
  const queryOptions = {
4661
5071
  limit: parseInt(options.limit, 10)
4662
5072
  };
@@ -4669,7 +5079,32 @@ Found ${changes2.length} change(s) matching "${options.grep}":
4669
5079
  if (options.type) {
4670
5080
  queryOptions.changeTypes = options.type.split(",").map((t) => t.trim());
4671
5081
  }
4672
- const changes = await taskOps.getTaskChanges(taskId, queryOptions);
5082
+ let changes = await taskOps.getTaskChanges(taskId2, queryOptions);
5083
+ if (options.source) {
5084
+ const source = options.source.toLowerCase();
5085
+ changes = changes.filter((c) => {
5086
+ const msg = c.message || "";
5087
+ switch (source) {
5088
+ case "sentry":
5089
+ return msg.startsWith("[Sentry]");
5090
+ case "github":
5091
+ return msg.startsWith("[GitHub]");
5092
+ case "slack":
5093
+ return msg.startsWith("[Slack]");
5094
+ case "email":
5095
+ return msg.startsWith("[Email]");
5096
+ case "ai":
5097
+ return c.actor_type === "ai";
5098
+ case "human":
5099
+ return c.actor_type === "human";
5100
+ case "integration":
5101
+ case "integrations":
5102
+ return msg.startsWith("[Sentry]") || msg.startsWith("[GitHub]") || msg.startsWith("[Slack]") || msg.startsWith("[Email]");
5103
+ default:
5104
+ return true;
5105
+ }
5106
+ });
5107
+ }
4673
5108
  if (changes.length === 0) {
4674
5109
  console.log(chalk25.gray(`
4675
5110
  No change history found for task: ${task.title}
@@ -4678,7 +5113,7 @@ No change history found for task: ${task.title}
4678
5113
  }
4679
5114
  console.log();
4680
5115
  console.log(chalk25.cyan.bold(`History for: ${task.title}`));
4681
- console.log(chalk25.gray(`Task ID: ${taskId}`));
5116
+ console.log(chalk25.gray(`Task ID: ${taskId2}`));
4682
5117
  console.log(chalk25.gray(`Status: ${task.status} | Changes: ${changes.length}`));
4683
5118
  console.log();
4684
5119
  for (const change of changes) {
@@ -4718,15 +5153,41 @@ function printChange(change, oneline = false, taskTitle) {
4718
5153
  handoff: chalk25.magenta
4719
5154
  };
4720
5155
  const colorFn = typeColors[change.change_type] || chalk25.white;
4721
- const actorIcon = change.actor_type === "ai" ? "\u{1F916}" : change.actor_type === "human" ? "\u{1F464}" : "\u2699\uFE0F";
5156
+ let actorIcon;
5157
+ const message = change.message || "";
5158
+ if (message.startsWith("[Sentry]")) {
5159
+ actorIcon = "\u{1F534}";
5160
+ } else if (message.startsWith("[GitHub]")) {
5161
+ actorIcon = "\u{1F419}";
5162
+ } else if (message.startsWith("[Slack]")) {
5163
+ actorIcon = "\u{1F4AC}";
5164
+ } else if (message.startsWith("[Email]")) {
5165
+ actorIcon = "\u{1F4E7}";
5166
+ } else if (change.actor_type === "ai") {
5167
+ actorIcon = "\u{1F916}";
5168
+ } else if (change.actor_type === "human") {
5169
+ actorIcon = "\u{1F464}";
5170
+ } else {
5171
+ actorIcon = "\u2699\uFE0F";
5172
+ }
4722
5173
  if (oneline) {
4723
- const message = change.message || change.change_type;
5174
+ const message2 = change.message || change.change_type;
4724
5175
  console.log(
4725
- `${chalk25.yellow(changeId)} ${chalk25.gray(dateStr)} ${colorFn(change.change_type.padEnd(16))} ${actorIcon} ${message}`
5176
+ `${chalk25.yellow(changeId)} ${chalk25.gray(dateStr)} ${colorFn(change.change_type.padEnd(16))} ${actorIcon} ${message2}`
4726
5177
  );
4727
5178
  } else {
4728
5179
  console.log(chalk25.yellow(`change ${change.id}`));
4729
- console.log(`Actor: ${actorIcon} ${change.actor_type}`);
5180
+ let actorDisplay = `${actorIcon} ${change.actor_type}`;
5181
+ if (message.startsWith("[Sentry]")) {
5182
+ actorDisplay = `${actorIcon} sentry`;
5183
+ } else if (message.startsWith("[GitHub]")) {
5184
+ actorDisplay = `${actorIcon} github`;
5185
+ } else if (message.startsWith("[Slack]")) {
5186
+ actorDisplay = `${actorIcon} slack`;
5187
+ } else if (message.startsWith("[Email]")) {
5188
+ actorDisplay = `${actorIcon} email`;
5189
+ }
5190
+ console.log(`Actor: ${actorDisplay}`);
4730
5191
  console.log(`Date: ${dateStr} ${timeStr}`);
4731
5192
  if (change.session_id) {
4732
5193
  console.log(chalk25.gray(`Session: ${change.session_id.substring(0, 8)}`));
@@ -4877,12 +5338,12 @@ Total: ${pausedTasks.length} paused task(s)
4877
5338
  console.log(chalk27.gray(" vibetasks pause --list # List paused tasks\n"));
4878
5339
  process.exit(1);
4879
5340
  }
4880
- let taskId = taskArg;
5341
+ let taskId2 = taskArg;
4881
5342
  if (/^\d+$/.test(taskArg)) {
4882
5343
  const shortIdManager = new ShortIdManager();
4883
5344
  const resolved = await shortIdManager.resolveId(taskArg);
4884
5345
  if (resolved) {
4885
- taskId = resolved;
5346
+ taskId2 = resolved;
4886
5347
  } else {
4887
5348
  console.error(chalk27.red(`Task #${taskArg} not found`));
4888
5349
  console.error(chalk27.gray("Run: vibetasks list --short-ids to see available tasks"));
@@ -4895,14 +5356,14 @@ Total: ${pausedTasks.length} paused task(s)
4895
5356
  console.error(chalk27.red(`Task not found: ${taskArg}`));
4896
5357
  process.exit(1);
4897
5358
  }
4898
- taskId = matchingTask.id;
5359
+ taskId2 = matchingTask.id;
4899
5360
  }
4900
- const task = await taskOps.getTask(taskId);
5361
+ const task = await taskOps.getTask(taskId2);
4901
5362
  if (task.status === "paused") {
4902
5363
  console.log(chalk27.yellow(`
4903
5364
  Task is already paused: ${task.title}
4904
5365
  `));
4905
- console.log(chalk27.gray("Resume with: vibetasks resume " + taskId.slice(0, 8) + "\n"));
5366
+ console.log(chalk27.gray("Resume with: vibetasks resume " + taskId2.slice(0, 8) + "\n"));
4906
5367
  process.exit(0);
4907
5368
  }
4908
5369
  if (task.status === "done" || task.status === "archived") {
@@ -4912,7 +5373,7 @@ Cannot pause a ${task.status} task.
4912
5373
  process.exit(1);
4913
5374
  }
4914
5375
  spinner.start("Pausing task...");
4915
- const pausedTask = await taskOps.pauseTask(taskId, options.message);
5376
+ const pausedTask = await taskOps.pauseTask(taskId2, options.message);
4916
5377
  spinner.succeed(chalk27.green("Task paused!"));
4917
5378
  console.log();
4918
5379
  console.log(chalk27.yellow("\u23F8 ") + chalk27.white(pausedTask.title));
@@ -4920,7 +5381,7 @@ Cannot pause a ${task.status} task.
4920
5381
  console.log(chalk27.gray(` Reason: ${options.message}`));
4921
5382
  }
4922
5383
  console.log();
4923
- console.log(chalk27.gray(`Resume with: vibetasks resume ${taskId.slice(0, 8)}`));
5384
+ console.log(chalk27.gray(`Resume with: vibetasks resume ${taskId2.slice(0, 8)}`));
4924
5385
  console.log(chalk27.gray(`View paused: vibetasks pause --list`));
4925
5386
  console.log();
4926
5387
  process.exit(0);
@@ -4956,10 +5417,10 @@ var resumeCommand = new Command27("resume").description("Resume a paused task (l
4956
5417
  console.log(chalk28.gray("Pause a task with: vibetasks pause <task-id>\n"));
4957
5418
  process.exit(0);
4958
5419
  }
4959
- let taskId;
5420
+ let taskId2;
4960
5421
  if (!taskArg) {
4961
5422
  if (pausedTasks.length === 1) {
4962
- taskId = pausedTasks[0].id;
5423
+ taskId2 = pausedTasks[0].id;
4963
5424
  console.log(chalk28.gray(`
4964
5425
  Auto-selecting the only paused task...
4965
5426
  `));
@@ -4973,15 +5434,15 @@ Auto-selecting the only paused task...
4973
5434
  value: t.id
4974
5435
  }))
4975
5436
  }]);
4976
- taskId = selectedTask;
5437
+ taskId2 = selectedTask;
4977
5438
  }
4978
5439
  } else {
4979
- taskId = taskArg;
5440
+ taskId2 = taskArg;
4980
5441
  if (/^\d+$/.test(taskArg)) {
4981
5442
  const shortIdManager = new ShortIdManager();
4982
5443
  const resolved = await shortIdManager.resolveId(taskArg);
4983
5444
  if (resolved) {
4984
- taskId = resolved;
5445
+ taskId2 = resolved;
4985
5446
  } else {
4986
5447
  console.error(chalk28.red(`Task #${taskArg} not found`));
4987
5448
  console.error(chalk28.gray("Run: vibetasks list --short-ids to see available tasks"));
@@ -4993,21 +5454,21 @@ Auto-selecting the only paused task...
4993
5454
  console.error(chalk28.red(`Task not found: ${taskArg}`));
4994
5455
  process.exit(1);
4995
5456
  }
4996
- taskId = matchingTask.id;
5457
+ taskId2 = matchingTask.id;
4997
5458
  }
4998
5459
  }
4999
- const task = await taskOps.getTask(taskId);
5460
+ const task = await taskOps.getTask(taskId2);
5000
5461
  if (task.status !== "paused") {
5001
5462
  console.log(chalk28.yellow(`
5002
5463
  Task is not paused (status: ${task.status}): ${task.title}
5003
5464
  `));
5004
5465
  if (task.status === "todo") {
5005
- console.log(chalk28.gray("Start vibing with: vibetasks vibing " + taskId.slice(0, 8) + "\n"));
5466
+ console.log(chalk28.gray("Start vibing with: vibetasks vibing " + taskId2.slice(0, 8) + "\n"));
5006
5467
  }
5007
5468
  process.exit(0);
5008
5469
  }
5009
5470
  spinner.start("Resuming task...");
5010
- const resumedTask = await taskOps.resumeTask(taskId);
5471
+ const resumedTask = await taskOps.resumeTask(taskId2);
5011
5472
  spinner.succeed(chalk28.green("Task resumed!"));
5012
5473
  console.log();
5013
5474
  console.log(chalk28.magenta("\u26A1 ") + chalk28.white(resumedTask.title));
@@ -5016,8 +5477,8 @@ Task is not paused (status: ${task.status}): ${task.title}
5016
5477
  console.log(chalk28.gray(` Context: ${task.context_notes.split("\n").pop()?.slice(0, 60)}...`));
5017
5478
  }
5018
5479
  console.log();
5019
- console.log(chalk28.gray(`Complete with: vibetasks done ${taskId.slice(0, 8)}`));
5020
- console.log(chalk28.gray(`Pause again: vibetasks pause ${taskId.slice(0, 8)}`));
5480
+ console.log(chalk28.gray(`Complete with: vibetasks done ${taskId2.slice(0, 8)}`));
5481
+ console.log(chalk28.gray(`Pause again: vibetasks pause ${taskId2.slice(0, 8)}`));
5021
5482
  console.log();
5022
5483
  process.exit(0);
5023
5484
  } catch (error) {
@@ -5046,13 +5507,13 @@ var revertCommand = new Command28("revert").description("Revert a specific chang
5046
5507
  const authManager = new AuthManager27();
5047
5508
  const taskOps = await TaskOperations23.fromAuthManager(authManager);
5048
5509
  if (options.task || !changeId) {
5049
- let taskId;
5510
+ let taskId2;
5050
5511
  if (options.task) {
5051
5512
  if (/^\d+$/.test(options.task)) {
5052
5513
  const shortIdManager = new ShortIdManager();
5053
5514
  const resolved = await shortIdManager.resolveId(options.task);
5054
5515
  if (resolved) {
5055
- taskId = resolved;
5516
+ taskId2 = resolved;
5056
5517
  } else {
5057
5518
  console.error(chalk29.red(`Task #${options.task} not found`));
5058
5519
  process.exit(1);
@@ -5061,21 +5522,21 @@ var revertCommand = new Command28("revert").description("Revert a specific chang
5061
5522
  const allTasks = await taskOps.getTasks("all");
5062
5523
  const matchingTask = allTasks.find((t) => t.id.startsWith(options.task));
5063
5524
  if (matchingTask) {
5064
- taskId = matchingTask.id;
5525
+ taskId2 = matchingTask.id;
5065
5526
  }
5066
5527
  } else {
5067
- taskId = options.task;
5528
+ taskId2 = options.task;
5068
5529
  }
5069
5530
  }
5070
- if (!taskId) {
5531
+ if (!taskId2) {
5071
5532
  console.log(chalk29.yellow("\nUsage:"));
5072
5533
  console.log(chalk29.gray(" vibetasks revert <change-id> # Revert specific change"));
5073
5534
  console.log(chalk29.gray(" vibetasks revert --task <task-id> # Pick from task changes"));
5074
5535
  console.log(chalk29.gray(" vibetasks log <task-id> # View changes first\n"));
5075
5536
  process.exit(1);
5076
5537
  }
5077
- const changes = await taskOps.getTaskChanges(taskId, { limit: 20 });
5078
- const task2 = await taskOps.getTask(taskId);
5538
+ const changes = await taskOps.getTaskChanges(taskId2, { limit: 20 });
5539
+ const task2 = await taskOps.getTask(taskId2);
5079
5540
  if (changes.length === 0) {
5080
5541
  console.log(chalk29.yellow(`
5081
5542
  No changes found for task: ${task2.title}
@@ -5200,12 +5661,12 @@ var blameCommand = new Command29("blame").description("Show who changed what on
5200
5661
  try {
5201
5662
  const authManager = new AuthManager28();
5202
5663
  const taskOps = await TaskOperations24.fromAuthManager(authManager);
5203
- let taskId = taskArg;
5664
+ let taskId2 = taskArg;
5204
5665
  if (/^\d+$/.test(taskArg)) {
5205
5666
  const shortIdManager = new ShortIdManager();
5206
5667
  const resolved = await shortIdManager.resolveId(taskArg);
5207
5668
  if (resolved) {
5208
- taskId = resolved;
5669
+ taskId2 = resolved;
5209
5670
  } else {
5210
5671
  console.error(chalk30.red(`Task #${taskArg} not found`));
5211
5672
  console.error(chalk30.gray("Run: vibetasks list --short-ids to see available tasks"));
@@ -5218,10 +5679,10 @@ var blameCommand = new Command29("blame").description("Show who changed what on
5218
5679
  console.error(chalk30.red(`Task not found: ${taskArg}`));
5219
5680
  process.exit(1);
5220
5681
  }
5221
- taskId = matchingTask.id;
5682
+ taskId2 = matchingTask.id;
5222
5683
  }
5223
- const task = await taskOps.getTask(taskId);
5224
- const changes = await taskOps.getTaskChanges(taskId, { limit: 100 });
5684
+ const task = await taskOps.getTask(taskId2);
5685
+ const changes = await taskOps.getTaskChanges(taskId2, { limit: 100 });
5225
5686
  if (changes.length === 0) {
5226
5687
  console.log(chalk30.yellow(`
5227
5688
  No change history for task: ${task.title}
@@ -5257,7 +5718,7 @@ No change history for task: ${task.title}
5257
5718
  }
5258
5719
  console.log();
5259
5720
  console.log(chalk30.cyan.bold(`Blame for: ${task.title}`));
5260
- console.log(chalk30.gray(`Task ID: ${taskId}`));
5721
+ console.log(chalk30.gray(`Task ID: ${taskId2}`));
5261
5722
  console.log();
5262
5723
  const fields = options.field ? [options.field] : Object.keys(blameMap).sort();
5263
5724
  if (fields.length === 0) {
@@ -5300,7 +5761,7 @@ No change history for task: ${task.title}
5300
5761
  console.log(chalk30.gray("\u2500".repeat(50)));
5301
5762
  console.log(chalk30.gray(`Total changes: ${totalChanges} | \u{1F916} AI: ${aiChanges} | \u{1F464} Human: ${humanChanges}`));
5302
5763
  console.log(chalk30.gray(`
5303
- Full history: vibetasks log ${taskId.slice(0, 8)}`));
5764
+ Full history: vibetasks log ${taskId2.slice(0, 8)}`));
5304
5765
  console.log();
5305
5766
  process.exit(0);
5306
5767
  } catch (error) {
@@ -5326,10 +5787,1068 @@ function formatValue3(value) {
5326
5787
  return str.length > 60 ? str.slice(0, 57) + "..." : str;
5327
5788
  }
5328
5789
 
5790
+ // src/commands/research.ts
5791
+ import { Command as Command30 } from "commander";
5792
+ import chalk31 from "chalk";
5793
+ import Table4 from "cli-table3";
5794
+ var researchCommand = new Command30("research").description("Manage research documents").action(() => {
5795
+ researchCommand.outputHelp();
5796
+ });
5797
+ researchCommand.command("list").alias("ls").description("List saved research documents").option("-p, --project <project>", "Filter by project").option("-s, --search <query>", "Search research docs").option("-l, --limit <number>", "Limit results", "20").action(async (options) => {
5798
+ try {
5799
+ const response = await fetch(`${process.env.VIBETASKS_API_URL || "http://localhost:3847"}/api/workspace-docs?` + new URLSearchParams({
5800
+ doc_type: "research",
5801
+ ...options.project && { project: options.project },
5802
+ ...options.search && { search: options.search },
5803
+ limit: options.limit
5804
+ }), {
5805
+ headers: {
5806
+ "Authorization": `Bearer ${process.env.VIBETASKS_AUTH_TOKEN}`
5807
+ }
5808
+ });
5809
+ if (!response.ok) {
5810
+ throw new Error("Failed to fetch research docs");
5811
+ }
5812
+ const data = await response.json();
5813
+ const docs = data.docs || [];
5814
+ if (docs.length === 0) {
5815
+ console.log(chalk31.yellow("\n\u{1F4DA} No research documents found.\n"));
5816
+ console.log(chalk31.dim("Use the MCP tool capture_research() to save research findings."));
5817
+ return;
5818
+ }
5819
+ console.log(chalk31.bold(`
5820
+ \u{1F4DA} Research Documents (${docs.length})
5821
+ `));
5822
+ const table = new Table4({
5823
+ head: [
5824
+ chalk31.cyan("#"),
5825
+ chalk31.cyan("Title"),
5826
+ chalk31.cyan("Project"),
5827
+ chalk31.cyan("Sources"),
5828
+ chalk31.cyan("Created")
5829
+ ],
5830
+ colWidths: [5, 40, 20, 10, 12],
5831
+ wordWrap: true
5832
+ });
5833
+ docs.forEach((doc, idx) => {
5834
+ const date = new Date(doc.created_at).toLocaleDateString();
5835
+ const sourceCount = doc.source_urls?.length || 0;
5836
+ table.push([
5837
+ idx + 1,
5838
+ doc.title,
5839
+ doc.project_tag || "none",
5840
+ sourceCount > 0 ? chalk31.green(sourceCount) : chalk31.dim("-"),
5841
+ date
5842
+ ]);
5843
+ });
5844
+ console.log(table.toString());
5845
+ console.log(chalk31.dim("\nUse: vibetasks research show <number> to view details\n"));
5846
+ } catch (error) {
5847
+ console.error(chalk31.red(`\u2717 Error: ${error.message}`));
5848
+ process.exit(1);
5849
+ }
5850
+ });
5851
+ researchCommand.command("show <id>").description("Show research document details").action(async (id) => {
5852
+ try {
5853
+ let docId = id;
5854
+ if (/^\d+$/.test(id)) {
5855
+ const response2 = await fetch(`${process.env.VIBETASKS_API_URL || "http://localhost:3847"}/api/workspace-docs?` + new URLSearchParams({
5856
+ doc_type: "research",
5857
+ limit: "100"
5858
+ }), {
5859
+ headers: {
5860
+ "Authorization": `Bearer ${process.env.VIBETASKS_AUTH_TOKEN}`
5861
+ }
5862
+ });
5863
+ const data2 = await response2.json();
5864
+ const docs = data2.docs || [];
5865
+ const idx = parseInt(id) - 1;
5866
+ if (idx < 0 || idx >= docs.length) {
5867
+ console.error(chalk31.red(`\u2717 Research doc #${id} not found`));
5868
+ process.exit(1);
5869
+ }
5870
+ docId = docs[idx].id;
5871
+ }
5872
+ const response = await fetch(`${process.env.VIBETASKS_API_URL || "http://localhost:3847"}/api/workspace-docs/${docId}`, {
5873
+ headers: {
5874
+ "Authorization": `Bearer ${process.env.VIBETASKS_AUTH_TOKEN}`
5875
+ }
5876
+ });
5877
+ if (!response.ok) {
5878
+ throw new Error("Failed to fetch research doc");
5879
+ }
5880
+ const data = await response.json();
5881
+ const doc = data.doc;
5882
+ console.log(chalk31.bold(`
5883
+ \u{1F4DA} ${doc.title}
5884
+ `));
5885
+ console.log(chalk31.dim("\u2500".repeat(80)));
5886
+ console.log(chalk31.cyan("Project:"), doc.project_tag || "none");
5887
+ console.log(chalk31.cyan("Created:"), new Date(doc.created_at).toLocaleString());
5888
+ console.log(chalk31.cyan("Created by:"), doc.created_by);
5889
+ if (doc.source_urls && doc.source_urls.length > 0) {
5890
+ console.log(chalk31.cyan("\nSources:"));
5891
+ doc.source_urls.forEach((url, idx) => {
5892
+ console.log(chalk31.dim(` ${idx + 1}. ${url}`));
5893
+ });
5894
+ }
5895
+ if (doc.search_queries && doc.search_queries.length > 0) {
5896
+ console.log(chalk31.cyan("\nSearch Queries:"));
5897
+ doc.search_queries.forEach((query) => {
5898
+ console.log(chalk31.dim(` \u2022 ${query}`));
5899
+ });
5900
+ }
5901
+ console.log(chalk31.cyan("\nContent:"));
5902
+ console.log(chalk31.dim("\u2500".repeat(80)));
5903
+ console.log(doc.content);
5904
+ console.log(chalk31.dim("\u2500".repeat(80) + "\n"));
5905
+ } catch (error) {
5906
+ console.error(chalk31.red(`\u2717 Error: ${error.message}`));
5907
+ process.exit(1);
5908
+ }
5909
+ });
5910
+
5911
+ // src/commands/bundle.ts
5912
+ import { Command as Command31 } from "commander";
5913
+ import chalk32 from "chalk";
5914
+ import ora19 from "ora";
5915
+ import {
5916
+ installBundle,
5917
+ removeBundle,
5918
+ updateBundle,
5919
+ activateBundle,
5920
+ deactivateBundle,
5921
+ loadInstalledBundles,
5922
+ loadBundleFromDirectory,
5923
+ getGlobalBundlesDir,
5924
+ getActiveBundle,
5925
+ getRequiredScaffoldVariables
5926
+ } from "@vibetasks/ralph-engine";
5927
+ import * as path5 from "path";
5928
+ import * as fs3 from "fs";
5929
+ var _inquirer = null;
5930
+ var _readline = null;
5931
+ async function getInquirer() {
5932
+ if (_inquirer === null) {
5933
+ try {
5934
+ _inquirer = (await import("inquirer")).default;
5935
+ } catch {
5936
+ _inquirer = false;
5937
+ }
5938
+ }
5939
+ return _inquirer;
5940
+ }
5941
+ async function getReadline() {
5942
+ if (_readline === null) {
5943
+ _readline = await import("readline");
5944
+ }
5945
+ return _readline;
5946
+ }
5947
+ async function promptFn(message) {
5948
+ const inquirer9 = await getInquirer();
5949
+ if (inquirer9) {
5950
+ const answers = await inquirer9.prompt([{ type: "input", name: "value", message }]);
5951
+ return answers.value;
5952
+ }
5953
+ const readline2 = await getReadline();
5954
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
5955
+ return new Promise((resolve) => rl.question(`${message} `, (answer) => {
5956
+ rl.close();
5957
+ resolve(answer);
5958
+ }));
5959
+ }
5960
+ async function confirmFn(message) {
5961
+ const inquirer9 = await getInquirer();
5962
+ if (inquirer9) {
5963
+ const answers = await inquirer9.prompt([{ type: "confirm", name: "confirm", message, default: true }]);
5964
+ return answers.confirm;
5965
+ }
5966
+ const answer = await promptFn(`${message} (y/n)`);
5967
+ return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
5968
+ }
5969
+ var installCommand = new Command31("install").description("Install a bundle from GitHub or local path").argument("<source>", "Bundle source (github:owner/repo, owner/repo, or local path)").option("-f, --force", "Overwrite if already installed").option("-g, --global", "Install globally (default)", true).option("-p, --project", "Install to current project only").option("-a, --activate", "Activate bundle after installing").action(async (source, options) => {
5970
+ const spinner = ora19(`Installing bundle from ${source}...`).start();
5971
+ try {
5972
+ const target = options.project ? process.cwd() : "global";
5973
+ const result = await installBundle(source, {
5974
+ target,
5975
+ force: options.force,
5976
+ activate: options.activate
5977
+ });
5978
+ if (!result.success) {
5979
+ spinner.fail(chalk32.red(result.error || "Installation failed"));
5980
+ if (result.warnings?.length) {
5981
+ result.warnings.forEach((w) => console.log(chalk32.yellow(` \u26A0 ${w}`)));
5982
+ }
5983
+ process.exit(1);
5984
+ }
5985
+ spinner.succeed(chalk32.green(`Installed ${result.bundle.manifest.name} v${result.bundle.manifest.version}`));
5986
+ if (result.warnings?.length) {
5987
+ result.warnings.forEach((w) => console.log(chalk32.yellow(` \u26A0 ${w}`)));
5988
+ }
5989
+ console.log(chalk32.gray(`
5990
+ Path: ${result.installPath}`));
5991
+ console.log(chalk32.gray(` Blueprints: ${result.bundle.blueprints.length}`));
5992
+ console.log(chalk32.gray(` Has Studio config: ${result.bundle.studio ? "Yes" : "No"}`));
5993
+ console.log(chalk32.gray(` Has Scaffold: ${result.bundle.scaffold ? "Yes" : "No"}`));
5994
+ if (options.activate) {
5995
+ console.log(chalk32.green(`
5996
+ \u2713 Bundle activated on current project`));
5997
+ } else {
5998
+ console.log(chalk32.blue(`
5999
+ To use this bundle: vibetasks bundle use ${result.bundle.manifest.name}`));
6000
+ }
6001
+ process.exit(0);
6002
+ } catch (error) {
6003
+ spinner.fail(chalk32.red("Installation failed"));
6004
+ console.error(chalk32.red(`
6005
+ Error: ${error.message}
6006
+ `));
6007
+ process.exit(1);
6008
+ }
6009
+ });
6010
+ var useCommand = new Command31("use").description("Activate a bundle on current project").argument("<name>", "Bundle name").option("-s, --scaffold", "Also apply scaffold template").option("-r, --run-commands", "Run post-scaffold commands").action(async (name, options) => {
6011
+ const spinner = ora19(`Activating bundle ${name}...`).start();
6012
+ try {
6013
+ const projectPath = process.cwd();
6014
+ const bundlePath = path5.join(getGlobalBundlesDir(), name);
6015
+ if (!fs3.existsSync(bundlePath)) {
6016
+ spinner.fail(chalk32.red(`Bundle '${name}' not found`));
6017
+ console.log(chalk32.gray("\nRun `vibetasks bundle list` to see installed bundles"));
6018
+ process.exit(1);
6019
+ }
6020
+ const bundle = await loadBundleFromDirectory(bundlePath, { type: "local", path: bundlePath });
6021
+ let scaffoldVariables = {};
6022
+ if (options.scaffold && bundle.scaffold) {
6023
+ const requiredVars = getRequiredScaffoldVariables(bundle.scaffold);
6024
+ if (requiredVars.length > 0) {
6025
+ spinner.stop();
6026
+ console.log(chalk32.blue("\nScaffold requires the following values:\n"));
6027
+ for (const variable of requiredVars) {
6028
+ const value = await promptFn(`${variable.label}${variable.description ? ` (${variable.description})` : ""}: `);
6029
+ scaffoldVariables[variable.name] = value;
6030
+ }
6031
+ spinner.start("Activating bundle...");
6032
+ }
6033
+ }
6034
+ const result = await activateBundle(name, {
6035
+ projectPath,
6036
+ applyScaffold: options.scaffold,
6037
+ scaffoldVariables,
6038
+ runPostScaffold: options.runCommands
6039
+ });
6040
+ if (!result.success) {
6041
+ spinner.fail(chalk32.red(result.error || "Activation failed"));
6042
+ if (result.warnings?.length) {
6043
+ result.warnings.forEach((w) => console.log(chalk32.yellow(` \u26A0 ${w}`)));
6044
+ }
6045
+ process.exit(1);
6046
+ }
6047
+ spinner.succeed(chalk32.green(`Activated ${name}`));
6048
+ if (result.files?.length) {
6049
+ console.log(chalk32.gray("\n Files:"));
6050
+ result.files.forEach((f) => {
6051
+ const icon = f.action === "created" ? "+" : f.action === "modified" ? "~" : "\u25CB";
6052
+ const color = f.action === "created" ? chalk32.green : f.action === "modified" ? chalk32.yellow : chalk32.gray;
6053
+ console.log(color(` ${icon} ${f.path}`));
6054
+ });
6055
+ }
6056
+ if (result.blueprintsLoaded) {
6057
+ console.log(chalk32.gray(`
6058
+ Blueprints loaded: ${result.blueprintsLoaded.blueprints.length}`));
6059
+ }
6060
+ if (result.commandResults?.length) {
6061
+ console.log(chalk32.gray("\n Post-scaffold commands:"));
6062
+ result.commandResults.forEach((r) => {
6063
+ const icon = r.success ? "\u2713" : "\u2717";
6064
+ const color = r.success ? chalk32.green : chalk32.red;
6065
+ console.log(color(` ${icon} ${r.command}`));
6066
+ });
6067
+ }
6068
+ if (result.warnings?.length) {
6069
+ console.log(chalk32.yellow("\n Warnings:"));
6070
+ result.warnings.forEach((w) => console.log(chalk32.yellow(` \u26A0 ${w}`)));
6071
+ }
6072
+ console.log(chalk32.blue("\n\u2713 Bundle is now active. VibBot will use its blueprints."));
6073
+ process.exit(0);
6074
+ } catch (error) {
6075
+ spinner.fail(chalk32.red("Activation failed"));
6076
+ console.error(chalk32.red(`
6077
+ Error: ${error.message}
6078
+ `));
6079
+ process.exit(1);
6080
+ }
6081
+ });
6082
+ var listCommand2 = new Command31("list").alias("ls").description("List installed bundles").option("-a, --active", "Show only active bundle for current project").action(async (options) => {
6083
+ try {
6084
+ if (options.active) {
6085
+ const activeBundle = await getActiveBundle(process.cwd());
6086
+ if (activeBundle) {
6087
+ console.log(chalk32.green(`
6088
+ Active bundle: ${activeBundle.manifest.name} v${activeBundle.manifest.version}`));
6089
+ console.log(chalk32.gray(` ${activeBundle.manifest.description}
6090
+ `));
6091
+ } else {
6092
+ console.log(chalk32.gray("\n No active bundle for current project\n"));
6093
+ }
6094
+ process.exit(0);
6095
+ }
6096
+ const { bundles, errors } = await loadInstalledBundles();
6097
+ if (bundles.length === 0) {
6098
+ console.log(chalk32.gray("\n No bundles installed\n"));
6099
+ console.log(chalk32.blue(" Install a bundle: vibetasks bundle install github:owner/repo\n"));
6100
+ process.exit(0);
6101
+ }
6102
+ console.log(chalk32.white(`
6103
+ Installed Bundles (${bundles.length}):
6104
+ `));
6105
+ for (const bundle of bundles) {
6106
+ console.log(chalk32.cyan(` \u2022 ${bundle.manifest.name}`), chalk32.gray(`v${bundle.manifest.version}`));
6107
+ console.log(chalk32.gray(` ${bundle.manifest.description}`));
6108
+ console.log(chalk32.gray(` Blueprints: ${bundle.blueprints.length} | Studio: ${bundle.studio ? "\u2713" : "\u2717"} | Scaffold: ${bundle.scaffold ? "\u2713" : "\u2717"}`));
6109
+ console.log();
6110
+ }
6111
+ if (errors.length > 0) {
6112
+ console.log(chalk32.yellow(" Errors loading some bundles:"));
6113
+ errors.forEach((e) => console.log(chalk32.yellow(` \u2022 ${e.message}`)));
6114
+ console.log();
6115
+ }
6116
+ process.exit(0);
6117
+ } catch (error) {
6118
+ console.error(chalk32.red(`
6119
+ Error: ${error.message}
6120
+ `));
6121
+ process.exit(1);
6122
+ }
6123
+ });
6124
+ var infoCommand = new Command31("info").description("Show detailed information about a bundle").argument("<name>", "Bundle name").action(async (name) => {
6125
+ try {
6126
+ const bundlePath = path5.join(getGlobalBundlesDir(), name);
6127
+ if (!fs3.existsSync(bundlePath)) {
6128
+ console.log(chalk32.red(`
6129
+ Bundle '${name}' not found
6130
+ `));
6131
+ process.exit(1);
6132
+ }
6133
+ const bundle = await loadBundleFromDirectory(bundlePath, { type: "local", path: bundlePath });
6134
+ console.log(chalk32.white(`
6135
+ ${bundle.manifest.displayName || bundle.manifest.name}`));
6136
+ console.log(chalk32.gray(` v${bundle.manifest.version}`));
6137
+ console.log();
6138
+ console.log(chalk32.gray(` ${bundle.manifest.description}`));
6139
+ console.log();
6140
+ if (bundle.manifest.author) {
6141
+ const author = typeof bundle.manifest.author === "string" ? bundle.manifest.author : bundle.manifest.author.name;
6142
+ console.log(chalk32.gray(` Author: ${author}`));
6143
+ }
6144
+ if (bundle.manifest.domain) {
6145
+ console.log(chalk32.gray(` Domain: ${bundle.manifest.domain}`));
6146
+ }
6147
+ if (bundle.manifest.tags?.length) {
6148
+ console.log(chalk32.gray(` Tags: ${bundle.manifest.tags.join(", ")}`));
6149
+ }
6150
+ if (bundle.manifest.homepage) {
6151
+ console.log(chalk32.gray(` Homepage: ${bundle.manifest.homepage}`));
6152
+ }
6153
+ if (bundle.manifest.repository) {
6154
+ console.log(chalk32.gray(` Repository: ${bundle.manifest.repository}`));
6155
+ }
6156
+ console.log();
6157
+ console.log(chalk32.cyan(" Components:"));
6158
+ console.log(chalk32.gray(` \u2022 Blueprints: ${bundle.blueprints.length}`));
6159
+ if (bundle.blueprints.length > 0) {
6160
+ bundle.blueprints.slice(0, 5).forEach((b) => {
6161
+ console.log(chalk32.gray(` - ${b.frontmatter.name}: ${b.frontmatter.description}`));
6162
+ });
6163
+ if (bundle.blueprints.length > 5) {
6164
+ console.log(chalk32.gray(` ... and ${bundle.blueprints.length - 5} more`));
6165
+ }
6166
+ }
6167
+ console.log(chalk32.gray(` \u2022 Studio config: ${bundle.studio ? "Yes" : "No"}`));
6168
+ console.log(chalk32.gray(` \u2022 Scaffold: ${bundle.scaffold ? "Yes" : "No"}`));
6169
+ if (bundle.scaffold) {
6170
+ const requiredVars = getRequiredScaffoldVariables(bundle.scaffold);
6171
+ if (requiredVars.length > 0) {
6172
+ console.log(chalk32.gray(` \u2022 Required variables: ${requiredVars.map((v) => v.name).join(", ")}`));
6173
+ }
6174
+ }
6175
+ console.log();
6176
+ console.log(chalk32.gray(` Path: ${bundlePath}`));
6177
+ console.log();
6178
+ process.exit(0);
6179
+ } catch (error) {
6180
+ console.error(chalk32.red(`
6181
+ Error: ${error.message}
6182
+ `));
6183
+ process.exit(1);
6184
+ }
6185
+ });
6186
+ var removeCommand = new Command31("remove").alias("rm").description("Remove an installed bundle").argument("<name>", "Bundle name").option("-f, --force", "Skip confirmation").action(async (name, options) => {
6187
+ try {
6188
+ if (!options.force) {
6189
+ const confirm = await confirmFn(`Remove bundle '${name}'?`);
6190
+ if (!confirm) {
6191
+ console.log(chalk32.gray("\n Cancelled\n"));
6192
+ process.exit(0);
6193
+ }
6194
+ }
6195
+ const spinner = ora19(`Removing bundle ${name}...`).start();
6196
+ const result = await removeBundle(name);
6197
+ if (!result.success) {
6198
+ spinner.fail(chalk32.red(result.error || "Remove failed"));
6199
+ process.exit(1);
6200
+ }
6201
+ spinner.succeed(chalk32.green(`Removed ${name}`));
6202
+ process.exit(0);
6203
+ } catch (error) {
6204
+ console.error(chalk32.red(`
6205
+ Error: ${error.message}
6206
+ `));
6207
+ process.exit(1);
6208
+ }
6209
+ });
6210
+ var updateCommand2 = new Command31("update").description("Update a bundle from its source").argument("<name>", "Bundle name").action(async (name) => {
6211
+ const spinner = ora19(`Updating bundle ${name}...`).start();
6212
+ try {
6213
+ const result = await updateBundle(name);
6214
+ if (!result.success) {
6215
+ spinner.fail(chalk32.red(result.error || "Update failed"));
6216
+ process.exit(1);
6217
+ }
6218
+ spinner.succeed(chalk32.green(`Updated ${result.bundle.manifest.name} to v${result.bundle.manifest.version}`));
6219
+ process.exit(0);
6220
+ } catch (error) {
6221
+ spinner.fail(chalk32.red("Update failed"));
6222
+ console.error(chalk32.red(`
6223
+ Error: ${error.message}
6224
+ `));
6225
+ process.exit(1);
6226
+ }
6227
+ });
6228
+ var deactivateCommand = new Command31("deactivate").description("Deactivate bundle from current project").argument("<name>", "Bundle name").action(async (name) => {
6229
+ const spinner = ora19(`Deactivating bundle ${name}...`).start();
6230
+ try {
6231
+ const result = await deactivateBundle(name, process.cwd());
6232
+ if (!result.success) {
6233
+ spinner.fail(chalk32.red(result.error || "Deactivation failed"));
6234
+ process.exit(1);
6235
+ }
6236
+ spinner.succeed(chalk32.green(`Deactivated ${name}`));
6237
+ process.exit(0);
6238
+ } catch (error) {
6239
+ spinner.fail(chalk32.red("Deactivation failed"));
6240
+ console.error(chalk32.red(`
6241
+ Error: ${error.message}
6242
+ `));
6243
+ process.exit(1);
6244
+ }
6245
+ });
6246
+ var bundleCommand = new Command31("bundle").description("Manage VibeTasks bundles (Studio + Scaffold + Blueprints)").addCommand(installCommand).addCommand(useCommand).addCommand(listCommand2).addCommand(infoCommand).addCommand(removeCommand).addCommand(updateCommand2).addCommand(deactivateCommand);
6247
+
6248
+ // src/commands/ready.ts
6249
+ import { Command as Command32 } from "commander";
6250
+ import chalk33 from "chalk";
6251
+ import Table5 from "cli-table3";
6252
+ import { AuthManager as AuthManager29, TaskOperations as TaskOperations25 } from "@vibetasks/core";
6253
+ var readyCommand = new Command32("ready").description("Show tasks ready to work on (no blockers, not deferred)").option("-l, --limit <number>", "Maximum number of tasks to show", "20").option("--status <status>", "Filter by status: todo, vibing (default: vibing)", "vibing").option("--short-ids", "Show short numeric IDs for easy reference").option("--json", "Output as JSON").action(async (options) => {
6254
+ try {
6255
+ const authManager = new AuthManager29();
6256
+ const taskOps = await TaskOperations25.fromAuthManager(authManager);
6257
+ const tasks = await taskOps.getReadyTasks({
6258
+ status: options.status,
6259
+ limit: parseInt(options.limit, 10)
6260
+ });
6261
+ if (options.json) {
6262
+ console.log(JSON.stringify(tasks, null, 2));
6263
+ process.exit(0);
6264
+ }
6265
+ if (tasks.length === 0) {
6266
+ console.log(chalk33.green("\n\u2728 No ready tasks - all caught up!\n"));
6267
+ console.log(chalk33.gray("You might have:"));
6268
+ console.log(chalk33.gray(" - Tasks blocked by dependencies"));
6269
+ console.log(chalk33.gray(" - Tasks deferred for later (defer_until)"));
6270
+ console.log(chalk33.gray(" - No open tasks at all\n"));
6271
+ console.log(chalk33.gray("Run: vibetasks list --status todo to see all pending tasks"));
6272
+ console.log(chalk33.gray("Run: vibetasks blocked to see what's blocking you\n"));
6273
+ process.exit(0);
6274
+ }
6275
+ console.log(chalk33.green(`
6276
+ \u2705 ${tasks.length} task(s) ready to work on:
6277
+ `));
6278
+ const shortIdManager = new ShortIdManager();
6279
+ if (options.shortIds) {
6280
+ await shortIdManager.saveMappings(tasks.map((t) => ({
6281
+ id: t.id,
6282
+ short_id: t.short_id
6283
+ })));
6284
+ }
6285
+ const table = new Table5({
6286
+ head: options.shortIds ? [chalk33.cyan("#"), chalk33.cyan("Pri"), chalk33.cyan("Title"), chalk33.cyan("Due")] : [chalk33.cyan("ID"), chalk33.cyan("Pri"), chalk33.cyan("Title"), chalk33.cyan("Due")],
6287
+ colWidths: options.shortIds ? [8, 8, 45, 12] : [12, 8, 45, 12],
6288
+ wordWrap: true
6289
+ });
6290
+ const priorityColors = {
6291
+ high: chalk33.red,
6292
+ medium: chalk33.yellow,
6293
+ low: chalk33.blue,
6294
+ none: chalk33.gray
6295
+ };
6296
+ const priorityEmojis = {
6297
+ high: "\u{1F534}",
6298
+ medium: "\u{1F7E1}",
6299
+ low: "\u{1F535}",
6300
+ none: "\u26AA"
6301
+ };
6302
+ tasks.forEach((task, index) => {
6303
+ const priorityColor = priorityColors[task.priority || "none"];
6304
+ const priorityEmoji = priorityEmojis[task.priority || "none"];
6305
+ const shortId = chalk33.yellow.bold(task.short_id || `${index + 1}`);
6306
+ const id = task.id.substring(0, 8);
6307
+ const priority = priorityColor(`${priorityEmoji}`);
6308
+ const title = task.title.length > 42 ? task.title.substring(0, 39) + "..." : task.title;
6309
+ const dueDate = task.due_date ? task.due_date.split("T")[0] : chalk33.gray("-");
6310
+ if (options.shortIds) {
6311
+ table.push([shortId, priority, title, dueDate]);
6312
+ } else {
6313
+ table.push([id, priority, title, dueDate]);
6314
+ }
6315
+ });
6316
+ console.log(table.toString());
6317
+ if (options.shortIds) {
6318
+ console.log(chalk33.gray(`
6319
+ Use: vibetasks vibing <short-id> to start working`));
6320
+ } else {
6321
+ console.log(chalk33.gray(`
6322
+ Use: vibetasks vibing <id> to start working`));
6323
+ }
6324
+ console.log();
6325
+ process.exit(0);
6326
+ } catch (error) {
6327
+ if (error.message.includes("Not authenticated")) {
6328
+ console.error(chalk33.yellow("\n\u26A0\uFE0F You need to login first"));
6329
+ console.error(chalk33.gray("Run: vibetasks login\n"));
6330
+ } else {
6331
+ console.error(chalk33.red(`
6332
+ Error: ${error.message}
6333
+ `));
6334
+ }
6335
+ process.exit(1);
6336
+ }
6337
+ });
6338
+
6339
+ // src/commands/stale.ts
6340
+ import { Command as Command33 } from "commander";
6341
+ import chalk34 from "chalk";
6342
+ import Table6 from "cli-table3";
6343
+ import { AuthManager as AuthManager30, TaskOperations as TaskOperations26 } from "@vibetasks/core";
6344
+ var staleCommand = new Command33("stale").description("Show stale tasks (not updated recently)").option("-d, --days <number>", "Tasks not updated in this many days", "7").option("-l, --limit <number>", "Maximum number of tasks to show", "50").option("--status <status>", "Filter by status: todo, vibing").option("--short-ids", "Show short numeric IDs for easy reference").option("--json", "Output as JSON").option("--fix", "Interactively close or update stale tasks").action(async (options) => {
6345
+ try {
6346
+ const authManager = new AuthManager30();
6347
+ const taskOps = await TaskOperations26.fromAuthManager(authManager);
6348
+ const days = parseInt(options.days, 10);
6349
+ const tasks = await taskOps.getStaleTasks({
6350
+ days,
6351
+ status: options.status,
6352
+ limit: parseInt(options.limit, 10)
6353
+ });
6354
+ if (options.json) {
6355
+ console.log(JSON.stringify(tasks, null, 2));
6356
+ process.exit(0);
6357
+ }
6358
+ if (tasks.length === 0) {
6359
+ console.log(chalk34.green(`
6360
+ \u2728 No stale tasks found (all updated within ${days} days)
6361
+ `));
6362
+ process.exit(0);
6363
+ }
6364
+ console.log(chalk34.yellow(`
6365
+ \u23F0 ${tasks.length} stale task(s) (not updated in ${days}+ days):
6366
+ `));
6367
+ const shortIdManager = new ShortIdManager();
6368
+ if (options.shortIds) {
6369
+ await shortIdManager.saveMappings(tasks.map((t) => ({
6370
+ id: t.id,
6371
+ short_id: t.short_id
6372
+ })));
6373
+ }
6374
+ const table = new Table6({
6375
+ head: [
6376
+ options.shortIds ? chalk34.cyan("#") : chalk34.cyan("ID"),
6377
+ chalk34.cyan("Status"),
6378
+ chalk34.cyan("Title"),
6379
+ chalk34.cyan("Days Stale"),
6380
+ chalk34.cyan("Session")
6381
+ ],
6382
+ colWidths: options.shortIds ? [8, 10, 35, 12, 12] : [12, 10, 35, 12, 12],
6383
+ wordWrap: true
6384
+ });
6385
+ const statusColors = {
6386
+ todo: chalk34.gray,
6387
+ vibing: chalk34.magenta,
6388
+ paused: chalk34.yellow
6389
+ };
6390
+ tasks.forEach((task, index) => {
6391
+ const statusColor = statusColors[task.status] || chalk34.white;
6392
+ const daysStale = task.days_stale || Math.floor((Date.now() - new Date(task.updated_at).getTime()) / (1e3 * 60 * 60 * 24));
6393
+ const shortId = chalk34.yellow.bold(task.short_id || `${index + 1}`);
6394
+ const id = task.id.substring(0, 8);
6395
+ const status = statusColor(task.status);
6396
+ const title = task.title.length > 32 ? task.title.substring(0, 29) + "..." : task.title;
6397
+ const daysStaleStr = daysStale > 30 ? chalk34.red(`${daysStale}d`) : chalk34.yellow(`${daysStale}d`);
6398
+ const sessionId = task.session_id ? task.session_id.substring(0, 8) : chalk34.gray("none");
6399
+ if (options.shortIds) {
6400
+ table.push([shortId, status, title, daysStaleStr, sessionId]);
6401
+ } else {
6402
+ table.push([id, status, title, daysStaleStr, sessionId]);
6403
+ }
6404
+ });
6405
+ console.log(table.toString());
6406
+ console.log(chalk34.gray("\nThese tasks may be:"));
6407
+ console.log(chalk34.gray(" - Abandoned work from a previous session"));
6408
+ console.log(chalk34.gray(" - Completed but not marked done"));
6409
+ console.log(chalk34.gray(" - Blocked and forgotten"));
6410
+ console.log();
6411
+ if (!options.fix) {
6412
+ console.log(chalk34.gray("Run: vibetasks stale --fix to interactively clean up"));
6413
+ console.log(chalk34.gray("Run: vibetasks done <id> to mark a task complete"));
6414
+ console.log(chalk34.gray("Run: vibetasks archive <id> to archive a task"));
6415
+ } else {
6416
+ console.log(chalk34.cyan("\n--- Interactive Cleanup Mode ---\n"));
6417
+ const readline2 = await import("readline");
6418
+ const rl = readline2.createInterface({
6419
+ input: process.stdin,
6420
+ output: process.stdout
6421
+ });
6422
+ for (const task of tasks) {
6423
+ const answer = await new Promise((resolve) => {
6424
+ rl.question(
6425
+ chalk34.white(`
6426
+ "${task.title}" (${task.status}, ${task.days_stale || "?"}d stale)
6427
+ `) + chalk34.gray(" [d]one, [a]rchive, [s]kip, [q]uit: "),
6428
+ resolve
6429
+ );
6430
+ });
6431
+ const choice = answer.toLowerCase().trim();
6432
+ if (choice === "q" || choice === "quit") {
6433
+ console.log(chalk34.gray("\nExiting cleanup mode."));
6434
+ break;
6435
+ } else if (choice === "d" || choice === "done") {
6436
+ await taskOps.completeTask(task.id);
6437
+ console.log(chalk34.green(` \u2713 Marked done: ${task.title}`));
6438
+ } else if (choice === "a" || choice === "archive") {
6439
+ await taskOps.archiveTask(task.id);
6440
+ console.log(chalk34.blue(` \u{1F4E6} Archived: ${task.title}`));
6441
+ } else {
6442
+ console.log(chalk34.gray(" Skipped"));
6443
+ }
6444
+ }
6445
+ rl.close();
6446
+ console.log(chalk34.green("\n\u2705 Cleanup complete!\n"));
6447
+ }
6448
+ console.log();
6449
+ process.exit(0);
6450
+ } catch (error) {
6451
+ if (error.message.includes("Not authenticated")) {
6452
+ console.error(chalk34.yellow("\n\u26A0\uFE0F You need to login first"));
6453
+ console.error(chalk34.gray("Run: vibetasks login\n"));
6454
+ } else {
6455
+ console.error(chalk34.red(`
6456
+ Error: ${error.message}
6457
+ `));
6458
+ }
6459
+ process.exit(1);
6460
+ }
6461
+ });
6462
+
6463
+ // src/commands/orphans.ts
6464
+ import { Command as Command34 } from "commander";
6465
+ import chalk35 from "chalk";
6466
+ import Table7 from "cli-table3";
6467
+ import { AuthManager as AuthManager31, TaskOperations as TaskOperations27 } from "@vibetasks/core";
6468
+ var orphansCommand = new Command34("orphans").description("Find orphaned tasks (incomplete tasks from old AI sessions)").option("-l, --limit <number>", "Maximum number of tasks to show", "50").option("--short-ids", "Show short numeric IDs for easy reference").option("--json", "Output as JSON").option("--fix", "Interactively close or reassign orphaned tasks").option("--adopt", "Adopt orphaned tasks into current session").action(async (options) => {
6469
+ try {
6470
+ const authManager = new AuthManager31();
6471
+ const taskOps = await TaskOperations27.fromAuthManager(authManager);
6472
+ const sessionManager2 = getSessionManager();
6473
+ const currentSession = await sessionManager2.getCurrentSession();
6474
+ const currentSessionId = currentSession?.id;
6475
+ const tasks = await taskOps.getOrphanedTasks({
6476
+ currentSessionId,
6477
+ limit: parseInt(options.limit, 10)
6478
+ });
6479
+ if (options.json) {
6480
+ console.log(JSON.stringify(tasks, null, 2));
6481
+ process.exit(0);
6482
+ }
6483
+ if (tasks.length === 0) {
6484
+ console.log(chalk35.green("\n\u2728 No orphaned tasks found!\n"));
6485
+ console.log(chalk35.gray("All in-progress tasks are from the current session."));
6486
+ process.exit(0);
6487
+ }
6488
+ console.log(chalk35.yellow(`
6489
+ \u{1F50D} Found ${tasks.length} orphaned task(s) from previous sessions:
6490
+ `));
6491
+ const shortIdManager = new ShortIdManager();
6492
+ if (options.shortIds) {
6493
+ await shortIdManager.saveMappings(tasks.map((t) => ({
6494
+ id: t.id,
6495
+ short_id: t.short_id
6496
+ })));
6497
+ }
6498
+ const table = new Table7({
6499
+ head: [
6500
+ options.shortIds ? chalk35.cyan("#") : chalk35.cyan("ID"),
6501
+ chalk35.cyan("Status"),
6502
+ chalk35.cyan("Title"),
6503
+ chalk35.cyan("Progress"),
6504
+ chalk35.cyan("Session")
6505
+ ],
6506
+ colWidths: options.shortIds ? [8, 10, 35, 12, 12] : [12, 10, 35, 12, 12],
6507
+ wordWrap: true
6508
+ });
6509
+ const statusColors = {
6510
+ todo: chalk35.gray,
6511
+ vibing: chalk35.magenta,
6512
+ paused: chalk35.yellow
6513
+ };
6514
+ tasks.forEach((task, index) => {
6515
+ const statusColor = statusColors[task.status] || chalk35.white;
6516
+ const shortId = chalk35.yellow.bold(task.short_id || `${index + 1}`);
6517
+ const id = task.id.substring(0, 8);
6518
+ const status = statusColor(task.status);
6519
+ const title = task.title.length > 32 ? task.title.substring(0, 29) + "..." : task.title;
6520
+ const subtasksTotal = task.subtasks_total || 0;
6521
+ const subtasksDone = task.subtasks_done || 0;
6522
+ const progress = subtasksTotal > 0 ? subtasksDone === subtasksTotal ? chalk35.green(`${subtasksDone}/${subtasksTotal}`) : chalk35.yellow(`${subtasksDone}/${subtasksTotal}`) : chalk35.gray("0/0");
6523
+ const sessionId = task.session_id ? task.session_id === currentSessionId ? chalk35.green("current") : task.session_id.substring(0, 8) : chalk35.gray("none");
6524
+ if (options.shortIds) {
6525
+ table.push([shortId, status, title, progress, sessionId]);
6526
+ } else {
6527
+ table.push([id, status, title, progress, sessionId]);
6528
+ }
6529
+ });
6530
+ console.log(table.toString());
6531
+ const tasksWithContext = tasks.filter((t) => t.context_notes);
6532
+ if (tasksWithContext.length > 0) {
6533
+ console.log(chalk35.cyan("\n\u{1F4DD} Tasks with handoff context:\n"));
6534
+ tasksWithContext.slice(0, 3).forEach((task) => {
6535
+ console.log(chalk35.white(` ${task.title}:`));
6536
+ const preview = task.context_notes.length > 100 ? task.context_notes.substring(0, 100) + "..." : task.context_notes;
6537
+ console.log(chalk35.gray(` ${preview}
6538
+ `));
6539
+ });
6540
+ }
6541
+ console.log(chalk35.gray("\nThese tasks were started by a previous AI session but not completed."));
6542
+ console.log(chalk35.gray("They may represent:"));
6543
+ console.log(chalk35.gray(" - Work interrupted by session timeout/crash"));
6544
+ console.log(chalk35.gray(" - Tasks the AI forgot to complete"));
6545
+ console.log(chalk35.gray(" - Partially done work that needs finishing"));
6546
+ console.log();
6547
+ if (options.adopt && currentSessionId) {
6548
+ console.log(chalk35.cyan("\u{1F504} Adopting orphaned tasks into current session...\n"));
6549
+ for (const task of tasks) {
6550
+ await taskOps.updateTask(task.id, { session_id: currentSessionId });
6551
+ console.log(chalk35.green(` \u2713 Adopted: ${task.title}`));
6552
+ }
6553
+ console.log(chalk35.green(`
6554
+ \u2705 Adopted ${tasks.length} task(s) into current session.
6555
+ `));
6556
+ } else if (options.fix) {
6557
+ console.log(chalk35.cyan("\n--- Interactive Cleanup Mode ---\n"));
6558
+ const readline2 = await import("readline");
6559
+ const rl = readline2.createInterface({
6560
+ input: process.stdin,
6561
+ output: process.stdout
6562
+ });
6563
+ for (const task of tasks) {
6564
+ const progressStr = `${task.subtasks_done || 0}/${task.subtasks_total || 0}`;
6565
+ const answer = await new Promise((resolve) => {
6566
+ rl.question(
6567
+ chalk35.white(`
6568
+ "${task.title}" (${task.status}, ${progressStr} subtasks)
6569
+ `) + (task.context_notes ? chalk35.gray(` Context: ${task.context_notes.substring(0, 80)}...
6570
+ `) : "") + chalk35.gray(" [a]dopt to current session, [d]one, [r]emove, [s]kip, [q]uit: "),
6571
+ resolve
6572
+ );
6573
+ });
6574
+ const choice = answer.toLowerCase().trim();
6575
+ if (choice === "q" || choice === "quit") {
6576
+ console.log(chalk35.gray("\nExiting cleanup mode."));
6577
+ break;
6578
+ } else if (choice === "a" || choice === "adopt") {
6579
+ if (currentSessionId) {
6580
+ await taskOps.updateTask(task.id, { session_id: currentSessionId });
6581
+ console.log(chalk35.green(` \u2713 Adopted: ${task.title}`));
6582
+ } else {
6583
+ console.log(chalk35.yellow(" No current session. Start one with: vibetasks session start"));
6584
+ }
6585
+ } else if (choice === "d" || choice === "done") {
6586
+ await taskOps.completeTask(task.id);
6587
+ console.log(chalk35.green(` \u2713 Marked done: ${task.title}`));
6588
+ } else if (choice === "r" || choice === "remove") {
6589
+ await taskOps.archiveTask(task.id);
6590
+ console.log(chalk35.blue(` \u{1F4E6} Archived: ${task.title}`));
6591
+ } else {
6592
+ console.log(chalk35.gray(" Skipped"));
6593
+ }
6594
+ }
6595
+ rl.close();
6596
+ console.log(chalk35.green("\n\u2705 Cleanup complete!\n"));
6597
+ } else {
6598
+ console.log(chalk35.gray("Run: vibetasks orphans --fix to interactively clean up"));
6599
+ console.log(chalk35.gray("Run: vibetasks orphans --adopt to adopt all into current session"));
6600
+ console.log(chalk35.gray("Run: vibetasks show <id> to see full task details"));
6601
+ console.log();
6602
+ }
6603
+ process.exit(0);
6604
+ } catch (error) {
6605
+ if (error.message.includes("Not authenticated")) {
6606
+ console.error(chalk35.yellow("\n\u26A0\uFE0F You need to login first"));
6607
+ console.error(chalk35.gray("Run: vibetasks login\n"));
6608
+ } else {
6609
+ console.error(chalk35.red(`
6610
+ Error: ${error.message}
6611
+ `));
6612
+ }
6613
+ process.exit(1);
6614
+ }
6615
+ });
6616
+
6617
+ // src/commands/blocked.ts
6618
+ import { Command as Command35 } from "commander";
6619
+ import chalk36 from "chalk";
6620
+ import Table8 from "cli-table3";
6621
+ import { AuthManager as AuthManager32, TaskOperations as TaskOperations28 } from "@vibetasks/core";
6622
+ var blockedCommand = new Command35("blocked").description("Show tasks blocked by dependencies").option("-l, --limit <number>", "Maximum number of tasks to show", "50").option("--short-ids", "Show short numeric IDs for easy reference").option("--json", "Output as JSON").action(async (options) => {
6623
+ try {
6624
+ const authManager = new AuthManager32();
6625
+ const taskOps = await TaskOperations28.fromAuthManager(authManager);
6626
+ const tasks = await taskOps.getBlockedTasks({
6627
+ limit: parseInt(options.limit, 10)
6628
+ });
6629
+ if (options.json) {
6630
+ console.log(JSON.stringify(tasks, null, 2));
6631
+ process.exit(0);
6632
+ }
6633
+ if (tasks.length === 0) {
6634
+ console.log(chalk36.green("\n\u2728 No blocked tasks - nothing is waiting!\n"));
6635
+ process.exit(0);
6636
+ }
6637
+ console.log(chalk36.yellow(`
6638
+ \u{1F6A7} ${tasks.length} blocked task(s):
6639
+ `));
6640
+ const shortIdManager = new ShortIdManager();
6641
+ if (options.shortIds) {
6642
+ await shortIdManager.saveMappings(tasks.map((t) => ({
6643
+ id: t.id,
6644
+ short_id: t.short_id
6645
+ })));
6646
+ }
6647
+ const table = new Table8({
6648
+ head: [
6649
+ options.shortIds ? chalk36.cyan("#") : chalk36.cyan("ID"),
6650
+ chalk36.cyan("Pri"),
6651
+ chalk36.cyan("Title"),
6652
+ chalk36.cyan("Blocked By")
6653
+ ],
6654
+ colWidths: options.shortIds ? [8, 6, 35, 30] : [12, 6, 35, 30],
6655
+ wordWrap: true
6656
+ });
6657
+ const priorityColors = {
6658
+ high: chalk36.red,
6659
+ medium: chalk36.yellow,
6660
+ low: chalk36.blue,
6661
+ none: chalk36.gray
6662
+ };
6663
+ tasks.forEach((task, index) => {
6664
+ const priorityKey = task.priority || "none";
6665
+ const priorityColor = priorityColors[priorityKey];
6666
+ const shortId = chalk36.yellow.bold(task.short_id || `${index + 1}`);
6667
+ const id = task.id.substring(0, 8);
6668
+ const priority = priorityColor(task.priority?.[0]?.toUpperCase() || "-");
6669
+ const title = task.title.length > 32 ? task.title.substring(0, 29) + "..." : task.title;
6670
+ const blockerCount = task.blocked_by_count || 0;
6671
+ const blockerTitles = (task.blocker_titles || []).slice(0, 2);
6672
+ let blockedBy = "";
6673
+ if (blockerCount === 0) {
6674
+ blockedBy = chalk36.gray("deferred");
6675
+ } else if (blockerTitles.length > 0) {
6676
+ blockedBy = blockerTitles.map(
6677
+ (t) => chalk36.red(t.length > 12 ? t.substring(0, 9) + "..." : t)
6678
+ ).join(", ");
6679
+ if (blockerCount > blockerTitles.length) {
6680
+ blockedBy += chalk36.gray(` +${blockerCount - blockerTitles.length} more`);
6681
+ }
6682
+ } else {
6683
+ blockedBy = chalk36.red(`${blockerCount} task(s)`);
6684
+ }
6685
+ if (options.shortIds) {
6686
+ table.push([shortId, priority, title, blockedBy]);
6687
+ } else {
6688
+ table.push([id, priority, title, blockedBy]);
6689
+ }
6690
+ });
6691
+ console.log(table.toString());
6692
+ console.log(chalk36.gray("\nTo unblock tasks:"));
6693
+ console.log(chalk36.gray(" 1. Complete the blocking tasks first"));
6694
+ console.log(chalk36.gray(" 2. Or remove the dependency with: vibetasks dep remove <task-id> <blocker-id>"));
6695
+ console.log();
6696
+ console.log(chalk36.gray("Run: vibetasks ready to see tasks you can work on now"));
6697
+ console.log();
6698
+ process.exit(0);
6699
+ } catch (error) {
6700
+ if (error.message.includes("Not authenticated")) {
6701
+ console.error(chalk36.yellow("\n\u26A0\uFE0F You need to login first"));
6702
+ console.error(chalk36.gray("Run: vibetasks login\n"));
6703
+ } else {
6704
+ console.error(chalk36.red(`
6705
+ Error: ${error.message}
6706
+ `));
6707
+ }
6708
+ process.exit(1);
6709
+ }
6710
+ });
6711
+
6712
+ // src/commands/compact.ts
6713
+ import { Command as Command36 } from "commander";
6714
+ import chalk37 from "chalk";
6715
+ import ora20 from "ora";
6716
+ import { AuthManager as AuthManager33, TaskOperations as TaskOperations29, createCompactionService } from "@vibetasks/core";
6717
+ var compactCommand = new Command36("compact").description("Compact old completed tasks using AI summarization").option("-d, --days <number>", "Minimum days since completion (default: 7)", "7").option("-l, --limit <number>", "Maximum tasks to compact in one run", "10").option("--dry-run", "Preview what would be compacted without making changes").option("--stats", "Show compaction statistics only").option("--json", "Output as JSON").action(async (options) => {
6718
+ try {
6719
+ const authManager = new AuthManager33();
6720
+ const taskOps = await TaskOperations29.fromAuthManager(authManager);
6721
+ if (options.stats) {
6722
+ const stats = await taskOps.getCompactionStats();
6723
+ if (options.json) {
6724
+ console.log(JSON.stringify(stats, null, 2));
6725
+ process.exit(0);
6726
+ }
6727
+ console.log(chalk37.cyan("\n\u{1F4CA} Compaction Statistics\n"));
6728
+ console.log(` Total completed tasks: ${chalk37.white(stats.totalCompleted)}`);
6729
+ console.log(` Already compacted: ${chalk37.green(stats.compacted)}`);
6730
+ console.log(` Eligible for compact: ${chalk37.yellow(stats.eligible)}`);
6731
+ console.log();
6732
+ if (stats.eligible > 0) {
6733
+ console.log(chalk37.gray(`Run: vibetasks compact to summarize ${stats.eligible} old task(s)`));
6734
+ } else {
6735
+ console.log(chalk37.green("\u2728 All eligible tasks are already compacted!"));
6736
+ }
6737
+ console.log();
6738
+ process.exit(0);
6739
+ }
6740
+ const minDays = parseInt(options.days, 10);
6741
+ const limit = parseInt(options.limit, 10);
6742
+ const tasks = await taskOps.getCompactableTasks({
6743
+ minDaysOld: minDays,
6744
+ limit
6745
+ });
6746
+ if (tasks.length === 0) {
6747
+ console.log(chalk37.green("\n\u2728 No tasks eligible for compaction\n"));
6748
+ console.log(chalk37.gray(`Tasks must be completed more than ${minDays} days ago to be compacted.`));
6749
+ console.log(chalk37.gray("Run: vibetasks compact --stats to see compaction statistics"));
6750
+ console.log();
6751
+ process.exit(0);
6752
+ }
6753
+ if (options.dryRun) {
6754
+ console.log(chalk37.yellow(`
6755
+ \u{1F50D} Dry run: ${tasks.length} task(s) would be compacted:
6756
+ `));
6757
+ tasks.forEach((task, i) => {
6758
+ const completedDate = task.completed_at ? new Date(task.completed_at).toLocaleDateString() : "unknown";
6759
+ console.log(` ${i + 1}. ${task.title}`);
6760
+ console.log(chalk37.gray(` Completed: ${completedDate}`));
6761
+ });
6762
+ console.log();
6763
+ console.log(chalk37.gray("Remove --dry-run to actually compact these tasks"));
6764
+ console.log();
6765
+ process.exit(0);
6766
+ }
6767
+ if (!process.env.ANTHROPIC_API_KEY) {
6768
+ console.error(chalk37.red("\n\u274C ANTHROPIC_API_KEY environment variable required for compaction"));
6769
+ console.error(chalk37.gray("Set it with: export ANTHROPIC_API_KEY=your-key"));
6770
+ process.exit(1);
6771
+ }
6772
+ console.log(chalk37.cyan(`
6773
+ \u{1F5DC}\uFE0F Compacting ${tasks.length} old task(s)...
6774
+ `));
6775
+ const compactionService = createCompactionService();
6776
+ const results = [];
6777
+ let successCount = 0;
6778
+ let failCount = 0;
6779
+ let totalSaved = 0;
6780
+ for (const task of tasks) {
6781
+ const spinner = ora20(`Compacting: ${task.title.substring(0, 40)}...`).start();
6782
+ try {
6783
+ const result = await compactionService.compactTask(task);
6784
+ if (result.success) {
6785
+ await taskOps.applyCompaction(task.id, result.summary);
6786
+ const saved = result.originalSize - result.compactedSize;
6787
+ totalSaved += saved;
6788
+ successCount++;
6789
+ spinner.succeed(
6790
+ `${task.title.substring(0, 30)}... ` + chalk37.green(`(${result.originalSize} \u2192 ${result.compactedSize} chars, saved ${saved})`)
6791
+ );
6792
+ results.push({
6793
+ taskId: task.id,
6794
+ title: task.title,
6795
+ ...result
6796
+ });
6797
+ } else {
6798
+ failCount++;
6799
+ spinner.fail(`${task.title.substring(0, 30)}... ${chalk37.red(result.error)}`);
6800
+ results.push({
6801
+ taskId: task.id,
6802
+ title: task.title,
6803
+ ...result
6804
+ });
6805
+ }
6806
+ } catch (error) {
6807
+ failCount++;
6808
+ spinner.fail(`${task.title.substring(0, 30)}... ${chalk37.red(error.message)}`);
6809
+ results.push({
6810
+ taskId: task.id,
6811
+ title: task.title,
6812
+ success: false,
6813
+ error: error.message
6814
+ });
6815
+ }
6816
+ }
6817
+ if (options.json) {
6818
+ console.log(JSON.stringify(results, null, 2));
6819
+ process.exit(0);
6820
+ }
6821
+ console.log();
6822
+ console.log(chalk37.cyan("\u{1F4CA} Summary:"));
6823
+ console.log(` Compacted: ${chalk37.green(successCount)} task(s)`);
6824
+ if (failCount > 0) {
6825
+ console.log(` Failed: ${chalk37.red(failCount)} task(s)`);
6826
+ }
6827
+ console.log(` Saved: ${chalk37.yellow(totalSaved)} characters`);
6828
+ console.log();
6829
+ if (successCount > 0) {
6830
+ console.log(chalk37.green("\u2705 Context compaction complete!"));
6831
+ console.log(chalk37.gray("Compacted summaries are stored in context_summary field."));
6832
+ }
6833
+ console.log();
6834
+ process.exit(failCount > 0 ? 1 : 0);
6835
+ } catch (error) {
6836
+ if (error.message.includes("Not authenticated")) {
6837
+ console.error(chalk37.yellow("\n\u26A0\uFE0F You need to login first"));
6838
+ console.error(chalk37.gray("Run: vibetasks login\n"));
6839
+ } else {
6840
+ console.error(chalk37.red(`
6841
+ Error: ${error.message}
6842
+ `));
6843
+ }
6844
+ process.exit(1);
6845
+ }
6846
+ });
6847
+
5329
6848
  // bin/vibetasks.ts
5330
6849
  var require2 = createRequire(import.meta.url);
5331
6850
  var pkg = require2("../../package.json");
5332
- var program = new Command30();
6851
+ var program = new Command37();
5333
6852
  program.name("vibetasks").description("VibeTasks CLI - Fast task management for vibers").version(pkg.version);
5334
6853
  program.addCommand(setupCommand);
5335
6854
  program.addCommand(loginCommand);
@@ -5361,4 +6880,11 @@ program.addCommand(pauseCommand);
5361
6880
  program.addCommand(resumeCommand);
5362
6881
  program.addCommand(revertCommand);
5363
6882
  program.addCommand(blameCommand);
6883
+ program.addCommand(researchCommand);
6884
+ program.addCommand(bundleCommand);
6885
+ program.addCommand(readyCommand);
6886
+ program.addCommand(staleCommand);
6887
+ program.addCommand(orphansCommand);
6888
+ program.addCommand(blockedCommand);
6889
+ program.addCommand(compactCommand);
5364
6890
  program.parse();