@vibetasks/cli 0.6.10 → 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 +3299 -272
  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 Command21 } from "commander";
14
+ import { Command as Command37 } from "commander";
15
15
  import { createRequire } from "module";
16
16
 
17
17
  // src/commands/login.ts
@@ -368,7 +368,7 @@ var loginCommand = new Command("login").description("Authenticate with VibeTasks
368
368
  import { Command as Command2 } from "commander";
369
369
  import ora2 from "ora";
370
370
  import chalk3 from "chalk";
371
- import { AuthManager as AuthManager2, TaskOperations } from "@vibetasks/core";
371
+ import { AuthManager as AuthManager3, TaskOperations } from "@vibetasks/core";
372
372
 
373
373
  // src/utils/date-parser.ts
374
374
  import { addDays, addWeeks, addMonths, format, parse, isValid } from "date-fns";
@@ -415,7 +415,8 @@ 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
+ import { AuthManager as AuthManager2 } from "@vibetasks/core";
419
420
  var ADJECTIVES = [
420
421
  "swift",
421
422
  "calm",
@@ -455,16 +456,111 @@ var NOUNS = [
455
456
  var SessionManager = class {
456
457
  storePath;
457
458
  store = null;
459
+ authManager;
458
460
  constructor() {
459
461
  this.storePath = join(homedir(), ".vibetasks", "sessions.json");
462
+ this.authManager = new AuthManager2();
463
+ }
464
+ /**
465
+ * Get the API base URL for syncing sessions
466
+ */
467
+ getApiBaseUrl() {
468
+ if (process.env.VIBETASKS_AUTH_URL) {
469
+ return process.env.VIBETASKS_AUTH_URL;
470
+ }
471
+ if (process.env.TASKFLOW_WEB_PORT) {
472
+ return `http://localhost:${process.env.TASKFLOW_WEB_PORT}`;
473
+ }
474
+ return "https://vibetasks.dev";
475
+ }
476
+ /**
477
+ * Sync a single action to the server in real-time
478
+ * Called after each action is logged locally
479
+ */
480
+ async syncActionToServer(sessionId, action) {
481
+ try {
482
+ const token = await this.authManager.getValidAccessToken();
483
+ if (!token) {
484
+ return false;
485
+ }
486
+ const apiUrl = `${this.getApiBaseUrl()}/api/sessions/${sessionId}`;
487
+ const payload = {
488
+ action_type: action.type,
489
+ description: action.description,
490
+ timestamp: action.timestamp,
491
+ task_id: action.taskId
492
+ };
493
+ const response = await fetch(apiUrl, {
494
+ method: "POST",
495
+ headers: {
496
+ "Content-Type": "application/json",
497
+ "Authorization": `Bearer ${token}`
498
+ },
499
+ body: JSON.stringify(payload)
500
+ });
501
+ return response.ok;
502
+ } catch {
503
+ return false;
504
+ }
505
+ }
506
+ /**
507
+ * Sync a session to the server database
508
+ * Called automatically when a session ends
509
+ */
510
+ async syncToServer(session) {
511
+ try {
512
+ const token = await this.authManager.getValidAccessToken();
513
+ if (!token) {
514
+ return false;
515
+ }
516
+ const apiUrl = `${this.getApiBaseUrl()}/api/sessions`;
517
+ const payload = {
518
+ session_id: session.id,
519
+ status: session.endedAt ? "ended" : "active",
520
+ started_at: session.startedAt,
521
+ ended_at: session.endedAt,
522
+ project_tag: session.project,
523
+ summary: session.summary,
524
+ handoff_notes: session.summary,
525
+ // Use summary as handoff notes
526
+ actions: session.actions.map((action) => ({
527
+ action_type: action.type,
528
+ description: action.description,
529
+ timestamp: action.timestamp,
530
+ task_id: action.taskId
531
+ }))
532
+ };
533
+ const response = await fetch(apiUrl, {
534
+ method: "POST",
535
+ headers: {
536
+ "Content-Type": "application/json",
537
+ "Authorization": `Bearer ${token}`
538
+ },
539
+ body: JSON.stringify(payload)
540
+ });
541
+ if (!response.ok) {
542
+ const error = await response.text();
543
+ if (process.stderr.isTTY) {
544
+ console.error(`Session sync failed: ${error}`);
545
+ }
546
+ return false;
547
+ }
548
+ return true;
549
+ } catch (error) {
550
+ if (process.stderr.isTTY) {
551
+ console.error(`Session sync error: ${error.message}`);
552
+ }
553
+ return false;
554
+ }
460
555
  }
461
556
  /**
462
557
  * Generate a memorable session ID like "swift-fox-42"
558
+ * Uses cryptographically secure random generation
463
559
  */
464
560
  generateSessionId() {
465
- const adj = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
466
- const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
467
- 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);
468
564
  return `${adj}-${noun}-${num}`;
469
565
  }
470
566
  /**
@@ -510,6 +606,8 @@ var SessionManager = class {
510
606
  store.sessions.push(session);
511
607
  store.currentSession = session.id;
512
608
  await this.save();
609
+ await this.syncToServer(session).catch(() => {
610
+ });
513
611
  return session;
514
612
  }
515
613
  /**
@@ -532,23 +630,22 @@ var SessionManager = class {
532
630
  }
533
631
  /**
534
632
  * Log an action in the current session
633
+ * Syncs to server in real-time (non-blocking)
535
634
  */
536
635
  async logAction(action) {
537
636
  const store = await this.load();
538
- const session = store.sessions.find((s) => s.id === store.currentSession);
637
+ let session = store.sessions.find((s) => s.id === store.currentSession);
638
+ const actionWithTimestamp = {
639
+ ...action,
640
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
641
+ };
539
642
  if (!session) {
540
- const newSession = await this.startSession();
541
- newSession.actions.push({
542
- ...action,
543
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
544
- });
545
- } else {
546
- session.actions.push({
547
- ...action,
548
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
549
- });
643
+ session = await this.startSession();
550
644
  }
645
+ session.actions.push(actionWithTimestamp);
551
646
  await this.save();
647
+ this.syncActionToServer(session.id, actionWithTimestamp).catch(() => {
648
+ });
552
649
  }
553
650
  /**
554
651
  * End the current session
@@ -561,6 +658,8 @@ var SessionManager = class {
561
658
  session.summary = summary;
562
659
  store.currentSession = void 0;
563
660
  await this.save();
661
+ this.syncToServer(session).catch(() => {
662
+ });
564
663
  }
565
664
  return session || null;
566
665
  }
@@ -622,6 +721,58 @@ var SessionManager = class {
622
721
  }
623
722
  return lines.filter(Boolean).join("\n");
624
723
  }
724
+ /**
725
+ * Generate a comprehensive handoff for the next AI
726
+ * Includes: vibing tasks with context, git status, files changed, next steps
727
+ */
728
+ async generateHandoff() {
729
+ const { execSync: execSync3 } = await import("child_process");
730
+ const lines = [];
731
+ lines.push("# AI Handoff");
732
+ lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}`);
733
+ lines.push("");
734
+ const currentSession = await this.getCurrentSession();
735
+ if (currentSession) {
736
+ lines.push(`## Current Session: ${currentSession.id}`);
737
+ lines.push(`Project: ${currentSession.project || "unknown"}`);
738
+ lines.push(`Started: ${new Date(currentSession.startedAt).toLocaleString()}`);
739
+ lines.push(`Actions: ${currentSession.actions.length}`);
740
+ lines.push("");
741
+ }
742
+ try {
743
+ const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
744
+ if (gitStatus) {
745
+ const changedFiles = gitStatus.split("\n").slice(0, 20);
746
+ lines.push("## Uncommitted Changes");
747
+ changedFiles.forEach((f) => lines.push(`- ${f}`));
748
+ if (gitStatus.split("\n").length > 20) {
749
+ lines.push(`- ... and ${gitStatus.split("\n").length - 20} more files`);
750
+ }
751
+ lines.push("");
752
+ }
753
+ } catch {
754
+ }
755
+ try {
756
+ const recentCommits = execSync3("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
757
+ if (recentCommits) {
758
+ lines.push("## Recent Commits");
759
+ recentCommits.split("\n").forEach((c) => lines.push(`- ${c}`));
760
+ lines.push("");
761
+ }
762
+ } catch {
763
+ }
764
+ lines.push("---");
765
+ lines.push("## For Next AI");
766
+ lines.push("");
767
+ lines.push("1. Check vibing tasks: `vibetasks list --status vibing --short-ids`");
768
+ lines.push('2. Read context_notes on vibing tasks for "where I left off"');
769
+ lines.push("3. Check git status for uncommitted work");
770
+ lines.push("");
771
+ if (currentSession) {
772
+ lines.push(`To continue this session's work: "Continue from session ${currentSession.id}"`);
773
+ }
774
+ return lines.join("\n");
775
+ }
625
776
  };
626
777
  var sessionManager = null;
627
778
  function getSessionManager() {
@@ -631,11 +782,114 @@ function getSessionManager() {
631
782
  return sessionManager;
632
783
  }
633
784
 
785
+ // src/utils/short-ids.ts
786
+ import { writeFile as writeFile2, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
787
+ import { join as join2 } from "path";
788
+ import { homedir as homedir2 } from "os";
789
+ var ShortIdManager = class {
790
+ storePath;
791
+ maxIds = 9999;
792
+ // Support up to 9999 short IDs (database assigns them)
793
+ constructor() {
794
+ this.storePath = join2(homedir2(), ".vibetasks", "short-ids.json");
795
+ }
796
+ /**
797
+ * Save mappings from a list of tasks with their short_ids
798
+ * Uses the stable short_id from the database
799
+ */
800
+ async saveMappings(tasks) {
801
+ const mappings = tasks.filter((t) => t.short_id).slice(0, this.maxIds).map((task, index) => ({
802
+ shortId: task.short_id,
803
+ // Use actual short_id from database
804
+ fullId: task.id,
805
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
806
+ }));
807
+ const store = {
808
+ mappings,
809
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
810
+ };
811
+ const dir = join2(homedir2(), ".vibetasks");
812
+ await mkdir2(dir, { recursive: true });
813
+ await writeFile2(this.storePath, JSON.stringify(store, null, 2));
814
+ }
815
+ /**
816
+ * Get the full UUID for a short ID
817
+ */
818
+ async getFullId(shortId) {
819
+ try {
820
+ const content = await readFile2(this.storePath, "utf-8");
821
+ const store = JSON.parse(content);
822
+ const mapping = store.mappings.find((m) => m.shortId === shortId);
823
+ return mapping?.fullId || null;
824
+ } catch {
825
+ return null;
826
+ }
827
+ }
828
+ /**
829
+ * Get the short ID for a full UUID (if it exists in current mappings)
830
+ */
831
+ async getShortId(fullId) {
832
+ try {
833
+ const content = await readFile2(this.storePath, "utf-8");
834
+ const store = JSON.parse(content);
835
+ const mapping = store.mappings.find((m) => m.fullId === fullId);
836
+ return mapping?.shortId || null;
837
+ } catch {
838
+ return null;
839
+ }
840
+ }
841
+ /**
842
+ * Resolve a partial UUID (8 characters) to full UUID by searching database
843
+ */
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) {
851
+ throw new Error(
852
+ `No task found starting with "${partialId}". Run 'vibetasks list' to see all tasks.`
853
+ );
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
+ }
884
+ return idOrShortId;
885
+ }
886
+ };
887
+
634
888
  // src/commands/add.ts
635
- 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").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) => {
636
890
  const spinner = ora2("Creating task...").start();
637
891
  try {
638
- const authManager = new AuthManager2();
892
+ const authManager = new AuthManager3();
639
893
  const taskOps = await TaskOperations.fromAuthManager(authManager);
640
894
  let projectTag;
641
895
  if (options.project) {
@@ -670,11 +924,41 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
670
924
  throw new Error(`Invalid energy level. Must be one of: ${validEnergy.join(", ")}`);
671
925
  }
672
926
  }
673
- const subtasksJson = options.subtasks?.map((subtaskTitle) => ({
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
+ }
955
+ const acceptanceCriteria = options.acceptance?.map((text) => ({
674
956
  id: randomUUID2(),
675
- title: subtaskTitle,
957
+ text,
676
958
  done: false
677
959
  })) || [];
960
+ const relevantFiles = options.files || [];
961
+ const nonGoals = options.nonGoals || [];
678
962
  let sessionId;
679
963
  let sessionHistory = [];
680
964
  const sessionManager2 = getSessionManager();
@@ -687,6 +971,7 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
687
971
  at: (/* @__PURE__ */ new Date()).toISOString()
688
972
  }];
689
973
  }
974
+ const assignedTo = options.for === "human" ? "human" : options.for === "ai" ? "ai" : options.for === "both" ? "both" : "both";
690
975
  const task = await taskOps.createTask({
691
976
  title,
692
977
  notes: options.notes,
@@ -697,14 +982,20 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
697
982
  // AUTO-TAGGED!
698
983
  created_by: createdBy,
699
984
  // ai or human
985
+ assigned_to: assignedTo,
986
+ // who can complete this task
700
987
  status: createdBy === "ai" ? "vibing" : "todo",
701
988
  // AI tasks start vibing
702
989
  energy_required: options.energy,
703
990
  subtasks_json: subtasksJson,
704
991
  session_id: sessionId,
705
992
  // Current AI session
706
- session_history: sessionHistory
993
+ session_history: sessionHistory,
707
994
  // Full history of sessions
995
+ // New AI-assisted planning fields
996
+ acceptance_criteria: acceptanceCriteria.length > 0 ? acceptanceCriteria : void 0,
997
+ relevant_files: relevantFiles.length > 0 ? relevantFiles : void 0,
998
+ non_goals: nonGoals.length > 0 ? nonGoals : void 0
708
999
  });
709
1000
  if (options.tags && options.tags.length > 0) {
710
1001
  const tagIds = [];
@@ -715,6 +1006,12 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
715
1006
  await taskOps.linkTaskTags(task.id, tagIds);
716
1007
  }
717
1008
  spinner.succeed(chalk3.green("Task created!"));
1009
+ try {
1010
+ const shortIdManager = new ShortIdManager();
1011
+ const allTasks = await taskOps.getTasks("all");
1012
+ await shortIdManager.saveMappings(allTasks);
1013
+ } catch (e) {
1014
+ }
718
1015
  if (currentSession) {
719
1016
  await sessionManager2.logAction({
720
1017
  type: "task_created",
@@ -731,6 +1028,11 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
731
1028
  console.log(chalk3.gray(`\u{1F916} Created by: ${chalk3.cyan("AI")}`));
732
1029
  console.log(chalk3.gray(`\u26A1 Status: ${chalk3.magenta("vibing")}`));
733
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
+ }
734
1036
  console.log(chalk3.gray(`ID: ${task.id.substring(0, 8)}...`));
735
1037
  console.log(chalk3.white.bold(task.title));
736
1038
  if (task.notes) {
@@ -758,8 +1060,35 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
758
1060
  console.log(chalk3.cyan(`Subtasks: ${subtasksJson.length}`));
759
1061
  subtasksJson.forEach((st, i) => {
760
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
+ }
761
1078
  });
762
1079
  }
1080
+ if (acceptanceCriteria.length > 0) {
1081
+ console.log(chalk3.cyan(`Done when:`));
1082
+ acceptanceCriteria.forEach((ac, i) => {
1083
+ console.log(chalk3.gray(` - ${ac.text}`));
1084
+ });
1085
+ }
1086
+ if (relevantFiles.length > 0) {
1087
+ console.log(chalk3.yellow(`Files: ${relevantFiles.join(", ")}`));
1088
+ }
1089
+ if (nonGoals.length > 0) {
1090
+ console.log(chalk3.gray(`Not in scope: ${nonGoals.join(", ")}`));
1091
+ }
763
1092
  console.log();
764
1093
  process.exit(0);
765
1094
  } catch (error) {
@@ -783,82 +1112,7 @@ function isRunningInClaudeCode() {
783
1112
  import { Command as Command3 } from "commander";
784
1113
  import chalk4 from "chalk";
785
1114
  import Table from "cli-table3";
786
- import { AuthManager as AuthManager3, TaskOperations as TaskOperations2 } from "@vibetasks/core";
787
-
788
- // src/utils/short-ids.ts
789
- import { writeFile as writeFile2, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
790
- import { join as join2 } from "path";
791
- import { homedir as homedir2 } from "os";
792
- var ShortIdManager = class {
793
- storePath;
794
- maxIds = 99;
795
- constructor() {
796
- this.storePath = join2(homedir2(), ".vibetasks", "short-ids.json");
797
- }
798
- /**
799
- * Save mappings from a list of tasks with their short_ids
800
- * Uses the stable short_id from the database
801
- */
802
- async saveMappings(tasks) {
803
- const mappings = tasks.filter((t) => t.short_id).slice(0, this.maxIds).map((task, index) => ({
804
- shortId: task.short_id,
805
- // Use actual short_id from database
806
- fullId: task.id,
807
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
808
- }));
809
- const store = {
810
- mappings,
811
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
812
- };
813
- const dir = join2(homedir2(), ".vibetasks");
814
- await mkdir2(dir, { recursive: true });
815
- await writeFile2(this.storePath, JSON.stringify(store, null, 2));
816
- }
817
- /**
818
- * Get the full UUID for a short ID
819
- */
820
- async getFullId(shortId) {
821
- try {
822
- const content = await readFile2(this.storePath, "utf-8");
823
- const store = JSON.parse(content);
824
- const mapping = store.mappings.find((m) => m.shortId === shortId);
825
- return mapping?.fullId || null;
826
- } catch {
827
- return null;
828
- }
829
- }
830
- /**
831
- * Get the short ID for a full UUID (if it exists in current mappings)
832
- */
833
- async getShortId(fullId) {
834
- try {
835
- const content = await readFile2(this.storePath, "utf-8");
836
- const store = JSON.parse(content);
837
- const mapping = store.mappings.find((m) => m.fullId === fullId);
838
- return mapping?.shortId || null;
839
- } catch {
840
- return null;
841
- }
842
- }
843
- /**
844
- * Resolve an ID that could be either short (1-99) or full UUID
845
- */
846
- async resolveId(idOrShortId) {
847
- const shortId = parseInt(idOrShortId, 10);
848
- if (!isNaN(shortId) && shortId >= 1 && shortId <= this.maxIds) {
849
- const fullId = await this.getFullId(shortId);
850
- if (fullId) {
851
- return fullId;
852
- }
853
- throw new Error(
854
- `Short ID #${shortId} not found. Run 'vibetasks list --short-ids' first to generate mappings.`
855
- );
856
- }
857
- return idOrShortId;
858
- }
859
- };
860
-
861
- // src/commands/list.ts
1115
+ import { AuthManager as AuthManager4, TaskOperations as TaskOperations2 } from "@vibetasks/core";
862
1116
  var listCommand = new Command3("list").description("List tasks").argument("[filter]", "Filter: all, today, upcoming, completed", "all").option("-l, --limit <number>", "Maximum number of tasks to show", "50").option("--project <name>", "Filter by project tag").option("--created-by <source>", "Filter by creator: ai or human").option("--status <status>", "Filter by status: todo, vibing, done").option("--session [id]", "Filter by session (current session if no ID given)").option("--my-session", "Only show tasks from current AI session").option("--short-ids", "Show short numeric IDs (1-99) for easy reference").action(async (filter, options) => {
863
1117
  try {
864
1118
  const validFilters = ["all", "today", "upcoming", "completed"];
@@ -867,7 +1121,7 @@ var listCommand = new Command3("list").description("List tasks").argument("[filt
867
1121
  console.error(chalk4.gray(`Valid filters: ${validFilters.join(", ")}`));
868
1122
  process.exit(1);
869
1123
  }
870
- const authManager = new AuthManager3();
1124
+ const authManager = new AuthManager4();
871
1125
  const taskOps = await TaskOperations2.fromAuthManager(authManager);
872
1126
  let tasks = await taskOps.getTasks(filter);
873
1127
  if (options.project) {
@@ -882,9 +1136,9 @@ var listCommand = new Command3("list").description("List tasks").argument("[filt
882
1136
  tasks = tasks.filter((t) => t.created_by === options.createdBy);
883
1137
  }
884
1138
  if (options.status) {
885
- const validStatuses = ["todo", "vibing", "done"];
1139
+ const validStatuses = ["todo", "vibing", "done", "paused", "archived"];
886
1140
  if (!validStatuses.includes(options.status)) {
887
- 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`));
888
1142
  process.exit(1);
889
1143
  }
890
1144
  tasks = tasks.filter((t) => t.status === options.status);
@@ -980,7 +1234,13 @@ ${filterMessages[filter] || "No tasks found"}.
980
1234
  const id = task.id.substring(0, 8);
981
1235
  const status = statusColor(`${statusEmojis[task.status || "todo"]} ${task.status || "todo"}`);
982
1236
  const title = task.status === "done" ? chalk4.strikethrough(task.title) : task.title;
983
- 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;
984
1244
  const project = task.project_tag ? chalk4.blue(task.project_tag) : chalk4.gray("-");
985
1245
  const priority = priorityColor(task.priority || "none");
986
1246
  const dueDate = task.due_date ? task.due_date.split("T")[0] : chalk4.gray("-");
@@ -1030,17 +1290,17 @@ Error: ${error.message}
1030
1290
  import { Command as Command4 } from "commander";
1031
1291
  import ora3 from "ora";
1032
1292
  import chalk5 from "chalk";
1033
- import { AuthManager as AuthManager4, TaskOperations as TaskOperations3 } from "@vibetasks/core";
1293
+ import { AuthManager as AuthManager5, TaskOperations as TaskOperations3 } from "@vibetasks/core";
1034
1294
  var doneCommand = new Command4("done").description("Mark a task as complete").argument("<id>", "Task ID, short # (1-99), or first 8 characters").option("-n, --notes <notes>", "Completion notes - what was done, any final notes").option("-c, --context <context>", "Context notes (alias for --notes)").action(async (id, options) => {
1035
1295
  const spinner = ora3("Completing task...").start();
1036
1296
  try {
1037
- const authManager = new AuthManager4();
1297
+ const authManager = new AuthManager5();
1038
1298
  const taskOps = await TaskOperations3.fromAuthManager(authManager);
1039
1299
  const shortIdManager = new ShortIdManager();
1040
- let taskId = await shortIdManager.resolveId(id);
1041
- if (taskId.length < 32) {
1300
+ let taskId2 = await shortIdManager.resolveId(id, taskOps);
1301
+ if (taskId2.length < 32) {
1042
1302
  const allTasks = await taskOps.getTasks("all");
1043
- const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
1303
+ const matchingTask = allTasks.find((t) => t.id.startsWith(taskId2));
1044
1304
  if (!matchingTask) {
1045
1305
  spinner.fail(chalk5.red("Task not found"));
1046
1306
  console.error(chalk5.gray(`
@@ -1048,11 +1308,11 @@ No task found with ID starting with: ${id}
1048
1308
  `));
1049
1309
  process.exit(1);
1050
1310
  }
1051
- taskId = matchingTask.id;
1311
+ taskId2 = matchingTask.id;
1052
1312
  }
1053
1313
  const sessionManager2 = getSessionManager();
1054
1314
  const currentSession = await sessionManager2.getOrCreateSession();
1055
- const existingTask = await taskOps.getTask(taskId);
1315
+ const existingTask = await taskOps.getTask(taskId2);
1056
1316
  const existingHistory = existingTask?.session_history || [];
1057
1317
  const updatedHistory = [
1058
1318
  ...existingHistory,
@@ -1071,8 +1331,8 @@ No task found with ID starting with: ${id}
1071
1331
  if (contextNotes) {
1072
1332
  updatePayload.context_notes = contextNotes;
1073
1333
  }
1074
- await taskOps.updateTask(taskId, updatePayload);
1075
- const task = await taskOps.completeTask(taskId);
1334
+ await taskOps.updateTask(taskId2, updatePayload);
1335
+ const task = await taskOps.completeTask(taskId2);
1076
1336
  await sessionManager2.logAction({
1077
1337
  type: "task_completed",
1078
1338
  description: `Completed: "${task.title}"`,
@@ -1093,10 +1353,22 @@ No task found with ID starting with: ${id}
1093
1353
  if (error.message.includes("Not authenticated")) {
1094
1354
  console.error(chalk5.yellow("\n\u26A0\uFE0F You need to login first"));
1095
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"));
1096
1364
  } else {
1097
1365
  console.error(chalk5.red(`
1098
- Error: ${error.message}
1099
- `));
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();
1100
1372
  }
1101
1373
  process.exit(1);
1102
1374
  }
@@ -1107,16 +1379,20 @@ import { Command as Command5 } from "commander";
1107
1379
  import chalk6 from "chalk";
1108
1380
  import ora4 from "ora";
1109
1381
  import inquirer from "inquirer";
1110
- import { AuthManager as AuthManager5, TaskOperations as TaskOperations4 } from "@vibetasks/core";
1111
- 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) => {
1112
1388
  const spinner = ora4();
1113
1389
  try {
1114
- const authManager = new AuthManager5();
1390
+ const authManager = new AuthManager6();
1115
1391
  const taskOps = await TaskOperations4.fromAuthManager(authManager);
1116
1392
  const shortIdManager = new ShortIdManager();
1117
- let taskId;
1393
+ let taskId2;
1118
1394
  if (taskIdInput) {
1119
- taskId = await shortIdManager.resolveId(taskIdInput);
1395
+ taskId2 = await shortIdManager.resolveId(taskIdInput, taskOps);
1120
1396
  }
1121
1397
  const allTasks = await taskOps.getTasks("all");
1122
1398
  const vibingTasks = allTasks.filter((t) => t.status === "vibing" && !t.completed);
@@ -1124,7 +1400,7 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
1124
1400
  console.log(chalk6.gray(`
1125
1401
  Currently vibing on ${vibingTasks.length} task(s)`));
1126
1402
  }
1127
- if (!taskId || options.pick) {
1403
+ if (!taskId2 || options.pick) {
1128
1404
  const todoTasks = allTasks.filter((t) => t.status === "todo" && !t.completed);
1129
1405
  if (todoTasks.length === 0) {
1130
1406
  console.log(chalk6.yellow('\nNo todo tasks found. Add one with `vibetasks add "task title"`\n'));
@@ -1139,10 +1415,10 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
1139
1415
  value: t.id
1140
1416
  }))
1141
1417
  }]);
1142
- taskId = selectedTask;
1418
+ taskId2 = selectedTask;
1143
1419
  }
1144
1420
  spinner.start("Starting task...");
1145
- const task = allTasks.find((t) => t.id === taskId);
1421
+ const task = allTasks.find((t) => t.id === taskId2);
1146
1422
  const sessionManager2 = getSessionManager();
1147
1423
  const currentSession = await sessionManager2.getOrCreateSession();
1148
1424
  const existingSessionHistory = task?.session_history || [];
@@ -1158,7 +1434,7 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
1158
1434
  }
1159
1435
  ];
1160
1436
  }
1161
- await taskOps.updateTask(taskId, {
1437
+ await taskOps.updateTask(taskId2, {
1162
1438
  status: "vibing",
1163
1439
  completed: false,
1164
1440
  session_id: currentSession.id,
@@ -1167,16 +1443,88 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
1167
1443
  });
1168
1444
  spinner.succeed(chalk6.green("Now vibing on:"));
1169
1445
  console.log(chalk6.white(`
1170
- ${task?.title || taskId}
1446
+ ${task?.title || taskId2}
1171
1447
  `));
1172
1448
  if (lastSession && lastSession.session_id !== currentSession.id) {
1173
1449
  console.log(chalk6.cyan(` \u{1F4CB} Continuing from session: ${lastSession.session_id}`));
1174
1450
  console.log(chalk6.gray(` This task has been worked on by ${existingSessionHistory.length} session(s)
1175
1451
  `));
1176
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
+ }
1177
1523
  console.log(chalk6.gray(" Tips:"));
1178
1524
  console.log(chalk6.gray(" \u2022 Update context notes as you work"));
1179
- 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"));
1180
1528
  console.log(chalk6.gray(' \u2022 In Claude Code, say "show my vibing tasks"\n'));
1181
1529
  } catch (error) {
1182
1530
  spinner.fail(chalk6.red("Failed to start task"));
@@ -1189,10 +1537,10 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
1189
1537
  import { Command as Command6 } from "commander";
1190
1538
  import chalk7 from "chalk";
1191
1539
  import Table2 from "cli-table3";
1192
- import { AuthManager as AuthManager6, TaskOperations as TaskOperations5 } from "@vibetasks/core";
1540
+ import { AuthManager as AuthManager7, TaskOperations as TaskOperations5 } from "@vibetasks/core";
1193
1541
  var searchCommand = new Command6("search").description("Search tasks by title").argument("<query>", "Search query").option("-l, --limit <number>", "Maximum number of results", "20").action(async (query, options) => {
1194
1542
  try {
1195
- const authManager = new AuthManager6();
1543
+ const authManager = new AuthManager7();
1196
1544
  const taskOps = await TaskOperations5.fromAuthManager(authManager);
1197
1545
  const limit = parseInt(options.limit, 10);
1198
1546
  const tasks = await taskOps.searchTasks(query, limit);
@@ -1249,7 +1597,7 @@ Error: ${error.message}
1249
1597
  import { Command as Command7 } from "commander";
1250
1598
  import ora5 from "ora";
1251
1599
  import chalk8 from "chalk";
1252
- import { AuthManager as AuthManager7, TaskOperations as TaskOperations6 } from "@vibetasks/core";
1600
+ import { AuthManager as AuthManager8, TaskOperations as TaskOperations6 } from "@vibetasks/core";
1253
1601
  var updateCommand = new Command7("update").description("Update a task").argument("<id>", "Task ID (full or first 8 characters)").option("-t, --title <title>", "New title").option("-n, --notes <notes>", "New notes").option("-d, --due <date>", "New due date").option("-p, --priority <level>", "New priority: low, medium, high, none").option("-s, --status <status>", "New status: todo, vibing, done").option("--project <name>", "New project tag").option("-e, --energy <level>", "Energy required: low, medium, high").option("-c, --context <notes>", "Update context notes").action(async (id, options) => {
1254
1602
  if (!options.title && !options.notes && !options.due && !options.priority && !options.status && !options.project && !options.energy && !options.context) {
1255
1603
  console.error(chalk8.red("\nError: No updates specified"));
@@ -1258,21 +1606,10 @@ var updateCommand = new Command7("update").description("Update a task").argument
1258
1606
  }
1259
1607
  const spinner = ora5("Updating task...").start();
1260
1608
  try {
1261
- const authManager = new AuthManager7();
1609
+ const authManager = new AuthManager8();
1262
1610
  const taskOps = await TaskOperations6.fromAuthManager(authManager);
1263
- let taskId = id;
1264
- if (id.length < 32) {
1265
- const allTasks = await taskOps.getTasks("all");
1266
- const matchingTask = allTasks.find((t) => t.id.startsWith(id));
1267
- if (!matchingTask) {
1268
- spinner.fail(chalk8.red("Task not found"));
1269
- console.error(chalk8.gray(`
1270
- No task found with ID starting with: ${id}
1271
- `));
1272
- process.exit(1);
1273
- }
1274
- taskId = matchingTask.id;
1275
- }
1611
+ const shortIdManager = new ShortIdManager();
1612
+ let taskId2 = await shortIdManager.resolveId(id, taskOps);
1276
1613
  const updates = {};
1277
1614
  if (options.title) updates.title = options.title;
1278
1615
  if (options.notes) updates.notes = options.notes;
@@ -1300,7 +1637,7 @@ No task found with ID starting with: ${id}
1300
1637
  }
1301
1638
  updates.energy_required = options.energy;
1302
1639
  }
1303
- const task = await taskOps.updateTask(taskId, updates);
1640
+ const task = await taskOps.updateTask(taskId2, updates);
1304
1641
  spinner.succeed(chalk8.green("Task updated!"));
1305
1642
  console.log(chalk8.gray(`
1306
1643
  \u2713 ${task.title}
@@ -1325,28 +1662,15 @@ import { Command as Command8 } from "commander";
1325
1662
  import ora6 from "ora";
1326
1663
  import chalk9 from "chalk";
1327
1664
  import inquirer2 from "inquirer";
1328
- import { AuthManager as AuthManager8, TaskOperations as TaskOperations7 } from "@vibetasks/core";
1665
+ import { AuthManager as AuthManager9, TaskOperations as TaskOperations7 } from "@vibetasks/core";
1329
1666
  var deleteCommand = new Command8("delete").description("Delete a task").argument("<id>", "Task ID (full or first 8 characters)").option("-y, --yes", "Skip confirmation").action(async (id, options) => {
1330
1667
  try {
1331
- const authManager = new AuthManager8();
1668
+ const authManager = new AuthManager9();
1332
1669
  const taskOps = await TaskOperations7.fromAuthManager(authManager);
1333
- let taskId = id;
1334
- let taskTitle = "";
1335
- if (id.length < 32) {
1336
- const allTasks = await taskOps.getTasks("all");
1337
- const matchingTask = allTasks.find((t) => t.id.startsWith(id));
1338
- if (!matchingTask) {
1339
- console.error(chalk9.red("\nTask not found"));
1340
- console.error(chalk9.gray(`No task found with ID starting with: ${id}
1341
- `));
1342
- process.exit(1);
1343
- }
1344
- taskId = matchingTask.id;
1345
- taskTitle = matchingTask.title;
1346
- } else {
1347
- const task = await taskOps.getTask(taskId);
1348
- taskTitle = task.title;
1349
- }
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;
1350
1674
  if (!options.yes) {
1351
1675
  const answers = await inquirer2.prompt([
1352
1676
  {
@@ -1362,7 +1686,7 @@ var deleteCommand = new Command8("delete").description("Delete a task").argument
1362
1686
  }
1363
1687
  }
1364
1688
  const spinner = ora6("Deleting task...").start();
1365
- await taskOps.deleteTask(taskId);
1689
+ await taskOps.deleteTask(taskId2);
1366
1690
  spinner.succeed(chalk9.green("Task deleted!"));
1367
1691
  console.log(chalk9.gray(`
1368
1692
  \u2717 ${taskTitle}
@@ -1384,10 +1708,10 @@ var deleteCommand = new Command8("delete").description("Delete a task").argument
1384
1708
  // src/commands/config.ts
1385
1709
  import { Command as Command9 } from "commander";
1386
1710
  import chalk10 from "chalk";
1387
- import { AuthManager as AuthManager9 } from "@vibetasks/core";
1711
+ import { AuthManager as AuthManager10 } from "@vibetasks/core";
1388
1712
  var configCommand = new Command9("config").description("View or set configuration").argument("[key]", "Configuration key").argument("[value]", "Configuration value").action(async (key, value) => {
1389
1713
  try {
1390
- const authManager = new AuthManager9();
1714
+ const authManager = new AuthManager10();
1391
1715
  if (!key) {
1392
1716
  const configManager = authManager["configManager"];
1393
1717
  const config = await configManager.getConfig();
@@ -1446,17 +1770,17 @@ import { Command as Command10 } from "commander";
1446
1770
  import chalk11 from "chalk";
1447
1771
  import ora7 from "ora";
1448
1772
  import fs from "fs/promises";
1449
- import path from "path";
1773
+ import path2 from "path";
1450
1774
  function getVibetasksHooksPath() {
1451
- 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)));
1452
1776
  const normalizedPath = process.platform === "win32" && cliRoot.startsWith("/") ? cliRoot.slice(1) : cliRoot;
1453
- return path.join(normalizedPath, "hooks");
1777
+ return path2.join(normalizedPath, "hooks");
1454
1778
  }
1455
1779
  var hooksCommand = new Command10("hooks").description("Manage hooks integration (git and Claude Code)");
1456
1780
  hooksCommand.command("install").description("Install git hooks in current repository").action(async () => {
1457
1781
  const spinner = ora7("Installing git hooks...").start();
1458
1782
  try {
1459
- const gitDir = path.join(process.cwd(), ".git");
1783
+ const gitDir = path2.join(process.cwd(), ".git");
1460
1784
  try {
1461
1785
  await fs.access(gitDir);
1462
1786
  } catch {
@@ -1464,9 +1788,9 @@ hooksCommand.command("install").description("Install git hooks in current reposi
1464
1788
  console.error(chalk11.gray("\nRun this command from the root of a git repository.\n"));
1465
1789
  process.exit(1);
1466
1790
  }
1467
- const hooksDir = path.join(gitDir, "hooks");
1791
+ const hooksDir = path2.join(gitDir, "hooks");
1468
1792
  await fs.mkdir(hooksDir, { recursive: true });
1469
- const postCommitPath = path.join(hooksDir, "post-commit");
1793
+ const postCommitPath = path2.join(hooksDir, "post-commit");
1470
1794
  const hookScript = `#!/bin/sh
1471
1795
  # VibeTasks post-commit hook
1472
1796
  # Automatically updates tasks based on commit messages
@@ -1520,7 +1844,7 @@ hooksCommand.command("claude").description("Generate Claude Code hook configurat
1520
1844
  try {
1521
1845
  const projectPath = options.project || process.cwd();
1522
1846
  const hooksPath = getVibetasksHooksPath();
1523
- const syncScriptPath = path.join(hooksPath, "sync-todos.js");
1847
+ const syncScriptPath = path2.join(hooksPath, "sync-todos.js");
1524
1848
  const hookConfig = {
1525
1849
  hooks: {
1526
1850
  SubagentStop: [
@@ -1539,9 +1863,9 @@ hooksCommand.command("claude").description("Generate Claude Code hook configurat
1539
1863
  console.log(JSON.stringify(hookConfig, null, 2));
1540
1864
  process.exit(0);
1541
1865
  }
1542
- const claudeDir = path.join(projectPath, ".claude");
1866
+ const claudeDir = path2.join(projectPath, ".claude");
1543
1867
  await fs.mkdir(claudeDir, { recursive: true });
1544
- const settingsPath = path.join(claudeDir, "settings.json");
1868
+ const settingsPath = path2.join(claudeDir, "settings.json");
1545
1869
  let existingSettings = {};
1546
1870
  try {
1547
1871
  const data = await fs.readFile(settingsPath, "utf-8");
@@ -1586,8 +1910,8 @@ Error: ${error.message}
1586
1910
  });
1587
1911
  hooksCommand.command("status").description("Show status of installed hooks").action(async () => {
1588
1912
  console.log(chalk11.blue.bold("\n VibeTasks Hooks Status\n"));
1589
- const gitHooksDir = path.join(process.cwd(), ".git", "hooks");
1590
- const postCommitPath = path.join(gitHooksDir, "post-commit");
1913
+ const gitHooksDir = path2.join(process.cwd(), ".git", "hooks");
1914
+ const postCommitPath = path2.join(gitHooksDir, "post-commit");
1591
1915
  try {
1592
1916
  const content = await fs.readFile(postCommitPath, "utf-8");
1593
1917
  if (content.includes("vibetasks") || content.includes("VibeTasks")) {
@@ -1598,7 +1922,7 @@ hooksCommand.command("status").description("Show status of installed hooks").act
1598
1922
  } catch {
1599
1923
  console.log(chalk11.gray(" [ ] Git post-commit hook not installed"));
1600
1924
  }
1601
- const claudeSettingsPath = path.join(process.cwd(), ".claude", "settings.json");
1925
+ const claudeSettingsPath = path2.join(process.cwd(), ".claude", "settings.json");
1602
1926
  try {
1603
1927
  const content = await fs.readFile(claudeSettingsPath, "utf-8");
1604
1928
  const settings = JSON.parse(content);
@@ -1621,12 +1945,12 @@ import { Command as Command11 } from "commander";
1621
1945
  import inquirer3 from "inquirer";
1622
1946
  import ora8 from "ora";
1623
1947
  import chalk12 from "chalk";
1624
- import { AuthManager as AuthManager10 } from "@vibetasks/core";
1948
+ import { AuthManager as AuthManager11 } from "@vibetasks/core";
1625
1949
  import { detectProject as detectProject2 } from "@vibetasks/shared/utils/project-detector";
1626
1950
  var initCommand = new Command11("init").description("Initialize TaskFlow for current project (auto-detect from git)").option("--name <name>", "Manually specify project name").action(async (options) => {
1627
1951
  const spinner = ora8("Detecting project...").start();
1628
1952
  try {
1629
- const authManager = new AuthManager10();
1953
+ const authManager = new AuthManager11();
1630
1954
  const cwd = process.cwd();
1631
1955
  let project;
1632
1956
  if (options.name) {
@@ -1686,12 +2010,12 @@ import inquirer4 from "inquirer";
1686
2010
  import ora9 from "ora";
1687
2011
  import chalk13 from "chalk";
1688
2012
  import fs2 from "fs/promises";
1689
- import path2 from "path";
2013
+ import path3 from "path";
1690
2014
  import os from "os";
1691
2015
  import { createServer as createServer2 } from "http";
1692
2016
  import { exec as exec2 } from "child_process";
1693
2017
  import { promisify as promisify2 } from "util";
1694
- import { AuthManager as AuthManager11, TaskOperations as TaskOperations8, createSupabaseClient } from "@vibetasks/core";
2018
+ import { AuthManager as AuthManager12, TaskOperations as TaskOperations8, createSupabaseClient } from "@vibetasks/core";
1695
2019
  import { detectProject as detectProject3 } from "@vibetasks/shared/utils/project-detector";
1696
2020
  var execAsync2 = promisify2(exec2);
1697
2021
  var SUPABASE_URL = "https://ihmayqzxqyednchbezya.supabase.co";
@@ -1728,12 +2052,12 @@ async function getAvailablePort2(startPort = 3737) {
1728
2052
  });
1729
2053
  }
1730
2054
  function getClaudeConfigPath() {
1731
- const configHome = process.env.XDG_CONFIG_HOME || path2.join(os.homedir(), ".config");
1732
- 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");
1733
2057
  }
1734
2058
  async function claudeCodeConfigExists() {
1735
2059
  try {
1736
- const configDir = path2.dirname(getClaudeConfigPath());
2060
+ const configDir = path3.dirname(getClaudeConfigPath());
1737
2061
  await fs2.access(configDir);
1738
2062
  return true;
1739
2063
  } catch {
@@ -1756,7 +2080,7 @@ function showWelcome() {
1756
2080
  }
1757
2081
  async function checkExistingAuth() {
1758
2082
  try {
1759
- const authManager = new AuthManager11();
2083
+ const authManager = new AuthManager12();
1760
2084
  const token = await authManager.getAccessToken();
1761
2085
  if (!token) return { authenticated: false };
1762
2086
  const supabaseUrl = await authManager.getConfig("supabase_url") || SUPABASE_URL;
@@ -1800,7 +2124,7 @@ async function runBrowserAuth() {
1800
2124
  return;
1801
2125
  }
1802
2126
  try {
1803
- const authManager = new AuthManager11();
2127
+ const authManager = new AuthManager12();
1804
2128
  await authManager.setAccessToken(accessToken);
1805
2129
  await authManager.setRefreshToken(refreshToken);
1806
2130
  if (supabaseUrl) await authManager.setConfig("supabase_url", supabaseUrl);
@@ -1893,7 +2217,7 @@ async function stepAuthentication() {
1893
2217
  ]);
1894
2218
  const spinner = ora9("Authenticating...").start();
1895
2219
  try {
1896
- const authManager = new AuthManager11();
2220
+ const authManager = new AuthManager12();
1897
2221
  await authManager.setConfig("supabase_url", SUPABASE_URL);
1898
2222
  await authManager.setConfig("supabase_key", SUPABASE_ANON_KEY);
1899
2223
  const supabase = createSupabaseClient({
@@ -1948,7 +2272,7 @@ async function stepClaudeCodeConfig() {
1948
2272
  const spinner = ora9("Configuring Claude Code...").start();
1949
2273
  try {
1950
2274
  const configPath = getClaudeConfigPath();
1951
- const configDir = path2.dirname(configPath);
2275
+ const configDir = path3.dirname(configPath);
1952
2276
  await fs2.mkdir(configDir, { recursive: true });
1953
2277
  let config = {};
1954
2278
  try {
@@ -2042,7 +2366,7 @@ async function stepProjectInit() {
2042
2366
  return { success: true, skipped: true };
2043
2367
  }
2044
2368
  try {
2045
- const authManager = new AuthManager11();
2369
+ const authManager = new AuthManager12();
2046
2370
  await authManager.setConfig(`project_${process.cwd()}`, project.name);
2047
2371
  console.log(chalk13.green("\n \u2713") + chalk13.white(` Project "${project.name}" configured`));
2048
2372
  return { success: true, projectName: project.name };
@@ -2059,7 +2383,7 @@ async function stepVerify() {
2059
2383
  };
2060
2384
  const supabaseSpinner = ora9("Testing Supabase connection...").start();
2061
2385
  try {
2062
- const authManager = new AuthManager11();
2386
+ const authManager = new AuthManager12();
2063
2387
  const taskOps = await TaskOperations8.fromAuthManager(authManager);
2064
2388
  const tasks = await taskOps.getTasks("all");
2065
2389
  result.supabaseConnected = true;
@@ -2197,7 +2521,7 @@ async function runNonInteractiveSetup() {
2197
2521
  }
2198
2522
  console.log(chalk13.gray("\nConfiguring Claude Code MCP server..."));
2199
2523
  const configPath = getClaudeConfigPath();
2200
- const configDir = path2.dirname(configPath);
2524
+ const configDir = path3.dirname(configPath);
2201
2525
  try {
2202
2526
  await fs2.mkdir(configDir, { recursive: true });
2203
2527
  let config = {};
@@ -2232,7 +2556,7 @@ async function runNonInteractiveSetup() {
2232
2556
  if (authResult.success) {
2233
2557
  console.log(chalk13.gray("\nVerifying connection..."));
2234
2558
  try {
2235
- const authManager = new AuthManager11();
2559
+ const authManager = new AuthManager12();
2236
2560
  const taskOps = await TaskOperations8.fromAuthManager(authManager);
2237
2561
  const tasks = await taskOps.getTasks("all");
2238
2562
  console.log(chalk13.green("\u2713") + ` Connected (${tasks.length} tasks)`);
@@ -2303,7 +2627,7 @@ import { Command as Command13 } from "commander";
2303
2627
  import chalk14 from "chalk";
2304
2628
  import ora10 from "ora";
2305
2629
  import inquirer5 from "inquirer";
2306
- import { AuthManager as AuthManager12, TaskOperations as TaskOperations9 } from "@vibetasks/core";
2630
+ import { AuthManager as AuthManager13, TaskOperations as TaskOperations9 } from "@vibetasks/core";
2307
2631
  import { detectProject as detectProject4 } from "@vibetasks/shared/utils/project-detector";
2308
2632
  import { generateErrorTaskTitle } from "@vibetasks/shared";
2309
2633
  var watchCommand = new Command13("watch").description("Monitor clipboard for errors and offer to capture them as tasks").option("-i, --interval <ms>", "Polling interval in milliseconds", "1000").option("-a, --auto", "Auto-create tasks without prompting").option("-q, --quiet", "Minimal output (only show errors)").option("--project <name>", "Override auto-detected project").action(async (options) => {
@@ -2317,7 +2641,7 @@ var watchCommand = new Command13("watch").description("Monitor clipboard for err
2317
2641
  let authManager;
2318
2642
  let taskOps;
2319
2643
  try {
2320
- authManager = new AuthManager12();
2644
+ authManager = new AuthManager13();
2321
2645
  taskOps = await TaskOperations9.fromAuthManager(authManager);
2322
2646
  } catch (error) {
2323
2647
  if (error.message.includes("Not authenticated")) {
@@ -2527,11 +2851,11 @@ import { Command as Command14 } from "commander";
2527
2851
  import chalk15 from "chalk";
2528
2852
  import ora11 from "ora";
2529
2853
  import inquirer6 from "inquirer";
2530
- import { AuthManager as AuthManager13, TaskOperations as TaskOperations10 } from "@vibetasks/core";
2531
- 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) => {
2854
+ import { AuthManager as AuthManager14, TaskOperations as TaskOperations10 } from "@vibetasks/core";
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) => {
2532
2856
  const spinner = ora11();
2533
2857
  try {
2534
- const authManager = new AuthManager13();
2858
+ const authManager = new AuthManager14();
2535
2859
  const taskOps = await TaskOperations10.fromAuthManager(authManager);
2536
2860
  if (options.list) {
2537
2861
  spinner.start("Fetching archived tasks...");
@@ -2577,7 +2901,7 @@ Restored: ${task2.title}`));
2577
2901
  `));
2578
2902
  process.exit(0);
2579
2903
  }
2580
- if (options.pick || !taskId) {
2904
+ if (options.pick || !taskId2) {
2581
2905
  const allTasks = await taskOps.getTasks("completed");
2582
2906
  if (allTasks.length === 0) {
2583
2907
  console.log(chalk15.yellow("\nNo completed tasks to archive. Complete some tasks first!\n"));
@@ -2592,16 +2916,16 @@ Restored: ${task2.title}`));
2592
2916
  value: t.id
2593
2917
  }))
2594
2918
  }]);
2595
- taskId = selectedTask;
2919
+ taskId2 = selectedTask;
2596
2920
  }
2597
- if (!taskId) {
2921
+ if (!taskId2) {
2598
2922
  console.log(chalk15.yellow("\nUsage: vibetasks archive <task-id>"));
2599
2923
  console.log(chalk15.gray(" or: vibetasks archive --pick"));
2600
2924
  console.log(chalk15.gray(" or: vibetasks archive --list"));
2601
2925
  console.log(chalk15.gray(" or: vibetasks archive --unarchive <id>\n"));
2602
2926
  process.exit(1);
2603
2927
  }
2604
- const validTaskId = taskId;
2928
+ const validTaskId = taskId2;
2605
2929
  spinner.start("Archiving task...");
2606
2930
  let targetId = validTaskId;
2607
2931
  if (validTaskId.length < 32) {
@@ -2610,7 +2934,7 @@ Restored: ${task2.title}`));
2610
2934
  if (!matchingTask) {
2611
2935
  spinner.fail(chalk15.red("Task not found"));
2612
2936
  console.error(chalk15.gray(`
2613
- No task found with ID starting with: ${taskId}
2937
+ No task found with ID starting with: ${taskId2}
2614
2938
  `));
2615
2939
  process.exit(1);
2616
2940
  }
@@ -2643,13 +2967,13 @@ Error: ${error.message}
2643
2967
  import { Command as Command15 } from "commander";
2644
2968
  import chalk16 from "chalk";
2645
2969
  import ora12 from "ora";
2646
- import { spawn } from "child_process";
2647
- import path3 from "path";
2648
- import { fileURLToPath } from "url";
2649
- import { AuthManager as AuthManager14, TaskOperations as TaskOperations11 } from "@vibetasks/core";
2970
+ import { spawn as spawn2 } from "child_process";
2971
+ import path4 from "path";
2972
+ import { fileURLToPath as fileURLToPath2 } from "url";
2973
+ import { AuthManager as AuthManager15, TaskOperations as TaskOperations11 } from "@vibetasks/core";
2650
2974
  import { generateErrorTaskTitle as generateErrorTaskTitle2 } from "@vibetasks/shared";
2651
- var __filename2 = fileURLToPath(import.meta.url);
2652
- var __dirname2 = path3.dirname(__filename2);
2975
+ var __filename2 = fileURLToPath2(import.meta.url);
2976
+ var __dirname3 = path4.dirname(__filename2);
2653
2977
  var daemonCommand = new Command15("daemon").description("Manage the VibeTasks error capture daemon").addCommand(createStartCommand()).addCommand(createStopCommand()).addCommand(createStatusCommand()).addCommand(createConfigureCommand()).addCommand(createCaptureCommand());
2654
2978
  function createStartCommand() {
2655
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) => {
@@ -2849,8 +3173,8 @@ function createCaptureCommand() {
2849
3173
  });
2850
3174
  }
2851
3175
  async function startDaemonBackground(config) {
2852
- const workerPath = path3.resolve(__dirname2, "..", "daemon-worker.js");
2853
- const child = spawn(process.execPath, [workerPath], {
3176
+ const workerPath = path4.resolve(__dirname3, "..", "daemon-worker.js");
3177
+ const child = spawn2(process.execPath, [workerPath], {
2854
3178
  detached: true,
2855
3179
  stdio: "ignore",
2856
3180
  env: {
@@ -2970,7 +3294,7 @@ async function showNotification(title, message, type = "info", duration) {
2970
3294
  async function createTaskFromError2(error) {
2971
3295
  const spinner = ora12("Creating task from error...").start();
2972
3296
  try {
2973
- const authManager = new AuthManager14();
3297
+ const authManager = new AuthManager15();
2974
3298
  const taskOps = await TaskOperations11.fromAuthManager(authManager);
2975
3299
  const title = generateErrorTaskTitle2(error);
2976
3300
  const notes = formatErrorForNotes(error);
@@ -3033,11 +3357,11 @@ function getTagColor2(category) {
3033
3357
  // src/commands/next.ts
3034
3358
  import { Command as Command16 } from "commander";
3035
3359
  import chalk17 from "chalk";
3036
- import { AuthManager as AuthManager15, TaskOperations as TaskOperations12 } from "@vibetasks/core";
3360
+ import { AuthManager as AuthManager16, TaskOperations as TaskOperations12 } from "@vibetasks/core";
3037
3361
  var PRIORITY_ORDER = { high: 0, medium: 1, low: 2, none: 3 };
3038
3362
  var nextCommand = new Command16("next").description("Show and optionally start the highest priority task").option("-s, --start", "Start vibing on the task immediately").option("--project <name>", "Filter by project tag").action(async (options) => {
3039
3363
  try {
3040
- const authManager = new AuthManager15();
3364
+ const authManager = new AuthManager16();
3041
3365
  const taskOps = await TaskOperations12.fromAuthManager(authManager);
3042
3366
  let tasks = await taskOps.getTasks("all");
3043
3367
  tasks = tasks.filter((t) => t.status !== "done" && t.status !== "archived" && !t.completed);
@@ -3198,7 +3522,7 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
3198
3522
  }
3199
3523
  })
3200
3524
  ).addCommand(
3201
- new Command17("end").description("End the current session").option("-s, --summary <text>", "Add a summary of what was done").action(async (options) => {
3525
+ new Command17("end").description("End the current session").option("-s, --summary <text>", "Add a summary of what was done").option("--no-stats", "Skip showing session statistics").action(async (options) => {
3202
3526
  const sessionManager2 = getSessionManager();
3203
3527
  const session = await sessionManager2.endSession(options.summary);
3204
3528
  if (!session) {
@@ -3206,10 +3530,51 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
3206
3530
  return;
3207
3531
  }
3208
3532
  console.log(chalk18.green("\u2713 Session ended:"), chalk18.bold(session.id));
3209
- console.log(chalk18.gray("Duration:"), formatDuration(session.startedAt, session.endedAt));
3210
- console.log(chalk18.gray("Actions:"), session.actions.length);
3533
+ console.log();
3534
+ if (options.stats !== false) {
3535
+ const duration = formatDuration(session.startedAt, session.endedAt);
3536
+ const tasksCreated = session.actions.filter((a) => a.type === "task_created").length;
3537
+ const tasksCompleted = session.actions.filter((a) => a.type === "task_completed").length;
3538
+ const tasksUpdated = session.actions.filter((a) => a.type === "task_updated").length;
3539
+ const filesModified = session.actions.filter((a) => a.type === "file_modified").length;
3540
+ const notes = session.actions.filter((a) => a.type === "note").length;
3541
+ console.log(chalk18.cyan("\u{1F4CA} Session Statistics"));
3542
+ console.log(chalk18.gray("\u2500".repeat(40)));
3543
+ console.log(chalk18.white(` Duration: ${chalk18.bold(duration)}`));
3544
+ console.log(chalk18.white(` Actions logged: ${chalk18.bold(session.actions.length.toString())}`));
3545
+ console.log();
3546
+ if (tasksCreated > 0 || tasksCompleted > 0 || tasksUpdated > 0) {
3547
+ console.log(chalk18.cyan(" Tasks:"));
3548
+ if (tasksCreated > 0) {
3549
+ console.log(chalk18.green(` \u2795 Created: ${tasksCreated}`));
3550
+ }
3551
+ if (tasksCompleted > 0) {
3552
+ console.log(chalk18.green(` \u2713 Completed: ${tasksCompleted}`));
3553
+ }
3554
+ if (tasksUpdated > 0) {
3555
+ console.log(chalk18.blue(` \u{1F4DD} Updated: ${tasksUpdated}`));
3556
+ }
3557
+ }
3558
+ if (filesModified > 0) {
3559
+ console.log(chalk18.yellow(` \u{1F4C4} Files modified: ${filesModified}`));
3560
+ }
3561
+ if (notes > 0) {
3562
+ console.log(chalk18.gray(` \u{1F4AC} Notes logged: ${notes}`));
3563
+ }
3564
+ console.log(chalk18.gray("\u2500".repeat(40)));
3565
+ console.log();
3566
+ }
3211
3567
  if (options.summary) {
3212
- console.log(chalk18.gray("Summary:"), options.summary);
3568
+ console.log(chalk18.cyan("Summary:"), options.summary);
3569
+ console.log();
3570
+ }
3571
+ const totalTasks = session.actions.filter(
3572
+ (a) => a.type === "task_created" || a.type === "task_completed"
3573
+ ).length;
3574
+ if (totalTasks === 0) {
3575
+ console.log(chalk18.yellow("\u{1F4A1} Tip: Use `vibetasks add` and `vibetasks done` to track work"));
3576
+ } else if (totalTasks >= 5) {
3577
+ console.log(chalk18.green("\u{1F525} Productive session!"));
3213
3578
  }
3214
3579
  })
3215
3580
  ).addCommand(
@@ -3235,7 +3600,7 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
3235
3600
  }
3236
3601
  })
3237
3602
  ).addCommand(
3238
- new Command17("show").alias("handoff").description("Show session details for handoff to next AI").argument("<session-id>", "Session ID (partial match supported)").action(async (sessionId) => {
3603
+ new Command17("show").description("Show session details").argument("<session-id>", "Session ID (partial match supported)").action(async (sessionId) => {
3239
3604
  const sessionManager2 = getSessionManager();
3240
3605
  const handoff = await sessionManager2.getSessionHandoff(sessionId);
3241
3606
  console.log(handoff);
@@ -3249,6 +3614,48 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
3249
3614
  });
3250
3615
  console.log(chalk18.green("\u2713 Logged to session"));
3251
3616
  })
3617
+ ).addCommand(
3618
+ new Command17("sync").description("Sync current session and all actions to the server").action(async () => {
3619
+ const sessionManager2 = getSessionManager();
3620
+ const session = await sessionManager2.getCurrentSession();
3621
+ if (!session) {
3622
+ console.log(chalk18.yellow("No active session to sync"));
3623
+ return;
3624
+ }
3625
+ console.log(chalk18.cyan("Syncing session:"), chalk18.bold(session.id));
3626
+ console.log(chalk18.gray(`Actions to sync: ${session.actions.length}`));
3627
+ const success = await sessionManager2.syncToServer(session);
3628
+ if (success) {
3629
+ console.log(chalk18.green("\u2713 Session synced to server"));
3630
+ } else {
3631
+ console.log(chalk18.yellow("\u26A0 Sync failed (not logged in or server unavailable)"));
3632
+ }
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
+ })
3252
3659
  );
3253
3660
  function formatDuration(start, end) {
3254
3661
  const ms = new Date(end).getTime() - new Date(start).getTime();
@@ -3264,7 +3671,7 @@ function formatDuration(start, end) {
3264
3671
  import { Command as Command18 } from "commander";
3265
3672
  import ora13 from "ora";
3266
3673
  import chalk19 from "chalk";
3267
- import { AuthManager as AuthManager16, TaskOperations as TaskOperations13 } from "@vibetasks/core";
3674
+ import { AuthManager as AuthManager17, TaskOperations as TaskOperations13 } from "@vibetasks/core";
3268
3675
  function parseIndices(args) {
3269
3676
  const indices = /* @__PURE__ */ new Set();
3270
3677
  for (const arg of args) {
@@ -3306,23 +3713,11 @@ var subtaskCommand = new Command18("subtask").description("Manage subtasks withi
3306
3713
  subtaskCommand.command("list").description("List subtasks for a task").argument("<task-id>", "Task ID, short # (1-99), or first 8 characters").action(async (taskIdArg) => {
3307
3714
  const spinner = ora13("Loading subtasks...").start();
3308
3715
  try {
3309
- const authManager = new AuthManager16();
3716
+ const authManager = new AuthManager17();
3310
3717
  const taskOps = await TaskOperations13.fromAuthManager(authManager);
3311
3718
  const shortIdManager = new ShortIdManager();
3312
- let taskId = await shortIdManager.resolveId(taskIdArg);
3313
- if (taskId.length < 32) {
3314
- const allTasks = await taskOps.getTasks("all");
3315
- const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
3316
- if (!matchingTask) {
3317
- spinner.fail(chalk19.red("Task not found"));
3318
- console.error(chalk19.gray(`
3319
- No task found with ID: ${taskIdArg}
3320
- `));
3321
- process.exit(1);
3322
- }
3323
- taskId = matchingTask.id;
3324
- }
3325
- const task = await taskOps.getTask(taskId);
3719
+ let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
3720
+ const task = await taskOps.getTask(taskId2);
3326
3721
  const subtasks = task.subtasks_json || [];
3327
3722
  spinner.stop();
3328
3723
  console.log();
@@ -3354,23 +3749,11 @@ Error: ${error.message}
3354
3749
  subtaskCommand.command("done").description("Mark subtasks as complete").argument("<task-id>", "Task ID, short # (1-99), or first 8 characters").argument("<indices...>", 'Subtask indices (1-based): "1 2 3", "1-5", "1,3,5", or partial title match').action(async (taskIdArg, indicesArgs) => {
3355
3750
  const spinner = ora13("Updating subtasks...").start();
3356
3751
  try {
3357
- const authManager = new AuthManager16();
3752
+ const authManager = new AuthManager17();
3358
3753
  const taskOps = await TaskOperations13.fromAuthManager(authManager);
3359
3754
  const shortIdManager = new ShortIdManager();
3360
- let taskId = await shortIdManager.resolveId(taskIdArg);
3361
- if (taskId.length < 32) {
3362
- const allTasks = await taskOps.getTasks("all");
3363
- const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
3364
- if (!matchingTask) {
3365
- spinner.fail(chalk19.red("Task not found"));
3366
- console.error(chalk19.gray(`
3367
- No task found with ID: ${taskIdArg}
3368
- `));
3369
- process.exit(1);
3370
- }
3371
- taskId = matchingTask.id;
3372
- }
3373
- const task = await taskOps.getTask(taskId);
3755
+ let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
3756
+ const task = await taskOps.getTask(taskId2);
3374
3757
  const subtasks = [...task.subtasks_json || []];
3375
3758
  if (subtasks.length === 0) {
3376
3759
  spinner.fail(chalk19.yellow("No subtasks to update"));
@@ -3404,7 +3787,7 @@ Valid range: 1-${subtasks.length}
3404
3787
  spinner.succeed(chalk19.yellow("All specified subtasks were already done"));
3405
3788
  process.exit(0);
3406
3789
  }
3407
- await taskOps.updateTask(taskId, { subtasks_json: subtasks });
3790
+ await taskOps.updateTask(taskId2, { subtasks_json: subtasks });
3408
3791
  const doneCount = subtasks.filter((s) => s.done).length;
3409
3792
  spinner.succeed(chalk19.green(`Marked ${updated.length} subtask(s) as done`));
3410
3793
  console.log();
@@ -3415,7 +3798,7 @@ Valid range: 1-${subtasks.length}
3415
3798
  console.log(chalk19.gray(`Progress: ${doneCount}/${subtasks.length} completed`));
3416
3799
  if (doneCount === subtasks.length) {
3417
3800
  console.log();
3418
- 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)}`));
3419
3802
  }
3420
3803
  console.log();
3421
3804
  process.exit(0);
@@ -3430,23 +3813,11 @@ Error: ${error.message}
3430
3813
  subtaskCommand.command("undo").description("Mark subtasks as incomplete").argument("<task-id>", "Task ID, short # (1-99), or first 8 characters").argument("<indices...>", 'Subtask indices (1-based): "1 2 3", "1-5", "1,3,5", or partial title match').action(async (taskIdArg, indicesArgs) => {
3431
3814
  const spinner = ora13("Updating subtasks...").start();
3432
3815
  try {
3433
- const authManager = new AuthManager16();
3816
+ const authManager = new AuthManager17();
3434
3817
  const taskOps = await TaskOperations13.fromAuthManager(authManager);
3435
3818
  const shortIdManager = new ShortIdManager();
3436
- let taskId = await shortIdManager.resolveId(taskIdArg);
3437
- if (taskId.length < 32) {
3438
- const allTasks = await taskOps.getTasks("all");
3439
- const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
3440
- if (!matchingTask) {
3441
- spinner.fail(chalk19.red("Task not found"));
3442
- console.error(chalk19.gray(`
3443
- No task found with ID: ${taskIdArg}
3444
- `));
3445
- process.exit(1);
3446
- }
3447
- taskId = matchingTask.id;
3448
- }
3449
- const task = await taskOps.getTask(taskId);
3819
+ let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
3820
+ const task = await taskOps.getTask(taskId2);
3450
3821
  const subtasks = [...task.subtasks_json || []];
3451
3822
  if (subtasks.length === 0) {
3452
3823
  spinner.fail(chalk19.yellow("No subtasks to update"));
@@ -3480,7 +3851,7 @@ Valid range: 1-${subtasks.length}
3480
3851
  spinner.succeed(chalk19.yellow("All specified subtasks were already incomplete"));
3481
3852
  process.exit(0);
3482
3853
  }
3483
- await taskOps.updateTask(taskId, { subtasks_json: subtasks });
3854
+ await taskOps.updateTask(taskId2, { subtasks_json: subtasks });
3484
3855
  const doneCount = subtasks.filter((s) => s.done).length;
3485
3856
  spinner.succeed(chalk19.yellow(`Marked ${updated.length} subtask(s) as incomplete`));
3486
3857
  console.log();
@@ -3504,7 +3875,7 @@ Error: ${error.message}
3504
3875
  import { Command as Command19 } from "commander";
3505
3876
  import chalk20 from "chalk";
3506
3877
  import ora14 from "ora";
3507
- import { AuthManager as AuthManager17, TaskOperations as TaskOperations14 } from "@vibetasks/core";
3878
+ import { AuthManager as AuthManager18, TaskOperations as TaskOperations14 } from "@vibetasks/core";
3508
3879
  import {
3509
3880
  parseError,
3510
3881
  generateErrorTaskTitle as generateErrorTaskTitle3,
@@ -3602,7 +3973,7 @@ var errorCommand = new Command19("error").description("Capture an error and crea
3602
3973
  process.exit(1);
3603
3974
  }
3604
3975
  spinner.start("Creating error task...");
3605
- const authManager = new AuthManager17();
3976
+ const authManager = new AuthManager18();
3606
3977
  const taskOps = await TaskOperations14.fromAuthManager(authManager);
3607
3978
  const errorContext = parseError(errorText, "terminal");
3608
3979
  const title = errorContext ? generateErrorTaskTitle3(errorContext) : `Error: ${errorText.split("\n")[0].slice(0, 80)}`;
@@ -3642,7 +4013,7 @@ ${errorText}
3642
4013
  import { Command as Command20 } from "commander";
3643
4014
  import chalk21 from "chalk";
3644
4015
  import Table3 from "cli-table3";
3645
- import { AuthManager as AuthManager18, TaskOperations as TaskOperations15 } from "@vibetasks/core";
4016
+ import { AuthManager as AuthManager19, TaskOperations as TaskOperations15 } from "@vibetasks/core";
3646
4017
  var sourceDisplay = {
3647
4018
  sentry: { label: "Sentry", color: chalk21.red },
3648
4019
  ci: { label: "CI", color: chalk21.yellow },
@@ -3669,7 +4040,7 @@ function formatSource(sourceType) {
3669
4040
  }
3670
4041
  var inboxCommand = new Command20("inbox").description("View inbox items from all sources (Sentry, GitHub, Email, Slack, CI)").option("-s, --source <source>", "Filter by source: sentry, ci, github, email, slack, errors, feedback").option("-p, --priority <priority>", "Filter by priority: high, medium, low").option("-a, --all", "Include acknowledged items").option("-l, --limit <number>", "Maximum items to show", "20").action(async (options) => {
3671
4042
  try {
3672
- const authManager = new AuthManager18();
4043
+ const authManager = new AuthManager19();
3673
4044
  const taskOps = await TaskOperations15.fromAuthManager(authManager);
3674
4045
  let sourceTypes;
3675
4046
  if (options.source) {
@@ -3778,7 +4149,7 @@ Error: ${error.message}
3778
4149
  });
3779
4150
  var inboxStatsCommand = new Command20("stats").description("Show inbox statistics").action(async () => {
3780
4151
  try {
3781
- const authManager = new AuthManager18();
4152
+ const authManager = new AuthManager19();
3782
4153
  const taskOps = await TaskOperations15.fromAuthManager(authManager);
3783
4154
  const stats = await taskOps.getInboxStats();
3784
4155
  console.log("\n" + chalk21.bold("Inbox Statistics") + "\n");
@@ -3834,10 +4205,2650 @@ Error: ${error.message}
3834
4205
  });
3835
4206
  inboxCommand.addCommand(inboxStatsCommand);
3836
4207
 
4208
+ // src/commands/handoff.ts
4209
+ import { Command as Command21 } from "commander";
4210
+ import chalk22 from "chalk";
4211
+ import { randomUUID as randomUUID3 } from "crypto";
4212
+ import { AuthManager as AuthManager20, TaskOperations as TaskOperations16 } from "@vibetasks/core";
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) => {
4437
+ const sessionManager2 = getSessionManager();
4438
+ if (sessionId && !options?.json) {
4439
+ const handoff = await sessionManager2.getSessionHandoff(sessionId);
4440
+ console.log(handoff);
4441
+ return;
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
+ }
4493
+ console.log(chalk22.cyan("# AI Handoff"));
4494
+ console.log(chalk22.gray(`Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}`));
4495
+ console.log();
4496
+ const currentSession = await sessionManager2.getCurrentSession();
4497
+ if (currentSession) {
4498
+ console.log(chalk22.yellow("## Session"));
4499
+ console.log(`ID: ${chalk22.bold(currentSession.id)}`);
4500
+ console.log(`Project: ${currentSession.project || "unknown"}`);
4501
+ console.log(`Started: ${new Date(currentSession.startedAt).toLocaleString()}`);
4502
+ console.log(`Actions: ${currentSession.actions.length}`);
4503
+ if (currentSession.summary) {
4504
+ console.log(`Summary: ${currentSession.summary}`);
4505
+ }
4506
+ console.log();
4507
+ }
4508
+ try {
4509
+ const authManager = new AuthManager20();
4510
+ const taskOps = await TaskOperations16.fromAuthManager(authManager);
4511
+ const allTasks = await taskOps.getTasks("all");
4512
+ const vibingTasks = allTasks.filter((t) => t.status === "vibing");
4513
+ if (vibingTasks.length > 0) {
4514
+ console.log(chalk22.yellow("## Vibing Tasks (Active Work)"));
4515
+ console.log();
4516
+ for (const task of vibingTasks) {
4517
+ const shortId = task.short_id || task.id.slice(0, 8);
4518
+ console.log(chalk22.green(`### #${shortId}: ${task.title}`));
4519
+ if (task.project_tag) {
4520
+ console.log(chalk22.gray(`Project: ${task.project_tag}`));
4521
+ }
4522
+ const subtasks = task.subtasks_json || task.subtasks || [];
4523
+ if (subtasks.length > 0) {
4524
+ const done = subtasks.filter((s) => s.done).length;
4525
+ console.log(`Progress: ${done}/${subtasks.length} subtasks`);
4526
+ subtasks.forEach((s, i) => {
4527
+ const status = s.done ? chalk22.green("\u2713") : chalk22.gray("\u25CB");
4528
+ const title = s.done ? chalk22.gray(s.title) : s.title;
4529
+ console.log(` ${status} ${i + 1}. ${title}`);
4530
+ });
4531
+ }
4532
+ if (task.context_notes) {
4533
+ console.log();
4534
+ console.log(chalk22.cyan("Where I left off:"));
4535
+ console.log(task.context_notes);
4536
+ } else {
4537
+ console.log();
4538
+ console.log(chalk22.yellow("\u26A0 No context notes - update with:"));
4539
+ console.log(chalk22.gray(` vibetasks update ${shortId} --context "..."`));
4540
+ }
4541
+ console.log();
4542
+ }
4543
+ } else {
4544
+ console.log(chalk22.yellow("## No Vibing Tasks"));
4545
+ console.log(chalk22.gray("All clear - ready for new work."));
4546
+ console.log();
4547
+ }
4548
+ } catch (error) {
4549
+ console.log(chalk22.gray("(Could not fetch tasks - may need to login)"));
4550
+ console.log();
4551
+ }
4552
+ try {
4553
+ const { execSync: execSync3 } = await import("child_process");
4554
+ const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
4555
+ if (gitStatus) {
4556
+ const files = gitStatus.split("\n");
4557
+ console.log(chalk22.yellow(`## Uncommitted Changes (${files.length} files)`));
4558
+ files.slice(0, 15).forEach((f) => console.log(chalk22.gray(` ${f}`)));
4559
+ if (files.length > 15) {
4560
+ console.log(chalk22.gray(` ... and ${files.length - 15} more`));
4561
+ }
4562
+ console.log();
4563
+ }
4564
+ const commits = execSync3("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
4565
+ if (commits) {
4566
+ console.log(chalk22.yellow("## Recent Commits"));
4567
+ commits.split("\n").forEach((c) => console.log(chalk22.gray(` ${c}`)));
4568
+ console.log();
4569
+ }
4570
+ } catch {
4571
+ }
4572
+ console.log(chalk22.cyan("\u2500".repeat(60)));
4573
+ console.log(chalk22.yellow("## For Next AI"));
4574
+ console.log();
4575
+ console.log("1. Run: `vibetasks session current || vibetasks session start`");
4576
+ console.log("2. Run: `vibetasks list --status vibing --short-ids`");
4577
+ console.log('3. Read context_notes above for "where I left off"');
4578
+ console.log("4. Continue the vibing task or ask user what to do");
4579
+ if (currentSession) {
4580
+ console.log();
4581
+ console.log(chalk22.gray(`To reference this session: "Continue from session ${currentSession.id}"`));
4582
+ }
4583
+ });
4584
+
4585
+ // src/commands/parse-prd.ts
4586
+ import { Command as Command22 } from "commander";
4587
+ import ora15 from "ora";
4588
+ import chalk23 from "chalk";
4589
+ import { readFile as readFile3 } from "fs/promises";
4590
+ import { existsSync } from "fs";
4591
+ import { AuthManager as AuthManager21, TaskOperations as TaskOperations17 } from "@vibetasks/core";
4592
+ import { detectProject as detectProject6 } from "@vibetasks/shared/utils/project-detector";
4593
+ import { randomUUID as randomUUID4 } from "crypto";
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) => {
4595
+ const spinner = ora15("Parsing PRD...").start();
4596
+ try {
4597
+ if (!existsSync(filePath)) {
4598
+ throw new Error(`File not found: ${filePath}`);
4599
+ }
4600
+ const content = await readFile3(filePath, "utf-8");
4601
+ spinner.text = "Analyzing document structure...";
4602
+ const parsed = parsePrdContent(content, options.verbose);
4603
+ if (options.verbose) {
4604
+ spinner.info("Parsed structure:");
4605
+ console.log(chalk23.gray(` Title: ${parsed.title}`));
4606
+ console.log(chalk23.gray(` Summary: ${parsed.summary?.substring(0, 100)}...`));
4607
+ console.log(chalk23.gray(` Sections: ${parsed.sections.length}`));
4608
+ console.log(chalk23.gray(` Tasks: ${parsed.tasks.length}`));
4609
+ spinner.start("Continuing...");
4610
+ }
4611
+ const finalTitle = options.title || parsed.title || "Implement PRD";
4612
+ const subtasksJson = parsed.tasks.map((task2, i) => ({
4613
+ id: randomUUID4(),
4614
+ title: task2,
4615
+ done: false
4616
+ }));
4617
+ const acceptanceCriteria = parsed.acceptanceCriteria || [];
4618
+ let notes = "";
4619
+ if (parsed.summary) {
4620
+ notes += `## Summary
4621
+ ${parsed.summary}
4622
+
4623
+ `;
4624
+ }
4625
+ if (acceptanceCriteria.length > 0) {
4626
+ notes += `DONE WHEN:
4627
+ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}
4628
+
4629
+ `;
4630
+ }
4631
+ if (parsed.nonGoals && parsed.nonGoals.length > 0) {
4632
+ notes += `## Out of Scope
4633
+ ${parsed.nonGoals.map((n) => `- ${n}`).join("\n")}
4634
+ `;
4635
+ }
4636
+ if (options.dryRun) {
4637
+ spinner.succeed(chalk23.yellow("Dry run - no task created"));
4638
+ console.log();
4639
+ console.log(chalk23.white.bold(`Task: ${finalTitle}`));
4640
+ console.log(chalk23.gray(`Priority: ${options.priority}`));
4641
+ console.log();
4642
+ if (subtasksJson.length > 0) {
4643
+ console.log(chalk23.cyan("Subtasks:"));
4644
+ subtasksJson.forEach((st, i) => {
4645
+ console.log(chalk23.gray(` ${i + 1}. ${st.title}`));
4646
+ });
4647
+ console.log();
4648
+ }
4649
+ if (acceptanceCriteria.length > 0) {
4650
+ console.log(chalk23.green("Acceptance Criteria:"));
4651
+ acceptanceCriteria.forEach((c, i) => {
4652
+ console.log(chalk23.gray(` \u2713 ${c}`));
4653
+ });
4654
+ console.log();
4655
+ }
4656
+ if (parsed.nonGoals && parsed.nonGoals.length > 0) {
4657
+ console.log(chalk23.red("Non-Goals (out of scope):"));
4658
+ parsed.nonGoals.forEach((n) => {
4659
+ console.log(chalk23.gray(` \u2717 ${n}`));
4660
+ });
4661
+ }
4662
+ process.exit(0);
4663
+ return;
4664
+ }
4665
+ spinner.text = "Creating task...";
4666
+ const authManager = new AuthManager21();
4667
+ const taskOps = await TaskOperations17.fromAuthManager(authManager);
4668
+ let projectTag;
4669
+ if (options.project) {
4670
+ projectTag = options.project;
4671
+ } else {
4672
+ try {
4673
+ const detected = await detectProject6(process.cwd());
4674
+ projectTag = detected.fullName || detected.name;
4675
+ } catch {
4676
+ }
4677
+ }
4678
+ let sessionId;
4679
+ let sessionHistory = [];
4680
+ const sessionManager2 = getSessionManager();
4681
+ const currentSession = await sessionManager2.getCurrentSession();
4682
+ if (currentSession) {
4683
+ sessionId = currentSession.id;
4684
+ sessionHistory = [{
4685
+ session_id: currentSession.id,
4686
+ action: "created",
4687
+ at: (/* @__PURE__ */ new Date()).toISOString()
4688
+ }];
4689
+ }
4690
+ const task = await taskOps.createTask({
4691
+ title: finalTitle,
4692
+ notes,
4693
+ notes_format: "markdown",
4694
+ priority: options.priority,
4695
+ project_tag: projectTag,
4696
+ created_by: "ai",
4697
+ status: "todo",
4698
+ subtasks_json: subtasksJson,
4699
+ session_id: sessionId,
4700
+ session_history: sessionHistory,
4701
+ is_plan: true
4702
+ // Mark as a plan task
4703
+ });
4704
+ try {
4705
+ const shortIdManager = new ShortIdManager();
4706
+ const allTasks = await taskOps.getTasks("all");
4707
+ await shortIdManager.saveMappings(allTasks);
4708
+ } catch {
4709
+ }
4710
+ if (currentSession) {
4711
+ await sessionManager2.logAction({
4712
+ type: "task_created",
4713
+ description: `Created task from PRD: "${finalTitle}"`,
4714
+ taskId: task.id,
4715
+ taskTitle: finalTitle
4716
+ });
4717
+ }
4718
+ spinner.succeed(chalk23.green("Task created from PRD!"));
4719
+ console.log();
4720
+ console.log(chalk23.white.bold(finalTitle));
4721
+ if (projectTag) {
4722
+ console.log(chalk23.gray(`\u{1F4C1} Project: ${chalk23.white(projectTag)}`));
4723
+ }
4724
+ console.log(chalk23.gray(`ID: ${task.id.substring(0, 8)}...`));
4725
+ console.log();
4726
+ if (subtasksJson.length > 0) {
4727
+ console.log(chalk23.cyan(`\u{1F4CB} ${subtasksJson.length} subtasks created:`));
4728
+ subtasksJson.slice(0, 10).forEach((st, i) => {
4729
+ console.log(chalk23.gray(` ${i + 1}. ${st.title}`));
4730
+ });
4731
+ if (subtasksJson.length > 10) {
4732
+ console.log(chalk23.gray(` ... and ${subtasksJson.length - 10} more`));
4733
+ }
4734
+ console.log();
4735
+ }
4736
+ if (acceptanceCriteria.length > 0) {
4737
+ console.log(chalk23.green(`\u2713 ${acceptanceCriteria.length} acceptance criteria defined`));
4738
+ }
4739
+ console.log();
4740
+ console.log(chalk23.gray("Start working with:"));
4741
+ console.log(chalk23.white(` vibetasks vibing ${task.short_id || task.id.substring(0, 8)}`));
4742
+ console.log();
4743
+ process.exit(0);
4744
+ } catch (error) {
4745
+ spinner.fail(chalk23.red("Failed to parse PRD"));
4746
+ if (error.message.includes("Not authenticated")) {
4747
+ console.error(chalk23.yellow("\n\u26A0\uFE0F You need to login first"));
4748
+ console.error(chalk23.gray("Run: vibetasks login\n"));
4749
+ } else {
4750
+ console.error(chalk23.red(`
4751
+ Error: ${error.message}
4752
+ `));
4753
+ }
4754
+ process.exit(1);
4755
+ }
4756
+ });
4757
+ function parsePrdContent(content, verbose = false) {
4758
+ const lines = content.split("\n");
4759
+ const result = {
4760
+ title: "",
4761
+ sections: [],
4762
+ tasks: [],
4763
+ acceptanceCriteria: [],
4764
+ nonGoals: []
4765
+ };
4766
+ const h1Match = content.match(/^#\s+(.+)$/m);
4767
+ if (h1Match) {
4768
+ result.title = h1Match[1].trim();
4769
+ }
4770
+ const summaryMatch = content.match(/^#\s+.+\n\n([\s\S]*?)(?=\n##|$)/);
4771
+ if (summaryMatch) {
4772
+ result.summary = summaryMatch[1].trim().split("\n\n")[0];
4773
+ }
4774
+ const sectionPattern = /^##\s+(.+)$/gm;
4775
+ let match;
4776
+ while ((match = sectionPattern.exec(content)) !== null) {
4777
+ result.sections.push(match[1].trim());
4778
+ }
4779
+ const taskPatterns = [
4780
+ /^[-*]\s+\[[ x]\]\s+(.+)$/gm,
4781
+ // Checkbox items
4782
+ /^[-*]\s+(.+)$/gm,
4783
+ // Regular bullet points under task-like headers
4784
+ /^\d+\.\s+(.+)$/gm
4785
+ // Numbered items
4786
+ ];
4787
+ const taskSectionPatterns = [
4788
+ /##\s*(?:tasks?|implementation|steps?|work breakdown|subtasks?)\s*\n([\s\S]*?)(?=\n##|$)/gi,
4789
+ /##\s*(?:requirements?|deliverables?|milestones?)\s*\n([\s\S]*?)(?=\n##|$)/gi
4790
+ ];
4791
+ for (const pattern of taskSectionPatterns) {
4792
+ let sectionMatch;
4793
+ while ((sectionMatch = pattern.exec(content)) !== null) {
4794
+ const sectionContent = sectionMatch[1];
4795
+ const bulletPattern = /^[-*]\s+(.+)$/gm;
4796
+ let bulletMatch;
4797
+ while ((bulletMatch = bulletPattern.exec(sectionContent)) !== null) {
4798
+ const task = bulletMatch[1].trim();
4799
+ const cleanTask = task.replace(/^\[[ x]\]\s*/, "").trim();
4800
+ if (cleanTask && !result.tasks.includes(cleanTask)) {
4801
+ result.tasks.push(cleanTask);
4802
+ }
4803
+ }
4804
+ const numberedPattern = /^\d+\.\s+(.+)$/gm;
4805
+ while ((bulletMatch = numberedPattern.exec(sectionContent)) !== null) {
4806
+ const task = bulletMatch[1].trim();
4807
+ if (task && !result.tasks.includes(task)) {
4808
+ result.tasks.push(task);
4809
+ }
4810
+ }
4811
+ }
4812
+ }
4813
+ if (result.tasks.length === 0) {
4814
+ const bulletPattern = /^[-*]\s+(.+)$/gm;
4815
+ while ((match = bulletPattern.exec(content)) !== null) {
4816
+ const task = match[1].trim();
4817
+ if (!task.toLowerCase().includes("done when") && !task.toLowerCase().includes("success") && !task.toLowerCase().includes("criterion")) {
4818
+ result.tasks.push(task);
4819
+ }
4820
+ }
4821
+ }
4822
+ const criteriaPatterns = [
4823
+ /##\s*(?:goals?|success criteria|acceptance criteria|done when|definition of done)\s*\n([\s\S]*?)(?=\n##|$)/gi
4824
+ ];
4825
+ for (const pattern of criteriaPatterns) {
4826
+ let criteriaMatch;
4827
+ while ((criteriaMatch = pattern.exec(content)) !== null) {
4828
+ const sectionContent = criteriaMatch[1];
4829
+ const bulletPattern = /^[-*]\s+(.+)$/gm;
4830
+ let bulletMatch;
4831
+ while ((bulletMatch = bulletPattern.exec(sectionContent)) !== null) {
4832
+ const criterion = bulletMatch[1].trim();
4833
+ if (criterion && !result.acceptanceCriteria.includes(criterion)) {
4834
+ result.acceptanceCriteria.push(criterion);
4835
+ }
4836
+ }
4837
+ }
4838
+ }
4839
+ const nonGoalPatterns = [
4840
+ /##\s*(?:non-goals?|out of scope|not in scope|exclusions?)\s*\n([\s\S]*?)(?=\n##|$)/gi
4841
+ ];
4842
+ for (const pattern of nonGoalPatterns) {
4843
+ let nonGoalMatch;
4844
+ while ((nonGoalMatch = pattern.exec(content)) !== null) {
4845
+ const sectionContent = nonGoalMatch[1];
4846
+ const bulletPattern = /^[-*]\s+(.+)$/gm;
4847
+ let bulletMatch;
4848
+ while ((bulletMatch = bulletPattern.exec(sectionContent)) !== null) {
4849
+ const nonGoal = bulletMatch[1].trim();
4850
+ if (nonGoal && !result.nonGoals.includes(nonGoal)) {
4851
+ result.nonGoals.push(nonGoal);
4852
+ }
4853
+ }
4854
+ }
4855
+ }
4856
+ if (result.tasks.length > 20) {
4857
+ result.tasks = result.tasks.slice(0, 20);
4858
+ }
4859
+ return result;
4860
+ }
4861
+
4862
+ // src/commands/checkpoint.ts
4863
+ import { Command as Command23 } from "commander";
4864
+ import chalk24 from "chalk";
4865
+ import { AuthManager as AuthManager22, TaskOperations as TaskOperations18 } from "@vibetasks/core";
4866
+ import { execSync as execSync2 } from "child_process";
4867
+ import * as readline from "readline";
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) => {
4869
+ try {
4870
+ const authManager = new AuthManager22();
4871
+ const taskOps = await TaskOperations18.fromAuthManager(authManager);
4872
+ const sessionManager2 = getSessionManager();
4873
+ let task;
4874
+ if (taskId2) {
4875
+ const allTasks = await taskOps.getTasks("all");
4876
+ task = allTasks.find(
4877
+ (t) => t.id.startsWith(taskId2) || t.short_id?.toString() === taskId2
4878
+ );
4879
+ if (!task) {
4880
+ console.error(chalk24.red(`Task not found: ${taskId2}`));
4881
+ process.exit(1);
4882
+ }
4883
+ } else {
4884
+ const currentSession2 = await sessionManager2.getCurrentSession();
4885
+ const allTasks = await taskOps.getTasks("all");
4886
+ const vibingTasks = allTasks.filter((t) => t.status === "vibing");
4887
+ if (vibingTasks.length === 0) {
4888
+ console.error(chalk24.yellow("No vibing tasks found"));
4889
+ console.error(chalk24.gray("Start one with: vibetasks vibing <task-id>"));
4890
+ process.exit(1);
4891
+ }
4892
+ if (vibingTasks.length === 1) {
4893
+ task = vibingTasks[0];
4894
+ } else {
4895
+ const sessionTask = currentSession2 ? vibingTasks.find((t) => t.session_id === currentSession2.fullId) : null;
4896
+ if (sessionTask) {
4897
+ task = sessionTask;
4898
+ } else {
4899
+ console.log(chalk24.yellow("Multiple vibing tasks found:"));
4900
+ vibingTasks.forEach((t, i) => {
4901
+ console.log(chalk24.gray(` ${i + 1}. [${t.short_id || t.id.slice(0, 8)}] ${t.title}`));
4902
+ });
4903
+ console.log(chalk24.gray("\nSpecify task: vibetasks checkpoint <task-id>"));
4904
+ process.exit(1);
4905
+ }
4906
+ }
4907
+ }
4908
+ console.log(chalk24.cyan("Checkpointing:"), chalk24.bold(task.title));
4909
+ console.log(chalk24.gray(`ID: ${task.short_id || task.id.slice(0, 8)}`));
4910
+ console.log();
4911
+ let contextParts = [];
4912
+ let gitDiff = "";
4913
+ if (options.git !== false) {
4914
+ try {
4915
+ gitDiff = getGitContext();
4916
+ if (gitDiff) {
4917
+ contextParts.push(`## Git Changes
4918
+ \`\`\`
4919
+ ${gitDiff}
4920
+ \`\`\``);
4921
+ console.log(chalk24.green("\u2713"), chalk24.gray("Captured git changes"));
4922
+ }
4923
+ } catch {
4924
+ }
4925
+ }
4926
+ if (options.quick) {
4927
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4928
+ contextParts.unshift(`Checkpoint at ${timestamp}`);
4929
+ const contextNotes2 = contextParts.join("\n\n");
4930
+ await taskOps.updateTask(task.id, { context_notes: contextNotes2 });
4931
+ console.log(chalk24.green("\u2713 Quick checkpoint saved"));
4932
+ process.exit(0);
4933
+ }
4934
+ if (options.message) {
4935
+ contextParts.unshift(`## Progress
4936
+ ${options.message}`);
4937
+ const contextNotes2 = contextParts.join("\n\n");
4938
+ await taskOps.updateTask(task.id, { context_notes: contextNotes2 });
4939
+ console.log(chalk24.green("\u2713 Checkpoint saved"));
4940
+ process.exit(0);
4941
+ }
4942
+ const rl = readline.createInterface({
4943
+ input: process.stdin,
4944
+ output: process.stdout
4945
+ });
4946
+ const prompt = (question) => {
4947
+ return new Promise((resolve) => {
4948
+ rl.question(chalk24.cyan(question), (answer) => {
4949
+ resolve(answer.trim());
4950
+ });
4951
+ });
4952
+ };
4953
+ console.log(chalk24.gray("Press Enter to skip any question\n"));
4954
+ const completed = await prompt("What did you just complete? ");
4955
+ if (completed) {
4956
+ contextParts.unshift(`## Completed
4957
+ ${completed}`);
4958
+ }
4959
+ const nextStep = await prompt("What's the next step? ");
4960
+ if (nextStep) {
4961
+ contextParts.push(`## Next
4962
+ ${nextStep}`);
4963
+ }
4964
+ const blockers = await prompt("Any blockers? (or press Enter) ");
4965
+ if (blockers) {
4966
+ contextParts.push(`## Blockers
4967
+ ${blockers}`);
4968
+ }
4969
+ rl.close();
4970
+ if (contextParts.length === 0) {
4971
+ console.log(chalk24.yellow("Nothing to save (all fields empty)"));
4972
+ process.exit(0);
4973
+ }
4974
+ const contextNotes = contextParts.join("\n\n");
4975
+ await taskOps.updateTask(task.id, { context_notes: contextNotes });
4976
+ const currentSession = await sessionManager2.getCurrentSession();
4977
+ if (currentSession) {
4978
+ await sessionManager2.logAction({
4979
+ type: "task_updated",
4980
+ description: `Checkpoint: ${task.title}`,
4981
+ taskId: task.id,
4982
+ taskTitle: task.title
4983
+ });
4984
+ }
4985
+ console.log();
4986
+ console.log(chalk24.green("\u2713 Checkpoint saved"));
4987
+ console.log(chalk24.gray("Context will be available for the next AI session"));
4988
+ process.exit(0);
4989
+ } catch (error) {
4990
+ if (error.message.includes("Not authenticated")) {
4991
+ console.error(chalk24.yellow("\n\u26A0\uFE0F You need to login first"));
4992
+ console.error(chalk24.gray("Run: vibetasks login\n"));
4993
+ } else {
4994
+ console.error(chalk24.red(`
4995
+ Error: ${error.message}
4996
+ `));
4997
+ }
4998
+ process.exit(1);
4999
+ }
5000
+ });
5001
+ function getGitContext() {
5002
+ const parts = [];
5003
+ try {
5004
+ const branch = execSync2("git branch --show-current", { encoding: "utf-8" }).trim();
5005
+ parts.push(`Branch: ${branch}`);
5006
+ const status = execSync2("git status --porcelain", { encoding: "utf-8" }).trim();
5007
+ if (status) {
5008
+ const lines = status.split("\n").slice(0, 20);
5009
+ parts.push(`
5010
+ Changed files:
5011
+ ${lines.join("\n")}`);
5012
+ if (status.split("\n").length > 20) {
5013
+ parts.push(`... and ${status.split("\n").length - 20} more`);
5014
+ }
5015
+ }
5016
+ const log = execSync2("git log --oneline -3", { encoding: "utf-8" }).trim();
5017
+ if (log) {
5018
+ parts.push(`
5019
+ Recent commits:
5020
+ ${log}`);
5021
+ }
5022
+ } catch {
5023
+ return "";
5024
+ }
5025
+ return parts.join("\n");
5026
+ }
5027
+
5028
+ // src/commands/log.ts
5029
+ import { Command as Command24 } from "commander";
5030
+ import chalk25 from "chalk";
5031
+ import { AuthManager as AuthManager23, TaskOperations as TaskOperations19 } from "@vibetasks/core";
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) => {
5033
+ try {
5034
+ const authManager = new AuthManager23();
5035
+ const taskOps = await TaskOperations19.fromAuthManager(authManager);
5036
+ if (options.grep) {
5037
+ const changes2 = await taskOps.searchChanges(options.grep, parseInt(options.limit, 10));
5038
+ if (changes2.length === 0) {
5039
+ console.log(chalk25.gray(`
5040
+ No changes found matching: "${options.grep}"
5041
+ `));
5042
+ process.exit(0);
5043
+ }
5044
+ console.log(chalk25.gray(`
5045
+ Found ${changes2.length} change(s) matching "${options.grep}":
5046
+ `));
5047
+ for (const change of changes2) {
5048
+ printChange(change, options.oneline);
5049
+ }
5050
+ process.exit(0);
5051
+ }
5052
+ if (!taskArg) {
5053
+ console.error(chalk25.red("Error: Task ID or number required"));
5054
+ console.error(chalk25.gray('Usage: vibetasks log <task-id> or vibetasks log --grep "query"'));
5055
+ process.exit(1);
5056
+ }
5057
+ let taskId2 = taskArg;
5058
+ if (/^\d+$/.test(taskArg)) {
5059
+ const shortIdManager = new ShortIdManager();
5060
+ const resolved = await shortIdManager.resolveId(taskArg);
5061
+ if (resolved) {
5062
+ taskId2 = resolved;
5063
+ } else {
5064
+ console.error(chalk25.red(`Task #${taskArg} not found`));
5065
+ console.error(chalk25.gray("Run: vibetasks list --short-ids to see available tasks"));
5066
+ process.exit(1);
5067
+ }
5068
+ }
5069
+ const task = await taskOps.getTask(taskId2);
5070
+ const queryOptions = {
5071
+ limit: parseInt(options.limit, 10)
5072
+ };
5073
+ if (options.since) {
5074
+ queryOptions.since = new Date(options.since).toISOString();
5075
+ }
5076
+ if (options.until) {
5077
+ queryOptions.until = new Date(options.until).toISOString();
5078
+ }
5079
+ if (options.type) {
5080
+ queryOptions.changeTypes = options.type.split(",").map((t) => t.trim());
5081
+ }
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
+ }
5108
+ if (changes.length === 0) {
5109
+ console.log(chalk25.gray(`
5110
+ No change history found for task: ${task.title}
5111
+ `));
5112
+ process.exit(0);
5113
+ }
5114
+ console.log();
5115
+ console.log(chalk25.cyan.bold(`History for: ${task.title}`));
5116
+ console.log(chalk25.gray(`Task ID: ${taskId2}`));
5117
+ console.log(chalk25.gray(`Status: ${task.status} | Changes: ${changes.length}`));
5118
+ console.log();
5119
+ for (const change of changes) {
5120
+ printChange(change, options.oneline, task.title);
5121
+ }
5122
+ console.log();
5123
+ process.exit(0);
5124
+ } catch (error) {
5125
+ if (error.message.includes("Not authenticated")) {
5126
+ console.error(chalk25.yellow("\nYou need to login first"));
5127
+ console.error(chalk25.gray("Run: vibetasks login\n"));
5128
+ } else {
5129
+ console.error(chalk25.red(`
5130
+ Error: ${error.message}
5131
+ `));
5132
+ }
5133
+ process.exit(1);
5134
+ }
5135
+ });
5136
+ function printChange(change, oneline = false, taskTitle) {
5137
+ const changeId = change.id.substring(0, 8);
5138
+ const date = new Date(change.created_at);
5139
+ const dateStr = date.toLocaleDateString();
5140
+ const timeStr = date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
5141
+ const typeColors = {
5142
+ created: chalk25.green,
5143
+ status_changed: chalk25.cyan,
5144
+ title_changed: chalk25.yellow,
5145
+ context_updated: chalk25.blue,
5146
+ priority_changed: chalk25.magenta,
5147
+ subtask_done: chalk25.green,
5148
+ subtask_undone: chalk25.red,
5149
+ subtask_added: chalk25.cyan,
5150
+ archived: chalk25.gray,
5151
+ restored: chalk25.green,
5152
+ revert: chalk25.red,
5153
+ handoff: chalk25.magenta
5154
+ };
5155
+ const colorFn = typeColors[change.change_type] || chalk25.white;
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
+ }
5173
+ if (oneline) {
5174
+ const message2 = change.message || change.change_type;
5175
+ console.log(
5176
+ `${chalk25.yellow(changeId)} ${chalk25.gray(dateStr)} ${colorFn(change.change_type.padEnd(16))} ${actorIcon} ${message2}`
5177
+ );
5178
+ } else {
5179
+ console.log(chalk25.yellow(`change ${change.id}`));
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}`);
5191
+ console.log(`Date: ${dateStr} ${timeStr}`);
5192
+ if (change.session_id) {
5193
+ console.log(chalk25.gray(`Session: ${change.session_id.substring(0, 8)}`));
5194
+ }
5195
+ console.log();
5196
+ console.log(` ${colorFn(change.change_type)}: ${change.message || ""}`);
5197
+ if (change.old_value !== void 0 || change.new_value !== void 0) {
5198
+ if (change.field_changed === "status") {
5199
+ console.log(chalk25.gray(` ${chalk25.red(`- ${change.old_value}`)} ${chalk25.green(`+ ${change.new_value}`)}`));
5200
+ } else if (change.field_changed === "context_notes") {
5201
+ const oldVal = truncate(String(change.old_value || ""), 60);
5202
+ const newVal = truncate(String(change.new_value || ""), 60);
5203
+ if (oldVal) console.log(chalk25.red(` - ${oldVal}`));
5204
+ if (newVal) console.log(chalk25.green(` + ${newVal}`));
5205
+ } else if (change.field_changed === "subtasks") {
5206
+ const newSub = change.new_value;
5207
+ if (newSub?.title) {
5208
+ console.log(chalk25.gray(` Subtask: ${newSub.title}`));
5209
+ }
5210
+ } else if (change.old_value !== void 0 && change.new_value !== void 0) {
5211
+ console.log(chalk25.red(` - ${change.old_value}`));
5212
+ console.log(chalk25.green(` + ${change.new_value}`));
5213
+ }
5214
+ }
5215
+ console.log();
5216
+ }
5217
+ }
5218
+ function truncate(str, maxLength) {
5219
+ if (!str) return "";
5220
+ if (str.length <= maxLength) return str;
5221
+ return str.substring(0, maxLength - 3) + "...";
5222
+ }
5223
+
5224
+ // src/commands/show.ts
5225
+ import { Command as Command25 } from "commander";
5226
+ import chalk26 from "chalk";
5227
+ import { AuthManager as AuthManager24, TaskOperations as TaskOperations20 } from "@vibetasks/core";
5228
+ var showCommand = new Command25("show").description("Show details of a specific change (like git show)").argument("<change-id>", "Change ID (full or partial)").action(async (changeId, options) => {
5229
+ try {
5230
+ const authManager = new AuthManager24();
5231
+ const taskOps = await TaskOperations20.fromAuthManager(authManager);
5232
+ const change = await taskOps.getChange(changeId);
5233
+ const task = await taskOps.getTask(change.task_id);
5234
+ const date = new Date(change.created_at);
5235
+ const dateStr = date.toLocaleDateString();
5236
+ const timeStr = date.toLocaleTimeString();
5237
+ const actorIcon = change.actor_type === "ai" ? "\u{1F916}" : change.actor_type === "human" ? "\u{1F464}" : "\u2699\uFE0F";
5238
+ console.log();
5239
+ console.log(chalk26.yellow(`change ${change.id}`));
5240
+ console.log(`Task: ${chalk26.cyan(task.title)}`);
5241
+ console.log(` ${chalk26.gray(change.task_id)}`);
5242
+ console.log(`Actor: ${actorIcon} ${change.actor_type}`);
5243
+ console.log(`Date: ${dateStr} ${timeStr}`);
5244
+ if (change.session_id) {
5245
+ console.log(`Session: ${chalk26.gray(change.session_id)}`);
5246
+ }
5247
+ console.log();
5248
+ console.log(chalk26.bold(`${change.change_type}`));
5249
+ if (change.message) {
5250
+ console.log(` ${change.message}`);
5251
+ }
5252
+ console.log();
5253
+ if (change.field_changed) {
5254
+ console.log(chalk26.gray(`Field: ${change.field_changed}`));
5255
+ console.log();
5256
+ if (change.old_value !== void 0) {
5257
+ const oldVal = formatValue(change.old_value);
5258
+ console.log(chalk26.red(`- ${oldVal}`));
5259
+ }
5260
+ if (change.new_value !== void 0) {
5261
+ const newVal = formatValue(change.new_value);
5262
+ console.log(chalk26.green(`+ ${newVal}`));
5263
+ }
5264
+ }
5265
+ if (change.reverted_change_id) {
5266
+ console.log();
5267
+ console.log(chalk26.gray(`Reverted change: ${change.reverted_change_id}`));
5268
+ }
5269
+ console.log();
5270
+ process.exit(0);
5271
+ } catch (error) {
5272
+ if (error.message.includes("Not authenticated")) {
5273
+ console.error(chalk26.yellow("\nYou need to login first"));
5274
+ console.error(chalk26.gray("Run: vibetasks login\n"));
5275
+ } else if (error.code === "PGRST116") {
5276
+ console.error(chalk26.red(`
5277
+ Change not found: ${changeId}
5278
+ `));
5279
+ } else {
5280
+ console.error(chalk26.red(`
5281
+ Error: ${error.message}
5282
+ `));
5283
+ }
5284
+ process.exit(1);
5285
+ }
5286
+ });
5287
+ function formatValue(value) {
5288
+ if (value === null || value === void 0) {
5289
+ return "(empty)";
5290
+ }
5291
+ if (typeof value === "object") {
5292
+ return JSON.stringify(value, null, 2);
5293
+ }
5294
+ return String(value);
5295
+ }
5296
+
5297
+ // src/commands/pause.ts
5298
+ import { Command as Command26 } from "commander";
5299
+ import chalk27 from "chalk";
5300
+ import ora16 from "ora";
5301
+ import { AuthManager as AuthManager25, TaskOperations as TaskOperations21 } from "@vibetasks/core";
5302
+ var pauseCommand = new Command26("pause").description("Pause a task (like git stash) - can be resumed later").argument("[task]", "Task ID or short number to pause").option("-m, --message <reason>", "Reason for pausing").option("-l, --list", "List all paused tasks").action(async (taskArg, options) => {
5303
+ const spinner = ora16();
5304
+ try {
5305
+ const authManager = new AuthManager25();
5306
+ const taskOps = await TaskOperations21.fromAuthManager(authManager);
5307
+ if (options.list) {
5308
+ spinner.start("Fetching paused tasks...");
5309
+ const allTasks = await taskOps.getTasks("all");
5310
+ const pausedTasks = allTasks.filter((t) => t.status === "paused");
5311
+ spinner.stop();
5312
+ if (pausedTasks.length === 0) {
5313
+ console.log(chalk27.yellow("\nNo paused tasks found.\n"));
5314
+ console.log(chalk27.gray('Pause a task with: vibetasks pause <task-id> -m "reason"\n'));
5315
+ process.exit(0);
5316
+ }
5317
+ console.log(chalk27.white("\nPaused Tasks:\n"));
5318
+ pausedTasks.forEach((task2, index) => {
5319
+ console.log(
5320
+ chalk27.gray(`${index + 1}. `) + chalk27.yellow("\u23F8 ") + chalk27.white(task2.title) + chalk27.gray(` [${task2.id.slice(0, 8)}]`)
5321
+ );
5322
+ if (task2.context_notes) {
5323
+ const lastLine = task2.context_notes.split("\n").pop();
5324
+ if (lastLine) {
5325
+ console.log(chalk27.gray(` \u2514\u2500 ${lastLine}`));
5326
+ }
5327
+ }
5328
+ });
5329
+ console.log(chalk27.gray(`
5330
+ Total: ${pausedTasks.length} paused task(s)
5331
+ `));
5332
+ console.log(chalk27.gray("Resume with: vibetasks resume <task-id>\n"));
5333
+ process.exit(0);
5334
+ }
5335
+ if (!taskArg) {
5336
+ console.log(chalk27.yellow("\nUsage:"));
5337
+ console.log(chalk27.gray(" vibetasks pause <task-id> # Pause a specific task"));
5338
+ console.log(chalk27.gray(" vibetasks pause --list # List paused tasks\n"));
5339
+ process.exit(1);
5340
+ }
5341
+ let taskId2 = taskArg;
5342
+ if (/^\d+$/.test(taskArg)) {
5343
+ const shortIdManager = new ShortIdManager();
5344
+ const resolved = await shortIdManager.resolveId(taskArg);
5345
+ if (resolved) {
5346
+ taskId2 = resolved;
5347
+ } else {
5348
+ console.error(chalk27.red(`Task #${taskArg} not found`));
5349
+ console.error(chalk27.gray("Run: vibetasks list --short-ids to see available tasks"));
5350
+ process.exit(1);
5351
+ }
5352
+ } else if (taskArg.length < 32) {
5353
+ const allTasks = await taskOps.getTasks("all");
5354
+ const matchingTask = allTasks.find((t) => t.id.startsWith(taskArg));
5355
+ if (!matchingTask) {
5356
+ console.error(chalk27.red(`Task not found: ${taskArg}`));
5357
+ process.exit(1);
5358
+ }
5359
+ taskId2 = matchingTask.id;
5360
+ }
5361
+ const task = await taskOps.getTask(taskId2);
5362
+ if (task.status === "paused") {
5363
+ console.log(chalk27.yellow(`
5364
+ Task is already paused: ${task.title}
5365
+ `));
5366
+ console.log(chalk27.gray("Resume with: vibetasks resume " + taskId2.slice(0, 8) + "\n"));
5367
+ process.exit(0);
5368
+ }
5369
+ if (task.status === "done" || task.status === "archived") {
5370
+ console.error(chalk27.red(`
5371
+ Cannot pause a ${task.status} task.
5372
+ `));
5373
+ process.exit(1);
5374
+ }
5375
+ spinner.start("Pausing task...");
5376
+ const pausedTask = await taskOps.pauseTask(taskId2, options.message);
5377
+ spinner.succeed(chalk27.green("Task paused!"));
5378
+ console.log();
5379
+ console.log(chalk27.yellow("\u23F8 ") + chalk27.white(pausedTask.title));
5380
+ if (options.message) {
5381
+ console.log(chalk27.gray(` Reason: ${options.message}`));
5382
+ }
5383
+ console.log();
5384
+ console.log(chalk27.gray(`Resume with: vibetasks resume ${taskId2.slice(0, 8)}`));
5385
+ console.log(chalk27.gray(`View paused: vibetasks pause --list`));
5386
+ console.log();
5387
+ process.exit(0);
5388
+ } catch (error) {
5389
+ spinner.fail(chalk27.red("Failed to pause task"));
5390
+ if (error.message.includes("Not authenticated")) {
5391
+ console.error(chalk27.yellow("\nYou need to login first"));
5392
+ console.error(chalk27.gray("Run: vibetasks login\n"));
5393
+ } else {
5394
+ console.error(chalk27.red(`
5395
+ Error: ${error.message}
5396
+ `));
5397
+ }
5398
+ process.exit(1);
5399
+ }
5400
+ });
5401
+
5402
+ // src/commands/resume.ts
5403
+ import { Command as Command27 } from "commander";
5404
+ import chalk28 from "chalk";
5405
+ import ora17 from "ora";
5406
+ import inquirer7 from "inquirer";
5407
+ import { AuthManager as AuthManager26, TaskOperations as TaskOperations22 } from "@vibetasks/core";
5408
+ var resumeCommand = new Command27("resume").description("Resume a paused task (like git stash pop)").argument("[task]", "Task ID or short number to resume (or pick from list)").action(async (taskArg) => {
5409
+ const spinner = ora17();
5410
+ try {
5411
+ const authManager = new AuthManager26();
5412
+ const taskOps = await TaskOperations22.fromAuthManager(authManager);
5413
+ const allTasks = await taskOps.getTasks("all");
5414
+ const pausedTasks = allTasks.filter((t) => t.status === "paused");
5415
+ if (pausedTasks.length === 0) {
5416
+ console.log(chalk28.yellow("\nNo paused tasks to resume.\n"));
5417
+ console.log(chalk28.gray("Pause a task with: vibetasks pause <task-id>\n"));
5418
+ process.exit(0);
5419
+ }
5420
+ let taskId2;
5421
+ if (!taskArg) {
5422
+ if (pausedTasks.length === 1) {
5423
+ taskId2 = pausedTasks[0].id;
5424
+ console.log(chalk28.gray(`
5425
+ Auto-selecting the only paused task...
5426
+ `));
5427
+ } else {
5428
+ const { selectedTask } = await inquirer7.prompt([{
5429
+ type: "list",
5430
+ name: "selectedTask",
5431
+ message: "Pick a paused task to resume:",
5432
+ choices: pausedTasks.map((t) => ({
5433
+ name: `${chalk28.yellow("\u23F8")} ${t.title}${t.context_notes ? chalk28.gray(` - ${t.context_notes.split("\n").pop()?.slice(0, 40)}...`) : ""}`,
5434
+ value: t.id
5435
+ }))
5436
+ }]);
5437
+ taskId2 = selectedTask;
5438
+ }
5439
+ } else {
5440
+ taskId2 = taskArg;
5441
+ if (/^\d+$/.test(taskArg)) {
5442
+ const shortIdManager = new ShortIdManager();
5443
+ const resolved = await shortIdManager.resolveId(taskArg);
5444
+ if (resolved) {
5445
+ taskId2 = resolved;
5446
+ } else {
5447
+ console.error(chalk28.red(`Task #${taskArg} not found`));
5448
+ console.error(chalk28.gray("Run: vibetasks list --short-ids to see available tasks"));
5449
+ process.exit(1);
5450
+ }
5451
+ } else if (taskArg.length < 32) {
5452
+ const matchingTask = pausedTasks.find((t) => t.id.startsWith(taskArg)) || allTasks.find((t) => t.id.startsWith(taskArg));
5453
+ if (!matchingTask) {
5454
+ console.error(chalk28.red(`Task not found: ${taskArg}`));
5455
+ process.exit(1);
5456
+ }
5457
+ taskId2 = matchingTask.id;
5458
+ }
5459
+ }
5460
+ const task = await taskOps.getTask(taskId2);
5461
+ if (task.status !== "paused") {
5462
+ console.log(chalk28.yellow(`
5463
+ Task is not paused (status: ${task.status}): ${task.title}
5464
+ `));
5465
+ if (task.status === "todo") {
5466
+ console.log(chalk28.gray("Start vibing with: vibetasks vibing " + taskId2.slice(0, 8) + "\n"));
5467
+ }
5468
+ process.exit(0);
5469
+ }
5470
+ spinner.start("Resuming task...");
5471
+ const resumedTask = await taskOps.resumeTask(taskId2);
5472
+ spinner.succeed(chalk28.green("Task resumed!"));
5473
+ console.log();
5474
+ console.log(chalk28.magenta("\u26A1 ") + chalk28.white(resumedTask.title));
5475
+ console.log(chalk28.gray(` Status: vibing`));
5476
+ if (task.context_notes) {
5477
+ console.log(chalk28.gray(` Context: ${task.context_notes.split("\n").pop()?.slice(0, 60)}...`));
5478
+ }
5479
+ console.log();
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)}`));
5482
+ console.log();
5483
+ process.exit(0);
5484
+ } catch (error) {
5485
+ spinner.fail(chalk28.red("Failed to resume task"));
5486
+ if (error.message.includes("Not authenticated")) {
5487
+ console.error(chalk28.yellow("\nYou need to login first"));
5488
+ console.error(chalk28.gray("Run: vibetasks login\n"));
5489
+ } else {
5490
+ console.error(chalk28.red(`
5491
+ Error: ${error.message}
5492
+ `));
5493
+ }
5494
+ process.exit(1);
5495
+ }
5496
+ });
5497
+
5498
+ // src/commands/revert.ts
5499
+ import { Command as Command28 } from "commander";
5500
+ import chalk29 from "chalk";
5501
+ import ora18 from "ora";
5502
+ import inquirer8 from "inquirer";
5503
+ import { AuthManager as AuthManager27, TaskOperations as TaskOperations23 } from "@vibetasks/core";
5504
+ var revertCommand = new Command28("revert").description("Revert a specific change (like git revert) - non-destructive").argument("[change-id]", "Change ID to revert (or pick from recent changes)").option("-t, --task <task>", "Task ID or number to show changes for").option("-m, --message <message>", "Custom revert message").action(async (changeId, options) => {
5505
+ const spinner = ora18();
5506
+ try {
5507
+ const authManager = new AuthManager27();
5508
+ const taskOps = await TaskOperations23.fromAuthManager(authManager);
5509
+ if (options.task || !changeId) {
5510
+ let taskId2;
5511
+ if (options.task) {
5512
+ if (/^\d+$/.test(options.task)) {
5513
+ const shortIdManager = new ShortIdManager();
5514
+ const resolved = await shortIdManager.resolveId(options.task);
5515
+ if (resolved) {
5516
+ taskId2 = resolved;
5517
+ } else {
5518
+ console.error(chalk29.red(`Task #${options.task} not found`));
5519
+ process.exit(1);
5520
+ }
5521
+ } else if (options.task.length < 32) {
5522
+ const allTasks = await taskOps.getTasks("all");
5523
+ const matchingTask = allTasks.find((t) => t.id.startsWith(options.task));
5524
+ if (matchingTask) {
5525
+ taskId2 = matchingTask.id;
5526
+ }
5527
+ } else {
5528
+ taskId2 = options.task;
5529
+ }
5530
+ }
5531
+ if (!taskId2) {
5532
+ console.log(chalk29.yellow("\nUsage:"));
5533
+ console.log(chalk29.gray(" vibetasks revert <change-id> # Revert specific change"));
5534
+ console.log(chalk29.gray(" vibetasks revert --task <task-id> # Pick from task changes"));
5535
+ console.log(chalk29.gray(" vibetasks log <task-id> # View changes first\n"));
5536
+ process.exit(1);
5537
+ }
5538
+ const changes = await taskOps.getTaskChanges(taskId2, { limit: 20 });
5539
+ const task2 = await taskOps.getTask(taskId2);
5540
+ if (changes.length === 0) {
5541
+ console.log(chalk29.yellow(`
5542
+ No changes found for task: ${task2.title}
5543
+ `));
5544
+ process.exit(0);
5545
+ }
5546
+ const revertableChanges = changes.filter((c) => c.change_type !== "created" && c.change_type !== "revert");
5547
+ if (revertableChanges.length === 0) {
5548
+ console.log(chalk29.yellow(`
5549
+ No revertable changes for task: ${task2.title}`));
5550
+ console.log(chalk29.gray("Only the initial creation exists.\n"));
5551
+ process.exit(0);
5552
+ }
5553
+ console.log(chalk29.cyan(`
5554
+ Recent changes for: ${task2.title}
5555
+ `));
5556
+ const { selectedChange } = await inquirer8.prompt([{
5557
+ type: "list",
5558
+ name: "selectedChange",
5559
+ message: "Select a change to revert:",
5560
+ choices: revertableChanges.map((c) => {
5561
+ const date = new Date(c.created_at).toLocaleDateString();
5562
+ const typeColor = getTypeColor(c.change_type);
5563
+ return {
5564
+ name: `${chalk29.gray(c.id.slice(0, 8))} ${chalk29.gray(date)} ${typeColor(c.change_type)} - ${c.message || "No message"}`,
5565
+ value: c.id
5566
+ };
5567
+ })
5568
+ }]);
5569
+ changeId = selectedChange;
5570
+ }
5571
+ spinner.start("Loading change...");
5572
+ const change = await taskOps.getChange(changeId);
5573
+ const task = await taskOps.getTask(change.task_id);
5574
+ spinner.stop();
5575
+ console.log();
5576
+ console.log(chalk29.cyan("Change to revert:"));
5577
+ console.log(chalk29.gray(` ID: ${change.id}`));
5578
+ console.log(chalk29.gray(` Task: ${task.title}`));
5579
+ console.log(chalk29.gray(` Type: ${change.change_type}`));
5580
+ console.log(chalk29.gray(` Message: ${change.message || "No message"}`));
5581
+ if (change.old_value !== void 0 || change.new_value !== void 0) {
5582
+ console.log(chalk29.gray(` Field: ${change.field_changed}`));
5583
+ if (change.old_value !== void 0) {
5584
+ console.log(chalk29.red(` Old: ${formatValue2(change.old_value)}`));
5585
+ }
5586
+ if (change.new_value !== void 0) {
5587
+ console.log(chalk29.green(` New: ${formatValue2(change.new_value)}`));
5588
+ }
5589
+ }
5590
+ console.log();
5591
+ const { confirm } = await inquirer8.prompt([{
5592
+ type: "confirm",
5593
+ name: "confirm",
5594
+ message: "Revert this change?",
5595
+ default: true
5596
+ }]);
5597
+ if (!confirm) {
5598
+ console.log(chalk29.gray("\nRevert cancelled.\n"));
5599
+ process.exit(0);
5600
+ }
5601
+ spinner.start("Reverting change...");
5602
+ const revertChange = await taskOps.revertChange(changeId, options.message);
5603
+ spinner.succeed(chalk29.green("Change reverted!"));
5604
+ console.log();
5605
+ console.log(chalk29.yellow("\u21A9 ") + chalk29.white(`Reverted: ${change.message || change.change_type}`));
5606
+ console.log(chalk29.gray(` Revert ID: ${revertChange.id.slice(0, 8)}`));
5607
+ console.log();
5608
+ console.log(chalk29.gray(`View history: vibetasks log ${task.id.slice(0, 8)}`));
5609
+ console.log();
5610
+ process.exit(0);
5611
+ } catch (error) {
5612
+ spinner.fail(chalk29.red("Failed to revert change"));
5613
+ if (error.message.includes("Not authenticated")) {
5614
+ console.error(chalk29.yellow("\nYou need to login first"));
5615
+ console.error(chalk29.gray("Run: vibetasks login\n"));
5616
+ } else if (error.code === "PGRST116") {
5617
+ console.error(chalk29.red(`
5618
+ Change not found: ${changeId}
5619
+ `));
5620
+ console.error(chalk29.gray("Use: vibetasks log <task-id> to see valid change IDs\n"));
5621
+ } else {
5622
+ console.error(chalk29.red(`
5623
+ Error: ${error.message}
5624
+ `));
5625
+ }
5626
+ process.exit(1);
5627
+ }
5628
+ });
5629
+ function getTypeColor(changeType) {
5630
+ const colors = {
5631
+ created: chalk29.green,
5632
+ status_changed: chalk29.cyan,
5633
+ title_changed: chalk29.yellow,
5634
+ context_updated: chalk29.blue,
5635
+ priority_changed: chalk29.magenta,
5636
+ subtask_done: chalk29.green,
5637
+ subtask_undone: chalk29.red,
5638
+ subtask_added: chalk29.cyan,
5639
+ archived: chalk29.gray,
5640
+ restored: chalk29.green,
5641
+ revert: chalk29.red
5642
+ };
5643
+ return colors[changeType] || chalk29.white;
5644
+ }
5645
+ function formatValue2(value) {
5646
+ if (value === null || value === void 0) {
5647
+ return "(empty)";
5648
+ }
5649
+ if (typeof value === "object") {
5650
+ return JSON.stringify(value);
5651
+ }
5652
+ const str = String(value);
5653
+ return str.length > 50 ? str.slice(0, 47) + "..." : str;
5654
+ }
5655
+
5656
+ // src/commands/blame.ts
5657
+ import { Command as Command29 } from "commander";
5658
+ import chalk30 from "chalk";
5659
+ import { AuthManager as AuthManager28, TaskOperations as TaskOperations24 } from "@vibetasks/core";
5660
+ var blameCommand = new Command29("blame").description("Show who changed what on a task (like git blame)").argument("<task>", "Task ID or short number").option("-f, --field <field>", "Focus on specific field (status, title, context_notes, priority)").action(async (taskArg, options) => {
5661
+ try {
5662
+ const authManager = new AuthManager28();
5663
+ const taskOps = await TaskOperations24.fromAuthManager(authManager);
5664
+ let taskId2 = taskArg;
5665
+ if (/^\d+$/.test(taskArg)) {
5666
+ const shortIdManager = new ShortIdManager();
5667
+ const resolved = await shortIdManager.resolveId(taskArg);
5668
+ if (resolved) {
5669
+ taskId2 = resolved;
5670
+ } else {
5671
+ console.error(chalk30.red(`Task #${taskArg} not found`));
5672
+ console.error(chalk30.gray("Run: vibetasks list --short-ids to see available tasks"));
5673
+ process.exit(1);
5674
+ }
5675
+ } else if (taskArg.length < 32) {
5676
+ const allTasks = await taskOps.getTasks("all", true);
5677
+ const matchingTask = allTasks.find((t) => t.id.startsWith(taskArg));
5678
+ if (!matchingTask) {
5679
+ console.error(chalk30.red(`Task not found: ${taskArg}`));
5680
+ process.exit(1);
5681
+ }
5682
+ taskId2 = matchingTask.id;
5683
+ }
5684
+ const task = await taskOps.getTask(taskId2);
5685
+ const changes = await taskOps.getTaskChanges(taskId2, { limit: 100 });
5686
+ if (changes.length === 0) {
5687
+ console.log(chalk30.yellow(`
5688
+ No change history for task: ${task.title}
5689
+ `));
5690
+ process.exit(0);
5691
+ }
5692
+ const blameMap = {};
5693
+ const currentValues = {
5694
+ status: task.status,
5695
+ title: task.title,
5696
+ priority: task.priority,
5697
+ context_notes: task.context_notes,
5698
+ description: task.description
5699
+ };
5700
+ for (const change of changes) {
5701
+ if (!change.field_changed) continue;
5702
+ if (options.field && change.field_changed !== options.field) continue;
5703
+ if (!blameMap[change.field_changed]) {
5704
+ blameMap[change.field_changed] = {
5705
+ currentValue: currentValues[change.field_changed],
5706
+ changes: []
5707
+ };
5708
+ }
5709
+ blameMap[change.field_changed].changes.push({
5710
+ changeId: change.id,
5711
+ actor: change.session_id?.slice(0, 8) || "unknown",
5712
+ actorType: change.actor_type,
5713
+ date: new Date(change.created_at),
5714
+ oldValue: change.old_value,
5715
+ newValue: change.new_value,
5716
+ message: change.message || change.change_type
5717
+ });
5718
+ }
5719
+ console.log();
5720
+ console.log(chalk30.cyan.bold(`Blame for: ${task.title}`));
5721
+ console.log(chalk30.gray(`Task ID: ${taskId2}`));
5722
+ console.log();
5723
+ const fields = options.field ? [options.field] : Object.keys(blameMap).sort();
5724
+ if (fields.length === 0) {
5725
+ console.log(chalk30.yellow("No field changes tracked."));
5726
+ console.log(chalk30.gray("Only status, title, priority, context_notes changes are tracked.\n"));
5727
+ process.exit(0);
5728
+ }
5729
+ for (const field of fields) {
5730
+ const blame = blameMap[field];
5731
+ if (!blame) continue;
5732
+ console.log(chalk30.white.bold(`${field}:`));
5733
+ console.log(chalk30.gray(` Current: ${formatValue3(blame.currentValue)}`));
5734
+ console.log();
5735
+ for (const change of blame.changes.slice(0, 5)) {
5736
+ const actorIcon = change.actorType === "ai" ? "\u{1F916}" : change.actorType === "human" ? "\u{1F464}" : "\u2699\uFE0F";
5737
+ const dateStr = change.date.toLocaleDateString();
5738
+ const timeStr = change.date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
5739
+ console.log(
5740
+ chalk30.gray(` ${chalk30.yellow(change.changeId.slice(0, 8))} `) + chalk30.gray(`${dateStr} ${timeStr} `) + `${actorIcon} ` + chalk30.white(change.message)
5741
+ );
5742
+ if (change.oldValue !== void 0 || change.newValue !== void 0) {
5743
+ if (field === "status") {
5744
+ console.log(chalk30.gray(` ${chalk30.red(String(change.oldValue))} \u2192 ${chalk30.green(String(change.newValue))}`));
5745
+ } else {
5746
+ const oldStr = formatValue3(change.oldValue);
5747
+ const newStr = formatValue3(change.newValue);
5748
+ if (oldStr) console.log(chalk30.red(` - ${oldStr}`));
5749
+ if (newStr) console.log(chalk30.green(` + ${newStr}`));
5750
+ }
5751
+ }
5752
+ }
5753
+ if (blame.changes.length > 5) {
5754
+ console.log(chalk30.gray(` ... and ${blame.changes.length - 5} more changes`));
5755
+ }
5756
+ console.log();
5757
+ }
5758
+ const totalChanges = Object.values(blameMap).reduce((sum, b) => sum + b.changes.length, 0);
5759
+ const aiChanges = changes.filter((c) => c.actor_type === "ai").length;
5760
+ const humanChanges = changes.filter((c) => c.actor_type === "human").length;
5761
+ console.log(chalk30.gray("\u2500".repeat(50)));
5762
+ console.log(chalk30.gray(`Total changes: ${totalChanges} | \u{1F916} AI: ${aiChanges} | \u{1F464} Human: ${humanChanges}`));
5763
+ console.log(chalk30.gray(`
5764
+ Full history: vibetasks log ${taskId2.slice(0, 8)}`));
5765
+ console.log();
5766
+ process.exit(0);
5767
+ } catch (error) {
5768
+ if (error.message.includes("Not authenticated")) {
5769
+ console.error(chalk30.yellow("\nYou need to login first"));
5770
+ console.error(chalk30.gray("Run: vibetasks login\n"));
5771
+ } else {
5772
+ console.error(chalk30.red(`
5773
+ Error: ${error.message}
5774
+ `));
5775
+ }
5776
+ process.exit(1);
5777
+ }
5778
+ });
5779
+ function formatValue3(value) {
5780
+ if (value === null || value === void 0) {
5781
+ return "(empty)";
5782
+ }
5783
+ if (typeof value === "object") {
5784
+ return JSON.stringify(value);
5785
+ }
5786
+ const str = String(value);
5787
+ return str.length > 60 ? str.slice(0, 57) + "..." : str;
5788
+ }
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
+
3837
6848
  // bin/vibetasks.ts
3838
6849
  var require2 = createRequire(import.meta.url);
3839
6850
  var pkg = require2("../../package.json");
3840
- var program = new Command21();
6851
+ var program = new Command37();
3841
6852
  program.name("vibetasks").description("VibeTasks CLI - Fast task management for vibers").version(pkg.version);
3842
6853
  program.addCommand(setupCommand);
3843
6854
  program.addCommand(loginCommand);
@@ -3860,4 +6871,20 @@ program.addCommand(sessionCommand);
3860
6871
  program.addCommand(subtaskCommand);
3861
6872
  program.addCommand(errorCommand);
3862
6873
  program.addCommand(inboxCommand);
6874
+ program.addCommand(handoffCommand);
6875
+ program.addCommand(parsePrdCommand);
6876
+ program.addCommand(checkpointCommand);
6877
+ program.addCommand(logCommand);
6878
+ program.addCommand(showCommand);
6879
+ program.addCommand(pauseCommand);
6880
+ program.addCommand(resumeCommand);
6881
+ program.addCommand(revertCommand);
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);
3863
6890
  program.parse();